flavor-code 1.4.0-beta.1 → 1.4.0-beta.3
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/agent/loop.d.ts +3 -0
- package/dist/{app-C5ZQUOKJ.js → app-H7RYDMD3.js} +6 -6
- package/dist/{chunk-FOTMP4ZS.js → chunk-3BHKW5A7.js} +1 -1
- package/dist/{chunk-ODRWR4KK.js → chunk-IQAZ42TM.js} +1 -1
- package/dist/{chunk-URUVFECH.js → chunk-KBHWS7M6.js} +4 -4
- package/dist/{chunk-6MNOZ2TX.js → chunk-NIKCPCJG.js} +559 -377
- package/dist/{chunk-3WCDZ6ZL.js → chunk-ZJCAQXA7.js} +2 -1
- package/dist/{claude-ink-LP32NSX5.js → claude-ink-ECZ6QF3P.js} +2 -2
- package/dist/cli-main.js +7 -7
- package/dist/desktop/main.js +626 -443
- package/dist/desktop-renderer/assets/{index-CqRIr-hz.js → index-CYd5JkTR.js} +2 -2
- package/dist/desktop-renderer/assets/{interactive-terminal-C4o7MNPO.js → interactive-terminal-j52Ty3QO.js} +1 -1
- package/dist/desktop-renderer/index.html +1 -1
- package/dist/git/service.d.ts +3 -3
- package/dist/git/tools.d.ts +1 -1
- package/dist/harness/local.d.ts +2 -0
- package/dist/init/project.d.ts +0 -6
- package/dist/jobs/registry.d.ts +1 -1
- package/dist/{load-V2UIYYQI.js → load-SLSL3IWR.js} +1 -1
- package/dist/sdk/index.js +4 -4
- package/dist/tools/lsp.d.ts +13 -10
- package/dist/tools/runtime.d.ts +2 -1
- package/dist/tools/search.d.ts +1 -0
- package/dist/tools/types.d.ts +1 -1
- package/dist/utils/abort.d.ts +4 -0
- package/dist/utils/execFileNoThrow.d.ts +1 -0
- package/dist/utils/process-tree.d.ts +2 -0
- package/package.json +2 -2
- package//346/212/200/346/234/257/346/226/271/346/241/210/346/212/245/345/221/212.md +7 -7
- package/dist/astgraph/db.mjs +0 -237
- package/dist/astgraph/extract.mjs +0 -239
- package/dist/astgraph/flavor-plugin.json +0 -25
- package/dist/astgraph/grammars.mjs +0 -68
- package/dist/astgraph/index.js +0 -301
- package/dist/astgraph/indexer.mjs +0 -175
- package/dist/astgraph/query.mjs +0 -165
- package/dist/astgraph/resolve.mjs +0 -93
- package/dist/astgraph/vendor/tree-sitter-javascript.wasm +0 -0
- package/dist/astgraph/vendor/tree-sitter-tsx.wasm +0 -0
- package/dist/astgraph/vendor/tree-sitter-typescript.wasm +0 -0
- package/dist/astgraph/vendor/tree-sitter.js +0 -3980
- package/dist/astgraph/vendor/tree-sitter.wasm +0 -0
- package/dist/astgraph/vendor/zod/LICENSE +0 -21
- package/dist/astgraph/vendor/zod/index.js +0 -4
- package/dist/astgraph/vendor/zod/locales/index.js +0 -1
- package/dist/astgraph/vendor/zod/locales/package.json +0 -7
- package/dist/astgraph/vendor/zod/package.json +0 -135
- package/dist/astgraph/vendor/zod/v4/classic/checks.js +0 -1
- package/dist/astgraph/vendor/zod/v4/classic/coerce.js +0 -17
- package/dist/astgraph/vendor/zod/v4/classic/compat.js +0 -31
- package/dist/astgraph/vendor/zod/v4/classic/errors.js +0 -48
- package/dist/astgraph/vendor/zod/v4/classic/external.js +0 -20
- package/dist/astgraph/vendor/zod/v4/classic/from-json-schema.js +0 -599
- package/dist/astgraph/vendor/zod/v4/classic/index.js +0 -4
- package/dist/astgraph/vendor/zod/v4/classic/iso.js +0 -30
- package/dist/astgraph/vendor/zod/v4/classic/package.json +0 -7
- package/dist/astgraph/vendor/zod/v4/classic/parse.js +0 -15
- package/dist/astgraph/vendor/zod/v4/classic/schemas.js +0 -1395
- package/dist/astgraph/vendor/zod/v4/core/api.js +0 -1087
- package/dist/astgraph/vendor/zod/v4/core/checks.js +0 -575
- package/dist/astgraph/vendor/zod/v4/core/core.js +0 -78
- package/dist/astgraph/vendor/zod/v4/core/doc.js +0 -35
- package/dist/astgraph/vendor/zod/v4/core/errors.js +0 -185
- package/dist/astgraph/vendor/zod/v4/core/index.js +0 -16
- package/dist/astgraph/vendor/zod/v4/core/json-schema-generator.js +0 -95
- package/dist/astgraph/vendor/zod/v4/core/json-schema-processors.js +0 -601
- package/dist/astgraph/vendor/zod/v4/core/json-schema.js +0 -1
- package/dist/astgraph/vendor/zod/v4/core/package.json +0 -7
- package/dist/astgraph/vendor/zod/v4/core/parse.js +0 -93
- package/dist/astgraph/vendor/zod/v4/core/regexes.js +0 -139
- package/dist/astgraph/vendor/zod/v4/core/registries.js +0 -51
- package/dist/astgraph/vendor/zod/v4/core/schemas.js +0 -2239
- package/dist/astgraph/vendor/zod/v4/core/standard-schema.js +0 -1
- package/dist/astgraph/vendor/zod/v4/core/to-json-schema.js +0 -448
- package/dist/astgraph/vendor/zod/v4/core/util.js +0 -674
- package/dist/astgraph/vendor/zod/v4/core/versions.js +0 -5
- package/dist/astgraph/vendor/zod/v4/index.js +0 -3
- package/dist/astgraph/vendor/zod/v4/locales/ar.js +0 -106
- package/dist/astgraph/vendor/zod/v4/locales/az.js +0 -105
- package/dist/astgraph/vendor/zod/v4/locales/be.js +0 -156
- package/dist/astgraph/vendor/zod/v4/locales/bg.js +0 -120
- package/dist/astgraph/vendor/zod/v4/locales/ca.js +0 -107
- package/dist/astgraph/vendor/zod/v4/locales/cs.js +0 -111
- package/dist/astgraph/vendor/zod/v4/locales/da.js +0 -115
- package/dist/astgraph/vendor/zod/v4/locales/de.js +0 -108
- package/dist/astgraph/vendor/zod/v4/locales/el.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/en.js +0 -113
- package/dist/astgraph/vendor/zod/v4/locales/eo.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/es.js +0 -132
- package/dist/astgraph/vendor/zod/v4/locales/fa.js +0 -114
- package/dist/astgraph/vendor/zod/v4/locales/fi.js +0 -112
- package/dist/astgraph/vendor/zod/v4/locales/fr-CA.js +0 -107
- package/dist/astgraph/vendor/zod/v4/locales/fr.js +0 -125
- package/dist/astgraph/vendor/zod/v4/locales/he.js +0 -214
- package/dist/astgraph/vendor/zod/v4/locales/hr.js +0 -122
- package/dist/astgraph/vendor/zod/v4/locales/hu.js +0 -108
- package/dist/astgraph/vendor/zod/v4/locales/hy.js +0 -147
- package/dist/astgraph/vendor/zod/v4/locales/id.js +0 -106
- package/dist/astgraph/vendor/zod/v4/locales/index.js +0 -52
- package/dist/astgraph/vendor/zod/v4/locales/is.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/it.js +0 -108
- package/dist/astgraph/vendor/zod/v4/locales/ja.js +0 -107
- package/dist/astgraph/vendor/zod/v4/locales/ka.js +0 -112
- package/dist/astgraph/vendor/zod/v4/locales/kh.js +0 -5
- package/dist/astgraph/vendor/zod/v4/locales/km.js +0 -110
- package/dist/astgraph/vendor/zod/v4/locales/ko.js +0 -111
- package/dist/astgraph/vendor/zod/v4/locales/lt.js +0 -203
- package/dist/astgraph/vendor/zod/v4/locales/mk.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/ms.js +0 -107
- package/dist/astgraph/vendor/zod/v4/locales/nl.js +0 -110
- package/dist/astgraph/vendor/zod/v4/locales/no.js +0 -108
- package/dist/astgraph/vendor/zod/v4/locales/ota.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/package.json +0 -7
- package/dist/astgraph/vendor/zod/v4/locales/pl.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/ps.js +0 -114
- package/dist/astgraph/vendor/zod/v4/locales/pt.js +0 -108
- package/dist/astgraph/vendor/zod/v4/locales/ro.js +0 -119
- package/dist/astgraph/vendor/zod/v4/locales/ru.js +0 -156
- package/dist/astgraph/vendor/zod/v4/locales/sl.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/sv.js +0 -110
- package/dist/astgraph/vendor/zod/v4/locales/ta.js +0 -110
- package/dist/astgraph/vendor/zod/v4/locales/th.js +0 -110
- package/dist/astgraph/vendor/zod/v4/locales/tr.js +0 -105
- package/dist/astgraph/vendor/zod/v4/locales/ua.js +0 -5
- package/dist/astgraph/vendor/zod/v4/locales/uk.js +0 -108
- package/dist/astgraph/vendor/zod/v4/locales/ur.js +0 -110
- package/dist/astgraph/vendor/zod/v4/locales/uz.js +0 -110
- package/dist/astgraph/vendor/zod/v4/locales/vi.js +0 -108
- package/dist/astgraph/vendor/zod/v4/locales/yo.js +0 -107
- package/dist/astgraph/vendor/zod/v4/locales/zh-CN.js +0 -109
- package/dist/astgraph/vendor/zod/v4/locales/zh-TW.js +0 -107
- package/dist/astgraph/vendor/zod/v4/mini/checks.js +0 -1
- package/dist/astgraph/vendor/zod/v4/mini/coerce.js +0 -22
- package/dist/astgraph/vendor/zod/v4/mini/external.js +0 -14
- package/dist/astgraph/vendor/zod/v4/mini/index.js +0 -3
- package/dist/astgraph/vendor/zod/v4/mini/iso.js +0 -34
- package/dist/astgraph/vendor/zod/v4/mini/package.json +0 -7
- package/dist/astgraph/vendor/zod/v4/mini/parse.js +0 -1
- package/dist/astgraph/vendor/zod/v4/mini/schemas.js +0 -961
- package/dist/astgraph/vendor/zod/v4/package.json +0 -7
package/dist/desktop/main.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
import { appendFileSync } from "fs";
|
|
3
3
|
import { createHash as createHash28, randomUUID as randomUUID29 } from "crypto";
|
|
4
4
|
import { readFile as readFile40, stat as stat19, writeFile as writeFile19 } from "fs/promises";
|
|
5
|
-
import { basename as basename12, dirname as
|
|
6
|
-
import { fileURLToPath as
|
|
5
|
+
import { basename as basename12, dirname as dirname23, join as join50, resolve as resolve43 } from "path";
|
|
6
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7
7
|
import { app, BrowserWindow as BrowserWindow2, clipboard, dialog, ipcMain, Menu, nativeImage, Notification, shell } from "electron";
|
|
8
8
|
|
|
9
9
|
// src/desktop/contracts.ts
|
|
@@ -293,13 +293,13 @@ var FlavorConfigSchema = z2.object({
|
|
|
293
293
|
evaluationTimeoutMs: z2.number().int().min(100).max(3e4).default(2e3)
|
|
294
294
|
}).prefault({}),
|
|
295
295
|
maxIterations: z2.object({
|
|
296
|
-
main: z2.number().int().min(10).max(500).default(
|
|
297
|
-
subagent: z2.number().int().min(10).max(200).default(
|
|
296
|
+
main: z2.number().int().min(10).max(500).default(300),
|
|
297
|
+
subagent: z2.number().int().min(10).max(200).default(100),
|
|
298
298
|
softLimitFactor: z2.number().min(0.5).max(1).default(0.8),
|
|
299
|
-
extendBy: z2.number().int().min(5).max(100).default(
|
|
299
|
+
extendBy: z2.number().int().min(5).max(100).default(50)
|
|
300
300
|
}).prefault({}),
|
|
301
301
|
loop: z2.object({
|
|
302
|
-
maxCycles: z2.number().int().positive().max(1e4).default(
|
|
302
|
+
maxCycles: z2.number().int().positive().max(1e4).default(100),
|
|
303
303
|
maxTokens: z2.number().int().positive().max(Number.MAX_SAFE_INTEGER).default(5e5),
|
|
304
304
|
isolation: z2.literal("auto").default("auto")
|
|
305
305
|
}).prefault({}),
|
|
@@ -1990,7 +1990,7 @@ function createEmbeddedD2cAutomation(getHost, options = {}) {
|
|
|
1990
1990
|
const ready = await candidate.executeJavaScript("document.readyState", false).catch(() => void 0);
|
|
1991
1991
|
if (ready === "complete" || ready === "interactive") return candidate;
|
|
1992
1992
|
}
|
|
1993
|
-
await new Promise((
|
|
1993
|
+
await new Promise((resolve44) => setTimeout(resolve44, pollIntervalMs));
|
|
1994
1994
|
}
|
|
1995
1995
|
throw new Error(`D2C embedded preview did not navigate within ${navigationTimeoutMs} ms: ${url}${observedUrl === void 0 ? "" : `; last observed ${observedUrl}`}${observedFrames.length === 0 ? "; no live frames" : `; live frames ${observedFrames.join(", ")}`}`);
|
|
1996
1996
|
};
|
|
@@ -2005,13 +2005,13 @@ function createEmbeddedD2cAutomation(getHost, options = {}) {
|
|
|
2005
2005
|
while (Date.now() <= deadline) {
|
|
2006
2006
|
last = await frame(baseUrl).executeJavaScript(assertionScript(step), true);
|
|
2007
2007
|
if (last.passed) return last;
|
|
2008
|
-
await new Promise((
|
|
2008
|
+
await new Promise((resolve44) => setTimeout(resolve44, 50));
|
|
2009
2009
|
}
|
|
2010
2010
|
return last;
|
|
2011
2011
|
};
|
|
2012
2012
|
const settleAfterAction = async (previousUrl) => {
|
|
2013
2013
|
const origin = new URL(previousUrl).origin;
|
|
2014
|
-
await new Promise((
|
|
2014
|
+
await new Promise((resolve44) => setTimeout(resolve44, Math.min(180, Math.max(20, visualDelayMs * 0.25))));
|
|
2015
2015
|
const deadline = Date.now() + Math.min(2e3, navigationTimeoutMs);
|
|
2016
2016
|
while (Date.now() <= deadline) {
|
|
2017
2017
|
const candidate = host().mainFrame.framesInSubtree.find((item) => !item.isDestroyed() && sameOrigin(item.url, origin));
|
|
@@ -2019,7 +2019,7 @@ function createEmbeddedD2cAutomation(getHost, options = {}) {
|
|
|
2019
2019
|
const ready = await candidate.executeJavaScript("document.readyState", false).catch(() => void 0);
|
|
2020
2020
|
if (ready === "complete" || ready === "interactive") return candidate;
|
|
2021
2021
|
}
|
|
2022
|
-
await new Promise((
|
|
2022
|
+
await new Promise((resolve44) => setTimeout(resolve44, pollIntervalMs));
|
|
2023
2023
|
}
|
|
2024
2024
|
throw new Error("D2C preview frame was unavailable after an interaction action");
|
|
2025
2025
|
};
|
|
@@ -2106,7 +2106,7 @@ function createEmbeddedD2cAutomation(getHost, options = {}) {
|
|
|
2106
2106
|
return result;
|
|
2107
2107
|
},
|
|
2108
2108
|
settle: async () => {
|
|
2109
|
-
await new Promise((
|
|
2109
|
+
await new Promise((resolve44) => setTimeout(resolve44, 150));
|
|
2110
2110
|
requests = await frame(activeUrl).executeJavaScript(`performance.getEntriesByType("resource").filter((entry) => {
|
|
2111
2111
|
try { return ["fetch", "xmlhttprequest"].includes(entry.initiatorType) && new URL(entry.name).origin === ${JSON.stringify(mockOrigin)}; } catch { return false; }
|
|
2112
2112
|
}).length`, false).catch(() => 0);
|
|
@@ -2192,7 +2192,7 @@ function responseText(protocol, value) {
|
|
|
2192
2192
|
}
|
|
2193
2193
|
throw new Error("D2C Judge response did not contain text output");
|
|
2194
2194
|
}
|
|
2195
|
-
function createD2cJudgeClient(fetcher = globalThis.fetch, retryDelay = (ms) => new Promise((
|
|
2195
|
+
function createD2cJudgeClient(fetcher = globalThis.fetch, retryDelay = (ms) => new Promise((resolve44) => setTimeout(resolve44, ms))) {
|
|
2196
2196
|
const request = async (config, prompt, images, maxTokens) => {
|
|
2197
2197
|
const openAi = config.protocol === "openai-compatible";
|
|
2198
2198
|
const imageContent = openAi ? images.map((image) => ({ type: "image_url", image_url: { url: dataUrl(image), detail: "high" } })) : images.map((image) => ({ type: "image", source: { type: "base64", media_type: "image/png", data: image.toString("base64") } }));
|
|
@@ -2551,7 +2551,7 @@ import { z as z7 } from "zod";
|
|
|
2551
2551
|
import { Worker } from "worker_threads";
|
|
2552
2552
|
function comparePngsInWorker(left, right, signal) {
|
|
2553
2553
|
signal?.throwIfAborted();
|
|
2554
|
-
return new Promise((
|
|
2554
|
+
return new Promise((resolve44, reject) => {
|
|
2555
2555
|
const worker = new Worker(new URL("./pixel-worker.js", import.meta.url));
|
|
2556
2556
|
let settled = false;
|
|
2557
2557
|
const finish = (callback) => {
|
|
@@ -2569,7 +2569,7 @@ function comparePngsInWorker(left, right, signal) {
|
|
|
2569
2569
|
finish(() => reject(new Error(value.error ?? "D2C pixel worker failed")));
|
|
2570
2570
|
return;
|
|
2571
2571
|
}
|
|
2572
|
-
finish(() =>
|
|
2572
|
+
finish(() => resolve44({ ...value.result, heatmapPng: Buffer.from(value.result.heatmapPng) }));
|
|
2573
2573
|
});
|
|
2574
2574
|
worker.once("error", (error) => finish(() => reject(error)));
|
|
2575
2575
|
worker.once("exit", (code) => {
|
|
@@ -3961,11 +3961,11 @@ function createD2cTools(workspace, options = {}) {
|
|
|
3961
3961
|
name: "D2cImport",
|
|
3962
3962
|
description: "Import a Pixso HTML design export for D2C (design-to-code) comparison. Copies the export directory into .flavor/d2c/<task>/design/. The task name must be lowercase letters, digits and dashes.",
|
|
3963
3963
|
inputSchema: D2cImportInput,
|
|
3964
|
-
paths: (input) => [input.exportDir],
|
|
3964
|
+
paths: (input) => [resolve4(workspace, input.exportDir), resolve4(workspace, ".flavor", "d2c", input.task)],
|
|
3965
3965
|
summarize: (input) => `${input.task} \u2190 ${input.exportDir}`,
|
|
3966
3966
|
execute: async (input, signal) => {
|
|
3967
3967
|
signal.throwIfAborted();
|
|
3968
|
-
const manifest = await importDesign(workspace, input.task, input.exportDir);
|
|
3968
|
+
const manifest = await importDesign(workspace, input.task, resolve4(workspace, input.exportDir));
|
|
3969
3969
|
return {
|
|
3970
3970
|
task: input.task,
|
|
3971
3971
|
entryHtml: manifest.entryHtml,
|
|
@@ -3979,7 +3979,7 @@ function createD2cTools(workspace, options = {}) {
|
|
|
3979
3979
|
name: "D2cCompare",
|
|
3980
3980
|
description: "Compare an imported Pixso design against the rendered implementation and score visual fidelity. The implementation is a frontend project directory (Vue or React, Vite-based; dependencies are installed and the dev server is started and stopped automatically), a localhost URL of an already running server, or a workspace-relative .html file. Returns a similarity score plus structured offsets, color deviations, font mismatches and missing or extra elements, and stores the report with screenshots under .flavor/d2c/<task>/reports/. Requires the desktop app for rendering.",
|
|
3981
3981
|
inputSchema: D2cCompareInput,
|
|
3982
|
-
paths: (input) => [input.implementation],
|
|
3982
|
+
paths: (input) => [resolve4(workspace, ".flavor", "d2c", input.task), .../^https?:\/\//iu.test(input.implementation) ? [] : [resolve4(workspace, input.implementation)]],
|
|
3983
3983
|
summarize: (input) => input.task,
|
|
3984
3984
|
execute: async (input, signal) => {
|
|
3985
3985
|
signal.throwIfAborted();
|
|
@@ -4226,10 +4226,10 @@ ${summarizeReport(report2)}`).join("\n\n")
|
|
|
4226
4226
|
|
|
4227
4227
|
// src/production.ts
|
|
4228
4228
|
import { readFile as readFile28 } from "fs/promises";
|
|
4229
|
-
import { mkdirSync as
|
|
4229
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
4230
4230
|
import { createHash as createHash21, randomUUID as randomUUID25 } from "crypto";
|
|
4231
4231
|
import { homedir as homedir3, release as osRelease, tmpdir as tmpdir4, version as osVersion } from "os";
|
|
4232
|
-
import { basename as basename9, dirname as
|
|
4232
|
+
import { basename as basename9, dirname as dirname22, join as join37, relative as relative25, resolve as resolve36, sep as sep25 } from "path";
|
|
4233
4233
|
|
|
4234
4234
|
// src/agent/subagents.ts
|
|
4235
4235
|
import { z as z10 } from "zod";
|
|
@@ -4442,12 +4442,12 @@ function hasCycle(nodes) {
|
|
|
4442
4442
|
|
|
4443
4443
|
// src/utils/async.ts
|
|
4444
4444
|
function withTimeout(promise, ms) {
|
|
4445
|
-
return new Promise((
|
|
4445
|
+
return new Promise((resolve44, reject) => {
|
|
4446
4446
|
let settled = false;
|
|
4447
4447
|
const timer = setTimeout(() => {
|
|
4448
4448
|
if (settled) return;
|
|
4449
4449
|
settled = true;
|
|
4450
|
-
|
|
4450
|
+
resolve44({ timedOut: true, value: void 0 });
|
|
4451
4451
|
}, ms);
|
|
4452
4452
|
timer.unref?.();
|
|
4453
4453
|
promise.then(
|
|
@@ -4455,7 +4455,7 @@ function withTimeout(promise, ms) {
|
|
|
4455
4455
|
if (!settled) {
|
|
4456
4456
|
settled = true;
|
|
4457
4457
|
clearTimeout(timer);
|
|
4458
|
-
|
|
4458
|
+
resolve44({ timedOut: false, value });
|
|
4459
4459
|
}
|
|
4460
4460
|
},
|
|
4461
4461
|
(error) => {
|
|
@@ -4470,13 +4470,13 @@ function withTimeout(promise, ms) {
|
|
|
4470
4470
|
}
|
|
4471
4471
|
function awaitWithSignal3(promise, signal) {
|
|
4472
4472
|
signal.throwIfAborted();
|
|
4473
|
-
return new Promise((
|
|
4473
|
+
return new Promise((resolve44, reject) => {
|
|
4474
4474
|
const onAbort = () => reject(signal.reason);
|
|
4475
4475
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
4476
4476
|
promise.then(
|
|
4477
4477
|
(value) => {
|
|
4478
4478
|
signal.removeEventListener("abort", onAbort);
|
|
4479
|
-
|
|
4479
|
+
resolve44(value);
|
|
4480
4480
|
},
|
|
4481
4481
|
(error) => {
|
|
4482
4482
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -4739,7 +4739,7 @@ var TaskPlanSchema = z11.object({
|
|
|
4739
4739
|
}
|
|
4740
4740
|
dependencies2.add(dependency);
|
|
4741
4741
|
}
|
|
4742
|
-
if (task.status === "completed") {
|
|
4742
|
+
if (task.status === "completed" || task.status === "in_progress") {
|
|
4743
4743
|
const incomplete = task.dependencies.find((dependency) => plan.tasks.find((candidate) => candidate.id === dependency)?.status !== "completed");
|
|
4744
4744
|
if (incomplete !== void 0) {
|
|
4745
4745
|
context.addIssue({
|
|
@@ -4768,7 +4768,7 @@ function updatePlanTask(plan, input) {
|
|
|
4768
4768
|
if (!isValidTransition(previousStatus, update.status)) {
|
|
4769
4769
|
throw new Error(`Invalid task transition for ${update.taskId}: ${previousStatus} -> ${update.status}`);
|
|
4770
4770
|
}
|
|
4771
|
-
if (update.status === "completed") {
|
|
4771
|
+
if (update.status === "completed" || update.status === "in_progress") {
|
|
4772
4772
|
const incomplete = current.tasks[index].dependencies.find((dependency) => current.tasks.find((task) => task.id === dependency)?.status !== "completed");
|
|
4773
4773
|
if (incomplete !== void 0) throw new Error(`Task ${update.taskId} has an incomplete dependency: ${incomplete}`);
|
|
4774
4774
|
}
|
|
@@ -4793,6 +4793,7 @@ function isValidTransition(from, to) {
|
|
|
4793
4793
|
if (from === "in_progress") {
|
|
4794
4794
|
return to === "completed" || to === "failed" || to === "blocked" || to === "cancelled";
|
|
4795
4795
|
}
|
|
4796
|
+
if (from === "blocked" || from === "failed") return to === "pending" || to === "in_progress" || to === "cancelled";
|
|
4796
4797
|
return false;
|
|
4797
4798
|
}
|
|
4798
4799
|
function normalizeAbandonedPlan(plan) {
|
|
@@ -6163,8 +6164,8 @@ function withStructuredOutput(options) {
|
|
|
6163
6164
|
if (retry.backoffMs.length < retry.maxRetries) {
|
|
6164
6165
|
throw new Error("Structured output backoffMs must provide one delay per retry");
|
|
6165
6166
|
}
|
|
6166
|
-
for (const
|
|
6167
|
-
if (!Number.isSafeInteger(
|
|
6167
|
+
for (const delay3 of retry.backoffMs.slice(0, retry.maxRetries)) {
|
|
6168
|
+
if (!Number.isSafeInteger(delay3) || delay3 < 0) {
|
|
6168
6169
|
throw new Error("Structured output retry delays must be non-negative integers");
|
|
6169
6170
|
}
|
|
6170
6171
|
}
|
|
@@ -6525,10 +6526,10 @@ function sanitize(message2, secrets) {
|
|
|
6525
6526
|
}
|
|
6526
6527
|
function waitForRetry(delayMs, signal) {
|
|
6527
6528
|
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
6528
|
-
return new Promise((
|
|
6529
|
+
return new Promise((resolve44, reject) => {
|
|
6529
6530
|
const timer = setTimeout(() => {
|
|
6530
6531
|
signal?.removeEventListener("abort", onAbort);
|
|
6531
|
-
|
|
6532
|
+
resolve44();
|
|
6532
6533
|
}, delayMs);
|
|
6533
6534
|
const onAbort = () => {
|
|
6534
6535
|
clearTimeout(timer);
|
|
@@ -6599,6 +6600,15 @@ var AgentLoop = class {
|
|
|
6599
6600
|
get iterationLimitMode() {
|
|
6600
6601
|
return this.#iterationLimitMode;
|
|
6601
6602
|
}
|
|
6603
|
+
get maxIterations() {
|
|
6604
|
+
return this.#options.maxIterations;
|
|
6605
|
+
}
|
|
6606
|
+
get softLimitFactor() {
|
|
6607
|
+
return this.#options.softLimitFactor;
|
|
6608
|
+
}
|
|
6609
|
+
get extendIterations() {
|
|
6610
|
+
return this.#options.extendIterations;
|
|
6611
|
+
}
|
|
6602
6612
|
setModel(modelId) {
|
|
6603
6613
|
this.#options.registry.get(modelId);
|
|
6604
6614
|
this.#options.modelId = modelId;
|
|
@@ -7166,10 +7176,10 @@ function serializeToolInput(input) {
|
|
|
7166
7176
|
}
|
|
7167
7177
|
function waitForRetry2(delayMs, signal) {
|
|
7168
7178
|
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
7169
|
-
return new Promise((
|
|
7179
|
+
return new Promise((resolve44, reject) => {
|
|
7170
7180
|
const timer = setTimeout(() => {
|
|
7171
7181
|
signal?.removeEventListener("abort", onAbort);
|
|
7172
|
-
|
|
7182
|
+
resolve44();
|
|
7173
7183
|
}, delayMs);
|
|
7174
7184
|
const onAbort = () => {
|
|
7175
7185
|
clearTimeout(timer);
|
|
@@ -7824,8 +7834,9 @@ var ToolRuntime = class {
|
|
|
7824
7834
|
}
|
|
7825
7835
|
}
|
|
7826
7836
|
async execute(call, context) {
|
|
7837
|
+
if (this.#disposed) return { ok: false, error: { code: "runtime_disposed", message: "ToolRuntime is disposed" } };
|
|
7827
7838
|
const definition = this.#tools.get(call.name);
|
|
7828
|
-
const journalId = definition === void 0 || this.#journal === void 0 ? void 0 : this.#journal.start(call.name, call.input, definition.retrySafe === true || definition.readOnly === true);
|
|
7839
|
+
const journalId = definition === void 0 || this.#journal === void 0 ? void 0 : this.#journal.start(call.name, call.input, definition.retrySafe === true || definition.readOnly === true || getToolCategory(call.name) === "read");
|
|
7829
7840
|
try {
|
|
7830
7841
|
const result = await this.#execute(call, context);
|
|
7831
7842
|
if (journalId !== void 0) this.#journal?.complete(journalId, result);
|
|
@@ -7852,6 +7863,10 @@ var ToolRuntime = class {
|
|
|
7852
7863
|
if (tool === void 0) return { ok: false, error: { code: "unknown_tool", message: `Unknown tool: ${call.name}` } };
|
|
7853
7864
|
const signal = context.signal ?? new AbortController().signal;
|
|
7854
7865
|
const toolCallId = call.id ?? randomUUID7();
|
|
7866
|
+
if (tool.agents !== void 0 && !tool.agents.includes(context.agent)) {
|
|
7867
|
+
return this.#fail(toolCallId, tool.name, call.input, context.agent, "permission_denied", `Tool ${tool.name} is not available to ${context.agent} agents`);
|
|
7868
|
+
}
|
|
7869
|
+
if (signal.aborted) return this.#fail(toolCallId, tool.name, call.input, context.agent, "cancelled", message(signal.reason));
|
|
7855
7870
|
const validation = this.normalize(call);
|
|
7856
7871
|
if (!validation.ok) {
|
|
7857
7872
|
return this.#fail(toolCallId, call.name, call.input, context.agent, validation.error.code, validation.error.message);
|
|
@@ -7959,17 +7974,37 @@ var ToolRuntime = class {
|
|
|
7959
7974
|
} catch (error) {
|
|
7960
7975
|
return this.#fail(toolCallId, tool.name, input, context.agent, "invalid_output", message(error));
|
|
7961
7976
|
}
|
|
7962
|
-
const
|
|
7963
|
-
|
|
7964
|
-
|
|
7977
|
+
const warnings = [];
|
|
7978
|
+
let presentation = getToolPresentation(rawOutput);
|
|
7979
|
+
try {
|
|
7980
|
+
presentation = tool.presentResult?.(rawOutput, input) ?? presentation;
|
|
7981
|
+
} catch (error) {
|
|
7982
|
+
warnings.push(`Tool succeeded; result presentation failed: ${message(error)}`);
|
|
7983
|
+
}
|
|
7984
|
+
let rendered;
|
|
7985
|
+
try {
|
|
7986
|
+
rendered = tool.renderForModel?.(rawOutput, input);
|
|
7987
|
+
} catch (error) {
|
|
7988
|
+
warnings.push(`Tool succeeded; result rendering failed: ${message(error)}`);
|
|
7989
|
+
}
|
|
7965
7990
|
const output = rendered === void 0 ? await this.#limitOutput(tool.name, rawOutput) : rawOutput;
|
|
7966
7991
|
const content = rendered === void 0 ? void 0 : await this.#limitText(tool.name, rendered);
|
|
7967
|
-
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
|
|
7971
|
-
|
|
7972
|
-
}
|
|
7992
|
+
let additionalContext = [];
|
|
7993
|
+
try {
|
|
7994
|
+
additionalContext = await this.#afterSuccess?.(tool.name, tool.paths(input), input, rawOutput, context) ?? [];
|
|
7995
|
+
} catch (error) {
|
|
7996
|
+
warnings.push(`Tool succeeded; context update failed: ${message(error)}`);
|
|
7997
|
+
}
|
|
7998
|
+
try {
|
|
7999
|
+
await this.#hooks.emit({
|
|
8000
|
+
version: 1,
|
|
8001
|
+
type: "PostToolUse",
|
|
8002
|
+
payload: { toolCallId, tool: tool.name, input, agent: context.agent, output }
|
|
8003
|
+
});
|
|
8004
|
+
} catch (error) {
|
|
8005
|
+
warnings.push(`Tool succeeded; PostToolUse hook failed: ${message(error)}`);
|
|
8006
|
+
}
|
|
8007
|
+
additionalContext = [...additionalContext, ...warnings];
|
|
7973
8008
|
return {
|
|
7974
8009
|
ok: true,
|
|
7975
8010
|
output,
|
|
@@ -8000,17 +8035,23 @@ var ToolRuntime = class {
|
|
|
8000
8035
|
const preview = headTailPreview(serialized, previewChars);
|
|
8001
8036
|
this.#turnOutputChars += preview.length;
|
|
8002
8037
|
const directory = join7(this.#workspace, ".flavor", "tool-results");
|
|
8003
|
-
await mkdir6(directory, { recursive: true });
|
|
8004
8038
|
const filename2 = `${(/* @__PURE__ */ new Date()).toISOString().replaceAll(":", "-")}-${randomUUID7()}-${safeFilename(tool)}.txt`;
|
|
8005
8039
|
const savedTo = join7(directory, filename2);
|
|
8006
|
-
|
|
8040
|
+
let storage;
|
|
8041
|
+
try {
|
|
8042
|
+
await mkdir6(directory, { recursive: true });
|
|
8043
|
+
await writeFile3(savedTo, serialized, "utf8");
|
|
8044
|
+
storage = { savedTo };
|
|
8045
|
+
} catch (error) {
|
|
8046
|
+
storage = { storageError: `Tool succeeded, but the full output could not be saved: ${message(error)}` };
|
|
8047
|
+
}
|
|
8007
8048
|
return {
|
|
8008
8049
|
truncated: true,
|
|
8009
8050
|
reason,
|
|
8010
8051
|
preview,
|
|
8011
8052
|
originalChars: serialized.length,
|
|
8012
8053
|
previewChars: preview.length,
|
|
8013
|
-
|
|
8054
|
+
...storage
|
|
8014
8055
|
};
|
|
8015
8056
|
}
|
|
8016
8057
|
callPresentation(call) {
|
|
@@ -8211,6 +8252,8 @@ var LocalHarness = class {
|
|
|
8211
8252
|
agent,
|
|
8212
8253
|
ownerId,
|
|
8213
8254
|
...maxIterations === void 0 ? {} : { maxIterations },
|
|
8255
|
+
...this.#options.softLimitFactor === void 0 ? {} : { softLimitFactor: this.#options.softLimitFactor },
|
|
8256
|
+
...this.#options.extendIterations === void 0 ? {} : { extendIterations: this.#options.extendIterations },
|
|
8214
8257
|
...isMain && this.#options.hasActiveProgress !== void 0 ? { hasActiveProgress: this.#options.hasActiveProgress } : {},
|
|
8215
8258
|
...isMain && this.#options.hallucinationGuard !== void 0 ? { hallucinationGuard: this.#options.hallucinationGuard } : {},
|
|
8216
8259
|
...this.#options.modelJournal === void 0 ? {} : { modelJournal: this.#options.modelJournal }
|
|
@@ -8752,7 +8795,7 @@ function resolveWindowsCommand(command, cwd, env) {
|
|
|
8752
8795
|
...pathValue.split(";").filter(Boolean).map((entry2) => resolve9(unquote(entry2), command))
|
|
8753
8796
|
];
|
|
8754
8797
|
for (const base of bases) {
|
|
8755
|
-
const candidates = extname2(base).length > 0 ? [base] : [
|
|
8798
|
+
const candidates = extname2(base).length > 0 ? [base] : [...pathExt.map((extension2) => `${base}${extension2}`), base];
|
|
8756
8799
|
for (const candidate of candidates) {
|
|
8757
8800
|
if (isRegularFile(candidate)) return resolve9(candidate);
|
|
8758
8801
|
}
|
|
@@ -8898,7 +8941,7 @@ function decoratePayload(payload, envelope) {
|
|
|
8898
8941
|
return envelope === void 0 ? payload : { ...payload, ...envelope };
|
|
8899
8942
|
}
|
|
8900
8943
|
function runShellHandler(descriptor, event, signal) {
|
|
8901
|
-
return new Promise((
|
|
8944
|
+
return new Promise((resolve44, reject) => {
|
|
8902
8945
|
const env = { ...process.env, ...descriptor.env };
|
|
8903
8946
|
const invocation = prepareSpawnInvocation(descriptor.command, descriptor.args ?? [], { env });
|
|
8904
8947
|
const child = spawn(invocation.command, invocation.args, {
|
|
@@ -8944,7 +8987,7 @@ function runShellHandler(descriptor, event, signal) {
|
|
|
8944
8987
|
return;
|
|
8945
8988
|
}
|
|
8946
8989
|
try {
|
|
8947
|
-
|
|
8990
|
+
resolve44(HookDecisionSchema.parse(JSON.parse(stdout)));
|
|
8948
8991
|
} catch (error) {
|
|
8949
8992
|
reject(error);
|
|
8950
8993
|
}
|
|
@@ -8983,15 +9026,16 @@ function zodRecord(payload) {
|
|
|
8983
9026
|
// src/utils/execFileNoThrow.ts
|
|
8984
9027
|
import { execFile } from "child_process";
|
|
8985
9028
|
function execFileNoThrow(file, args, options = {}) {
|
|
8986
|
-
return new Promise((
|
|
9029
|
+
return new Promise((resolve44) => {
|
|
8987
9030
|
const child = execFile(file, args, {
|
|
8988
9031
|
timeout: options.timeout,
|
|
8989
9032
|
cwd: options.useCwd === false ? void 0 : process.cwd(),
|
|
8990
9033
|
env: options.env,
|
|
8991
9034
|
encoding: "utf8",
|
|
8992
|
-
windowsHide: true
|
|
9035
|
+
windowsHide: true,
|
|
9036
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
8993
9037
|
}, (error, stdout, stderr) => {
|
|
8994
|
-
|
|
9038
|
+
resolve44({
|
|
8995
9039
|
stdout,
|
|
8996
9040
|
stderr,
|
|
8997
9041
|
code: typeof error?.code === "number" ? error.code : error ? 1 : 0,
|
|
@@ -9163,14 +9207,14 @@ async function createIslandControlServer(options) {
|
|
|
9163
9207
|
void dispatch(line, socket, options, token);
|
|
9164
9208
|
});
|
|
9165
9209
|
});
|
|
9166
|
-
await new Promise((
|
|
9210
|
+
await new Promise((resolve44, reject) => {
|
|
9167
9211
|
const onError = (error) => {
|
|
9168
9212
|
server.removeListener("listening", onListening);
|
|
9169
9213
|
reject(error);
|
|
9170
9214
|
};
|
|
9171
9215
|
const onListening = () => {
|
|
9172
9216
|
server.removeListener("error", onError);
|
|
9173
|
-
|
|
9217
|
+
resolve44();
|
|
9174
9218
|
};
|
|
9175
9219
|
server.once("error", onError);
|
|
9176
9220
|
server.once("listening", onListening);
|
|
@@ -9241,11 +9285,11 @@ function write(socket, payload) {
|
|
|
9241
9285
|
}
|
|
9242
9286
|
}
|
|
9243
9287
|
function closeServer(server) {
|
|
9244
|
-
return new Promise((
|
|
9288
|
+
return new Promise((resolve44) => {
|
|
9245
9289
|
try {
|
|
9246
|
-
server.close(() =>
|
|
9290
|
+
server.close(() => resolve44());
|
|
9247
9291
|
} catch {
|
|
9248
|
-
|
|
9292
|
+
resolve44();
|
|
9249
9293
|
}
|
|
9250
9294
|
});
|
|
9251
9295
|
}
|
|
@@ -10430,7 +10474,7 @@ var USAGE = [
|
|
|
10430
10474
|
" clear reset signals, done markers, and verified markers"
|
|
10431
10475
|
].join("\n");
|
|
10432
10476
|
function runCommand(command, cwd, timeoutMs) {
|
|
10433
|
-
return new Promise((
|
|
10477
|
+
return new Promise((resolve44) => {
|
|
10434
10478
|
const isWindows = process.platform === "win32";
|
|
10435
10479
|
const child = spawn2(isWindows ? "cmd.exe" : "/bin/sh", isWindows ? ["/d", "/s", "/c", command] : ["-c", command], {
|
|
10436
10480
|
cwd,
|
|
@@ -10440,7 +10484,7 @@ function runCommand(command, cwd, timeoutMs) {
|
|
|
10440
10484
|
let stderr = "";
|
|
10441
10485
|
const timer = setTimeout(() => {
|
|
10442
10486
|
child.kill();
|
|
10443
|
-
|
|
10487
|
+
resolve44({ ok: false, stdout, stderr: `${stderr}
|
|
10444
10488
|
[killed: exceeded ${timeoutMs}ms]`, code: "timeout" });
|
|
10445
10489
|
}, timeoutMs);
|
|
10446
10490
|
child.stdout.on("data", (chunk) => {
|
|
@@ -10451,12 +10495,12 @@ function runCommand(command, cwd, timeoutMs) {
|
|
|
10451
10495
|
});
|
|
10452
10496
|
child.on("error", (error) => {
|
|
10453
10497
|
clearTimeout(timer);
|
|
10454
|
-
|
|
10498
|
+
resolve44({ ok: false, stdout, stderr: `${stderr}
|
|
10455
10499
|
${error.message}`, code: "spawn" });
|
|
10456
10500
|
});
|
|
10457
10501
|
child.on("close", (code) => {
|
|
10458
10502
|
clearTimeout(timer);
|
|
10459
|
-
|
|
10503
|
+
resolve44({ ok: code === 0, stdout, stderr, code: code ?? "unknown" });
|
|
10460
10504
|
});
|
|
10461
10505
|
});
|
|
10462
10506
|
}
|
|
@@ -10827,10 +10871,12 @@ ${result.stderr.slice(-4e3)}` : ""}`;
|
|
|
10827
10871
|
name: "evolve_improve",
|
|
10828
10872
|
description: "Implement a fix for one repeated tool failure. Default kind=plugin scaffolds the fix-<tool>/ plugin dir and writes PLAN.md with instructions for implementing, verifying, reloading, and testing it. kind=prompt_rule instead stores a concise guardrail rule (from `implementation`) that is injected into future system prompts and closes the suggestion. Use when the model proposes a concrete fix for a suggestion in the system prompt.",
|
|
10829
10873
|
inputSchema,
|
|
10830
|
-
paths: () => [],
|
|
10831
|
-
execute: async (input) => {
|
|
10874
|
+
paths: (input) => [join12(workspace, ".flavor", "evolve"), ...input.kind === "prompt_rule" ? [] : [join12(workspace, ".flavor", "plugins")]],
|
|
10875
|
+
execute: async (input, signal) => {
|
|
10876
|
+
signal.throwIfAborted();
|
|
10832
10877
|
const { suggestionId, implementation, kind } = input;
|
|
10833
10878
|
const suggestions = await openWithTrends(100);
|
|
10879
|
+
signal.throwIfAborted();
|
|
10834
10880
|
const suggestion = suggestions.find((item) => item.id === suggestionId);
|
|
10835
10881
|
if (suggestion === void 0) throw new Error(`No open suggestion with id "${suggestionId}".`);
|
|
10836
10882
|
if (kind === "prompt_rule") {
|
|
@@ -10869,9 +10915,7 @@ ${result.stderr.slice(-4e3)}` : ""}`;
|
|
|
10869
10915
|
|
|
10870
10916
|
// src/init/project.ts
|
|
10871
10917
|
import { lstat as lstat4, mkdir as mkdir9, readdir as readdir4, readFile as readFile9, realpath as realpath7, writeFile as writeFile8 } from "fs/promises";
|
|
10872
|
-
import {
|
|
10873
|
-
import { basename as basename4, dirname as dirname9, isAbsolute as isAbsolute8, join as join13, relative as relative8, sep as sep8 } from "path";
|
|
10874
|
-
import { fileURLToPath } from "url";
|
|
10918
|
+
import { basename as basename4, isAbsolute as isAbsolute8, join as join13, relative as relative8, sep as sep8 } from "path";
|
|
10875
10919
|
var GENERATED_START = "<!-- flavor-code:start -->";
|
|
10876
10920
|
var GENERATED_END = "<!-- flavor-code:end -->";
|
|
10877
10921
|
var MAX_MANIFEST_BYTES2 = 1e6;
|
|
@@ -11322,29 +11366,8 @@ async function createExampleFlavorConfig(cwd) {
|
|
|
11322
11366
|
};
|
|
11323
11367
|
await writeFile8(configPath, JSON.stringify(example, null, 2) + "\n");
|
|
11324
11368
|
}
|
|
11325
|
-
var ASTGRAPH_SRC_DIR = join13(dirname9(fileURLToPath(import.meta.url)), "astgraph");
|
|
11326
|
-
async function copyAstgraphPlugin(cwd) {
|
|
11327
|
-
const pluginDir = join13(cwd, ".flavor", "plugins", "astgraph");
|
|
11328
|
-
try {
|
|
11329
|
-
await lstat4(pluginDir);
|
|
11330
|
-
return;
|
|
11331
|
-
} catch (error) {
|
|
11332
|
-
if (!isMissingPathError(error)) throw error;
|
|
11333
|
-
}
|
|
11334
|
-
copyDirectory(ASTGRAPH_SRC_DIR, pluginDir);
|
|
11335
|
-
}
|
|
11336
|
-
function copyDirectory(from, to) {
|
|
11337
|
-
mkdirSync2(to, { recursive: true, mode: 448 });
|
|
11338
|
-
for (const entry2 of readdirSync(from, { withFileTypes: true })) {
|
|
11339
|
-
const source = join13(from, entry2.name);
|
|
11340
|
-
const target = join13(to, entry2.name);
|
|
11341
|
-
if (entry2.isDirectory()) copyDirectory(source, target);
|
|
11342
|
-
else if (entry2.isFile()) copyFileSync(source, target);
|
|
11343
|
-
}
|
|
11344
|
-
}
|
|
11345
11369
|
async function initializeFlavor(cwd) {
|
|
11346
11370
|
await ensureFlavorDirectories(cwd);
|
|
11347
|
-
await copyAstgraphPlugin(cwd);
|
|
11348
11371
|
await createExampleFlavorConfig(cwd);
|
|
11349
11372
|
const facts = await inspectProject(cwd);
|
|
11350
11373
|
const path = join13(cwd, "FLAVOR.md");
|
|
@@ -12439,7 +12462,7 @@ function formatPriorGaps(gaps) {
|
|
|
12439
12462
|
import { constants as constants4 } from "fs";
|
|
12440
12463
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
12441
12464
|
import { lstat as lstat5, mkdir as mkdir11, open as open5, rename as rename4, rm as rm6 } from "fs/promises";
|
|
12442
|
-
import { dirname as
|
|
12465
|
+
import { dirname as dirname9, join as join16, relative as relative9, resolve as resolve12, sep as sep9 } from "path";
|
|
12443
12466
|
var DEFAULT_MAX_BYTES = 1024 * 1024;
|
|
12444
12467
|
var GoalStore = class {
|
|
12445
12468
|
#workspace;
|
|
@@ -12452,7 +12475,7 @@ var GoalStore = class {
|
|
|
12452
12475
|
}
|
|
12453
12476
|
async save(input) {
|
|
12454
12477
|
const state = GoalStateSchema.parse(input);
|
|
12455
|
-
await assertNoSymlink(this.#workspace,
|
|
12478
|
+
await assertNoSymlink(this.#workspace, dirname9(this.#root));
|
|
12456
12479
|
await mkdir11(this.#root, { recursive: true, mode: 448 });
|
|
12457
12480
|
await assertNoSymlink(this.#workspace, this.#root);
|
|
12458
12481
|
const target = join16(this.#root, `${state.id}.json`);
|
|
@@ -12486,7 +12509,7 @@ async function assertNoSymlink(root, target) {
|
|
|
12486
12509
|
const chain = [];
|
|
12487
12510
|
while (cursor !== stop) {
|
|
12488
12511
|
chain.push(cursor);
|
|
12489
|
-
cursor =
|
|
12512
|
+
cursor = dirname9(cursor);
|
|
12490
12513
|
}
|
|
12491
12514
|
for (const path of chain.reverse()) {
|
|
12492
12515
|
try {
|
|
@@ -12573,7 +12596,7 @@ async function ensureWorktreeDirectoryIgnored(root) {
|
|
|
12573
12596
|
// src/loop/store.ts
|
|
12574
12597
|
import { constants as constants5 } from "fs";
|
|
12575
12598
|
import { lstat as lstat6, mkdir as mkdir13, open as open6, readFile as readFile12, realpath as realpath8, rename as rename5, rm as rm7, stat as stat6 } from "fs/promises";
|
|
12576
|
-
import { dirname as
|
|
12599
|
+
import { dirname as dirname10, join as join18, relative as relative10, resolve as resolve14, sep as sep10 } from "path";
|
|
12577
12600
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
12578
12601
|
var LOOP_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
|
|
12579
12602
|
var DEFAULT_MAX_BYTES2 = 5 * 1024 * 1024;
|
|
@@ -12655,7 +12678,7 @@ var LoopStore = class {
|
|
|
12655
12678
|
}
|
|
12656
12679
|
async #prepare(loopId) {
|
|
12657
12680
|
validateId(loopId);
|
|
12658
|
-
await assertNoSymlink2(this.#workspace,
|
|
12681
|
+
await assertNoSymlink2(this.#workspace, dirname10(this.#root));
|
|
12659
12682
|
await mkdir13(this.#root, { recursive: true, mode: 448 });
|
|
12660
12683
|
await assertNoSymlink2(this.#workspace, this.#root);
|
|
12661
12684
|
const directory = join18(this.#root, loopId);
|
|
@@ -12688,7 +12711,7 @@ async function assertNoSymlink2(root, target) {
|
|
|
12688
12711
|
const chain = [];
|
|
12689
12712
|
while (cursor !== stop) {
|
|
12690
12713
|
chain.push(cursor);
|
|
12691
|
-
cursor =
|
|
12714
|
+
cursor = dirname10(cursor);
|
|
12692
12715
|
}
|
|
12693
12716
|
for (const path of chain.reverse()) {
|
|
12694
12717
|
try {
|
|
@@ -12718,7 +12741,7 @@ import { readFile as readFile13 } from "fs/promises";
|
|
|
12718
12741
|
import { join as join20 } from "path";
|
|
12719
12742
|
|
|
12720
12743
|
// src/tools/shell.ts
|
|
12721
|
-
import { spawn as
|
|
12744
|
+
import { spawn as spawn4 } from "child_process";
|
|
12722
12745
|
import { existsSync as existsSync4 } from "fs";
|
|
12723
12746
|
import { basename as basename5, delimiter as delimiter2, isAbsolute as isAbsolute9, join as join19, relative as relative11, resolve as resolve15, sep as sep11 } from "path";
|
|
12724
12747
|
import { z as z23 } from "zod";
|
|
@@ -12761,7 +12784,7 @@ function createJobTools(registry) {
|
|
|
12761
12784
|
inputSchema: IdInput,
|
|
12762
12785
|
paths: () => [],
|
|
12763
12786
|
execute: async (input, _signal, context) => {
|
|
12764
|
-
registry.kill(input.id, owner(context));
|
|
12787
|
+
await registry.kill(input.id, owner(context));
|
|
12765
12788
|
return registry.read(input.id, owner(context));
|
|
12766
12789
|
},
|
|
12767
12790
|
presentResult: (output) => jobPresentation("kill", output)
|
|
@@ -12801,11 +12824,51 @@ function owner(context) {
|
|
|
12801
12824
|
return context?.ownerId ?? context?.agent ?? "main";
|
|
12802
12825
|
}
|
|
12803
12826
|
|
|
12827
|
+
// src/utils/process-tree.ts
|
|
12828
|
+
import { spawn as spawn3 } from "child_process";
|
|
12829
|
+
async function terminateProcessTree3(pid) {
|
|
12830
|
+
if (pid === void 0) return;
|
|
12831
|
+
if (process.platform === "win32") {
|
|
12832
|
+
await new Promise((resolve44) => {
|
|
12833
|
+
const child = spawn3("taskkill", ["/pid", String(pid), "/T", "/F"], { shell: false, windowsHide: true });
|
|
12834
|
+
const finish = () => {
|
|
12835
|
+
clearTimeout(timer);
|
|
12836
|
+
resolve44();
|
|
12837
|
+
};
|
|
12838
|
+
const timer = setTimeout(finish, 5e3);
|
|
12839
|
+
timer.unref();
|
|
12840
|
+
child.once("error", finish);
|
|
12841
|
+
child.once("close", finish);
|
|
12842
|
+
});
|
|
12843
|
+
try {
|
|
12844
|
+
process.kill(pid);
|
|
12845
|
+
} catch {
|
|
12846
|
+
}
|
|
12847
|
+
} else {
|
|
12848
|
+
try {
|
|
12849
|
+
process.kill(-pid, "SIGTERM");
|
|
12850
|
+
} catch {
|
|
12851
|
+
try {
|
|
12852
|
+
process.kill(pid, "SIGTERM");
|
|
12853
|
+
} catch {
|
|
12854
|
+
}
|
|
12855
|
+
}
|
|
12856
|
+
await new Promise((resolve44) => setTimeout(resolve44, 250));
|
|
12857
|
+
try {
|
|
12858
|
+
process.kill(-pid, "SIGKILL");
|
|
12859
|
+
} catch {
|
|
12860
|
+
try {
|
|
12861
|
+
process.kill(pid, "SIGKILL");
|
|
12862
|
+
} catch {
|
|
12863
|
+
}
|
|
12864
|
+
}
|
|
12865
|
+
}
|
|
12866
|
+
}
|
|
12867
|
+
|
|
12804
12868
|
// src/tools/shell.ts
|
|
12805
12869
|
var DEFAULT_MAX_OUTPUT_BYTES2 = 1048576;
|
|
12806
12870
|
var DEFAULT_SHELL_TIMEOUT_MS = 10 * 6e4;
|
|
12807
12871
|
var ELLIPSIS = "\u2026";
|
|
12808
|
-
var TERMINATION_GRACE_MS = 250;
|
|
12809
12872
|
var TERMINATION_FAILURE_MS = 5e3;
|
|
12810
12873
|
var STREAM_CLOSE_GRACE_MS = 500;
|
|
12811
12874
|
var ShellInput = z23.object({
|
|
@@ -12824,12 +12887,6 @@ function normalizeShellCommand(input) {
|
|
|
12824
12887
|
if ((executable === "cmd" || executable === "cmd.exe") && /^\/[ck]$/iu.test(args[0] ?? "")) {
|
|
12825
12888
|
return { command, args: [args[0], ...args.slice(1).map(unquoteToken)] };
|
|
12826
12889
|
}
|
|
12827
|
-
if ((executable === "pwsh" || executable === "pwsh.exe" || executable === "powershell" || executable === "powershell.exe") && /^(?:-c|-command)$/iu.test(args[0] ?? "") && args.length > 1) {
|
|
12828
|
-
return { command, args: [args[0], ...args.slice(1).map(unquoteToken)] };
|
|
12829
|
-
}
|
|
12830
|
-
if (/^(?:sh|bash|zsh|fish)$/iu.test(executable) && args[0] === "-c" && args[1] !== void 0) {
|
|
12831
|
-
return { command, args: [args[0], unquoteToken(args[1]), ...args.slice(2)] };
|
|
12832
|
-
}
|
|
12833
12890
|
if (!/\s/u.test(command) || containsShellSyntax(command)) return { command, args };
|
|
12834
12891
|
const tokens2 = tokenizeCommandLine(command);
|
|
12835
12892
|
if (tokens2.length <= 1) return { command, args };
|
|
@@ -12947,7 +13004,8 @@ function shellInvocation(input, cwd, environment) {
|
|
|
12947
13004
|
const payload = serializeCmdPayload(input.args.slice(commandFlag + 1));
|
|
12948
13005
|
return {
|
|
12949
13006
|
command: resolved,
|
|
12950
|
-
args: [...input.args.slice(0, commandFlag + 1), `chcp 65001>nul & ${payload}`]
|
|
13007
|
+
args: [...input.args.slice(0, commandFlag + 1), `chcp 65001>nul & ${payload}`],
|
|
13008
|
+
windowsVerbatimArguments: true
|
|
12951
13009
|
};
|
|
12952
13010
|
}
|
|
12953
13011
|
}
|
|
@@ -12963,7 +13021,8 @@ function shellInvocation(input, cwd, environment) {
|
|
|
12963
13021
|
if (shell2.kind === "posix") return { command: shell2.command, args: ["-c", script] };
|
|
12964
13022
|
if (shell2.kind === "cmd") return {
|
|
12965
13023
|
command: shell2.command,
|
|
12966
|
-
args: ["/d", "/s", "/c", `chcp 65001>nul & ${script}`]
|
|
13024
|
+
args: ["/d", "/s", "/c", `chcp 65001>nul & ${script}`],
|
|
13025
|
+
windowsVerbatimArguments: true
|
|
12967
13026
|
};
|
|
12968
13027
|
const encoded = Buffer.from(script, "utf16le").toString("base64");
|
|
12969
13028
|
return {
|
|
@@ -12986,8 +13045,8 @@ function executionEnvironmentCommand(environment, command, args) {
|
|
|
12986
13045
|
function structuredShellScript(shell2, command, args) {
|
|
12987
13046
|
if (shell2.kind === "posix") return [command, ...args].map(posixQuote).join(" ");
|
|
12988
13047
|
if (shell2.kind === "cmd") return serializeCmdCommand([command, ...args]);
|
|
12989
|
-
const invocation = `& ${psQuote(command)}${args.length > 0 ? ` ${args.map(psQuote).join(" ")}` : ""}`;
|
|
12990
|
-
return `$ErrorActionPreference='Stop'; try { ${invocation}; if ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE } else { exit 0 } } catch { [Console]::Error.WriteLine($_.Exception.Message); exit 127 }`;
|
|
13048
|
+
const invocation = `& ${psQuote(command)}${args.length > 0 ? ` ${args.map((arg) => /^-[A-Za-z][A-Za-z0-9-]*$/u.test(arg) ? arg : psQuote(arg)).join(" ")}` : ""}`;
|
|
13049
|
+
return `$ErrorActionPreference='Stop'; try { ${invocation}; if ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE } else { exit 0 } } catch { [Console]::Error.WriteLine($_.Exception.Message); if ($_.Exception -is [System.Management.Automation.CommandNotFoundException]) { exit 127 } else { exit 1 } }`;
|
|
12991
13050
|
}
|
|
12992
13051
|
function posixQuote(value) {
|
|
12993
13052
|
return `'${value.replace(/'/gu, `'"'"'`)}'`;
|
|
@@ -13020,7 +13079,7 @@ ${result.stderr}`;
|
|
|
13020
13079
|
return {
|
|
13021
13080
|
kind: "command-not-found",
|
|
13022
13081
|
message: `Executable "${command}" was not found by the selected runtime shell.`,
|
|
13023
|
-
hint: "Use a command available on PATH, or pass an existing executable path without surrounding quotes."
|
|
13082
|
+
hint: "Use a command available on PATH, or pass an existing executable path without surrounding quotes. For an unavailable PowerShell cmdlet, check Get-Command and its module in the selected shell; do not keep retrying the same command."
|
|
13024
13083
|
};
|
|
13025
13084
|
}
|
|
13026
13085
|
if (code === "EACCES" || /(?:permission denied|access is denied|拒绝访问)/iu.test(detail)) {
|
|
@@ -13029,11 +13088,11 @@ ${result.stderr}`;
|
|
|
13029
13088
|
if (/(?:no such file or directory|cannot find (?:the )?path|system cannot find|系统找不到指定的路径)/iu.test(detail)) {
|
|
13030
13089
|
return { kind: "path-not-found", message: "The command referenced a path that does not exist." };
|
|
13031
13090
|
}
|
|
13032
|
-
if (/(?:syntax
|
|
13091
|
+
if (/(?:syntax\s*error|unexpected token|parsererror|unterminated (?:regexp|string)|语法错误)/iu.test(detail)) {
|
|
13033
13092
|
return {
|
|
13034
13093
|
kind: "shell-syntax",
|
|
13035
|
-
message: "The
|
|
13036
|
-
hint:
|
|
13094
|
+
message: "The command or interpreter rejected the syntax.",
|
|
13095
|
+
hint: 'Pass normal arguments through args. For JavaScript use command="node", args=["-e", "<complete script>"] without an extra shell or surrounding quotes. Legacy Windows PowerShell can strip quotes when forwarding native arguments.'
|
|
13037
13096
|
};
|
|
13038
13097
|
}
|
|
13039
13098
|
return {
|
|
@@ -13055,7 +13114,7 @@ function createShellTool(workspace, options = {}) {
|
|
|
13055
13114
|
}
|
|
13056
13115
|
return {
|
|
13057
13116
|
name: "Shell",
|
|
13058
|
-
description:
|
|
13117
|
+
description: 'Run a bounded command reliably inside the workspace. Pass a bare executable in command and one logical value per args entry, without adding surrounding shell quotes. For Node scripts use command="node", args=["-e", "<complete JavaScript>"]; do not wrap native commands in cmd /c or PowerShell -Command unless shell syntax is needed, since legacy PowerShell can strip embedded quotes. Use Grep for text searches and Read/Edit for file changes. stdout/stderr are returned verbatim and failures include a structured diagnostic. background=true returns a managed job id.',
|
|
13059
13118
|
inputSchema: ShellInput,
|
|
13060
13119
|
paths: (input) => [workingDirectory(root, input.cwd ?? void 0)],
|
|
13061
13120
|
summarize: (input) => [input.command, ...input.args].join(" "),
|
|
@@ -13155,7 +13214,7 @@ async function executeShell(root, input, cancellation, maxBytes, environment = p
|
|
|
13155
13214
|
return;
|
|
13156
13215
|
}
|
|
13157
13216
|
const invocation = shellInvocation(input, cwd, environment);
|
|
13158
|
-
const child =
|
|
13217
|
+
const child = spawn4(invocation.command, invocation.args, {
|
|
13159
13218
|
cwd,
|
|
13160
13219
|
shell: false,
|
|
13161
13220
|
windowsHide: true,
|
|
@@ -13182,7 +13241,7 @@ async function executeShell(root, input, cancellation, maxBytes, environment = p
|
|
|
13182
13241
|
void finishResolve(observedExitCode ?? child.exitCode, observedSignal ?? child.signalCode);
|
|
13183
13242
|
return;
|
|
13184
13243
|
}
|
|
13185
|
-
termination =
|
|
13244
|
+
termination = terminateProcessTree3(child.pid);
|
|
13186
13245
|
terminalTimer = setTimeout(() => finishReject(new Error(`Process did not close after ${reason} termination`)), TERMINATION_FAILURE_MS);
|
|
13187
13246
|
terminalTimer.unref();
|
|
13188
13247
|
};
|
|
@@ -13342,52 +13401,6 @@ function workingDirectory(root, cwd = ".") {
|
|
|
13342
13401
|
if (delta === ".." || delta.startsWith(`..${sep11}`) || isAbsolute9(delta)) throw new Error("Working directory is outside the workspace");
|
|
13343
13402
|
return candidate;
|
|
13344
13403
|
}
|
|
13345
|
-
async function terminateTree(pid) {
|
|
13346
|
-
if (pid === void 0) return;
|
|
13347
|
-
if (process.platform === "win32") {
|
|
13348
|
-
await waitForProcess(spawn3("taskkill", ["/pid", String(pid), "/T", "/F"], { shell: false, windowsHide: true }));
|
|
13349
|
-
try {
|
|
13350
|
-
process.kill(pid);
|
|
13351
|
-
} catch {
|
|
13352
|
-
}
|
|
13353
|
-
} else {
|
|
13354
|
-
try {
|
|
13355
|
-
process.kill(-pid, "SIGTERM");
|
|
13356
|
-
} catch {
|
|
13357
|
-
try {
|
|
13358
|
-
process.kill(pid, "SIGTERM");
|
|
13359
|
-
} catch {
|
|
13360
|
-
}
|
|
13361
|
-
}
|
|
13362
|
-
await delay2(TERMINATION_GRACE_MS);
|
|
13363
|
-
try {
|
|
13364
|
-
process.kill(-pid, "SIGKILL");
|
|
13365
|
-
} catch {
|
|
13366
|
-
try {
|
|
13367
|
-
process.kill(pid, "SIGKILL");
|
|
13368
|
-
} catch {
|
|
13369
|
-
}
|
|
13370
|
-
}
|
|
13371
|
-
}
|
|
13372
|
-
}
|
|
13373
|
-
function waitForProcess(child) {
|
|
13374
|
-
return new Promise((resolvePromise) => {
|
|
13375
|
-
let settled = false;
|
|
13376
|
-
const finish = () => {
|
|
13377
|
-
if (settled) return;
|
|
13378
|
-
settled = true;
|
|
13379
|
-
clearTimeout(timer);
|
|
13380
|
-
resolvePromise();
|
|
13381
|
-
};
|
|
13382
|
-
const timer = setTimeout(finish, TERMINATION_FAILURE_MS);
|
|
13383
|
-
timer.unref();
|
|
13384
|
-
child.once("error", finish);
|
|
13385
|
-
child.once("close", finish);
|
|
13386
|
-
});
|
|
13387
|
-
}
|
|
13388
|
-
function delay2(milliseconds) {
|
|
13389
|
-
return new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
|
|
13390
|
-
}
|
|
13391
13404
|
function decodeOutput(buffer) {
|
|
13392
13405
|
const encoding = outputEncoding(buffer);
|
|
13393
13406
|
return new TextDecoder(encoding).decode(buffer);
|
|
@@ -13639,7 +13652,7 @@ function isEnvTruthy(value) {
|
|
|
13639
13652
|
|
|
13640
13653
|
// src/utils/log.ts
|
|
13641
13654
|
import { appendFile as appendFile3, mkdir as mkdir14 } from "fs/promises";
|
|
13642
|
-
import { dirname as
|
|
13655
|
+
import { dirname as dirname11, join as join21, resolve as resolve16 } from "path";
|
|
13643
13656
|
var pending = Promise.resolve();
|
|
13644
13657
|
var usagePending = Promise.resolve();
|
|
13645
13658
|
function usageLogPath() {
|
|
@@ -13658,7 +13671,7 @@ async function appendUsageLog(line) {
|
|
|
13658
13671
|
usagePending = usagePending.catch(() => void 0).then(async () => {
|
|
13659
13672
|
try {
|
|
13660
13673
|
const path = usageLogPath();
|
|
13661
|
-
await mkdir14(
|
|
13674
|
+
await mkdir14(dirname11(path), { recursive: true, mode: 448 });
|
|
13662
13675
|
const flag = truncateOnNextAppend ? "w" : "a";
|
|
13663
13676
|
truncateOnNextAppend = false;
|
|
13664
13677
|
await appendFile3(path, `${line}
|
|
@@ -13686,7 +13699,7 @@ var AuditLogger = class {
|
|
|
13686
13699
|
}
|
|
13687
13700
|
async #write(line) {
|
|
13688
13701
|
try {
|
|
13689
|
-
await mkdir14(
|
|
13702
|
+
await mkdir14(dirname11(this.#path), { recursive: true, mode: 448 });
|
|
13690
13703
|
await appendFile3(this.#path, line, { encoding: "utf8", mode: 384, flag: "a" });
|
|
13691
13704
|
} catch {
|
|
13692
13705
|
}
|
|
@@ -14546,12 +14559,12 @@ import {
|
|
|
14546
14559
|
|
|
14547
14560
|
// src/utils/version.ts
|
|
14548
14561
|
import { readFileSync as readFileSync2 } from "fs";
|
|
14549
|
-
import { dirname as
|
|
14550
|
-
import { fileURLToPath
|
|
14562
|
+
import { dirname as dirname12, join as join22 } from "path";
|
|
14563
|
+
import { fileURLToPath } from "url";
|
|
14551
14564
|
var cached;
|
|
14552
14565
|
function packageVersion() {
|
|
14553
14566
|
if (cached !== void 0) return cached;
|
|
14554
|
-
let directory =
|
|
14567
|
+
let directory = dirname12(fileURLToPath(import.meta.url));
|
|
14555
14568
|
for (; ; ) {
|
|
14556
14569
|
try {
|
|
14557
14570
|
const manifest = JSON.parse(readFileSync2(join22(directory, "package.json"), "utf8"));
|
|
@@ -14561,7 +14574,7 @@ function packageVersion() {
|
|
|
14561
14574
|
}
|
|
14562
14575
|
} catch {
|
|
14563
14576
|
}
|
|
14564
|
-
const parent =
|
|
14577
|
+
const parent = dirname12(directory);
|
|
14565
14578
|
if (parent === directory) break;
|
|
14566
14579
|
directory = parent;
|
|
14567
14580
|
}
|
|
@@ -14787,8 +14800,8 @@ var OAuthCallbackAuthProvider = class {
|
|
|
14787
14800
|
async #startCallbackServer(expectedState, signal) {
|
|
14788
14801
|
let serverResolve;
|
|
14789
14802
|
let serverReject;
|
|
14790
|
-
const codePromise = new Promise((
|
|
14791
|
-
serverResolve =
|
|
14803
|
+
const codePromise = new Promise((resolve44, reject) => {
|
|
14804
|
+
serverResolve = resolve44;
|
|
14792
14805
|
serverReject = reject;
|
|
14793
14806
|
});
|
|
14794
14807
|
const server = createServer3((req, res) => {
|
|
@@ -14822,7 +14835,7 @@ var OAuthCallbackAuthProvider = class {
|
|
|
14822
14835
|
);
|
|
14823
14836
|
serverResolve(code);
|
|
14824
14837
|
});
|
|
14825
|
-
return new Promise((
|
|
14838
|
+
return new Promise((resolve44, reject) => {
|
|
14826
14839
|
server.once("error", (error) => {
|
|
14827
14840
|
reject(new Error(`OAuth callback server failed: ${error.message}`));
|
|
14828
14841
|
});
|
|
@@ -14839,7 +14852,7 @@ var OAuthCallbackAuthProvider = class {
|
|
|
14839
14852
|
signal.addEventListener("abort", () => {
|
|
14840
14853
|
serverReject(new Error("OAuth flow aborted"));
|
|
14841
14854
|
}, { once: true });
|
|
14842
|
-
|
|
14855
|
+
resolve44({ redirectUri, promise: codePromise });
|
|
14843
14856
|
});
|
|
14844
14857
|
});
|
|
14845
14858
|
}
|
|
@@ -14921,7 +14934,7 @@ function escapeHtml(text) {
|
|
|
14921
14934
|
}
|
|
14922
14935
|
|
|
14923
14936
|
// src/auth/store.ts
|
|
14924
|
-
import { dirname as
|
|
14937
|
+
import { dirname as dirname13 } from "path";
|
|
14925
14938
|
async function retainOnlyCredentials(store, keepCredentialId) {
|
|
14926
14939
|
const tokens2 = await store.load();
|
|
14927
14940
|
const retained = Object.fromEntries(Object.entries(tokens2).filter(([id]) => id === keepCredentialId));
|
|
@@ -14932,7 +14945,7 @@ function createFileTokenStore(filePath) {
|
|
|
14932
14945
|
const store = {
|
|
14933
14946
|
tokens: {},
|
|
14934
14947
|
async load() {
|
|
14935
|
-
const key = await loadOrCreateConfigKey(
|
|
14948
|
+
const key = await loadOrCreateConfigKey(dirname13(filePath));
|
|
14936
14949
|
const result = await readRecoverableFile(filePath, (raw) => parseTokens(decryptDocument(raw, key)));
|
|
14937
14950
|
const tokens2 = result?.value ?? {};
|
|
14938
14951
|
baseline = { ...tokens2 };
|
|
@@ -14940,7 +14953,7 @@ function createFileTokenStore(filePath) {
|
|
|
14940
14953
|
return tokens2;
|
|
14941
14954
|
},
|
|
14942
14955
|
async save(tokens2) {
|
|
14943
|
-
const key = await loadOrCreateConfigKey(
|
|
14956
|
+
const key = await loadOrCreateConfigKey(dirname13(filePath));
|
|
14944
14957
|
const removals = Object.keys(baseline).filter((providerId) => !(providerId in tokens2));
|
|
14945
14958
|
const upserts = Object.fromEntries(Object.entries(tokens2).filter(
|
|
14946
14959
|
([providerId, token]) => !sameToken(baseline[providerId], token)
|
|
@@ -15769,10 +15782,14 @@ function createSkillResourceTool(registry) {
|
|
|
15769
15782
|
name: "SkillResource",
|
|
15770
15783
|
description: "Read a bounded resource explicitly referenced by a discovered skill; scripts are returned as data and never executed",
|
|
15771
15784
|
inputSchema: SkillResourceInput,
|
|
15785
|
+
readOnly: true,
|
|
15772
15786
|
paths: () => [],
|
|
15773
15787
|
execute: async (input, signal) => {
|
|
15774
15788
|
signal.throwIfAborted();
|
|
15775
|
-
const
|
|
15789
|
+
const requested = input.skill.trim();
|
|
15790
|
+
const alias = requested.includes(":") ? requested.slice(requested.lastIndexOf(":") + 1) : requested;
|
|
15791
|
+
const discovered = await registry.discover();
|
|
15792
|
+
const skill = discovered.find((candidate) => candidate.name === requested) ?? discovered.find((candidate) => candidate.name === alias);
|
|
15776
15793
|
if (skill === void 0) throw new Error(`Unknown skill: ${input.skill}`);
|
|
15777
15794
|
const capability = await registry.resolveResource(skill, input.reference);
|
|
15778
15795
|
const content = await registry.readResource(capability);
|
|
@@ -15790,7 +15807,7 @@ function createSkillResourceTool(registry) {
|
|
|
15790
15807
|
// src/session/store.ts
|
|
15791
15808
|
import { constants as constants7 } from "fs";
|
|
15792
15809
|
import { lstat as lstat8, mkdir as mkdir15, open as open7, readFile as readFile17, readdir as readdir6, realpath as realpath10, rename as rename6, rm as rm8, stat as stat7 } from "fs/promises";
|
|
15793
|
-
import { basename as basename6, dirname as
|
|
15810
|
+
import { basename as basename6, dirname as dirname14, join as join24, relative as relative13, resolve as resolve20, sep as sep13 } from "path";
|
|
15794
15811
|
import { randomUUID as randomUUID13 } from "crypto";
|
|
15795
15812
|
import { z as z27 } from "zod";
|
|
15796
15813
|
|
|
@@ -16712,7 +16729,7 @@ var SessionStore = class {
|
|
|
16712
16729
|
}
|
|
16713
16730
|
}
|
|
16714
16731
|
async #prepareDirectory() {
|
|
16715
|
-
await assertNoSymlink3(this.#workspace,
|
|
16732
|
+
await assertNoSymlink3(this.#workspace, dirname14(this.#sessions));
|
|
16716
16733
|
await mkdir15(this.#sessions, { recursive: true, mode: 448 });
|
|
16717
16734
|
await this.#assertSafeExistingDirectory();
|
|
16718
16735
|
}
|
|
@@ -16940,7 +16957,7 @@ async function assertNoSymlink3(root, target) {
|
|
|
16940
16957
|
const chain = [];
|
|
16941
16958
|
while (cursor !== stop) {
|
|
16942
16959
|
chain.push(cursor);
|
|
16943
|
-
cursor =
|
|
16960
|
+
cursor = dirname14(cursor);
|
|
16944
16961
|
}
|
|
16945
16962
|
for (const path of chain.reverse()) {
|
|
16946
16963
|
try {
|
|
@@ -16985,13 +17002,13 @@ function sanitize2(input, seen = /* @__PURE__ */ new WeakSet()) {
|
|
|
16985
17002
|
// src/session/tree.ts
|
|
16986
17003
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
16987
17004
|
import { mkdir as mkdir17, readFile as readFile19, rename as rename8, writeFile as writeFile11 } from "fs/promises";
|
|
16988
|
-
import { dirname as
|
|
17005
|
+
import { dirname as dirname16, join as join26, resolve as resolve22 } from "path";
|
|
16989
17006
|
|
|
16990
17007
|
// src/session/checkpoint.ts
|
|
16991
17008
|
import { createHash as createHash15, randomUUID as randomUUID14 } from "crypto";
|
|
16992
17009
|
import { execFile as execFile2 } from "child_process";
|
|
16993
17010
|
import { lstat as lstat9, mkdir as mkdir16, open as open8, readFile as readFile18, readdir as readdir7, rename as rename7, rm as rm9, writeFile as writeFile10 } from "fs/promises";
|
|
16994
|
-
import { dirname as
|
|
17011
|
+
import { dirname as dirname15, join as join25, relative as relative14, resolve as resolve21, sep as sep14 } from "path";
|
|
16995
17012
|
var EXCLUDED = /* @__PURE__ */ new Set([".git", ".flavor", "node_modules", "dist", "build", "release", ".worktrees"]);
|
|
16996
17013
|
var MAX_FILE_BYTES = 16 * 1024 * 1024;
|
|
16997
17014
|
var MAX_TOTAL_BYTES = 128 * 1024 * 1024;
|
|
@@ -17077,7 +17094,7 @@ var WorkspaceCheckpointStore = class {
|
|
|
17077
17094
|
for (const file of checkpoint.files) {
|
|
17078
17095
|
const target = contained(this.#workspace, file.path);
|
|
17079
17096
|
const object = objects.get(file.digest);
|
|
17080
|
-
await mkdir16(
|
|
17097
|
+
await mkdir16(dirname15(target), { recursive: true });
|
|
17081
17098
|
const temporary = `${target}.${process.pid}.${randomUUID14()}.tmp`;
|
|
17082
17099
|
await writeFile10(temporary, object, { mode: file.mode });
|
|
17083
17100
|
await rename7(temporary, target);
|
|
@@ -17149,7 +17166,7 @@ function contained(workspace, path) {
|
|
|
17149
17166
|
return absolute;
|
|
17150
17167
|
}
|
|
17151
17168
|
async function atomicJson(path, value) {
|
|
17152
|
-
await mkdir16(
|
|
17169
|
+
await mkdir16(dirname15(path), { recursive: true });
|
|
17153
17170
|
const temporary = `${path}.${process.pid}.${randomUUID14()}.tmp`;
|
|
17154
17171
|
await writeFile10(temporary, `${JSON.stringify(value)}
|
|
17155
17172
|
`, { encoding: "utf8", mode: 384 });
|
|
@@ -17264,7 +17281,7 @@ var SessionHistory = class _SessionHistory {
|
|
|
17264
17281
|
leafId: this.#leafId,
|
|
17265
17282
|
nodes: this.#nodes
|
|
17266
17283
|
};
|
|
17267
|
-
await mkdir17(
|
|
17284
|
+
await mkdir17(dirname16(this.#path), { recursive: true });
|
|
17268
17285
|
const temporary = `${this.#path}.${process.pid}.${randomUUID15()}.tmp`;
|
|
17269
17286
|
await writeFile11(temporary, `${JSON.stringify(document)}
|
|
17270
17287
|
`, { encoding: "utf8", mode: 384 });
|
|
@@ -17563,14 +17580,14 @@ var ProjectSleepScheduler = class {
|
|
|
17563
17580
|
const next = new Date(now);
|
|
17564
17581
|
next.setDate(next.getDate() + 1);
|
|
17565
17582
|
next.setHours(0, 0, 0, 0);
|
|
17566
|
-
const
|
|
17583
|
+
const delay3 = Math.max(1, next.getTime() - now.getTime());
|
|
17567
17584
|
const targetDate = localDateKey(now);
|
|
17568
17585
|
this.#dailyTimer = setTimeout(() => {
|
|
17569
17586
|
this.#dailyTimer = void 0;
|
|
17570
17587
|
if (this.#disposed) return;
|
|
17571
17588
|
this.#enqueue(targetDate);
|
|
17572
17589
|
this.#scheduleNextMidnight();
|
|
17573
|
-
},
|
|
17590
|
+
}, delay3);
|
|
17574
17591
|
this.#dailyTimer.unref?.();
|
|
17575
17592
|
}
|
|
17576
17593
|
#enqueue(date) {
|
|
@@ -17865,7 +17882,7 @@ function computeSessionStats(documents) {
|
|
|
17865
17882
|
// src/tools/files.ts
|
|
17866
17883
|
import { constants as constants9 } from "fs";
|
|
17867
17884
|
import { mkdir as mkdir19, open as open10, readFile as readFile20, realpath as realpath12, rename as rename10, rm as rm11, stat as stat8 } from "fs/promises";
|
|
17868
|
-
import { dirname as
|
|
17885
|
+
import { dirname as dirname17, isAbsolute as isAbsolute12, relative as relative16, resolve as resolve24, sep as sep16 } from "path";
|
|
17869
17886
|
import { randomUUID as randomUUID17 } from "crypto";
|
|
17870
17887
|
import { z as z29 } from "zod";
|
|
17871
17888
|
|
|
@@ -17983,8 +18000,12 @@ var ReadInput2 = z29.object({
|
|
|
17983
18000
|
force: z29.union([z29.boolean(), z29.string().refine((value) => value === "true" || value === "false")]).nullable().optional()
|
|
17984
18001
|
});
|
|
17985
18002
|
var WriteInput = z29.object({ path: z29.string().min(1), content: z29.string() });
|
|
17986
|
-
var EditInput = z29.object({
|
|
17987
|
-
|
|
18003
|
+
var EditInput = z29.object({
|
|
18004
|
+
path: z29.string().min(1),
|
|
18005
|
+
oldText: z29.string().min(1).describe("Copy exact current file text from Read, without displayed line numbers or diff prefixes. Include enough context for one unique match."),
|
|
18006
|
+
newText: z29.string()
|
|
18007
|
+
});
|
|
18008
|
+
var ApplyPatchInput = z29.object({ patch: z29.string().min(1).describe("One-file unified diff with --- a/path, +++ b/path and @@ hunk headers. Use /dev/null as the old path to create a file.") });
|
|
17988
18009
|
var FileObservationStore = class {
|
|
17989
18010
|
#versions = /* @__PURE__ */ new Map();
|
|
17990
18011
|
get(path) {
|
|
@@ -18027,9 +18048,9 @@ function createReadTool(workspace, options = {}) {
|
|
|
18027
18048
|
const windowBytes = rangeRequested ? Math.max(maxBytes, Math.min(info.size, MAX_READ_BYTES)) : maxBytes;
|
|
18028
18049
|
const contents = await readBounded2(path, windowBytes, signal, openFile2);
|
|
18029
18050
|
if (isBinary(contents)) throw new Error("Cannot read binary file as text");
|
|
18030
|
-
const textEnd = utf8SafeEnd(contents, windowBytes);
|
|
18051
|
+
const textEnd = contents.length > windowBytes ? utf8SafeEnd(contents, windowBytes) : contents.length;
|
|
18031
18052
|
const truncated = contents.length > windowBytes;
|
|
18032
|
-
const text = contents.subarray(0, textEnd)
|
|
18053
|
+
const text = decodeFileText(contents.subarray(0, textEnd)).replaceAll("\r\n", "\n");
|
|
18033
18054
|
options.observations?.set(path, versionOfStat(info));
|
|
18034
18055
|
const lines = text.split("\n");
|
|
18035
18056
|
if (lines.at(-1) === "") lines.pop();
|
|
@@ -18105,7 +18126,7 @@ function createEditTool(workspace, options = {}) {
|
|
|
18105
18126
|
const guard = createPathGuard(workspace);
|
|
18106
18127
|
return {
|
|
18107
18128
|
name: "Edit",
|
|
18108
|
-
description: "Replace one unique exact text match",
|
|
18129
|
+
description: "Replace one unique exact text match, accepting LF or CRLF input. Read the file before editing; after a mismatch, re-read with force=true and copy the current text instead of retrying the same oldText.",
|
|
18109
18130
|
inputSchema: EditInput,
|
|
18110
18131
|
paths: (input) => [guard.lexical(input.path)],
|
|
18111
18132
|
execute: async (input, signal) => {
|
|
@@ -18115,14 +18136,14 @@ function createEditTool(workspace, options = {}) {
|
|
|
18115
18136
|
const expected = await expectedMutationVersion(path, options.observations);
|
|
18116
18137
|
const contents = await readText(path);
|
|
18117
18138
|
const hasCRLF = contents.includes("\r\n");
|
|
18118
|
-
const norm = (s) =>
|
|
18139
|
+
const norm = (s) => s.replace(/\r\n/g, "\n");
|
|
18119
18140
|
const contentsLF = norm(contents);
|
|
18120
18141
|
const oldTextLF = norm(input.oldText);
|
|
18121
18142
|
const newTextLF = norm(input.newText);
|
|
18122
18143
|
const first = contentsLF.indexOf(oldTextLF);
|
|
18123
|
-
const second = first < 0 ? -1 : contentsLF.indexOf(oldTextLF, first +
|
|
18144
|
+
const second = first < 0 ? -1 : contentsLF.indexOf(oldTextLF, first + 1);
|
|
18124
18145
|
if (first < 0 || second >= 0) {
|
|
18125
|
-
const diagnosis = buildEditDiagnosis(contentsLF, oldTextLF,
|
|
18146
|
+
const diagnosis = buildEditDiagnosis(contentsLF, oldTextLF, second);
|
|
18126
18147
|
throw new Error(diagnosis);
|
|
18127
18148
|
}
|
|
18128
18149
|
const updatedLF = contentsLF.slice(0, first) + newTextLF + contentsLF.slice(first + oldTextLF.length);
|
|
@@ -18142,7 +18163,7 @@ function createApplyPatchTool(workspace, options = {}) {
|
|
|
18142
18163
|
const guard = createPathGuard(workspace);
|
|
18143
18164
|
return {
|
|
18144
18165
|
name: "ApplyPatch",
|
|
18145
|
-
description: "Apply a workspace-limited unified diff, relocating hunks only by unique exact context",
|
|
18166
|
+
description: "Apply a single-file workspace-limited unified diff, relocating hunks only by unique exact context. Submit separate calls for multiple files.",
|
|
18146
18167
|
inputSchema: ApplyPatchInput,
|
|
18147
18168
|
paths: (input) => parsePatch(input.patch).map((file) => guard.lexical(file.path)),
|
|
18148
18169
|
execute: async (input, signal) => {
|
|
@@ -18207,7 +18228,7 @@ function createPathGuard(workspace) {
|
|
|
18207
18228
|
break;
|
|
18208
18229
|
} catch (error) {
|
|
18209
18230
|
if (!isMissing2(error)) throw error;
|
|
18210
|
-
const parent =
|
|
18231
|
+
const parent = dirname17(ancestor);
|
|
18211
18232
|
if (parent === ancestor) throw error;
|
|
18212
18233
|
ancestor = parent;
|
|
18213
18234
|
}
|
|
@@ -18218,12 +18239,19 @@ function createPathGuard(workspace) {
|
|
|
18218
18239
|
}
|
|
18219
18240
|
async function atomicWrite(path, content, signal, expectedVersion) {
|
|
18220
18241
|
abortIfNeeded(signal);
|
|
18221
|
-
await mkdir19(
|
|
18242
|
+
await mkdir19(dirname17(path), { recursive: true });
|
|
18222
18243
|
const temporary = `${path}.${randomUUID17()}.tmp`;
|
|
18244
|
+
let mode;
|
|
18245
|
+
try {
|
|
18246
|
+
mode = (await stat8(path)).mode & 511;
|
|
18247
|
+
} catch (error) {
|
|
18248
|
+
if (!isMissing2(error)) throw error;
|
|
18249
|
+
}
|
|
18223
18250
|
try {
|
|
18224
18251
|
const handle = await open10(temporary, constants9.O_CREAT | constants9.O_EXCL | constants9.O_WRONLY, 384);
|
|
18225
18252
|
try {
|
|
18226
18253
|
await handle.writeFile(content, "utf8");
|
|
18254
|
+
if (mode !== void 0) await handle.chmod(mode);
|
|
18227
18255
|
await handle.sync();
|
|
18228
18256
|
} finally {
|
|
18229
18257
|
await handle.close();
|
|
@@ -18240,7 +18268,14 @@ async function atomicWrite(path, content, signal, expectedVersion) {
|
|
|
18240
18268
|
async function readText(path) {
|
|
18241
18269
|
const contents = await readFile20(path);
|
|
18242
18270
|
if (isBinary(contents)) throw new Error("Cannot edit binary file as text");
|
|
18243
|
-
return contents
|
|
18271
|
+
return decodeFileText(contents);
|
|
18272
|
+
}
|
|
18273
|
+
function decodeFileText(contents) {
|
|
18274
|
+
try {
|
|
18275
|
+
return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(contents);
|
|
18276
|
+
} catch {
|
|
18277
|
+
throw new Error("File is not valid UTF-8 text. Convert its encoding before using Read/Edit/ApplyPatch to avoid corrupting its contents.");
|
|
18278
|
+
}
|
|
18244
18279
|
}
|
|
18245
18280
|
var ABSENT_VERSION = "absent";
|
|
18246
18281
|
async function expectedMutationVersion(path, observations) {
|
|
@@ -18450,11 +18485,12 @@ function patchPath(header) {
|
|
|
18450
18485
|
const raw = header.split(" ", 1)[0].trim();
|
|
18451
18486
|
return raw.startsWith("a/") || raw.startsWith("b/") ? raw.slice(2) : raw;
|
|
18452
18487
|
}
|
|
18453
|
-
function buildEditDiagnosis(contentsLF, oldTextLF,
|
|
18488
|
+
function buildEditDiagnosis(contentsLF, oldTextLF, second) {
|
|
18454
18489
|
let diagnosis = "oldText must match exactly once";
|
|
18455
18490
|
if (second >= 0) {
|
|
18456
|
-
|
|
18457
|
-
|
|
18491
|
+
let count = 0;
|
|
18492
|
+
for (let index = contentsLF.indexOf(oldTextLF); index >= 0; index = contentsLF.indexOf(oldTextLF, index + 1)) count += 1;
|
|
18493
|
+
diagnosis += ` \u2014 matched ${count} times in the file. No changes made. Include more surrounding text to select one occurrence.`;
|
|
18458
18494
|
return diagnosis;
|
|
18459
18495
|
}
|
|
18460
18496
|
const oldLines = oldTextLF.split("\n");
|
|
@@ -18490,7 +18526,8 @@ ${snippet}
|
|
|
18490
18526
|
First lines of file:
|
|
18491
18527
|
${fileSnippet}`;
|
|
18492
18528
|
}
|
|
18493
|
-
return diagnosis
|
|
18529
|
+
return `${diagnosis}
|
|
18530
|
+
No changes made. Use Read with force=true to retrieve current text, or Grep with fixedStrings=true to locate it. Copy oldText exactly without line-number prefixes; do not repeat the same failed edit.`;
|
|
18494
18531
|
}
|
|
18495
18532
|
function applyHunks(original, hunks) {
|
|
18496
18533
|
const hasCRLF = original.includes("\r\n");
|
|
@@ -18576,9 +18613,9 @@ function matchesLines(source, start, expected) {
|
|
|
18576
18613
|
}
|
|
18577
18614
|
|
|
18578
18615
|
// src/tools/search.ts
|
|
18579
|
-
import { spawn as
|
|
18616
|
+
import { spawn as spawn5 } from "child_process";
|
|
18580
18617
|
import { open as open11, opendir, stat as stat9 } from "fs/promises";
|
|
18581
|
-
import {
|
|
18618
|
+
import { isAbsolute as isAbsolute13, relative as relative17, resolve as resolve25, sep as sep17 } from "path";
|
|
18582
18619
|
import { rgPath as bundledRgPath } from "@vscode/ripgrep";
|
|
18583
18620
|
import createIgnore from "ignore";
|
|
18584
18621
|
import { z as z30 } from "zod";
|
|
@@ -18669,8 +18706,9 @@ var GlobInput = z30.object({
|
|
|
18669
18706
|
limit: z30.coerce.number().int().positive().max(1e5).nullable().optional()
|
|
18670
18707
|
});
|
|
18671
18708
|
var GrepInput = z30.object({
|
|
18672
|
-
pattern: z30.string().min(1),
|
|
18673
|
-
|
|
18709
|
+
pattern: z30.string().min(1).describe("Regular expression without / delimiters, or literal text when fixedStrings=true."),
|
|
18710
|
+
fixedStrings: BooleanInput.describe("Match literal text, useful for Markdown links and paths containing regex punctuation.").nullable().optional(),
|
|
18711
|
+
path: z30.string().min(1).describe("Workspace-relative file or directory. A file restricts the search to that file. Use glob for multiple filenames and JSON null for the workspace root.").nullable().optional(),
|
|
18674
18712
|
glob: z30.string().min(1).nullable().optional(),
|
|
18675
18713
|
fileType: z30.enum(FILE_TYPES).nullable().optional(),
|
|
18676
18714
|
includeIgnored: BooleanInput.describe("Include files excluded by .gitignore or .ignore. Git metadata remains excluded.").nullable().optional(),
|
|
@@ -18719,7 +18757,7 @@ function createGrepTool(workspace, options = {}) {
|
|
|
18719
18757
|
const resources = searchResources(options);
|
|
18720
18758
|
return {
|
|
18721
18759
|
name: "Grep",
|
|
18722
|
-
description: `Search workspace text with a regular expression. fileType must be one of: ${FILE_TYPES.join(", ")}.`,
|
|
18760
|
+
description: `Search workspace text with a regular expression or fixedStrings=true for literal text. Zero matches is a successful search, not a path error. fileType must be one of: ${FILE_TYPES.join(", ")}.`,
|
|
18723
18761
|
inputSchema: GrepInput,
|
|
18724
18762
|
paths: (input) => [scope(root, searchPath(input.path))],
|
|
18725
18763
|
summarize: (input) => {
|
|
@@ -18727,10 +18765,10 @@ function createGrepTool(workspace, options = {}) {
|
|
|
18727
18765
|
const where = path ? ` in ${path}` : "";
|
|
18728
18766
|
const kind = input.fileType ? ` [${input.fileType}]` : "";
|
|
18729
18767
|
const ignored2 = input.includeIgnored === true ? " (including ignored files)" : "";
|
|
18730
|
-
return `pattern:
|
|
18768
|
+
return `pattern: ${input.fixedStrings === true ? JSON.stringify(input.pattern) : `/${input.pattern}/`}${kind}${where}${ignored2}`;
|
|
18731
18769
|
},
|
|
18732
18770
|
execute: async (input, signal) => {
|
|
18733
|
-
const expression = new RegExp(input.pattern);
|
|
18771
|
+
const expression = new RegExp(input.fixedStrings === true ? input.pattern.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&") : input.pattern);
|
|
18734
18772
|
const limit = input.limit ?? options.defaultLimit ?? DEFAULT_RESULT_LIMIT;
|
|
18735
18773
|
const context = input.context ?? 0;
|
|
18736
18774
|
const start = await resolveGrepPath(root, searchPath(input.path));
|
|
@@ -18781,10 +18819,12 @@ async function rgGrep(root, start, input, context, signal, executable, argsPrefi
|
|
|
18781
18819
|
}
|
|
18782
18820
|
const args = ["--json", "--line-number", "--column", "--sort", "path", "--hidden", "--glob", "!.git", "--glob", "!.git/**", "--max-filesize", String(maxFileBytes), "--context", String(context)];
|
|
18783
18821
|
if (input.includeIgnored === true) args.push("--no-ignore");
|
|
18822
|
+
if (input.fixedStrings === true) args.push("--fixed-strings");
|
|
18784
18823
|
if (input.glob !== void 0 && input.glob !== null) args.push("--glob", input.glob);
|
|
18785
18824
|
else for (const extension2 of typeExtensions ?? []) args.push("--glob", `*${extension2}`);
|
|
18786
18825
|
args.push("--regexp", input.pattern, "--", relative17(root, start) || ".");
|
|
18787
18826
|
const matches = [];
|
|
18827
|
+
const matcher = input.glob == null ? void 0 : globRegex(input.glob);
|
|
18788
18828
|
const ignoreLayers = input.includeIgnored === true ? [] : await collectIgnoreLayers(root, start, signal, resources, ignoreBudget);
|
|
18789
18829
|
const parser = delimitedParser(10, (record3) => {
|
|
18790
18830
|
if (record3.length === 0) return true;
|
|
@@ -18797,6 +18837,7 @@ async function rgGrep(root, start, input, context, signal, executable, argsPrefi
|
|
|
18797
18837
|
const lineWithEnding = tryDecodeUtf8(lineValue);
|
|
18798
18838
|
if (decodedPath === void 0 || lineWithEnding === void 0) return true;
|
|
18799
18839
|
const path = normalizePath(decodedPath).replace(/^\.\//, "");
|
|
18840
|
+
if (matcher !== void 0 && !matcher.test(path)) return true;
|
|
18800
18841
|
if (typeExtensions !== void 0 && !typeExtensions.some((extension2) => path.endsWith(extension2))) return true;
|
|
18801
18842
|
if (ignored(path, false, ignoreLayers)) return true;
|
|
18802
18843
|
const text = lineWithEnding.replace(/\r?\n$/, "");
|
|
@@ -18921,7 +18962,12 @@ async function nodeFiles(root, start, signal, accept, limit, resources, ignoreBu
|
|
|
18921
18962
|
}
|
|
18922
18963
|
ignores.length = ruleCount;
|
|
18923
18964
|
}
|
|
18924
|
-
await
|
|
18965
|
+
if ((await stat9(start)).isFile()) {
|
|
18966
|
+
const path = normalizedRelative(root, start);
|
|
18967
|
+
if (!isGitMetadataPath(root, start) && (includeIgnored || !ignored(path, false, ignores)) && accept(path)) output.push(path);
|
|
18968
|
+
} else {
|
|
18969
|
+
await walk(start);
|
|
18970
|
+
}
|
|
18925
18971
|
return output;
|
|
18926
18972
|
}
|
|
18927
18973
|
async function collectIgnoreLayers(root, start, signal, resources, ignoreBudget) {
|
|
@@ -18951,7 +18997,7 @@ async function collectIgnoreLayers(root, start, signal, resources, ignoreBudget)
|
|
|
18951
18997
|
if (!ignored(path, true, layers)) await walk(absolute);
|
|
18952
18998
|
}
|
|
18953
18999
|
}
|
|
18954
|
-
await walk(start);
|
|
19000
|
+
if (!(await stat9(start)).isFile()) await walk(start);
|
|
18955
19001
|
return layers;
|
|
18956
19002
|
}
|
|
18957
19003
|
async function loadIgnoreFiles(directory, base, rules, budget, signal) {
|
|
@@ -19071,8 +19117,12 @@ async function resolveGrepPath(root, path = ".") {
|
|
|
19071
19117
|
const candidate = scope(root, path);
|
|
19072
19118
|
try {
|
|
19073
19119
|
const info = await stat9(candidate);
|
|
19074
|
-
if (info.isFile())
|
|
19075
|
-
} catch {
|
|
19120
|
+
if (!info.isFile() && !info.isDirectory()) throw new Error("Search path must be a file or directory");
|
|
19121
|
+
} catch (error) {
|
|
19122
|
+
if (error.code === "ENOENT") {
|
|
19123
|
+
throw new Error(`Search path does not exist: ${path}. Use Glob to find the file; put filename patterns in glob, not path.`);
|
|
19124
|
+
}
|
|
19125
|
+
throw error;
|
|
19076
19126
|
}
|
|
19077
19127
|
return candidate;
|
|
19078
19128
|
}
|
|
@@ -19142,7 +19192,7 @@ function delimitedParser(delimiter4, onRecord) {
|
|
|
19142
19192
|
function runStreaming(executable, args, cwd, signal, maxBytes, parser) {
|
|
19143
19193
|
return new Promise((resolvePromise, reject) => {
|
|
19144
19194
|
abort(signal);
|
|
19145
|
-
const child =
|
|
19195
|
+
const child = spawn5(executable, args, { cwd, shell: false, windowsHide: true });
|
|
19146
19196
|
let stdoutBytes = 0;
|
|
19147
19197
|
let stderrBytes = 0;
|
|
19148
19198
|
let stderr = Buffer.alloc(0);
|
|
@@ -19270,6 +19320,19 @@ import { isIP } from "net";
|
|
|
19270
19320
|
import { request as httpRequest } from "http";
|
|
19271
19321
|
import { request as httpsRequest } from "https";
|
|
19272
19322
|
import { z as z31 } from "zod";
|
|
19323
|
+
|
|
19324
|
+
// src/utils/abort.ts
|
|
19325
|
+
function waitWithSignal(operation, signal) {
|
|
19326
|
+
if (signal === void 0) return operation;
|
|
19327
|
+
return new Promise((resolve44, reject) => {
|
|
19328
|
+
const abort2 = () => reject(signal.reason);
|
|
19329
|
+
if (signal.aborted) abort2();
|
|
19330
|
+
else signal.addEventListener("abort", abort2, { once: true });
|
|
19331
|
+
operation.then(resolve44, reject).finally(() => signal.removeEventListener("abort", abort2));
|
|
19332
|
+
});
|
|
19333
|
+
}
|
|
19334
|
+
|
|
19335
|
+
// src/tools/web.ts
|
|
19273
19336
|
var WebFetchInput = z31.object({
|
|
19274
19337
|
url: z31.string().url().max(8192),
|
|
19275
19338
|
timeoutMs: z31.coerce.number().int().positive().max(12e4).optional(),
|
|
@@ -19315,7 +19378,12 @@ function createWebSearchTool(provider = createDefaultWebSearchProvider()) {
|
|
|
19315
19378
|
summarize: (input) => input.query,
|
|
19316
19379
|
presentCall: (input) => ({ kind: "web", title: `Searching: ${input.query}` }),
|
|
19317
19380
|
permissions: () => ({ paths: [] }),
|
|
19318
|
-
execute: async (input, signal) =>
|
|
19381
|
+
execute: async (input, signal) => {
|
|
19382
|
+
signal.throwIfAborted();
|
|
19383
|
+
const maxResults = input.maxResults ?? 8;
|
|
19384
|
+
const results = await waitWithSignal(provider.search(input.query, maxResults, signal), signal);
|
|
19385
|
+
return { query: input.query, results: [...results].slice(0, maxResults) };
|
|
19386
|
+
},
|
|
19319
19387
|
renderForModel: (output) => output.results.length === 0 ? `No web results found for: ${output.query}` : output.results.map((item, index) => `${index + 1}. ${item.title}
|
|
19320
19388
|
${item.url}
|
|
19321
19389
|
${item.snippet}`).join("\n\n"),
|
|
@@ -19360,10 +19428,14 @@ var MultiSourceWebSearchProvider = class {
|
|
|
19360
19428
|
}
|
|
19361
19429
|
};
|
|
19362
19430
|
async function fetchPublic(initialUrl, options) {
|
|
19431
|
+
options.signal.throwIfAborted();
|
|
19432
|
+
const deadline = AbortSignal.timeout(options.timeoutMs);
|
|
19433
|
+
options = { ...options, signal: AbortSignal.any([options.signal, deadline]) };
|
|
19363
19434
|
let current = new URL(initialUrl);
|
|
19364
19435
|
for (let redirects = 0; redirects <= 5; redirects += 1) {
|
|
19436
|
+
options.signal.throwIfAborted();
|
|
19365
19437
|
validateUrl(current);
|
|
19366
|
-
const addresses = await lookup(normalizedHostname(current.hostname), { all: true, verbatim: true });
|
|
19438
|
+
const addresses = await waitWithSignal(lookup(normalizedHostname(current.hostname), { all: true, verbatim: true }), options.signal);
|
|
19367
19439
|
if (addresses.length === 0) throw new Error("Host did not resolve");
|
|
19368
19440
|
if (addresses.some((entry2) => isBlockedResolvedAddress(entry2.address, current.hostname))) {
|
|
19369
19441
|
throw new Error("Blocked private or special network destination");
|
|
@@ -19375,7 +19447,13 @@ async function fetchPublic(initialUrl, options) {
|
|
|
19375
19447
|
continue;
|
|
19376
19448
|
}
|
|
19377
19449
|
const contentType = response2.contentType;
|
|
19378
|
-
|
|
19450
|
+
let decoder;
|
|
19451
|
+
try {
|
|
19452
|
+
decoder = new TextDecoder(charset(contentType), { fatal: false });
|
|
19453
|
+
} catch {
|
|
19454
|
+
decoder = new TextDecoder("utf-8");
|
|
19455
|
+
}
|
|
19456
|
+
const decoded = decoder.decode(response2.body);
|
|
19379
19457
|
const content = !options.rawHtml && /text\/html|application\/xhtml\+xml/i.test(contentType) ? htmlToText(decoded) : decoded;
|
|
19380
19458
|
return { url: current.href, status: response2.status, contentType, content, truncated: response2.truncated };
|
|
19381
19459
|
}
|
|
@@ -19410,8 +19488,8 @@ async function requestAddress(url, address, options) {
|
|
|
19410
19488
|
const status = response2.statusCode ?? 0;
|
|
19411
19489
|
const location = response2.headers.location;
|
|
19412
19490
|
if (location !== void 0 && [301, 302, 303, 307, 308].includes(status)) {
|
|
19413
|
-
response2.resume();
|
|
19414
19491
|
finish({ status, contentType: "", body: new Uint8Array(), truncated: false, redirect: location });
|
|
19492
|
+
response2.destroy();
|
|
19415
19493
|
return;
|
|
19416
19494
|
}
|
|
19417
19495
|
const chunks = [];
|
|
@@ -19427,7 +19505,11 @@ async function requestAddress(url, address, options) {
|
|
|
19427
19505
|
const accepted = chunk.subarray(0, options.maxBytes - bytes);
|
|
19428
19506
|
chunks.push(accepted);
|
|
19429
19507
|
bytes += accepted.length;
|
|
19430
|
-
if (accepted.length < chunk.length)
|
|
19508
|
+
if (accepted.length < chunk.length) {
|
|
19509
|
+
truncated = true;
|
|
19510
|
+
finish({ status, contentType: String(response2.headers["content-type"] ?? "application/octet-stream"), body: Buffer.concat(chunks), truncated });
|
|
19511
|
+
response2.destroy();
|
|
19512
|
+
}
|
|
19431
19513
|
});
|
|
19432
19514
|
response2.once("end", () => finish({
|
|
19433
19515
|
status,
|
|
@@ -19573,11 +19655,11 @@ function charset(contentType) {
|
|
|
19573
19655
|
}
|
|
19574
19656
|
|
|
19575
19657
|
// src/tools/lsp.ts
|
|
19576
|
-
import { spawn as
|
|
19658
|
+
import { spawn as spawn6 } from "child_process";
|
|
19577
19659
|
import { existsSync as existsSync5 } from "fs";
|
|
19578
19660
|
import { access as access2, constants as constants10, readFile as readFile21 } from "fs/promises";
|
|
19579
|
-
import { basename as basename7, dirname as
|
|
19580
|
-
import { fileURLToPath as
|
|
19661
|
+
import { basename as basename7, dirname as dirname18, extname as extname3, isAbsolute as isAbsolute14, relative as relative18, resolve as resolve26, sep as sep18 } from "path";
|
|
19662
|
+
import { fileURLToPath as fileURLToPath2, pathToFileURL as pathToFileURL3 } from "url";
|
|
19581
19663
|
import { z as z32 } from "zod";
|
|
19582
19664
|
var LSP_REQUEST_TIMEOUT_MS = 15e3;
|
|
19583
19665
|
var LspConnection = class {
|
|
@@ -19589,12 +19671,14 @@ var LspConnection = class {
|
|
|
19589
19671
|
#nextId = 1;
|
|
19590
19672
|
#disposed = false;
|
|
19591
19673
|
#initError;
|
|
19674
|
+
#shutdownPromise;
|
|
19592
19675
|
constructor(command, args, cwd) {
|
|
19593
19676
|
const invocation = prepareSpawnInvocation(command, args, { cwd });
|
|
19594
|
-
this.#process =
|
|
19677
|
+
this.#process = spawn6(invocation.command, invocation.args, {
|
|
19595
19678
|
cwd,
|
|
19596
19679
|
stdio: ["pipe", "pipe", "pipe"],
|
|
19597
19680
|
windowsHide: true,
|
|
19681
|
+
detached: process.platform !== "win32",
|
|
19598
19682
|
...invocation.windowsVerbatimArguments === void 0 ? {} : { windowsVerbatimArguments: invocation.windowsVerbatimArguments }
|
|
19599
19683
|
});
|
|
19600
19684
|
this.#process.on("error", (err) => {
|
|
@@ -19603,9 +19687,15 @@ var LspConnection = class {
|
|
|
19603
19687
|
});
|
|
19604
19688
|
this.#process.once("exit", (code, signal) => {
|
|
19605
19689
|
const reason = signal !== null ? `LSP server "${command}" exited with signal ${signal}` : `LSP server "${command}" exited with code ${code}`;
|
|
19606
|
-
this.#
|
|
19690
|
+
this.#initError = new Error(reason);
|
|
19691
|
+
this.#rejectAll(this.#initError);
|
|
19607
19692
|
});
|
|
19608
19693
|
this.#process.stdout.on("data", (chunk) => this.#buffer.feed(chunk, this.#onMessage));
|
|
19694
|
+
this.#process.stderr.resume();
|
|
19695
|
+
this.#process.stdin.on("error", (error) => {
|
|
19696
|
+
this.#initError = error;
|
|
19697
|
+
this.#rejectAll(error);
|
|
19698
|
+
});
|
|
19609
19699
|
}
|
|
19610
19700
|
async initialize(rootUri) {
|
|
19611
19701
|
if (this.#initError !== void 0) throw this.#initError;
|
|
@@ -19627,67 +19717,84 @@ var LspConnection = class {
|
|
|
19627
19717
|
if (initResult === void 0) throw new Error("LSP initialize returned no capabilities");
|
|
19628
19718
|
this.#notify("initialized", {});
|
|
19629
19719
|
}
|
|
19630
|
-
async findReferences(uri, line, character) {
|
|
19720
|
+
async findReferences(uri, line, character, signal) {
|
|
19721
|
+
signal?.throwIfAborted();
|
|
19631
19722
|
await this.#syncDocument(uri);
|
|
19632
19723
|
const result = await this.#request("textDocument/references", {
|
|
19633
19724
|
textDocument: { uri },
|
|
19634
19725
|
position: { line, character },
|
|
19635
19726
|
context: { includeDeclaration: false }
|
|
19636
|
-
});
|
|
19727
|
+
}, signal);
|
|
19637
19728
|
if (result === null || result === void 0) return [];
|
|
19638
19729
|
if (!Array.isArray(result)) return [];
|
|
19639
19730
|
return result.filter(isLocation);
|
|
19640
19731
|
}
|
|
19641
|
-
async hover(uri, line, character) {
|
|
19732
|
+
async hover(uri, line, character, signal) {
|
|
19733
|
+
signal?.throwIfAborted();
|
|
19642
19734
|
await this.#syncDocument(uri);
|
|
19643
19735
|
const result = await this.#request("textDocument/hover", {
|
|
19644
19736
|
textDocument: { uri },
|
|
19645
19737
|
position: { line, character }
|
|
19646
|
-
});
|
|
19738
|
+
}, signal);
|
|
19647
19739
|
if (result === null || result === void 0) return null;
|
|
19648
19740
|
if (typeof result === "object" && "contents" in result) {
|
|
19649
19741
|
return result;
|
|
19650
19742
|
}
|
|
19651
19743
|
return null;
|
|
19652
19744
|
}
|
|
19653
|
-
async diagnostics(uri) {
|
|
19745
|
+
async diagnostics(uri, signal) {
|
|
19746
|
+
signal?.throwIfAborted();
|
|
19654
19747
|
await this.#syncDocument(uri);
|
|
19655
19748
|
const result = await this.#request("textDocument/diagnostic", {
|
|
19656
19749
|
textDocument: { uri }
|
|
19657
|
-
});
|
|
19750
|
+
}, signal);
|
|
19658
19751
|
if (result === null || result === void 0) return [];
|
|
19659
19752
|
const items = result?.items ?? result;
|
|
19660
19753
|
if (!Array.isArray(items)) return [];
|
|
19661
19754
|
return items.filter(isDiagnostic);
|
|
19662
19755
|
}
|
|
19663
|
-
|
|
19664
|
-
if (this.#
|
|
19756
|
+
shutdown() {
|
|
19757
|
+
if (this.#shutdownPromise !== void 0) return this.#shutdownPromise;
|
|
19665
19758
|
this.#disposed = true;
|
|
19666
|
-
try {
|
|
19667
|
-
await this.#request("shutdown", null);
|
|
19668
|
-
} catch {
|
|
19669
|
-
}
|
|
19670
|
-
this.#notify("exit", null);
|
|
19671
|
-
this.#process.kill();
|
|
19672
19759
|
this.#rejectAll(new Error("LSP connection disposed"));
|
|
19760
|
+
this.#shutdownPromise = (async () => {
|
|
19761
|
+
await terminateProcessTree3(this.#process.pid);
|
|
19762
|
+
this.#process.stdin.destroy();
|
|
19763
|
+
this.#process.stdout.destroy();
|
|
19764
|
+
this.#process.stderr.destroy();
|
|
19765
|
+
})();
|
|
19766
|
+
return this.#shutdownPromise;
|
|
19673
19767
|
}
|
|
19674
19768
|
// -- JSON-RPC core -------------------------------------------------------
|
|
19675
|
-
async #request(method, params) {
|
|
19769
|
+
async #request(method, params, signal) {
|
|
19676
19770
|
if (this.#disposed) throw new Error("LSP connection disposed");
|
|
19771
|
+
if (this.#initError !== void 0) throw this.#initError;
|
|
19772
|
+
signal?.throwIfAborted();
|
|
19677
19773
|
const id = this.#nextId++;
|
|
19678
19774
|
const request = { jsonrpc: "2.0", id, method, params };
|
|
19679
|
-
|
|
19680
|
-
|
|
19681
|
-
|
|
19682
|
-
|
|
19683
|
-
|
|
19684
|
-
|
|
19685
|
-
|
|
19775
|
+
let timer;
|
|
19776
|
+
let onAbort;
|
|
19777
|
+
const promise = new Promise((resolve44, reject) => {
|
|
19778
|
+
this.#pending.set(id, { resolve: resolve44, reject });
|
|
19779
|
+
timer = setTimeout(() => reject(new Error(`LSP request "${method}" timed out after ${LSP_REQUEST_TIMEOUT_MS}ms`)), LSP_REQUEST_TIMEOUT_MS);
|
|
19780
|
+
timer.unref();
|
|
19781
|
+
onAbort = () => {
|
|
19782
|
+
try {
|
|
19783
|
+
this.#notify("$/cancelRequest", { id });
|
|
19784
|
+
} catch {
|
|
19686
19785
|
}
|
|
19687
|
-
|
|
19786
|
+
reject(signal?.reason);
|
|
19787
|
+
};
|
|
19788
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
19688
19789
|
});
|
|
19689
|
-
|
|
19690
|
-
|
|
19790
|
+
try {
|
|
19791
|
+
this.#send(request);
|
|
19792
|
+
return await promise;
|
|
19793
|
+
} finally {
|
|
19794
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
19795
|
+
if (onAbort !== void 0) signal?.removeEventListener("abort", onAbort);
|
|
19796
|
+
this.#pending.delete(id);
|
|
19797
|
+
}
|
|
19691
19798
|
}
|
|
19692
19799
|
#notify(method, params) {
|
|
19693
19800
|
const notification = { jsonrpc: "2.0", method, params };
|
|
@@ -19707,6 +19814,7 @@ var LspConnection = class {
|
|
|
19707
19814
|
} catch {
|
|
19708
19815
|
return;
|
|
19709
19816
|
}
|
|
19817
|
+
if (typeof parsed !== "object" || parsed === null) return;
|
|
19710
19818
|
if ("method" in parsed) {
|
|
19711
19819
|
if ("id" in parsed) this.#handleServerRequest(parsed);
|
|
19712
19820
|
return;
|
|
@@ -19742,7 +19850,7 @@ var LspConnection = class {
|
|
|
19742
19850
|
async #syncDocumentNow(uri) {
|
|
19743
19851
|
const url = new URL(uri);
|
|
19744
19852
|
if (url.protocol !== "file:") return;
|
|
19745
|
-
const filePath =
|
|
19853
|
+
const filePath = fileURLToPath2(url);
|
|
19746
19854
|
const text = await readFile21(filePath, "utf8");
|
|
19747
19855
|
const current = this.#documents.get(uri);
|
|
19748
19856
|
if (current === void 0) {
|
|
@@ -19795,7 +19903,7 @@ var LspBuffer = class {
|
|
|
19795
19903
|
}
|
|
19796
19904
|
};
|
|
19797
19905
|
var TYPESCRIPT_CLI = resolve26(
|
|
19798
|
-
|
|
19906
|
+
dirname18(fileURLToPath2(import.meta.resolve("typescript/package.json"))),
|
|
19799
19907
|
"lib",
|
|
19800
19908
|
"tsc.js"
|
|
19801
19909
|
);
|
|
@@ -19830,6 +19938,8 @@ var KNOWN_SERVERS = [
|
|
|
19830
19938
|
}
|
|
19831
19939
|
];
|
|
19832
19940
|
var RealLspManager = class {
|
|
19941
|
+
#disposed = false;
|
|
19942
|
+
#disposal;
|
|
19833
19943
|
#workspace;
|
|
19834
19944
|
#connections = /* @__PURE__ */ new Map();
|
|
19835
19945
|
#pendingStarts = /* @__PURE__ */ new Map();
|
|
@@ -19841,27 +19951,32 @@ var RealLspManager = class {
|
|
|
19841
19951
|
this.#onStatus = options.onStatus;
|
|
19842
19952
|
}
|
|
19843
19953
|
dispose() {
|
|
19844
|
-
|
|
19845
|
-
|
|
19846
|
-
|
|
19847
|
-
|
|
19954
|
+
if (this.#disposal !== void 0) return this.#disposal;
|
|
19955
|
+
this.#disposed = true;
|
|
19956
|
+
const shutdowns = [...this.#connections.values()].map((connection) => connection.shutdown());
|
|
19957
|
+
shutdowns.push(...[...this.#pendingStarts.values()].map(async (start) => {
|
|
19958
|
+
const connection = await start;
|
|
19959
|
+
await connection.shutdown();
|
|
19960
|
+
}));
|
|
19848
19961
|
this.#connections.clear();
|
|
19849
19962
|
this.#pendingStarts.clear();
|
|
19963
|
+
this.#disposal = Promise.allSettled(shutdowns).then(() => void 0);
|
|
19964
|
+
return this.#disposal;
|
|
19850
19965
|
}
|
|
19851
|
-
async findReferences(uri, line, character) {
|
|
19852
|
-
|
|
19853
|
-
|
|
19854
|
-
return connection.findReferences(uri, line, character);
|
|
19966
|
+
async findReferences(uri, line, character, signal) {
|
|
19967
|
+
signal?.throwIfAborted();
|
|
19968
|
+
const connection = await waitWithSignal(this.#getConnection(uri), signal);
|
|
19969
|
+
return connection.findReferences(uri, line, character, signal);
|
|
19855
19970
|
}
|
|
19856
|
-
async hover(uri, line, character) {
|
|
19857
|
-
|
|
19858
|
-
|
|
19859
|
-
return connection.hover(uri, line, character);
|
|
19971
|
+
async hover(uri, line, character, signal) {
|
|
19972
|
+
signal?.throwIfAborted();
|
|
19973
|
+
const connection = await waitWithSignal(this.#getConnection(uri), signal);
|
|
19974
|
+
return connection.hover(uri, line, character, signal);
|
|
19860
19975
|
}
|
|
19861
|
-
async diagnostics(uri) {
|
|
19862
|
-
|
|
19863
|
-
|
|
19864
|
-
return connection.diagnostics(uri);
|
|
19976
|
+
async diagnostics(uri, signal) {
|
|
19977
|
+
signal?.throwIfAborted();
|
|
19978
|
+
const connection = await waitWithSignal(this.#getConnection(uri), signal);
|
|
19979
|
+
return connection.diagnostics(uri, signal);
|
|
19865
19980
|
}
|
|
19866
19981
|
#languageForUri(uri) {
|
|
19867
19982
|
try {
|
|
@@ -19877,20 +19992,25 @@ var RealLspManager = class {
|
|
|
19877
19992
|
return void 0;
|
|
19878
19993
|
}
|
|
19879
19994
|
async #getConnection(uri) {
|
|
19995
|
+
if (this.#disposed) throw new Error("LSP manager is disposed");
|
|
19880
19996
|
const language = this.#languageForUri(uri);
|
|
19881
|
-
if (language === void 0)
|
|
19997
|
+
if (language === void 0) throw new Error(`No language server configured for ${uri}; use Read/Grep or the project compiler`);
|
|
19882
19998
|
const existing = this.#connections.get(language);
|
|
19883
19999
|
if (existing !== void 0) return existing;
|
|
19884
20000
|
const pending2 = this.#pendingStarts.get(language);
|
|
19885
20001
|
if (pending2 !== void 0) return pending2;
|
|
19886
20002
|
const config = this.#serverConfigs.find((c) => c.language === language);
|
|
19887
|
-
if (config === void 0)
|
|
20003
|
+
if (config === void 0) throw new Error(`No language server configured for ${language}`);
|
|
19888
20004
|
const hasRoot = config.rootFiles.some((file) => existsSync5(resolve26(this.#workspace, file)));
|
|
19889
|
-
if (!hasRoot)
|
|
20005
|
+
if (!hasRoot) throw new Error(`Cannot start ${language} language server: workspace needs ${config.rootFiles.join(" or ")}`);
|
|
19890
20006
|
const startPromise = this.#startServer(config);
|
|
19891
20007
|
this.#pendingStarts.set(language, startPromise);
|
|
19892
20008
|
try {
|
|
19893
20009
|
const connection = await startPromise;
|
|
20010
|
+
if (this.#disposed) {
|
|
20011
|
+
await connection.shutdown();
|
|
20012
|
+
throw new Error("LSP manager is disposed");
|
|
20013
|
+
}
|
|
19894
20014
|
this.#connections.set(language, connection);
|
|
19895
20015
|
return connection;
|
|
19896
20016
|
} finally {
|
|
@@ -19908,6 +20028,7 @@ var RealLspManager = class {
|
|
|
19908
20028
|
await connection.initialize(rootUri);
|
|
19909
20029
|
this.#onStatus?.(`${config.language} Language Server ready (${config.command})`);
|
|
19910
20030
|
} catch (error) {
|
|
20031
|
+
await connection.shutdown();
|
|
19911
20032
|
this.#onStatus?.(`${config.language} Language Server failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
19912
20033
|
throw error;
|
|
19913
20034
|
}
|
|
@@ -19916,13 +20037,13 @@ var RealLspManager = class {
|
|
|
19916
20037
|
};
|
|
19917
20038
|
var LspFindRefsInput = z32.object({
|
|
19918
20039
|
file: z32.string().min(1),
|
|
19919
|
-
line: z32.coerce.number().int().nonnegative(),
|
|
19920
|
-
character: z32.coerce.number().int().nonnegative()
|
|
20040
|
+
line: z32.coerce.number().int().nonnegative().describe("Zero-based line number (first line is 0)."),
|
|
20041
|
+
character: z32.coerce.number().int().nonnegative().describe("Zero-based UTF-16 character offset.")
|
|
19921
20042
|
});
|
|
19922
20043
|
var LspHoverInput = z32.object({
|
|
19923
20044
|
file: z32.string().min(1),
|
|
19924
|
-
line: z32.coerce.number().int().nonnegative(),
|
|
19925
|
-
character: z32.coerce.number().int().nonnegative()
|
|
20045
|
+
line: z32.coerce.number().int().nonnegative().describe("Zero-based line number (first line is 0)."),
|
|
20046
|
+
character: z32.coerce.number().int().nonnegative().describe("Zero-based UTF-16 character offset.")
|
|
19926
20047
|
});
|
|
19927
20048
|
var LspDiagnosticsInput = z32.object({
|
|
19928
20049
|
file: z32.string().min(1)
|
|
@@ -19948,10 +20069,11 @@ function createLspTools(workspace, options = {}) {
|
|
|
19948
20069
|
inputSchema: LspFindRefsInput,
|
|
19949
20070
|
paths: (input) => [guard(input.file)],
|
|
19950
20071
|
summarize: (input) => `${basename7(input.file)}:${input.line}:${input.character}${langTag(input.file)}`,
|
|
19951
|
-
execute: async (input,
|
|
20072
|
+
execute: async (input, signal) => {
|
|
20073
|
+
signal.throwIfAborted();
|
|
19952
20074
|
const path = guard(input.file);
|
|
19953
20075
|
const uri = toUri(path);
|
|
19954
|
-
const refs = await manager.findReferences(uri, input.line, input.character);
|
|
20076
|
+
const refs = await waitWithSignal(manager.findReferences(uri, input.line, input.character, signal), signal);
|
|
19955
20077
|
if (refs.length === 0) return `No references found at ${input.file}:${input.line}:${input.character}`;
|
|
19956
20078
|
return refs.map((loc) => {
|
|
19957
20079
|
const filePath = uriToPath(loc.uri);
|
|
@@ -19965,12 +20087,14 @@ function createLspTools(workspace, options = {}) {
|
|
|
19965
20087
|
inputSchema: LspHoverInput,
|
|
19966
20088
|
paths: (input) => [guard(input.file)],
|
|
19967
20089
|
summarize: (input) => `${basename7(input.file)}:${input.line}:${input.character}${langTag(input.file)}`,
|
|
19968
|
-
execute: async (input,
|
|
20090
|
+
execute: async (input, signal) => {
|
|
20091
|
+
signal.throwIfAborted();
|
|
19969
20092
|
const path = guard(input.file);
|
|
19970
20093
|
const uri = toUri(path);
|
|
19971
|
-
const info = await manager.hover(uri, input.line, input.character);
|
|
20094
|
+
const info = await waitWithSignal(manager.hover(uri, input.line, input.character, signal), signal);
|
|
19972
20095
|
if (info === null) return `No hover information at ${input.file}:${input.line}:${input.character}`;
|
|
19973
|
-
const
|
|
20096
|
+
const parts = Array.isArray(info.contents) ? info.contents : [info.contents];
|
|
20097
|
+
const contents = parts.map((part) => typeof part === "string" ? part : part.value).join("\n\n");
|
|
19974
20098
|
return contents;
|
|
19975
20099
|
}
|
|
19976
20100
|
};
|
|
@@ -19980,10 +20104,11 @@ function createLspTools(workspace, options = {}) {
|
|
|
19980
20104
|
inputSchema: LspDiagnosticsInput,
|
|
19981
20105
|
paths: (input) => [guard(input.file)],
|
|
19982
20106
|
summarize: (input) => `${basename7(input.file)}${langTag(input.file)}`,
|
|
19983
|
-
execute: async (input,
|
|
20107
|
+
execute: async (input, signal) => {
|
|
20108
|
+
signal.throwIfAborted();
|
|
19984
20109
|
const path = guard(input.file);
|
|
19985
20110
|
const uri = toUri(path);
|
|
19986
|
-
const diags = await manager.diagnostics(uri);
|
|
20111
|
+
const diags = await waitWithSignal(manager.diagnostics(uri, signal), signal);
|
|
19987
20112
|
if (diags.length === 0) return `No diagnostics for ${input.file}`;
|
|
19988
20113
|
return diags.map((d) => {
|
|
19989
20114
|
const severity = severityLabel(d.severity);
|
|
@@ -20002,6 +20127,14 @@ function languageIdForPath(filePath) {
|
|
|
20002
20127
|
return "typescript";
|
|
20003
20128
|
case ".cts":
|
|
20004
20129
|
return "typescript";
|
|
20130
|
+
case ".py":
|
|
20131
|
+
case ".pyi":
|
|
20132
|
+
case ".pyx":
|
|
20133
|
+
return "python";
|
|
20134
|
+
case ".rs":
|
|
20135
|
+
return "rust";
|
|
20136
|
+
case ".go":
|
|
20137
|
+
return "go";
|
|
20005
20138
|
default:
|
|
20006
20139
|
return "typescript";
|
|
20007
20140
|
}
|
|
@@ -20013,7 +20146,7 @@ function isWithin7(root, candidate) {
|
|
|
20013
20146
|
function uriToPath(uri) {
|
|
20014
20147
|
try {
|
|
20015
20148
|
const url = new URL(uri);
|
|
20016
|
-
if (url.protocol === "file:") return
|
|
20149
|
+
if (url.protocol === "file:") return fileURLToPath2(url);
|
|
20017
20150
|
} catch {
|
|
20018
20151
|
}
|
|
20019
20152
|
return uri;
|
|
@@ -20040,12 +20173,14 @@ function severityLabel(severity) {
|
|
|
20040
20173
|
}
|
|
20041
20174
|
|
|
20042
20175
|
// src/git/service.ts
|
|
20043
|
-
async function git2(workspace, args, timeoutMs = 3e4) {
|
|
20044
|
-
|
|
20176
|
+
async function git2(workspace, args, timeoutMs = 3e4, signal) {
|
|
20177
|
+
signal?.throwIfAborted();
|
|
20178
|
+
const result = await execFileNoThrow("git", ["-C", workspace, ...args], { timeout: timeoutMs, useCwd: false, ...signal === void 0 ? {} : { signal } });
|
|
20179
|
+
signal?.throwIfAborted();
|
|
20045
20180
|
return { ok: result.code === 0, stdout: result.stdout, stderr: result.stderr, code: result.code };
|
|
20046
20181
|
}
|
|
20047
|
-
async function isGitRepository(workspace) {
|
|
20048
|
-
const result = await git2(workspace, ["rev-parse", "--is-inside-work-tree"], 2e3);
|
|
20182
|
+
async function isGitRepository(workspace, signal) {
|
|
20183
|
+
const result = await git2(workspace, ["rev-parse", "--is-inside-work-tree"], 2e3, signal);
|
|
20049
20184
|
return result.ok && result.stdout.trim() === "true";
|
|
20050
20185
|
}
|
|
20051
20186
|
function gitFailure(result, action) {
|
|
@@ -20123,16 +20258,21 @@ async function commit(workspace, commitMessage) {
|
|
|
20123
20258
|
const head = await git2(workspace, ["log", "-1", "--pretty=%h %s"]);
|
|
20124
20259
|
return head.stdout.trim() || "committed";
|
|
20125
20260
|
}
|
|
20126
|
-
async function fileHistory(workspace, path, limit) {
|
|
20261
|
+
async function fileHistory(workspace, path, limit, signal) {
|
|
20127
20262
|
const args = [
|
|
20263
|
+
"--literal-pathspecs",
|
|
20128
20264
|
"log",
|
|
20129
20265
|
`--max-count=${limit}`,
|
|
20130
20266
|
"--date=short",
|
|
20131
20267
|
"--pretty=format:%h%x1f%ad%x1f%an%x1f%s",
|
|
20132
20268
|
...path === void 0 ? [] : ["--follow", "--", path]
|
|
20133
20269
|
];
|
|
20134
|
-
const result = await git2(workspace, args, 15e3);
|
|
20135
|
-
if (!result.ok)
|
|
20270
|
+
const result = await git2(workspace, args, 15e3, signal);
|
|
20271
|
+
if (!result.ok) {
|
|
20272
|
+
const head = await git2(workspace, ["rev-parse", "--verify", "HEAD"], 2e3, signal);
|
|
20273
|
+
if (!head.ok && await isGitRepository(workspace, signal)) return [];
|
|
20274
|
+
throw gitFailure(result, "log");
|
|
20275
|
+
}
|
|
20136
20276
|
return result.stdout.split("\n").filter((line) => line.trim().length > 0).map((line) => {
|
|
20137
20277
|
const [hash = "", date = "", author = "", ...subject] = line.split("");
|
|
20138
20278
|
return { hash, date, author, subject: subject.join("") };
|
|
@@ -20471,24 +20611,33 @@ function mapNode(value) {
|
|
|
20471
20611
|
|
|
20472
20612
|
// src/git/tools.ts
|
|
20473
20613
|
import { z as z34 } from "zod";
|
|
20614
|
+
import { isAbsolute as isAbsolute15, relative as relative19, resolve as resolve28, sep as sep19 } from "path";
|
|
20474
20615
|
var GitHistoryInput = z34.object({
|
|
20475
|
-
path: z34.string().describe("
|
|
20476
|
-
limit: z34.number().int().min(1).max(50).describe("Maximum number of commits to return (default 20)").optional()
|
|
20616
|
+
path: z34.string().min(1).describe("Workspace-relative or absolute file path; omit for the whole repository history").optional(),
|
|
20617
|
+
limit: z34.coerce.number().int().min(1).max(50).describe("Maximum number of commits to return (default 20)").optional()
|
|
20477
20618
|
});
|
|
20478
20619
|
function createGitHistoryTool(workspace) {
|
|
20620
|
+
const root = resolve28(workspace);
|
|
20621
|
+
const file = (path) => {
|
|
20622
|
+
const absolute = resolve28(root, path);
|
|
20623
|
+
const delta = relative19(root, absolute);
|
|
20624
|
+
if (delta === ".." || delta.startsWith(`..${sep19}`) || isAbsolute15(delta)) throw new Error("GitHistory path is outside the workspace");
|
|
20625
|
+
return absolute;
|
|
20626
|
+
};
|
|
20479
20627
|
return {
|
|
20480
20628
|
name: "GitHistory",
|
|
20481
20629
|
description: "Show git commit history for the repository or for one file (follows renames). Use to understand when and why code changed.",
|
|
20482
20630
|
inputSchema: GitHistoryInput,
|
|
20483
20631
|
readOnly: true,
|
|
20484
|
-
paths: (input) => input.path === void 0 ? [] : [input.path],
|
|
20632
|
+
paths: (input) => input.path === void 0 ? [root] : [file(input.path)],
|
|
20485
20633
|
summarize: (input) => input.path ?? "repository",
|
|
20486
20634
|
renderForModel: (output) => output,
|
|
20487
20635
|
async execute(input, signal) {
|
|
20488
20636
|
signal.throwIfAborted();
|
|
20489
|
-
|
|
20637
|
+
const path = input.path === void 0 ? void 0 : relative19(root, file(input.path));
|
|
20638
|
+
if (!await isGitRepository(root, signal)) throw new Error("Not a git repository");
|
|
20490
20639
|
const limit = input.limit ?? 20;
|
|
20491
|
-
const entries = await fileHistory(
|
|
20640
|
+
const entries = await fileHistory(root, path, limit, signal);
|
|
20492
20641
|
if (entries.length === 0) {
|
|
20493
20642
|
return input.path === void 0 ? "No commits found." : `No commits found for ${input.path} (the file may be untracked).`;
|
|
20494
20643
|
}
|
|
@@ -20551,7 +20700,8 @@ var QuestionBridge = class {
|
|
|
20551
20700
|
}
|
|
20552
20701
|
async #askRelayed(questions, signal) {
|
|
20553
20702
|
try {
|
|
20554
|
-
const answers = await this.#relay(questions, signal);
|
|
20703
|
+
const answers = await waitWithSignal(this.#relay(questions, signal), signal);
|
|
20704
|
+
signal.throwIfAborted();
|
|
20555
20705
|
if (answers !== void 0) {
|
|
20556
20706
|
this.#active = false;
|
|
20557
20707
|
return answers;
|
|
@@ -20563,10 +20713,15 @@ var QuestionBridge = class {
|
|
|
20563
20713
|
}
|
|
20564
20714
|
}
|
|
20565
20715
|
#askLocally(questions, signal) {
|
|
20566
|
-
|
|
20716
|
+
if (signal.aborted) {
|
|
20717
|
+
this.#active = false;
|
|
20718
|
+
return Promise.reject(signal.reason);
|
|
20719
|
+
}
|
|
20720
|
+
return new Promise((resolve44, reject) => {
|
|
20567
20721
|
const onAbort = () => {
|
|
20568
20722
|
this.#pending = void 0;
|
|
20569
20723
|
this.#active = false;
|
|
20724
|
+
this.#onChange?.();
|
|
20570
20725
|
reject(signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason)));
|
|
20571
20726
|
};
|
|
20572
20727
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
@@ -20577,7 +20732,7 @@ var QuestionBridge = class {
|
|
|
20577
20732
|
this.#pending = void 0;
|
|
20578
20733
|
this.#active = false;
|
|
20579
20734
|
this.#onChange?.();
|
|
20580
|
-
|
|
20735
|
+
resolve44(answers);
|
|
20581
20736
|
},
|
|
20582
20737
|
reject: (error) => {
|
|
20583
20738
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -20670,7 +20825,7 @@ function createTodoWriteTool() {
|
|
|
20670
20825
|
// src/tools/managed.ts
|
|
20671
20826
|
import { randomUUID as randomUUID18 } from "crypto";
|
|
20672
20827
|
import { link, lstat as lstat11, mkdir as mkdir20, readFile as readFile22, readdir as readdir9, unlink as unlink3, writeFile as writeFile12 } from "fs/promises";
|
|
20673
|
-
import { dirname as
|
|
20828
|
+
import { dirname as dirname19, join as join30, resolve as resolve29 } from "path";
|
|
20674
20829
|
import { z as z37 } from "zod";
|
|
20675
20830
|
var MANAGED_TOOL_VERSION = 1;
|
|
20676
20831
|
var MAX_RECORD_BYTES = 256 * 1024;
|
|
@@ -20712,9 +20867,9 @@ var ManagedToolStore = class {
|
|
|
20712
20867
|
#entries = /* @__PURE__ */ new Map();
|
|
20713
20868
|
#diagnostics = [];
|
|
20714
20869
|
constructor(options) {
|
|
20715
|
-
this.#workspace =
|
|
20870
|
+
this.#workspace = resolve29(options.workspace);
|
|
20716
20871
|
this.#projectRoot = join30(this.#workspace, ".flavor", "tools");
|
|
20717
|
-
this.#globalRoot = join30(
|
|
20872
|
+
this.#globalRoot = join30(resolve29(options.home), ".flavor-code", "tools");
|
|
20718
20873
|
this.#now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
20719
20874
|
}
|
|
20720
20875
|
get diagnostics() {
|
|
@@ -20954,7 +21109,7 @@ ${sourceUrl}`
|
|
|
20954
21109
|
}
|
|
20955
21110
|
}
|
|
20956
21111
|
async function writeExclusive(path, content) {
|
|
20957
|
-
await mkdir20(
|
|
21112
|
+
await mkdir20(dirname19(path), { recursive: true });
|
|
20958
21113
|
const temporary = `${path}.${randomUUID18()}.tmp`;
|
|
20959
21114
|
try {
|
|
20960
21115
|
await writeFile12(temporary, content, { encoding: "utf8", flag: "wx", mode: 384 });
|
|
@@ -22268,9 +22423,9 @@ Address this now (for example run the required verification or fix), then finish
|
|
|
22268
22423
|
}
|
|
22269
22424
|
#waitForPlanningIdle(key) {
|
|
22270
22425
|
if (!this.#activePlanning.has(key)) return Promise.resolve();
|
|
22271
|
-
return new Promise((
|
|
22426
|
+
return new Promise((resolve44) => {
|
|
22272
22427
|
const check2 = () => {
|
|
22273
|
-
if (!this.#activePlanning.has(key))
|
|
22428
|
+
if (!this.#activePlanning.has(key)) resolve44();
|
|
22274
22429
|
else this.#planningIdleWaiters.add(check2);
|
|
22275
22430
|
};
|
|
22276
22431
|
this.#planningIdleWaiters.add(check2);
|
|
@@ -22381,7 +22536,7 @@ function requiredPals(service) {
|
|
|
22381
22536
|
import { constants as constants11 } from "fs";
|
|
22382
22537
|
import { access as access3, lstat as lstat12, readFile as readFile23, readdir as readdir10, stat as stat11 } from "fs/promises";
|
|
22383
22538
|
import { homedir } from "os";
|
|
22384
|
-
import { isAbsolute as
|
|
22539
|
+
import { isAbsolute as isAbsolute16, join as join31, relative as relative20, resolve as resolve30, sep as sep20 } from "path";
|
|
22385
22540
|
import { rgPath as bundledRgPath2 } from "@vscode/ripgrep";
|
|
22386
22541
|
|
|
22387
22542
|
// src/utils/semver.ts
|
|
@@ -22398,8 +22553,8 @@ async function fetchLatestVersion(options = {}) {
|
|
|
22398
22553
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
22399
22554
|
const fetchImpl = options.fetchImpl ?? ((url, init) => fetch(url, init));
|
|
22400
22555
|
let timer;
|
|
22401
|
-
const timeout = new Promise((
|
|
22402
|
-
timer = setTimeout(() =>
|
|
22556
|
+
const timeout = new Promise((resolve44) => {
|
|
22557
|
+
timer = setTimeout(() => resolve44(void 0), timeoutMs);
|
|
22403
22558
|
timer.unref?.();
|
|
22404
22559
|
});
|
|
22405
22560
|
try {
|
|
@@ -22419,8 +22574,8 @@ async function fetchLatestVersion(options = {}) {
|
|
|
22419
22574
|
|
|
22420
22575
|
// src/doctor.ts
|
|
22421
22576
|
async function runDoctor(options = {}, dependencies2 = {}) {
|
|
22422
|
-
const workspace =
|
|
22423
|
-
const home =
|
|
22577
|
+
const workspace = resolve30(options.workspace ?? process.cwd());
|
|
22578
|
+
const home = resolve30(options.home ?? homedir());
|
|
22424
22579
|
const environment = options.environment ?? process.env;
|
|
22425
22580
|
const platform = options.platform ?? process.platform;
|
|
22426
22581
|
const arch = options.arch ?? process.arch;
|
|
@@ -22568,14 +22723,14 @@ async function pluginCheck(directories) {
|
|
|
22568
22723
|
}
|
|
22569
22724
|
for (const entry2 of entries) {
|
|
22570
22725
|
if (!entry2.isDirectory() || entry2.name.startsWith(".")) continue;
|
|
22571
|
-
const root =
|
|
22726
|
+
const root = resolve30(directory, entry2.name);
|
|
22572
22727
|
try {
|
|
22573
22728
|
const rootInfo = await lstat12(root);
|
|
22574
22729
|
if (rootInfo.isSymbolicLink()) throw new Error("plugin directory must not be a symbolic link");
|
|
22575
22730
|
const manifest = PluginManifestSchema.parse(JSON.parse(await readFile23(join31(root, "flavor-plugin.json"), "utf8")));
|
|
22576
|
-
const main =
|
|
22577
|
-
const delta =
|
|
22578
|
-
if (delta === ".." || delta.startsWith(`..${
|
|
22731
|
+
const main = resolve30(root, manifest.main);
|
|
22732
|
+
const delta = relative20(root, main);
|
|
22733
|
+
if (delta === ".." || delta.startsWith(`..${sep20}`) || isAbsolute16(delta)) throw new Error("plugin entry escapes its root");
|
|
22579
22734
|
const mainInfo = await lstat12(main);
|
|
22580
22735
|
if (!mainInfo.isFile() || mainInfo.isSymbolicLink()) throw new Error("plugin entry must be a regular file");
|
|
22581
22736
|
valid += 1;
|
|
@@ -22639,7 +22794,7 @@ var LANG_LABELS = /* @__PURE__ */ new Map([
|
|
|
22639
22794
|
]);
|
|
22640
22795
|
|
|
22641
22796
|
// src/utils/crash-guard.ts
|
|
22642
|
-
import { mkdirSync as
|
|
22797
|
+
import { mkdirSync as mkdirSync2, writeFileSync } from "fs";
|
|
22643
22798
|
import { join as join32 } from "path";
|
|
22644
22799
|
var installed = false;
|
|
22645
22800
|
var crashing = false;
|
|
@@ -22679,7 +22834,7 @@ function installCrashGuard(options = {}) {
|
|
|
22679
22834
|
const writeCrashLog = (kind, detail) => {
|
|
22680
22835
|
try {
|
|
22681
22836
|
const dir = crashLogDir();
|
|
22682
|
-
|
|
22837
|
+
mkdirSync2(dir, { recursive: true });
|
|
22683
22838
|
const path = join32(dir, `crash-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "")}-${process.pid}.log`);
|
|
22684
22839
|
writeFileSync(path, [
|
|
22685
22840
|
`kind: ${kind}`,
|
|
@@ -23452,7 +23607,7 @@ ${total}`;
|
|
|
23452
23607
|
// src/memory/store.ts
|
|
23453
23608
|
import { createHash as createHash17 } from "crypto";
|
|
23454
23609
|
import { readFile as readFile24, rm as rm12 } from "fs/promises";
|
|
23455
|
-
import { resolve as
|
|
23610
|
+
import { resolve as resolve31, join as join33, relative as relative21, sep as sep21 } from "path";
|
|
23456
23611
|
|
|
23457
23612
|
// src/memory/similarity.ts
|
|
23458
23613
|
function normalizeForSimilarity(value) {
|
|
@@ -23554,7 +23709,7 @@ var MemoryStore = class {
|
|
|
23554
23709
|
constructor(options) {
|
|
23555
23710
|
if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1) throw new Error("maxEntries must be an integer of at least 1");
|
|
23556
23711
|
if (!Number.isSafeInteger(options.maxEntryChars) || options.maxEntryChars < 1) throw new Error("maxEntryChars must be an integer of at least 1");
|
|
23557
|
-
this.workspace =
|
|
23712
|
+
this.workspace = resolve31(options.workspace);
|
|
23558
23713
|
this.#memoryRoot = join33(this.workspace, ".flavor", "memory");
|
|
23559
23714
|
this.path = join33(this.#memoryRoot, "MEMORY.md");
|
|
23560
23715
|
this.#behaviorPath = join33(this.#memoryRoot, "behavior.json");
|
|
@@ -23780,9 +23935,9 @@ var MemoryStore = class {
|
|
|
23780
23935
|
}
|
|
23781
23936
|
#resolveContentPath(contentPath) {
|
|
23782
23937
|
if (!/^tasks\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}\.md$/.test(contentPath)) throw new Error("Invalid memory content path");
|
|
23783
|
-
const path =
|
|
23784
|
-
const rel =
|
|
23785
|
-
if (rel === ".." || rel.startsWith(`..${
|
|
23938
|
+
const path = resolve31(this.#memoryRoot, ...contentPath.split("/"));
|
|
23939
|
+
const rel = relative21(this.#memoryRoot, path);
|
|
23940
|
+
if (rel === ".." || rel.startsWith(`..${sep21}`)) throw new Error("Memory content path escapes the memory directory");
|
|
23786
23941
|
return path;
|
|
23787
23942
|
}
|
|
23788
23943
|
async #updateIndex(update) {
|
|
@@ -24239,14 +24394,14 @@ var MemoryReviewBridge = class {
|
|
|
24239
24394
|
};
|
|
24240
24395
|
|
|
24241
24396
|
// src/execution/docker.ts
|
|
24242
|
-
import { spawn as
|
|
24243
|
-
import { relative as
|
|
24397
|
+
import { spawn as spawn7 } from "child_process";
|
|
24398
|
+
import { relative as relative22, resolve as resolve32, sep as sep22, win32 } from "path";
|
|
24244
24399
|
function buildDockerInvocation(options) {
|
|
24245
24400
|
const workspace = options.workspace;
|
|
24246
24401
|
const cwd = options.request.cwd;
|
|
24247
24402
|
const windows = /^[A-Za-z]:[\\/]/.test(workspace);
|
|
24248
|
-
const delta = windows ? win32.relative(workspace, cwd) :
|
|
24249
|
-
if (delta === ".." || delta.startsWith(`..${windows ? "\\" :
|
|
24403
|
+
const delta = windows ? win32.relative(workspace, cwd) : relative22(resolve32(workspace), resolve32(cwd));
|
|
24404
|
+
if (delta === ".." || delta.startsWith(`..${windows ? "\\" : sep22}`)) throw new Error("Docker cwd escapes workspace");
|
|
24250
24405
|
const containerCwd = delta.length === 0 ? "/workspace" : `/workspace/${delta.replaceAll("\\", "/")}`;
|
|
24251
24406
|
const args = [
|
|
24252
24407
|
"run",
|
|
@@ -24298,7 +24453,7 @@ function runDockerInvocation(invocation, signal, timeoutMs, maxOutputBytes = DEF
|
|
|
24298
24453
|
resolvePromise({ exitCode: null, signal: null, stdout: "", stderr: "", terminationReason: "cancelled" });
|
|
24299
24454
|
return;
|
|
24300
24455
|
}
|
|
24301
|
-
const child =
|
|
24456
|
+
const child = spawn7(invocation.command, invocation.args, { windowsHide: true, shell: false });
|
|
24302
24457
|
const stdout = new BoundedUtf8Output(maxOutputBytes);
|
|
24303
24458
|
const stderr = new BoundedUtf8Output(maxOutputBytes);
|
|
24304
24459
|
let reason = null;
|
|
@@ -24416,7 +24571,7 @@ function createExecutionEnvironment(workspace, config) {
|
|
|
24416
24571
|
|
|
24417
24572
|
// src/ide/client.ts
|
|
24418
24573
|
import { readFile as readFile25, readdir as readdir11, stat as stat12 } from "fs/promises";
|
|
24419
|
-
import { basename as basename8, join as join34, relative as
|
|
24574
|
+
import { basename as basename8, join as join34, relative as relative23, resolve as resolve33, sep as sep23 } from "path";
|
|
24420
24575
|
var FlavorIdeClient = class {
|
|
24421
24576
|
#workspace;
|
|
24422
24577
|
#home;
|
|
@@ -24427,8 +24582,8 @@ var FlavorIdeClient = class {
|
|
|
24427
24582
|
#sessionId;
|
|
24428
24583
|
#eventTail = Promise.resolve();
|
|
24429
24584
|
constructor(options) {
|
|
24430
|
-
this.#workspace =
|
|
24431
|
-
this.#home =
|
|
24585
|
+
this.#workspace = resolve33(options.workspace);
|
|
24586
|
+
this.#home = resolve33(options.home);
|
|
24432
24587
|
this.#environment = options.environment ?? process.env;
|
|
24433
24588
|
this.#fetch = options.fetch ?? fetch;
|
|
24434
24589
|
}
|
|
@@ -24626,16 +24781,16 @@ function parsePort(value) {
|
|
|
24626
24781
|
return Number.isInteger(port2) && port2 > 0 && port2 <= 65535 ? port2 : void 0;
|
|
24627
24782
|
}
|
|
24628
24783
|
function isWithin8(parent, child) {
|
|
24629
|
-
const base = normalize(
|
|
24630
|
-
const target = normalize(
|
|
24631
|
-
return target === base || target.startsWith(`${base}${
|
|
24784
|
+
const base = normalize(resolve33(parent));
|
|
24785
|
+
const target = normalize(resolve33(child));
|
|
24786
|
+
return target === base || target.startsWith(`${base}${sep23}`);
|
|
24632
24787
|
}
|
|
24633
24788
|
function normalize(path) {
|
|
24634
24789
|
const normalized = path.normalize("NFC");
|
|
24635
24790
|
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
24636
24791
|
}
|
|
24637
24792
|
function relativeDisplay(workspace, filePath) {
|
|
24638
|
-
const path =
|
|
24793
|
+
const path = relative23(workspace, filePath).replaceAll("\\", "/");
|
|
24639
24794
|
return path && !path.startsWith("../") ? path : basename8(filePath);
|
|
24640
24795
|
}
|
|
24641
24796
|
|
|
@@ -24654,11 +24809,12 @@ var JobRegistry = class {
|
|
|
24654
24809
|
}
|
|
24655
24810
|
create(input) {
|
|
24656
24811
|
this.#pruneCompleted();
|
|
24812
|
+
if (this.#jobs.size >= this.#maxJobs) throw new Error(`Background job limit (${this.#maxJobs}) reached; wait for or cancel an existing job first`);
|
|
24657
24813
|
const id = `job-${randomUUID19().slice(0, 12)}`;
|
|
24658
24814
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24659
24815
|
let settle;
|
|
24660
|
-
const done = new Promise((
|
|
24661
|
-
settle =
|
|
24816
|
+
const done = new Promise((resolve44) => {
|
|
24817
|
+
settle = resolve44;
|
|
24662
24818
|
});
|
|
24663
24819
|
const record3 = {
|
|
24664
24820
|
id,
|
|
@@ -24692,7 +24848,7 @@ var JobRegistry = class {
|
|
|
24692
24848
|
},
|
|
24693
24849
|
complete: (result = {}) => this.#finish(
|
|
24694
24850
|
record3,
|
|
24695
|
-
result.exitCode !== void 0 && result.exitCode !== null && result.exitCode !== 0 ? "failed" : "completed",
|
|
24851
|
+
result.error !== void 0 || result.exitCode !== void 0 && result.exitCode !== null && result.exitCode !== 0 ? "failed" : "completed",
|
|
24696
24852
|
result
|
|
24697
24853
|
),
|
|
24698
24854
|
fail: (error) => this.#finish(record3, "failed", { error: error instanceof Error ? error.message : String(error) }),
|
|
@@ -24708,21 +24864,24 @@ var JobRegistry = class {
|
|
|
24708
24864
|
return { ...snapshot2(job), output: job.output.slice(effective - job.outputStart), cursor: job.outputStart + job.output.length };
|
|
24709
24865
|
}
|
|
24710
24866
|
async wait(id, owner2, signal) {
|
|
24867
|
+
signal?.throwIfAborted();
|
|
24711
24868
|
const job = this.#owned(id, owner2);
|
|
24712
24869
|
if (job.state !== "running") return snapshot2(job);
|
|
24713
|
-
|
|
24714
|
-
return Promise.race([job.done, new Promise((_resolve, reject) => {
|
|
24715
|
-
if (signal.aborted) {
|
|
24716
|
-
reject(signal.reason);
|
|
24717
|
-
return;
|
|
24718
|
-
}
|
|
24719
|
-
signal.addEventListener("abort", () => reject(signal.reason), { once: true });
|
|
24720
|
-
})]);
|
|
24870
|
+
return waitWithSignal(job.done, signal);
|
|
24721
24871
|
}
|
|
24722
24872
|
kill(id, owner2) {
|
|
24723
24873
|
const job = this.#owned(id, owner2);
|
|
24724
24874
|
if (job.state !== "running") return;
|
|
24725
|
-
|
|
24875
|
+
try {
|
|
24876
|
+
const cancellation = job.cancel?.();
|
|
24877
|
+
if (cancellation !== void 0) return cancellation.then(
|
|
24878
|
+
() => this.#finish(job, "cancelled", {}),
|
|
24879
|
+
(error) => this.#finish(job, "failed", { error: `Cancellation failed: ${error instanceof Error ? error.message : String(error)}` })
|
|
24880
|
+
);
|
|
24881
|
+
} catch (error) {
|
|
24882
|
+
this.#finish(job, "failed", { error: `Cancellation failed: ${error instanceof Error ? error.message : String(error)}` });
|
|
24883
|
+
return;
|
|
24884
|
+
}
|
|
24726
24885
|
this.#finish(job, "cancelled", {});
|
|
24727
24886
|
}
|
|
24728
24887
|
subscribe(listener) {
|
|
@@ -24782,7 +24941,7 @@ function snapshot2(job) {
|
|
|
24782
24941
|
// src/terminal/service.ts
|
|
24783
24942
|
import { randomUUID as randomUUID20 } from "crypto";
|
|
24784
24943
|
import { createRequire as nodeCreateRequire2 } from "module";
|
|
24785
|
-
import { isAbsolute as
|
|
24944
|
+
import { isAbsolute as isAbsolute17, relative as relative24, resolve as resolve34, sep as sep24 } from "path";
|
|
24786
24945
|
var TerminalService = class {
|
|
24787
24946
|
#root;
|
|
24788
24947
|
#factory;
|
|
@@ -24790,10 +24949,11 @@ var TerminalService = class {
|
|
|
24790
24949
|
#maxOutputChars;
|
|
24791
24950
|
#sessions = /* @__PURE__ */ new Map();
|
|
24792
24951
|
constructor(workspace, options = {}) {
|
|
24793
|
-
this.#root =
|
|
24952
|
+
this.#root = resolve34(workspace);
|
|
24794
24953
|
this.#factory = options.factory ?? defaultFactory;
|
|
24795
24954
|
this.#jobs = options.jobs;
|
|
24796
24955
|
this.#maxOutputChars = options.maxOutputChars ?? 2e5;
|
|
24956
|
+
if (!Number.isSafeInteger(this.#maxOutputChars) || this.#maxOutputChars <= 0) throw new Error("maxOutputChars must be a positive integer");
|
|
24797
24957
|
}
|
|
24798
24958
|
open(input) {
|
|
24799
24959
|
const cwd = workspacePath(this.#root, input.cwd ?? ".");
|
|
@@ -24820,7 +24980,13 @@ var TerminalService = class {
|
|
|
24820
24980
|
truncated: false,
|
|
24821
24981
|
disposables: []
|
|
24822
24982
|
};
|
|
24823
|
-
|
|
24983
|
+
let job;
|
|
24984
|
+
try {
|
|
24985
|
+
job = this.#jobs?.create({ kind: "terminal", owner: input.owner, label: shell2, cancel: () => this.close(id, input.owner) });
|
|
24986
|
+
} catch (error) {
|
|
24987
|
+
pty.kill();
|
|
24988
|
+
throw error;
|
|
24989
|
+
}
|
|
24824
24990
|
if (job !== void 0) {
|
|
24825
24991
|
jobId = job.id;
|
|
24826
24992
|
record3.jobId = job.id;
|
|
@@ -24846,10 +25012,10 @@ var TerminalService = class {
|
|
|
24846
25012
|
return { ...snapshot3(record3), ...jobId === void 0 ? {} : { jobId } };
|
|
24847
25013
|
}
|
|
24848
25014
|
write(id, owner2, data2) {
|
|
24849
|
-
this.#
|
|
25015
|
+
this.#running(id, owner2).pty.write(data2);
|
|
24850
25016
|
}
|
|
24851
25017
|
resize(id, owner2, columns, rows) {
|
|
24852
|
-
this.#
|
|
25018
|
+
this.#running(id, owner2).pty.resize(columns, rows);
|
|
24853
25019
|
}
|
|
24854
25020
|
read(id, owner2, cursor = 0) {
|
|
24855
25021
|
const record3 = this.#owned(id, owner2);
|
|
@@ -24880,6 +25046,11 @@ var TerminalService = class {
|
|
|
24880
25046
|
if (record3.owner !== owner2) throw new Error("Terminal owner mismatch");
|
|
24881
25047
|
return record3;
|
|
24882
25048
|
}
|
|
25049
|
+
#running(id, owner2) {
|
|
25050
|
+
const record3 = this.#owned(id, owner2);
|
|
25051
|
+
if (record3.state !== "running") throw new Error(`Terminal ${id} is ${record3.state}; use TerminalOpen to start a new session`);
|
|
25052
|
+
return record3;
|
|
25053
|
+
}
|
|
24883
25054
|
};
|
|
24884
25055
|
function snapshot3(record3) {
|
|
24885
25056
|
return {
|
|
@@ -24908,15 +25079,15 @@ function cleanEnvironment(environment) {
|
|
|
24908
25079
|
return Object.fromEntries(Object.entries(environment).filter((entry2) => entry2[1] !== void 0));
|
|
24909
25080
|
}
|
|
24910
25081
|
function workspacePath(root, input) {
|
|
24911
|
-
const path =
|
|
24912
|
-
const delta =
|
|
24913
|
-
if (delta === ".." || delta.startsWith(`..${
|
|
25082
|
+
const path = resolve34(root, input);
|
|
25083
|
+
const delta = relative24(root, path);
|
|
25084
|
+
if (delta === ".." || delta.startsWith(`..${sep24}`) || isAbsolute17(delta)) throw new Error("Terminal cwd is outside the workspace");
|
|
24914
25085
|
return path;
|
|
24915
25086
|
}
|
|
24916
25087
|
|
|
24917
25088
|
// src/tools/terminal.ts
|
|
24918
25089
|
import { z as z40 } from "zod";
|
|
24919
|
-
import { resolve as
|
|
25090
|
+
import { resolve as resolve35 } from "path";
|
|
24920
25091
|
var Id = z40.string().regex(/^term-[a-f0-9-]+$/);
|
|
24921
25092
|
var OpenInput = z40.object({ cwd: z40.string().optional(), shell: z40.string().optional(), args: z40.array(z40.string()).optional(), columns: z40.coerce.number().int().min(10).max(500).optional(), rows: z40.coerce.number().int().min(2).max(300).optional() });
|
|
24922
25093
|
var OptionalBoolean = z40.union([z40.boolean(), z40.string().refine((value) => value === "true" || value === "false")]);
|
|
@@ -24930,8 +25101,8 @@ function createTerminalTools(terminals, workspace = process.cwd()) {
|
|
|
24930
25101
|
name: "TerminalOpen",
|
|
24931
25102
|
description: "Open a persistent interactive pseudo-terminal in the workspace",
|
|
24932
25103
|
inputSchema: OpenInput,
|
|
24933
|
-
paths: (input) => [
|
|
24934
|
-
permissions: (input) => ({ paths: [
|
|
25104
|
+
paths: (input) => [resolve35(workspace, input.cwd ?? ".")],
|
|
25105
|
+
permissions: (input) => ({ paths: [resolve35(workspace, input.cwd ?? ".")], command: input.shell ?? "system-shell", args: input.args ?? [], cwd: resolve35(workspace, input.cwd ?? ".") }),
|
|
24935
25106
|
execute: async (input, _signal, context) => terminals.open({
|
|
24936
25107
|
owner: owner(context),
|
|
24937
25108
|
...input.cwd === void 0 ? {} : { cwd: input.cwd },
|
|
@@ -24960,7 +25131,7 @@ function createTerminalTools(terminals, workspace = process.cwd()) {
|
|
|
24960
25131
|
inputSchema: ReadInput3,
|
|
24961
25132
|
paths: () => [],
|
|
24962
25133
|
execute: async (input, _signal, context) => terminals.read(input.id, owner(context), input.cursor ?? 0),
|
|
24963
|
-
renderForModel: (output) =>
|
|
25134
|
+
renderForModel: (output) => JSON.stringify(output),
|
|
24964
25135
|
presentResult: (output) => {
|
|
24965
25136
|
const result = output;
|
|
24966
25137
|
return {
|
|
@@ -25028,13 +25199,13 @@ import { createHash as createHash20 } from "crypto";
|
|
|
25028
25199
|
import { Socket as Socket2 } from "net";
|
|
25029
25200
|
import { tmpdir as tmpdir3 } from "os";
|
|
25030
25201
|
import { join as join36 } from "path";
|
|
25031
|
-
import { spawn as
|
|
25202
|
+
import { spawn as spawn8 } from "child_process";
|
|
25032
25203
|
|
|
25033
25204
|
// src/pals/broker.ts
|
|
25034
25205
|
import { createHash as createHash19, randomUUID as randomUUID23, timingSafeEqual } from "crypto";
|
|
25035
25206
|
import { chmod as chmod3, lstat as lstat13, mkdir as mkdir23, unlink as unlink6 } from "fs/promises";
|
|
25036
25207
|
import { createServer as createServer4, Socket } from "net";
|
|
25037
|
-
import { dirname as
|
|
25208
|
+
import { dirname as dirname21 } from "path";
|
|
25038
25209
|
|
|
25039
25210
|
// src/pals/auth.ts
|
|
25040
25211
|
import { randomBytes as randomBytes3, randomUUID as randomUUID21 } from "crypto";
|
|
@@ -25085,7 +25256,7 @@ function validatePalAuthToken(token) {
|
|
|
25085
25256
|
// src/pals/lifecycle.ts
|
|
25086
25257
|
import { randomUUID as randomUUID22 } from "crypto";
|
|
25087
25258
|
import { mkdir as mkdir22, open as open13, readFile as readFile27, rename as rename11, stat as stat13, unlink as unlink5 } from "fs/promises";
|
|
25088
|
-
import { dirname as
|
|
25259
|
+
import { dirname as dirname20 } from "path";
|
|
25089
25260
|
function defaultProcessAlive(pid) {
|
|
25090
25261
|
if (!Number.isSafeInteger(pid) || pid <= 0) return true;
|
|
25091
25262
|
try {
|
|
@@ -25106,7 +25277,7 @@ function parseMetadata(value) {
|
|
|
25106
25277
|
}
|
|
25107
25278
|
async function acquirePalFileLock(options) {
|
|
25108
25279
|
if (await options.endpointLive()) return void 0;
|
|
25109
|
-
await mkdir22(
|
|
25280
|
+
await mkdir22(dirname20(options.path), { recursive: true, mode: 448 });
|
|
25110
25281
|
const pid = options.pid ?? process.pid;
|
|
25111
25282
|
const now = options.now ?? Date.now;
|
|
25112
25283
|
const nonce = randomUUID22();
|
|
@@ -25161,16 +25332,16 @@ async function acquirePalFileLock(options) {
|
|
|
25161
25332
|
|
|
25162
25333
|
// src/pals/broker-cli.ts
|
|
25163
25334
|
var startupAttempts = /* @__PURE__ */ new Map();
|
|
25164
|
-
function
|
|
25165
|
-
return new Promise((
|
|
25335
|
+
function delay2(milliseconds) {
|
|
25336
|
+
return new Promise((resolve44) => setTimeout(resolve44, milliseconds));
|
|
25166
25337
|
}
|
|
25167
25338
|
function canConnect(address) {
|
|
25168
|
-
return new Promise((
|
|
25339
|
+
return new Promise((resolve44) => {
|
|
25169
25340
|
const socket = new Socket2();
|
|
25170
25341
|
const finish = (connected) => {
|
|
25171
25342
|
socket.removeAllListeners();
|
|
25172
25343
|
socket.destroy();
|
|
25173
|
-
|
|
25344
|
+
resolve44(connected);
|
|
25174
25345
|
};
|
|
25175
25346
|
socket.once("connect", () => finish(true));
|
|
25176
25347
|
socket.once("error", () => finish(false));
|
|
@@ -25193,7 +25364,7 @@ async function spawnPalBrokerProcess(options) {
|
|
|
25193
25364
|
const lock = await acquirePalFileLock({ path: lockPath, endpointLive: () => canConnect(address) });
|
|
25194
25365
|
if (lock === void 0) return;
|
|
25195
25366
|
try {
|
|
25196
|
-
const child =
|
|
25367
|
+
const child = spawn8(options.executable, [...options.args], {
|
|
25197
25368
|
detached: true,
|
|
25198
25369
|
stdio: "ignore",
|
|
25199
25370
|
windowsHide: true,
|
|
@@ -25201,7 +25372,7 @@ async function spawnPalBrokerProcess(options) {
|
|
|
25201
25372
|
});
|
|
25202
25373
|
child.unref();
|
|
25203
25374
|
const deadline = Date.now() + 5e3;
|
|
25204
|
-
while (Date.now() < deadline && !await canConnect(address)) await
|
|
25375
|
+
while (Date.now() < deadline && !await canConnect(address)) await delay2(25);
|
|
25205
25376
|
if (!await canConnect(address)) throw new Error(`Pals broker process did not become ready at '${address}'`);
|
|
25206
25377
|
} finally {
|
|
25207
25378
|
await lock.release();
|
|
@@ -25222,7 +25393,7 @@ async function ensureOnce(options) {
|
|
|
25222
25393
|
const deadline = Date.now() + timeoutMs;
|
|
25223
25394
|
do {
|
|
25224
25395
|
if (await canConnect(options.address)) return;
|
|
25225
|
-
await
|
|
25396
|
+
await delay2(retryDelayMs);
|
|
25226
25397
|
} while (Date.now() < deadline);
|
|
25227
25398
|
throw new Error(`Timed out waiting for pals broker at '${options.address}'`);
|
|
25228
25399
|
}
|
|
@@ -25361,8 +25532,8 @@ var PalClient = class {
|
|
|
25361
25532
|
let disconnectTimer;
|
|
25362
25533
|
await Promise.race([
|
|
25363
25534
|
this.request({ version: 1, type: "disconnect", requestId: randomUUID24() }).catch(() => void 0),
|
|
25364
|
-
new Promise((
|
|
25365
|
-
disconnectTimer = setTimeout(
|
|
25535
|
+
new Promise((resolve44) => {
|
|
25536
|
+
disconnectTimer = setTimeout(resolve44, Math.min(this.requestTimeoutMs, 250));
|
|
25366
25537
|
})
|
|
25367
25538
|
]);
|
|
25368
25539
|
if (disconnectTimer !== void 0) clearTimeout(disconnectTimer);
|
|
@@ -25408,7 +25579,7 @@ var PalClient = class {
|
|
|
25408
25579
|
return presence;
|
|
25409
25580
|
}
|
|
25410
25581
|
openSocket() {
|
|
25411
|
-
return new Promise((
|
|
25582
|
+
return new Promise((resolve44, reject) => {
|
|
25412
25583
|
let settled = false;
|
|
25413
25584
|
const rejectConnection = (error) => {
|
|
25414
25585
|
if (settled) return;
|
|
@@ -25431,7 +25602,7 @@ var PalClient = class {
|
|
|
25431
25602
|
settled = true;
|
|
25432
25603
|
if (this.connectingReject === rejectConnection) this.connectingReject = void 0;
|
|
25433
25604
|
socket.off("error", onInitialError);
|
|
25434
|
-
|
|
25605
|
+
resolve44();
|
|
25435
25606
|
};
|
|
25436
25607
|
const onInitialError = (error) => {
|
|
25437
25608
|
socket.off("connect", onConnect);
|
|
@@ -25462,12 +25633,12 @@ var PalClient = class {
|
|
|
25462
25633
|
if (this.closed || socket === void 0 || socket.destroyed || !socket.writable) {
|
|
25463
25634
|
return Promise.reject(new Error(this.closed ? "Pal client is closed" : "Pal client is not connected"));
|
|
25464
25635
|
}
|
|
25465
|
-
return new Promise((
|
|
25636
|
+
return new Promise((resolve44, reject) => {
|
|
25466
25637
|
const timer = setTimeout(() => {
|
|
25467
25638
|
this.pending.delete(request.requestId);
|
|
25468
25639
|
reject(new Error(`Pal request '${request.type}' timed out`));
|
|
25469
25640
|
}, this.requestTimeoutMs);
|
|
25470
|
-
this.pending.set(request.requestId, { resolve:
|
|
25641
|
+
this.pending.set(request.requestId, { resolve: resolve44, reject, timer });
|
|
25471
25642
|
let frame;
|
|
25472
25643
|
try {
|
|
25473
25644
|
frame = encodeControlFrame(BrokerRequestSchema.parse(request));
|
|
@@ -25571,12 +25742,12 @@ var PalClient = class {
|
|
|
25571
25742
|
}
|
|
25572
25743
|
scheduleReconnect() {
|
|
25573
25744
|
if (this.reconnectTimer !== void 0 || this.closed) return;
|
|
25574
|
-
const
|
|
25745
|
+
const delay3 = this.reconnectDelayMs;
|
|
25575
25746
|
this.reconnectDelayMs = Math.min(this.reconnectDelayMs * 2, this.reconnectMaxDelayMs);
|
|
25576
25747
|
this.reconnectTimer = setTimeout(() => {
|
|
25577
25748
|
this.reconnectTimer = void 0;
|
|
25578
25749
|
void this.start().catch(() => this.scheduleReconnect());
|
|
25579
|
-
},
|
|
25750
|
+
}, delay3);
|
|
25580
25751
|
this.reconnectTimer.unref?.();
|
|
25581
25752
|
}
|
|
25582
25753
|
};
|
|
@@ -25678,6 +25849,7 @@ function createPalsTools(client, options) {
|
|
|
25678
25849
|
execute: async (input, signal) => {
|
|
25679
25850
|
signal.throwIfAborted();
|
|
25680
25851
|
const recipientId = await assertSendTarget(client, input.target, input.coWorkId);
|
|
25852
|
+
signal.throwIfAborted();
|
|
25681
25853
|
return client.sendChat(recipientId, shareGuard.protect(input.message));
|
|
25682
25854
|
}
|
|
25683
25855
|
},
|
|
@@ -25701,6 +25873,7 @@ function createPalsTools(client, options) {
|
|
|
25701
25873
|
execute: async (input, signal) => {
|
|
25702
25874
|
signal.throwIfAborted();
|
|
25703
25875
|
assertRequiredParticipant(await client.coWorkStatus(input.plan.coWorkId), options.selfId);
|
|
25876
|
+
signal.throwIfAborted();
|
|
25704
25877
|
const plan = CoWorkPlanSchema.parse({
|
|
25705
25878
|
...input.plan,
|
|
25706
25879
|
goal: shareGuard.protect(input.plan.goal),
|
|
@@ -25717,11 +25890,13 @@ function createPalsTools(client, options) {
|
|
|
25717
25890
|
execute: async (input, signal) => {
|
|
25718
25891
|
signal.throwIfAborted();
|
|
25719
25892
|
let state = await assertCurrentToken(client, input);
|
|
25893
|
+
signal.throwIfAborted();
|
|
25720
25894
|
assertRequiredParticipant(state, options.selfId);
|
|
25721
25895
|
if (!state.planAcceptedParticipantIds.includes(options.selfId)) {
|
|
25722
25896
|
state = await client.coWorkAction({ type: "cowork-plan-accept", ...input });
|
|
25723
25897
|
}
|
|
25724
25898
|
if (state.readyParticipantIds.includes(options.selfId) || state.phase === "running") return state;
|
|
25899
|
+
signal.throwIfAborted();
|
|
25725
25900
|
return client.coWorkAction({ type: "cowork-ready", ...input });
|
|
25726
25901
|
}
|
|
25727
25902
|
},
|
|
@@ -25735,6 +25910,7 @@ function createPalsTools(client, options) {
|
|
|
25735
25910
|
execute: async (input, signal) => {
|
|
25736
25911
|
signal.throwIfAborted();
|
|
25737
25912
|
const recipientId = await assertSendTarget(client, input.target, input.coWorkId);
|
|
25913
|
+
signal.throwIfAborted();
|
|
25738
25914
|
return client.sendChat(recipientId, `[co-work ${input.coWorkId} progress] ${shareGuard.protect(input.detail)}`);
|
|
25739
25915
|
}
|
|
25740
25916
|
},
|
|
@@ -25748,6 +25924,7 @@ function createPalsTools(client, options) {
|
|
|
25748
25924
|
execute: async (input, signal) => {
|
|
25749
25925
|
signal.throwIfAborted();
|
|
25750
25926
|
const state = await assertCurrentToken(client, input);
|
|
25927
|
+
signal.throwIfAborted();
|
|
25751
25928
|
assertRequiredParticipant(state, options.selfId);
|
|
25752
25929
|
if (state.phase !== "running" || !state.acceptedParticipantIds.includes(options.selfId) || !state.readyParticipantIds.includes(options.selfId)) {
|
|
25753
25930
|
throw new Error("A required participant must be accepted, ready, and running before reporting completion");
|
|
@@ -25765,6 +25942,7 @@ function createPalsTools(client, options) {
|
|
|
25765
25942
|
execute: async (input, signal) => {
|
|
25766
25943
|
signal.throwIfAborted();
|
|
25767
25944
|
const state = await assertCurrentToken(client, input);
|
|
25945
|
+
signal.throwIfAborted();
|
|
25768
25946
|
assertRequiredParticipant(state, options.selfId);
|
|
25769
25947
|
if (state.integrationOwnerId !== options.selfId) throw new Error("Only the broker-designated integration owner can finalize integration");
|
|
25770
25948
|
if (state.phase !== "verifying") throw new Error("Co-work is not ready for integration verification");
|
|
@@ -25871,8 +26049,8 @@ var ApprovalBridge = class {
|
|
|
25871
26049
|
}
|
|
25872
26050
|
};
|
|
25873
26051
|
async function createProductionRuntime(options) {
|
|
25874
|
-
const workspace =
|
|
25875
|
-
const home =
|
|
26052
|
+
const workspace = resolve36(options.workspace ?? process.cwd());
|
|
26053
|
+
const home = resolve36(options.home ?? homedir3());
|
|
25876
26054
|
const environment = options.environment ?? process.env;
|
|
25877
26055
|
const ide = new FlavorIdeClient({ workspace, home, environment });
|
|
25878
26056
|
const ideReady = ide.initialize();
|
|
@@ -26046,6 +26224,7 @@ async function createProductionRuntime(options) {
|
|
|
26046
26224
|
const executionEnvironment = createExecutionEnvironment(workspace, config.execution);
|
|
26047
26225
|
const jobs = new JobRegistry();
|
|
26048
26226
|
const terminals = new TerminalService(workspace, { jobs });
|
|
26227
|
+
const lspManager = new RealLspManager({ workspace, onStatus: (message2) => emitOutput({ type: "notice", message: message2 }) });
|
|
26049
26228
|
const observations = new FileObservationStore();
|
|
26050
26229
|
const workspaceInstructions = new WorkspaceInstructions(workspace);
|
|
26051
26230
|
const fileMutationOptions = {
|
|
@@ -26069,9 +26248,7 @@ async function createProductionRuntime(options) {
|
|
|
26069
26248
|
createWebSearchTool(),
|
|
26070
26249
|
...createJobTools(jobs),
|
|
26071
26250
|
...createTerminalTools(terminals, workspace),
|
|
26072
|
-
...createLspTools(workspace, {
|
|
26073
|
-
onStatus: (message2) => emitOutput({ type: "notice", message: message2 })
|
|
26074
|
-
}),
|
|
26251
|
+
...createLspTools(workspace, { manager: lspManager }),
|
|
26075
26252
|
...options.approvalPolicy === "deny" ? [] : [createAskUserQuestionTool(askUserQuestionHandler)],
|
|
26076
26253
|
createTaskOutputTool(),
|
|
26077
26254
|
createTodoWriteTool()
|
|
@@ -26608,6 +26785,8 @@ async function createProductionRuntime(options) {
|
|
|
26608
26785
|
permissionPolicy,
|
|
26609
26786
|
maxIterationsMain: config.maxIterations.main,
|
|
26610
26787
|
maxIterationsSubagent: config.maxIterations.subagent,
|
|
26788
|
+
softLimitFactor: config.maxIterations.softLimitFactor,
|
|
26789
|
+
extendIterations: config.maxIterations.extendBy,
|
|
26611
26790
|
hasActiveProgress,
|
|
26612
26791
|
afterToolSuccess: async (_tool, paths, _input, _output, context) => workspaceInstructions.discover(paths, context.ownerId ?? context.agent),
|
|
26613
26792
|
toolJournal: {
|
|
@@ -26999,6 +27178,8 @@ async function createProductionRuntime(options) {
|
|
|
26999
27178
|
permissionPolicy,
|
|
27000
27179
|
maxIterationsMain: config.maxIterations.main,
|
|
27001
27180
|
maxIterationsSubagent: config.maxIterations.subagent,
|
|
27181
|
+
softLimitFactor: config.maxIterations.softLimitFactor,
|
|
27182
|
+
extendIterations: config.maxIterations.extendBy,
|
|
27002
27183
|
loopMode: true,
|
|
27003
27184
|
afterToolSuccess: async (_tool, paths, _input, _output, context) => loopInstructions.discover(paths, context.ownerId ?? context.agent),
|
|
27004
27185
|
toolJournal: {
|
|
@@ -27288,9 +27469,9 @@ async function createProductionRuntime(options) {
|
|
|
27288
27469
|
explain: (query, focus, signal, onText) => runExplain({
|
|
27289
27470
|
graph: new DesktopAstGraphService(workspace),
|
|
27290
27471
|
readFile: async (path) => {
|
|
27291
|
-
const root =
|
|
27292
|
-
const abs =
|
|
27293
|
-
if (abs !== root && !abs.startsWith(root +
|
|
27472
|
+
const root = resolve36(workspace);
|
|
27473
|
+
const abs = resolve36(root, path);
|
|
27474
|
+
if (abs !== root && !abs.startsWith(root + sep25)) throw new Error(`path outside workspace: ${path}`);
|
|
27294
27475
|
return readFile28(abs, "utf8");
|
|
27295
27476
|
},
|
|
27296
27477
|
history: async (path, limit) => fileHistory(workspace, path, limit),
|
|
@@ -27642,6 +27823,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
27642
27823
|
if (executionEnvironment !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "execution-environment", executionEnvironment.dispose());
|
|
27643
27824
|
if (islandControlServer !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "island-control", islandControlServer.close());
|
|
27644
27825
|
terminals.dispose();
|
|
27826
|
+
await boundedStep(stepTimeoutMs, diagnostics, "language-servers", lspManager.dispose());
|
|
27645
27827
|
await boundedStep(stepTimeoutMs, diagnostics, "jobs", jobs.dispose());
|
|
27646
27828
|
auditLogger.close();
|
|
27647
27829
|
evolveService.dispose();
|
|
@@ -27682,6 +27864,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
27682
27864
|
if (sleepScheduler !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "sleep-scheduler", sleepScheduler.dispose());
|
|
27683
27865
|
if (executionEnvironment !== void 0) await boundedStep(stepTimeoutMs, diagnostics, "execution-environment", executionEnvironment.dispose());
|
|
27684
27866
|
terminals.dispose();
|
|
27867
|
+
await boundedStep(stepTimeoutMs, diagnostics, "language-servers", lspManager.dispose());
|
|
27685
27868
|
await boundedStep(stepTimeoutMs, diagnostics, "jobs", jobs.dispose());
|
|
27686
27869
|
unsubscribeCollaboration?.();
|
|
27687
27870
|
pendingCollaborationEvents.length = 0;
|
|
@@ -27835,9 +28018,9 @@ async function workspaceFingerprint(workspace) {
|
|
|
27835
28018
|
let remaining = 5 * 1024 * 1024;
|
|
27836
28019
|
for (const name of untracked.stdout.split("\0").filter(Boolean).sort()) {
|
|
27837
28020
|
if (name === ".flavor/loops" || name.startsWith(".flavor/loops/")) continue;
|
|
27838
|
-
const path =
|
|
27839
|
-
const relativePath =
|
|
27840
|
-
if (relativePath === ".." || relativePath.startsWith(`..${
|
|
28021
|
+
const path = resolve36(workspace, name);
|
|
28022
|
+
const relativePath = relative25(resolve36(workspace), path);
|
|
28023
|
+
if (relativePath === ".." || relativePath.startsWith(`..${sep25}`)) continue;
|
|
27841
28024
|
hash.update(name);
|
|
27842
28025
|
if (remaining <= 0) continue;
|
|
27843
28026
|
try {
|
|
@@ -27969,7 +28152,7 @@ function requestMemoryRestart(workspace, sessionId, emitOutput) {
|
|
|
27969
28152
|
previous = parseMemoryRestartMarker(readFileSync3(markerPath, "utf8"));
|
|
27970
28153
|
} catch {
|
|
27971
28154
|
}
|
|
27972
|
-
|
|
28155
|
+
mkdirSync3(dirname22(markerPath), { recursive: true });
|
|
27973
28156
|
writeFileSync2(markerPath, `${JSON.stringify(nextMemoryRestartMarker(previous, sessionId, /* @__PURE__ */ new Date()))}
|
|
27974
28157
|
`, "utf8");
|
|
27975
28158
|
} catch {
|
|
@@ -28504,7 +28687,7 @@ function createMemoryTaskId() {
|
|
|
28504
28687
|
import { createHash as createHash27 } from "crypto";
|
|
28505
28688
|
import { mkdir as mkdir27, readFile as readFile36, readdir as readdir13, rm as rm15, stat as stat16, writeFile as writeFile17 } from "fs/promises";
|
|
28506
28689
|
import { homedir as homedir5 } from "os";
|
|
28507
|
-
import { basename as basename10, join as join47, resolve as
|
|
28690
|
+
import { basename as basename10, join as join47, resolve as resolve39 } from "path";
|
|
28508
28691
|
|
|
28509
28692
|
// src/d2c/workflow.ts
|
|
28510
28693
|
import { createHash as createHash22 } from "crypto";
|
|
@@ -29032,7 +29215,7 @@ function confirmApiMapping(mapping, operationKey, operations) {
|
|
|
29032
29215
|
|
|
29033
29216
|
// src/d2c/modules.ts
|
|
29034
29217
|
import { readFile as readFile29 } from "fs/promises";
|
|
29035
|
-
import { isAbsolute as
|
|
29218
|
+
import { isAbsolute as isAbsolute18, join as join39, normalize as normalize2, posix, sep as sep26, win32 as win322 } from "path";
|
|
29036
29219
|
import { z as z43 } from "zod";
|
|
29037
29220
|
var ModuleSchema = z43.object({
|
|
29038
29221
|
id: z43.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/),
|
|
@@ -29044,10 +29227,10 @@ var ModuleSchema = z43.object({
|
|
|
29044
29227
|
}).strict();
|
|
29045
29228
|
var ManifestSchema3 = z43.object({ schema: z43.literal(1), modules: z43.array(ModuleSchema).min(1).max(500) }).passthrough();
|
|
29046
29229
|
function safeSource(path) {
|
|
29047
|
-
if (
|
|
29230
|
+
if (isAbsolute18(path) || posix.isAbsolute(path) || win322.isAbsolute(path) || path.includes("\0")) return false;
|
|
29048
29231
|
const value = normalize2(path);
|
|
29049
29232
|
const portableSegments = path.replaceAll("\\", "/").split("/");
|
|
29050
|
-
return value !== ".." && !value.startsWith(`..${
|
|
29233
|
+
return value !== ".." && !value.startsWith(`..${sep26}`) && !portableSegments.includes("..");
|
|
29051
29234
|
}
|
|
29052
29235
|
function d2cOutputDirectory(workspace, task) {
|
|
29053
29236
|
if (!D2C_TASK_PATTERN.test(task)) throw new Error(`Invalid D2C task: ${task}`);
|
|
@@ -29270,7 +29453,7 @@ http.interceptors.response.use((response) => response, (error) => Promise.reject
|
|
|
29270
29453
|
}
|
|
29271
29454
|
|
|
29272
29455
|
// src/d2c/mock-runner.ts
|
|
29273
|
-
import { spawn as
|
|
29456
|
+
import { spawn as spawn9 } from "child_process";
|
|
29274
29457
|
import { access as access4, readFile as readFile31 } from "fs/promises";
|
|
29275
29458
|
import { createServer as createServer5 } from "net";
|
|
29276
29459
|
import { join as join41 } from "path";
|
|
@@ -29293,7 +29476,7 @@ async function port() {
|
|
|
29293
29476
|
});
|
|
29294
29477
|
});
|
|
29295
29478
|
}
|
|
29296
|
-
function
|
|
29479
|
+
function waitForProcess(child, timeoutMs, label) {
|
|
29297
29480
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
29298
29481
|
const timer = setTimeout(() => {
|
|
29299
29482
|
void terminateProcessTree(child, true);
|
|
@@ -29323,7 +29506,7 @@ async function install(projectDir, signal) {
|
|
|
29323
29506
|
if (key.toLowerCase() === "npm_config_allow_scripts") delete env[key];
|
|
29324
29507
|
}
|
|
29325
29508
|
}
|
|
29326
|
-
const child =
|
|
29509
|
+
const child = spawn9(command, args, { cwd: projectDir, env, shell: false, windowsHide: true });
|
|
29327
29510
|
let output = "";
|
|
29328
29511
|
const append = (chunk) => {
|
|
29329
29512
|
output = (output + chunk.toString("utf8")).slice(-8e3);
|
|
@@ -29335,7 +29518,7 @@ async function install(projectDir, signal) {
|
|
|
29335
29518
|
};
|
|
29336
29519
|
signal?.addEventListener("abort", abort2, { once: true });
|
|
29337
29520
|
try {
|
|
29338
|
-
await
|
|
29521
|
+
await waitForProcess(child, 18e4, "D2C mock dependency install");
|
|
29339
29522
|
return output;
|
|
29340
29523
|
} catch (error) {
|
|
29341
29524
|
throw new Error(`${error instanceof Error ? error.message : String(error)}${output.trim() ? `
|
|
@@ -29355,7 +29538,7 @@ ${output.trim()}` : ""}`);
|
|
|
29355
29538
|
async function installPython(serverDir, signal) {
|
|
29356
29539
|
signal?.throwIfAborted();
|
|
29357
29540
|
const executable = process.env.FLAVOR_PYTHON?.trim() || (process.platform === "win32" ? "python" : "python3");
|
|
29358
|
-
const child =
|
|
29541
|
+
const child = spawn9(executable, ["-m", "pip", "install", "-r", "requirements.txt"], {
|
|
29359
29542
|
cwd: serverDir,
|
|
29360
29543
|
env: process.env,
|
|
29361
29544
|
shell: false,
|
|
@@ -29372,7 +29555,7 @@ async function installPython(serverDir, signal) {
|
|
|
29372
29555
|
};
|
|
29373
29556
|
signal?.addEventListener("abort", abort2, { once: true });
|
|
29374
29557
|
try {
|
|
29375
|
-
await
|
|
29558
|
+
await waitForProcess(child, 18e4, "E2E backend dependency install");
|
|
29376
29559
|
} catch (error) {
|
|
29377
29560
|
throw new Error(`${error instanceof Error ? error.message : String(error)}${output.trim() ? `
|
|
29378
29561
|
${output.trim()}` : ""}`);
|
|
@@ -29386,7 +29569,7 @@ async function runPythonBackend(projectDir, options) {
|
|
|
29386
29569
|
const selectedPort = await port();
|
|
29387
29570
|
const url = `http://127.0.0.1:${selectedPort}`;
|
|
29388
29571
|
const executable = process.env.FLAVOR_PYTHON?.trim() || (process.platform === "win32" ? "python" : "python3");
|
|
29389
|
-
const child =
|
|
29572
|
+
const child = spawn9(executable, ["-m", "uvicorn", "main:app", "--host", "127.0.0.1", "--port", String(selectedPort)], {
|
|
29390
29573
|
cwd: serverDir,
|
|
29391
29574
|
windowsHide: true,
|
|
29392
29575
|
shell: false,
|
|
@@ -29446,7 +29629,7 @@ async function runD2cMockServer(projectDir, options = {}) {
|
|
|
29446
29629
|
if (options.installDependencies !== false) await install(projectDir, options.signal);
|
|
29447
29630
|
const selectedPort = await port();
|
|
29448
29631
|
const url = `http://127.0.0.1:${selectedPort}`;
|
|
29449
|
-
const child =
|
|
29632
|
+
const child = spawn9(process.execPath, [serverPath], {
|
|
29450
29633
|
cwd: projectDir,
|
|
29451
29634
|
windowsHide: true,
|
|
29452
29635
|
env: { ...process.env, D2C_MOCK_PORT: String(selectedPort), ELECTRON_RUN_AS_NODE: "1" }
|
|
@@ -30189,14 +30372,14 @@ async function updateDeliveryRun(workspace, task, expectedRevision, mutation) {
|
|
|
30189
30372
|
// src/skills/manager.ts
|
|
30190
30373
|
import { lstat as lstat14, mkdir as mkdir26, readFile as readFile35, readdir as readdir12, realpath as realpath13, rm as rm14, writeFile as writeFile16 } from "fs/promises";
|
|
30191
30374
|
import { homedir as homedir4 } from "os";
|
|
30192
|
-
import { isAbsolute as
|
|
30375
|
+
import { isAbsolute as isAbsolute19, join as join44, relative as relative26, resolve as resolve37, sep as sep27 } from "path";
|
|
30193
30376
|
import { stringify } from "yaml";
|
|
30194
30377
|
var SkillManager = class {
|
|
30195
30378
|
#workspace;
|
|
30196
30379
|
#home;
|
|
30197
30380
|
constructor(options) {
|
|
30198
|
-
this.#workspace =
|
|
30199
|
-
this.#home =
|
|
30381
|
+
this.#workspace = resolve37(options.workspace);
|
|
30382
|
+
this.#home = resolve37(options.home ?? homedir4());
|
|
30200
30383
|
}
|
|
30201
30384
|
async list() {
|
|
30202
30385
|
const { registry, disabled } = await this.#registry();
|
|
@@ -30340,12 +30523,12 @@ async function discoverPluginSkillRoots(directory) {
|
|
|
30340
30523
|
try {
|
|
30341
30524
|
const manifest = PluginManifestSchema.parse(JSON.parse(await readFile35(join44(pluginRoot, "flavor-plugin.json"), "utf8")));
|
|
30342
30525
|
for (const contribution of manifest.contributes.skillRoots) {
|
|
30343
|
-
const candidate =
|
|
30526
|
+
const candidate = resolve37(pluginRoot, contribution.path);
|
|
30344
30527
|
const info = await lstat14(candidate);
|
|
30345
30528
|
if (!info.isDirectory() || info.isSymbolicLink()) continue;
|
|
30346
30529
|
const physical = await realpath13(candidate);
|
|
30347
|
-
const relation =
|
|
30348
|
-
if (relation === ".." || relation.startsWith(`..${
|
|
30530
|
+
const relation = relative26(pluginRoot, physical);
|
|
30531
|
+
if (relation === ".." || relation.startsWith(`..${sep27}`) || isAbsolute19(relation)) continue;
|
|
30349
30532
|
roots.push(physical);
|
|
30350
30533
|
}
|
|
30351
30534
|
} catch {
|
|
@@ -30605,7 +30788,7 @@ function protocolLabel(type) {
|
|
|
30605
30788
|
import { createReadStream as createReadStream2 } from "fs";
|
|
30606
30789
|
import { realpath as realpath14, stat as stat15 } from "fs/promises";
|
|
30607
30790
|
import { createServer as createServer6 } from "http";
|
|
30608
|
-
import { extname as extname4, isAbsolute as
|
|
30791
|
+
import { extname as extname4, isAbsolute as isAbsolute20, relative as relative27, resolve as resolve38, sep as sep28 } from "path";
|
|
30609
30792
|
var MIME_TYPES = {
|
|
30610
30793
|
".css": "text/css; charset=utf-8",
|
|
30611
30794
|
".gif": "image/gif",
|
|
@@ -30623,11 +30806,11 @@ function sendNotFound(response2) {
|
|
|
30623
30806
|
response2.end("Not found");
|
|
30624
30807
|
}
|
|
30625
30808
|
function isInside(root, candidate) {
|
|
30626
|
-
const delta =
|
|
30627
|
-
return delta === "" || !delta.startsWith(`..${
|
|
30809
|
+
const delta = relative27(root, candidate);
|
|
30810
|
+
return delta === "" || !delta.startsWith(`..${sep28}`) && delta !== ".." && !isAbsolute20(delta);
|
|
30628
30811
|
}
|
|
30629
30812
|
async function startD2cProductPreview(rootDirectory) {
|
|
30630
|
-
const root = await realpath14(
|
|
30813
|
+
const root = await realpath14(resolve38(rootDirectory));
|
|
30631
30814
|
const rootInfo = await stat15(root);
|
|
30632
30815
|
if (!rootInfo.isDirectory()) throw new Error("D2C product prototype directory is unavailable");
|
|
30633
30816
|
const server = createServer6(async (request, response2) => {
|
|
@@ -30639,7 +30822,7 @@ async function startD2cProductPreview(rootDirectory) {
|
|
|
30639
30822
|
let pathname = decodeURIComponent(url.pathname);
|
|
30640
30823
|
if (pathname === "/") pathname = "/index.html";
|
|
30641
30824
|
if (pathname.includes("\0")) return sendNotFound(response2);
|
|
30642
|
-
const unresolved =
|
|
30825
|
+
const unresolved = resolve38(root, `.${pathname}`);
|
|
30643
30826
|
if (!isInside(root, unresolved)) return sendNotFound(response2);
|
|
30644
30827
|
const target = await realpath14(unresolved).catch(() => void 0);
|
|
30645
30828
|
if (target === void 0 || !isInside(root, target)) return sendNotFound(response2);
|
|
@@ -30758,7 +30941,7 @@ var DesktopRuntimeController = class {
|
|
|
30758
30941
|
/** Backend source digest observed when the managed process was started. */
|
|
30759
30942
|
#d2cMockSourceFingerprints = /* @__PURE__ */ new Map();
|
|
30760
30943
|
constructor(options) {
|
|
30761
|
-
this.#home =
|
|
30944
|
+
this.#home = resolve39(options.home ?? homedir5());
|
|
30762
30945
|
this.#createRuntime = options.createRuntime ?? (async (runtimeOptions) => createProductionRuntime(runtimeOptions));
|
|
30763
30946
|
this.#createTerminalService = options.createTerminalService ?? ((workspace) => new TerminalService(workspace));
|
|
30764
30947
|
this.#listSessions = options.listSessions ?? (async (workspace) => {
|
|
@@ -30833,7 +31016,7 @@ var DesktopRuntimeController = class {
|
|
|
30833
31016
|
};
|
|
30834
31017
|
}
|
|
30835
31018
|
async openWorkspace(path) {
|
|
30836
|
-
const workspace =
|
|
31019
|
+
const workspace = resolve39(path);
|
|
30837
31020
|
if (workspace !== this.#workspace) {
|
|
30838
31021
|
await this.#disposeRuntime();
|
|
30839
31022
|
await this.#stopAllD2cPreviews();
|
|
@@ -32055,7 +32238,7 @@ async function loginShellEnvironment(environment, platform = process.platform, d
|
|
|
32055
32238
|
|
|
32056
32239
|
// src/desktop/git-manager.ts
|
|
32057
32240
|
import { readFile as readFile37, rm as rm16 } from "fs/promises";
|
|
32058
|
-
import { isAbsolute as
|
|
32241
|
+
import { isAbsolute as isAbsolute21, relative as relative28, resolve as resolve40 } from "path";
|
|
32059
32242
|
function failure(action, stderr, stdout) {
|
|
32060
32243
|
return new Error(`${action}\u5931\u8D25\uFF1A${(stderr.trim() || stdout.trim() || "\u672A\u77E5 Git \u9519\u8BEF").split("\n")[0]}`);
|
|
32061
32244
|
}
|
|
@@ -32148,13 +32331,13 @@ async function desktopLastTurnDiff(workspace, sessionId, paths = []) {
|
|
|
32148
32331
|
return path.replaceAll("\\", "/");
|
|
32149
32332
|
}));
|
|
32150
32333
|
try {
|
|
32151
|
-
const tree = JSON.parse(await readFile37(
|
|
32334
|
+
const tree = JSON.parse(await readFile37(resolve40(workspace, ".flavor", "session-trees", sessionId, "tree.json"), "utf8"));
|
|
32152
32335
|
const leaf = tree.nodes?.find((node) => node.id === tree.leafId);
|
|
32153
32336
|
const parent = leaf?.parentId == null ? void 0 : tree.nodes?.find((node) => node.id === leaf.parentId);
|
|
32154
32337
|
if (leaf === void 0 || parent === void 0) return desktopGitReviewDiff(workspace, { scope: "last-turn", paths });
|
|
32155
32338
|
const load = async (id) => {
|
|
32156
32339
|
if (!/^checkpoint-[A-Za-z0-9-]+$/.test(id)) throw new Error("Invalid checkpoint id");
|
|
32157
|
-
return JSON.parse(await readFile37(
|
|
32340
|
+
return JSON.parse(await readFile37(resolve40(workspace, ".flavor", "checkpoints", "manifests", `${id}.json`), "utf8"));
|
|
32158
32341
|
};
|
|
32159
32342
|
const [before, after] = await Promise.all([load(parent.checkpointId), load(leaf.checkpointId)]);
|
|
32160
32343
|
const oldFiles = new Map(before.files.map((file) => [file.path, file.digest]));
|
|
@@ -32163,7 +32346,7 @@ async function desktopLastTurnDiff(workspace, sessionId, paths = []) {
|
|
|
32163
32346
|
const object = async (digest) => {
|
|
32164
32347
|
if (digest === void 0) return "";
|
|
32165
32348
|
if (!/^[a-f0-9]{64}$/.test(digest)) throw new Error("Invalid checkpoint object");
|
|
32166
|
-
const value = await readFile37(
|
|
32349
|
+
const value = await readFile37(resolve40(workspace, ".flavor", "checkpoints", "objects", digest));
|
|
32167
32350
|
return value.includes(0) ? "[binary content]" : value.toString("utf8");
|
|
32168
32351
|
};
|
|
32169
32352
|
const chunks = await Promise.all(changed.map(async (path) => wholeFileDiff(path, await object(oldFiles.get(path)), await object(newFiles.get(path)))));
|
|
@@ -32187,11 +32370,11 @@ async function assertListed(workspace, path) {
|
|
|
32187
32370
|
return file;
|
|
32188
32371
|
}
|
|
32189
32372
|
function safeWorkspacePath(workspace, path) {
|
|
32190
|
-
if (
|
|
32191
|
-
const root =
|
|
32192
|
-
const target =
|
|
32193
|
-
const child =
|
|
32194
|
-
if (child === "" || child.startsWith("..") ||
|
|
32373
|
+
if (isAbsolute21(path)) throw new Error("Git \u6587\u4EF6\u8DEF\u5F84\u5FC5\u987B\u662F\u9879\u76EE\u5185\u7684\u76F8\u5BF9\u8DEF\u5F84");
|
|
32374
|
+
const root = resolve40(workspace);
|
|
32375
|
+
const target = resolve40(root, path);
|
|
32376
|
+
const child = relative28(root, target);
|
|
32377
|
+
if (child === "" || child.startsWith("..") || isAbsolute21(child)) throw new Error("\u62D2\u7EDD\u8BBF\u95EE\u9879\u76EE\u4E4B\u5916\u7684\u8DEF\u5F84");
|
|
32195
32378
|
return target;
|
|
32196
32379
|
}
|
|
32197
32380
|
function safeGitTarget(value) {
|
|
@@ -32203,13 +32386,13 @@ function safeGitTarget(value) {
|
|
|
32203
32386
|
// src/desktop/worktree-manager.ts
|
|
32204
32387
|
import { randomUUID as randomUUID28 } from "crypto";
|
|
32205
32388
|
import { mkdir as mkdir28, readFile as readFile38, readdir as readdir14, rm as rm17, stat as stat17, writeFile as writeFile18 } from "fs/promises";
|
|
32206
|
-
import { basename as basename11, isAbsolute as
|
|
32389
|
+
import { basename as basename11, isAbsolute as isAbsolute22, join as join48, relative as relative29, resolve as resolve41, sep as sep29 } from "path";
|
|
32207
32390
|
var DesktopWorktreeManager = class {
|
|
32208
32391
|
#repository;
|
|
32209
32392
|
#storage;
|
|
32210
32393
|
constructor(options) {
|
|
32211
|
-
this.#repository =
|
|
32212
|
-
this.#storage =
|
|
32394
|
+
this.#repository = resolve41(options.repository);
|
|
32395
|
+
this.#storage = resolve41(options.storage);
|
|
32213
32396
|
}
|
|
32214
32397
|
async create(label) {
|
|
32215
32398
|
if (!await isGitRepository(this.#repository)) throw new Error("Task worktrees require a Git repository");
|
|
@@ -32290,21 +32473,21 @@ var DesktopWorktreeManager = class {
|
|
|
32290
32473
|
return this.#safeStoragePath(`${id}.json`);
|
|
32291
32474
|
}
|
|
32292
32475
|
#safeStoragePath(name) {
|
|
32293
|
-
const target =
|
|
32294
|
-
const delta =
|
|
32295
|
-
if (delta === "" || delta === ".." || delta.startsWith(`..${
|
|
32476
|
+
const target = resolve41(this.#storage, name);
|
|
32477
|
+
const delta = relative29(this.#storage, target);
|
|
32478
|
+
if (delta === "" || delta === ".." || delta.startsWith(`..${sep29}`) || isAbsolute22(delta)) throw new Error("Worktree path escapes storage");
|
|
32296
32479
|
return target;
|
|
32297
32480
|
}
|
|
32298
32481
|
};
|
|
32299
32482
|
function validateStored(value, repository, storage) {
|
|
32300
32483
|
if (typeof value !== "object" || value === null) throw new Error("Invalid worktree metadata");
|
|
32301
32484
|
const item = value;
|
|
32302
|
-
if (item.version !== 1 || item.mode !== "worktree" || typeof item.id !== "string" || typeof item.path !== "string" || typeof item.branch !== "string" || typeof item.createdAt !== "string" ||
|
|
32485
|
+
if (item.version !== 1 || item.mode !== "worktree" || typeof item.id !== "string" || typeof item.path !== "string" || typeof item.branch !== "string" || typeof item.createdAt !== "string" || resolve41(item.repository ?? "") !== repository) {
|
|
32303
32486
|
throw new Error("Invalid worktree metadata");
|
|
32304
32487
|
}
|
|
32305
|
-
const path =
|
|
32306
|
-
const delta =
|
|
32307
|
-
if (delta === "" || delta === ".." || delta.startsWith(`..${
|
|
32488
|
+
const path = resolve41(item.path);
|
|
32489
|
+
const delta = relative29(storage, path);
|
|
32490
|
+
if (delta === "" || delta === ".." || delta.startsWith(`..${sep29}`) || isAbsolute22(delta) || basename11(path) !== item.id) {
|
|
32308
32491
|
throw new Error("Worktree metadata escapes storage");
|
|
32309
32492
|
}
|
|
32310
32493
|
return { version: 1, id: item.id, mode: "worktree", repository, path, branch: item.branch, createdAt: item.createdAt };
|
|
@@ -32313,15 +32496,15 @@ function validateStored(value, repository, storage) {
|
|
|
32313
32496
|
// src/desktop/workbench-service.ts
|
|
32314
32497
|
import { readFile as readFile39, readdir as readdir15, stat as stat18 } from "fs/promises";
|
|
32315
32498
|
import { homedir as homedir6 } from "os";
|
|
32316
|
-
import { join as join49, resolve as
|
|
32499
|
+
import { join as join49, resolve as resolve42 } from "path";
|
|
32317
32500
|
var MAX_TEXT = 128e3;
|
|
32318
32501
|
var MAX_ITEMS = 200;
|
|
32319
32502
|
var DesktopWorkbenchService = class {
|
|
32320
32503
|
#workspace;
|
|
32321
32504
|
#home;
|
|
32322
32505
|
constructor(workspace, home = homedir6()) {
|
|
32323
|
-
this.#workspace =
|
|
32324
|
-
this.#home =
|
|
32506
|
+
this.#workspace = resolve42(workspace);
|
|
32507
|
+
this.#home = resolve42(home);
|
|
32325
32508
|
}
|
|
32326
32509
|
async inspect(sessionId) {
|
|
32327
32510
|
const [goals, instructions, permissionFiles, audit, context] = await Promise.all([
|
|
@@ -32372,7 +32555,7 @@ var DesktopWorkbenchService = class {
|
|
|
32372
32555
|
}
|
|
32373
32556
|
async #permissionFiles() {
|
|
32374
32557
|
const candidates = [
|
|
32375
|
-
...process.env.FLAVOR_MANAGED_PERMISSIONS ? [{ tier: "managed", path:
|
|
32558
|
+
...process.env.FLAVOR_MANAGED_PERMISSIONS ? [{ tier: "managed", path: resolve42(process.env.FLAVOR_MANAGED_PERMISSIONS), content: "" }] : [],
|
|
32376
32559
|
{ tier: "user", path: join49(this.#home, ".flavor-code", "permissions.json"), content: "" },
|
|
32377
32560
|
{ tier: "project", path: join49(this.#workspace, ".flavor", "permissions.json"), content: "" },
|
|
32378
32561
|
{ tier: "local", path: join49(this.#workspace, ".flavor", "permissions.local.json"), content: "" }
|
|
@@ -32445,14 +32628,14 @@ function redact2(value, key = "") {
|
|
|
32445
32628
|
|
|
32446
32629
|
// src/desktop/main.ts
|
|
32447
32630
|
installCrashGuard();
|
|
32448
|
-
var moduleDirectory =
|
|
32631
|
+
var moduleDirectory = dirname23(fileURLToPath3(import.meta.url));
|
|
32449
32632
|
var developmentUrl = process.env.FLAVOR_DESKTOP_DEV_URL;
|
|
32450
32633
|
var mainWindow;
|
|
32451
32634
|
var appMenu;
|
|
32452
32635
|
var quitting = false;
|
|
32453
32636
|
function logStartup(step, detail) {
|
|
32454
32637
|
try {
|
|
32455
|
-
const logPath = join50(
|
|
32638
|
+
const logPath = join50(dirname23(process.execPath), "flavor-code-startup.log");
|
|
32456
32639
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
32457
32640
|
appendFileSync(logPath, `[${ts}] ${step}${detail ? ` | ${detail}` : ""}
|
|
32458
32641
|
`);
|
|
@@ -32502,7 +32685,7 @@ function sessionMetaKey(workspace, sessionId) {
|
|
|
32502
32685
|
return `${workspace}\0${sessionId}`;
|
|
32503
32686
|
}
|
|
32504
32687
|
function worktreeManager(workspace) {
|
|
32505
|
-
const key = createHash28("sha256").update(
|
|
32688
|
+
const key = createHash28("sha256").update(resolve43(workspace)).digest("hex").slice(0, 20);
|
|
32506
32689
|
return new DesktopWorktreeManager({ repository: workspace, storage: join50(app.getPath("userData"), "task-worktrees", key) });
|
|
32507
32690
|
}
|
|
32508
32691
|
function activeWorkingDirectory() {
|
|
@@ -32718,7 +32901,7 @@ async function assertDirectory(path) {
|
|
|
32718
32901
|
if (!(await stat19(path)).isDirectory()) throw new Error("The selected project is not a directory");
|
|
32719
32902
|
}
|
|
32720
32903
|
async function openWorkspace(path) {
|
|
32721
|
-
const workspace =
|
|
32904
|
+
const workspace = resolve43(path);
|
|
32722
32905
|
await assertDirectory(workspace);
|
|
32723
32906
|
let project = managedProjects.get(workspace);
|
|
32724
32907
|
if (project === void 0) {
|
|
@@ -32729,10 +32912,10 @@ async function openWorkspace(path) {
|
|
|
32729
32912
|
activeWorkspace = workspace;
|
|
32730
32913
|
controller = nextController;
|
|
32731
32914
|
project.snapshot = await nextController.openWorkspace(workspace);
|
|
32732
|
-
for (const saved of workbench.taskCheckouts.filter((item) =>
|
|
32915
|
+
for (const saved of workbench.taskCheckouts.filter((item) => resolve43(item.workspace) === workspace)) {
|
|
32733
32916
|
try {
|
|
32734
32917
|
await assertDirectory(saved.workingDirectory);
|
|
32735
|
-
const task = { environment: "worktree", workingDirectory:
|
|
32918
|
+
const task = { environment: "worktree", workingDirectory: resolve43(saved.workingDirectory), worktreeId: saved.worktreeId };
|
|
32736
32919
|
task.controller = createDesktopController(workspace, task);
|
|
32737
32920
|
task.snapshot = await task.controller.openWorkspace(task.workingDirectory);
|
|
32738
32921
|
task.sessionId = saved.sessionId;
|
|
@@ -32862,7 +33045,7 @@ function installIpcHandlers() {
|
|
|
32862
33045
|
} catch {
|
|
32863
33046
|
}
|
|
32864
33047
|
}
|
|
32865
|
-
if (persisted.workspace !== void 0 && managedProjects.has(
|
|
33048
|
+
if (persisted.workspace !== void 0 && managedProjects.has(resolve43(persisted.workspace))) {
|
|
32866
33049
|
try {
|
|
32867
33050
|
return await openWorkspace(persisted.workspace);
|
|
32868
33051
|
} catch {
|
|
@@ -32923,7 +33106,7 @@ function installIpcHandlers() {
|
|
|
32923
33106
|
});
|
|
32924
33107
|
ipcMain.handle(DESKTOP_CHANNELS.updateProject, async (_event, value) => {
|
|
32925
33108
|
const input = UpdateProjectInputSchema.parse(value);
|
|
32926
|
-
const workspace =
|
|
33109
|
+
const workspace = resolve43(input.workspace);
|
|
32927
33110
|
if (!managedProjects.has(workspace)) throw new Error("\u9879\u76EE\u672A\u6253\u5F00");
|
|
32928
33111
|
workbench.projectMeta[workspace] = { ...workbench.projectMeta[workspace], ...input.label === void 0 ? {} : { label: input.label || void 0 }, ...input.pinned === void 0 ? {} : { pinned: input.pinned } };
|
|
32929
33112
|
await savePersistedProjects();
|
|
@@ -32931,7 +33114,7 @@ function installIpcHandlers() {
|
|
|
32931
33114
|
});
|
|
32932
33115
|
ipcMain.handle(DESKTOP_CHANNELS.closeProject, async (_event, value) => {
|
|
32933
33116
|
const input = CloseProjectInputSchema.parse(value);
|
|
32934
|
-
const workspace =
|
|
33117
|
+
const workspace = resolve43(input.workspace);
|
|
32935
33118
|
const project = managedProjects.get(workspace);
|
|
32936
33119
|
if (project === void 0) return decorateSnapshot(controller.snapshot());
|
|
32937
33120
|
if ([...project.tasks].some((task) => task.snapshot.activeSession?.busy) && !input.force) throw new Error("\u9879\u76EE\u4E2D\u4ECD\u6709\u4EFB\u52A1\u8FD0\u884C\uFF1B\u786E\u8BA4\u540E\u518D\u5173\u95ED\u9879\u76EE");
|
|
@@ -32947,12 +33130,12 @@ function installIpcHandlers() {
|
|
|
32947
33130
|
return decorateSnapshot(controller.snapshot());
|
|
32948
33131
|
});
|
|
32949
33132
|
ipcMain.handle(DESKTOP_CHANNELS.revealProject, async (_event, value) => {
|
|
32950
|
-
const workspace =
|
|
33133
|
+
const workspace = resolve43(ProjectPathInputSchema.parse(value).workspace);
|
|
32951
33134
|
await assertDirectory(workspace);
|
|
32952
33135
|
await shell.openPath(workspace);
|
|
32953
33136
|
});
|
|
32954
33137
|
ipcMain.handle(DESKTOP_CHANNELS.copyProjectPath, async (_event, value) => {
|
|
32955
|
-
clipboard.writeText(
|
|
33138
|
+
clipboard.writeText(resolve43(ProjectPathInputSchema.parse(value).workspace));
|
|
32956
33139
|
});
|
|
32957
33140
|
ipcMain.handle(DESKTOP_CHANNELS.dismissRecovery, async (_event, value) => {
|
|
32958
33141
|
const { sessionId } = DeleteSessionInputSchema.parse(value);
|