vigthoria-cli 1.13.20 → 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 +63 -128
  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 +3 -42
  12. package/dist/commands/chat.js +378 -1123
  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 +23 -29
  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 -1589
  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 +44 -266
  71. package/dist/utils/api.js +647 -3180
  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 +220 -94
  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 +5 -6
  106. package/dist/utils/fastAgentRouter.js +17 -25
  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 +17 -3
  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 +85 -142
  167. package/install.sh +75 -80
  168. package/package.json +43 -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 -174
  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
@@ -9,6 +9,9 @@
9
9
  import { createHash } from 'node:crypto';
10
10
  import fs from 'node:fs';
11
11
  import path from 'node:path';
12
+ import { isSensitivePath } from './secret-policy.js';
13
+ import { DurableJsonStore } from './durable-json.js';
14
+ import { resolveWorkspacePath } from './workspace-boundary.js';
12
15
  const CACHE_SUBDIR = '.vigthoria';
13
16
  const CACHE_FILENAME = 'file-hashes.json';
14
17
  function getCachePath(workspacePath) {
@@ -24,20 +27,37 @@ function hashFile(absPath) {
24
27
  }
25
28
  function loadCache(workspacePath) {
26
29
  try {
27
- return JSON.parse(fs.readFileSync(getCachePath(workspacePath), 'utf-8'));
30
+ return cacheStore(workspacePath).read();
28
31
  }
29
32
  catch {
30
33
  return {};
31
34
  }
32
35
  }
33
36
  function persistCache(workspacePath, hashes) {
37
+ cacheStore(workspacePath).update((current) => ({ ...current, ...hashes }));
38
+ }
39
+ function cacheStore(workspacePath) {
40
+ const isHashMap = (value) => Boolean(value && typeof value === 'object' && !Array.isArray(value)
41
+ && Object.entries(value).every(([key, hash]) => (Boolean(key) && typeof hash === 'string' && /^[a-f0-9]{64}$/i.test(hash))));
42
+ return new DurableJsonStore({
43
+ filePath: getCachePath(workspacePath),
44
+ schemaVersion: 1,
45
+ defaultValue: () => ({}),
46
+ validate: isHashMap,
47
+ migrate: (value) => isHashMap(value) ? value : {},
48
+ });
49
+ }
50
+ function resolveTrackedFile(workspacePath, candidate) {
34
51
  try {
35
- const cacheDir = path.join(workspacePath, CACHE_SUBDIR);
36
- if (!fs.existsSync(cacheDir))
37
- fs.mkdirSync(cacheDir, { recursive: true });
38
- fs.writeFileSync(getCachePath(workspacePath), JSON.stringify(hashes, null, 2), 'utf-8');
52
+ const absolutePath = resolveWorkspacePath(workspacePath, candidate, { allowAbsoluteInsideRoot: true });
53
+ const key = path.relative(fs.realpathSync(workspacePath), absolutePath).replace(/\\/g, '/');
54
+ if (!key || key.startsWith('../') || isSensitivePath(key))
55
+ return null;
56
+ return { absolutePath, key };
57
+ }
58
+ catch {
59
+ return null;
39
60
  }
40
- catch { /* non-fatal */ }
41
61
  }
42
62
  /**
43
63
  * Partition file paths into changed and unchanged sets.
@@ -50,17 +70,18 @@ export function getChangedFiles(workspacePath, relativePaths) {
50
70
  const changed = [];
51
71
  const unchanged = [];
52
72
  for (const rel of relativePaths) {
53
- const abs = path.isAbsolute(rel) ? rel : path.join(workspacePath, rel);
54
- const key = path.relative(workspacePath, abs).replace(/\\/g, '/');
55
- const currentHash = hashFile(abs);
56
- if (!currentHash || cache[key] !== currentHash) {
73
+ const tracked = resolveTrackedFile(workspacePath, rel);
74
+ if (!tracked)
75
+ continue;
76
+ const currentHash = hashFile(tracked.absolutePath);
77
+ if (!currentHash || cache[tracked.key] !== currentHash) {
57
78
  changed.push(rel);
58
79
  }
59
80
  else {
60
81
  unchanged.push(rel);
61
82
  }
62
83
  }
63
- return { changed, unchanged, total: relativePaths.length };
84
+ return { changed, unchanged, total: changed.length + unchanged.length };
64
85
  }
65
86
  /**
66
87
  * Write current hashes for the given files into the persistent cache.
@@ -71,11 +92,12 @@ export function updateWorkspaceCache(workspacePath, relativePaths) {
71
92
  return;
72
93
  const cache = loadCache(workspacePath);
73
94
  for (const rel of relativePaths) {
74
- const abs = path.isAbsolute(rel) ? rel : path.join(workspacePath, rel);
75
- const key = path.relative(workspacePath, abs).replace(/\\/g, '/');
76
- const hash = hashFile(abs);
95
+ const tracked = resolveTrackedFile(workspacePath, rel);
96
+ if (!tracked)
97
+ continue;
98
+ const hash = hashFile(tracked.absolutePath);
77
99
  if (hash)
78
- cache[key] = hash;
100
+ cache[tracked.key] = hash;
79
101
  }
80
102
  persistCache(workspacePath, cache);
81
103
  }
@@ -22,7 +22,10 @@ import fs from 'fs';
22
22
  import path from 'path';
23
23
  import WebSocket from 'ws';
24
24
  import { Logger } from './logger.js';
25
- import { joinV3WorkspacePath, normalizeV3WorkspaceRelativePath } from './v3-workspace-path.js';
25
+ import { normalizeV3WorkspaceRelativePath } from './v3-workspace-path.js';
26
+ import { assertTrustedWebSocketEndpoint } from './network-policy.js';
27
+ import { resolveWorkspacePath } from './workspace-boundary.js';
28
+ import { containsHighConfidenceSecret, isSensitivePath } from './secret-policy.js';
26
29
  const logger = new Logger();
27
30
  logger.setVerbose(!!process.env.VIGTHORIA_DEBUG);
28
31
  // Files/dirs to ignore in the watcher
@@ -36,6 +39,12 @@ const IGNORE_PATTERNS = [
36
39
  '**/.next/**',
37
40
  '**/.cache/**',
38
41
  '**/.vigthoria/**',
42
+ '**/.env',
43
+ '**/.env.*',
44
+ '**/credentials*',
45
+ '**/secrets*',
46
+ '**/*.pem',
47
+ '**/*.key',
39
48
  '**/coverage/**',
40
49
  '**/*.pyc',
41
50
  // CRITICAL: Windows AppData & system directories (prevents EPERM errors)
@@ -80,10 +89,15 @@ export function applyFileMutation(event, workspaceRoot) {
80
89
  logger.warn(`Refusing to apply mutation with invalid path: ${event.path}`);
81
90
  return false;
82
91
  }
83
- const absPath = joinV3WorkspacePath(workspaceRoot, relativePath);
84
- const resolvedRoot = path.resolve(workspaceRoot);
85
- // Safety: ensure the resolved path is within the workspace
86
- if (!absPath.startsWith(resolvedRoot + path.sep) && absPath !== resolvedRoot) {
92
+ if (isSensitivePath(relativePath)) {
93
+ logger.warn(`Refusing to apply mutation to sensitive path: ${path.basename(relativePath)}`);
94
+ return false;
95
+ }
96
+ let absPath;
97
+ try {
98
+ absPath = resolveWorkspacePath(workspaceRoot, relativePath, { allowMissing: event.action !== 'delete' });
99
+ }
100
+ catch {
87
101
  logger.warn(`Refusing to apply mutation outside workspace: ${event.path}`);
88
102
  return false;
89
103
  }
@@ -115,7 +129,7 @@ export class WorkspaceWatcher {
115
129
  onFileChange;
116
130
  _ready = false;
117
131
  constructor(options) {
118
- this.workspaceRoot = path.resolve(options.workspaceRoot);
132
+ this.workspaceRoot = fs.realpathSync(path.resolve(options.workspaceRoot));
119
133
  this.onFileChange = options.onFileChange;
120
134
  }
121
135
  start() {
@@ -132,6 +146,7 @@ export class WorkspaceWatcher {
132
146
  interval: 100,
133
147
  binaryInterval: 300,
134
148
  depth: 50, // Limit traversal depth
149
+ followSymlinks: false,
135
150
  });
136
151
  this.watcher
137
152
  .on('ready', () => {
@@ -183,9 +198,16 @@ export class WorkspaceWatcher {
183
198
  const ext = path.extname(filePath).toLowerCase();
184
199
  if (BINARY_EXTENSIONS.has(ext))
185
200
  return;
186
- const relativePath = path.relative(this.workspaceRoot, filePath);
187
- if (relativePath.startsWith('..'))
188
- return; // safety
201
+ const relativePath = path.relative(this.workspaceRoot, filePath).replace(/\\/g, '/');
202
+ if (isSensitivePath(relativePath))
203
+ return;
204
+ try {
205
+ resolveWorkspacePath(this.workspaceRoot, relativePath, { allowMissing: action === 'delete' });
206
+ }
207
+ catch {
208
+ logger.warn(`Refusing to synchronize path outside workspace: ${relativePath}`);
209
+ return;
210
+ }
189
211
  if (action === 'delete') {
190
212
  this.onFileChange?.(relativePath, null, 'delete');
191
213
  return;
@@ -195,6 +217,8 @@ export class WorkspaceWatcher {
195
217
  if (stat.size > MAX_SYNC_FILE_BYTES)
196
218
  return;
197
219
  const content = fs.readFileSync(filePath, 'utf8');
220
+ if (containsHighConfidenceSecret(content))
221
+ return;
198
222
  this.onFileChange?.(relativePath, content, 'write');
199
223
  }
200
224
  catch {
@@ -216,7 +240,8 @@ export class WorkspaceWSClient {
216
240
  if (this.ws)
217
241
  return;
218
242
  const url = `${this.opts.serverUrl}/ws/workspace`;
219
- this.ws = new WebSocket(url);
243
+ assertTrustedWebSocketEndpoint(url, 'v3', Boolean(this.opts.token));
244
+ this.ws = new WebSocket(url, { handshakeTimeout: 10_000 });
220
245
  this.ws.on('open', () => {
221
246
  this._connected = true;
222
247
  this._send({
package/install.ps1 CHANGED
@@ -8,26 +8,44 @@ $ErrorActionPreference = "Stop"
8
8
  $CLI_VERSION = "1.13.20"
9
9
  $INSTALL_DIR = "$env:USERPROFILE\.vigthoria"
10
10
  $NPM_PACKAGE = "vigthoria-cli"
11
- $GIT_PACKAGE_URL = "git+https://market.vigthoria.io/vigthoria/vigthoria-cli.git"
12
11
  $MANIFEST_URL = "https://extension.vigthoria.io/downloads/manifest.json"
13
12
  $HOSTED_TARBALL_URL = "https://extension.vigthoria.io/downloads/vigthoria-cli-$CLI_VERSION.tgz"
13
+ $HOSTED_TARBALL_SHA256 = ""
14
+ $RELEASE_RESOLVER = Join-Path $PSScriptRoot "scripts\release\resolve-release-manifest.mjs"
15
+ $RELEASE_INSTALLER = Join-Path $PSScriptRoot "scripts\release\install-release.mjs"
16
+
17
+ function Assert-TrustedReleaseUrl([string]$Value) {
18
+ $allowed = @(
19
+ "extension.vigthoria.io", "landing.vigthoria.io", "cli.vigthoria.io",
20
+ "coder.vigthoria.io", "market.vigthoria.io", "registry.npmjs.org"
21
+ )
22
+ try { $uri = [Uri]$Value } catch { throw "Invalid release URL" }
23
+ if ($uri.Scheme -ne "https" -or -not $uri.IsDefaultPort -or $uri.UserInfo -or $allowed -notcontains $uri.DnsSafeHost.ToLowerInvariant() -or $Value.Contains("\")) {
24
+ throw "Release URL must use HTTPS on an exact approved host and port"
25
+ }
26
+ return $uri.AbsoluteUri
27
+ }
14
28
 
15
29
  function Resolve-ReleaseManifest {
30
+ # The hosted path is enabled only when the packaged shared policy verifier
31
+ # can validate strict metadata and the detached Ed25519 signature.
32
+ if (-not (Test-Path $RELEASE_RESOLVER)) { return }
16
33
  try {
17
- $manifest = Invoke-WebRequest -Uri $MANIFEST_URL -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop |
18
- Select-Object -ExpandProperty Content |
19
- ConvertFrom-Json
20
- $stable = $manifest.channels.stable
21
- if ($stable.version) {
22
- $script:CLI_VERSION = [string]$stable.version
34
+ $trustedManifest = Assert-TrustedReleaseUrl $MANIFEST_URL
35
+ $resolvedJson = & node $RELEASE_RESOLVER $trustedManifest stable
36
+ if ($LASTEXITCODE -ne 0) { throw "release resolver exited with status $LASTEXITCODE" }
37
+ $resolved = $resolvedJson | ConvertFrom-Json
38
+ if ($resolved.version) {
39
+ $script:CLI_VERSION = [string]$resolved.version
23
40
  }
24
- if ($stable.url) {
25
- $script:HOSTED_TARBALL_URL = [string]$stable.url
26
- } elseif ($stable.version) {
27
- $script:HOSTED_TARBALL_URL = "https://extension.vigthoria.io/downloads/vigthoria-cli-$($stable.version).tgz"
41
+ if ($resolved.url) {
42
+ $script:HOSTED_TARBALL_URL = Assert-TrustedReleaseUrl ([string]$resolved.url)
43
+ }
44
+ if ($resolved.sha256 -and ([string]$resolved.sha256) -match '^[a-fA-F0-9]{64}$') {
45
+ $script:HOSTED_TARBALL_SHA256 = ([string]$resolved.sha256).ToLowerInvariant()
28
46
  }
29
47
  } catch {
30
- Write-Host "Release manifest unavailable, using bundled fallback $CLI_VERSION" -ForegroundColor Yellow
48
+ Write-Host "Signed release manifest unavailable, using exact pinned fallback $CLI_VERSION" -ForegroundColor Yellow
31
49
  }
32
50
  }
33
51
 
@@ -48,7 +66,8 @@ function Test-NetworkConnectivity {
48
66
  Write-Host "[CHECK] Checking network connectivity..." -ForegroundColor Yellow
49
67
 
50
68
  try {
51
- $response = Invoke-WebRequest -Uri $HOSTED_TARBALL_URL -Method Head -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop
69
+ $trustedTarball = Assert-TrustedReleaseUrl $HOSTED_TARBALL_URL
70
+ $response = Invoke-WebRequest -Uri $trustedTarball -Method Head -UseBasicParsing -MaximumRedirection 0 -TimeoutSec 10 -ErrorAction Stop
52
71
  Write-Host "[OK] Hosted CLI package is reachable" -ForegroundColor Green
53
72
  return $true
54
73
  }
@@ -57,7 +76,7 @@ function Test-NetworkConnectivity {
57
76
  }
58
77
 
59
78
  try {
60
- $response = Invoke-WebRequest -Uri "https://registry.npmjs.org/vigthoria-cli" -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop
79
+ $response = Invoke-WebRequest -Uri "https://registry.npmjs.org/vigthoria-cli" -UseBasicParsing -MaximumRedirection 0 -TimeoutSec 10 -ErrorAction Stop
61
80
  Write-Host "[OK] npm registry is reachable" -ForegroundColor Green
62
81
  return $true
63
82
  }
@@ -114,31 +133,44 @@ function Install-VigthoriaCLI-NPM {
114
133
  Write-Host ""
115
134
  Write-Host "[INSTALL] Installing Vigthoria CLI..." -ForegroundColor Cyan
116
135
 
117
- try {
118
- npm install -g $HOSTED_TARBALL_URL
119
- Write-Host "[OK] Vigthoria CLI installed successfully!" -ForegroundColor Green
120
- return $true
121
- }
122
- catch {
123
- Write-Host "[WARN] Hosted package install failed, trying npm registry..." -ForegroundColor Yellow
136
+ if ($HOSTED_TARBALL_SHA256) {
137
+ $releaseTemp = Join-Path ([IO.Path]::GetTempPath()) ("vigthoria-cli-install-" + [Guid]::NewGuid().ToString("N"))
138
+ $releaseArchive = Join-Path $releaseTemp "candidate.tgz"
139
+ try {
140
+ New-Item -ItemType Directory -Path $releaseTemp -Force | Out-Null
141
+ $trustedTarball = Assert-TrustedReleaseUrl $HOSTED_TARBALL_URL
142
+ Invoke-WebRequest -Uri $trustedTarball -OutFile $releaseArchive -UseBasicParsing -MaximumRedirection 0 -TimeoutSec 60 -ErrorAction Stop
143
+ $actualSha256 = (Get-FileHash -Path $releaseArchive -Algorithm SHA256).Hash.ToLowerInvariant()
144
+ if ($actualSha256 -ne $HOSTED_TARBALL_SHA256) { throw "Release checksum mismatch" }
145
+ if (-not (Test-Path $RELEASE_INSTALLER)) { throw "Transactional release installer is unavailable" }
146
+ & node $RELEASE_INSTALLER $releaseArchive $CLI_VERSION
147
+ if ($LASTEXITCODE -ne 0) { throw "transactional installer exited with status $LASTEXITCODE" }
148
+ Write-Host "[OK] Vigthoria CLI installed from the checksum-verified release!" -ForegroundColor Green
149
+ return $true
150
+ }
151
+ catch {
152
+ Write-Host "[WARN] Verified hosted package install failed, trying npm registry: $_" -ForegroundColor Yellow
153
+ }
154
+ finally {
155
+ if (Test-Path $releaseTemp) { Remove-Item -Recurse -Force $releaseTemp }
156
+ }
157
+ } else {
158
+ Write-Host "[WARN] Hosted package has no trusted manifest SHA-256; skipping direct URL installation." -ForegroundColor Yellow
124
159
  }
125
160
 
126
161
  try {
127
- npm install -g vigthoria-cli
162
+ if (Test-Path $RELEASE_INSTALLER) {
163
+ & node $RELEASE_INSTALLER "$NPM_PACKAGE@$CLI_VERSION" $CLI_VERSION
164
+ if ($LASTEXITCODE -ne 0) { throw "transactional installer exited with status $LASTEXITCODE" }
165
+ } else {
166
+ npm install -g --registry=https://registry.npmjs.org/ "$NPM_PACKAGE@$CLI_VERSION"
167
+ if ($LASTEXITCODE -ne 0) { throw "npm exited with status $LASTEXITCODE" }
168
+ }
128
169
  Write-Host "[OK] Vigthoria CLI installed successfully!" -ForegroundColor Green
129
170
  return $true
130
171
  }
131
172
  catch {
132
- Write-Host "[WARN] npm registry install failed, trying git package..." -ForegroundColor Yellow
133
- }
134
-
135
- try {
136
- npm install -g $GIT_PACKAGE_URL
137
- Write-Host "[OK] Vigthoria CLI installed successfully from git package!" -ForegroundColor Green
138
- return $true
139
- }
140
- catch {
141
- Write-Host "[ERROR] Install failed (hosted package + npm registry + git package): $_" -ForegroundColor Red
173
+ Write-Host "[ERROR] Install failed: no checksum-verified artifact or exact npm release $CLI_VERSION is available: $_" -ForegroundColor Red
142
174
  return $false
143
175
  }
144
176
  }
@@ -146,58 +178,18 @@ function Install-VigthoriaCLI-NPM {
146
178
  function Install-VigthoriaCLI-Direct {
147
179
  Write-Host ""
148
180
  Write-Host "[INSTALL] Installing Vigthoria CLI (Direct Download)..." -ForegroundColor Cyan
149
-
150
- # Create install directory
151
- if (-not (Test-Path $INSTALL_DIR)) {
152
- New-Item -ItemType Directory -Path $INSTALL_DIR -Force | Out-Null
153
- }
154
-
155
- $tarballUrl = $HOSTED_TARBALL_URL
156
- $tarballPath = "$INSTALL_DIR\vigthoria-cli.tgz"
157
- $extractPath = "$INSTALL_DIR\cli"
158
-
181
+ $releaseTemp = Join-Path ([IO.Path]::GetTempPath()) ("vigthoria-cli-direct-" + [Guid]::NewGuid().ToString("N"))
182
+ $tarballPath = Join-Path $releaseTemp "candidate.tgz"
159
183
  try {
160
- # Download tarball
161
- Write-Host " Downloading from $tarballUrl..." -ForegroundColor Gray
162
- Invoke-WebRequest -Uri $tarballUrl -OutFile $tarballPath -UseBasicParsing
163
-
164
- # Extract
165
- Write-Host " Extracting..." -ForegroundColor Gray
166
- if (Test-Path $extractPath) {
167
- Remove-Item -Recurse -Force $extractPath
168
- }
169
- New-Item -ItemType Directory -Path $extractPath -Force | Out-Null
170
-
171
- # Use tar to extract (available in Windows 10+)
172
- tar -xzf $tarballPath -C $extractPath
173
-
174
- # The tarball extracts to a 'package' folder
175
- $packagePath = "$extractPath\package"
176
-
177
- # Install dependencies
178
- Write-Host " Installing dependencies..." -ForegroundColor Gray
179
- Push-Location $packagePath
180
- npm install --production
181
- Pop-Location
182
-
183
- # Create batch file wrapper
184
- $batContent = @"
185
- @echo off
186
- node "$packagePath\dist\index.js" %*
187
- "@
188
- Set-Content -Path "$INSTALL_DIR\vigthoria.bat" -Value $batContent
189
- Set-Content -Path "$INSTALL_DIR\vig.bat" -Value $batContent
190
-
191
- # Add to PATH if not already there
192
- $userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
193
- if ($userPath -notlike "*$INSTALL_DIR*") {
194
- [Environment]::SetEnvironmentVariable("PATH", "$userPath;$INSTALL_DIR", "User")
195
- Write-Host " Added $INSTALL_DIR to PATH" -ForegroundColor Gray
196
- }
197
-
198
- # Cleanup
199
- Remove-Item $tarballPath -Force
200
-
184
+ if (-not $HOSTED_TARBALL_SHA256) { throw "Direct installation requires a signed manifest SHA-256" }
185
+ if (-not (Test-Path $RELEASE_INSTALLER)) { throw "Transactional release installer is unavailable" }
186
+ New-Item -ItemType Directory -Path $releaseTemp -ErrorAction Stop | Out-Null
187
+ $tarballUrl = Assert-TrustedReleaseUrl $HOSTED_TARBALL_URL
188
+ Invoke-WebRequest -Uri $tarballUrl -OutFile $tarballPath -UseBasicParsing -MaximumRedirection 0 -TimeoutSec 60 -ErrorAction Stop
189
+ $actualSha256 = (Get-FileHash -Path $tarballPath -Algorithm SHA256).Hash.ToLowerInvariant()
190
+ if ($actualSha256 -ne $HOSTED_TARBALL_SHA256) { throw "Release checksum mismatch" }
191
+ & node $RELEASE_INSTALLER $tarballPath $CLI_VERSION
192
+ if ($LASTEXITCODE -ne 0) { throw "transactional installer exited with status $LASTEXITCODE" }
201
193
  Write-Host "[OK] Vigthoria CLI installed successfully!" -ForegroundColor Green
202
194
  return $true
203
195
  }
@@ -205,51 +197,8 @@ node "$packagePath\dist\index.js" %*
205
197
  Write-Host "[ERROR] Direct install failed: $_" -ForegroundColor Red
206
198
  return $false
207
199
  }
208
- }
209
-
210
- function Install-VigthoriaCLI-GitHub {
211
- Write-Host ""
212
- Write-Host "[INSTALL] Installing Vigthoria CLI (from Vigthoria Market)..." -ForegroundColor Cyan
213
-
214
- # Create install directory
215
- if (-not (Test-Path $INSTALL_DIR)) {
216
- New-Item -ItemType Directory -Path $INSTALL_DIR -Force | Out-Null
217
- }
218
-
219
- $clonePath = "$INSTALL_DIR\cli"
220
-
221
- try {
222
- # Check if git is available
223
- $gitVersion = git --version 2>$null
224
- if (-not $gitVersion) {
225
- Write-Host "[ERROR] Git is not installed" -ForegroundColor Red
226
- return $false
227
- }
228
-
229
- # Clone repository
230
- if (Test-Path $clonePath) {
231
- Remove-Item -Recurse -Force $clonePath
232
- }
233
-
234
- Write-Host " Cloning from Vigthoria Market..." -ForegroundColor Gray
235
- git clone --depth 1 https://market.vigthoria.io/vigthoria/vigthoria-cli.git $clonePath
236
-
237
- # Install and build
238
- Push-Location $clonePath
239
- Write-Host " Installing dependencies..." -ForegroundColor Gray
240
- npm install
241
- Write-Host " Building..." -ForegroundColor Gray
242
- npm run build
243
- Write-Host " Linking globally..." -ForegroundColor Gray
244
- npm link
245
- Pop-Location
246
-
247
- Write-Host "[OK] Vigthoria CLI installed successfully!" -ForegroundColor Green
248
- return $true
249
- }
250
- catch {
251
- Write-Host "[ERROR] Git install failed: $_" -ForegroundColor Red
252
- return $false
200
+ finally {
201
+ if (Test-Path $releaseTemp) { Remove-Item -Recurse -Force $releaseTemp }
253
202
  }
254
203
  }
255
204
 
@@ -268,17 +217,13 @@ function Show-NetworkTroubleshooting {
268
217
  Write-Host " npm config set proxy http://proxy.company.com:8080" -ForegroundColor Gray
269
218
  Write-Host " npm config set https-proxy http://proxy.company.com:8080" -ForegroundColor Gray
270
219
  Write-Host ""
271
- Write-Host "3. Try a different npm registry:" -ForegroundColor Cyan
272
- Write-Host " npm config set registry https://registry.npmmirror.com" -ForegroundColor Gray
273
- Write-Host ""
274
- Write-Host "4. Check Windows Firewall/Antivirus settings" -ForegroundColor Cyan
220
+ Write-Host "3. Check Windows Firewall/Antivirus settings" -ForegroundColor Cyan
275
221
  Write-Host ""
276
- Write-Host "5. Try using a VPN" -ForegroundColor Cyan
222
+ Write-Host "4. Try using a VPN" -ForegroundColor Cyan
277
223
  Write-Host ""
278
- Write-Host "6. Manual download alternatives:" -ForegroundColor Cyan
224
+ Write-Host "5. Manual download alternative:" -ForegroundColor Cyan
279
225
  Write-Host " Hosted package: $HOSTED_TARBALL_URL" -ForegroundColor Gray
280
226
  Write-Host " npm package: https://www.npmjs.com/package/vigthoria-cli" -ForegroundColor Gray
281
- Write-Host " git package: $GIT_PACKAGE_URL" -ForegroundColor Gray
282
227
  Write-Host ""
283
228
  }
284
229
 
@@ -325,21 +270,19 @@ if ($networkOk) {
325
270
  # Network failed or npm install failed
326
271
  Show-NetworkTroubleshooting
327
272
 
328
- Write-Host "Would you like to try alternative installation methods?" -ForegroundColor Yellow
329
- $choice = Read-Host "Enter 'y' to try direct download, 'g' for git clone, or 'n' to exit"
273
+ Write-Host "Would you like to try the checksum-verified direct download?" -ForegroundColor Yellow
274
+ $choice = Read-Host "Enter 'y' to try direct download or 'n' to exit"
330
275
 
331
276
  switch ($choice.ToLower()) {
332
277
  'y' {
333
278
  if (Install-VigthoriaCLI-Direct) {
334
279
  Show-Success
280
+ exit 0
335
281
  }
336
- }
337
- 'g' {
338
- if (Install-VigthoriaCLI-GitHub) {
339
- Show-Success
340
- }
282
+ exit 1
341
283
  }
342
284
  default {
343
285
  Write-Host "Installation cancelled." -ForegroundColor Gray
286
+ exit 1
344
287
  }
345
288
  }