vigthoria-cli 1.11.12 → 1.11.17

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.
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Sub-second path: Template Service match → write index.html (skip V3 planner).
3
+ */
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ function ensurePopupScript(html, vision) {
7
+ const text = String(vision || '').toLowerCase();
8
+ if (!/\b(popup|alert|hello\s*world)\b/.test(text)) {
9
+ return html;
10
+ }
11
+ if (/\balert\s*\(/i.test(html)) {
12
+ return html;
13
+ }
14
+ const script = "<script>window.addEventListener('load',()=>alert('Hello World'));</script>";
15
+ if (/<\/body>/i.test(html)) {
16
+ return html.replace(/<\/body>/i, `${script}\n</body>`);
17
+ }
18
+ return `${html}\n${script}`;
19
+ }
20
+ function minimalHelloWorldHtml(popupMessage = 'Hello World') {
21
+ const safe = String(popupMessage || 'Hello World').replace(/\\/g, '\\\\').replace(/'/g, "\\'");
22
+ return `<!DOCTYPE html>
23
+ <html lang="en">
24
+ <head>
25
+ <meta charset="UTF-8">
26
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
27
+ <title>Hello World</title>
28
+ <style>
29
+ body { font-family: system-ui, sans-serif; display: grid; place-items: center; min-height: 100vh; margin: 0; background: #0a0a1a; color: #e6f0ff; }
30
+ h1 { font-size: 2rem; }
31
+ </style>
32
+ </head>
33
+ <body>
34
+ <h1>Hello World</h1>
35
+ <script>window.addEventListener('load', () => alert('${safe}'));</script>
36
+ </body>
37
+ </html>
38
+ `;
39
+ }
40
+ async function fetchTemplateMatch(api, vision, timeoutMs = 15000) {
41
+ const bases = api.getTemplateServiceBaseUrls();
42
+ let lastError = 'Template Service unreachable';
43
+ for (const base of bases) {
44
+ const controller = new AbortController();
45
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
46
+ try {
47
+ const response = await fetch(`${base}/match`, {
48
+ method: 'POST',
49
+ headers: { 'Content-Type': 'application/json' },
50
+ body: JSON.stringify({ vision }),
51
+ signal: controller.signal,
52
+ });
53
+ clearTimeout(timer);
54
+ const payload = await response.json().catch(() => null);
55
+ if (!response.ok) {
56
+ lastError = payload?.error || `HTTP ${response.status} from ${base}`;
57
+ continue;
58
+ }
59
+ if (!payload?.success || !payload?.html) {
60
+ lastError = payload?.message || payload?.error || 'No template match';
61
+ continue;
62
+ }
63
+ const templateMeta = payload.template;
64
+ const templateName = typeof templateMeta === 'string'
65
+ ? templateMeta
66
+ : (templateMeta?.name || templateMeta?.id);
67
+ return {
68
+ html: String(payload.html),
69
+ template: templateName,
70
+ confidence: Number(payload.confidence) || 0,
71
+ processingMs: Number(payload.processing_time || payload.processingTime) || 0,
72
+ };
73
+ }
74
+ catch (err) {
75
+ clearTimeout(timer);
76
+ lastError = err?.message || String(err);
77
+ }
78
+ }
79
+ return { error: lastError };
80
+ }
81
+ function extractPopupMessage(vision) {
82
+ const quoted = vision.match(/popup\s+with\s+["']([^"']+)["']/i);
83
+ if (quoted?.[1])
84
+ return quoted[1];
85
+ if (/\bhello\s*world\b/i.test(vision))
86
+ return 'Hello World';
87
+ return 'Hello World';
88
+ }
89
+ export async function runTemplateInstantPath(api, vision, workspacePath, options = {}) {
90
+ const entryPath = options.entryPath || 'index.html';
91
+ const target = path.join(workspacePath, entryPath);
92
+ let html;
93
+ let usedFallback = false;
94
+ let match = {};
95
+ if (options.forceMinimalFallback) {
96
+ html = minimalHelloWorldHtml(extractPopupMessage(vision));
97
+ usedFallback = true;
98
+ }
99
+ else {
100
+ match = await fetchTemplateMatch(api, vision);
101
+ html = match.html;
102
+ if (!html && options.allowFallback !== false) {
103
+ html = minimalHelloWorldHtml(extractPopupMessage(vision));
104
+ usedFallback = true;
105
+ }
106
+ }
107
+ if (!html) {
108
+ return { ok: false, error: match.error || 'Template match failed' };
109
+ }
110
+ html = ensurePopupScript(html, vision);
111
+ fs.mkdirSync(path.dirname(target), { recursive: true });
112
+ fs.writeFileSync(target, html, 'utf8');
113
+ return {
114
+ ok: true,
115
+ entryPath,
116
+ templateName: match.template || (usedFallback ? 'minimal-hello-world' : undefined),
117
+ confidence: match.confidence,
118
+ processingMs: match.processingMs,
119
+ usedFallback,
120
+ };
121
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Normalize V3 SSE / tool paths to a safe workspace-relative POSIX path.
3
+ * Decodes vigthoria:// boundary URIs from server scrubbing before local writes.
4
+ */
5
+ export declare function normalizeV3WorkspaceRelativePath(rawPath: string, rootPath?: string): string;
6
+ /** Join workspace root + relative path without path.resolve URI corruption on Windows. */
7
+ export declare function joinV3WorkspacePath(rootPath: string, relativePath: string): string;
@@ -0,0 +1,85 @@
1
+ import path from 'path';
2
+ /**
3
+ * Normalize V3 SSE / tool paths to a safe workspace-relative POSIX path.
4
+ * Decodes vigthoria:// boundary URIs from server scrubbing before local writes.
5
+ */
6
+ export function normalizeV3WorkspaceRelativePath(rawPath, rootPath) {
7
+ let input = String(rawPath || '').trim().replace(/\\/g, '/').replace(/^\.\//, '');
8
+ if (!input) {
9
+ return '';
10
+ }
11
+ const safeRelative = (candidate) => {
12
+ const stripped = String(candidate || '').replace(/^\/+/, '');
13
+ if (!stripped) {
14
+ return '';
15
+ }
16
+ if (process.platform === 'win32' && /:/.test(stripped) && !/^[a-zA-Z]:/.test(stripped)) {
17
+ return '';
18
+ }
19
+ if (/^[a-zA-Z]:\//.test(stripped)) {
20
+ return '';
21
+ }
22
+ const normalized = path.posix.normalize(stripped);
23
+ if (!normalized || normalized === '.' || normalized === '..' || normalized.startsWith('../') || path.posix.isAbsolute(normalized)) {
24
+ return '';
25
+ }
26
+ return normalized;
27
+ };
28
+ const decodeBoundaryUri = (value) => {
29
+ const match = value.match(/^vigthoria:\/\/(?:workspace|server-internal)\/?(.*)$/i);
30
+ if (match) {
31
+ return safeRelative(match[1] || '');
32
+ }
33
+ return null;
34
+ };
35
+ const decoded = decodeBoundaryUri(input);
36
+ if (decoded !== null) {
37
+ return decoded;
38
+ }
39
+ if (/^vigthoria:\/\//i.test(input)) {
40
+ return '';
41
+ }
42
+ const internalWorkspacePatterns = [
43
+ /^\/?var\/www\/\.vigthoria\/v3-temp\/vig-remote-[^/]+\/(.+)$/i,
44
+ /^\/?var\/www\/vigthoria:\/\/?server-internal\/?(.+)$/i,
45
+ /^var\/www\/vigthoria:\/\/?server-internal\/?(.+)$/i,
46
+ /^var\/www\/vigthoria:\/\/server-internal\/?(.+)$/i,
47
+ /^\.vigthoria\/v3-temp\/vig-remote-[^/]+\/(.+)$/i,
48
+ /^\/?tmp\/vig-remote(?:-server)?-[^/]+\/(.+)$/i,
49
+ /^\/?tmp\/vig-fork-[^/]+\/(.+)$/i,
50
+ /^\/?home\/user\/(.+)$/i,
51
+ /^\/?root\/(.+)$/i,
52
+ ];
53
+ for (const pattern of internalWorkspacePatterns) {
54
+ const match = input.match(pattern);
55
+ if (match && match[1]) {
56
+ return safeRelative(match[1]);
57
+ }
58
+ }
59
+ const normalizedRoot = String(rootPath || '').trim().replace(/\\/g, '/').replace(/\/+$/g, '');
60
+ if (normalizedRoot) {
61
+ const rootNoLeadingSlash = normalizedRoot.replace(/^\//, '');
62
+ const rootBase = path.posix.basename(normalizedRoot);
63
+ const prefixes = [
64
+ `${normalizedRoot}/`,
65
+ `${rootNoLeadingSlash}/`,
66
+ `${rootBase}/`,
67
+ ];
68
+ for (const prefix of prefixes) {
69
+ if (input.startsWith(prefix)) {
70
+ return safeRelative(input.slice(prefix.length));
71
+ }
72
+ }
73
+ const embeddedRoot = `/${rootBase}/`;
74
+ const embeddedIndex = input.indexOf(embeddedRoot);
75
+ if (embeddedIndex >= 0) {
76
+ return safeRelative(input.slice(embeddedIndex + embeddedRoot.length));
77
+ }
78
+ }
79
+ return safeRelative(input);
80
+ }
81
+ /** Join workspace root + relative path without path.resolve URI corruption on Windows. */
82
+ export function joinV3WorkspacePath(rootPath, relativePath) {
83
+ const segments = relativePath.split('/').filter((segment) => segment && segment !== '.');
84
+ return path.join(rootPath, ...segments);
85
+ }
@@ -22,6 +22,7 @@ import fs from 'fs';
22
22
  import path from 'path';
23
23
  import WebSocket from 'ws';
24
24
  import { Logger } from './logger.js';
25
+ import { joinV3WorkspacePath, normalizeV3WorkspaceRelativePath } from './v3-workspace-path.js';
25
26
  const logger = new Logger();
26
27
  logger.setVerbose(!!process.env.VIGTHORIA_DEBUG);
27
28
  // Files/dirs to ignore in the watcher
@@ -74,9 +75,15 @@ export function applyFileMutation(event, workspaceRoot) {
74
75
  return false;
75
76
  if (!event.path || !workspaceRoot)
76
77
  return false;
77
- const absPath = path.resolve(workspaceRoot, event.path);
78
+ const relativePath = normalizeV3WorkspaceRelativePath(event.path, workspaceRoot);
79
+ if (!relativePath) {
80
+ logger.warn(`Refusing to apply mutation with invalid path: ${event.path}`);
81
+ return false;
82
+ }
83
+ const absPath = joinV3WorkspacePath(workspaceRoot, relativePath);
84
+ const resolvedRoot = path.resolve(workspaceRoot);
78
85
  // Safety: ensure the resolved path is within the workspace
79
- if (!absPath.startsWith(path.resolve(workspaceRoot) + path.sep) && absPath !== path.resolve(workspaceRoot)) {
86
+ if (!absPath.startsWith(resolvedRoot + path.sep) && absPath !== resolvedRoot) {
80
87
  logger.warn(`Refusing to apply mutation outside workspace: ${event.path}`);
81
88
  return false;
82
89
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.11.12",
3
+ "version": "1.11.17",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -41,6 +41,7 @@
41
41
  "test:regression": "npm run build && node scripts/test-regression-1.6.22.js",
42
42
  "test:agent:smoke": "npm run build && node scripts/test-agent-smoke.js",
43
43
  "test:agent:routing": "npm run build && node scripts/test-agent-routing-policy.js",
44
+ "test:agent:route": "npm run build && node scripts/test-request-intent.js && node scripts/test-agent-route.js && node scripts/test-local-use-cases.js",
44
45
  "test:agent:context": "npm run build && node scripts/test-agent-context-trace-e2e.js",
45
46
  "test:mcp:context": "npm run build && node scripts/test-mcp-context-session-e2e.js",
46
47
  "test:workflow:surface": "npm run build && node scripts/test-workflow-surface-e2e.js",