cascade-ai 0.66.2 → 0.67.0
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 +8 -0
- package/dist/cli.cjs +1031 -261
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1024 -254
- package/dist/cli.js.map +1 -1
- package/dist/desktop-core.cjs +70129 -1790
- package/dist/index.cjs +1007 -210
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +270 -1
- package/dist/index.d.ts +270 -1
- package/dist/index.js +979 -204
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/dist/index.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var Database = require('better-sqlite3');
|
|
4
|
-
var
|
|
5
|
-
var
|
|
4
|
+
var path25 = require('path');
|
|
5
|
+
var fs24 = require('fs');
|
|
6
6
|
var os6 = require('os');
|
|
7
7
|
var crypto4 = require('crypto');
|
|
8
8
|
var EventEmitter = require('events');
|
|
@@ -15,7 +15,7 @@ var http = require('http');
|
|
|
15
15
|
var https = require('https');
|
|
16
16
|
var zlib = require('zlib');
|
|
17
17
|
var stream = require('stream');
|
|
18
|
-
var
|
|
18
|
+
var fs5 = require('fs/promises');
|
|
19
19
|
var ignoreFactory = require('ignore');
|
|
20
20
|
var child_process = require('child_process');
|
|
21
21
|
var util = require('util');
|
|
@@ -61,8 +61,8 @@ function _interopNamespace(e) {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
var Database__default = /*#__PURE__*/_interopDefault(Database);
|
|
64
|
-
var
|
|
65
|
-
var
|
|
64
|
+
var path25__default = /*#__PURE__*/_interopDefault(path25);
|
|
65
|
+
var fs24__default = /*#__PURE__*/_interopDefault(fs24);
|
|
66
66
|
var os6__default = /*#__PURE__*/_interopDefault(os6);
|
|
67
67
|
var crypto4__default = /*#__PURE__*/_interopDefault(crypto4);
|
|
68
68
|
var EventEmitter__default = /*#__PURE__*/_interopDefault(EventEmitter);
|
|
@@ -72,7 +72,7 @@ var dns__default = /*#__PURE__*/_interopDefault(dns);
|
|
|
72
72
|
var http__default = /*#__PURE__*/_interopDefault(http);
|
|
73
73
|
var https__default = /*#__PURE__*/_interopDefault(https);
|
|
74
74
|
var zlib__default = /*#__PURE__*/_interopDefault(zlib);
|
|
75
|
-
var
|
|
75
|
+
var fs5__default = /*#__PURE__*/_interopDefault(fs5);
|
|
76
76
|
var ignoreFactory__namespace = /*#__PURE__*/_interopNamespace(ignoreFactory);
|
|
77
77
|
var PDFDocument__default = /*#__PURE__*/_interopDefault(PDFDocument);
|
|
78
78
|
var dns2__default = /*#__PURE__*/_interopDefault(dns2);
|
|
@@ -107,12 +107,12 @@ var init_audit_logger = __esm({
|
|
|
107
107
|
constructor(workspacePath, debugMode = false) {
|
|
108
108
|
this.workspacePath = workspacePath;
|
|
109
109
|
this.debugMode = debugMode;
|
|
110
|
-
const cascadeDir =
|
|
111
|
-
if (!
|
|
112
|
-
|
|
110
|
+
const cascadeDir = path25__default.default.join(workspacePath, ".cascade");
|
|
111
|
+
if (!fs24__default.default.existsSync(cascadeDir)) {
|
|
112
|
+
fs24__default.default.mkdirSync(cascadeDir, { recursive: true });
|
|
113
113
|
}
|
|
114
|
-
this.keyPath =
|
|
115
|
-
this.dbPath =
|
|
114
|
+
this.keyPath = path25__default.default.join(cascadeDir, "audit_log.key");
|
|
115
|
+
this.dbPath = path25__default.default.join(cascadeDir, "audit_log.db");
|
|
116
116
|
this.initEncryptionKey();
|
|
117
117
|
this.db = new Database__default.default(this.dbPath);
|
|
118
118
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -142,11 +142,11 @@ var init_audit_logger = __esm({
|
|
|
142
142
|
return crypto4__default.default.createHash("sha256").update(`${prevHash}|${timestamp}|${eventType}|${tierId}|${encryptedPayload}`).digest("hex");
|
|
143
143
|
}
|
|
144
144
|
initEncryptionKey() {
|
|
145
|
-
if (
|
|
146
|
-
this.encryptionKey =
|
|
145
|
+
if (fs24__default.default.existsSync(this.keyPath)) {
|
|
146
|
+
this.encryptionKey = fs24__default.default.readFileSync(this.keyPath);
|
|
147
147
|
} else {
|
|
148
148
|
this.encryptionKey = crypto4__default.default.randomBytes(32);
|
|
149
|
-
|
|
149
|
+
fs24__default.default.writeFileSync(this.keyPath, this.encryptionKey);
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
encrypt(text) {
|
|
@@ -224,9 +224,9 @@ var init_audit_logger = __esm({
|
|
|
224
224
|
dumpDebugIfNeeded() {
|
|
225
225
|
if (!this.debugMode) return;
|
|
226
226
|
try {
|
|
227
|
-
const dumpPath =
|
|
227
|
+
const dumpPath = path25__default.default.join(os6__default.default.tmpdir(), "cascade_audit_logs_debug.json");
|
|
228
228
|
const entries = this.getAllLogs();
|
|
229
|
-
|
|
229
|
+
fs24__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
|
|
230
230
|
} catch (err) {
|
|
231
231
|
console.error("Failed to dump debug audit logs", err);
|
|
232
232
|
}
|
|
@@ -1908,7 +1908,7 @@ function reconcilePrice(model, live) {
|
|
|
1908
1908
|
}
|
|
1909
1909
|
|
|
1910
1910
|
// src/constants.ts
|
|
1911
|
-
var CASCADE_VERSION = "0.
|
|
1911
|
+
var CASCADE_VERSION = "0.67.0";
|
|
1912
1912
|
var CASCADE_CONFIG_DIR = ".cascade";
|
|
1913
1913
|
var CASCADE_MD_FILE = "CASCADE.md";
|
|
1914
1914
|
var CASCADE_IGNORE_FILE = ".cascadeignore";
|
|
@@ -4494,13 +4494,13 @@ var DeadModelStore = class {
|
|
|
4494
4494
|
function fileDeadModelPersistence(filePath) {
|
|
4495
4495
|
return {
|
|
4496
4496
|
load() {
|
|
4497
|
-
if (!
|
|
4498
|
-
const raw = JSON.parse(
|
|
4497
|
+
if (!fs24.existsSync(filePath)) return [];
|
|
4498
|
+
const raw = JSON.parse(fs24.readFileSync(filePath, "utf-8"));
|
|
4499
4499
|
return Array.isArray(raw) ? raw : [];
|
|
4500
4500
|
},
|
|
4501
4501
|
save(records) {
|
|
4502
|
-
|
|
4503
|
-
|
|
4502
|
+
fs24.mkdirSync(path25.dirname(filePath), { recursive: true });
|
|
4503
|
+
fs24.writeFileSync(filePath, JSON.stringify(records, null, 2));
|
|
4504
4504
|
}
|
|
4505
4505
|
};
|
|
4506
4506
|
}
|
|
@@ -4663,7 +4663,7 @@ function computeDelegationSavings(stats, t1Model) {
|
|
|
4663
4663
|
var DEFAULT_SNAPSHOT_URL = "https://raw.githubusercontent.com/Varun-SV/Cascade-AI/main/src/core/router/benchmark-data.json";
|
|
4664
4664
|
var OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
|
|
4665
4665
|
var FETCH_TIMEOUT_MS = 8e3;
|
|
4666
|
-
var DEFAULT_CACHE_FILE =
|
|
4666
|
+
var DEFAULT_CACHE_FILE = path25__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, "benchmarks-cache.json");
|
|
4667
4667
|
function normalizeModelId2(id) {
|
|
4668
4668
|
let s = id.toLowerCase();
|
|
4669
4669
|
const slash = s.lastIndexOf("/");
|
|
@@ -4698,7 +4698,7 @@ var LiveDataProvider = class {
|
|
|
4698
4698
|
if (this.loaded) return;
|
|
4699
4699
|
this.loaded = true;
|
|
4700
4700
|
try {
|
|
4701
|
-
const raw = await
|
|
4701
|
+
const raw = await fs5__default.default.readFile(this.opts.cacheFile, "utf-8");
|
|
4702
4702
|
const cache = JSON.parse(raw);
|
|
4703
4703
|
if (cache.snapshot?.families) {
|
|
4704
4704
|
this.snapshot = cache.snapshot;
|
|
@@ -4810,14 +4810,14 @@ var LiveDataProvider = class {
|
|
|
4810
4810
|
}
|
|
4811
4811
|
async saveCache() {
|
|
4812
4812
|
try {
|
|
4813
|
-
await
|
|
4813
|
+
await fs5__default.default.mkdir(path25__default.default.dirname(this.opts.cacheFile), { recursive: true });
|
|
4814
4814
|
const cache = {
|
|
4815
4815
|
fetchedAt: this.fetchedAt,
|
|
4816
4816
|
snapshot: this.snapshot ?? void 0,
|
|
4817
4817
|
prices: Object.fromEntries(this.prices),
|
|
4818
4818
|
capabilities: Object.fromEntries(this.capabilities)
|
|
4819
4819
|
};
|
|
4820
|
-
await
|
|
4820
|
+
await fs5__default.default.writeFile(this.opts.cacheFile, JSON.stringify(cache, null, 2), "utf-8");
|
|
4821
4821
|
} catch {
|
|
4822
4822
|
}
|
|
4823
4823
|
}
|
|
@@ -6644,7 +6644,8 @@ var KNOWN_TOOLS = [
|
|
|
6644
6644
|
"generate_image",
|
|
6645
6645
|
"generate_video",
|
|
6646
6646
|
"generate_speech",
|
|
6647
|
-
"transcribe_audio"
|
|
6647
|
+
"transcribe_audio",
|
|
6648
|
+
"generate_document"
|
|
6648
6649
|
];
|
|
6649
6650
|
var PROVIDER_BACKED_TOOLS = /* @__PURE__ */ new Set([
|
|
6650
6651
|
"generate_image",
|
|
@@ -6661,18 +6662,45 @@ function buildWorkerRules(has) {
|
|
|
6661
6662
|
canWriteFiles && "- If the task asks for a file or artifact, you must actually create it in the workspace, verify that it exists, and inspect it before claiming success.",
|
|
6662
6663
|
has("web_search") && '- Use the "web_search" tool to find current information, documentation, news, or general web data.',
|
|
6663
6664
|
has("pdf_create") && '- Use the "pdf_create" tool for PDF requests.',
|
|
6665
|
+
// A .docx/.pptx/.xlsx is a ZIP of OOXML parts, not text with a suffix.
|
|
6666
|
+
// file_write does exactly what it promises — writes the model's characters
|
|
6667
|
+
// verbatim — so without this rule the worker "wrote" a Word document that
|
|
6668
|
+
// Word correctly reported as corrupted. Stated as an explicit prohibition
|
|
6669
|
+
// on the tool it would otherwise reach for, because "use generate_document"
|
|
6670
|
+
// alone competes with a file_write habit the model already has.
|
|
6671
|
+
has("generate_document") && '- For a Word (.docx), PowerPoint (.pptx) or Excel (.xlsx) deliverable you MUST call the "generate_document" tool and NEVER "file_write" or "run_code" \u2014 those formats are ZIP archives of XML, so text written straight to the path opens as a corrupted file. Pass the target path plus the source: Markdown for .docx, Markdown slides separated by --- rules for .pptx (each slide starts with a heading), CSV for .xlsx.',
|
|
6672
|
+
// The alternative to this rule is not "a worse chart" — it is a paragraph
|
|
6673
|
+
// describing a chart that does not exist, which is what real decks came
|
|
6674
|
+
// back with. A `chart:` block is checkable, carries the exact numbers, and
|
|
6675
|
+
// becomes a genuine editable PowerPoint chart object.
|
|
6676
|
+
has("generate_document") && '- For any data-driven visualization (a chart, graph, trend, breakdown or comparison of numbers), emit a fenced ```chart:bar block \u2014 also chart:line, chart:pie, chart:doughnut, chart:area, chart:scatter \u2014 whose body is an optional "title: ..." line followed by CSV: a header row of "<category label>,<series name>,<series name>", then one row per category. In a PowerPoint deliverable that becomes a REAL, editable chart carrying your exact numbers; in Word and Excel the same block keeps every value as a table or worksheet. Never write prose describing a chart in place of emitting one.',
|
|
6664
6677
|
// Without this the model "writes the image" as prose — a bracketed
|
|
6665
6678
|
// description sitting in the deck where the picture should be — even with a
|
|
6666
6679
|
// working image model registered. Naming the reference syntax matters as
|
|
6667
6680
|
// much as naming the tool: a generated image nobody embeds is still a
|
|
6668
|
-
// missing image.
|
|
6681
|
+
// missing image. It also has to say the reference must stand ALONE on its
|
|
6682
|
+
// line, because only a standalone reference is parsed as a picture (see
|
|
6683
|
+
// core/documents/blocks.ts matchImageLine) — one embedded mid-sentence
|
|
6684
|
+
// silently degrades to caption text.
|
|
6669
6685
|
//
|
|
6670
|
-
// Scoped to PowerPoint/Word specifically: those are the only
|
|
6671
|
-
//
|
|
6672
|
-
//
|
|
6673
|
-
//
|
|
6674
|
-
//
|
|
6675
|
-
has("generate_image") &&
|
|
6686
|
+
// Scoped to PowerPoint/Word specifically: those are the only renderers that
|
|
6687
|
+
// actually embed a Markdown image reference. PDF and plain-text/Markdown
|
|
6688
|
+
// deliverables flatten a reference straight to caption text — telling the
|
|
6689
|
+
// model to call generate_image for THOSE just pays for an image nobody
|
|
6690
|
+
// ever sees.
|
|
6691
|
+
has("generate_image") && // Supersedes the earlier wording of this same rule: it now names the
|
|
6692
|
+
// standalone-line parser constraint, says to generate BEFORE writing the
|
|
6693
|
+
// document, and points data-driven visuals at a ```chart: block rather
|
|
6694
|
+
// than a Markdown table, which is only the right advice now that chart
|
|
6695
|
+
// blocks render as real chart objects.
|
|
6696
|
+
'- When a slide deck (PowerPoint) or Word document deliverable calls for a decorative image, photo or illustration, you MUST call the "generate_image" tool \u2014 once per image the deliverable needs, BEFORE you write the document \u2014 and then put each result on a line of its OWN using Markdown image syntax: , where "location" is exactly the string the tool reported back. A reference inside a sentence stays prose and no picture appears. NEVER write a text placeholder or a bracketed description such as [image: a cat] in its place, and never state that an image is included when you did not generate one. Do NOT use "generate_image" for a chart, graph or diagram that must show exact data (numbers, axes, labels) \u2014 an image model cannot guarantee those values are correct; emit a ```chart: block instead. For any OTHER deliverable format (PDF, plain text, code, etc.) do NOT call "generate_image" \u2014 those cannot embed the result, so describe the visual in words instead.',
|
|
6697
|
+
// The other half of image reliability, and the half no amount of
|
|
6698
|
+
// instruction-tightening could fix: generate_image only exists when an
|
|
6699
|
+
// OpenAI or Gemini key is configured (multimodal/registry.ts CAPABILITIES).
|
|
6700
|
+
// With neither, the model was never given a choice — and a model that
|
|
6701
|
+
// doesn't know that writes "[illustration of a cat]" and moves on. Said out
|
|
6702
|
+
// loud, up front, so the fallback is a real chart or honest prose.
|
|
6703
|
+
!has("generate_image") && (has("generate_document") || canWriteFiles) && "- No image-generation model is available on this run, so there is NO tool that can draw a picture. Do not emit a Markdown image reference, a bracketed placeholder such as [image: a cat], or any claim that an illustration is included. If the request needs a data visualization use a ```chart: block, which needs no image model; otherwise describe the visual in words and say plainly that no image could be generated.",
|
|
6676
6704
|
// The video counterpart of the image rule above, and the fix for the
|
|
6677
6705
|
// reported "it writes scripts forever and never makes the video" run. The
|
|
6678
6706
|
// failure mode is identical — the model narrates the deliverable instead of
|
|
@@ -6682,7 +6710,7 @@ function buildWorkerRules(has) {
|
|
|
6682
6710
|
// the deliverable rather than an illustration inside one, and the reference
|
|
6683
6711
|
// syntax is a plain link: Markdown image syntax does not embed a video.
|
|
6684
6712
|
has("generate_video") && '- When the subtask deliverable is a video, you MUST call the "generate_video" tool \u2014 that call IS the deliverable, and the subtask is not done until it has returned. NEVER deliver the video as prose, a script, a storyboard, or a bracketed placeholder such as [video: a cat skating], and NEVER claim a video exists unless the tool reported a location for it. Call it exactly ONCE: it is billed per second of output and renders for minutes, so a second call charges the user again. Then report the location string the tool returned VERBATIM as your result, referencing it as [description](location) if it goes inside a document. If the tool reports a failure or a timeout, report that failure verbatim and stop \u2014 do NOT call it again, and do NOT substitute a written description of a video that was never made.',
|
|
6685
|
-
has("run_code") &&
|
|
6713
|
+
has("run_code") && `- Use the "run_code" tool for data processing, archives, and file formats not covered by a dedicated tool. ${has("generate_document") ? 'Do NOT use it to build a .docx, .pptx or .xlsx \u2014 "generate_document" already produces those correctly. ' : ""}${has("pdf_create") ? 'Do NOT use it to build a PDF \u2014 "pdf_create" does that. ' : ""}Always cleanup after code execution.`,
|
|
6686
6714
|
"- If you are not making meaningful progress, stop and escalate rather than looping or padding the response.",
|
|
6687
6715
|
has("peer_message") && '- Use the "peer_message" tool to communicate with other T3 workers if your tasks have dependencies or shared state. You can send updates or wait for signals.',
|
|
6688
6716
|
hasAnyTool && "- Only use tools directly relevant to THIS subtask. Do not reach for an unrelated connected-service action (e.g. creating, deleting, or modifying a repository, issue, or PR; sending a message) unless the subtask explicitly calls for it.",
|
|
@@ -6693,13 +6721,34 @@ function buildWorkerRules(has) {
|
|
|
6693
6721
|
Rules:
|
|
6694
6722
|
${rules.filter((r) => r !== false).join("\n")}`;
|
|
6695
6723
|
}
|
|
6696
|
-
var ARTIFACT_TOOLS = /* @__PURE__ */ new Set(["file_write", "file_edit", "shell"]);
|
|
6724
|
+
var ARTIFACT_TOOLS = /* @__PURE__ */ new Set(["file_write", "file_edit", "shell", "generate_document"]);
|
|
6725
|
+
var ARTIFACT_FILE_RE = /\b[\w./-]+\.(pdf|md|html|txt|json|csv|py|js|ts|tsx|jsx|docx?|pptx?|xlsx?|png|jpg|jpeg|svg|gif)\b/i;
|
|
6697
6726
|
function shouldRequireArtifact(assignment, toolNames) {
|
|
6698
6727
|
if (!toolNames.some((n) => ARTIFACT_TOOLS.has(n))) return false;
|
|
6699
6728
|
if (assignment?.files?.length) return true;
|
|
6700
6729
|
const haystack = `${assignment?.description ?? ""}
|
|
6701
6730
|
${assignment?.expectedOutput ?? ""}`;
|
|
6702
|
-
return
|
|
6731
|
+
return ARTIFACT_FILE_RE.test(haystack) || /save (?:a|the)? file|create (?:a|the)? file|write (?:a|the)? file/i.test(haystack);
|
|
6732
|
+
}
|
|
6733
|
+
var VISUAL_REQUEST_RE = /\b(image|images|picture|pictures|photo|photos|illustration|illustrations|visual|visuals|visualisation|visualization|visualizations|chart|charts|graph|graphs|plot|plots|diagram|diagrams|infographic)\b/i;
|
|
6734
|
+
var IMAGE_REF_RE = /!\[[^\]]*\]\([^)]+\)/;
|
|
6735
|
+
var CHART_BLOCK_RE = /^\s*```+\s*chart\s*:/im;
|
|
6736
|
+
function missingVisualEvidence(assignment, output, calledTools, toolNames) {
|
|
6737
|
+
const canVisualize = toolNames.includes("generate_image") || toolNames.includes("generate_document");
|
|
6738
|
+
if (!canVisualize) return null;
|
|
6739
|
+
const haystack = `${assignment?.description ?? ""}
|
|
6740
|
+
${assignment?.expectedOutput ?? ""}`;
|
|
6741
|
+
if (!VISUAL_REQUEST_RE.test(haystack)) return null;
|
|
6742
|
+
if (calledTools.includes("generate_image") || calledTools.includes("generate_document")) return null;
|
|
6743
|
+
if (IMAGE_REF_RE.test(output) || CHART_BLOCK_RE.test(output)) return null;
|
|
6744
|
+
const options = [];
|
|
6745
|
+
if (toolNames.includes("generate_image")) {
|
|
6746
|
+
options.push('call "generate_image" and reference the result on its own line as ');
|
|
6747
|
+
}
|
|
6748
|
+
if (toolNames.includes("generate_document")) {
|
|
6749
|
+
options.push("emit a fenced ```chart:bar (or chart:line / chart:pie) block whose body is CSV, for anything data-driven");
|
|
6750
|
+
}
|
|
6751
|
+
return `The subtask asks for a visual, but the output contains no embedded image and no chart block \u2014 only prose. ${options.join(", or ")}. Describing a picture is not producing one.`;
|
|
6703
6752
|
}
|
|
6704
6753
|
var T3Worker = class extends BaseTier {
|
|
6705
6754
|
router;
|
|
@@ -6880,6 +6929,18 @@ Now execute your subtask using this context where relevant.`
|
|
|
6880
6929
|
return this.buildResult("ESCALATED", output, { checksRun, passed, failed }, issues, correctionAttempts);
|
|
6881
6930
|
}
|
|
6882
6931
|
}
|
|
6932
|
+
const visualIssue = missingVisualEvidence(
|
|
6933
|
+
assignment,
|
|
6934
|
+
output,
|
|
6935
|
+
toolCalls.map((t) => t.name),
|
|
6936
|
+
this.tools.map((t) => t.name)
|
|
6937
|
+
);
|
|
6938
|
+
if (visualIssue) {
|
|
6939
|
+
correctionAttempts = 1;
|
|
6940
|
+
issues.push(visualIssue);
|
|
6941
|
+
this.sendStatusUpdate({ progressPct: 68, currentAction: "Verifying requested visuals", status: "IN_PROGRESS" });
|
|
6942
|
+
output = await this.correctOutput(output, [visualIssue]);
|
|
6943
|
+
}
|
|
6883
6944
|
this.sendStatusUpdate({ progressPct: 70, currentAction: "Self-testing output", status: "IN_PROGRESS" });
|
|
6884
6945
|
const testResult = await this.selfTest(assignment, output);
|
|
6885
6946
|
checksRun.push(...testResult.checksRun);
|
|
@@ -7194,8 +7255,8 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : "") + textToolSuffix,
|
|
|
7194
7255
|
// Media generation can run for a minute; without this a cancelled run
|
|
7195
7256
|
// still pays for an image nobody will see.
|
|
7196
7257
|
...this.signal ? { signal: this.signal } : {},
|
|
7197
|
-
saveSnapshot: async (
|
|
7198
|
-
this.store?.addFileSnapshot(this.taskId,
|
|
7258
|
+
saveSnapshot: async (path27, content) => {
|
|
7259
|
+
this.store?.addFileSnapshot(this.taskId, path27, content);
|
|
7199
7260
|
},
|
|
7200
7261
|
sendPeerSync: (to, syncType, content) => {
|
|
7201
7262
|
this.peerBus?.send(this.id, to, syncType, this.assignment?.subtaskId ?? "", content);
|
|
@@ -7371,7 +7432,7 @@ ${assignment.expectedOutput}`;
|
|
|
7371
7432
|
const declared = (assignment.files ?? []).map((f) => f.trim()).filter((f) => f.includes("."));
|
|
7372
7433
|
const haystack = `${assignment.description}
|
|
7373
7434
|
${assignment.expectedOutput}`;
|
|
7374
|
-
const matches = haystack.match(
|
|
7435
|
+
const matches = haystack.match(new RegExp(ARTIFACT_FILE_RE.source, "gi")) ?? [];
|
|
7375
7436
|
return [.../* @__PURE__ */ new Set([...declared, ...matches.map((m) => m.trim())])];
|
|
7376
7437
|
}
|
|
7377
7438
|
async verifyArtifacts(assignment) {
|
|
@@ -7382,9 +7443,9 @@ ${assignment.expectedOutput}`;
|
|
|
7382
7443
|
const { promisify: promisify4 } = await import('util');
|
|
7383
7444
|
const execAsync2 = promisify4(exec2);
|
|
7384
7445
|
for (const artifactPath of artifactPaths) {
|
|
7385
|
-
const absolutePath =
|
|
7446
|
+
const absolutePath = path25__default.default.resolve(process.cwd(), artifactPath);
|
|
7386
7447
|
try {
|
|
7387
|
-
const stat = await
|
|
7448
|
+
const stat = await fs5__default.default.stat(absolutePath);
|
|
7388
7449
|
if (!stat.isFile()) {
|
|
7389
7450
|
issues.push(`Expected artifact is not a file: ${artifactPath}`);
|
|
7390
7451
|
continue;
|
|
@@ -7393,8 +7454,22 @@ ${assignment.expectedOutput}`;
|
|
|
7393
7454
|
issues.push(`Artifact is empty: ${artifactPath}`);
|
|
7394
7455
|
continue;
|
|
7395
7456
|
}
|
|
7396
|
-
if (
|
|
7397
|
-
const
|
|
7457
|
+
if (/\.(docx|pptx|xlsx)$/i.test(artifactPath)) {
|
|
7458
|
+
const head = Buffer.alloc(4);
|
|
7459
|
+
const fh = await fs5__default.default.open(absolutePath, "r");
|
|
7460
|
+
try {
|
|
7461
|
+
await fh.read(head, 0, 4, 0);
|
|
7462
|
+
} finally {
|
|
7463
|
+
await fh.close();
|
|
7464
|
+
}
|
|
7465
|
+
if (head.toString("latin1") !== "PK") {
|
|
7466
|
+
issues.push(
|
|
7467
|
+
`${artifactPath} is not a valid Office file \u2014 it must be written with the generate_document tool, which renders the real OOXML archive. Plain text saved under that extension opens as corrupted.`
|
|
7468
|
+
);
|
|
7469
|
+
continue;
|
|
7470
|
+
}
|
|
7471
|
+
} else if (!/\.pdf$/i.test(artifactPath)) {
|
|
7472
|
+
const content = await fs5__default.default.readFile(absolutePath, "utf-8");
|
|
7398
7473
|
if (!content.trim()) {
|
|
7399
7474
|
issues.push(`Artifact content is empty: ${artifactPath}`);
|
|
7400
7475
|
continue;
|
|
@@ -7403,7 +7478,7 @@ ${assignment.expectedOutput}`;
|
|
|
7403
7478
|
issues.push(`PDF artifact looks too small to be valid: ${artifactPath}`);
|
|
7404
7479
|
continue;
|
|
7405
7480
|
}
|
|
7406
|
-
const ext =
|
|
7481
|
+
const ext = path25__default.default.extname(absolutePath).toLowerCase();
|
|
7407
7482
|
try {
|
|
7408
7483
|
if (ext === ".ts" || ext === ".tsx") {
|
|
7409
7484
|
await execAsync2(`npx tsc --noEmit ${absolutePath}`, { timeout: 1e4 });
|
|
@@ -10350,6 +10425,705 @@ function buildMediaTools(deps, readFile) {
|
|
|
10350
10425
|
if (modalities.has("transcription") && readFile) tools.push(new TranscribeAudioTool(deps, readFile));
|
|
10351
10426
|
return tools;
|
|
10352
10427
|
}
|
|
10428
|
+
var WorkspaceSandboxError = class extends Error {
|
|
10429
|
+
constructor(attempted, workspaceRoot) {
|
|
10430
|
+
super(`Refusing access to "${attempted}" \u2014 outside workspace root "${workspaceRoot}".`);
|
|
10431
|
+
this.name = "WorkspaceSandboxError";
|
|
10432
|
+
}
|
|
10433
|
+
};
|
|
10434
|
+
function resolveInWorkspace(workspaceRoot, input) {
|
|
10435
|
+
if (typeof input !== "string" || input.length === 0) {
|
|
10436
|
+
throw new WorkspaceSandboxError(String(input), workspaceRoot);
|
|
10437
|
+
}
|
|
10438
|
+
const root = path25__default.default.resolve(workspaceRoot);
|
|
10439
|
+
const abs = path25__default.default.isAbsolute(input) ? path25__default.default.resolve(input) : path25__default.default.resolve(root, input);
|
|
10440
|
+
const rel = path25__default.default.relative(root, abs);
|
|
10441
|
+
if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path25__default.default.isAbsolute(rel)) {
|
|
10442
|
+
throw new WorkspaceSandboxError(input, root);
|
|
10443
|
+
}
|
|
10444
|
+
try {
|
|
10445
|
+
const real = fs24__default.default.realpathSync(abs);
|
|
10446
|
+
const realRel = path25__default.default.relative(root, real);
|
|
10447
|
+
if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path25__default.default.isAbsolute(realRel))) {
|
|
10448
|
+
throw new WorkspaceSandboxError(input, root);
|
|
10449
|
+
}
|
|
10450
|
+
} catch (e) {
|
|
10451
|
+
if (e instanceof WorkspaceSandboxError) throw e;
|
|
10452
|
+
}
|
|
10453
|
+
return abs;
|
|
10454
|
+
}
|
|
10455
|
+
|
|
10456
|
+
// src/core/documents/blocks.ts
|
|
10457
|
+
function fileExt(name) {
|
|
10458
|
+
const base = name.split(/[\\/]/).pop() ?? name;
|
|
10459
|
+
const dot = base.lastIndexOf(".");
|
|
10460
|
+
return dot > 0 ? base.slice(dot + 1).toLowerCase() : "";
|
|
10461
|
+
}
|
|
10462
|
+
function parseDelimited(text, name = "") {
|
|
10463
|
+
const delim = fileExt(name) === "tsv" || text.indexOf(" ") !== -1 && text.indexOf(",") === -1 ? " " : ",";
|
|
10464
|
+
const rows = [];
|
|
10465
|
+
let row = [];
|
|
10466
|
+
let cell = "";
|
|
10467
|
+
let quoted = false;
|
|
10468
|
+
for (let i = 0; i < text.length; i++) {
|
|
10469
|
+
const ch = text[i];
|
|
10470
|
+
if (quoted) {
|
|
10471
|
+
if (ch === '"') {
|
|
10472
|
+
if (text[i + 1] === '"') {
|
|
10473
|
+
cell += '"';
|
|
10474
|
+
i++;
|
|
10475
|
+
} else quoted = false;
|
|
10476
|
+
} else cell += ch;
|
|
10477
|
+
} else if (ch === '"') {
|
|
10478
|
+
quoted = true;
|
|
10479
|
+
} else if (ch === delim) {
|
|
10480
|
+
row.push(cell);
|
|
10481
|
+
cell = "";
|
|
10482
|
+
} else if (ch === "\n") {
|
|
10483
|
+
row.push(cell);
|
|
10484
|
+
rows.push(row);
|
|
10485
|
+
row = [];
|
|
10486
|
+
cell = "";
|
|
10487
|
+
} else if (ch === "\r") ; else {
|
|
10488
|
+
cell += ch;
|
|
10489
|
+
}
|
|
10490
|
+
if (rows.length > 5e3) break;
|
|
10491
|
+
}
|
|
10492
|
+
if (cell !== "" || row.length) {
|
|
10493
|
+
row.push(cell);
|
|
10494
|
+
rows.push(row);
|
|
10495
|
+
}
|
|
10496
|
+
return rows.filter((r) => r.length > 1 || (r[0] ?? "") !== "");
|
|
10497
|
+
}
|
|
10498
|
+
var CHART_ALIASES = {
|
|
10499
|
+
bar: "bar",
|
|
10500
|
+
column: "bar",
|
|
10501
|
+
bars: "bar",
|
|
10502
|
+
barchart: "bar",
|
|
10503
|
+
horizontalbar: "bar",
|
|
10504
|
+
line: "line",
|
|
10505
|
+
lines: "line",
|
|
10506
|
+
linechart: "line",
|
|
10507
|
+
pie: "pie",
|
|
10508
|
+
piechart: "pie",
|
|
10509
|
+
doughnut: "doughnut",
|
|
10510
|
+
donut: "doughnut",
|
|
10511
|
+
area: "area",
|
|
10512
|
+
areachart: "area",
|
|
10513
|
+
scatter: "scatter",
|
|
10514
|
+
xy: "scatter"
|
|
10515
|
+
};
|
|
10516
|
+
function chartKind(raw) {
|
|
10517
|
+
return CHART_ALIASES[raw.trim().toLowerCase().replace(/[\s_-]/g, "")] ?? null;
|
|
10518
|
+
}
|
|
10519
|
+
function toNumber(cell) {
|
|
10520
|
+
const cleaned = cell.replace(/[$£€¥,\s%]/g, "").replace(/^\((.*)\)$/, "-$1");
|
|
10521
|
+
const n = Number.parseFloat(cleaned);
|
|
10522
|
+
return Number.isFinite(n) ? n : 0;
|
|
10523
|
+
}
|
|
10524
|
+
function parseChartSpec(kind, body) {
|
|
10525
|
+
const lines = body.replace(/\r\n/g, "\n").split("\n");
|
|
10526
|
+
let title;
|
|
10527
|
+
while (lines.length && !lines[0].trim()) lines.shift();
|
|
10528
|
+
const titleMatch = lines[0]?.match(/^\s*title\s*:\s*(.+)$/i);
|
|
10529
|
+
if (titleMatch) {
|
|
10530
|
+
title = titleMatch[1].trim();
|
|
10531
|
+
lines.shift();
|
|
10532
|
+
}
|
|
10533
|
+
const rows = parseDelimited(lines.join("\n").trim());
|
|
10534
|
+
if (rows.length < 2) return null;
|
|
10535
|
+
const header = rows[0].map((c) => c.trim());
|
|
10536
|
+
if (header.length < 2) return null;
|
|
10537
|
+
const dataRows = rows.slice(1).filter((r) => r.some((c) => c.trim() !== ""));
|
|
10538
|
+
if (!dataRows.length) return null;
|
|
10539
|
+
const categories = dataRows.map((r, i) => (r[0] ?? "").trim() || `Item ${i + 1}`);
|
|
10540
|
+
const series = header.slice(1).map((name, col) => ({
|
|
10541
|
+
name: name || `Series ${col + 1}`,
|
|
10542
|
+
values: dataRows.map((r) => toNumber((r[col + 1] ?? "").trim()))
|
|
10543
|
+
}));
|
|
10544
|
+
if (!series.length) return null;
|
|
10545
|
+
return {
|
|
10546
|
+
kind,
|
|
10547
|
+
...title ? { title } : {},
|
|
10548
|
+
categoryName: header[0] || "Category",
|
|
10549
|
+
categories,
|
|
10550
|
+
series
|
|
10551
|
+
};
|
|
10552
|
+
}
|
|
10553
|
+
function chartToTableRows(spec) {
|
|
10554
|
+
return [
|
|
10555
|
+
[spec.categoryName, ...spec.series.map((s) => s.name)],
|
|
10556
|
+
...spec.categories.map((cat, i) => [cat, ...spec.series.map((s) => String(s.values[i] ?? 0))])
|
|
10557
|
+
];
|
|
10558
|
+
}
|
|
10559
|
+
function parseBlocks(md) {
|
|
10560
|
+
const out = [];
|
|
10561
|
+
const lines = md.replace(/\r\n/g, "\n").split("\n");
|
|
10562
|
+
let i = 0;
|
|
10563
|
+
let ordinal = 0;
|
|
10564
|
+
const reset = () => {
|
|
10565
|
+
ordinal = 0;
|
|
10566
|
+
};
|
|
10567
|
+
while (i < lines.length) {
|
|
10568
|
+
const line = lines[i] ?? "";
|
|
10569
|
+
const fence = line.match(/^```+\s*(.*)$/);
|
|
10570
|
+
if (fence) {
|
|
10571
|
+
const info = (fence[1] ?? "").trim();
|
|
10572
|
+
i++;
|
|
10573
|
+
const code = [];
|
|
10574
|
+
while (i < lines.length && !/^```/.test(lines[i] ?? "")) {
|
|
10575
|
+
code.push(lines[i] ?? "");
|
|
10576
|
+
i++;
|
|
10577
|
+
}
|
|
10578
|
+
i++;
|
|
10579
|
+
const chartInfo = info.match(/^chart\s*:\s*(.+)$/i);
|
|
10580
|
+
const kind = chartInfo ? chartKind(chartInfo[1]) : null;
|
|
10581
|
+
const spec = kind ? parseChartSpec(kind, code.join("\n")) : null;
|
|
10582
|
+
out.push(spec ? { t: "chart", spec } : { t: "code", lines: code });
|
|
10583
|
+
reset();
|
|
10584
|
+
continue;
|
|
10585
|
+
}
|
|
10586
|
+
if (!line.trim()) {
|
|
10587
|
+
i++;
|
|
10588
|
+
reset();
|
|
10589
|
+
continue;
|
|
10590
|
+
}
|
|
10591
|
+
const h = line.match(/^(#{1,6})\s+(.*)$/);
|
|
10592
|
+
if (h) {
|
|
10593
|
+
out.push({ t: "heading", level: h[1].length, text: h[2] });
|
|
10594
|
+
i++;
|
|
10595
|
+
reset();
|
|
10596
|
+
continue;
|
|
10597
|
+
}
|
|
10598
|
+
if (/^\s*([-*_])\s*(\1\s*){2,}$/.test(line)) {
|
|
10599
|
+
out.push({ t: "hr" });
|
|
10600
|
+
i++;
|
|
10601
|
+
reset();
|
|
10602
|
+
continue;
|
|
10603
|
+
}
|
|
10604
|
+
const li = line.match(/^(\s*)([-*+]|\d+[.)])\s+(.*)$/);
|
|
10605
|
+
if (li) {
|
|
10606
|
+
const ordered = /\d/.test(li[2]);
|
|
10607
|
+
ordinal = ordered ? ordinal + 1 : 0;
|
|
10608
|
+
out.push({ t: "bullet", ordered, index: ordered ? ordinal : 0, text: li[3] });
|
|
10609
|
+
i++;
|
|
10610
|
+
continue;
|
|
10611
|
+
}
|
|
10612
|
+
if (/^>\s?/.test(line)) {
|
|
10613
|
+
out.push({ t: "quote", text: line.replace(/^>\s?/, "") });
|
|
10614
|
+
i++;
|
|
10615
|
+
reset();
|
|
10616
|
+
continue;
|
|
10617
|
+
}
|
|
10618
|
+
if (/^\|.*\|/.test(line)) {
|
|
10619
|
+
const rows = [];
|
|
10620
|
+
while (i < lines.length && /^\|.*\|/.test(lines[i] ?? "")) {
|
|
10621
|
+
const r = lines[i];
|
|
10622
|
+
if (!/^\|[\s:|-]+\|?\s*$/.test(r)) rows.push(r.replace(/^\||\|\s*$/g, "").split("|").map((c) => c.trim()));
|
|
10623
|
+
i++;
|
|
10624
|
+
}
|
|
10625
|
+
out.push({ t: "table", rows });
|
|
10626
|
+
reset();
|
|
10627
|
+
continue;
|
|
10628
|
+
}
|
|
10629
|
+
out.push({ t: "para", text: line });
|
|
10630
|
+
i++;
|
|
10631
|
+
reset();
|
|
10632
|
+
}
|
|
10633
|
+
return out;
|
|
10634
|
+
}
|
|
10635
|
+
var IMAGE_LINE = /^!\[([^\]]*)\]\(([^)]+)\)$/;
|
|
10636
|
+
function matchImageLine(s) {
|
|
10637
|
+
const m = s.trim().match(IMAGE_LINE);
|
|
10638
|
+
return m ? { alt: m[1].trim(), url: m[2].trim() } : null;
|
|
10639
|
+
}
|
|
10640
|
+
function sniffImage(b) {
|
|
10641
|
+
const dv = new DataView(b.buffer, b.byteOffset, b.byteLength);
|
|
10642
|
+
if (b.length > 24 && b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71) {
|
|
10643
|
+
return { type: "png", mime: "image/png", width: dv.getUint32(16), height: dv.getUint32(20) };
|
|
10644
|
+
}
|
|
10645
|
+
if (b.length > 10 && b[0] === 71 && b[1] === 73 && b[2] === 70) {
|
|
10646
|
+
return { type: "gif", mime: "image/gif", width: dv.getUint16(6, true), height: dv.getUint16(8, true) };
|
|
10647
|
+
}
|
|
10648
|
+
if (b.length > 26 && b[0] === 66 && b[1] === 77) {
|
|
10649
|
+
return { type: "bmp", mime: "image/bmp", width: Math.abs(dv.getInt32(18, true)), height: Math.abs(dv.getInt32(22, true)) };
|
|
10650
|
+
}
|
|
10651
|
+
if (b.length > 4 && b[0] === 255 && b[1] === 216) {
|
|
10652
|
+
let i = 2;
|
|
10653
|
+
while (i + 9 < b.length) {
|
|
10654
|
+
if (b[i] !== 255) {
|
|
10655
|
+
i++;
|
|
10656
|
+
continue;
|
|
10657
|
+
}
|
|
10658
|
+
const marker = b[i + 1];
|
|
10659
|
+
if (marker >= 192 && marker <= 207 && marker !== 196 && marker !== 200 && marker !== 204) {
|
|
10660
|
+
return { type: "jpg", mime: "image/jpeg", height: dv.getUint16(i + 5), width: dv.getUint16(i + 7) };
|
|
10661
|
+
}
|
|
10662
|
+
i += 2 + dv.getUint16(i + 2);
|
|
10663
|
+
}
|
|
10664
|
+
}
|
|
10665
|
+
return null;
|
|
10666
|
+
}
|
|
10667
|
+
function bytesToBase64(bytes) {
|
|
10668
|
+
const g = globalThis;
|
|
10669
|
+
if (g.Buffer) return g.Buffer.from(bytes).toString("base64");
|
|
10670
|
+
const CHUNK = 32768;
|
|
10671
|
+
let binary = "";
|
|
10672
|
+
for (let i = 0; i < bytes.length; i += CHUNK) {
|
|
10673
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
|
|
10674
|
+
}
|
|
10675
|
+
return g.btoa(binary);
|
|
10676
|
+
}
|
|
10677
|
+
function stripInline(s) {
|
|
10678
|
+
return s.replace(/\*\*(.+?)\*\*/g, "$1").replace(/(^|[^*])\*(?!\s)(.+?)\*/g, "$1$2").replace(/`([^`]+)`/g, "$1").replace(/\[(.+?)\]\((.+?)\)/g, "$1 ($2)");
|
|
10679
|
+
}
|
|
10680
|
+
function inlineRuns(text) {
|
|
10681
|
+
const acc = [];
|
|
10682
|
+
const re = /(\*\*[^*]+\*\*|`[^`]+`|\[[^\]]{1,500}\]\([^)]{1,2000}\)|\*[^*\s][^*]*\*)/g;
|
|
10683
|
+
let last = 0;
|
|
10684
|
+
let m;
|
|
10685
|
+
while ((m = re.exec(text)) !== null) {
|
|
10686
|
+
if (m.index > last) acc.push({ text: text.slice(last, m.index) });
|
|
10687
|
+
const tok = m[0];
|
|
10688
|
+
if (tok.startsWith("**")) acc.push({ text: tok.slice(2, -2), bold: true });
|
|
10689
|
+
else if (tok.startsWith("`")) acc.push({ text: tok.slice(1, -1), code: true });
|
|
10690
|
+
else if (tok.startsWith("[")) {
|
|
10691
|
+
const l = tok.match(/\[([^\]]{1,500})\]\(([^)]{1,2000})\)/);
|
|
10692
|
+
acc.push({ text: l ? `${l[1]} (${l[2]})` : tok });
|
|
10693
|
+
} else acc.push({ text: tok.slice(1, -1), italic: true });
|
|
10694
|
+
last = re.lastIndex;
|
|
10695
|
+
}
|
|
10696
|
+
if (last < text.length) acc.push({ text: text.slice(last) });
|
|
10697
|
+
return acc.length ? acc : [{ text }];
|
|
10698
|
+
}
|
|
10699
|
+
function splitSlides(md) {
|
|
10700
|
+
const norm = md.replace(/\r\n/g, "\n").trim();
|
|
10701
|
+
let chunks = norm.split(/\n\s*-{3,}\s*\n/);
|
|
10702
|
+
if (chunks.length === 1) {
|
|
10703
|
+
const lines = norm.split("\n");
|
|
10704
|
+
const sections = [];
|
|
10705
|
+
let cur = [];
|
|
10706
|
+
for (const ln of lines) {
|
|
10707
|
+
if (/^#{1,2}\s+/.test(ln) && cur.some((l) => l.trim())) {
|
|
10708
|
+
sections.push(cur.join("\n"));
|
|
10709
|
+
cur = [];
|
|
10710
|
+
}
|
|
10711
|
+
cur.push(ln);
|
|
10712
|
+
}
|
|
10713
|
+
if (cur.length) sections.push(cur.join("\n"));
|
|
10714
|
+
if (sections.length > 1) chunks = sections;
|
|
10715
|
+
}
|
|
10716
|
+
return chunks.map(parseSlide).filter((s) => s.title || s.body.length || s.images.length || s.charts.length);
|
|
10717
|
+
}
|
|
10718
|
+
function parseSlide(chunk) {
|
|
10719
|
+
let title = "";
|
|
10720
|
+
const body = [];
|
|
10721
|
+
const images = [];
|
|
10722
|
+
const charts = [];
|
|
10723
|
+
const lines = chunk.split("\n");
|
|
10724
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10725
|
+
const raw = lines[i] ?? "";
|
|
10726
|
+
const fence = raw.match(/^[ \t]*```+[ \t]*chart[ \t]*:(.*)/i);
|
|
10727
|
+
if (fence) {
|
|
10728
|
+
const kind = chartKind(fence[1]);
|
|
10729
|
+
const bodyLines = [];
|
|
10730
|
+
i++;
|
|
10731
|
+
while (i < lines.length && !/^\s*```/.test(lines[i] ?? "")) {
|
|
10732
|
+
bodyLines.push(lines[i] ?? "");
|
|
10733
|
+
i++;
|
|
10734
|
+
}
|
|
10735
|
+
const spec = kind ? parseChartSpec(kind, bodyLines.join("\n")) : null;
|
|
10736
|
+
if (spec) charts.push(spec);
|
|
10737
|
+
else for (const bl of bodyLines) if (bl.trim()) body.push(stripInline(bl.trim()));
|
|
10738
|
+
continue;
|
|
10739
|
+
}
|
|
10740
|
+
const line = raw.trim();
|
|
10741
|
+
if (!line) continue;
|
|
10742
|
+
const h = line.match(/^#{1,6}[ \t]+(.*)/);
|
|
10743
|
+
if (h) {
|
|
10744
|
+
if (!title) title = stripInline(h[1]);
|
|
10745
|
+
else body.push(stripInline(h[1]));
|
|
10746
|
+
continue;
|
|
10747
|
+
}
|
|
10748
|
+
const img = matchImageLine(line);
|
|
10749
|
+
if (img) {
|
|
10750
|
+
images.push(img);
|
|
10751
|
+
continue;
|
|
10752
|
+
}
|
|
10753
|
+
const li = line.match(/^([-*+]|\d+[.)])[ \t]+(.*)/);
|
|
10754
|
+
if (li) {
|
|
10755
|
+
const bulletImg = matchImageLine(li[2]);
|
|
10756
|
+
if (bulletImg) {
|
|
10757
|
+
images.push(bulletImg);
|
|
10758
|
+
continue;
|
|
10759
|
+
}
|
|
10760
|
+
body.push(stripInline(li[2]));
|
|
10761
|
+
continue;
|
|
10762
|
+
}
|
|
10763
|
+
if (/^>\s?/.test(line)) {
|
|
10764
|
+
body.push(stripInline(line.replace(/^>\s?/, "")));
|
|
10765
|
+
continue;
|
|
10766
|
+
}
|
|
10767
|
+
body.push(stripInline(line));
|
|
10768
|
+
}
|
|
10769
|
+
return { title, body, images, charts };
|
|
10770
|
+
}
|
|
10771
|
+
function extractCharts(source) {
|
|
10772
|
+
const lines = source.replace(/\r\n/g, "\n").split("\n");
|
|
10773
|
+
const charts = [];
|
|
10774
|
+
const rest = [];
|
|
10775
|
+
for (let i = 0; i < lines.length; i++) {
|
|
10776
|
+
const fence = lines[i].match(/^[ \t]*```+[ \t]*chart[ \t]*:(.*)/i);
|
|
10777
|
+
if (!fence) {
|
|
10778
|
+
rest.push(lines[i]);
|
|
10779
|
+
continue;
|
|
10780
|
+
}
|
|
10781
|
+
const kind = chartKind(fence[1]);
|
|
10782
|
+
const body = [];
|
|
10783
|
+
i++;
|
|
10784
|
+
while (i < lines.length && !/^\s*```/.test(lines[i] ?? "")) {
|
|
10785
|
+
body.push(lines[i] ?? "");
|
|
10786
|
+
i++;
|
|
10787
|
+
}
|
|
10788
|
+
const spec = kind ? parseChartSpec(kind, body.join("\n")) : null;
|
|
10789
|
+
if (spec) charts.push(spec);
|
|
10790
|
+
else rest.push(...body);
|
|
10791
|
+
}
|
|
10792
|
+
return { charts, rest: rest.join("\n") };
|
|
10793
|
+
}
|
|
10794
|
+
|
|
10795
|
+
// src/core/documents/render.ts
|
|
10796
|
+
var DOCUMENT_MIME = {
|
|
10797
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
10798
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
10799
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
10800
|
+
};
|
|
10801
|
+
function isDocumentFormat(ext) {
|
|
10802
|
+
return ext === "docx" || ext === "pptx" || ext === "xlsx";
|
|
10803
|
+
}
|
|
10804
|
+
async function loadImage(ref, load) {
|
|
10805
|
+
if (!load) return null;
|
|
10806
|
+
try {
|
|
10807
|
+
const bytes = await load(ref.url);
|
|
10808
|
+
if (!bytes || !bytes.length) return null;
|
|
10809
|
+
const info = sniffImage(bytes);
|
|
10810
|
+
if (!info || info.width <= 0 || info.height <= 0) return null;
|
|
10811
|
+
return { ...info, bytes, alt: ref.alt };
|
|
10812
|
+
} catch {
|
|
10813
|
+
return null;
|
|
10814
|
+
}
|
|
10815
|
+
}
|
|
10816
|
+
async function renderXlsx(source, opts = {}) {
|
|
10817
|
+
const XLSX = await import('xlsx');
|
|
10818
|
+
const { charts, rest } = extractCharts(source);
|
|
10819
|
+
const name = opts.name ?? "";
|
|
10820
|
+
const rows = parseDelimited(rest, name.toLowerCase().endsWith(".tsv") ? "x.tsv" : "x.csv");
|
|
10821
|
+
const wb = XLSX.utils.book_new();
|
|
10822
|
+
XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(rows.length ? rows : [[""]]), "Sheet1");
|
|
10823
|
+
const used = /* @__PURE__ */ new Set(["Sheet1"]);
|
|
10824
|
+
charts.forEach((spec, i) => {
|
|
10825
|
+
const base = (spec.title || `Chart ${i + 1}`).replace(/[:\\/?*[\]]/g, " ").trim().slice(0, 28) || `Chart ${i + 1}`;
|
|
10826
|
+
let sheetName = base;
|
|
10827
|
+
let n = 2;
|
|
10828
|
+
while (used.has(sheetName)) sheetName = `${base} ${n++}`.slice(0, 31);
|
|
10829
|
+
used.add(sheetName);
|
|
10830
|
+
const aoa = [
|
|
10831
|
+
[spec.categoryName, ...spec.series.map((s) => s.name)],
|
|
10832
|
+
...spec.categories.map((cat, r) => [cat, ...spec.series.map((s) => s.values[r] ?? 0)])
|
|
10833
|
+
];
|
|
10834
|
+
XLSX.utils.book_append_sheet(wb, XLSX.utils.aoa_to_sheet(aoa), sheetName);
|
|
10835
|
+
});
|
|
10836
|
+
return new Uint8Array(XLSX.write(wb, { type: "array", bookType: "xlsx" }));
|
|
10837
|
+
}
|
|
10838
|
+
var DOCX_MAX_W = 600;
|
|
10839
|
+
var DOCX_MAX_H = 864;
|
|
10840
|
+
async function renderDocx(md, opts = {}) {
|
|
10841
|
+
const d = await import('docx');
|
|
10842
|
+
const {
|
|
10843
|
+
Document,
|
|
10844
|
+
Packer,
|
|
10845
|
+
Paragraph,
|
|
10846
|
+
TextRun,
|
|
10847
|
+
ImageRun,
|
|
10848
|
+
HeadingLevel,
|
|
10849
|
+
Table,
|
|
10850
|
+
TableRow,
|
|
10851
|
+
TableCell,
|
|
10852
|
+
WidthType,
|
|
10853
|
+
BorderStyle,
|
|
10854
|
+
AlignmentType
|
|
10855
|
+
} = d;
|
|
10856
|
+
const HEADINGS = [
|
|
10857
|
+
HeadingLevel.HEADING_1,
|
|
10858
|
+
HeadingLevel.HEADING_2,
|
|
10859
|
+
HeadingLevel.HEADING_3,
|
|
10860
|
+
HeadingLevel.HEADING_4,
|
|
10861
|
+
HeadingLevel.HEADING_5,
|
|
10862
|
+
HeadingLevel.HEADING_6
|
|
10863
|
+
];
|
|
10864
|
+
const runs = (text) => inlineRuns(text).map((r) => new TextRun({
|
|
10865
|
+
text: r.text,
|
|
10866
|
+
...r.bold ? { bold: true } : {},
|
|
10867
|
+
...r.italic ? { italics: true } : {},
|
|
10868
|
+
...r.code ? { font: "Courier New" } : {}
|
|
10869
|
+
}));
|
|
10870
|
+
const tableOf = (rows) => new Table({
|
|
10871
|
+
width: { size: 100, type: WidthType.PERCENTAGE },
|
|
10872
|
+
rows: rows.map((r) => new TableRow({
|
|
10873
|
+
children: r.map((c) => new TableCell({ children: [new Paragraph({ children: runs(c) })] }))
|
|
10874
|
+
}))
|
|
10875
|
+
});
|
|
10876
|
+
const imageParagraph = (img) => {
|
|
10877
|
+
const scale = Math.min(1, DOCX_MAX_W / img.width, DOCX_MAX_H / img.height);
|
|
10878
|
+
return new Paragraph({
|
|
10879
|
+
children: [new ImageRun({
|
|
10880
|
+
type: img.type,
|
|
10881
|
+
data: img.bytes,
|
|
10882
|
+
transformation: { width: Math.round(img.width * scale), height: Math.round(img.height * scale) },
|
|
10883
|
+
...img.alt ? { altText: { name: img.alt, description: img.alt, title: img.alt } } : {}
|
|
10884
|
+
})]
|
|
10885
|
+
});
|
|
10886
|
+
};
|
|
10887
|
+
const asImage = async (text) => {
|
|
10888
|
+
const ref = matchImageLine(text);
|
|
10889
|
+
return ref ? await loadImage(ref, opts.loadImageBytes) : null;
|
|
10890
|
+
};
|
|
10891
|
+
const children = [];
|
|
10892
|
+
for (const b of parseBlocks(md)) {
|
|
10893
|
+
switch (b.t) {
|
|
10894
|
+
case "heading":
|
|
10895
|
+
children.push(new Paragraph({ heading: HEADINGS[Math.min(b.level, 6) - 1], children: runs(b.text) }));
|
|
10896
|
+
break;
|
|
10897
|
+
case "para": {
|
|
10898
|
+
const img = await asImage(b.text);
|
|
10899
|
+
children.push(img ? imageParagraph(img) : new Paragraph({ children: runs(b.text) }));
|
|
10900
|
+
break;
|
|
10901
|
+
}
|
|
10902
|
+
case "bullet": {
|
|
10903
|
+
const img = await asImage(b.text);
|
|
10904
|
+
children.push(img ? imageParagraph(img) : new Paragraph({
|
|
10905
|
+
children: runs(b.text),
|
|
10906
|
+
...b.ordered ? { numbering: { reference: "ol", level: 0 } } : { bullet: { level: 0 } }
|
|
10907
|
+
}));
|
|
10908
|
+
break;
|
|
10909
|
+
}
|
|
10910
|
+
case "code":
|
|
10911
|
+
for (const cl of b.lines) {
|
|
10912
|
+
children.push(new Paragraph({ children: [new TextRun({ text: cl || " ", font: "Courier New", size: 18 })] }));
|
|
10913
|
+
}
|
|
10914
|
+
break;
|
|
10915
|
+
case "quote":
|
|
10916
|
+
children.push(new Paragraph({ indent: { left: 360 }, children: [new TextRun({ text: stripInline(b.text), italics: true })] }));
|
|
10917
|
+
break;
|
|
10918
|
+
case "table":
|
|
10919
|
+
children.push(tableOf(b.rows));
|
|
10920
|
+
break;
|
|
10921
|
+
case "chart":
|
|
10922
|
+
if (b.spec.title) {
|
|
10923
|
+
children.push(new Paragraph({ children: [new TextRun({ text: b.spec.title, bold: true })] }));
|
|
10924
|
+
}
|
|
10925
|
+
children.push(tableOf(chartToTableRows(b.spec)));
|
|
10926
|
+
break;
|
|
10927
|
+
case "hr":
|
|
10928
|
+
children.push(new Paragraph({
|
|
10929
|
+
children: [],
|
|
10930
|
+
border: { bottom: { color: "CCCCCC", space: 1, style: BorderStyle.SINGLE, size: 6 } }
|
|
10931
|
+
}));
|
|
10932
|
+
break;
|
|
10933
|
+
}
|
|
10934
|
+
}
|
|
10935
|
+
const doc = new Document({
|
|
10936
|
+
numbering: {
|
|
10937
|
+
config: [{ reference: "ol", levels: [{ level: 0, format: "decimal", text: "%1.", alignment: AlignmentType.START }] }]
|
|
10938
|
+
},
|
|
10939
|
+
sections: [{ children: children.length ? children : [new Paragraph({ children: [new TextRun("")] })] }]
|
|
10940
|
+
});
|
|
10941
|
+
return new Uint8Array(await Packer.toArrayBuffer(doc));
|
|
10942
|
+
}
|
|
10943
|
+
function pptxConstructor(mod) {
|
|
10944
|
+
let c = mod;
|
|
10945
|
+
for (let i = 0; i < 3 && c && typeof c !== "function"; i++) c = c.default;
|
|
10946
|
+
if (typeof c !== "function") throw new Error("pptxgenjs did not export a constructor.");
|
|
10947
|
+
return c;
|
|
10948
|
+
}
|
|
10949
|
+
var SLIDE_H = 7.5;
|
|
10950
|
+
var CONTENT_X = 0.6;
|
|
10951
|
+
var CONTENT_W = 12.1;
|
|
10952
|
+
var CONTENT_BOTTOM = 0.4;
|
|
10953
|
+
async function renderPptx(md, opts = {}) {
|
|
10954
|
+
const PptxGen = pptxConstructor(await import('pptxgenjs'));
|
|
10955
|
+
const pptx = new PptxGen();
|
|
10956
|
+
pptx.layout = "LAYOUT_WIDE";
|
|
10957
|
+
const CHART_TYPES = {
|
|
10958
|
+
bar: pptx.ChartType["bar"],
|
|
10959
|
+
line: pptx.ChartType["line"],
|
|
10960
|
+
pie: pptx.ChartType["pie"],
|
|
10961
|
+
doughnut: pptx.ChartType["doughnut"],
|
|
10962
|
+
area: pptx.ChartType["area"],
|
|
10963
|
+
scatter: pptx.ChartType["scatter"]
|
|
10964
|
+
};
|
|
10965
|
+
const slides = splitSlides(md);
|
|
10966
|
+
const blank = { title: "", body: [""], images: [], charts: [] };
|
|
10967
|
+
for (const s of slides.length ? slides : [blank]) {
|
|
10968
|
+
const slide = pptx.addSlide();
|
|
10969
|
+
if (s.title) slide.addText(s.title, { x: 0.5, y: 0.35, w: 12.3, h: 0.9, fontSize: 28, bold: true, color: "1F2937" });
|
|
10970
|
+
const top = s.title ? 1.5 : 0.5;
|
|
10971
|
+
const pics = (await Promise.all(s.images.map((r) => loadImage(r, opts.loadImageBytes)))).filter((p) => p !== null);
|
|
10972
|
+
const visuals = [
|
|
10973
|
+
...s.charts.map((spec) => ({ kind: "chart", spec })),
|
|
10974
|
+
...pics.map((img) => ({ kind: "image", img }))
|
|
10975
|
+
];
|
|
10976
|
+
const bodyH = visuals.length ? 2.2 : 5.4;
|
|
10977
|
+
if (s.body.length) {
|
|
10978
|
+
slide.addText(
|
|
10979
|
+
s.body.map((t) => ({ text: t, options: { bullet: true, breakLine: true } })),
|
|
10980
|
+
{ x: CONTENT_X, y: top, w: CONTENT_W, h: bodyH, fontSize: 16, color: "374151", valign: "top" }
|
|
10981
|
+
);
|
|
10982
|
+
}
|
|
10983
|
+
if (visuals.length) {
|
|
10984
|
+
const y = top + (s.body.length ? bodyH + 0.2 : 0);
|
|
10985
|
+
const h = Math.max(1, SLIDE_H - CONTENT_BOTTOM - y);
|
|
10986
|
+
const gap = 0.3;
|
|
10987
|
+
const w = (CONTENT_W - gap * (visuals.length - 1)) / visuals.length;
|
|
10988
|
+
visuals.forEach((v, i) => {
|
|
10989
|
+
const x = CONTENT_X + i * (w + gap);
|
|
10990
|
+
if (v.kind === "image") {
|
|
10991
|
+
slide.addImage({
|
|
10992
|
+
data: `data:${v.img.mime};base64,${bytesToBase64(v.img.bytes)}`,
|
|
10993
|
+
x,
|
|
10994
|
+
y,
|
|
10995
|
+
w,
|
|
10996
|
+
h,
|
|
10997
|
+
// 'contain' keeps the aspect ratio inside the box — a stretched
|
|
10998
|
+
// render looks worse than a smaller correct one.
|
|
10999
|
+
sizing: { type: "contain", w, h },
|
|
11000
|
+
...v.img.alt ? { altText: v.img.alt } : {}
|
|
11001
|
+
});
|
|
11002
|
+
} else {
|
|
11003
|
+
slide.addChart(CHART_TYPES[v.spec.kind], chartData(v.spec), {
|
|
11004
|
+
x,
|
|
11005
|
+
y,
|
|
11006
|
+
w,
|
|
11007
|
+
h,
|
|
11008
|
+
showLegend: v.spec.series.length > 1 || v.spec.kind === "pie" || v.spec.kind === "doughnut",
|
|
11009
|
+
legendPos: "b",
|
|
11010
|
+
showTitle: !!v.spec.title,
|
|
11011
|
+
...v.spec.title ? { title: v.spec.title } : {},
|
|
11012
|
+
showValue: v.spec.kind === "pie" || v.spec.kind === "doughnut"
|
|
11013
|
+
});
|
|
11014
|
+
}
|
|
11015
|
+
});
|
|
11016
|
+
}
|
|
11017
|
+
}
|
|
11018
|
+
return new Uint8Array(await pptx.write({ outputType: "arraybuffer" }));
|
|
11019
|
+
}
|
|
11020
|
+
function chartData(spec) {
|
|
11021
|
+
const series = spec.kind === "pie" || spec.kind === "doughnut" ? spec.series.slice(0, 1) : spec.series;
|
|
11022
|
+
return series.map((s) => ({ name: s.name, labels: spec.categories, values: s.values }));
|
|
11023
|
+
}
|
|
11024
|
+
async function renderDocument(format, source, opts = {}) {
|
|
11025
|
+
if (format === "docx") return renderDocx(source, opts);
|
|
11026
|
+
if (format === "pptx") return renderPptx(source, opts);
|
|
11027
|
+
return renderXlsx(source, opts);
|
|
11028
|
+
}
|
|
11029
|
+
|
|
11030
|
+
// src/tools/generate-document.ts
|
|
11031
|
+
var SOURCE_HINT = {
|
|
11032
|
+
docx: "Markdown",
|
|
11033
|
+
pptx: "Markdown slides (separate slides with a --- rule; each slide starts with a heading)",
|
|
11034
|
+
xlsx: "CSV (one header row, then data rows)"
|
|
11035
|
+
};
|
|
11036
|
+
var GenerateDocumentTool = class extends BaseTool {
|
|
11037
|
+
/**
|
|
11038
|
+
* `readFile` is injected so the image loader is testable and so a host with a
|
|
11039
|
+
* different workspace (not plain disk) can supply its own reader.
|
|
11040
|
+
*/
|
|
11041
|
+
constructor(readFile = async (p) => new Uint8Array(await fs5__default.default.readFile(p))) {
|
|
11042
|
+
super();
|
|
11043
|
+
this.readFile = readFile;
|
|
11044
|
+
}
|
|
11045
|
+
readFile;
|
|
11046
|
+
name = "generate_document";
|
|
11047
|
+
description = "Create a REAL Microsoft Office file (.docx Word, .pptx PowerPoint, .xlsx Excel) in the workspace. You write the SOURCE and this tool renders the actual binary: Markdown for .docx, Markdown slides (separated by --- rules) for .pptx, CSV for .xlsx. ALWAYS use this instead of file_write for those three extensions \u2014 file_write stores your text verbatim, which Office reports as a corrupted file. Supports embedded images (reference a generated image as  on its own line) and real, data-accurate charts (a ```chart:bar / chart:line / chart:pie fenced block whose body is CSV). Use pdf_create for PDFs.";
|
|
11048
|
+
inputSchema = {
|
|
11049
|
+
type: "object",
|
|
11050
|
+
properties: {
|
|
11051
|
+
path: {
|
|
11052
|
+
type: "string",
|
|
11053
|
+
description: "Where to write the file, relative to the workspace root. The extension decides the format: .docx, .pptx or .xlsx."
|
|
11054
|
+
},
|
|
11055
|
+
content: {
|
|
11056
|
+
type: "string",
|
|
11057
|
+
description: 'The document source. Markdown for .docx; Markdown slides separated by --- rules for .pptx (each slide starts with a heading); CSV for .xlsx. A standalone  line embeds that image. A fenced ```chart:bar (or chart:line, chart:pie, chart:area, chart:doughnut, chart:scatter) block whose body is CSV \u2014 an optional "title: \u2026" line, then a header row of "<category>,<series1>,<series2>", then data rows \u2014 becomes a real chart.'
|
|
11058
|
+
},
|
|
11059
|
+
format: {
|
|
11060
|
+
type: "string",
|
|
11061
|
+
description: 'Optional override: "docx", "pptx" or "xlsx". Normally inferred from the path extension.'
|
|
11062
|
+
}
|
|
11063
|
+
},
|
|
11064
|
+
required: ["path", "content"]
|
|
11065
|
+
};
|
|
11066
|
+
isDangerous() {
|
|
11067
|
+
return true;
|
|
11068
|
+
}
|
|
11069
|
+
async execute(input, options) {
|
|
11070
|
+
const filePath = String(input["path"] ?? "").trim();
|
|
11071
|
+
if (!filePath) return 'Provide a "path" for the document, e.g. "report.docx".';
|
|
11072
|
+
const content = typeof input["content"] === "string" ? input["content"] : "";
|
|
11073
|
+
if (!content.trim()) return 'Provide "content" \u2014 the Markdown (docx/pptx) or CSV (xlsx) source for the document.';
|
|
11074
|
+
const requested = String(input["format"] ?? "").trim().toLowerCase().replace(/^\./, "");
|
|
11075
|
+
const ext = requested || fileExt(filePath);
|
|
11076
|
+
if (!isDocumentFormat(ext)) {
|
|
11077
|
+
return `generate_document renders .docx, .pptx and .xlsx. "${filePath}" is not one of those \u2014 use pdf_create for a PDF, or file_write for a text file.`;
|
|
11078
|
+
}
|
|
11079
|
+
const absPath = resolveInWorkspace(this.workspaceRoot, filePath);
|
|
11080
|
+
const embedded = [];
|
|
11081
|
+
const skipped = [];
|
|
11082
|
+
const loadImageBytes = async (url) => {
|
|
11083
|
+
if (/^(https?|ftp):\/\//i.test(url)) {
|
|
11084
|
+
skipped.push(`${url} (remote URL \u2014 download it into the workspace first)`);
|
|
11085
|
+
return null;
|
|
11086
|
+
}
|
|
11087
|
+
const local = url.replace(/^file:\/\//i, "").split("?")[0].split("#")[0];
|
|
11088
|
+
try {
|
|
11089
|
+
const bytes2 = await this.readFile(resolveInWorkspace(this.workspaceRoot, decodeURIComponent(local)));
|
|
11090
|
+
embedded.push(local);
|
|
11091
|
+
return bytes2;
|
|
11092
|
+
} catch {
|
|
11093
|
+
skipped.push(`${local} (not found in the workspace)`);
|
|
11094
|
+
return null;
|
|
11095
|
+
}
|
|
11096
|
+
};
|
|
11097
|
+
if (options.saveSnapshot) {
|
|
11098
|
+
try {
|
|
11099
|
+
const old = await fs5__default.default.readFile(absPath, "utf-8");
|
|
11100
|
+
await options.saveSnapshot(absPath, old);
|
|
11101
|
+
} catch {
|
|
11102
|
+
}
|
|
11103
|
+
}
|
|
11104
|
+
const bytes = await renderDocument(ext, content, { name: filePath, loadImageBytes });
|
|
11105
|
+
await fs5__default.default.mkdir(path25__default.default.dirname(absPath), { recursive: true });
|
|
11106
|
+
await fs5__default.default.writeFile(absPath, bytes);
|
|
11107
|
+
const charts = countCharts(content);
|
|
11108
|
+
return [
|
|
11109
|
+
`Wrote a real ${LABEL[ext]} file to ${filePath} (${bytes.length} bytes) from ${SOURCE_HINT[ext]}.`,
|
|
11110
|
+
embedded.length ? `Embedded ${embedded.length} image(s): ${embedded.join(", ")}.` : "",
|
|
11111
|
+
// Named, not counted: a dropped image is the failure the user actually
|
|
11112
|
+
// notices, and the model can only fix it if it is told which reference died.
|
|
11113
|
+
skipped.length ? `Could NOT embed ${skipped.length} image reference(s): ${skipped.join("; ")}.` : "",
|
|
11114
|
+
charts && ext === "pptx" ? `Rendered ${charts} chart block(s) as real, editable PowerPoint charts.` : "",
|
|
11115
|
+
charts && ext === "docx" ? `Rendered ${charts} chart block(s) as data tables \u2014 Word documents cannot hold a native chart object.` : "",
|
|
11116
|
+
charts && ext === "xlsx" ? `Wrote ${charts} chart block(s) onto their own worksheets as real rows the user can chart in Excel.` : ""
|
|
11117
|
+
].filter(Boolean).join("\n");
|
|
11118
|
+
}
|
|
11119
|
+
};
|
|
11120
|
+
var LABEL = { docx: "Word", pptx: "PowerPoint", xlsx: "Excel" };
|
|
11121
|
+
function countCharts(source) {
|
|
11122
|
+
return (source.match(/^\s*```+\s*chart\s*:/gim) ?? []).length;
|
|
11123
|
+
}
|
|
11124
|
+
function buildDocumentTools(readFile) {
|
|
11125
|
+
return readFile ? [new GenerateDocumentTool(readFile)] : [];
|
|
11126
|
+
}
|
|
10353
11127
|
|
|
10354
11128
|
// src/core/run-breaker.ts
|
|
10355
11129
|
var DEFAULT_FAILURE_THRESHOLD = 3;
|
|
@@ -10672,35 +11446,6 @@ ${[e.stdout, e.stderr].filter(Boolean).join("\n").trim()}`;
|
|
|
10672
11446
|
}
|
|
10673
11447
|
}
|
|
10674
11448
|
};
|
|
10675
|
-
var WorkspaceSandboxError = class extends Error {
|
|
10676
|
-
constructor(attempted, workspaceRoot) {
|
|
10677
|
-
super(`Refusing access to "${attempted}" \u2014 outside workspace root "${workspaceRoot}".`);
|
|
10678
|
-
this.name = "WorkspaceSandboxError";
|
|
10679
|
-
}
|
|
10680
|
-
};
|
|
10681
|
-
function resolveInWorkspace(workspaceRoot, input) {
|
|
10682
|
-
if (typeof input !== "string" || input.length === 0) {
|
|
10683
|
-
throw new WorkspaceSandboxError(String(input), workspaceRoot);
|
|
10684
|
-
}
|
|
10685
|
-
const root = path24__default.default.resolve(workspaceRoot);
|
|
10686
|
-
const abs = path24__default.default.isAbsolute(input) ? path24__default.default.resolve(input) : path24__default.default.resolve(root, input);
|
|
10687
|
-
const rel = path24__default.default.relative(root, abs);
|
|
10688
|
-
if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path24__default.default.isAbsolute(rel)) {
|
|
10689
|
-
throw new WorkspaceSandboxError(input, root);
|
|
10690
|
-
}
|
|
10691
|
-
try {
|
|
10692
|
-
const real = fs23__default.default.realpathSync(abs);
|
|
10693
|
-
const realRel = path24__default.default.relative(root, real);
|
|
10694
|
-
if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path24__default.default.isAbsolute(realRel))) {
|
|
10695
|
-
throw new WorkspaceSandboxError(input, root);
|
|
10696
|
-
}
|
|
10697
|
-
} catch (e) {
|
|
10698
|
-
if (e instanceof WorkspaceSandboxError) throw e;
|
|
10699
|
-
}
|
|
10700
|
-
return abs;
|
|
10701
|
-
}
|
|
10702
|
-
|
|
10703
|
-
// src/tools/file.ts
|
|
10704
11449
|
var FileReadTool = class extends BaseTool {
|
|
10705
11450
|
name = "file_read";
|
|
10706
11451
|
description = "Read the contents of a file. Returns the file content as a string.";
|
|
@@ -10718,7 +11463,7 @@ var FileReadTool = class extends BaseTool {
|
|
|
10718
11463
|
const absPath = resolveInWorkspace(this.workspaceRoot, filePath);
|
|
10719
11464
|
const offset = input["offset"] ?? 1;
|
|
10720
11465
|
const limit = input["limit"];
|
|
10721
|
-
const content = await
|
|
11466
|
+
const content = await fs5__default.default.readFile(absPath, "utf-8");
|
|
10722
11467
|
const lines = content.split("\n");
|
|
10723
11468
|
const start = Math.max(0, offset - 1);
|
|
10724
11469
|
const end = limit ? start + limit : lines.length;
|
|
@@ -10747,13 +11492,13 @@ var FileWriteTool = class extends BaseTool {
|
|
|
10747
11492
|
const content = input["content"];
|
|
10748
11493
|
if (options.saveSnapshot) {
|
|
10749
11494
|
try {
|
|
10750
|
-
const oldContent = await
|
|
11495
|
+
const oldContent = await fs5__default.default.readFile(absPath, "utf-8");
|
|
10751
11496
|
await options.saveSnapshot(absPath, oldContent);
|
|
10752
11497
|
} catch {
|
|
10753
11498
|
}
|
|
10754
11499
|
}
|
|
10755
|
-
await
|
|
10756
|
-
await
|
|
11500
|
+
await fs5__default.default.mkdir(path25__default.default.dirname(absPath), { recursive: true });
|
|
11501
|
+
await fs5__default.default.writeFile(absPath, content, "utf-8");
|
|
10757
11502
|
return `Written ${content.length} characters to ${filePath}`;
|
|
10758
11503
|
}
|
|
10759
11504
|
};
|
|
@@ -10779,7 +11524,7 @@ var FileEditTool = class extends BaseTool {
|
|
|
10779
11524
|
const oldString = input["old_string"];
|
|
10780
11525
|
const newString = input["new_string"];
|
|
10781
11526
|
const replaceAll = input["replace_all"] ?? false;
|
|
10782
|
-
const rawContent = await
|
|
11527
|
+
const rawContent = await fs5__default.default.readFile(absPath, "utf-8");
|
|
10783
11528
|
if (options.saveSnapshot) {
|
|
10784
11529
|
await options.saveSnapshot(absPath, rawContent);
|
|
10785
11530
|
}
|
|
@@ -10791,7 +11536,7 @@ var FileEditTool = class extends BaseTool {
|
|
|
10791
11536
|
);
|
|
10792
11537
|
}
|
|
10793
11538
|
const updated = replaceAll ? content.split(normalizedOld).join(newString) : content.replace(normalizedOld, newString);
|
|
10794
|
-
await
|
|
11539
|
+
await fs5__default.default.writeFile(absPath, updated, "utf-8");
|
|
10795
11540
|
const count = replaceAll ? content.split(normalizedOld).length - 1 : 1;
|
|
10796
11541
|
return `Replaced ${count} occurrence(s) in ${filePath}`;
|
|
10797
11542
|
}
|
|
@@ -10814,12 +11559,12 @@ var FileDeleteTool = class extends BaseTool {
|
|
|
10814
11559
|
const absPath = resolveInWorkspace(this.workspaceRoot, filePath);
|
|
10815
11560
|
if (options.saveSnapshot) {
|
|
10816
11561
|
try {
|
|
10817
|
-
const oldContent = await
|
|
11562
|
+
const oldContent = await fs5__default.default.readFile(absPath, "utf-8");
|
|
10818
11563
|
await options.saveSnapshot(absPath, oldContent);
|
|
10819
11564
|
} catch {
|
|
10820
11565
|
}
|
|
10821
11566
|
}
|
|
10822
|
-
await
|
|
11567
|
+
await fs5__default.default.rm(absPath, { recursive: false });
|
|
10823
11568
|
return `Deleted ${filePath}`;
|
|
10824
11569
|
}
|
|
10825
11570
|
};
|
|
@@ -10836,7 +11581,7 @@ var FileListTool = class extends BaseTool {
|
|
|
10836
11581
|
async execute(input, _options) {
|
|
10837
11582
|
const inputPath = input["path"] || ".";
|
|
10838
11583
|
const absPath = resolveInWorkspace(this.workspaceRoot, inputPath);
|
|
10839
|
-
const entries = await
|
|
11584
|
+
const entries = await fs5__default.default.readdir(absPath, { withFileTypes: true });
|
|
10840
11585
|
return entries.map((e) => `${e.isDirectory() ? "[DIR] " : " "}${e.name}`).join("\n") || "(empty directory)";
|
|
10841
11586
|
}
|
|
10842
11587
|
};
|
|
@@ -11249,8 +11994,8 @@ var ImageAnalyzeTool = class extends BaseTool {
|
|
|
11249
11994
|
}
|
|
11250
11995
|
};
|
|
11251
11996
|
async function fileToImageAttachment(filePath) {
|
|
11252
|
-
const data = await
|
|
11253
|
-
const ext =
|
|
11997
|
+
const data = await fs5__default.default.readFile(filePath);
|
|
11998
|
+
const ext = path25__default.default.extname(filePath).toLowerCase();
|
|
11254
11999
|
const mimeMap = {
|
|
11255
12000
|
".jpg": "image/jpeg",
|
|
11256
12001
|
".jpeg": "image/jpeg",
|
|
@@ -11284,14 +12029,14 @@ var PDFCreateTool = class extends BaseTool {
|
|
|
11284
12029
|
const filePath = input["path"];
|
|
11285
12030
|
const content = input["content"];
|
|
11286
12031
|
const title = input["title"];
|
|
11287
|
-
const dir =
|
|
11288
|
-
if (!
|
|
11289
|
-
|
|
12032
|
+
const dir = path25__default.default.dirname(filePath);
|
|
12033
|
+
if (!fs24__default.default.existsSync(dir)) {
|
|
12034
|
+
fs24__default.default.mkdirSync(dir, { recursive: true });
|
|
11290
12035
|
}
|
|
11291
12036
|
return new Promise((resolve2, reject) => {
|
|
11292
12037
|
try {
|
|
11293
12038
|
const doc = new PDFDocument__default.default({ margin: 50 });
|
|
11294
|
-
const stream =
|
|
12039
|
+
const stream = fs24__default.default.createWriteStream(filePath);
|
|
11295
12040
|
doc.pipe(stream);
|
|
11296
12041
|
if (title) {
|
|
11297
12042
|
doc.info["Title"] = title;
|
|
@@ -11369,22 +12114,22 @@ var CodeInterpreterTool = class extends BaseTool {
|
|
|
11369
12114
|
}
|
|
11370
12115
|
cmdPrefix = NODE_CMD;
|
|
11371
12116
|
}
|
|
11372
|
-
const tmpDir =
|
|
11373
|
-
if (!
|
|
11374
|
-
|
|
12117
|
+
const tmpDir = path25__default.default.join(this.workspaceRoot, ".cascade", "tmp");
|
|
12118
|
+
if (!fs24__default.default.existsSync(tmpDir)) {
|
|
12119
|
+
fs24__default.default.mkdirSync(tmpDir, { recursive: true });
|
|
11375
12120
|
}
|
|
11376
12121
|
const extension = language === "python" ? "py" : "js";
|
|
11377
12122
|
const fileName = `intp_${crypto4.randomUUID().slice(0, 8)}.${extension}`;
|
|
11378
|
-
const filePath =
|
|
11379
|
-
|
|
12123
|
+
const filePath = path25__default.default.join(tmpDir, fileName);
|
|
12124
|
+
fs24__default.default.writeFileSync(filePath, code, "utf-8");
|
|
11380
12125
|
const execArgs = [filePath, ...args];
|
|
11381
12126
|
return new Promise((resolve2) => {
|
|
11382
12127
|
const startMs = Date.now();
|
|
11383
12128
|
child_process.execFile(cmdPrefix, execArgs, { cwd: this.workspaceRoot, timeout: 3e4 }, (error, stdout, stderr) => {
|
|
11384
12129
|
const duration = Date.now() - startMs;
|
|
11385
12130
|
try {
|
|
11386
|
-
if (
|
|
11387
|
-
|
|
12131
|
+
if (fs24__default.default.existsSync(filePath)) {
|
|
12132
|
+
fs24__default.default.unlinkSync(filePath);
|
|
11388
12133
|
}
|
|
11389
12134
|
} catch (cleanupErr) {
|
|
11390
12135
|
console.error(`Failed to cleanup interpreter script ${filePath}:`, cleanupErr);
|
|
@@ -11696,7 +12441,7 @@ var GlobTool = class extends BaseTool {
|
|
|
11696
12441
|
};
|
|
11697
12442
|
async execute(input, _options) {
|
|
11698
12443
|
const pattern = input["pattern"];
|
|
11699
|
-
const searchPath = input["path"] ?
|
|
12444
|
+
const searchPath = input["path"] ? path25__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
|
|
11700
12445
|
const matches = await glob.glob(pattern, {
|
|
11701
12446
|
cwd: searchPath,
|
|
11702
12447
|
ignore: ["node_modules/**", ".git/**", "dist/**", "build/**"],
|
|
@@ -11709,7 +12454,7 @@ var GlobTool = class extends BaseTool {
|
|
|
11709
12454
|
const withMtime = await Promise.all(
|
|
11710
12455
|
matches.map(async (rel) => {
|
|
11711
12456
|
try {
|
|
11712
|
-
const stat = await
|
|
12457
|
+
const stat = await fs5__default.default.stat(path25__default.default.join(searchPath, rel));
|
|
11713
12458
|
return { rel, mtime: stat.mtimeMs };
|
|
11714
12459
|
} catch {
|
|
11715
12460
|
return { rel, mtime: 0 };
|
|
@@ -11758,7 +12503,7 @@ var GrepTool = class extends BaseTool {
|
|
|
11758
12503
|
};
|
|
11759
12504
|
async execute(input, _options) {
|
|
11760
12505
|
const pattern = input["pattern"];
|
|
11761
|
-
const searchPath = input["path"] ?
|
|
12506
|
+
const searchPath = input["path"] ? path25__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
|
|
11762
12507
|
const globPattern = input["glob"];
|
|
11763
12508
|
const outputMode = input["output_mode"] ?? "content";
|
|
11764
12509
|
const context = input["context"] ?? 0;
|
|
@@ -11812,15 +12557,15 @@ var GrepTool = class extends BaseTool {
|
|
|
11812
12557
|
nodir: true
|
|
11813
12558
|
});
|
|
11814
12559
|
} catch {
|
|
11815
|
-
files = [
|
|
12560
|
+
files = [path25__default.default.relative(searchPath, searchPath) || "."];
|
|
11816
12561
|
}
|
|
11817
12562
|
const results = [];
|
|
11818
12563
|
let totalCount = 0;
|
|
11819
12564
|
for (const rel of files) {
|
|
11820
|
-
const abs =
|
|
12565
|
+
const abs = path25__default.default.join(searchPath, rel);
|
|
11821
12566
|
let content;
|
|
11822
12567
|
try {
|
|
11823
|
-
content = await
|
|
12568
|
+
content = await fs5__default.default.readFile(abs, "utf-8");
|
|
11824
12569
|
} catch {
|
|
11825
12570
|
continue;
|
|
11826
12571
|
}
|
|
@@ -12204,10 +12949,10 @@ var ToolRegistry = class extends EventEmitter__default.default {
|
|
|
12204
12949
|
}
|
|
12205
12950
|
isIgnored(filePath) {
|
|
12206
12951
|
if (!filePath) return false;
|
|
12207
|
-
const abs =
|
|
12208
|
-
const rel =
|
|
12209
|
-
if (!rel || rel.startsWith("..") ||
|
|
12210
|
-
const posixRel = rel.split(
|
|
12952
|
+
const abs = path25__default.default.resolve(this.workspaceRoot, filePath);
|
|
12953
|
+
const rel = path25__default.default.relative(this.workspaceRoot, abs);
|
|
12954
|
+
if (!rel || rel.startsWith("..") || path25__default.default.isAbsolute(rel)) return true;
|
|
12955
|
+
const posixRel = rel.split(path25__default.default.sep).join("/");
|
|
12211
12956
|
return this.ignoreMatcher.ignores(posixRel);
|
|
12212
12957
|
}
|
|
12213
12958
|
};
|
|
@@ -12441,22 +13186,22 @@ var FileMcpOAuthStore = class {
|
|
|
12441
13186
|
filePath;
|
|
12442
13187
|
load() {
|
|
12443
13188
|
try {
|
|
12444
|
-
return JSON.parse(
|
|
13189
|
+
return JSON.parse(fs24__default.default.readFileSync(this.filePath, "utf-8"));
|
|
12445
13190
|
} catch {
|
|
12446
13191
|
return void 0;
|
|
12447
13192
|
}
|
|
12448
13193
|
}
|
|
12449
13194
|
save(state) {
|
|
12450
|
-
|
|
12451
|
-
|
|
13195
|
+
fs24__default.default.mkdirSync(path25__default.default.dirname(this.filePath), { recursive: true, mode: 448 });
|
|
13196
|
+
fs24__default.default.writeFileSync(this.filePath, JSON.stringify(state, null, 2), { mode: 384 });
|
|
12452
13197
|
try {
|
|
12453
|
-
|
|
13198
|
+
fs24__default.default.chmodSync(this.filePath, 384);
|
|
12454
13199
|
} catch {
|
|
12455
13200
|
}
|
|
12456
13201
|
}
|
|
12457
13202
|
clear() {
|
|
12458
13203
|
try {
|
|
12459
|
-
|
|
13204
|
+
fs24__default.default.rmSync(this.filePath);
|
|
12460
13205
|
} catch {
|
|
12461
13206
|
}
|
|
12462
13207
|
}
|
|
@@ -13495,7 +14240,7 @@ var TaskAnalyzer = class {
|
|
|
13495
14240
|
analysisCache.clear();
|
|
13496
14241
|
}
|
|
13497
14242
|
};
|
|
13498
|
-
var DEFAULT_STATS_FILE =
|
|
14243
|
+
var DEFAULT_STATS_FILE = path25__default.default.join(os6__default.default.homedir(), ".cascade", "model-perf.json");
|
|
13499
14244
|
var ModelPerformanceTracker = class {
|
|
13500
14245
|
stats = /* @__PURE__ */ new Map();
|
|
13501
14246
|
featureStats = /* @__PURE__ */ new Map();
|
|
@@ -13515,7 +14260,7 @@ var ModelPerformanceTracker = class {
|
|
|
13515
14260
|
if (this.loaded) return;
|
|
13516
14261
|
this.loaded = true;
|
|
13517
14262
|
try {
|
|
13518
|
-
const raw = await
|
|
14263
|
+
const raw = await fs5__default.default.readFile(this.statsFile, "utf-8");
|
|
13519
14264
|
const parsed = JSON.parse(raw);
|
|
13520
14265
|
if (parsed.models) {
|
|
13521
14266
|
for (const [key, stat] of Object.entries(parsed.models)) this.stats.set(key, stat);
|
|
@@ -13535,15 +14280,15 @@ var ModelPerformanceTracker = class {
|
|
|
13535
14280
|
async save() {
|
|
13536
14281
|
if (this.readOnly) return;
|
|
13537
14282
|
try {
|
|
13538
|
-
await
|
|
14283
|
+
await fs5__default.default.mkdir(path25__default.default.dirname(this.statsFile), { recursive: true });
|
|
13539
14284
|
const modelsObj = {};
|
|
13540
14285
|
const featuresObj = {};
|
|
13541
14286
|
for (const [key, stat] of this.stats) modelsObj[key] = stat;
|
|
13542
14287
|
for (const [key, stat] of this.featureStats) featuresObj[key] = stat;
|
|
13543
14288
|
const json = JSON.stringify({ models: modelsObj, features: featuresObj }, null, 2);
|
|
13544
14289
|
const tmp = `${this.statsFile}.${process.pid}.${Date.now()}.tmp`;
|
|
13545
|
-
await
|
|
13546
|
-
await
|
|
14290
|
+
await fs5__default.default.writeFile(tmp, json, "utf-8");
|
|
14291
|
+
await fs5__default.default.rename(tmp, this.statsFile);
|
|
13547
14292
|
} catch {
|
|
13548
14293
|
}
|
|
13549
14294
|
}
|
|
@@ -14083,9 +14828,9 @@ Required capability: ${description.slice(0, 300)}`;
|
|
|
14083
14828
|
* any dangerous action, so a silently-reloaded tool can't act without approval. */
|
|
14084
14829
|
async loadPersistedTools() {
|
|
14085
14830
|
if (!this.workspacePath || !this.persistEnabled) return;
|
|
14086
|
-
const file =
|
|
14831
|
+
const file = path25__default.default.join(this.workspacePath, ".cascade", DYNAMIC_TOOLS_FILE);
|
|
14087
14832
|
try {
|
|
14088
|
-
const raw = await
|
|
14833
|
+
const raw = await fs5__default.default.readFile(file, "utf-8");
|
|
14089
14834
|
const specs = JSON.parse(raw);
|
|
14090
14835
|
if (!Array.isArray(specs)) return;
|
|
14091
14836
|
let loaded = 0;
|
|
@@ -14107,11 +14852,11 @@ Required capability: ${description.slice(0, 300)}`;
|
|
|
14107
14852
|
}
|
|
14108
14853
|
async persist() {
|
|
14109
14854
|
if (!this.workspacePath || !this.persistEnabled) return;
|
|
14110
|
-
const dir =
|
|
14111
|
-
const file =
|
|
14855
|
+
const dir = path25__default.default.join(this.workspacePath, ".cascade");
|
|
14856
|
+
const file = path25__default.default.join(dir, DYNAMIC_TOOLS_FILE);
|
|
14112
14857
|
try {
|
|
14113
|
-
await
|
|
14114
|
-
await
|
|
14858
|
+
await fs5__default.default.mkdir(dir, { recursive: true });
|
|
14859
|
+
await fs5__default.default.writeFile(file, JSON.stringify(Array.from(this.specs.values()), null, 2), "utf-8");
|
|
14115
14860
|
} catch (err) {
|
|
14116
14861
|
this.log(`[tool-creator] Failed to persist tools: ${err instanceof Error ? err.message : String(err)}`);
|
|
14117
14862
|
}
|
|
@@ -14128,12 +14873,12 @@ var WorldStateDB = class {
|
|
|
14128
14873
|
constructor(workspacePath, debugMode = false) {
|
|
14129
14874
|
this.workspacePath = workspacePath;
|
|
14130
14875
|
this.debugMode = debugMode;
|
|
14131
|
-
const cascadeDir =
|
|
14132
|
-
if (!
|
|
14133
|
-
|
|
14876
|
+
const cascadeDir = path25__default.default.join(workspacePath, ".cascade");
|
|
14877
|
+
if (!fs24__default.default.existsSync(cascadeDir)) {
|
|
14878
|
+
fs24__default.default.mkdirSync(cascadeDir, { recursive: true });
|
|
14134
14879
|
}
|
|
14135
|
-
this.keyPath =
|
|
14136
|
-
this.dbPath =
|
|
14880
|
+
this.keyPath = path25__default.default.join(cascadeDir, "world_state.key");
|
|
14881
|
+
this.dbPath = path25__default.default.join(cascadeDir, "world_state.db");
|
|
14137
14882
|
this.initEncryptionKey();
|
|
14138
14883
|
this.db = new Database__default.default(this.dbPath);
|
|
14139
14884
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -14190,11 +14935,11 @@ var WorldStateDB = class {
|
|
|
14190
14935
|
).run(e, r, row.encrypted_value, row.source_worker, row.timestamp, validTo, change);
|
|
14191
14936
|
}
|
|
14192
14937
|
initEncryptionKey() {
|
|
14193
|
-
if (
|
|
14194
|
-
this.encryptionKey =
|
|
14938
|
+
if (fs24__default.default.existsSync(this.keyPath)) {
|
|
14939
|
+
this.encryptionKey = fs24__default.default.readFileSync(this.keyPath);
|
|
14195
14940
|
} else {
|
|
14196
14941
|
this.encryptionKey = crypto4__default.default.randomBytes(32);
|
|
14197
|
-
|
|
14942
|
+
fs24__default.default.writeFileSync(this.keyPath, this.encryptionKey);
|
|
14198
14943
|
}
|
|
14199
14944
|
}
|
|
14200
14945
|
encrypt(text) {
|
|
@@ -14480,9 +15225,9 @@ var WorldStateDB = class {
|
|
|
14480
15225
|
dumpDebugIfNeeded() {
|
|
14481
15226
|
if (!this.debugMode) return;
|
|
14482
15227
|
try {
|
|
14483
|
-
const dumpPath =
|
|
15228
|
+
const dumpPath = path25__default.default.join(os6__default.default.tmpdir(), "cascade_world_state_debug.json");
|
|
14484
15229
|
const entries = this.getAllEntries();
|
|
14485
|
-
|
|
15230
|
+
fs24__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
|
|
14486
15231
|
} catch (err) {
|
|
14487
15232
|
console.error("Failed to dump debug world state", err);
|
|
14488
15233
|
}
|
|
@@ -14537,9 +15282,9 @@ var CascadeIgnore = class {
|
|
|
14537
15282
|
]);
|
|
14538
15283
|
}
|
|
14539
15284
|
async load(workspacePath) {
|
|
14540
|
-
const filePath =
|
|
15285
|
+
const filePath = path25__default.default.join(workspacePath, ".cascadeignore");
|
|
14541
15286
|
try {
|
|
14542
|
-
const content = await
|
|
15287
|
+
const content = await fs5__default.default.readFile(filePath, "utf-8");
|
|
14543
15288
|
const lines = content.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
|
|
14544
15289
|
this.ig.add(lines);
|
|
14545
15290
|
this.loaded = true;
|
|
@@ -14548,7 +15293,7 @@ var CascadeIgnore = class {
|
|
|
14548
15293
|
}
|
|
14549
15294
|
isIgnored(filePath, workspacePath) {
|
|
14550
15295
|
try {
|
|
14551
|
-
const relative = workspacePath ?
|
|
15296
|
+
const relative = workspacePath ? path25__default.default.relative(workspacePath, filePath) : filePath;
|
|
14552
15297
|
return this.ig.ignores(relative);
|
|
14553
15298
|
} catch {
|
|
14554
15299
|
return false;
|
|
@@ -14891,7 +15636,7 @@ var WorkspaceIndex = class {
|
|
|
14891
15636
|
isIgnored;
|
|
14892
15637
|
chunker;
|
|
14893
15638
|
constructor(opts) {
|
|
14894
|
-
this.root =
|
|
15639
|
+
this.root = path25__default.default.resolve(opts.root);
|
|
14895
15640
|
this.db = opts.db;
|
|
14896
15641
|
this.namespace = opts.namespace ?? "code";
|
|
14897
15642
|
this.extensions = new Set((opts.extensions ?? DEFAULT_EXTENSIONS).map((e) => e.replace(/^\./, "").toLowerCase()));
|
|
@@ -14918,19 +15663,19 @@ var WorkspaceIndex = class {
|
|
|
14918
15663
|
const walk = async (dir) => {
|
|
14919
15664
|
let entries;
|
|
14920
15665
|
try {
|
|
14921
|
-
entries = await
|
|
15666
|
+
entries = await fs5__default.default.readdir(dir, { withFileTypes: true });
|
|
14922
15667
|
} catch {
|
|
14923
15668
|
return;
|
|
14924
15669
|
}
|
|
14925
15670
|
for (const entry of entries) {
|
|
14926
|
-
const abs =
|
|
15671
|
+
const abs = path25__default.default.join(dir, entry.name);
|
|
14927
15672
|
if (entry.isDirectory()) {
|
|
14928
15673
|
if (HARD_SKIP_DIRS.has(entry.name) || this.isIgnored(abs)) continue;
|
|
14929
15674
|
await walk(abs);
|
|
14930
15675
|
} else if (entry.isFile()) {
|
|
14931
|
-
const ext =
|
|
15676
|
+
const ext = path25__default.default.extname(entry.name).slice(1).toLowerCase();
|
|
14932
15677
|
if (!this.extensions.has(ext) || this.isIgnored(abs)) continue;
|
|
14933
|
-
out.push(
|
|
15678
|
+
out.push(path25__default.default.relative(this.root, abs));
|
|
14934
15679
|
}
|
|
14935
15680
|
}
|
|
14936
15681
|
};
|
|
@@ -14960,10 +15705,10 @@ var WorkspaceIndex = class {
|
|
|
14960
15705
|
const entries = [];
|
|
14961
15706
|
for (const rel of rels) {
|
|
14962
15707
|
try {
|
|
14963
|
-
const abs =
|
|
14964
|
-
const stat = await
|
|
15708
|
+
const abs = path25__default.default.join(this.root, rel);
|
|
15709
|
+
const stat = await fs5__default.default.stat(abs);
|
|
14965
15710
|
if (stat.size > this.maxFileBytes) continue;
|
|
14966
|
-
const buf = await
|
|
15711
|
+
const buf = await fs5__default.default.readFile(abs);
|
|
14967
15712
|
if (buf.includes(0)) continue;
|
|
14968
15713
|
entries.push({ path: rel, hash: hashContent(buf), text: buf.toString("utf8") });
|
|
14969
15714
|
} catch {
|
|
@@ -15532,7 +16277,7 @@ var Cascade = class _Cascade extends EventEmitter__default.default {
|
|
|
15532
16277
|
this.toolRegistry = new ToolRegistry(this.config.tools, workspacePath);
|
|
15533
16278
|
try {
|
|
15534
16279
|
this.router.setDeadModelStore(new DeadModelStore(
|
|
15535
|
-
fileDeadModelPersistence(
|
|
16280
|
+
fileDeadModelPersistence(path25__default.default.join(workspacePath, ".cascade", "dead-models.json"))
|
|
15536
16281
|
));
|
|
15537
16282
|
} catch {
|
|
15538
16283
|
}
|
|
@@ -15540,6 +16285,7 @@ var Cascade = class _Cascade extends EventEmitter__default.default {
|
|
|
15540
16285
|
(this.config.providers ?? []).map((p) => p.type)
|
|
15541
16286
|
);
|
|
15542
16287
|
this.registerMediaTools(workspacePath);
|
|
16288
|
+
this.registerDocumentTools(workspacePath);
|
|
15543
16289
|
this.telemetry = config.telemetry?.enabled ? new Telemetry(config.telemetry, config.telemetry.distinctId ?? "anonymous") : noopTelemetry;
|
|
15544
16290
|
this.worldStateDB = new WorldStateDB(this.workspacePath, this.config.workspace?.debugWorldState ?? false);
|
|
15545
16291
|
this.router.setWorldStateDB(this.worldStateDB);
|
|
@@ -15619,8 +16365,8 @@ var Cascade = class _Cascade extends EventEmitter__default.default {
|
|
|
15619
16365
|
this.emit("log", { level: "warn", message: "Code index enabled but no embeddings-capable provider is configured \u2014 skipping." });
|
|
15620
16366
|
return;
|
|
15621
16367
|
}
|
|
15622
|
-
const dbPath = ci.dbPath ||
|
|
15623
|
-
|
|
16368
|
+
const dbPath = ci.dbPath || path25__default.default.join(this.workspacePath, ".cascade", "code-index.db");
|
|
16369
|
+
fs24__default.default.mkdirSync(path25__default.default.dirname(dbPath), { recursive: true });
|
|
15624
16370
|
const db = new Database__default.default(dbPath);
|
|
15625
16371
|
db.pragma("journal_mode = WAL");
|
|
15626
16372
|
const complete = chatCompleterFromProviders(this.config.providers);
|
|
@@ -15960,6 +16706,35 @@ ${input}` }],
|
|
|
15960
16706
|
this.toolRegistry.register(tool);
|
|
15961
16707
|
}
|
|
15962
16708
|
}
|
|
16709
|
+
/**
|
|
16710
|
+
* Register `generate_document` where there is a real workspace to write into.
|
|
16711
|
+
*
|
|
16712
|
+
* A `.docx`/`.pptx`/`.xlsx` is a ZIP of OOXML parts, so `file_write` — which
|
|
16713
|
+
* does exactly what it says and writes the model's text verbatim — produced
|
|
16714
|
+
* files Word/Excel/PowerPoint correctly reported as corrupted. This tool owns
|
|
16715
|
+
* the conversion (see tools/generate-document.ts for why it isn't folded into
|
|
16716
|
+
* file_write).
|
|
16717
|
+
*
|
|
16718
|
+
* Registered OUTSIDE `enabledTools` for the same reason the media tools are:
|
|
16719
|
+
* that allowlist is a blast-radius control for tools that reach the machine
|
|
16720
|
+
* or the network, and this one only writes the artifact the user asked for,
|
|
16721
|
+
* into the workspace the run already owns. `disabledTools` still removes it.
|
|
16722
|
+
* A host with no filesystem (the cloud) has its own browser-side exporter and
|
|
16723
|
+
* simply never reaches this path — `buildDocumentTools` returns nothing
|
|
16724
|
+
* without a file reader, mirroring `transcribe_audio`.
|
|
16725
|
+
*/
|
|
16726
|
+
registerDocumentTools(workspacePath) {
|
|
16727
|
+
const denied = new Set(this.config.tools?.disabledTools ?? []);
|
|
16728
|
+
const tools = buildDocumentTools(async (p) => {
|
|
16729
|
+
const fsp = await import('fs/promises');
|
|
16730
|
+
return new Uint8Array(await fsp.readFile(p));
|
|
16731
|
+
});
|
|
16732
|
+
for (const tool of tools) {
|
|
16733
|
+
if (denied.has(tool.name)) continue;
|
|
16734
|
+
tool.setWorkspaceRoot(workspacePath);
|
|
16735
|
+
this.toolRegistry.register(tool);
|
|
16736
|
+
}
|
|
16737
|
+
}
|
|
15963
16738
|
/**
|
|
15964
16739
|
* Supply per-model thumbs-up/down counts so Auto routing can weigh how models
|
|
15965
16740
|
* have actually performed for this user, on top of public benchmarks.
|
|
@@ -16908,7 +17683,7 @@ var Keystore = class {
|
|
|
16908
17683
|
const creds = await this.keytar.findCredentials(KEYTAR_SERVICE);
|
|
16909
17684
|
this.cache = Object.fromEntries(creds.map((c) => [c.account, c.password]));
|
|
16910
17685
|
this.backend = "keytar";
|
|
16911
|
-
if (password &&
|
|
17686
|
+
if (password && fs24__default.default.existsSync(this.storePath)) {
|
|
16912
17687
|
try {
|
|
16913
17688
|
const fileEntries = this.decryptFile(password);
|
|
16914
17689
|
for (const [k, v] of Object.entries(fileEntries)) {
|
|
@@ -16927,7 +17702,7 @@ var Keystore = class {
|
|
|
16927
17702
|
"Keystore unlock requires a password because the OS keychain (keytar) is not available on this system."
|
|
16928
17703
|
);
|
|
16929
17704
|
}
|
|
16930
|
-
if (!
|
|
17705
|
+
if (!fs24__default.default.existsSync(this.storePath)) {
|
|
16931
17706
|
const salt = crypto4__default.default.randomBytes(SALT_LEN);
|
|
16932
17707
|
this.masterKey = this.deriveKey(password, salt);
|
|
16933
17708
|
this.writeWithSalt({}, salt);
|
|
@@ -16941,7 +17716,7 @@ var Keystore = class {
|
|
|
16941
17716
|
}
|
|
16942
17717
|
/** Synchronous legacy unlock kept for AES-only environments. */
|
|
16943
17718
|
unlockSync(password) {
|
|
16944
|
-
if (!
|
|
17719
|
+
if (!fs24__default.default.existsSync(this.storePath)) {
|
|
16945
17720
|
const salt = crypto4__default.default.randomBytes(SALT_LEN);
|
|
16946
17721
|
this.masterKey = this.deriveKey(password, salt);
|
|
16947
17722
|
this.writeWithSalt({}, salt);
|
|
@@ -16999,7 +17774,7 @@ var Keystore = class {
|
|
|
16999
17774
|
}
|
|
17000
17775
|
}
|
|
17001
17776
|
decryptFile(password, knownSalt) {
|
|
17002
|
-
if (!
|
|
17777
|
+
if (!fs24__default.default.existsSync(this.storePath)) return {};
|
|
17003
17778
|
try {
|
|
17004
17779
|
const { salt, ciphertext, iv, tag } = this.readRaw();
|
|
17005
17780
|
const useSalt = knownSalt ?? salt;
|
|
@@ -17021,8 +17796,8 @@ var Keystore = class {
|
|
|
17021
17796
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
17022
17797
|
const tag = cipher.getAuthTag();
|
|
17023
17798
|
const out = Buffer.concat([raw.salt, iv, tag, ciphertext]);
|
|
17024
|
-
|
|
17025
|
-
|
|
17799
|
+
fs24__default.default.mkdirSync(path25__default.default.dirname(this.storePath), { recursive: true });
|
|
17800
|
+
fs24__default.default.writeFileSync(this.storePath, out, { mode: 384 });
|
|
17026
17801
|
}
|
|
17027
17802
|
writeWithSalt(data, salt) {
|
|
17028
17803
|
if (!this.masterKey) throw new Error("writeWithSalt called before masterKey was set");
|
|
@@ -17032,11 +17807,11 @@ var Keystore = class {
|
|
|
17032
17807
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
17033
17808
|
const tag = cipher.getAuthTag();
|
|
17034
17809
|
const out = Buffer.concat([salt, iv, tag, ciphertext]);
|
|
17035
|
-
|
|
17036
|
-
|
|
17810
|
+
fs24__default.default.mkdirSync(path25__default.default.dirname(this.storePath), { recursive: true });
|
|
17811
|
+
fs24__default.default.writeFileSync(this.storePath, out, { mode: 384 });
|
|
17037
17812
|
}
|
|
17038
17813
|
readRaw() {
|
|
17039
|
-
const buf =
|
|
17814
|
+
const buf = fs24__default.default.readFileSync(this.storePath);
|
|
17040
17815
|
let offset = 0;
|
|
17041
17816
|
const salt = buf.subarray(offset, offset + SALT_LEN);
|
|
17042
17817
|
offset += SALT_LEN;
|
|
@@ -17052,9 +17827,9 @@ var Keystore = class {
|
|
|
17052
17827
|
}
|
|
17053
17828
|
};
|
|
17054
17829
|
async function loadCascadeMd(workspacePath) {
|
|
17055
|
-
const filePath =
|
|
17830
|
+
const filePath = path25__default.default.join(workspacePath, "CASCADE.md");
|
|
17056
17831
|
try {
|
|
17057
|
-
const raw = await
|
|
17832
|
+
const raw = await fs5__default.default.readFile(filePath, "utf-8");
|
|
17058
17833
|
return parseCascadeMd(raw);
|
|
17059
17834
|
} catch {
|
|
17060
17835
|
return null;
|
|
@@ -17083,7 +17858,7 @@ ${raw.trim()}`;
|
|
|
17083
17858
|
var MemoryStore = class _MemoryStore {
|
|
17084
17859
|
db;
|
|
17085
17860
|
constructor(dbPath) {
|
|
17086
|
-
|
|
17861
|
+
fs24__default.default.mkdirSync(path25__default.default.dirname(dbPath), { recursive: true });
|
|
17087
17862
|
try {
|
|
17088
17863
|
this.db = new Database__default.default(dbPath, { timeout: 5e3 });
|
|
17089
17864
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -17919,7 +18694,7 @@ Original error: ${err.message}`
|
|
|
17919
18694
|
}
|
|
17920
18695
|
};
|
|
17921
18696
|
function credentialsPath(globalDir2) {
|
|
17922
|
-
return
|
|
18697
|
+
return path25__default.default.join(globalDir2, GLOBAL_CREDENTIALS_FILE);
|
|
17923
18698
|
}
|
|
17924
18699
|
function providerKey(p) {
|
|
17925
18700
|
if (p.type === "azure") {
|
|
@@ -17932,7 +18707,7 @@ function isPersistable(p) {
|
|
|
17932
18707
|
}
|
|
17933
18708
|
function loadGlobalCredentials(globalDir2) {
|
|
17934
18709
|
try {
|
|
17935
|
-
const raw =
|
|
18710
|
+
const raw = fs24__default.default.readFileSync(credentialsPath(globalDir2), "utf-8");
|
|
17936
18711
|
const parsed = JSON.parse(raw);
|
|
17937
18712
|
if (!Array.isArray(parsed.providers)) return [];
|
|
17938
18713
|
return parsed.providers.filter(
|
|
@@ -17944,13 +18719,13 @@ function loadGlobalCredentials(globalDir2) {
|
|
|
17944
18719
|
}
|
|
17945
18720
|
function saveGlobalCredentials(globalDir2, providers) {
|
|
17946
18721
|
const filePath = credentialsPath(globalDir2);
|
|
17947
|
-
|
|
18722
|
+
fs24__default.default.mkdirSync(globalDir2, { recursive: true, mode: 448 });
|
|
17948
18723
|
const body = { version: 1, providers: providers.filter(isPersistable) };
|
|
17949
18724
|
const tmp = `${filePath}.tmp`;
|
|
17950
|
-
|
|
17951
|
-
|
|
18725
|
+
fs24__default.default.writeFileSync(tmp, JSON.stringify(body, null, 2), { encoding: "utf-8", mode: 384 });
|
|
18726
|
+
fs24__default.default.renameSync(tmp, filePath);
|
|
17952
18727
|
try {
|
|
17953
|
-
|
|
18728
|
+
fs24__default.default.chmodSync(filePath, 384);
|
|
17954
18729
|
} catch {
|
|
17955
18730
|
}
|
|
17956
18731
|
}
|
|
@@ -17985,7 +18760,7 @@ var ConfigManager = class {
|
|
|
17985
18760
|
/** `globalDirOverride` exists for tests — never point it at the real home dir there. */
|
|
17986
18761
|
constructor(workspacePath = process.cwd(), globalDirOverride) {
|
|
17987
18762
|
this.workspacePath = workspacePath;
|
|
17988
|
-
this.globalDir = globalDirOverride ??
|
|
18763
|
+
this.globalDir = globalDirOverride ?? path25__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR);
|
|
17989
18764
|
}
|
|
17990
18765
|
async load() {
|
|
17991
18766
|
this.config = await this.loadConfig();
|
|
@@ -17999,8 +18774,8 @@ var ConfigManager = class {
|
|
|
17999
18774
|
this.ignore = new CascadeIgnore();
|
|
18000
18775
|
await this.ignore.load(this.workspacePath);
|
|
18001
18776
|
this.cascadeMd = await loadCascadeMd(this.workspacePath);
|
|
18002
|
-
this.keystore = new Keystore(
|
|
18003
|
-
this.store = new MemoryStore(
|
|
18777
|
+
this.keystore = new Keystore(path25__default.default.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
|
|
18778
|
+
this.store = new MemoryStore(path25__default.default.join(this.workspacePath, CASCADE_DB_FILE));
|
|
18004
18779
|
await this.injectEnvKeys();
|
|
18005
18780
|
this.config.providers = mergeGlobalCredentials(this.config.providers, loadGlobalCredentials(this.globalDir));
|
|
18006
18781
|
await this.ensureDefaultIdentity();
|
|
@@ -18025,9 +18800,9 @@ var ConfigManager = class {
|
|
|
18025
18800
|
return this.workspacePath;
|
|
18026
18801
|
}
|
|
18027
18802
|
async save() {
|
|
18028
|
-
const configPath =
|
|
18029
|
-
await
|
|
18030
|
-
await
|
|
18803
|
+
const configPath = path25__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
18804
|
+
await fs5__default.default.mkdir(path25__default.default.dirname(configPath), { recursive: true });
|
|
18805
|
+
await fs5__default.default.writeFile(configPath, JSON.stringify(this.config, null, 2), "utf-8");
|
|
18031
18806
|
try {
|
|
18032
18807
|
saveGlobalCredentials(this.globalDir, this.config.providers);
|
|
18033
18808
|
} catch (err) {
|
|
@@ -18062,9 +18837,9 @@ var ConfigManager = class {
|
|
|
18062
18837
|
return configProvider?.apiKey;
|
|
18063
18838
|
}
|
|
18064
18839
|
async loadConfig() {
|
|
18065
|
-
const configPath =
|
|
18840
|
+
const configPath = path25__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
18066
18841
|
try {
|
|
18067
|
-
const raw = await
|
|
18842
|
+
const raw = await fs5__default.default.readFile(configPath, "utf-8");
|
|
18068
18843
|
return validateConfig(JSON.parse(raw));
|
|
18069
18844
|
} catch (err) {
|
|
18070
18845
|
if (err.code === "ENOENT") {
|
|
@@ -18594,7 +19369,7 @@ function aggregateCostStats(sessions, opts = {}) {
|
|
|
18594
19369
|
}
|
|
18595
19370
|
|
|
18596
19371
|
// src/dashboard/server.ts
|
|
18597
|
-
var __dirname$1 =
|
|
19372
|
+
var __dirname$1 = path25__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
|
|
18598
19373
|
var ORPHANED_ESCALATION_GRACE_MS = 3e4;
|
|
18599
19374
|
var DashboardServer = class {
|
|
18600
19375
|
app;
|
|
@@ -18874,14 +19649,14 @@ var DashboardServer = class {
|
|
|
18874
19649
|
*/
|
|
18875
19650
|
persistConfig() {
|
|
18876
19651
|
try {
|
|
18877
|
-
const configPath =
|
|
18878
|
-
|
|
18879
|
-
|
|
19652
|
+
const configPath = path25__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
19653
|
+
fs24__default.default.mkdirSync(path25__default.default.dirname(configPath), { recursive: true });
|
|
19654
|
+
fs24__default.default.writeFileSync(configPath, JSON.stringify(this.config, null, 2), "utf-8");
|
|
18880
19655
|
} catch (err) {
|
|
18881
19656
|
console.warn(`[dashboard] Failed to persist config: ${err instanceof Error ? err.message : String(err)}`);
|
|
18882
19657
|
}
|
|
18883
19658
|
try {
|
|
18884
|
-
saveGlobalCredentials(
|
|
19659
|
+
saveGlobalCredentials(path25__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR), this.config.providers ?? []);
|
|
18885
19660
|
} catch (err) {
|
|
18886
19661
|
console.warn(`[dashboard] Failed to sync global credentials: ${err instanceof Error ? err.message : String(err)}`);
|
|
18887
19662
|
}
|
|
@@ -18896,15 +19671,15 @@ var DashboardServer = class {
|
|
|
18896
19671
|
resolveDashboardSecret() {
|
|
18897
19672
|
const fromConfig = this.config.dashboard.secret ?? process.env["CASCADE_DASHBOARD_SECRET"];
|
|
18898
19673
|
if (fromConfig) return fromConfig;
|
|
18899
|
-
const secretPath =
|
|
19674
|
+
const secretPath = path25__default.default.join(this.workspacePath, CASCADE_DASHBOARD_SECRET_FILE);
|
|
18900
19675
|
try {
|
|
18901
|
-
if (
|
|
18902
|
-
const existing =
|
|
19676
|
+
if (fs24__default.default.existsSync(secretPath)) {
|
|
19677
|
+
const existing = fs24__default.default.readFileSync(secretPath, "utf-8").trim();
|
|
18903
19678
|
if (existing.length >= 16) return existing;
|
|
18904
19679
|
}
|
|
18905
19680
|
const generated = crypto4.randomUUID();
|
|
18906
|
-
|
|
18907
|
-
|
|
19681
|
+
fs24__default.default.mkdirSync(path25__default.default.dirname(secretPath), { recursive: true });
|
|
19682
|
+
fs24__default.default.writeFileSync(secretPath, generated, { encoding: "utf-8", mode: 384 });
|
|
18908
19683
|
if (this.config.dashboard.auth) {
|
|
18909
19684
|
console.warn(
|
|
18910
19685
|
`Dashboard auth enabled with no secret configured; persisted a generated secret to ${secretPath}. Set CASCADE_DASHBOARD_SECRET or config.dashboard.secret to override.`
|
|
@@ -18931,7 +19706,7 @@ var DashboardServer = class {
|
|
|
18931
19706
|
// ── Setup ─────────────────────────────────────
|
|
18932
19707
|
getGlobalStore() {
|
|
18933
19708
|
if (!this.globalStore) {
|
|
18934
|
-
const globalDbPath =
|
|
19709
|
+
const globalDbPath = path25__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
18935
19710
|
this.globalStore = new MemoryStore(globalDbPath);
|
|
18936
19711
|
}
|
|
18937
19712
|
return this.globalStore;
|
|
@@ -19311,12 +20086,12 @@ ${prompt}`;
|
|
|
19311
20086
|
}
|
|
19312
20087
|
}
|
|
19313
20088
|
watchRuntimeChanges() {
|
|
19314
|
-
const workspaceDbPath =
|
|
19315
|
-
const globalDbPath =
|
|
20089
|
+
const workspaceDbPath = path25__default.default.join(this.workspacePath, CASCADE_DB_FILE);
|
|
20090
|
+
const globalDbPath = path25__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
19316
20091
|
const watchPaths = [workspaceDbPath, globalDbPath].filter((p, index, arr) => arr.indexOf(p) === index);
|
|
19317
20092
|
for (const watchPath of watchPaths) {
|
|
19318
|
-
if (!
|
|
19319
|
-
|
|
20093
|
+
if (!fs24__default.default.existsSync(watchPath)) continue;
|
|
20094
|
+
fs24__default.default.watchFile(watchPath, { interval: 3e3 }, () => {
|
|
19320
20095
|
this.throttledBroadcast(watchPath === globalDbPath ? "global" : "workspace");
|
|
19321
20096
|
});
|
|
19322
20097
|
}
|
|
@@ -19438,7 +20213,7 @@ ${prompt}`;
|
|
|
19438
20213
|
const sessionId = req.params.id;
|
|
19439
20214
|
this.store.deleteSession(sessionId);
|
|
19440
20215
|
this.store.deleteRuntimeSession(sessionId);
|
|
19441
|
-
const globalDbPath =
|
|
20216
|
+
const globalDbPath = path25__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
19442
20217
|
const globalStore = new MemoryStore(globalDbPath);
|
|
19443
20218
|
try {
|
|
19444
20219
|
globalStore.deleteRuntimeSession(sessionId);
|
|
@@ -19531,7 +20306,7 @@ ${prompt}`;
|
|
|
19531
20306
|
});
|
|
19532
20307
|
this.app.delete("/api/sessions", auth2, (req, res) => {
|
|
19533
20308
|
const body = req.body;
|
|
19534
|
-
const globalDbPath =
|
|
20309
|
+
const globalDbPath = path25__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
19535
20310
|
if (body?.ids && Array.isArray(body.ids) && body.ids.length > 0) {
|
|
19536
20311
|
const globalStore = new MemoryStore(globalDbPath);
|
|
19537
20312
|
try {
|
|
@@ -19554,7 +20329,7 @@ ${prompt}`;
|
|
|
19554
20329
|
});
|
|
19555
20330
|
this.app.delete("/api/runtime", auth2, (_req, res) => {
|
|
19556
20331
|
this.store.deleteAllRuntimeNodes();
|
|
19557
|
-
const globalDbPath =
|
|
20332
|
+
const globalDbPath = path25__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
19558
20333
|
const globalStore = new MemoryStore(globalDbPath);
|
|
19559
20334
|
try {
|
|
19560
20335
|
globalStore.deleteAllRuntimeNodes();
|
|
@@ -19640,12 +20415,12 @@ ${prompt}`;
|
|
|
19640
20415
|
if (body["tierLimits"]) this.config.tierLimits = { ...this.config.tierLimits, ...body["tierLimits"] };
|
|
19641
20416
|
if (body["budget"]) this.config.budget = { ...this.config.budget, ...body["budget"] };
|
|
19642
20417
|
try {
|
|
19643
|
-
const configPath =
|
|
19644
|
-
const existing =
|
|
20418
|
+
const configPath = path25__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
20419
|
+
const existing = fs24__default.default.existsSync(configPath) ? JSON.parse(fs24__default.default.readFileSync(configPath, "utf-8")) : {};
|
|
19645
20420
|
const updated = { ...existing, tierLimits: this.config.tierLimits, budget: this.config.budget };
|
|
19646
20421
|
const tmp = configPath + ".tmp";
|
|
19647
|
-
|
|
19648
|
-
|
|
20422
|
+
fs24__default.default.writeFileSync(tmp, JSON.stringify(updated, null, 2), "utf-8");
|
|
20423
|
+
fs24__default.default.renameSync(tmp, configPath);
|
|
19649
20424
|
res.json({ ok: true });
|
|
19650
20425
|
} catch (err) {
|
|
19651
20426
|
res.status(500).json({ error: `Failed to save config: ${err instanceof Error ? err.message : String(err)}` });
|
|
@@ -19673,7 +20448,7 @@ ${prompt}`;
|
|
|
19673
20448
|
this.app.get("/api/runtime", auth2, (req, res) => {
|
|
19674
20449
|
const scope = req.query["scope"] ?? "workspace";
|
|
19675
20450
|
if (scope === "global") {
|
|
19676
|
-
const globalDbPath =
|
|
20451
|
+
const globalDbPath = path25__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
19677
20452
|
const globalStore = new MemoryStore(globalDbPath);
|
|
19678
20453
|
try {
|
|
19679
20454
|
res.json({
|
|
@@ -20014,13 +20789,13 @@ ${prompt}`;
|
|
|
20014
20789
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
20015
20790
|
}
|
|
20016
20791
|
});
|
|
20017
|
-
const prodPath =
|
|
20018
|
-
const devPath =
|
|
20019
|
-
const webDistPath =
|
|
20020
|
-
if (
|
|
20792
|
+
const prodPath = path25__default.default.resolve(__dirname$1, "../web/dist");
|
|
20793
|
+
const devPath = path25__default.default.resolve(__dirname$1, "../../web/dist");
|
|
20794
|
+
const webDistPath = fs24__default.default.existsSync(prodPath) ? prodPath : devPath;
|
|
20795
|
+
if (fs24__default.default.existsSync(webDistPath)) {
|
|
20021
20796
|
this.app.use(express__default.default.static(webDistPath));
|
|
20022
20797
|
this.app.get("*", (_req, res) => {
|
|
20023
|
-
res.sendFile(
|
|
20798
|
+
res.sendFile(path25__default.default.join(webDistPath, "index.html"));
|
|
20024
20799
|
});
|
|
20025
20800
|
} else {
|
|
20026
20801
|
this.app.get("/", (_req, res) => {
|
|
@@ -20207,14 +20982,14 @@ function planRetrieval(ctx) {
|
|
|
20207
20982
|
}
|
|
20208
20983
|
var CLOUD_SESSION_FILE = "cloud-session.json";
|
|
20209
20984
|
function globalDir(homeDir = os6__default.default.homedir()) {
|
|
20210
|
-
return
|
|
20985
|
+
return path25__default.default.join(homeDir, GLOBAL_CONFIG_DIR);
|
|
20211
20986
|
}
|
|
20212
20987
|
function cloudSessionPath(dir = globalDir()) {
|
|
20213
|
-
return
|
|
20988
|
+
return path25__default.default.join(dir, CLOUD_SESSION_FILE);
|
|
20214
20989
|
}
|
|
20215
20990
|
function loadCloudSession(dir = globalDir()) {
|
|
20216
20991
|
try {
|
|
20217
|
-
const raw =
|
|
20992
|
+
const raw = fs24__default.default.readFileSync(cloudSessionPath(dir), "utf-8");
|
|
20218
20993
|
const parsed = JSON.parse(raw);
|
|
20219
20994
|
if (!parsed?.refreshToken || !parsed?.serverUrl) return null;
|
|
20220
20995
|
return parsed;
|
|
@@ -20223,17 +20998,17 @@ function loadCloudSession(dir = globalDir()) {
|
|
|
20223
20998
|
}
|
|
20224
20999
|
}
|
|
20225
21000
|
function saveCloudSession(session, dir = globalDir()) {
|
|
20226
|
-
|
|
21001
|
+
fs24__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
20227
21002
|
const file = cloudSessionPath(dir);
|
|
20228
|
-
|
|
21003
|
+
fs24__default.default.writeFileSync(file, JSON.stringify(session, null, 2), { mode: 384 });
|
|
20229
21004
|
try {
|
|
20230
|
-
|
|
21005
|
+
fs24__default.default.chmodSync(file, 384);
|
|
20231
21006
|
} catch {
|
|
20232
21007
|
}
|
|
20233
21008
|
}
|
|
20234
21009
|
function clearCloudSession(dir = globalDir()) {
|
|
20235
21010
|
try {
|
|
20236
|
-
|
|
21011
|
+
fs24__default.default.rmSync(cloudSessionPath(dir));
|
|
20237
21012
|
} catch {
|
|
20238
21013
|
}
|
|
20239
21014
|
}
|
|
@@ -20405,16 +21180,16 @@ var CloudClient = class _CloudClient {
|
|
|
20405
21180
|
return (await this.refresh()).accessToken;
|
|
20406
21181
|
}
|
|
20407
21182
|
/** Authenticated request that refreshes once on a 401 (e.g. server-side revoke). */
|
|
20408
|
-
async authed(
|
|
20409
|
-
const call = (token) => fetch(this.url(
|
|
21183
|
+
async authed(path27, init = {}) {
|
|
21184
|
+
const call = (token) => fetch(this.url(path27), { ...init, headers: { ...init.headers ?? {}, Authorization: `Bearer ${token}` } });
|
|
20410
21185
|
let res = await call(await this.accessToken());
|
|
20411
21186
|
if (res.status === 401) res = await call((await this.refresh()).accessToken);
|
|
20412
21187
|
if (!res.ok) throw new Error(`Request failed (${res.status}).`);
|
|
20413
21188
|
const text = await res.text();
|
|
20414
21189
|
return text ? JSON.parse(text) : {};
|
|
20415
21190
|
}
|
|
20416
|
-
authedGet(
|
|
20417
|
-
return this.authed(
|
|
21191
|
+
authedGet(path27) {
|
|
21192
|
+
return this.authed(path27);
|
|
20418
21193
|
}
|
|
20419
21194
|
async fetchMe(token) {
|
|
20420
21195
|
const res = await fetch(this.url("/api/me"), { headers: { Authorization: `Bearer ${token}` } });
|
|
@@ -20783,6 +21558,7 @@ exports.DEFAULT_DASHBOARD_PORT = DEFAULT_DASHBOARD_PORT;
|
|
|
20783
21558
|
exports.DEFAULT_MAX_SESSION_MESSAGES = DEFAULT_MAX_SESSION_MESSAGES;
|
|
20784
21559
|
exports.DEFAULT_RETENTION_DAYS = DEFAULT_RETENTION_DAYS;
|
|
20785
21560
|
exports.DEFAULT_THEME = DEFAULT_THEME;
|
|
21561
|
+
exports.DOCUMENT_MIME = DOCUMENT_MIME;
|
|
20786
21562
|
exports.DashboardServer = DashboardServer;
|
|
20787
21563
|
exports.FileMcpOAuthStore = FileMcpOAuthStore;
|
|
20788
21564
|
exports.GITHUB_MODELS_API_VERSION = GITHUB_MODELS_API_VERSION;
|
|
@@ -20793,6 +21569,7 @@ exports.GLOBAL_CREDENTIALS_FILE = GLOBAL_CREDENTIALS_FILE;
|
|
|
20793
21569
|
exports.GLOBAL_DB_FILE = GLOBAL_DB_FILE;
|
|
20794
21570
|
exports.GLOBAL_KEYSTORE_FILE = GLOBAL_KEYSTORE_FILE;
|
|
20795
21571
|
exports.GLOBAL_RUNTIME_DB_FILE = GLOBAL_RUNTIME_DB_FILE;
|
|
21572
|
+
exports.GenerateDocumentTool = GenerateDocumentTool;
|
|
20796
21573
|
exports.GraphRetriever = GraphRetriever;
|
|
20797
21574
|
exports.GraphSearchTool = GraphSearchTool;
|
|
20798
21575
|
exports.HooksRunner = HooksRunner;
|
|
@@ -20826,9 +21603,13 @@ exports.WorkspaceIndex = WorkspaceIndex;
|
|
|
20826
21603
|
exports.applySyncBundle = applySyncBundle;
|
|
20827
21604
|
exports.azureModelForDeployment = azureModelForDeployment;
|
|
20828
21605
|
exports.beginMcpOAuth = beginMcpOAuth;
|
|
21606
|
+
exports.buildDocumentTools = buildDocumentTools;
|
|
20829
21607
|
exports.buildManifest = buildManifest;
|
|
20830
21608
|
exports.buildSessionTranscript = buildSessionTranscript;
|
|
21609
|
+
exports.bytesToBase64 = bytesToBase64;
|
|
20831
21610
|
exports.cagCharBudget = cagCharBudget;
|
|
21611
|
+
exports.chartKind = chartKind;
|
|
21612
|
+
exports.chartToTableRows = chartToTableRows;
|
|
20832
21613
|
exports.chatCompleterFromProviders = chatCompleterFromProviders;
|
|
20833
21614
|
exports.chunkCode = chunkCode;
|
|
20834
21615
|
exports.chunkText = chunkText2;
|
|
@@ -20843,24 +21624,40 @@ exports.discoverMcpTools = discoverMcpTools;
|
|
|
20843
21624
|
exports.distillSessionFacts = distillSessionFacts;
|
|
20844
21625
|
exports.embedderFromProviders = embedderFromProviders;
|
|
20845
21626
|
exports.encryptSyncBlob = encryptJSON;
|
|
21627
|
+
exports.extractCharts = extractCharts;
|
|
21628
|
+
exports.fileExt = fileExt;
|
|
20846
21629
|
exports.fileOAuthProvider = fileOAuthProvider;
|
|
20847
21630
|
exports.gatherSyncBundle = gatherSyncBundle;
|
|
20848
21631
|
exports.hashContent = hashContent;
|
|
20849
21632
|
exports.heuristicCodeChunker = heuristicCodeChunker;
|
|
20850
21633
|
exports.inferAzureBaseModel = inferAzureBaseModel;
|
|
21634
|
+
exports.inlineRuns = inlineRuns;
|
|
21635
|
+
exports.isDocumentFormat = isDocumentFormat;
|
|
20851
21636
|
exports.isMcpToolName = isMcpToolName;
|
|
21637
|
+
exports.matchImageLine = matchImageLine;
|
|
20852
21638
|
exports.mcpServerPrefix = mcpServerPrefix;
|
|
20853
21639
|
exports.mcpToolName = mcpToolName;
|
|
20854
21640
|
exports.nodeHttpFetch = nodeHttpFetch;
|
|
21641
|
+
exports.parseBlocks = parseBlocks;
|
|
21642
|
+
exports.parseChartSpec = parseChartSpec;
|
|
21643
|
+
exports.parseDelimited = parseDelimited;
|
|
20855
21644
|
exports.parseRankOrder = parseRankOrder;
|
|
21645
|
+
exports.parseSlide = parseSlide;
|
|
20856
21646
|
exports.planRetrieval = planRetrieval;
|
|
20857
21647
|
exports.preferIpv4Host = preferIpv4Host;
|
|
20858
21648
|
exports.reciprocalRankFusion = reciprocalRankFusion;
|
|
20859
21649
|
exports.refreshMcpToken = refreshMcpToken;
|
|
20860
21650
|
exports.removeMcpServerDenials = removeMcpServerDenials;
|
|
21651
|
+
exports.renderDocument = renderDocument;
|
|
21652
|
+
exports.renderDocx = renderDocx;
|
|
21653
|
+
exports.renderPptx = renderPptx;
|
|
21654
|
+
exports.renderXlsx = renderXlsx;
|
|
20861
21655
|
exports.runCascade = runCascade;
|
|
20862
21656
|
exports.sessionWorthRemembering = sessionWorthRemembering;
|
|
21657
|
+
exports.sniffImage = sniffImage;
|
|
21658
|
+
exports.splitSlides = splitSlides;
|
|
20863
21659
|
exports.streamCascade = streamCascade;
|
|
21660
|
+
exports.stripInline = stripInline;
|
|
20864
21661
|
exports.uniqueMcpServerName = uniqueMcpServerName;
|
|
20865
21662
|
//# sourceMappingURL=index.cjs.map
|
|
20866
21663
|
//# sourceMappingURL=index.cjs.map
|