vigthoria-cli 1.13.18 → 1.13.22

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.
Files changed (185) hide show
  1. package/completions/_vigthoria +49 -0
  2. package/completions/vigthoria.bash +6 -0
  3. package/completions/vigthoria.fish +41 -0
  4. package/dist/commands/auth.d.ts +1 -0
  5. package/dist/commands/auth.js +84 -135
  6. package/dist/commands/background.d.ts +0 -2
  7. package/dist/commands/background.js +26 -34
  8. package/dist/commands/bridge.js +45 -41
  9. package/dist/commands/cancel.d.ts +1 -0
  10. package/dist/commands/cancel.js +22 -16
  11. package/dist/commands/chat.d.ts +5 -42
  12. package/dist/commands/chat.js +462 -1122
  13. package/dist/commands/coding-registration.d.ts +4 -0
  14. package/dist/commands/coding-registration.js +24 -0
  15. package/dist/commands/config.js +32 -42
  16. package/dist/commands/conversation-registration.d.ts +4 -0
  17. package/dist/commands/conversation-registration.js +122 -0
  18. package/dist/commands/deploy.d.ts +8 -2
  19. package/dist/commands/deploy.js +92 -90
  20. package/dist/commands/device.d.ts +1 -2
  21. package/dist/commands/device.js +37 -21
  22. package/dist/commands/doctor-registration.d.ts +4 -0
  23. package/dist/commands/doctor-registration.js +200 -0
  24. package/dist/commands/edit.js +22 -33
  25. package/dist/commands/explain.js +7 -10
  26. package/dist/commands/fork.d.ts +1 -2
  27. package/dist/commands/fork.js +72 -22
  28. package/dist/commands/game.d.ts +1 -1
  29. package/dist/commands/game.js +17 -6
  30. package/dist/commands/generate.js +7 -9
  31. package/dist/commands/history.js +11 -7
  32. package/dist/commands/hub.d.ts +2 -2
  33. package/dist/commands/hub.js +34 -35
  34. package/dist/commands/legion.d.ts +0 -1
  35. package/dist/commands/legion.js +62 -79
  36. package/dist/commands/music.d.ts +1 -2
  37. package/dist/commands/music.js +62 -27
  38. package/dist/commands/platform-registration.d.ts +4 -0
  39. package/dist/commands/platform-registration.js +45 -0
  40. package/dist/commands/preview.d.ts +0 -1
  41. package/dist/commands/preview.js +82 -35
  42. package/dist/commands/product-run-registration.d.ts +4 -0
  43. package/dist/commands/product-run-registration.js +490 -0
  44. package/dist/commands/replay.d.ts +1 -2
  45. package/dist/commands/replay.js +15 -11
  46. package/dist/commands/repo.d.ts +14 -7
  47. package/dist/commands/repo.js +245 -196
  48. package/dist/commands/review.js +9 -12
  49. package/dist/commands/security.d.ts +1 -2
  50. package/dist/commands/security.js +12 -6
  51. package/dist/commands/update-registration.d.ts +3 -0
  52. package/dist/commands/update-registration.js +338 -0
  53. package/dist/commands/v4-registration.d.ts +4 -0
  54. package/dist/commands/v4-registration.js +24 -0
  55. package/dist/commands/v4.d.ts +1 -1
  56. package/dist/commands/v4.js +46 -8
  57. package/dist/commands/wallet.d.ts +1 -3
  58. package/dist/commands/wallet.js +18 -45
  59. package/dist/commands/workflow.d.ts +2 -0
  60. package/dist/commands/workflow.js +52 -39
  61. package/dist/index.js +122 -1498
  62. package/dist/utils/agent-stream-state.d.ts +27 -0
  63. package/dist/utils/agent-stream-state.js +93 -0
  64. package/dist/utils/agentRoute.d.ts +2 -1
  65. package/dist/utils/agentRoute.js +22 -9
  66. package/dist/utils/agentRunOutcome.d.ts +16 -0
  67. package/dist/utils/agentRunOutcome.js +37 -2
  68. package/dist/utils/api-client-factory.d.ts +8 -0
  69. package/dist/utils/api-client-factory.js +8 -0
  70. package/dist/utils/api.d.ts +45 -269
  71. package/dist/utils/api.js +664 -3210
  72. package/dist/utils/auth-session.d.ts +41 -0
  73. package/dist/utils/auth-session.js +224 -0
  74. package/dist/utils/brain-hub-client.d.ts +0 -4
  75. package/dist/utils/brain-hub-client.js +6 -3
  76. package/dist/utils/bridge-client.js +2 -0
  77. package/dist/utils/capability-health-service.d.ts +48 -0
  78. package/dist/utils/capability-health-service.js +64 -0
  79. package/dist/utils/chat-prompt-policy.d.ts +18 -0
  80. package/dist/utils/chat-prompt-policy.js +61 -0
  81. package/dist/utils/chat-tool-call-parser.d.ts +11 -0
  82. package/dist/utils/chat-tool-call-parser.js +71 -0
  83. package/dist/utils/cli-state.d.ts +22 -4
  84. package/dist/utils/cli-state.js +105 -47
  85. package/dist/utils/code-operations-service.d.ts +104 -0
  86. package/dist/utils/code-operations-service.js +837 -0
  87. package/dist/utils/codebase-indexer.d.ts +1 -0
  88. package/dist/utils/codebase-indexer.js +28 -8
  89. package/dist/utils/command-contract.d.ts +61 -0
  90. package/dist/utils/command-contract.js +149 -0
  91. package/dist/utils/command-policy.d.ts +10 -0
  92. package/dist/utils/command-policy.js +54 -0
  93. package/dist/utils/config.d.ts +13 -5
  94. package/dist/utils/config.js +227 -99
  95. package/dist/utils/context-ranker.js +11 -2
  96. package/dist/utils/context-state.d.ts +39 -0
  97. package/dist/utils/context-state.js +52 -0
  98. package/dist/utils/deckEvents.d.ts +0 -5
  99. package/dist/utils/deckEvents.js +3 -2
  100. package/dist/utils/desktop-bridge-client.js +2 -0
  101. package/dist/utils/direct-output-policy.d.ts +3 -0
  102. package/dist/utils/direct-output-policy.js +76 -0
  103. package/dist/utils/durable-json.d.ts +57 -0
  104. package/dist/utils/durable-json.js +319 -0
  105. package/dist/utils/fastAgentRouter.d.ts +6 -7
  106. package/dist/utils/fastAgentRouter.js +21 -30
  107. package/dist/utils/files.js +21 -13
  108. package/dist/utils/frontend-preview-service.d.ts +76 -0
  109. package/dist/utils/frontend-preview-service.js +482 -0
  110. package/dist/utils/goaEvents.d.ts +0 -4
  111. package/dist/utils/goaEvents.js +5 -2
  112. package/dist/utils/logger.d.ts +2 -1
  113. package/dist/utils/logger.js +27 -20
  114. package/dist/utils/mcp-context-client.d.ts +24 -0
  115. package/dist/utils/mcp-context-client.js +58 -0
  116. package/dist/utils/model-governance.d.ts +18 -0
  117. package/dist/utils/model-governance.js +75 -0
  118. package/dist/utils/model-transport-service.d.ts +44 -0
  119. package/dist/utils/model-transport-service.js +111 -0
  120. package/dist/utils/mutation-journal.d.ts +42 -0
  121. package/dist/utils/mutation-journal.js +387 -0
  122. package/dist/utils/network-policy.d.ts +24 -0
  123. package/dist/utils/network-policy.js +202 -0
  124. package/dist/utils/operator-client.d.ts +45 -0
  125. package/dist/utils/operator-client.js +159 -0
  126. package/dist/utils/post-write-validator.js +7 -9
  127. package/dist/utils/preview-screenshot-adapter.d.ts +13 -0
  128. package/dist/utils/preview-screenshot-adapter.js +36 -0
  129. package/dist/utils/process-policy.d.ts +23 -0
  130. package/dist/utils/process-policy.js +284 -0
  131. package/dist/utils/project-memory.d.ts +13 -3
  132. package/dist/utils/project-memory.js +70 -17
  133. package/dist/utils/prompt-workspace-resolver.d.ts +14 -0
  134. package/dist/utils/prompt-workspace-resolver.js +64 -0
  135. package/dist/utils/release-install.d.ts +11 -0
  136. package/dist/utils/release-install.js +155 -0
  137. package/dist/utils/release-policy.d.ts +64 -0
  138. package/dist/utils/release-policy.js +293 -0
  139. package/dist/utils/requestIntent.js +22 -3
  140. package/dist/utils/runtime-capability.d.ts +8 -0
  141. package/dist/utils/runtime-capability.js +16 -0
  142. package/dist/utils/secret-policy.d.ts +20 -0
  143. package/dist/utils/secret-policy.js +160 -0
  144. package/dist/utils/session.d.ts +34 -2
  145. package/dist/utils/session.js +354 -110
  146. package/dist/utils/tool-approval-service.d.ts +17 -0
  147. package/dist/utils/tool-approval-service.js +78 -0
  148. package/dist/utils/tool-capability-providers.d.ts +57 -0
  149. package/dist/utils/tool-capability-providers.js +57 -0
  150. package/dist/utils/tools.d.ts +2 -23
  151. package/dist/utils/tools.js +216 -508
  152. package/dist/utils/update-policy.d.ts +18 -0
  153. package/dist/utils/update-policy.js +137 -0
  154. package/dist/utils/v3-agent-client.d.ts +40 -0
  155. package/dist/utils/v3-agent-client.js +165 -0
  156. package/dist/utils/v3-workspace-service.d.ts +28 -0
  157. package/dist/utils/v3-workspace-service.js +279 -0
  158. package/dist/utils/vigflow-client.d.ts +95 -0
  159. package/dist/utils/vigflow-client.js +105 -0
  160. package/dist/utils/workspace-boundary.d.ts +45 -0
  161. package/dist/utils/workspace-boundary.js +377 -0
  162. package/dist/utils/workspace-brain-service.d.ts +2 -0
  163. package/dist/utils/workspace-brain-service.js +11 -1
  164. package/dist/utils/workspace-cache.js +37 -15
  165. package/dist/utils/workspace-stream.js +35 -10
  166. package/install.ps1 +86 -143
  167. package/install.sh +76 -81
  168. package/package.json +44 -9
  169. package/release-policy.json +43 -0
  170. package/scripts/release/LOCAL_MACHINE_USER_VERIFICATION.md +1 -1
  171. package/scripts/release/generate-completions.mjs +24 -0
  172. package/scripts/release/install-release.mjs +10 -0
  173. package/scripts/release/publish-cli-release.mjs +116 -0
  174. package/scripts/release/publish-cli-release.sh +2 -70
  175. package/scripts/release/resolve-release-manifest.mjs +26 -0
  176. package/scripts/release/test-balanced-model-live.sh +42 -0
  177. package/scripts/release/validate-live-service-gates.sh +90 -0
  178. package/scripts/release/validate-no-go-gates.sh +135 -172
  179. package/scripts/release/verify-runtime-consistency.mjs +4 -1
  180. package/dist/commands/index.d.ts +0 -12
  181. package/dist/commands/index.js +0 -194
  182. package/dist/commands/update.d.ts +0 -9
  183. package/dist/commands/update.js +0 -301
  184. package/dist/utils/templateInstantPath.d.ts +0 -18
  185. package/dist/utils/templateInstantPath.js +0 -228
@@ -0,0 +1,49 @@
1
+ #compdef vigthoria vig vigthoria-chat
2
+ # Generated from contracts/phase-0/command-manifest.json; do not edit.
3
+ _vigthoria() {
4
+ local -a commands
5
+ commands=(
6
+ 'agent:Start agentic mode - AI can read/write files, run commands'
7
+ 'auth:Manage Vigthoria CLI authentication'
8
+ 'background:Run and manage detached Vigthoria Agent jobs'
9
+ 'bridge:Inspect local DevTools Bridge availability for browser debugging tasks'
10
+ 'cancel:Cancel an in-flight V3 agent run (by context_id), or use --all to cancel every active run'
11
+ 'chat:Start interactive chat with Vigthoria AI'
12
+ 'chat-resume:Resume the latest chat session for the current or specified project'
13
+ 'commands:List every CLI command grouped by command family'
14
+ 'config:Configure Vigthoria CLI settings'
15
+ 'deploy:Deploy and host your project on Vigthoria infrastructure'
16
+ 'device:Android Developer Bridge commands via local ADB'
17
+ 'devtools:DevTools Bridge commands for browser debugging'
18
+ 'doctor:Run local Vigthoria CLI diagnostics (no network calls unless --check-api)'
19
+ 'edit:Edit a file with AI assistance'
20
+ 'explain:Explain code in a file'
21
+ 'fix:Fix issues in a file'
22
+ 'fork:Fork from an existing V3 agent run with new instructions'
23
+ 'game:Create, run, and validate Babylon/VGE game projects'
24
+ 'generate:Generate code from description'
25
+ 'history:List recent V3 agent runs'
26
+ 'hub:Discover, search, and activate Vigthoria API modules'
27
+ 'hyper-loop:Hyper Loop Legion orchestrator commands'
28
+ 'init:Initialize Vigthoria in current project'
29
+ 'legion:Run parallel tasks via Hyper Loop Legion orchestrator'
30
+ 'login:Login to Vigthoria Coder (prompts for credentials when run without flags)'
31
+ 'logout:Logout from Vigthoria Coder'
32
+ 'menu:Open the interactive command-family menu'
33
+ 'music:Generate songs with Vigthoria Music AI / ACE-Step'
34
+ 'operator:Start BMAD operator mode for infrastructure and system workflows'
35
+ 'preview:Preview project locally with visual diffs and proof validation'
36
+ 'replay:Replay events from a V3 agent run step-by-step'
37
+ 'repo:Push and pull projects to/from your Vigthoria Community Repository'
38
+ 'review:Review code quality'
39
+ 'security:Run security scans, scores, and fix plans via Vigthoria MCP security tools'
40
+ 'status:Show authentication and subscription status'
41
+ 'update:Check for updates and upgrade Vigthoria CLI. Default manifest: https://extension.vigthoria.io/downloads/manifest.json'
42
+ 'v4:Launch Vigthoria V4 Operating Agent (DeerFlow 2.0 harness)'
43
+ 'v4-menu:Internal: interactive V3/V4 agent + provider picker used by the V4 Operating Agent launcher'
44
+ 'wallet:Inspect monetary Vigthoria Credits used for Cloud, API, Video, and other PAYG services'
45
+ 'workflow:List, instantiate, and run repeatable VigFlow workflows'
46
+ )
47
+ _describe 'command' commands
48
+ }
49
+ _vigthoria "$@"
@@ -0,0 +1,6 @@
1
+ # Generated from contracts/phase-0/command-manifest.json; do not edit.
2
+ _vigthoria_completions() {
3
+ local cur="${COMP_WORDS[COMP_CWORD]}"
4
+ COMPREPLY=( $(compgen -W 'agent auth background bridge cancel chat chat-resume commands config deploy device devtools doctor edit explain fix fork game generate history hub hyper-loop init legion login logout menu music operator preview replay repo review security status update v4 v4-menu wallet workflow' -- "$cur") )
5
+ }
6
+ complete -F _vigthoria_completions vigthoria vig vigthoria-chat
@@ -0,0 +1,41 @@
1
+ # Generated from contracts/phase-0/command-manifest.json; do not edit.
2
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'agent' -d 'Start agentic mode - AI can read/write files, run commands'
3
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'auth' -d 'Manage Vigthoria CLI authentication'
4
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'background' -d 'Run and manage detached Vigthoria Agent jobs'
5
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'bridge' -d 'Inspect local DevTools Bridge availability for browser debugging tasks'
6
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'cancel' -d 'Cancel an in-flight V3 agent run (by context_id), or use --all to cancel every active run'
7
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'chat' -d 'Start interactive chat with Vigthoria AI'
8
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'chat-resume' -d 'Resume the latest chat session for the current or specified project'
9
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'commands' -d 'List every CLI command grouped by command family'
10
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'config' -d 'Configure Vigthoria CLI settings'
11
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'deploy' -d 'Deploy and host your project on Vigthoria infrastructure'
12
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'device' -d 'Android Developer Bridge commands via local ADB'
13
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'devtools' -d 'DevTools Bridge commands for browser debugging'
14
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'doctor' -d 'Run local Vigthoria CLI diagnostics (no network calls unless --check-api)'
15
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'edit' -d 'Edit a file with AI assistance'
16
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'explain' -d 'Explain code in a file'
17
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'fix' -d 'Fix issues in a file'
18
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'fork' -d 'Fork from an existing V3 agent run with new instructions'
19
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'game' -d 'Create, run, and validate Babylon/VGE game projects'
20
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'generate' -d 'Generate code from description'
21
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'history' -d 'List recent V3 agent runs'
22
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'hub' -d 'Discover, search, and activate Vigthoria API modules'
23
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'hyper-loop' -d 'Hyper Loop Legion orchestrator commands'
24
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'init' -d 'Initialize Vigthoria in current project'
25
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'legion' -d 'Run parallel tasks via Hyper Loop Legion orchestrator'
26
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'login' -d 'Login to Vigthoria Coder (prompts for credentials when run without flags)'
27
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'logout' -d 'Logout from Vigthoria Coder'
28
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'menu' -d 'Open the interactive command-family menu'
29
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'music' -d 'Generate songs with Vigthoria Music AI / ACE-Step'
30
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'operator' -d 'Start BMAD operator mode for infrastructure and system workflows'
31
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'preview' -d 'Preview project locally with visual diffs and proof validation'
32
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'replay' -d 'Replay events from a V3 agent run step-by-step'
33
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'repo' -d 'Push and pull projects to/from your Vigthoria Community Repository'
34
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'review' -d 'Review code quality'
35
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'security' -d 'Run security scans, scores, and fix plans via Vigthoria MCP security tools'
36
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'status' -d 'Show authentication and subscription status'
37
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'update' -d 'Check for updates and upgrade Vigthoria CLI. Default manifest: https://extension.vigthoria.io/downloads/manifest.json'
38
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'v4' -d 'Launch Vigthoria V4 Operating Agent (DeerFlow 2.0 harness)'
39
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'v4-menu' -d 'Internal: interactive V3/V4 agent + provider picker used by the V4 Operating Agent launcher'
40
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'wallet' -d 'Inspect monetary Vigthoria Credits used for Cloud, API, Video, and other PAYG services'
41
+ complete -c vigthoria -c vig -c vigthoria-chat -n '__fish_use_subcommand' -a 'workflow' -d 'List, instantiate, and run repeatable VigFlow workflows'
@@ -2,6 +2,7 @@ import { Command } from 'commander';
2
2
  export interface AuthConfig {
3
3
  apiUrl: string;
4
4
  token?: string;
5
+ refreshToken?: string;
5
6
  v3ServiceKey?: string;
6
7
  user?: {
7
8
  id?: string;
@@ -1,19 +1,21 @@
1
1
  import chalk from 'chalk';
2
- import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
3
- import { homedir } from 'os';
4
- import path from 'path';
5
2
  import readline from 'readline';
6
3
  import { Config } from '../utils/config.js';
7
4
  import { clearGatewayPreflightCache } from '../utils/cli-state.js';
5
+ import { AuthSessionService } from '../utils/auth-session.js';
6
+ import { assertTrustedEndpoint, guardedFetch } from '../utils/network-policy.js';
7
+ import { CliCommandError, commandFailure } from '../utils/command-contract.js';
8
8
  const DEFAULT_API_URL = 'https://coder.vigthoria.io';
9
- const CONFIG_DIR = path.join(homedir(), '.vigthoria');
10
- const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
11
9
  const KNOWN_AUTH_BASE_URLS = ['https://coder.vigthoria.io'];
12
10
  class HttpError extends Error {
13
11
  status;
14
- constructor(status, message) {
12
+ code;
13
+ upgradeUrl;
14
+ constructor(status, message, code, upgradeUrl) {
15
15
  super(message);
16
16
  this.status = status;
17
+ this.code = code;
18
+ this.upgradeUrl = upgradeUrl;
17
19
  }
18
20
  }
19
21
  function trimTrailingSlash(value) {
@@ -23,68 +25,13 @@ function uniqueStrings(values) {
23
25
  return [...new Set(values.filter(Boolean))];
24
26
  }
25
27
  function getApiUrl() {
26
- return trimTrailingSlash(process.env.VIGTHORIA_API_URL || DEFAULT_API_URL);
27
- }
28
- function derivePeerHost(baseUrl) {
29
- if (baseUrl.includes('://api.vigthoria.io')) {
30
- return baseUrl.replace('://api.vigthoria.io', '://coder.vigthoria.io');
31
- }
32
- if (baseUrl.includes('://coder.vigthoria.io')) {
33
- return baseUrl.replace('://coder.vigthoria.io', '://api.vigthoria.io');
34
- }
35
- return undefined;
28
+ const configured = trimTrailingSlash(process.env.VIGTHORIA_API_URL || DEFAULT_API_URL);
29
+ return trimTrailingSlash(assertTrustedEndpoint(configured, { audience: 'coder', credentialBearing: true, ignoreOffline: true }).toString());
36
30
  }
37
31
  function getAuthBaseCandidates(seedBaseUrl) {
38
32
  const seed = trimTrailingSlash(seedBaseUrl || getApiUrl());
39
- const peer = derivePeerHost(seed);
40
- return uniqueStrings([seed, ...(peer ? [peer] : []), ...KNOWN_AUTH_BASE_URLS]).map(trimTrailingSlash);
41
- }
42
- function ensureConfigDir() {
43
- if (!existsSync(CONFIG_DIR)) {
44
- mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
45
- }
46
- try {
47
- chmodSync(CONFIG_DIR, 0o700);
48
- }
49
- catch {
50
- // Best-effort on non-POSIX filesystems.
51
- }
52
- }
53
- function syncSharedConfig(config) {
54
- try {
55
- const shared = new Config();
56
- shared.set('apiUrl', trimTrailingSlash(config.apiUrl || getApiUrl()));
57
- if (config.token) {
58
- shared.set('authToken', config.token);
59
- }
60
- if (config.user?.id) {
61
- shared.set('userId', String(config.user.id));
62
- }
63
- if (config.user?.email) {
64
- shared.set('email', String(config.user.email));
65
- }
66
- // Always overwrite (never conditionally skip) so a fresh login can never
67
- // leave a stale/rotated key behind from a prior session — a falsy value
68
- // here explicitly clears the cache, forcing ensureV3ServiceKey() to fetch
69
- // a real one on next use instead of silently trusting stale state.
70
- shared.set('v3ServiceKey', config.v3ServiceKey || null);
71
- }
72
- catch {
73
- // Keep legacy auth flow working even if shared config write fails.
74
- }
75
- }
76
- function clearSharedConfigAuth() {
77
- try {
78
- const shared = new Config();
79
- shared.set('authToken', null);
80
- shared.set('refreshToken', null);
81
- shared.set('userId', null);
82
- shared.set('email', null);
83
- shared.set('v3ServiceKey', null);
84
- }
85
- catch {
86
- // Ignore shared config clear failures during logout.
87
- }
33
+ return uniqueStrings([seed, ...KNOWN_AUTH_BASE_URLS])
34
+ .map((candidate) => trimTrailingSlash(assertTrustedEndpoint(candidate, { audience: 'coder', credentialBearing: true, ignoreOffline: true }).toString()));
88
35
  }
89
36
  function humanMessage(error) {
90
37
  const raw = error instanceof Error ? error.message : String(error);
@@ -124,16 +71,17 @@ function extractAuthUser(payload, fallbackEmail) {
124
71
  return fallbackEmail ? { email: fallbackEmail } : undefined;
125
72
  }
126
73
  export function loadAuthConfig() {
127
- if (!existsSync(CONFIG_FILE)) {
128
- return { apiUrl: getApiUrl() };
129
- }
130
74
  try {
131
- const parsed = JSON.parse(readFileSync(CONFIG_FILE, 'utf8'));
75
+ const shared = new Config();
132
76
  return {
133
- apiUrl: trimTrailingSlash(parsed.apiUrl || getApiUrl()),
134
- token: parsed.token || parsed.authToken,
135
- v3ServiceKey: typeof parsed.v3ServiceKey === 'string' ? parsed.v3ServiceKey : undefined,
136
- user: parsed.user,
77
+ apiUrl: trimTrailingSlash(String(shared.get('apiUrl') || getApiUrl())),
78
+ token: shared.get('authToken') || undefined,
79
+ refreshToken: shared.get('refreshToken') || undefined,
80
+ v3ServiceKey: shared.get('v3ServiceKey') || undefined,
81
+ user: {
82
+ id: shared.get('userId') || undefined,
83
+ email: shared.get('email') || undefined,
84
+ },
137
85
  };
138
86
  }
139
87
  catch (error) {
@@ -142,25 +90,23 @@ export function loadAuthConfig() {
142
90
  }
143
91
  }
144
92
  export function saveAuthConfig(config) {
145
- ensureConfigDir();
146
- writeFileSync(CONFIG_FILE, `${JSON.stringify(config, null, 2)}\n`, 'utf8');
147
- try {
148
- chmodSync(CONFIG_FILE, 0o600);
149
- }
150
- catch {
151
- // Best-effort on non-POSIX filesystems.
152
- }
153
- syncSharedConfig(config);
93
+ if (!config.token)
94
+ throw new Error('Cannot persist an authentication record without a token.');
95
+ const shared = new Config();
96
+ shared.setAuth({
97
+ token: config.token,
98
+ refreshToken: config.refreshToken,
99
+ userId: config.user?.id,
100
+ email: config.user?.email,
101
+ apiUrl: trimTrailingSlash(config.apiUrl || getApiUrl()),
102
+ v3ServiceKey: config.v3ServiceKey || null,
103
+ });
154
104
  // A fresh login must never be blocked by a stale cached gateway-preflight
155
105
  // failure from a prior (invalid) session.
156
106
  clearGatewayPreflightCache();
157
107
  }
158
108
  export function clearAuthConfig() {
159
- if (existsSync(CONFIG_FILE)) {
160
- rmSync(CONFIG_FILE, { force: true });
161
- }
162
- clearSharedConfigAuth();
163
- clearGatewayPreflightCache();
109
+ new AuthSessionService(new Config()).clearLocalCredentials();
164
110
  }
165
111
  export function getAuthToken() {
166
112
  return process.env.VIGTHORIA_TOKEN || loadAuthConfig().token;
@@ -172,7 +118,7 @@ async function requestJson(url, init) {
172
118
  const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
173
119
  let response;
174
120
  try {
175
- response = await fetch(url, {
121
+ response = await guardedFetch(url, {
176
122
  ...init,
177
123
  signal: controller.signal,
178
124
  headers: {
@@ -180,7 +126,7 @@ async function requestJson(url, init) {
180
126
  'content-type': 'application/json',
181
127
  ...(init.headers || {}),
182
128
  },
183
- });
129
+ }, { audience: 'coder' });
184
130
  }
185
131
  catch (error) {
186
132
  const name = error?.name;
@@ -206,12 +152,19 @@ async function requestJson(url, init) {
206
152
  }
207
153
  }
208
154
  if (!response.ok) {
209
- const message = typeof body === 'object' && body && 'error' in body
210
- ? String(body.error)
211
- : typeof body === 'object' && body && 'message' in body
212
- ? String(body.message)
213
- : `Request failed with status ${response.status}`;
214
- throw new HttpError(response.status, message);
155
+ const errorBody = typeof body === 'object' && body ? body : {};
156
+ const code = String(errorBody.code || '');
157
+ const planNames = Array.isArray(errorBody.requiredPlans)
158
+ ? errorBody.requiredPlans.map(plan => plan.name || plan.id).filter(Boolean)
159
+ : [];
160
+ const message = code === 'SUBSCRIPTION_REQUIRED'
161
+ ? `This project requires a paid Vigthoria subscription.${planNames.length ? ` Eligible plans: ${planNames.join(', ')}.` : ''} Upgrade: ${errorBody.upgradeUrl || 'https://hub.vigthoria.io/subscriptions?application=coder'}`
162
+ : 'error' in errorBody && errorBody.error
163
+ ? String(errorBody.error)
164
+ : 'message' in errorBody && errorBody.message
165
+ ? String(errorBody.message)
166
+ : `Request failed with status ${response.status}`;
167
+ throw new HttpError(response.status, message, code || undefined, errorBody.upgradeUrl);
215
168
  }
216
169
  return body;
217
170
  }
@@ -266,12 +219,6 @@ async function ask(question, hidden = false) {
266
219
  rl.close();
267
220
  }
268
221
  }
269
- function markSuccessExit() {
270
- process.exitCode = 0;
271
- }
272
- function markErrorExit() {
273
- process.exitCode = 1;
274
- }
275
222
  export async function login(email, password) {
276
223
  try {
277
224
  const resolvedEmail = (email || '').trim();
@@ -304,18 +251,28 @@ export async function login(email, password) {
304
251
  }
305
252
  const normalizedBase = trimTrailingSlash(base);
306
253
  const v3ServiceKey = await fetchV3ServiceKey(normalizedBase, token);
254
+ const identity = extractAuthUser(result, resolvedEmail);
255
+ const refreshToken = result.tokens?.refresh_token || undefined;
256
+ await new AuthSessionService(new Config()).validateAndPersistToken(token, {
257
+ apiUrl: normalizedBase,
258
+ refreshToken,
259
+ identity: { userId: identity?.id, email: identity?.email },
260
+ v3ServiceKey: v3ServiceKey || null,
261
+ });
307
262
  const config = {
308
263
  apiUrl: normalizedBase,
309
264
  token,
310
- user: extractAuthUser(result, resolvedEmail),
265
+ ...(refreshToken ? { refreshToken } : {}),
266
+ user: identity,
311
267
  ...(v3ServiceKey ? { v3ServiceKey } : {}),
312
268
  success: true,
313
269
  };
314
- saveAuthConfig(config);
315
- markSuccessExit();
316
270
  return config;
317
271
  }
318
272
  catch (error) {
273
+ if (error instanceof HttpError && error.code === 'SUBSCRIPTION_REQUIRED') {
274
+ throw error;
275
+ }
319
276
  const message = humanMessage(error);
320
277
  failures.push(`${endpoint} -> ${message}`);
321
278
  }
@@ -325,8 +282,12 @@ export async function login(email, password) {
325
282
  }
326
283
  catch (error) {
327
284
  const message = humanMessage(error);
328
- markErrorExit();
329
- throw new Error(message);
285
+ if (error instanceof HttpError && error.code === 'SUBSCRIPTION_REQUIRED') {
286
+ throw new CliCommandError(message, {
287
+ code: 'SUBSCRIPTION_REQUIRED', category: 'authorization', status: error.status, cause: error,
288
+ });
289
+ }
290
+ throw commandFailure(error, { code: 'LOGIN_FAILED', category: 'authentication', message });
330
291
  }
331
292
  }
332
293
  export async function logout() {
@@ -358,7 +319,6 @@ export async function logout() {
358
319
  }
359
320
  }
360
321
  clearAuthConfig();
361
- process.exitCode = 0;
362
322
  }
363
323
  export async function whoami() {
364
324
  const config = loadAuthConfig();
@@ -410,32 +370,33 @@ export async function whoami() {
410
370
  }
411
371
  export async function doctor() {
412
372
  const config = loadAuthConfig();
373
+ const shared = new Config();
413
374
  const report = {
414
375
  nodeVersion: process.version,
415
376
  platform: process.platform,
416
377
  arch: process.arch,
417
- configFile: CONFIG_FILE,
378
+ configFile: shared.getConfigPath(),
418
379
  loggedIn: Boolean(config.token),
419
380
  apiUrl: config.apiUrl,
420
381
  };
421
- process.exitCode = 0;
422
382
  return report;
423
383
  }
424
384
  export async function handleLogin(options = {}) {
425
385
  try {
426
386
  if (options.token) {
387
+ const apiUrl = getApiUrl();
388
+ const v3ServiceKey = await fetchV3ServiceKey(apiUrl, options.token);
389
+ await new AuthSessionService(new Config()).validateAndPersistToken(options.token, {
390
+ apiUrl,
391
+ v3ServiceKey: v3ServiceKey || null,
392
+ });
427
393
  const config = {
428
- apiUrl: getApiUrl(),
394
+ apiUrl,
429
395
  token: options.token,
396
+ ...(v3ServiceKey ? { v3ServiceKey } : {}),
430
397
  success: true,
431
398
  };
432
- const v3ServiceKey = await fetchV3ServiceKey(config.apiUrl, options.token);
433
- if (v3ServiceKey) {
434
- config.v3ServiceKey = v3ServiceKey;
435
- }
436
- saveAuthConfig(config);
437
399
  console.log(chalk.green('Logged in successfully with API token.'));
438
- process.exitCode = 0;
439
400
  return config;
440
401
  }
441
402
  let email = options.email?.trim();
@@ -488,24 +449,19 @@ export async function handleLogin(options = {}) {
488
449
  }
489
450
  catch { /* non-fatal */ }
490
451
  console.log(`API: ${config.apiUrl}`);
491
- process.exitCode = 0;
492
452
  return config;
493
453
  }
494
454
  catch (error) {
495
- console.error(chalk.red(`Login failed: ${humanMessage(error)}`));
496
- process.exitCode = 1;
497
- return undefined;
455
+ throw commandFailure(error, { code: 'LOGIN_FAILED', category: 'authentication', message: humanMessage(error) });
498
456
  }
499
457
  }
500
458
  export async function handleLogout(_options) {
501
459
  try {
502
460
  await logout();
503
461
  console.log(chalk.green('Logged out successfully.'));
504
- process.exitCode = 0;
505
462
  }
506
463
  catch (error) {
507
- console.error(chalk.red(`Logout failed: ${humanMessage(error)}`));
508
- process.exitCode = 1;
464
+ throw commandFailure(error, { code: 'LOGOUT_FAILED', message: humanMessage(error) });
509
465
  }
510
466
  }
511
467
  export async function statusAction() {
@@ -518,7 +474,6 @@ export async function statusAction() {
518
474
  const effectiveToken = sharedToken || legacyConfig.token || '';
519
475
  if (!effectiveToken) {
520
476
  console.log(chalk.yellow('Not logged in.'));
521
- process.exitCode = 0;
522
477
  return;
523
478
  }
524
479
  const authProbeHeaders = {
@@ -536,10 +491,9 @@ export async function statusAction() {
536
491
  // Network failures should not force logout status.
537
492
  }
538
493
  if (!authLooksValid) {
539
- console.log(chalk.yellow('Not logged in. Session expired or invalid.'));
540
- console.log(chalk.gray('Run: vigthoria login'));
541
- process.exitCode = 1;
542
- return;
494
+ throw new CliCommandError('Not logged in. Session expired or invalid. Run: vigthoria login', {
495
+ code: 'AUTH_SESSION_INVALID', category: 'authentication',
496
+ });
543
497
  }
544
498
  let user = legacyConfig.user;
545
499
  try {
@@ -581,11 +535,9 @@ export async function statusAction() {
581
535
  console.log(`Coder API: ${coderStatus}`);
582
536
  console.log(`Models API: ${modelsStatus}`);
583
537
  console.log(`API: ${effectiveApiUrl}`);
584
- process.exitCode = 0;
585
538
  }
586
539
  catch (error) {
587
- console.error(chalk.red(`Unable to read status: ${humanMessage(error)}`));
588
- process.exitCode = 1;
540
+ throw commandFailure(error, { code: 'AUTH_STATUS_FAILED', message: humanMessage(error) });
589
541
  }
590
542
  }
591
543
  export function registerAuthCommands(program) {
@@ -614,16 +566,13 @@ export function registerAuthCommands(program) {
614
566
  const user = await whoami();
615
567
  if (!user) {
616
568
  console.log(chalk.yellow('Not logged in.'));
617
- process.exitCode = 0;
618
569
  return;
619
570
  }
620
571
  console.log(chalk.green('Logged in.'));
621
572
  console.log(user.email || user.name || user.id || 'Authenticated user');
622
- process.exitCode = 0;
623
573
  }
624
574
  catch (error) {
625
- console.error(chalk.red(`Unable to fetch account: ${humanMessage(error)}`));
626
- process.exitCode = 1;
575
+ throw commandFailure(error, { code: 'AUTH_WHOAMI_FAILED', message: humanMessage(error) });
627
576
  }
628
577
  });
629
578
  }
@@ -8,8 +8,6 @@ interface BackgroundOptions {
8
8
  model?: string;
9
9
  }
10
10
  export declare class BackgroundCommand {
11
- private config;
12
- private logger;
13
11
  private api;
14
12
  constructor(config: Config, logger: Logger);
15
13
  start(promptParts: string[], options?: BackgroundOptions): Promise<void>;