vigthoria-cli 1.13.31 → 1.13.42
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 +11 -56
- package/SECURITY_HARDENING.md +2 -4
- package/dist/commands/auth.js +16 -14
- package/dist/commands/cancel.d.ts +1 -0
- package/dist/commands/cancel.js +21 -4
- package/dist/commands/chat.d.ts +13 -0
- package/dist/commands/chat.js +170 -14
- package/dist/commands/fork.js +8 -3
- package/dist/commands/history.js +8 -3
- package/dist/commands/legion.js +1 -1
- package/dist/commands/replay.js +8 -3
- package/dist/commands/update-registration.js +63 -206
- package/dist/commands/v4-menu.d.ts +0 -1
- package/dist/commands/v4-menu.js +8 -6
- package/dist/commands/v4-registration.js +0 -2
- package/dist/commands/v4.js +9 -4
- package/dist/commands/wallet.d.ts +1 -1
- package/dist/commands/wallet.js +3 -3
- package/dist/index.js +2 -3
- package/dist/utils/agent-stream-state.d.ts +7 -0
- package/dist/utils/agent-stream-state.js +31 -0
- package/dist/utils/agentRunOutcome.js +5 -1
- package/dist/utils/api.d.ts +21 -3
- package/dist/utils/api.js +379 -550
- package/dist/utils/frontend-preview-service.d.ts +12 -2
- package/dist/utils/frontend-preview-service.js +214 -24
- package/dist/utils/javascript-syntax.d.ts +19 -0
- package/dist/utils/javascript-syntax.js +35 -0
- package/dist/utils/localTestMode.js +1 -1
- package/dist/utils/mutation-journal.d.ts +10 -0
- package/dist/utils/mutation-journal.js +104 -76
- package/dist/utils/network-policy.js +7 -10
- package/dist/utils/preview-screenshot-adapter.d.ts +111 -1
- package/dist/utils/preview-screenshot-adapter.js +990 -7
- package/dist/utils/release-install.d.ts +0 -1
- package/dist/utils/release-install.js +44 -7
- package/dist/utils/requestIntent.d.ts +1 -1
- package/dist/utils/requestIntent.js +17 -3
- package/dist/utils/tools.js +21 -15
- package/dist/utils/update-policy.d.ts +0 -4
- package/dist/utils/update-policy.js +5 -9
- package/dist/utils/v3-agent-client.js +25 -11
- package/install.ps1 +30 -50
- package/install.sh +12 -23
- package/package.json +4 -2
- package/release-policy.json +1 -5
- package/scripts/release/LOCAL_MACHINE_USER_VERIFICATION.md +6 -7
- package/scripts/release/install-release.mjs +5 -4
- package/scripts/release/publish-cli-release.mjs +21 -8
- package/scripts/release/test-balanced-model-live.sh +4 -1
- package/scripts/release/validate-live-service-gates.sh +27 -5
|
@@ -8,39 +8,11 @@ import { CH } from '../utils/logger.js';
|
|
|
8
8
|
import { isOfflineMode, isUpdateCheckSuppressed, readCachedLatestVersion, writeCachedLatestVersion } from '../utils/cli-state.js';
|
|
9
9
|
import { guardedFetch } from '../utils/network-policy.js';
|
|
10
10
|
import { safeChildProcessEnv } from '../utils/secret-policy.js';
|
|
11
|
-
import { CliCommandError
|
|
12
|
-
import { compareSemanticVersions as compareVersions
|
|
11
|
+
import { CliCommandError } from '../utils/command-contract.js';
|
|
12
|
+
import { compareSemanticVersions as compareVersions } from '../utils/update-policy.js';
|
|
13
13
|
import { assertReleaseTransition, assertReleaseUrl, getReleasePolicy, parseReleaseManifestText, verifyReleaseSignature } from '../utils/release-policy.js';
|
|
14
14
|
import { installReleaseTransaction } from '../utils/release-install.js';
|
|
15
15
|
import { createRuntimeTempDirectory, removeRuntimeTempDirectory } from '../utils/runtime-temp.js';
|
|
16
|
-
function maxVersion(...versions) {
|
|
17
|
-
let best = null;
|
|
18
|
-
for (const version of versions) {
|
|
19
|
-
if (!version)
|
|
20
|
-
continue;
|
|
21
|
-
if (!best || compareVersions(version, best) > 0)
|
|
22
|
-
best = version;
|
|
23
|
-
}
|
|
24
|
-
return best;
|
|
25
|
-
}
|
|
26
|
-
async function fetchNpmLatestVersion() {
|
|
27
|
-
try {
|
|
28
|
-
// Hard 5s timeout: a slow/blocked path to the public npm registry must
|
|
29
|
-
// never hang the caller (e.g. the Workbench's blocking `vigthoria update`
|
|
30
|
-
// invocation) indefinitely.
|
|
31
|
-
const latest = execFileSyncProcess(process.platform === 'win32' ? 'npm.cmd' : 'npm', ['view', 'vigthoria-cli', 'version'], {
|
|
32
|
-
encoding: 'utf8',
|
|
33
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
34
|
-
windowsHide: true,
|
|
35
|
-
timeout: 5000,
|
|
36
|
-
env: safeChildProcessEnv(),
|
|
37
|
-
}).trim();
|
|
38
|
-
return latest || null;
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
16
|
const VIGTHORIA_DEFAULT_MANIFEST_URL = getReleasePolicy().origins.manifest;
|
|
45
17
|
const MAX_RELEASE_ARCHIVE_BYTES = 256 * 1024 * 1024;
|
|
46
18
|
async function readBoundedResponse(response, maximumBytes) {
|
|
@@ -114,15 +86,31 @@ function renderUpdateBanner(latestVersion, version) {
|
|
|
114
86
|
console.log(chalk.red.bold(`\n${CH.warnEmoji} SECURITY UPDATE AVAILABLE`));
|
|
115
87
|
console.log(chalk.red(` Version ${version} has security vulnerabilities.`));
|
|
116
88
|
console.log(chalk.yellow(` Please update to ${latestVersion} immediately:`));
|
|
117
|
-
console.log(chalk.white.bold('
|
|
89
|
+
console.log(chalk.white.bold(' vigthoria update\n'));
|
|
118
90
|
}
|
|
119
91
|
else {
|
|
120
92
|
console.log(chalk.yellow(`\n${CH.warnEmoji} Update available: ${version} -> ${latestVersion}`));
|
|
121
93
|
console.log(chalk.gray(' Run `vigthoria update` to install\n'));
|
|
122
94
|
}
|
|
123
95
|
}
|
|
124
|
-
|
|
125
|
-
|
|
96
|
+
async function fetchVerifiedManifestEntry(manifestUrl, channel) {
|
|
97
|
+
assertReleaseUrl(manifestUrl, 'manifest');
|
|
98
|
+
const response = await guardedFetch(manifestUrl, {
|
|
99
|
+
headers: { Accept: 'application/json' },
|
|
100
|
+
}, { audience: 'release', maxRedirects: getReleasePolicy().origins.maximumRedirects });
|
|
101
|
+
if (!response.ok)
|
|
102
|
+
throw new Error(`Manifest returned HTTP ${response.status}`);
|
|
103
|
+
const entry = parseReleaseManifestText(await response.text(), channel);
|
|
104
|
+
const signatureResponse = await guardedFetch(entry.signature.url, {
|
|
105
|
+
headers: { Accept: 'application/octet-stream' },
|
|
106
|
+
}, { audience: 'release', maxRedirects: getReleasePolicy().origins.maximumRedirects });
|
|
107
|
+
if (!signatureResponse.ok)
|
|
108
|
+
throw new Error(`Detached signature returned HTTP ${signatureResponse.status}`);
|
|
109
|
+
verifyReleaseSignature(channel, entry, await signatureResponse.text());
|
|
110
|
+
return entry;
|
|
111
|
+
}
|
|
112
|
+
// Check the signed extension manifest quietly on startup. Cached (24 h) so
|
|
113
|
+
// startup does not repeatedly reach the network. No registry fallback exists.
|
|
126
114
|
export async function checkForUpdatesQuietly(version) {
|
|
127
115
|
if (isUpdateCheckSuppressed()) {
|
|
128
116
|
return;
|
|
@@ -134,26 +122,15 @@ export async function checkForUpdatesQuietly(version) {
|
|
|
134
122
|
}
|
|
135
123
|
return;
|
|
136
124
|
}
|
|
137
|
-
// Cache miss: probe npm registry in the background, with a hard 5 s
|
|
138
|
-
// timeout. Result is cached for 24 h so subsequent runs are instant.
|
|
139
125
|
try {
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
windowsHide: true,
|
|
145
|
-
env: safeChildProcessEnv(),
|
|
146
|
-
}).trim();
|
|
147
|
-
if (!npmVersion) {
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
writeCachedLatestVersion(npmVersion);
|
|
151
|
-
if (compareVersions(npmVersion, version) > 0) {
|
|
152
|
-
renderUpdateBanner(npmVersion, version);
|
|
126
|
+
const entry = await fetchVerifiedManifestEntry(VIGTHORIA_DEFAULT_MANIFEST_URL, 'stable');
|
|
127
|
+
writeCachedLatestVersion(entry.version);
|
|
128
|
+
if (compareVersions(entry.version, version) > 0) {
|
|
129
|
+
renderUpdateBanner(entry.version, version);
|
|
153
130
|
}
|
|
154
131
|
}
|
|
155
132
|
catch {
|
|
156
|
-
// Network
|
|
133
|
+
// Network/signature failure never blocks startup and is never cached.
|
|
157
134
|
}
|
|
158
135
|
}
|
|
159
136
|
export function registerUpdateCommand(program, version) {
|
|
@@ -163,72 +140,13 @@ export function registerUpdateCommand(program, version) {
|
|
|
163
140
|
.alias('upgrade')
|
|
164
141
|
.description('Check for updates and upgrade Vigthoria CLI. Default manifest: https://extension.vigthoria.io/downloads/manifest.json')
|
|
165
142
|
.option('-c, --check', 'Only check for updates, don\'t install')
|
|
166
|
-
.option('-f, --from <target>', 'Install from exact vigthoria-cli@version, local .tgz, or trusted HTTPS .tgz')
|
|
167
|
-
.option('--sha256 <hex>', 'Required SHA-256 for HTTPS archives; optional verification for local archives')
|
|
168
143
|
.option('-m, --manifest <url>', 'Update manifest URL for server-driven releases')
|
|
169
144
|
.option('--channel <name>', 'Release channel to use from manifest (default: stable)', 'stable')
|
|
170
145
|
.option('--allow-downgrade', 'Allow installing an older version from custom update source')
|
|
171
146
|
.action(async (options) => {
|
|
172
|
-
const updateTarget = typeof options.from === 'string' ? options.from.trim() : '';
|
|
173
147
|
const manifestUrl = typeof options.manifest === 'string' ? options.manifest.trim() : VIGTHORIA_DEFAULT_MANIFEST_URL.trim();
|
|
174
148
|
const channel = typeof options.channel === 'string' ? options.channel.trim() : 'stable';
|
|
175
149
|
const allowDowngrade = !!options.allowDowngrade;
|
|
176
|
-
if (updateTarget) {
|
|
177
|
-
const source = resolveUpdateSource(updateTarget, { sha256: options.sha256 });
|
|
178
|
-
try {
|
|
179
|
-
if (options.check) {
|
|
180
|
-
console.log(chalk.cyan(`Update source validated (${source.kind}): ${updateTarget}`));
|
|
181
|
-
console.log(chalk.gray('Run `vigthoria update --from <target>` to install from this source'));
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
console.log(chalk.cyan(`Installing update from ${updateTarget}...`));
|
|
185
|
-
let installTarget = source.kind === 'remote' ? '' : source.installTarget;
|
|
186
|
-
let updateTempDirectory = null;
|
|
187
|
-
try {
|
|
188
|
-
if (source.kind === 'remote') {
|
|
189
|
-
updateTempDirectory = createRuntimeTempDirectory('update-', 320 * 1024 * 1024);
|
|
190
|
-
installTarget = path.join(updateTempDirectory, 'candidate.tgz');
|
|
191
|
-
await downloadFile(source.downloadUrl, installTarget);
|
|
192
|
-
}
|
|
193
|
-
if (source.expectedSha256) {
|
|
194
|
-
const actual = sha256File(installTarget).toLowerCase();
|
|
195
|
-
if (actual !== source.expectedSha256) {
|
|
196
|
-
throw new CliCommandError('Update archive checksum verification failed.', {
|
|
197
|
-
code: 'UPDATE_CHECKSUM_MISMATCH',
|
|
198
|
-
details: { expected: source.expectedSha256, actual },
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
const identity = source.kind === 'registry'
|
|
203
|
-
? { name: getReleasePolicy().packageName, version: installTarget.slice(installTarget.lastIndexOf('@') + 1) }
|
|
204
|
-
: inspectReleaseArchive(installTarget);
|
|
205
|
-
if (identity.name !== getReleasePolicy().packageName) {
|
|
206
|
-
throw new CliCommandError('Update archive package identity is not vigthoria-cli.', { code: 'UPDATE_PACKAGE_IDENTITY_INVALID' });
|
|
207
|
-
}
|
|
208
|
-
if (!allowDowngrade && compareVersions(identity.version, version) < 0) {
|
|
209
|
-
throw new CliCommandError(`Downgrade from ${version} to ${identity.version} requires --allow-downgrade.`, { code: 'UPDATE_DOWNGRADE_PROHIBITED' });
|
|
210
|
-
}
|
|
211
|
-
const installed = installReleaseTransaction(installTarget, identity.version, {
|
|
212
|
-
...(source.kind === 'registry' ? { npmRegistry: 'https://registry.npmjs.org/' } : {}),
|
|
213
|
-
});
|
|
214
|
-
console.log(chalk.gray(`Versioned installation: ${installed.versionDirectory}`));
|
|
215
|
-
console.log(chalk.gray(`Active pointer: ${installed.currentLink}`));
|
|
216
|
-
}
|
|
217
|
-
finally {
|
|
218
|
-
if (updateTempDirectory)
|
|
219
|
-
removeRuntimeTempDirectory(updateTempDirectory);
|
|
220
|
-
}
|
|
221
|
-
console.log(chalk.green('Update installed successfully'));
|
|
222
|
-
console.log(chalk.gray('Please restart the CLI to use the new version'));
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
catch (error) {
|
|
226
|
-
throw commandFailure(error, {
|
|
227
|
-
code: 'UPDATE_INSTALL_FAILED',
|
|
228
|
-
message: `Failed to install the validated ${source.kind} update source.`,
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
150
|
if (isOfflineMode()) {
|
|
233
151
|
console.log(chalk.yellow('Offline mode (VIGTHORIA_OFFLINE=1): skipping update check.'));
|
|
234
152
|
return;
|
|
@@ -237,55 +155,25 @@ export function registerUpdateCommand(program, version) {
|
|
|
237
155
|
let manifestEntry = null;
|
|
238
156
|
if (manifestUrl) {
|
|
239
157
|
try {
|
|
240
|
-
assertReleaseUrl(manifestUrl, 'manifest');
|
|
241
158
|
console.log(chalk.cyan(`Checking manifest channel ${channel}...`));
|
|
242
|
-
|
|
243
|
-
headers: { Accept: 'application/json' },
|
|
244
|
-
}, { audience: 'release', maxRedirects: getReleasePolicy().origins.maximumRedirects });
|
|
245
|
-
if (!response.ok)
|
|
246
|
-
throw new Error(`Manifest returned HTTP ${response.status}`);
|
|
247
|
-
const manifestText = await response.text();
|
|
248
|
-
manifestEntry = parseReleaseManifestText(manifestText, channel);
|
|
249
|
-
const signatureResponse = await guardedFetch(manifestEntry.signature.url, {
|
|
250
|
-
headers: { Accept: 'application/octet-stream' },
|
|
251
|
-
}, { audience: 'release', maxRedirects: getReleasePolicy().origins.maximumRedirects });
|
|
252
|
-
if (!signatureResponse.ok)
|
|
253
|
-
throw new Error(`Detached signature returned HTTP ${signatureResponse.status}`);
|
|
254
|
-
verifyReleaseSignature(channel, manifestEntry, await signatureResponse.text());
|
|
159
|
+
manifestEntry = await fetchVerifiedManifestEntry(manifestUrl, channel);
|
|
255
160
|
console.log(chalk.gray(`Verified ${channel} manifest: version=${manifestEntry.version} package=${manifestEntry.packageName}@${manifestEntry.packageVersion} size=${manifestEntry.size} sha256=${manifestEntry.sha256}`));
|
|
256
161
|
}
|
|
257
162
|
catch (error) {
|
|
258
163
|
if (error instanceof CliCommandError)
|
|
259
164
|
throw error;
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
// The release manifest (server-curated, sha256-verified) is the
|
|
265
|
-
// authoritative source of truth for this private CLI. Only fall back
|
|
266
|
-
// to probing the public npm registry when the manifest itself has no
|
|
267
|
-
// usable entry - never let an npm version number override a valid,
|
|
268
|
-
// verified manifest entry (npm install -g against the public registry
|
|
269
|
-
// is slower, unverified, and has previously caused the Workbench's
|
|
270
|
-
// update button to hang for minutes).
|
|
271
|
-
let npmVersion = null;
|
|
272
|
-
if (manifestEntry?.version && manifestEntry?.url) {
|
|
273
|
-
console.log(chalk.gray('Release manifest has a valid entry; skipping public npm registry lookup.'));
|
|
274
|
-
}
|
|
275
|
-
else {
|
|
276
|
-
console.log(chalk.cyan('Checking npm registry...'));
|
|
277
|
-
npmVersion = await fetchNpmLatestVersion();
|
|
278
|
-
if (!npmVersion) {
|
|
279
|
-
console.log(chalk.yellow('Could not read latest version from npm registry.'));
|
|
165
|
+
throw new CliCommandError(`Signed extension release manifest is unavailable: ${error.message}`, {
|
|
166
|
+
code: 'UPDATE_MANIFEST_UNAVAILABLE',
|
|
167
|
+
category: 'network',
|
|
168
|
+
});
|
|
280
169
|
}
|
|
281
170
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
code: 'UPDATE_VERSION_UNAVAILABLE',
|
|
171
|
+
if (!manifestEntry)
|
|
172
|
+
throw new CliCommandError('Signed extension release manifest is unavailable.', {
|
|
173
|
+
code: 'UPDATE_MANIFEST_UNAVAILABLE',
|
|
286
174
|
category: 'network',
|
|
287
175
|
});
|
|
288
|
-
|
|
176
|
+
const effectiveLatest = manifestEntry.version;
|
|
289
177
|
if (!allowDowngrade && compareVersions(effectiveLatest, currentVersion) <= 0) {
|
|
290
178
|
console.log(chalk.green(`You are running the latest version (${currentVersion})`));
|
|
291
179
|
return;
|
|
@@ -298,72 +186,41 @@ export function registerUpdateCommand(program, version) {
|
|
|
298
186
|
console.log(chalk.gray('Run `vigthoria update` to install the update'));
|
|
299
187
|
return;
|
|
300
188
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (actual !== expected) {
|
|
315
|
-
throw new CliCommandError('Release checksum verification failed.', {
|
|
316
|
-
code: 'UPDATE_CHECKSUM_MISMATCH',
|
|
317
|
-
details: { expected, actual },
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
if (fs.statSync(tmpFile).size !== manifestEntry.size) {
|
|
321
|
-
throw new CliCommandError('Release archive size does not match signed manifest metadata.', {
|
|
322
|
-
code: 'UPDATE_SIZE_MISMATCH',
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
console.log(chalk.green('Checksum verification passed'));
|
|
326
|
-
console.log(chalk.cyan('Installing update...'));
|
|
327
|
-
const identity = inspectReleaseArchive(tmpFile);
|
|
328
|
-
if (identity.name !== manifestEntry.packageName || identity.version !== manifestEntry.packageVersion) {
|
|
329
|
-
throw new CliCommandError('Release archive package metadata conflicts with signed manifest identity.', {
|
|
330
|
-
code: 'UPDATE_PACKAGE_IDENTITY_INVALID',
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
const installed = installReleaseTransaction(tmpFile, manifestEntry.version);
|
|
334
|
-
console.log(chalk.gray(`Versioned installation: ${installed.versionDirectory}`));
|
|
335
|
-
console.log(chalk.green(`Updated to version ${manifestEntry.version}`));
|
|
336
|
-
console.log(chalk.gray('Please restart the CLI to use the new version'));
|
|
337
|
-
return;
|
|
189
|
+
assertReleaseTransition(currentVersion, manifestEntry, channel, allowDowngrade);
|
|
190
|
+
const updateTempDirectory = createRuntimeTempDirectory('update-', 320 * 1024 * 1024);
|
|
191
|
+
const tmpFile = path.join(updateTempDirectory, 'candidate.tgz');
|
|
192
|
+
try {
|
|
193
|
+
console.log(chalk.cyan(`Downloading release package (${manifestEntry.version})...`));
|
|
194
|
+
await downloadFile(manifestEntry.url, tmpFile);
|
|
195
|
+
const expected = String(manifestEntry.sha256).toLowerCase();
|
|
196
|
+
const actual = sha256File(tmpFile).toLowerCase();
|
|
197
|
+
if (actual !== expected) {
|
|
198
|
+
throw new CliCommandError('Release checksum verification failed.', {
|
|
199
|
+
code: 'UPDATE_CHECKSUM_MISMATCH',
|
|
200
|
+
details: { expected, actual },
|
|
201
|
+
});
|
|
338
202
|
}
|
|
339
|
-
|
|
340
|
-
|
|
203
|
+
if (fs.statSync(tmpFile).size !== manifestEntry.size) {
|
|
204
|
+
throw new CliCommandError('Release archive size does not match signed manifest metadata.', {
|
|
205
|
+
code: 'UPDATE_SIZE_MISMATCH',
|
|
206
|
+
});
|
|
341
207
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
if (registrySource.kind !== 'registry') {
|
|
349
|
-
throw new CliCommandError('Registry fallback did not resolve to an exact package version.', {
|
|
350
|
-
code: 'UPDATE_REGISTRY_SOURCE_INVALID',
|
|
351
|
-
category: 'configuration',
|
|
208
|
+
console.log(chalk.green('Checksum verification passed'));
|
|
209
|
+
console.log(chalk.cyan('Installing update...'));
|
|
210
|
+
const identity = inspectReleaseArchive(tmpFile);
|
|
211
|
+
if (identity.name !== manifestEntry.packageName || identity.version !== manifestEntry.packageVersion) {
|
|
212
|
+
throw new CliCommandError('Release archive package metadata conflicts with signed manifest identity.', {
|
|
213
|
+
code: 'UPDATE_PACKAGE_IDENTITY_INVALID',
|
|
352
214
|
});
|
|
353
215
|
}
|
|
354
|
-
|
|
355
|
-
const installed = installReleaseTransaction(registrySource.installTarget, effectiveLatest, {
|
|
356
|
-
npmRegistry: 'https://registry.npmjs.org/',
|
|
357
|
-
});
|
|
216
|
+
const installed = installReleaseTransaction(tmpFile, manifestEntry.version);
|
|
358
217
|
console.log(chalk.gray(`Versioned installation: ${installed.versionDirectory}`));
|
|
359
|
-
console.log(chalk.green(`Updated to version ${
|
|
218
|
+
console.log(chalk.green(`Updated to version ${manifestEntry.version}`));
|
|
360
219
|
console.log(chalk.gray('Please restart the CLI to use the new version'));
|
|
220
|
+
return;
|
|
361
221
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
code: 'UPDATE_REGISTRY_INSTALL_FAILED',
|
|
365
|
-
message: `Failed to install the exact registry package ${npmSpec}.`,
|
|
366
|
-
});
|
|
222
|
+
finally {
|
|
223
|
+
removeRuntimeTempDirectory(updateTempDirectory);
|
|
367
224
|
}
|
|
368
225
|
});
|
|
369
226
|
}
|
package/dist/commands/v4-menu.js
CHANGED
|
@@ -18,7 +18,9 @@ function isCancelError(error) {
|
|
|
18
18
|
return name === 'ExitPromptError' || name === 'ERR_CLOSED';
|
|
19
19
|
}
|
|
20
20
|
function writeResult(outFile, result) {
|
|
21
|
-
fs.writeFileSync(outFile, JSON.stringify(result), 'utf8');
|
|
21
|
+
fs.writeFileSync(outFile, JSON.stringify(result), { encoding: 'utf8', mode: 0o600 });
|
|
22
|
+
if (process.platform !== 'win32')
|
|
23
|
+
fs.chmodSync(outFile, 0o600);
|
|
22
24
|
}
|
|
23
25
|
export async function runV4Menu(options) {
|
|
24
26
|
const defaultAgent = options.defaultAgent === 'v4' ? 'v4' : 'v3';
|
|
@@ -71,16 +73,16 @@ export async function runV4Menu(options) {
|
|
|
71
73
|
writeResult(options.outFile, { agent: 'v4', provider });
|
|
72
74
|
return;
|
|
73
75
|
}
|
|
74
|
-
const existingApiKey = String(
|
|
76
|
+
const existingApiKey = String(process.env.VIGTHORIA_BYOK_API_KEY || '').trim();
|
|
75
77
|
const existingBaseUrl = String(options.byokBaseUrl || '').trim();
|
|
76
78
|
const existingModel = String(options.byokModel || '').trim();
|
|
77
|
-
const
|
|
79
|
+
const missingDefaults = !existingBaseUrl || !existingModel;
|
|
78
80
|
const questions = [];
|
|
79
81
|
if (!existingApiKey) {
|
|
80
82
|
questions.push({
|
|
81
83
|
type: 'password',
|
|
82
84
|
name: 'apiKey',
|
|
83
|
-
message: 'BYOK API key:',
|
|
85
|
+
message: 'BYOK API key (used for this session only):',
|
|
84
86
|
mask: '*',
|
|
85
87
|
validate: (value) => (String(value || '').trim() ? true : 'API key is required.'),
|
|
86
88
|
});
|
|
@@ -106,12 +108,12 @@ export async function runV4Menu(options) {
|
|
|
106
108
|
const baseUrl = String(answers.baseUrl || existingBaseUrl).trim();
|
|
107
109
|
const model = String(answers.model || existingModel).trim();
|
|
108
110
|
let save = false;
|
|
109
|
-
if (
|
|
111
|
+
if (missingDefaults) {
|
|
110
112
|
const { saveLocally } = await inquirer.prompt([
|
|
111
113
|
{
|
|
112
114
|
type: 'confirm',
|
|
113
115
|
name: 'saveLocally',
|
|
114
|
-
message: 'Store
|
|
116
|
+
message: 'Store the non-secret base URL and model locally? (The API key is never stored.)',
|
|
115
117
|
default: true,
|
|
116
118
|
},
|
|
117
119
|
]);
|
|
@@ -7,7 +7,6 @@ export function registerV4Commands(program, config, logger) {
|
|
|
7
7
|
.description('Internal: interactive V3/V4 agent + provider picker used by the V4 Operating Agent launcher')
|
|
8
8
|
.option('--default-agent <agent>', 'v3 | v4', 'v3')
|
|
9
9
|
.option('--default-provider <provider>', 'cloud | v3_local | byok', 'cloud')
|
|
10
|
-
.option('--byok-api-key <key>', 'Existing BYOK api key', '')
|
|
11
10
|
.option('--byok-base-url <url>', 'Existing BYOK base url', '')
|
|
12
11
|
.option('--byok-model <name>', 'Existing BYOK model', '')
|
|
13
12
|
.requiredOption('--out-file <path>', 'Write JSON selection result to this file')
|
|
@@ -15,7 +14,6 @@ export function registerV4Commands(program, config, logger) {
|
|
|
15
14
|
await runV4Menu({
|
|
16
15
|
defaultAgent: options.defaultAgent,
|
|
17
16
|
defaultProvider: options.defaultProvider,
|
|
18
|
-
byokApiKey: options.byokApiKey,
|
|
19
17
|
byokBaseUrl: options.byokBaseUrl,
|
|
20
18
|
byokModel: options.byokModel,
|
|
21
19
|
outFile: options.outFile,
|
package/dist/commands/v4.js
CHANGED
|
@@ -32,7 +32,7 @@ export class V4Command {
|
|
|
32
32
|
.alias('v4-agent')
|
|
33
33
|
.description('Launch Vigthoria V4 Operating Agent (DeerFlow 2.0 harness)')
|
|
34
34
|
.option('--provider <provider>', 'Provider: cloud | v3_local | byok')
|
|
35
|
-
.option('--api-key <key>', 'BYOK API key for
|
|
35
|
+
.option('--api-key <key>', 'BYOK API key for compatibility; prefer VIGTHORIA_BYOK_API_KEY to avoid process-list exposure')
|
|
36
36
|
.option('--base-url <url>', 'Provider base URL override')
|
|
37
37
|
.option('--model <name>', 'Provider model override')
|
|
38
38
|
.option('--transport <mode>', 'Bridge transport: auto | ws | sse', 'auto')
|
|
@@ -68,8 +68,13 @@ export class V4Command {
|
|
|
68
68
|
if (options.provider) {
|
|
69
69
|
args.push('--provider', String(options.provider));
|
|
70
70
|
}
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
const childEnv = safeChildProcessEnv(process.env, { PYTHONUNBUFFERED: '1' });
|
|
72
|
+
const runtimeByokKey = options.apiKey || process.env.VIGTHORIA_BYOK_API_KEY;
|
|
73
|
+
if (runtimeByokKey) {
|
|
74
|
+
// Do not place provider secrets in the child process argument vector.
|
|
75
|
+
// The CLI option is retained for backwards compatibility, while the
|
|
76
|
+
// V4 process receives it through an ephemeral environment variable.
|
|
77
|
+
childEnv.VIGTHORIA_BYOK_API_KEY = String(runtimeByokKey);
|
|
73
78
|
}
|
|
74
79
|
if (options.baseUrl) {
|
|
75
80
|
args.push('--base-url', String(options.baseUrl));
|
|
@@ -110,7 +115,7 @@ export class V4Command {
|
|
|
110
115
|
const child = spawn(pythonBin, args, {
|
|
111
116
|
cwd: v4Path,
|
|
112
117
|
stdio: 'inherit',
|
|
113
|
-
env:
|
|
118
|
+
env: childEnv,
|
|
114
119
|
});
|
|
115
120
|
const exitCode = await new Promise((resolve, reject) => {
|
|
116
121
|
let interrupted = false;
|
package/dist/commands/wallet.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* wallet.ts —
|
|
2
|
+
* wallet.ts — monetary Vigthoria Credits management for Vigthoria CLI.
|
|
3
3
|
*
|
|
4
4
|
* vigthoria wallet balance — show current balance
|
|
5
5
|
* vigthoria wallet history [--n 20] — recent transactions
|
|
@@ -131,7 +131,7 @@ export class WalletCommand {
|
|
|
131
131
|
}
|
|
132
132
|
const txs = data.transactions || [];
|
|
133
133
|
console.log('');
|
|
134
|
-
console.log(chalk.bold.cyan(`
|
|
134
|
+
console.log(chalk.bold.cyan(` Vigthoria Credits Transaction History (last ${txs.length})`));
|
|
135
135
|
console.log(chalk.gray(' ──────────────────────────────────────────────────────────'));
|
|
136
136
|
if (txs.length === 0) {
|
|
137
137
|
console.log(chalk.gray(' No transactions found.'));
|
|
@@ -141,7 +141,7 @@ export class WalletCommand {
|
|
|
141
141
|
const sign = tx.amount >= 0 ? chalk.green(`+${tx.amount}`) : chalk.red(String(tx.amount));
|
|
142
142
|
const date = new Date(tx.created_at).toLocaleString();
|
|
143
143
|
const desc = tx.description || tx.action || tx.type;
|
|
144
|
-
console.log(` ${chalk.gray(date)} ${sign.padStart(8)}
|
|
144
|
+
console.log(` ${chalk.gray(date)} ${sign.padStart(8)} credits ${chalk.gray(`→`)} ${chalk.yellow(tx.balance_after)} credits ${desc}`);
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
console.log('');
|
package/dist/index.js
CHANGED
|
@@ -179,9 +179,8 @@ export function validateReleaseMetadata() {
|
|
|
179
179
|
const readme = fs.readFileSync(readmePath, 'utf8');
|
|
180
180
|
const bins = pkg.bin && typeof pkg.bin === 'object' ? pkg.bin : {};
|
|
181
181
|
const requiredReadmePhrases = [
|
|
182
|
-
'
|
|
183
|
-
'
|
|
184
|
-
'irm https://cli.vigthoria.io/install.ps1 | iex',
|
|
182
|
+
'curl -fsSL https://extension.vigthoria.io/downloads/install.sh | bash',
|
|
183
|
+
'irm https://extension.vigthoria.io/downloads/install.ps1 | iex',
|
|
185
184
|
'vigthoria login',
|
|
186
185
|
'vigthoria chat',
|
|
187
186
|
'vig c',
|
|
@@ -3,6 +3,11 @@ export interface AgentStreamStateDependencies {
|
|
|
3
3
|
isJsonOutput(): boolean;
|
|
4
4
|
idleTimeoutMs(): number;
|
|
5
5
|
}
|
|
6
|
+
export type AgentToolResultPresentation = 'normal' | 'hidden' | 'correcting';
|
|
7
|
+
/** Decode escaped line breaks without corrupting POSIX paths such as src/render.js. */
|
|
8
|
+
export declare function normalizeAgentToolOutputForDisplay(output: string): string;
|
|
9
|
+
/** Keep governed executor recovery details out of the normal user transcript. */
|
|
10
|
+
export declare function classifyAgentToolResultPresentation(event: any): AgentToolResultPresentation;
|
|
6
11
|
/** Owns the mutable lifecycle and deduplication state for one rendered agent stream. */
|
|
7
12
|
export declare class AgentStreamState {
|
|
8
13
|
private readonly dependencies;
|
|
@@ -18,9 +23,11 @@ export declare class AgentStreamState {
|
|
|
18
23
|
readonly seenToolResults: Set<string>;
|
|
19
24
|
private idleWatchInterval;
|
|
20
25
|
private idleNoticeShown;
|
|
26
|
+
private lastCorrectionNoticeAt;
|
|
21
27
|
constructor(dependencies: AgentStreamStateDependencies);
|
|
22
28
|
reset(): void;
|
|
23
29
|
noteActivity(): void;
|
|
30
|
+
shouldShowCorrectionNotice(now?: number, minimumIntervalMs?: number): boolean;
|
|
24
31
|
startIdleWatch(spinner: Ora | null): void;
|
|
25
32
|
stopIdleWatch(): void;
|
|
26
33
|
sanitizeVisibleText(text: string): string;
|
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
+
/** Decode escaped line breaks without corrupting POSIX paths such as src/render.js. */
|
|
3
|
+
export function normalizeAgentToolOutputForDisplay(output) {
|
|
4
|
+
return String(output || '')
|
|
5
|
+
.replace(/\\r\\n|\\n/g, '\n')
|
|
6
|
+
.replace(/\\r/g, '')
|
|
7
|
+
.replace(/\t/g, ' ');
|
|
8
|
+
}
|
|
9
|
+
/** Keep governed executor recovery details out of the normal user transcript. */
|
|
10
|
+
export function classifyAgentToolResultPresentation(event) {
|
|
11
|
+
if (!event || event.type !== 'tool_result')
|
|
12
|
+
return 'normal';
|
|
13
|
+
if (event.user_visible === false || String(event.visibility || '').toLowerCase() === 'internal') {
|
|
14
|
+
return 'hidden';
|
|
15
|
+
}
|
|
16
|
+
if (event.success !== false)
|
|
17
|
+
return 'normal';
|
|
18
|
+
const detail = String(event.error || event.output || '');
|
|
19
|
+
if (/\b(?:deferred a second mutation|byte-identical source|duplicate reads are blocked|mutation recovery is active|was not offered for this atomic task|verification repair is pinned|blocked wrong-path|out-of-scope mutation|old_string found 0 times|mutation rejected before (?:write|edit)|degenerate mutation blocked before task acceptance|skipped (?:syntax|preview|runtime)_check[^.]*file does not exist yet|file not found in the local workspace)\b/i.test(detail)) {
|
|
20
|
+
return 'correcting';
|
|
21
|
+
}
|
|
22
|
+
return 'normal';
|
|
23
|
+
}
|
|
2
24
|
/** Owns the mutable lifecycle and deduplication state for one rendered agent stream. */
|
|
3
25
|
export class AgentStreamState {
|
|
4
26
|
dependencies;
|
|
@@ -14,6 +36,7 @@ export class AgentStreamState {
|
|
|
14
36
|
seenToolResults = new Set();
|
|
15
37
|
idleWatchInterval = null;
|
|
16
38
|
idleNoticeShown = false;
|
|
39
|
+
lastCorrectionNoticeAt = 0;
|
|
17
40
|
constructor(dependencies) {
|
|
18
41
|
this.dependencies = dependencies;
|
|
19
42
|
}
|
|
@@ -29,8 +52,16 @@ export class AgentStreamState {
|
|
|
29
52
|
this.seenToolCalls.clear();
|
|
30
53
|
this.seenToolResults.clear();
|
|
31
54
|
this.idleNoticeShown = false;
|
|
55
|
+
this.lastCorrectionNoticeAt = 0;
|
|
32
56
|
}
|
|
33
57
|
noteActivity() { this.lastActivity = Date.now(); }
|
|
58
|
+
shouldShowCorrectionNotice(now = Date.now(), minimumIntervalMs = 5_000) {
|
|
59
|
+
if (this.lastCorrectionNoticeAt > 0 && now - this.lastCorrectionNoticeAt < minimumIntervalMs) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
this.lastCorrectionNoticeAt = now;
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
34
65
|
startIdleWatch(spinner) {
|
|
35
66
|
this.stopIdleWatch();
|
|
36
67
|
if (this.dependencies.isJsonOutput() || !spinner)
|
|
@@ -42,7 +42,11 @@ const LIST_ONLY_DISCOVERY_TOOLS = new Set([
|
|
|
42
42
|
'glob',
|
|
43
43
|
'dir',
|
|
44
44
|
]);
|
|
45
|
-
|
|
45
|
+
// Match only the complete placeholder. A real completion report commonly
|
|
46
|
+
// starts with "Done" before listing verified work and the user's next step;
|
|
47
|
+
// treating every such report as a stub discards the authoritative server
|
|
48
|
+
// summary and leaves the CLI showing only a filename list.
|
|
49
|
+
const GENERIC_SUMMARY_RE = /^(completed the requested analysis|reviewed the workspace without writing changes|task completed|done|finished|analysis completed)[.!]?$/i;
|
|
46
50
|
/** Planner fallback / degraded inference — generic greeting unrelated to the user question. */
|
|
47
51
|
const GENERIC_FALLBACK_GREETING_RE = /^(?:hello!?|hi!?|hey!?)\s*(?:how can i assist|how can i help|what would you like|whether you'?re looking to build)/i;
|
|
48
52
|
/** System-prompt fragments leaked when the model falls back without real context. */
|