vigthoria-cli 1.13.26 → 1.13.29
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/dist/commands/chat.js +73 -26
- package/dist/commands/config.js +4 -4
- package/dist/commands/fork.d.ts +3 -2
- package/dist/commands/fork.js +124 -123
- package/dist/commands/game.d.ts +8 -0
- package/dist/commands/game.js +113 -9
- package/dist/commands/history.d.ts +0 -1
- package/dist/commands/history.js +8 -22
- package/dist/commands/hub.d.ts +20 -0
- package/dist/commands/hub.js +17 -3
- package/dist/commands/preview.js +7 -2
- package/dist/commands/product-run-registration.js +1 -1
- package/dist/commands/replay.d.ts +0 -1
- package/dist/commands/replay.js +10 -19
- package/dist/commands/repo.js +16 -4
- package/dist/commands/update-registration.js +2 -2
- package/dist/commands/workflow.d.ts +4 -0
- package/dist/commands/workflow.js +27 -0
- package/dist/index.js +6 -4
- package/dist/utils/agentRunOutcome.d.ts +7 -0
- package/dist/utils/agentRunOutcome.js +13 -0
- package/dist/utils/api.d.ts +20 -5
- package/dist/utils/api.js +428 -43
- package/dist/utils/command-policy.js +1 -1
- package/dist/utils/config.d.ts +2 -0
- package/dist/utils/config.js +8 -3
- package/dist/utils/frontend-preview-service.d.ts +1 -0
- package/dist/utils/frontend-preview-service.js +54 -5
- package/dist/utils/model-governance.js +23 -14
- package/dist/utils/model-transport-service.js +1 -1
- package/dist/utils/network-policy.js +15 -3
- package/dist/utils/operator-client.js +23 -4
- package/dist/utils/post-write-validator.js +7 -3
- package/dist/utils/preview-screenshot-adapter.d.ts +16 -41
- package/dist/utils/preview-screenshot-adapter.js +273 -64
- package/dist/utils/runtime-capability.d.ts +7 -0
- package/dist/utils/runtime-capability.js +11 -0
- package/dist/utils/runtime-temp.d.ts +5 -2
- package/dist/utils/runtime-temp.js +125 -29
- package/dist/utils/tools.js +1 -1
- package/dist/utils/v3-stream-events.js +10 -2
- package/dist/utils/v3-workspace-service.d.ts +1 -0
- package/dist/utils/v3-workspace-service.js +38 -1
- package/dist/utils/vigflow-client.d.ts +9 -0
- package/dist/utils/vigflow-client.js +48 -2
- package/dist/utils/workspace-reference.d.ts +8 -0
- package/dist/utils/workspace-reference.js +21 -0
- package/package.json +4 -6
- package/scripts/release/LOCAL_MACHINE_USER_VERIFICATION.md +2 -2
- package/scripts/release/validate-live-service-gates.sh +3 -3
- package/scripts/release/validate-no-go-gates.sh +2 -0
package/dist/commands/game.js
CHANGED
|
@@ -46,11 +46,114 @@ export function gameProcessInvocation(pm, args, platform = process.platform, com
|
|
|
46
46
|
const command = [executable, ...args].join(' ');
|
|
47
47
|
return { executable: comSpec, args: ['/d', '/s', '/c', command] };
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
export function windowsProcessTreeTerminationInvocation(pid, systemRoot = process.env.SystemRoot || 'C:\\Windows') {
|
|
50
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) {
|
|
51
|
+
throw new CliCommandError('Cannot terminate a game process without a valid owned PID.', {
|
|
52
|
+
code: 'GAME_PROCESS_ID_INVALID',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
executable: path.win32.join(systemRoot, 'System32', 'taskkill.exe'),
|
|
57
|
+
args: ['/PID', String(pid), '/T', '/F'],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async function terminateOwnedProcessTree(child, platform) {
|
|
61
|
+
const pid = child.pid;
|
|
62
|
+
if (!pid)
|
|
63
|
+
return;
|
|
64
|
+
if (platform === 'win32') {
|
|
65
|
+
const invocation = windowsProcessTreeTerminationInvocation(pid);
|
|
66
|
+
await new Promise((resolve) => {
|
|
67
|
+
const killer = spawn(invocation.executable, invocation.args, {
|
|
68
|
+
stdio: 'ignore',
|
|
69
|
+
windowsHide: true,
|
|
70
|
+
env: safeChildProcessEnv(),
|
|
71
|
+
});
|
|
72
|
+
const timer = setTimeout(() => { try {
|
|
73
|
+
killer.kill();
|
|
74
|
+
}
|
|
75
|
+
catch { /* exact helper only */ } resolve(); }, 5_000);
|
|
76
|
+
killer.once('error', () => { clearTimeout(timer); try {
|
|
77
|
+
child.kill();
|
|
78
|
+
}
|
|
79
|
+
catch { /* already gone */ } resolve(); });
|
|
80
|
+
killer.once('exit', () => { clearTimeout(timer); resolve(); });
|
|
81
|
+
});
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
process.kill(-pid, 'SIGTERM');
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
try {
|
|
89
|
+
child.kill('SIGTERM');
|
|
90
|
+
}
|
|
91
|
+
catch { /* already gone */ }
|
|
92
|
+
}
|
|
93
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
94
|
+
try {
|
|
95
|
+
process.kill(-pid, 0);
|
|
96
|
+
process.kill(-pid, 'SIGKILL');
|
|
97
|
+
}
|
|
98
|
+
catch { /* process group exited */ }
|
|
99
|
+
}
|
|
100
|
+
export const runGameProcess = (pm, args, cwd, timeoutMs = 10 * 60_000, outputMode = 'inherit') => new Promise((resolve, reject) => {
|
|
101
|
+
const invocation = gameProcessInvocation(pm, args);
|
|
102
|
+
const platform = process.platform;
|
|
103
|
+
const child = spawn(invocation.executable, invocation.args, {
|
|
104
|
+
cwd,
|
|
105
|
+
stdio: outputMode === 'capture' ? ['ignore', 'pipe', 'pipe'] : 'inherit',
|
|
106
|
+
windowsHide: true,
|
|
107
|
+
env: safeChildProcessEnv(),
|
|
108
|
+
detached: platform !== 'win32',
|
|
109
|
+
});
|
|
110
|
+
let interrupted = false;
|
|
111
|
+
let timedOut = false;
|
|
112
|
+
let settled = false;
|
|
113
|
+
let stdout = '';
|
|
114
|
+
let stderr = '';
|
|
115
|
+
const appendBounded = (current, chunk) => `${current}${String(chunk)}`.slice(-64 * 1024);
|
|
116
|
+
if (outputMode === 'capture') {
|
|
117
|
+
child.stdout?.setEncoding('utf8');
|
|
118
|
+
child.stderr?.setEncoding('utf8');
|
|
119
|
+
child.stdout?.on('data', (chunk) => { stdout = appendBounded(stdout, chunk); });
|
|
120
|
+
child.stderr?.on('data', (chunk) => { stderr = appendBounded(stderr, chunk); });
|
|
121
|
+
}
|
|
122
|
+
const settle = (error) => {
|
|
123
|
+
if (settled)
|
|
124
|
+
return;
|
|
125
|
+
settled = true;
|
|
126
|
+
if (timer)
|
|
127
|
+
clearTimeout(timer);
|
|
128
|
+
process.removeListener('SIGINT', onInterrupt);
|
|
129
|
+
error ? reject(error) : resolve({ stdout, stderr });
|
|
130
|
+
};
|
|
131
|
+
const onInterrupt = () => {
|
|
132
|
+
interrupted = true;
|
|
133
|
+
void terminateOwnedProcessTree(child, platform);
|
|
134
|
+
};
|
|
135
|
+
process.once('SIGINT', onInterrupt);
|
|
136
|
+
const timer = timeoutMs > 0 ? setTimeout(() => {
|
|
137
|
+
timedOut = true;
|
|
138
|
+
void terminateOwnedProcessTree(child, platform).finally(() => settle(new CliCommandError(`${pm} command timed out after ${timeoutMs}ms`, { code: 'GAME_PROCESS_TIMEOUT' })));
|
|
139
|
+
}, timeoutMs) : null;
|
|
140
|
+
child.once('error', (error) => settle(error));
|
|
141
|
+
child.once('exit', (code, signal) => {
|
|
142
|
+
if (timedOut) {
|
|
143
|
+
settle(new CliCommandError(`${pm} command timed out after ${timeoutMs}ms`, { code: 'GAME_PROCESS_TIMEOUT' }));
|
|
144
|
+
}
|
|
145
|
+
else if (interrupted) {
|
|
146
|
+
settle(new CliCommandError(`${pm} command cancelled by user`, { code: 'GAME_PROCESS_CANCELLED', category: 'cancelled' }));
|
|
147
|
+
}
|
|
148
|
+
else if (code === 0) {
|
|
149
|
+
settle();
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
const detail = outputMode === 'capture' ? `: ${(stderr || stdout).trim().slice(-4_000)}` : '';
|
|
153
|
+
settle(new Error(`${pm} exited with ${signal || code}${detail}`));
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
});
|
|
54
157
|
const html = `<!doctype html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>VGE Game</title></head><body><canvas id="game"></canvas><div id="hud"><strong>Vigthoria Gaming Engine</strong><span>Ready</span></div><script type="module" src="/src/main.ts"></script></body></html>\n`;
|
|
55
158
|
const style = `*{box-sizing:border-box}html,body,#game{width:100%;height:100%;margin:0;overflow:hidden}body{background:#030510;color:#e8f7ff;font-family:Inter,system-ui,sans-serif}#game{display:block;touch-action:none}#hud{position:fixed;left:24px;top:24px;display:flex;gap:16px;padding:12px 16px;border:1px solid #00d4ff66;border-radius:12px;background:#070a18cc;backdrop-filter:blur(12px);box-shadow:0 0 32px #7c3aed33}#hud span{color:#5eead4}\n`;
|
|
56
159
|
const main = `import { ArcRotateCamera, Color3, Color4, DirectionalLight, Engine, HemisphericLight, MeshBuilder, PBRMaterial, Scene, ShadowGenerator, Vector3 } from '@babylonjs/core';
|
|
@@ -80,21 +183,22 @@ export class GameCommand {
|
|
|
80
183
|
if (fs.existsSync(root) && fs.readdirSync(root).length)
|
|
81
184
|
throw new Error(`Target directory is not empty: ${root}`);
|
|
82
185
|
fs.mkdirSync(path.join(root, 'src'), { recursive: true });
|
|
83
|
-
const pkg = { name: slug, version: '0.1.0', private: true, type: 'module', vigthoria: { kind: 'game', engine: 'babylon', renderTransport: 'client-state', schemaVersion: 1 }, scripts: { dev: 'vite', build: 'tsc --noEmit && vite build', preview: 'vite preview' }, dependencies: { '@babylonjs/core': '
|
|
186
|
+
const pkg = { name: slug, version: '0.1.0', private: true, type: 'module', engines: { node: '>=20.19.0' }, vigthoria: { kind: 'game', engine: 'babylon', renderTransport: 'client-state', schemaVersion: 1 }, scripts: { dev: 'node ./node_modules/vite/bin/vite.js', build: 'node ./node_modules/typescript/bin/tsc --noEmit && node ./node_modules/vite/bin/vite.js build', preview: 'node ./node_modules/vite/bin/vite.js preview' }, dependencies: { '@babylonjs/core': '9.20.0', '@babylonjs/loaders': '9.20.0' }, devDependencies: { typescript: '5.9.3', vite: '7.3.6' } };
|
|
84
187
|
fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify(pkg, null, 2) + '\n');
|
|
85
188
|
fs.writeFileSync(path.join(root, 'index.html'), html);
|
|
86
189
|
fs.writeFileSync(path.join(root, 'src/main.ts'), main);
|
|
87
190
|
fs.writeFileSync(path.join(root, 'src/style.css'), style);
|
|
191
|
+
fs.writeFileSync(path.join(root, 'src/vite-env.d.ts'), '/// <reference types="vite/client" />\n');
|
|
88
192
|
fs.writeFileSync(path.join(root, '.gitignore'), 'node_modules\ndist\n.vigthoria\n');
|
|
89
193
|
fs.writeFileSync(path.join(root, 'tsconfig.json'), JSON.stringify({ compilerOptions: { target: 'ES2022', module: 'ESNext', lib: ['ES2022', 'DOM'], skipLibCheck: true, moduleResolution: 'Bundler', isolatedModules: true, noEmit: true, strict: true }, include: ['src'] }, null, 2) + '\n');
|
|
90
194
|
if (o.install !== false)
|
|
91
|
-
await
|
|
195
|
+
await runGameProcess(manager(root, o.packageManager), ['install'], root);
|
|
92
196
|
this.logger.success(`Babylon/VGE game created: ${root}`);
|
|
93
197
|
this.logger.info(`Next: cd ${slug} && vigthoria game run`);
|
|
94
198
|
}
|
|
95
199
|
async run(o) { const root = path.resolve(o.project || process.cwd()); this.project(root); const args = ['run', 'dev', '--', '--host', '127.0.0.1']; if (o.port)
|
|
96
200
|
args.push('--port', String(o.port)); if (o.open !== false)
|
|
97
|
-
args.push('--open'); await
|
|
201
|
+
args.push('--open'); await runGameProcess(manager(root, o.packageManager), args, root, 0); }
|
|
98
202
|
async validate(o) {
|
|
99
203
|
const root = path.resolve(o.project || process.cwd());
|
|
100
204
|
const checks = [];
|
|
@@ -110,7 +214,7 @@ export class GameCommand {
|
|
|
110
214
|
const source = this.sources(root);
|
|
111
215
|
checks.push({ check: 'Babylon client-state metadata', passed: pkg.vigthoria?.engine === 'babylon' && pkg.vigthoria?.renderTransport === 'client-state' }, { check: 'Pinned local Babylon dependencies', passed: /^\d/.test(pkg.dependencies?.['@babylonjs/core'] || '') && /^\d/.test(pkg.dependencies?.['@babylonjs/loaders'] || '') }, { check: 'No CDN engine imports', passed: !/https?:\/\/(cdn|unpkg|jsdelivr)[^\s"']*(babylon|three)/i.test(source) }, { check: 'Runtime diagnostics', passed: source.includes('__vgeDiagnostics') }, { check: 'Client source entry', passed: fs.existsSync(path.join(root, 'src/main.ts')) || fs.existsSync(path.join(root, 'src/main.js')) });
|
|
112
216
|
try {
|
|
113
|
-
await
|
|
217
|
+
await runGameProcess(manager(root, o.packageManager), ['run', 'build'], root, 10 * 60_000, o.json ? 'capture' : 'inherit');
|
|
114
218
|
checks.push({ check: 'Production build', passed: true });
|
|
115
219
|
}
|
|
116
220
|
catch (e) {
|
package/dist/commands/history.js
CHANGED
|
@@ -3,10 +3,10 @@ import { hasLocalV3AgentCapability } from '../utils/runtime-capability.js';
|
|
|
3
3
|
* history.ts — List recent V3 agent runs with summaries.
|
|
4
4
|
*/
|
|
5
5
|
import chalk from 'chalk';
|
|
6
|
-
import { createRequire } from 'node:module';
|
|
7
6
|
import { createSpinner, CH } from '../utils/logger.js';
|
|
8
7
|
import { CliCommandError, commandFailure, formatSuccessJson } from '../utils/command-contract.js';
|
|
9
|
-
|
|
8
|
+
import { buildLocalWorkspaceReference } from '../utils/workspace-reference.js';
|
|
9
|
+
import { guardedFetch } from '../utils/network-policy.js';
|
|
10
10
|
export class HistoryCommand {
|
|
11
11
|
config;
|
|
12
12
|
logger;
|
|
@@ -36,36 +36,22 @@ export class HistoryCommand {
|
|
|
36
36
|
(allowLocal ? 'http://127.0.0.1:8030' : null) ||
|
|
37
37
|
configuredApiUrl);
|
|
38
38
|
}
|
|
39
|
-
resolveWorkspaceRoot(project) {
|
|
40
|
-
// On Windows or non-absolute paths, send empty so the server uses its fallback root
|
|
41
|
-
if (/^[a-zA-Z]:[\\/]/.test(project) || /^\\\\/.test(project))
|
|
42
|
-
return '';
|
|
43
|
-
if (typeof require !== 'undefined') {
|
|
44
|
-
try {
|
|
45
|
-
const path = require('path');
|
|
46
|
-
if (!path.isAbsolute(project))
|
|
47
|
-
return '';
|
|
48
|
-
}
|
|
49
|
-
catch { }
|
|
50
|
-
}
|
|
51
|
-
return project;
|
|
52
|
-
}
|
|
53
39
|
async run(options) {
|
|
54
40
|
const limit = options.limit || 20;
|
|
55
41
|
const project = options.project || process.cwd();
|
|
56
|
-
const
|
|
42
|
+
const workspaceRef = buildLocalWorkspaceReference(project, String(this.config.get('userId') || this.config.get('email') || ''));
|
|
57
43
|
const spinner = createSpinner('Loading run history...').start();
|
|
58
44
|
try {
|
|
59
45
|
const baseUrl = this.getBaseUrl();
|
|
60
46
|
const params = new URLSearchParams({
|
|
61
47
|
limit: String(limit),
|
|
62
|
-
workspace_root:
|
|
63
|
-
local_workspace_path:
|
|
64
|
-
project_path:
|
|
48
|
+
workspace_root: '',
|
|
49
|
+
local_workspace_path: workspaceRef,
|
|
50
|
+
project_path: workspaceRef,
|
|
65
51
|
});
|
|
66
|
-
const resp = await
|
|
52
|
+
const resp = await guardedFetch(`${baseUrl}/api/runs?${params}`, {
|
|
67
53
|
headers: this.getHeaders(),
|
|
68
|
-
});
|
|
54
|
+
}, { audience: 'v3' });
|
|
69
55
|
if (!resp.ok) {
|
|
70
56
|
spinner.stop();
|
|
71
57
|
if (resp.status === 404 || resp.status === 502 || resp.status === 503) {
|
package/dist/commands/hub.d.ts
CHANGED
|
@@ -6,6 +6,25 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Config } from '../utils/config.js';
|
|
8
8
|
import { Logger } from '../utils/logger.js';
|
|
9
|
+
interface Module {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
endpoint: string;
|
|
14
|
+
documentation: string;
|
|
15
|
+
pricing: {
|
|
16
|
+
unit: string;
|
|
17
|
+
cost: number;
|
|
18
|
+
example: string;
|
|
19
|
+
};
|
|
20
|
+
status: string;
|
|
21
|
+
category: string;
|
|
22
|
+
tags: string[];
|
|
23
|
+
unit?: string;
|
|
24
|
+
creditsPerUnit?: number | string;
|
|
25
|
+
euroPerUnit?: number | string;
|
|
26
|
+
}
|
|
27
|
+
export declare function moduleMatchesIdentifier(module: Pick<Module, 'id' | 'name'>, requested: string): boolean;
|
|
9
28
|
export declare class HubCommand {
|
|
10
29
|
private config;
|
|
11
30
|
constructor(config: Config, _logger: Logger);
|
|
@@ -39,3 +58,4 @@ export declare class HubCommand {
|
|
|
39
58
|
*/
|
|
40
59
|
discover(): Promise<void>;
|
|
41
60
|
}
|
|
61
|
+
export {};
|
package/dist/commands/hub.js
CHANGED
|
@@ -8,6 +8,19 @@ import chalk from 'chalk';
|
|
|
8
8
|
import { guardedFetch } from '../utils/network-policy.js';
|
|
9
9
|
import { CliCommandError, commandFailure } from '../utils/command-contract.js';
|
|
10
10
|
const API_BASE = 'https://hub.vigthoria.io';
|
|
11
|
+
function normalizeModuleIdentifier(value) {
|
|
12
|
+
return String(value || '').trim().toLowerCase().replace(/[\s_]+/g, '-');
|
|
13
|
+
}
|
|
14
|
+
export function moduleMatchesIdentifier(module, requested) {
|
|
15
|
+
const needle = normalizeModuleIdentifier(requested);
|
|
16
|
+
if (!needle)
|
|
17
|
+
return false;
|
|
18
|
+
const id = normalizeModuleIdentifier(module.id);
|
|
19
|
+
const name = normalizeModuleIdentifier(module.name);
|
|
20
|
+
return needle === id
|
|
21
|
+
|| needle === name
|
|
22
|
+
|| needle === id.replace(/-api$/, '');
|
|
23
|
+
}
|
|
11
24
|
export class HubCommand {
|
|
12
25
|
config;
|
|
13
26
|
constructor(config, _logger) {
|
|
@@ -76,6 +89,7 @@ export class HubCommand {
|
|
|
76
89
|
data.results.forEach((module, index) => {
|
|
77
90
|
const statusColor = module.status === 'available' ? chalk.green : chalk.gray;
|
|
78
91
|
console.log(chalk.bold.white(` ${index + 1}. ${module.name}`));
|
|
92
|
+
console.log(chalk.gray(` Module ID: ${module.id}`));
|
|
79
93
|
console.log(chalk.gray(` ${module.description.substring(0, 80)}...`));
|
|
80
94
|
console.log(chalk.cyan(` 💰 ${module.pricing.example}`));
|
|
81
95
|
console.log(statusColor(` 📊 Status: ${module.status.toUpperCase()}`));
|
|
@@ -86,7 +100,7 @@ export class HubCommand {
|
|
|
86
100
|
console.log(chalk.cyan(`💡 ${data.suggestion}`));
|
|
87
101
|
}
|
|
88
102
|
console.log(chalk.gray('\nTo activate a module: vigthoria hub activate <module-name>'));
|
|
89
|
-
console.log(chalk.gray(
|
|
103
|
+
console.log(chalk.gray(`Example: vigthoria hub activate ${data.results[0].id}\n`));
|
|
90
104
|
}
|
|
91
105
|
catch (error) {
|
|
92
106
|
throw commandFailure(error, { code: 'HUB_SEARCH_FAILED' });
|
|
@@ -144,7 +158,7 @@ export class HubCommand {
|
|
|
144
158
|
this.getAuthToken();
|
|
145
159
|
console.log(chalk.cyan(`\n🔌 Activating module: ${moduleId}...\n`));
|
|
146
160
|
try {
|
|
147
|
-
const module = (await this.moduleCatalog()).find((entry) => entry
|
|
161
|
+
const module = (await this.moduleCatalog()).find((entry) => moduleMatchesIdentifier(entry, moduleId));
|
|
148
162
|
if (!module)
|
|
149
163
|
throw new CliCommandError(`Unknown Hub module: ${moduleId}`, { code: 'HUB_MODULE_NOT_FOUND', category: 'usage' });
|
|
150
164
|
console.log(chalk.green(`✅ ${module.name} is published in the Vigthoria module catalog.`));
|
|
@@ -186,7 +200,7 @@ export class HubCommand {
|
|
|
186
200
|
async info(moduleId) {
|
|
187
201
|
console.log(chalk.cyan(`\n📖 Module Info: ${moduleId}\n`));
|
|
188
202
|
try {
|
|
189
|
-
const module = (await this.moduleCatalog()).find((entry) => entry
|
|
203
|
+
const module = (await this.moduleCatalog()).find((entry) => moduleMatchesIdentifier(entry, moduleId));
|
|
190
204
|
if (!module)
|
|
191
205
|
throw new CliCommandError(`Unknown Hub module: ${moduleId}`, { code: 'HUB_MODULE_NOT_FOUND', category: 'usage' });
|
|
192
206
|
console.log(chalk.bold.white(`${module.name}`));
|
package/dist/commands/preview.js
CHANGED
|
@@ -81,7 +81,7 @@ export class PreviewCommand {
|
|
|
81
81
|
// Run Template Service preview proof gate
|
|
82
82
|
if (isPreviewProofRequested(options)) {
|
|
83
83
|
try {
|
|
84
|
-
await this.runProofGate(projectPath, options.screenshot);
|
|
84
|
+
await this.runProofGate(projectPath, options.screenshot, options.screenshot === true && options.proof !== true);
|
|
85
85
|
}
|
|
86
86
|
catch (error) {
|
|
87
87
|
this.api.destroy();
|
|
@@ -435,7 +435,7 @@ export class PreviewCommand {
|
|
|
435
435
|
/**
|
|
436
436
|
* Run Template Service preview gate and persist proof bundle
|
|
437
437
|
*/
|
|
438
|
-
async runProofGate(projectPath, captureScreenshot) {
|
|
438
|
+
async runProofGate(projectPath, captureScreenshot, localScreenshotProof = false) {
|
|
439
439
|
const spinner = createSpinner('Running preview proof gate...').start();
|
|
440
440
|
try {
|
|
441
441
|
const result = await this.api.runTemplateServicePreviewGate('', {
|
|
@@ -444,6 +444,11 @@ export class PreviewCommand {
|
|
|
444
444
|
targetPath: projectPath,
|
|
445
445
|
forceFrontendPreview: true,
|
|
446
446
|
requireScreenshot: captureScreenshot === true,
|
|
447
|
+
// An explicit screenshot-only command is a local operation. Do not
|
|
448
|
+
// upload the user's frontend or require an ecosystem login merely to
|
|
449
|
+
// capture local visual evidence. `--proof` retains the hosted proof
|
|
450
|
+
// contract, including when combined with `--screenshot`.
|
|
451
|
+
localScreenshotProof,
|
|
447
452
|
});
|
|
448
453
|
spinner.stop();
|
|
449
454
|
console.log(chalk.bold.white(` ${CH.hLine.repeat(3)} Preview Proof Gate ${CH.hLine.repeat(39)}`));
|
|
@@ -386,7 +386,7 @@ Examples:
|
|
|
386
386
|
.option('--no-open', 'Do not auto-open browser')
|
|
387
387
|
.option('--diff', 'Show consolidated diff of recent agent changes')
|
|
388
388
|
.option('--proof', 'Run Template Service preview gate and persist proof bundle')
|
|
389
|
-
.option('--screenshot', 'Capture screenshot via
|
|
389
|
+
.option('--screenshot', 'Capture screenshot via an installed system browser')
|
|
390
390
|
.action(async (options) => {
|
|
391
391
|
const preview = new PreviewCommand(config, logger);
|
|
392
392
|
await preview.run({
|
package/dist/commands/replay.js
CHANGED
|
@@ -3,10 +3,10 @@ import { hasLocalV3AgentCapability } from '../utils/runtime-capability.js';
|
|
|
3
3
|
* replay.ts — Replay events from a V3 agent run step-by-step.
|
|
4
4
|
*/
|
|
5
5
|
import chalk from 'chalk';
|
|
6
|
-
import { createRequire } from 'node:module';
|
|
7
6
|
import { createSpinner, CH } from '../utils/logger.js';
|
|
8
7
|
import { CliCommandError, commandFailure, formatSuccessJson } from '../utils/command-contract.js';
|
|
9
|
-
|
|
8
|
+
import { buildLocalWorkspaceReference } from '../utils/workspace-reference.js';
|
|
9
|
+
import { guardedFetch } from '../utils/network-policy.js';
|
|
10
10
|
export class ReplayCommand {
|
|
11
11
|
config;
|
|
12
12
|
constructor(config, _logger) {
|
|
@@ -34,33 +34,24 @@ export class ReplayCommand {
|
|
|
34
34
|
(allowLocal ? 'http://127.0.0.1:8030' : null) ||
|
|
35
35
|
configuredApiUrl);
|
|
36
36
|
}
|
|
37
|
-
resolveWorkspaceRoot(project) {
|
|
38
|
-
if (/^[a-zA-Z]:[\\/]/.test(project) || /^\\\\/.test(project))
|
|
39
|
-
return '';
|
|
40
|
-
if (typeof require !== 'undefined') {
|
|
41
|
-
try {
|
|
42
|
-
const path = require('path');
|
|
43
|
-
if (!path.isAbsolute(project))
|
|
44
|
-
return '';
|
|
45
|
-
}
|
|
46
|
-
catch { }
|
|
47
|
-
}
|
|
48
|
-
return project;
|
|
49
|
-
}
|
|
50
37
|
sleep(ms) {
|
|
51
38
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
52
39
|
}
|
|
53
40
|
async run(runId, options) {
|
|
54
41
|
const speed = options.speed || 200;
|
|
55
42
|
const project = options.project || process.cwd();
|
|
56
|
-
const
|
|
43
|
+
const workspaceRef = buildLocalWorkspaceReference(project, String(this.config.get('userId') || this.config.get('email') || ''));
|
|
57
44
|
const spinner = createSpinner(`Loading events for run ${runId}...`).start();
|
|
58
45
|
try {
|
|
59
46
|
const baseUrl = this.getBaseUrl();
|
|
60
|
-
const params = new URLSearchParams({
|
|
61
|
-
|
|
62
|
-
|
|
47
|
+
const params = new URLSearchParams({
|
|
48
|
+
workspace_root: '',
|
|
49
|
+
local_workspace_path: workspaceRef,
|
|
50
|
+
project_path: workspaceRef,
|
|
63
51
|
});
|
|
52
|
+
const resp = await guardedFetch(`${baseUrl}/api/runs/${encodeURIComponent(runId)}/events?${params}`, {
|
|
53
|
+
headers: this.getHeaders(),
|
|
54
|
+
}, { audience: 'v3' });
|
|
64
55
|
if (!resp.ok) {
|
|
65
56
|
spinner.stop();
|
|
66
57
|
if (resp.status === 404) {
|
package/dist/commands/repo.js
CHANGED
|
@@ -161,6 +161,9 @@ export class RepoCommand {
|
|
|
161
161
|
};
|
|
162
162
|
try {
|
|
163
163
|
const proxyResponse = await attempt(proxyPath, 'coder');
|
|
164
|
+
if (options.terminalStatuses?.includes(proxyResponse.status)) {
|
|
165
|
+
return proxyResponse;
|
|
166
|
+
}
|
|
164
167
|
// A large repository payload can exceed the Coder API's general
|
|
165
168
|
// 50 MiB parser while remaining valid under Community's 100 MiB
|
|
166
169
|
// repository contract. Retry that one status directly.
|
|
@@ -615,7 +618,7 @@ export class RepoCommand {
|
|
|
615
618
|
if (fs.existsSync(outputPath))
|
|
616
619
|
validateExtractedWorkspace(outputPath);
|
|
617
620
|
if (isCompleteRepositoryInlinePayload(data)) {
|
|
618
|
-
const stagingRoot = createRuntimeTempDirectory('repo-inline-');
|
|
621
|
+
const stagingRoot = createRuntimeTempDirectory('repo-inline-', 128 * 1024 * 1024);
|
|
619
622
|
try {
|
|
620
623
|
let totalBytes = 0;
|
|
621
624
|
for (const file of data.files) {
|
|
@@ -650,7 +653,7 @@ export class RepoCommand {
|
|
|
650
653
|
throw new Error('Failed to download project archive');
|
|
651
654
|
const archiveBuffer = Buffer.from(await archiveResponse.arrayBuffer());
|
|
652
655
|
inspectZipArchive(archiveBuffer);
|
|
653
|
-
const stagingRoot = createRuntimeTempDirectory('repo-pull-');
|
|
656
|
+
const stagingRoot = createRuntimeTempDirectory('repo-pull-', 256 * 1024 * 1024);
|
|
654
657
|
const tempArchive = path.join(stagingRoot, 'archive.zip');
|
|
655
658
|
const extractedPath = path.join(stagingRoot, 'extracted');
|
|
656
659
|
try {
|
|
@@ -920,11 +923,20 @@ export class RepoCommand {
|
|
|
920
923
|
const spinner = createSpinner('Deleting project...').start();
|
|
921
924
|
try {
|
|
922
925
|
const operationId = createOperationId();
|
|
923
|
-
|
|
926
|
+
// A numeric canonical ID is safe to retry directly even after the
|
|
927
|
+
// first deletion removed it from the active repository listing.
|
|
928
|
+
const repo = /^\d+$/.test(projectName.trim())
|
|
929
|
+
? { id: projectName.trim() }
|
|
930
|
+
: await this.resolveRepoByName(projectName);
|
|
924
931
|
const response = await this.repoFetch(`/api/repo/projects/${encodeURIComponent(String(repo.id))}`, {
|
|
925
932
|
method: 'DELETE',
|
|
926
933
|
headers: { 'X-Vigthoria-Operation-Id': operationId },
|
|
927
|
-
}, { operationId });
|
|
934
|
+
}, { operationId, terminalStatuses: /^\d+$/.test(projectName.trim()) ? [404] : undefined });
|
|
935
|
+
if (response.status === 404 && /^\d+$/.test(projectName.trim())) {
|
|
936
|
+
spinner.succeed(chalk.green('Project was already deleted from Vigthoria Community Repository'));
|
|
937
|
+
console.log(chalk.gray('\nNote: Your local files are not affected.\n'));
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
928
940
|
if (!response.ok) {
|
|
929
941
|
const error = await response.json();
|
|
930
942
|
throw new Error(error.error || 'Failed to delete project');
|
|
@@ -186,7 +186,7 @@ export function registerUpdateCommand(program, version) {
|
|
|
186
186
|
let updateTempDirectory = null;
|
|
187
187
|
try {
|
|
188
188
|
if (source.kind === 'remote') {
|
|
189
|
-
updateTempDirectory = createRuntimeTempDirectory('update-');
|
|
189
|
+
updateTempDirectory = createRuntimeTempDirectory('update-', 320 * 1024 * 1024);
|
|
190
190
|
installTarget = path.join(updateTempDirectory, 'candidate.tgz');
|
|
191
191
|
await downloadFile(source.downloadUrl, installTarget);
|
|
192
192
|
}
|
|
@@ -304,7 +304,7 @@ export function registerUpdateCommand(program, version) {
|
|
|
304
304
|
&& compareVersions(manifestEntry.version, currentVersion) > 0);
|
|
305
305
|
if (manifestIsAuthoritative && manifestEntry) {
|
|
306
306
|
assertReleaseTransition(currentVersion, manifestEntry, channel, allowDowngrade);
|
|
307
|
-
const updateTempDirectory = createRuntimeTempDirectory('update-');
|
|
307
|
+
const updateTempDirectory = createRuntimeTempDirectory('update-', 320 * 1024 * 1024);
|
|
308
308
|
const tmpFile = path.join(updateTempDirectory, 'candidate.tgz');
|
|
309
309
|
try {
|
|
310
310
|
console.log(chalk.cyan(`Downloading release package (${manifestEntry.version})...`));
|
|
@@ -19,6 +19,9 @@ interface WorkflowRunOptions extends WorkflowOutputOptions {
|
|
|
19
19
|
interface WorkflowStatusOptions extends WorkflowOutputOptions {
|
|
20
20
|
brain?: boolean;
|
|
21
21
|
}
|
|
22
|
+
interface WorkflowDeleteOptions extends WorkflowOutputOptions {
|
|
23
|
+
yes?: boolean;
|
|
24
|
+
}
|
|
22
25
|
export declare class WorkflowCommand {
|
|
23
26
|
private config;
|
|
24
27
|
private logger;
|
|
@@ -35,6 +38,7 @@ export declare class WorkflowCommand {
|
|
|
35
38
|
useTemplate(templateId: string, options: WorkflowUseOptions): Promise<void>;
|
|
36
39
|
run(workflowId: string, options: WorkflowRunOptions): Promise<void>;
|
|
37
40
|
status(executionId: string, options: WorkflowStatusOptions): Promise<void>;
|
|
41
|
+
delete(selector: string, options: WorkflowDeleteOptions): Promise<void>;
|
|
38
42
|
}
|
|
39
43
|
export declare function registerWorkflowCommands(program: Command, config: Config, logger: Logger): void;
|
|
40
44
|
export {};
|
|
@@ -190,6 +190,29 @@ export class WorkflowCommand {
|
|
|
190
190
|
`Completed: ${execution.completedAt || '-'}`,
|
|
191
191
|
].join('\n'), 'Workflow Status');
|
|
192
192
|
}
|
|
193
|
+
async delete(selector, options) {
|
|
194
|
+
this.ensureAuthenticated();
|
|
195
|
+
if (options.yes !== true) {
|
|
196
|
+
throw new CliCommandError('Workflow deletion requires explicit confirmation with --yes.', {
|
|
197
|
+
code: 'WORKFLOW_DELETE_CONFIRMATION_REQUIRED',
|
|
198
|
+
category: 'usage',
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
let deleted;
|
|
202
|
+
try {
|
|
203
|
+
deleted = await this.api.deleteVigFlowWorkflow(selector);
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
throw commandFailure(error, { code: 'WORKFLOW_DELETE_FAILED' });
|
|
207
|
+
}
|
|
208
|
+
if (options.json) {
|
|
209
|
+
this.printJson('workflow delete', { deleted });
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
this.logger.success(deleted.alreadyDeleted
|
|
213
|
+
? `Workflow ${deleted.id} was already deleted.`
|
|
214
|
+
: `Workflow ${deleted.name || deleted.id} deleted.`);
|
|
215
|
+
}
|
|
193
216
|
}
|
|
194
217
|
export function registerWorkflowCommands(program, config, logger) {
|
|
195
218
|
const workflowCommand = program.command('workflow').alias('flow')
|
|
@@ -215,5 +238,9 @@ export function registerWorkflowCommands(program, config, logger) {
|
|
|
215
238
|
.option('--no-brain', 'Do not remember this workflow status in local Project Brain')
|
|
216
239
|
.option('--json', 'Emit machine-readable JSON output', false)
|
|
217
240
|
.action(async (executionId, options) => new WorkflowCommand(config, logger).status(executionId, options));
|
|
241
|
+
workflowCommand.command('delete <workflowIdOrName>').alias('rm').description('Delete an owned workflow (idempotent by workflow ID)')
|
|
242
|
+
.option('--yes', 'Confirm permanent workflow deletion', false)
|
|
243
|
+
.option('--json', 'Emit machine-readable JSON output', false)
|
|
244
|
+
.action(async (selector, options) => new WorkflowCommand(config, logger).delete(selector, options));
|
|
218
245
|
workflowCommand.action(async () => new WorkflowCommand(config, logger).templates({}));
|
|
219
246
|
}
|
package/dist/index.js
CHANGED
|
@@ -51,6 +51,7 @@ import { installConsoleRedaction } from './utils/secret-policy.js';
|
|
|
51
51
|
import { commandNameFromArgv, failureEnvelope, normalizeCommandError, CliCommandError, } from './utils/command-contract.js';
|
|
52
52
|
import { commandAuthRequirement, commanderCommandPath } from './utils/command-policy.js';
|
|
53
53
|
import { initializeRuntimeTempStorage } from './utils/runtime-temp.js';
|
|
54
|
+
import { hasLocalV3ServiceIdentity } from './utils/runtime-capability.js';
|
|
54
55
|
initializeRuntimeTempStorage();
|
|
55
56
|
applyLocalTestfarmDefaults();
|
|
56
57
|
if (process.env.VIGTHORIA_CAPTURE_RUNTIME_MODEL !== '1')
|
|
@@ -534,11 +535,12 @@ export async function main(args) {
|
|
|
534
535
|
const authRequirement = commandAuthRequirement(commandPath);
|
|
535
536
|
if (authRequirement === 'none')
|
|
536
537
|
return;
|
|
537
|
-
//
|
|
538
|
-
//
|
|
539
|
-
|
|
538
|
+
// A local V3 service identity is valid only for Agent. It must never
|
|
539
|
+
// bypass session policy for coding, repository, deploy, workflow, or any
|
|
540
|
+
// other command merely because a service key exists on this host.
|
|
541
|
+
const localAgentServiceIdentity = commandPath === 'agent' && hasLocalV3ServiceIdentity();
|
|
540
542
|
const legionCortex = commandPath === 'legion' && actionCommand.opts().cortex === true;
|
|
541
|
-
if (
|
|
543
|
+
if (localAgentServiceIdentity || legionCortex)
|
|
542
544
|
return;
|
|
543
545
|
const explicitEnvToken = Boolean(process.env.VIGTHORIA_TOKEN || process.env.VIGTHORIA_AUTH_TOKEN);
|
|
544
546
|
if (authRequirement === 'required-session' && !config.isAuthenticated() && !explicitEnvToken) {
|
|
@@ -37,6 +37,13 @@ export interface RunEvaluation {
|
|
|
37
37
|
statusHeadline: string;
|
|
38
38
|
uiTheme: 'success' | 'warning' | 'error';
|
|
39
39
|
}
|
|
40
|
+
/** Preserve mutation-journal truth while avoiding a false partial-mutation
|
|
41
|
+
* classification for a read-only run that changed no files. A failed or
|
|
42
|
+
* unknown rollback always wins over the observed final file count because a
|
|
43
|
+
* rollback can remove the changed-file snapshot while still leaving state
|
|
44
|
+
* uncertain on disk.
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolveAgentPartialMutation(changedFileCount: number, reported: unknown): false | 'unknown';
|
|
40
47
|
export declare function createLiveOutcome(): LiveOutcome;
|
|
41
48
|
export declare function isExecutorTimeoutFailure(liveOutcome: LiveOutcome): boolean;
|
|
42
49
|
export declare function isInferenceDegradedFailure(liveOutcome: LiveOutcome): boolean;
|
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
* Task-and-quality-validated success evaluation for V3 agent runs.
|
|
3
3
|
* Decouples CLI verdict from raw file-change counts (1.11.0+).
|
|
4
4
|
*/
|
|
5
|
+
/** Preserve mutation-journal truth while avoiding a false partial-mutation
|
|
6
|
+
* classification for a read-only run that changed no files. A failed or
|
|
7
|
+
* unknown rollback always wins over the observed final file count because a
|
|
8
|
+
* rollback can remove the changed-file snapshot while still leaving state
|
|
9
|
+
* uncertain on disk.
|
|
10
|
+
*/
|
|
11
|
+
export function resolveAgentPartialMutation(changedFileCount, reported) {
|
|
12
|
+
if (reported === true || reported === 'unknown')
|
|
13
|
+
return 'unknown';
|
|
14
|
+
if (reported === false)
|
|
15
|
+
return false;
|
|
16
|
+
return changedFileCount > 0 ? 'unknown' : false;
|
|
17
|
+
}
|
|
5
18
|
export function createLiveOutcome() {
|
|
6
19
|
return {
|
|
7
20
|
executorFailed: false,
|