vigthoria-cli 1.11.19 → 1.11.27
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.d.ts +1 -0
- package/dist/commands/chat.js +16 -12
- package/dist/commands/index.js +6 -0
- package/dist/commands/music.d.ts +31 -0
- package/dist/commands/music.js +137 -0
- package/dist/index.js +33 -2
- package/dist/utils/agentRunOutcome.d.ts +1 -0
- package/dist/utils/agentRunOutcome.js +23 -0
- package/dist/utils/api.d.ts +1 -0
- package/dist/utils/api.js +165 -71
- package/dist/utils/config.d.ts +1 -0
- package/dist/utils/config.js +2 -0
- package/dist/utils/requestIntent.js +11 -6
- package/dist/utils/templateInstantPath.js +89 -11
- package/dist/utils/tools.js +1 -0
- package/dist/utils/v3-workspace-path.js +15 -1
- package/install.ps1 +17 -9
- package/install.sh +45 -2
- package/package.json +1 -1
- package/scripts/release/publish-cli-release.sh +1 -1
package/dist/commands/chat.d.ts
CHANGED
package/dist/commands/chat.js
CHANGED
|
@@ -23,26 +23,23 @@ import { runTemplateInstantPath } from '../utils/templateInstantPath.js';
|
|
|
23
23
|
const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
|
|
24
24
|
const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS;
|
|
25
25
|
if (!rawValue) {
|
|
26
|
-
return
|
|
26
|
+
return 0;
|
|
27
27
|
}
|
|
28
28
|
const parsed = Number.parseInt(rawValue, 10);
|
|
29
|
-
return Number.isFinite(parsed) && parsed
|
|
29
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
|
|
30
30
|
})();
|
|
31
31
|
const DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS = (() => {
|
|
32
32
|
const rawValue = process.env.VIGTHORIA_AGENT_IDLE_TIMEOUT_MS || process.env.V3_AGENT_IDLE_TIMEOUT_MS;
|
|
33
33
|
if (!rawValue) {
|
|
34
|
-
return
|
|
34
|
+
return 0;
|
|
35
35
|
}
|
|
36
36
|
const parsed = Number.parseInt(rawValue, 10);
|
|
37
|
-
return Number.isFinite(parsed) && parsed
|
|
37
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
|
|
38
38
|
})();
|
|
39
39
|
const DEFAULT_V3_AGENT_SOFT_TIMEOUT_MS = (() => {
|
|
40
|
-
const rawValue = process.env.VIGTHORIA_AGENT_SOFT_TIMEOUT_MS || process.env.V3_AGENT_SOFT_TIMEOUT_MS;
|
|
41
|
-
if (!rawValue) {
|
|
42
|
-
return 180000;
|
|
43
|
-
}
|
|
40
|
+
const rawValue = process.env.VIGTHORIA_AGENT_SOFT_TIMEOUT_MS || process.env.V3_AGENT_SOFT_TIMEOUT_MS || '300000';
|
|
44
41
|
const parsed = Number.parseInt(rawValue, 10);
|
|
45
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed :
|
|
42
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 300000;
|
|
46
43
|
})();
|
|
47
44
|
export class ChatCommand {
|
|
48
45
|
config;
|
|
@@ -77,6 +74,7 @@ export class ChatCommand {
|
|
|
77
74
|
v3StreamedAnswerDisplayed = false;
|
|
78
75
|
v3SeenToolCalls = new Set();
|
|
79
76
|
v3SeenToolResults = new Set();
|
|
77
|
+
v3StartSeen = false;
|
|
80
78
|
lastAgentRoute = null;
|
|
81
79
|
// Last completed Agent run — used by /retry, /continue, and the final summary block.
|
|
82
80
|
lastAgentRunOutcome = null;
|
|
@@ -299,7 +297,7 @@ export class ChatCommand {
|
|
|
299
297
|
resolveAgentExecutionPolicy(prompt) {
|
|
300
298
|
const explicitModel = this.modelExplicitlySelected;
|
|
301
299
|
const heavyTask = this.config.isComplexTask(prompt);
|
|
302
|
-
const cloudEligible = this.config.hasCloudAccess()
|
|
300
|
+
const cloudEligible = this.config.hasCloudAccess();
|
|
303
301
|
const requiresV3Workflow = this.shouldRequireV3AgentWorkflow(prompt);
|
|
304
302
|
if (explicitModel) {
|
|
305
303
|
return {
|
|
@@ -1282,8 +1280,11 @@ export class ChatCommand {
|
|
|
1282
1280
|
return;
|
|
1283
1281
|
}
|
|
1284
1282
|
if (event.type === 'start') {
|
|
1285
|
-
this.
|
|
1286
|
-
|
|
1283
|
+
if (this.v3StartSeen) {
|
|
1284
|
+
spinner.text = 'Working...';
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
this.v3StartSeen = true;
|
|
1287
1288
|
if (spinner.isSpinning)
|
|
1288
1289
|
spinner.stop();
|
|
1289
1290
|
process.stderr.write(chalk.cyan(' [Start] ') + 'Agent initialized\n');
|
|
@@ -2793,6 +2794,7 @@ export class ChatCommand {
|
|
|
2793
2794
|
console.log(chalk.gray('━━━ ROUTING DECISION ━━━'));
|
|
2794
2795
|
console.log(chalk.gray(`Reason: ${routingPolicy.routeReason}`));
|
|
2795
2796
|
console.log(chalk.gray(`Model: ${routingPolicy.selectedModel}`));
|
|
2797
|
+
console.log(chalk.gray('Remote Engine Session: true'));
|
|
2796
2798
|
console.log(chalk.gray(`Cloud Eligible: ${routingPolicy.cloudEligible}`));
|
|
2797
2799
|
console.log(chalk.gray(`Cloud Selected: ${routingPolicy.cloudSelected}`));
|
|
2798
2800
|
if (routingPolicy.heavyTask) {
|
|
@@ -2808,6 +2810,7 @@ export class ChatCommand {
|
|
|
2808
2810
|
this.v3StreamingStarted = false;
|
|
2809
2811
|
this.v3StreamedAnswerBuffer = '';
|
|
2810
2812
|
this.v3StreamedAnswerDisplayed = false;
|
|
2813
|
+
this.v3StartSeen = false;
|
|
2811
2814
|
this.v3SeenToolCalls.clear();
|
|
2812
2815
|
this.v3SeenToolResults.clear();
|
|
2813
2816
|
emitDeckEvent({ type: 'agent_start' });
|
|
@@ -3363,6 +3366,7 @@ export class ChatCommand {
|
|
|
3363
3366
|
this.v3ToolCallCount = 0;
|
|
3364
3367
|
this.v3LastActivity = Date.now();
|
|
3365
3368
|
this.v3StreamingStarted = false;
|
|
3369
|
+
this.v3StartSeen = false;
|
|
3366
3370
|
try {
|
|
3367
3371
|
const retryTaskType = this.inferAgentTaskType(rawPrompt);
|
|
3368
3372
|
const retryResponse = await this.api.runV3AgentWorkflow(executionPrompt, {
|
package/dist/commands/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { GenerateCommand } from './generate.js';
|
|
|
11
11
|
import { HistoryCommand } from './history.js';
|
|
12
12
|
import { HubCommand } from './hub.js';
|
|
13
13
|
import { LegionCommand } from './legion.js';
|
|
14
|
+
import { MusicCommand } from './music.js';
|
|
14
15
|
import { PreviewCommand } from './preview.js';
|
|
15
16
|
import { ReplayCommand } from './replay.js';
|
|
16
17
|
import { RepoCommand } from './repo.js';
|
|
@@ -96,6 +97,11 @@ const commandRegistry = [
|
|
|
96
97
|
name: 'hub',
|
|
97
98
|
handler: createClassRegistrar(HubCommand),
|
|
98
99
|
},
|
|
100
|
+
{
|
|
101
|
+
name: 'music',
|
|
102
|
+
aliases: ['audio'],
|
|
103
|
+
handler: createClassRegistrar(MusicCommand),
|
|
104
|
+
},
|
|
99
105
|
{
|
|
100
106
|
name: 'repo',
|
|
101
107
|
handler: createClassRegistrar(RepoCommand),
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Config } from '../utils/config.js';
|
|
2
|
+
import { Logger } from '../utils/logger.js';
|
|
3
|
+
type MusicGenerateOptions = {
|
|
4
|
+
prompt?: string;
|
|
5
|
+
lyrics?: string;
|
|
6
|
+
duration?: string | number;
|
|
7
|
+
genre?: string;
|
|
8
|
+
language?: string;
|
|
9
|
+
voiceType?: string;
|
|
10
|
+
styleTags?: string;
|
|
11
|
+
voice?: string;
|
|
12
|
+
wait?: boolean;
|
|
13
|
+
pollInterval?: string | number;
|
|
14
|
+
json?: boolean;
|
|
15
|
+
};
|
|
16
|
+
type MusicStatusOptions = {
|
|
17
|
+
json?: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare class MusicCommand {
|
|
20
|
+
private config;
|
|
21
|
+
private logger;
|
|
22
|
+
constructor(config: Config, logger: Logger);
|
|
23
|
+
private apiBase;
|
|
24
|
+
private authHeaders;
|
|
25
|
+
private request;
|
|
26
|
+
private uploadVoiceReference;
|
|
27
|
+
generate(promptArg: string | undefined, options: MusicGenerateOptions): Promise<void>;
|
|
28
|
+
status(taskId: string, options?: MusicStatusOptions): Promise<void>;
|
|
29
|
+
private waitForStatus;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
export class MusicCommand {
|
|
5
|
+
config;
|
|
6
|
+
logger;
|
|
7
|
+
constructor(config, logger) {
|
|
8
|
+
this.config = config;
|
|
9
|
+
this.logger = logger;
|
|
10
|
+
}
|
|
11
|
+
apiBase() {
|
|
12
|
+
const configured = String(this.config.getAll().musicApiUrl || '').trim();
|
|
13
|
+
return (process.env.VIGTHORIA_MUSIC_API_URL || configured || 'https://music.vigthoria.io').replace(/\/+$/, '');
|
|
14
|
+
}
|
|
15
|
+
authHeaders() {
|
|
16
|
+
const token = this.config.get('authToken');
|
|
17
|
+
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
18
|
+
}
|
|
19
|
+
async request(endpoint, init = {}) {
|
|
20
|
+
const response = await fetch(`${this.apiBase()}${endpoint}`, {
|
|
21
|
+
...init,
|
|
22
|
+
headers: {
|
|
23
|
+
...this.authHeaders(),
|
|
24
|
+
...init.headers,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
const text = await response.text();
|
|
28
|
+
let data = {};
|
|
29
|
+
try {
|
|
30
|
+
data = text ? JSON.parse(text) : {};
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
data = { message: text };
|
|
34
|
+
}
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
throw new Error(String(data.error || data.message || `Music API request failed: ${response.status}`));
|
|
37
|
+
}
|
|
38
|
+
return data;
|
|
39
|
+
}
|
|
40
|
+
async uploadVoiceReference(filePath) {
|
|
41
|
+
const absolutePath = path.resolve(filePath);
|
|
42
|
+
if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile()) {
|
|
43
|
+
throw new Error(`Voice reference file not found: ${filePath}`);
|
|
44
|
+
}
|
|
45
|
+
const form = new FormData();
|
|
46
|
+
const blob = new Blob([fs.readFileSync(absolutePath)]);
|
|
47
|
+
form.append('audio', blob, path.basename(absolutePath));
|
|
48
|
+
const data = await this.request('/api/voice/upload', {
|
|
49
|
+
method: 'POST',
|
|
50
|
+
body: form,
|
|
51
|
+
});
|
|
52
|
+
const referencePath = String(data.reference_audio_path || data.path || data.file_path || '').trim();
|
|
53
|
+
if (!referencePath) {
|
|
54
|
+
throw new Error('Voice upload succeeded but did not return a reference_audio_path.');
|
|
55
|
+
}
|
|
56
|
+
return referencePath;
|
|
57
|
+
}
|
|
58
|
+
async generate(promptArg, options) {
|
|
59
|
+
const prompt = String(promptArg || options.prompt || '').trim();
|
|
60
|
+
if (!prompt) {
|
|
61
|
+
throw new Error('Music generation requires a prompt.');
|
|
62
|
+
}
|
|
63
|
+
let referenceAudioPath = '';
|
|
64
|
+
if (options.voice) {
|
|
65
|
+
referenceAudioPath = await this.uploadVoiceReference(options.voice);
|
|
66
|
+
}
|
|
67
|
+
const duration = Math.max(1, Number(options.duration || 120));
|
|
68
|
+
const payload = {
|
|
69
|
+
prompt,
|
|
70
|
+
language: options.language || 'en',
|
|
71
|
+
voice_type: options.voiceType || 'female',
|
|
72
|
+
genre: options.genre || 'pop',
|
|
73
|
+
style_tags: options.styleTags || '',
|
|
74
|
+
lyrics: options.lyrics || undefined,
|
|
75
|
+
duration,
|
|
76
|
+
engine: 'v4',
|
|
77
|
+
is_preview: false,
|
|
78
|
+
};
|
|
79
|
+
if (referenceAudioPath) {
|
|
80
|
+
payload.voice_cloning = {
|
|
81
|
+
enabled: true,
|
|
82
|
+
reference_audio_path: referenceAudioPath,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const data = await this.request('/api/quick/generate', {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: { 'Content-Type': 'application/json' },
|
|
88
|
+
body: JSON.stringify(payload),
|
|
89
|
+
});
|
|
90
|
+
const taskId = String(data.task_id || data.session_id || '').trim();
|
|
91
|
+
if (options.json) {
|
|
92
|
+
console.log(JSON.stringify({ ...data, task_id: taskId || data.task_id }, null, 2));
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
console.log(chalk.green('Music generation queued.'));
|
|
96
|
+
console.log(chalk.cyan('Task ID: ') + (taskId || '(pending)'));
|
|
97
|
+
if (data.queue_position)
|
|
98
|
+
console.log(chalk.gray(`Queue position: ${data.queue_position}`));
|
|
99
|
+
if (data.estimated_wait_seconds)
|
|
100
|
+
console.log(chalk.gray(`Estimated wait: ${data.estimated_wait_seconds}s`));
|
|
101
|
+
}
|
|
102
|
+
if (options.wait && taskId) {
|
|
103
|
+
await this.waitForStatus(taskId, Number(options.pollInterval || 5), Boolean(options.json));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async status(taskId, options = {}) {
|
|
107
|
+
const data = await this.request(`/api/status/${encodeURIComponent(taskId)}`);
|
|
108
|
+
if (options.json) {
|
|
109
|
+
console.log(JSON.stringify(data, null, 2));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
console.log(chalk.cyan(`Music task ${taskId}`));
|
|
113
|
+
console.log(`Status: ${data.status || 'unknown'}`);
|
|
114
|
+
if (data.progress !== undefined)
|
|
115
|
+
console.log(`Progress: ${data.progress}%`);
|
|
116
|
+
const downloadUrl = data.download_url || data.audio_url || data.file_url || data.result_url;
|
|
117
|
+
if (downloadUrl)
|
|
118
|
+
console.log(`Download: ${downloadUrl}`);
|
|
119
|
+
}
|
|
120
|
+
async waitForStatus(taskId, intervalSeconds, json) {
|
|
121
|
+
const delay = Math.max(1, intervalSeconds) * 1000;
|
|
122
|
+
for (;;) {
|
|
123
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
124
|
+
const data = await this.request(`/api/status/${encodeURIComponent(taskId)}`);
|
|
125
|
+
const status = String(data.status || '').toLowerCase();
|
|
126
|
+
if (!json) {
|
|
127
|
+
const progress = data.progress !== undefined ? ` (${data.progress}%)` : '';
|
|
128
|
+
console.log(chalk.gray(`Status: ${status || 'unknown'}${progress}`));
|
|
129
|
+
}
|
|
130
|
+
if (['completed', 'complete', 'finished', 'failed', 'error', 'cancelled'].includes(status)) {
|
|
131
|
+
if (json)
|
|
132
|
+
console.log(JSON.stringify(data, null, 2));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import { handleLogin, handleLogout, statusAction } from './commands/auth.js';
|
|
|
24
24
|
import { ConfigCommand } from './commands/config.js';
|
|
25
25
|
import { ReviewCommand } from './commands/review.js';
|
|
26
26
|
import { HubCommand } from './commands/hub.js';
|
|
27
|
+
import { MusicCommand } from './commands/music.js';
|
|
27
28
|
import { RepoCommand } from './commands/repo.js';
|
|
28
29
|
import { DeployCommand } from './commands/deploy.js';
|
|
29
30
|
import { BridgeCommand } from './commands/bridge.js';
|
|
@@ -207,7 +208,7 @@ async function fetchNpmLatestVersion() {
|
|
|
207
208
|
}
|
|
208
209
|
}
|
|
209
210
|
const VERSION = getVersion();
|
|
210
|
-
const VIGTHORIA_DEFAULT_MANIFEST_URL = process.env.VIGTHORIA_UPDATE_MANIFEST_URL || "https://
|
|
211
|
+
const VIGTHORIA_DEFAULT_MANIFEST_URL = process.env.VIGTHORIA_UPDATE_MANIFEST_URL || "https://extension.vigthoria.io/downloads/manifest.json";
|
|
211
212
|
function resolveManifestEntry(manifest, channel) {
|
|
212
213
|
const normalized = String(channel || 'stable').trim() || 'stable';
|
|
213
214
|
if (manifest.channels && manifest.channels[normalized]) {
|
|
@@ -1043,6 +1044,36 @@ export async function main(args) {
|
|
|
1043
1044
|
const hub = new HubCommand(config, logger);
|
|
1044
1045
|
await hub.discover();
|
|
1045
1046
|
});
|
|
1047
|
+
const musicCommand = program
|
|
1048
|
+
.command('music')
|
|
1049
|
+
.alias('audio')
|
|
1050
|
+
.description('Generate songs with Vigthoria Music AI / ACE-Step');
|
|
1051
|
+
musicCommand
|
|
1052
|
+
.command('generate [prompt]')
|
|
1053
|
+
.alias('g')
|
|
1054
|
+
.description('Queue full-song generation and return a task id')
|
|
1055
|
+
.option('--lyrics <text>', 'Provide fixed lyrics instead of auto-writing lyrics')
|
|
1056
|
+
.option('--duration <seconds>', 'Song duration in seconds', '120')
|
|
1057
|
+
.option('--genre <genre>', 'Music genre', 'pop')
|
|
1058
|
+
.option('--language <code>', 'Vocal language', 'en')
|
|
1059
|
+
.option('--voice-type <type>', 'Voice type (female, male, mixed)', 'female')
|
|
1060
|
+
.option('--style-tags <tags>', 'Additional style tags')
|
|
1061
|
+
.option('--voice <path>', 'Upload a local voice reference for cloning')
|
|
1062
|
+
.option('--wait', 'Poll until the task reaches a terminal state', false)
|
|
1063
|
+
.option('--poll-interval <seconds>', 'Polling interval when --wait is used', '5')
|
|
1064
|
+
.option('--json', 'Emit machine-readable JSON output', false)
|
|
1065
|
+
.action(async (prompt, options) => {
|
|
1066
|
+
const music = new MusicCommand(config, logger);
|
|
1067
|
+
await music.generate(prompt, options);
|
|
1068
|
+
});
|
|
1069
|
+
musicCommand
|
|
1070
|
+
.command('status <taskId>')
|
|
1071
|
+
.description('Check a queued music generation task')
|
|
1072
|
+
.option('--json', 'Emit machine-readable JSON output', false)
|
|
1073
|
+
.action(async (taskId, options) => {
|
|
1074
|
+
const music = new MusicCommand(config, logger);
|
|
1075
|
+
await music.status(taskId, options);
|
|
1076
|
+
});
|
|
1046
1077
|
// ==================== REPO COMMANDS ====================
|
|
1047
1078
|
// Repo command - Push/Pull projects to/from Vigthoria Repository
|
|
1048
1079
|
const repoCommand = program
|
|
@@ -1514,7 +1545,7 @@ Examples:
|
|
|
1514
1545
|
program
|
|
1515
1546
|
.command('update')
|
|
1516
1547
|
.alias('upgrade')
|
|
1517
|
-
.description('Check for updates and upgrade Vigthoria CLI. Default manifest: https://
|
|
1548
|
+
.description('Check for updates and upgrade Vigthoria CLI. Default manifest: https://extension.vigthoria.io/downloads/manifest.json')
|
|
1518
1549
|
.option('-c, --check', 'Only check for updates, don\'t install')
|
|
1519
1550
|
.option('-f, --from <target>', 'Install update from npm spec, local .tgz path, or URL')
|
|
1520
1551
|
.option('-m, --manifest <url>', 'Update manifest URL for server-driven releases')
|
|
@@ -33,6 +33,7 @@ export interface RunEvaluation {
|
|
|
33
33
|
uiTheme: 'success' | 'warning' | 'error';
|
|
34
34
|
}
|
|
35
35
|
export declare function createLiveOutcome(): LiveOutcome;
|
|
36
|
+
export declare function isExecutorTimeoutFailure(liveOutcome: LiveOutcome): boolean;
|
|
36
37
|
/** Tool-trace stubs like `[read_file] "index.html"` or `<read_file>…</read_file>` — not a user-facing answer. */
|
|
37
38
|
export declare function isToolEvidenceStubAnswer(text: string): boolean;
|
|
38
39
|
/** True when text looks like a real answer, not an executor/system placeholder. */
|
|
@@ -30,6 +30,10 @@ const LIST_ONLY_DISCOVERY_TOOLS = new Set([
|
|
|
30
30
|
'dir',
|
|
31
31
|
]);
|
|
32
32
|
const GENERIC_SUMMARY_RE = /^(completed the requested analysis|reviewed the workspace without writing changes|task completed|done|finished|analysis completed)([.\s]|$)/i;
|
|
33
|
+
export function isExecutorTimeoutFailure(liveOutcome) {
|
|
34
|
+
const errorText = String(liveOutcome.executorError || '').trim();
|
|
35
|
+
return /executor llm call timed out|fast fallback also failed|executor model timed out/i.test(errorText);
|
|
36
|
+
}
|
|
33
37
|
function looksLikeRawV3EventPayload(text) {
|
|
34
38
|
const trimmed = String(text || '').trim();
|
|
35
39
|
if (!/^[{[]/.test(trimmed))
|
|
@@ -164,6 +168,15 @@ export function evaluateExecutorSuccess(liveOutcome) {
|
|
|
164
168
|
|| Boolean(liveOutcome.streamAborted);
|
|
165
169
|
if (liveOutcome.streamAborted && liveOutcome.tasksTotal > 0) {
|
|
166
170
|
const partial = liveOutcome.tasksSucceeded > 0;
|
|
171
|
+
if (isExecutorTimeoutFailure(liveOutcome)) {
|
|
172
|
+
return {
|
|
173
|
+
executorSucceeded: false,
|
|
174
|
+
statusHeadline: partial
|
|
175
|
+
? `⚠ ${liveOutcome.tasksSucceeded}/${liveOutcome.tasksTotal} tasks completed — executor model timed out`
|
|
176
|
+
: `✕ Executor model timed out — 0/${liveOutcome.tasksTotal} tasks completed`,
|
|
177
|
+
uiTheme: 'warning',
|
|
178
|
+
};
|
|
179
|
+
}
|
|
167
180
|
return {
|
|
168
181
|
executorSucceeded: false,
|
|
169
182
|
statusHeadline: partial
|
|
@@ -172,6 +185,16 @@ export function evaluateExecutorSuccess(liveOutcome) {
|
|
|
172
185
|
uiTheme: 'warning',
|
|
173
186
|
};
|
|
174
187
|
}
|
|
188
|
+
if (liveOutcome.tasksTotal > 0 && isExecutorTimeoutFailure(liveOutcome)) {
|
|
189
|
+
const partial = liveOutcome.tasksSucceeded > 0;
|
|
190
|
+
return {
|
|
191
|
+
executorSucceeded: false,
|
|
192
|
+
statusHeadline: partial
|
|
193
|
+
? `⚠ ${liveOutcome.tasksSucceeded}/${liveOutcome.tasksTotal} tasks completed — executor model timed out`
|
|
194
|
+
: `✕ Executor model timed out — 0/${liveOutcome.tasksTotal} tasks completed`,
|
|
195
|
+
uiTheme: 'error',
|
|
196
|
+
};
|
|
197
|
+
}
|
|
175
198
|
if (hasFatalError && liveOutcome.tasksTotal === 0) {
|
|
176
199
|
return {
|
|
177
200
|
executorSucceeded: false,
|
package/dist/utils/api.d.ts
CHANGED
|
@@ -342,6 +342,7 @@ export declare class APIClient {
|
|
|
342
342
|
private materializeEmergencySaaSWorkspace;
|
|
343
343
|
private ensureExecutionContext;
|
|
344
344
|
bindExecutionContext(context?: Record<string, any>): Promise<Record<string, any>>;
|
|
345
|
+
private isServerManagedTempWorkspacePath;
|
|
345
346
|
private resolveAgentTargetPath;
|
|
346
347
|
private isLikelyWindowsPath;
|
|
347
348
|
private resolveServerBindableWorkspacePath;
|
package/dist/utils/api.js
CHANGED
|
@@ -259,15 +259,18 @@ export function propagateError(err) {
|
|
|
259
259
|
const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
|
|
260
260
|
const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS;
|
|
261
261
|
if (!rawValue) {
|
|
262
|
-
return
|
|
262
|
+
return 0;
|
|
263
263
|
}
|
|
264
264
|
const parsed = Number.parseInt(rawValue, 10);
|
|
265
|
-
return Number.isFinite(parsed) && parsed
|
|
265
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
|
|
266
266
|
})();
|
|
267
267
|
const DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS = (() => {
|
|
268
|
-
const rawValue = process.env.VIGTHORIA_AGENT_IDLE_TIMEOUT_MS || process.env.V3_AGENT_IDLE_TIMEOUT_MS
|
|
268
|
+
const rawValue = process.env.VIGTHORIA_AGENT_IDLE_TIMEOUT_MS || process.env.V3_AGENT_IDLE_TIMEOUT_MS;
|
|
269
|
+
if (!rawValue) {
|
|
270
|
+
return 0;
|
|
271
|
+
}
|
|
269
272
|
const parsed = Number.parseInt(rawValue, 10);
|
|
270
|
-
return Number.isFinite(parsed) && parsed
|
|
273
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
|
|
271
274
|
})();
|
|
272
275
|
const DEFAULT_OPERATOR_TIMEOUT_MS = (() => {
|
|
273
276
|
const rawValue = process.env.VIGTHORIA_OPERATOR_TIMEOUT_MS || process.env.OPERATOR_TIMEOUT_MS;
|
|
@@ -2236,6 +2239,10 @@ menu {
|
|
|
2236
2239
|
}
|
|
2237
2240
|
return {
|
|
2238
2241
|
...executionContext,
|
|
2242
|
+
localWorkspacePath: localWorkspacePath || executionContext.localWorkspacePath || null,
|
|
2243
|
+
targetPath: localWorkspacePath || executionContext.targetPath || executionContext.projectPath || executionContext.workspacePath || null,
|
|
2244
|
+
projectPath: localWorkspacePath || executionContext.projectPath || executionContext.targetPath || executionContext.workspacePath || null,
|
|
2245
|
+
workspacePath: localWorkspacePath || executionContext.workspacePath || executionContext.projectPath || executionContext.targetPath || null,
|
|
2239
2246
|
mcpContextId: binding.mcpContextId,
|
|
2240
2247
|
mcpContextBackendUrl: binding.backendUrl,
|
|
2241
2248
|
};
|
|
@@ -2276,10 +2283,38 @@ menu {
|
|
|
2276
2283
|
this.logger.debug(`Failed to bind MCP context via ${baseUrl}:`, error.message);
|
|
2277
2284
|
}
|
|
2278
2285
|
}
|
|
2279
|
-
return
|
|
2286
|
+
return {
|
|
2287
|
+
...executionContext,
|
|
2288
|
+
localWorkspacePath: localWorkspacePath || executionContext.localWorkspacePath || null,
|
|
2289
|
+
targetPath: localWorkspacePath || executionContext.targetPath || executionContext.projectPath || executionContext.workspacePath || null,
|
|
2290
|
+
projectPath: localWorkspacePath || executionContext.projectPath || executionContext.targetPath || executionContext.workspacePath || null,
|
|
2291
|
+
workspacePath: localWorkspacePath || executionContext.workspacePath || executionContext.projectPath || executionContext.targetPath || null,
|
|
2292
|
+
};
|
|
2293
|
+
}
|
|
2294
|
+
isServerManagedTempWorkspacePath(pathValue) {
|
|
2295
|
+
const normalized = String(pathValue || '').replace(/\\/g, '/');
|
|
2296
|
+
return /(?:^|\/)v3-temp\/vig-remote-/i.test(normalized)
|
|
2297
|
+
|| /(?:^|\/)tmp\/vig-remote(?:-server)?-/i.test(normalized)
|
|
2298
|
+
|| /(?:^|\/)var\/tmp\/vig-remote(?:-server)?-/i.test(normalized);
|
|
2280
2299
|
}
|
|
2281
2300
|
resolveAgentTargetPath(context = {}) {
|
|
2282
|
-
|
|
2301
|
+
const explicitLocal = String(context.localWorkspacePath || '').trim();
|
|
2302
|
+
if (explicitLocal
|
|
2303
|
+
&& !explicitLocal.startsWith('vigthoria://')
|
|
2304
|
+
&& (this.isLikelyWindowsPath(explicitLocal) || path.isAbsolute(explicitLocal))) {
|
|
2305
|
+
return explicitLocal;
|
|
2306
|
+
}
|
|
2307
|
+
const candidate = context.targetPath || context.projectPath || context.workspacePath || context.projectRoot || process.cwd();
|
|
2308
|
+
if (this.isServerManagedTempWorkspacePath(candidate) && explicitLocal && !explicitLocal.startsWith('vigthoria://')) {
|
|
2309
|
+
return explicitLocal;
|
|
2310
|
+
}
|
|
2311
|
+
if (this.isServerManagedTempWorkspacePath(candidate)) {
|
|
2312
|
+
const preserved = String(context.__localWorkspacePath || context.clientWorkspacePath || '').trim();
|
|
2313
|
+
if (preserved && !preserved.startsWith('vigthoria://')) {
|
|
2314
|
+
return preserved;
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
return candidate;
|
|
2283
2318
|
}
|
|
2284
2319
|
isLikelyWindowsPath(pathValue) {
|
|
2285
2320
|
return /^[a-zA-Z]:[\\/]/.test(pathValue) || /^\\\\/.test(pathValue);
|
|
@@ -2819,9 +2854,30 @@ menu {
|
|
|
2819
2854
|
return { success: true, output: matches.slice(0, 100).join('\n') || '(no matches)' };
|
|
2820
2855
|
}
|
|
2821
2856
|
if (name === 'syntax_check') {
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2857
|
+
if (!fs.existsSync(target.absolutePath)) {
|
|
2858
|
+
return {
|
|
2859
|
+
success: false,
|
|
2860
|
+
output: '',
|
|
2861
|
+
error: `File not found: ${target.relativePath}. Write the file before running syntax_check.`,
|
|
2862
|
+
};
|
|
2863
|
+
}
|
|
2864
|
+
const ext = path.extname(target.absolutePath).toLowerCase();
|
|
2865
|
+
if (ext === '.json') {
|
|
2866
|
+
JSON.parse(fs.readFileSync(target.absolutePath, 'utf8'));
|
|
2867
|
+
return { success: true, output: `Syntax check passed: ${target.relativePath}` };
|
|
2868
|
+
}
|
|
2869
|
+
if (ext === '.js' || ext === '.mjs' || ext === '.cjs') {
|
|
2870
|
+
const { execSync } = await import('child_process');
|
|
2871
|
+
try {
|
|
2872
|
+
execSync(`node --check "${target.absolutePath}"`, { stdio: 'pipe' });
|
|
2873
|
+
return { success: true, output: `Syntax check passed: ${target.relativePath}` };
|
|
2874
|
+
}
|
|
2875
|
+
catch (error) {
|
|
2876
|
+
const stderr = error?.stderr?.toString?.() || error?.message || String(error);
|
|
2877
|
+
return { success: false, output: '', error: stderr.trim() || `Syntax check failed: ${target.relativePath}` };
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
fs.readFileSync(target.absolutePath, 'utf8');
|
|
2825
2881
|
return { success: true, output: `Syntax check passed: ${target.relativePath}` };
|
|
2826
2882
|
}
|
|
2827
2883
|
if (name === 'run_command') {
|
|
@@ -2955,7 +3011,9 @@ menu {
|
|
|
2955
3011
|
return;
|
|
2956
3012
|
}
|
|
2957
3013
|
}
|
|
2958
|
-
const targets = expectedFiles.length > 0
|
|
3014
|
+
const targets = expectedFiles.length > 0
|
|
3015
|
+
? Array.from(new Set([...expectedFiles, ...Object.keys(streamedFiles)]))
|
|
3016
|
+
: Object.keys(streamedFiles);
|
|
2959
3017
|
for (const targetPath of targets) {
|
|
2960
3018
|
const relativePath = this.normalizeAgentWorkspaceRelativePath(targetPath, rootPath);
|
|
2961
3019
|
if (!relativePath) {
|
|
@@ -3521,6 +3579,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3521
3579
|
let buffer = '';
|
|
3522
3580
|
const events = [];
|
|
3523
3581
|
let final = null;
|
|
3582
|
+
let streamDone = false;
|
|
3524
3583
|
let contextId = response.headers.get('x-context-id') || String(context.contextId || '').trim() || null;
|
|
3525
3584
|
let serverWorkspaceRoot = null;
|
|
3526
3585
|
const streamedFiles = {};
|
|
@@ -3528,48 +3587,71 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3528
3587
|
this.activeV3StreamContext = context;
|
|
3529
3588
|
context.__v3StreamedFiles = streamedFiles;
|
|
3530
3589
|
const idleTimeoutMs = (() => {
|
|
3531
|
-
const base = context.agentIdleTimeoutMs
|
|
3532
|
-
if (
|
|
3533
|
-
|
|
3534
|
-
const extended = Number.parseInt(raw, 10);
|
|
3535
|
-
return Math.max(base, Number.isFinite(extended) && extended > 0 ? extended : 300000);
|
|
3590
|
+
const base = context.agentIdleTimeoutMs ?? DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS;
|
|
3591
|
+
if (base <= 0) {
|
|
3592
|
+
return 0;
|
|
3536
3593
|
}
|
|
3537
|
-
|
|
3594
|
+
if (!isPlannerBuildTask(String(context.agentTaskType || ''), String(context.rawPrompt || ''))) {
|
|
3595
|
+
return base;
|
|
3596
|
+
}
|
|
3597
|
+
const raw = process.env.VIGTHORIA_AGENT_PLANNER_IDLE_TIMEOUT_MS || process.env.V3_AGENT_PLANNER_IDLE_TIMEOUT_MS;
|
|
3598
|
+
if (!raw) {
|
|
3599
|
+
return base;
|
|
3600
|
+
}
|
|
3601
|
+
const extended = Number.parseInt(raw, 10);
|
|
3602
|
+
return Number.isFinite(extended) && extended > 0 ? Math.max(base, extended) : base;
|
|
3538
3603
|
})();
|
|
3539
3604
|
try {
|
|
3540
3605
|
while (true) {
|
|
3541
3606
|
let chunk;
|
|
3542
3607
|
try {
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
if (result !== timeoutSentinel) {
|
|
3555
|
-
chunk = result;
|
|
3556
|
-
break;
|
|
3557
|
-
}
|
|
3558
|
-
if (this.pendingV3ClientToolTasks.size > 0) {
|
|
3559
|
-
continue;
|
|
3560
|
-
}
|
|
3561
|
-
if (this.hasAgentWorkspaceOutput(context)) {
|
|
3562
|
-
const stalledError = new Error('V3 agent stream stalled after writing workspace output');
|
|
3563
|
-
stalledError.name = 'AbortError';
|
|
3564
|
-
stalledError.partialData = {
|
|
3565
|
-
task_id: events.find((event) => event && event.task_id)?.task_id || null,
|
|
3566
|
-
context_id: contextId,
|
|
3567
|
-
result: final,
|
|
3568
|
-
events,
|
|
3569
|
-
files: streamedFiles,
|
|
3608
|
+
if (idleTimeoutMs <= 0) {
|
|
3609
|
+
chunk = await reader.read();
|
|
3610
|
+
}
|
|
3611
|
+
else {
|
|
3612
|
+
chunk = await new Promise((resolve, reject) => {
|
|
3613
|
+
let idleTimer = null;
|
|
3614
|
+
const clearIdleTimer = () => {
|
|
3615
|
+
if (idleTimer) {
|
|
3616
|
+
clearTimeout(idleTimer);
|
|
3617
|
+
idleTimer = null;
|
|
3618
|
+
}
|
|
3570
3619
|
};
|
|
3571
|
-
|
|
3572
|
-
|
|
3620
|
+
const scheduleIdleTimer = () => {
|
|
3621
|
+
clearIdleTimer();
|
|
3622
|
+
idleTimer = setTimeout(() => {
|
|
3623
|
+
if (this.pendingV3ClientToolTasks.size > 0) {
|
|
3624
|
+
scheduleIdleTimer();
|
|
3625
|
+
return;
|
|
3626
|
+
}
|
|
3627
|
+
if (this.hasAgentWorkspaceOutput(context)) {
|
|
3628
|
+
const stalledError = new Error('V3 agent stream stalled after writing workspace output');
|
|
3629
|
+
stalledError.name = 'AbortError';
|
|
3630
|
+
stalledError.partialData = {
|
|
3631
|
+
task_id: events.find((event) => event && event.task_id)?.task_id || null,
|
|
3632
|
+
context_id: contextId,
|
|
3633
|
+
result: final,
|
|
3634
|
+
events,
|
|
3635
|
+
files: streamedFiles,
|
|
3636
|
+
};
|
|
3637
|
+
clearIdleTimer();
|
|
3638
|
+
reject(stalledError);
|
|
3639
|
+
return;
|
|
3640
|
+
}
|
|
3641
|
+
scheduleIdleTimer();
|
|
3642
|
+
}, idleTimeoutMs);
|
|
3643
|
+
};
|
|
3644
|
+
scheduleIdleTimer();
|
|
3645
|
+
reader.read()
|
|
3646
|
+
.then((result) => {
|
|
3647
|
+
clearIdleTimer();
|
|
3648
|
+
resolve(result);
|
|
3649
|
+
})
|
|
3650
|
+
.catch((error) => {
|
|
3651
|
+
clearIdleTimer();
|
|
3652
|
+
reject(error);
|
|
3653
|
+
});
|
|
3654
|
+
});
|
|
3573
3655
|
}
|
|
3574
3656
|
}
|
|
3575
3657
|
catch (error) {
|
|
@@ -3609,9 +3691,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3609
3691
|
}
|
|
3610
3692
|
}
|
|
3611
3693
|
const payload = dataLines.join('\n').trim();
|
|
3612
|
-
if (!payload
|
|
3694
|
+
if (!payload) {
|
|
3613
3695
|
continue;
|
|
3614
3696
|
}
|
|
3697
|
+
if (payload === '[DONE]') {
|
|
3698
|
+
streamDone = true;
|
|
3699
|
+
break;
|
|
3700
|
+
}
|
|
3615
3701
|
let event;
|
|
3616
3702
|
try {
|
|
3617
3703
|
event = JSON.parse(payload);
|
|
@@ -3702,20 +3788,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3702
3788
|
if (event.type === 'complete' || event.type === 'message') {
|
|
3703
3789
|
final = event;
|
|
3704
3790
|
}
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
await Promise.allSettled([...this.pendingV3ClientToolTasks]);
|
|
3709
|
-
reader.cancel().catch(() => { });
|
|
3710
|
-
return {
|
|
3711
|
-
task_id: events.find((entry) => entry && entry.task_id)?.task_id || null,
|
|
3712
|
-
context_id: contextId,
|
|
3713
|
-
result: final,
|
|
3714
|
-
events,
|
|
3715
|
-
files: streamedFiles,
|
|
3716
|
-
serverWorkspaceRoot: serverWorkspaceRoot || null,
|
|
3717
|
-
};
|
|
3718
|
-
}
|
|
3791
|
+
}
|
|
3792
|
+
if (streamDone) {
|
|
3793
|
+
break;
|
|
3719
3794
|
}
|
|
3720
3795
|
}
|
|
3721
3796
|
await Promise.allSettled([...this.pendingV3ClientToolTasks]);
|
|
@@ -3742,11 +3817,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
3742
3817
|
const resolvedModel = this.resolvePermittedModelId(requestedModel);
|
|
3743
3818
|
const preferLocalV3 = /(premium|polished|landing|site|page|dashboard|saas|frontend|ui|responsive|animated|create the required project files and write them to the workspace)/i.test(message)
|
|
3744
3819
|
&& context.localMachineCapable !== false;
|
|
3745
|
-
const
|
|
3746
|
-
&& /(saas|dashboard|analytics|billing|team management|activity feed|login screen)/i.test(message);
|
|
3747
|
-
const timeoutMs = rescueEligibleSaaS
|
|
3748
|
-
? Math.min(resolvePlannerAgentTimeoutMs(baseTimeoutMs, String(executionContext.agentTaskType || ''), String(executionContext.rawPrompt || message || '')), 210000)
|
|
3749
|
-
: resolvePlannerAgentTimeoutMs(baseTimeoutMs, String(executionContext.agentTaskType || ''), String(executionContext.rawPrompt || message || ''));
|
|
3820
|
+
const timeoutMs = resolvePlannerAgentTimeoutMs(baseTimeoutMs, String(executionContext.agentTaskType || ''), String(executionContext.rawPrompt || message || ''));
|
|
3750
3821
|
const maxAttempts = preferLocalV3 ? 2 : 1;
|
|
3751
3822
|
let lastErrors = [];
|
|
3752
3823
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
@@ -5532,11 +5603,20 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
5532
5603
|
'vigthoria-code': 'vigthoria-v3-code-35b',
|
|
5533
5604
|
'vigthoria-agent': 'vigthoria-v3-code-35b',
|
|
5534
5605
|
// ═══════════════════════════════════════════════════════════════
|
|
5535
|
-
// VIGTHORIA CLOUD -
|
|
5606
|
+
// VIGTHORIA CLOUD - current billing catalog aliases
|
|
5536
5607
|
// ═══════════════════════════════════════════════════════════════
|
|
5537
|
-
'cloud': 'vigthoria-cloud-
|
|
5538
|
-
'cloud-
|
|
5539
|
-
'
|
|
5608
|
+
'cloud': 'vigthoria-cloud-balanced',
|
|
5609
|
+
'cloud-code': 'vigthoria-cloud-code',
|
|
5610
|
+
'cloud-balanced': 'vigthoria-cloud-balanced',
|
|
5611
|
+
'cloud-fast': 'vigthoria-cloud-fast',
|
|
5612
|
+
'cloud-power': 'vigthoria-cloud-power',
|
|
5613
|
+
'cloud-maximum': 'vigthoria-cloud-maximum',
|
|
5614
|
+
'cloud-reason': 'vigthoria-cloud-power',
|
|
5615
|
+
'ultra': 'vigthoria-cloud-maximum',
|
|
5616
|
+
// Legacy aliases kept for backward compatibility
|
|
5617
|
+
'cloud-pro': 'vigthoria-cloud-power',
|
|
5618
|
+
'cloud-k2': 'vigthoria-cloud-power',
|
|
5619
|
+
'cloud-ultra': 'vigthoria-cloud-maximum',
|
|
5540
5620
|
};
|
|
5541
5621
|
// If already a full model ID, return as-is
|
|
5542
5622
|
if (shortName.includes('vigthoria') || shortName.includes('/') || shortName.includes(':')) {
|
|
@@ -5991,9 +6071,23 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
5991
6071
|
}
|
|
5992
6072
|
async attemptV3ServiceRecovery(reason = '', _options = {}) {
|
|
5993
6073
|
const safeReason = sanitizeUserFacingErrorText(reason || 'unknown failure');
|
|
6074
|
+
const isTransient = /timed out|aborted|econnreset|fetch failed|network|503|502|504|socket hang up/i.test(safeReason);
|
|
6075
|
+
if (!isTransient) {
|
|
6076
|
+
return {
|
|
6077
|
+
recovered: false,
|
|
6078
|
+
message: safeReason ? `Recovery unavailable: ${safeReason}` : 'Recovery unavailable',
|
|
6079
|
+
};
|
|
6080
|
+
}
|
|
6081
|
+
const health = await this.runV3HealthCheck();
|
|
6082
|
+
if (!health.healthy) {
|
|
6083
|
+
return {
|
|
6084
|
+
recovered: false,
|
|
6085
|
+
message: health.error || 'V3 service is still unreachable',
|
|
6086
|
+
};
|
|
6087
|
+
}
|
|
5994
6088
|
return {
|
|
5995
|
-
recovered:
|
|
5996
|
-
message:
|
|
6089
|
+
recovered: true,
|
|
6090
|
+
message: 'V3 service is reachable again — retrying the workflow once.',
|
|
5997
6091
|
};
|
|
5998
6092
|
}
|
|
5999
6093
|
async getDevtoolsBridgeStatus() {
|
package/dist/utils/config.d.ts
CHANGED
package/dist/utils/config.js
CHANGED
|
@@ -6,6 +6,7 @@ import { secureFileMode } from './cli-state.js';
|
|
|
6
6
|
const defaultConfig = {
|
|
7
7
|
apiUrl: 'https://coder.vigthoria.io',
|
|
8
8
|
modelsApiUrl: 'https://api.vigthoria.io', // Direct AI Models API - no proxying through Coder
|
|
9
|
+
musicApiUrl: 'https://music.vigthoria.io',
|
|
9
10
|
wsUrl: 'wss://coder.vigthoria.io/ws',
|
|
10
11
|
selfHostedModelsApiUrl: null,
|
|
11
12
|
authToken: null,
|
|
@@ -59,6 +60,7 @@ export class Config {
|
|
|
59
60
|
schema: {
|
|
60
61
|
apiUrl: { type: 'string' },
|
|
61
62
|
modelsApiUrl: { type: 'string' },
|
|
63
|
+
musicApiUrl: { type: 'string' },
|
|
62
64
|
wsUrl: { type: 'string' },
|
|
63
65
|
selfHostedModelsApiUrl: { type: ['string', 'null'] },
|
|
64
66
|
authToken: { type: ['string', 'null'] },
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
const CLI_SHAPING_BLOCK = /(?:^|\n\n)Platform:\s*(?:Windows|macOS|Linux)\.[\s\S]*?(?=\n\n[A-Z][^\n]{0,40}:|\s*$)/i;
|
|
6
6
|
const READONLY_SHAPING_BLOCK = /(?:^|\n\n)(?:Read-only analysis mode is active\.|Diagnostic mode is active\.)[\s\S]*?(?=\n\n[A-Z][^\n]{0,40}:|\s*$)/i;
|
|
7
|
-
const BUILD_VERBS = /\b(build|create|make|implement|complete|fix|repair|edit|modify|write|generate|add|finish|scaffold|develop|update|change|refactor|let's|let us|we need|we should|erstelle|erstellen|schreib|schreibe|bearbeite)\b/i;
|
|
8
|
-
const ARTIFACT_NOUNS = /\b(file|files|project|game|spiel|app|website|html5|frontend|component|feature|code|workspace|repo|clone|replica|remake|page|site|canvas|sprite|level|levels|enemy|enemies|character|npc|world|scene|script|module|api|database|config|landing|dashboard|platformer|arcade|playable|collectible|scoreboard|leaderboard|pitfall|pacman|rogue|html|css|javascript|typescript)\b/i;
|
|
7
|
+
const BUILD_VERBS = /\b(build|create|make|implement|complete|fix|repair|edit|modify|write|generate|add|finish|scaffold|develop|update|change|refactor|want|need|require|looking for|let's|let us|we need|we should|erstelle|erstellen|schreib|schreibe|bearbeite)\b/i;
|
|
8
|
+
const ARTIFACT_NOUNS = /\b(file|files|project|game|spiel|app|website|html5|frontend|component|feature|code|workspace|repo|clone|replica|remake|page|site|screen|button|popup|modal|alert|canvas|sprite|level|levels|enemy|enemies|character|npc|world|scene|script|module|api|database|config|landing|dashboard|platformer|arcade|playable|collectible|scoreboard|leaderboard|pitfall|pacman|rogue|html|css|javascript|typescript)\b/i;
|
|
9
9
|
/** Canvas/playable games only — bare "html5" or "website" must NOT match. */
|
|
10
10
|
const GAME_INTENT = /\b(game|spiel|playable|html5\s+(?:game|canvas)|html5\s+game|canvas\s+game|game\s+canvas|arcade|platformer|side[- ]?scroller|pitfall|pac[- ]?man|tetris|snake|breakout|pong|roguelike|metroidvania|tower\s+defense|clone\s+of\s+(?:a\s+)?(?:game|pitfall|pac|mario|zelda|arcade)|gameplay|collectible|boss\s+fight|level\s+design|sprite\s+sheet|wild\s+(?:boar|pig|forest)|forest\s+pig)\b/i;
|
|
11
11
|
const WEB_PAGE_INTENT = /\b(website|web\s*site|webpage|web\s*page|landing\s+page|home\s*page|index\.html|html\s+page|static\s+page|single\s+page|popup|alert|modal|hello\s+world)\b/i;
|
|
@@ -54,7 +54,7 @@ export function isTrivialHtmlPageRequest(prompt) {
|
|
|
54
54
|
if (isContinueOrRetryPrompt(prompt)) {
|
|
55
55
|
return false;
|
|
56
56
|
}
|
|
57
|
-
if (!hasBuildVerb(prompt) && !/\b(write|create|make|show)\b/i.test(text)) {
|
|
57
|
+
if (!hasBuildVerb(prompt) && !/\b(write|create|make|show|want|need|looking\s+for)\b/i.test(text)) {
|
|
58
58
|
return false;
|
|
59
59
|
}
|
|
60
60
|
if (hasGameIntent(prompt)) {
|
|
@@ -150,10 +150,15 @@ export function resolvePlannerAgentTimeoutMs(baseTimeoutMs, agentTaskType, promp
|
|
|
150
150
|
if (!isPlannerBuildTask(agentTaskType, prompt)) {
|
|
151
151
|
return baseTimeoutMs;
|
|
152
152
|
}
|
|
153
|
-
const raw = process.env.VIGTHORIA_AGENT_PLANNER_TIMEOUT_MS || process.env.V3_AGENT_PLANNER_TIMEOUT_MS
|
|
153
|
+
const raw = process.env.VIGTHORIA_AGENT_PLANNER_TIMEOUT_MS || process.env.V3_AGENT_PLANNER_TIMEOUT_MS;
|
|
154
|
+
if (!raw) {
|
|
155
|
+
return baseTimeoutMs;
|
|
156
|
+
}
|
|
154
157
|
const extended = Number.parseInt(raw, 10);
|
|
155
|
-
|
|
156
|
-
|
|
158
|
+
if (!Number.isFinite(extended) || extended <= 0) {
|
|
159
|
+
return baseTimeoutMs;
|
|
160
|
+
}
|
|
161
|
+
return Math.max(baseTimeoutMs, extended);
|
|
157
162
|
}
|
|
158
163
|
/** V3 workflow mode: suppress write path when the user did not ask for file changes. */
|
|
159
164
|
export function resolveWorkflowType(agentTaskType, prompt) {
|
|
@@ -4,22 +4,108 @@
|
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import { stripExecutionShaping } from './requestIntent.js';
|
|
7
|
+
function escapeJsString(value) {
|
|
8
|
+
return String(value || '').replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
9
|
+
}
|
|
10
|
+
function escapeHtml(value) {
|
|
11
|
+
return String(value || '')
|
|
12
|
+
.replace(/&/g, '&')
|
|
13
|
+
.replace(/</g, '<')
|
|
14
|
+
.replace(/>/g, '>')
|
|
15
|
+
.replace(/"/g, '"');
|
|
16
|
+
}
|
|
17
|
+
function extractPopupMessage(vision) {
|
|
18
|
+
const explicitShown = vision.match(/(?:popup\s+is\s+shown|popup\s+shown|shown|shows?|display(?:s|ed)?)\s*:?\s*["']([^"']+)["']/i);
|
|
19
|
+
if (explicitShown?.[1])
|
|
20
|
+
return explicitShown[1];
|
|
21
|
+
const popupPhrases = [...vision.matchAll(/popup[^"']*(?:message|text|says?|shown|with)\s*:?\s*["']([^"']+)["']/gi)];
|
|
22
|
+
if (popupPhrases.length > 0)
|
|
23
|
+
return popupPhrases[popupPhrases.length - 1][1];
|
|
24
|
+
const quoted = vision.match(/(?:alert|message)\s*(?:with|says?|text|:)\s*["']([^"']+)["']/i);
|
|
25
|
+
if (quoted?.[1])
|
|
26
|
+
return quoted[1];
|
|
27
|
+
if (/\bhello\s*world\b/i.test(vision))
|
|
28
|
+
return 'hello world';
|
|
29
|
+
return 'Hello World';
|
|
30
|
+
}
|
|
31
|
+
function extractButtonLabel(vision) {
|
|
32
|
+
const label = vision.match(/\blabel\s+["']([^"']+)["']/i)
|
|
33
|
+
|| vision.match(/\bbutton(?:\s+(?:text|label|called|named))?\s+["']([^"']+)["']/i);
|
|
34
|
+
if (label?.[1])
|
|
35
|
+
return label[1];
|
|
36
|
+
if (/\bclick\b/i.test(vision))
|
|
37
|
+
return 'CLICK';
|
|
38
|
+
return 'Click';
|
|
39
|
+
}
|
|
40
|
+
function isSimplePopupButtonPrompt(vision) {
|
|
41
|
+
const text = String(vision || '').toLowerCase();
|
|
42
|
+
return /\b(popup|alert|message)\b/.test(text)
|
|
43
|
+
&& /\b(button|click|label)\b/.test(text)
|
|
44
|
+
&& /\bhello\s*world\b/.test(text);
|
|
45
|
+
}
|
|
46
|
+
function exactPopupButtonHtml(vision) {
|
|
47
|
+
const popupMessage = extractPopupMessage(vision);
|
|
48
|
+
const buttonLabel = extractButtonLabel(vision);
|
|
49
|
+
const blue = /\bblue\b/i.test(vision) ? '#0000ff' : '#0a0a1a';
|
|
50
|
+
return `<!DOCTYPE html>
|
|
51
|
+
<html lang="en">
|
|
52
|
+
<head>
|
|
53
|
+
<meta charset="UTF-8">
|
|
54
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
55
|
+
<title>${escapeHtml(buttonLabel)} Popup</title>
|
|
56
|
+
<style>
|
|
57
|
+
* { box-sizing: border-box; }
|
|
58
|
+
body {
|
|
59
|
+
min-height: 100vh;
|
|
60
|
+
margin: 0;
|
|
61
|
+
display: grid;
|
|
62
|
+
place-items: center;
|
|
63
|
+
background: ${blue};
|
|
64
|
+
font-family: Arial, sans-serif;
|
|
65
|
+
}
|
|
66
|
+
button {
|
|
67
|
+
padding: 14px 28px;
|
|
68
|
+
border: 0;
|
|
69
|
+
border-radius: 10px;
|
|
70
|
+
background: #ffffff;
|
|
71
|
+
color: #0000aa;
|
|
72
|
+
font-size: 20px;
|
|
73
|
+
font-weight: 700;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.25);
|
|
76
|
+
}
|
|
77
|
+
</style>
|
|
78
|
+
</head>
|
|
79
|
+
<body>
|
|
80
|
+
<button id="vigthoria-popup-button" type="button">${escapeHtml(buttonLabel)}</button>
|
|
81
|
+
<script>
|
|
82
|
+
document.getElementById('vigthoria-popup-button').addEventListener('click', function () {
|
|
83
|
+
alert('${escapeJsString(popupMessage)}');
|
|
84
|
+
});
|
|
85
|
+
</script>
|
|
86
|
+
</body>
|
|
87
|
+
</html>
|
|
88
|
+
`;
|
|
89
|
+
}
|
|
7
90
|
function ensurePopupScript(html, vision) {
|
|
8
91
|
const text = String(vision || '').toLowerCase();
|
|
9
92
|
if (!/\b(popup|alert|hello\s*world)\b/.test(text)) {
|
|
10
93
|
return html;
|
|
11
94
|
}
|
|
95
|
+
if (isSimplePopupButtonPrompt(vision)) {
|
|
96
|
+
return exactPopupButtonHtml(vision);
|
|
97
|
+
}
|
|
12
98
|
if (/\balert\s*\(/i.test(html)) {
|
|
13
99
|
return html;
|
|
14
100
|
}
|
|
15
|
-
const script =
|
|
101
|
+
const script = `<script>document.addEventListener('DOMContentLoaded',()=>{const b=document.querySelector('button,a.cta,.cta');if(b)b.addEventListener('click',()=>alert('${escapeJsString(extractPopupMessage(vision))}'));});</script>`;
|
|
16
102
|
if (/<\/body>/i.test(html)) {
|
|
17
103
|
return html.replace(/<\/body>/i, `${script}\n</body>`);
|
|
18
104
|
}
|
|
19
105
|
return `${html}\n${script}`;
|
|
20
106
|
}
|
|
21
107
|
function minimalHelloWorldHtml(popupMessage = 'Hello World') {
|
|
22
|
-
const safe =
|
|
108
|
+
const safe = escapeJsString(popupMessage || 'Hello World');
|
|
23
109
|
return `<!DOCTYPE html>
|
|
24
110
|
<html lang="en">
|
|
25
111
|
<head>
|
|
@@ -33,7 +119,7 @@ function minimalHelloWorldHtml(popupMessage = 'Hello World') {
|
|
|
33
119
|
</head>
|
|
34
120
|
<body>
|
|
35
121
|
<h1>Hello World</h1>
|
|
36
|
-
<
|
|
122
|
+
<button type="button" onclick="alert('${safe}')">CLICK</button>
|
|
37
123
|
</body>
|
|
38
124
|
</html>
|
|
39
125
|
`;
|
|
@@ -79,14 +165,6 @@ async function fetchTemplateMatch(api, vision, timeoutMs = 15000) {
|
|
|
79
165
|
}
|
|
80
166
|
return { error: lastError };
|
|
81
167
|
}
|
|
82
|
-
function extractPopupMessage(vision) {
|
|
83
|
-
const quoted = vision.match(/popup\s+with\s+["']([^"']+)["']/i);
|
|
84
|
-
if (quoted?.[1])
|
|
85
|
-
return quoted[1];
|
|
86
|
-
if (/\bhello\s*world\b/i.test(vision))
|
|
87
|
-
return 'Hello World';
|
|
88
|
-
return 'Hello World';
|
|
89
|
-
}
|
|
90
168
|
export async function runTemplateInstantPath(api, vision, workspacePath, options = {}) {
|
|
91
169
|
const entryPath = options.entryPath || 'index.html';
|
|
92
170
|
const target = path.join(workspacePath, entryPath);
|
package/dist/utils/tools.js
CHANGED
|
@@ -25,8 +25,18 @@ export function normalizeV3WorkspaceRelativePath(rawPath, rootPath) {
|
|
|
25
25
|
}
|
|
26
26
|
return normalized;
|
|
27
27
|
};
|
|
28
|
+
const stripWorkspaceAlias = (value) => {
|
|
29
|
+
const candidate = String(value || '').replace(/^\/+/, '');
|
|
30
|
+
if (!candidate || candidate === '.' || /^workspace\/?$/i.test(candidate)) {
|
|
31
|
+
return '';
|
|
32
|
+
}
|
|
33
|
+
if (/^workspace\//i.test(candidate)) {
|
|
34
|
+
return safeRelative(candidate.replace(/^workspace\/+/i, ''));
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
};
|
|
28
38
|
const decodeBoundaryUri = (value) => {
|
|
29
|
-
const match = value.match(/^vigthoria:\/\/(?:workspace|server-internal)\/?(.*)$/i);
|
|
39
|
+
const match = value.match(/^vigthoria:\/\/(?:workspace|server-internal|local-workspace)\/?(.*)$/i);
|
|
30
40
|
if (match) {
|
|
31
41
|
return safeRelative(match[1] || '');
|
|
32
42
|
}
|
|
@@ -39,6 +49,10 @@ export function normalizeV3WorkspaceRelativePath(rawPath, rootPath) {
|
|
|
39
49
|
if (/^vigthoria:\/\//i.test(input)) {
|
|
40
50
|
return '';
|
|
41
51
|
}
|
|
52
|
+
const workspaceAlias = stripWorkspaceAlias(input);
|
|
53
|
+
if (workspaceAlias !== null) {
|
|
54
|
+
return workspaceAlias;
|
|
55
|
+
}
|
|
42
56
|
const internalWorkspacePatterns = [
|
|
43
57
|
/^\/?var\/www\/\.vigthoria\/v3-temp\/vig-remote-[^/]+\/(.+)$/i,
|
|
44
58
|
/^\/?var\/www\/vigthoria:\/\/?server-internal\/?(.+)$/i,
|
package/install.ps1
CHANGED
|
@@ -5,25 +5,33 @@
|
|
|
5
5
|
$ErrorActionPreference = "Stop"
|
|
6
6
|
|
|
7
7
|
# Configuration
|
|
8
|
-
$CLI_VERSION = "1.
|
|
8
|
+
$CLI_VERSION = "1.11.25"
|
|
9
9
|
$INSTALL_DIR = "$env:USERPROFILE\.vigthoria"
|
|
10
10
|
$NPM_PACKAGE = "vigthoria-cli"
|
|
11
11
|
$GIT_PACKAGE_URL = "git+https://market.vigthoria.io/vigthoria/vigthoria-cli.git"
|
|
12
|
-
$
|
|
12
|
+
$MANIFEST_URL = "https://extension.vigthoria.io/downloads/manifest.json"
|
|
13
|
+
$HOSTED_TARBALL_URL = "https://extension.vigthoria.io/downloads/vigthoria-cli-$CLI_VERSION.tgz"
|
|
13
14
|
|
|
14
|
-
function Resolve-
|
|
15
|
+
function Resolve-ReleaseManifest {
|
|
15
16
|
try {
|
|
16
|
-
$
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
$manifest = Invoke-WebRequest -Uri $MANIFEST_URL -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop |
|
|
18
|
+
Select-Object -ExpandProperty Content |
|
|
19
|
+
ConvertFrom-Json
|
|
20
|
+
$stable = $manifest.channels.stable
|
|
21
|
+
if ($stable.version) {
|
|
22
|
+
$script:CLI_VERSION = [string]$stable.version
|
|
23
|
+
}
|
|
24
|
+
if ($stable.url) {
|
|
25
|
+
$script:HOSTED_TARBALL_URL = [string]$stable.url
|
|
26
|
+
} elseif ($stable.version) {
|
|
27
|
+
$script:HOSTED_TARBALL_URL = "https://extension.vigthoria.io/downloads/vigthoria-cli-$($stable.version).tgz"
|
|
20
28
|
}
|
|
21
29
|
} catch {
|
|
22
|
-
Write-Host "Release
|
|
30
|
+
Write-Host "Release manifest unavailable, using bundled fallback $CLI_VERSION" -ForegroundColor Yellow
|
|
23
31
|
}
|
|
24
32
|
}
|
|
25
33
|
|
|
26
|
-
Resolve-
|
|
34
|
+
Resolve-ReleaseManifest
|
|
27
35
|
|
|
28
36
|
function Write-Banner {
|
|
29
37
|
Write-Host ""
|
package/install.sh
CHANGED
|
@@ -26,11 +26,54 @@ else
|
|
|
26
26
|
fi
|
|
27
27
|
|
|
28
28
|
# Configuration
|
|
29
|
-
CLI_VERSION="1.
|
|
29
|
+
CLI_VERSION="1.11.25"
|
|
30
30
|
INSTALL_DIR="$HOME/.vigthoria"
|
|
31
31
|
REPO_URL="https://market.vigthoria.io/vigthoria/vigthoria-cli"
|
|
32
32
|
GIT_PACKAGE_URL="git+https://market.vigthoria.io/vigthoria/vigthoria-cli.git"
|
|
33
|
-
|
|
33
|
+
MANIFEST_URL="${VIGTHORIA_UPDATE_MANIFEST_URL:-https://extension.vigthoria.io/downloads/manifest.json}"
|
|
34
|
+
HOSTED_TARBALL_URL="https://extension.vigthoria.io/downloads/vigthoria-cli-${CLI_VERSION}.tgz"
|
|
35
|
+
|
|
36
|
+
resolve_release_manifest() {
|
|
37
|
+
if ! command -v curl >/dev/null 2>&1 || ! command -v python3 >/dev/null 2>&1; then
|
|
38
|
+
return 0
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
local manifest_json
|
|
42
|
+
manifest_json="$(curl -fsSL "$MANIFEST_URL" 2>/dev/null || true)"
|
|
43
|
+
if [[ -z "$manifest_json" ]]; then
|
|
44
|
+
return 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
local resolved
|
|
48
|
+
resolved="$(printf '%s' "$manifest_json" | python3 -c "
|
|
49
|
+
import json, sys
|
|
50
|
+
try:
|
|
51
|
+
data = json.load(sys.stdin)
|
|
52
|
+
stable = (data.get('channels') or {}).get('stable') or {}
|
|
53
|
+
version = str(stable.get('version') or '').strip()
|
|
54
|
+
url = str(stable.get('url') or '').strip()
|
|
55
|
+
if version:
|
|
56
|
+
print(version)
|
|
57
|
+
if url:
|
|
58
|
+
print(url)
|
|
59
|
+
except Exception:
|
|
60
|
+
pass
|
|
61
|
+
" 2>/dev/null || true)"
|
|
62
|
+
|
|
63
|
+
if [[ -n "$resolved" ]]; then
|
|
64
|
+
mapfile -t _manifest_lines <<< "$resolved"
|
|
65
|
+
if [[ -n "${_manifest_lines[0]:-}" ]]; then
|
|
66
|
+
CLI_VERSION="${_manifest_lines[0]}"
|
|
67
|
+
fi
|
|
68
|
+
if [[ -n "${_manifest_lines[1]:-}" ]]; then
|
|
69
|
+
HOSTED_TARBALL_URL="${_manifest_lines[1]}"
|
|
70
|
+
elif [[ -n "${_manifest_lines[0]:-}" ]]; then
|
|
71
|
+
HOSTED_TARBALL_URL="https://extension.vigthoria.io/downloads/vigthoria-cli-${CLI_VERSION}.tgz"
|
|
72
|
+
fi
|
|
73
|
+
fi
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
resolve_release_manifest
|
|
34
77
|
|
|
35
78
|
# Detect platform and set appropriate bin directory
|
|
36
79
|
detect_platform() {
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@ REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
|
17
17
|
PKG_FILE="$REPO_ROOT/vigthoria-cli-${VERSION}.tgz"
|
|
18
18
|
HOST_DOWNLOADS_DIR="${HOST_DOWNLOADS_DIR:-}"
|
|
19
19
|
HOST_MANIFEST_FILE="${HOST_MANIFEST_FILE:-}"
|
|
20
|
-
PUBLIC_BASE_URL="${PUBLIC_BASE_URL:-https://
|
|
20
|
+
PUBLIC_BASE_URL="${PUBLIC_BASE_URL:-https://extension.vigthoria.io/downloads}"
|
|
21
21
|
CHANNEL="${CHANNEL:-stable}"
|
|
22
22
|
|
|
23
23
|
if [[ ! -f "$PKG_FILE" ]]; then
|