praxis-agent 0.43.1 → 0.45.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 CHANGED
@@ -27,7 +27,9 @@ sessions, configuration, or compatibility directories.
27
27
  - macOS or Linux
28
28
  - Node.js 24 or newer
29
29
  - [`ripgrep`](https://github.com/BurntSushi/ripgrep) (`rg`) for the Grep tool
30
- - an API key and model ID for an Anthropic or OpenAI-compatible provider
30
+ - an API key and model ID for an Anthropic or OpenAI-compatible provider (the
31
+ stable setup), or the explicitly enabled experimental ChatGPT-backed Codex
32
+ subscription integration
31
33
 
32
34
  Praxis does not use Claude subscription authentication. Claude-shaped message,
33
35
  tool, and CLI protocol forms remain supported where they are part of the
@@ -61,6 +63,15 @@ cd /path/to/project
61
63
  praxis
62
64
  ```
63
65
 
66
+ Praxis also has an experimental `openai-codex` provider for ChatGPT-backed
67
+ Codex subscriptions. It is separate from OpenAI API-key access, requires
68
+ `experimental.codexSubscription: true`, and stores OAuth credentials in the
69
+ native Vault. Start with `praxis auth login openai-codex`; see [Getting
70
+ Started](docs/GETTING_STARTED.md) for the browser/device flow and limitations.
71
+ This uses an undocumented third-party subscription/backend contract and may
72
+ change; it is not Claude subscription authentication. Subscription runs retain
73
+ token usage but do not provide API-dollar cost or enforce USD budgets.
74
+
64
75
  For Anthropic Messages:
65
76
 
66
77
  ```sh
@@ -90,7 +101,8 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
90
101
  ## What Praxis provides
91
102
 
92
103
  - **Local agent runtime** — C+ Quiet Operator responsive TUI with a linear
93
- `you>`/`praxis>` conversation, compact stable tool rows, responsive density,
104
+ `❯` user / `⏺` assistant conversation, `✻` thinking activity, and `!` shell
105
+ composer grammar, compact stable tool rows, responsive density,
94
106
  terminal-native background, and a minimal composer/status row. Interactive
95
107
  surfaces share the same presentation across terminals, with English
96
108
  permission/configuration choices and a taught `❯` / Up/Down / Enter / Esc
@@ -128,8 +140,9 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
128
140
  provider-free `/terminal-setup` diagnostics and repeatable Shift+Enter setup
129
141
  for supported local terminals,
130
142
  `Ctrl+V` text/image clipboard paste, `Ctrl+Z` shell suspension and `fg`
131
- recovery, permission-gated `!` shell turns, navigable current/per-turn Git
132
- diff views, semantic plan/question decision panels with complete
143
+ recovery, permission-gated provider-free `!` shell turns that persist shell
144
+ input/output for later ordinary prompts without creating an assistant turn,
145
+ navigable current/per-turn Git diff views, semantic plan/question decision panels with complete
133
146
  screen-reader actions, semantic screen projection across selectable surfaces,
134
147
  deterministic resize-aware URL/form elicitation rendering, and measured
135
148
  context budgets; print mode,
@@ -154,11 +167,11 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
154
167
  isolation, explicit ask/deny precedence, sandbox-only auto-allow,
155
168
  write-allowlist/deny-within-allow enforcement, per-command overrides and
156
169
  exclusions, violation reporting, and bare-repository control-file cleanup,
157
- safe-property Skill auto-allow, interactive
158
- workspace-directory add/remove controls, path confinement, credential
159
- redaction, sanitized child processes, and exact-fingerprint workspace trust
160
- that blocks automatically discovered project/local hooks and MCP servers
161
- until the canonical workspace configuration is explicitly accepted.
170
+ safe-property Skill auto-allow, interactive workspace-directory add/remove
171
+ controls, path confinement, credential redaction, sanitized child processes,
172
+ and exact-fingerprint workspace trust that blocks automatically discovered
173
+ project/local provider selection, hooks, and MCP until the canonical
174
+ workspace configuration is accepted.
162
175
  - **Durable local work** — resumable sessions, full-history forks, file
163
176
  checkpoints, tasks, foreground/background subagents, top-level agents, and
164
177
  Claude-compatible main-thread agent definitions with native prompt, model,
@@ -187,9 +200,10 @@ troubleshooting. Run `praxis --help` for the authoritative command surface.
187
200
  plugins, and append-only `praxis.transcript` JSONL sessions under `~/.praxis`,
188
201
  with bounded MCP connection, discovery, and tool operations plus safe
189
202
  disconnect recovery that never replays an already-dispatched call.
190
- - **Provider-neutral models** — native Anthropic Messages and OpenAI-compatible
191
- streaming adapters with explicit capability checks, metering controls, and
192
- a bounded absolute deadline for every provider attempt.
203
+ - **Provider-neutral models** — native Provider Registry/Vault routing, API
204
+ adapters, an experimental Codex OAuth adapter, explicit capability checks,
205
+ per-attempt bounded deadlines, and token-only/no-API-dollar accounting for
206
+ subscription runs.
193
207
  - **Transactional self-update** — `praxis update` verifies the package before
194
208
  installing it, rejects concurrent updates, and can roll back after an
195
209
  interruption or crash.
@@ -198,6 +212,22 @@ Detailed feature status and executable evidence live in the
198
212
  [parity matrix](https://github.com/Forest-Isle/Praxis/blob/main/docs/PARITY_MATRIX.md),
199
213
  not in this entry-point README.
200
214
 
215
+ ## Native data plane
216
+
217
+ Praxis defaults to an independent local native data plane:
218
+
219
+ ```text
220
+ Praxis ─── ~/.praxis (or PRAXIS_HOME)
221
+ ```
222
+
223
+ All sessions, memory, tasks, scheduled tasks, resources, and private state live
224
+ under `~/.praxis` (or `PRAXIS_HOME`). The authoritative transcript is
225
+ append-only `praxis.transcript` v1 JSONL; legacy Claude transcripts, indexes,
226
+ sidechains, and migration/recovery paths have been removed. `CLAUDE_CONFIG_DIR`
227
+ does not participate in native runs, and legacy directories are neither read
228
+ nor written. Claude-shaped messages and tool fields describe protocol shape
229
+ only; they do not change Praxis data ownership.
230
+
201
231
  ## Documentation
202
232
 
203
233
  | Need | Document |
@@ -216,7 +246,7 @@ not in this entry-point README.
216
246
 
217
247
  Praxis targets one local OS user working across multiple repositories and
218
248
  sessions. It is CLI-only and provider-capability-aware. Organization, tenant,
219
- RBAC, subscription authentication and billing, enterprise gateway,
249
+ RBAC, billing, enterprise gateway,
220
250
  IDE/Desktop/mobile clients, Remote Control, Claude Desktop import, and hosted
221
251
  review-product surfaces are permanent non-goals.
222
252
 
@@ -251,6 +281,8 @@ implemented profile checks, not qualification of the full native package.
251
281
  injected regression protection, plus Quiet Operator input echo `<50 ms` and
252
282
  normal/low-capability full-frame p95 budgets of `<16.7/<33 ms`.
253
283
  `npm run check` also enforces the corresponding source dependency direction.
284
+ `npm run test:coverage` measures all production code under `src/**` with V8 and
285
+ enforces global floors of 79% statements, 70% branches, 85% functions, and 81% lines.
254
286
  `npm run test:core-completion` runs the 56-story #402 audit and reports
255
287
  implemented, qualified, blocked, deferred, and out-of-scope states separately;
256
288
  it never treats missing live prerequisites as a pass.
@@ -2592,7 +2592,9 @@ export class ClaudeSessionService {
2592
2592
  });
2593
2593
  }
2594
2594
  const initialPricing = this.options.pricing?.resolve(provider.model ?? 'praxis/provider');
2595
- if (this.options.maxBudgetUsd !== undefined && !initialPricing) {
2595
+ if (shellCommand === undefined &&
2596
+ this.options.maxBudgetUsd !== undefined &&
2597
+ !initialPricing) {
2596
2598
  throw new Error(`Cannot enforce --max-budget-usd: no pricing is configured for model ${provider.model ?? 'praxis/provider'}`);
2597
2599
  }
2598
2600
  const shellInputUuid = shellCommand === undefined ? null : randomUUID();
@@ -3699,7 +3701,9 @@ export class ClaudeSessionService {
3699
3701
  };
3700
3702
  },
3701
3703
  });
3702
- await contextEngine.prepare(contextTransitionPort(pendingUserMessages), signal);
3704
+ if (shellCommand === undefined) {
3705
+ await contextEngine.prepare(contextTransitionPort(pendingUserMessages), signal);
3706
+ }
3703
3707
  for (const [index, message] of expandedMessages.entries()) {
3704
3708
  if (shellCommand !== undefined)
3705
3709
  break;
@@ -3783,6 +3787,9 @@ export class ClaudeSessionService {
3783
3787
  }
3784
3788
  let shellUsage = { inputTokens: 0, outputTokens: 0 };
3785
3789
  let shellModelUsage;
3790
+ let shellDurationApiMs;
3791
+ let shellDurationApiWithoutRetriesMs;
3792
+ let shellToolDurationMs;
3786
3793
  if (shellCommand !== undefined) {
3787
3794
  const call = {
3788
3795
  id: `shell_${randomUUID().replaceAll('-', '')}`,
@@ -3822,6 +3829,10 @@ export class ClaudeSessionService {
3822
3829
  throw error;
3823
3830
  }
3824
3831
  shellUsage = shellResult.usage ?? shellUsage;
3832
+ shellDurationApiMs = shellResult.durationApiMs;
3833
+ shellDurationApiWithoutRetriesMs =
3834
+ shellResult.durationApiWithoutRetriesMs;
3835
+ shellToolDurationMs = shellResult.durationToolMs;
3825
3836
  if (!shellResult.isError) {
3826
3837
  shellModelUsage = shellResult.modelUsage;
3827
3838
  foregroundLineChanges.add(shellResult);
@@ -3872,7 +3883,74 @@ export class ClaudeSessionService {
3872
3883
  isError: shellResult.isError,
3873
3884
  });
3874
3885
  }
3875
- if (budget) {
3886
+ if (shellCommand !== undefined) {
3887
+ const tracker = this.sessionCostTrackers.get(sessionId);
3888
+ if (!tracker) {
3889
+ throw new Error(`Session cost tracker is not active for session ${sessionId}`);
3890
+ }
3891
+ const totalUsage = mergeUsage(recoveryUsage, shellUsage);
3892
+ const turnModelUsage = mergeSessionRawModelUsage(recoveryModelUsage, shellModelUsage);
3893
+ let rawCostUsd;
3894
+ if (turnModelUsage) {
3895
+ for (const [model, usage] of Object.entries(turnModelUsage)) {
3896
+ const pricing = this.options.pricing?.resolve(model);
3897
+ const costUsd = pricing
3898
+ ? usageCostUsd(usage, pricing)
3899
+ : undefined;
3900
+ if (costUsd !== undefined)
3901
+ rawCostUsd = (rawCostUsd ?? 0) + costUsd;
3902
+ tracker.recordTurn({
3903
+ model,
3904
+ usage,
3905
+ ...(costUsd === undefined ? {} : { costUsd }),
3906
+ ...(usage.webSearchRequests === undefined
3907
+ ? {}
3908
+ : { webSearchRequests: usage.webSearchRequests }),
3909
+ });
3910
+ }
3911
+ }
3912
+ let combinedToolDurationMs = 0;
3913
+ for (const recoveryResult of recoveryResults) {
3914
+ combinedToolDurationMs = addToolDuration(recoveryResult.durationToolMs, combinedToolDurationMs);
3915
+ }
3916
+ combinedToolDurationMs = addToolDuration(shellToolDurationMs, combinedToolDurationMs);
3917
+ tracker.recordDurations({
3918
+ ...(shellDurationApiMs === undefined
3919
+ ? {}
3920
+ : { apiDurationMs: shellDurationApiMs }),
3921
+ ...(shellDurationApiWithoutRetriesMs === undefined
3922
+ ? {}
3923
+ : {
3924
+ apiDurationWithoutRetriesMs: shellDurationApiWithoutRetriesMs,
3925
+ }),
3926
+ ...(combinedToolDurationMs === 0
3927
+ ? {}
3928
+ : { toolDurationMs: combinedToolDurationMs }),
3929
+ });
3930
+ if (foregroundLineChanges.linesAdded !== 0 ||
3931
+ foregroundLineChanges.linesRemoved !== 0) {
3932
+ tracker.recordLineChanges({
3933
+ ...(foregroundLineChanges.linesAdded === 0
3934
+ ? {}
3935
+ : { linesAdded: foregroundLineChanges.linesAdded }),
3936
+ ...(foregroundLineChanges.linesRemoved === 0
3937
+ ? {}
3938
+ : { linesRemoved: foregroundLineChanges.linesRemoved }),
3939
+ });
3940
+ }
3941
+ turnCompleted = true;
3942
+ return {
3943
+ sessionId,
3944
+ text: '',
3945
+ usage: totalUsage,
3946
+ ...(rawCostUsd === undefined ? {} : { costUsd: rawCostUsd }),
3947
+ ...(turnModelUsage ? { modelUsage: { ...turnModelUsage } } : {}),
3948
+ ...(shellDurationApiMs === undefined
3949
+ ? {}
3950
+ : { durationApiMs: shellDurationApiMs }),
3951
+ };
3952
+ }
3953
+ if (shellCommand === undefined && budget) {
3876
3954
  await contextEngine.prepare(contextTransitionPort([], currentTurnUserMessages ?? []), signal);
3877
3955
  budget.assertFits(budget.evaluate([
3878
3956
  ...contextMessages,
@@ -3911,7 +3989,9 @@ export class ClaudeSessionService {
3911
3989
  ];
3912
3990
  }
3913
3991
  await refreshRuntimeContext?.();
3914
- await contextEngine.prepare(contextTransitionPort([], currentTurnUserMessages ?? []), signal);
3992
+ if (shellCommand === undefined) {
3993
+ await contextEngine.prepare(contextTransitionPort([], currentTurnUserMessages ?? []), signal);
3994
+ }
3915
3995
  runtimeRequest.stableSystemMessageCount = stableSystemMessageCount;
3916
3996
  return [
3917
3997
  ...contextMessages,
@@ -35,8 +35,15 @@ export interface TopLevelAgentManagerOptions {
35
35
  cliPath: string;
36
36
  executablePath?: string;
37
37
  environment?: NodeJS.ProcessEnv;
38
+ resolveProviderEnvironment?: (request: {
39
+ cwd: string;
40
+ argv: readonly string[];
41
+ }) => Promise<ProviderEnvironmentOverride>;
38
42
  version: string;
39
43
  }
44
+ export interface ProviderEnvironmentOverride {
45
+ PRAXIS_API_KEY?: string;
46
+ }
40
47
  export declare function topLevelAgentProcessRegistryRoot(configRoot: string, dataPlane?: DataPlane): string;
41
48
  export declare class TopLevelAgentManager {
42
49
  private readonly options;
@@ -24,8 +24,11 @@ const SOCKET_RETRY_INTERVAL_MS = 25;
24
24
  // own provider from the same CLI/environment contract.
25
25
  const WORKER_RUNTIME_ENVIRONMENT = [
26
26
  'PRAXIS_API_KEY',
27
+ 'CLAUDE_CODE_SIMPLE',
27
28
  'PRAXIS_MODEL',
28
29
  'PRAXIS_PROVIDER',
30
+ 'PRAXIS_PROVIDER_PROFILE',
31
+ 'PRAXIS_PROVIDER_DEADLINE_MS',
29
32
  'PRAXIS_BASE_URL',
30
33
  'PRAXIS_MAX_OUTPUT_TOKENS',
31
34
  'PRAXIS_ANTHROPIC_VERSION',
@@ -38,10 +41,11 @@ const WORKER_RUNTIME_ENVIRONMENT = [
38
41
  'PRAXIS_FILES_BASE_URL',
39
42
  'PRAXIS_FILES_BEARER_TOKEN',
40
43
  'PRAXIS_FILES_API_KEY',
44
+ 'PRAXIS_PROVIDER_CREDENTIAL_STORE',
41
45
  'PRAXIS_MCP_OAUTH_STORE',
42
46
  'CLAUDE_CODE_ENABLE_SDK_FILE_CHECKPOINTING',
43
47
  ];
44
- function workerEnvironment(source, configRoot) {
48
+ function workerEnvironment(source, configRoot, resolved) {
45
49
  const environment = sanitizeChildEnvironment({ PATH: source.PATH }, {});
46
50
  for (const name of WORKER_RUNTIME_ENVIRONMENT) {
47
51
  const value = source[name];
@@ -50,6 +54,8 @@ function workerEnvironment(source, configRoot) {
50
54
  }
51
55
  environment.PRAXIS_DATA_PLANE = 'native';
52
56
  environment.PRAXIS_HOME = configRoot;
57
+ if (resolved?.PRAXIS_API_KEY !== undefined)
58
+ environment.PRAXIS_API_KEY = resolved.PRAXIS_API_KEY;
53
59
  return environment;
54
60
  }
55
61
  function socketPath(configRoot, id) {
@@ -285,8 +291,15 @@ export class TopLevelAgentManager {
285
291
  throw new Error('Could not allocate agent ID');
286
292
  const allocatedExecution = execution;
287
293
  let child;
294
+ let resolvedProviderEnvironment;
288
295
  const failLaunch = async (error) => {
289
- const message = redactSensitiveText(error instanceof Error ? error.message : String(error), sensitiveEnvironmentValues(this.options.environment ?? process.env));
296
+ const sensitiveValues = [
297
+ ...sensitiveEnvironmentValues(this.options.environment ?? process.env),
298
+ ...(resolvedProviderEnvironment?.PRAXIS_API_KEY === undefined
299
+ ? []
300
+ : [resolvedProviderEnvironment.PRAXIS_API_KEY]),
301
+ ];
302
+ const message = redactSensitiveText(error instanceof Error ? error.message : String(error), sensitiveValues);
290
303
  const secondary = [];
291
304
  if (child?.pid !== undefined) {
292
305
  try {
@@ -321,9 +334,14 @@ export class TopLevelAgentManager {
321
334
  throw new AggregateError([error, ...secondary], 'Could not start background agent');
322
335
  };
323
336
  try {
337
+ resolvedProviderEnvironment =
338
+ await this.options.resolveProviderEnvironment?.({
339
+ cwd,
340
+ argv: options.argv,
341
+ });
324
342
  child = spawn(this.options.executablePath ?? process.execPath, [this.options.cliPath, '__background-worker', identity.id], {
325
343
  cwd,
326
- env: workerEnvironment(this.options.environment ?? process.env, this.options.configRoot),
344
+ env: workerEnvironment(this.options.environment ?? process.env, this.options.configRoot, resolvedProviderEnvironment),
327
345
  detached: true,
328
346
  stdio: 'ignore',
329
347
  });
@@ -206,6 +206,10 @@ export async function resolveCliControls(controls, cwd) {
206
206
  name: controls.name,
207
207
  sessionPersistence: controls.sessionPersistence,
208
208
  ...(controls.model === undefined ? {} : { model: controls.model }),
209
+ ...(controls.provider === undefined ? {} : { provider: controls.provider }),
210
+ ...(controls.providerProfile === undefined
211
+ ? {}
212
+ : { providerProfile: controls.providerProfile }),
209
213
  ...(controls.effort === undefined ? {} : { effort: controls.effort }),
210
214
  ...(controls.thinking === undefined ? {} : { thinking: controls.thinking }),
211
215
  ...(controls.maxThinkingTokens === undefined
@@ -3240,7 +3240,9 @@ export function InteractiveApp({ dataPlane = resolveDataPlane(), configRoot: sup
3240
3240
  streamingFrameRef.current?.resetText();
3241
3241
  streamingFrameRef.current?.resetThinking();
3242
3242
  streamingFrameRef.current?.flush();
3243
- append({ kind: 'assistant', text: result.text });
3243
+ if (shellCommand === undefined) {
3244
+ append({ kind: 'assistant', text: result.text });
3245
+ }
3244
3246
  if (turnMutatedFilesRef.current) {
3245
3247
  try {
3246
3248
  const snapshot = await loadDiffSnapshot();
@@ -52,6 +52,8 @@ export interface CliControls {
52
52
  rewindFiles?: string;
53
53
  name: string | undefined;
54
54
  sessionPersistence: boolean;
55
+ provider?: string;
56
+ providerProfile?: string;
55
57
  model?: string;
56
58
  effort?: 'low' | 'medium' | 'high' | 'xhigh' | 'max';
57
59
  thinking?: CliThinkingMode;
@@ -114,6 +116,9 @@ export interface CliInvocation extends CliControls {
114
116
  mcpClientSecret: boolean;
115
117
  mcpNoBrowser: boolean;
116
118
  mcpDebug: boolean;
119
+ /** Auth-command-only profile selector; distinct from providerProfile. */
120
+ authProfile?: string;
121
+ authDevice: boolean;
117
122
  }
118
123
  export interface StreamUserMessage {
119
124
  message: {
@@ -387,6 +387,8 @@ export function parseCliInvocation(argv) {
387
387
  let worktreeRequested = false;
388
388
  let tmux;
389
389
  let model;
390
+ let provider;
391
+ let providerProfile;
390
392
  let effort;
391
393
  let thinking;
392
394
  let maxThinkingTokens;
@@ -404,6 +406,8 @@ export function parseCliInvocation(argv) {
404
406
  let mcpClientSecret = false;
405
407
  let mcpNoBrowser = false;
406
408
  let mcpDebug = false;
409
+ let authProfile;
410
+ let authDevice = false;
407
411
  for (let index = 0; index < argv.length; index += 1) {
408
412
  const value = argv[index];
409
413
  if (value === undefined)
@@ -444,6 +448,30 @@ export function parseCliInvocation(argv) {
444
448
  index += selectedModel.consumed;
445
449
  continue;
446
450
  }
451
+ const selectedProvider = optionValue(argv, index, '--provider');
452
+ if (selectedProvider) {
453
+ if (provider !== undefined)
454
+ throw new Error('--provider may only be specified once');
455
+ provider = selectedProvider.value;
456
+ index += selectedProvider.consumed;
457
+ continue;
458
+ }
459
+ const selectedProviderProfile = optionValue(argv, index, '--provider-profile');
460
+ if (selectedProviderProfile) {
461
+ if (providerProfile !== undefined)
462
+ throw new Error('--provider-profile may only be specified once');
463
+ providerProfile = selectedProviderProfile.value;
464
+ index += selectedProviderProfile.consumed;
465
+ continue;
466
+ }
467
+ const selectedAuthProfile = optionValue(argv, index, '--profile');
468
+ if (selectedAuthProfile) {
469
+ if (authProfile !== undefined)
470
+ throw new Error('--profile may only be specified once');
471
+ authProfile = selectedAuthProfile.value;
472
+ index += selectedAuthProfile.consumed;
473
+ continue;
474
+ }
447
475
  const selectedAutocompact = optionValue(argv, index, '--autocompact');
448
476
  if (selectedAutocompact) {
449
477
  if (autocompact !== undefined)
@@ -1123,6 +1151,10 @@ export function parseCliInvocation(argv) {
1123
1151
  mcpNoBrowser = true;
1124
1152
  continue;
1125
1153
  }
1154
+ if (value === '--device') {
1155
+ authDevice = true;
1156
+ continue;
1157
+ }
1126
1158
  if (value === '--json') {
1127
1159
  legacyJson = true;
1128
1160
  continue;
@@ -1174,6 +1206,14 @@ export function parseCliInvocation(argv) {
1174
1206
  if (debug !== undefined && args[0] === 'mcp' && args[1] === 'serve') {
1175
1207
  mcpDebug = true;
1176
1208
  }
1209
+ if (authProfile !== undefined && args[0] !== 'auth')
1210
+ throw new Error('--profile is only valid with auth commands');
1211
+ if (authDevice && !(args[0] === 'auth' && args[1] === 'login'))
1212
+ throw new Error('--device is only valid with auth login');
1213
+ if (mcpNoBrowser &&
1214
+ !((args[0] === 'mcp' && args[1] === 'login') ||
1215
+ (args[0] === 'auth' && args[1] === 'login')))
1216
+ throw new Error('--no-browser is only valid with mcp login or auth login');
1177
1217
  if (background && print) {
1178
1218
  throw new Error("--bg and --print conflict: --print never starts the interactive session that `claude agents` attaches to, so the job would be unattachable. The prompt is the positional — drop --print: `claude --bg '<task>'`.");
1179
1219
  }
@@ -1215,6 +1255,7 @@ export function parseCliInvocation(argv) {
1215
1255
  'plugin',
1216
1256
  'doctor',
1217
1257
  'import',
1258
+ 'auth',
1218
1259
  ].includes(args[0] ?? '');
1219
1260
  if (promptSuggestions &&
1220
1261
  !managementCommand &&
@@ -1385,6 +1426,8 @@ export function parseCliInvocation(argv) {
1385
1426
  ...(worktreeRequested ? { worktreeRequested: true } : {}),
1386
1427
  ...(tmux === undefined ? {} : { tmux }),
1387
1428
  ...(model === undefined ? {} : { model }),
1429
+ ...(provider === undefined ? {} : { provider }),
1430
+ ...(providerProfile === undefined ? {} : { providerProfile }),
1388
1431
  ...(effort === undefined ? {} : { effort }),
1389
1432
  ...(thinking === undefined ? {} : { thinking }),
1390
1433
  ...(maxThinkingTokens === undefined ? {} : { maxThinkingTokens }),
@@ -1402,6 +1445,8 @@ export function parseCliInvocation(argv) {
1402
1445
  mcpClientSecret,
1403
1446
  mcpNoBrowser,
1404
1447
  mcpDebug,
1448
+ ...(authProfile === undefined ? {} : { authProfile }),
1449
+ authDevice,
1405
1450
  };
1406
1451
  }
1407
1452
  function parseUserMessage(value, lineNumber) {
@@ -0,0 +1,32 @@
1
+ import { type ProviderCredentialKey, type ProviderCredentialMetadata, type ProviderCredentialInput, type ProviderCredentialRecord } from '../persistence/provider-credential-vault.js';
2
+ import { deviceLoginWithCodexOAuth, loginWithCodexOAuth } from '../providers/codex-oauth.js';
3
+ export interface ProviderAuthCommandIO {
4
+ stdout(message: string): void;
5
+ stderr?(message: string): void;
6
+ readSecret?(prompt: string, signal?: AbortSignal): Promise<string>;
7
+ }
8
+ export interface ProviderAuthVault {
9
+ read(key: ProviderCredentialKey): Promise<ProviderCredentialRecord | undefined>;
10
+ list(): Promise<ProviderCredentialMetadata[]>;
11
+ modify(key: ProviderCredentialKey, callback: (current: ProviderCredentialRecord | undefined) => ProviderCredentialInput | undefined | Promise<ProviderCredentialInput | undefined>): Promise<ProviderCredentialRecord | undefined>;
12
+ delete(key: ProviderCredentialKey): Promise<void>;
13
+ }
14
+ export interface ProviderAuthCommandOptions {
15
+ io: ProviderAuthCommandIO;
16
+ vault?: ProviderAuthVault;
17
+ configRoot?: string;
18
+ cwd?: string;
19
+ environment?: Readonly<Record<string, string | undefined>>;
20
+ signal?: AbortSignal;
21
+ profile?: string;
22
+ providerProfile?: string;
23
+ noBrowser?: boolean;
24
+ json?: boolean;
25
+ device?: boolean;
26
+ loginWithCodexOAuth?: typeof loginWithCodexOAuth;
27
+ deviceLoginWithCodexOAuth?: typeof deviceLoginWithCodexOAuth;
28
+ assertCodexLoginEnabled?: (profileId: string) => Promise<void>;
29
+ }
30
+ export declare function secretLine(value: string): string;
31
+ export declare function executeProviderAuthCommand(args: readonly string[], options: ProviderAuthCommandOptions): Promise<number>;
32
+ //# sourceMappingURL=provider-auth-command.d.ts.map