@evalops/maestro 0.10.37 → 0.10.39
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/cli/rpc-mode.d.ts.map +1 -1
- package/dist/cli/rpc-mode.js +19 -9
- package/dist/cli/rpc-mode.js.map +1 -1
- package/dist/cli.js +203 -65
- package/dist/node_modules/@evalops/contracts/package.json +1 -1
- package/dist/node_modules/@evalops/tui/package.json +1 -1
- package/dist/rpc/rpc-client.js +1 -1
- package/dist/rpc/rpc-client.js.map +1 -1
- package/dist/safety/validators/network-policy-validator.d.ts.map +1 -1
- package/dist/safety/validators/network-policy-validator.js +38 -38
- package/dist/safety/validators/network-policy-validator.js.map +1 -1
- package/dist/telemetry/maestro-event-catalog.d.ts +1 -1
- package/dist/telemetry/maestro-event-catalog.d.ts.map +1 -1
- package/dist/telemetry/maestro-event-catalog.js +2 -1
- package/dist/telemetry/maestro-event-catalog.js.map +1 -1
- package/dist/tools/find.js +1 -1
- package/dist/tools/find.js.map +1 -1
- package/dist/tools/ripgrep-utils.d.ts.map +1 -1
- package/dist/tools/ripgrep-utils.js +88 -1
- package/dist/tools/ripgrep-utils.js.map +1 -1
- package/dist/tools/tools-manager.d.ts +1 -1
- package/dist/tools/tools-manager.d.ts.map +1 -1
- package/dist/tools/tools-manager.js +60 -18
- package/dist/tools/tools-manager.js.map +1 -1
- package/dist/version.json +2 -2
- package/package.json +8 -7
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { Type } from "@sinclair/typebox";
|
|
3
3
|
import { safeJsonParse } from "../utils/json.js";
|
|
4
|
+
import { ensureTool } from "./tools-manager.js";
|
|
4
5
|
export const pathSchema = Type.Optional(Type.Union([
|
|
5
6
|
Type.String({
|
|
6
7
|
description: "Directory or file to search",
|
|
@@ -28,6 +29,90 @@ export function toArray(value) {
|
|
|
28
29
|
return Array.isArray(value) ? value : [value];
|
|
29
30
|
}
|
|
30
31
|
const MAX_RIPGREP_OUTPUT_BYTES = 2_000_000; // ~2MB safeguard
|
|
32
|
+
let ripgrepExecutablePromise = null;
|
|
33
|
+
let ripgrepInstallController = null;
|
|
34
|
+
let ripgrepExecutableWaiters = 0;
|
|
35
|
+
function ripgrepAbortError() {
|
|
36
|
+
return new Error("ripgrep search aborted before start");
|
|
37
|
+
}
|
|
38
|
+
function resetRipgrepExecutablePromise() {
|
|
39
|
+
ripgrepExecutablePromise = null;
|
|
40
|
+
ripgrepInstallController = null;
|
|
41
|
+
}
|
|
42
|
+
function getRipgrepExecutablePromise() {
|
|
43
|
+
if (!ripgrepExecutablePromise) {
|
|
44
|
+
const controller = new AbortController();
|
|
45
|
+
ripgrepInstallController = controller;
|
|
46
|
+
const promise = ensureTool("rg", true, controller.signal).then((executable) => {
|
|
47
|
+
if (ripgrepInstallController === controller) {
|
|
48
|
+
ripgrepInstallController = null;
|
|
49
|
+
}
|
|
50
|
+
if (ripgrepExecutablePromise === promise && !executable) {
|
|
51
|
+
ripgrepExecutablePromise = null;
|
|
52
|
+
}
|
|
53
|
+
return executable;
|
|
54
|
+
}, (error) => {
|
|
55
|
+
if (ripgrepInstallController === controller) {
|
|
56
|
+
ripgrepInstallController = null;
|
|
57
|
+
}
|
|
58
|
+
if (ripgrepExecutablePromise === promise) {
|
|
59
|
+
ripgrepExecutablePromise = null;
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
});
|
|
63
|
+
ripgrepExecutablePromise = promise;
|
|
64
|
+
}
|
|
65
|
+
return ripgrepExecutablePromise;
|
|
66
|
+
}
|
|
67
|
+
function releaseRipgrepExecutableWaiter(signal) {
|
|
68
|
+
ripgrepExecutableWaiters = Math.max(0, ripgrepExecutableWaiters - 1);
|
|
69
|
+
if (signal?.aborted && ripgrepExecutableWaiters === 0) {
|
|
70
|
+
const controller = ripgrepInstallController;
|
|
71
|
+
resetRipgrepExecutablePromise();
|
|
72
|
+
controller?.abort(signal.reason);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async function waitForRipgrepExecutableWithAbort(promise, signal) {
|
|
76
|
+
throwIfRipgrepAborted(signal);
|
|
77
|
+
return await new Promise((resolve, reject) => {
|
|
78
|
+
const onAbort = () => {
|
|
79
|
+
signal.removeEventListener("abort", onAbort);
|
|
80
|
+
reject(ripgrepAbortError());
|
|
81
|
+
};
|
|
82
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
83
|
+
promise.then(resolve, reject).finally(() => {
|
|
84
|
+
signal.removeEventListener("abort", onAbort);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
async function resolveRipgrepExecutable(signal) {
|
|
89
|
+
ripgrepExecutableWaiters += 1;
|
|
90
|
+
try {
|
|
91
|
+
const promise = getRipgrepExecutablePromise();
|
|
92
|
+
const executable = signal
|
|
93
|
+
? await waitForRipgrepExecutableWithAbort(promise, signal)
|
|
94
|
+
: await promise;
|
|
95
|
+
if (!executable) {
|
|
96
|
+
throw new Error("ripgrep is not available and could not be downloaded");
|
|
97
|
+
}
|
|
98
|
+
return executable;
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
releaseRipgrepExecutableWaiter(signal);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function throwIfRipgrepAborted(signal) {
|
|
105
|
+
if (signal?.aborted) {
|
|
106
|
+
throw ripgrepAbortError();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function resolveRipgrepExecutableWithAbort(signal) {
|
|
110
|
+
throwIfRipgrepAborted(signal);
|
|
111
|
+
if (!signal) {
|
|
112
|
+
return await resolveRipgrepExecutable();
|
|
113
|
+
}
|
|
114
|
+
return await resolveRipgrepExecutable(signal);
|
|
115
|
+
}
|
|
31
116
|
function shellQuoteArg(value) {
|
|
32
117
|
if (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(value)) {
|
|
33
118
|
return value;
|
|
@@ -38,7 +123,9 @@ export function formatRipgrepCommand(args) {
|
|
|
38
123
|
return ["rg", ...args].map(shellQuoteArg).join(" ");
|
|
39
124
|
}
|
|
40
125
|
export async function runRipgrep(args, signal, cwd) {
|
|
41
|
-
const
|
|
126
|
+
const executable = await resolveRipgrepExecutableWithAbort(signal);
|
|
127
|
+
throwIfRipgrepAborted(signal);
|
|
128
|
+
const child = spawn(executable, args, {
|
|
42
129
|
cwd: cwd ?? process.cwd(),
|
|
43
130
|
stdio: ["ignore", "pipe", "pipe"],
|
|
44
131
|
signal,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ripgrep-utils.js","sourceRoot":"","sources":["../../src/tools/ripgrep-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { Type } from \"@sinclair/typebox\";\nimport { safeJsonParse } from \"../utils/json.js\";\n\nexport const pathSchema = Type.Optional(\n\tType.Union([\n\t\tType.String({\n\t\t\tdescription: \"Directory or file to search\",\n\t\t\tminLength: 1,\n\t\t}),\n\t\tType.Array(\n\t\t\tType.String({\n\t\t\t\tdescription: \"Multiple directories or files to search\",\n\t\t\t\tminLength: 1,\n\t\t\t}),\n\t\t\t{ minItems: 1 },\n\t\t),\n\t]),\n);\n\nexport const globSchema = Type.Optional(\n\tType.Union([\n\t\tType.String({\n\t\t\tdescription: \"Glob pattern passed to ripgrep\",\n\t\t\tminLength: 1,\n\t\t}),\n\t\tType.Array(\n\t\t\tType.String({\n\t\t\t\tdescription: \"Multiple glob patterns\",\n\t\t\t\tminLength: 1,\n\t\t\t}),\n\t\t\t{ minItems: 1 },\n\t\t),\n\t]),\n);\n\nexport function toArray<T>(value: T | T[] | undefined): T[] {\n\tif (value === undefined) {\n\t\treturn [];\n\t}\n\treturn Array.isArray(value) ? value : [value];\n}\n\nconst MAX_RIPGREP_OUTPUT_BYTES = 2_000_000; // ~2MB safeguard\n\nfunction shellQuoteArg(value: string): string {\n\tif (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(value)) {\n\t\treturn value;\n\t}\n\treturn `'${value.replace(/'/g, \"'\\\\''\")}'`;\n}\n\nexport function formatRipgrepCommand(args: string[]): string {\n\treturn [\"rg\", ...args].map(shellQuoteArg).join(\" \");\n}\n\nexport async function runRipgrep(\n\targs: string[],\n\tsignal?: AbortSignal,\n\tcwd?: string,\n): Promise<{\n\tstdout: string;\n\tstderr: string;\n\texitCode: number;\n\ttruncated: boolean;\n}> {\n\tconst child = spawn(\"rg\", args, {\n\t\tcwd: cwd ?? process.cwd(),\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\tsignal,\n\t});\n\n\treturn await new Promise((resolve, reject) => {\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tlet truncated = false;\n\n\t\tchild.stdout.setEncoding(\"utf-8\");\n\t\tchild.stdout.on(\"data\", (chunk) => {\n\t\t\tif (stdout.length + chunk.length > MAX_RIPGREP_OUTPUT_BYTES) {\n\t\t\t\ttruncated = true;\n\t\t\t\tstdout += chunk\n\t\t\t\t\t.slice(0, Math.max(0, MAX_RIPGREP_OUTPUT_BYTES - stdout.length))\n\t\t\t\t\t.toString();\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstdout += chunk;\n\t\t});\n\n\t\tchild.stderr.setEncoding(\"utf-8\");\n\t\tchild.stderr.on(\"data\", (chunk) => {\n\t\t\tif (stderr.length + chunk.length > MAX_RIPGREP_OUTPUT_BYTES) {\n\t\t\t\ttruncated = true;\n\t\t\t\tstderr += chunk\n\t\t\t\t\t.slice(0, Math.max(0, MAX_RIPGREP_OUTPUT_BYTES - stderr.length))\n\t\t\t\t\t.toString();\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstderr += chunk;\n\t\t});\n\n\t\tchild.once(\"error\", (error) => {\n\t\t\treject(\n\t\t\t\terror instanceof Error\n\t\t\t\t\t? new Error(`Failed to start ripgrep: ${error.message}`)\n\t\t\t\t\t: new Error(`Failed to start ripgrep: ${String(error)}`),\n\t\t\t);\n\t\t});\n\n\t\tchild.once(\"close\", (code) => {\n\t\t\tresolve({ stdout, stderr, exitCode: code ?? 0, truncated });\n\t\t});\n\t});\n}\n\nexport type RipgrepMatch = {\n\tfile: string;\n\tline: number;\n\tcolumn: number;\n\tmatch: string;\n\tlines: string;\n};\n\nexport function parseRipgrepJson(output: string): RipgrepMatch[] {\n\tconst matches: RipgrepMatch[] = [];\n\tfor (const line of output.split(/\\r?\\n/)) {\n\t\tif (!line.trim()) continue;\n\t\tconst parsed = safeJsonParse<unknown>(line, \"ripgrep output\");\n\t\tif (!parsed.success) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst event = parsed.data as {\n\t\t\ttype?: string;\n\t\t\tdata?: {\n\t\t\t\tpath?: { text?: string };\n\t\t\t\tline_number?: number;\n\t\t\t\tlines?: { text?: string };\n\t\t\t\tsubmatches?: Array<{\n\t\t\t\t\tstart?: number;\n\t\t\t\t\tmatch?: { text?: string };\n\t\t\t\t}>;\n\t\t\t};\n\t\t};\n\t\tif (event.type !== \"match\") {\n\t\t\tcontinue;\n\t\t}\n\t\tconst pathText = event.data?.path?.text ?? \"\";\n\t\tfor (const submatch of event.data?.submatches ?? []) {\n\t\t\tmatches.push({\n\t\t\t\tfile: pathText,\n\t\t\t\tline: event.data?.line_number ?? 0,\n\t\t\t\tcolumn: submatch.start ?? 0,\n\t\t\t\tmatch: submatch.match?.text ?? \"\",\n\t\t\t\tlines: event.data?.lines?.text ?? \"\",\n\t\t\t});\n\t\t}\n\t}\n\treturn matches;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ripgrep-utils.js","sourceRoot":"","sources":["../../src/tools/ripgrep-utils.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { Type } from \"@sinclair/typebox\";\nimport { safeJsonParse } from \"../utils/json.js\";\nimport { ensureTool } from \"./tools-manager.js\";\n\nexport const pathSchema = Type.Optional(\n\tType.Union([\n\t\tType.String({\n\t\t\tdescription: \"Directory or file to search\",\n\t\t\tminLength: 1,\n\t\t}),\n\t\tType.Array(\n\t\t\tType.String({\n\t\t\t\tdescription: \"Multiple directories or files to search\",\n\t\t\t\tminLength: 1,\n\t\t\t}),\n\t\t\t{ minItems: 1 },\n\t\t),\n\t]),\n);\n\nexport const globSchema = Type.Optional(\n\tType.Union([\n\t\tType.String({\n\t\t\tdescription: \"Glob pattern passed to ripgrep\",\n\t\t\tminLength: 1,\n\t\t}),\n\t\tType.Array(\n\t\t\tType.String({\n\t\t\t\tdescription: \"Multiple glob patterns\",\n\t\t\t\tminLength: 1,\n\t\t\t}),\n\t\t\t{ minItems: 1 },\n\t\t),\n\t]),\n);\n\nexport function toArray<T>(value: T | T[] | undefined): T[] {\n\tif (value === undefined) {\n\t\treturn [];\n\t}\n\treturn Array.isArray(value) ? value : [value];\n}\n\nconst MAX_RIPGREP_OUTPUT_BYTES = 2_000_000; // ~2MB safeguard\nlet ripgrepExecutablePromise: Promise<string | null> | null = null;\nlet ripgrepInstallController: AbortController | null = null;\nlet ripgrepExecutableWaiters = 0;\n\nfunction ripgrepAbortError(): Error {\n\treturn new Error(\"ripgrep search aborted before start\");\n}\n\nfunction resetRipgrepExecutablePromise(): void {\n\tripgrepExecutablePromise = null;\n\tripgrepInstallController = null;\n}\n\nfunction getRipgrepExecutablePromise(): Promise<string | null> {\n\tif (!ripgrepExecutablePromise) {\n\t\tconst controller = new AbortController();\n\t\tripgrepInstallController = controller;\n\t\tconst promise = ensureTool(\"rg\", true, controller.signal).then(\n\t\t\t(executable) => {\n\t\t\t\tif (ripgrepInstallController === controller) {\n\t\t\t\t\tripgrepInstallController = null;\n\t\t\t\t}\n\t\t\t\tif (ripgrepExecutablePromise === promise && !executable) {\n\t\t\t\t\tripgrepExecutablePromise = null;\n\t\t\t\t}\n\t\t\t\treturn executable;\n\t\t\t},\n\t\t\t(error) => {\n\t\t\t\tif (ripgrepInstallController === controller) {\n\t\t\t\t\tripgrepInstallController = null;\n\t\t\t\t}\n\t\t\t\tif (ripgrepExecutablePromise === promise) {\n\t\t\t\t\tripgrepExecutablePromise = null;\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t},\n\t\t);\n\t\tripgrepExecutablePromise = promise;\n\t}\n\treturn ripgrepExecutablePromise;\n}\n\nfunction releaseRipgrepExecutableWaiter(signal?: AbortSignal): void {\n\tripgrepExecutableWaiters = Math.max(0, ripgrepExecutableWaiters - 1);\n\tif (signal?.aborted && ripgrepExecutableWaiters === 0) {\n\t\tconst controller = ripgrepInstallController;\n\t\tresetRipgrepExecutablePromise();\n\t\tcontroller?.abort(signal.reason);\n\t}\n}\n\nasync function waitForRipgrepExecutableWithAbort(\n\tpromise: Promise<string | null>,\n\tsignal: AbortSignal,\n): Promise<string | null> {\n\tthrowIfRipgrepAborted(signal);\n\treturn await new Promise<string | null>((resolve, reject) => {\n\t\tconst onAbort = (): void => {\n\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t\treject(ripgrepAbortError());\n\t\t};\n\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\tpromise.then(resolve, reject).finally(() => {\n\t\t\tsignal.removeEventListener(\"abort\", onAbort);\n\t\t});\n\t});\n}\n\nasync function resolveRipgrepExecutable(signal?: AbortSignal): Promise<string> {\n\tripgrepExecutableWaiters += 1;\n\ttry {\n\t\tconst promise = getRipgrepExecutablePromise();\n\t\tconst executable = signal\n\t\t\t? await waitForRipgrepExecutableWithAbort(promise, signal)\n\t\t\t: await promise;\n\t\tif (!executable) {\n\t\t\tthrow new Error(\"ripgrep is not available and could not be downloaded\");\n\t\t}\n\t\treturn executable;\n\t} finally {\n\t\treleaseRipgrepExecutableWaiter(signal);\n\t}\n}\n\nfunction throwIfRipgrepAborted(signal?: AbortSignal): void {\n\tif (signal?.aborted) {\n\t\tthrow ripgrepAbortError();\n\t}\n}\n\nasync function resolveRipgrepExecutableWithAbort(\n\tsignal?: AbortSignal,\n): Promise<string> {\n\tthrowIfRipgrepAborted(signal);\n\tif (!signal) {\n\t\treturn await resolveRipgrepExecutable();\n\t}\n\n\treturn await resolveRipgrepExecutable(signal);\n}\n\nfunction shellQuoteArg(value: string): string {\n\tif (/^[A-Za-z0-9_/:=.,@%+-]+$/.test(value)) {\n\t\treturn value;\n\t}\n\treturn `'${value.replace(/'/g, \"'\\\\''\")}'`;\n}\n\nexport function formatRipgrepCommand(args: string[]): string {\n\treturn [\"rg\", ...args].map(shellQuoteArg).join(\" \");\n}\n\nexport async function runRipgrep(\n\targs: string[],\n\tsignal?: AbortSignal,\n\tcwd?: string,\n): Promise<{\n\tstdout: string;\n\tstderr: string;\n\texitCode: number;\n\ttruncated: boolean;\n}> {\n\tconst executable = await resolveRipgrepExecutableWithAbort(signal);\n\tthrowIfRipgrepAborted(signal);\n\tconst child = spawn(executable, args, {\n\t\tcwd: cwd ?? process.cwd(),\n\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\tsignal,\n\t});\n\n\treturn await new Promise((resolve, reject) => {\n\t\tlet stdout = \"\";\n\t\tlet stderr = \"\";\n\t\tlet truncated = false;\n\n\t\tchild.stdout.setEncoding(\"utf-8\");\n\t\tchild.stdout.on(\"data\", (chunk) => {\n\t\t\tif (stdout.length + chunk.length > MAX_RIPGREP_OUTPUT_BYTES) {\n\t\t\t\ttruncated = true;\n\t\t\t\tstdout += chunk\n\t\t\t\t\t.slice(0, Math.max(0, MAX_RIPGREP_OUTPUT_BYTES - stdout.length))\n\t\t\t\t\t.toString();\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstdout += chunk;\n\t\t});\n\n\t\tchild.stderr.setEncoding(\"utf-8\");\n\t\tchild.stderr.on(\"data\", (chunk) => {\n\t\t\tif (stderr.length + chunk.length > MAX_RIPGREP_OUTPUT_BYTES) {\n\t\t\t\ttruncated = true;\n\t\t\t\tstderr += chunk\n\t\t\t\t\t.slice(0, Math.max(0, MAX_RIPGREP_OUTPUT_BYTES - stderr.length))\n\t\t\t\t\t.toString();\n\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tstderr += chunk;\n\t\t});\n\n\t\tchild.once(\"error\", (error) => {\n\t\t\treject(\n\t\t\t\terror instanceof Error\n\t\t\t\t\t? new Error(`Failed to start ripgrep: ${error.message}`)\n\t\t\t\t\t: new Error(`Failed to start ripgrep: ${String(error)}`),\n\t\t\t);\n\t\t});\n\n\t\tchild.once(\"close\", (code) => {\n\t\t\tresolve({ stdout, stderr, exitCode: code ?? 0, truncated });\n\t\t});\n\t});\n}\n\nexport type RipgrepMatch = {\n\tfile: string;\n\tline: number;\n\tcolumn: number;\n\tmatch: string;\n\tlines: string;\n};\n\nexport function parseRipgrepJson(output: string): RipgrepMatch[] {\n\tconst matches: RipgrepMatch[] = [];\n\tfor (const line of output.split(/\\r?\\n/)) {\n\t\tif (!line.trim()) continue;\n\t\tconst parsed = safeJsonParse<unknown>(line, \"ripgrep output\");\n\t\tif (!parsed.success) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst event = parsed.data as {\n\t\t\ttype?: string;\n\t\t\tdata?: {\n\t\t\t\tpath?: { text?: string };\n\t\t\t\tline_number?: number;\n\t\t\t\tlines?: { text?: string };\n\t\t\t\tsubmatches?: Array<{\n\t\t\t\t\tstart?: number;\n\t\t\t\t\tmatch?: { text?: string };\n\t\t\t\t}>;\n\t\t\t};\n\t\t};\n\t\tif (event.type !== \"match\") {\n\t\t\tcontinue;\n\t\t}\n\t\tconst pathText = event.data?.path?.text ?? \"\";\n\t\tfor (const submatch of event.data?.submatches ?? []) {\n\t\t\tmatches.push({\n\t\t\t\tfile: pathText,\n\t\t\t\tline: event.data?.line_number ?? 0,\n\t\t\t\tcolumn: submatch.start ?? 0,\n\t\t\t\tmatch: submatch.match?.text ?? \"\",\n\t\t\t\tlines: event.data?.lines?.text ?? \"\",\n\t\t\t});\n\t\t}\n\t}\n\treturn matches;\n}\n"]}
|
|
@@ -39,5 +39,5 @@ export declare function getToolPath(tool: "fd" | "rg"): string | null;
|
|
|
39
39
|
* @param silent - If true, suppress console output
|
|
40
40
|
* @returns Path to the tool binary, or null if installation failed
|
|
41
41
|
*/
|
|
42
|
-
export declare function ensureTool(tool: "fd" | "rg", silent?: boolean): Promise<string | null>;
|
|
42
|
+
export declare function ensureTool(tool: "fd" | "rg", silent?: boolean, signal?: AbortSignal): Promise<string | null>;
|
|
43
43
|
//# sourceMappingURL=tools-manager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools-manager.d.ts","sourceRoot":"","sources":["../../src/tools/tools-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;
|
|
1
|
+
{"version":3,"file":"tools-manager.d.ts","sourceRoot":"","sources":["../../src/tools/tools-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAgIH;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAmB5D;AAkSD;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAC/B,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,MAAM,UAAQ,EACd,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAoCxB"}
|
|
@@ -28,6 +28,14 @@ import { PATHS } from "../config/constants.js";
|
|
|
28
28
|
const TOOLS_DIR = PATHS.TOOLS_DIR;
|
|
29
29
|
const FETCH_TIMEOUT_MS = 30_000;
|
|
30
30
|
const DOWNLOAD_IDLE_TIMEOUT_MS = 60_000;
|
|
31
|
+
function toolInstallAbortError(config) {
|
|
32
|
+
return new Error(`${config.name} installation aborted`);
|
|
33
|
+
}
|
|
34
|
+
function throwIfToolInstallAborted(config, signal) {
|
|
35
|
+
if (signal?.aborted) {
|
|
36
|
+
throw toolInstallAbortError(config);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
31
39
|
/**
|
|
32
40
|
* Registry of supported external tools and their configurations.
|
|
33
41
|
* Each tool defines platform-specific asset name patterns for GitHub releases.
|
|
@@ -126,25 +134,40 @@ export function getToolPath(tool) {
|
|
|
126
134
|
* @param repo - Repository in owner/repo format
|
|
127
135
|
* @returns Version string (without "v" prefix)
|
|
128
136
|
*/
|
|
129
|
-
async function fetchWithTimeout(url, init) {
|
|
137
|
+
async function fetchWithTimeout(url, init, signal) {
|
|
130
138
|
const controller = new AbortController();
|
|
131
139
|
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
140
|
+
const onAbort = () => {
|
|
141
|
+
controller.abort(signal?.reason);
|
|
142
|
+
};
|
|
143
|
+
if (signal?.aborted) {
|
|
144
|
+
onAbort();
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
148
|
+
}
|
|
149
|
+
const clearFetchTimeout = () => clearTimeout(timeout);
|
|
150
|
+
const cleanup = () => {
|
|
151
|
+
clearFetchTimeout();
|
|
152
|
+
signal?.removeEventListener("abort", onAbort);
|
|
153
|
+
};
|
|
132
154
|
try {
|
|
133
155
|
const response = await fetch(url, { ...init, signal: controller.signal });
|
|
134
156
|
return {
|
|
135
157
|
response,
|
|
136
|
-
clearTimeout:
|
|
158
|
+
clearTimeout: clearFetchTimeout,
|
|
159
|
+
cleanup,
|
|
137
160
|
};
|
|
138
161
|
}
|
|
139
162
|
catch (error) {
|
|
140
|
-
|
|
163
|
+
cleanup();
|
|
141
164
|
throw error;
|
|
142
165
|
}
|
|
143
166
|
}
|
|
144
|
-
async function getLatestVersion(repo) {
|
|
145
|
-
const { response,
|
|
167
|
+
async function getLatestVersion(repo, signal) {
|
|
168
|
+
const { response, cleanup: cleanupFetch } = await fetchWithTimeout(`https://api.github.com/repos/${repo}/releases/latest`, {
|
|
146
169
|
headers: { "User-Agent": "composer-coding-agent" },
|
|
147
|
-
});
|
|
170
|
+
}, signal);
|
|
148
171
|
try {
|
|
149
172
|
if (!response.ok) {
|
|
150
173
|
throw new Error(`GitHub API error: ${response.status}`);
|
|
@@ -154,15 +177,15 @@ async function getLatestVersion(repo) {
|
|
|
154
177
|
return data.tag_name.replace(/^v/, "");
|
|
155
178
|
}
|
|
156
179
|
finally {
|
|
157
|
-
|
|
180
|
+
cleanupFetch();
|
|
158
181
|
}
|
|
159
182
|
}
|
|
160
183
|
/**
|
|
161
184
|
* Download a file from a URL to a local path.
|
|
162
185
|
* Uses streaming to handle large files efficiently.
|
|
163
186
|
*/
|
|
164
|
-
async function downloadFile(url, dest) {
|
|
165
|
-
const { response, clearTimeout: clearFetchTimeout } = await fetchWithTimeout(url);
|
|
187
|
+
async function downloadFile(url, dest, signal) {
|
|
188
|
+
const { response, clearTimeout: clearFetchTimeout, cleanup: cleanupFetch, } = await fetchWithTimeout(url, undefined, signal);
|
|
166
189
|
try {
|
|
167
190
|
if (!response.ok) {
|
|
168
191
|
throw new Error(`Failed to download: ${response.status}`);
|
|
@@ -175,6 +198,11 @@ async function downloadFile(url, dest) {
|
|
|
175
198
|
const stream = Readable.fromWeb(response.body);
|
|
176
199
|
const fileStream = createWriteStream(dest);
|
|
177
200
|
let idleTimeoutId = null;
|
|
201
|
+
const onAbort = () => {
|
|
202
|
+
const error = new Error("Tool download aborted");
|
|
203
|
+
stream.destroy(error);
|
|
204
|
+
fileStream.destroy(error);
|
|
205
|
+
};
|
|
178
206
|
const clearIdleTimeout = () => {
|
|
179
207
|
if (idleTimeoutId) {
|
|
180
208
|
clearTimeout(idleTimeoutId);
|
|
@@ -194,6 +222,12 @@ async function downloadFile(url, dest) {
|
|
|
194
222
|
clearIdleTimeout();
|
|
195
223
|
};
|
|
196
224
|
resetIdleTimeout();
|
|
225
|
+
if (signal?.aborted) {
|
|
226
|
+
onAbort();
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
230
|
+
}
|
|
197
231
|
stream.on("data", resetIdleTimeout);
|
|
198
232
|
stream.on("end", onStreamEnd);
|
|
199
233
|
stream.on("close", onStreamEnd);
|
|
@@ -206,11 +240,12 @@ async function downloadFile(url, dest) {
|
|
|
206
240
|
stream.off("end", onStreamEnd);
|
|
207
241
|
stream.off("close", onStreamEnd);
|
|
208
242
|
stream.off("error", onStreamEnd);
|
|
243
|
+
signal?.removeEventListener("abort", onAbort);
|
|
209
244
|
clearIdleTimeout();
|
|
210
245
|
}
|
|
211
246
|
}
|
|
212
247
|
finally {
|
|
213
|
-
|
|
248
|
+
cleanupFetch();
|
|
214
249
|
}
|
|
215
250
|
}
|
|
216
251
|
function runExtractor(command, args, description) {
|
|
@@ -259,14 +294,16 @@ function findBinary(root, binaryName) {
|
|
|
259
294
|
* @param tool - The tool identifier ("fd" or "rg")
|
|
260
295
|
* @returns Path to the installed binary
|
|
261
296
|
*/
|
|
262
|
-
async function downloadTool(tool) {
|
|
297
|
+
async function downloadTool(tool, signal) {
|
|
263
298
|
const config = TOOLS[tool];
|
|
264
299
|
if (!config)
|
|
265
300
|
throw new Error(`Unknown tool: ${tool}`);
|
|
301
|
+
throwIfToolInstallAborted(config, signal);
|
|
266
302
|
const plat = platform();
|
|
267
303
|
const architecture = arch();
|
|
268
304
|
// Get the latest version from GitHub
|
|
269
|
-
const version = await getLatestVersion(config.repo);
|
|
305
|
+
const version = await getLatestVersion(config.repo, signal);
|
|
306
|
+
throwIfToolInstallAborted(config, signal);
|
|
270
307
|
// Determine the correct asset for this platform
|
|
271
308
|
const assetName = config.getAssetName(version, plat, architecture);
|
|
272
309
|
if (!assetName) {
|
|
@@ -280,7 +317,8 @@ async function downloadTool(tool) {
|
|
|
280
317
|
const binaryExt = plat === "win32" ? ".exe" : "";
|
|
281
318
|
const binaryPath = join(TOOLS_DIR, config.binaryName + binaryExt);
|
|
282
319
|
// Download the release archive
|
|
283
|
-
await downloadFile(downloadUrl, archivePath);
|
|
320
|
+
await downloadFile(downloadUrl, archivePath, signal);
|
|
321
|
+
throwIfToolInstallAborted(config, signal);
|
|
284
322
|
// Create temp directory for extraction
|
|
285
323
|
const extractDir = mkdtempSync(join(TOOLS_DIR, "extract-"));
|
|
286
324
|
try {
|
|
@@ -325,27 +363,31 @@ async function downloadTool(tool) {
|
|
|
325
363
|
* @param silent - If true, suppress console output
|
|
326
364
|
* @returns Path to the tool binary, or null if installation failed
|
|
327
365
|
*/
|
|
328
|
-
export async function ensureTool(tool, silent = false) {
|
|
366
|
+
export async function ensureTool(tool, silent = false, signal) {
|
|
367
|
+
const config = TOOLS[tool];
|
|
368
|
+
if (!config)
|
|
369
|
+
return null;
|
|
370
|
+
throwIfToolInstallAborted(config, signal);
|
|
329
371
|
// Check if already installed
|
|
330
372
|
const existingPath = getToolPath(tool);
|
|
331
373
|
if (existingPath) {
|
|
332
374
|
return existingPath;
|
|
333
375
|
}
|
|
334
|
-
const config = TOOLS[tool];
|
|
335
|
-
if (!config)
|
|
336
|
-
return null;
|
|
337
376
|
// Notify user about download (unless silent)
|
|
338
377
|
if (!silent) {
|
|
339
378
|
console.log(chalk.dim(`${config.name} not found. Downloading...`));
|
|
340
379
|
}
|
|
341
380
|
try {
|
|
342
|
-
const path = await downloadTool(tool);
|
|
381
|
+
const path = await downloadTool(tool, signal);
|
|
343
382
|
if (!silent) {
|
|
344
383
|
console.log(chalk.dim(`${config.name} installed to ${path}`));
|
|
345
384
|
}
|
|
346
385
|
return path;
|
|
347
386
|
}
|
|
348
387
|
catch (e) {
|
|
388
|
+
if (signal?.aborted) {
|
|
389
|
+
throw toolInstallAbortError(config);
|
|
390
|
+
}
|
|
349
391
|
// Download failed - log and return null (tool is optional)
|
|
350
392
|
if (!silent) {
|
|
351
393
|
console.log(chalk.yellow(`Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools-manager.js","sourceRoot":"","sources":["../../src/tools/tools-manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * External Tools Manager\n *\n * This module manages optional external command-line tools that enhance\n * the agent's capabilities. Currently supported tools:\n *\n * - fd: Fast file finder (sharkdp/fd)\n * - rg: Ripgrep for fast content search (BurntSushi/ripgrep)\n *\n * The manager handles:\n * 1. Detection of existing installations (system PATH or local)\n * 2. Automatic download from GitHub releases\n * 3. Platform-specific binary selection (macOS, Linux, Windows)\n * 4. Architecture support (x86_64, arm64)\n *\n * Tools are installed to ~/.maestro/tools/ to avoid requiring\n * system-wide permissions or polluting the system PATH.\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport {\n\tchmodSync,\n\tcreateWriteStream,\n\texistsSync,\n\tmkdirSync,\n\tmkdtempSync,\n\treaddirSync,\n\trenameSync,\n\trmSync,\n} from \"node:fs\";\nimport { arch, platform } from \"node:os\";\nimport { join } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { finished } from \"node:stream/promises\";\nimport chalk from \"chalk\";\nimport { PATHS } from \"../config/constants.js\";\n\n/** Directory where downloaded tools are installed */\nconst TOOLS_DIR = PATHS.TOOLS_DIR;\nconst FETCH_TIMEOUT_MS = 30_000;\nconst DOWNLOAD_IDLE_TIMEOUT_MS = 60_000;\n\n/**\n * Configuration for an external tool.\n * Defines how to find, download, and install the tool.\n */\ninterface ToolConfig {\n\t/** Human-readable name for display */\n\tname: string;\n\t/** GitHub repository in owner/repo format */\n\trepo: string;\n\t/** Name of the binary executable */\n\tbinaryName: string;\n\t/** Prefix for version tags (e.g., \"v\" for \"v1.0.0\") */\n\ttagPrefix: string;\n\t/** Function to determine the release asset name for a platform */\n\tgetAssetName: (\n\t\tversion: string,\n\t\tplat: string,\n\t\tarchitecture: string,\n\t) => string | null;\n}\n\n/**\n * Registry of supported external tools and their configurations.\n * Each tool defines platform-specific asset name patterns for GitHub releases.\n */\nconst TOOLS: Record<string, ToolConfig> = {\n\t// fd: A simple, fast and user-friendly alternative to 'find'\n\tfd: {\n\t\tname: \"fd\",\n\t\trepo: \"sharkdp/fd\",\n\t\tbinaryName: \"fd\",\n\t\ttagPrefix: \"v\",\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\t// Map Node.js arch names to Rust target names\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"linux\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-unknown-linux-gnu.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null; // Unsupported platform\n\t\t},\n\t},\n\n\t// ripgrep: A line-oriented search tool (faster than grep)\n\trg: {\n\t\tname: \"ripgrep\",\n\t\trepo: \"BurntSushi/ripgrep\",\n\t\tbinaryName: \"rg\",\n\t\ttagPrefix: \"\", // ripgrep uses bare version numbers (e.g., \"14.0.0\")\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"linux\") {\n\t\t\t\t// Linux x86_64 uses musl for better compatibility\n\t\t\t\tif (architecture === \"arm64\") {\n\t\t\t\t\treturn `ripgrep-${version}-aarch64-unknown-linux-gnu.tar.gz`;\n\t\t\t\t}\n\t\t\t\treturn `ripgrep-${version}-x86_64-unknown-linux-musl.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null; // Unsupported platform\n\t\t},\n\t},\n};\n\n/**\n * Check if a command exists on the system PATH.\n * Uses --version flag as a safe probe that most tools support.\n */\nfunction commandExists(cmd: string): boolean {\n\ttry {\n\t\tconst result = spawnSync(cmd, [\"--version\"], { stdio: \"pipe\" });\n\t\treturn result.error === undefined || result.error === null;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Get the path to a tool binary, checking local install first then system PATH.\n *\n * Priority order:\n * 1. Local installation in ~/.maestro/tools/\n * 2. System PATH\n *\n * @param tool - The tool identifier (\"fd\" or \"rg\")\n * @returns Path to the binary, or null if not found\n */\nexport function getToolPath(tool: \"fd\" | \"rg\"): string | null {\n\tconst config = TOOLS[tool];\n\tif (!config) return null;\n\n\t// Check our tools directory first (preferred - known version)\n\tconst localPath = join(\n\t\tTOOLS_DIR,\n\t\tconfig.binaryName + (platform() === \"win32\" ? \".exe\" : \"\"),\n\t);\n\tif (existsSync(localPath)) {\n\t\treturn localPath;\n\t}\n\n\t// Fall back to system PATH (may be different version)\n\tif (commandExists(config.binaryName)) {\n\t\treturn config.binaryName;\n\t}\n\n\treturn null;\n}\n\n/**\n * Fetch the latest version number from GitHub releases API.\n *\n * @param repo - Repository in owner/repo format\n * @returns Version string (without \"v\" prefix)\n */\nasync function fetchWithTimeout(\n\turl: string,\n\tinit?: RequestInit,\n): Promise<{ response: Response; clearTimeout: () => void }> {\n\tconst controller = new AbortController();\n\tconst timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\ttry {\n\t\tconst response = await fetch(url, { ...init, signal: controller.signal });\n\t\treturn {\n\t\t\tresponse,\n\t\t\tclearTimeout: () => clearTimeout(timeout),\n\t\t};\n\t} catch (error) {\n\t\tclearTimeout(timeout);\n\t\tthrow error;\n\t}\n}\n\nasync function getLatestVersion(repo: string): Promise<string> {\n\tconst { response, clearTimeout: clearFetchTimeout } = await fetchWithTimeout(\n\t\t`https://api.github.com/repos/${repo}/releases/latest`,\n\t\t{\n\t\t\theaders: { \"User-Agent\": \"composer-coding-agent\" },\n\t\t},\n\t);\n\ttry {\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`GitHub API error: ${response.status}`);\n\t\t}\n\n\t\tconst data = (await response.json()) as { tag_name: string };\n\t\t// Strip \"v\" prefix if present for consistent version handling\n\t\treturn data.tag_name.replace(/^v/, \"\");\n\t} finally {\n\t\tclearFetchTimeout();\n\t}\n}\n\n/**\n * Download a file from a URL to a local path.\n * Uses streaming to handle large files efficiently.\n */\nasync function downloadFile(url: string, dest: string): Promise<void> {\n\tconst { response, clearTimeout: clearFetchTimeout } =\n\t\tawait fetchWithTimeout(url);\n\ttry {\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Failed to download: ${response.status}`);\n\t\t}\n\n\t\tif (!response.body) {\n\t\t\tthrow new Error(\"No response body\");\n\t\t}\n\n\t\tclearFetchTimeout();\n\n\t\t// Stream the response directly to disk\n\t\tconst stream = Readable.fromWeb(\n\t\t\tresponse.body as Parameters<typeof Readable.fromWeb>[0],\n\t\t);\n\t\tconst fileStream = createWriteStream(dest);\n\t\tlet idleTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\n\t\tconst clearIdleTimeout = (): void => {\n\t\t\tif (idleTimeoutId) {\n\t\t\t\tclearTimeout(idleTimeoutId);\n\t\t\t\tidleTimeoutId = null;\n\t\t\t}\n\t\t};\n\n\t\tconst resetIdleTimeout = (): void => {\n\t\t\tclearIdleTimeout();\n\t\t\tidleTimeoutId = setTimeout(() => {\n\t\t\t\tconst error = new Error(\n\t\t\t\t\t`Tool download idle timeout after ${Math.round(DOWNLOAD_IDLE_TIMEOUT_MS / 1000)}s`,\n\t\t\t\t);\n\t\t\t\tstream.destroy();\n\t\t\t\tfileStream.destroy();\n\t\t\t\tfileStream.emit(\"error\", error);\n\t\t\t}, DOWNLOAD_IDLE_TIMEOUT_MS);\n\t\t};\n\n\t\tconst onStreamEnd = (): void => {\n\t\t\tclearIdleTimeout();\n\t\t};\n\n\t\tresetIdleTimeout();\n\t\tstream.on(\"data\", resetIdleTimeout);\n\t\tstream.on(\"end\", onStreamEnd);\n\t\tstream.on(\"close\", onStreamEnd);\n\t\tstream.on(\"error\", onStreamEnd);\n\n\t\ttry {\n\t\t\tawait finished(stream.pipe(fileStream));\n\t\t} finally {\n\t\t\tstream.off(\"data\", resetIdleTimeout);\n\t\t\tstream.off(\"end\", onStreamEnd);\n\t\t\tstream.off(\"close\", onStreamEnd);\n\t\t\tstream.off(\"error\", onStreamEnd);\n\t\t\tclearIdleTimeout();\n\t\t}\n\t} finally {\n\t\tclearFetchTimeout();\n\t}\n}\n\nfunction runExtractor(\n\tcommand: string,\n\targs: string[],\n\tdescription: string,\n): void {\n\tconst result = spawnSync(command, args, { stdio: \"pipe\" });\n\tif (result.error) {\n\t\tthrow new Error(`${description} failed: ${result.error.message}`);\n\t}\n\tif (result.status !== 0) {\n\t\tconst stderr = result.stderr?.toString().trim();\n\t\tthrow new Error(\n\t\t\t`${description} exited with code ${result.status}${\n\t\t\t\tstderr ? `: ${stderr}` : \"\"\n\t\t\t}`,\n\t\t);\n\t}\n}\n\nfunction findBinary(root: string, binaryName: string): string | null {\n\tconst stack = [root];\n\twhile (stack.length > 0) {\n\t\tconst current = stack.pop();\n\t\tif (!current) continue;\n\t\tconst entries = readdirSync(current, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst entryPath = join(current, entry.name);\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tstack.push(entryPath);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (entry.isFile() && entry.name === binaryName) {\n\t\t\t\treturn entryPath;\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Download and install a tool from GitHub releases.\n *\n * Process:\n * 1. Fetch latest version from GitHub API\n * 2. Determine platform-specific asset name\n * 3. Download the release archive\n * 4. Extract the binary\n * 5. Move to tools directory and set permissions\n *\n * @param tool - The tool identifier (\"fd\" or \"rg\")\n * @returns Path to the installed binary\n */\nasync function downloadTool(tool: \"fd\" | \"rg\"): Promise<string> {\n\tconst config = TOOLS[tool];\n\tif (!config) throw new Error(`Unknown tool: ${tool}`);\n\n\tconst plat = platform();\n\tconst architecture = arch();\n\n\t// Get the latest version from GitHub\n\tconst version = await getLatestVersion(config.repo);\n\n\t// Determine the correct asset for this platform\n\tconst assetName = config.getAssetName(version, plat, architecture);\n\tif (!assetName) {\n\t\tthrow new Error(`Unsupported platform: ${plat}/${architecture}`);\n\t}\n\n\t// Ensure tools directory exists\n\tmkdirSync(TOOLS_DIR, { recursive: true });\n\n\t// Build paths\n\tconst downloadUrl = `https://github.com/${config.repo}/releases/download/${config.tagPrefix}${version}/${assetName}`;\n\tconst archivePath = join(TOOLS_DIR, assetName);\n\tconst binaryExt = plat === \"win32\" ? \".exe\" : \"\";\n\tconst binaryPath = join(TOOLS_DIR, config.binaryName + binaryExt);\n\n\t// Download the release archive\n\tawait downloadFile(downloadUrl, archivePath);\n\n\t// Create temp directory for extraction\n\tconst extractDir = mkdtempSync(join(TOOLS_DIR, \"extract-\"));\n\n\ttry {\n\t\t// Extract based on archive type\n\t\tif (assetName.endsWith(\".tar.gz\")) {\n\t\t\trunExtractor(\n\t\t\t\t\"tar\",\n\t\t\t\t[\"-xzf\", archivePath, \"-C\", extractDir],\n\t\t\t\t\"tar extract\",\n\t\t\t);\n\t\t} else if (assetName.endsWith(\".zip\")) {\n\t\t\trunExtractor(\n\t\t\t\t\"unzip\",\n\t\t\t\t[\"-o\", archivePath, \"-d\", extractDir],\n\t\t\t\t\"unzip extract\",\n\t\t\t);\n\t\t} else {\n\t\t\tthrow new Error(`Unsupported archive format: ${assetName}`);\n\t\t}\n\n\t\tconst extractedBinary =\n\t\t\tfindBinary(extractDir, config.binaryName + binaryExt) ??\n\t\t\t(() => {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Binary not found in archive: ${config.binaryName}${binaryExt}`,\n\t\t\t\t);\n\t\t\t})();\n\n\t\t// Move binary to final location\n\t\trmSync(binaryPath, { force: true });\n\t\trenameSync(extractedBinary, binaryPath);\n\n\t\t// Make executable on Unix systems\n\t\tif (plat !== \"win32\") {\n\t\t\tchmodSync(binaryPath, 0o755);\n\t\t}\n\t} finally {\n\t\t// Clean up downloaded archive and temp directory\n\t\trmSync(archivePath, { force: true });\n\t\trmSync(extractDir, { recursive: true, force: true });\n\t}\n\n\treturn binaryPath;\n}\n\n/**\n * Ensure a tool is available, downloading it if necessary.\n *\n * This is the main entry point for tool management. It:\n * 1. Checks if the tool already exists (local or system)\n * 2. If not, downloads and installs it\n * 3. Returns the path to use, or null if unavailable\n *\n * @param tool - The tool identifier (\"fd\" or \"rg\")\n * @param silent - If true, suppress console output\n * @returns Path to the tool binary, or null if installation failed\n */\nexport async function ensureTool(\n\ttool: \"fd\" | \"rg\",\n\tsilent = false,\n): Promise<string | null> {\n\t// Check if already installed\n\tconst existingPath = getToolPath(tool);\n\tif (existingPath) {\n\t\treturn existingPath;\n\t}\n\n\tconst config = TOOLS[tool];\n\tif (!config) return null;\n\n\t// Notify user about download (unless silent)\n\tif (!silent) {\n\t\tconsole.log(chalk.dim(`${config.name} not found. Downloading...`));\n\t}\n\n\ttry {\n\t\tconst path = await downloadTool(tool);\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.dim(`${config.name} installed to ${path}`));\n\t\t}\n\t\treturn path;\n\t} catch (e) {\n\t\t// Download failed - log and return null (tool is optional)\n\t\tif (!silent) {\n\t\t\tconsole.log(\n\t\t\t\tchalk.yellow(\n\t\t\t\t\t`Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tools-manager.js","sourceRoot":"","sources":["../../src/tools/tools-manager.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * External Tools Manager\n *\n * This module manages optional external command-line tools that enhance\n * the agent's capabilities. Currently supported tools:\n *\n * - fd: Fast file finder (sharkdp/fd)\n * - rg: Ripgrep for fast content search (BurntSushi/ripgrep)\n *\n * The manager handles:\n * 1. Detection of existing installations (system PATH or local)\n * 2. Automatic download from GitHub releases\n * 3. Platform-specific binary selection (macOS, Linux, Windows)\n * 4. Architecture support (x86_64, arm64)\n *\n * Tools are installed to ~/.maestro/tools/ to avoid requiring\n * system-wide permissions or polluting the system PATH.\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport {\n\tchmodSync,\n\tcreateWriteStream,\n\texistsSync,\n\tmkdirSync,\n\tmkdtempSync,\n\treaddirSync,\n\trenameSync,\n\trmSync,\n} from \"node:fs\";\nimport { arch, platform } from \"node:os\";\nimport { join } from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { finished } from \"node:stream/promises\";\nimport chalk from \"chalk\";\nimport { PATHS } from \"../config/constants.js\";\n\n/** Directory where downloaded tools are installed */\nconst TOOLS_DIR = PATHS.TOOLS_DIR;\nconst FETCH_TIMEOUT_MS = 30_000;\nconst DOWNLOAD_IDLE_TIMEOUT_MS = 60_000;\n\n/**\n * Configuration for an external tool.\n * Defines how to find, download, and install the tool.\n */\ninterface ToolConfig {\n\t/** Human-readable name for display */\n\tname: string;\n\t/** GitHub repository in owner/repo format */\n\trepo: string;\n\t/** Name of the binary executable */\n\tbinaryName: string;\n\t/** Prefix for version tags (e.g., \"v\" for \"v1.0.0\") */\n\ttagPrefix: string;\n\t/** Function to determine the release asset name for a platform */\n\tgetAssetName: (\n\t\tversion: string,\n\t\tplat: string,\n\t\tarchitecture: string,\n\t) => string | null;\n}\n\nfunction toolInstallAbortError(config: ToolConfig): Error {\n\treturn new Error(`${config.name} installation aborted`);\n}\n\nfunction throwIfToolInstallAborted(\n\tconfig: ToolConfig,\n\tsignal?: AbortSignal,\n): void {\n\tif (signal?.aborted) {\n\t\tthrow toolInstallAbortError(config);\n\t}\n}\n\n/**\n * Registry of supported external tools and their configurations.\n * Each tool defines platform-specific asset name patterns for GitHub releases.\n */\nconst TOOLS: Record<string, ToolConfig> = {\n\t// fd: A simple, fast and user-friendly alternative to 'find'\n\tfd: {\n\t\tname: \"fd\",\n\t\trepo: \"sharkdp/fd\",\n\t\tbinaryName: \"fd\",\n\t\ttagPrefix: \"v\",\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\t// Map Node.js arch names to Rust target names\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"linux\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-unknown-linux-gnu.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `fd-v${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null; // Unsupported platform\n\t\t},\n\t},\n\n\t// ripgrep: A line-oriented search tool (faster than grep)\n\trg: {\n\t\tname: \"ripgrep\",\n\t\trepo: \"BurntSushi/ripgrep\",\n\t\tbinaryName: \"rg\",\n\t\ttagPrefix: \"\", // ripgrep uses bare version numbers (e.g., \"14.0.0\")\n\t\tgetAssetName: (version, plat, architecture) => {\n\t\t\tif (plat === \"darwin\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-apple-darwin.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"linux\") {\n\t\t\t\t// Linux x86_64 uses musl for better compatibility\n\t\t\t\tif (architecture === \"arm64\") {\n\t\t\t\t\treturn `ripgrep-${version}-aarch64-unknown-linux-gnu.tar.gz`;\n\t\t\t\t}\n\t\t\t\treturn `ripgrep-${version}-x86_64-unknown-linux-musl.tar.gz`;\n\t\t\t}\n\t\t\tif (plat === \"win32\") {\n\t\t\t\tconst archStr = architecture === \"arm64\" ? \"aarch64\" : \"x86_64\";\n\t\t\t\treturn `ripgrep-${version}-${archStr}-pc-windows-msvc.zip`;\n\t\t\t}\n\t\t\treturn null; // Unsupported platform\n\t\t},\n\t},\n};\n\n/**\n * Check if a command exists on the system PATH.\n * Uses --version flag as a safe probe that most tools support.\n */\nfunction commandExists(cmd: string): boolean {\n\ttry {\n\t\tconst result = spawnSync(cmd, [\"--version\"], { stdio: \"pipe\" });\n\t\treturn result.error === undefined || result.error === null;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Get the path to a tool binary, checking local install first then system PATH.\n *\n * Priority order:\n * 1. Local installation in ~/.maestro/tools/\n * 2. System PATH\n *\n * @param tool - The tool identifier (\"fd\" or \"rg\")\n * @returns Path to the binary, or null if not found\n */\nexport function getToolPath(tool: \"fd\" | \"rg\"): string | null {\n\tconst config = TOOLS[tool];\n\tif (!config) return null;\n\n\t// Check our tools directory first (preferred - known version)\n\tconst localPath = join(\n\t\tTOOLS_DIR,\n\t\tconfig.binaryName + (platform() === \"win32\" ? \".exe\" : \"\"),\n\t);\n\tif (existsSync(localPath)) {\n\t\treturn localPath;\n\t}\n\n\t// Fall back to system PATH (may be different version)\n\tif (commandExists(config.binaryName)) {\n\t\treturn config.binaryName;\n\t}\n\n\treturn null;\n}\n\n/**\n * Fetch the latest version number from GitHub releases API.\n *\n * @param repo - Repository in owner/repo format\n * @returns Version string (without \"v\" prefix)\n */\nasync function fetchWithTimeout(\n\turl: string,\n\tinit?: RequestInit,\n\tsignal?: AbortSignal,\n): Promise<{\n\tresponse: Response;\n\tclearTimeout: () => void;\n\tcleanup: () => void;\n}> {\n\tconst controller = new AbortController();\n\tconst timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\tconst onAbort = (): void => {\n\t\tcontroller.abort(signal?.reason);\n\t};\n\tif (signal?.aborted) {\n\t\tonAbort();\n\t} else {\n\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t}\n\tconst clearFetchTimeout = (): void => clearTimeout(timeout);\n\tconst cleanup = (): void => {\n\t\tclearFetchTimeout();\n\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t};\n\ttry {\n\t\tconst response = await fetch(url, { ...init, signal: controller.signal });\n\t\treturn {\n\t\t\tresponse,\n\t\t\tclearTimeout: clearFetchTimeout,\n\t\t\tcleanup,\n\t\t};\n\t} catch (error) {\n\t\tcleanup();\n\t\tthrow error;\n\t}\n}\n\nasync function getLatestVersion(\n\trepo: string,\n\tsignal?: AbortSignal,\n): Promise<string> {\n\tconst { response, cleanup: cleanupFetch } = await fetchWithTimeout(\n\t\t`https://api.github.com/repos/${repo}/releases/latest`,\n\t\t{\n\t\t\theaders: { \"User-Agent\": \"composer-coding-agent\" },\n\t\t},\n\t\tsignal,\n\t);\n\ttry {\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`GitHub API error: ${response.status}`);\n\t\t}\n\n\t\tconst data = (await response.json()) as { tag_name: string };\n\t\t// Strip \"v\" prefix if present for consistent version handling\n\t\treturn data.tag_name.replace(/^v/, \"\");\n\t} finally {\n\t\tcleanupFetch();\n\t}\n}\n\n/**\n * Download a file from a URL to a local path.\n * Uses streaming to handle large files efficiently.\n */\nasync function downloadFile(\n\turl: string,\n\tdest: string,\n\tsignal?: AbortSignal,\n): Promise<void> {\n\tconst {\n\t\tresponse,\n\t\tclearTimeout: clearFetchTimeout,\n\t\tcleanup: cleanupFetch,\n\t} = await fetchWithTimeout(url, undefined, signal);\n\ttry {\n\t\tif (!response.ok) {\n\t\t\tthrow new Error(`Failed to download: ${response.status}`);\n\t\t}\n\n\t\tif (!response.body) {\n\t\t\tthrow new Error(\"No response body\");\n\t\t}\n\n\t\tclearFetchTimeout();\n\n\t\t// Stream the response directly to disk\n\t\tconst stream = Readable.fromWeb(\n\t\t\tresponse.body as Parameters<typeof Readable.fromWeb>[0],\n\t\t);\n\t\tconst fileStream = createWriteStream(dest);\n\t\tlet idleTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\t\tconst onAbort = (): void => {\n\t\t\tconst error = new Error(\"Tool download aborted\");\n\t\t\tstream.destroy(error);\n\t\t\tfileStream.destroy(error);\n\t\t};\n\n\t\tconst clearIdleTimeout = (): void => {\n\t\t\tif (idleTimeoutId) {\n\t\t\t\tclearTimeout(idleTimeoutId);\n\t\t\t\tidleTimeoutId = null;\n\t\t\t}\n\t\t};\n\n\t\tconst resetIdleTimeout = (): void => {\n\t\t\tclearIdleTimeout();\n\t\t\tidleTimeoutId = setTimeout(() => {\n\t\t\t\tconst error = new Error(\n\t\t\t\t\t`Tool download idle timeout after ${Math.round(DOWNLOAD_IDLE_TIMEOUT_MS / 1000)}s`,\n\t\t\t\t);\n\t\t\t\tstream.destroy();\n\t\t\t\tfileStream.destroy();\n\t\t\t\tfileStream.emit(\"error\", error);\n\t\t\t}, DOWNLOAD_IDLE_TIMEOUT_MS);\n\t\t};\n\n\t\tconst onStreamEnd = (): void => {\n\t\t\tclearIdleTimeout();\n\t\t};\n\n\t\tresetIdleTimeout();\n\t\tif (signal?.aborted) {\n\t\t\tonAbort();\n\t\t} else {\n\t\t\tsignal?.addEventListener(\"abort\", onAbort, { once: true });\n\t\t}\n\t\tstream.on(\"data\", resetIdleTimeout);\n\t\tstream.on(\"end\", onStreamEnd);\n\t\tstream.on(\"close\", onStreamEnd);\n\t\tstream.on(\"error\", onStreamEnd);\n\n\t\ttry {\n\t\t\tawait finished(stream.pipe(fileStream));\n\t\t} finally {\n\t\t\tstream.off(\"data\", resetIdleTimeout);\n\t\t\tstream.off(\"end\", onStreamEnd);\n\t\t\tstream.off(\"close\", onStreamEnd);\n\t\t\tstream.off(\"error\", onStreamEnd);\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tclearIdleTimeout();\n\t\t}\n\t} finally {\n\t\tcleanupFetch();\n\t}\n}\n\nfunction runExtractor(\n\tcommand: string,\n\targs: string[],\n\tdescription: string,\n): void {\n\tconst result = spawnSync(command, args, { stdio: \"pipe\" });\n\tif (result.error) {\n\t\tthrow new Error(`${description} failed: ${result.error.message}`);\n\t}\n\tif (result.status !== 0) {\n\t\tconst stderr = result.stderr?.toString().trim();\n\t\tthrow new Error(\n\t\t\t`${description} exited with code ${result.status}${\n\t\t\t\tstderr ? `: ${stderr}` : \"\"\n\t\t\t}`,\n\t\t);\n\t}\n}\n\nfunction findBinary(root: string, binaryName: string): string | null {\n\tconst stack = [root];\n\twhile (stack.length > 0) {\n\t\tconst current = stack.pop();\n\t\tif (!current) continue;\n\t\tconst entries = readdirSync(current, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tif (entry.isSymbolicLink()) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst entryPath = join(current, entry.name);\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tstack.push(entryPath);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (entry.isFile() && entry.name === binaryName) {\n\t\t\t\treturn entryPath;\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Download and install a tool from GitHub releases.\n *\n * Process:\n * 1. Fetch latest version from GitHub API\n * 2. Determine platform-specific asset name\n * 3. Download the release archive\n * 4. Extract the binary\n * 5. Move to tools directory and set permissions\n *\n * @param tool - The tool identifier (\"fd\" or \"rg\")\n * @returns Path to the installed binary\n */\nasync function downloadTool(\n\ttool: \"fd\" | \"rg\",\n\tsignal?: AbortSignal,\n): Promise<string> {\n\tconst config = TOOLS[tool];\n\tif (!config) throw new Error(`Unknown tool: ${tool}`);\n\tthrowIfToolInstallAborted(config, signal);\n\n\tconst plat = platform();\n\tconst architecture = arch();\n\n\t// Get the latest version from GitHub\n\tconst version = await getLatestVersion(config.repo, signal);\n\tthrowIfToolInstallAborted(config, signal);\n\n\t// Determine the correct asset for this platform\n\tconst assetName = config.getAssetName(version, plat, architecture);\n\tif (!assetName) {\n\t\tthrow new Error(`Unsupported platform: ${plat}/${architecture}`);\n\t}\n\n\t// Ensure tools directory exists\n\tmkdirSync(TOOLS_DIR, { recursive: true });\n\n\t// Build paths\n\tconst downloadUrl = `https://github.com/${config.repo}/releases/download/${config.tagPrefix}${version}/${assetName}`;\n\tconst archivePath = join(TOOLS_DIR, assetName);\n\tconst binaryExt = plat === \"win32\" ? \".exe\" : \"\";\n\tconst binaryPath = join(TOOLS_DIR, config.binaryName + binaryExt);\n\n\t// Download the release archive\n\tawait downloadFile(downloadUrl, archivePath, signal);\n\tthrowIfToolInstallAborted(config, signal);\n\n\t// Create temp directory for extraction\n\tconst extractDir = mkdtempSync(join(TOOLS_DIR, \"extract-\"));\n\n\ttry {\n\t\t// Extract based on archive type\n\t\tif (assetName.endsWith(\".tar.gz\")) {\n\t\t\trunExtractor(\n\t\t\t\t\"tar\",\n\t\t\t\t[\"-xzf\", archivePath, \"-C\", extractDir],\n\t\t\t\t\"tar extract\",\n\t\t\t);\n\t\t} else if (assetName.endsWith(\".zip\")) {\n\t\t\trunExtractor(\n\t\t\t\t\"unzip\",\n\t\t\t\t[\"-o\", archivePath, \"-d\", extractDir],\n\t\t\t\t\"unzip extract\",\n\t\t\t);\n\t\t} else {\n\t\t\tthrow new Error(`Unsupported archive format: ${assetName}`);\n\t\t}\n\n\t\tconst extractedBinary =\n\t\t\tfindBinary(extractDir, config.binaryName + binaryExt) ??\n\t\t\t(() => {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Binary not found in archive: ${config.binaryName}${binaryExt}`,\n\t\t\t\t);\n\t\t\t})();\n\n\t\t// Move binary to final location\n\t\trmSync(binaryPath, { force: true });\n\t\trenameSync(extractedBinary, binaryPath);\n\n\t\t// Make executable on Unix systems\n\t\tif (plat !== \"win32\") {\n\t\t\tchmodSync(binaryPath, 0o755);\n\t\t}\n\t} finally {\n\t\t// Clean up downloaded archive and temp directory\n\t\trmSync(archivePath, { force: true });\n\t\trmSync(extractDir, { recursive: true, force: true });\n\t}\n\n\treturn binaryPath;\n}\n\n/**\n * Ensure a tool is available, downloading it if necessary.\n *\n * This is the main entry point for tool management. It:\n * 1. Checks if the tool already exists (local or system)\n * 2. If not, downloads and installs it\n * 3. Returns the path to use, or null if unavailable\n *\n * @param tool - The tool identifier (\"fd\" or \"rg\")\n * @param silent - If true, suppress console output\n * @returns Path to the tool binary, or null if installation failed\n */\nexport async function ensureTool(\n\ttool: \"fd\" | \"rg\",\n\tsilent = false,\n\tsignal?: AbortSignal,\n): Promise<string | null> {\n\tconst config = TOOLS[tool];\n\tif (!config) return null;\n\tthrowIfToolInstallAborted(config, signal);\n\n\t// Check if already installed\n\tconst existingPath = getToolPath(tool);\n\tif (existingPath) {\n\t\treturn existingPath;\n\t}\n\n\t// Notify user about download (unless silent)\n\tif (!silent) {\n\t\tconsole.log(chalk.dim(`${config.name} not found. Downloading...`));\n\t}\n\n\ttry {\n\t\tconst path = await downloadTool(tool, signal);\n\t\tif (!silent) {\n\t\t\tconsole.log(chalk.dim(`${config.name} installed to ${path}`));\n\t\t}\n\t\treturn path;\n\t} catch (e) {\n\t\tif (signal?.aborted) {\n\t\t\tthrow toolInstallAbortError(config);\n\t\t}\n\t\t// Download failed - log and return null (tool is optional)\n\t\tif (!silent) {\n\t\t\tconsole.log(\n\t\t\t\tchalk.yellow(\n\t\t\t\t\t`Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n}\n"]}
|
package/dist/version.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.10.
|
|
3
|
-
"notes": "Maestro by EvalOps - Deterministic coding agent with TUI/CLI and Web UI for AI-assisted development v0.10.
|
|
2
|
+
"version": "0.10.39",
|
|
3
|
+
"notes": "Maestro by EvalOps - Deterministic coding agent with TUI/CLI and Web UI for AI-assisted development v0.10.39 is now available."
|
|
4
4
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evalops/maestro",
|
|
3
3
|
"description": "Maestro by EvalOps - Deterministic coding agent with TUI/CLI and Web UI for AI-assisted development",
|
|
4
|
-
"version": "0.10.
|
|
4
|
+
"version": "0.10.39",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
7
7
|
"workspaces": [
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"check:context-manifest": "tsx scripts/check-context-manifest-contract.ts",
|
|
44
44
|
"check:session-wire-contract": "tsx scripts/check-session-wire-contract.ts",
|
|
45
45
|
"check:cli-runtime-conformance": "tsx scripts/check-cli-runtime-conformance.ts",
|
|
46
|
+
"check:rpc-protocol-conformance": "tsx scripts/check-rpc-protocol-conformance.ts",
|
|
46
47
|
"check:session-replay-fixtures": "tsx scripts/check-session-replay-fixtures.ts",
|
|
47
48
|
"check:agent-trajectory-fixtures": "tsx scripts/check-agent-trajectory-fixtures.ts",
|
|
48
49
|
"check:agent-trajectory-replay-fixtures": "tsx scripts/check-agent-trajectory-replay-fixtures.ts",
|
|
@@ -56,8 +57,9 @@
|
|
|
56
57
|
"check:scenario-replay-gate": "node scripts/run-scenario-replay-gate.mjs",
|
|
57
58
|
"check:codex-parity": "node scripts/check-codex-parity-conformance.mjs",
|
|
58
59
|
"check:codex-operating-layer": "node scripts/check-codex-operating-layer-conformance.mjs",
|
|
60
|
+
"check:platform-runtime-conformance": "node scripts/check-platform-runtime-conformance.mjs",
|
|
59
61
|
"check:release-surface": "node scripts/check-release-surface-conformance.mjs",
|
|
60
|
-
"lint:evals": "bun run lint:headless-proto && node scripts/ensure-deps.js --no-install --workspace @evalops/contracts && node scripts/verify-evals.js && node scripts/verify-tool-versions.js && node scripts/validate-system-paths.js && node scripts/validate-package-boundaries.js && node scripts/validate-public-package-deps.js && node scripts/check-public-surface-boundary.mjs && npm run check:context-manifest && npm run check:session-wire-contract && npm run check:cli-runtime-conformance && npm run check:evidence-integrity && npm run check:maestro-release-gate-events && npm run check:session-replay-fixtures && npm run check:agent-trajectory-fixtures && npm run check:agent-trajectory-replay-fixtures && npm run check:agent-trajectory-score-fixtures && npm run check:agent-trajectory-inspection-fixtures && npm run check:agent-trajectory-scenario-fixtures && npm run check:slack-contract-lab-scenarios && npm run check:scripted-scenario-fixtures && node scripts/session-wire-format-codegen.mjs --check && node scripts/headless-protocol-codegen.mjs --check && npm run check:app-server-schema && npm run check:drift-surfaces && npm run check:staged-rollout && npm run check:codex-parity && npm run check:codex-operating-layer && npm run check:release-surface && npm run developer-surface:check",
|
|
62
|
+
"lint:evals": "bun run lint:headless-proto && node scripts/ensure-deps.js --no-install --workspace @evalops/contracts && node scripts/verify-evals.js && node scripts/verify-tool-versions.js && node scripts/validate-system-paths.js && node scripts/validate-package-boundaries.js && node scripts/validate-public-package-deps.js && node scripts/check-public-surface-boundary.mjs && npm run check:context-manifest && npm run check:session-wire-contract && npm run check:cli-runtime-conformance && npm run check:rpc-protocol-conformance && npm run check:evidence-integrity && npm run check:maestro-release-gate-events && npm run check:session-replay-fixtures && npm run check:agent-trajectory-fixtures && npm run check:agent-trajectory-replay-fixtures && npm run check:agent-trajectory-score-fixtures && npm run check:agent-trajectory-inspection-fixtures && npm run check:agent-trajectory-scenario-fixtures && npm run check:slack-contract-lab-scenarios && npm run check:scripted-scenario-fixtures && node scripts/session-wire-format-codegen.mjs --check && node scripts/headless-protocol-codegen.mjs --check && npm run check:app-server-schema && npm run check:drift-surfaces && npm run check:staged-rollout && npm run check:codex-parity && npm run check:codex-operating-layer && npm run check:platform-runtime-conformance && npm run check:release-surface && npm run developer-surface:check",
|
|
61
63
|
"platform:sdk-smoke": "tsx scripts/check-platform-sdk-contract.ts",
|
|
62
64
|
"platform:agentruntime-e2e": "tsx scripts/smoke-platform-agentruntime-lifecycle.ts",
|
|
63
65
|
"platform:timeline-e2e": "tsx scripts/smoke-platform-timeline-e2e.ts",
|
|
@@ -173,13 +175,12 @@
|
|
|
173
175
|
"@aws-sdk/client-bedrock-runtime": "^3.1020.0",
|
|
174
176
|
"@bufbuild/protobuf": "^2.11.0",
|
|
175
177
|
"@crosscopy/clipboard": "^0.2.8",
|
|
176
|
-
"@daytonaio/sdk": "^0.155.0",
|
|
177
178
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
178
179
|
"@opentelemetry/api": "^1.9.1",
|
|
179
|
-
"@opentelemetry/auto-instrumentations-node": "^0.
|
|
180
|
-
"@opentelemetry/resources": "^2.
|
|
181
|
-
"@opentelemetry/sdk-node": "0.
|
|
182
|
-
"@opentelemetry/semantic-conventions": "^1.
|
|
180
|
+
"@opentelemetry/auto-instrumentations-node": "^0.76.0",
|
|
181
|
+
"@opentelemetry/resources": "^2.7.1",
|
|
182
|
+
"@opentelemetry/sdk-node": "0.218.0",
|
|
183
|
+
"@opentelemetry/semantic-conventions": "^1.41.1",
|
|
183
184
|
"@sentry/node": "^10.53.1",
|
|
184
185
|
"@sinclair/typebox": "^0.34.49",
|
|
185
186
|
"ajv": "^8.18.0",
|