@zergai/ztc 0.2.6-dev.36101352252 → 0.2.7-dev.36236183737
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -17
- package/bin/zmux-darwin-arm64 +0 -0
- package/bin/ztc-tui-darwin-arm64 +0 -0
- package/bin/zterm-darwin-arm64 +0 -0
- package/dist/App.js +19 -5
- package/dist/agent/agent.d.ts +5 -1
- package/dist/agent/agent.js +51 -8
- package/dist/agent/backends/account_pool.d.ts +5 -6
- package/dist/agent/backends/account_pool.js +39 -14
- package/dist/agent/backends/account_rotation.d.ts +3 -0
- package/dist/agent/backends/account_rotation.js +14 -3
- package/dist/agent/backends/codex_account_status.d.ts +42 -0
- package/dist/agent/backends/codex_account_status.js +196 -0
- package/dist/agent/backends/codex_app_server.d.ts +12 -2
- package/dist/agent/backends/codex_app_server.js +66 -6
- package/dist/agent/backends/index.d.ts +2 -0
- package/dist/agent/backends/index.js +13 -5
- package/dist/agent/backends/openai.d.ts +2 -2
- package/dist/agent/backends/openai.js +4 -3
- package/dist/agent/backends/openai_compatible.js +10 -10
- package/dist/agent/backends/openai_responses.d.ts +17 -5
- package/dist/agent/backends/openai_responses.js +2 -4
- package/dist/agent/backends/token_usage.js +1 -1
- package/dist/agent/backends/types.d.ts +3 -0
- package/dist/agent/commands/accounts.d.ts +7 -1
- package/dist/agent/commands/accounts.js +299 -101
- package/dist/agent/commands/config.js +11 -3
- package/dist/agent/commands/config_profile_switch.d.ts +2 -0
- package/dist/agent/commands/config_profile_switch.js +22 -0
- package/dist/agent/commands/context.js +4 -3
- package/dist/agent/commands/index.js +1 -2
- package/dist/agent/commands/input_mode.js +1 -1
- package/dist/agent/commands/model.js +7 -2
- package/dist/agent/commands/plan.d.ts +12 -0
- package/dist/agent/commands/plan.js +39 -28
- package/dist/agent/commands/profile.d.ts +1 -1
- package/dist/agent/commands/profile.js +34 -257
- package/dist/agent/commands/provider_choices.d.ts +12 -1
- package/dist/agent/commands/provider_choices.js +29 -2
- package/dist/agent/commands/provider_selection.d.ts +3 -0
- package/dist/agent/commands/provider_selection.js +63 -0
- package/dist/agent/commands/settings.d.ts +2 -0
- package/dist/agent/commands/settings.js +655 -50
- package/dist/agent/commands/types.d.ts +19 -2
- package/dist/agent/tools/ask_user.js +2 -2
- package/dist/app/agent_runner.d.ts +1 -1
- package/dist/app/agent_runner.js +2 -2
- package/dist/app/ask_user_response.d.ts +3 -0
- package/dist/app/ask_user_response.js +16 -0
- package/dist/app/command_context.js +29 -13
- package/dist/app/controller.d.ts +17 -2
- package/dist/app/controller.js +24 -1
- package/dist/app/selection_completion.d.ts +9 -3
- package/dist/app/selection_completion.js +54 -22
- package/dist/cli_args.js +10 -1
- package/dist/cli_main.js +85 -2
- package/dist/cli_ratatui.d.ts +22 -4
- package/dist/cli_ratatui.js +421 -146
- package/dist/config/defaults.d.ts +6 -0
- package/dist/config/defaults.js +20 -0
- package/dist/config/profiles.d.ts +9 -0
- package/dist/config/profiles.js +87 -0
- package/dist/config/types.d.ts +2 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +283 -40
- package/dist/headless.js +14 -0
- package/dist/panes/model_catalog.d.ts +1 -1
- package/dist/panes/model_catalog.js +24 -2
- package/dist/panes/ztc_launch.js +6 -0
- package/dist/router_auth.js +3 -2
- package/dist/session.d.ts +2 -0
- package/dist/session.js +3 -0
- package/dist/types.d.ts +23 -2
- package/dist/ui/views/app.d.ts +3 -1
- package/dist/ui/views/app.js +3 -2
- package/dist/ui/views/completion_menu.js +6 -6
- package/dist/ui/views/input_area.d.ts +2 -1
- package/dist/ui/views/input_area.js +9 -8
- package/dist/ui/views/status_bar.d.ts +2 -0
- package/dist/ui/views/status_bar.js +15 -4
- package/dist/ui/widgets/color_panel.d.ts +9 -0
- package/dist/ui/widgets/color_panel.js +41 -26
- package/dist/ui/widgets/navigator_panel.d.ts +9 -0
- package/dist/ui/widgets/navigator_panel.js +17 -16
- package/dist/ui/widgets/panel_frame.d.ts +83 -3
- package/dist/ui/widgets/panel_frame.js +107 -9
- package/dist/ui/widgets/questionnaire.d.ts +9 -0
- package/dist/ui/widgets/questionnaire.js +214 -16
- package/dist/ui/widgets/resume_panel.d.ts +9 -0
- package/dist/ui/widgets/resume_panel.js +18 -17
- package/dist/ui/widgets/rewind_panel.d.ts +9 -0
- package/dist/ui/widgets/rewind_panel.js +15 -16
- package/dist/ui/widgets/select.d.ts +6 -0
- package/dist/ui/widgets/select.js +47 -12
- package/dist/ui/widgets/settings_panel.d.ts +75 -0
- package/dist/ui/widgets/settings_panel.js +197 -39
- package/dist/ui/widgets/status_line_panel.d.ts +9 -0
- package/dist/ui/widgets/status_line_panel.js +18 -19
- package/dist/ui/widgets/transcript_preview.d.ts +8 -0
- package/dist/ui/widgets/transcript_preview.js +10 -0
- package/dist/utils/context.js +2 -0
- package/dist/utils/fireworks.d.ts +20 -0
- package/dist/utils/fireworks.js +23 -0
- package/dist/utils/models.d.ts +2 -1
- package/dist/utils/models.js +19 -0
- package/dist/web/accounts_mirror.d.ts +7 -0
- package/dist/web/accounts_mirror.js +59 -7
- package/dist/web/mirror_stream.js +19 -5
- package/dist/zlm-embed.mjs +5734 -5362
- package/package.json +2 -2
- package/dist/agent/commands/provider.d.ts +0 -2
- package/dist/agent/commands/provider.js +0 -75
|
@@ -15,6 +15,12 @@ export declare const DEFAULT_CLARIFAI_MODEL = "https://clarifai.com/openai/chat-
|
|
|
15
15
|
* (factory) and to display the active effort in the model picker.
|
|
16
16
|
*/
|
|
17
17
|
export declare function resolveCodexEffort(config: ZTCConfig, provider?: string, model?: string): CodexReasoningEffort | undefined;
|
|
18
|
+
/**
|
|
19
|
+
* The highest-priority saved effort when the active model rejects it, so the
|
|
20
|
+
* UI can say the choice is not applied instead of silently falling back.
|
|
21
|
+
* Undefined when the top choice is supported or nothing is saved.
|
|
22
|
+
*/
|
|
23
|
+
export declare function unsupportedCodexEffort(config: ZTCConfig, provider?: string, model?: string): CodexReasoningEffort | undefined;
|
|
18
24
|
export declare function inferDefaultProvider(): string;
|
|
19
25
|
export declare function getDefaultModelForProvider(provider: string): string;
|
|
20
26
|
export declare function getDefaultApiKeyForProvider(provider: string): string | undefined;
|
package/dist/config/defaults.js
CHANGED
|
@@ -4,6 +4,7 @@ import { KIMI_CODE_AUTH_SENTINEL, hasUsableKimiCodeAuth } from '../agent/backend
|
|
|
4
4
|
import { CODEX_APP_SERVER_SENTINEL, CODEX_LEGACY_PROVIDER, isCodexAppServerProvider, isCodexLegacyProvider } from '../agent/backends/provider_ids.js';
|
|
5
5
|
import { isCodexReasoningEffortSupported } from '../utils/models.js';
|
|
6
6
|
import { DEEPSEEK_FLASH_MODEL, MODAL_DEEPSEEK_FLASH_MODEL } from '../utils/deepseek.js';
|
|
7
|
+
import { FIREWORKS_FUNDED_MODEL } from '../utils/fireworks.js';
|
|
7
8
|
export const DEFAULT_OPENAI_MODEL = 'gpt-5.4-nano';
|
|
8
9
|
export const DEFAULT_CODEX_MODEL = 'gpt-5.6-sol';
|
|
9
10
|
export const DEFAULT_ANTHROPIC_MODEL = 'claude-opus-5';
|
|
@@ -31,6 +32,21 @@ export function resolveCodexEffort(config, provider, model) {
|
|
|
31
32
|
}
|
|
32
33
|
return undefined;
|
|
33
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* The highest-priority saved effort when the active model rejects it, so the
|
|
37
|
+
* UI can say the choice is not applied instead of silently falling back.
|
|
38
|
+
* Undefined when the top choice is supported or nothing is saved.
|
|
39
|
+
*/
|
|
40
|
+
export function unsupportedCodexEffort(config, provider, model) {
|
|
41
|
+
const chosen = [
|
|
42
|
+
model ? config.codexEffortByModel?.[model] : undefined,
|
|
43
|
+
provider ? config.codexEffortByProvider?.[provider] : undefined,
|
|
44
|
+
config.codexEffort,
|
|
45
|
+
].find((candidate) => Boolean(candidate));
|
|
46
|
+
if (!chosen || isCodexReasoningEffortSupported(provider, model, chosen))
|
|
47
|
+
return undefined;
|
|
48
|
+
return chosen;
|
|
49
|
+
}
|
|
34
50
|
function has_env_key(name) {
|
|
35
51
|
const value = process.env[name];
|
|
36
52
|
return typeof value === 'string' && value.length > 0;
|
|
@@ -44,6 +60,8 @@ export function inferDefaultProvider() {
|
|
|
44
60
|
return 'gemini';
|
|
45
61
|
if (has_env_key('KIMI_API_KEY') || has_env_key('MOONSHOT_API_KEY'))
|
|
46
62
|
return 'kimi';
|
|
63
|
+
if (has_env_key('FIREWORKS_API_KEY'))
|
|
64
|
+
return 'fireworks';
|
|
47
65
|
if (has_env_key('OPENAI_COMPAT_API_KEY'))
|
|
48
66
|
return 'openai_compatible';
|
|
49
67
|
if (has_env_key('INCEPTION_API_KEY'))
|
|
@@ -80,6 +98,8 @@ export function getDefaultModelForProvider(provider) {
|
|
|
80
98
|
return DEFAULT_DEEPSEEK_MODEL;
|
|
81
99
|
case 'modal':
|
|
82
100
|
return MODAL_DEEPSEEK_FLASH_MODEL;
|
|
101
|
+
case 'fireworks':
|
|
102
|
+
return FIREWORKS_FUNDED_MODEL;
|
|
83
103
|
case 'clarifai':
|
|
84
104
|
return DEFAULT_CLARIFAI_MODEL;
|
|
85
105
|
case 'openai':
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const BUILTIN_CONFIG_PROFILES: readonly ["ztc", "ztc-coder", "ztc-datascience", "scientist"];
|
|
2
|
+
export declare function builtinProfileEmulation(name: string): string | undefined;
|
|
3
|
+
export declare function validateProfileName(name: string): string;
|
|
4
|
+
export declare function activeProfile(): string;
|
|
5
|
+
export declare function profileConfigDir(profile?: string): string;
|
|
6
|
+
/** Profiles live beside the default config. Listing never creates or changes one. */
|
|
7
|
+
export declare function listConfigProfiles(): Promise<string[]>;
|
|
8
|
+
/** Replace a launch's original --profile with the profile selected in this process. */
|
|
9
|
+
export declare function withActiveProfileArg(argv: readonly string[]): string[];
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { homedir } from 'node:os';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { readdir } from 'node:fs/promises';
|
|
5
|
+
export const BUILTIN_CONFIG_PROFILES = ['ztc', 'ztc-coder', 'ztc-datascience', 'scientist'];
|
|
6
|
+
export function builtinProfileEmulation(name) {
|
|
7
|
+
const builtin = BUILTIN_CONFIG_PROFILES.find(profile => profile === name);
|
|
8
|
+
if (!builtin)
|
|
9
|
+
return undefined;
|
|
10
|
+
const root = process.env.ZTC_CONFIG_DIR || join(homedir(), '.ztc');
|
|
11
|
+
return existsSync(join(root, 'profiles', name)) ? undefined : builtin;
|
|
12
|
+
}
|
|
13
|
+
export function validateProfileName(name) {
|
|
14
|
+
if (name !== 'default' && !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(name)) {
|
|
15
|
+
throw new Error('Profile name must be 1–64 characters: letters, numbers, dots, underscores, or hyphens; it must start with a letter or number.');
|
|
16
|
+
}
|
|
17
|
+
if (name === '.' || name === '..') {
|
|
18
|
+
throw new Error('Profile name cannot be . or ..');
|
|
19
|
+
}
|
|
20
|
+
return name;
|
|
21
|
+
}
|
|
22
|
+
export function activeProfile() {
|
|
23
|
+
return validateProfileName(process.env.ZTC_PROFILE || 'default');
|
|
24
|
+
}
|
|
25
|
+
export function profileConfigDir(profile = activeProfile()) {
|
|
26
|
+
const root = process.env.ZTC_CONFIG_DIR || join(homedir(), '.ztc');
|
|
27
|
+
const name = validateProfileName(profile);
|
|
28
|
+
if (name === 'default')
|
|
29
|
+
return root;
|
|
30
|
+
const namedDir = join(root, 'profiles', name);
|
|
31
|
+
// Built-in presets share the default config and sign-ins. Preserve an older
|
|
32
|
+
// explicitly created directory with the same name instead of hiding its data.
|
|
33
|
+
return builtinProfileEmulation(name) ? root : namedDir;
|
|
34
|
+
}
|
|
35
|
+
/** Profiles live beside the default config. Listing never creates or changes one. */
|
|
36
|
+
export async function listConfigProfiles() {
|
|
37
|
+
const root = process.env.ZTC_CONFIG_DIR || join(homedir(), '.ztc');
|
|
38
|
+
let entries;
|
|
39
|
+
try {
|
|
40
|
+
entries = await readdir(join(root, 'profiles'), { withFileTypes: true });
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (error.code === 'ENOENT') {
|
|
44
|
+
return [...BUILTIN_CONFIG_PROFILES, 'default', activeProfile()]
|
|
45
|
+
.filter((name, index, names) => names.indexOf(name) === index)
|
|
46
|
+
.sort(profileOrder);
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
const named = entries.filter(entry => entry.isDirectory() && entry.name !== 'default')
|
|
51
|
+
.map(entry => entry.name)
|
|
52
|
+
.filter(name => {
|
|
53
|
+
try {
|
|
54
|
+
validateProfileName(name);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
const profiles = new Set([activeProfile(), ...BUILTIN_CONFIG_PROFILES, ...named]);
|
|
62
|
+
profiles.delete('default');
|
|
63
|
+
return ['default', ...profiles].sort(profileOrder);
|
|
64
|
+
}
|
|
65
|
+
function profileOrder(left, right) {
|
|
66
|
+
const rank = (name) => {
|
|
67
|
+
if (name === 'default')
|
|
68
|
+
return 0;
|
|
69
|
+
const index = BUILTIN_CONFIG_PROFILES.findIndex(profile => profile === name);
|
|
70
|
+
return index < 0 ? Number.MAX_SAFE_INTEGER : index + 1;
|
|
71
|
+
};
|
|
72
|
+
return rank(left) - rank(right) || left.localeCompare(right);
|
|
73
|
+
}
|
|
74
|
+
/** Replace a launch's original --profile with the profile selected in this process. */
|
|
75
|
+
export function withActiveProfileArg(argv) {
|
|
76
|
+
const delimiter = argv.indexOf('--');
|
|
77
|
+
const flags = delimiter < 0 ? argv : argv.slice(0, delimiter);
|
|
78
|
+
const retained = [];
|
|
79
|
+
for (let i = 0; i < flags.length; i++) {
|
|
80
|
+
if (flags[i] === '--profile') {
|
|
81
|
+
i += 1;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
retained.push(flags[i]);
|
|
85
|
+
}
|
|
86
|
+
return [...retained, '--profile', activeProfile(), ...(delimiter < 0 ? [] : argv.slice(delimiter))];
|
|
87
|
+
}
|
package/dist/config/types.d.ts
CHANGED
|
@@ -297,6 +297,8 @@ export interface CompactionSettings {
|
|
|
297
297
|
jev?: JevCompactionSettings;
|
|
298
298
|
}
|
|
299
299
|
export interface ZTCConfig {
|
|
300
|
+
/** Marks the one-time replacement of pre-preset settings in this profile. */
|
|
301
|
+
settingsDefaultsVersion?: number;
|
|
300
302
|
/** Configurable terminal status line fields, ordering, separator, and colors. */
|
|
301
303
|
statusLine?: import('../ui/status_line_config.js').StatusLineSettings;
|
|
302
304
|
apiKey?: string;
|
package/dist/config.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare function build_default_config(): ZTCConfig;
|
|
|
12
12
|
export declare function shouldRefreshSuperuserStatus(env?: NodeJS.ProcessEnv): boolean;
|
|
13
13
|
export declare class ConfigStore {
|
|
14
14
|
private config;
|
|
15
|
+
private profileEmulation;
|
|
15
16
|
private legacyApiKeyFromEnvironment;
|
|
16
17
|
private routerCredentials;
|
|
17
18
|
private routerCredentialsError;
|
|
@@ -34,7 +35,11 @@ export declare class ConfigStore {
|
|
|
34
35
|
private applyEnvOverrides;
|
|
35
36
|
load(force?: boolean): Promise<ZTCConfig>;
|
|
36
37
|
refresh(): Promise<void>;
|
|
38
|
+
/** Load a profile in this process; restore the old view if runtime activation fails. */
|
|
39
|
+
switchProfile(name: string, activate: () => void | Promise<void>): Promise<void>;
|
|
37
40
|
save(): Promise<void>;
|
|
41
|
+
/** Clear the active profile's saved settings and reset this process's settings view. */
|
|
42
|
+
factoryReset(): Promise<void>;
|
|
38
43
|
get(): ZTCConfig;
|
|
39
44
|
/**
|
|
40
45
|
* The configuration without this process's `--effort` override: the saved
|
|
@@ -105,6 +110,8 @@ export declare class ConfigStore {
|
|
|
105
110
|
* per-model > per-provider > global. Defaults to the current provider/model.
|
|
106
111
|
*/
|
|
107
112
|
getCodexEffort(provider?: string, model?: string): CodexReasoningEffort | undefined;
|
|
113
|
+
/** Saved effort the active (or given) model rejects; undefined when it applies. */
|
|
114
|
+
getUnsupportedCodexEffort(provider?: string, model?: string): CodexReasoningEffort | undefined;
|
|
108
115
|
/** Effort chosen by a startup `--effort` flag for this process (= none when unset). */
|
|
109
116
|
getCodexEffortOverride(): CodexReasoningEffort | undefined;
|
|
110
117
|
/**
|
package/dist/config.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { homedir } from 'os';
|
|
2
1
|
import { join } from 'path';
|
|
3
|
-
import { readFile, writeFile, mkdir, chmod } from 'fs/promises';
|
|
2
|
+
import { readFile, writeFile, mkdir, chmod, unlink, rename, realpath } from 'fs/promises';
|
|
3
|
+
import { randomUUID } from 'crypto';
|
|
4
|
+
import { activeProfile, builtinProfileEmulation, profileConfigDir, validateProfileName } from './config/profiles.js';
|
|
4
5
|
import { accountSourceToBackendOptions, canonicalAccountProvider, probeAccountIsolated, resolveActiveAccount, resolveRotation, } from './agent/backends/account_pool.js';
|
|
5
6
|
import { notifyAccountsChanged } from './agent/backends/account_events.js';
|
|
6
|
-
import { inferDefaultProvider, getDefaultApiKeyForProvider, getDefaultModelForProvider, resolveCodexEffort } from './config/defaults.js';
|
|
7
|
+
import { inferDefaultProvider, getDefaultApiKeyForProvider, getDefaultModelForProvider, resolveCodexEffort, unsupportedCodexEffort } from './config/defaults.js';
|
|
7
8
|
import { resolveRepoModelOverride } from './config/repo_config.js';
|
|
8
9
|
import { CODEX_AUTH_SENTINEL, hasUsableCodexAuth } from './agent/backends/codex_auth.js';
|
|
9
10
|
import { CLAUDE_CODE_AUTH_SENTINEL, hasUsableClaudeCodeCliAuth as hasUsableClaudeCodeAuth } from './agent/backends/claude_cli.js';
|
|
@@ -182,21 +183,44 @@ const DEFAULT_WORKFLOWS = {
|
|
|
182
183
|
maxAgents: 50,
|
|
183
184
|
workflowTimeoutMs: 43_200_000,
|
|
184
185
|
};
|
|
186
|
+
const SETTINGS_DEFAULTS_VERSION = 1;
|
|
187
|
+
const PERSONAL_DEFAULT_PROVIDER = 'modal';
|
|
188
|
+
const PERSONAL_DEFAULT_MODEL = 'deepseek-v4.1-flash';
|
|
189
|
+
const PERSONAL_DEFAULT_PROXY_URL = 'https://zergrouter.com';
|
|
190
|
+
/** Environment variables that already name a working provider route. */
|
|
191
|
+
const ROUTE_CREDENTIAL_ENV = [
|
|
192
|
+
'ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'GOOGLE_API_KEY', 'GEMINI_API_KEY', 'KIMI_API_KEY',
|
|
193
|
+
'MOONSHOT_API_KEY', 'FIREWORKS_API_KEY', 'OPENAI_COMPAT_API_KEY', 'INCEPTION_API_KEY',
|
|
194
|
+
'CLARIFAI_API_KEY', 'DEEPSEEK_API_KEY', 'ZTC_AUTH_TOKEN',
|
|
195
|
+
];
|
|
196
|
+
/**
|
|
197
|
+
* Modal DeepSeek through Zerg Router is the default for a person starting
|
|
198
|
+
* fresh. The Modal route needs a Router device login, so a process launched
|
|
199
|
+
* with another credential in its environment (a provider key, or a platform
|
|
200
|
+
* token as headless hosts pass) keeps the route that credential serves, as
|
|
201
|
+
* ZTC did before the personal default. Only the environment is consulted, so
|
|
202
|
+
* the default does not depend on files in the user's home.
|
|
203
|
+
*/
|
|
185
204
|
function default_provider() {
|
|
186
|
-
|
|
205
|
+
const hasRouteCredential = ROUTE_CREDENTIAL_ENV.some(name => Boolean(process.env[name]?.trim()));
|
|
206
|
+
return hasRouteCredential ? inferDefaultProvider() : PERSONAL_DEFAULT_PROVIDER;
|
|
187
207
|
}
|
|
188
208
|
export function build_default_config() {
|
|
189
209
|
const provider = default_provider();
|
|
190
210
|
return {
|
|
211
|
+
settingsDefaultsVersion: SETTINGS_DEFAULTS_VERSION,
|
|
191
212
|
apiKey: getDefaultApiKeyForProvider(provider),
|
|
192
213
|
apiKeys: undefined,
|
|
193
214
|
provider,
|
|
194
215
|
openaiCompatibleBaseUrl: undefined,
|
|
195
|
-
model: getDefaultModelForProvider(provider),
|
|
216
|
+
model: provider === PERSONAL_DEFAULT_PROVIDER ? PERSONAL_DEFAULT_MODEL : getDefaultModelForProvider(provider),
|
|
196
217
|
// Like Codex, leave output limits to the provider unless explicitly set.
|
|
197
218
|
maxTokens: undefined,
|
|
198
219
|
zergEndpoint: undefined,
|
|
220
|
+
// `auto` routes through the Router once signed in and still honors an
|
|
221
|
+
// explicit provider with a direct key; `zrouter` would reject that key.
|
|
199
222
|
authMode: 'auto',
|
|
223
|
+
zergProxyUrl: PERSONAL_DEFAULT_PROXY_URL,
|
|
200
224
|
emulationId: undefined,
|
|
201
225
|
spinnerVerbs: DEFAULT_SPINNER_VERBS,
|
|
202
226
|
toolPermissions: { ...DEFAULT_TOOL_PERMISSIONS },
|
|
@@ -238,11 +262,132 @@ export function build_default_config() {
|
|
|
238
262
|
mcpServers: {},
|
|
239
263
|
};
|
|
240
264
|
}
|
|
265
|
+
/**
|
|
266
|
+
* The settings this release gives new defaults: the compaction and display
|
|
267
|
+
* behavior every existing profile adopts once. Everything else a profile saved
|
|
268
|
+
* (security settings, effort choices, limits, keybindings, the status line) is
|
|
269
|
+
* the user's and carries over untouched.
|
|
270
|
+
*/
|
|
271
|
+
const ADOPTED_DEFAULT_KEYS = [
|
|
272
|
+
'autoCompactThreshold',
|
|
273
|
+
'compactionTargetPercent',
|
|
274
|
+
'compactionTriggerMode',
|
|
275
|
+
'compaction',
|
|
276
|
+
'contextTokenBudget',
|
|
277
|
+
'autoContinue',
|
|
278
|
+
'messageDisplay',
|
|
279
|
+
];
|
|
280
|
+
/** Keys that pick the provider route. Replaced together so a route stays coherent. */
|
|
281
|
+
const ROUTING_SETTING_KEYS = [
|
|
282
|
+
'provider',
|
|
283
|
+
'model',
|
|
284
|
+
'authMode',
|
|
285
|
+
'zergProxyUrl',
|
|
286
|
+
'zergEndpoint',
|
|
287
|
+
'apiEnvironment',
|
|
288
|
+
'zrouterRoutePlan',
|
|
289
|
+
];
|
|
290
|
+
function normalizedOrigin(value) {
|
|
291
|
+
return value?.trim().replace(/\/+$/, '') || undefined;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* True when the saved provider route can already reach a model: a direct or
|
|
295
|
+
* platform credential, a subscription or account login, a configured
|
|
296
|
+
* OpenAI-compatible endpoint, an explicit direct mode, or a router other than
|
|
297
|
+
* the new default. Only a route that cannot work is replaced by the new
|
|
298
|
+
* personal default.
|
|
299
|
+
*/
|
|
300
|
+
function savedRouteIsUsable(saved) {
|
|
301
|
+
if (!saved.provider)
|
|
302
|
+
return false;
|
|
303
|
+
const provider = normalizeProviderId(saved.provider);
|
|
304
|
+
if (saved.authMode === 'direct')
|
|
305
|
+
return true;
|
|
306
|
+
const origin = normalizedOrigin(saved.zergProxyUrl);
|
|
307
|
+
if (origin && origin !== PERSONAL_DEFAULT_PROXY_URL)
|
|
308
|
+
return true;
|
|
309
|
+
if (saved.auth?.token)
|
|
310
|
+
return true;
|
|
311
|
+
if (saved.apiKey || saved.apiKeys?.[provider] || saved.apiKeys?.[saved.provider])
|
|
312
|
+
return true;
|
|
313
|
+
if (getDefaultApiKeyForProvider(provider))
|
|
314
|
+
return true;
|
|
315
|
+
if (provider === 'openai_compatible'
|
|
316
|
+
&& (saved.openaiCompatibleBaseUrl || saved.openaiCompatible?.baseUrl))
|
|
317
|
+
return true;
|
|
318
|
+
return Boolean(saved.accounts?.accounts?.some(account => normalizeProviderId(account.provider) === provider));
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Adopt this release's new defaults once: the compaction and display settings,
|
|
322
|
+
* and the Modal route for a profile whose saved route cannot reach a model.
|
|
323
|
+
* Everything else the profile saved carries over, so the migration can never
|
|
324
|
+
* widen a security setting or drop a credential.
|
|
325
|
+
*/
|
|
326
|
+
function migrateSettingsDefaults(saved) {
|
|
327
|
+
const defaults = build_default_config();
|
|
328
|
+
const migrated = { ...saved, settingsDefaultsVersion: SETTINGS_DEFAULTS_VERSION };
|
|
329
|
+
const record = migrated;
|
|
330
|
+
const fresh = defaults;
|
|
331
|
+
const adopted = savedRouteIsUsable(saved)
|
|
332
|
+
? ADOPTED_DEFAULT_KEYS
|
|
333
|
+
: [...ADOPTED_DEFAULT_KEYS, ...ROUTING_SETTING_KEYS];
|
|
334
|
+
for (const key of adopted) {
|
|
335
|
+
if (fresh[key] === undefined)
|
|
336
|
+
delete record[key];
|
|
337
|
+
else
|
|
338
|
+
record[key] = fresh[key];
|
|
339
|
+
}
|
|
340
|
+
// The legacy single slot belongs to the saved provider; give it a
|
|
341
|
+
// provider-scoped home so a later provider change cannot misattribute it.
|
|
342
|
+
if (saved.apiKey !== undefined) {
|
|
343
|
+
const oldProvider = saved.provider || inferDefaultProvider();
|
|
344
|
+
migrated.apiKeys = { [oldProvider]: saved.apiKey, ...saved.apiKeys };
|
|
345
|
+
delete migrated.apiKey;
|
|
346
|
+
}
|
|
347
|
+
return migrated;
|
|
348
|
+
}
|
|
349
|
+
async function writeMigratedSettings(path, settings, original) {
|
|
350
|
+
// A symlinked config (dotfiles setups) is migrated at its target, so the link
|
|
351
|
+
// keeps pointing at the file ZTC reads instead of being replaced by a copy.
|
|
352
|
+
let target = path;
|
|
353
|
+
try {
|
|
354
|
+
target = await realpath(path);
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
if (error.code !== 'ENOENT')
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
// Keep the pre-migration file once, so a user can recover anything the new
|
|
361
|
+
// defaults replaced. Never overwrite an earlier backup.
|
|
362
|
+
try {
|
|
363
|
+
await writeFile(`${target}.pre-defaults-v${SETTINGS_DEFAULTS_VERSION}.bak`, original, {
|
|
364
|
+
encoding: 'utf-8', mode: 0o600, flag: 'wx',
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
if (error.code !== 'EEXIST')
|
|
369
|
+
throw error;
|
|
370
|
+
}
|
|
371
|
+
const temporary = `${target}.${process.pid}.${randomUUID()}.tmp`;
|
|
372
|
+
try {
|
|
373
|
+
await writeFile(temporary, JSON.stringify(settings, null, 2), { encoding: 'utf-8', mode: 0o600, flag: 'wx' });
|
|
374
|
+
await rename(temporary, target);
|
|
375
|
+
}
|
|
376
|
+
finally {
|
|
377
|
+
try {
|
|
378
|
+
await unlink(temporary);
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
if (error.code !== 'ENOENT')
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
241
386
|
/** Config dir, overridable via ZTC_CONFIG_DIR (test isolation + custom locations).
|
|
242
387
|
* Resolved at call time so a test can point it at a temp dir without import-order
|
|
243
388
|
* games, and so it never writes the real ~/.ztc. */
|
|
244
389
|
function configDir() {
|
|
245
|
-
return
|
|
390
|
+
return profileConfigDir();
|
|
246
391
|
}
|
|
247
392
|
function configFile() {
|
|
248
393
|
return join(configDir(), 'config.json');
|
|
@@ -253,6 +398,7 @@ export function shouldRefreshSuperuserStatus(env = process.env) {
|
|
|
253
398
|
}
|
|
254
399
|
export class ConfigStore {
|
|
255
400
|
config = build_default_config();
|
|
401
|
+
profileEmulation = builtinProfileEmulation(activeProfile());
|
|
256
402
|
// The legacy slot also carries runtime defaults. Only a configured key may
|
|
257
403
|
// be migrated into apiKeys or written to disk.
|
|
258
404
|
legacyApiKeyFromEnvironment = true;
|
|
@@ -375,6 +521,7 @@ export class ConfigStore {
|
|
|
375
521
|
async load(force = false) {
|
|
376
522
|
if (this.loaded && !force)
|
|
377
523
|
return this.config;
|
|
524
|
+
this.profileEmulation = builtinProfileEmulation(activeProfile());
|
|
378
525
|
this.routerCredentialsError = null;
|
|
379
526
|
try {
|
|
380
527
|
this.routerCredentials = await loadRouterCredentials();
|
|
@@ -386,46 +533,62 @@ export class ConfigStore {
|
|
|
386
533
|
this.routerCredentialsError = error instanceof Error ? error
|
|
387
534
|
: new Error('Invalid ZTC router credentials. Run ztc auth login.');
|
|
388
535
|
}
|
|
536
|
+
let saved;
|
|
537
|
+
let savedText = '';
|
|
389
538
|
try {
|
|
390
539
|
const data = await readFile(configFile(), 'utf-8');
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
// a saved key must not inherit that other provider's credential.
|
|
396
|
-
if (saved.provider && saved.apiKey === undefined) {
|
|
397
|
-
this.config.apiKey = getDefaultApiKeyForProvider(saved.provider);
|
|
398
|
-
}
|
|
399
|
-
if (saved.provider && saved.model === undefined) {
|
|
400
|
-
this.config.model = getDefaultModelForProvider(saved.provider);
|
|
401
|
-
}
|
|
402
|
-
if (this.config.model === 'claude-opus-4-5-20250514' || this.config.model === 'claude-opus-4-20250514') {
|
|
403
|
-
this.config.model = 'claude-opus-5';
|
|
540
|
+
savedText = data;
|
|
541
|
+
const parsed = JSON.parse(data);
|
|
542
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
543
|
+
throw new Error('config.json must contain an object');
|
|
404
544
|
}
|
|
405
|
-
|
|
406
|
-
this.config.spinnerVerbs = DEFAULT_SPINNER_VERBS;
|
|
407
|
-
}
|
|
408
|
-
if (!this.config.toolPermissions) {
|
|
409
|
-
this.config.toolPermissions = { ...DEFAULT_TOOL_PERMISSIONS };
|
|
410
|
-
}
|
|
411
|
-
this.config.permissionProfiles = {
|
|
412
|
-
...DEFAULT_PERMISSION_PROFILES,
|
|
413
|
-
...(this.config.permissionProfiles || {}),
|
|
414
|
-
};
|
|
415
|
-
this.config.permissionProfile = this.config.permissionProfile || 'trusted';
|
|
416
|
-
this.config.sandbox = { ...DEFAULT_SANDBOX, ...(this.config.sandbox || {}) };
|
|
417
|
-
this.config.intents = { ...DEFAULT_INTENTS, ...(this.config.intents || {}) };
|
|
418
|
-
this.config.contextProgressBar = { ...DEFAULT_CONTEXT_PROGRESS_BAR, ...(this.config.contextProgressBar || {}) };
|
|
419
|
-
this.config.messageDisplay = { ...DEFAULT_MESSAGE_DISPLAY, ...(this.config.messageDisplay || {}) };
|
|
420
|
-
this.config.compaction = resolveCompactionSettings(this.config);
|
|
421
|
-
this.applyEnvOverrides();
|
|
545
|
+
saved = parsed;
|
|
422
546
|
}
|
|
423
547
|
catch {
|
|
424
|
-
// File doesn't exist or is invalid, use defaults
|
|
548
|
+
// File doesn't exist or is invalid, use defaults.
|
|
425
549
|
this.config = build_default_config();
|
|
426
550
|
this.legacyApiKeyFromEnvironment = true;
|
|
427
551
|
this.applyEnvOverrides();
|
|
552
|
+
this.loaded = true;
|
|
553
|
+
return this.config;
|
|
554
|
+
}
|
|
555
|
+
if (typeof saved.settingsDefaultsVersion !== 'number'
|
|
556
|
+
|| saved.settingsDefaultsVersion < SETTINGS_DEFAULTS_VERSION) {
|
|
557
|
+
const migrated = migrateSettingsDefaults(saved);
|
|
558
|
+
// A failed write must not make an unmigrated profile appear migrated.
|
|
559
|
+
await writeMigratedSettings(configFile(), migrated, savedText);
|
|
560
|
+
saved = migrated;
|
|
561
|
+
}
|
|
562
|
+
this.config = { ...build_default_config(), ...saved };
|
|
563
|
+
this.legacyApiKeyFromEnvironment = saved.apiKey === undefined;
|
|
564
|
+
// Defaults were built for the preset provider. A saved provider without
|
|
565
|
+
// a saved key must not inherit that other provider's credential.
|
|
566
|
+
if (saved.provider && saved.apiKey === undefined) {
|
|
567
|
+
this.config.apiKey = getDefaultApiKeyForProvider(saved.provider);
|
|
428
568
|
}
|
|
569
|
+
if (saved.provider && saved.model === undefined) {
|
|
570
|
+
this.config.model = getDefaultModelForProvider(saved.provider);
|
|
571
|
+
}
|
|
572
|
+
if (this.config.model === 'claude-opus-4-5-20250514' || this.config.model === 'claude-opus-4-20250514') {
|
|
573
|
+
this.config.model = 'claude-opus-5';
|
|
574
|
+
}
|
|
575
|
+
if (!this.config.spinnerVerbs) {
|
|
576
|
+
this.config.spinnerVerbs = DEFAULT_SPINNER_VERBS;
|
|
577
|
+
}
|
|
578
|
+
if (!this.config.toolPermissions) {
|
|
579
|
+
this.config.toolPermissions = { ...DEFAULT_TOOL_PERMISSIONS };
|
|
580
|
+
}
|
|
581
|
+
this.config.permissionProfiles = {
|
|
582
|
+
...DEFAULT_PERMISSION_PROFILES,
|
|
583
|
+
...(this.config.permissionProfiles || {}),
|
|
584
|
+
};
|
|
585
|
+
this.config.permissionProfile = this.config.permissionProfile || 'trusted';
|
|
586
|
+
this.config.sandbox = { ...DEFAULT_SANDBOX, ...(this.config.sandbox || {}) };
|
|
587
|
+
this.config.intents = { ...DEFAULT_INTENTS, ...(this.config.intents || {}) };
|
|
588
|
+
this.config.contextProgressBar = { ...DEFAULT_CONTEXT_PROGRESS_BAR, ...(this.config.contextProgressBar || {}) };
|
|
589
|
+
this.config.messageDisplay = { ...DEFAULT_MESSAGE_DISPLAY, ...(this.config.messageDisplay || {}) };
|
|
590
|
+
this.config.compaction = resolveCompactionSettings(this.config);
|
|
591
|
+
this.applyEnvOverrides();
|
|
429
592
|
this.loaded = true;
|
|
430
593
|
return this.config;
|
|
431
594
|
}
|
|
@@ -436,6 +599,64 @@ export class ConfigStore {
|
|
|
436
599
|
this.sessionOverrides = sessionOverrides;
|
|
437
600
|
this.configScope = scope;
|
|
438
601
|
}
|
|
602
|
+
/** Load a profile in this process; restore the old view if runtime activation fails. */
|
|
603
|
+
async switchProfile(name, activate) {
|
|
604
|
+
const next = validateProfileName(name);
|
|
605
|
+
if (next === activeProfile())
|
|
606
|
+
return;
|
|
607
|
+
const sameConfigDirectory = profileConfigDir(next) === profileConfigDir();
|
|
608
|
+
const target = join(profileConfigDir(next), 'config.json');
|
|
609
|
+
try {
|
|
610
|
+
const saved = JSON.parse(await readFile(target, 'utf-8'));
|
|
611
|
+
if (!saved || typeof saved !== 'object' || Array.isArray(saved)) {
|
|
612
|
+
throw new Error('config.json must contain an object');
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
catch (error) {
|
|
616
|
+
if (error.code !== 'ENOENT') {
|
|
617
|
+
throw new Error(`Cannot load config profile ${next}: ${error.message}`);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
const previousEnv = process.env.ZTC_PROFILE;
|
|
621
|
+
const previous = {
|
|
622
|
+
config: this.config,
|
|
623
|
+
profileEmulation: this.profileEmulation,
|
|
624
|
+
loaded: this.loaded,
|
|
625
|
+
legacyApiKeyFromEnvironment: this.legacyApiKeyFromEnvironment,
|
|
626
|
+
routerCredentials: this.routerCredentials,
|
|
627
|
+
routerCredentialsError: this.routerCredentialsError,
|
|
628
|
+
routerFallbackWarning: this.routerFallbackWarning,
|
|
629
|
+
sessionOverrides: this.sessionOverrides,
|
|
630
|
+
cliOverrides: this.cliOverrides,
|
|
631
|
+
runtimeActivePerProvider: this.runtimeActivePerProvider,
|
|
632
|
+
configScope: this.configScope,
|
|
633
|
+
};
|
|
634
|
+
process.env.ZTC_PROFILE = next;
|
|
635
|
+
if (sameConfigDirectory) {
|
|
636
|
+
// Preset switches keep the current settings layer, except an explicit
|
|
637
|
+
// emulation override that would conceal the selected preset.
|
|
638
|
+
const { emulationId: _emulationId, ...remainingOverrides } = this.sessionOverrides;
|
|
639
|
+
this.sessionOverrides = remainingOverrides;
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
this.sessionOverrides = {};
|
|
643
|
+
this.cliOverrides = {};
|
|
644
|
+
this.runtimeActivePerProvider = {};
|
|
645
|
+
this.configScope = 'global';
|
|
646
|
+
}
|
|
647
|
+
try {
|
|
648
|
+
await this.load(true);
|
|
649
|
+
await activate();
|
|
650
|
+
}
|
|
651
|
+
catch (error) {
|
|
652
|
+
if (previousEnv === undefined)
|
|
653
|
+
delete process.env.ZTC_PROFILE;
|
|
654
|
+
else
|
|
655
|
+
process.env.ZTC_PROFILE = previousEnv;
|
|
656
|
+
Object.assign(this, previous);
|
|
657
|
+
throw error;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
439
660
|
async save() {
|
|
440
661
|
if (this.configScope === 'session') {
|
|
441
662
|
return;
|
|
@@ -464,6 +685,23 @@ export class ConfigStore {
|
|
|
464
685
|
runtimeDiag('error', `Failed to save config: ${err?.message ?? err}`);
|
|
465
686
|
}
|
|
466
687
|
}
|
|
688
|
+
/** Clear the active profile's saved settings and reset this process's settings view. */
|
|
689
|
+
async factoryReset() {
|
|
690
|
+
for (const path of [configFile(), join(configDir(), 'router-auth.json')]) {
|
|
691
|
+
try {
|
|
692
|
+
await unlink(path);
|
|
693
|
+
}
|
|
694
|
+
catch (error) {
|
|
695
|
+
if (error.code !== 'ENOENT')
|
|
696
|
+
throw error;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
this.sessionOverrides = {};
|
|
700
|
+
this.cliOverrides = {};
|
|
701
|
+
this.runtimeActivePerProvider = {};
|
|
702
|
+
this.configScope = 'global';
|
|
703
|
+
await this.load(true);
|
|
704
|
+
}
|
|
467
705
|
get() {
|
|
468
706
|
return this.composeConfig(true);
|
|
469
707
|
}
|
|
@@ -478,6 +716,7 @@ export class ConfigStore {
|
|
|
478
716
|
composeConfig(includeEffortOverride) {
|
|
479
717
|
return {
|
|
480
718
|
...this.config,
|
|
719
|
+
...(this.profileEmulation ? { emulationId: this.profileEmulation } : {}),
|
|
481
720
|
...this.sessionOverrides,
|
|
482
721
|
...(includeEffortOverride ? this.cliOverrides : this.cliOverridesWithoutEffort()),
|
|
483
722
|
// Explicit process effort outranks saved scoped defaults without changing them.
|
|
@@ -515,7 +754,7 @@ export class ConfigStore {
|
|
|
515
754
|
}
|
|
516
755
|
}
|
|
517
756
|
set(key, value) {
|
|
518
|
-
if (this.configScope === 'session') {
|
|
757
|
+
if (this.configScope === 'session' || (key === 'emulationId' && this.profileEmulation)) {
|
|
519
758
|
this.sessionOverrides[key] = value;
|
|
520
759
|
return;
|
|
521
760
|
}
|
|
@@ -606,10 +845,10 @@ export class ConfigStore {
|
|
|
606
845
|
}
|
|
607
846
|
}
|
|
608
847
|
getEmulationId() {
|
|
609
|
-
return this.
|
|
848
|
+
return this.get().emulationId;
|
|
610
849
|
}
|
|
611
850
|
setEmulationId(id) {
|
|
612
|
-
this.
|
|
851
|
+
this.set('emulationId', id);
|
|
613
852
|
}
|
|
614
853
|
getMaskedApiKey() {
|
|
615
854
|
const key = this.getApiKey();
|
|
@@ -780,6 +1019,10 @@ export class ConfigStore {
|
|
|
780
1019
|
getCodexEffort(provider, model) {
|
|
781
1020
|
return resolveCodexEffort(this.get(), provider ?? this.getProvider(), model ?? this.getModel());
|
|
782
1021
|
}
|
|
1022
|
+
/** Saved effort the active (or given) model rejects; undefined when it applies. */
|
|
1023
|
+
getUnsupportedCodexEffort(provider, model) {
|
|
1024
|
+
return unsupportedCodexEffort(this.get(), provider ?? this.getProvider(), model ?? this.getModel());
|
|
1025
|
+
}
|
|
783
1026
|
/** Effort chosen by a startup `--effort` flag for this process (= none when unset). */
|
|
784
1027
|
getCodexEffortOverride() {
|
|
785
1028
|
return this.cliOverrides.codexEffort;
|
package/dist/headless.js
CHANGED
|
@@ -668,11 +668,18 @@ export class HeadlessApp {
|
|
|
668
668
|
inputTokens: event.usage.inputTokens,
|
|
669
669
|
outputTokens: event.usage.outputTokens,
|
|
670
670
|
totalTokens: event.usage.totalTokens,
|
|
671
|
+
...(event.usage.cachedInputTokens === undefined ? {} : { cachedInputTokens: event.usage.cachedInputTokens }),
|
|
671
672
|
estimated: event.usage.estimated === true,
|
|
672
673
|
},
|
|
673
674
|
...(typeof event.contextTokens === 'number'
|
|
674
675
|
? { contextTokens: event.contextTokens }
|
|
675
676
|
: {}),
|
|
677
|
+
...(typeof event.cacheInputTokens === 'number'
|
|
678
|
+
? { cacheInputTokens: event.cacheInputTokens }
|
|
679
|
+
: {}),
|
|
680
|
+
...(typeof event.contextCachedInputTokens === 'number'
|
|
681
|
+
? { contextCachedInputTokens: event.contextCachedInputTokens }
|
|
682
|
+
: {}),
|
|
676
683
|
...(typeof event.contextWindow === 'number'
|
|
677
684
|
? { contextWindow: event.contextWindow }
|
|
678
685
|
: {}),
|
|
@@ -989,6 +996,13 @@ export class HeadlessApp {
|
|
|
989
996
|
* Initialize the app
|
|
990
997
|
*/
|
|
991
998
|
async init() {
|
|
999
|
+
// Load the persisted profile before reading any credential signal.
|
|
1000
|
+
// `runHeadless` is a public entry point, so it cannot assume its caller ran
|
|
1001
|
+
// the CLI loader (headless_cli.ts does; direct library callers may not).
|
|
1002
|
+
// Without this the gate below reads unloaded defaults and rejects a profile
|
|
1003
|
+
// whose real credential (for example a Codex subscription auth.json) is on
|
|
1004
|
+
// disk. `load()` is idempotent, so the CLI path is unaffected.
|
|
1005
|
+
await configStore.load();
|
|
992
1006
|
// Require either direct provider key or platform auth token.
|
|
993
1007
|
if (!configStore.hasApiKey() && !configStore.isAuthenticated()) {
|
|
994
1008
|
this.output({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const PANE_PROVIDERS: readonly ["anthropic", "claude_code", "openai", "codex_app_server", "codex_legacy", "gemini", "deepseek", "modal", "kimi", "kimi_code"];
|
|
1
|
+
export declare const PANE_PROVIDERS: readonly ["anthropic", "claude_code", "openai", "codex_app_server", "codex_legacy", "gemini", "deepseek", "fireworks", "modal", "kimi", "kimi_code"];
|
|
2
2
|
export type PaneProvider = typeof PANE_PROVIDERS[number];
|
|
3
3
|
export interface CatalogModel {
|
|
4
4
|
id: string;
|