praxis-agent 0.43.1 → 0.44.0
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 +24 -10
- package/dist/application/top-level-agent-manager.d.ts +7 -0
- package/dist/application/top-level-agent-manager.js +21 -3
- package/dist/cli/controls.js +4 -0
- package/dist/cli/protocol.d.ts +5 -0
- package/dist/cli/protocol.js +45 -0
- package/dist/cli/provider-auth-command.d.ts +32 -0
- package/dist/cli/provider-auth-command.js +249 -0
- package/dist/cli/workspace-trust-prompt.js +2 -2
- package/dist/cli-runtime.d.ts +34 -1
- package/dist/cli-runtime.js +587 -184
- package/dist/maintenance/doctor.js +100 -22
- package/dist/persistence/provider-credential-vault.d.ts +72 -0
- package/dist/persistence/provider-credential-vault.js +498 -0
- package/dist/providers/codex-oauth.d.ts +104 -0
- package/dist/providers/codex-oauth.js +770 -0
- package/dist/providers/codex-subscription.d.ts +33 -0
- package/dist/providers/codex-subscription.js +849 -0
- package/dist/providers/provider-auth.d.ts +71 -0
- package/dist/providers/provider-auth.js +149 -0
- package/dist/providers/provider-registry.d.ts +52 -0
- package/dist/providers/provider-registry.js +187 -0
- package/dist/providers/provider-settings.d.ts +38 -0
- package/dist/providers/provider-settings.js +334 -0
- package/dist/security/workspace-trust.d.ts +4 -3
- package/dist/security/workspace-trust.js +35 -1
- package/package.json +6 -3
package/dist/cli-runtime.js
CHANGED
|
@@ -33,7 +33,7 @@ import { createClaudeModelAutoClassifier, defaultClaudeAutoModeConfig, loadClaud
|
|
|
33
33
|
import { ClaudeExtensionPermissionResolver, ClaudeExtensionToolRegistry, } from './extensions/claude-extension-tools.js';
|
|
34
34
|
import { ClaudeExtensionCatalog } from './extensions/claude-extensions.js';
|
|
35
35
|
import { ClaudeHookRunner } from './hooks/claude-hooks.js';
|
|
36
|
-
import { allowedWorkspaceHookSettings, allowedWorkspaceMcpResources, assessWorkspaceTrust, persistWorkspaceTrust, workspaceTrustDecisionKey, workspaceTrustInventory, } from './security/workspace-trust.js';
|
|
36
|
+
import { allowedWorkspaceHookSettings, allowedWorkspaceMcpResources, assessWorkspaceTrust, hasWorkspaceProviderSelection, persistWorkspaceTrust, workspaceTrustDecisionKey, workspaceTrustInventory, } from './security/workspace-trust.js';
|
|
37
37
|
import { createWorkspaceTrustDecisionCache, promptWorkspaceTrust, safeWorkspaceTrustDisplayField, } from './cli/workspace-trust-prompt.js';
|
|
38
38
|
import { ClaudeSessionEnvironment } from './hooks/claude-session-environment.js';
|
|
39
39
|
import { ClaudeMcpToolRegistry, } from './mcp/claude-mcp-tools.js';
|
|
@@ -42,14 +42,17 @@ import { authenticateMcpServer, ClaudeMcpOAuthStore, mcpOAuthServerIdentity, rea
|
|
|
42
42
|
import { servePraxisMcpStdio } from './mcp/praxis-mcp-server.js';
|
|
43
43
|
const VERIFIED_CLAUDE_SCHEMA_VERSION = 'native';
|
|
44
44
|
const isClaudeSessionId = isSessionId;
|
|
45
|
+
function truthyEnvironmentValue(value) {
|
|
46
|
+
return /^(?:1|true|yes|on)$/iu.test((value ?? '').trim());
|
|
47
|
+
}
|
|
45
48
|
import { writeFileAtomically } from './platform/atomic-write.js';
|
|
46
49
|
import { redactSensitiveText, sensitiveEnvironmentValues, } from './platform/sensitive-data.js';
|
|
47
|
-
import { AnthropicCompatibleProvider } from './providers/anthropic-compatible.js';
|
|
48
|
-
import { createAnthropicPromptCachePolicyResolver } from './providers/anthropic-prompt-cache.js';
|
|
49
50
|
import { FallbackModelProvider } from './providers/fallback-provider.js';
|
|
50
|
-
import {
|
|
51
|
-
import { OpenAICompatibleProvider } from './providers/openai-compatible.js';
|
|
51
|
+
import { ProviderCredentialVault } from './persistence/provider-credential-vault.js';
|
|
52
52
|
import { parseContextEnvironment, parseProviderEnvironment, } from './providers/environment.js';
|
|
53
|
+
import { ProviderAuthenticationError, resolveProviderCredential, } from './providers/provider-auth.js';
|
|
54
|
+
import { resolveProviderRegistry } from './providers/provider-registry.js';
|
|
55
|
+
import { ProviderSettingsError, resolveProviderTarget, } from './providers/provider-settings.js';
|
|
53
56
|
import { ModelPricingRegistry, usageCostUsd } from './core/usage.js';
|
|
54
57
|
import { LocalToolRegistry } from './tools/local-tools.js';
|
|
55
58
|
import { ClaudeLspToolManager } from './tools/claude-lsp-tool.js';
|
|
@@ -63,6 +66,7 @@ import { launchTmuxWorktree } from './platform/tmux-worktree.js';
|
|
|
63
66
|
import { claudeSandboxRuntime } from './sandbox/claude-sandbox-runtime.js';
|
|
64
67
|
import { nativeSandboxTempDirectory, loadClaudeSandboxSettings, } from './sandbox/claude-sandbox-settings.js';
|
|
65
68
|
import { createErrorResult, createSuccessResult, isHeadlessCostCommand, matchHeadlessColorCommand, parseCliInvocation, readStreamJsonMessages, StreamJsonOutput, } from './cli/protocol.js';
|
|
69
|
+
import { executeProviderAuthCommand } from './cli/provider-auth-command.js';
|
|
66
70
|
import { describeClaudePlugin, initClaudePlugin, installClaudePlugin, loadClaudePlugins, readPluginRegistry, setClaudePluginEnabled, uninstallClaudePlugin, updateClaudePlugin, validateClaudePlugin, } from './plugins/claude-plugin-runtime.js';
|
|
67
71
|
import { addClaudeMarketplace, disableAllNativePlugins, installClaudeMarketplacePlugin, listClaudeMarketplaceAvailablePlugins, listNativePluginRecords, readClaudeKnownMarketplaces, removeClaudeMarketplace, setNativePluginEnabled, saveClaudePluginConfig, uninstallNativePlugin, updateClaudeMarketplace, updateNativePlugin, validateClaudeMarketplace, } from './plugins/claude-plugin-marketplace.js';
|
|
68
72
|
import { executeClaudePluginEvalCommand, PLUGIN_EVAL_HELP, } from './plugins/claude-plugin-eval.js';
|
|
@@ -97,60 +101,6 @@ function fileResourceHeaders(environment, providerEnvironment, credential) {
|
|
|
97
101
|
: {}),
|
|
98
102
|
};
|
|
99
103
|
}
|
|
100
|
-
function createProviderForModel({ apiKey, environment, provider, context, controls, explicitThinkingControls, }) {
|
|
101
|
-
const resolvePromptCachePolicy = createAnthropicPromptCachePolicyResolver(environment);
|
|
102
|
-
return (selectedModel) => {
|
|
103
|
-
const providerOptions = {
|
|
104
|
-
apiKey,
|
|
105
|
-
model: selectedModel,
|
|
106
|
-
baseUrl: provider.baseUrl,
|
|
107
|
-
...('contextWindowTokens' in context
|
|
108
|
-
? { contextWindowTokens: context.contextWindowTokens }
|
|
109
|
-
: {}),
|
|
110
|
-
};
|
|
111
|
-
const concrete = provider.provider === 'anthropic'
|
|
112
|
-
? new AnthropicCompatibleProvider({
|
|
113
|
-
...providerOptions,
|
|
114
|
-
promptCaching: resolvePromptCachePolicy({
|
|
115
|
-
baseUrl: provider.baseUrl,
|
|
116
|
-
model: selectedModel,
|
|
117
|
-
}),
|
|
118
|
-
thinking: {
|
|
119
|
-
mode: controls.thinking ?? 'enabled',
|
|
120
|
-
...(controls.maxThinkingTokens === undefined
|
|
121
|
-
? {}
|
|
122
|
-
: { maxTokens: controls.maxThinkingTokens }),
|
|
123
|
-
},
|
|
124
|
-
...('maxOutputTokens' in provider
|
|
125
|
-
? { maxOutputTokens: provider.maxOutputTokens }
|
|
126
|
-
: {}),
|
|
127
|
-
...('anthropicVersion' in provider
|
|
128
|
-
? { anthropicVersion: provider.anthropicVersion }
|
|
129
|
-
: {}),
|
|
130
|
-
...('webSearch' in provider
|
|
131
|
-
? { webSearch: provider.webSearch }
|
|
132
|
-
: {}),
|
|
133
|
-
})
|
|
134
|
-
: new OpenAICompatibleProvider({
|
|
135
|
-
...providerOptions,
|
|
136
|
-
...(explicitThinkingControls.thinking === undefined &&
|
|
137
|
-
explicitThinkingControls.maxThinkingTokens === undefined
|
|
138
|
-
? {}
|
|
139
|
-
: {
|
|
140
|
-
thinking: {
|
|
141
|
-
mode: controls.thinking ?? 'enabled',
|
|
142
|
-
...(controls.maxThinkingTokens === undefined
|
|
143
|
-
? {}
|
|
144
|
-
: { maxTokens: controls.maxThinkingTokens }),
|
|
145
|
-
},
|
|
146
|
-
}),
|
|
147
|
-
});
|
|
148
|
-
return new DeadlineModelProvider({
|
|
149
|
-
provider: concrete,
|
|
150
|
-
deadlineMs: provider.deadlineMs,
|
|
151
|
-
});
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
104
|
const HELP = `Praxis — local-first general agent
|
|
155
105
|
|
|
156
106
|
Usage:
|
|
@@ -174,6 +124,7 @@ Usage:
|
|
|
174
124
|
praxis auto-mode <config|defaults|critique>
|
|
175
125
|
praxis plugin|plugins <details|list|install|uninstall|enable|disable|update|init|prune|tag|validate|marketplace> ...
|
|
176
126
|
praxis doctor [--json]
|
|
127
|
+
praxis auth <status|set-key|login|logout> ...
|
|
177
128
|
praxis import [options] [source]
|
|
178
129
|
praxis install [--force] [stable|latest|version]
|
|
179
130
|
praxis update|upgrade
|
|
@@ -189,6 +140,8 @@ Options:
|
|
|
189
140
|
--resume-session-at <message-id> Resume at an active conversation message
|
|
190
141
|
--session-id <uuid> Use an explicit ID for a new session
|
|
191
142
|
-n, --name <name> Set session display name
|
|
143
|
+
--provider <id> Select provider for this session
|
|
144
|
+
--provider-profile <id> Select provider profile for this session
|
|
192
145
|
--model <model> Select model for this session
|
|
193
146
|
--effort <level> low, medium, high, xhigh, or max
|
|
194
147
|
--environment <environment_id> Unsupported: Praxis runs sessions locally
|
|
@@ -225,7 +178,7 @@ Options:
|
|
|
225
178
|
--setting-sources <sources> user, project, local, or an empty list
|
|
226
179
|
--safe-mode Disable shared customizations
|
|
227
180
|
--bare Use only explicitly supplied context
|
|
228
|
-
--trust-project Trust current workspace
|
|
181
|
+
--trust-project Trust current workspace-controlled resources/configuration
|
|
229
182
|
--system-prompt <prompt> Set system prompt
|
|
230
183
|
--append-system-prompt <prompt> Append system prompt
|
|
231
184
|
--exclude-dynamic-system-prompt-sections
|
|
@@ -259,13 +212,65 @@ Options:
|
|
|
259
212
|
--tmux[=classic] Launch worktree in an iTerm2 native pane when available; classic forces tmux
|
|
260
213
|
|
|
261
214
|
Provider environment:
|
|
262
|
-
PRAXIS_PROVIDER
|
|
215
|
+
PRAXIS_PROVIDER=<provider-id>, PRAXIS_API_KEY, PRAXIS_MODEL
|
|
216
|
+
PRAXIS_PROVIDER_PROFILE
|
|
263
217
|
PRAXIS_BASE_URL, PRAXIS_MAX_OUTPUT_TOKENS, PRAXIS_ANTHROPIC_VERSION
|
|
264
218
|
PRAXIS_ANTHROPIC_WEB_SEARCH=true|false
|
|
265
219
|
PRAXIS_ANTHROPIC_PROMPT_CACHING=true|false
|
|
266
220
|
PRAXIS_ANTHROPIC_PROMPT_CACHE_TTL=5m|1h
|
|
267
221
|
PRAXIS_CONTEXT_WINDOW_TOKENS, PRAXIS_CONTEXT_RESERVE_TOKENS
|
|
268
222
|
`;
|
|
223
|
+
const AUTH_HELP = `Usage: praxis auth <command>
|
|
224
|
+
|
|
225
|
+
Commands:
|
|
226
|
+
status [provider] [--profile <profile>] [--json]
|
|
227
|
+
set-key <provider> [--profile <profile>] [--json]
|
|
228
|
+
login openai-codex [--profile <profile>] [--device] [--no-browser] [--json]
|
|
229
|
+
logout <provider> [--profile <profile>] [--json]
|
|
230
|
+
|
|
231
|
+
Options:
|
|
232
|
+
-h, --help Display help for command
|
|
233
|
+
`;
|
|
234
|
+
const AUTH_ACTION_HELP = {
|
|
235
|
+
status: `Usage: praxis auth status [provider] [--profile <profile>] [--json]
|
|
236
|
+
|
|
237
|
+
List credential metadata without exposing secrets.
|
|
238
|
+
|
|
239
|
+
Options:
|
|
240
|
+
--profile <profile> Filter by provider profile
|
|
241
|
+
--json Output machine-readable metadata
|
|
242
|
+
-h, --help Display help for command
|
|
243
|
+
`,
|
|
244
|
+
'set-key': `Usage: praxis auth set-key <provider> [--profile <profile>] [--json]
|
|
245
|
+
|
|
246
|
+
Read an API key securely from the terminal or stdin and store it in the native Vault.
|
|
247
|
+
|
|
248
|
+
Options:
|
|
249
|
+
--profile <profile> Select provider profile (default: default)
|
|
250
|
+
--json Output a machine-readable confirmation
|
|
251
|
+
-h, --help Display help for command
|
|
252
|
+
`,
|
|
253
|
+
login: `Usage: praxis auth login openai-codex [--profile <profile>] [--device] [--no-browser] [--json]
|
|
254
|
+
|
|
255
|
+
Authorize an experimental Codex subscription profile with OpenAI OAuth.
|
|
256
|
+
|
|
257
|
+
Options:
|
|
258
|
+
--profile <profile> Select Codex profile (default: default)
|
|
259
|
+
--device Use the device authorization flow
|
|
260
|
+
--no-browser Print instructions without opening a browser
|
|
261
|
+
--json Keep stdout machine-readable
|
|
262
|
+
-h, --help Display help for command
|
|
263
|
+
`,
|
|
264
|
+
logout: `Usage: praxis auth logout <provider> [--profile <profile>] [--json]
|
|
265
|
+
|
|
266
|
+
Delete exactly one native Vault credential.
|
|
267
|
+
|
|
268
|
+
Options:
|
|
269
|
+
--profile <profile> Select provider profile (default: default)
|
|
270
|
+
--json Output a machine-readable confirmation
|
|
271
|
+
-h, --help Display help for command
|
|
272
|
+
`,
|
|
273
|
+
};
|
|
269
274
|
const AGENTS_HELP = `Usage: praxis agents [options]
|
|
270
275
|
|
|
271
276
|
Manage background agents
|
|
@@ -296,6 +301,10 @@ Options:
|
|
|
296
301
|
dispatched sessions (repeatable)
|
|
297
302
|
--model <model> Default model for sessions dispatched
|
|
298
303
|
from agent view
|
|
304
|
+
--provider <id> Default provider for sessions dispatched
|
|
305
|
+
from agent view
|
|
306
|
+
--provider-profile <id> Default provider profile for dispatched
|
|
307
|
+
sessions
|
|
299
308
|
--permission-mode <mode> Default permission mode for sessions
|
|
300
309
|
dispatched from agent view
|
|
301
310
|
--plugin-dir <path> Load plugins from specified directory
|
|
@@ -759,11 +768,119 @@ Commands:
|
|
|
759
768
|
attach <team-id> [--json]
|
|
760
769
|
`;
|
|
761
770
|
export { parseContextEnvironment, parseProviderEnvironment };
|
|
771
|
+
export async function readConsoleSecret(prompt, signal, input = process.stdin, output = process.stderr) {
|
|
772
|
+
const limit = 64 * 1024;
|
|
773
|
+
if (signal?.aborted)
|
|
774
|
+
throw new Error('Credential input cancelled');
|
|
775
|
+
if (!input.isTTY || typeof input.setRawMode !== 'function') {
|
|
776
|
+
const chunks = [];
|
|
777
|
+
let size = 0;
|
|
778
|
+
for await (const chunk of input) {
|
|
779
|
+
if (signal?.aborted)
|
|
780
|
+
throw new Error('Credential input cancelled');
|
|
781
|
+
const bytes = typeof chunk === 'string' ? Buffer.from(chunk) : Buffer.from(chunk);
|
|
782
|
+
size += bytes.byteLength;
|
|
783
|
+
if (size > limit)
|
|
784
|
+
throw new Error('Credential exceeds the 64 KiB limit');
|
|
785
|
+
chunks.push(bytes);
|
|
786
|
+
}
|
|
787
|
+
if (signal?.aborted)
|
|
788
|
+
throw new Error('Credential input cancelled');
|
|
789
|
+
return Buffer.concat(chunks, size).toString('utf8');
|
|
790
|
+
}
|
|
791
|
+
output.write(prompt);
|
|
792
|
+
const wasRaw = input.isRaw;
|
|
793
|
+
const wasPaused = input.isPaused();
|
|
794
|
+
const value = [];
|
|
795
|
+
return await new Promise((resolveSecret, reject) => {
|
|
796
|
+
let settled = false;
|
|
797
|
+
let rawModeChanged = false;
|
|
798
|
+
let listenerAttached = false;
|
|
799
|
+
const restore = () => {
|
|
800
|
+
let failure;
|
|
801
|
+
if (listenerAttached) {
|
|
802
|
+
input.removeListener('data', onData);
|
|
803
|
+
listenerAttached = false;
|
|
804
|
+
}
|
|
805
|
+
signal?.removeEventListener('abort', onAbort);
|
|
806
|
+
if (rawModeChanged) {
|
|
807
|
+
try {
|
|
808
|
+
input.setRawMode?.(Boolean(wasRaw));
|
|
809
|
+
}
|
|
810
|
+
catch (error) {
|
|
811
|
+
failure = error instanceof Error ? error : new Error(String(error));
|
|
812
|
+
}
|
|
813
|
+
rawModeChanged = false;
|
|
814
|
+
}
|
|
815
|
+
if (wasPaused) {
|
|
816
|
+
try {
|
|
817
|
+
input.pause();
|
|
818
|
+
}
|
|
819
|
+
catch (error) {
|
|
820
|
+
failure ??= error instanceof Error ? error : new Error(String(error));
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return failure;
|
|
824
|
+
};
|
|
825
|
+
const finish = (error) => {
|
|
826
|
+
if (settled)
|
|
827
|
+
return;
|
|
828
|
+
settled = true;
|
|
829
|
+
const restorationError = restore();
|
|
830
|
+
let outputError;
|
|
831
|
+
try {
|
|
832
|
+
output.write('\n');
|
|
833
|
+
}
|
|
834
|
+
catch (writeError) {
|
|
835
|
+
outputError =
|
|
836
|
+
writeError instanceof Error
|
|
837
|
+
? writeError
|
|
838
|
+
: new Error(String(writeError));
|
|
839
|
+
}
|
|
840
|
+
const failure = error ?? restorationError ?? outputError;
|
|
841
|
+
if (failure)
|
|
842
|
+
reject(failure);
|
|
843
|
+
else
|
|
844
|
+
resolveSecret(Buffer.from(value).toString('utf8'));
|
|
845
|
+
};
|
|
846
|
+
const onAbort = () => finish(new Error('Credential input cancelled'));
|
|
847
|
+
const onData = (chunk) => {
|
|
848
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
849
|
+
for (const byte of bytes) {
|
|
850
|
+
if (byte === 3)
|
|
851
|
+
return finish(new Error('Credential input cancelled'));
|
|
852
|
+
if (byte === 13 || byte === 10)
|
|
853
|
+
return finish();
|
|
854
|
+
if (byte === 8 || byte === 127) {
|
|
855
|
+
value.pop();
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
858
|
+
value.push(byte);
|
|
859
|
+
if (value.length > limit)
|
|
860
|
+
return finish(new Error('Credential exceeds the 64 KiB limit'));
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
864
|
+
if (signal?.aborted)
|
|
865
|
+
return onAbort();
|
|
866
|
+
try {
|
|
867
|
+
input.setRawMode?.(true);
|
|
868
|
+
rawModeChanged = true;
|
|
869
|
+
input.resume();
|
|
870
|
+
input.on('data', onData);
|
|
871
|
+
listenerAttached = true;
|
|
872
|
+
}
|
|
873
|
+
catch (error) {
|
|
874
|
+
finish(error instanceof Error ? error : new Error(String(error)));
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
}
|
|
762
878
|
const consoleIO = {
|
|
763
879
|
stdout: (message) => process.stdout.write(message),
|
|
764
880
|
stderr: (message) => process.stderr.write(message),
|
|
765
881
|
isTTY: Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
766
882
|
readStdinLines: () => process.stdin,
|
|
883
|
+
readSecret: readConsoleSecret,
|
|
767
884
|
};
|
|
768
885
|
function warningEventSink(io) {
|
|
769
886
|
return (event) => {
|
|
@@ -801,6 +918,34 @@ export function resolveInteractiveRuntimeSettingsLocation(dataPlane, environment
|
|
|
801
918
|
statePath: join(configRoot, 'state.json'),
|
|
802
919
|
};
|
|
803
920
|
}
|
|
921
|
+
async function assessCurrentWorkspaceProviderSelection(options) {
|
|
922
|
+
const shared = await loadNativeSharedResources({
|
|
923
|
+
root: options.configRoot,
|
|
924
|
+
cwd: options.cwd,
|
|
925
|
+
environment: options.environment,
|
|
926
|
+
includeProjectMemory: false,
|
|
927
|
+
});
|
|
928
|
+
const plugins = await loadClaudePlugins({
|
|
929
|
+
configRoot: options.configRoot,
|
|
930
|
+
cwd: options.cwd,
|
|
931
|
+
pluginDirectories: options.pluginDirectories ?? [],
|
|
932
|
+
pluginUrls: options.pluginUrls ?? [],
|
|
933
|
+
strictPluginDirectories: (options.pluginDirectories?.length ?? 0) +
|
|
934
|
+
(options.pluginUrls?.length ?? 0) >
|
|
935
|
+
0,
|
|
936
|
+
loadInstalled: true,
|
|
937
|
+
readOnlyExecutables: true,
|
|
938
|
+
environment: options.environment,
|
|
939
|
+
});
|
|
940
|
+
const settings = [...shared.settings, ...plugins.settings];
|
|
941
|
+
if (!hasWorkspaceProviderSelection(settings))
|
|
942
|
+
return undefined;
|
|
943
|
+
return assessWorkspaceTrust(await workspaceTrustInventory({
|
|
944
|
+
cwd: options.cwd,
|
|
945
|
+
settings,
|
|
946
|
+
mcp: options.strictMcpConfig ? [] : [...shared.mcp, ...plugins.mcp],
|
|
947
|
+
}), options.statePath);
|
|
948
|
+
}
|
|
804
949
|
export function resolveUnknownCostSidecarPath(dataPlane, configRoot) {
|
|
805
950
|
assertNativeDataPlane(dataPlane);
|
|
806
951
|
return join(configRoot, 'state', 'unknown-cost-sidecar.json');
|
|
@@ -820,7 +965,7 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
|
|
|
820
965
|
});
|
|
821
966
|
const claudeStatePath = join(configRoot, 'state.json');
|
|
822
967
|
const simpleMode = controls.bare ||
|
|
823
|
-
|
|
968
|
+
truthyEnvironmentValue(runtimeEnvironment.CLAUDE_CODE_SIMPLE);
|
|
824
969
|
const runtimeSettings = controls.safeMode || simpleMode
|
|
825
970
|
? undefined
|
|
826
971
|
: await loadRuntimeSettings({ configRoot, statePath: claudeStatePath });
|
|
@@ -881,48 +1026,145 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
|
|
|
881
1026
|
let provider;
|
|
882
1027
|
let providerForModel;
|
|
883
1028
|
let providerForMainModel;
|
|
1029
|
+
let providerBillingMode;
|
|
884
1030
|
const context = parseContextEnvironment(runtimeEnvironment);
|
|
885
1031
|
const apiKey = runtimeEnvironment.PRAXIS_API_KEY;
|
|
886
|
-
|
|
887
|
-
const providerEnvironment =
|
|
1032
|
+
let model = resolveRuntimeModel(interactiveModel ?? controls.model, runtimeEnvironment, runtimeSettings);
|
|
1033
|
+
const providerEnvironment = cli.fileResources.length > 0
|
|
888
1034
|
? parseProviderEnvironment(runtimeEnvironment)
|
|
889
|
-
:
|
|
890
|
-
? parseProviderEnvironment(runtimeEnvironment)
|
|
891
|
-
: undefined;
|
|
1035
|
+
: undefined;
|
|
892
1036
|
const fileCredential = runtimeEnvironment.PRAXIS_FILES_BEARER_TOKEN ??
|
|
893
1037
|
runtimeEnvironment.PRAXIS_FILES_API_KEY ??
|
|
894
1038
|
apiKey;
|
|
895
1039
|
if (cli.fileResources.length > 0 && !fileCredential) {
|
|
896
1040
|
throw new Error('--file requires PRAXIS_FILES_BEARER_TOKEN, PRAXIS_FILES_API_KEY, or PRAXIS_API_KEY');
|
|
897
1041
|
}
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1042
|
+
const warnedWorkspaceFingerprints = new Set();
|
|
1043
|
+
let trustProjectRequestAvailable = cli.trustProject;
|
|
1044
|
+
const authorizeWorkspaceResources = async (automaticSettings, automaticMcp, runtimeCwd, precomputedAssessment) => {
|
|
1045
|
+
if (cli.safeMode || simpleMode)
|
|
1046
|
+
return true;
|
|
1047
|
+
const trustRequested = trustProjectRequestAvailable;
|
|
1048
|
+
trustProjectRequestAvailable = false;
|
|
1049
|
+
const assessment = precomputedAssessment ??
|
|
1050
|
+
(await assessWorkspaceTrust(await workspaceTrustInventory({
|
|
1051
|
+
cwd: runtimeCwd,
|
|
1052
|
+
settings: automaticSettings,
|
|
1053
|
+
mcp: automaticMcp,
|
|
1054
|
+
}), claudeStatePath));
|
|
1055
|
+
if (assessment.status !== 'untrusted')
|
|
1056
|
+
return true;
|
|
1057
|
+
let approved = trustRequested;
|
|
1058
|
+
if (!approved && approveWorkspaceTrust) {
|
|
1059
|
+
try {
|
|
1060
|
+
approved = await approveWorkspaceTrust(assessment);
|
|
1061
|
+
}
|
|
1062
|
+
catch (error) {
|
|
1063
|
+
if (!(error instanceof Error &&
|
|
1064
|
+
(error.name === 'AbortError' || error.name === 'CancellationError'))) {
|
|
1065
|
+
throw error;
|
|
1066
|
+
}
|
|
1067
|
+
approved = false;
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
if (approved) {
|
|
1071
|
+
await persistWorkspaceTrust(assessment, claudeStatePath);
|
|
1072
|
+
return true;
|
|
904
1073
|
}
|
|
905
|
-
|
|
906
|
-
|
|
1074
|
+
const warningKey = workspaceTrustDecisionKey(assessment);
|
|
1075
|
+
if (!warnedWorkspaceFingerprints.has(warningKey)) {
|
|
1076
|
+
warnedWorkspaceFingerprints.add(warningKey);
|
|
1077
|
+
runtimeEventSink({
|
|
1078
|
+
type: 'warning',
|
|
1079
|
+
message: `Workspace-controlled resources/configuration blocked for ${safeWorkspaceTrustDisplayField(assessment.canonicalPath)}; restart to review them interactively, or rerun with --trust-project to approve the current fingerprint.`,
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
return false;
|
|
1083
|
+
};
|
|
1084
|
+
let workspaceProviderSettingsTrusted = false;
|
|
1085
|
+
if (!hooksOnly && !cli.safeMode && !simpleMode) {
|
|
1086
|
+
const assessment = await assessCurrentWorkspaceProviderSelection({
|
|
1087
|
+
configRoot,
|
|
1088
|
+
statePath: claudeStatePath,
|
|
1089
|
+
cwd,
|
|
907
1090
|
environment: runtimeEnvironment,
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
controls: cli,
|
|
912
|
-
explicitThinkingControls: controls,
|
|
1091
|
+
pluginDirectories: cli.pluginDirectories,
|
|
1092
|
+
pluginUrls: cli.pluginUrls,
|
|
1093
|
+
strictMcpConfig: cli.strictMcpConfig,
|
|
913
1094
|
});
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
:
|
|
924
|
-
|
|
925
|
-
|
|
1095
|
+
if (assessment) {
|
|
1096
|
+
workspaceProviderSettingsTrusted = await authorizeWorkspaceResources([], [], cwd, assessment);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
if (!hooksOnly) {
|
|
1100
|
+
try {
|
|
1101
|
+
const registry = await resolveProviderRegistry({
|
|
1102
|
+
configRoot,
|
|
1103
|
+
cwd,
|
|
1104
|
+
environment: runtimeEnvironment,
|
|
1105
|
+
...((interactiveModel ?? controls.model) === undefined
|
|
1106
|
+
? {}
|
|
1107
|
+
: { model: interactiveModel ?? controls.model }),
|
|
1108
|
+
...(controls.provider === undefined
|
|
1109
|
+
? {}
|
|
1110
|
+
: { provider: controls.provider }),
|
|
1111
|
+
...(controls.providerProfile === undefined
|
|
1112
|
+
? {}
|
|
1113
|
+
: { profile: controls.providerProfile }),
|
|
1114
|
+
includeSettings: !(controls.safeMode || simpleMode),
|
|
1115
|
+
includeProjectSettings: workspaceProviderSettingsTrusted,
|
|
1116
|
+
context,
|
|
1117
|
+
vault: new ProviderCredentialVault({
|
|
1118
|
+
configRoot,
|
|
1119
|
+
environment: runtimeEnvironment,
|
|
1120
|
+
}),
|
|
1121
|
+
anthropicThinking: {
|
|
1122
|
+
mode: cli.thinking ?? 'enabled',
|
|
1123
|
+
...(cli.maxThinkingTokens === undefined
|
|
1124
|
+
? {}
|
|
1125
|
+
: { maxTokens: cli.maxThinkingTokens }),
|
|
1126
|
+
},
|
|
1127
|
+
codexThinking: {
|
|
1128
|
+
mode: cli.thinking ?? 'enabled',
|
|
1129
|
+
...(cli.maxThinkingTokens === undefined
|
|
1130
|
+
? {}
|
|
1131
|
+
: { maxTokens: cli.maxThinkingTokens }),
|
|
1132
|
+
},
|
|
1133
|
+
...(controls.thinking === undefined &&
|
|
1134
|
+
controls.maxThinkingTokens === undefined
|
|
1135
|
+
? {}
|
|
1136
|
+
: {
|
|
1137
|
+
openAiThinking: {
|
|
1138
|
+
mode: cli.thinking ?? 'enabled',
|
|
1139
|
+
...(cli.maxThinkingTokens === undefined
|
|
1140
|
+
? {}
|
|
1141
|
+
: { maxTokens: cli.maxThinkingTokens }),
|
|
1142
|
+
},
|
|
1143
|
+
}),
|
|
1144
|
+
});
|
|
1145
|
+
model = registry.target.modelId;
|
|
1146
|
+
providerBillingMode = registry.target.billingMode;
|
|
1147
|
+
providerForModel = (selectedModel) => registry.create(selectedModel);
|
|
1148
|
+
const createProvider = providerForModel;
|
|
1149
|
+
providerForMainModel = (primaryModel) => {
|
|
1150
|
+
const models = [primaryModel, ...(cli.fallbackModels ?? [])].filter((candidate, index, all) => all.indexOf(candidate) === index);
|
|
1151
|
+
const providers = models.map((candidate) => createProvider(candidate));
|
|
1152
|
+
const selected = providers[0];
|
|
1153
|
+
if (!selected)
|
|
1154
|
+
throw new Error('A primary model is required');
|
|
1155
|
+
return providers.length > 1
|
|
1156
|
+
? new FallbackModelProvider({ providers })
|
|
1157
|
+
: selected;
|
|
1158
|
+
};
|
|
1159
|
+
provider = providerForMainModel(model);
|
|
1160
|
+
}
|
|
1161
|
+
catch (error) {
|
|
1162
|
+
const optionalProviderError = error instanceof ProviderAuthenticationError ||
|
|
1163
|
+
(error instanceof ProviderSettingsError &&
|
|
1164
|
+
error.code === 'model_required');
|
|
1165
|
+
if (requireProvider || !optionalProviderError)
|
|
1166
|
+
throw error;
|
|
1167
|
+
}
|
|
926
1168
|
}
|
|
927
1169
|
const costStateStore = new ClaudeCostStateStore({
|
|
928
1170
|
statePath: claudeStatePath,
|
|
@@ -957,7 +1199,11 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
|
|
|
957
1199
|
...(cli.maxBudgetUsd === undefined
|
|
958
1200
|
? {}
|
|
959
1201
|
: { maxBudgetUsd: cli.maxBudgetUsd }),
|
|
960
|
-
|
|
1202
|
+
...(providerBillingMode === 'subscription'
|
|
1203
|
+
? {}
|
|
1204
|
+
: {
|
|
1205
|
+
pricing: ModelPricingRegistry.fromEnvironment(runtimeEnvironment.PRAXIS_PRICING_JSON),
|
|
1206
|
+
}),
|
|
961
1207
|
collectMetrics: true,
|
|
962
1208
|
...(!experimentalNativeTranscriptWrites && sessionMemoryProviderFactory
|
|
963
1209
|
? { sessionMemoryProviderFactory }
|
|
@@ -987,48 +1233,6 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
|
|
|
987
1233
|
}
|
|
988
1234
|
: {}),
|
|
989
1235
|
};
|
|
990
|
-
const warnedWorkspaceFingerprints = new Set();
|
|
991
|
-
let trustProjectRequestAvailable = cli.trustProject;
|
|
992
|
-
const authorizeWorkspaceExecutables = async (automaticSettings, automaticMcp, runtimeCwd) => {
|
|
993
|
-
if (cli.safeMode || simpleMode)
|
|
994
|
-
return true;
|
|
995
|
-
const trustRequested = trustProjectRequestAvailable;
|
|
996
|
-
trustProjectRequestAvailable = false;
|
|
997
|
-
const inventory = await workspaceTrustInventory({
|
|
998
|
-
cwd: runtimeCwd,
|
|
999
|
-
settings: automaticSettings,
|
|
1000
|
-
mcp: automaticMcp,
|
|
1001
|
-
});
|
|
1002
|
-
const assessment = await assessWorkspaceTrust(inventory, claudeStatePath);
|
|
1003
|
-
if (assessment.status !== 'untrusted')
|
|
1004
|
-
return true;
|
|
1005
|
-
let approved = trustRequested;
|
|
1006
|
-
if (!approved && approveWorkspaceTrust) {
|
|
1007
|
-
try {
|
|
1008
|
-
approved = await approveWorkspaceTrust(assessment);
|
|
1009
|
-
}
|
|
1010
|
-
catch (error) {
|
|
1011
|
-
if (!(error instanceof Error &&
|
|
1012
|
-
(error.name === 'AbortError' || error.name === 'CancellationError'))) {
|
|
1013
|
-
throw error;
|
|
1014
|
-
}
|
|
1015
|
-
approved = false;
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
if (approved) {
|
|
1019
|
-
await persistWorkspaceTrust(assessment, claudeStatePath);
|
|
1020
|
-
return true;
|
|
1021
|
-
}
|
|
1022
|
-
const warningKey = workspaceTrustDecisionKey(assessment);
|
|
1023
|
-
if (!warnedWorkspaceFingerprints.has(warningKey)) {
|
|
1024
|
-
warnedWorkspaceFingerprints.add(warningKey);
|
|
1025
|
-
runtimeEventSink({
|
|
1026
|
-
type: 'warning',
|
|
1027
|
-
message: `Workspace executable resources blocked for ${safeWorkspaceTrustDisplayField(assessment.canonicalPath)}; restart to review them interactively, or rerun with --trust-project to approve the current fingerprint.`,
|
|
1028
|
-
});
|
|
1029
|
-
}
|
|
1030
|
-
return false;
|
|
1031
|
-
};
|
|
1032
1236
|
const hookConfiguration = async () => {
|
|
1033
1237
|
const [sharedResources, pluginResources] = await Promise.all([
|
|
1034
1238
|
cli.safeMode || simpleMode
|
|
@@ -1057,7 +1261,7 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
|
|
|
1057
1261
|
const automaticMcp = cli.strictMcpConfig
|
|
1058
1262
|
? []
|
|
1059
1263
|
: [...sharedResources.mcp, ...pluginResources.mcp];
|
|
1060
|
-
const trusted = await
|
|
1264
|
+
const trusted = await authorizeWorkspaceResources(automaticSettings, automaticMcp, cwd);
|
|
1061
1265
|
return projectTuiHooks([
|
|
1062
1266
|
...allowedWorkspaceHookSettings(automaticSettings, trusted),
|
|
1063
1267
|
...(cli.additionalSettings ? [cli.additionalSettings] : []),
|
|
@@ -1155,7 +1359,7 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
|
|
|
1155
1359
|
const trustMcp = cli.strictMcpConfig
|
|
1156
1360
|
? []
|
|
1157
1361
|
: [...loadedResources.mcp, ...executablePluginResources.mcp];
|
|
1158
|
-
const workspaceExecutablesTrusted = await
|
|
1362
|
+
const workspaceExecutablesTrusted = await authorizeWorkspaceResources(trustSettings, trustMcp, cwd);
|
|
1159
1363
|
const loadedPluginResources = await loadClaudePlugins({
|
|
1160
1364
|
...pluginLoadOptions,
|
|
1161
1365
|
allowWorkspaceMcpb: workspaceExecutablesTrusted,
|
|
@@ -1406,7 +1610,7 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
|
|
|
1406
1610
|
...pluginResources.settings,
|
|
1407
1611
|
];
|
|
1408
1612
|
const refreshedMcp = [...refreshed.mcp, ...pluginResources.mcp];
|
|
1409
|
-
const trusted = await
|
|
1613
|
+
const trusted = await authorizeWorkspaceResources(refreshedSettings, refreshedMcp, workspace.cwd());
|
|
1410
1614
|
return runtimeMcpResources([
|
|
1411
1615
|
...allowedWorkspaceMcpResources(refreshedMcp, trusted),
|
|
1412
1616
|
...cli.mcpResources,
|
|
@@ -1994,30 +2198,31 @@ const createDefaultService = async ({ eventSink, requireProvider, hooksOnly = fa
|
|
|
1994
2198
|
throw error;
|
|
1995
2199
|
}
|
|
1996
2200
|
};
|
|
1997
|
-
const createDefaultAutoModeCritic = async ({ model, dataPlane, configRoot, statePath }) => {
|
|
1998
|
-
const apiKey = process.env.PRAXIS_API_KEY;
|
|
2201
|
+
const createDefaultAutoModeCritic = async ({ model, provider, providerProfile, dataPlane, configRoot, statePath, }) => {
|
|
1999
2202
|
const resolvedDataPlane = dataPlane ?? resolveDataPlane();
|
|
2000
2203
|
const location = resolveInteractiveRuntimeSettingsLocation(resolvedDataPlane);
|
|
2001
2204
|
const resolvedConfigRoot = configRoot ?? location.configRoot;
|
|
2002
|
-
const
|
|
2003
|
-
const runtimeSettings = await loadRuntimeSettings({
|
|
2205
|
+
const assessment = await assessCurrentWorkspaceProviderSelection({
|
|
2004
2206
|
configRoot: resolvedConfigRoot,
|
|
2005
|
-
statePath:
|
|
2207
|
+
statePath: statePath ?? join(resolvedConfigRoot, 'state.json'),
|
|
2208
|
+
cwd: process.cwd(),
|
|
2209
|
+
environment: process.env,
|
|
2006
2210
|
});
|
|
2007
|
-
const
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
}
|
|
2011
|
-
const providerEnvironment = parseProviderEnvironment(process.env);
|
|
2012
|
-
return createProviderForModel({
|
|
2013
|
-
apiKey,
|
|
2211
|
+
const registry = await resolveProviderRegistry({
|
|
2212
|
+
configRoot: resolvedConfigRoot,
|
|
2213
|
+
cwd: process.cwd(),
|
|
2014
2214
|
environment: process.env,
|
|
2015
|
-
|
|
2016
|
-
provider:
|
|
2215
|
+
...(model === undefined ? {} : { model }),
|
|
2216
|
+
...(provider === undefined ? {} : { provider }),
|
|
2217
|
+
...(providerProfile === undefined ? {} : { profile: providerProfile }),
|
|
2218
|
+
includeProjectSettings: assessment?.status === 'trusted',
|
|
2017
2219
|
context: parseContextEnvironment(process.env),
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2220
|
+
vault: new ProviderCredentialVault({
|
|
2221
|
+
configRoot: resolvedConfigRoot,
|
|
2222
|
+
environment: process.env,
|
|
2223
|
+
}),
|
|
2224
|
+
});
|
|
2225
|
+
return registry.create(model ?? registry.target.modelId);
|
|
2021
2226
|
};
|
|
2022
2227
|
const defaultPluginEvalRuntimeFactory = {
|
|
2023
2228
|
create: async (options) => {
|
|
@@ -2108,19 +2313,29 @@ const defaultPluginEvalRuntimeFactory = {
|
|
|
2108
2313
|
const defaultPluginEvalJudge = {
|
|
2109
2314
|
vote: async ({ criteria, focus, baseline, model, signal }) => {
|
|
2110
2315
|
const environment = process.env;
|
|
2111
|
-
const
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
apiKey,
|
|
2316
|
+
const configRoot = resolveDataPlaneRoot({ environment });
|
|
2317
|
+
const assessment = await assessCurrentWorkspaceProviderSelection({
|
|
2318
|
+
configRoot,
|
|
2319
|
+
statePath: join(configRoot, 'state.json'),
|
|
2320
|
+
cwd: process.cwd(),
|
|
2117
2321
|
environment,
|
|
2118
|
-
|
|
2119
|
-
|
|
2322
|
+
});
|
|
2323
|
+
const registry = await resolveProviderRegistry({
|
|
2324
|
+
configRoot,
|
|
2325
|
+
cwd: process.cwd(),
|
|
2326
|
+
environment,
|
|
2327
|
+
...(model === undefined ? {} : { model }),
|
|
2328
|
+
includeProjectSettings: assessment?.status === 'trusted',
|
|
2120
2329
|
context: parseContextEnvironment(environment),
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2330
|
+
vault: new ProviderCredentialVault({
|
|
2331
|
+
configRoot,
|
|
2332
|
+
environment,
|
|
2333
|
+
}),
|
|
2334
|
+
});
|
|
2335
|
+
if (registry.target.billingMode === 'subscription') {
|
|
2336
|
+
throw new Error('Plugin eval LLM judges require API-billed cost; subscription cost is unavailable');
|
|
2337
|
+
}
|
|
2338
|
+
const provider = registry.create(model ?? registry.target.modelId);
|
|
2124
2339
|
const prompt = `You are an eval judge. Return only JSON matching {"passed":boolean,"explanation":string}.
|
|
2125
2340
|
|
|
2126
2341
|
Criteria:
|
|
@@ -2170,6 +2385,135 @@ ${focus}${baseline === undefined ? '' : `\n\nBaseline:\n${baseline}`}`;
|
|
|
2170
2385
|
};
|
|
2171
2386
|
},
|
|
2172
2387
|
};
|
|
2388
|
+
async function resolveDetachedWorkerProviderEnvironment(request) {
|
|
2389
|
+
const environment = process.env;
|
|
2390
|
+
const invocation = parseCliInvocation(request.argv);
|
|
2391
|
+
const configRoot = resolveDataPlaneRoot({ environment });
|
|
2392
|
+
const statePath = join(configRoot, 'state.json');
|
|
2393
|
+
const safe = invocation.safeMode ||
|
|
2394
|
+
invocation.bare ||
|
|
2395
|
+
truthyEnvironmentValue(environment.CLAUDE_CODE_SIMPLE);
|
|
2396
|
+
let includeProjectSettings = false;
|
|
2397
|
+
if (!safe) {
|
|
2398
|
+
const assessment = await assessCurrentWorkspaceProviderSelection({
|
|
2399
|
+
configRoot,
|
|
2400
|
+
statePath,
|
|
2401
|
+
cwd: request.cwd,
|
|
2402
|
+
environment,
|
|
2403
|
+
pluginDirectories: invocation.pluginDirectories,
|
|
2404
|
+
pluginUrls: invocation.pluginUrls,
|
|
2405
|
+
strictMcpConfig: invocation.strictMcpConfig,
|
|
2406
|
+
});
|
|
2407
|
+
if (assessment?.status === 'trusted')
|
|
2408
|
+
includeProjectSettings = true;
|
|
2409
|
+
else if (assessment?.status === 'untrusted' && invocation.trustProject) {
|
|
2410
|
+
await persistWorkspaceTrust(assessment, statePath);
|
|
2411
|
+
includeProjectSettings = true;
|
|
2412
|
+
}
|
|
2413
|
+
}
|
|
2414
|
+
const target = await resolveProviderTarget({
|
|
2415
|
+
configRoot,
|
|
2416
|
+
cwd: request.cwd,
|
|
2417
|
+
environment,
|
|
2418
|
+
...(invocation.model === undefined ? {} : { model: invocation.model }),
|
|
2419
|
+
...(invocation.provider === undefined
|
|
2420
|
+
? {}
|
|
2421
|
+
: { provider: invocation.provider }),
|
|
2422
|
+
...(invocation.providerProfile === undefined
|
|
2423
|
+
? {}
|
|
2424
|
+
: { profile: invocation.providerProfile }),
|
|
2425
|
+
includeSettings: !safe,
|
|
2426
|
+
includeProjectSettings,
|
|
2427
|
+
});
|
|
2428
|
+
const credential = await resolveProviderCredential({
|
|
2429
|
+
target,
|
|
2430
|
+
environment,
|
|
2431
|
+
vault: new ProviderCredentialVault({ configRoot, environment }),
|
|
2432
|
+
});
|
|
2433
|
+
return credential.type === 'api-key'
|
|
2434
|
+
? { PRAXIS_API_KEY: credential.secret }
|
|
2435
|
+
: {};
|
|
2436
|
+
}
|
|
2437
|
+
export async function resolveInteractiveProviderStartup(options) {
|
|
2438
|
+
const environment = options.environment ?? process.env;
|
|
2439
|
+
const disabled = options.controls.safeMode ||
|
|
2440
|
+
options.controls.bare ||
|
|
2441
|
+
truthyEnvironmentValue(environment.CLAUDE_CODE_SIMPLE);
|
|
2442
|
+
if (disabled) {
|
|
2443
|
+
let effectiveModel;
|
|
2444
|
+
try {
|
|
2445
|
+
effectiveModel = (await resolveProviderTarget({
|
|
2446
|
+
configRoot: options.configRoot,
|
|
2447
|
+
cwd: options.cwd,
|
|
2448
|
+
environment,
|
|
2449
|
+
...(options.controls.model === undefined
|
|
2450
|
+
? {}
|
|
2451
|
+
: { model: options.controls.model }),
|
|
2452
|
+
...(options.controls.provider === undefined
|
|
2453
|
+
? {}
|
|
2454
|
+
: { provider: options.controls.provider }),
|
|
2455
|
+
...(options.controls.providerProfile === undefined
|
|
2456
|
+
? {}
|
|
2457
|
+
: { profile: options.controls.providerProfile }),
|
|
2458
|
+
includeSettings: false,
|
|
2459
|
+
})).modelId;
|
|
2460
|
+
}
|
|
2461
|
+
catch (error) {
|
|
2462
|
+
if (!(error instanceof ProviderSettingsError &&
|
|
2463
|
+
error.code === 'model_required'))
|
|
2464
|
+
throw error;
|
|
2465
|
+
}
|
|
2466
|
+
return {
|
|
2467
|
+
effectiveModel,
|
|
2468
|
+
trustProjectRequestAvailable: options.controls.trustProject,
|
|
2469
|
+
};
|
|
2470
|
+
}
|
|
2471
|
+
const assessment = await assessCurrentWorkspaceProviderSelection({
|
|
2472
|
+
configRoot: options.configRoot,
|
|
2473
|
+
statePath: options.statePath,
|
|
2474
|
+
cwd: options.cwd,
|
|
2475
|
+
environment,
|
|
2476
|
+
pluginDirectories: options.controls.pluginDirectories,
|
|
2477
|
+
pluginUrls: options.controls.pluginUrls,
|
|
2478
|
+
strictMcpConfig: options.controls.strictMcpConfig,
|
|
2479
|
+
});
|
|
2480
|
+
let includeProjectSettings = false;
|
|
2481
|
+
let trustProjectRequestAvailable = options.controls.trustProject;
|
|
2482
|
+
if (assessment) {
|
|
2483
|
+
trustProjectRequestAvailable = false;
|
|
2484
|
+
if (assessment.status === 'trusted')
|
|
2485
|
+
includeProjectSettings = true;
|
|
2486
|
+
else if (options.controls.trustProject ||
|
|
2487
|
+
(await options.approveWorkspaceTrust?.(assessment))) {
|
|
2488
|
+
await persistWorkspaceTrust(assessment, options.statePath);
|
|
2489
|
+
includeProjectSettings = true;
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
let effectiveModel;
|
|
2493
|
+
try {
|
|
2494
|
+
effectiveModel = (await resolveProviderTarget({
|
|
2495
|
+
configRoot: options.configRoot,
|
|
2496
|
+
cwd: options.cwd,
|
|
2497
|
+
environment,
|
|
2498
|
+
...(options.controls.model === undefined
|
|
2499
|
+
? {}
|
|
2500
|
+
: { model: options.controls.model }),
|
|
2501
|
+
...(options.controls.provider === undefined
|
|
2502
|
+
? {}
|
|
2503
|
+
: { provider: options.controls.provider }),
|
|
2504
|
+
...(options.controls.providerProfile === undefined
|
|
2505
|
+
? {}
|
|
2506
|
+
: { profile: options.controls.providerProfile }),
|
|
2507
|
+
includeSettings: true,
|
|
2508
|
+
includeProjectSettings,
|
|
2509
|
+
})).modelId;
|
|
2510
|
+
}
|
|
2511
|
+
catch (error) {
|
|
2512
|
+
if (!(error instanceof ProviderSettingsError && error.code === 'model_required'))
|
|
2513
|
+
throw error;
|
|
2514
|
+
}
|
|
2515
|
+
return { effectiveModel, trustProjectRequestAvailable };
|
|
2516
|
+
}
|
|
2173
2517
|
export function createDefaultDependencies(entrypoint = fileURLToPath(import.meta.url)) {
|
|
2174
2518
|
const dependencies = {
|
|
2175
2519
|
createService: createDefaultService,
|
|
@@ -2193,13 +2537,15 @@ export function createDefaultDependencies(entrypoint = fileURLToPath(import.meta
|
|
|
2193
2537
|
const initialAdditionalDirectories = interactiveControls.addDirectories.map((directory) => realpathSync(resolve(process.cwd(), directory)));
|
|
2194
2538
|
const interactiveDataPlane = interactiveControls.dataPlane ?? resolveDataPlane();
|
|
2195
2539
|
const { configRoot: interactiveConfigRoot, statePath: interactiveStatePath, } = resolveInteractiveRuntimeSettingsLocation(interactiveDataPlane);
|
|
2196
|
-
const
|
|
2197
|
-
|
|
2198
|
-
:
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2540
|
+
const startup = await resolveInteractiveProviderStartup({
|
|
2541
|
+
controls: interactiveControls,
|
|
2542
|
+
configRoot: interactiveConfigRoot,
|
|
2543
|
+
statePath: interactiveStatePath,
|
|
2544
|
+
cwd: process.cwd(),
|
|
2545
|
+
approveWorkspaceTrust: cachedWorkspaceTrustDecision,
|
|
2546
|
+
});
|
|
2547
|
+
const effectiveModel = startup.effectiveModel;
|
|
2548
|
+
const trustProjectRequestAvailable = startup.trustProjectRequestAvailable;
|
|
2203
2549
|
const resumePath = typeof resume?.sessionSelector === 'string' &&
|
|
2204
2550
|
isResumePathSelector(resume.sessionSelector)
|
|
2205
2551
|
? resume.sessionSelector
|
|
@@ -2218,7 +2564,7 @@ export function createDefaultDependencies(entrypoint = fileURLToPath(import.meta
|
|
|
2218
2564
|
...(agent === undefined ? {} : { agent }),
|
|
2219
2565
|
controls: {
|
|
2220
2566
|
...interactiveControls,
|
|
2221
|
-
trustProject: workspaceTrustPreflightOpen &&
|
|
2567
|
+
trustProject: workspaceTrustPreflightOpen && trustProjectRequestAvailable,
|
|
2222
2568
|
addDirectories: options.additionalDirectories ??
|
|
2223
2569
|
interactiveControls.addDirectories,
|
|
2224
2570
|
},
|
|
@@ -2237,7 +2583,7 @@ export function createDefaultDependencies(entrypoint = fileURLToPath(import.meta
|
|
|
2237
2583
|
statePath: interactiveStatePath,
|
|
2238
2584
|
factory: {
|
|
2239
2585
|
createService: createInteractiveService,
|
|
2240
|
-
scheduledPrompts: Boolean(
|
|
2586
|
+
scheduledPrompts: Boolean(effectiveModel),
|
|
2241
2587
|
},
|
|
2242
2588
|
...(signal ? { signal } : {}),
|
|
2243
2589
|
...(initialPrompt === undefined ? {} : { initialPrompt }),
|
|
@@ -2311,6 +2657,7 @@ export function createDefaultDependencies(entrypoint = fileURLToPath(import.meta
|
|
|
2311
2657
|
dataPlane,
|
|
2312
2658
|
cwd: process.cwd(),
|
|
2313
2659
|
cliPath: entrypoint,
|
|
2660
|
+
resolveProviderEnvironment: resolveDetachedWorkerProviderEnvironment,
|
|
2314
2661
|
version: VERSION,
|
|
2315
2662
|
}),
|
|
2316
2663
|
launchTmux: launchTmuxWorktree,
|
|
@@ -2689,6 +3036,12 @@ async function executeAutoModeCommand(args, invocation, io, dependencies, signal
|
|
|
2689
3036
|
io.stdout('Analyzing your auto mode rules…\n\n');
|
|
2690
3037
|
const provider = await (dependencies.createAutoModeCritic ?? createDefaultAutoModeCritic)({
|
|
2691
3038
|
...(invocation.model === undefined ? {} : { model: invocation.model }),
|
|
3039
|
+
...(invocation.provider === undefined
|
|
3040
|
+
? {}
|
|
3041
|
+
: { provider: invocation.provider }),
|
|
3042
|
+
...(invocation.providerProfile === undefined
|
|
3043
|
+
? {}
|
|
3044
|
+
: { providerProfile: invocation.providerProfile }),
|
|
2692
3045
|
dataPlane,
|
|
2693
3046
|
configRoot,
|
|
2694
3047
|
statePath,
|
|
@@ -2760,21 +3113,27 @@ async function executeDoctorCommand(args, invocation, io) {
|
|
|
2760
3113
|
configRoot,
|
|
2761
3114
|
statePath: claudeStatePath,
|
|
2762
3115
|
});
|
|
2763
|
-
const effectiveModel = resolveRuntimeModel(invocation.model, process.env, runtimeSettings);
|
|
2764
3116
|
const report = await runDoctor({
|
|
2765
3117
|
version: VERSION,
|
|
2766
3118
|
executablePath: fileURLToPath(import.meta.url),
|
|
2767
3119
|
nodeExecutablePath: process.execPath,
|
|
2768
3120
|
nodeVersion: process.version,
|
|
2769
3121
|
configRoot,
|
|
3122
|
+
claudeStatePath,
|
|
2770
3123
|
cwd: process.cwd(),
|
|
2771
|
-
environment:
|
|
2772
|
-
|
|
2773
|
-
:
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
3124
|
+
environment: {
|
|
3125
|
+
...process.env,
|
|
3126
|
+
PRAXIS_DATA_PLANE: dataPlane,
|
|
3127
|
+
...(invocation.provider === undefined
|
|
3128
|
+
? {}
|
|
3129
|
+
: { PRAXIS_PROVIDER: invocation.provider }),
|
|
3130
|
+
...(invocation.providerProfile === undefined
|
|
3131
|
+
? {}
|
|
3132
|
+
: { PRAXIS_PROVIDER_PROFILE: invocation.providerProfile }),
|
|
3133
|
+
...(invocation.model === undefined
|
|
3134
|
+
? {}
|
|
3135
|
+
: { PRAXIS_MODEL: invocation.model }),
|
|
3136
|
+
},
|
|
2778
3137
|
autoUpdateChannel: runtimeSettings.autoUpdatesChannel,
|
|
2779
3138
|
...(process.argv[1] === undefined
|
|
2780
3139
|
? {}
|
|
@@ -3939,8 +4298,16 @@ function backgroundWorkerArgv(argv) {
|
|
|
3939
4298
|
}
|
|
3940
4299
|
return filtered;
|
|
3941
4300
|
}
|
|
3942
|
-
function agentDashboardWorkerArgv(invocation) {
|
|
4301
|
+
export function agentDashboardWorkerArgv(invocation) {
|
|
3943
4302
|
const argv = [];
|
|
4303
|
+
if (invocation.safeMode)
|
|
4304
|
+
argv.push('--safe-mode');
|
|
4305
|
+
if (invocation.bare)
|
|
4306
|
+
argv.push('--bare');
|
|
4307
|
+
if (invocation.provider !== undefined)
|
|
4308
|
+
argv.push('--provider', invocation.provider);
|
|
4309
|
+
if (invocation.providerProfile !== undefined)
|
|
4310
|
+
argv.push('--provider-profile', invocation.providerProfile);
|
|
3944
4311
|
if (invocation.model !== undefined)
|
|
3945
4312
|
argv.push('--model', invocation.model);
|
|
3946
4313
|
if (invocation.effort !== undefined)
|
|
@@ -3973,6 +4340,8 @@ function agentDashboardWorkerArgv(invocation) {
|
|
|
3973
4340
|
for (const directory of invocation.pluginDirectories) {
|
|
3974
4341
|
argv.push('--plugin-dir', directory);
|
|
3975
4342
|
}
|
|
4343
|
+
for (const url of invocation.pluginUrls)
|
|
4344
|
+
argv.push('--plugin-url', url);
|
|
3976
4345
|
return argv;
|
|
3977
4346
|
}
|
|
3978
4347
|
function backgroundLaunchMessage(id) {
|
|
@@ -3995,6 +4364,8 @@ function assertAgentsOptionAllowlist(argv) {
|
|
|
3995
4364
|
'--effort',
|
|
3996
4365
|
'--mcp-config',
|
|
3997
4366
|
'--model',
|
|
4367
|
+
'--provider',
|
|
4368
|
+
'--provider-profile',
|
|
3998
4369
|
'--permission-mode',
|
|
3999
4370
|
'--plugin-dir',
|
|
4000
4371
|
'--setting-sources',
|
|
@@ -4164,6 +4535,7 @@ function printCommandHelp(argv, io) {
|
|
|
4164
4535
|
'auto-mode',
|
|
4165
4536
|
'project',
|
|
4166
4537
|
'import',
|
|
4538
|
+
'auth',
|
|
4167
4539
|
'team',
|
|
4168
4540
|
].includes(value));
|
|
4169
4541
|
if (commandIndex < 0)
|
|
@@ -4252,6 +4624,19 @@ function printCommandHelp(argv, io) {
|
|
|
4252
4624
|
io.stdout(IMPORT_HELP);
|
|
4253
4625
|
return true;
|
|
4254
4626
|
}
|
|
4627
|
+
if (command === 'auth') {
|
|
4628
|
+
const action = helpActionAt(argv, commandIndex, [
|
|
4629
|
+
'help',
|
|
4630
|
+
...Object.keys(AUTH_ACTION_HELP),
|
|
4631
|
+
]);
|
|
4632
|
+
if (!hasHelpFlag && action?.value !== 'help')
|
|
4633
|
+
return false;
|
|
4634
|
+
const target = action?.value === 'help'
|
|
4635
|
+
? helpActionAt(argv, action.index, Object.keys(AUTH_ACTION_HELP))
|
|
4636
|
+
: action;
|
|
4637
|
+
io.stdout(AUTH_ACTION_HELP[target?.value ?? ''] ?? AUTH_HELP);
|
|
4638
|
+
return true;
|
|
4639
|
+
}
|
|
4255
4640
|
if (command === 'team') {
|
|
4256
4641
|
if (!hasHelpFlag)
|
|
4257
4642
|
return false;
|
|
@@ -4649,7 +5034,25 @@ async function execute(argv, io, dependencies, signal) {
|
|
|
4649
5034
|
'upgrade',
|
|
4650
5035
|
'project',
|
|
4651
5036
|
'import',
|
|
5037
|
+
'auth',
|
|
4652
5038
|
].includes(command ?? '');
|
|
5039
|
+
if (command === 'auth') {
|
|
5040
|
+
return (dependencies.executeProviderAuthCommand ?? executeProviderAuthCommand)(args, {
|
|
5041
|
+
io,
|
|
5042
|
+
configRoot: resolveDataPlaneRoot({ environment: process.env }),
|
|
5043
|
+
environment: process.env,
|
|
5044
|
+
...(signal === undefined ? {} : { signal }),
|
|
5045
|
+
...(invocation.authProfile === undefined
|
|
5046
|
+
? {}
|
|
5047
|
+
: { profile: invocation.authProfile }),
|
|
5048
|
+
...(invocation.providerProfile === undefined
|
|
5049
|
+
? {}
|
|
5050
|
+
: { providerProfile: invocation.providerProfile }),
|
|
5051
|
+
noBrowser: invocation.mcpNoBrowser,
|
|
5052
|
+
json: invocation.legacyJson,
|
|
5053
|
+
device: invocation.authDevice,
|
|
5054
|
+
});
|
|
5055
|
+
}
|
|
4653
5056
|
if (args[0] === 'agents')
|
|
4654
5057
|
assertAgentsOptionAllowlist(argv);
|
|
4655
5058
|
const interactiveResume = invocation.resumeSelector !== undefined &&
|