vigthoria-cli 1.13.18 → 1.13.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/completions/_vigthoria +49 -0
- package/completions/vigthoria.bash +6 -0
- package/completions/vigthoria.fish +41 -0
- package/dist/commands/auth.d.ts +1 -0
- package/dist/commands/auth.js +84 -135
- package/dist/commands/background.d.ts +0 -2
- package/dist/commands/background.js +26 -34
- package/dist/commands/bridge.js +45 -41
- package/dist/commands/cancel.d.ts +1 -0
- package/dist/commands/cancel.js +22 -16
- package/dist/commands/chat.d.ts +5 -42
- package/dist/commands/chat.js +462 -1122
- package/dist/commands/coding-registration.d.ts +4 -0
- package/dist/commands/coding-registration.js +24 -0
- package/dist/commands/config.js +32 -42
- package/dist/commands/conversation-registration.d.ts +4 -0
- package/dist/commands/conversation-registration.js +122 -0
- package/dist/commands/deploy.d.ts +8 -2
- package/dist/commands/deploy.js +92 -90
- package/dist/commands/device.d.ts +1 -2
- package/dist/commands/device.js +37 -21
- package/dist/commands/doctor-registration.d.ts +4 -0
- package/dist/commands/doctor-registration.js +200 -0
- package/dist/commands/edit.js +22 -33
- package/dist/commands/explain.js +7 -10
- package/dist/commands/fork.d.ts +1 -2
- package/dist/commands/fork.js +72 -22
- package/dist/commands/game.d.ts +1 -1
- package/dist/commands/game.js +17 -6
- package/dist/commands/generate.js +7 -9
- package/dist/commands/history.js +11 -7
- package/dist/commands/hub.d.ts +2 -2
- package/dist/commands/hub.js +34 -35
- package/dist/commands/legion.d.ts +0 -1
- package/dist/commands/legion.js +62 -79
- package/dist/commands/music.d.ts +1 -2
- package/dist/commands/music.js +62 -27
- package/dist/commands/platform-registration.d.ts +4 -0
- package/dist/commands/platform-registration.js +45 -0
- package/dist/commands/preview.d.ts +0 -1
- package/dist/commands/preview.js +82 -35
- package/dist/commands/product-run-registration.d.ts +4 -0
- package/dist/commands/product-run-registration.js +490 -0
- package/dist/commands/replay.d.ts +1 -2
- package/dist/commands/replay.js +15 -11
- package/dist/commands/repo.d.ts +14 -7
- package/dist/commands/repo.js +245 -196
- package/dist/commands/review.js +9 -12
- package/dist/commands/security.d.ts +1 -2
- package/dist/commands/security.js +12 -6
- package/dist/commands/update-registration.d.ts +3 -0
- package/dist/commands/update-registration.js +338 -0
- package/dist/commands/v4-registration.d.ts +4 -0
- package/dist/commands/v4-registration.js +24 -0
- package/dist/commands/v4.d.ts +1 -1
- package/dist/commands/v4.js +46 -8
- package/dist/commands/wallet.d.ts +1 -3
- package/dist/commands/wallet.js +18 -45
- package/dist/commands/workflow.d.ts +2 -0
- package/dist/commands/workflow.js +52 -39
- package/dist/index.js +122 -1498
- package/dist/utils/agent-stream-state.d.ts +27 -0
- package/dist/utils/agent-stream-state.js +93 -0
- package/dist/utils/agentRoute.d.ts +2 -1
- package/dist/utils/agentRoute.js +22 -9
- package/dist/utils/agentRunOutcome.d.ts +16 -0
- package/dist/utils/agentRunOutcome.js +37 -2
- package/dist/utils/api-client-factory.d.ts +8 -0
- package/dist/utils/api-client-factory.js +8 -0
- package/dist/utils/api.d.ts +45 -269
- package/dist/utils/api.js +664 -3210
- package/dist/utils/auth-session.d.ts +41 -0
- package/dist/utils/auth-session.js +224 -0
- package/dist/utils/brain-hub-client.d.ts +0 -4
- package/dist/utils/brain-hub-client.js +6 -3
- package/dist/utils/bridge-client.js +2 -0
- package/dist/utils/capability-health-service.d.ts +48 -0
- package/dist/utils/capability-health-service.js +64 -0
- package/dist/utils/chat-prompt-policy.d.ts +18 -0
- package/dist/utils/chat-prompt-policy.js +61 -0
- package/dist/utils/chat-tool-call-parser.d.ts +11 -0
- package/dist/utils/chat-tool-call-parser.js +71 -0
- package/dist/utils/cli-state.d.ts +22 -4
- package/dist/utils/cli-state.js +105 -47
- package/dist/utils/code-operations-service.d.ts +104 -0
- package/dist/utils/code-operations-service.js +837 -0
- package/dist/utils/codebase-indexer.d.ts +1 -0
- package/dist/utils/codebase-indexer.js +28 -8
- package/dist/utils/command-contract.d.ts +61 -0
- package/dist/utils/command-contract.js +149 -0
- package/dist/utils/command-policy.d.ts +10 -0
- package/dist/utils/command-policy.js +54 -0
- package/dist/utils/config.d.ts +13 -5
- package/dist/utils/config.js +227 -99
- package/dist/utils/context-ranker.js +11 -2
- package/dist/utils/context-state.d.ts +39 -0
- package/dist/utils/context-state.js +52 -0
- package/dist/utils/deckEvents.d.ts +0 -5
- package/dist/utils/deckEvents.js +3 -2
- package/dist/utils/desktop-bridge-client.js +2 -0
- package/dist/utils/direct-output-policy.d.ts +3 -0
- package/dist/utils/direct-output-policy.js +76 -0
- package/dist/utils/durable-json.d.ts +57 -0
- package/dist/utils/durable-json.js +319 -0
- package/dist/utils/fastAgentRouter.d.ts +6 -7
- package/dist/utils/fastAgentRouter.js +21 -30
- package/dist/utils/files.js +21 -13
- package/dist/utils/frontend-preview-service.d.ts +76 -0
- package/dist/utils/frontend-preview-service.js +482 -0
- package/dist/utils/goaEvents.d.ts +0 -4
- package/dist/utils/goaEvents.js +5 -2
- package/dist/utils/logger.d.ts +2 -1
- package/dist/utils/logger.js +27 -20
- package/dist/utils/mcp-context-client.d.ts +24 -0
- package/dist/utils/mcp-context-client.js +58 -0
- package/dist/utils/model-governance.d.ts +18 -0
- package/dist/utils/model-governance.js +75 -0
- package/dist/utils/model-transport-service.d.ts +44 -0
- package/dist/utils/model-transport-service.js +111 -0
- package/dist/utils/mutation-journal.d.ts +42 -0
- package/dist/utils/mutation-journal.js +387 -0
- package/dist/utils/network-policy.d.ts +24 -0
- package/dist/utils/network-policy.js +202 -0
- package/dist/utils/operator-client.d.ts +45 -0
- package/dist/utils/operator-client.js +159 -0
- package/dist/utils/post-write-validator.js +7 -9
- package/dist/utils/preview-screenshot-adapter.d.ts +13 -0
- package/dist/utils/preview-screenshot-adapter.js +36 -0
- package/dist/utils/process-policy.d.ts +23 -0
- package/dist/utils/process-policy.js +284 -0
- package/dist/utils/project-memory.d.ts +13 -3
- package/dist/utils/project-memory.js +70 -17
- package/dist/utils/prompt-workspace-resolver.d.ts +14 -0
- package/dist/utils/prompt-workspace-resolver.js +64 -0
- package/dist/utils/release-install.d.ts +11 -0
- package/dist/utils/release-install.js +155 -0
- package/dist/utils/release-policy.d.ts +64 -0
- package/dist/utils/release-policy.js +293 -0
- package/dist/utils/requestIntent.js +22 -3
- package/dist/utils/runtime-capability.d.ts +8 -0
- package/dist/utils/runtime-capability.js +16 -0
- package/dist/utils/secret-policy.d.ts +20 -0
- package/dist/utils/secret-policy.js +160 -0
- package/dist/utils/session.d.ts +34 -2
- package/dist/utils/session.js +354 -110
- package/dist/utils/tool-approval-service.d.ts +17 -0
- package/dist/utils/tool-approval-service.js +78 -0
- package/dist/utils/tool-capability-providers.d.ts +57 -0
- package/dist/utils/tool-capability-providers.js +57 -0
- package/dist/utils/tools.d.ts +2 -23
- package/dist/utils/tools.js +216 -508
- package/dist/utils/update-policy.d.ts +18 -0
- package/dist/utils/update-policy.js +137 -0
- package/dist/utils/v3-agent-client.d.ts +40 -0
- package/dist/utils/v3-agent-client.js +165 -0
- package/dist/utils/v3-workspace-service.d.ts +28 -0
- package/dist/utils/v3-workspace-service.js +279 -0
- package/dist/utils/vigflow-client.d.ts +95 -0
- package/dist/utils/vigflow-client.js +105 -0
- package/dist/utils/workspace-boundary.d.ts +45 -0
- package/dist/utils/workspace-boundary.js +377 -0
- package/dist/utils/workspace-brain-service.d.ts +2 -0
- package/dist/utils/workspace-brain-service.js +11 -1
- package/dist/utils/workspace-cache.js +37 -15
- package/dist/utils/workspace-stream.js +35 -10
- package/install.ps1 +86 -143
- package/install.sh +76 -81
- package/package.json +44 -9
- package/release-policy.json +43 -0
- package/scripts/release/LOCAL_MACHINE_USER_VERIFICATION.md +1 -1
- package/scripts/release/generate-completions.mjs +24 -0
- package/scripts/release/install-release.mjs +10 -0
- package/scripts/release/publish-cli-release.mjs +116 -0
- package/scripts/release/publish-cli-release.sh +2 -70
- package/scripts/release/resolve-release-manifest.mjs +26 -0
- package/scripts/release/test-balanced-model-live.sh +42 -0
- package/scripts/release/validate-live-service-gates.sh +90 -0
- package/scripts/release/validate-no-go-gates.sh +135 -172
- package/scripts/release/verify-runtime-consistency.mjs +4 -1
- package/dist/commands/index.d.ts +0 -12
- package/dist/commands/index.js +0 -194
- package/dist/commands/update.d.ts +0 -9
- package/dist/commands/update.js +0 -301
- package/dist/utils/templateInstantPath.d.ts +0 -18
- package/dist/utils/templateInstantPath.js +0 -228
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as os from 'node:os';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import axios from 'axios';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import { isIOSEnvironment, resolveClientOsSlug } from '../utils/clientManifest.js';
|
|
8
|
+
import { createAPIClient } from '../utils/api-client-factory.js';
|
|
9
|
+
import { sanitizeUserFacingErrorText } from '../utils/api.js';
|
|
10
|
+
import { getCliStateFile, isOfflineMode, isUpdateCheckSuppressed } from '../utils/cli-state.js';
|
|
11
|
+
import { CliCommandError, formatSuccessJson } from '../utils/command-contract.js';
|
|
12
|
+
export function registerDoctorCommand(program, config, logger, version) {
|
|
13
|
+
program
|
|
14
|
+
.command('doctor')
|
|
15
|
+
.description('Run local Vigthoria CLI diagnostics (no network calls unless --check-api)')
|
|
16
|
+
.option('--check-api', 'Also probe Vigthoria API reachability', false)
|
|
17
|
+
.option('--json', 'Emit machine-readable JSON output', false)
|
|
18
|
+
.action(async (options) => {
|
|
19
|
+
const apiUrl = String(config.get('apiUrl') || 'https://coder.vigthoria.io').replace(/\/$/, '');
|
|
20
|
+
const modelsApiUrl = String(config.get('modelsApiUrl') || 'https://api.vigthoria.io').replace(/\/$/, '');
|
|
21
|
+
const offline = isOfflineMode();
|
|
22
|
+
const updateSuppressed = isUpdateCheckSuppressed();
|
|
23
|
+
const subscription = config.get('subscription') || { plan: null, status: null, expiresAt: null };
|
|
24
|
+
const iosSandbox = isIOSEnvironment();
|
|
25
|
+
const platformSupport = iosSandbox
|
|
26
|
+
? 'experimental-mobile-terminal-sandbox'
|
|
27
|
+
: process.platform === 'darwin'
|
|
28
|
+
? 'supported-macos-desktop'
|
|
29
|
+
: process.platform === 'win32' || process.platform === 'linux'
|
|
30
|
+
? 'supported-desktop'
|
|
31
|
+
: 'unsupported';
|
|
32
|
+
const nodeMatch = process.versions.node.match(/^(\d+)\.(\d+)/);
|
|
33
|
+
const nodeCompatible = Boolean(nodeMatch && (Number(nodeMatch[1]) > 20
|
|
34
|
+
|| (Number(nodeMatch[1]) === 20 && Number(nodeMatch[2]) >= 19)));
|
|
35
|
+
const runtimeRequire = createRequire(import.meta.url);
|
|
36
|
+
const requiredRuntimePackages = ['axios', 'commander', 'inquirer', 'ws'];
|
|
37
|
+
const missingRuntimePackages = requiredRuntimePackages.filter((name) => {
|
|
38
|
+
try {
|
|
39
|
+
runtimeRequire.resolve(name);
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
const sessionsDir = path.join(os.homedir(), '.vigthoria', 'sessions');
|
|
47
|
+
let sessionStorageWritable = false;
|
|
48
|
+
let latestCheckpoint = null;
|
|
49
|
+
try {
|
|
50
|
+
fs.mkdirSync(sessionsDir, { recursive: true, mode: 0o700 });
|
|
51
|
+
const probePath = path.join(sessionsDir, `.doctor-${process.pid}-${Date.now()}.tmp`);
|
|
52
|
+
fs.writeFileSync(probePath, '{}', { encoding: 'utf8', mode: 0o600 });
|
|
53
|
+
fs.unlinkSync(probePath);
|
|
54
|
+
sessionStorageWritable = true;
|
|
55
|
+
const latestSessionFile = fs.readdirSync(sessionsDir)
|
|
56
|
+
.filter((name) => name.endsWith('.json') && name !== 'index.json')
|
|
57
|
+
.map((name) => ({ name, mtime: fs.statSync(path.join(sessionsDir, name)).mtimeMs }))
|
|
58
|
+
.sort((left, right) => right.mtime - left.mtime)[0]?.name;
|
|
59
|
+
if (latestSessionFile) {
|
|
60
|
+
const document = JSON.parse(fs.readFileSync(path.join(sessionsDir, latestSessionFile), 'utf8'));
|
|
61
|
+
const persisted = document?.data && typeof document.data === 'object' ? document.data : document;
|
|
62
|
+
const checkpoint = persisted?.activeAgentRun;
|
|
63
|
+
if (checkpoint && typeof checkpoint === 'object') {
|
|
64
|
+
latestCheckpoint = {
|
|
65
|
+
executionId: checkpoint.executionId || null,
|
|
66
|
+
status: checkpoint.status || null,
|
|
67
|
+
updatedAt: checkpoint.updatedAt || null,
|
|
68
|
+
resumable: ['submitted', 'running', 'failed'].includes(String(checkpoint.status || '')),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
sessionStorageWritable = false;
|
|
75
|
+
}
|
|
76
|
+
const envToken = String(process.env.VIGTHORIA_AUTH_TOKEN || process.env.VIGTHORIA_TOKEN || '').trim();
|
|
77
|
+
const configuredToken = envToken || String(config.get('authToken') || '').trim();
|
|
78
|
+
const tokenSegments = configuredToken ? configuredToken.split('.').length : 0;
|
|
79
|
+
const report = {
|
|
80
|
+
cliVersion: version,
|
|
81
|
+
nodeVersion: process.version,
|
|
82
|
+
nodeCompatible,
|
|
83
|
+
runtimeDependencies: missingRuntimePackages.length === 0 ? 'complete' : `missing: ${missingRuntimePackages.join(', ')}`,
|
|
84
|
+
platform: `${process.platform} ${process.arch}`,
|
|
85
|
+
clientOs: resolveClientOsSlug(),
|
|
86
|
+
platformSupport,
|
|
87
|
+
platformLimitations: iosSandbox
|
|
88
|
+
? 'Not a native iOS app. Requires an iSH-like Linux terminal with Node.js >=20.19, npm and filesystem access; Workbench and device tooling are unavailable.'
|
|
89
|
+
: null,
|
|
90
|
+
cwd: process.cwd(),
|
|
91
|
+
homeDir: os.homedir(),
|
|
92
|
+
configPath: config.getConfigPath(),
|
|
93
|
+
stateFile: getCliStateFile(),
|
|
94
|
+
apiUrl,
|
|
95
|
+
modelsApiUrl,
|
|
96
|
+
loggedIn: config.isAuthenticated(),
|
|
97
|
+
authToken: configuredToken
|
|
98
|
+
? { present: true, source: envToken ? 'environment' : 'secure-config', structure: tokenSegments === 3 ? 'jwt' : 'opaque' }
|
|
99
|
+
: { present: false, source: null, structure: null },
|
|
100
|
+
sessionPersistence: {
|
|
101
|
+
directory: sessionsDir,
|
|
102
|
+
writable: sessionStorageWritable,
|
|
103
|
+
latestCheckpoint,
|
|
104
|
+
},
|
|
105
|
+
subscriptionPlan: subscription.plan || null,
|
|
106
|
+
subscriptionStatus: subscription.status || null,
|
|
107
|
+
offlineMode: offline,
|
|
108
|
+
updateCheckSuppressed: updateSuppressed,
|
|
109
|
+
envOverrides: {
|
|
110
|
+
VIGTHORIA_API_URL: process.env.VIGTHORIA_API_URL || null,
|
|
111
|
+
VIGTHORIA_V3_AGENT_URL: process.env.VIGTHORIA_V3_AGENT_URL || null,
|
|
112
|
+
VIGTHORIA_OFFLINE: process.env.VIGTHORIA_OFFLINE || null,
|
|
113
|
+
VIGTHORIA_TOKEN: process.env.VIGTHORIA_TOKEN ? '<set>' : null,
|
|
114
|
+
HYPERLOOP_SERVICE_KEY: process.env.HYPERLOOP_SERVICE_KEY ? '<set>' : null,
|
|
115
|
+
V3_SERVICE_KEY: process.env.V3_SERVICE_KEY ? '<set>' : null,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
if (options.checkApi && !offline) {
|
|
119
|
+
const api = createAPIClient(config, logger);
|
|
120
|
+
if (configuredToken) {
|
|
121
|
+
try {
|
|
122
|
+
const tokenCheck = await api.validateToken({ allowNetworkFailOpen: false, enforceTokenShape: true });
|
|
123
|
+
report.authValidation = tokenCheck.valid ? 'authenticated' : `failed (${tokenCheck.error || 'invalid token'})`;
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
report.authValidation = `unreachable (${sanitizeUserFacingErrorText(error.message)})`;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
report.authValidation = 'not checked (login required)';
|
|
131
|
+
}
|
|
132
|
+
try {
|
|
133
|
+
const probe = await axios.get(`${apiUrl}/api/health`, { timeout: 12000, validateStatus: () => true });
|
|
134
|
+
report.apiHealth = probe.status >= 200 && probe.status < 400 ? 'online' : `status ${probe.status}`;
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
report.apiHealth = `unreachable (${error.message})`;
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
const probe = await axios.get(`${modelsApiUrl}/health`, { timeout: 12000, validateStatus: () => true });
|
|
141
|
+
const body = typeof probe.data === 'string' ? probe.data : JSON.stringify(probe.data || {});
|
|
142
|
+
report.modelsApiHealth = probe.status >= 200 && probe.status < 400
|
|
143
|
+
? 'online'
|
|
144
|
+
: probe.status === 503 && /pre-release validation|closed/i.test(body)
|
|
145
|
+
? 'pre-release-closed (Agent mode uses the Coder gateway)'
|
|
146
|
+
: `status ${probe.status}`;
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
report.modelsApiHealth = `unreachable (${error.message})`;
|
|
150
|
+
}
|
|
151
|
+
if (configuredToken) {
|
|
152
|
+
try {
|
|
153
|
+
const capabilities = await api.getCapabilityTruthStatus({ workspacePath: process.cwd() });
|
|
154
|
+
report.agentInfrastructure = {
|
|
155
|
+
overall: capabilities.overallOk ? 'online' : 'degraded',
|
|
156
|
+
v3Agent: capabilities.v3Agent.ok ? 'online' : capabilities.v3Agent.error || 'offline',
|
|
157
|
+
hyperLoop: capabilities.hyperLoop.ok ? 'online' : capabilities.hyperLoop.error || 'offline',
|
|
158
|
+
repoMemory: capabilities.repoMemory.ok ? 'online' : capabilities.repoMemory.error || 'offline',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
report.agentInfrastructure = `unreachable (${sanitizeUserFacingErrorText(error.message)})`;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const printHumanReport = () => {
|
|
167
|
+
console.log(chalk.bold('Vigthoria CLI diagnostics'));
|
|
168
|
+
for (const [key, value] of Object.entries(report)) {
|
|
169
|
+
if (value && typeof value === 'object') {
|
|
170
|
+
console.log(chalk.gray(`- ${key}:`));
|
|
171
|
+
for (const [subKey, subVal] of Object.entries(value)) {
|
|
172
|
+
console.log(chalk.gray(` - ${subKey}: ${subVal ?? '(none)'}`));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
console.log(chalk.gray(`- ${key}: ${value ?? '(none)'}`));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (!options.checkApi) {
|
|
180
|
+
console.log(chalk.gray('\nTip: pass --check-api to verify API reachability.'));
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
const requiredLocalChecksPassed = nodeCompatible && missingRuntimePackages.length === 0 && sessionStorageWritable;
|
|
184
|
+
const coderReachable = !options.checkApi || offline || report.apiHealth === 'online';
|
|
185
|
+
if (!requiredLocalChecksPassed || !coderReachable) {
|
|
186
|
+
if (!options.json)
|
|
187
|
+
printHumanReport();
|
|
188
|
+
throw new CliCommandError('One or more required doctor checks failed.', {
|
|
189
|
+
code: 'DOCTOR_CHECK_FAILED',
|
|
190
|
+
details: report,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
if (options.json) {
|
|
194
|
+
console.log(formatSuccessJson('doctor', report));
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
printHumanReport();
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
}
|
package/dist/commands/edit.js
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import inquirer from 'inquirer';
|
|
6
6
|
import { createSpinner } from '../utils/logger.js';
|
|
7
|
-
import {
|
|
7
|
+
import { createAPIClient } from '../utils/api-client-factory.js';
|
|
8
8
|
import { FileUtils } from '../utils/files.js';
|
|
9
|
+
import { CliCommandError, commandFailure } from '../utils/command-contract.js';
|
|
9
10
|
export class EditCommand {
|
|
10
11
|
config;
|
|
11
12
|
logger;
|
|
@@ -14,26 +15,23 @@ export class EditCommand {
|
|
|
14
15
|
constructor(config, logger) {
|
|
15
16
|
this.config = config;
|
|
16
17
|
this.logger = logger;
|
|
17
|
-
this.api =
|
|
18
|
+
this.api = createAPIClient(config, logger);
|
|
18
19
|
this.fileUtils = new FileUtils(process.cwd(), config.get('project').ignorePatterns);
|
|
19
20
|
}
|
|
20
21
|
async run(filePath, options) {
|
|
21
22
|
// Check auth
|
|
22
23
|
if (!this.config.isAuthenticated()) {
|
|
23
|
-
|
|
24
|
-
return;
|
|
24
|
+
throw new CliCommandError('Not authenticated. Run: vigthoria login', { code: 'AUTH_REQUIRED', category: 'authentication' });
|
|
25
25
|
}
|
|
26
26
|
// Server-side token validation — fail fast instead of waiting for 401
|
|
27
27
|
const tokenCheck = await this.api.validateToken();
|
|
28
28
|
if (!tokenCheck.valid) {
|
|
29
|
-
|
|
30
|
-
return;
|
|
29
|
+
throw new CliCommandError(tokenCheck.error || 'Auth token is invalid. Run: vigthoria login', { code: 'AUTH_SESSION_INVALID', category: 'authentication' });
|
|
31
30
|
}
|
|
32
31
|
// Read file
|
|
33
32
|
const file = this.fileUtils.readFile(filePath);
|
|
34
33
|
if (!file) {
|
|
35
|
-
|
|
36
|
-
return;
|
|
34
|
+
throw new CliCommandError(`File not found: ${filePath}`, { code: 'FILE_NOT_FOUND', category: 'configuration' });
|
|
37
35
|
}
|
|
38
36
|
this.logger.section(`Editing: ${file.relativePath}`);
|
|
39
37
|
console.log(chalk.gray(`Language: ${file.language} | Lines: ${file.lines}`));
|
|
@@ -43,13 +41,11 @@ export class EditCommand {
|
|
|
43
41
|
if (!instruction) {
|
|
44
42
|
// --apply requires --instruction to avoid hanging on non-interactive terminals
|
|
45
43
|
if (options.apply) {
|
|
46
|
-
|
|
47
|
-
return;
|
|
44
|
+
throw new CliCommandError('The --apply flag requires --instruction. Example: vigthoria edit file.ts --apply --instruction "fix the bug"', { code: 'INVALID_OPTION_COMBINATION', category: 'usage' });
|
|
48
45
|
}
|
|
49
46
|
// Non-TTY stdin cannot prompt interactively
|
|
50
47
|
if (!process.stdin.isTTY) {
|
|
51
|
-
|
|
52
|
-
return;
|
|
48
|
+
throw new CliCommandError('No --instruction provided and stdin is not interactive. Use: vigthoria edit file.ts --instruction "..."', { code: 'INSTRUCTION_REQUIRED', category: 'usage' });
|
|
53
49
|
}
|
|
54
50
|
const answer = await inquirer.prompt([
|
|
55
51
|
{
|
|
@@ -100,15 +96,14 @@ Return the complete modified file content:`,
|
|
|
100
96
|
// Extract code from response
|
|
101
97
|
let modifiedCode = this.extractCode(response.message, file.language);
|
|
102
98
|
if (!modifiedCode) {
|
|
103
|
-
|
|
104
|
-
return;
|
|
99
|
+
throw new CliCommandError('Failed to generate valid code changes', { code: 'EDIT_RESPONSE_INVALID' });
|
|
105
100
|
}
|
|
106
101
|
// Always deduplicate — extractCode only dedupes on the no-fence
|
|
107
102
|
// fallback path, so fenced responses (the common case) need this.
|
|
108
103
|
modifiedCode = this.deduplicateCode(modifiedCode);
|
|
109
104
|
// Show diff and apply
|
|
110
105
|
if (options.apply) {
|
|
111
|
-
await this.applyFix(file.path,
|
|
106
|
+
await this.applyFix(file.path, modifiedCode);
|
|
112
107
|
}
|
|
113
108
|
else {
|
|
114
109
|
await this.showDiffAndConfirm(file.path, file.content, modifiedCode);
|
|
@@ -116,27 +111,23 @@ Return the complete modified file content:`,
|
|
|
116
111
|
}
|
|
117
112
|
catch (error) {
|
|
118
113
|
spinner.stop();
|
|
119
|
-
|
|
120
|
-
this.logger.error(formatCLIError(cliErr));
|
|
114
|
+
throw commandFailure(error, { code: 'EDIT_FAILED' });
|
|
121
115
|
}
|
|
122
116
|
}
|
|
123
117
|
async fix(filePath, options) {
|
|
124
118
|
// Check auth
|
|
125
119
|
if (!this.config.isAuthenticated()) {
|
|
126
|
-
|
|
127
|
-
return;
|
|
120
|
+
throw new CliCommandError('Not authenticated. Run: vigthoria login', { code: 'AUTH_REQUIRED', category: 'authentication' });
|
|
128
121
|
}
|
|
129
122
|
// Server-side token validation — fail fast instead of waiting for 401
|
|
130
123
|
const tokenCheck = await this.api.validateToken();
|
|
131
124
|
if (!tokenCheck.valid) {
|
|
132
|
-
|
|
133
|
-
return;
|
|
125
|
+
throw new CliCommandError(tokenCheck.error || 'Auth token is invalid. Run: vigthoria login', { code: 'AUTH_SESSION_INVALID', category: 'authentication' });
|
|
134
126
|
}
|
|
135
127
|
// Read file
|
|
136
128
|
const file = this.fileUtils.readFile(filePath);
|
|
137
129
|
if (!file) {
|
|
138
|
-
|
|
139
|
-
return;
|
|
130
|
+
throw new CliCommandError(`File not found: ${filePath}`, { code: 'FILE_NOT_FOUND', category: 'configuration' });
|
|
140
131
|
}
|
|
141
132
|
this.logger.section(`Fixing: ${file.relativePath}`);
|
|
142
133
|
console.log(chalk.gray(`Fix type: ${options.type} | Language: ${file.language}`));
|
|
@@ -159,7 +150,7 @@ Return the complete modified file content:`,
|
|
|
159
150
|
console.log(chalk.yellow('1. Operator/character-level fix detected'));
|
|
160
151
|
console.log();
|
|
161
152
|
if (options.apply) {
|
|
162
|
-
await this.applyFix(file.path,
|
|
153
|
+
await this.applyFix(file.path, result.fixed);
|
|
163
154
|
}
|
|
164
155
|
else {
|
|
165
156
|
await this.showDiffAndConfirm(file.path, file.content, result.fixed);
|
|
@@ -180,7 +171,7 @@ Return the complete modified file content:`,
|
|
|
180
171
|
});
|
|
181
172
|
// Apply or confirm
|
|
182
173
|
if (options.apply) {
|
|
183
|
-
await this.applyFix(file.path,
|
|
174
|
+
await this.applyFix(file.path, result.fixed);
|
|
184
175
|
}
|
|
185
176
|
else {
|
|
186
177
|
await this.showDiffAndConfirm(file.path, file.content, result.fixed);
|
|
@@ -188,8 +179,7 @@ Return the complete modified file content:`,
|
|
|
188
179
|
}
|
|
189
180
|
catch (error) {
|
|
190
181
|
spinner.stop();
|
|
191
|
-
|
|
192
|
-
this.logger.error(formatCLIError(cliErr));
|
|
182
|
+
throw commandFailure(error, { code: 'FIX_FAILED' });
|
|
193
183
|
}
|
|
194
184
|
}
|
|
195
185
|
extractCode(response, language) {
|
|
@@ -359,10 +349,9 @@ Return the complete modified file content:`,
|
|
|
359
349
|
});
|
|
360
350
|
console.log();
|
|
361
351
|
// Confirm
|
|
362
|
-
const showDiffs = this.config.get('preferences').showDiffs;
|
|
363
352
|
const autoApply = this.config.get('preferences').autoApplyFixes;
|
|
364
353
|
if (autoApply) {
|
|
365
|
-
await this.applyFix(filePath,
|
|
354
|
+
await this.applyFix(filePath, modified);
|
|
366
355
|
return;
|
|
367
356
|
}
|
|
368
357
|
// Non-TTY: show the diff but don't try to prompt — re-run with --apply
|
|
@@ -384,7 +373,7 @@ Return the complete modified file content:`,
|
|
|
384
373
|
]);
|
|
385
374
|
switch (action) {
|
|
386
375
|
case 'apply':
|
|
387
|
-
await this.applyFix(filePath,
|
|
376
|
+
await this.applyFix(filePath, modified);
|
|
388
377
|
break;
|
|
389
378
|
case 'view':
|
|
390
379
|
this.showFullDiff(original, modified);
|
|
@@ -397,7 +386,7 @@ Return the complete modified file content:`,
|
|
|
397
386
|
},
|
|
398
387
|
]);
|
|
399
388
|
if (confirm) {
|
|
400
|
-
await this.applyFix(filePath,
|
|
389
|
+
await this.applyFix(filePath, modified);
|
|
401
390
|
}
|
|
402
391
|
break;
|
|
403
392
|
case 'discard':
|
|
@@ -463,7 +452,7 @@ Return the complete modified file content:`,
|
|
|
463
452
|
console.log(chalk.gray('─'.repeat(60)));
|
|
464
453
|
console.log();
|
|
465
454
|
}
|
|
466
|
-
async applyFix(filePath,
|
|
455
|
+
async applyFix(filePath, modified) {
|
|
467
456
|
// Create backup
|
|
468
457
|
const backup = this.fileUtils.backupFile(filePath);
|
|
469
458
|
if (backup) {
|
|
@@ -474,7 +463,7 @@ Return the complete modified file content:`,
|
|
|
474
463
|
this.logger.success(`Changes applied to ${filePath}`);
|
|
475
464
|
}
|
|
476
465
|
else {
|
|
477
|
-
|
|
466
|
+
throw new CliCommandError(`Failed to write changes to ${filePath}`, { code: 'FILE_WRITE_FAILED' });
|
|
478
467
|
}
|
|
479
468
|
}
|
|
480
469
|
}
|
package/dist/commands/explain.js
CHANGED
|
@@ -5,8 +5,9 @@ import chalk from 'chalk';
|
|
|
5
5
|
import { Marked } from 'marked';
|
|
6
6
|
import { markedTerminal } from 'marked-terminal';
|
|
7
7
|
import { createSpinner } from '../utils/logger.js';
|
|
8
|
-
import {
|
|
8
|
+
import { createAPIClient } from '../utils/api-client-factory.js';
|
|
9
9
|
import { FileUtils } from '../utils/files.js';
|
|
10
|
+
import { CliCommandError, commandFailure } from '../utils/command-contract.js';
|
|
10
11
|
export class ExplainCommand {
|
|
11
12
|
config;
|
|
12
13
|
logger;
|
|
@@ -16,7 +17,7 @@ export class ExplainCommand {
|
|
|
16
17
|
constructor(config, logger) {
|
|
17
18
|
this.config = config;
|
|
18
19
|
this.logger = logger;
|
|
19
|
-
this.api =
|
|
20
|
+
this.api = createAPIClient(config, logger);
|
|
20
21
|
this.fileUtils = new FileUtils(process.cwd(), config.get('project').ignorePatterns);
|
|
21
22
|
this.marked = new Marked();
|
|
22
23
|
this.marked.use(markedTerminal({
|
|
@@ -29,20 +30,17 @@ export class ExplainCommand {
|
|
|
29
30
|
async run(filePath, options) {
|
|
30
31
|
// Check auth
|
|
31
32
|
if (!this.config.isAuthenticated()) {
|
|
32
|
-
|
|
33
|
-
return;
|
|
33
|
+
throw new CliCommandError('Not authenticated. Run: vigthoria login', { code: 'AUTH_REQUIRED', category: 'authentication' });
|
|
34
34
|
}
|
|
35
35
|
// Server-side token validation — fail fast instead of waiting for 401
|
|
36
36
|
const tokenCheck = await this.api.validateToken();
|
|
37
37
|
if (!tokenCheck.valid) {
|
|
38
|
-
|
|
39
|
-
return;
|
|
38
|
+
throw new CliCommandError(tokenCheck.error || 'Auth token is invalid. Run: vigthoria login', { code: 'AUTH_SESSION_INVALID', category: 'authentication' });
|
|
40
39
|
}
|
|
41
40
|
// Read file
|
|
42
41
|
const file = this.fileUtils.readFile(filePath);
|
|
43
42
|
if (!file) {
|
|
44
|
-
|
|
45
|
-
return;
|
|
43
|
+
throw new CliCommandError(`File not found: ${filePath}`, { code: 'FILE_NOT_FOUND', category: 'configuration' });
|
|
46
44
|
}
|
|
47
45
|
// Get code to explain
|
|
48
46
|
let codeToExplain = file.content;
|
|
@@ -80,8 +78,7 @@ export class ExplainCommand {
|
|
|
80
78
|
}
|
|
81
79
|
catch (error) {
|
|
82
80
|
spinner.stop();
|
|
83
|
-
|
|
84
|
-
this.logger.error(formatCLIError(cliErr));
|
|
81
|
+
throw commandFailure(error, { code: 'EXPLAIN_FAILED' });
|
|
85
82
|
}
|
|
86
83
|
}
|
|
87
84
|
/**
|
package/dist/commands/fork.d.ts
CHANGED
|
@@ -7,8 +7,7 @@ interface ForkOptions {
|
|
|
7
7
|
}
|
|
8
8
|
export declare class ForkCommand {
|
|
9
9
|
private config;
|
|
10
|
-
|
|
11
|
-
constructor(config: Config, logger: Logger);
|
|
10
|
+
constructor(config: Config, _logger: Logger);
|
|
12
11
|
private getHeaders;
|
|
13
12
|
private getBaseUrl;
|
|
14
13
|
private resolveWorkspaceRoot;
|
package/dist/commands/fork.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { hasLocalV3AgentCapability } from '../utils/runtime-capability.js';
|
|
2
2
|
/**
|
|
3
3
|
* fork.ts — Fork from an existing V3 agent run and stream the result.
|
|
4
4
|
*/
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import { createRequire } from 'node:module';
|
|
7
7
|
import { createSpinner, CH } from '../utils/logger.js';
|
|
8
|
+
import { CliCommandError, commandFailure, formatSuccessJson } from '../utils/command-contract.js';
|
|
8
9
|
const require = createRequire(import.meta.url);
|
|
9
10
|
export class ForkCommand {
|
|
10
11
|
config;
|
|
11
|
-
|
|
12
|
-
constructor(config, logger) {
|
|
12
|
+
constructor(config, _logger) {
|
|
13
13
|
this.config = config;
|
|
14
|
-
this.logger = logger;
|
|
15
14
|
}
|
|
16
15
|
getHeaders() {
|
|
17
16
|
const headers = { 'Content-Type': 'application/json' };
|
|
@@ -29,7 +28,7 @@ export class ForkCommand {
|
|
|
29
28
|
}
|
|
30
29
|
getBaseUrl() {
|
|
31
30
|
const configuredApiUrl = String(this.config.get('apiUrl') || 'https://coder.vigthoria.io').replace(/\/$/, '');
|
|
32
|
-
const allowLocal =
|
|
31
|
+
const allowLocal = hasLocalV3AgentCapability();
|
|
33
32
|
return (process.env.VIGTHORIA_V3_AGENT_URL ||
|
|
34
33
|
process.env.V3_AGENT_URL ||
|
|
35
34
|
(allowLocal ? 'http://127.0.0.1:8030' : null) ||
|
|
@@ -53,6 +52,13 @@ export class ForkCommand {
|
|
|
53
52
|
const workspace = this.resolveWorkspaceRoot(project);
|
|
54
53
|
const eventIndex = options.eventIndex || 0;
|
|
55
54
|
const spinner = createSpinner(`Forking run ${runId}...`).start();
|
|
55
|
+
const streamController = new AbortController();
|
|
56
|
+
let interrupted = false;
|
|
57
|
+
const onInterrupt = () => {
|
|
58
|
+
interrupted = true;
|
|
59
|
+
streamController.abort();
|
|
60
|
+
};
|
|
61
|
+
process.once('SIGINT', onInterrupt);
|
|
56
62
|
try {
|
|
57
63
|
const baseUrl = this.getBaseUrl();
|
|
58
64
|
const body = {
|
|
@@ -66,33 +72,57 @@ export class ForkCommand {
|
|
|
66
72
|
method: 'POST',
|
|
67
73
|
headers: this.getHeaders(),
|
|
68
74
|
body: JSON.stringify(body),
|
|
75
|
+
signal: streamController.signal,
|
|
69
76
|
});
|
|
70
77
|
if (!resp.ok) {
|
|
71
78
|
spinner.stop();
|
|
72
79
|
if (resp.status === 404) {
|
|
73
|
-
|
|
80
|
+
throw new CliCommandError(`Run ${runId} not found or has no event log.`, {
|
|
81
|
+
code: 'RUN_NOT_FOUND', status: resp.status,
|
|
82
|
+
});
|
|
74
83
|
}
|
|
75
84
|
else if (resp.status === 502 || resp.status === 503) {
|
|
76
|
-
|
|
85
|
+
throw new CliCommandError(`V3 run-history route is not available on ${baseUrl}. Backend may not expose /api/runs.`, {
|
|
86
|
+
code: 'RUN_HISTORY_UNAVAILABLE', status: resp.status, category: 'network',
|
|
87
|
+
});
|
|
77
88
|
}
|
|
78
89
|
else {
|
|
79
|
-
|
|
90
|
+
throw new CliCommandError(`Fork failed: ${resp.status} ${resp.statusText}`, {
|
|
91
|
+
code: 'RUN_FORK_FAILED', status: resp.status,
|
|
92
|
+
});
|
|
80
93
|
}
|
|
81
|
-
return;
|
|
82
94
|
}
|
|
83
95
|
spinner.stop();
|
|
84
|
-
|
|
96
|
+
if (!options.json)
|
|
97
|
+
console.log(chalk.bold(`\n${CH.success} Forked from ${chalk.cyan(runId)} at event ${eventIndex}\n`));
|
|
85
98
|
if (!resp.body) {
|
|
86
|
-
|
|
87
|
-
return;
|
|
99
|
+
throw new CliCommandError('Fork response did not contain an event stream', { code: 'RUN_FORK_STREAM_MISSING' });
|
|
88
100
|
}
|
|
89
101
|
// Stream SSE events
|
|
90
102
|
const reader = resp.body.getReader();
|
|
91
103
|
const decoder = new TextDecoder();
|
|
92
104
|
let buffer = '';
|
|
93
105
|
let toolCallNum = 0;
|
|
94
|
-
|
|
95
|
-
|
|
106
|
+
let completed = false;
|
|
107
|
+
const events = [];
|
|
108
|
+
const configuredIdle = Number.parseInt(process.env.VIGTHORIA_FORK_IDLE_TIMEOUT_MS || '', 10);
|
|
109
|
+
const idleTimeoutMs = Number.isFinite(configuredIdle) && configuredIdle > 0 ? configuredIdle : 60_000;
|
|
110
|
+
stream: while (true) {
|
|
111
|
+
let timer;
|
|
112
|
+
const { done, value } = await Promise.race([
|
|
113
|
+
reader.read(),
|
|
114
|
+
new Promise((_resolve, reject) => {
|
|
115
|
+
timer = setTimeout(() => {
|
|
116
|
+
streamController.abort();
|
|
117
|
+
reject(new CliCommandError(`Fork event stream was idle for ${idleTimeoutMs}ms.`, {
|
|
118
|
+
code: 'RUN_FORK_IDLE_TIMEOUT', category: 'network',
|
|
119
|
+
}));
|
|
120
|
+
}, idleTimeoutMs);
|
|
121
|
+
}),
|
|
122
|
+
]).finally(() => {
|
|
123
|
+
if (timer)
|
|
124
|
+
clearTimeout(timer);
|
|
125
|
+
});
|
|
96
126
|
if (done)
|
|
97
127
|
break;
|
|
98
128
|
buffer += decoder.decode(value, { stream: true });
|
|
@@ -103,14 +133,14 @@ export class ForkCommand {
|
|
|
103
133
|
continue;
|
|
104
134
|
const payload = line.slice(6).trim();
|
|
105
135
|
if (payload === '[DONE]') {
|
|
106
|
-
|
|
107
|
-
|
|
136
|
+
completed = true;
|
|
137
|
+
break stream;
|
|
108
138
|
}
|
|
109
139
|
try {
|
|
110
140
|
const evt = JSON.parse(payload);
|
|
111
141
|
const type = evt.type || 'unknown';
|
|
142
|
+
events.push(evt);
|
|
112
143
|
if (options.json) {
|
|
113
|
-
console.log(JSON.stringify(evt));
|
|
114
144
|
continue;
|
|
115
145
|
}
|
|
116
146
|
switch (type) {
|
|
@@ -144,21 +174,41 @@ export class ForkCommand {
|
|
|
144
174
|
break;
|
|
145
175
|
}
|
|
146
176
|
case 'error':
|
|
147
|
-
|
|
148
|
-
|
|
177
|
+
throw new CliCommandError(String(evt.message || 'Fork stream reported an error.'), {
|
|
178
|
+
code: 'RUN_FORK_STREAM_ERROR', details: evt,
|
|
179
|
+
});
|
|
149
180
|
default:
|
|
150
181
|
console.log(chalk.dim(` ${type}: ${JSON.stringify(evt).substring(0, 80)}`));
|
|
151
182
|
}
|
|
152
183
|
}
|
|
153
|
-
catch {
|
|
154
|
-
|
|
184
|
+
catch (error) {
|
|
185
|
+
if (error instanceof CliCommandError)
|
|
186
|
+
throw error;
|
|
187
|
+
throw new CliCommandError('Fork event stream contained malformed JSON.', {
|
|
188
|
+
code: 'RUN_FORK_STREAM_MALFORMED', details: { payload: payload.slice(0, 200) }, cause: error,
|
|
189
|
+
});
|
|
155
190
|
}
|
|
156
191
|
}
|
|
157
192
|
}
|
|
193
|
+
if (!completed) {
|
|
194
|
+
throw new CliCommandError('Fork event stream ended without a completion marker.', { code: 'RUN_FORK_STREAM_INCOMPLETE' });
|
|
195
|
+
}
|
|
196
|
+
if (options.json) {
|
|
197
|
+
console.log(formatSuccessJson('fork', { runId, eventIndex, events }, { stream: true, eventCount: events.length }));
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
console.log(chalk.bold(`\n${CH.success} Fork run complete\n`));
|
|
201
|
+
}
|
|
158
202
|
}
|
|
159
203
|
catch (err) {
|
|
160
204
|
spinner.stop();
|
|
161
|
-
|
|
205
|
+
if (interrupted) {
|
|
206
|
+
throw new CliCommandError('Fork cancelled by user.', { code: 'RUN_FORK_CANCELLED', category: 'cancelled', cause: err });
|
|
207
|
+
}
|
|
208
|
+
throw commandFailure(err, { code: 'RUN_FORK_FAILED' });
|
|
209
|
+
}
|
|
210
|
+
finally {
|
|
211
|
+
process.removeListener('SIGINT', onInterrupt);
|
|
162
212
|
}
|
|
163
213
|
}
|
|
164
214
|
}
|