flavor-code 1.3.23 → 1.4.0-beta.1
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/README.md +437 -434
- package/README.zh-CN.md +434 -431
- package/dist/{app-NTEUPJKJ.js → app-C5ZQUOKJ.js} +43 -155
- package/dist/astgraph/grammars.mjs +68 -68
- package/dist/astgraph/vendor/tree-sitter.js +3980 -3980
- package/dist/astgraph/vendor/zod/LICENSE +21 -21
- package/dist/astgraph/vendor/zod/index.js +4 -4
- package/dist/astgraph/vendor/zod/locales/index.js +1 -1
- package/dist/astgraph/vendor/zod/locales/package.json +7 -7
- package/dist/astgraph/vendor/zod/package.json +135 -135
- package/dist/astgraph/vendor/zod/v4/classic/checks.js +1 -1
- package/dist/astgraph/vendor/zod/v4/classic/coerce.js +17 -17
- package/dist/astgraph/vendor/zod/v4/classic/compat.js +31 -31
- package/dist/astgraph/vendor/zod/v4/classic/errors.js +48 -48
- package/dist/astgraph/vendor/zod/v4/classic/external.js +20 -20
- package/dist/astgraph/vendor/zod/v4/classic/from-json-schema.js +599 -599
- package/dist/astgraph/vendor/zod/v4/classic/index.js +4 -4
- package/dist/astgraph/vendor/zod/v4/classic/iso.js +30 -30
- package/dist/astgraph/vendor/zod/v4/classic/package.json +7 -7
- package/dist/astgraph/vendor/zod/v4/classic/parse.js +15 -15
- package/dist/astgraph/vendor/zod/v4/classic/schemas.js +1395 -1395
- package/dist/astgraph/vendor/zod/v4/core/api.js +1087 -1087
- package/dist/astgraph/vendor/zod/v4/core/checks.js +575 -575
- package/dist/astgraph/vendor/zod/v4/core/core.js +78 -78
- package/dist/astgraph/vendor/zod/v4/core/doc.js +35 -35
- package/dist/astgraph/vendor/zod/v4/core/errors.js +185 -185
- package/dist/astgraph/vendor/zod/v4/core/index.js +16 -16
- package/dist/astgraph/vendor/zod/v4/core/json-schema-generator.js +95 -95
- package/dist/astgraph/vendor/zod/v4/core/json-schema-processors.js +601 -601
- package/dist/astgraph/vendor/zod/v4/core/json-schema.js +1 -1
- package/dist/astgraph/vendor/zod/v4/core/package.json +7 -7
- package/dist/astgraph/vendor/zod/v4/core/parse.js +93 -93
- package/dist/astgraph/vendor/zod/v4/core/regexes.js +139 -139
- package/dist/astgraph/vendor/zod/v4/core/registries.js +51 -51
- package/dist/astgraph/vendor/zod/v4/core/schemas.js +2239 -2239
- package/dist/astgraph/vendor/zod/v4/core/standard-schema.js +1 -1
- package/dist/astgraph/vendor/zod/v4/core/to-json-schema.js +448 -448
- package/dist/astgraph/vendor/zod/v4/core/util.js +674 -674
- package/dist/astgraph/vendor/zod/v4/core/versions.js +5 -5
- package/dist/astgraph/vendor/zod/v4/index.js +3 -3
- package/dist/astgraph/vendor/zod/v4/locales/ar.js +106 -106
- package/dist/astgraph/vendor/zod/v4/locales/az.js +105 -105
- package/dist/astgraph/vendor/zod/v4/locales/be.js +156 -156
- package/dist/astgraph/vendor/zod/v4/locales/bg.js +120 -120
- package/dist/astgraph/vendor/zod/v4/locales/ca.js +107 -107
- package/dist/astgraph/vendor/zod/v4/locales/cs.js +111 -111
- package/dist/astgraph/vendor/zod/v4/locales/da.js +115 -115
- package/dist/astgraph/vendor/zod/v4/locales/de.js +108 -108
- package/dist/astgraph/vendor/zod/v4/locales/el.js +109 -109
- package/dist/astgraph/vendor/zod/v4/locales/en.js +113 -113
- package/dist/astgraph/vendor/zod/v4/locales/eo.js +109 -109
- package/dist/astgraph/vendor/zod/v4/locales/es.js +132 -132
- package/dist/astgraph/vendor/zod/v4/locales/fa.js +114 -114
- package/dist/astgraph/vendor/zod/v4/locales/fi.js +112 -112
- package/dist/astgraph/vendor/zod/v4/locales/fr-CA.js +107 -107
- package/dist/astgraph/vendor/zod/v4/locales/fr.js +125 -125
- package/dist/astgraph/vendor/zod/v4/locales/he.js +214 -214
- package/dist/astgraph/vendor/zod/v4/locales/hr.js +122 -122
- package/dist/astgraph/vendor/zod/v4/locales/hu.js +108 -108
- package/dist/astgraph/vendor/zod/v4/locales/hy.js +147 -147
- package/dist/astgraph/vendor/zod/v4/locales/id.js +106 -106
- package/dist/astgraph/vendor/zod/v4/locales/index.js +52 -52
- package/dist/astgraph/vendor/zod/v4/locales/is.js +109 -109
- package/dist/astgraph/vendor/zod/v4/locales/it.js +108 -108
- package/dist/astgraph/vendor/zod/v4/locales/ja.js +107 -107
- package/dist/astgraph/vendor/zod/v4/locales/ka.js +112 -112
- package/dist/astgraph/vendor/zod/v4/locales/kh.js +5 -5
- package/dist/astgraph/vendor/zod/v4/locales/km.js +110 -110
- package/dist/astgraph/vendor/zod/v4/locales/ko.js +111 -111
- package/dist/astgraph/vendor/zod/v4/locales/lt.js +203 -203
- package/dist/astgraph/vendor/zod/v4/locales/mk.js +109 -109
- package/dist/astgraph/vendor/zod/v4/locales/ms.js +107 -107
- package/dist/astgraph/vendor/zod/v4/locales/nl.js +110 -110
- package/dist/astgraph/vendor/zod/v4/locales/no.js +108 -108
- package/dist/astgraph/vendor/zod/v4/locales/ota.js +109 -109
- package/dist/astgraph/vendor/zod/v4/locales/package.json +7 -7
- package/dist/astgraph/vendor/zod/v4/locales/pl.js +109 -109
- package/dist/astgraph/vendor/zod/v4/locales/ps.js +114 -114
- package/dist/astgraph/vendor/zod/v4/locales/pt.js +108 -108
- package/dist/astgraph/vendor/zod/v4/locales/ro.js +119 -119
- package/dist/astgraph/vendor/zod/v4/locales/ru.js +156 -156
- package/dist/astgraph/vendor/zod/v4/locales/sl.js +109 -109
- package/dist/astgraph/vendor/zod/v4/locales/sv.js +110 -110
- package/dist/astgraph/vendor/zod/v4/locales/ta.js +110 -110
- package/dist/astgraph/vendor/zod/v4/locales/th.js +110 -110
- package/dist/astgraph/vendor/zod/v4/locales/tr.js +105 -105
- package/dist/astgraph/vendor/zod/v4/locales/ua.js +5 -5
- package/dist/astgraph/vendor/zod/v4/locales/uk.js +108 -108
- package/dist/astgraph/vendor/zod/v4/locales/ur.js +110 -110
- package/dist/astgraph/vendor/zod/v4/locales/uz.js +110 -110
- package/dist/astgraph/vendor/zod/v4/locales/vi.js +108 -108
- package/dist/astgraph/vendor/zod/v4/locales/yo.js +107 -107
- package/dist/astgraph/vendor/zod/v4/locales/zh-CN.js +109 -109
- package/dist/astgraph/vendor/zod/v4/locales/zh-TW.js +107 -107
- package/dist/astgraph/vendor/zod/v4/mini/checks.js +1 -1
- package/dist/astgraph/vendor/zod/v4/mini/coerce.js +22 -22
- package/dist/astgraph/vendor/zod/v4/mini/external.js +14 -14
- package/dist/astgraph/vendor/zod/v4/mini/index.js +3 -3
- package/dist/astgraph/vendor/zod/v4/mini/iso.js +34 -34
- package/dist/astgraph/vendor/zod/v4/mini/package.json +7 -7
- package/dist/astgraph/vendor/zod/v4/mini/parse.js +1 -1
- package/dist/astgraph/vendor/zod/v4/mini/schemas.js +961 -961
- package/dist/astgraph/vendor/zod/v4/package.json +7 -7
- package/dist/{chunk-7OICVL2M.js → chunk-2BF7CVWZ.js} +0 -42
- package/dist/{chunk-RQTYHUMK.js → chunk-3WCDZ6ZL.js} +15 -0
- package/dist/{chunk-Z62P4LL7.js → chunk-6MNOZ2TX.js} +8962 -8422
- package/dist/{chunk-VEUSROUQ.js → chunk-FOTMP4ZS.js} +1 -1
- package/dist/{chunk-SUSLIHXO.js → chunk-ODRWR4KK.js} +1 -1
- package/dist/{chunk-RPFWB45Y.js → chunk-URUVFECH.js} +1 -1
- package/dist/{claude-ink-6CEG2XZZ.js → claude-ink-LP32NSX5.js} +2 -3
- package/dist/cli-main.js +27 -11
- package/dist/config/load.d.ts +2 -0
- package/dist/desktop/main.js +1092 -541
- package/dist/desktop-renderer/assets/index-Bk6FYhyh.css +1 -0
- package/dist/desktop-renderer/assets/{index-D75ozXnZ.js → index-CqRIr-hz.js} +3 -3
- package/dist/desktop-renderer/assets/{interactive-terminal-CTFo4fK9.js → interactive-terminal-C4o7MNPO.js} +1 -1
- package/dist/desktop-renderer/index.html +2 -2
- package/dist/doctor.d.ts +38 -0
- package/dist/execution/docker.d.ts +5 -1
- package/dist/execution/types.d.ts +11 -0
- package/dist/jobs/registry.d.ts +1 -0
- package/dist/{load-4FWNXE6F.js → load-V2UIYYQI.js} +1 -1
- package/dist/sdk/index.js +4 -4
- package/dist/tools/shell.d.ts +17 -12
- package/dist/tools/types.d.ts +1 -0
- package/dist/ui/commands.d.ts +1 -1
- package/dist/ui/session.d.ts +1 -0
- package/dist/update/check.d.ts +20 -0
- package/dist/utils/semver.d.ts +3 -0
- package/dist/utils/spawn-executable.d.ts +2 -0
- package/package.json +1 -1
- package//346/212/200/346/234/257/346/226/271/346/241/210/346/212/245/345/221/212.md +5848 -5848
- package/dist/chunk-OTYFL4AG.js +0 -16
- package/dist/desktop-renderer/assets/index-ZZsUx916.css +0 -1
package/dist/desktop/main.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// src/desktop/main.ts
|
|
2
2
|
import { appendFileSync } from "fs";
|
|
3
3
|
import { createHash as createHash28, randomUUID as randomUUID29 } from "crypto";
|
|
4
|
-
import { readFile as
|
|
5
|
-
import { basename as
|
|
4
|
+
import { readFile as readFile40, stat as stat19, writeFile as writeFile19 } from "fs/promises";
|
|
5
|
+
import { basename as basename12, dirname as dirname25, join as join50, resolve as resolve42 } from "path";
|
|
6
6
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
7
7
|
import { app, BrowserWindow as BrowserWindow2, clipboard, dialog, ipcMain, Menu, nativeImage, Notification, shell } from "electron";
|
|
8
8
|
|
|
@@ -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((resolve43) => setTimeout(resolve43, 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((resolve43) => setTimeout(resolve43, 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((resolve43) => setTimeout(resolve43, 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((resolve43) => setTimeout(resolve43, 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((resolve43) => setTimeout(resolve43, 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((resolve43) => setTimeout(resolve43, 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((resolve43, 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(() => resolve43({ ...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) => {
|
|
@@ -4225,11 +4225,11 @@ ${summarizeReport(report2)}`).join("\n\n")
|
|
|
4225
4225
|
}
|
|
4226
4226
|
|
|
4227
4227
|
// src/production.ts
|
|
4228
|
-
import { readFile as
|
|
4228
|
+
import { readFile as readFile28 } from "fs/promises";
|
|
4229
4229
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
4230
4230
|
import { createHash as createHash21, randomUUID as randomUUID25 } from "crypto";
|
|
4231
|
-
import { homedir as
|
|
4232
|
-
import { basename as
|
|
4231
|
+
import { homedir as homedir3, release as osRelease, tmpdir as tmpdir4, version as osVersion } from "os";
|
|
4232
|
+
import { basename as basename9, dirname as dirname24, join as join37, relative as relative24, resolve as resolve35, sep as sep24 } 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((resolve43, reject) => {
|
|
4446
4446
|
let settled = false;
|
|
4447
4447
|
const timer = setTimeout(() => {
|
|
4448
4448
|
if (settled) return;
|
|
4449
4449
|
settled = true;
|
|
4450
|
-
|
|
4450
|
+
resolve43({ 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
|
+
resolve43({ 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((resolve43, 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
|
+
resolve43(value);
|
|
4480
4480
|
},
|
|
4481
4481
|
(error) => {
|
|
4482
4482
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -5000,7 +5000,7 @@ async function loadConfig(options) {
|
|
|
5000
5000
|
process.env[key] = value;
|
|
5001
5001
|
}
|
|
5002
5002
|
}
|
|
5003
|
-
await seedGlobalEnv(globalEnvPath);
|
|
5003
|
+
if (options.seedGlobalEnv !== false) await seedGlobalEnv(globalEnvPath);
|
|
5004
5004
|
const projectRead = await readJsonIfPresent(projectPath);
|
|
5005
5005
|
const projectConfig = projectRead?.value;
|
|
5006
5006
|
if (projectRead) sources.push(projectRead.source);
|
|
@@ -6525,10 +6525,10 @@ function sanitize(message2, secrets) {
|
|
|
6525
6525
|
}
|
|
6526
6526
|
function waitForRetry(delayMs, signal) {
|
|
6527
6527
|
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
6528
|
-
return new Promise((
|
|
6528
|
+
return new Promise((resolve43, reject) => {
|
|
6529
6529
|
const timer = setTimeout(() => {
|
|
6530
6530
|
signal?.removeEventListener("abort", onAbort);
|
|
6531
|
-
|
|
6531
|
+
resolve43();
|
|
6532
6532
|
}, delayMs);
|
|
6533
6533
|
const onAbort = () => {
|
|
6534
6534
|
clearTimeout(timer);
|
|
@@ -7166,10 +7166,10 @@ function serializeToolInput(input) {
|
|
|
7166
7166
|
}
|
|
7167
7167
|
function waitForRetry2(delayMs, signal) {
|
|
7168
7168
|
if (signal?.aborted) return Promise.reject(signal.reason);
|
|
7169
|
-
return new Promise((
|
|
7169
|
+
return new Promise((resolve43, reject) => {
|
|
7170
7170
|
const timer = setTimeout(() => {
|
|
7171
7171
|
signal?.removeEventListener("abort", onAbort);
|
|
7172
|
-
|
|
7172
|
+
resolve43();
|
|
7173
7173
|
}, delayMs);
|
|
7174
7174
|
const onAbort = () => {
|
|
7175
7175
|
clearTimeout(timer);
|
|
@@ -8026,8 +8026,8 @@ var ToolRuntime = class {
|
|
|
8026
8026
|
const limited = await this.#limitOutput(tool, content);
|
|
8027
8027
|
return typeof limited === "string" ? limited : serializeToolOutput(limited);
|
|
8028
8028
|
}
|
|
8029
|
-
async #fail(toolCallId, tool, input, agent, code,
|
|
8030
|
-
const result = { ok: false, error: { code, message:
|
|
8029
|
+
async #fail(toolCallId, tool, input, agent, code, errorMessage4) {
|
|
8030
|
+
const result = { ok: false, error: { code, message: errorMessage4 } };
|
|
8031
8031
|
try {
|
|
8032
8032
|
await this.#hooks.emit({
|
|
8033
8033
|
version: 1,
|
|
@@ -8715,16 +8715,16 @@ var HookDecisionSchema = z16.object({
|
|
|
8715
8715
|
});
|
|
8716
8716
|
|
|
8717
8717
|
// src/utils/spawn-executable.ts
|
|
8718
|
-
import { existsSync as existsSync3, statSync as statSync2 } from "fs";
|
|
8719
|
-
import { extname as extname2, isAbsolute as isAbsolute6, resolve as resolve9 } from "path";
|
|
8720
|
-
var
|
|
8718
|
+
import { accessSync, constants as constants3, existsSync as existsSync3, statSync as statSync2 } from "fs";
|
|
8719
|
+
import { delimiter, extname as extname2, isAbsolute as isAbsolute6, resolve as resolve9 } from "path";
|
|
8720
|
+
var WINDOWS_BATCH = /\.(?:bat|cmd)$/iu;
|
|
8721
8721
|
var WINDOWS_CMD_SHIM = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/iu;
|
|
8722
8722
|
var CMD_META = /([()\][%!^"`<>&|;, *?])/gu;
|
|
8723
8723
|
function prepareSpawnInvocation(command, args, options = {}) {
|
|
8724
8724
|
if ((options.platform ?? process.platform) !== "win32") return { command, args: [...args] };
|
|
8725
8725
|
const env = options.env ?? process.env;
|
|
8726
|
-
const commandFile =
|
|
8727
|
-
if (commandFile === void 0 ||
|
|
8726
|
+
const commandFile = resolveExecutablePath(command, options);
|
|
8727
|
+
if (commandFile === void 0 || !WINDOWS_BATCH.test(commandFile)) {
|
|
8728
8728
|
return { command: commandFile ?? command, args: [...args] };
|
|
8729
8729
|
}
|
|
8730
8730
|
const doubleEscape = WINDOWS_CMD_SHIM.test(commandFile);
|
|
@@ -8735,11 +8735,22 @@ function prepareSpawnInvocation(command, args, options = {}) {
|
|
|
8735
8735
|
windowsVerbatimArguments: true
|
|
8736
8736
|
};
|
|
8737
8737
|
}
|
|
8738
|
+
function resolveExecutablePath(command, options = {}) {
|
|
8739
|
+
const platform = options.platform ?? process.platform;
|
|
8740
|
+
const env = options.env ?? process.env;
|
|
8741
|
+
if (platform === "win32") return resolveWindowsCommand(command, options.cwd, env);
|
|
8742
|
+
const hasPath = isAbsolute6(command) || command.includes("/");
|
|
8743
|
+
const bases = hasPath ? [isAbsolute6(command) ? command : resolve9(options.cwd ?? process.cwd(), command)] : (env.PATH ?? "").split(delimiter).filter(Boolean).map((entry2) => resolve9(entry2, command));
|
|
8744
|
+
return bases.find(isExecutableFile);
|
|
8745
|
+
}
|
|
8738
8746
|
function resolveWindowsCommand(command, cwd, env) {
|
|
8739
8747
|
const pathValue = environmentValue(env, "PATH") ?? "";
|
|
8740
8748
|
const pathExt = (environmentValue(env, "PATHEXT") ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
|
|
8741
8749
|
const hasPath = isAbsolute6(command) || /[\\/]/u.test(command);
|
|
8742
|
-
const bases = hasPath ? [isAbsolute6(command) ? command : resolve9(cwd ?? process.cwd(), command)] :
|
|
8750
|
+
const bases = hasPath ? [isAbsolute6(command) ? command : resolve9(cwd ?? process.cwd(), command)] : [
|
|
8751
|
+
resolve9(cwd ?? process.cwd(), command),
|
|
8752
|
+
...pathValue.split(";").filter(Boolean).map((entry2) => resolve9(unquote(entry2), command))
|
|
8753
|
+
];
|
|
8743
8754
|
for (const base of bases) {
|
|
8744
8755
|
const candidates = extname2(base).length > 0 ? [base] : [base, ...pathExt.map((extension2) => `${base}${extension2}`)];
|
|
8745
8756
|
for (const candidate of candidates) {
|
|
@@ -8763,6 +8774,15 @@ function isRegularFile(path) {
|
|
|
8763
8774
|
return false;
|
|
8764
8775
|
}
|
|
8765
8776
|
}
|
|
8777
|
+
function isExecutableFile(path) {
|
|
8778
|
+
try {
|
|
8779
|
+
if (!existsSync3(path) || !statSync2(path).isFile()) return false;
|
|
8780
|
+
accessSync(path, constants3.X_OK);
|
|
8781
|
+
return true;
|
|
8782
|
+
} catch {
|
|
8783
|
+
return false;
|
|
8784
|
+
}
|
|
8785
|
+
}
|
|
8766
8786
|
function escapeCmdCommand(value) {
|
|
8767
8787
|
return value.replace(CMD_META, "^$1");
|
|
8768
8788
|
}
|
|
@@ -8878,7 +8898,7 @@ function decoratePayload(payload, envelope) {
|
|
|
8878
8898
|
return envelope === void 0 ? payload : { ...payload, ...envelope };
|
|
8879
8899
|
}
|
|
8880
8900
|
function runShellHandler(descriptor, event, signal) {
|
|
8881
|
-
return new Promise((
|
|
8901
|
+
return new Promise((resolve43, reject) => {
|
|
8882
8902
|
const env = { ...process.env, ...descriptor.env };
|
|
8883
8903
|
const invocation = prepareSpawnInvocation(descriptor.command, descriptor.args ?? [], { env });
|
|
8884
8904
|
const child = spawn(invocation.command, invocation.args, {
|
|
@@ -8924,7 +8944,7 @@ function runShellHandler(descriptor, event, signal) {
|
|
|
8924
8944
|
return;
|
|
8925
8945
|
}
|
|
8926
8946
|
try {
|
|
8927
|
-
|
|
8947
|
+
resolve43(HookDecisionSchema.parse(JSON.parse(stdout)));
|
|
8928
8948
|
} catch (error) {
|
|
8929
8949
|
reject(error);
|
|
8930
8950
|
}
|
|
@@ -8963,7 +8983,7 @@ function zodRecord(payload) {
|
|
|
8963
8983
|
// src/utils/execFileNoThrow.ts
|
|
8964
8984
|
import { execFile } from "child_process";
|
|
8965
8985
|
function execFileNoThrow(file, args, options = {}) {
|
|
8966
|
-
return new Promise((
|
|
8986
|
+
return new Promise((resolve43) => {
|
|
8967
8987
|
const child = execFile(file, args, {
|
|
8968
8988
|
timeout: options.timeout,
|
|
8969
8989
|
cwd: options.useCwd === false ? void 0 : process.cwd(),
|
|
@@ -8971,7 +8991,7 @@ function execFileNoThrow(file, args, options = {}) {
|
|
|
8971
8991
|
encoding: "utf8",
|
|
8972
8992
|
windowsHide: true
|
|
8973
8993
|
}, (error, stdout, stderr) => {
|
|
8974
|
-
|
|
8994
|
+
resolve43({
|
|
8975
8995
|
stdout,
|
|
8976
8996
|
stderr,
|
|
8977
8997
|
code: typeof error?.code === "number" ? error.code : error ? 1 : 0,
|
|
@@ -9143,14 +9163,14 @@ async function createIslandControlServer(options) {
|
|
|
9143
9163
|
void dispatch(line, socket, options, token);
|
|
9144
9164
|
});
|
|
9145
9165
|
});
|
|
9146
|
-
await new Promise((
|
|
9166
|
+
await new Promise((resolve43, reject) => {
|
|
9147
9167
|
const onError = (error) => {
|
|
9148
9168
|
server.removeListener("listening", onListening);
|
|
9149
9169
|
reject(error);
|
|
9150
9170
|
};
|
|
9151
9171
|
const onListening = () => {
|
|
9152
9172
|
server.removeListener("error", onError);
|
|
9153
|
-
|
|
9173
|
+
resolve43();
|
|
9154
9174
|
};
|
|
9155
9175
|
server.once("error", onError);
|
|
9156
9176
|
server.once("listening", onListening);
|
|
@@ -9221,11 +9241,11 @@ function write(socket, payload) {
|
|
|
9221
9241
|
}
|
|
9222
9242
|
}
|
|
9223
9243
|
function closeServer(server) {
|
|
9224
|
-
return new Promise((
|
|
9244
|
+
return new Promise((resolve43) => {
|
|
9225
9245
|
try {
|
|
9226
|
-
server.close(() =>
|
|
9246
|
+
server.close(() => resolve43());
|
|
9227
9247
|
} catch {
|
|
9228
|
-
|
|
9248
|
+
resolve43();
|
|
9229
9249
|
}
|
|
9230
9250
|
});
|
|
9231
9251
|
}
|
|
@@ -10117,8 +10137,8 @@ import { dirname as dirname8, join as join11 } from "path";
|
|
|
10117
10137
|
function normalizeError(message2) {
|
|
10118
10138
|
return String(message2 ?? "").replace(/\s+/g, " ").replace(/"[^"]*"/g, '"\u2026"').replace(/`[^`]*`/g, '"\u2026"').trim().slice(0, 160);
|
|
10119
10139
|
}
|
|
10120
|
-
function fingerprint(tool,
|
|
10121
|
-
return createHash9("sha1").update(`${tool}::${
|
|
10140
|
+
function fingerprint(tool, errorCode3, error) {
|
|
10141
|
+
return createHash9("sha1").update(`${tool}::${errorCode3 ?? ""}::${normalizeError(error)}`).digest("hex").slice(0, 12);
|
|
10122
10142
|
}
|
|
10123
10143
|
function argKeys(args) {
|
|
10124
10144
|
if (args === null || typeof args !== "object") return [];
|
|
@@ -10410,7 +10430,7 @@ var USAGE = [
|
|
|
10410
10430
|
" clear reset signals, done markers, and verified markers"
|
|
10411
10431
|
].join("\n");
|
|
10412
10432
|
function runCommand(command, cwd, timeoutMs) {
|
|
10413
|
-
return new Promise((
|
|
10433
|
+
return new Promise((resolve43) => {
|
|
10414
10434
|
const isWindows = process.platform === "win32";
|
|
10415
10435
|
const child = spawn2(isWindows ? "cmd.exe" : "/bin/sh", isWindows ? ["/d", "/s", "/c", command] : ["-c", command], {
|
|
10416
10436
|
cwd,
|
|
@@ -10420,7 +10440,7 @@ function runCommand(command, cwd, timeoutMs) {
|
|
|
10420
10440
|
let stderr = "";
|
|
10421
10441
|
const timer = setTimeout(() => {
|
|
10422
10442
|
child.kill();
|
|
10423
|
-
|
|
10443
|
+
resolve43({ ok: false, stdout, stderr: `${stderr}
|
|
10424
10444
|
[killed: exceeded ${timeoutMs}ms]`, code: "timeout" });
|
|
10425
10445
|
}, timeoutMs);
|
|
10426
10446
|
child.stdout.on("data", (chunk) => {
|
|
@@ -10431,12 +10451,12 @@ function runCommand(command, cwd, timeoutMs) {
|
|
|
10431
10451
|
});
|
|
10432
10452
|
child.on("error", (error) => {
|
|
10433
10453
|
clearTimeout(timer);
|
|
10434
|
-
|
|
10454
|
+
resolve43({ ok: false, stdout, stderr: `${stderr}
|
|
10435
10455
|
${error.message}`, code: "spawn" });
|
|
10436
10456
|
});
|
|
10437
10457
|
child.on("close", (code) => {
|
|
10438
10458
|
clearTimeout(timer);
|
|
10439
|
-
|
|
10459
|
+
resolve43({ ok: code === 0, stdout, stderr, code: code ?? "unknown" });
|
|
10440
10460
|
});
|
|
10441
10461
|
});
|
|
10442
10462
|
}
|
|
@@ -10513,12 +10533,12 @@ function createEvolveService(options) {
|
|
|
10513
10533
|
logger.warn(`prompt section failed \u2014 ${error instanceof Error ? error.message : String(error)}`);
|
|
10514
10534
|
}
|
|
10515
10535
|
}
|
|
10516
|
-
async function captureFailure(tool,
|
|
10536
|
+
async function captureFailure(tool, errorCode3, message2, args) {
|
|
10517
10537
|
toolErrors += 1;
|
|
10518
10538
|
runToolErrors[tool] = (runToolErrors[tool] ?? 0) + 1;
|
|
10519
10539
|
const { record: record3 } = await store.recordSignal({
|
|
10520
10540
|
tool,
|
|
10521
|
-
...
|
|
10541
|
+
...errorCode3 === void 0 ? {} : { errorCode: errorCode3 },
|
|
10522
10542
|
error: message2,
|
|
10523
10543
|
args
|
|
10524
10544
|
});
|
|
@@ -10559,8 +10579,12 @@ function createEvolveService(options) {
|
|
|
10559
10579
|
try {
|
|
10560
10580
|
const stderr = typeof shell2.stderr === "string" ? shell2.stderr : "";
|
|
10561
10581
|
const stdout = typeof shell2.stdout === "string" ? shell2.stdout : "";
|
|
10562
|
-
const
|
|
10563
|
-
|
|
10582
|
+
const diagnostic = shell2.diagnostic !== null && typeof shell2.diagnostic === "object" ? shell2.diagnostic : void 0;
|
|
10583
|
+
const diagnosticMessage = typeof diagnostic?.message === "string" ? diagnostic.message : "";
|
|
10584
|
+
const diagnosticKind = typeof diagnostic?.kind === "string" ? diagnostic.kind : void 0;
|
|
10585
|
+
const message2 = diagnosticMessage.trim().slice(0, 300) || stderr.trim().slice(0, 300) || stdout.trim().slice(0, 300) || (exitCode === void 0 ? "shell timed out" : `exit code ${exitCode}`);
|
|
10586
|
+
const errorCode3 = diagnosticKind === void 0 ? exitCode === void 0 ? "shell_exit_timeout" : `shell_exit_${exitCode}` : `shell_${diagnosticKind}`;
|
|
10587
|
+
await captureFailure(tool, errorCode3, message2, payload.input);
|
|
10564
10588
|
} catch (error) {
|
|
10565
10589
|
logger.warn(`shell capture failed \u2014 ${error instanceof Error ? error.message : String(error)}`);
|
|
10566
10590
|
}
|
|
@@ -12412,7 +12436,7 @@ function formatPriorGaps(gaps) {
|
|
|
12412
12436
|
}
|
|
12413
12437
|
|
|
12414
12438
|
// src/goal/store.ts
|
|
12415
|
-
import { constants as
|
|
12439
|
+
import { constants as constants4 } from "fs";
|
|
12416
12440
|
import { randomUUID as randomUUID11 } from "crypto";
|
|
12417
12441
|
import { lstat as lstat5, mkdir as mkdir11, open as open5, rename as rename4, rm as rm6 } from "fs/promises";
|
|
12418
12442
|
import { dirname as dirname10, join as join16, relative as relative9, resolve as resolve12, sep as sep9 } from "path";
|
|
@@ -12440,7 +12464,7 @@ var GoalStore = class {
|
|
|
12440
12464
|
const temporary = join16(this.#root, `.${state.id}.${process.pid}.${randomUUID11()}.tmp`);
|
|
12441
12465
|
let handle;
|
|
12442
12466
|
try {
|
|
12443
|
-
handle = await open5(temporary,
|
|
12467
|
+
handle = await open5(temporary, constants4.O_CREAT | constants4.O_EXCL | constants4.O_WRONLY, 384);
|
|
12444
12468
|
await handle.writeFile(body, "utf8");
|
|
12445
12469
|
await handle.sync();
|
|
12446
12470
|
await handle.close();
|
|
@@ -12547,7 +12571,7 @@ async function ensureWorktreeDirectoryIgnored(root) {
|
|
|
12547
12571
|
}
|
|
12548
12572
|
|
|
12549
12573
|
// src/loop/store.ts
|
|
12550
|
-
import { constants as
|
|
12574
|
+
import { constants as constants5 } from "fs";
|
|
12551
12575
|
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";
|
|
12552
12576
|
import { dirname as dirname11, join as join18, relative as relative10, resolve as resolve14, sep as sep10 } from "path";
|
|
12553
12577
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
@@ -12584,7 +12608,7 @@ var LoopStore = class {
|
|
|
12584
12608
|
const temporary = join18(directory, `.state.${process.pid}.${randomUUID12()}.tmp`);
|
|
12585
12609
|
let handle;
|
|
12586
12610
|
try {
|
|
12587
|
-
handle = await open6(temporary,
|
|
12611
|
+
handle = await open6(temporary, constants5.O_CREAT | constants5.O_EXCL | constants5.O_WRONLY, 384);
|
|
12588
12612
|
await handle.writeFile(body, "utf8");
|
|
12589
12613
|
await handle.sync();
|
|
12590
12614
|
await handle.close();
|
|
@@ -12618,7 +12642,7 @@ var LoopStore = class {
|
|
|
12618
12642
|
const next = previous.catch(() => void 0).then(async () => {
|
|
12619
12643
|
const directory = await this.#prepare(event.loopId);
|
|
12620
12644
|
const path = join18(directory, "events.jsonl");
|
|
12621
|
-
const handle = await open6(path,
|
|
12645
|
+
const handle = await open6(path, constants5.O_CREAT | constants5.O_APPEND | constants5.O_WRONLY, 384);
|
|
12622
12646
|
try {
|
|
12623
12647
|
await handle.writeFile(`${JSON.stringify(event)}
|
|
12624
12648
|
`, "utf8");
|
|
@@ -12691,11 +12715,12 @@ function isCode5(error, code) {
|
|
|
12691
12715
|
|
|
12692
12716
|
// src/loop/verifier.ts
|
|
12693
12717
|
import { readFile as readFile13 } from "fs/promises";
|
|
12694
|
-
import { join as
|
|
12718
|
+
import { join as join20 } from "path";
|
|
12695
12719
|
|
|
12696
12720
|
// src/tools/shell.ts
|
|
12697
|
-
import { spawn as spawn3
|
|
12698
|
-
import {
|
|
12721
|
+
import { spawn as spawn3 } from "child_process";
|
|
12722
|
+
import { existsSync as existsSync4 } from "fs";
|
|
12723
|
+
import { basename as basename5, delimiter as delimiter2, isAbsolute as isAbsolute9, join as join19, relative as relative11, resolve as resolve15, sep as sep11 } from "path";
|
|
12699
12724
|
import { z as z23 } from "zod";
|
|
12700
12725
|
|
|
12701
12726
|
// src/tools/jobs.ts
|
|
@@ -12784,8 +12809,8 @@ var TERMINATION_GRACE_MS = 250;
|
|
|
12784
12809
|
var TERMINATION_FAILURE_MS = 5e3;
|
|
12785
12810
|
var STREAM_CLOSE_GRACE_MS = 500;
|
|
12786
12811
|
var ShellInput = z23.object({
|
|
12787
|
-
command: z23.string().min(1),
|
|
12788
|
-
args: z23.array(z23.string()),
|
|
12812
|
+
command: z23.string().trim().min(1).refine((value) => !value.includes("\0"), "command cannot contain a null byte").describe("Executable name or path. Put each argument in args; use a shell expression here only for pipes, redirects, or other shell syntax."),
|
|
12813
|
+
args: z23.array(z23.string().refine((value) => !value.includes("\0"), "argument cannot contain a null byte").describe("One logical argument without surrounding shell quotes.")),
|
|
12789
12814
|
cwd: z23.string().min(1).nullable().optional(),
|
|
12790
12815
|
timeoutMs: z23.coerce.number().int().positive().max(864e5).nullable().optional(),
|
|
12791
12816
|
// Accepts booleans and their string forms (weak-typed models emit "true");
|
|
@@ -12793,23 +12818,39 @@ var ShellInput = z23.object({
|
|
|
12793
12818
|
background: z23.union([z23.boolean(), z23.string().refine((value) => value === "true" || value === "false")]).optional()
|
|
12794
12819
|
});
|
|
12795
12820
|
function normalizeShellCommand(input) {
|
|
12796
|
-
const
|
|
12797
|
-
|
|
12821
|
+
const command = unquoteToken(input.command.trim());
|
|
12822
|
+
const args = [...input.args];
|
|
12823
|
+
const executable = basename5(command).toLowerCase();
|
|
12824
|
+
if ((executable === "cmd" || executable === "cmd.exe") && /^\/[ck]$/iu.test(args[0] ?? "")) {
|
|
12825
|
+
return { command, args: [args[0], ...args.slice(1).map(unquoteToken)] };
|
|
12826
|
+
}
|
|
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
|
+
if (!/\s/u.test(command) || containsShellSyntax(command)) return { command, args };
|
|
12798
12834
|
const tokens2 = tokenizeCommandLine(command);
|
|
12799
|
-
if (tokens2.length <= 1) return { command, args
|
|
12835
|
+
if (tokens2.length <= 1) return { command, args };
|
|
12800
12836
|
const head = unquoteToken(tokens2[0]);
|
|
12801
|
-
if (head.includes("/") || head.includes("\\")) return { command, args
|
|
12837
|
+
if (head.includes("/") || head.includes("\\")) return { command, args };
|
|
12802
12838
|
return { command: head, args: [...tokens2.slice(1).map(unquoteToken), ...args] };
|
|
12803
12839
|
}
|
|
12804
12840
|
function tokenizeCommandLine(command) {
|
|
12805
12841
|
const tokens2 = [];
|
|
12806
12842
|
let current = "";
|
|
12807
|
-
let
|
|
12808
|
-
for (
|
|
12809
|
-
|
|
12810
|
-
|
|
12843
|
+
let quote;
|
|
12844
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
12845
|
+
const char = command[index];
|
|
12846
|
+
if (char === "\\" && quote === '"' && command[index + 1] !== void 0) {
|
|
12847
|
+
current += `${char}${command[index + 1]}`;
|
|
12848
|
+
index += 1;
|
|
12849
|
+
} else if (char === '"' || char === "'") {
|
|
12850
|
+
if (quote === void 0) quote = char;
|
|
12851
|
+
else if (quote === char) quote = void 0;
|
|
12811
12852
|
current += char;
|
|
12812
|
-
} else if (char
|
|
12853
|
+
} else if (/\s/u.test(char) && quote === void 0) {
|
|
12813
12854
|
if (current !== "") {
|
|
12814
12855
|
tokens2.push(current);
|
|
12815
12856
|
current = "";
|
|
@@ -12822,33 +12863,190 @@ function tokenizeCommandLine(command) {
|
|
|
12822
12863
|
return tokens2;
|
|
12823
12864
|
}
|
|
12824
12865
|
function unquoteToken(token) {
|
|
12825
|
-
|
|
12866
|
+
const trimmed = token.trim();
|
|
12867
|
+
if (trimmed.length < 2) return trimmed;
|
|
12868
|
+
const first = trimmed[0];
|
|
12869
|
+
return (first === '"' || first === "'") && trimmed.endsWith(first) ? trimmed.slice(1, -1) : trimmed;
|
|
12870
|
+
}
|
|
12871
|
+
function serializeCmdCommand(args) {
|
|
12872
|
+
return args.map((arg) => {
|
|
12873
|
+
const value = unquoteToken(arg);
|
|
12874
|
+
return /[\s&|<>^()]/u.test(value) ? `"${value.replace(/"/gu, '""')}"` : value;
|
|
12875
|
+
}).join(" ");
|
|
12876
|
+
}
|
|
12877
|
+
function serializeCmdPayload(args) {
|
|
12878
|
+
if (args.length === 1) return unquoteToken(args[0]);
|
|
12879
|
+
return args.map((arg) => /^(?:&&|\|\||[|&<>])$/u.test(arg) ? arg : serializeCmdCommand([arg])).join(" ");
|
|
12880
|
+
}
|
|
12881
|
+
function containsShellSyntax(command, platform = process.platform) {
|
|
12882
|
+
let quote;
|
|
12883
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
12884
|
+
const char = command[index];
|
|
12885
|
+
if (quote !== void 0) {
|
|
12886
|
+
if (char === quote) quote = void 0;
|
|
12887
|
+
else if (char === "\\" && quote === '"') index += 1;
|
|
12888
|
+
else if (char === "$" && quote === '"') return true;
|
|
12889
|
+
continue;
|
|
12890
|
+
}
|
|
12891
|
+
if (char === "\\" && platform !== "win32") {
|
|
12892
|
+
index += 1;
|
|
12893
|
+
continue;
|
|
12894
|
+
}
|
|
12895
|
+
if (char === '"' || char === "'") {
|
|
12896
|
+
quote = char;
|
|
12897
|
+
continue;
|
|
12898
|
+
}
|
|
12899
|
+
if (/[|&;<>`$*?()\n\r]/u.test(char)) return true;
|
|
12900
|
+
if (char === "[" && command.indexOf("]", index + 1) > index) return true;
|
|
12901
|
+
if (char === "~" && (index === 0 || /\s/u.test(command[index - 1])) && /[\\/]/u.test(command[index + 1] ?? "")) return true;
|
|
12902
|
+
if (platform === "win32" && char === "%" && /^%[A-Za-z_][A-Za-z0-9_]*%/u.test(command.slice(index))) return true;
|
|
12903
|
+
}
|
|
12904
|
+
return quote !== void 0;
|
|
12826
12905
|
}
|
|
12827
|
-
|
|
12828
|
-
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
|
|
12832
|
-
|
|
12906
|
+
function hasUnmatchedShellQuote(command) {
|
|
12907
|
+
let quote;
|
|
12908
|
+
for (let index = 0; index < command.length; index += 1) {
|
|
12909
|
+
const char = command[index];
|
|
12910
|
+
if (char === "\\" && quote !== "'") {
|
|
12911
|
+
index += 1;
|
|
12912
|
+
continue;
|
|
12913
|
+
}
|
|
12914
|
+
if (char !== '"' && char !== "'") continue;
|
|
12915
|
+
if (quote === void 0) quote = char;
|
|
12916
|
+
else if (quote === char) quote = void 0;
|
|
12833
12917
|
}
|
|
12918
|
+
return quote !== void 0;
|
|
12834
12919
|
}
|
|
12835
|
-
function
|
|
12836
|
-
if (
|
|
12837
|
-
|
|
12838
|
-
|
|
12920
|
+
function resolveRuntimeShell(platform = process.platform, environment = process.env) {
|
|
12921
|
+
if (platform === "win32") {
|
|
12922
|
+
for (const candidate of ["pwsh", "powershell"]) {
|
|
12923
|
+
const command2 = resolveExecutablePath(candidate, { platform: "win32", env: environment });
|
|
12924
|
+
if (command2 !== void 0) return { kind: candidate, command: command2 };
|
|
12925
|
+
}
|
|
12926
|
+
const configured = environment.ComSpec ?? environment.COMSPEC ?? "cmd.exe";
|
|
12927
|
+
const command = resolveExecutablePath(configured, { platform: "win32", env: environment });
|
|
12928
|
+
return command === void 0 ? void 0 : { kind: "cmd", command };
|
|
12929
|
+
}
|
|
12930
|
+
for (const candidate of [environment.SHELL, "/bin/sh"]) {
|
|
12931
|
+
if (!candidate) continue;
|
|
12932
|
+
const command = resolveExecutablePath(candidate, { platform, env: environment });
|
|
12933
|
+
if (command !== void 0) return { kind: "posix", command };
|
|
12934
|
+
}
|
|
12935
|
+
return void 0;
|
|
12839
12936
|
}
|
|
12840
12937
|
function psQuote(token) {
|
|
12841
12938
|
return `'${token.replace(/'/g, "''")}'`;
|
|
12842
12939
|
}
|
|
12843
|
-
function
|
|
12844
|
-
|
|
12845
|
-
|
|
12846
|
-
|
|
12940
|
+
function shellInvocation(input, cwd, environment) {
|
|
12941
|
+
const resolved = resolveExecutablePath(input.command, { cwd, env: environment });
|
|
12942
|
+
if (resolved !== void 0) {
|
|
12943
|
+
const executable = basename5(resolved).toLowerCase();
|
|
12944
|
+
if (process.platform === "win32" && (executable === "cmd" || executable === "cmd.exe")) {
|
|
12945
|
+
const commandFlag = input.args.findIndex((arg) => /^\/[ck]$/iu.test(arg));
|
|
12946
|
+
if (commandFlag >= 0 && input.args[commandFlag + 1] !== void 0) {
|
|
12947
|
+
const payload = serializeCmdPayload(input.args.slice(commandFlag + 1));
|
|
12948
|
+
return {
|
|
12949
|
+
command: resolved,
|
|
12950
|
+
args: [...input.args.slice(0, commandFlag + 1), `chcp 65001>nul & ${payload}`]
|
|
12951
|
+
};
|
|
12952
|
+
}
|
|
12953
|
+
}
|
|
12954
|
+
return prepareSpawnInvocation(resolved, input.args, { cwd, env: environment });
|
|
12955
|
+
}
|
|
12956
|
+
const isScript = containsShellSyntax(input.command);
|
|
12957
|
+
if (!isScript && (isAbsolute9(input.command) || /[\\/]/u.test(input.command))) {
|
|
12958
|
+
return { command: input.command, args: [...input.args] };
|
|
12959
|
+
}
|
|
12960
|
+
const shell2 = resolveRuntimeShell(process.platform, environment);
|
|
12961
|
+
if (shell2 === void 0) return { command: input.command, args: [...input.args] };
|
|
12962
|
+
const script = isScript ? appendShellArguments(shell2, input.command, input.args) : structuredShellScript(shell2, input.command, input.args);
|
|
12963
|
+
if (shell2.kind === "posix") return { command: shell2.command, args: ["-c", script] };
|
|
12964
|
+
if (shell2.kind === "cmd") return {
|
|
12965
|
+
command: shell2.command,
|
|
12966
|
+
args: ["/d", "/s", "/c", `chcp 65001>nul & ${script}`]
|
|
12967
|
+
};
|
|
12847
12968
|
const encoded = Buffer.from(script, "utf16le").toString("base64");
|
|
12848
|
-
return
|
|
12969
|
+
return {
|
|
12970
|
+
command: shell2.command,
|
|
12971
|
+
args: ["-NoLogo", "-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-EncodedCommand", encoded]
|
|
12972
|
+
};
|
|
12973
|
+
}
|
|
12974
|
+
function appendShellArguments(shell2, script, args) {
|
|
12975
|
+
if (args.length === 0) return script;
|
|
12976
|
+
if (shell2.kind === "posix") return `${script} ${args.map(posixQuote).join(" ")}`;
|
|
12977
|
+
if (shell2.kind === "cmd") return `${script} ${serializeCmdCommand(args)}`;
|
|
12978
|
+
return `${script} ${args.map(psQuote).join(" ")}`;
|
|
12979
|
+
}
|
|
12980
|
+
var POSIX_SHELL_BUILTINS = /* @__PURE__ */ new Set([".", ":", "alias", "cd", "command", "eval", "exec", "export", "read", "set", "source", "trap", "type", "ulimit", "umask", "unalias", "unset", "wait"]);
|
|
12981
|
+
function executionEnvironmentCommand(environment, command, args) {
|
|
12982
|
+
if (environment.kind !== "docker") return { command, args: [...args] };
|
|
12983
|
+
const script = containsShellSyntax(command, "linux") ? appendShellArguments({ kind: "posix", command: "/bin/sh" }, command, args) : POSIX_SHELL_BUILTINS.has(command) ? structuredShellScript({ kind: "posix", command: "/bin/sh" }, command, args) : void 0;
|
|
12984
|
+
return script === void 0 ? { command, args: [...args] } : { command: "/bin/sh", args: ["-c", script] };
|
|
12985
|
+
}
|
|
12986
|
+
function structuredShellScript(shell2, command, args) {
|
|
12987
|
+
if (shell2.kind === "posix") return [command, ...args].map(posixQuote).join(" ");
|
|
12988
|
+
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 }`;
|
|
12991
|
+
}
|
|
12992
|
+
function posixQuote(value) {
|
|
12993
|
+
return `'${value.replace(/'/gu, `'"'"'`)}'`;
|
|
12994
|
+
}
|
|
12995
|
+
function withShellDiagnostic(result, command, spawnError) {
|
|
12996
|
+
const diagnostic = classifyShellFailure(result, command, spawnError);
|
|
12997
|
+
if (diagnostic === void 0) return result;
|
|
12998
|
+
return { ...result, diagnostic };
|
|
12999
|
+
}
|
|
13000
|
+
function classifyShellFailure(result, command, spawnError) {
|
|
13001
|
+
if (result.terminationReason === "timeout") return {
|
|
13002
|
+
kind: "timeout",
|
|
13003
|
+
message: `Command exceeded its timeout and was terminated.`,
|
|
13004
|
+
hint: "Increase timeoutMs only if this command is expected to take longer."
|
|
13005
|
+
};
|
|
13006
|
+
if (result.terminationReason === "cancelled") return { kind: "cancelled", message: "Command was cancelled." };
|
|
13007
|
+
if (result.exitCode === 0 && result.signal === null && spawnError === void 0) return void 0;
|
|
13008
|
+
if (hasUnmatchedShellQuote(command)) return {
|
|
13009
|
+
kind: "shell-syntax",
|
|
13010
|
+
message: "The command contains an unmatched shell quote.",
|
|
13011
|
+
hint: "Close the quoted value, or pass it as one unquoted args item when shell expansion is not required."
|
|
13012
|
+
};
|
|
13013
|
+
const detail = `${spawnError?.message ?? ""}
|
|
13014
|
+
${result.stderr}`;
|
|
13015
|
+
const code = errorCode(spawnError);
|
|
13016
|
+
if (code === "ENOENT" && (isAbsolute9(command) || /[\\/]/u.test(command))) {
|
|
13017
|
+
return { kind: "path-not-found", message: `Executable path "${command}" does not exist.` };
|
|
13018
|
+
}
|
|
13019
|
+
if (code === "ENOENT" || /(?:not recognized as (?:an internal|(?:a|the) name)|command not found|not found\s*$|无法将.+识别为|不是内部或外部命令)/imu.test(detail)) {
|
|
13020
|
+
return {
|
|
13021
|
+
kind: "command-not-found",
|
|
13022
|
+
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."
|
|
13024
|
+
};
|
|
13025
|
+
}
|
|
13026
|
+
if (code === "EACCES" || /(?:permission denied|access is denied|拒绝访问)/iu.test(detail)) {
|
|
13027
|
+
return { kind: "permission-denied", message: `Command "${command}" could not be executed because access was denied.` };
|
|
13028
|
+
}
|
|
13029
|
+
if (/(?:no such file or directory|cannot find (?:the )?path|system cannot find|系统找不到指定的路径)/iu.test(detail)) {
|
|
13030
|
+
return { kind: "path-not-found", message: "The command referenced a path that does not exist." };
|
|
13031
|
+
}
|
|
13032
|
+
if (/(?:syntax error|unexpected token|parsererror|语法错误)/iu.test(detail)) {
|
|
13033
|
+
return {
|
|
13034
|
+
kind: "shell-syntax",
|
|
13035
|
+
message: "The selected runtime shell rejected the command syntax.",
|
|
13036
|
+
hint: "Pass normal arguments through args; reserve shell syntax for pipes, redirects, and operators."
|
|
13037
|
+
};
|
|
13038
|
+
}
|
|
13039
|
+
return {
|
|
13040
|
+
kind: "non-zero-exit",
|
|
13041
|
+
message: result.signal === null ? `Command exited with code ${result.exitCode ?? "unknown"}.` : `Command was terminated by signal ${result.signal}.`
|
|
13042
|
+
};
|
|
13043
|
+
}
|
|
13044
|
+
function errorCode(error) {
|
|
13045
|
+
return error instanceof Error && "code" in error ? String(error.code) : void 0;
|
|
12849
13046
|
}
|
|
12850
13047
|
function createShellTool(workspace, options = {}) {
|
|
12851
13048
|
const root = resolve15(workspace);
|
|
13049
|
+
const environment = shellEnvironment(root, options.environment ?? process.env);
|
|
12852
13050
|
const maxBytes = options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES2;
|
|
12853
13051
|
const defaultTimeoutMs = options.defaultTimeoutMs ?? DEFAULT_SHELL_TIMEOUT_MS;
|
|
12854
13052
|
if (!Number.isSafeInteger(maxBytes) || maxBytes <= 0) throw new Error("maxOutputBytes must be a positive integer");
|
|
@@ -12857,7 +13055,7 @@ function createShellTool(workspace, options = {}) {
|
|
|
12857
13055
|
}
|
|
12858
13056
|
return {
|
|
12859
13057
|
name: "Shell",
|
|
12860
|
-
description: "Run a bounded command
|
|
13058
|
+
description: "Run a bounded command reliably inside the workspace. Pass a bare executable in command and one unquoted logical value per args entry; shell syntax is supported only when command contains pipes, redirects, or operators. stdout/stderr are returned verbatim and failures include a structured diagnostic. background=true returns a managed job id.",
|
|
12861
13059
|
inputSchema: ShellInput,
|
|
12862
13060
|
paths: (input) => [workingDirectory(root, input.cwd ?? void 0)],
|
|
12863
13061
|
summarize: (input) => [input.command, ...input.args].join(" "),
|
|
@@ -12878,7 +13076,7 @@ function createShellTool(workspace, options = {}) {
|
|
|
12878
13076
|
jobKind: "shell",
|
|
12879
13077
|
label: [input.command, ...input.args].join(" "),
|
|
12880
13078
|
state: "running"
|
|
12881
|
-
} : { kind: "terminal", variant: "command", title: [input.command, ...input.args].join(" "), command: [input.command, ...input.args].join(" "), stdout: output.stdout, stderr: output.stderr, exitCode: output.exitCode, state: output.terminationReason === "cancelled" ? "cancelled" : output.exitCode === 0 ? "completed" : "failed", truncated: output.truncated },
|
|
13079
|
+
} : { kind: "terminal", variant: "command", title: [input.command, ...input.args].join(" "), command: [input.command, ...input.args].join(" "), stdout: output.stdout, stderr: output.stderr, ...output.diagnostic === void 0 ? {} : { diagnostic: output.diagnostic.message }, exitCode: output.exitCode, state: output.terminationReason === "cancelled" ? "cancelled" : output.exitCode === 0 ? "completed" : "failed", truncated: output.truncated },
|
|
12882
13080
|
execute: async (input, signal, context) => {
|
|
12883
13081
|
const normalized = normalizeShellCommand(input);
|
|
12884
13082
|
const effective = { ...input, command: normalized.command, args: normalized.args };
|
|
@@ -12896,19 +13094,23 @@ function createShellTool(workspace, options = {}) {
|
|
|
12896
13094
|
try {
|
|
12897
13095
|
let result2;
|
|
12898
13096
|
if (options.executionEnvironment === void 0) {
|
|
12899
|
-
result2 = await executeShell(root, { ...effective, timeoutMs }, cancellation.signal, maxBytes, (stream, text) => job.append(stream === "stderr" ? `[stderr] ${text}` : text));
|
|
13097
|
+
result2 = await executeShell(root, { ...effective, timeoutMs }, cancellation.signal, maxBytes, environment, (stream, text) => job.append(stream === "stderr" ? `[stderr] ${text}` : text));
|
|
12900
13098
|
} else {
|
|
13099
|
+
const invocation2 = executionEnvironmentCommand(options.executionEnvironment, effective.command, effective.args);
|
|
12901
13100
|
const raw = await options.executionEnvironment.exec({
|
|
12902
|
-
|
|
12903
|
-
args: effective.args,
|
|
13101
|
+
...invocation2,
|
|
12904
13102
|
cwd: workingDirectory(root, effective.cwd ?? void 0),
|
|
12905
|
-
timeoutMs
|
|
13103
|
+
timeoutMs,
|
|
13104
|
+
maxOutputBytes: maxBytes
|
|
12906
13105
|
}, cancellation.signal);
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
result2
|
|
13106
|
+
result2 = boundedExecutionResult(raw, maxBytes, effective.command);
|
|
13107
|
+
job.append(result2.stdout);
|
|
13108
|
+
if (result2.stderr !== "") job.append(`[stderr] ${result2.stderr}`);
|
|
12910
13109
|
}
|
|
12911
|
-
job.complete({
|
|
13110
|
+
job.complete({
|
|
13111
|
+
exitCode: result2.exitCode,
|
|
13112
|
+
...result2.diagnostic === void 0 ? {} : { error: result2.diagnostic.message }
|
|
13113
|
+
});
|
|
12912
13114
|
} catch (error) {
|
|
12913
13115
|
job.fail(error);
|
|
12914
13116
|
}
|
|
@@ -12930,32 +13132,20 @@ function createShellTool(workspace, options = {}) {
|
|
|
12930
13132
|
};
|
|
12931
13133
|
}
|
|
12932
13134
|
if (options.executionEnvironment === void 0) {
|
|
12933
|
-
return executeShell(root, { ...effective, timeoutMs }, signal, maxBytes);
|
|
13135
|
+
return executeShell(root, { ...effective, timeoutMs }, signal, maxBytes, environment);
|
|
12934
13136
|
}
|
|
13137
|
+
const invocation = executionEnvironmentCommand(options.executionEnvironment, effective.command, effective.args);
|
|
12935
13138
|
const result = await options.executionEnvironment.exec({
|
|
12936
|
-
|
|
12937
|
-
args: effective.args,
|
|
13139
|
+
...invocation,
|
|
12938
13140
|
cwd: workingDirectory(root, effective.cwd ?? void 0),
|
|
12939
|
-
timeoutMs
|
|
13141
|
+
timeoutMs,
|
|
13142
|
+
maxOutputBytes: maxBytes
|
|
12940
13143
|
}, signal);
|
|
12941
|
-
|
|
12942
|
-
const stderr = new BoundedOutput(maxBytes);
|
|
12943
|
-
stdout.add(Buffer.from(result.stdout));
|
|
12944
|
-
stderr.add(Buffer.from(result.stderr));
|
|
12945
|
-
return {
|
|
12946
|
-
...result,
|
|
12947
|
-
stdout: stdout.text(),
|
|
12948
|
-
stderr: stderr.text(),
|
|
12949
|
-
truncated: stdout.truncated || stderr.truncated,
|
|
12950
|
-
truncation: {
|
|
12951
|
-
stdout: stdout.metadata(),
|
|
12952
|
-
stderr: stderr.metadata()
|
|
12953
|
-
}
|
|
12954
|
-
};
|
|
13144
|
+
return boundedExecutionResult(result, maxBytes, effective.command);
|
|
12955
13145
|
}
|
|
12956
13146
|
};
|
|
12957
13147
|
}
|
|
12958
|
-
async function executeShell(root, input, cancellation, maxBytes, onOutput) {
|
|
13148
|
+
async function executeShell(root, input, cancellation, maxBytes, environment = process.env, onOutput) {
|
|
12959
13149
|
const cwd = workingDirectory(root, input.cwd ?? void 0);
|
|
12960
13150
|
const stdout = new BoundedOutput(maxBytes);
|
|
12961
13151
|
const stderr = new BoundedOutput(maxBytes);
|
|
@@ -12964,15 +13154,14 @@ async function executeShell(root, input, cancellation, maxBytes, onOutput) {
|
|
|
12964
13154
|
reject(cancellation.reason);
|
|
12965
13155
|
return;
|
|
12966
13156
|
}
|
|
12967
|
-
const
|
|
12968
|
-
const
|
|
12969
|
-
const commandLine = quoted.length > 0 ? `${quoteArg(input.command)} ${quoted}` : quoteArg(input.command);
|
|
12970
|
-
const shellCommandLine = process.platform === "win32" ? buildWindowsCommandLine(detectWindowsShell(), commandLine, input.command, input.args) : commandLine;
|
|
12971
|
-
const child = spawn3(shellCommandLine, {
|
|
13157
|
+
const invocation = shellInvocation(input, cwd, environment);
|
|
13158
|
+
const child = spawn3(invocation.command, invocation.args, {
|
|
12972
13159
|
cwd,
|
|
12973
|
-
shell:
|
|
13160
|
+
shell: false,
|
|
12974
13161
|
windowsHide: true,
|
|
12975
|
-
detached: process.platform !== "win32"
|
|
13162
|
+
detached: process.platform !== "win32",
|
|
13163
|
+
env: environment,
|
|
13164
|
+
...invocation.windowsVerbatimArguments === void 0 ? {} : { windowsVerbatimArguments: invocation.windowsVerbatimArguments }
|
|
12976
13165
|
});
|
|
12977
13166
|
let exitObserved = false;
|
|
12978
13167
|
let observedExitCode = null;
|
|
@@ -13033,12 +13222,12 @@ async function executeShell(root, input, cancellation, maxBytes, onOutput) {
|
|
|
13033
13222
|
streamCloseTimer.unref();
|
|
13034
13223
|
});
|
|
13035
13224
|
child.once("error", (error) => {
|
|
13036
|
-
|
|
13225
|
+
void finishResolve(errorCode(error) === "ENOENT" ? 127 : 1, null, error);
|
|
13037
13226
|
});
|
|
13038
13227
|
child.once("close", (exitCode, signal) => {
|
|
13039
13228
|
void finishResolve(exitCode, signal);
|
|
13040
13229
|
});
|
|
13041
|
-
async function finishResolve(exitCode, exitSignal) {
|
|
13230
|
+
async function finishResolve(exitCode, exitSignal, spawnError) {
|
|
13042
13231
|
if (settled) return;
|
|
13043
13232
|
settled = true;
|
|
13044
13233
|
cleanup();
|
|
@@ -13047,7 +13236,7 @@ async function executeShell(root, input, cancellation, maxBytes, onOutput) {
|
|
|
13047
13236
|
const stderrTail = stderrDecoder?.end() ?? "";
|
|
13048
13237
|
if (stdoutTail !== "") onOutput?.("stdout", stdoutTail);
|
|
13049
13238
|
if (stderrTail !== "") onOutput?.("stderr", stderrTail);
|
|
13050
|
-
resolvePromise({
|
|
13239
|
+
resolvePromise(withShellDiagnostic({
|
|
13051
13240
|
exitCode,
|
|
13052
13241
|
signal: exitSignal,
|
|
13053
13242
|
stdout: stdout.text(),
|
|
@@ -13055,7 +13244,7 @@ async function executeShell(root, input, cancellation, maxBytes, onOutput) {
|
|
|
13055
13244
|
truncated: stdout.truncated || stderr.truncated,
|
|
13056
13245
|
truncation: { stdout: stdout.metadata(), stderr: stderr.metadata() },
|
|
13057
13246
|
terminationReason
|
|
13058
|
-
});
|
|
13247
|
+
}, input.command, spawnError));
|
|
13059
13248
|
}
|
|
13060
13249
|
function finishReject(error) {
|
|
13061
13250
|
if (settled) return;
|
|
@@ -13113,7 +13302,7 @@ var BoundedOutput = class {
|
|
|
13113
13302
|
text() {
|
|
13114
13303
|
if (!this.truncated) return decodeOutput(this.#complete);
|
|
13115
13304
|
const tail = Buffer.concat(this.#tailChunks).subarray(-this.#tailLimit);
|
|
13116
|
-
const encoding =
|
|
13305
|
+
const encoding = outputEncoding(this.#complete);
|
|
13117
13306
|
return `${decodePrefix(this.#head, encoding)}${ELLIPSIS}${decodeSuffix(tail, encoding)}`;
|
|
13118
13307
|
}
|
|
13119
13308
|
};
|
|
@@ -13123,14 +13312,15 @@ var AdaptiveOutputDecoder = class {
|
|
|
13123
13312
|
write(chunk) {
|
|
13124
13313
|
if (this.#decoder !== void 0) return this.#decoder.decode(chunk, { stream: true });
|
|
13125
13314
|
this.#pending = Buffer.concat([this.#pending, chunk]);
|
|
13126
|
-
if (this.#pending.
|
|
13315
|
+
if (this.#pending.length === 1 && (this.#pending[0] === 255 || this.#pending[0] === 254)) return "";
|
|
13316
|
+
if (!this.#pending.includes(0) && this.#pending.every((byte) => byte < 128)) {
|
|
13127
13317
|
const ascii = this.#pending.toString("ascii");
|
|
13128
13318
|
this.#pending = Buffer.alloc(0);
|
|
13129
13319
|
return ascii;
|
|
13130
13320
|
}
|
|
13131
13321
|
const classification = classifyUtf8(this.#pending);
|
|
13132
13322
|
if (classification === "incomplete") return "";
|
|
13133
|
-
const encoding = classification
|
|
13323
|
+
const encoding = outputEncoding(this.#pending, classification);
|
|
13134
13324
|
this.#decoder = new TextDecoder(encoding);
|
|
13135
13325
|
const text = this.#decoder.decode(this.#pending, { stream: true });
|
|
13136
13326
|
this.#pending = Buffer.alloc(0);
|
|
@@ -13199,9 +13389,56 @@ function delay2(milliseconds) {
|
|
|
13199
13389
|
return new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds));
|
|
13200
13390
|
}
|
|
13201
13391
|
function decodeOutput(buffer) {
|
|
13202
|
-
const encoding =
|
|
13392
|
+
const encoding = outputEncoding(buffer);
|
|
13203
13393
|
return new TextDecoder(encoding).decode(buffer);
|
|
13204
13394
|
}
|
|
13395
|
+
function shellEnvironment(root, environment) {
|
|
13396
|
+
const localBin = join19(root, "node_modules", ".bin");
|
|
13397
|
+
if (!existsSync4(localBin)) return environment;
|
|
13398
|
+
const pathKey = Object.keys(environment).find((key) => key.toLowerCase() === "path") ?? "PATH";
|
|
13399
|
+
const current = environment[pathKey] ?? "";
|
|
13400
|
+
const entries = current.split(delimiter2).filter(Boolean);
|
|
13401
|
+
if (entries.some((entry2) => resolve15(entry2) === resolve15(localBin))) return environment;
|
|
13402
|
+
return { ...environment, [pathKey]: [localBin, ...entries].join(delimiter2) };
|
|
13403
|
+
}
|
|
13404
|
+
function boundedExecutionResult(result, maxBytes, command) {
|
|
13405
|
+
if (result.truncation !== void 0) {
|
|
13406
|
+
return withShellDiagnostic({
|
|
13407
|
+
...result,
|
|
13408
|
+
truncated: result.truncated ?? (result.truncation.stdout.truncated || result.truncation.stderr.truncated),
|
|
13409
|
+
truncation: result.truncation
|
|
13410
|
+
}, command);
|
|
13411
|
+
}
|
|
13412
|
+
const stdout = new BoundedOutput(maxBytes);
|
|
13413
|
+
const stderr = new BoundedOutput(maxBytes);
|
|
13414
|
+
stdout.add(Buffer.from(result.stdout));
|
|
13415
|
+
stderr.add(Buffer.from(result.stderr));
|
|
13416
|
+
return withShellDiagnostic({
|
|
13417
|
+
...result,
|
|
13418
|
+
stdout: stdout.text(),
|
|
13419
|
+
stderr: stderr.text(),
|
|
13420
|
+
truncated: stdout.truncated || stderr.truncated,
|
|
13421
|
+
truncation: { stdout: stdout.metadata(), stderr: stderr.metadata() }
|
|
13422
|
+
}, command);
|
|
13423
|
+
}
|
|
13424
|
+
function outputEncoding(buffer, utf8 = classifyUtf8(buffer)) {
|
|
13425
|
+
if (buffer.length >= 2) {
|
|
13426
|
+
if (buffer[0] === 255 && buffer[1] === 254) return "utf-16le";
|
|
13427
|
+
if (buffer[0] === 254 && buffer[1] === 255) return "utf-16be";
|
|
13428
|
+
}
|
|
13429
|
+
const sampleLength = Math.min(buffer.length, 256);
|
|
13430
|
+
let evenZeros = 0;
|
|
13431
|
+
let oddZeros = 0;
|
|
13432
|
+
for (let index = 0; index < sampleLength; index += 1) {
|
|
13433
|
+
if (buffer[index] !== 0) continue;
|
|
13434
|
+
if (index % 2 === 0) evenZeros += 1;
|
|
13435
|
+
else oddZeros += 1;
|
|
13436
|
+
}
|
|
13437
|
+
const threshold = Math.max(2, Math.floor(sampleLength / 8));
|
|
13438
|
+
if (oddZeros >= threshold && oddZeros > evenZeros * 2) return "utf-16le";
|
|
13439
|
+
if (evenZeros >= threshold && evenZeros > oddZeros * 2) return "utf-16be";
|
|
13440
|
+
return utf8 === "valid" || process.platform !== "win32" ? "utf-8" : "gb18030";
|
|
13441
|
+
}
|
|
13205
13442
|
function decodePrefix(buffer, encoding) {
|
|
13206
13443
|
for (let end = buffer.length; end >= Math.max(0, buffer.length - 3); end -= 1) {
|
|
13207
13444
|
const candidate = buffer.subarray(0, end);
|
|
@@ -13259,13 +13496,13 @@ var SCRIPT_PRIORITY = ["test", "typecheck", "lint", "build", "smoke:install"];
|
|
|
13259
13496
|
async function inferVerificationPlan(workspace) {
|
|
13260
13497
|
const commands = [];
|
|
13261
13498
|
const seen = /* @__PURE__ */ new Set();
|
|
13262
|
-
const packageJson = await optionalJson(
|
|
13499
|
+
const packageJson = await optionalJson(join20(workspace, "package.json"));
|
|
13263
13500
|
const scripts = isRecord3(packageJson?.scripts) ? packageJson.scripts : {};
|
|
13264
13501
|
for (const name of SCRIPT_PRIORITY) {
|
|
13265
13502
|
if (typeof scripts[name] !== "string" || isUnconditionalPassThrough(scripts[name])) continue;
|
|
13266
13503
|
add(commands, seen, npmScript(name));
|
|
13267
13504
|
}
|
|
13268
|
-
const flavor = await optionalText(
|
|
13505
|
+
const flavor = await optionalText(join20(workspace, "FLAVOR.md"));
|
|
13269
13506
|
if (flavor !== void 0) {
|
|
13270
13507
|
for (const match of flavor.matchAll(/^- `([^`]+)`\s*$/gm)) {
|
|
13271
13508
|
const parsed = parseTrustedCommand(match[1] ?? "");
|
|
@@ -13402,11 +13639,11 @@ function isEnvTruthy(value) {
|
|
|
13402
13639
|
|
|
13403
13640
|
// src/utils/log.ts
|
|
13404
13641
|
import { appendFile as appendFile3, mkdir as mkdir14 } from "fs/promises";
|
|
13405
|
-
import { dirname as dirname12, join as
|
|
13642
|
+
import { dirname as dirname12, join as join21, resolve as resolve16 } from "path";
|
|
13406
13643
|
var pending = Promise.resolve();
|
|
13407
13644
|
var usagePending = Promise.resolve();
|
|
13408
13645
|
function usageLogPath() {
|
|
13409
|
-
return process.env.FLAVOR_USAGE_FILE ??
|
|
13646
|
+
return process.env.FLAVOR_USAGE_FILE ?? join21(process.cwd(), ".flavor", "usage.jsonl");
|
|
13410
13647
|
}
|
|
13411
13648
|
var usageSessionId = "unknown";
|
|
13412
13649
|
var truncateOnNextAppend = false;
|
|
@@ -13435,7 +13672,7 @@ var AuditLogger = class {
|
|
|
13435
13672
|
#path;
|
|
13436
13673
|
#closed = false;
|
|
13437
13674
|
constructor(workspace) {
|
|
13438
|
-
this.#path =
|
|
13675
|
+
this.#path = join21(resolve16(workspace), ".flavor", "audit.jsonl");
|
|
13439
13676
|
}
|
|
13440
13677
|
get path() {
|
|
13441
13678
|
return this.#path;
|
|
@@ -14309,7 +14546,7 @@ import {
|
|
|
14309
14546
|
|
|
14310
14547
|
// src/utils/version.ts
|
|
14311
14548
|
import { readFileSync as readFileSync2 } from "fs";
|
|
14312
|
-
import { dirname as dirname13, join as
|
|
14549
|
+
import { dirname as dirname13, join as join22 } from "path";
|
|
14313
14550
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14314
14551
|
var cached;
|
|
14315
14552
|
function packageVersion() {
|
|
@@ -14317,7 +14554,7 @@ function packageVersion() {
|
|
|
14317
14554
|
let directory = dirname13(fileURLToPath2(import.meta.url));
|
|
14318
14555
|
for (; ; ) {
|
|
14319
14556
|
try {
|
|
14320
|
-
const manifest = JSON.parse(readFileSync2(
|
|
14557
|
+
const manifest = JSON.parse(readFileSync2(join22(directory, "package.json"), "utf8"));
|
|
14321
14558
|
if (typeof manifest.version === "string" && manifest.version.length > 0) {
|
|
14322
14559
|
cached = manifest.version;
|
|
14323
14560
|
return cached;
|
|
@@ -14550,8 +14787,8 @@ var OAuthCallbackAuthProvider = class {
|
|
|
14550
14787
|
async #startCallbackServer(expectedState, signal) {
|
|
14551
14788
|
let serverResolve;
|
|
14552
14789
|
let serverReject;
|
|
14553
|
-
const codePromise = new Promise((
|
|
14554
|
-
serverResolve =
|
|
14790
|
+
const codePromise = new Promise((resolve43, reject) => {
|
|
14791
|
+
serverResolve = resolve43;
|
|
14555
14792
|
serverReject = reject;
|
|
14556
14793
|
});
|
|
14557
14794
|
const server = createServer3((req, res) => {
|
|
@@ -14585,7 +14822,7 @@ var OAuthCallbackAuthProvider = class {
|
|
|
14585
14822
|
);
|
|
14586
14823
|
serverResolve(code);
|
|
14587
14824
|
});
|
|
14588
|
-
return new Promise((
|
|
14825
|
+
return new Promise((resolve43, reject) => {
|
|
14589
14826
|
server.once("error", (error) => {
|
|
14590
14827
|
reject(new Error(`OAuth callback server failed: ${error.message}`));
|
|
14591
14828
|
});
|
|
@@ -14602,7 +14839,7 @@ var OAuthCallbackAuthProvider = class {
|
|
|
14602
14839
|
signal.addEventListener("abort", () => {
|
|
14603
14840
|
serverReject(new Error("OAuth flow aborted"));
|
|
14604
14841
|
}, { once: true });
|
|
14605
|
-
|
|
14842
|
+
resolve43({ redirectUri, promise: codePromise });
|
|
14606
14843
|
});
|
|
14607
14844
|
});
|
|
14608
14845
|
}
|
|
@@ -14745,7 +14982,7 @@ function isStoredToken(value) {
|
|
|
14745
14982
|
|
|
14746
14983
|
// src/permissions/policy.ts
|
|
14747
14984
|
import { readFile as readFile16 } from "fs/promises";
|
|
14748
|
-
import { join as
|
|
14985
|
+
import { join as join23, resolve as resolve18 } from "path";
|
|
14749
14986
|
import { z as z25 } from "zod";
|
|
14750
14987
|
var DecisionSchema2 = z25.enum(["allow", "ask", "deny"]);
|
|
14751
14988
|
var PrefixSchema = z25.array(z25.string().min(1).max(1024)).min(1).max(64);
|
|
@@ -14782,9 +15019,9 @@ var CompiledPermissionPolicy = class {
|
|
|
14782
15019
|
async function loadPermissionPolicy(options) {
|
|
14783
15020
|
const sources = [
|
|
14784
15021
|
...options.managedPath === void 0 ? [] : [{ tier: "managed", path: resolve18(options.managedPath) }],
|
|
14785
|
-
{ tier: "user", path:
|
|
14786
|
-
{ tier: "project", path:
|
|
14787
|
-
{ tier: "local", path:
|
|
15022
|
+
{ tier: "user", path: join23(resolve18(options.home), ".flavor-code", "permissions.json") },
|
|
15023
|
+
{ tier: "project", path: join23(resolve18(options.workspace), ".flavor", "permissions.json") },
|
|
15024
|
+
{ tier: "local", path: join23(resolve18(options.workspace), ".flavor", "permissions.local.json") }
|
|
14788
15025
|
];
|
|
14789
15026
|
const rules = [];
|
|
14790
15027
|
for (const source of sources) {
|
|
@@ -14946,7 +15183,7 @@ function toolsSection(toolNames) {
|
|
|
14946
15183
|
addToolRule(rules, toolNames, "ApplyPatch", "Use `ApplyPatch` only when an edit genuinely spans multiple regions of one file; hunks may relocate only when their exact context has one unique nearby match. Prefer `Edit` for a single small replacement \u2014 it is easier to get right.");
|
|
14947
15184
|
addToolRule(rules, toolNames, "Glob", "Use `Glob` to find files by path pattern instead of shell directory crawling. Confirm the exact path before reading, searching, or editing; the search scope is limited to the workspace, so never pass absolute paths outside it. Set `includeIgnored: true` only when the user explicitly asks to inspect ignored files or an ignored path is required for the task.");
|
|
14948
15185
|
addToolRule(rules, toolNames, "Grep", "Use `Grep` to search file contents instead of shell text-search commands. Set `includeIgnored: true` only when the user explicitly asks to inspect ignored files or an ignored path is required for the task.");
|
|
14949
|
-
addToolRule(rules, toolNames, "Shell", "Use `Shell` for builds, tests, Git inspection, and commands without a dedicated tool. Keep commands scoped, non-interactive, and easy to audit.");
|
|
15186
|
+
addToolRule(rules, toolNames, "Shell", "Use `Shell` for builds, tests, Git inspection, and commands without a dedicated tool. Pass only the executable name or path in `command`, and put each logical argument in its own `args` item without shell quotes. Put pipes, redirects, and operators in `command` only when shell syntax is actually required. Prefer `Glob` and `Grep` over platform-specific directory and text-search commands. Keep commands scoped, non-interactive, and easy to audit.");
|
|
14950
15187
|
addToolRule(rules, toolNames, "LspFindRefs", "Use `LspFindRefs` to find all references to a symbol using the Language Server Protocol. Prefer this over Grep when you need precise semantic results.");
|
|
14951
15188
|
addToolRule(rules, toolNames, "LspHover", "Use `LspHover` to get type information and documentation for a symbol at a cursor position.");
|
|
14952
15189
|
addToolRule(rules, toolNames, "LspDiagnostics", "Use `LspDiagnostics` to read compiler and linter errors for a file. Always run this before claiming a code change is correct.");
|
|
@@ -15004,7 +15241,7 @@ function data(value) {
|
|
|
15004
15241
|
}
|
|
15005
15242
|
|
|
15006
15243
|
// src/skills/registry.ts
|
|
15007
|
-
import { constants as
|
|
15244
|
+
import { constants as constants6 } from "fs";
|
|
15008
15245
|
import { lstat as lstat7, open as defaultOpen, readdir as readdir5, realpath as realpath9 } from "fs/promises";
|
|
15009
15246
|
import { isAbsolute as isAbsolute10, relative as relative12, resolve as resolve19, sep as sep12 } from "path";
|
|
15010
15247
|
import { marked } from "marked";
|
|
@@ -15329,8 +15566,8 @@ async function openVerifiedFile(path, root, label, opener, expected) {
|
|
|
15329
15566
|
assertRegular(initial, label);
|
|
15330
15567
|
const canonical3 = await realpath9(path);
|
|
15331
15568
|
if (!isWithin4(root, canonical3)) throw new Error(`${label} escapes the skill root`);
|
|
15332
|
-
const noFollow = typeof
|
|
15333
|
-
const handle = await opener(canonical3,
|
|
15569
|
+
const noFollow = typeof constants6.O_NOFOLLOW === "number" ? constants6.O_NOFOLLOW : 0;
|
|
15570
|
+
const handle = await opener(canonical3, constants6.O_RDONLY | noFollow);
|
|
15334
15571
|
try {
|
|
15335
15572
|
const opened = await handle.stat({ bigint: true });
|
|
15336
15573
|
assertRegular(opened, label);
|
|
@@ -15551,9 +15788,9 @@ function createSkillResourceTool(registry) {
|
|
|
15551
15788
|
}
|
|
15552
15789
|
|
|
15553
15790
|
// src/session/store.ts
|
|
15554
|
-
import { constants as
|
|
15791
|
+
import { constants as constants7 } from "fs";
|
|
15555
15792
|
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";
|
|
15556
|
-
import { basename as
|
|
15793
|
+
import { basename as basename6, dirname as dirname15, join as join24, relative as relative13, resolve as resolve20, sep as sep13 } from "path";
|
|
15557
15794
|
import { randomUUID as randomUUID13 } from "crypto";
|
|
15558
15795
|
import { z as z27 } from "zod";
|
|
15559
15796
|
|
|
@@ -16249,7 +16486,7 @@ var SessionStore = class {
|
|
|
16249
16486
|
#maxSessions;
|
|
16250
16487
|
constructor(options) {
|
|
16251
16488
|
this.#workspace = resolve20(options.workspace);
|
|
16252
|
-
this.#sessions =
|
|
16489
|
+
this.#sessions = join24(this.#workspace, ".flavor", "sessions");
|
|
16253
16490
|
this.#maxBytes = options.maxBytes ?? DEFAULT_MAX_SESSION_BYTES;
|
|
16254
16491
|
if (!Number.isSafeInteger(this.#maxBytes) || this.#maxBytes < 256) throw new Error("maxBytes must be an integer of at least 256");
|
|
16255
16492
|
this.#maxSessions = options.maxSessions ?? 50;
|
|
@@ -16281,10 +16518,10 @@ var SessionStore = class {
|
|
|
16281
16518
|
const body = `${lines.join("\n")}
|
|
16282
16519
|
`;
|
|
16283
16520
|
if (Buffer.byteLength(body) > this.#maxBytes) throw new Error(`Session exceeds maximum size of ${this.#maxBytes} bytes`);
|
|
16284
|
-
const temporary =
|
|
16521
|
+
const temporary = join24(this.#sessions, `.${document.sessionId}.${process.pid}.${randomUUID13()}.tmp`);
|
|
16285
16522
|
let handle;
|
|
16286
16523
|
try {
|
|
16287
|
-
handle = await open7(temporary,
|
|
16524
|
+
handle = await open7(temporary, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
|
|
16288
16525
|
await handle.writeFile(body, "utf8");
|
|
16289
16526
|
await handle.sync();
|
|
16290
16527
|
await handle.close();
|
|
@@ -16300,13 +16537,13 @@ var SessionStore = class {
|
|
|
16300
16537
|
async acquireLease(sessionId) {
|
|
16301
16538
|
SessionIdSchema.parse(sessionId);
|
|
16302
16539
|
await this.#prepareDirectory();
|
|
16303
|
-
const path =
|
|
16540
|
+
const path = join24(this.#sessions, `.${sessionId}.lock`);
|
|
16304
16541
|
const token = JSON.stringify({ pid: process.pid, nonce: randomUUID13(), createdAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
16305
16542
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
16306
16543
|
let handle;
|
|
16307
16544
|
let created = false;
|
|
16308
16545
|
try {
|
|
16309
|
-
handle = await open7(path,
|
|
16546
|
+
handle = await open7(path, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
|
|
16310
16547
|
created = true;
|
|
16311
16548
|
await handle.writeFile(token, "utf8");
|
|
16312
16549
|
await handle.sync();
|
|
@@ -16358,7 +16595,7 @@ var SessionStore = class {
|
|
|
16358
16595
|
parsed = this.#parseSession(raw);
|
|
16359
16596
|
} catch (error) {
|
|
16360
16597
|
const quarantine = await this.#quarantine(path);
|
|
16361
|
-
throw new Error(`Session "${sessionId}" is corrupt or incompatible and was quarantined as ${
|
|
16598
|
+
throw new Error(`Session "${sessionId}" is corrupt or incompatible and was quarantined as ${basename6(quarantine)}: ${message(error)}`);
|
|
16362
16599
|
}
|
|
16363
16600
|
await this.#assertWorkspace(parsed.workspace.path);
|
|
16364
16601
|
const storedVersion = storedSessionVersion(raw);
|
|
@@ -16434,14 +16671,14 @@ var SessionStore = class {
|
|
|
16434
16671
|
if (isCode8(error, "ENOENT")) return;
|
|
16435
16672
|
throw error;
|
|
16436
16673
|
}
|
|
16437
|
-
const owner2 = await reclaimStaleSessionLock(
|
|
16674
|
+
const owner2 = await reclaimStaleSessionLock(join24(this.#sessions, `.${sessionId}.lock`));
|
|
16438
16675
|
if (owner2 !== void 0) throw new Error(`Session "${sessionId}" is active in process ${owner2} and cannot be deleted`);
|
|
16439
16676
|
const path = this.#path(sessionId);
|
|
16440
16677
|
try {
|
|
16441
16678
|
const metadata = await lstat8(path);
|
|
16442
16679
|
if (!metadata.isFile()) throw new Error("Session path is not a regular file");
|
|
16443
16680
|
await rm8(path);
|
|
16444
|
-
await rm8(
|
|
16681
|
+
await rm8(join24(this.#sessions, `${sessionId}.events.jsonl`), { force: true }).catch(() => void 0);
|
|
16445
16682
|
} catch (error) {
|
|
16446
16683
|
if (!isCode8(error, "ENOENT")) throw error;
|
|
16447
16684
|
}
|
|
@@ -16457,14 +16694,14 @@ var SessionStore = class {
|
|
|
16457
16694
|
if (entries.length <= this.#maxSessions) return;
|
|
16458
16695
|
let retained = 0;
|
|
16459
16696
|
for (const entry2 of entries) {
|
|
16460
|
-
const owner2 = await reclaimStaleSessionLock(
|
|
16697
|
+
const owner2 = await reclaimStaleSessionLock(join24(this.#sessions, `.${entry2.sessionId}.lock`));
|
|
16461
16698
|
if (owner2 !== void 0) continue;
|
|
16462
16699
|
if (retained < this.#maxSessions) {
|
|
16463
16700
|
retained += 1;
|
|
16464
16701
|
continue;
|
|
16465
16702
|
}
|
|
16466
16703
|
await rm8(this.#path(entry2.sessionId), { force: true }).catch(() => void 0);
|
|
16467
|
-
await rm8(
|
|
16704
|
+
await rm8(join24(this.#sessions, `${entry2.sessionId}.events.jsonl`), { force: true }).catch(() => void 0);
|
|
16468
16705
|
}
|
|
16469
16706
|
}
|
|
16470
16707
|
async #assertWorkspace(stored) {
|
|
@@ -16494,7 +16731,7 @@ var SessionStore = class {
|
|
|
16494
16731
|
const target = `${path}.v${version}.bak`;
|
|
16495
16732
|
let handle;
|
|
16496
16733
|
try {
|
|
16497
|
-
handle = await open7(target,
|
|
16734
|
+
handle = await open7(target, constants7.O_CREAT | constants7.O_EXCL | constants7.O_WRONLY, 384);
|
|
16498
16735
|
await handle.writeFile(raw, "utf8");
|
|
16499
16736
|
await handle.sync();
|
|
16500
16737
|
} catch (error) {
|
|
@@ -16748,13 +16985,13 @@ function sanitize2(input, seen = /* @__PURE__ */ new WeakSet()) {
|
|
|
16748
16985
|
// src/session/tree.ts
|
|
16749
16986
|
import { randomUUID as randomUUID15 } from "crypto";
|
|
16750
16987
|
import { mkdir as mkdir17, readFile as readFile19, rename as rename8, writeFile as writeFile11 } from "fs/promises";
|
|
16751
|
-
import { dirname as dirname17, join as
|
|
16988
|
+
import { dirname as dirname17, join as join26, resolve as resolve22 } from "path";
|
|
16752
16989
|
|
|
16753
16990
|
// src/session/checkpoint.ts
|
|
16754
16991
|
import { createHash as createHash15, randomUUID as randomUUID14 } from "crypto";
|
|
16755
16992
|
import { execFile as execFile2 } from "child_process";
|
|
16756
16993
|
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";
|
|
16757
|
-
import { dirname as dirname16, join as
|
|
16994
|
+
import { dirname as dirname16, join as join25, relative as relative14, resolve as resolve21, sep as sep14 } from "path";
|
|
16758
16995
|
var EXCLUDED = /* @__PURE__ */ new Set([".git", ".flavor", "node_modules", "dist", "build", "release", ".worktrees"]);
|
|
16759
16996
|
var MAX_FILE_BYTES = 16 * 1024 * 1024;
|
|
16760
16997
|
var MAX_TOTAL_BYTES = 128 * 1024 * 1024;
|
|
@@ -16767,9 +17004,9 @@ var WorkspaceCheckpointStore = class {
|
|
|
16767
17004
|
#maxTotalBytes;
|
|
16768
17005
|
constructor(options) {
|
|
16769
17006
|
this.#workspace = resolve21(options.workspace);
|
|
16770
|
-
this.#root =
|
|
16771
|
-
this.#objects =
|
|
16772
|
-
this.#manifests =
|
|
17007
|
+
this.#root = join25(this.#workspace, ".flavor", "checkpoints");
|
|
17008
|
+
this.#objects = join25(this.#root, "objects");
|
|
17009
|
+
this.#manifests = join25(this.#root, "manifests");
|
|
16773
17010
|
this.#maxFileBytes = options.maxFileBytes ?? MAX_FILE_BYTES;
|
|
16774
17011
|
this.#maxTotalBytes = options.maxTotalBytes ?? MAX_TOTAL_BYTES;
|
|
16775
17012
|
}
|
|
@@ -16789,7 +17026,7 @@ var WorkspaceCheckpointStore = class {
|
|
|
16789
17026
|
if (total > this.#maxTotalBytes) throw new Error("Checkpoint exceeds total size limit");
|
|
16790
17027
|
const content = await readFile18(absolute);
|
|
16791
17028
|
const digest = createHash15("sha256").update(content).digest("hex");
|
|
16792
|
-
const objectPath =
|
|
17029
|
+
const objectPath = join25(this.#objects, digest);
|
|
16793
17030
|
try {
|
|
16794
17031
|
const handle = await open8(objectPath, "wx");
|
|
16795
17032
|
try {
|
|
@@ -16809,12 +17046,12 @@ var WorkspaceCheckpointStore = class {
|
|
|
16809
17046
|
...label === void 0 || label.trim().length === 0 ? {} : { label: label.trim() },
|
|
16810
17047
|
files
|
|
16811
17048
|
};
|
|
16812
|
-
await atomicJson(
|
|
17049
|
+
await atomicJson(join25(this.#manifests, `${checkpoint.id}.json`), checkpoint);
|
|
16813
17050
|
return checkpoint;
|
|
16814
17051
|
}
|
|
16815
17052
|
async load(id) {
|
|
16816
17053
|
if (!/^checkpoint-[A-Za-z0-9-]+$/.test(id)) throw new Error("Invalid checkpoint id");
|
|
16817
|
-
const value = JSON.parse(await readFile18(
|
|
17054
|
+
const value = JSON.parse(await readFile18(join25(this.#manifests, `${id}.json`), "utf8"));
|
|
16818
17055
|
if (value.version !== 1 || value.id !== id || !Array.isArray(value.files)) throw new Error("Invalid checkpoint manifest");
|
|
16819
17056
|
for (const file of value.files) {
|
|
16820
17057
|
contained(this.#workspace, file.path);
|
|
@@ -16826,7 +17063,7 @@ var WorkspaceCheckpointStore = class {
|
|
|
16826
17063
|
const checkpoint = await this.load(id);
|
|
16827
17064
|
const objects = /* @__PURE__ */ new Map();
|
|
16828
17065
|
for (const file of checkpoint.files) {
|
|
16829
|
-
const object = await readFile18(
|
|
17066
|
+
const object = await readFile18(join25(this.#objects, file.digest));
|
|
16830
17067
|
if (object.length !== file.size || createHash15("sha256").update(object).digest("hex") !== file.digest) {
|
|
16831
17068
|
throw new Error(`Checkpoint object is corrupt: ${file.digest}`);
|
|
16832
17069
|
}
|
|
@@ -16849,7 +17086,7 @@ var WorkspaceCheckpointStore = class {
|
|
|
16849
17086
|
}
|
|
16850
17087
|
};
|
|
16851
17088
|
async function discoverFiles(workspace) {
|
|
16852
|
-
if (await exists2(
|
|
17089
|
+
if (await exists2(join25(workspace, ".git"))) {
|
|
16853
17090
|
const listed = await gitFiles(workspace);
|
|
16854
17091
|
return listed.filter((path) => !EXCLUDED.has(path.split("/")[0] ?? "")).sort();
|
|
16855
17092
|
}
|
|
@@ -16858,7 +17095,7 @@ async function discoverFiles(workspace) {
|
|
|
16858
17095
|
const entries = await readdir7(directory, { withFileTypes: true });
|
|
16859
17096
|
for (const entry2 of entries) {
|
|
16860
17097
|
if (EXCLUDED.has(entry2.name)) continue;
|
|
16861
|
-
const absolute =
|
|
17098
|
+
const absolute = join25(directory, entry2.name);
|
|
16862
17099
|
const path = normalizeRelative(relative14(workspace, absolute));
|
|
16863
17100
|
if (entry2.isSymbolicLink()) throw new Error(`Checkpoint refuses symbolic link: ${path}`);
|
|
16864
17101
|
if (entry2.isDirectory()) await walk(absolute);
|
|
@@ -16933,14 +17170,14 @@ var SessionHistory = class _SessionHistory {
|
|
|
16933
17170
|
#leafId;
|
|
16934
17171
|
#unrevert;
|
|
16935
17172
|
constructor(options, document) {
|
|
16936
|
-
this.#path =
|
|
17173
|
+
this.#path = join26(resolve22(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
|
|
16937
17174
|
this.#checkpoints = new WorkspaceCheckpointStore({ workspace: options.workspace });
|
|
16938
17175
|
this.#restoreContext = options.restoreContext;
|
|
16939
17176
|
this.#nodes = boundedNodes(document.nodes);
|
|
16940
17177
|
this.#leafId = document.leafId !== null && this.#nodes.some((node) => node.id === document.leafId) ? document.leafId : this.#nodes.at(-1)?.id ?? null;
|
|
16941
17178
|
}
|
|
16942
17179
|
static async open(options) {
|
|
16943
|
-
const path =
|
|
17180
|
+
const path = join26(resolve22(options.workspace), ".flavor", "session-trees", options.sessionId, "tree.json");
|
|
16944
17181
|
let document = { version: 1, sessionId: options.sessionId, leafId: null, nodes: [] };
|
|
16945
17182
|
try {
|
|
16946
17183
|
const parsed = JSON.parse(await readFile19(path, "utf8"));
|
|
@@ -17071,12 +17308,12 @@ function isCode10(error, code) {
|
|
|
17071
17308
|
}
|
|
17072
17309
|
|
|
17073
17310
|
// src/utils/memory-restart.ts
|
|
17074
|
-
import { join as
|
|
17311
|
+
import { join as join27 } from "path";
|
|
17075
17312
|
var MEMORY_RESTART_EXIT_CODE = 75;
|
|
17076
17313
|
var MEMORY_RESTART_MARKER_FILE = "memory-restart.json";
|
|
17077
17314
|
var MEMORY_RESTART_WINDOW_MS = 30 * 60 * 1e3;
|
|
17078
17315
|
function memoryRestartMarkerPath(workspace) {
|
|
17079
|
-
return
|
|
17316
|
+
return join27(workspace, ".flavor", "tmp", MEMORY_RESTART_MARKER_FILE);
|
|
17080
17317
|
}
|
|
17081
17318
|
function nextMemoryRestartMarker(previous, sessionId, now) {
|
|
17082
17319
|
const reusable = previous !== void 0 && previous.sessionId === sessionId && now.getTime() - Date.parse(previous.requestedAt) < MEMORY_RESTART_WINDOW_MS && Number.isInteger(previous.attempts) && previous.attempts > 0;
|
|
@@ -17095,10 +17332,10 @@ function parseMemoryRestartMarker(json) {
|
|
|
17095
17332
|
}
|
|
17096
17333
|
|
|
17097
17334
|
// src/sleep/organizer.ts
|
|
17098
|
-
import { constants as
|
|
17335
|
+
import { constants as constants8 } from "fs";
|
|
17099
17336
|
import { randomUUID as randomUUID16 } from "crypto";
|
|
17100
17337
|
import { lstat as lstat10, mkdir as mkdir18, open as open9, readdir as readdir8, realpath as realpath11, rename as rename9, rm as rm10 } from "fs/promises";
|
|
17101
|
-
import { isAbsolute as isAbsolute11, join as
|
|
17338
|
+
import { isAbsolute as isAbsolute11, join as join28, relative as relative15, resolve as resolve23, sep as sep15 } from "path";
|
|
17102
17339
|
import { z as z28 } from "zod";
|
|
17103
17340
|
var DateKeySchema = z28.string().regex(/^\d{4}-\d{2}-\d{2}$/);
|
|
17104
17341
|
var ReviewSchema = z28.object({
|
|
@@ -17153,7 +17390,7 @@ var ProjectSleepOrganizer = class {
|
|
|
17153
17390
|
#generate;
|
|
17154
17391
|
constructor(options) {
|
|
17155
17392
|
this.#workspace = resolve23(options.workspace);
|
|
17156
|
-
this.#sleepDirectory =
|
|
17393
|
+
this.#sleepDirectory = join28(this.#workspace, ".flavor", "sleep");
|
|
17157
17394
|
this.#sessions = options.sessions ?? new SessionStore({ workspace: this.#workspace });
|
|
17158
17395
|
this.#generate = options.generate;
|
|
17159
17396
|
}
|
|
@@ -17162,10 +17399,10 @@ var ProjectSleepOrganizer = class {
|
|
|
17162
17399
|
signal.throwIfAborted();
|
|
17163
17400
|
await this.#prepareDirectory();
|
|
17164
17401
|
if (await this.#hasReport(date)) return { status: "exists", date };
|
|
17165
|
-
const lockPath =
|
|
17402
|
+
const lockPath = join28(this.#sleepDirectory, `.${date}.lock`);
|
|
17166
17403
|
let lock;
|
|
17167
17404
|
try {
|
|
17168
|
-
lock = await open9(lockPath,
|
|
17405
|
+
lock = await open9(lockPath, constants8.O_CREAT | constants8.O_EXCL | constants8.O_WRONLY, 384);
|
|
17169
17406
|
} catch (error) {
|
|
17170
17407
|
if (hasCode(error, "EEXIST")) return { status: "locked", date };
|
|
17171
17408
|
throw error;
|
|
@@ -17197,7 +17434,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
17197
17434
|
}
|
|
17198
17435
|
if (review === void 0) throw lastError;
|
|
17199
17436
|
const filename2 = `${date}-${filenameSummary(review.title)}.md`;
|
|
17200
|
-
const path =
|
|
17437
|
+
const path = join28(this.#sleepDirectory, filename2);
|
|
17201
17438
|
const markdown = renderSleepReport(date, review, documents);
|
|
17202
17439
|
await this.#atomicWrite(path, markdown);
|
|
17203
17440
|
return { status: "written", date, path, sessionCount: documents.length };
|
|
@@ -17239,7 +17476,7 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
17239
17476
|
});
|
|
17240
17477
|
}
|
|
17241
17478
|
async #prepareDirectory() {
|
|
17242
|
-
const flavorDirectory =
|
|
17479
|
+
const flavorDirectory = join28(this.#workspace, ".flavor");
|
|
17243
17480
|
await assertNotSymlink(flavorDirectory);
|
|
17244
17481
|
await mkdir18(flavorDirectory, { recursive: true, mode: 448 });
|
|
17245
17482
|
await assertNotSymlink(this.#sleepDirectory);
|
|
@@ -17253,10 +17490,10 @@ Previous output was invalid. Repair it and return strict JSON only.`;
|
|
|
17253
17490
|
return (await readdir8(this.#sleepDirectory, { withFileTypes: true })).some((entry2) => entry2.isFile() && entry2.name.startsWith(prefix) && entry2.name.endsWith(".md"));
|
|
17254
17491
|
}
|
|
17255
17492
|
async #atomicWrite(path, content) {
|
|
17256
|
-
const temporary =
|
|
17493
|
+
const temporary = join28(this.#sleepDirectory, `.${randomUUID16()}.tmp`);
|
|
17257
17494
|
let handle;
|
|
17258
17495
|
try {
|
|
17259
|
-
handle = await open9(temporary,
|
|
17496
|
+
handle = await open9(temporary, constants8.O_CREAT | constants8.O_EXCL | constants8.O_WRONLY, 384);
|
|
17260
17497
|
await handle.writeFile(content, "utf8");
|
|
17261
17498
|
await handle.sync();
|
|
17262
17499
|
await handle.close();
|
|
@@ -17626,7 +17863,7 @@ function computeSessionStats(documents) {
|
|
|
17626
17863
|
}
|
|
17627
17864
|
|
|
17628
17865
|
// src/tools/files.ts
|
|
17629
|
-
import { constants as
|
|
17866
|
+
import { constants as constants9 } from "fs";
|
|
17630
17867
|
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";
|
|
17631
17868
|
import { dirname as dirname18, isAbsolute as isAbsolute12, relative as relative16, resolve as resolve24, sep as sep16 } from "path";
|
|
17632
17869
|
import { randomUUID as randomUUID17 } from "crypto";
|
|
@@ -17766,7 +18003,7 @@ var FileObservationStore = class {
|
|
|
17766
18003
|
};
|
|
17767
18004
|
function createReadTool(workspace, options = {}) {
|
|
17768
18005
|
const guard = createPathGuard(workspace);
|
|
17769
|
-
const openFile2 = options.openFile ?? ((path) => open10(path,
|
|
18006
|
+
const openFile2 = options.openFile ?? ((path) => open10(path, constants9.O_RDONLY));
|
|
17770
18007
|
const readHistory = /* @__PURE__ */ new Map();
|
|
17771
18008
|
return {
|
|
17772
18009
|
name: "Read",
|
|
@@ -17984,7 +18221,7 @@ async function atomicWrite(path, content, signal, expectedVersion) {
|
|
|
17984
18221
|
await mkdir19(dirname18(path), { recursive: true });
|
|
17985
18222
|
const temporary = `${path}.${randomUUID17()}.tmp`;
|
|
17986
18223
|
try {
|
|
17987
|
-
const handle = await open10(temporary,
|
|
18224
|
+
const handle = await open10(temporary, constants9.O_CREAT | constants9.O_EXCL | constants9.O_WRONLY, 384);
|
|
17988
18225
|
try {
|
|
17989
18226
|
await handle.writeFile(content, "utf8");
|
|
17990
18227
|
await handle.sync();
|
|
@@ -18876,7 +19113,7 @@ var SearchSpawnError = class extends Error {
|
|
|
18876
19113
|
}
|
|
18877
19114
|
fallbackSafe;
|
|
18878
19115
|
};
|
|
18879
|
-
function delimitedParser(
|
|
19116
|
+
function delimitedParser(delimiter4, onRecord) {
|
|
18880
19117
|
let pending2 = Buffer.alloc(0);
|
|
18881
19118
|
let stopped = false;
|
|
18882
19119
|
return {
|
|
@@ -18885,7 +19122,7 @@ function delimitedParser(delimiter2, onRecord) {
|
|
|
18885
19122
|
pending2 = pending2.length === 0 ? chunk : Buffer.concat([pending2, chunk]);
|
|
18886
19123
|
let offset = 0;
|
|
18887
19124
|
while (true) {
|
|
18888
|
-
const end = pending2.indexOf(
|
|
19125
|
+
const end = pending2.indexOf(delimiter4, offset);
|
|
18889
19126
|
if (end < 0) break;
|
|
18890
19127
|
if (!onRecord(pending2.subarray(offset, end))) {
|
|
18891
19128
|
stopped = true;
|
|
@@ -19337,9 +19574,9 @@ function charset(contentType) {
|
|
|
19337
19574
|
|
|
19338
19575
|
// src/tools/lsp.ts
|
|
19339
19576
|
import { spawn as spawn5 } from "child_process";
|
|
19340
|
-
import { existsSync as
|
|
19341
|
-
import { access as access2, constants as
|
|
19342
|
-
import { basename as
|
|
19577
|
+
import { existsSync as existsSync5 } from "fs";
|
|
19578
|
+
import { access as access2, constants as constants10, readFile as readFile21 } from "fs/promises";
|
|
19579
|
+
import { basename as basename7, dirname as dirname20, extname as extname3, isAbsolute as isAbsolute14, relative as relative18, resolve as resolve26, sep as sep18 } from "path";
|
|
19343
19580
|
import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL3 } from "url";
|
|
19344
19581
|
import { z as z32 } from "zod";
|
|
19345
19582
|
var LSP_REQUEST_TIMEOUT_MS = 15e3;
|
|
@@ -19440,8 +19677,8 @@ var LspConnection = class {
|
|
|
19440
19677
|
const id = this.#nextId++;
|
|
19441
19678
|
const request = { jsonrpc: "2.0", id, method, params };
|
|
19442
19679
|
const timeout = AbortSignal.timeout(LSP_REQUEST_TIMEOUT_MS);
|
|
19443
|
-
const promise = new Promise((
|
|
19444
|
-
this.#pending.set(id, { resolve:
|
|
19680
|
+
const promise = new Promise((resolve43, reject) => {
|
|
19681
|
+
this.#pending.set(id, { resolve: resolve43, reject });
|
|
19445
19682
|
timeout.addEventListener("abort", () => {
|
|
19446
19683
|
if (this.#pending.has(id)) {
|
|
19447
19684
|
this.#pending.delete(id);
|
|
@@ -19648,7 +19885,7 @@ var RealLspManager = class {
|
|
|
19648
19885
|
if (pending2 !== void 0) return pending2;
|
|
19649
19886
|
const config = this.#serverConfigs.find((c) => c.language === language);
|
|
19650
19887
|
if (config === void 0) return void 0;
|
|
19651
|
-
const hasRoot = config.rootFiles.some((file) =>
|
|
19888
|
+
const hasRoot = config.rootFiles.some((file) => existsSync5(resolve26(this.#workspace, file)));
|
|
19652
19889
|
if (!hasRoot) return void 0;
|
|
19653
19890
|
const startPromise = this.#startServer(config);
|
|
19654
19891
|
this.#pendingStarts.set(language, startPromise);
|
|
@@ -19662,7 +19899,7 @@ var RealLspManager = class {
|
|
|
19662
19899
|
}
|
|
19663
19900
|
async #startServer(config) {
|
|
19664
19901
|
try {
|
|
19665
|
-
await access2(config.command,
|
|
19902
|
+
await access2(config.command, constants10.X_OK);
|
|
19666
19903
|
} catch {
|
|
19667
19904
|
}
|
|
19668
19905
|
const connection = new LspConnection(config.command, config.args, this.#workspace);
|
|
@@ -19710,7 +19947,7 @@ function createLspTools(workspace, options = {}) {
|
|
|
19710
19947
|
description: "Find all references to a symbol at a file position using the Language Server Protocol",
|
|
19711
19948
|
inputSchema: LspFindRefsInput,
|
|
19712
19949
|
paths: (input) => [guard(input.file)],
|
|
19713
|
-
summarize: (input) => `${
|
|
19950
|
+
summarize: (input) => `${basename7(input.file)}:${input.line}:${input.character}${langTag(input.file)}`,
|
|
19714
19951
|
execute: async (input, _signal) => {
|
|
19715
19952
|
const path = guard(input.file);
|
|
19716
19953
|
const uri = toUri(path);
|
|
@@ -19727,7 +19964,7 @@ function createLspTools(workspace, options = {}) {
|
|
|
19727
19964
|
description: "Get type information and documentation for a symbol at a file position using LSP",
|
|
19728
19965
|
inputSchema: LspHoverInput,
|
|
19729
19966
|
paths: (input) => [guard(input.file)],
|
|
19730
|
-
summarize: (input) => `${
|
|
19967
|
+
summarize: (input) => `${basename7(input.file)}:${input.line}:${input.character}${langTag(input.file)}`,
|
|
19731
19968
|
execute: async (input, _signal) => {
|
|
19732
19969
|
const path = guard(input.file);
|
|
19733
19970
|
const uri = toUri(path);
|
|
@@ -19742,7 +19979,7 @@ function createLspTools(workspace, options = {}) {
|
|
|
19742
19979
|
description: "Read compiler and linter diagnostics for a file using LSP",
|
|
19743
19980
|
inputSchema: LspDiagnosticsInput,
|
|
19744
19981
|
paths: (input) => [guard(input.file)],
|
|
19745
|
-
summarize: (input) => `${
|
|
19982
|
+
summarize: (input) => `${basename7(input.file)}${langTag(input.file)}`,
|
|
19746
19983
|
execute: async (input, _signal) => {
|
|
19747
19984
|
const path = guard(input.file);
|
|
19748
19985
|
const uri = toUri(path);
|
|
@@ -19845,19 +20082,19 @@ async function changeSummary(workspace) {
|
|
|
19845
20082
|
};
|
|
19846
20083
|
}
|
|
19847
20084
|
async function stagedDiff(workspace, maxChars = 96e3) {
|
|
19848
|
-
const
|
|
20085
|
+
const stat20 = await git2(workspace, ["diff", "--staged", "--stat"]);
|
|
19849
20086
|
const diff = await git2(workspace, ["diff", "--staged", "--no-ext-diff", "--no-color"]);
|
|
19850
20087
|
if (!diff.ok) throw gitFailure(diff, "diff --staged");
|
|
19851
20088
|
const text = diff.stdout;
|
|
19852
20089
|
return {
|
|
19853
20090
|
diff: text.length > maxChars ? `${text.slice(0, maxChars)}
|
|
19854
20091
|
\u2026[diff truncated]` : text,
|
|
19855
|
-
stat:
|
|
20092
|
+
stat: stat20.stdout.trim(),
|
|
19856
20093
|
truncated: text.length > maxChars
|
|
19857
20094
|
};
|
|
19858
20095
|
}
|
|
19859
20096
|
async function uncommittedDiff(workspace, maxChars = 96e3) {
|
|
19860
|
-
const
|
|
20097
|
+
const stat20 = await git2(workspace, ["diff", "HEAD", "--stat"]);
|
|
19861
20098
|
const diff = await git2(workspace, ["diff", "HEAD", "--no-ext-diff", "--no-color"]);
|
|
19862
20099
|
if (!diff.ok && !diff.stderr.includes("unknown revision")) throw gitFailure(diff, "diff HEAD");
|
|
19863
20100
|
const summary2 = await changeSummary(workspace);
|
|
@@ -19865,7 +20102,7 @@ async function uncommittedDiff(workspace, maxChars = 96e3) {
|
|
|
19865
20102
|
return {
|
|
19866
20103
|
diff: text.length > maxChars ? `${text.slice(0, maxChars)}
|
|
19867
20104
|
\u2026[diff truncated]` : text,
|
|
19868
|
-
stat:
|
|
20105
|
+
stat: stat20.stdout.trim(),
|
|
19869
20106
|
untracked: summary2.untrackedFiles
|
|
19870
20107
|
};
|
|
19871
20108
|
}
|
|
@@ -20159,12 +20396,12 @@ ${explanation}` : "";
|
|
|
20159
20396
|
// src/desktop/astgraph-service.ts
|
|
20160
20397
|
import { stat as stat10 } from "fs/promises";
|
|
20161
20398
|
import { createRequire as nodeCreateRequire } from "module";
|
|
20162
|
-
import { join as
|
|
20399
|
+
import { join as join29, resolve as resolve27 } from "path";
|
|
20163
20400
|
var FIELDS = "id, kind, name, qualified_name, file_path, language, start_line, end_line, signature";
|
|
20164
20401
|
var DesktopAstGraphService = class {
|
|
20165
20402
|
#path;
|
|
20166
20403
|
constructor(workspace) {
|
|
20167
|
-
this.#path =
|
|
20404
|
+
this.#path = join29(resolve27(workspace), ".flavor", "astgraph", "index.db");
|
|
20168
20405
|
}
|
|
20169
20406
|
async status() {
|
|
20170
20407
|
if ((await stat10(this.#path).catch(() => void 0))?.isFile() !== true) {
|
|
@@ -20326,7 +20563,7 @@ var QuestionBridge = class {
|
|
|
20326
20563
|
}
|
|
20327
20564
|
}
|
|
20328
20565
|
#askLocally(questions, signal) {
|
|
20329
|
-
return new Promise((
|
|
20566
|
+
return new Promise((resolve43, reject) => {
|
|
20330
20567
|
const onAbort = () => {
|
|
20331
20568
|
this.#pending = void 0;
|
|
20332
20569
|
this.#active = false;
|
|
@@ -20340,7 +20577,7 @@ var QuestionBridge = class {
|
|
|
20340
20577
|
this.#pending = void 0;
|
|
20341
20578
|
this.#active = false;
|
|
20342
20579
|
this.#onChange?.();
|
|
20343
|
-
|
|
20580
|
+
resolve43(answers);
|
|
20344
20581
|
},
|
|
20345
20582
|
reject: (error) => {
|
|
20346
20583
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -20433,7 +20670,7 @@ function createTodoWriteTool() {
|
|
|
20433
20670
|
// src/tools/managed.ts
|
|
20434
20671
|
import { randomUUID as randomUUID18 } from "crypto";
|
|
20435
20672
|
import { link, lstat as lstat11, mkdir as mkdir20, readFile as readFile22, readdir as readdir9, unlink as unlink3, writeFile as writeFile12 } from "fs/promises";
|
|
20436
|
-
import { dirname as dirname21, join as
|
|
20673
|
+
import { dirname as dirname21, join as join30, resolve as resolve28 } from "path";
|
|
20437
20674
|
import { z as z37 } from "zod";
|
|
20438
20675
|
var MANAGED_TOOL_VERSION = 1;
|
|
20439
20676
|
var MAX_RECORD_BYTES = 256 * 1024;
|
|
@@ -20476,8 +20713,8 @@ var ManagedToolStore = class {
|
|
|
20476
20713
|
#diagnostics = [];
|
|
20477
20714
|
constructor(options) {
|
|
20478
20715
|
this.#workspace = resolve28(options.workspace);
|
|
20479
|
-
this.#projectRoot =
|
|
20480
|
-
this.#globalRoot =
|
|
20716
|
+
this.#projectRoot = join30(this.#workspace, ".flavor", "tools");
|
|
20717
|
+
this.#globalRoot = join30(resolve28(options.home), ".flavor-code", "tools");
|
|
20481
20718
|
this.#now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
|
|
20482
20719
|
}
|
|
20483
20720
|
get diagnostics() {
|
|
@@ -20524,7 +20761,7 @@ var ManagedToolStore = class {
|
|
|
20524
20761
|
version: MANAGED_TOOL_VERSION,
|
|
20525
20762
|
createdAt: this.#now()
|
|
20526
20763
|
});
|
|
20527
|
-
const path =
|
|
20764
|
+
const path = join30(this.directory(input.scope), filename(input.name));
|
|
20528
20765
|
const definition = compileDefinition(record3, input.scope, this.#workspace);
|
|
20529
20766
|
await writeExclusive(path, `${JSON.stringify(record3, void 0, 2)}
|
|
20530
20767
|
`);
|
|
@@ -20549,7 +20786,7 @@ var ManagedToolStore = class {
|
|
|
20549
20786
|
if (!parsed.success) return [];
|
|
20550
20787
|
const matches = this.#matching(parsed.data.name).filter((entry2) => parsed.data.scope === void 0 || entry2.scope === parsed.data.scope);
|
|
20551
20788
|
if (matches.length > 0) return matches.map((entry2) => entry2.path);
|
|
20552
|
-
return [
|
|
20789
|
+
return [join30(this.directory(parsed.data.scope ?? "project"), filename(parsed.data.name))];
|
|
20553
20790
|
}
|
|
20554
20791
|
async #loadDirectory(scope2) {
|
|
20555
20792
|
const root = this.directory(scope2);
|
|
@@ -20562,7 +20799,7 @@ var ManagedToolStore = class {
|
|
|
20562
20799
|
return;
|
|
20563
20800
|
}
|
|
20564
20801
|
for (const name of names) {
|
|
20565
|
-
const path =
|
|
20802
|
+
const path = join30(root, name);
|
|
20566
20803
|
try {
|
|
20567
20804
|
const info = await lstat11(path);
|
|
20568
20805
|
if (!info.isFile() || info.isSymbolicLink()) throw new Error("Managed tool record must be a regular file");
|
|
@@ -21145,6 +21382,7 @@ var MVP_COMMANDS = [
|
|
|
21145
21382
|
"exit",
|
|
21146
21383
|
"audit",
|
|
21147
21384
|
"usage",
|
|
21385
|
+
"doctor",
|
|
21148
21386
|
"loop",
|
|
21149
21387
|
"goal",
|
|
21150
21388
|
"evolve",
|
|
@@ -21435,7 +21673,7 @@ var HELP = [
|
|
|
21435
21673
|
"/permissions <default|acceptEdits|plan|bypassPermissions|auto|bubble>",
|
|
21436
21674
|
"/login authenticate via OAuth PKCE",
|
|
21437
21675
|
"/logout clear stored OAuth credentials",
|
|
21438
|
-
"/init /config /skills /plugins /hooks /tasks",
|
|
21676
|
+
"/init /config /skills /plugins /hooks /tasks /doctor",
|
|
21439
21677
|
"/memory /remember [type] <text> /forget <text-or-id> /forget-cold /finish",
|
|
21440
21678
|
"/checkpoint [label] /tree /rewind <node> /unrevert /fork <node>",
|
|
21441
21679
|
"/compact /clear /help /exit",
|
|
@@ -21917,6 +22155,7 @@ Address this now (for example run the required verification or fix), then finish
|
|
|
21917
22155
|
else if (command.name === "hooks") this.#notice(format(this.#services.hooksStatus()));
|
|
21918
22156
|
else if (command.name === "tasks") this.#notice(format(this.#services.tasks()));
|
|
21919
22157
|
else if (command.name === "audit") this.#notice(await this.#services.audit(command.toolFilter));
|
|
22158
|
+
else if (command.name === "doctor") this.#notice(await required(this.#services.doctor, "doctor")());
|
|
21920
22159
|
else if (command.name === "evolve") this.#notice(await this.#services.evolve(command.args));
|
|
21921
22160
|
else if (command.name === "usage") this.#notice(await this.#services.usage());
|
|
21922
22161
|
else if (command.name === "clear") {
|
|
@@ -22029,9 +22268,9 @@ Address this now (for example run the required verification or fix), then finish
|
|
|
22029
22268
|
}
|
|
22030
22269
|
#waitForPlanningIdle(key) {
|
|
22031
22270
|
if (!this.#activePlanning.has(key)) return Promise.resolve();
|
|
22032
|
-
return new Promise((
|
|
22271
|
+
return new Promise((resolve43) => {
|
|
22033
22272
|
const check2 = () => {
|
|
22034
|
-
if (!this.#activePlanning.has(key))
|
|
22273
|
+
if (!this.#activePlanning.has(key)) resolve43();
|
|
22035
22274
|
else this.#planningIdleWaiters.add(check2);
|
|
22036
22275
|
};
|
|
22037
22276
|
this.#planningIdleWaiters.add(check2);
|
|
@@ -22138,6 +22377,225 @@ function requiredPals(service) {
|
|
|
22138
22377
|
return service;
|
|
22139
22378
|
}
|
|
22140
22379
|
|
|
22380
|
+
// src/doctor.ts
|
|
22381
|
+
import { constants as constants11 } from "fs";
|
|
22382
|
+
import { access as access3, lstat as lstat12, readFile as readFile23, readdir as readdir10, stat as stat11 } from "fs/promises";
|
|
22383
|
+
import { homedir } from "os";
|
|
22384
|
+
import { isAbsolute as isAbsolute15, join as join31, relative as relative19, resolve as resolve29, sep as sep19 } from "path";
|
|
22385
|
+
import { rgPath as bundledRgPath2 } from "@vscode/ripgrep";
|
|
22386
|
+
|
|
22387
|
+
// src/utils/semver.ts
|
|
22388
|
+
import { gt as semverGt, gte as semverGte, valid as semverValid } from "semver";
|
|
22389
|
+
function gte(a, b) {
|
|
22390
|
+
return semverGte(a, b, { loose: true });
|
|
22391
|
+
}
|
|
22392
|
+
|
|
22393
|
+
// src/update/check.ts
|
|
22394
|
+
var NPM_PACKAGE_NAME = "flavor-code";
|
|
22395
|
+
var REGISTRY_LATEST_URL = `https://registry.npmjs.org/${NPM_PACKAGE_NAME}/latest`;
|
|
22396
|
+
var DEFAULT_TIMEOUT_MS2 = 3e3;
|
|
22397
|
+
async function fetchLatestVersion(options = {}) {
|
|
22398
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
|
|
22399
|
+
const fetchImpl = options.fetchImpl ?? ((url, init) => fetch(url, init));
|
|
22400
|
+
let timer;
|
|
22401
|
+
const timeout = new Promise((resolve43) => {
|
|
22402
|
+
timer = setTimeout(() => resolve43(void 0), timeoutMs);
|
|
22403
|
+
timer.unref?.();
|
|
22404
|
+
});
|
|
22405
|
+
try {
|
|
22406
|
+
const response2 = await Promise.race([
|
|
22407
|
+
fetchImpl(REGISTRY_LATEST_URL, { signal: AbortSignal.timeout(timeoutMs) }),
|
|
22408
|
+
timeout
|
|
22409
|
+
]);
|
|
22410
|
+
if (response2 === void 0 || !response2.ok) return void 0;
|
|
22411
|
+
const body = await Promise.race([response2.json(), timeout]);
|
|
22412
|
+
return typeof body?.version === "string" ? body.version : void 0;
|
|
22413
|
+
} catch {
|
|
22414
|
+
return void 0;
|
|
22415
|
+
} finally {
|
|
22416
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
22417
|
+
}
|
|
22418
|
+
}
|
|
22419
|
+
|
|
22420
|
+
// src/doctor.ts
|
|
22421
|
+
async function runDoctor(options = {}, dependencies2 = {}) {
|
|
22422
|
+
const workspace = resolve29(options.workspace ?? process.cwd());
|
|
22423
|
+
const home = resolve29(options.home ?? homedir());
|
|
22424
|
+
const environment = options.environment ?? process.env;
|
|
22425
|
+
const platform = options.platform ?? process.platform;
|
|
22426
|
+
const arch = options.arch ?? process.arch;
|
|
22427
|
+
const nodeVersion = options.nodeVersion ?? process.version;
|
|
22428
|
+
const checks = [];
|
|
22429
|
+
checks.push(runtimeCheck(nodeVersion, platform, arch));
|
|
22430
|
+
checks.push(await directoryCheck("workspace", workspace, false));
|
|
22431
|
+
checks.push(await directoryCheck("state directory", join31(home, ".flavor-code"), true, home));
|
|
22432
|
+
let loaded;
|
|
22433
|
+
try {
|
|
22434
|
+
loaded = await (dependencies2.loadConfig ?? loadConfig)({
|
|
22435
|
+
cwd: workspace,
|
|
22436
|
+
home,
|
|
22437
|
+
environment,
|
|
22438
|
+
seedGlobalEnv: false
|
|
22439
|
+
});
|
|
22440
|
+
checks.push({
|
|
22441
|
+
name: "configuration",
|
|
22442
|
+
status: "pass",
|
|
22443
|
+
message: loaded.sources.length === 0 ? "defaults are valid; no config files found" : `valid (${loaded.sources.length} source${loaded.sources.length === 1 ? "" : "s"})`
|
|
22444
|
+
});
|
|
22445
|
+
} catch (error) {
|
|
22446
|
+
checks.push({ name: "configuration", status: "fail", message: errorMessage3(error) });
|
|
22447
|
+
}
|
|
22448
|
+
checks.push(providerCheck(loaded?.config, environment));
|
|
22449
|
+
checks.push(await searchCheck(dependencies2.rgPath ?? bundledRgPath2));
|
|
22450
|
+
try {
|
|
22451
|
+
const shell2 = await (dependencies2.probeShell ?? probeShell)(platform, environment);
|
|
22452
|
+
checks.push(shell2 === void 0 ? { name: "shell", status: "fail", message: "no usable command shell found" } : { name: "shell", status: "pass", message: shell2 });
|
|
22453
|
+
} catch (error) {
|
|
22454
|
+
checks.push({ name: "shell", status: "fail", message: errorMessage3(error) });
|
|
22455
|
+
}
|
|
22456
|
+
checks.push(await pluginCheck([
|
|
22457
|
+
join31(home, ".flavor-code", "plugins"),
|
|
22458
|
+
join31(workspace, ".flavor", "plugins")
|
|
22459
|
+
]));
|
|
22460
|
+
try {
|
|
22461
|
+
const latest = await (dependencies2.fetchLatestVersion ?? fetchLatestVersion)({
|
|
22462
|
+
timeoutMs: options.registryTimeoutMs ?? 3e3
|
|
22463
|
+
});
|
|
22464
|
+
checks.push(latest === void 0 ? { name: "npm registry", status: "warn", message: "unreachable or returned an invalid response" } : { name: "npm registry", status: "pass", message: `reachable; latest flavor-code is v${latest}` });
|
|
22465
|
+
} catch (error) {
|
|
22466
|
+
checks.push({ name: "npm registry", status: "warn", message: errorMessage3(error) });
|
|
22467
|
+
}
|
|
22468
|
+
const summary2 = {
|
|
22469
|
+
passed: checks.filter(({ status }) => status === "pass").length,
|
|
22470
|
+
warnings: checks.filter(({ status }) => status === "warn").length,
|
|
22471
|
+
failed: checks.filter(({ status }) => status === "fail").length
|
|
22472
|
+
};
|
|
22473
|
+
return {
|
|
22474
|
+
version: packageVersion(),
|
|
22475
|
+
platform,
|
|
22476
|
+
arch,
|
|
22477
|
+
workspace,
|
|
22478
|
+
checks,
|
|
22479
|
+
summary: summary2,
|
|
22480
|
+
ok: summary2.failed === 0
|
|
22481
|
+
};
|
|
22482
|
+
}
|
|
22483
|
+
function formatDoctorReport(report) {
|
|
22484
|
+
const labels = { pass: "ok", warn: "warn", fail: "fail" };
|
|
22485
|
+
const lines = [
|
|
22486
|
+
`Flavor Doctor v${report.version}`,
|
|
22487
|
+
`Platform: ${report.platform} ${report.arch}`,
|
|
22488
|
+
`Workspace: ${report.workspace}`,
|
|
22489
|
+
"",
|
|
22490
|
+
...report.checks.map((check2) => `[${labels[check2.status]}] ${check2.name}: ${check2.message}`),
|
|
22491
|
+
"",
|
|
22492
|
+
`Summary: ${report.summary.passed} passed, ${report.summary.warnings} warning${report.summary.warnings === 1 ? "" : "s"}, ${report.summary.failed} failed`
|
|
22493
|
+
];
|
|
22494
|
+
return `${lines.join("\n")}
|
|
22495
|
+
`;
|
|
22496
|
+
}
|
|
22497
|
+
function runtimeCheck(version, platform, arch) {
|
|
22498
|
+
const major = Number.parseInt(version.replace(/^v/u, "").split(".")[0] ?? "", 10);
|
|
22499
|
+
if (!Number.isInteger(major) || !gte(`${major}.0.0`, "20.0.0")) {
|
|
22500
|
+
return { name: "runtime", status: "fail", message: `Node ${version} is unsupported; Node 20+ is required` };
|
|
22501
|
+
}
|
|
22502
|
+
return { name: "runtime", status: "pass", message: `Node ${version} (${platform} ${arch})` };
|
|
22503
|
+
}
|
|
22504
|
+
async function directoryCheck(name, path, mayBeCreated, parent = path) {
|
|
22505
|
+
try {
|
|
22506
|
+
const info = await stat11(path);
|
|
22507
|
+
if (!info.isDirectory()) return { name, status: "fail", message: `${path} is not a directory` };
|
|
22508
|
+
await access3(path, constants11.R_OK | constants11.W_OK);
|
|
22509
|
+
return { name, status: "pass", message: `${path} is readable and writable` };
|
|
22510
|
+
} catch (error) {
|
|
22511
|
+
if (mayBeCreated && errorCode2(error) === "ENOENT") {
|
|
22512
|
+
try {
|
|
22513
|
+
await access3(parent, constants11.R_OK | constants11.W_OK);
|
|
22514
|
+
return { name, status: "pass", message: `${path} can be created` };
|
|
22515
|
+
} catch (parentError) {
|
|
22516
|
+
return { name, status: "fail", message: errorMessage3(parentError) };
|
|
22517
|
+
}
|
|
22518
|
+
}
|
|
22519
|
+
return { name, status: "fail", message: errorMessage3(error) };
|
|
22520
|
+
}
|
|
22521
|
+
}
|
|
22522
|
+
function providerCheck(config, environment) {
|
|
22523
|
+
if (config === void 0) return { name: "providers", status: "warn", message: "skipped because configuration is invalid" };
|
|
22524
|
+
const configured = Object.keys(config.providers);
|
|
22525
|
+
const environmentProviders = [
|
|
22526
|
+
environment.OPENAI_API_KEY ? "openai env" : void 0,
|
|
22527
|
+
environment.ANTHROPIC_API_KEY ? "anthropic env" : void 0
|
|
22528
|
+
].filter((value) => value !== void 0);
|
|
22529
|
+
if (configured.length === 0 && environmentProviders.length === 0) {
|
|
22530
|
+
return { name: "providers", status: "warn", message: "no provider or API credential detected" };
|
|
22531
|
+
}
|
|
22532
|
+
const parts = [
|
|
22533
|
+
configured.length > 0 ? `${configured.length} configured` : void 0,
|
|
22534
|
+
environmentProviders.length > 0 ? environmentProviders.join(", ") : void 0
|
|
22535
|
+
].filter((value) => value !== void 0);
|
|
22536
|
+
return { name: "providers", status: "pass", message: parts.join("; ") };
|
|
22537
|
+
}
|
|
22538
|
+
async function searchCheck(path) {
|
|
22539
|
+
try {
|
|
22540
|
+
await access3(path, constants11.R_OK);
|
|
22541
|
+
return { name: "search", status: "pass", message: `bundled ripgrep found at ${path}` };
|
|
22542
|
+
} catch (error) {
|
|
22543
|
+
return { name: "search", status: "fail", message: `bundled ripgrep is unavailable: ${errorMessage3(error)}` };
|
|
22544
|
+
}
|
|
22545
|
+
}
|
|
22546
|
+
async function probeShell(platform, environment) {
|
|
22547
|
+
const shell2 = resolveRuntimeShell(platform, environment);
|
|
22548
|
+
if (shell2 === void 0) return void 0;
|
|
22549
|
+
const sentinel = "flavor-shell-ok";
|
|
22550
|
+
const args = shell2.kind === "posix" ? ["-c", `printf '${sentinel}'`] : shell2.kind === "cmd" ? ["/d", "/s", "/c", `echo|set /p=${sentinel}`] : ["-NoLogo", "-NoProfile", "-NonInteractive", "-Command", `[Console]::Out.Write('${sentinel}')`];
|
|
22551
|
+
const result = await execFileNoThrow(shell2.command, args, { timeout: 2e3, useCwd: false, env: environment });
|
|
22552
|
+
if (result.code !== 0 || result.stdout !== sentinel) {
|
|
22553
|
+
const detail = result.stderr.trim() || result.error || `unexpected output ${JSON.stringify(result.stdout)}`;
|
|
22554
|
+
throw new Error(`${shell2.command} was found but failed its execution probe: ${detail}`);
|
|
22555
|
+
}
|
|
22556
|
+
return `${shell2.command} (${shell2.kind}; execution probe passed)`;
|
|
22557
|
+
}
|
|
22558
|
+
async function pluginCheck(directories) {
|
|
22559
|
+
let valid = 0;
|
|
22560
|
+
const diagnostics = [];
|
|
22561
|
+
for (const directory of directories) {
|
|
22562
|
+
let entries;
|
|
22563
|
+
try {
|
|
22564
|
+
entries = await readdir10(directory, { withFileTypes: true });
|
|
22565
|
+
} catch (error) {
|
|
22566
|
+
if (errorCode2(error) !== "ENOENT") diagnostics.push(`${directory}: ${errorMessage3(error)}`);
|
|
22567
|
+
continue;
|
|
22568
|
+
}
|
|
22569
|
+
for (const entry2 of entries) {
|
|
22570
|
+
if (!entry2.isDirectory() || entry2.name.startsWith(".")) continue;
|
|
22571
|
+
const root = resolve29(directory, entry2.name);
|
|
22572
|
+
try {
|
|
22573
|
+
const rootInfo = await lstat12(root);
|
|
22574
|
+
if (rootInfo.isSymbolicLink()) throw new Error("plugin directory must not be a symbolic link");
|
|
22575
|
+
const manifest = PluginManifestSchema.parse(JSON.parse(await readFile23(join31(root, "flavor-plugin.json"), "utf8")));
|
|
22576
|
+
const main = resolve29(root, manifest.main);
|
|
22577
|
+
const delta = relative19(root, main);
|
|
22578
|
+
if (delta === ".." || delta.startsWith(`..${sep19}`) || isAbsolute15(delta)) throw new Error("plugin entry escapes its root");
|
|
22579
|
+
const mainInfo = await lstat12(main);
|
|
22580
|
+
if (!mainInfo.isFile() || mainInfo.isSymbolicLink()) throw new Error("plugin entry must be a regular file");
|
|
22581
|
+
valid += 1;
|
|
22582
|
+
} catch (error) {
|
|
22583
|
+
diagnostics.push(`${entry2.name}: ${errorMessage3(error)}`);
|
|
22584
|
+
}
|
|
22585
|
+
}
|
|
22586
|
+
}
|
|
22587
|
+
if (diagnostics.length > 0) {
|
|
22588
|
+
return { name: "plugins", status: "warn", message: `${valid} valid; ${diagnostics.join("; ")}` };
|
|
22589
|
+
}
|
|
22590
|
+
return { name: "plugins", status: "pass", message: valid === 0 ? "no project or global plugins found" : `${valid} manifest${valid === 1 ? "" : "s"} valid` };
|
|
22591
|
+
}
|
|
22592
|
+
function errorCode2(error) {
|
|
22593
|
+
return error instanceof Error && "code" in error ? String(error.code) : void 0;
|
|
22594
|
+
}
|
|
22595
|
+
function errorMessage3(error) {
|
|
22596
|
+
return error instanceof Error ? error.message : String(error);
|
|
22597
|
+
}
|
|
22598
|
+
|
|
22141
22599
|
// src/utils/intl.ts
|
|
22142
22600
|
function detectSystemLocale() {
|
|
22143
22601
|
try {
|
|
@@ -22182,7 +22640,7 @@ var LANG_LABELS = /* @__PURE__ */ new Map([
|
|
|
22182
22640
|
|
|
22183
22641
|
// src/utils/crash-guard.ts
|
|
22184
22642
|
import { mkdirSync as mkdirSync3, writeFileSync } from "fs";
|
|
22185
|
-
import { join as
|
|
22643
|
+
import { join as join32 } from "path";
|
|
22186
22644
|
var installed = false;
|
|
22187
22645
|
var crashing = false;
|
|
22188
22646
|
var crashCleanups = /* @__PURE__ */ new Set();
|
|
@@ -22213,7 +22671,7 @@ function installCrashGuard(options = {}) {
|
|
|
22213
22671
|
installed = true;
|
|
22214
22672
|
const crashLogDir = () => {
|
|
22215
22673
|
try {
|
|
22216
|
-
return
|
|
22674
|
+
return join32((options.workspace ?? process.cwd)(), ".flavor");
|
|
22217
22675
|
} catch {
|
|
22218
22676
|
return process.cwd();
|
|
22219
22677
|
}
|
|
@@ -22222,7 +22680,7 @@ function installCrashGuard(options = {}) {
|
|
|
22222
22680
|
try {
|
|
22223
22681
|
const dir = crashLogDir();
|
|
22224
22682
|
mkdirSync3(dir, { recursive: true });
|
|
22225
|
-
const path =
|
|
22683
|
+
const path = join32(dir, `crash-${(/* @__PURE__ */ new Date()).toISOString().replace(/[^0-9]/g, "")}-${process.pid}.log`);
|
|
22226
22684
|
writeFileSync(path, [
|
|
22227
22685
|
`kind: ${kind}`,
|
|
22228
22686
|
`version: ${process.version}`,
|
|
@@ -22721,14 +23179,14 @@ var RetryMonitor = class {
|
|
|
22721
23179
|
recordCall(toolName, params) {
|
|
22722
23180
|
this.#slidingWindow.push(toolName, params);
|
|
22723
23181
|
}
|
|
22724
|
-
recordError(toolName, params,
|
|
23182
|
+
recordError(toolName, params, errorCode3) {
|
|
22725
23183
|
void params;
|
|
22726
23184
|
const existing = this.#errorStates.get(toolName);
|
|
22727
23185
|
if (existing === void 0) {
|
|
22728
|
-
this.#errorStates.set(toolName, { retryCount: 1, lastErrorCode:
|
|
23186
|
+
this.#errorStates.set(toolName, { retryCount: 1, lastErrorCode: errorCode3 });
|
|
22729
23187
|
} else {
|
|
22730
23188
|
existing.retryCount += 1;
|
|
22731
|
-
existing.lastErrorCode =
|
|
23189
|
+
existing.lastErrorCode = errorCode3;
|
|
22732
23190
|
}
|
|
22733
23191
|
}
|
|
22734
23192
|
recordSuccess(toolName) {
|
|
@@ -22993,8 +23451,8 @@ ${total}`;
|
|
|
22993
23451
|
|
|
22994
23452
|
// src/memory/store.ts
|
|
22995
23453
|
import { createHash as createHash17 } from "crypto";
|
|
22996
|
-
import { readFile as
|
|
22997
|
-
import { resolve as
|
|
23454
|
+
import { readFile as readFile24, rm as rm12 } from "fs/promises";
|
|
23455
|
+
import { resolve as resolve30, join as join33, relative as relative20, sep as sep20 } from "path";
|
|
22998
23456
|
|
|
22999
23457
|
// src/memory/similarity.ts
|
|
23000
23458
|
function normalizeForSimilarity(value) {
|
|
@@ -23096,10 +23554,10 @@ var MemoryStore = class {
|
|
|
23096
23554
|
constructor(options) {
|
|
23097
23555
|
if (!Number.isSafeInteger(options.maxEntries) || options.maxEntries < 1) throw new Error("maxEntries must be an integer of at least 1");
|
|
23098
23556
|
if (!Number.isSafeInteger(options.maxEntryChars) || options.maxEntryChars < 1) throw new Error("maxEntryChars must be an integer of at least 1");
|
|
23099
|
-
this.workspace =
|
|
23100
|
-
this.#memoryRoot =
|
|
23101
|
-
this.path =
|
|
23102
|
-
this.#behaviorPath =
|
|
23557
|
+
this.workspace = resolve30(options.workspace);
|
|
23558
|
+
this.#memoryRoot = join33(this.workspace, ".flavor", "memory");
|
|
23559
|
+
this.path = join33(this.#memoryRoot, "MEMORY.md");
|
|
23560
|
+
this.#behaviorPath = join33(this.#memoryRoot, "behavior.json");
|
|
23103
23561
|
this.#maxEntries = options.maxEntries;
|
|
23104
23562
|
this.#maxEntryChars = options.maxEntryChars;
|
|
23105
23563
|
}
|
|
@@ -23317,14 +23775,14 @@ var MemoryStore = class {
|
|
|
23317
23775
|
});
|
|
23318
23776
|
}
|
|
23319
23777
|
async #readTaskItem(reference) {
|
|
23320
|
-
const document = decodeTask(await
|
|
23778
|
+
const document = decodeTask(await readFile24(this.#resolveContentPath(reference.contentPath), "utf8"));
|
|
23321
23779
|
return document.items.find((item) => item.id === reference.id)?.content ?? reference.summary;
|
|
23322
23780
|
}
|
|
23323
23781
|
#resolveContentPath(contentPath) {
|
|
23324
23782
|
if (!/^tasks\/[A-Za-z0-9][A-Za-z0-9._-]{0,127}\.md$/.test(contentPath)) throw new Error("Invalid memory content path");
|
|
23325
|
-
const path =
|
|
23326
|
-
const rel =
|
|
23327
|
-
if (rel === ".." || rel.startsWith(`..${
|
|
23783
|
+
const path = resolve30(this.#memoryRoot, ...contentPath.split("/"));
|
|
23784
|
+
const rel = relative20(this.#memoryRoot, path);
|
|
23785
|
+
if (rel === ".." || rel.startsWith(`..${sep20}`)) throw new Error("Memory content path escapes the memory directory");
|
|
23328
23786
|
return path;
|
|
23329
23787
|
}
|
|
23330
23788
|
async #updateIndex(update) {
|
|
@@ -23782,13 +24240,13 @@ var MemoryReviewBridge = class {
|
|
|
23782
24240
|
|
|
23783
24241
|
// src/execution/docker.ts
|
|
23784
24242
|
import { spawn as spawn6 } from "child_process";
|
|
23785
|
-
import { relative as
|
|
24243
|
+
import { relative as relative21, resolve as resolve31, sep as sep21, win32 } from "path";
|
|
23786
24244
|
function buildDockerInvocation(options) {
|
|
23787
24245
|
const workspace = options.workspace;
|
|
23788
24246
|
const cwd = options.request.cwd;
|
|
23789
24247
|
const windows = /^[A-Za-z]:[\\/]/.test(workspace);
|
|
23790
|
-
const delta = windows ? win32.relative(workspace, cwd) :
|
|
23791
|
-
if (delta === ".." || delta.startsWith(`..${windows ? "\\" :
|
|
24248
|
+
const delta = windows ? win32.relative(workspace, cwd) : relative21(resolve31(workspace), resolve31(cwd));
|
|
24249
|
+
if (delta === ".." || delta.startsWith(`..${windows ? "\\" : sep21}`)) throw new Error("Docker cwd escapes workspace");
|
|
23792
24250
|
const containerCwd = delta.length === 0 ? "/workspace" : `/workspace/${delta.replaceAll("\\", "/")}`;
|
|
23793
24251
|
const args = [
|
|
23794
24252
|
"run",
|
|
@@ -23826,53 +24284,123 @@ var DockerExecutionEnvironment = class {
|
|
|
23826
24284
|
}
|
|
23827
24285
|
exec(request, signal) {
|
|
23828
24286
|
const invocation = buildDockerInvocation({ ...this.#options, request });
|
|
23829
|
-
return (this.#options.run ??
|
|
24287
|
+
return (this.#options.run ?? runDockerInvocation)(invocation, signal, request.timeoutMs, request.maxOutputBytes);
|
|
23830
24288
|
}
|
|
23831
24289
|
async dispose() {
|
|
23832
24290
|
}
|
|
23833
24291
|
};
|
|
23834
|
-
|
|
24292
|
+
var DEFAULT_DOCKER_MAX_OUTPUT_BYTES = 1048576;
|
|
24293
|
+
var DOCKER_TERMINATION_GRACE_MS = 250;
|
|
24294
|
+
var DOCKER_TERMINATION_FAILURE_MS = 5e3;
|
|
24295
|
+
function runDockerInvocation(invocation, signal, timeoutMs, maxOutputBytes = DEFAULT_DOCKER_MAX_OUTPUT_BYTES) {
|
|
23835
24296
|
return new Promise((resolvePromise) => {
|
|
23836
24297
|
if (signal?.aborted) {
|
|
23837
24298
|
resolvePromise({ exitCode: null, signal: null, stdout: "", stderr: "", terminationReason: "cancelled" });
|
|
23838
24299
|
return;
|
|
23839
24300
|
}
|
|
23840
24301
|
const child = spawn6(invocation.command, invocation.args, { windowsHide: true, shell: false });
|
|
23841
|
-
const stdout =
|
|
23842
|
-
const stderr =
|
|
24302
|
+
const stdout = new BoundedUtf8Output(maxOutputBytes);
|
|
24303
|
+
const stderr = new BoundedUtf8Output(maxOutputBytes);
|
|
23843
24304
|
let reason = null;
|
|
23844
24305
|
let settled = false;
|
|
24306
|
+
let forceTimer;
|
|
24307
|
+
let failureTimer;
|
|
23845
24308
|
const finish = (result) => {
|
|
23846
24309
|
if (settled) return;
|
|
23847
24310
|
settled = true;
|
|
23848
24311
|
if (timer !== void 0) clearTimeout(timer);
|
|
24312
|
+
if (forceTimer !== void 0) clearTimeout(forceTimer);
|
|
24313
|
+
if (failureTimer !== void 0) clearTimeout(failureTimer);
|
|
23849
24314
|
signal?.removeEventListener("abort", abort2);
|
|
23850
24315
|
resolvePromise(result);
|
|
23851
24316
|
};
|
|
24317
|
+
const collected = (exitCode, closedSignal) => ({
|
|
24318
|
+
exitCode,
|
|
24319
|
+
signal: closedSignal,
|
|
24320
|
+
stdout: stdout.text(),
|
|
24321
|
+
stderr: stderr.text(),
|
|
24322
|
+
terminationReason: reason,
|
|
24323
|
+
truncated: stdout.truncated || stderr.truncated,
|
|
24324
|
+
truncation: { stdout: stdout.metadata(), stderr: stderr.metadata() }
|
|
24325
|
+
});
|
|
23852
24326
|
const stop = (next) => {
|
|
24327
|
+
if (reason !== null || settled) return;
|
|
23853
24328
|
reason = next;
|
|
23854
|
-
child.kill();
|
|
24329
|
+
child.kill("SIGTERM");
|
|
24330
|
+
forceTimer = setTimeout(() => child.kill("SIGKILL"), DOCKER_TERMINATION_GRACE_MS);
|
|
24331
|
+
forceTimer.unref();
|
|
24332
|
+
failureTimer = setTimeout(() => finish(collected(null, null)), DOCKER_TERMINATION_FAILURE_MS);
|
|
24333
|
+
failureTimer.unref();
|
|
23855
24334
|
};
|
|
23856
24335
|
const timer = timeoutMs === void 0 ? void 0 : setTimeout(() => stop("timeout"), timeoutMs);
|
|
23857
24336
|
timer?.unref();
|
|
23858
24337
|
const abort2 = () => stop("cancelled");
|
|
23859
24338
|
signal?.addEventListener("abort", abort2, { once: true });
|
|
23860
|
-
child.stdout.on("data", (value) => stdout.
|
|
23861
|
-
child.stderr.on("data", (value) => stderr.
|
|
24339
|
+
child.stdout.on("data", (value) => stdout.add(value));
|
|
24340
|
+
child.stderr.on("data", (value) => stderr.add(value));
|
|
23862
24341
|
child.once("error", (error) => {
|
|
23863
|
-
|
|
24342
|
+
stderr.add(Buffer.from(error.message));
|
|
24343
|
+
finish(collected(127, null));
|
|
23864
24344
|
});
|
|
23865
24345
|
child.once("close", (code, closedSignal) => {
|
|
23866
|
-
finish(
|
|
23867
|
-
exitCode: code,
|
|
23868
|
-
signal: closedSignal,
|
|
23869
|
-
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
23870
|
-
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
23871
|
-
terminationReason: reason
|
|
23872
|
-
});
|
|
24346
|
+
finish(collected(code, closedSignal));
|
|
23873
24347
|
});
|
|
23874
24348
|
});
|
|
23875
24349
|
}
|
|
24350
|
+
var BoundedUtf8Output = class {
|
|
24351
|
+
#limit;
|
|
24352
|
+
#headLimit;
|
|
24353
|
+
#tailLimit;
|
|
24354
|
+
#head = Buffer.alloc(0);
|
|
24355
|
+
#tailChunks = [];
|
|
24356
|
+
#tailBytes = 0;
|
|
24357
|
+
#complete = Buffer.alloc(0);
|
|
24358
|
+
#bytes = 0;
|
|
24359
|
+
constructor(limit) {
|
|
24360
|
+
this.#limit = Number.isSafeInteger(limit) && limit > 0 ? limit : DEFAULT_DOCKER_MAX_OUTPUT_BYTES;
|
|
24361
|
+
this.#headLimit = Math.ceil(this.#limit / 2);
|
|
24362
|
+
this.#tailLimit = Math.floor(this.#limit / 2);
|
|
24363
|
+
}
|
|
24364
|
+
get truncated() {
|
|
24365
|
+
return this.#bytes > this.#limit;
|
|
24366
|
+
}
|
|
24367
|
+
add(chunk) {
|
|
24368
|
+
this.#bytes += chunk.length;
|
|
24369
|
+
if (this.#complete.length < this.#limit) {
|
|
24370
|
+
this.#complete = Buffer.concat([this.#complete, chunk]).subarray(0, this.#limit);
|
|
24371
|
+
}
|
|
24372
|
+
if (this.#head.length < this.#headLimit) {
|
|
24373
|
+
this.#head = Buffer.concat([this.#head, chunk.subarray(0, this.#headLimit - this.#head.length)]);
|
|
24374
|
+
}
|
|
24375
|
+
if (this.#tailLimit > 0) {
|
|
24376
|
+
this.#tailChunks.push(chunk);
|
|
24377
|
+
this.#tailBytes += chunk.length;
|
|
24378
|
+
if (this.#tailBytes >= this.#tailLimit * 2) {
|
|
24379
|
+
const merged = Buffer.concat(this.#tailChunks).subarray(-this.#tailLimit);
|
|
24380
|
+
this.#tailChunks = [merged];
|
|
24381
|
+
this.#tailBytes = merged.length;
|
|
24382
|
+
}
|
|
24383
|
+
}
|
|
24384
|
+
}
|
|
24385
|
+
metadata() {
|
|
24386
|
+
return { truncated: this.truncated, originalBytes: this.#bytes, limitBytes: this.#limit };
|
|
24387
|
+
}
|
|
24388
|
+
text() {
|
|
24389
|
+
if (!this.truncated) return this.#complete.toString("utf8");
|
|
24390
|
+
const tail = Buffer.concat(this.#tailChunks).subarray(-this.#tailLimit);
|
|
24391
|
+
return `${decodeUtf8Edge(this.#head, "prefix")}\u2026${decodeUtf8Edge(tail, "suffix")}`;
|
|
24392
|
+
}
|
|
24393
|
+
};
|
|
24394
|
+
function decodeUtf8Edge(buffer, edge) {
|
|
24395
|
+
for (let offset = 0; offset <= Math.min(3, buffer.length); offset += 1) {
|
|
24396
|
+
const candidate = edge === "prefix" ? buffer.subarray(0, buffer.length - offset) : buffer.subarray(offset);
|
|
24397
|
+
try {
|
|
24398
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(candidate);
|
|
24399
|
+
} catch {
|
|
24400
|
+
}
|
|
24401
|
+
}
|
|
24402
|
+
return new TextDecoder("utf-8").decode(buffer);
|
|
24403
|
+
}
|
|
23876
24404
|
|
|
23877
24405
|
// src/execution/factory.ts
|
|
23878
24406
|
function createExecutionEnvironment(workspace, config) {
|
|
@@ -23887,8 +24415,8 @@ function createExecutionEnvironment(workspace, config) {
|
|
|
23887
24415
|
}
|
|
23888
24416
|
|
|
23889
24417
|
// src/ide/client.ts
|
|
23890
|
-
import { readFile as
|
|
23891
|
-
import { basename as
|
|
24418
|
+
import { readFile as readFile25, readdir as readdir11, stat as stat12 } from "fs/promises";
|
|
24419
|
+
import { basename as basename8, join as join34, relative as relative22, resolve as resolve32, sep as sep22 } from "path";
|
|
23892
24420
|
var FlavorIdeClient = class {
|
|
23893
24421
|
#workspace;
|
|
23894
24422
|
#home;
|
|
@@ -23899,8 +24427,8 @@ var FlavorIdeClient = class {
|
|
|
23899
24427
|
#sessionId;
|
|
23900
24428
|
#eventTail = Promise.resolve();
|
|
23901
24429
|
constructor(options) {
|
|
23902
|
-
this.#workspace =
|
|
23903
|
-
this.#home =
|
|
24430
|
+
this.#workspace = resolve32(options.workspace);
|
|
24431
|
+
this.#home = resolve32(options.home);
|
|
23904
24432
|
this.#environment = options.environment ?? process.env;
|
|
23905
24433
|
this.#fetch = options.fetch ?? fetch;
|
|
23906
24434
|
}
|
|
@@ -23984,17 +24512,17 @@ var FlavorIdeClient = class {
|
|
|
23984
24512
|
};
|
|
23985
24513
|
if (await this.#healthy(configured)) return configured;
|
|
23986
24514
|
}
|
|
23987
|
-
const directory =
|
|
24515
|
+
const directory = join34(this.#home, ".flavor-code", "ide");
|
|
23988
24516
|
let entries;
|
|
23989
24517
|
try {
|
|
23990
|
-
entries = (await
|
|
24518
|
+
entries = (await readdir11(directory)).filter((name) => name.endsWith(".lock"));
|
|
23991
24519
|
} catch {
|
|
23992
24520
|
return void 0;
|
|
23993
24521
|
}
|
|
23994
24522
|
const candidates = (await Promise.all(entries.map(async (name) => {
|
|
23995
|
-
const path =
|
|
24523
|
+
const path = join34(directory, name);
|
|
23996
24524
|
try {
|
|
23997
|
-
const [contents, info] = await Promise.all([
|
|
24525
|
+
const [contents, info] = await Promise.all([readFile25(path, "utf8"), stat12(path)]);
|
|
23998
24526
|
const lock = parseLockfile(contents);
|
|
23999
24527
|
return lock === void 0 ? void 0 : { lock, modified: info.mtimeMs };
|
|
24000
24528
|
} catch {
|
|
@@ -24098,17 +24626,17 @@ function parsePort(value) {
|
|
|
24098
24626
|
return Number.isInteger(port2) && port2 > 0 && port2 <= 65535 ? port2 : void 0;
|
|
24099
24627
|
}
|
|
24100
24628
|
function isWithin8(parent, child) {
|
|
24101
|
-
const base = normalize(
|
|
24102
|
-
const target = normalize(
|
|
24103
|
-
return target === base || target.startsWith(`${base}${
|
|
24629
|
+
const base = normalize(resolve32(parent));
|
|
24630
|
+
const target = normalize(resolve32(child));
|
|
24631
|
+
return target === base || target.startsWith(`${base}${sep22}`);
|
|
24104
24632
|
}
|
|
24105
24633
|
function normalize(path) {
|
|
24106
24634
|
const normalized = path.normalize("NFC");
|
|
24107
24635
|
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
24108
24636
|
}
|
|
24109
24637
|
function relativeDisplay(workspace, filePath) {
|
|
24110
|
-
const path =
|
|
24111
|
-
return path && !path.startsWith("../") ? path :
|
|
24638
|
+
const path = relative22(workspace, filePath).replaceAll("\\", "/");
|
|
24639
|
+
return path && !path.startsWith("../") ? path : basename8(filePath);
|
|
24112
24640
|
}
|
|
24113
24641
|
|
|
24114
24642
|
// src/jobs/registry.ts
|
|
@@ -24129,8 +24657,8 @@ var JobRegistry = class {
|
|
|
24129
24657
|
const id = `job-${randomUUID19().slice(0, 12)}`;
|
|
24130
24658
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24131
24659
|
let settle;
|
|
24132
|
-
const done = new Promise((
|
|
24133
|
-
settle =
|
|
24660
|
+
const done = new Promise((resolve43) => {
|
|
24661
|
+
settle = resolve43;
|
|
24134
24662
|
});
|
|
24135
24663
|
const record3 = {
|
|
24136
24664
|
id,
|
|
@@ -24254,7 +24782,7 @@ function snapshot2(job) {
|
|
|
24254
24782
|
// src/terminal/service.ts
|
|
24255
24783
|
import { randomUUID as randomUUID20 } from "crypto";
|
|
24256
24784
|
import { createRequire as nodeCreateRequire2 } from "module";
|
|
24257
|
-
import { isAbsolute as
|
|
24785
|
+
import { isAbsolute as isAbsolute16, relative as relative23, resolve as resolve33, sep as sep23 } from "path";
|
|
24258
24786
|
var TerminalService = class {
|
|
24259
24787
|
#root;
|
|
24260
24788
|
#factory;
|
|
@@ -24262,7 +24790,7 @@ var TerminalService = class {
|
|
|
24262
24790
|
#maxOutputChars;
|
|
24263
24791
|
#sessions = /* @__PURE__ */ new Map();
|
|
24264
24792
|
constructor(workspace, options = {}) {
|
|
24265
|
-
this.#root =
|
|
24793
|
+
this.#root = resolve33(workspace);
|
|
24266
24794
|
this.#factory = options.factory ?? defaultFactory;
|
|
24267
24795
|
this.#jobs = options.jobs;
|
|
24268
24796
|
this.#maxOutputChars = options.maxOutputChars ?? 2e5;
|
|
@@ -24380,15 +24908,15 @@ function cleanEnvironment(environment) {
|
|
|
24380
24908
|
return Object.fromEntries(Object.entries(environment).filter((entry2) => entry2[1] !== void 0));
|
|
24381
24909
|
}
|
|
24382
24910
|
function workspacePath(root, input) {
|
|
24383
|
-
const path =
|
|
24384
|
-
const delta =
|
|
24385
|
-
if (delta === ".." || delta.startsWith(`..${
|
|
24911
|
+
const path = resolve33(root, input);
|
|
24912
|
+
const delta = relative23(root, path);
|
|
24913
|
+
if (delta === ".." || delta.startsWith(`..${sep23}`) || isAbsolute16(delta)) throw new Error("Terminal cwd is outside the workspace");
|
|
24386
24914
|
return path;
|
|
24387
24915
|
}
|
|
24388
24916
|
|
|
24389
24917
|
// src/tools/terminal.ts
|
|
24390
24918
|
import { z as z40 } from "zod";
|
|
24391
|
-
import { resolve as
|
|
24919
|
+
import { resolve as resolve34 } from "path";
|
|
24392
24920
|
var Id = z40.string().regex(/^term-[a-f0-9-]+$/);
|
|
24393
24921
|
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() });
|
|
24394
24922
|
var OptionalBoolean = z40.union([z40.boolean(), z40.string().refine((value) => value === "true" || value === "false")]);
|
|
@@ -24402,8 +24930,8 @@ function createTerminalTools(terminals, workspace = process.cwd()) {
|
|
|
24402
24930
|
name: "TerminalOpen",
|
|
24403
24931
|
description: "Open a persistent interactive pseudo-terminal in the workspace",
|
|
24404
24932
|
inputSchema: OpenInput,
|
|
24405
|
-
paths: (input) => [
|
|
24406
|
-
permissions: (input) => ({ paths: [
|
|
24933
|
+
paths: (input) => [resolve34(workspace, input.cwd ?? ".")],
|
|
24934
|
+
permissions: (input) => ({ paths: [resolve34(workspace, input.cwd ?? ".")], command: input.shell ?? "system-shell", args: input.args ?? [], cwd: resolve34(workspace, input.cwd ?? ".") }),
|
|
24407
24935
|
execute: async (input, _signal, context) => terminals.open({
|
|
24408
24936
|
owner: owner(context),
|
|
24409
24937
|
...input.cwd === void 0 ? {} : { cwd: input.cwd },
|
|
@@ -24499,29 +25027,29 @@ function palSocketAddress(options) {
|
|
|
24499
25027
|
import { createHash as createHash20 } from "crypto";
|
|
24500
25028
|
import { Socket as Socket2 } from "net";
|
|
24501
25029
|
import { tmpdir as tmpdir3 } from "os";
|
|
24502
|
-
import { join as
|
|
25030
|
+
import { join as join36 } from "path";
|
|
24503
25031
|
import { spawn as spawn7 } from "child_process";
|
|
24504
25032
|
|
|
24505
25033
|
// src/pals/broker.ts
|
|
24506
25034
|
import { createHash as createHash19, randomUUID as randomUUID23, timingSafeEqual } from "crypto";
|
|
24507
|
-
import { chmod as chmod3, lstat as
|
|
25035
|
+
import { chmod as chmod3, lstat as lstat13, mkdir as mkdir23, unlink as unlink6 } from "fs/promises";
|
|
24508
25036
|
import { createServer as createServer4, Socket } from "net";
|
|
24509
25037
|
import { dirname as dirname23 } from "path";
|
|
24510
25038
|
|
|
24511
25039
|
// src/pals/auth.ts
|
|
24512
25040
|
import { randomBytes as randomBytes3, randomUUID as randomUUID21 } from "crypto";
|
|
24513
|
-
import { chmod as chmod2, link as link2, mkdir as mkdir21, open as open12, readFile as
|
|
24514
|
-
import { homedir } from "os";
|
|
24515
|
-
import { join as
|
|
25041
|
+
import { chmod as chmod2, link as link2, mkdir as mkdir21, open as open12, readFile as readFile26, unlink as unlink4 } from "fs/promises";
|
|
25042
|
+
import { homedir as homedir2 } from "os";
|
|
25043
|
+
import { join as join35 } from "path";
|
|
24516
25044
|
var PAL_AUTH_TOKEN_BYTES = 32;
|
|
24517
25045
|
var TOKEN_PATTERN = /^[a-f0-9]{64}$/;
|
|
24518
25046
|
async function loadOrCreatePalAuthToken(options = {}) {
|
|
24519
|
-
const directory =
|
|
24520
|
-
const tokenPath =
|
|
25047
|
+
const directory = join35(options.home ?? homedir2(), ".flavor-code", "pals");
|
|
25048
|
+
const tokenPath = join35(directory, "auth-token");
|
|
24521
25049
|
await mkdir21(directory, { recursive: true, mode: 448 });
|
|
24522
25050
|
await chmod2(directory, 448);
|
|
24523
25051
|
const token = (options.random ?? randomBytes3)(PAL_AUTH_TOKEN_BYTES).toString("hex");
|
|
24524
|
-
const temporaryPath =
|
|
25052
|
+
const temporaryPath = join35(directory, `.auth-token-${process.pid}-${randomUUID21()}.tmp`);
|
|
24525
25053
|
let handle;
|
|
24526
25054
|
try {
|
|
24527
25055
|
handle = await open12(temporaryPath, "wx", 384);
|
|
@@ -24544,7 +25072,7 @@ async function loadOrCreatePalAuthToken(options = {}) {
|
|
|
24544
25072
|
if (error.code !== "ENOENT") throw error;
|
|
24545
25073
|
});
|
|
24546
25074
|
}
|
|
24547
|
-
const stored = (await
|
|
25075
|
+
const stored = (await readFile26(tokenPath, "utf8")).trim();
|
|
24548
25076
|
if (!TOKEN_PATTERN.test(stored)) throw new Error("Pals authentication token store is invalid");
|
|
24549
25077
|
await chmod2(tokenPath, 384);
|
|
24550
25078
|
return stored;
|
|
@@ -24556,7 +25084,7 @@ function validatePalAuthToken(token) {
|
|
|
24556
25084
|
|
|
24557
25085
|
// src/pals/lifecycle.ts
|
|
24558
25086
|
import { randomUUID as randomUUID22 } from "crypto";
|
|
24559
|
-
import { mkdir as mkdir22, open as open13, readFile as
|
|
25087
|
+
import { mkdir as mkdir22, open as open13, readFile as readFile27, rename as rename11, stat as stat13, unlink as unlink5 } from "fs/promises";
|
|
24560
25088
|
import { dirname as dirname22 } from "path";
|
|
24561
25089
|
function defaultProcessAlive(pid) {
|
|
24562
25090
|
if (!Number.isSafeInteger(pid) || pid <= 0) return true;
|
|
@@ -24588,7 +25116,7 @@ async function acquirePalFileLock(options) {
|
|
|
24588
25116
|
handle = await open13(options.path, "wx", 384);
|
|
24589
25117
|
} catch (error) {
|
|
24590
25118
|
if (error.code !== "EEXIST") throw error;
|
|
24591
|
-
const [contents, info] = await Promise.all([
|
|
25119
|
+
const [contents, info] = await Promise.all([readFile27(options.path, "utf8").catch(() => ""), stat13(options.path).catch(() => void 0)]);
|
|
24592
25120
|
if (await options.endpointLive()) return void 0;
|
|
24593
25121
|
const existing = parseMetadata(contents);
|
|
24594
25122
|
if (existing !== void 0 && (options.processAlive ?? defaultProcessAlive)(existing.pid)) return void 0;
|
|
@@ -24618,7 +25146,7 @@ async function acquirePalFileLock(options) {
|
|
|
24618
25146
|
await handle.close();
|
|
24619
25147
|
}
|
|
24620
25148
|
const release = async () => {
|
|
24621
|
-
const current = parseMetadata(await
|
|
25149
|
+
const current = parseMetadata(await readFile27(options.path, "utf8").catch(() => ""));
|
|
24622
25150
|
if (current?.nonce !== nonce) return;
|
|
24623
25151
|
await unlink5(options.path).catch((error) => {
|
|
24624
25152
|
if (error.code !== "ENOENT") throw error;
|
|
@@ -24634,15 +25162,15 @@ async function acquirePalFileLock(options) {
|
|
|
24634
25162
|
// src/pals/broker-cli.ts
|
|
24635
25163
|
var startupAttempts = /* @__PURE__ */ new Map();
|
|
24636
25164
|
function delay3(milliseconds) {
|
|
24637
|
-
return new Promise((
|
|
25165
|
+
return new Promise((resolve43) => setTimeout(resolve43, milliseconds));
|
|
24638
25166
|
}
|
|
24639
25167
|
function canConnect(address) {
|
|
24640
|
-
return new Promise((
|
|
25168
|
+
return new Promise((resolve43) => {
|
|
24641
25169
|
const socket = new Socket2();
|
|
24642
25170
|
const finish = (connected) => {
|
|
24643
25171
|
socket.removeAllListeners();
|
|
24644
25172
|
socket.destroy();
|
|
24645
|
-
|
|
25173
|
+
resolve43(connected);
|
|
24646
25174
|
};
|
|
24647
25175
|
socket.once("connect", () => finish(true));
|
|
24648
25176
|
socket.once("error", () => finish(false));
|
|
@@ -24651,7 +25179,7 @@ function canConnect(address) {
|
|
|
24651
25179
|
}
|
|
24652
25180
|
function startupLockPath(address) {
|
|
24653
25181
|
const digest = createHash20("sha256").update(address, "utf8").digest("hex").slice(0, 24);
|
|
24654
|
-
return
|
|
25182
|
+
return join36(tmpdir3(), `flavor-code-pals-${digest}.startup.lock`);
|
|
24655
25183
|
}
|
|
24656
25184
|
async function spawnProductionBroker(address) {
|
|
24657
25185
|
const entry2 = process.argv[1];
|
|
@@ -24833,8 +25361,8 @@ var PalClient = class {
|
|
|
24833
25361
|
let disconnectTimer;
|
|
24834
25362
|
await Promise.race([
|
|
24835
25363
|
this.request({ version: 1, type: "disconnect", requestId: randomUUID24() }).catch(() => void 0),
|
|
24836
|
-
new Promise((
|
|
24837
|
-
disconnectTimer = setTimeout(
|
|
25364
|
+
new Promise((resolve43) => {
|
|
25365
|
+
disconnectTimer = setTimeout(resolve43, Math.min(this.requestTimeoutMs, 250));
|
|
24838
25366
|
})
|
|
24839
25367
|
]);
|
|
24840
25368
|
if (disconnectTimer !== void 0) clearTimeout(disconnectTimer);
|
|
@@ -24880,7 +25408,7 @@ var PalClient = class {
|
|
|
24880
25408
|
return presence;
|
|
24881
25409
|
}
|
|
24882
25410
|
openSocket() {
|
|
24883
|
-
return new Promise((
|
|
25411
|
+
return new Promise((resolve43, reject) => {
|
|
24884
25412
|
let settled = false;
|
|
24885
25413
|
const rejectConnection = (error) => {
|
|
24886
25414
|
if (settled) return;
|
|
@@ -24903,7 +25431,7 @@ var PalClient = class {
|
|
|
24903
25431
|
settled = true;
|
|
24904
25432
|
if (this.connectingReject === rejectConnection) this.connectingReject = void 0;
|
|
24905
25433
|
socket.off("error", onInitialError);
|
|
24906
|
-
|
|
25434
|
+
resolve43();
|
|
24907
25435
|
};
|
|
24908
25436
|
const onInitialError = (error) => {
|
|
24909
25437
|
socket.off("connect", onConnect);
|
|
@@ -24934,12 +25462,12 @@ var PalClient = class {
|
|
|
24934
25462
|
if (this.closed || socket === void 0 || socket.destroyed || !socket.writable) {
|
|
24935
25463
|
return Promise.reject(new Error(this.closed ? "Pal client is closed" : "Pal client is not connected"));
|
|
24936
25464
|
}
|
|
24937
|
-
return new Promise((
|
|
25465
|
+
return new Promise((resolve43, reject) => {
|
|
24938
25466
|
const timer = setTimeout(() => {
|
|
24939
25467
|
this.pending.delete(request.requestId);
|
|
24940
25468
|
reject(new Error(`Pal request '${request.type}' timed out`));
|
|
24941
25469
|
}, this.requestTimeoutMs);
|
|
24942
|
-
this.pending.set(request.requestId, { resolve:
|
|
25470
|
+
this.pending.set(request.requestId, { resolve: resolve43, reject, timer });
|
|
24943
25471
|
let frame;
|
|
24944
25472
|
try {
|
|
24945
25473
|
frame = encodeControlFrame(BrokerRequestSchema.parse(request));
|
|
@@ -25304,7 +25832,7 @@ function createPromptEnvironment(input = {}) {
|
|
|
25304
25832
|
date: Number.isNaN(now.getTime()) ? "unknown" : localDateKey(now),
|
|
25305
25833
|
platform: promptEnvironmentValue(input.platform ?? process.platform),
|
|
25306
25834
|
osVersion: promptEnvironmentValue(input.osVersion ?? `${osVersion()} ${osRelease()}`),
|
|
25307
|
-
shell: promptEnvironmentValue(input.shell ??
|
|
25835
|
+
shell: promptEnvironmentValue(input.shell ?? resolveRuntimeShell()?.command),
|
|
25308
25836
|
isGitRepository: input.isGitRepository ?? "unknown"
|
|
25309
25837
|
};
|
|
25310
25838
|
}
|
|
@@ -25343,8 +25871,8 @@ var ApprovalBridge = class {
|
|
|
25343
25871
|
}
|
|
25344
25872
|
};
|
|
25345
25873
|
async function createProductionRuntime(options) {
|
|
25346
|
-
const workspace =
|
|
25347
|
-
const home =
|
|
25874
|
+
const workspace = resolve35(options.workspace ?? process.cwd());
|
|
25875
|
+
const home = resolve35(options.home ?? homedir3());
|
|
25348
25876
|
const environment = options.environment ?? process.env;
|
|
25349
25877
|
const ide = new FlavorIdeClient({ workspace, home, environment });
|
|
25350
25878
|
const ideReady = ide.initialize();
|
|
@@ -25534,6 +26062,7 @@ async function createProductionRuntime(options) {
|
|
|
25534
26062
|
createGitHistoryTool(workspace),
|
|
25535
26063
|
createShellTool(workspace, {
|
|
25536
26064
|
jobs,
|
|
26065
|
+
environment,
|
|
25537
26066
|
...executionEnvironment === void 0 ? {} : { executionEnvironment }
|
|
25538
26067
|
}),
|
|
25539
26068
|
createWebFetchTool(),
|
|
@@ -25587,8 +26116,8 @@ async function createProductionRuntime(options) {
|
|
|
25587
26116
|
const registeredProviders = registration.registered;
|
|
25588
26117
|
let effectiveLlm = registration.effectiveLlm;
|
|
25589
26118
|
const pluginHost = new PluginHost({
|
|
25590
|
-
globalPluginDirs: [
|
|
25591
|
-
projectPluginDirs: [
|
|
26119
|
+
globalPluginDirs: [join37(home, ".flavor-code", "plugins")],
|
|
26120
|
+
projectPluginDirs: [join37(workspace, ".flavor", "plugins")],
|
|
25592
26121
|
config,
|
|
25593
26122
|
// Existing project plugins (including astgraph and superharness) use Node.js
|
|
25594
26123
|
// built-ins. The Worker/vm sandbox deliberately blocks those imports, so it
|
|
@@ -25692,12 +26221,12 @@ async function createProductionRuntime(options) {
|
|
|
25692
26221
|
try {
|
|
25693
26222
|
if (options.collaboration !== void 0) {
|
|
25694
26223
|
const collaboration = options.collaboration;
|
|
25695
|
-
const defaultAlias = `${
|
|
26224
|
+
const defaultAlias = `${basename9(workspace) || "flavor"}-${collaboration.instanceId.slice(0, 8)}`.slice(0, 64);
|
|
25696
26225
|
const alias = PalAliasSchema.parse(collaboration.alias?.trim() || defaultAlias);
|
|
25697
26226
|
const address = palSocketAddress({
|
|
25698
26227
|
platform: process.platform,
|
|
25699
26228
|
userScope: home,
|
|
25700
|
-
runtimeDir:
|
|
26229
|
+
runtimeDir: join37(tmpdir4(), `flavor-code-pals-${createHash21("sha256").update(home).digest("hex").slice(0, 16)}`)
|
|
25701
26230
|
});
|
|
25702
26231
|
const baseCollaborationClient = collaboration.client ?? collaboration.createClient?.({
|
|
25703
26232
|
instanceId: collaboration.instanceId,
|
|
@@ -25808,8 +26337,8 @@ async function createProductionRuntime(options) {
|
|
|
25808
26337
|
diagnostics.push(`MCP servers could not start: ${message(error)}`);
|
|
25809
26338
|
});
|
|
25810
26339
|
const skills = new SkillRegistry({
|
|
25811
|
-
globalRoots: [
|
|
25812
|
-
projectRoots: [
|
|
26340
|
+
globalRoots: [join37(home, ".flavor-code", "skills")],
|
|
26341
|
+
projectRoots: [join37(workspace, ".flavor", "skills"), ...pluginSkillRoots],
|
|
25813
26342
|
authorizeResource: async () => true,
|
|
25814
26343
|
disabledNames: config.skills.disabled
|
|
25815
26344
|
});
|
|
@@ -25817,7 +26346,7 @@ async function createProductionRuntime(options) {
|
|
|
25817
26346
|
skillsReady.catch(() => void 0);
|
|
25818
26347
|
tools.push(createSkillTool(skills), createSkillResourceTool(skills));
|
|
25819
26348
|
if (options.extraTools !== void 0) tools.push(...options.extraTools);
|
|
25820
|
-
const flavor = await optionalText2(
|
|
26349
|
+
const flavor = await optionalText2(join37(workspace, "FLAVOR.md"));
|
|
25821
26350
|
const instructionBaseline = await workspaceInstructions.baseline();
|
|
25822
26351
|
let memoryContext;
|
|
25823
26352
|
if (memoryStore !== void 0) {
|
|
@@ -25998,7 +26527,7 @@ async function createProductionRuntime(options) {
|
|
|
25998
26527
|
now: /* @__PURE__ */ new Date(),
|
|
25999
26528
|
platform: process.platform,
|
|
26000
26529
|
osVersion: `${osVersion()} ${osRelease()}`,
|
|
26001
|
-
shell:
|
|
26530
|
+
shell: resolveRuntimeShell(process.platform, environment)?.command ?? "unknown",
|
|
26002
26531
|
isGitRepository: await gitRepository
|
|
26003
26532
|
});
|
|
26004
26533
|
const createContext = (agent, agentTools, contextModelId, parentContext) => {
|
|
@@ -26327,8 +26856,8 @@ async function createProductionRuntime(options) {
|
|
|
26327
26856
|
modelId,
|
|
26328
26857
|
agent,
|
|
26329
26858
|
providerError,
|
|
26330
|
-
errorCode,
|
|
26331
|
-
errorMessage:
|
|
26859
|
+
errorCode: errorCode3,
|
|
26860
|
+
errorMessage: errorMessage4,
|
|
26332
26861
|
attempt,
|
|
26333
26862
|
maxAttempts,
|
|
26334
26863
|
purpose,
|
|
@@ -26343,8 +26872,8 @@ async function createProductionRuntime(options) {
|
|
|
26343
26872
|
event: "ModelCallFailure",
|
|
26344
26873
|
model: typeof modelId === "string" ? modelId : void 0,
|
|
26345
26874
|
agent: typeof agent === "string" ? agent : void 0,
|
|
26346
|
-
errorCode: typeof
|
|
26347
|
-
errorMessage: typeof
|
|
26875
|
+
errorCode: typeof errorCode3 === "string" ? errorCode3 : void 0,
|
|
26876
|
+
errorMessage: typeof errorMessage4 === "string" ? errorMessage4 : void 0,
|
|
26348
26877
|
attempt: typeof attempt === "number" ? attempt : void 0,
|
|
26349
26878
|
maxAttempts: typeof maxAttempts === "number" ? maxAttempts : void 0,
|
|
26350
26879
|
purpose: typeof purpose === "string" ? purpose : void 0,
|
|
@@ -26387,6 +26916,7 @@ async function createProductionRuntime(options) {
|
|
|
26387
26916
|
createGrepTool(input.workspace),
|
|
26388
26917
|
createShellTool(input.workspace, {
|
|
26389
26918
|
jobs,
|
|
26919
|
+
environment,
|
|
26390
26920
|
...loopExecutionEnvironment === void 0 ? {} : { executionEnvironment: loopExecutionEnvironment }
|
|
26391
26921
|
}),
|
|
26392
26922
|
createWebFetchTool(),
|
|
@@ -26399,13 +26929,13 @@ async function createProductionRuntime(options) {
|
|
|
26399
26929
|
evolveService.toolDefinition(),
|
|
26400
26930
|
...mcpTools
|
|
26401
26931
|
];
|
|
26402
|
-
const loopFlavor = await optionalText2(
|
|
26932
|
+
const loopFlavor = await optionalText2(join37(input.workspace, "FLAVOR.md"));
|
|
26403
26933
|
const loopInstructionBaseline = await loopInstructions.baseline();
|
|
26404
26934
|
const loopEnvironment = createPromptEnvironment({
|
|
26405
26935
|
now: /* @__PURE__ */ new Date(),
|
|
26406
26936
|
platform: process.platform,
|
|
26407
26937
|
osVersion: `${osVersion()} ${osRelease()}`,
|
|
26408
|
-
shell:
|
|
26938
|
+
shell: resolveRuntimeShell(process.platform, environment)?.command ?? "unknown",
|
|
26409
26939
|
isGitRepository: await detectGitRepository(input.workspace)
|
|
26410
26940
|
});
|
|
26411
26941
|
let compactionInputTokens = 0;
|
|
@@ -26758,10 +27288,10 @@ async function createProductionRuntime(options) {
|
|
|
26758
27288
|
explain: (query, focus, signal, onText) => runExplain({
|
|
26759
27289
|
graph: new DesktopAstGraphService(workspace),
|
|
26760
27290
|
readFile: async (path) => {
|
|
26761
|
-
const root =
|
|
26762
|
-
const abs =
|
|
26763
|
-
if (abs !== root && !abs.startsWith(root +
|
|
26764
|
-
return
|
|
27291
|
+
const root = resolve35(workspace);
|
|
27292
|
+
const abs = resolve35(root, path);
|
|
27293
|
+
if (abs !== root && !abs.startsWith(root + sep24)) throw new Error(`path outside workspace: ${path}`);
|
|
27294
|
+
return readFile28(abs, "utf8");
|
|
26765
27295
|
},
|
|
26766
27296
|
history: async (path, limit) => fileHistory(workspace, path, limit),
|
|
26767
27297
|
questions,
|
|
@@ -26773,7 +27303,7 @@ async function createProductionRuntime(options) {
|
|
|
26773
27303
|
}, query, focus, signal),
|
|
26774
27304
|
audit: async (toolFilter) => {
|
|
26775
27305
|
try {
|
|
26776
|
-
const raw = await
|
|
27306
|
+
const raw = await readFile28(auditLogger.path, "utf8");
|
|
26777
27307
|
const lines = raw.trim().split("\n").filter((line) => line.length > 0);
|
|
26778
27308
|
const entries = lines.map((line) => {
|
|
26779
27309
|
try {
|
|
@@ -26810,9 +27340,10 @@ ${summary2}`;
|
|
|
26810
27340
|
return `Failed to read audit log: ${message(error)}`;
|
|
26811
27341
|
}
|
|
26812
27342
|
},
|
|
27343
|
+
doctor: async () => formatDoctorReport(await runDoctor({ workspace, home, environment })),
|
|
26813
27344
|
usage: async () => {
|
|
26814
27345
|
try {
|
|
26815
|
-
const raw = await
|
|
27346
|
+
const raw = await readFile28(usageLogPath(), "utf8");
|
|
26816
27347
|
return formatUsageSummary(summarizeUsage(parseUsageEntries(raw)));
|
|
26817
27348
|
} catch (error) {
|
|
26818
27349
|
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") {
|
|
@@ -26965,7 +27496,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
26965
27496
|
return `Provider "${providerName}" is missing authorizationUrl, tokenUrl, or clientId.`;
|
|
26966
27497
|
}
|
|
26967
27498
|
try {
|
|
26968
|
-
const tokenStore = createFileTokenStore(
|
|
27499
|
+
const tokenStore = createFileTokenStore(join37(home, ".flavor-code", "auth.json"));
|
|
26969
27500
|
const credentialId = oauthCredentialId(oauthConfig.tokenUrl, oauthConfig.clientId);
|
|
26970
27501
|
await migrateLegacyOAuthToken(tokenStore, providerName, credentialId);
|
|
26971
27502
|
const oauth = new OAuthCallbackAuthProvider({
|
|
@@ -27012,7 +27543,7 @@ ${renderMemoryDocument(entries)}`;
|
|
|
27012
27543
|
}
|
|
27013
27544
|
},
|
|
27014
27545
|
async logout() {
|
|
27015
|
-
const tokenStore = createFileTokenStore(
|
|
27546
|
+
const tokenStore = createFileTokenStore(join37(home, ".flavor-code", "auth.json"));
|
|
27016
27547
|
const tokens2 = await tokenStore.load();
|
|
27017
27548
|
const hadCredentials = Object.keys(tokens2).length > 0 || effectiveLlm !== void 0;
|
|
27018
27549
|
if (!hadCredentials) {
|
|
@@ -27304,13 +27835,13 @@ async function workspaceFingerprint(workspace) {
|
|
|
27304
27835
|
let remaining = 5 * 1024 * 1024;
|
|
27305
27836
|
for (const name of untracked.stdout.split("\0").filter(Boolean).sort()) {
|
|
27306
27837
|
if (name === ".flavor/loops" || name.startsWith(".flavor/loops/")) continue;
|
|
27307
|
-
const path =
|
|
27308
|
-
const relativePath =
|
|
27309
|
-
if (relativePath === ".." || relativePath.startsWith(`..${
|
|
27838
|
+
const path = resolve35(workspace, name);
|
|
27839
|
+
const relativePath = relative24(resolve35(workspace), path);
|
|
27840
|
+
if (relativePath === ".." || relativePath.startsWith(`..${sep24}`)) continue;
|
|
27310
27841
|
hash.update(name);
|
|
27311
27842
|
if (remaining <= 0) continue;
|
|
27312
27843
|
try {
|
|
27313
|
-
const content = await
|
|
27844
|
+
const content = await readFile28(path);
|
|
27314
27845
|
const slice = content.subarray(0, remaining);
|
|
27315
27846
|
hash.update(slice);
|
|
27316
27847
|
remaining -= slice.length;
|
|
@@ -27540,7 +28071,7 @@ async function registerConfiguredAdapters(providers, registry, environment, diag
|
|
|
27540
28071
|
const configured = { ...providers };
|
|
27541
28072
|
if (configured.openai === void 0 && environment.OPENAI_API_KEY) configured.openai = { type: "openai", apiKey: environment.OPENAI_API_KEY };
|
|
27542
28073
|
if (configured.anthropic === void 0 && environment.ANTHROPIC_API_KEY) configured.anthropic = { type: "anthropic", apiKey: environment.ANTHROPIC_API_KEY };
|
|
27543
|
-
const oauthTokenStore = createFileTokenStore(
|
|
28074
|
+
const oauthTokenStore = createFileTokenStore(join37(home, ".flavor-code", "auth.json"));
|
|
27544
28075
|
const registered = [];
|
|
27545
28076
|
let effectiveLlm;
|
|
27546
28077
|
for (const [name, provider] of Object.entries(configured)) {
|
|
@@ -27809,7 +28340,7 @@ async function generateSleepReview(registry, modelId, prompt, signal) {
|
|
|
27809
28340
|
}
|
|
27810
28341
|
async function optionalText2(path) {
|
|
27811
28342
|
try {
|
|
27812
|
-
return await
|
|
28343
|
+
return await readFile28(path, "utf8");
|
|
27813
28344
|
} catch (error) {
|
|
27814
28345
|
if (typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT") return void 0;
|
|
27815
28346
|
throw error;
|
|
@@ -27971,13 +28502,13 @@ function createMemoryTaskId() {
|
|
|
27971
28502
|
|
|
27972
28503
|
// src/desktop/runtime-controller.ts
|
|
27973
28504
|
import { createHash as createHash27 } from "crypto";
|
|
27974
|
-
import { mkdir as mkdir27, readFile as
|
|
27975
|
-
import { homedir as
|
|
27976
|
-
import { basename as
|
|
28505
|
+
import { mkdir as mkdir27, readFile as readFile36, readdir as readdir13, rm as rm15, stat as stat16, writeFile as writeFile17 } from "fs/promises";
|
|
28506
|
+
import { homedir as homedir5 } from "os";
|
|
28507
|
+
import { basename as basename10, join as join47, resolve as resolve38 } from "path";
|
|
27977
28508
|
|
|
27978
28509
|
// src/d2c/workflow.ts
|
|
27979
28510
|
import { createHash as createHash22 } from "crypto";
|
|
27980
|
-
import { join as
|
|
28511
|
+
import { join as join38 } from "path";
|
|
27981
28512
|
import { z as z42 } from "zod";
|
|
27982
28513
|
|
|
27983
28514
|
// src/d2c/workflow-shared.ts
|
|
@@ -28123,7 +28654,7 @@ function reviewsFor(report, previous) {
|
|
|
28123
28654
|
function implementationDirectory(report) {
|
|
28124
28655
|
const source = report.implementation.source;
|
|
28125
28656
|
if (!/^https?:\/\//i.test(source)) return source;
|
|
28126
|
-
return
|
|
28657
|
+
return join38("src", "d2c-output", report.task);
|
|
28127
28658
|
}
|
|
28128
28659
|
function createWorkflow(report, framework) {
|
|
28129
28660
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -28247,7 +28778,7 @@ function applyQualityIssueDecision(workflow, issueId2, decision) {
|
|
|
28247
28778
|
};
|
|
28248
28779
|
}
|
|
28249
28780
|
function workflowPath(workspace, task) {
|
|
28250
|
-
return
|
|
28781
|
+
return join38(taskDir(workspace, task), "workflow.json");
|
|
28251
28782
|
}
|
|
28252
28783
|
async function readWorkflow(workspace, task) {
|
|
28253
28784
|
return (await readRecoverableFile(workflowPath(workspace, task), decodeWorkflow))?.value;
|
|
@@ -28500,8 +29031,8 @@ function confirmApiMapping(mapping, operationKey, operations) {
|
|
|
28500
29031
|
}
|
|
28501
29032
|
|
|
28502
29033
|
// src/d2c/modules.ts
|
|
28503
|
-
import { readFile as
|
|
28504
|
-
import { isAbsolute as
|
|
29034
|
+
import { readFile as readFile29 } from "fs/promises";
|
|
29035
|
+
import { isAbsolute as isAbsolute17, join as join39, normalize as normalize2, posix, sep as sep25, win32 as win322 } from "path";
|
|
28505
29036
|
import { z as z43 } from "zod";
|
|
28506
29037
|
var ModuleSchema = z43.object({
|
|
28507
29038
|
id: z43.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/),
|
|
@@ -28513,20 +29044,20 @@ var ModuleSchema = z43.object({
|
|
|
28513
29044
|
}).strict();
|
|
28514
29045
|
var ManifestSchema3 = z43.object({ schema: z43.literal(1), modules: z43.array(ModuleSchema).min(1).max(500) }).passthrough();
|
|
28515
29046
|
function safeSource(path) {
|
|
28516
|
-
if (
|
|
29047
|
+
if (isAbsolute17(path) || posix.isAbsolute(path) || win322.isAbsolute(path) || path.includes("\0")) return false;
|
|
28517
29048
|
const value = normalize2(path);
|
|
28518
29049
|
const portableSegments = path.replaceAll("\\", "/").split("/");
|
|
28519
|
-
return value !== ".." && !value.startsWith(`..${
|
|
29050
|
+
return value !== ".." && !value.startsWith(`..${sep25}`) && !portableSegments.includes("..");
|
|
28520
29051
|
}
|
|
28521
29052
|
function d2cOutputDirectory(workspace, task) {
|
|
28522
29053
|
if (!D2C_TASK_PATTERN.test(task)) throw new Error(`Invalid D2C task: ${task}`);
|
|
28523
|
-
return
|
|
29054
|
+
return join39(workspace, "src", "d2c-output", task);
|
|
28524
29055
|
}
|
|
28525
29056
|
async function readD2cModules(workspace, task) {
|
|
28526
29057
|
const project = d2cOutputDirectory(workspace, task);
|
|
28527
29058
|
let raw;
|
|
28528
29059
|
try {
|
|
28529
|
-
raw = await
|
|
29060
|
+
raw = await readFile29(join39(project, "d2c.modules.json"), "utf8");
|
|
28530
29061
|
} catch {
|
|
28531
29062
|
return [{ id: "page", label: task, sourceFiles: ["src/App.vue", "src/App.jsx"], keywords: [task] }];
|
|
28532
29063
|
}
|
|
@@ -28546,8 +29077,8 @@ async function readD2cModules(workspace, task) {
|
|
|
28546
29077
|
}
|
|
28547
29078
|
|
|
28548
29079
|
// src/d2c/integration.ts
|
|
28549
|
-
import { mkdir as mkdir24, readFile as
|
|
28550
|
-
import { join as
|
|
29080
|
+
import { mkdir as mkdir24, readFile as readFile30, writeFile as writeFile13 } from "fs/promises";
|
|
29081
|
+
import { join as join40 } from "path";
|
|
28551
29082
|
function record2(value) {
|
|
28552
29083
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
28553
29084
|
}
|
|
@@ -28683,19 +29214,19 @@ async def reset_acceptance(request: Request):
|
|
|
28683
29214
|
}
|
|
28684
29215
|
async function generateIntegrationArtifacts(projectDir, document, mappings, options = {}) {
|
|
28685
29216
|
if (mappings.some((mapping) => mapping.status === "needs-confirmation")) throw new Error("Confirm every uncertain OpenAPI mapping before generating integration code");
|
|
28686
|
-
const packagePath =
|
|
29217
|
+
const packagePath = join40(projectDir, "package.json");
|
|
28687
29218
|
let pkg;
|
|
28688
29219
|
try {
|
|
28689
|
-
pkg = JSON.parse(await
|
|
29220
|
+
pkg = JSON.parse(await readFile30(packagePath, "utf8"));
|
|
28690
29221
|
} catch {
|
|
28691
29222
|
throw new Error(`D2C integration requires a valid package.json at ${packagePath}`);
|
|
28692
29223
|
}
|
|
28693
29224
|
const scripts = options.pythonServer ? { ...record2(pkg.scripts) ?? {} } : { ...record2(pkg.scripts) ?? {}, mock: "node mock/server.mjs" };
|
|
28694
29225
|
const dependencies2 = options.pythonServer ? { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9" } : { ...record2(pkg.dependencies) ?? {}, axios: "^1.7.9", express: "^5.2.1" };
|
|
28695
29226
|
const selected = [...new Set(mappings.map((mapping) => mapping.operationKey))].map((key) => document.operations.find((operation) => operation.id === key)).filter((operation) => operation !== void 0);
|
|
28696
|
-
const apiDir =
|
|
28697
|
-
const mockDir =
|
|
28698
|
-
const serverDir =
|
|
29227
|
+
const apiDir = join40(projectDir, "src", "api");
|
|
29228
|
+
const mockDir = join40(projectDir, "mock");
|
|
29229
|
+
const serverDir = join40(projectDir, "server");
|
|
28699
29230
|
await Promise.all([
|
|
28700
29231
|
mkdir24(apiDir, { recursive: true }),
|
|
28701
29232
|
...options.pythonServer ? [] : [mkdir24(mockDir, { recursive: true })],
|
|
@@ -28705,23 +29236,23 @@ async function generateIntegrationArtifacts(projectDir, document, mappings, opti
|
|
|
28705
29236
|
if (options.pythonServer) files.push("server/main.py", "server/requirements.txt", "server/README.md", "server/flavor-runtime.json");
|
|
28706
29237
|
else files.push("mock/server.mjs");
|
|
28707
29238
|
await Promise.all([
|
|
28708
|
-
writeFile13(
|
|
29239
|
+
writeFile13(join40(apiDir, "http.js"), `import axios from "axios";
|
|
28709
29240
|
|
|
28710
29241
|
export const http = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || ${JSON.stringify(document.baseUrl ?? "")}, timeout: 15000 });
|
|
28711
29242
|
|
|
28712
29243
|
http.interceptors.response.use((response) => response, (error) => Promise.reject({ name: "ApiError", status: error.response?.status, message: error.response?.data?.message || error.message, cause: error }));
|
|
28713
29244
|
`),
|
|
28714
|
-
writeFile13(
|
|
28715
|
-
writeFile13(
|
|
29245
|
+
writeFile13(join40(apiDir, "d2c-api.js"), apiModule(selected)),
|
|
29246
|
+
writeFile13(join40(apiDir, "d2c-bindings.json"), `${JSON.stringify({ schema: 1, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), mappings }, null, 2)}
|
|
28716
29247
|
`),
|
|
28717
|
-
...options.pythonServer ? [] : [writeFile13(
|
|
29248
|
+
...options.pythonServer ? [] : [writeFile13(join40(mockDir, "server.mjs"), mockModule({ ...document, operations: selected }))],
|
|
28718
29249
|
writeFile13(packagePath, `${JSON.stringify({ ...pkg, scripts, dependencies: dependencies2 }, null, 2)}
|
|
28719
29250
|
`),
|
|
28720
29251
|
...options.pythonServer ? [
|
|
28721
|
-
writeFile13(
|
|
28722
|
-
writeFile13(
|
|
28723
|
-
writeFile13(
|
|
28724
|
-
writeFile13(
|
|
29252
|
+
writeFile13(join40(serverDir, "main.py"), pythonServerModule({ ...document, operations: selected })),
|
|
29253
|
+
writeFile13(join40(serverDir, "requirements.txt"), "fastapi>=0.116,<1\nuvicorn[standard]>=0.35,<1\nsqlalchemy>=2.0,<3\n"),
|
|
29254
|
+
writeFile13(join40(serverDir, "README.md"), "# Python API service\n\nThis is the real integration service, not a mock. Development defaults to SQLite through `DATABASE_URL=sqlite:///./data/app.db`. Set the same environment variable to a SQLAlchemy MySQL or PostgreSQL URL for deployment; keep schema changes in migrations instead of database-specific SQL. Flavor Code starts the service automatically for acceptance.\n"),
|
|
29255
|
+
writeFile13(join40(serverDir, "flavor-runtime.json"), `${JSON.stringify({
|
|
28725
29256
|
schema: 1,
|
|
28726
29257
|
kind: "python-fastapi",
|
|
28727
29258
|
cwd: "server",
|
|
@@ -28740,12 +29271,12 @@ http.interceptors.response.use((response) => response, (error) => Promise.reject
|
|
|
28740
29271
|
|
|
28741
29272
|
// src/d2c/mock-runner.ts
|
|
28742
29273
|
import { spawn as spawn8 } from "child_process";
|
|
28743
|
-
import { access as
|
|
29274
|
+
import { access as access4, readFile as readFile31 } from "fs/promises";
|
|
28744
29275
|
import { createServer as createServer5 } from "net";
|
|
28745
|
-
import { join as
|
|
29276
|
+
import { join as join41 } from "path";
|
|
28746
29277
|
async function exists3(path) {
|
|
28747
29278
|
try {
|
|
28748
|
-
await
|
|
29279
|
+
await access4(path);
|
|
28749
29280
|
return true;
|
|
28750
29281
|
} catch {
|
|
28751
29282
|
return false;
|
|
@@ -28850,7 +29381,7 @@ ${output.trim()}` : ""}`);
|
|
|
28850
29381
|
}
|
|
28851
29382
|
}
|
|
28852
29383
|
async function runPythonBackend(projectDir, options) {
|
|
28853
|
-
const serverDir =
|
|
29384
|
+
const serverDir = join41(projectDir, "server");
|
|
28854
29385
|
if (options.installDependencies !== false) await installPython(serverDir, options.signal);
|
|
28855
29386
|
const selectedPort = await port();
|
|
28856
29387
|
const url = `http://127.0.0.1:${selectedPort}`;
|
|
@@ -28896,20 +29427,20 @@ ${managed.output().slice(-4e3)}`);
|
|
|
28896
29427
|
};
|
|
28897
29428
|
}
|
|
28898
29429
|
async function runD2cMockServer(projectDir, options = {}) {
|
|
28899
|
-
const runtimePath =
|
|
29430
|
+
const runtimePath = join41(projectDir, "server", "flavor-runtime.json");
|
|
28900
29431
|
if (await exists3(runtimePath)) {
|
|
28901
29432
|
let runtime;
|
|
28902
29433
|
try {
|
|
28903
|
-
runtime = JSON.parse(await
|
|
29434
|
+
runtime = JSON.parse(await readFile31(runtimePath, "utf8"));
|
|
28904
29435
|
} catch {
|
|
28905
29436
|
throw new Error(`E2E backend runtime manifest is invalid at ${runtimePath}`);
|
|
28906
29437
|
}
|
|
28907
29438
|
if (runtime.schema !== 1 || runtime.kind !== "python-fastapi") throw new Error("Unsupported E2E backend runtime manifest");
|
|
28908
29439
|
return runPythonBackend(projectDir, options);
|
|
28909
29440
|
}
|
|
28910
|
-
const serverPath =
|
|
29441
|
+
const serverPath = join41(projectDir, "mock", "server.mjs");
|
|
28911
29442
|
if (!await exists3(serverPath)) throw new Error(`D2C mock server is not generated at ${serverPath}`);
|
|
28912
|
-
if (options.installDependencies === false && !await exists3(
|
|
29443
|
+
if (options.installDependencies === false && !await exists3(join41(projectDir, "node_modules", "express", "package.json"))) {
|
|
28913
29444
|
throw new Error("D2C mock server dependencies are not installed");
|
|
28914
29445
|
}
|
|
28915
29446
|
if (options.installDependencies !== false) await install(projectDir, options.signal);
|
|
@@ -28951,13 +29482,13 @@ ${managed.output().slice(-4e3)}`);
|
|
|
28951
29482
|
|
|
28952
29483
|
// src/d2c/product.ts
|
|
28953
29484
|
import { createHash as createHash24, randomUUID as randomUUID27 } from "crypto";
|
|
28954
|
-
import { copyFile as copyFile2, mkdir as mkdir25, readFile as
|
|
28955
|
-
import { join as
|
|
29485
|
+
import { copyFile as copyFile2, mkdir as mkdir25, readFile as readFile33, rename as rename13, stat as stat14, unlink as unlink7, writeFile as writeFile15 } from "fs/promises";
|
|
29486
|
+
import { join as join42 } from "path";
|
|
28956
29487
|
import { z as z44 } from "zod";
|
|
28957
29488
|
|
|
28958
29489
|
// src/e2e/prd-governance.ts
|
|
28959
29490
|
import { createHash as createHash23, randomUUID as randomUUID26 } from "crypto";
|
|
28960
|
-
import { readFile as
|
|
29491
|
+
import { readFile as readFile32, rename as rename12, rm as rm13, writeFile as writeFile14 } from "fs/promises";
|
|
28961
29492
|
function hashPrd(markdown) {
|
|
28962
29493
|
return createHash23("sha256").update(markdown, "utf8").digest("hex");
|
|
28963
29494
|
}
|
|
@@ -29034,14 +29565,14 @@ function approvePrd(markdown, now = /* @__PURE__ */ new Date()) {
|
|
|
29034
29565
|
return { hash: hashPrd(markdown), approvedAt: now.toISOString(), criteria };
|
|
29035
29566
|
}
|
|
29036
29567
|
async function assertApprovedPrd(path, approved) {
|
|
29037
|
-
const current = await
|
|
29568
|
+
const current = await readFile32(path, "utf8");
|
|
29038
29569
|
if (hashPrd(current) !== approved.hash) {
|
|
29039
29570
|
throw new Error("PRD_LOCK_VIOLATION: confirmed PRD content changed after approval");
|
|
29040
29571
|
}
|
|
29041
29572
|
return approved;
|
|
29042
29573
|
}
|
|
29043
29574
|
async function updatePrdSectionFile(path, input) {
|
|
29044
|
-
const current = await
|
|
29575
|
+
const current = await readFile32(path, "utf8");
|
|
29045
29576
|
if (hashPrd(current) !== input.expectedHash) {
|
|
29046
29577
|
throw new Error("PRD_EDIT_CONFLICT: the PRD changed; refresh before saving this section");
|
|
29047
29578
|
}
|
|
@@ -29107,13 +29638,13 @@ var PlanSchema2 = z44.object({
|
|
|
29107
29638
|
}).strict();
|
|
29108
29639
|
function d2cProductDirectory(workspace, task) {
|
|
29109
29640
|
if (!D2C_TASK_PATTERN.test(task)) throw new Error("Invalid D2C task name");
|
|
29110
|
-
return
|
|
29641
|
+
return join42(taskDir(workspace, task), "product");
|
|
29111
29642
|
}
|
|
29112
29643
|
function d2cProductPrototypeDirectory(workspace, task) {
|
|
29113
|
-
return
|
|
29644
|
+
return join42(d2cProductDirectory(workspace, task), "prototype");
|
|
29114
29645
|
}
|
|
29115
29646
|
function planPath(workspace, task) {
|
|
29116
|
-
return
|
|
29647
|
+
return join42(taskDir(workspace, task), "product-plan.json");
|
|
29117
29648
|
}
|
|
29118
29649
|
function resolveD2cProductTechnology(requirement, fallbackFramework = "vue") {
|
|
29119
29650
|
const frontendCandidates = [
|
|
@@ -29201,18 +29732,18 @@ function buildD2cProductOpenApi(task, modules) {
|
|
|
29201
29732
|
`;
|
|
29202
29733
|
}
|
|
29203
29734
|
async function fileTimestamp(path) {
|
|
29204
|
-
const info = await
|
|
29735
|
+
const info = await stat14(path).catch(() => void 0);
|
|
29205
29736
|
return info?.isFile() ? info.mtime.toISOString() : void 0;
|
|
29206
29737
|
}
|
|
29207
29738
|
async function fileHash(path) {
|
|
29208
|
-
const buffer = await
|
|
29739
|
+
const buffer = await readFile33(path).catch((error) => {
|
|
29209
29740
|
if (error.code === "ENOENT") return void 0;
|
|
29210
29741
|
throw error;
|
|
29211
29742
|
});
|
|
29212
29743
|
return buffer === void 0 ? void 0 : createHash24("sha256").update(buffer).digest("hex");
|
|
29213
29744
|
}
|
|
29214
29745
|
async function validateAndNormalizePrototypeManifest(path) {
|
|
29215
|
-
const raw = await
|
|
29746
|
+
const raw = await readFile33(path, "utf8").catch((error) => {
|
|
29216
29747
|
if (error.code === "ENOENT") return void 0;
|
|
29217
29748
|
throw error;
|
|
29218
29749
|
});
|
|
@@ -29288,7 +29819,7 @@ async function createD2cProductPlan(workspace, input, now = () => /* @__PURE__ *
|
|
|
29288
29819
|
});
|
|
29289
29820
|
}
|
|
29290
29821
|
async function readD2cProductPlan(workspace, task) {
|
|
29291
|
-
const raw = await
|
|
29822
|
+
const raw = await readFile33(planPath(workspace, task), "utf8").catch((error) => {
|
|
29292
29823
|
if (error.code === "ENOENT") return void 0;
|
|
29293
29824
|
throw error;
|
|
29294
29825
|
});
|
|
@@ -29296,16 +29827,16 @@ async function readD2cProductPlan(workspace, task) {
|
|
|
29296
29827
|
}
|
|
29297
29828
|
async function discoverD2cProductArtifacts(workspace, plan) {
|
|
29298
29829
|
const product = d2cProductDirectory(workspace, plan.task);
|
|
29299
|
-
const prdUpdatedAt = await fileTimestamp(
|
|
29300
|
-
const prdHash = await fileHash(
|
|
29301
|
-
const prototypeUpdatedAt = await fileTimestamp(
|
|
29302
|
-
const prototypeHash = await fileHash(
|
|
29303
|
-
const expectedManifest =
|
|
29830
|
+
const prdUpdatedAt = await fileTimestamp(join42(product, "prd.md"));
|
|
29831
|
+
const prdHash = await fileHash(join42(product, "prd.md"));
|
|
29832
|
+
const prototypeUpdatedAt = await fileTimestamp(join42(product, "prototype", "index.html"));
|
|
29833
|
+
const prototypeHash = await fileHash(join42(product, "prototype", "index.html"));
|
|
29834
|
+
const expectedManifest = join42(product, "prototype", "interaction-manifest.json");
|
|
29304
29835
|
let manifestUpdatedAt = await fileTimestamp(expectedManifest);
|
|
29305
29836
|
if (manifestUpdatedAt === void 0) {
|
|
29306
|
-
const misplacedManifest =
|
|
29837
|
+
const misplacedManifest = join42(product, "interaction-manifest.json");
|
|
29307
29838
|
if (await fileTimestamp(misplacedManifest) !== void 0) {
|
|
29308
|
-
await mkdir25(
|
|
29839
|
+
await mkdir25(join42(product, "prototype"), { recursive: true });
|
|
29309
29840
|
await copyFile2(misplacedManifest, expectedManifest);
|
|
29310
29841
|
manifestUpdatedAt = await fileTimestamp(expectedManifest);
|
|
29311
29842
|
}
|
|
@@ -29338,8 +29869,8 @@ async function readD2cProductPlanView(workspace, task) {
|
|
|
29338
29869
|
if (stored === void 0) return void 0;
|
|
29339
29870
|
const plan = await discoverD2cProductArtifacts(workspace, stored);
|
|
29340
29871
|
if (plan.revision !== stored.revision) await writeD2cProductPlan(workspace, plan);
|
|
29341
|
-
const prdMarkdown = plan.prd === void 0 ? void 0 : await
|
|
29342
|
-
const manifestPath =
|
|
29872
|
+
const prdMarkdown = plan.prd === void 0 ? void 0 : await readFile33(join42(taskDir(workspace, task), plan.prd.path), "utf8");
|
|
29873
|
+
const manifestPath = join42(d2cProductDirectory(workspace, task), "prototype", "interaction-manifest.json");
|
|
29343
29874
|
const validationError = plan.phase === "design-generating" && await fileTimestamp(manifestPath) !== void 0 ? await validateAndNormalizePrototypeManifest(manifestPath) : void 0;
|
|
29344
29875
|
return {
|
|
29345
29876
|
plan,
|
|
@@ -29429,9 +29960,9 @@ ${prdMarkdown.slice(0, 2e4)}`
|
|
|
29429
29960
|
|
|
29430
29961
|
// src/e2e/acceptance-baseline.ts
|
|
29431
29962
|
import { createHash as createHash25 } from "crypto";
|
|
29432
|
-
import { readFile as
|
|
29963
|
+
import { readFile as readFile34 } from "fs/promises";
|
|
29433
29964
|
async function fileArtifact(path) {
|
|
29434
|
-
const content = await
|
|
29965
|
+
const content = await readFile34(path);
|
|
29435
29966
|
return { path, hash: createHash25("sha256").update(content).digest("hex"), bytes: content.byteLength };
|
|
29436
29967
|
}
|
|
29437
29968
|
async function verifyAcceptanceArtifact(path, expected, label) {
|
|
@@ -29498,7 +30029,7 @@ function createAcceptanceEvidence(coverage, run, now = /* @__PURE__ */ new Date(
|
|
|
29498
30029
|
|
|
29499
30030
|
// src/e2e/delivery-run.ts
|
|
29500
30031
|
import { createHash as createHash26 } from "crypto";
|
|
29501
|
-
import { join as
|
|
30032
|
+
import { join as join43 } from "path";
|
|
29502
30033
|
import { z as z45 } from "zod";
|
|
29503
30034
|
var DELIVERY_NODE_IDS = ["requirement", "prd", "design", "d2c", "api", "acceptance", "delivery"];
|
|
29504
30035
|
var ArtifactSchema = z45.object({
|
|
@@ -29615,7 +30146,7 @@ function completeDeliveryNode(run, node, outputs, now = /* @__PURE__ */ new Date
|
|
|
29615
30146
|
return { ...run, nodes, updatedAt: timestamp2 };
|
|
29616
30147
|
}
|
|
29617
30148
|
function deliveryRunPath(workspace, task) {
|
|
29618
|
-
return
|
|
30149
|
+
return join43(taskDir(workspace, task), "delivery-run.json");
|
|
29619
30150
|
}
|
|
29620
30151
|
function decodeDeliveryRun(raw) {
|
|
29621
30152
|
return DeliveryRunSchema.parse(JSON.parse(raw));
|
|
@@ -29656,16 +30187,16 @@ async function updateDeliveryRun(workspace, task, expectedRevision, mutation) {
|
|
|
29656
30187
|
}
|
|
29657
30188
|
|
|
29658
30189
|
// src/skills/manager.ts
|
|
29659
|
-
import { lstat as
|
|
29660
|
-
import { homedir as
|
|
29661
|
-
import { isAbsolute as
|
|
30190
|
+
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
|
+
import { homedir as homedir4 } from "os";
|
|
30192
|
+
import { isAbsolute as isAbsolute18, join as join44, relative as relative25, resolve as resolve36, sep as sep26 } from "path";
|
|
29662
30193
|
import { stringify } from "yaml";
|
|
29663
30194
|
var SkillManager = class {
|
|
29664
30195
|
#workspace;
|
|
29665
30196
|
#home;
|
|
29666
30197
|
constructor(options) {
|
|
29667
|
-
this.#workspace =
|
|
29668
|
-
this.#home =
|
|
30198
|
+
this.#workspace = resolve36(options.workspace);
|
|
30199
|
+
this.#home = resolve36(options.home ?? homedir4());
|
|
29669
30200
|
}
|
|
29670
30201
|
async list() {
|
|
29671
30202
|
const { registry, disabled } = await this.#registry();
|
|
@@ -29681,7 +30212,7 @@ var SkillManager = class {
|
|
|
29681
30212
|
async create(draft) {
|
|
29682
30213
|
const value = parseDraft(draft);
|
|
29683
30214
|
const projectRoot = this.#projectRoot();
|
|
29684
|
-
const directory =
|
|
30215
|
+
const directory = join44(projectRoot, value.name);
|
|
29685
30216
|
await mkdir26(projectRoot, { recursive: true, mode: 448 });
|
|
29686
30217
|
try {
|
|
29687
30218
|
await mkdir26(directory, { mode: 448 });
|
|
@@ -29690,7 +30221,7 @@ var SkillManager = class {
|
|
|
29690
30221
|
throw error;
|
|
29691
30222
|
}
|
|
29692
30223
|
try {
|
|
29693
|
-
await writeFile16(
|
|
30224
|
+
await writeFile16(join44(directory, "SKILL.md"), renderSkill(value), { encoding: "utf8", flag: "wx", mode: 384 });
|
|
29694
30225
|
await this.#persistEnabled(value.name, true);
|
|
29695
30226
|
return this.get(value.name);
|
|
29696
30227
|
} catch (error) {
|
|
@@ -29703,13 +30234,13 @@ var SkillManager = class {
|
|
|
29703
30234
|
const value = parseDraft(draft);
|
|
29704
30235
|
if (value.name !== validName) throw new Error("Rename is not supported; create a new skill instead");
|
|
29705
30236
|
const directory = this.#projectSkillDirectory(validName);
|
|
29706
|
-
const info = await
|
|
30237
|
+
const info = await lstat14(directory).catch((error) => {
|
|
29707
30238
|
if (isCode11(error, "ENOENT")) throw new Error(`Project skill not found: ${validName}`);
|
|
29708
30239
|
throw error;
|
|
29709
30240
|
});
|
|
29710
30241
|
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error(`Project skill path is not a real directory: ${validName}`);
|
|
29711
|
-
const skillFile =
|
|
29712
|
-
const fileInfo = await
|
|
30242
|
+
const skillFile = join44(directory, "SKILL.md");
|
|
30243
|
+
const fileInfo = await lstat14(skillFile);
|
|
29713
30244
|
if (!fileInfo.isFile() || fileInfo.isSymbolicLink()) throw new Error(`Project skill file is not a real file: ${validName}`);
|
|
29714
30245
|
await writeFile16(skillFile, renderSkill(value), { encoding: "utf8", mode: 384 });
|
|
29715
30246
|
return this.get(validName);
|
|
@@ -29717,7 +30248,7 @@ var SkillManager = class {
|
|
|
29717
30248
|
async delete(name) {
|
|
29718
30249
|
const validName = parseName(name);
|
|
29719
30250
|
const directory = this.#projectSkillDirectory(validName);
|
|
29720
|
-
const info = await
|
|
30251
|
+
const info = await lstat14(directory).catch((error) => {
|
|
29721
30252
|
if (isCode11(error, "ENOENT")) throw new Error(`Project skill not found: ${validName}`);
|
|
29722
30253
|
throw error;
|
|
29723
30254
|
});
|
|
@@ -29740,12 +30271,12 @@ var SkillManager = class {
|
|
|
29740
30271
|
async #registry() {
|
|
29741
30272
|
const loaded = await loadConfig({ cwd: this.#workspace, home: this.#home });
|
|
29742
30273
|
const [globalPluginRoots, projectPluginRoots] = await Promise.all([
|
|
29743
|
-
discoverPluginSkillRoots(
|
|
29744
|
-
discoverPluginSkillRoots(
|
|
30274
|
+
discoverPluginSkillRoots(join44(this.#home, ".flavor-code", "plugins")),
|
|
30275
|
+
discoverPluginSkillRoots(join44(this.#workspace, ".flavor", "plugins"))
|
|
29745
30276
|
]);
|
|
29746
30277
|
return {
|
|
29747
30278
|
registry: new SkillRegistry({
|
|
29748
|
-
globalRoots: [
|
|
30279
|
+
globalRoots: [join44(this.#home, ".flavor-code", "skills"), ...globalPluginRoots],
|
|
29749
30280
|
projectRoots: [this.#projectRoot(), ...projectPluginRoots]
|
|
29750
30281
|
}),
|
|
29751
30282
|
disabled: new Set(loaded.config.skills.disabled)
|
|
@@ -29761,10 +30292,10 @@ var SkillManager = class {
|
|
|
29761
30292
|
};
|
|
29762
30293
|
}
|
|
29763
30294
|
#projectRoot() {
|
|
29764
|
-
return
|
|
30295
|
+
return join44(this.#workspace, ".flavor", "skills");
|
|
29765
30296
|
}
|
|
29766
30297
|
#projectSkillDirectory(name) {
|
|
29767
|
-
return
|
|
30298
|
+
return join44(this.#projectRoot(), name);
|
|
29768
30299
|
}
|
|
29769
30300
|
};
|
|
29770
30301
|
function parseName(value) {
|
|
@@ -29797,7 +30328,7 @@ function isCode11(error, code) {
|
|
|
29797
30328
|
async function discoverPluginSkillRoots(directory) {
|
|
29798
30329
|
let entries;
|
|
29799
30330
|
try {
|
|
29800
|
-
entries = await
|
|
30331
|
+
entries = await readdir12(directory, { withFileTypes: true });
|
|
29801
30332
|
} catch (error) {
|
|
29802
30333
|
if (isCode11(error, "ENOENT")) return [];
|
|
29803
30334
|
throw error;
|
|
@@ -29805,16 +30336,16 @@ async function discoverPluginSkillRoots(directory) {
|
|
|
29805
30336
|
const roots = [];
|
|
29806
30337
|
for (const entry2 of entries) {
|
|
29807
30338
|
if (!entry2.isDirectory() || entry2.isSymbolicLink()) continue;
|
|
29808
|
-
const pluginRoot = await realpath13(
|
|
30339
|
+
const pluginRoot = await realpath13(join44(directory, entry2.name));
|
|
29809
30340
|
try {
|
|
29810
|
-
const manifest = PluginManifestSchema.parse(JSON.parse(await
|
|
30341
|
+
const manifest = PluginManifestSchema.parse(JSON.parse(await readFile35(join44(pluginRoot, "flavor-plugin.json"), "utf8")));
|
|
29811
30342
|
for (const contribution of manifest.contributes.skillRoots) {
|
|
29812
|
-
const candidate =
|
|
29813
|
-
const info = await
|
|
30343
|
+
const candidate = resolve36(pluginRoot, contribution.path);
|
|
30344
|
+
const info = await lstat14(candidate);
|
|
29814
30345
|
if (!info.isDirectory() || info.isSymbolicLink()) continue;
|
|
29815
30346
|
const physical = await realpath13(candidate);
|
|
29816
|
-
const relation =
|
|
29817
|
-
if (relation === ".." || relation.startsWith(`..${
|
|
30347
|
+
const relation = relative25(pluginRoot, physical);
|
|
30348
|
+
if (relation === ".." || relation.startsWith(`..${sep26}`) || isAbsolute18(relation)) continue;
|
|
29818
30349
|
roots.push(physical);
|
|
29819
30350
|
}
|
|
29820
30351
|
} catch {
|
|
@@ -29882,11 +30413,11 @@ async function createProjectMemoryManager(options) {
|
|
|
29882
30413
|
}
|
|
29883
30414
|
|
|
29884
30415
|
// src/mcp/config-manager.ts
|
|
29885
|
-
import { join as
|
|
30416
|
+
import { join as join45 } from "path";
|
|
29886
30417
|
var ProjectMcpConfigManager = class {
|
|
29887
30418
|
path;
|
|
29888
30419
|
constructor(workspace) {
|
|
29889
|
-
this.path =
|
|
30420
|
+
this.path = join45(workspace, ".flavor", "flavor.json");
|
|
29890
30421
|
}
|
|
29891
30422
|
async list() {
|
|
29892
30423
|
const document = await readRecoverableFile(this.path, (raw) => parseDocument2(this.path, raw));
|
|
@@ -29983,7 +30514,7 @@ function isRecord7(value) {
|
|
|
29983
30514
|
}
|
|
29984
30515
|
|
|
29985
30516
|
// src/desktop/model-config.ts
|
|
29986
|
-
import { join as
|
|
30517
|
+
import { join as join46 } from "path";
|
|
29987
30518
|
var DEFAULT_DESKTOP_MODELS = [
|
|
29988
30519
|
{
|
|
29989
30520
|
id: "anthropic:deepseek-v4-pro",
|
|
@@ -30004,7 +30535,7 @@ var DEFAULT_DESKTOP_MODELS = [
|
|
|
30004
30535
|
];
|
|
30005
30536
|
async function loadDesktopModels(workspace, home) {
|
|
30006
30537
|
try {
|
|
30007
|
-
const tokens2 = await createFileTokenStore(
|
|
30538
|
+
const tokens2 = await createFileTokenStore(join46(home, ".flavor-code", "auth.json")).load();
|
|
30008
30539
|
const pkceModels = [];
|
|
30009
30540
|
const knownPkce = /* @__PURE__ */ new Set();
|
|
30010
30541
|
for (const token of Object.values(tokens2)) {
|
|
@@ -30072,9 +30603,9 @@ function protocolLabel(type) {
|
|
|
30072
30603
|
|
|
30073
30604
|
// src/desktop/d2c-product-preview.ts
|
|
30074
30605
|
import { createReadStream as createReadStream2 } from "fs";
|
|
30075
|
-
import { realpath as realpath14, stat as
|
|
30606
|
+
import { realpath as realpath14, stat as stat15 } from "fs/promises";
|
|
30076
30607
|
import { createServer as createServer6 } from "http";
|
|
30077
|
-
import { extname as extname4, isAbsolute as
|
|
30608
|
+
import { extname as extname4, isAbsolute as isAbsolute19, relative as relative26, resolve as resolve37, sep as sep27 } from "path";
|
|
30078
30609
|
var MIME_TYPES = {
|
|
30079
30610
|
".css": "text/css; charset=utf-8",
|
|
30080
30611
|
".gif": "image/gif",
|
|
@@ -30092,12 +30623,12 @@ function sendNotFound(response2) {
|
|
|
30092
30623
|
response2.end("Not found");
|
|
30093
30624
|
}
|
|
30094
30625
|
function isInside(root, candidate) {
|
|
30095
|
-
const delta =
|
|
30096
|
-
return delta === "" || !delta.startsWith(`..${
|
|
30626
|
+
const delta = relative26(root, candidate);
|
|
30627
|
+
return delta === "" || !delta.startsWith(`..${sep27}`) && delta !== ".." && !isAbsolute19(delta);
|
|
30097
30628
|
}
|
|
30098
30629
|
async function startD2cProductPreview(rootDirectory) {
|
|
30099
|
-
const root = await realpath14(
|
|
30100
|
-
const rootInfo = await
|
|
30630
|
+
const root = await realpath14(resolve37(rootDirectory));
|
|
30631
|
+
const rootInfo = await stat15(root);
|
|
30101
30632
|
if (!rootInfo.isDirectory()) throw new Error("D2C product prototype directory is unavailable");
|
|
30102
30633
|
const server = createServer6(async (request, response2) => {
|
|
30103
30634
|
response2.setHeader("cache-control", "no-store");
|
|
@@ -30108,11 +30639,11 @@ async function startD2cProductPreview(rootDirectory) {
|
|
|
30108
30639
|
let pathname = decodeURIComponent(url.pathname);
|
|
30109
30640
|
if (pathname === "/") pathname = "/index.html";
|
|
30110
30641
|
if (pathname.includes("\0")) return sendNotFound(response2);
|
|
30111
|
-
const unresolved =
|
|
30642
|
+
const unresolved = resolve37(root, `.${pathname}`);
|
|
30112
30643
|
if (!isInside(root, unresolved)) return sendNotFound(response2);
|
|
30113
30644
|
const target = await realpath14(unresolved).catch(() => void 0);
|
|
30114
30645
|
if (target === void 0 || !isInside(root, target)) return sendNotFound(response2);
|
|
30115
|
-
const info = await
|
|
30646
|
+
const info = await stat15(target);
|
|
30116
30647
|
if (!info.isFile()) return sendNotFound(response2);
|
|
30117
30648
|
response2.writeHead(200, {
|
|
30118
30649
|
"content-length": info.size,
|
|
@@ -30149,33 +30680,33 @@ function pngDataUrl(png) {
|
|
|
30149
30680
|
}
|
|
30150
30681
|
async function detectD2cFramework(workspace, task) {
|
|
30151
30682
|
try {
|
|
30152
|
-
const pkg = JSON.parse(await
|
|
30683
|
+
const pkg = JSON.parse(await readFile36(join47(d2cOutputDirectory(workspace, task), "package.json"), "utf8"));
|
|
30153
30684
|
return pkg.dependencies?.react === void 0 ? "vue" : "react";
|
|
30154
30685
|
} catch {
|
|
30155
30686
|
return "vue";
|
|
30156
30687
|
}
|
|
30157
30688
|
}
|
|
30158
30689
|
function integrationDirectory(workspace, task) {
|
|
30159
|
-
return
|
|
30690
|
+
return join47(workspace, ".flavor", "d2c", task, "integration");
|
|
30160
30691
|
}
|
|
30161
30692
|
var D2C_BACKEND_SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".cfg", ".env", ".ini", ".js", ".json", ".lock", ".mjs", ".py", ".toml", ".txt", ".yaml", ".yml"]);
|
|
30162
30693
|
var D2C_BACKEND_IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([".git", ".venv", "__pycache__", "data", "node_modules", "venv"]);
|
|
30163
30694
|
async function d2cBackendSourceFingerprint(project) {
|
|
30164
|
-
const runtimeManifest =
|
|
30165
|
-
const realBackend = (await
|
|
30166
|
-
const root = realBackend ?
|
|
30167
|
-
if ((await
|
|
30695
|
+
const runtimeManifest = join47(project, "server", "flavor-runtime.json");
|
|
30696
|
+
const realBackend = (await stat16(runtimeManifest).catch(() => void 0))?.isFile() === true;
|
|
30697
|
+
const root = realBackend ? join47(project, "server") : join47(project, "mock");
|
|
30698
|
+
if ((await stat16(root).catch(() => void 0))?.isDirectory() !== true) return void 0;
|
|
30168
30699
|
const files = [];
|
|
30169
30700
|
const visit = async (directory) => {
|
|
30170
|
-
const entries = await
|
|
30701
|
+
const entries = await readdir13(directory, { withFileTypes: true });
|
|
30171
30702
|
for (const entry2 of entries) {
|
|
30172
30703
|
if (entry2.isDirectory()) {
|
|
30173
|
-
if (!D2C_BACKEND_IGNORED_DIRECTORIES.has(entry2.name)) await visit(
|
|
30704
|
+
if (!D2C_BACKEND_IGNORED_DIRECTORIES.has(entry2.name)) await visit(join47(directory, entry2.name));
|
|
30174
30705
|
continue;
|
|
30175
30706
|
}
|
|
30176
30707
|
if (!entry2.isFile()) continue;
|
|
30177
30708
|
const extension2 = entry2.name.includes(".") ? `.${entry2.name.split(".").pop().toLowerCase()}` : "";
|
|
30178
|
-
if (D2C_BACKEND_SOURCE_EXTENSIONS.has(extension2)) files.push(
|
|
30709
|
+
if (D2C_BACKEND_SOURCE_EXTENSIONS.has(extension2)) files.push(join47(directory, entry2.name));
|
|
30179
30710
|
}
|
|
30180
30711
|
};
|
|
30181
30712
|
await visit(root);
|
|
@@ -30183,7 +30714,7 @@ async function d2cBackendSourceFingerprint(project) {
|
|
|
30183
30714
|
for (const path of files.sort()) {
|
|
30184
30715
|
hash.update(path.slice(root.length));
|
|
30185
30716
|
hash.update("\0");
|
|
30186
|
-
hash.update(await
|
|
30717
|
+
hash.update(await readFile36(path));
|
|
30187
30718
|
hash.update("\0");
|
|
30188
30719
|
}
|
|
30189
30720
|
return hash.digest("hex");
|
|
@@ -30227,7 +30758,7 @@ var DesktopRuntimeController = class {
|
|
|
30227
30758
|
/** Backend source digest observed when the managed process was started. */
|
|
30228
30759
|
#d2cMockSourceFingerprints = /* @__PURE__ */ new Map();
|
|
30229
30760
|
constructor(options) {
|
|
30230
|
-
this.#home =
|
|
30761
|
+
this.#home = resolve38(options.home ?? homedir5());
|
|
30231
30762
|
this.#createRuntime = options.createRuntime ?? (async (runtimeOptions) => createProductionRuntime(runtimeOptions));
|
|
30232
30763
|
this.#createTerminalService = options.createTerminalService ?? ((workspace) => new TerminalService(workspace));
|
|
30233
30764
|
this.#listSessions = options.listSessions ?? (async (workspace) => {
|
|
@@ -30302,7 +30833,7 @@ var DesktopRuntimeController = class {
|
|
|
30302
30833
|
};
|
|
30303
30834
|
}
|
|
30304
30835
|
async openWorkspace(path) {
|
|
30305
|
-
const workspace =
|
|
30836
|
+
const workspace = resolve38(path);
|
|
30306
30837
|
if (workspace !== this.#workspace) {
|
|
30307
30838
|
await this.#disposeRuntime();
|
|
30308
30839
|
await this.#stopAllD2cPreviews();
|
|
@@ -30729,8 +31260,8 @@ var DesktopRuntimeController = class {
|
|
|
30729
31260
|
if (workflow === void 0 || !reviewProgress(workflow).complete || workflow.stage === "visual-review") {
|
|
30730
31261
|
throw new Error("Complete visual review before importing OpenAPI");
|
|
30731
31262
|
}
|
|
30732
|
-
const moduleManifest =
|
|
30733
|
-
const moduleManifestInfo = await
|
|
31263
|
+
const moduleManifest = join47(d2cOutputDirectory(workspace, task), "d2c.modules.json");
|
|
31264
|
+
const moduleManifestInfo = await stat16(moduleManifest).catch(() => void 0);
|
|
30734
31265
|
if (moduleManifestInfo === void 0 || !moduleManifestInfo.isFile()) {
|
|
30735
31266
|
throw new Error("D2C implementation must write d2c.modules.json before API mapping; ask the agent to split the project into modules first");
|
|
30736
31267
|
}
|
|
@@ -30742,8 +31273,8 @@ var DesktopRuntimeController = class {
|
|
|
30742
31273
|
await mkdir27(dir, { recursive: true });
|
|
30743
31274
|
const hash = createHash27("sha256").update(raw).digest("hex");
|
|
30744
31275
|
await Promise.all([
|
|
30745
|
-
writeFile17(
|
|
30746
|
-
writeFile17(
|
|
31276
|
+
writeFile17(join47(dir, "swagger.json"), raw),
|
|
31277
|
+
writeFile17(join47(dir, "openapi.normalized.json"), `${JSON.stringify(document, null, 2)}
|
|
30747
31278
|
`)
|
|
30748
31279
|
]);
|
|
30749
31280
|
const next = {
|
|
@@ -30767,9 +31298,9 @@ var DesktopRuntimeController = class {
|
|
|
30767
31298
|
const workspace = this.#workspace;
|
|
30768
31299
|
if (workspace === void 0) throw new Error("Open a project before importing OpenAPI");
|
|
30769
31300
|
await this.#assertProductPrdLocked(workspace, task);
|
|
30770
|
-
const info = await
|
|
31301
|
+
const info = await stat16(sourcePath);
|
|
30771
31302
|
if (!info.isFile() || info.size > 8 * 1024 * 1024) throw new Error("OpenAPI JSON must be a file no larger than 8 MiB");
|
|
30772
|
-
return this.#storeD2cOpenApi(workspace, task,
|
|
31303
|
+
return this.#storeD2cOpenApi(workspace, task, basename10(sourcePath), await readFile36(sourcePath, "utf8"), false);
|
|
30773
31304
|
}
|
|
30774
31305
|
async getD2cIntegration(task) {
|
|
30775
31306
|
const workspace = this.#workspace;
|
|
@@ -30778,8 +31309,8 @@ var DesktopRuntimeController = class {
|
|
|
30778
31309
|
let workflow = await readWorkflow(workspace, task);
|
|
30779
31310
|
if (workflow?.openapi === void 0 && workflow !== void 0 && reviewProgress(workflow).complete && await readD2cProductPlanView(workspace, task) !== void 0) {
|
|
30780
31311
|
const modules = await readD2cModules(workspace, task);
|
|
30781
|
-
const productOpenApiPath =
|
|
30782
|
-
const authored = await
|
|
31312
|
+
const productOpenApiPath = join47(taskDir(workspace, task), "product", "openapi.json");
|
|
31313
|
+
const authored = await readFile36(productOpenApiPath, "utf8").catch(() => void 0);
|
|
30783
31314
|
const generated = authored === void 0;
|
|
30784
31315
|
const prepared = await this.#storeD2cOpenApi(
|
|
30785
31316
|
workspace,
|
|
@@ -30791,7 +31322,7 @@ var DesktopRuntimeController = class {
|
|
|
30791
31322
|
return prepared;
|
|
30792
31323
|
}
|
|
30793
31324
|
if (workflow?.openapi === void 0 || workflow.mappings === void 0) return void 0;
|
|
30794
|
-
const document = JSON.parse(await
|
|
31325
|
+
const document = JSON.parse(await readFile36(join47(integrationDirectory(workspace, task), "openapi.normalized.json"), "utf8"));
|
|
30795
31326
|
return { workflow, document, mappings: workflow.mappings };
|
|
30796
31327
|
}
|
|
30797
31328
|
async confirmD2cMapping(task, moduleId, operationKey) {
|
|
@@ -30858,7 +31389,7 @@ var DesktopRuntimeController = class {
|
|
|
30858
31389
|
].join("\n");
|
|
30859
31390
|
const delivery = await readDeliveryRun(workspace, task);
|
|
30860
31391
|
if (delivery?.nodes.api.status === "running") {
|
|
30861
|
-
const apiArtifacts = await Promise.all(generated.files.map(async (file) => artifactRef(file, await
|
|
31392
|
+
const apiArtifacts = await Promise.all(generated.files.map(async (file) => artifactRef(file, await readFile36(join47(project, file)))));
|
|
30862
31393
|
const completed = await updateDeliveryRun(
|
|
30863
31394
|
workspace,
|
|
30864
31395
|
task,
|
|
@@ -30885,8 +31416,8 @@ var DesktopRuntimeController = class {
|
|
|
30885
31416
|
try {
|
|
30886
31417
|
const fingerprint2 = await d2cBackendSourceFingerprint(project);
|
|
30887
31418
|
if (fingerprint2 !== void 0) this.#d2cMockSourceFingerprints.set(task, fingerprint2);
|
|
30888
|
-
const environmentPath =
|
|
30889
|
-
const existingEnvironment = await
|
|
31419
|
+
const environmentPath = join47(project, ".env.local");
|
|
31420
|
+
const existingEnvironment = await readFile36(environmentPath, "utf8").catch(() => "");
|
|
30890
31421
|
const environment = `${existingEnvironment.split(/\r?\n/).filter((line) => !/^\s*VITE_API_BASE_URL\s*=/.test(line)).join("\n").trim()}
|
|
30891
31422
|
VITE_API_BASE_URL=${running.url}
|
|
30892
31423
|
`.replace(/^\n/, "");
|
|
@@ -30966,8 +31497,8 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30966
31497
|
const readyPreview = this.#d2cPreviews.get(task);
|
|
30967
31498
|
const readyMock = this.#d2cMocks.get(task);
|
|
30968
31499
|
if (readyPreview === void 0 || readyMock === void 0) throw new Error("D2C preview or backend service is unavailable after synchronization");
|
|
30969
|
-
const manifestPath =
|
|
30970
|
-
const manifest = parseInteractionManifest(await
|
|
31500
|
+
const manifestPath = join47(taskDir(workspace, task), "design", "interaction-manifest.json");
|
|
31501
|
+
const manifest = parseInteractionManifest(await readFile36(manifestPath, "utf8"));
|
|
30971
31502
|
const baseline = await this.#verifyAcceptanceBaseline(workspace, task);
|
|
30972
31503
|
const coverage = baseline === void 0 ? void 0 : verifyRequirementCoverage(baseline.approvedPrd.criteria, manifest);
|
|
30973
31504
|
let executableManifest = manifest;
|
|
@@ -30978,7 +31509,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30978
31509
|
await mkdir27(planDir, { recursive: true });
|
|
30979
31510
|
try {
|
|
30980
31511
|
const observations = await this.#observeD2cPages(manifest, readyPreview.url);
|
|
30981
|
-
const prd = await
|
|
31512
|
+
const prd = await readFile36(join47(taskDir(workspace, task), "product", "prd.md"), "utf8").catch(() => void 0);
|
|
30982
31513
|
const apiContext = JSON.stringify({ openapi: workflow.openapi, mappings: workflow.mappings }, null, 2);
|
|
30983
31514
|
const plan = await this.#d2cJudge.planInteractions({ task, seed: manifest, observations, ...prd === void 0 ? {} : { prd }, apiContext });
|
|
30984
31515
|
executableManifest = mergeInteractionManifests(manifest, plan.manifest);
|
|
@@ -30989,9 +31520,9 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30989
31520
|
plannedScenarios: executableManifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
|
|
30990
31521
|
pageAnalyses: plan.pageAnalyses
|
|
30991
31522
|
};
|
|
30992
|
-
await writeFile17(
|
|
31523
|
+
await writeFile17(join47(planDir, "autonomous-interaction-plan.json"), `${JSON.stringify({ ...plan, manifest: executableManifest }, null, 2)}
|
|
30993
31524
|
`, { encoding: "utf8", mode: 384 });
|
|
30994
|
-
await rm15(
|
|
31525
|
+
await rm15(join47(planDir, "autonomous-interaction-diagnostic.json"), { force: true });
|
|
30995
31526
|
} catch (cause) {
|
|
30996
31527
|
const detail = message(cause);
|
|
30997
31528
|
review = {
|
|
@@ -30999,8 +31530,8 @@ VITE_API_BASE_URL=${running.url}
|
|
|
30999
31530
|
plannedScenarios: manifest.pages.reduce((total, page) => total + page.scenarios.length, 0),
|
|
31000
31531
|
warning: `\u591A\u6A21\u6001\u81EA\u4E3B\u89C4\u5212\u5931\u8D25\uFF0C\u5DF2\u7EE7\u7EED\u6267\u884C\u5DF2\u786E\u8BA4\u7684\u8BBE\u8BA1\u5951\u7EA6\uFF1A${detail}`
|
|
31001
31532
|
};
|
|
31002
|
-
await rm15(
|
|
31003
|
-
await writeFile17(
|
|
31533
|
+
await rm15(join47(planDir, "autonomous-interaction-plan.json"), { force: true });
|
|
31534
|
+
await writeFile17(join47(planDir, "autonomous-interaction-diagnostic.json"), `${JSON.stringify({
|
|
31004
31535
|
schema: 1,
|
|
31005
31536
|
stage: "planning",
|
|
31006
31537
|
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -31014,13 +31545,13 @@ VITE_API_BASE_URL=${running.url}
|
|
|
31014
31545
|
let result = { ...rawResult, evidenceMode: review.mode };
|
|
31015
31546
|
if (coverage !== void 0) {
|
|
31016
31547
|
const evidence = createAcceptanceEvidence(coverage, result);
|
|
31017
|
-
await writeFile17(
|
|
31548
|
+
await writeFile17(join47(taskDir(workspace, task), "acceptance-evidence.json"), `${JSON.stringify({ ...evidence, baseline }, null, 2)}
|
|
31018
31549
|
`, { encoding: "utf8", mode: 384 });
|
|
31019
31550
|
if (!evidence.passed) result = { ...result, passed: false, failures: Math.max(1, result.failures) };
|
|
31020
31551
|
}
|
|
31021
31552
|
const dir = integrationDirectory(workspace, task);
|
|
31022
31553
|
await mkdir27(dir, { recursive: true });
|
|
31023
|
-
await writeFile17(
|
|
31554
|
+
await writeFile17(join47(dir, "interaction-results.json"), `${JSON.stringify(result, null, 2)}
|
|
31024
31555
|
`);
|
|
31025
31556
|
const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyInteractionRun(current, result));
|
|
31026
31557
|
return { workflow: next, result, review };
|
|
@@ -31033,7 +31564,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
31033
31564
|
if (mock === void 0 || !mock.exited() || result.failures === 0) return result;
|
|
31034
31565
|
const output = mock.output().trim();
|
|
31035
31566
|
try {
|
|
31036
|
-
const dir =
|
|
31567
|
+
const dir = join47(d2cOutputDirectory(workspace, task), "mock-server.log");
|
|
31037
31568
|
await writeFile17(dir, `${output}
|
|
31038
31569
|
`);
|
|
31039
31570
|
} catch {
|
|
@@ -31128,7 +31659,7 @@ VITE_API_BASE_URL=${running.url}
|
|
|
31128
31659
|
if (current === void 0) throw new Error("D2C product plan is unavailable");
|
|
31129
31660
|
if (current.plan.approvedPrd !== void 0) throw new Error("The approved PRD is immutable and cannot be edited");
|
|
31130
31661
|
if (current.plan.phase !== "prd-review") throw new Error("PRD sections can only be edited during PRD review");
|
|
31131
|
-
const path =
|
|
31662
|
+
const path = join47(taskDir(workspace, task), "product", "prd.md");
|
|
31132
31663
|
const updated = await updatePrdSectionFile(path, { sectionId, body, expectedHash });
|
|
31133
31664
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
31134
31665
|
await writeD2cProductPlan(workspace, {
|
|
@@ -31148,33 +31679,33 @@ VITE_API_BASE_URL=${running.url}
|
|
|
31148
31679
|
async #assertProductPrdLocked(workspace, task) {
|
|
31149
31680
|
const current = await readD2cProductPlanView(workspace, task);
|
|
31150
31681
|
if (current?.plan.approvedPrd === void 0) return;
|
|
31151
|
-
await assertApprovedPrd(
|
|
31682
|
+
await assertApprovedPrd(join47(taskDir(workspace, task), "product", "prd.md"), current.plan.approvedPrd);
|
|
31152
31683
|
}
|
|
31153
31684
|
async #acceptancePaths(workspace, task) {
|
|
31154
|
-
const product =
|
|
31155
|
-
const openapi =
|
|
31685
|
+
const product = join47(taskDir(workspace, task), "product");
|
|
31686
|
+
const openapi = join47(product, "openapi.json");
|
|
31156
31687
|
return {
|
|
31157
|
-
prd:
|
|
31158
|
-
prototype:
|
|
31159
|
-
interaction:
|
|
31160
|
-
...await
|
|
31688
|
+
prd: join47(product, "prd.md"),
|
|
31689
|
+
prototype: join47(product, "prototype", "index.html"),
|
|
31690
|
+
interaction: join47(product, "prototype", "interaction-manifest.json"),
|
|
31691
|
+
...await stat16(openapi).catch(() => void 0) === void 0 ? {} : { openapi }
|
|
31161
31692
|
};
|
|
31162
31693
|
}
|
|
31163
31694
|
async #verifyAcceptanceBaseline(workspace, task) {
|
|
31164
31695
|
const product = await readD2cProductPlanView(workspace, task);
|
|
31165
31696
|
if (product?.plan.approvedPrd === void 0) return void 0;
|
|
31166
31697
|
await this.#assertProductPrdLocked(workspace, task);
|
|
31167
|
-
const baseline = JSON.parse(await
|
|
31698
|
+
const baseline = JSON.parse(await readFile36(join47(taskDir(workspace, task), "acceptance-baseline.json"), "utf8"));
|
|
31168
31699
|
await verifyAcceptanceBaseline(await this.#acceptancePaths(workspace, task), baseline);
|
|
31169
|
-
const design =
|
|
31700
|
+
const design = join47(taskDir(workspace, task), "design");
|
|
31170
31701
|
await Promise.all([
|
|
31171
|
-
verifyAcceptanceArtifact(
|
|
31172
|
-
verifyAcceptanceArtifact(
|
|
31702
|
+
verifyAcceptanceArtifact(join47(design, "index.html"), baseline.artifacts.prototype, "runtime prototype"),
|
|
31703
|
+
verifyAcceptanceArtifact(join47(design, "interaction-manifest.json"), baseline.artifacts.interaction, "runtime interaction contract")
|
|
31173
31704
|
]);
|
|
31174
31705
|
const workflow = await readWorkflow(workspace, task);
|
|
31175
31706
|
if (workflow?.openapi !== void 0) {
|
|
31176
31707
|
await verifyAcceptanceArtifact(
|
|
31177
|
-
|
|
31708
|
+
join47(taskDir(workspace, task), "integration", "swagger.json"),
|
|
31178
31709
|
{ hash: workflow.openapi.hash },
|
|
31179
31710
|
"runtime OpenAPI contract"
|
|
31180
31711
|
);
|
|
@@ -31195,14 +31726,14 @@ VITE_API_BASE_URL=${running.url}
|
|
|
31195
31726
|
throw new Error("D2C PRD is empty and cannot be accepted");
|
|
31196
31727
|
}
|
|
31197
31728
|
if (stage === "design" && accepted) {
|
|
31198
|
-
const manifest =
|
|
31199
|
-
const normalizedManifest = parseInteractionManifest(await
|
|
31729
|
+
const manifest = join47(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json");
|
|
31730
|
+
const normalizedManifest = parseInteractionManifest(await readFile36(manifest, "utf8"));
|
|
31200
31731
|
if (current.plan.approvedPrd === void 0) throw new Error("Approve the PRD before confirming the design");
|
|
31201
31732
|
verifyRequirementCoverage(current.plan.approvedPrd.criteria, normalizedManifest);
|
|
31202
31733
|
await writeFile17(manifest, `${JSON.stringify(normalizedManifest, null, 2)}
|
|
31203
31734
|
`, { encoding: "utf8", mode: 384 });
|
|
31204
31735
|
const baseline = await captureAcceptanceBaseline(await this.#acceptancePaths(workspace, task), current.plan.approvedPrd);
|
|
31205
|
-
await writeFile17(
|
|
31736
|
+
await writeFile17(join47(taskDir(workspace, task), "acceptance-baseline.json"), `${JSON.stringify(baseline, null, 2)}
|
|
31206
31737
|
`, { encoding: "utf8", mode: 384 });
|
|
31207
31738
|
}
|
|
31208
31739
|
const plan = await writeD2cProductPlan(
|
|
@@ -31236,8 +31767,8 @@ VITE_API_BASE_URL=${running.url}
|
|
|
31236
31767
|
const imported = await this.importD2cDesign(task, d2cProductPrototypeDirectory(workspace, task));
|
|
31237
31768
|
const delivery = await readDeliveryRun(workspace, task);
|
|
31238
31769
|
if (delivery !== void 0) {
|
|
31239
|
-
const prototype = await
|
|
31240
|
-
const interaction = await
|
|
31770
|
+
const prototype = await readFile36(join47(d2cProductPrototypeDirectory(workspace, task), "index.html"), "utf8");
|
|
31771
|
+
const interaction = await readFile36(join47(d2cProductPrototypeDirectory(workspace, task), "interaction-manifest.json"), "utf8");
|
|
31241
31772
|
const completed = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "design", [
|
|
31242
31773
|
artifactRef("product/prototype/index.html", prototype),
|
|
31243
31774
|
artifactRef("product/prototype/interaction-manifest.json", interaction)
|
|
@@ -31321,10 +31852,10 @@ VITE_API_BASE_URL=${running.url}
|
|
|
31321
31852
|
const next = await updateWorkflow(workspace, task, workflow.revision, (current) => applyQualityJudgment(current, judgment));
|
|
31322
31853
|
const qualityContent = `${JSON.stringify(judgment, null, 2)}
|
|
31323
31854
|
`;
|
|
31324
|
-
await writeFile17(
|
|
31855
|
+
await writeFile17(join47(taskDir(workspace, task), "quality-judge.json"), qualityContent, { encoding: "utf8", mode: 384 });
|
|
31325
31856
|
const delivery = await readDeliveryRun(workspace, task);
|
|
31326
31857
|
if (next.stage === "completed" && delivery?.nodes.acceptance.status === "running") {
|
|
31327
|
-
const interactionContent = await
|
|
31858
|
+
const interactionContent = await readFile36(join47(integrationDirectory(workspace, task), "interaction-results.json"), "utf8");
|
|
31328
31859
|
const accepted = await updateDeliveryRun(workspace, task, delivery.revision, (run) => completeDeliveryNode(run, "acceptance", [
|
|
31329
31860
|
artifactRef("integration/interaction-results.json", interactionContent),
|
|
31330
31861
|
artifactRef("quality-judge.json", qualityContent)
|
|
@@ -31504,9 +32035,27 @@ function desktopWindowChrome(platform = process.platform) {
|
|
|
31504
32035
|
};
|
|
31505
32036
|
}
|
|
31506
32037
|
|
|
32038
|
+
// src/utils/login-shell-environment.ts
|
|
32039
|
+
import { delimiter as delimiter3 } from "path";
|
|
32040
|
+
async function loginShellEnvironment(environment, platform = process.platform, dependencies2 = {}) {
|
|
32041
|
+
if (platform !== "darwin") return { ...environment };
|
|
32042
|
+
const shell2 = environment.SHELL || "/bin/zsh";
|
|
32043
|
+
const result = await (dependencies2.execute ?? execFileNoThrow)(
|
|
32044
|
+
shell2,
|
|
32045
|
+
["-l", "-c", "command env"],
|
|
32046
|
+
{ timeout: 2e3, useCwd: false, env: environment }
|
|
32047
|
+
);
|
|
32048
|
+
if (result.code !== 0) return { ...environment };
|
|
32049
|
+
const loginPath = result.stdout.split(/\r?\n/u).filter((line) => line.startsWith("PATH=")).at(-1)?.slice(5);
|
|
32050
|
+
if (!loginPath) return { ...environment };
|
|
32051
|
+
const currentPath = environment.PATH ?? "";
|
|
32052
|
+
const entries = [...loginPath.split(delimiter3), ...currentPath.split(delimiter3)].filter((entry2, index, values) => entry2.length > 0 && values.indexOf(entry2) === index);
|
|
32053
|
+
return { ...environment, PATH: entries.join(delimiter3) };
|
|
32054
|
+
}
|
|
32055
|
+
|
|
31507
32056
|
// src/desktop/git-manager.ts
|
|
31508
|
-
import { readFile as
|
|
31509
|
-
import { isAbsolute as
|
|
32057
|
+
import { readFile as readFile37, rm as rm16 } from "fs/promises";
|
|
32058
|
+
import { isAbsolute as isAbsolute20, relative as relative27, resolve as resolve39 } from "path";
|
|
31510
32059
|
function failure(action, stderr, stdout) {
|
|
31511
32060
|
return new Error(`${action}\u5931\u8D25\uFF1A${(stderr.trim() || stdout.trim() || "\u672A\u77E5 Git \u9519\u8BEF").split("\n")[0]}`);
|
|
31512
32061
|
}
|
|
@@ -31537,7 +32086,7 @@ async function desktopGitDiff(workspace, path, staged = false) {
|
|
|
31537
32086
|
if (file === void 0) throw new Error("\u8BE5\u6587\u4EF6\u5DF2\u4E0D\u5728\u53D8\u66F4\u5217\u8868\u4E2D");
|
|
31538
32087
|
if (file.untracked) {
|
|
31539
32088
|
const target = safeWorkspacePath(workspace, path);
|
|
31540
|
-
const value = await
|
|
32089
|
+
const value = await readFile37(target, "utf8");
|
|
31541
32090
|
return value.length > 12e4 ? `${value.slice(0, 12e4)}
|
|
31542
32091
|
\u2026[\u5185\u5BB9\u5DF2\u622A\u65AD]` : value;
|
|
31543
32092
|
}
|
|
@@ -31599,13 +32148,13 @@ async function desktopLastTurnDiff(workspace, sessionId, paths = []) {
|
|
|
31599
32148
|
return path.replaceAll("\\", "/");
|
|
31600
32149
|
}));
|
|
31601
32150
|
try {
|
|
31602
|
-
const tree = JSON.parse(await
|
|
32151
|
+
const tree = JSON.parse(await readFile37(resolve39(workspace, ".flavor", "session-trees", sessionId, "tree.json"), "utf8"));
|
|
31603
32152
|
const leaf = tree.nodes?.find((node) => node.id === tree.leafId);
|
|
31604
32153
|
const parent = leaf?.parentId == null ? void 0 : tree.nodes?.find((node) => node.id === leaf.parentId);
|
|
31605
32154
|
if (leaf === void 0 || parent === void 0) return desktopGitReviewDiff(workspace, { scope: "last-turn", paths });
|
|
31606
32155
|
const load = async (id) => {
|
|
31607
32156
|
if (!/^checkpoint-[A-Za-z0-9-]+$/.test(id)) throw new Error("Invalid checkpoint id");
|
|
31608
|
-
return JSON.parse(await
|
|
32157
|
+
return JSON.parse(await readFile37(resolve39(workspace, ".flavor", "checkpoints", "manifests", `${id}.json`), "utf8"));
|
|
31609
32158
|
};
|
|
31610
32159
|
const [before, after] = await Promise.all([load(parent.checkpointId), load(leaf.checkpointId)]);
|
|
31611
32160
|
const oldFiles = new Map(before.files.map((file) => [file.path, file.digest]));
|
|
@@ -31614,7 +32163,7 @@ async function desktopLastTurnDiff(workspace, sessionId, paths = []) {
|
|
|
31614
32163
|
const object = async (digest) => {
|
|
31615
32164
|
if (digest === void 0) return "";
|
|
31616
32165
|
if (!/^[a-f0-9]{64}$/.test(digest)) throw new Error("Invalid checkpoint object");
|
|
31617
|
-
const value = await
|
|
32166
|
+
const value = await readFile37(resolve39(workspace, ".flavor", "checkpoints", "objects", digest));
|
|
31618
32167
|
return value.includes(0) ? "[binary content]" : value.toString("utf8");
|
|
31619
32168
|
};
|
|
31620
32169
|
const chunks = await Promise.all(changed.map(async (path) => wholeFileDiff(path, await object(oldFiles.get(path)), await object(newFiles.get(path)))));
|
|
@@ -31638,11 +32187,11 @@ async function assertListed(workspace, path) {
|
|
|
31638
32187
|
return file;
|
|
31639
32188
|
}
|
|
31640
32189
|
function safeWorkspacePath(workspace, path) {
|
|
31641
|
-
if (
|
|
31642
|
-
const root =
|
|
31643
|
-
const target =
|
|
31644
|
-
const child =
|
|
31645
|
-
if (child === "" || child.startsWith("..") ||
|
|
32190
|
+
if (isAbsolute20(path)) throw new Error("Git \u6587\u4EF6\u8DEF\u5F84\u5FC5\u987B\u662F\u9879\u76EE\u5185\u7684\u76F8\u5BF9\u8DEF\u5F84");
|
|
32191
|
+
const root = resolve39(workspace);
|
|
32192
|
+
const target = resolve39(root, path);
|
|
32193
|
+
const child = relative27(root, target);
|
|
32194
|
+
if (child === "" || child.startsWith("..") || isAbsolute20(child)) throw new Error("\u62D2\u7EDD\u8BBF\u95EE\u9879\u76EE\u4E4B\u5916\u7684\u8DEF\u5F84");
|
|
31646
32195
|
return target;
|
|
31647
32196
|
}
|
|
31648
32197
|
function safeGitTarget(value) {
|
|
@@ -31653,14 +32202,14 @@ function safeGitTarget(value) {
|
|
|
31653
32202
|
|
|
31654
32203
|
// src/desktop/worktree-manager.ts
|
|
31655
32204
|
import { randomUUID as randomUUID28 } from "crypto";
|
|
31656
|
-
import { mkdir as mkdir28, readFile as
|
|
31657
|
-
import { basename as
|
|
32205
|
+
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 isAbsolute21, join as join48, relative as relative28, resolve as resolve40, sep as sep28 } from "path";
|
|
31658
32207
|
var DesktopWorktreeManager = class {
|
|
31659
32208
|
#repository;
|
|
31660
32209
|
#storage;
|
|
31661
32210
|
constructor(options) {
|
|
31662
|
-
this.#repository =
|
|
31663
|
-
this.#storage =
|
|
32211
|
+
this.#repository = resolve40(options.repository);
|
|
32212
|
+
this.#storage = resolve40(options.storage);
|
|
31664
32213
|
}
|
|
31665
32214
|
async create(label) {
|
|
31666
32215
|
if (!await isGitRepository(this.#repository)) throw new Error("Task worktrees require a Git repository");
|
|
@@ -31693,15 +32242,15 @@ var DesktopWorktreeManager = class {
|
|
|
31693
32242
|
async list() {
|
|
31694
32243
|
let names;
|
|
31695
32244
|
try {
|
|
31696
|
-
names = await
|
|
32245
|
+
names = await readdir14(this.#storage);
|
|
31697
32246
|
} catch (error) {
|
|
31698
32247
|
if (error.code === "ENOENT") return [];
|
|
31699
32248
|
throw error;
|
|
31700
32249
|
}
|
|
31701
32250
|
const values = await Promise.all(names.filter((name) => name.endsWith(".json")).map(async (name) => {
|
|
31702
32251
|
try {
|
|
31703
|
-
const stored = validateStored(JSON.parse(await
|
|
31704
|
-
if ((await
|
|
32252
|
+
const stored = validateStored(JSON.parse(await readFile38(join48(this.#storage, name), "utf8")), this.#repository, this.#storage);
|
|
32253
|
+
if ((await stat17(stored.path).catch(() => void 0))?.isDirectory() !== true) return void 0;
|
|
31705
32254
|
const status = await git2(stored.path, ["status", "--porcelain"]);
|
|
31706
32255
|
const merged = await git2(this.#repository, ["merge-base", "--is-ancestor", stored.branch, "HEAD"]);
|
|
31707
32256
|
const { version: _version, ...item } = stored;
|
|
@@ -31734,45 +32283,45 @@ var DesktopWorktreeManager = class {
|
|
|
31734
32283
|
}
|
|
31735
32284
|
async #load(id) {
|
|
31736
32285
|
const path = this.#metadataPath(id);
|
|
31737
|
-
return validateStored(JSON.parse(await
|
|
32286
|
+
return validateStored(JSON.parse(await readFile38(path, "utf8")), this.#repository, this.#storage);
|
|
31738
32287
|
}
|
|
31739
32288
|
#metadataPath(id) {
|
|
31740
32289
|
if (!/^worktree-[a-z0-9._-]{1,80}$/.test(id)) throw new Error("Invalid worktree id");
|
|
31741
32290
|
return this.#safeStoragePath(`${id}.json`);
|
|
31742
32291
|
}
|
|
31743
32292
|
#safeStoragePath(name) {
|
|
31744
|
-
const target =
|
|
31745
|
-
const delta =
|
|
31746
|
-
if (delta === "" || delta === ".." || delta.startsWith(`..${
|
|
32293
|
+
const target = resolve40(this.#storage, name);
|
|
32294
|
+
const delta = relative28(this.#storage, target);
|
|
32295
|
+
if (delta === "" || delta === ".." || delta.startsWith(`..${sep28}`) || isAbsolute21(delta)) throw new Error("Worktree path escapes storage");
|
|
31747
32296
|
return target;
|
|
31748
32297
|
}
|
|
31749
32298
|
};
|
|
31750
32299
|
function validateStored(value, repository, storage) {
|
|
31751
32300
|
if (typeof value !== "object" || value === null) throw new Error("Invalid worktree metadata");
|
|
31752
32301
|
const item = value;
|
|
31753
|
-
if (item.version !== 1 || item.mode !== "worktree" || typeof item.id !== "string" || typeof item.path !== "string" || typeof item.branch !== "string" || typeof item.createdAt !== "string" ||
|
|
32302
|
+
if (item.version !== 1 || item.mode !== "worktree" || typeof item.id !== "string" || typeof item.path !== "string" || typeof item.branch !== "string" || typeof item.createdAt !== "string" || resolve40(item.repository ?? "") !== repository) {
|
|
31754
32303
|
throw new Error("Invalid worktree metadata");
|
|
31755
32304
|
}
|
|
31756
|
-
const path =
|
|
31757
|
-
const delta =
|
|
31758
|
-
if (delta === "" || delta === ".." || delta.startsWith(`..${
|
|
32305
|
+
const path = resolve40(item.path);
|
|
32306
|
+
const delta = relative28(storage, path);
|
|
32307
|
+
if (delta === "" || delta === ".." || delta.startsWith(`..${sep28}`) || isAbsolute21(delta) || basename11(path) !== item.id) {
|
|
31759
32308
|
throw new Error("Worktree metadata escapes storage");
|
|
31760
32309
|
}
|
|
31761
32310
|
return { version: 1, id: item.id, mode: "worktree", repository, path, branch: item.branch, createdAt: item.createdAt };
|
|
31762
32311
|
}
|
|
31763
32312
|
|
|
31764
32313
|
// src/desktop/workbench-service.ts
|
|
31765
|
-
import { readFile as
|
|
31766
|
-
import { homedir as
|
|
31767
|
-
import { join as
|
|
32314
|
+
import { readFile as readFile39, readdir as readdir15, stat as stat18 } from "fs/promises";
|
|
32315
|
+
import { homedir as homedir6 } from "os";
|
|
32316
|
+
import { join as join49, resolve as resolve41 } from "path";
|
|
31768
32317
|
var MAX_TEXT = 128e3;
|
|
31769
32318
|
var MAX_ITEMS = 200;
|
|
31770
32319
|
var DesktopWorkbenchService = class {
|
|
31771
32320
|
#workspace;
|
|
31772
32321
|
#home;
|
|
31773
|
-
constructor(workspace, home =
|
|
31774
|
-
this.#workspace =
|
|
31775
|
-
this.#home =
|
|
32322
|
+
constructor(workspace, home = homedir6()) {
|
|
32323
|
+
this.#workspace = resolve41(workspace);
|
|
32324
|
+
this.#home = resolve41(home);
|
|
31776
32325
|
}
|
|
31777
32326
|
async inspect(sessionId) {
|
|
31778
32327
|
const [goals, instructions, permissionFiles, audit, context] = await Promise.all([
|
|
@@ -31797,11 +32346,11 @@ var DesktopWorkbenchService = class {
|
|
|
31797
32346
|
return url.toString().replace(/\/$/, input.trim().endsWith("/") ? "/" : "");
|
|
31798
32347
|
}
|
|
31799
32348
|
async #goals() {
|
|
31800
|
-
const root =
|
|
31801
|
-
const names = await
|
|
32349
|
+
const root = join49(this.#workspace, ".flavor", "goals");
|
|
32350
|
+
const names = await readdir15(root).catch((error) => error.code === "ENOENT" ? [] : Promise.reject(error));
|
|
31802
32351
|
const values = await Promise.all(names.filter((name) => /^goal-[A-Za-z0-9._-]+\.json$/.test(name)).slice(0, MAX_ITEMS).map(async (name) => {
|
|
31803
32352
|
try {
|
|
31804
|
-
return GoalStateSchema.parse(JSON.parse(await boundedRead2(
|
|
32353
|
+
return GoalStateSchema.parse(JSON.parse(await boundedRead2(join49(root, name))));
|
|
31805
32354
|
} catch {
|
|
31806
32355
|
return void 0;
|
|
31807
32356
|
}
|
|
@@ -31811,7 +32360,7 @@ var DesktopWorkbenchService = class {
|
|
|
31811
32360
|
async #instructions() {
|
|
31812
32361
|
const names = ["FLAVOR.md", "AGENTS.md", "CLAUDE.md", "AGENTS.local.md", "CLAUDE.local.md"];
|
|
31813
32362
|
const items = await Promise.all(names.map(async (name) => {
|
|
31814
|
-
const path =
|
|
32363
|
+
const path = join49(this.#workspace, name);
|
|
31815
32364
|
try {
|
|
31816
32365
|
return { name, path, content: redactErrorText(await boundedRead2(path)) };
|
|
31817
32366
|
} catch (error) {
|
|
@@ -31823,10 +32372,10 @@ var DesktopWorkbenchService = class {
|
|
|
31823
32372
|
}
|
|
31824
32373
|
async #permissionFiles() {
|
|
31825
32374
|
const candidates = [
|
|
31826
|
-
...process.env.FLAVOR_MANAGED_PERMISSIONS ? [{ tier: "managed", path:
|
|
31827
|
-
{ tier: "user", path:
|
|
31828
|
-
{ tier: "project", path:
|
|
31829
|
-
{ tier: "local", path:
|
|
32375
|
+
...process.env.FLAVOR_MANAGED_PERMISSIONS ? [{ tier: "managed", path: resolve41(process.env.FLAVOR_MANAGED_PERMISSIONS), content: "" }] : [],
|
|
32376
|
+
{ tier: "user", path: join49(this.#home, ".flavor-code", "permissions.json"), content: "" },
|
|
32377
|
+
{ tier: "project", path: join49(this.#workspace, ".flavor", "permissions.json"), content: "" },
|
|
32378
|
+
{ tier: "local", path: join49(this.#workspace, ".flavor", "permissions.local.json"), content: "" }
|
|
31830
32379
|
];
|
|
31831
32380
|
const values = await Promise.all(candidates.map(async (item) => {
|
|
31832
32381
|
try {
|
|
@@ -31841,7 +32390,7 @@ var DesktopWorkbenchService = class {
|
|
|
31841
32390
|
async #audit() {
|
|
31842
32391
|
let raw;
|
|
31843
32392
|
try {
|
|
31844
|
-
raw = await boundedRead2(
|
|
32393
|
+
raw = await boundedRead2(join49(this.#workspace, ".flavor", "audit.jsonl"));
|
|
31845
32394
|
} catch (error) {
|
|
31846
32395
|
if (error.code === "ENOENT") return [];
|
|
31847
32396
|
throw error;
|
|
@@ -31856,7 +32405,7 @@ var DesktopWorkbenchService = class {
|
|
|
31856
32405
|
}).reverse();
|
|
31857
32406
|
}
|
|
31858
32407
|
async #context(sessionId) {
|
|
31859
|
-
const usage = await readJsonLines2(
|
|
32408
|
+
const usage = await readJsonLines2(join49(this.#workspace, ".flavor", "usage.jsonl"));
|
|
31860
32409
|
if (sessionId === void 0) return { visibility: [], usage };
|
|
31861
32410
|
try {
|
|
31862
32411
|
const document = await new SessionStore({ workspace: this.#workspace }).load(sessionId);
|
|
@@ -31867,10 +32416,10 @@ var DesktopWorkbenchService = class {
|
|
|
31867
32416
|
}
|
|
31868
32417
|
};
|
|
31869
32418
|
async function boundedRead2(path) {
|
|
31870
|
-
const info = await
|
|
32419
|
+
const info = await stat18(path);
|
|
31871
32420
|
if (!info.isFile()) throw new Error(`Expected a file: ${path}`);
|
|
31872
32421
|
if (info.size > MAX_TEXT) throw new Error(`Workbench file exceeds ${MAX_TEXT} bytes: ${path}`);
|
|
31873
|
-
return
|
|
32422
|
+
return readFile39(path, "utf8");
|
|
31874
32423
|
}
|
|
31875
32424
|
async function readJsonLines2(path) {
|
|
31876
32425
|
try {
|
|
@@ -31903,7 +32452,7 @@ var appMenu;
|
|
|
31903
32452
|
var quitting = false;
|
|
31904
32453
|
function logStartup(step, detail) {
|
|
31905
32454
|
try {
|
|
31906
|
-
const logPath =
|
|
32455
|
+
const logPath = join50(dirname25(process.execPath), "flavor-code-startup.log");
|
|
31907
32456
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
31908
32457
|
appendFileSync(logPath, `[${ts}] ${step}${detail ? ` | ${detail}` : ""}
|
|
31909
32458
|
`);
|
|
@@ -31928,7 +32477,7 @@ var d2cEmbedded = createEmbeddedD2cAutomation(() => {
|
|
|
31928
32477
|
};
|
|
31929
32478
|
});
|
|
31930
32479
|
var d2cJudgeClient = createD2cJudgeClient();
|
|
31931
|
-
var judgeStore = () => createD2cJudgeConfigStore(
|
|
32480
|
+
var judgeStore = () => createD2cJudgeConfigStore(join50(app.getPath("userData"), "d2c-judge.json"));
|
|
31932
32481
|
function judgeImage(png) {
|
|
31933
32482
|
const image = nativeImage.createFromBuffer(png);
|
|
31934
32483
|
const size = image.getSize();
|
|
@@ -31953,8 +32502,8 @@ function sessionMetaKey(workspace, sessionId) {
|
|
|
31953
32502
|
return `${workspace}\0${sessionId}`;
|
|
31954
32503
|
}
|
|
31955
32504
|
function worktreeManager(workspace) {
|
|
31956
|
-
const key = createHash28("sha256").update(
|
|
31957
|
-
return new DesktopWorktreeManager({ repository: workspace, storage:
|
|
32505
|
+
const key = createHash28("sha256").update(resolve42(workspace)).digest("hex").slice(0, 20);
|
|
32506
|
+
return new DesktopWorktreeManager({ repository: workspace, storage: join50(app.getPath("userData"), "task-worktrees", key) });
|
|
31958
32507
|
}
|
|
31959
32508
|
function activeWorkingDirectory() {
|
|
31960
32509
|
if (activeWorkspace === void 0) throw new Error("\u8BF7\u5148\u6253\u5F00\u9879\u76EE");
|
|
@@ -32096,10 +32645,10 @@ function createDesktopController(workspace, owner2) {
|
|
|
32096
32645
|
},
|
|
32097
32646
|
collaboration: {
|
|
32098
32647
|
instanceId: collaborationId,
|
|
32099
|
-
alias: `desktop-${
|
|
32648
|
+
alias: `desktop-${basename12(workspace ?? runtimeOptions.workspace ?? "flavor")}`.slice(0, 64),
|
|
32100
32649
|
optional: true,
|
|
32101
32650
|
startBroker: (address) => {
|
|
32102
|
-
const entry2 = app.isPackaged ?
|
|
32651
|
+
const entry2 = app.isPackaged ? join50(process.resourcesPath, "app.asar.unpacked", "dist", "desktop", "pals-broker-entry.js") : join50(moduleDirectory, "pals-broker-entry.js");
|
|
32103
32652
|
return spawnPalBrokerProcess({
|
|
32104
32653
|
address,
|
|
32105
32654
|
executable: process.execPath,
|
|
@@ -32121,11 +32670,11 @@ function createDesktopController(workspace, owner2) {
|
|
|
32121
32670
|
var detachedController = createDesktopController();
|
|
32122
32671
|
var controller = detachedController;
|
|
32123
32672
|
function statePath() {
|
|
32124
|
-
return
|
|
32673
|
+
return join50(app.getPath("userData"), "desktop-state.json");
|
|
32125
32674
|
}
|
|
32126
32675
|
async function loadPersistedProjects() {
|
|
32127
32676
|
try {
|
|
32128
|
-
const raw = await
|
|
32677
|
+
const raw = await readFile40(statePath(), "utf8");
|
|
32129
32678
|
if (raw.length > 1e5) return { projects: [] };
|
|
32130
32679
|
const parsed = JSON.parse(raw);
|
|
32131
32680
|
Object.assign(workbench, {
|
|
@@ -32166,10 +32715,10 @@ function validRecord(value) {
|
|
|
32166
32715
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
|
|
32167
32716
|
}
|
|
32168
32717
|
async function assertDirectory(path) {
|
|
32169
|
-
if (!(await
|
|
32718
|
+
if (!(await stat19(path)).isDirectory()) throw new Error("The selected project is not a directory");
|
|
32170
32719
|
}
|
|
32171
32720
|
async function openWorkspace(path) {
|
|
32172
|
-
const workspace =
|
|
32721
|
+
const workspace = resolve42(path);
|
|
32173
32722
|
await assertDirectory(workspace);
|
|
32174
32723
|
let project = managedProjects.get(workspace);
|
|
32175
32724
|
if (project === void 0) {
|
|
@@ -32180,10 +32729,10 @@ async function openWorkspace(path) {
|
|
|
32180
32729
|
activeWorkspace = workspace;
|
|
32181
32730
|
controller = nextController;
|
|
32182
32731
|
project.snapshot = await nextController.openWorkspace(workspace);
|
|
32183
|
-
for (const saved of workbench.taskCheckouts.filter((item) =>
|
|
32732
|
+
for (const saved of workbench.taskCheckouts.filter((item) => resolve42(item.workspace) === workspace)) {
|
|
32184
32733
|
try {
|
|
32185
32734
|
await assertDirectory(saved.workingDirectory);
|
|
32186
|
-
const task = { environment: "worktree", workingDirectory:
|
|
32735
|
+
const task = { environment: "worktree", workingDirectory: resolve42(saved.workingDirectory), worktreeId: saved.worktreeId };
|
|
32187
32736
|
task.controller = createDesktopController(workspace, task);
|
|
32188
32737
|
task.snapshot = await task.controller.openWorkspace(task.workingDirectory);
|
|
32189
32738
|
task.sessionId = saved.sessionId;
|
|
@@ -32297,7 +32846,7 @@ async function chooseAndOpenWorkspace() {
|
|
|
32297
32846
|
var cachedAppIconDataUrl;
|
|
32298
32847
|
function appIconDataUrl() {
|
|
32299
32848
|
if (cachedAppIconDataUrl !== void 0) return cachedAppIconDataUrl;
|
|
32300
|
-
const icon = nativeImage.createFromPath(
|
|
32849
|
+
const icon = nativeImage.createFromPath(join50(app.getAppPath(), "assets", "icon.png"));
|
|
32301
32850
|
if (icon.isEmpty()) return void 0;
|
|
32302
32851
|
cachedAppIconDataUrl = icon.toDataURL();
|
|
32303
32852
|
return cachedAppIconDataUrl;
|
|
@@ -32313,7 +32862,7 @@ function installIpcHandlers() {
|
|
|
32313
32862
|
} catch {
|
|
32314
32863
|
}
|
|
32315
32864
|
}
|
|
32316
|
-
if (persisted.workspace !== void 0 && managedProjects.has(
|
|
32865
|
+
if (persisted.workspace !== void 0 && managedProjects.has(resolve42(persisted.workspace))) {
|
|
32317
32866
|
try {
|
|
32318
32867
|
return await openWorkspace(persisted.workspace);
|
|
32319
32868
|
} catch {
|
|
@@ -32374,7 +32923,7 @@ function installIpcHandlers() {
|
|
|
32374
32923
|
});
|
|
32375
32924
|
ipcMain.handle(DESKTOP_CHANNELS.updateProject, async (_event, value) => {
|
|
32376
32925
|
const input = UpdateProjectInputSchema.parse(value);
|
|
32377
|
-
const workspace =
|
|
32926
|
+
const workspace = resolve42(input.workspace);
|
|
32378
32927
|
if (!managedProjects.has(workspace)) throw new Error("\u9879\u76EE\u672A\u6253\u5F00");
|
|
32379
32928
|
workbench.projectMeta[workspace] = { ...workbench.projectMeta[workspace], ...input.label === void 0 ? {} : { label: input.label || void 0 }, ...input.pinned === void 0 ? {} : { pinned: input.pinned } };
|
|
32380
32929
|
await savePersistedProjects();
|
|
@@ -32382,7 +32931,7 @@ function installIpcHandlers() {
|
|
|
32382
32931
|
});
|
|
32383
32932
|
ipcMain.handle(DESKTOP_CHANNELS.closeProject, async (_event, value) => {
|
|
32384
32933
|
const input = CloseProjectInputSchema.parse(value);
|
|
32385
|
-
const workspace =
|
|
32934
|
+
const workspace = resolve42(input.workspace);
|
|
32386
32935
|
const project = managedProjects.get(workspace);
|
|
32387
32936
|
if (project === void 0) return decorateSnapshot(controller.snapshot());
|
|
32388
32937
|
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");
|
|
@@ -32398,12 +32947,12 @@ function installIpcHandlers() {
|
|
|
32398
32947
|
return decorateSnapshot(controller.snapshot());
|
|
32399
32948
|
});
|
|
32400
32949
|
ipcMain.handle(DESKTOP_CHANNELS.revealProject, async (_event, value) => {
|
|
32401
|
-
const workspace =
|
|
32950
|
+
const workspace = resolve42(ProjectPathInputSchema.parse(value).workspace);
|
|
32402
32951
|
await assertDirectory(workspace);
|
|
32403
32952
|
await shell.openPath(workspace);
|
|
32404
32953
|
});
|
|
32405
32954
|
ipcMain.handle(DESKTOP_CHANNELS.copyProjectPath, async (_event, value) => {
|
|
32406
|
-
clipboard.writeText(
|
|
32955
|
+
clipboard.writeText(resolve42(ProjectPathInputSchema.parse(value).workspace));
|
|
32407
32956
|
});
|
|
32408
32957
|
ipcMain.handle(DESKTOP_CHANNELS.dismissRecovery, async (_event, value) => {
|
|
32409
32958
|
const { sessionId } = DeleteSessionInputSchema.parse(value);
|
|
@@ -32715,7 +33264,7 @@ function applicationMenu() {
|
|
|
32715
33264
|
]);
|
|
32716
33265
|
}
|
|
32717
33266
|
async function createWindow() {
|
|
32718
|
-
const rendererPath =
|
|
33267
|
+
const rendererPath = join50(app.getAppPath(), "dist", "desktop-renderer", "index.html");
|
|
32719
33268
|
mainWindow = new BrowserWindow2({
|
|
32720
33269
|
title: "Flavor Code",
|
|
32721
33270
|
width: 1280,
|
|
@@ -32726,14 +33275,14 @@ async function createWindow() {
|
|
|
32726
33275
|
show: false,
|
|
32727
33276
|
...desktopWindowChrome(),
|
|
32728
33277
|
webPreferences: {
|
|
32729
|
-
preload:
|
|
33278
|
+
preload: join50(moduleDirectory, "preload.cjs"),
|
|
32730
33279
|
contextIsolation: true,
|
|
32731
33280
|
nodeIntegration: false,
|
|
32732
33281
|
sandbox: false,
|
|
32733
33282
|
webSecurity: true
|
|
32734
33283
|
}
|
|
32735
33284
|
});
|
|
32736
|
-
const iconPath =
|
|
33285
|
+
const iconPath = join50(app.getAppPath(), "assets", "icon.png");
|
|
32737
33286
|
const icon = nativeImage.createFromPath(iconPath);
|
|
32738
33287
|
if (!icon.isEmpty()) {
|
|
32739
33288
|
mainWindow.setIcon(icon);
|
|
@@ -32772,6 +33321,8 @@ async function createWindow() {
|
|
|
32772
33321
|
});
|
|
32773
33322
|
}
|
|
32774
33323
|
app.whenReady().then(async () => {
|
|
33324
|
+
const desktopEnvironment = await loginShellEnvironment(process.env);
|
|
33325
|
+
if (desktopEnvironment.PATH !== void 0) process.env.PATH = desktopEnvironment.PATH;
|
|
32775
33326
|
installIpcHandlers();
|
|
32776
33327
|
appMenu = applicationMenu();
|
|
32777
33328
|
Menu.setApplicationMenu(appMenu);
|