vigthoria-cli 1.13.20 → 1.13.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +63 -128
- 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 +3 -42
- package/dist/commands/chat.js +378 -1123
- package/dist/commands/coding-registration.d.ts +4 -0
- package/dist/commands/coding-registration.js +24 -0
- package/dist/commands/config.js +23 -29
- 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 -1589
- 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 +44 -266
- package/dist/utils/api.js +647 -3180
- 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 +220 -94
- 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 +5 -6
- package/dist/utils/fastAgentRouter.js +17 -25
- 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 +17 -3
- 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 +85 -142
- package/install.sh +75 -80
- package/package.json +43 -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 -174
- 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,76 @@
|
|
|
1
|
+
import type { MutationJournalReport } from './mutation-journal.js';
|
|
2
|
+
import type { PreviewScreenshotPort } from './preview-screenshot-adapter.js';
|
|
3
|
+
export interface FrontendPreviewGateResult {
|
|
4
|
+
required: boolean;
|
|
5
|
+
passed: boolean;
|
|
6
|
+
skipped?: boolean;
|
|
7
|
+
operationId?: string;
|
|
8
|
+
partialMutation?: boolean;
|
|
9
|
+
mutation?: MutationJournalReport;
|
|
10
|
+
backendUrl?: string;
|
|
11
|
+
entryPath?: string;
|
|
12
|
+
assetPaths?: {
|
|
13
|
+
css: string[];
|
|
14
|
+
js: string[];
|
|
15
|
+
};
|
|
16
|
+
artifacts?: {
|
|
17
|
+
manifestPath?: string;
|
|
18
|
+
screenshotPath?: string;
|
|
19
|
+
previewFileUrl?: string;
|
|
20
|
+
screenshotCaptured?: boolean;
|
|
21
|
+
screenshotError?: string;
|
|
22
|
+
};
|
|
23
|
+
modes?: {
|
|
24
|
+
design?: {
|
|
25
|
+
ready: boolean;
|
|
26
|
+
devices?: string[];
|
|
27
|
+
variantCount?: number;
|
|
28
|
+
};
|
|
29
|
+
live?: {
|
|
30
|
+
ready: boolean;
|
|
31
|
+
entryPoint?: string;
|
|
32
|
+
};
|
|
33
|
+
production?: {
|
|
34
|
+
ready: boolean;
|
|
35
|
+
deploymentTarget?: string;
|
|
36
|
+
recommendedCommand?: string;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
summary?: Record<string, unknown>;
|
|
40
|
+
processingTimeMs?: number;
|
|
41
|
+
error?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface FrontendPreviewServiceDependencies {
|
|
44
|
+
getBaseUrls(): string[];
|
|
45
|
+
getAccessToken(): string | null;
|
|
46
|
+
fetch(input: string, init: RequestInit): Promise<Response>;
|
|
47
|
+
resolveTargetPath(context: Record<string, any>): string;
|
|
48
|
+
extractExpectedFiles(message: string, context: Record<string, any>): string[];
|
|
49
|
+
isAnalysisOnlyTask(message: string, context: Record<string, any>): boolean;
|
|
50
|
+
sanitizeError(text: string): string;
|
|
51
|
+
screenshotAdapter: PreviewScreenshotPort;
|
|
52
|
+
}
|
|
53
|
+
export declare class FrontendPreviewService {
|
|
54
|
+
private readonly dependencies;
|
|
55
|
+
constructor(dependencies: FrontendPreviewServiceDependencies);
|
|
56
|
+
private isFrontendTask;
|
|
57
|
+
/**
|
|
58
|
+
* Returns true when the prompt describes a read-only / analysis task.
|
|
59
|
+
* Used to suppress preview gate and other write-oriented side effects.
|
|
60
|
+
*/
|
|
61
|
+
isAnalysisOnlyTask(message?: string, context?: Record<string, any>): boolean;
|
|
62
|
+
normalizeWorkspaceRelativePath(filePath: string): string;
|
|
63
|
+
collectAgentStateSyncPaths(rootPath: string): string[];
|
|
64
|
+
private listFrontendWorkspaceFiles;
|
|
65
|
+
private chooseFrontendPreviewEntry;
|
|
66
|
+
extractLinkedFrontendAssets(html: string, entryPath: string): {
|
|
67
|
+
css: string[];
|
|
68
|
+
js: string[];
|
|
69
|
+
assets: string[];
|
|
70
|
+
};
|
|
71
|
+
private buildFrontendForPreview;
|
|
72
|
+
private gatherFrontendPreviewArtifacts;
|
|
73
|
+
private evaluateFrontendVisualProof;
|
|
74
|
+
private persistFrontendPreviewArtifacts;
|
|
75
|
+
runTemplateServicePreviewGate(message?: string, context?: Record<string, any>): Promise<FrontendPreviewGateResult>;
|
|
76
|
+
}
|
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
3
|
+
import * as path from 'node:path';
|
|
4
|
+
import { assertProcessNetworkAllowed } from './process-policy.js';
|
|
5
|
+
import { safeChildProcessEnv, scanOutboundContext } from './secret-policy.js';
|
|
6
|
+
import { resolveWorkspacePath } from './workspace-boundary.js';
|
|
7
|
+
export class FrontendPreviewService {
|
|
8
|
+
dependencies;
|
|
9
|
+
constructor(dependencies) {
|
|
10
|
+
this.dependencies = dependencies;
|
|
11
|
+
}
|
|
12
|
+
isFrontendTask(message = '', context = {}) {
|
|
13
|
+
if (context.forceFrontendPreview === true) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
// Never treat analysis-only tasks as frontend tasks — preview gate
|
|
17
|
+
// should not fire for read-only inspection prompts.
|
|
18
|
+
if (this.dependencies.isAnalysisOnlyTask(message, context)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
const prompt = String(message || '');
|
|
22
|
+
const expectedFiles = this.dependencies.extractExpectedFiles(message, context);
|
|
23
|
+
const hasHtmlEntry = expectedFiles.some((filePath) => /\.html?$/i.test(filePath));
|
|
24
|
+
return /(premium|polished|landing|site|page|dashboard|saas|frontend|ui|pricing|showcase|hero|responsive)/i.test(prompt)
|
|
25
|
+
|| hasHtmlEntry;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Returns true when the prompt describes a read-only / analysis task.
|
|
29
|
+
* Used to suppress preview gate and other write-oriented side effects.
|
|
30
|
+
*/
|
|
31
|
+
isAnalysisOnlyTask(message = '', context = {}) {
|
|
32
|
+
const prompt = String(message || '').toLowerCase();
|
|
33
|
+
const taskType = String(context.agentTaskType || '').toLowerCase();
|
|
34
|
+
if (taskType === 'debugging' || taskType === 'analysis' || taskType === 'analysis_only')
|
|
35
|
+
return true;
|
|
36
|
+
if (context.workflowType === 'analysis_only')
|
|
37
|
+
return true;
|
|
38
|
+
return /^(what|which|how many|list|show|check|inspect|analyze|analyse|audit|explain|describe|summarize|summarise|review|overview|find|count|read|look at|tell me|diagnos)/i.test(prompt.trim());
|
|
39
|
+
}
|
|
40
|
+
normalizeWorkspaceRelativePath(filePath) {
|
|
41
|
+
return String(filePath || '').trim().replace(/\\/g, '/').replace(/^\.\//, '');
|
|
42
|
+
}
|
|
43
|
+
collectAgentStateSyncPaths(rootPath) {
|
|
44
|
+
const paths = [];
|
|
45
|
+
const progressPath = path.join(rootPath, '.vigthoria', 'agent-state', 'progress.json');
|
|
46
|
+
if (!fs.existsSync(progressPath)) {
|
|
47
|
+
return paths;
|
|
48
|
+
}
|
|
49
|
+
paths.push('.vigthoria/agent-state/progress.json');
|
|
50
|
+
const tasksDir = path.join(rootPath, '.vigthoria', 'agent-state', 'tasks');
|
|
51
|
+
if (!fs.existsSync(tasksDir)) {
|
|
52
|
+
return paths;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
for (const entry of fs.readdirSync(tasksDir, { withFileTypes: true })) {
|
|
56
|
+
if (!entry.isFile() || !entry.name.endsWith('.json')) {
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
paths.push(`.vigthoria/agent-state/tasks/${entry.name}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Ignore unreadable task snapshots.
|
|
64
|
+
}
|
|
65
|
+
return paths;
|
|
66
|
+
}
|
|
67
|
+
listFrontendWorkspaceFiles(rootPath) {
|
|
68
|
+
const results = [];
|
|
69
|
+
const stack = [rootPath];
|
|
70
|
+
while (stack.length > 0) {
|
|
71
|
+
const current = stack.pop();
|
|
72
|
+
if (!current) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
|
76
|
+
if (entry.name === '.git' || entry.name === 'node_modules' || entry.name === '.vigthoria') {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const fullPath = path.join(current, entry.name);
|
|
80
|
+
if (entry.isDirectory()) {
|
|
81
|
+
stack.push(fullPath);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (!entry.isFile()) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
const relativePath = this.normalizeWorkspaceRelativePath(path.relative(rootPath, fullPath));
|
|
88
|
+
if (relativePath) {
|
|
89
|
+
results.push(relativePath);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
results.sort((left, right) => left.localeCompare(right));
|
|
94
|
+
return results;
|
|
95
|
+
}
|
|
96
|
+
chooseFrontendPreviewEntry(_rootPath, htmlPaths, expectedFiles = []) {
|
|
97
|
+
const normalizedExpected = expectedFiles
|
|
98
|
+
.map((filePath) => this.normalizeWorkspaceRelativePath(filePath))
|
|
99
|
+
.filter((filePath) => /\.html?$/i.test(filePath));
|
|
100
|
+
for (const expectedPath of normalizedExpected) {
|
|
101
|
+
if (htmlPaths.includes(expectedPath)) {
|
|
102
|
+
return expectedPath;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const scored = htmlPaths.map((relativePath) => {
|
|
106
|
+
const baseName = path.posix.basename(relativePath).toLowerCase();
|
|
107
|
+
const depth = relativePath.split('/').length - 1;
|
|
108
|
+
let score = 0;
|
|
109
|
+
const normalizedPath = relativePath.toLowerCase();
|
|
110
|
+
const topLevelDir = normalizedPath.split('/')[0];
|
|
111
|
+
if (['dist', 'build', 'out'].includes(topLevelDir)) {
|
|
112
|
+
score += 320;
|
|
113
|
+
}
|
|
114
|
+
else if (topLevelDir === 'public') {
|
|
115
|
+
score += 180;
|
|
116
|
+
}
|
|
117
|
+
if (normalizedPath === 'index.html') {
|
|
118
|
+
score += 200;
|
|
119
|
+
}
|
|
120
|
+
if (baseName === 'index.html') {
|
|
121
|
+
score += 120;
|
|
122
|
+
}
|
|
123
|
+
if (/(landing|home|main|dashboard|app)/i.test(baseName)) {
|
|
124
|
+
score += 60;
|
|
125
|
+
}
|
|
126
|
+
if (depth === 0) {
|
|
127
|
+
score += 40;
|
|
128
|
+
}
|
|
129
|
+
return { relativePath, score, depth };
|
|
130
|
+
}).sort((left, right) => right.score - left.score || left.depth - right.depth || left.relativePath.localeCompare(right.relativePath));
|
|
131
|
+
return scored[0]?.relativePath || null;
|
|
132
|
+
}
|
|
133
|
+
extractLinkedFrontendAssets(html, entryPath) {
|
|
134
|
+
const css = new Set();
|
|
135
|
+
const js = new Set();
|
|
136
|
+
const assets = new Set();
|
|
137
|
+
const entryDir = path.posix.dirname(this.normalizeWorkspaceRelativePath(entryPath) || '.');
|
|
138
|
+
const normalizeAsset = (assetPath) => {
|
|
139
|
+
const clean = String(assetPath || '').trim();
|
|
140
|
+
if (!clean || clean.startsWith('http://') || clean.startsWith('https://') || clean.startsWith('//') || clean.startsWith('data:') || clean.startsWith('#')) {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
const withoutQuery = clean.split('?')[0].split('#')[0];
|
|
144
|
+
if (!withoutQuery) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
if (withoutQuery.startsWith('/')) {
|
|
148
|
+
return this.normalizeWorkspaceRelativePath(path.posix.normalize(path.posix.join(entryDir, withoutQuery.slice(1))));
|
|
149
|
+
}
|
|
150
|
+
return this.normalizeWorkspaceRelativePath(path.posix.normalize(path.posix.join(entryDir, withoutQuery)));
|
|
151
|
+
};
|
|
152
|
+
const cssPattern = /<link[^>]+href=["']([^"']+\.css(?:[?#][^"']*)?)["'][^>]*>/gi;
|
|
153
|
+
const jsPattern = /<script[^>]+src=["']([^"']+\.(?:js|mjs|cjs)(?:[?#][^"']*)?)["'][^>]*>/gi;
|
|
154
|
+
let match;
|
|
155
|
+
while ((match = cssPattern.exec(String(html || ''))) !== null) {
|
|
156
|
+
const resolved = normalizeAsset(match[1]);
|
|
157
|
+
if (resolved) {
|
|
158
|
+
css.add(resolved);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
while ((match = jsPattern.exec(String(html || ''))) !== null) {
|
|
162
|
+
const resolved = normalizeAsset(match[1]);
|
|
163
|
+
if (resolved) {
|
|
164
|
+
js.add(resolved);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const assetPattern = /<(?:link|img|source|video|audio)\b[^>]+(?:href|src)=["']([^"']+)["'][^>]*>/gi;
|
|
168
|
+
while ((match = assetPattern.exec(String(html || ''))) !== null) {
|
|
169
|
+
const resolved = normalizeAsset(match[1]);
|
|
170
|
+
if (resolved)
|
|
171
|
+
assets.add(resolved);
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
css: Array.from(css),
|
|
175
|
+
js: Array.from(js),
|
|
176
|
+
assets: Array.from(assets),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
async buildFrontendForPreview(rootPath) {
|
|
180
|
+
const packagePath = path.join(rootPath, 'package.json');
|
|
181
|
+
if (!fs.existsSync(packagePath)) {
|
|
182
|
+
return { attempted: false, succeeded: false };
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
186
|
+
if (!packageJson.scripts?.build) {
|
|
187
|
+
return { attempted: false, succeeded: false };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
return { attempted: false, succeeded: false, error: `Invalid package.json: ${error?.message || String(error)}` };
|
|
192
|
+
}
|
|
193
|
+
const npmCommand = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
194
|
+
const npmArgs = ['run', 'build'];
|
|
195
|
+
assertProcessNetworkAllowed(npmCommand, npmArgs);
|
|
196
|
+
return await new Promise((resolve) => {
|
|
197
|
+
execFile(npmCommand, npmArgs, { cwd: rootPath, timeout: 150_000, windowsHide: true, maxBuffer: 2 * 1024 * 1024, env: safeChildProcessEnv() }, (error) => {
|
|
198
|
+
if (error) {
|
|
199
|
+
resolve({ attempted: true, succeeded: false, error: this.dependencies.sanitizeError(error.message).slice(0, 300) });
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
resolve({ attempted: true, succeeded: true });
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
gatherFrontendPreviewArtifacts(message = '', context = {}) {
|
|
207
|
+
const rootPath = this.dependencies.resolveTargetPath(context);
|
|
208
|
+
if (!rootPath || !fs.existsSync(rootPath)) {
|
|
209
|
+
return { error: 'Frontend preview gate requires a readable project workspace.' };
|
|
210
|
+
}
|
|
211
|
+
const workspaceFiles = this.listFrontendWorkspaceFiles(rootPath);
|
|
212
|
+
const expectedFiles = this.dependencies.extractExpectedFiles(message, context);
|
|
213
|
+
const htmlPaths = workspaceFiles.filter((filePath) => /\.html?$/i.test(filePath));
|
|
214
|
+
if (htmlPaths.length === 0) {
|
|
215
|
+
return { error: 'Frontend preview gate requires at least one HTML entry file in the workspace.' };
|
|
216
|
+
}
|
|
217
|
+
const htmlPath = this.chooseFrontendPreviewEntry(rootPath, htmlPaths, expectedFiles);
|
|
218
|
+
if (!htmlPath) {
|
|
219
|
+
return { error: 'Frontend preview gate could not determine a primary HTML entry file.' };
|
|
220
|
+
}
|
|
221
|
+
const html = fs.readFileSync(resolveWorkspacePath(rootPath, htmlPath), 'utf8');
|
|
222
|
+
const linkedAssets = this.extractLinkedFrontendAssets(html, htmlPath);
|
|
223
|
+
const cssFiles = workspaceFiles.filter((filePath) => /\.css$/i.test(filePath));
|
|
224
|
+
const jsFiles = workspaceFiles.filter((filePath) => /\.(?:js|mjs|cjs)$/i.test(filePath));
|
|
225
|
+
const expectedCss = expectedFiles
|
|
226
|
+
.map((filePath) => this.normalizeWorkspaceRelativePath(filePath))
|
|
227
|
+
.filter((filePath) => /\.css$/i.test(filePath));
|
|
228
|
+
const expectedJs = expectedFiles
|
|
229
|
+
.map((filePath) => this.normalizeWorkspaceRelativePath(filePath))
|
|
230
|
+
.filter((filePath) => /\.(?:js|mjs|cjs)$/i.test(filePath));
|
|
231
|
+
const chooseExisting = (candidates, fallback) => {
|
|
232
|
+
const selected = new Set();
|
|
233
|
+
for (const candidate of [...candidates, ...fallback]) {
|
|
234
|
+
const normalized = this.normalizeWorkspaceRelativePath(candidate);
|
|
235
|
+
if (!normalized || selected.has(normalized)) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
resolveWorkspacePath(rootPath, normalized);
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
selected.add(normalized);
|
|
245
|
+
}
|
|
246
|
+
return Array.from(selected);
|
|
247
|
+
};
|
|
248
|
+
// When the entry document declares assets, those links are authoritative.
|
|
249
|
+
// Pulling every CSS/JS file from the workspace also ingested audit copies,
|
|
250
|
+
// rejected model drafts, and test harnesses into the production proof body.
|
|
251
|
+
// Fall back to expected/workspace files only for documents without links.
|
|
252
|
+
const selectedCssFiles = (linkedAssets.css.length > 0
|
|
253
|
+
? chooseExisting(linkedAssets.css, [])
|
|
254
|
+
: chooseExisting(expectedCss, cssFiles)).slice(0, 12);
|
|
255
|
+
const selectedJsFiles = (linkedAssets.js.length > 0
|
|
256
|
+
? chooseExisting(linkedAssets.js, [])
|
|
257
|
+
: chooseExisting(expectedJs, jsFiles)).slice(0, 12);
|
|
258
|
+
const css = selectedCssFiles.map((filePath) => fs.readFileSync(resolveWorkspacePath(rootPath, filePath), 'utf8')).join('\n\n');
|
|
259
|
+
const js = selectedJsFiles.map((filePath) => fs.readFileSync(resolveWorkspacePath(rootPath, filePath), 'utf8')).join('\n\n');
|
|
260
|
+
return { htmlPath, html, css, js, cssPaths: selectedCssFiles, jsPaths: selectedJsFiles };
|
|
261
|
+
}
|
|
262
|
+
evaluateFrontendVisualProof(summary = {}, artifacts = {}) {
|
|
263
|
+
const reasons = [];
|
|
264
|
+
const cssPaths = Array.isArray(artifacts.cssPaths) ? artifacts.cssPaths : [];
|
|
265
|
+
const html = String(artifacts.html || '');
|
|
266
|
+
const css = String(artifacts.css || '');
|
|
267
|
+
const js = String(artifacts.js || '');
|
|
268
|
+
if (!summary.hasViewportMeta) {
|
|
269
|
+
reasons.push('missing viewport metadata');
|
|
270
|
+
}
|
|
271
|
+
const hasStyleEvidence = cssPaths.length > 0 || /<style[\s>]/i.test(html) || css.trim().length > 0;
|
|
272
|
+
if (!hasStyleEvidence) {
|
|
273
|
+
reasons.push('missing stylesheet or inline style evidence');
|
|
274
|
+
}
|
|
275
|
+
const hasInteractionOrResponsiveEvidence = Boolean(summary.hasResponsiveSignals) || Boolean(summary.hasInteractiveSignals) || /<script[\s>]/i.test(html) || js.trim().length > 0;
|
|
276
|
+
if (!hasInteractionOrResponsiveEvidence) {
|
|
277
|
+
reasons.push('missing responsive or interactive evidence');
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
ok: reasons.length === 0,
|
|
281
|
+
reasons,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
async persistFrontendPreviewArtifacts(previewGate, rootPath, context = {}) {
|
|
285
|
+
if (!previewGate.required || !previewGate.entryPath) {
|
|
286
|
+
return previewGate;
|
|
287
|
+
}
|
|
288
|
+
try {
|
|
289
|
+
const artifactsDir = resolveWorkspacePath(rootPath, '.vigthoria/proof/preview', { allowMissing: true });
|
|
290
|
+
fs.mkdirSync(artifactsDir, { recursive: true });
|
|
291
|
+
const contextId = String(context.contextId || 'preview').trim() || 'preview';
|
|
292
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
293
|
+
const baseName = `${timestamp}-${contextId}`;
|
|
294
|
+
const manifestPath = resolveWorkspacePath(rootPath, `.vigthoria/proof/preview/${baseName}.json`, { allowMissing: true });
|
|
295
|
+
const screenshotPath = resolveWorkspacePath(rootPath, `.vigthoria/proof/preview/${baseName}.png`, { allowMissing: true });
|
|
296
|
+
const entryAbsolutePath = resolveWorkspacePath(rootPath, previewGate.entryPath);
|
|
297
|
+
const previewFileUrl = `file://${entryAbsolutePath}`;
|
|
298
|
+
const screenshot = await this.dependencies.screenshotAdapter.capture(entryAbsolutePath, screenshotPath);
|
|
299
|
+
const screenshotRequired = context.requireScreenshot === true;
|
|
300
|
+
const effectivePreviewGate = screenshotRequired && !screenshot.captured
|
|
301
|
+
? {
|
|
302
|
+
...previewGate,
|
|
303
|
+
passed: false,
|
|
304
|
+
error: `${previewGate.error ? `${previewGate.error} ` : ''}Required screenshot proof is unavailable: ${screenshot.error || 'capture failed'}.`,
|
|
305
|
+
}
|
|
306
|
+
: previewGate;
|
|
307
|
+
const manifest = {
|
|
308
|
+
schemaVersion: 1,
|
|
309
|
+
createdAt: new Date().toISOString(),
|
|
310
|
+
contextId: context.contextId || null,
|
|
311
|
+
workspaceRoot: rootPath,
|
|
312
|
+
entryPath: previewGate.entryPath,
|
|
313
|
+
previewFileUrl,
|
|
314
|
+
previewCommand: `vigthoria preview -p ${rootPath}`,
|
|
315
|
+
previewGate: effectivePreviewGate,
|
|
316
|
+
validationArtifacts: {
|
|
317
|
+
summary: previewGate.summary || {},
|
|
318
|
+
modes: previewGate.modes || {},
|
|
319
|
+
backendUrl: previewGate.backendUrl || null,
|
|
320
|
+
processingTimeMs: previewGate.processingTimeMs || null,
|
|
321
|
+
assetPaths: previewGate.assetPaths || { css: [], js: [] },
|
|
322
|
+
},
|
|
323
|
+
screenshot: {
|
|
324
|
+
path: screenshot.captured ? screenshotPath : null,
|
|
325
|
+
captured: screenshot.captured,
|
|
326
|
+
error: screenshot.error || null,
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
|
330
|
+
return {
|
|
331
|
+
...effectivePreviewGate,
|
|
332
|
+
artifacts: {
|
|
333
|
+
manifestPath,
|
|
334
|
+
screenshotPath: screenshot.captured ? screenshotPath : undefined,
|
|
335
|
+
previewFileUrl,
|
|
336
|
+
screenshotCaptured: screenshot.captured,
|
|
337
|
+
screenshotError: screenshot.error,
|
|
338
|
+
},
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
return {
|
|
343
|
+
...previewGate,
|
|
344
|
+
...(context.requireScreenshot === true ? {
|
|
345
|
+
passed: false,
|
|
346
|
+
error: `${previewGate.error ? `${previewGate.error} ` : ''}Required screenshot proof could not be persisted.`,
|
|
347
|
+
} : {}),
|
|
348
|
+
artifacts: {
|
|
349
|
+
...(previewGate.artifacts || {}),
|
|
350
|
+
screenshotCaptured: false,
|
|
351
|
+
screenshotError: error?.message || String(error),
|
|
352
|
+
},
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
async runTemplateServicePreviewGate(message = '', context = {}) {
|
|
357
|
+
if (!this.isFrontendTask(message, context)) {
|
|
358
|
+
return { required: false, passed: true };
|
|
359
|
+
}
|
|
360
|
+
const rootPath = this.dependencies.resolveTargetPath(context);
|
|
361
|
+
let artifacts = this.gatherFrontendPreviewArtifacts(message, context);
|
|
362
|
+
let buildResult = null;
|
|
363
|
+
if (rootPath && artifacts.error?.includes('at least one HTML entry file')) {
|
|
364
|
+
buildResult = await this.buildFrontendForPreview(rootPath);
|
|
365
|
+
if (buildResult.attempted) {
|
|
366
|
+
artifacts = this.gatherFrontendPreviewArtifacts(message, context);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
if (artifacts.error || !artifacts.htmlPath || typeof artifacts.html !== 'string') {
|
|
370
|
+
const buildDetail = buildResult?.attempted
|
|
371
|
+
? ` Build retry ${buildResult.succeeded ? 'completed but produced no HTML entry' : `failed: ${buildResult.error || 'unknown build error'}`}.`
|
|
372
|
+
: '';
|
|
373
|
+
return {
|
|
374
|
+
required: true,
|
|
375
|
+
passed: false,
|
|
376
|
+
error: `${artifacts.error || 'Frontend preview gate could not collect frontend artifacts.'}${buildDetail}`,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
const previewScan = scanOutboundContext({
|
|
380
|
+
html: artifacts.html,
|
|
381
|
+
css: artifacts.css || '',
|
|
382
|
+
js: artifacts.js || '',
|
|
383
|
+
});
|
|
384
|
+
if (previewScan.findings.length > 0) {
|
|
385
|
+
return {
|
|
386
|
+
required: true,
|
|
387
|
+
passed: false,
|
|
388
|
+
skipped: true,
|
|
389
|
+
entryPath: artifacts.htmlPath,
|
|
390
|
+
error: 'Preview proof blocked because frontend artifacts contain secret material.',
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
// Cap artifact sizes to prevent 413 Payload Too Large
|
|
394
|
+
const PREVIEW_MAX_ARTIFACT_BYTES = 250 * 1024;
|
|
395
|
+
const html = artifacts.html.slice(0, PREVIEW_MAX_ARTIFACT_BYTES);
|
|
396
|
+
const css = (artifacts.css || '').slice(0, PREVIEW_MAX_ARTIFACT_BYTES);
|
|
397
|
+
const js = (artifacts.js || '').slice(0, PREVIEW_MAX_ARTIFACT_BYTES);
|
|
398
|
+
const errors = [];
|
|
399
|
+
for (const baseUrl of this.dependencies.getBaseUrls()) {
|
|
400
|
+
try {
|
|
401
|
+
const proofPayload = JSON.stringify({
|
|
402
|
+
vision: String(context.rawPrompt || message || '').slice(0, 2000),
|
|
403
|
+
html,
|
|
404
|
+
css,
|
|
405
|
+
js,
|
|
406
|
+
entryPath: artifacts.htmlPath,
|
|
407
|
+
workspaceName: path.basename(rootPath),
|
|
408
|
+
});
|
|
409
|
+
// Skip preview proof if payload is still too large (> 4 MB)
|
|
410
|
+
const configuredLimit = Number(context.previewMaxPayloadBytes);
|
|
411
|
+
const previewPayloadLimit = Number.isFinite(configuredLimit) && configuredLimit >= 1024
|
|
412
|
+
? Math.min(configuredLimit, 4 * 1024 * 1024)
|
|
413
|
+
: 4 * 1024 * 1024;
|
|
414
|
+
if (Buffer.byteLength(proofPayload, 'utf8') > previewPayloadLimit) {
|
|
415
|
+
return {
|
|
416
|
+
required: true,
|
|
417
|
+
passed: false,
|
|
418
|
+
skipped: true,
|
|
419
|
+
error: 'Preview proof skipped: payload exceeds size limit.',
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
const internalServiceKey = process.env.VIGTHORIA_TEMPLATE_SERVICE_KEY
|
|
423
|
+
|| process.env.V3_SERVICE_KEY;
|
|
424
|
+
const authToken = this.dependencies.getAccessToken();
|
|
425
|
+
const response = await this.dependencies.fetch(`${baseUrl}/preview-proof`, {
|
|
426
|
+
method: 'POST',
|
|
427
|
+
headers: {
|
|
428
|
+
'Content-Type': 'application/json',
|
|
429
|
+
Accept: 'application/json',
|
|
430
|
+
...(authToken ? {
|
|
431
|
+
Authorization: `Bearer ${authToken}`,
|
|
432
|
+
Cookie: `vigthoria-auth-token=${authToken}`,
|
|
433
|
+
} : {}),
|
|
434
|
+
...(internalServiceKey ? { 'x-vigthoria-service-key': internalServiceKey } : {}),
|
|
435
|
+
},
|
|
436
|
+
body: proofPayload,
|
|
437
|
+
});
|
|
438
|
+
if (!response.ok) {
|
|
439
|
+
const errorText = await response.text().catch(() => '');
|
|
440
|
+
throw new Error(`Template preview proof ${response.status}: ${this.dependencies.sanitizeError(errorText).slice(0, 200)}`);
|
|
441
|
+
}
|
|
442
|
+
const payload = await response.json();
|
|
443
|
+
const modes = payload?.modes || {};
|
|
444
|
+
const summary = payload?.summary || {};
|
|
445
|
+
const visualProof = this.evaluateFrontendVisualProof(summary, artifacts);
|
|
446
|
+
const passed = payload?.success === true
|
|
447
|
+
&& modes?.design?.ready === true
|
|
448
|
+
&& modes?.live?.ready === true
|
|
449
|
+
&& modes?.production?.ready === true
|
|
450
|
+
&& visualProof.ok;
|
|
451
|
+
const previewGate = {
|
|
452
|
+
required: true,
|
|
453
|
+
passed,
|
|
454
|
+
backendUrl: baseUrl,
|
|
455
|
+
entryPath: artifacts.htmlPath,
|
|
456
|
+
assetPaths: {
|
|
457
|
+
css: artifacts.cssPaths || [],
|
|
458
|
+
js: artifacts.jsPaths || [],
|
|
459
|
+
},
|
|
460
|
+
modes,
|
|
461
|
+
summary,
|
|
462
|
+
processingTimeMs: Number(payload?.processing_time || 0) || undefined,
|
|
463
|
+
error: passed ? undefined : (visualProof.ok ? 'Template Service preview modes did not all report ready.' : `Frontend preview proof is missing required visual evidence: ${visualProof.reasons.join(', ')}.`),
|
|
464
|
+
};
|
|
465
|
+
return await this.persistFrontendPreviewArtifacts(previewGate, rootPath, context);
|
|
466
|
+
}
|
|
467
|
+
catch (error) {
|
|
468
|
+
errors.push(`${baseUrl}: ${error?.message || String(error)}`);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return await this.persistFrontendPreviewArtifacts({
|
|
472
|
+
required: true,
|
|
473
|
+
passed: false,
|
|
474
|
+
entryPath: artifacts.htmlPath,
|
|
475
|
+
assetPaths: {
|
|
476
|
+
css: artifacts.cssPaths || [],
|
|
477
|
+
js: artifacts.jsPaths || [],
|
|
478
|
+
},
|
|
479
|
+
error: errors.join(' | ') || 'No Template Service preview endpoint was reachable.',
|
|
480
|
+
}, rootPath, context);
|
|
481
|
+
}
|
|
482
|
+
}
|
package/dist/utils/goaEvents.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Workbench GOA orchestrator context (VIGTHORIA_DECK_MODE / comfort prompt auto-route).
|
|
3
3
|
* Emitted as: @vigthoria-goa:{json}
|
|
4
4
|
*/
|
|
5
|
+
import { isSensitivePath, redactSensitiveText } from './secret-policy.js';
|
|
5
6
|
const GOA_PREFIX = '@vigthoria-goa:';
|
|
6
7
|
export function parseGoaLine(line) {
|
|
7
8
|
const trimmed = line.trim();
|
|
@@ -49,7 +50,9 @@ export function formatGoaSystemGrounding(context) {
|
|
|
49
50
|
if (typeof a.detected_framework === 'string')
|
|
50
51
|
parts.push(`- framework: ${a.detected_framework}`);
|
|
51
52
|
if (Array.isArray(a.target_files) && a.target_files.length) {
|
|
52
|
-
|
|
53
|
+
const safeTargets = a.target_files.filter((entry) => typeof entry === 'string' && !isSensitivePath(entry)).slice(0, 6);
|
|
54
|
+
if (safeTargets.length > 0)
|
|
55
|
+
parts.push(`- target_files: ${safeTargets.join(', ')}`);
|
|
53
56
|
}
|
|
54
57
|
if (typeof a.anchor_hint === 'string')
|
|
55
58
|
parts.push(`- anchor: ${a.anchor_hint}`);
|
|
@@ -59,5 +62,5 @@ export function formatGoaSystemGrounding(context) {
|
|
|
59
62
|
if (typeof context.instruction_count === 'number') {
|
|
60
63
|
parts.push(`- instructions_planned: ${context.instruction_count}`);
|
|
61
64
|
}
|
|
62
|
-
return parts.join('\n');
|
|
65
|
+
return redactSensitiveText(parts.join('\n'));
|
|
63
66
|
}
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -49,6 +49,7 @@ export declare function createSpinner(textOrOpts: string | OraOptions): Ora;
|
|
|
49
49
|
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'success';
|
|
50
50
|
export declare class Logger {
|
|
51
51
|
private verbose;
|
|
52
|
+
private safeArgs;
|
|
52
53
|
setVerbose(verbose: boolean): void;
|
|
53
54
|
debug(...args: unknown[]): void;
|
|
54
55
|
info(...args: unknown[]): void;
|
|
@@ -57,7 +58,7 @@ export declare class Logger {
|
|
|
57
58
|
success(...args: unknown[]): void;
|
|
58
59
|
ai(message: string): void;
|
|
59
60
|
user(message: string): void;
|
|
60
|
-
code(code: string,
|
|
61
|
+
code(code: string, _language?: string): void;
|
|
61
62
|
diff(added: string[], removed: string[]): void;
|
|
62
63
|
unifiedDiff(filePath: string, oldText: string, newText: string): void;
|
|
63
64
|
section(title: string): void;
|