fix: restore CLI and quota provider startup (#1857)
Export ngrok tunnel capabilities for CLI startup Import Clack spinner/progress helpers used in interactive CLI paths Restore Google quota provider registry exports
This commit is contained in:
@@ -15,6 +15,8 @@ import {
|
|||||||
select,
|
select,
|
||||||
text,
|
text,
|
||||||
password,
|
password,
|
||||||
|
spinner,
|
||||||
|
progress,
|
||||||
cancel,
|
cancel,
|
||||||
isCancel,
|
isCancel,
|
||||||
} from '@clack/prompts';
|
} from '@clack/prompts';
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
async function withInteractiveTty(fn) {
|
||||||
|
const stdoutDescriptor = Object.getOwnPropertyDescriptor(process.stdout, 'isTTY');
|
||||||
|
const stdinDescriptor = Object.getOwnPropertyDescriptor(process.stdin, 'isTTY');
|
||||||
|
|
||||||
|
Object.defineProperty(process.stdout, 'isTTY', { configurable: true, value: true });
|
||||||
|
Object.defineProperty(process.stdin, 'isTTY', { configurable: true, value: true });
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await fn();
|
||||||
|
} finally {
|
||||||
|
if (stdoutDescriptor) {
|
||||||
|
Object.defineProperty(process.stdout, 'isTTY', stdoutDescriptor);
|
||||||
|
} else {
|
||||||
|
delete process.stdout.isTTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdinDescriptor) {
|
||||||
|
Object.defineProperty(process.stdin, 'isTTY', stdinDescriptor);
|
||||||
|
} else {
|
||||||
|
delete process.stdin.isTTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('cli output', () => {
|
||||||
|
it('creates interactive clack spinner and progress helpers', async () => {
|
||||||
|
await withInteractiveTty(async () => {
|
||||||
|
const output = await import('./cli-output.js?interactive-test');
|
||||||
|
|
||||||
|
expect(output.createSpinner({})).toBeTruthy();
|
||||||
|
await expect(output.createProgress({}, { max: 2 })).resolves.toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -10,6 +10,11 @@ import { pathToFileURL } from 'url';
|
|||||||
import { isModuleCliExecution, normalizeCliEntryPath } from './cli-entry.js';
|
import { isModuleCliExecution, normalizeCliEntryPath } from './cli-entry.js';
|
||||||
import { requestJson } from './lib/cli-http.js';
|
import { requestJson } from './lib/cli-http.js';
|
||||||
import { inspectTunnelAttachability } from './lib/cli-lifecycle.js';
|
import { inspectTunnelAttachability } from './lib/cli-lifecycle.js';
|
||||||
|
import { DEFAULT_TUNNEL_PROVIDER_CAPABILITIES } from './lib/cli-tunnel-capabilities.js';
|
||||||
|
import {
|
||||||
|
TUNNEL_PROVIDER_CLOUDFLARE,
|
||||||
|
TUNNEL_PROVIDER_NGROK,
|
||||||
|
} from '../server/lib/tunnels/types.js';
|
||||||
import {
|
import {
|
||||||
assertAuthenticatedNetworkExposure,
|
assertAuthenticatedNetworkExposure,
|
||||||
commands,
|
commands,
|
||||||
@@ -173,6 +178,13 @@ function spawnOpenChamberLikeHungServer(port) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('cli args', () => {
|
describe('cli args', () => {
|
||||||
|
it('loads fallback tunnel provider capabilities for CLI startup', () => {
|
||||||
|
expect(DEFAULT_TUNNEL_PROVIDER_CAPABILITIES.map((provider) => provider.provider)).toEqual([
|
||||||
|
TUNNEL_PROVIDER_CLOUDFLARE,
|
||||||
|
TUNNEL_PROVIDER_NGROK,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
it('accepts legacy daemon flags as no-ops', () => {
|
it('accepts legacy daemon flags as no-ops', () => {
|
||||||
expect(parseArgs(['serve', '--daemon']).removedFlagErrors).toEqual([]);
|
expect(parseArgs(['serve', '--daemon']).removedFlagErrors).toEqual([]);
|
||||||
expect(parseArgs(['serve', '-d']).removedFlagErrors).toEqual([]);
|
expect(parseArgs(['serve', '-d']).removedFlagErrors).toEqual([]);
|
||||||
|
|||||||
@@ -11,12 +11,20 @@ import {
|
|||||||
fetchGoogleModels
|
fetchGoogleModels
|
||||||
} from './api.js';
|
} from './api.js';
|
||||||
|
|
||||||
|
export { resolveGoogleAuthSources } from './auth.js';
|
||||||
|
|
||||||
|
export const providerId = 'google';
|
||||||
|
export const providerName = 'Google';
|
||||||
|
export const aliases = ['google', 'google.oauth'];
|
||||||
|
|
||||||
|
export const isConfigured = () => resolveGoogleAuthSources().length > 0;
|
||||||
|
|
||||||
export const fetchGoogleQuota = async () => {
|
export const fetchGoogleQuota = async () => {
|
||||||
const authSources = resolveGoogleAuthSources();
|
const authSources = resolveGoogleAuthSources();
|
||||||
if (!authSources.length) {
|
if (!authSources.length) {
|
||||||
return buildResult({
|
return buildResult({
|
||||||
providerId: 'google',
|
providerId,
|
||||||
providerName: 'Google',
|
providerName,
|
||||||
ok: false,
|
ok: false,
|
||||||
configured: false,
|
configured: false,
|
||||||
error: 'Not configured'
|
error: 'Not configured'
|
||||||
@@ -76,8 +84,8 @@ export const fetchGoogleQuota = async () => {
|
|||||||
|
|
||||||
if (!Object.keys(models).length) {
|
if (!Object.keys(models).length) {
|
||||||
return buildResult({
|
return buildResult({
|
||||||
providerId: 'google',
|
providerId,
|
||||||
providerName: 'Google',
|
providerName,
|
||||||
ok: false,
|
ok: false,
|
||||||
configured: true,
|
configured: true,
|
||||||
error: sourceErrors[0] ?? 'Failed to fetch models'
|
error: sourceErrors[0] ?? 'Failed to fetch models'
|
||||||
@@ -85,8 +93,8 @@ export const fetchGoogleQuota = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return buildResult({
|
return buildResult({
|
||||||
providerId: 'google',
|
providerId,
|
||||||
providerName: 'Google',
|
providerName,
|
||||||
ok: true,
|
ok: true,
|
||||||
configured: true,
|
configured: true,
|
||||||
usage: {
|
usage: {
|
||||||
|
|||||||
@@ -43,9 +43,9 @@ const registry = {
|
|||||||
fetchQuota: cursor.fetchQuota
|
fetchQuota: cursor.fetchQuota
|
||||||
},
|
},
|
||||||
google: {
|
google: {
|
||||||
providerId: 'google',
|
providerId: google.providerId,
|
||||||
providerName: 'Google',
|
providerName: google.providerName,
|
||||||
isConfigured: () => google.resolveGoogleAuthSources().length > 0,
|
isConfigured: google.isConfigured,
|
||||||
fetchQuota: google.fetchGoogleQuota
|
fetchQuota: google.fetchGoogleQuota
|
||||||
},
|
},
|
||||||
'zai-coding-plan': {
|
'zai-coding-plan': {
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
|
||||||
|
import * as google from './google/index.js';
|
||||||
|
import { listConfiguredQuotaProviders } from './index.js';
|
||||||
|
|
||||||
|
describe('quota provider registry', () => {
|
||||||
|
it('exposes google provider configuration helpers through the provider module', () => {
|
||||||
|
expect(google.providerId).toBe('google');
|
||||||
|
expect(google.providerName).toBe('Google');
|
||||||
|
expect(typeof google.isConfigured).toBe('function');
|
||||||
|
expect(typeof google.resolveGoogleAuthSources).toBe('function');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can list configured providers without missing provider exports', () => {
|
||||||
|
expect(() => listConfiguredQuotaProviders()).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
} from '../types.js';
|
} from '../types.js';
|
||||||
import { getTunnelDependencyInstallInfo } from '../install-help.js';
|
import { getTunnelDependencyInstallInfo } from '../install-help.js';
|
||||||
|
|
||||||
const ngrokTunnelProviderCapabilities = {
|
export const ngrokTunnelProviderCapabilities = {
|
||||||
provider: TUNNEL_PROVIDER_NGROK,
|
provider: TUNNEL_PROVIDER_NGROK,
|
||||||
defaults: {
|
defaults: {
|
||||||
mode: TUNNEL_MODE_QUICK,
|
mode: TUNNEL_MODE_QUICK,
|
||||||
|
|||||||
Reference in New Issue
Block a user