cascade-ai 0.17.0 → 0.18.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/dist/cli.cjs +318 -185
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +315 -182
- package/dist/cli.js.map +1 -1
- package/dist/desktop-core.cjs +887 -756
- package/dist/index.cjs +243 -112
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.js +241 -111
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -7,8 +7,8 @@ import https from 'https';
|
|
|
7
7
|
import zlib from 'zlib';
|
|
8
8
|
import { Readable } from 'stream';
|
|
9
9
|
import Database2 from 'better-sqlite3';
|
|
10
|
-
import
|
|
11
|
-
import
|
|
10
|
+
import path26 from 'path';
|
|
11
|
+
import fs24 from 'fs';
|
|
12
12
|
import os8 from 'os';
|
|
13
13
|
import crypto, { randomUUID, timingSafeEqual } from 'crypto';
|
|
14
14
|
import { render, Box, Text, useStdout, useApp, useInput, Static } from 'ink';
|
|
@@ -17,7 +17,7 @@ import React2, { useState, useReducer, useRef, useCallback, useEffect, useMemo }
|
|
|
17
17
|
import chalk9 from 'chalk';
|
|
18
18
|
import dotenv from 'dotenv';
|
|
19
19
|
import { fileURLToPath } from 'url';
|
|
20
|
-
import
|
|
20
|
+
import fs10 from 'fs/promises';
|
|
21
21
|
import * as _ignoreModule from 'ignore';
|
|
22
22
|
import _ignoreModule__default from 'ignore';
|
|
23
23
|
import { z } from 'zod';
|
|
@@ -56,16 +56,17 @@ var __export = (target, all) => {
|
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
// src/constants.ts
|
|
59
|
-
var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SECRET_FILE, GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_RUNTIME_DB_FILE, DEFAULT_DASHBOARD_PORT, DEFAULT_CONTEXT_LIMIT, DEFAULT_AUTO_SUMMARIZE_AT, MODELS, T1_MODEL_PRIORITY, T2_MODEL_PRIORITY, T3_MODEL_PRIORITY, VISION_MODEL_PRIORITY, COMPLEXITY_T2_COUNT, THEME_NAMES, DEFAULT_THEME, OLLAMA_BASE_URL, LM_STUDIO_BASE_URL, AZURE_BASE_URL_TEMPLATE, TOOL_NAMES, DEFAULT_APPROVAL_REQUIRED;
|
|
59
|
+
var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SECRET_FILE, GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_CREDENTIALS_FILE, GLOBAL_RUNTIME_DB_FILE, DEFAULT_DASHBOARD_PORT, DEFAULT_CONTEXT_LIMIT, DEFAULT_AUTO_SUMMARIZE_AT, MODELS, T1_MODEL_PRIORITY, T2_MODEL_PRIORITY, T3_MODEL_PRIORITY, VISION_MODEL_PRIORITY, COMPLEXITY_T2_COUNT, THEME_NAMES, DEFAULT_THEME, OLLAMA_BASE_URL, LM_STUDIO_BASE_URL, AZURE_BASE_URL_TEMPLATE, TOOL_NAMES, DEFAULT_APPROVAL_REQUIRED;
|
|
60
60
|
var init_constants = __esm({
|
|
61
61
|
"src/constants.ts"() {
|
|
62
|
-
CASCADE_VERSION = "0.
|
|
62
|
+
CASCADE_VERSION = "0.18.0";
|
|
63
63
|
CASCADE_CONFIG_FILE = ".cascade/config.json";
|
|
64
64
|
CASCADE_DB_FILE = ".cascade/memory.db";
|
|
65
65
|
CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
|
|
66
66
|
GLOBAL_CONFIG_DIR = ".cascade-ai";
|
|
67
67
|
GLOBAL_DB_FILE = "memory.db";
|
|
68
68
|
GLOBAL_KEYSTORE_FILE = "keystore.enc";
|
|
69
|
+
GLOBAL_CREDENTIALS_FILE = "credentials.json";
|
|
69
70
|
GLOBAL_RUNTIME_DB_FILE = "runtime.db";
|
|
70
71
|
DEFAULT_DASHBOARD_PORT = 4891;
|
|
71
72
|
DEFAULT_CONTEXT_LIMIT = 2e5;
|
|
@@ -1580,12 +1581,12 @@ var init_audit_logger = __esm({
|
|
|
1580
1581
|
constructor(workspacePath, debugMode = false) {
|
|
1581
1582
|
this.workspacePath = workspacePath;
|
|
1582
1583
|
this.debugMode = debugMode;
|
|
1583
|
-
const cascadeDir =
|
|
1584
|
-
if (!
|
|
1585
|
-
|
|
1584
|
+
const cascadeDir = path26.join(workspacePath, ".cascade");
|
|
1585
|
+
if (!fs24.existsSync(cascadeDir)) {
|
|
1586
|
+
fs24.mkdirSync(cascadeDir, { recursive: true });
|
|
1586
1587
|
}
|
|
1587
|
-
this.keyPath =
|
|
1588
|
-
this.dbPath =
|
|
1588
|
+
this.keyPath = path26.join(cascadeDir, "audit_log.key");
|
|
1589
|
+
this.dbPath = path26.join(cascadeDir, "audit_log.db");
|
|
1589
1590
|
this.initEncryptionKey();
|
|
1590
1591
|
this.db = new Database2(this.dbPath);
|
|
1591
1592
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -1615,11 +1616,11 @@ var init_audit_logger = __esm({
|
|
|
1615
1616
|
return crypto.createHash("sha256").update(`${prevHash}|${timestamp}|${eventType}|${tierId}|${encryptedPayload}`).digest("hex");
|
|
1616
1617
|
}
|
|
1617
1618
|
initEncryptionKey() {
|
|
1618
|
-
if (
|
|
1619
|
-
this.encryptionKey =
|
|
1619
|
+
if (fs24.existsSync(this.keyPath)) {
|
|
1620
|
+
this.encryptionKey = fs24.readFileSync(this.keyPath);
|
|
1620
1621
|
} else {
|
|
1621
1622
|
this.encryptionKey = crypto.randomBytes(32);
|
|
1622
|
-
|
|
1623
|
+
fs24.writeFileSync(this.keyPath, this.encryptionKey);
|
|
1623
1624
|
}
|
|
1624
1625
|
}
|
|
1625
1626
|
encrypt(text) {
|
|
@@ -1697,9 +1698,9 @@ var init_audit_logger = __esm({
|
|
|
1697
1698
|
dumpDebugIfNeeded() {
|
|
1698
1699
|
if (!this.debugMode) return;
|
|
1699
1700
|
try {
|
|
1700
|
-
const dumpPath =
|
|
1701
|
+
const dumpPath = path26.join(os8.tmpdir(), "cascade_audit_logs_debug.json");
|
|
1701
1702
|
const entries = this.getAllLogs();
|
|
1702
|
-
|
|
1703
|
+
fs24.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
|
|
1703
1704
|
} catch (err) {
|
|
1704
1705
|
console.error("Failed to dump debug audit logs", err);
|
|
1705
1706
|
}
|
|
@@ -1754,7 +1755,7 @@ var Keystore = class {
|
|
|
1754
1755
|
const creds = await this.keytar.findCredentials(KEYTAR_SERVICE);
|
|
1755
1756
|
this.cache = Object.fromEntries(creds.map((c) => [c.account, c.password]));
|
|
1756
1757
|
this.backend = "keytar";
|
|
1757
|
-
if (password &&
|
|
1758
|
+
if (password && fs24.existsSync(this.storePath)) {
|
|
1758
1759
|
try {
|
|
1759
1760
|
const fileEntries = this.decryptFile(password);
|
|
1760
1761
|
for (const [k, v] of Object.entries(fileEntries)) {
|
|
@@ -1773,7 +1774,7 @@ var Keystore = class {
|
|
|
1773
1774
|
"Keystore unlock requires a password because the OS keychain (keytar) is not available on this system."
|
|
1774
1775
|
);
|
|
1775
1776
|
}
|
|
1776
|
-
if (!
|
|
1777
|
+
if (!fs24.existsSync(this.storePath)) {
|
|
1777
1778
|
const salt = crypto.randomBytes(SALT_LEN);
|
|
1778
1779
|
this.masterKey = this.deriveKey(password, salt);
|
|
1779
1780
|
this.writeWithSalt({}, salt);
|
|
@@ -1787,7 +1788,7 @@ var Keystore = class {
|
|
|
1787
1788
|
}
|
|
1788
1789
|
/** Synchronous legacy unlock kept for AES-only environments. */
|
|
1789
1790
|
unlockSync(password) {
|
|
1790
|
-
if (!
|
|
1791
|
+
if (!fs24.existsSync(this.storePath)) {
|
|
1791
1792
|
const salt = crypto.randomBytes(SALT_LEN);
|
|
1792
1793
|
this.masterKey = this.deriveKey(password, salt);
|
|
1793
1794
|
this.writeWithSalt({}, salt);
|
|
@@ -1845,7 +1846,7 @@ var Keystore = class {
|
|
|
1845
1846
|
}
|
|
1846
1847
|
}
|
|
1847
1848
|
decryptFile(password, knownSalt) {
|
|
1848
|
-
if (!
|
|
1849
|
+
if (!fs24.existsSync(this.storePath)) return {};
|
|
1849
1850
|
try {
|
|
1850
1851
|
const { salt, ciphertext, iv, tag } = this.readRaw();
|
|
1851
1852
|
const useSalt = knownSalt ?? salt;
|
|
@@ -1867,8 +1868,8 @@ var Keystore = class {
|
|
|
1867
1868
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
1868
1869
|
const tag = cipher.getAuthTag();
|
|
1869
1870
|
const out = Buffer.concat([raw.salt, iv, tag, ciphertext]);
|
|
1870
|
-
|
|
1871
|
-
|
|
1871
|
+
fs24.mkdirSync(path26.dirname(this.storePath), { recursive: true });
|
|
1872
|
+
fs24.writeFileSync(this.storePath, out, { mode: 384 });
|
|
1872
1873
|
}
|
|
1873
1874
|
writeWithSalt(data, salt) {
|
|
1874
1875
|
if (!this.masterKey) throw new Error("writeWithSalt called before masterKey was set");
|
|
@@ -1878,11 +1879,11 @@ var Keystore = class {
|
|
|
1878
1879
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
1879
1880
|
const tag = cipher.getAuthTag();
|
|
1880
1881
|
const out = Buffer.concat([salt, iv, tag, ciphertext]);
|
|
1881
|
-
|
|
1882
|
-
|
|
1882
|
+
fs24.mkdirSync(path26.dirname(this.storePath), { recursive: true });
|
|
1883
|
+
fs24.writeFileSync(this.storePath, out, { mode: 384 });
|
|
1883
1884
|
}
|
|
1884
1885
|
readRaw() {
|
|
1885
|
-
const buf =
|
|
1886
|
+
const buf = fs24.readFileSync(this.storePath);
|
|
1886
1887
|
let offset = 0;
|
|
1887
1888
|
const salt = buf.subarray(offset, offset + SALT_LEN);
|
|
1888
1889
|
offset += SALT_LEN;
|
|
@@ -1915,9 +1916,9 @@ var CascadeIgnore = class {
|
|
|
1915
1916
|
]);
|
|
1916
1917
|
}
|
|
1917
1918
|
async load(workspacePath) {
|
|
1918
|
-
const filePath =
|
|
1919
|
+
const filePath = path26.join(workspacePath, ".cascadeignore");
|
|
1919
1920
|
try {
|
|
1920
|
-
const content = await
|
|
1921
|
+
const content = await fs10.readFile(filePath, "utf-8");
|
|
1921
1922
|
const lines = content.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
|
|
1922
1923
|
this.ig.add(lines);
|
|
1923
1924
|
this.loaded = true;
|
|
@@ -1926,7 +1927,7 @@ var CascadeIgnore = class {
|
|
|
1926
1927
|
}
|
|
1927
1928
|
isIgnored(filePath, workspacePath) {
|
|
1928
1929
|
try {
|
|
1929
|
-
const relative = workspacePath ?
|
|
1930
|
+
const relative = workspacePath ? path26.relative(workspacePath, filePath) : filePath;
|
|
1930
1931
|
return this.ig.ignores(relative);
|
|
1931
1932
|
} catch {
|
|
1932
1933
|
return false;
|
|
@@ -1937,7 +1938,7 @@ var CascadeIgnore = class {
|
|
|
1937
1938
|
}
|
|
1938
1939
|
};
|
|
1939
1940
|
async function createDefaultIgnoreFile(workspacePath) {
|
|
1940
|
-
const filePath =
|
|
1941
|
+
const filePath = path26.join(workspacePath, ".cascadeignore");
|
|
1941
1942
|
const content = `# .cascadeignore \u2014 Files Cascade agents cannot read or modify
|
|
1942
1943
|
# Syntax identical to .gitignore
|
|
1943
1944
|
|
|
@@ -1964,12 +1965,12 @@ build/
|
|
|
1964
1965
|
.DS_Store
|
|
1965
1966
|
Thumbs.db
|
|
1966
1967
|
`;
|
|
1967
|
-
await
|
|
1968
|
+
await fs10.writeFile(filePath, content, "utf-8");
|
|
1968
1969
|
}
|
|
1969
1970
|
async function loadCascadeMd(workspacePath) {
|
|
1970
|
-
const filePath =
|
|
1971
|
+
const filePath = path26.join(workspacePath, "CASCADE.md");
|
|
1971
1972
|
try {
|
|
1972
|
-
const raw = await
|
|
1973
|
+
const raw = await fs10.readFile(filePath, "utf-8");
|
|
1973
1974
|
return parseCascadeMd(raw);
|
|
1974
1975
|
} catch {
|
|
1975
1976
|
return null;
|
|
@@ -1996,7 +1997,7 @@ ${raw.trim()}`;
|
|
|
1996
1997
|
return { raw, sections, systemPrompt };
|
|
1997
1998
|
}
|
|
1998
1999
|
async function createDefaultCascadeMd(workspacePath) {
|
|
1999
|
-
const filePath =
|
|
2000
|
+
const filePath = path26.join(workspacePath, "CASCADE.md");
|
|
2000
2001
|
const content = `# Cascade Project Instructions
|
|
2001
2002
|
|
|
2002
2003
|
This file contains project-specific instructions for the Cascade AI agent.
|
|
@@ -2026,12 +2027,12 @@ All tools are allowed unless specified otherwise.
|
|
|
2026
2027
|
|
|
2027
2028
|
List any areas the agent should not touch.
|
|
2028
2029
|
`;
|
|
2029
|
-
await
|
|
2030
|
+
await fs10.writeFile(filePath, content, "utf-8");
|
|
2030
2031
|
}
|
|
2031
2032
|
var MemoryStore = class _MemoryStore {
|
|
2032
2033
|
db;
|
|
2033
2034
|
constructor(dbPath) {
|
|
2034
|
-
|
|
2035
|
+
fs24.mkdirSync(path26.dirname(dbPath), { recursive: true });
|
|
2035
2036
|
try {
|
|
2036
2037
|
this.db = new Database2(dbPath, { timeout: 5e3 });
|
|
2037
2038
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -3178,6 +3179,63 @@ function validateConfig(raw) {
|
|
|
3178
3179
|
return result.data;
|
|
3179
3180
|
}
|
|
3180
3181
|
|
|
3182
|
+
// src/config/global-credentials.ts
|
|
3183
|
+
init_constants();
|
|
3184
|
+
function credentialsPath(globalDir) {
|
|
3185
|
+
return path26.join(globalDir, GLOBAL_CREDENTIALS_FILE);
|
|
3186
|
+
}
|
|
3187
|
+
function providerKey(p) {
|
|
3188
|
+
if (p.type === "azure") {
|
|
3189
|
+
return `azure:${p.deploymentName ?? p.baseUrl ?? p.label ?? ""}`;
|
|
3190
|
+
}
|
|
3191
|
+
return p.type;
|
|
3192
|
+
}
|
|
3193
|
+
function isPersistable(p) {
|
|
3194
|
+
return Boolean(p.apiKey || p.authToken || p.type === "azure" || p.baseUrl);
|
|
3195
|
+
}
|
|
3196
|
+
function loadGlobalCredentials(globalDir) {
|
|
3197
|
+
try {
|
|
3198
|
+
const raw = fs24.readFileSync(credentialsPath(globalDir), "utf-8");
|
|
3199
|
+
const parsed = JSON.parse(raw);
|
|
3200
|
+
if (!Array.isArray(parsed.providers)) return [];
|
|
3201
|
+
return parsed.providers.filter(
|
|
3202
|
+
(p) => Boolean(p) && typeof p.type === "string"
|
|
3203
|
+
);
|
|
3204
|
+
} catch {
|
|
3205
|
+
return [];
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
function saveGlobalCredentials(globalDir, providers) {
|
|
3209
|
+
const filePath = credentialsPath(globalDir);
|
|
3210
|
+
fs24.mkdirSync(globalDir, { recursive: true, mode: 448 });
|
|
3211
|
+
const body = { version: 1, providers: providers.filter(isPersistable) };
|
|
3212
|
+
const tmp = `${filePath}.tmp`;
|
|
3213
|
+
fs24.writeFileSync(tmp, JSON.stringify(body, null, 2), { encoding: "utf-8", mode: 384 });
|
|
3214
|
+
fs24.renameSync(tmp, filePath);
|
|
3215
|
+
try {
|
|
3216
|
+
fs24.chmodSync(filePath, 384);
|
|
3217
|
+
} catch {
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
function mergeGlobalCredentials(workspaceProviders, globalProviders) {
|
|
3221
|
+
const merged = [...workspaceProviders];
|
|
3222
|
+
const byKey = new Map(merged.map((p) => [providerKey(p), p]));
|
|
3223
|
+
for (const g of globalProviders) {
|
|
3224
|
+
const existing = byKey.get(providerKey(g));
|
|
3225
|
+
if (!existing) {
|
|
3226
|
+
merged.push({ ...g });
|
|
3227
|
+
byKey.set(providerKey(g), merged[merged.length - 1]);
|
|
3228
|
+
continue;
|
|
3229
|
+
}
|
|
3230
|
+
if (!existing.apiKey && g.apiKey) existing.apiKey = g.apiKey;
|
|
3231
|
+
if (!existing.authToken && g.authToken) existing.authToken = g.authToken;
|
|
3232
|
+
if (!existing.baseUrl && g.baseUrl) existing.baseUrl = g.baseUrl;
|
|
3233
|
+
if (!existing.apiVersion && g.apiVersion) existing.apiVersion = g.apiVersion;
|
|
3234
|
+
if (!existing.label && g.label) existing.label = g.label;
|
|
3235
|
+
}
|
|
3236
|
+
return merged;
|
|
3237
|
+
}
|
|
3238
|
+
|
|
3181
3239
|
// src/config/index.ts
|
|
3182
3240
|
init_constants();
|
|
3183
3241
|
var KEY_OPTIONAL_PROVIDER_TYPES = /* @__PURE__ */ new Set(["ollama", "openai-compatible"]);
|
|
@@ -3193,18 +3251,20 @@ var ConfigManager = class {
|
|
|
3193
3251
|
cascadeMd = null;
|
|
3194
3252
|
workspacePath;
|
|
3195
3253
|
globalDir;
|
|
3196
|
-
|
|
3254
|
+
/** `globalDirOverride` exists for tests — never point it at the real home dir there. */
|
|
3255
|
+
constructor(workspacePath = process.cwd(), globalDirOverride) {
|
|
3197
3256
|
this.workspacePath = workspacePath;
|
|
3198
|
-
this.globalDir =
|
|
3257
|
+
this.globalDir = globalDirOverride ?? path26.join(os8.homedir(), GLOBAL_CONFIG_DIR);
|
|
3199
3258
|
}
|
|
3200
3259
|
async load() {
|
|
3201
3260
|
this.config = await this.loadConfig();
|
|
3202
3261
|
this.ignore = new CascadeIgnore();
|
|
3203
3262
|
await this.ignore.load(this.workspacePath);
|
|
3204
3263
|
this.cascadeMd = await loadCascadeMd(this.workspacePath);
|
|
3205
|
-
this.keystore = new Keystore(
|
|
3206
|
-
this.store = new MemoryStore(
|
|
3264
|
+
this.keystore = new Keystore(path26.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
|
|
3265
|
+
this.store = new MemoryStore(path26.join(this.workspacePath, CASCADE_DB_FILE));
|
|
3207
3266
|
await this.injectEnvKeys();
|
|
3267
|
+
this.config.providers = mergeGlobalCredentials(this.config.providers, loadGlobalCredentials(this.globalDir));
|
|
3208
3268
|
await this.ensureDefaultIdentity();
|
|
3209
3269
|
}
|
|
3210
3270
|
getConfig() {
|
|
@@ -3226,9 +3286,14 @@ var ConfigManager = class {
|
|
|
3226
3286
|
return this.workspacePath;
|
|
3227
3287
|
}
|
|
3228
3288
|
async save() {
|
|
3229
|
-
const configPath =
|
|
3230
|
-
await
|
|
3231
|
-
await
|
|
3289
|
+
const configPath = path26.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
3290
|
+
await fs10.mkdir(path26.dirname(configPath), { recursive: true });
|
|
3291
|
+
await fs10.writeFile(configPath, JSON.stringify(this.config, null, 2), "utf-8");
|
|
3292
|
+
try {
|
|
3293
|
+
saveGlobalCredentials(this.globalDir, this.config.providers);
|
|
3294
|
+
} catch (err) {
|
|
3295
|
+
console.warn(`Failed to sync credentials to global store: ${err instanceof Error ? err.message : String(err)}`);
|
|
3296
|
+
}
|
|
3232
3297
|
}
|
|
3233
3298
|
async updateConfig(updates) {
|
|
3234
3299
|
this.config = validateConfig({ ...this.config, ...updates });
|
|
@@ -3251,9 +3316,9 @@ var ConfigManager = class {
|
|
|
3251
3316
|
return configProvider?.apiKey;
|
|
3252
3317
|
}
|
|
3253
3318
|
async loadConfig() {
|
|
3254
|
-
const configPath =
|
|
3319
|
+
const configPath = path26.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
3255
3320
|
try {
|
|
3256
|
-
const raw = await
|
|
3321
|
+
const raw = await fs10.readFile(configPath, "utf-8");
|
|
3257
3322
|
return validateConfig(JSON.parse(raw));
|
|
3258
3323
|
} catch (err) {
|
|
3259
3324
|
if (err.code === "ENOENT") {
|
|
@@ -4130,7 +4195,7 @@ init_constants();
|
|
|
4130
4195
|
var DEFAULT_SNAPSHOT_URL = "https://raw.githubusercontent.com/Varun-SV/Cascade-AI/main/src/core/router/benchmark-data.json";
|
|
4131
4196
|
var OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
|
|
4132
4197
|
var FETCH_TIMEOUT_MS = 8e3;
|
|
4133
|
-
var DEFAULT_CACHE_FILE =
|
|
4198
|
+
var DEFAULT_CACHE_FILE = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, "benchmarks-cache.json");
|
|
4134
4199
|
function normalizeModelId(id) {
|
|
4135
4200
|
let s = id.toLowerCase();
|
|
4136
4201
|
const slash = s.lastIndexOf("/");
|
|
@@ -4163,7 +4228,7 @@ var LiveDataProvider = class {
|
|
|
4163
4228
|
if (this.loaded) return;
|
|
4164
4229
|
this.loaded = true;
|
|
4165
4230
|
try {
|
|
4166
|
-
const raw = await
|
|
4231
|
+
const raw = await fs10.readFile(this.opts.cacheFile, "utf-8");
|
|
4167
4232
|
const cache = JSON.parse(raw);
|
|
4168
4233
|
if (cache.snapshot?.families) {
|
|
4169
4234
|
this.snapshot = cache.snapshot;
|
|
@@ -4275,14 +4340,14 @@ var LiveDataProvider = class {
|
|
|
4275
4340
|
}
|
|
4276
4341
|
async saveCache() {
|
|
4277
4342
|
try {
|
|
4278
|
-
await
|
|
4343
|
+
await fs10.mkdir(path26.dirname(this.opts.cacheFile), { recursive: true });
|
|
4279
4344
|
const cache = {
|
|
4280
4345
|
fetchedAt: this.fetchedAt,
|
|
4281
4346
|
snapshot: this.snapshot ?? void 0,
|
|
4282
4347
|
prices: Object.fromEntries(this.prices),
|
|
4283
4348
|
capabilities: Object.fromEntries(this.capabilities)
|
|
4284
4349
|
};
|
|
4285
|
-
await
|
|
4350
|
+
await fs10.writeFile(this.opts.cacheFile, JSON.stringify(cache, null, 2), "utf-8");
|
|
4286
4351
|
} catch {
|
|
4287
4352
|
}
|
|
4288
4353
|
}
|
|
@@ -6179,8 +6244,8 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : "") + textToolSuffix,
|
|
|
6179
6244
|
tierId: this.id,
|
|
6180
6245
|
sessionId: this.taskId,
|
|
6181
6246
|
requireApproval: false,
|
|
6182
|
-
saveSnapshot: async (
|
|
6183
|
-
this.store?.addFileSnapshot(this.taskId,
|
|
6247
|
+
saveSnapshot: async (path31, content) => {
|
|
6248
|
+
this.store?.addFileSnapshot(this.taskId, path31, content);
|
|
6184
6249
|
},
|
|
6185
6250
|
sendPeerSync: (to, syncType, content) => {
|
|
6186
6251
|
this.peerBus?.send(this.id, to, syncType, this.assignment?.subtaskId ?? "", content);
|
|
@@ -6350,9 +6415,9 @@ ${assignment.expectedOutput}`;
|
|
|
6350
6415
|
const { promisify: promisify4 } = await import('util');
|
|
6351
6416
|
const execAsync3 = promisify4(exec3);
|
|
6352
6417
|
for (const artifactPath of artifactPaths) {
|
|
6353
|
-
const absolutePath =
|
|
6418
|
+
const absolutePath = path26.resolve(process.cwd(), artifactPath);
|
|
6354
6419
|
try {
|
|
6355
|
-
const stat = await
|
|
6420
|
+
const stat = await fs10.stat(absolutePath);
|
|
6356
6421
|
if (!stat.isFile()) {
|
|
6357
6422
|
issues.push(`Expected artifact is not a file: ${artifactPath}`);
|
|
6358
6423
|
continue;
|
|
@@ -6362,7 +6427,7 @@ ${assignment.expectedOutput}`;
|
|
|
6362
6427
|
continue;
|
|
6363
6428
|
}
|
|
6364
6429
|
if (!/\.pdf$/i.test(artifactPath)) {
|
|
6365
|
-
const content = await
|
|
6430
|
+
const content = await fs10.readFile(absolutePath, "utf-8");
|
|
6366
6431
|
if (!content.trim()) {
|
|
6367
6432
|
issues.push(`Artifact content is empty: ${artifactPath}`);
|
|
6368
6433
|
continue;
|
|
@@ -6371,7 +6436,7 @@ ${assignment.expectedOutput}`;
|
|
|
6371
6436
|
issues.push(`PDF artifact looks too small to be valid: ${artifactPath}`);
|
|
6372
6437
|
continue;
|
|
6373
6438
|
}
|
|
6374
|
-
const ext =
|
|
6439
|
+
const ext = path26.extname(absolutePath).toLowerCase();
|
|
6375
6440
|
try {
|
|
6376
6441
|
if (ext === ".ts" || ext === ".tsx") {
|
|
6377
6442
|
await execAsync3(`npx tsc --noEmit ${absolutePath}`, { timeout: 1e4 });
|
|
@@ -8462,16 +8527,16 @@ function resolveInWorkspace(workspaceRoot, input) {
|
|
|
8462
8527
|
if (typeof input !== "string" || input.length === 0) {
|
|
8463
8528
|
throw new WorkspaceSandboxError(String(input), workspaceRoot);
|
|
8464
8529
|
}
|
|
8465
|
-
const root =
|
|
8466
|
-
const abs =
|
|
8467
|
-
const rel =
|
|
8468
|
-
if (rel === "" || rel === ".") ; else if (rel.startsWith("..") ||
|
|
8530
|
+
const root = path26.resolve(workspaceRoot);
|
|
8531
|
+
const abs = path26.isAbsolute(input) ? path26.resolve(input) : path26.resolve(root, input);
|
|
8532
|
+
const rel = path26.relative(root, abs);
|
|
8533
|
+
if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path26.isAbsolute(rel)) {
|
|
8469
8534
|
throw new WorkspaceSandboxError(input, root);
|
|
8470
8535
|
}
|
|
8471
8536
|
try {
|
|
8472
|
-
const real =
|
|
8473
|
-
const realRel =
|
|
8474
|
-
if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") ||
|
|
8537
|
+
const real = fs24.realpathSync(abs);
|
|
8538
|
+
const realRel = path26.relative(root, real);
|
|
8539
|
+
if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path26.isAbsolute(realRel))) {
|
|
8475
8540
|
throw new WorkspaceSandboxError(input, root);
|
|
8476
8541
|
}
|
|
8477
8542
|
} catch (e) {
|
|
@@ -8498,7 +8563,7 @@ var FileReadTool = class extends BaseTool {
|
|
|
8498
8563
|
const absPath = resolveInWorkspace(this.workspaceRoot, filePath);
|
|
8499
8564
|
const offset = input["offset"] ?? 1;
|
|
8500
8565
|
const limit = input["limit"];
|
|
8501
|
-
const content = await
|
|
8566
|
+
const content = await fs10.readFile(absPath, "utf-8");
|
|
8502
8567
|
const lines = content.split("\n");
|
|
8503
8568
|
const start = Math.max(0, offset - 1);
|
|
8504
8569
|
const end = limit ? start + limit : lines.length;
|
|
@@ -8527,13 +8592,13 @@ var FileWriteTool = class extends BaseTool {
|
|
|
8527
8592
|
const content = input["content"];
|
|
8528
8593
|
if (options.saveSnapshot) {
|
|
8529
8594
|
try {
|
|
8530
|
-
const oldContent = await
|
|
8595
|
+
const oldContent = await fs10.readFile(absPath, "utf-8");
|
|
8531
8596
|
await options.saveSnapshot(absPath, oldContent);
|
|
8532
8597
|
} catch {
|
|
8533
8598
|
}
|
|
8534
8599
|
}
|
|
8535
|
-
await
|
|
8536
|
-
await
|
|
8600
|
+
await fs10.mkdir(path26.dirname(absPath), { recursive: true });
|
|
8601
|
+
await fs10.writeFile(absPath, content, "utf-8");
|
|
8537
8602
|
return `Written ${content.length} characters to ${filePath}`;
|
|
8538
8603
|
}
|
|
8539
8604
|
};
|
|
@@ -8559,7 +8624,7 @@ var FileEditTool = class extends BaseTool {
|
|
|
8559
8624
|
const oldString = input["old_string"];
|
|
8560
8625
|
const newString = input["new_string"];
|
|
8561
8626
|
const replaceAll = input["replace_all"] ?? false;
|
|
8562
|
-
const rawContent = await
|
|
8627
|
+
const rawContent = await fs10.readFile(absPath, "utf-8");
|
|
8563
8628
|
if (options.saveSnapshot) {
|
|
8564
8629
|
await options.saveSnapshot(absPath, rawContent);
|
|
8565
8630
|
}
|
|
@@ -8571,7 +8636,7 @@ var FileEditTool = class extends BaseTool {
|
|
|
8571
8636
|
);
|
|
8572
8637
|
}
|
|
8573
8638
|
const updated = replaceAll ? content.split(normalizedOld).join(newString) : content.replace(normalizedOld, newString);
|
|
8574
|
-
await
|
|
8639
|
+
await fs10.writeFile(absPath, updated, "utf-8");
|
|
8575
8640
|
const count = replaceAll ? content.split(normalizedOld).length - 1 : 1;
|
|
8576
8641
|
return `Replaced ${count} occurrence(s) in ${filePath}`;
|
|
8577
8642
|
}
|
|
@@ -8594,12 +8659,12 @@ var FileDeleteTool = class extends BaseTool {
|
|
|
8594
8659
|
const absPath = resolveInWorkspace(this.workspaceRoot, filePath);
|
|
8595
8660
|
if (options.saveSnapshot) {
|
|
8596
8661
|
try {
|
|
8597
|
-
const oldContent = await
|
|
8662
|
+
const oldContent = await fs10.readFile(absPath, "utf-8");
|
|
8598
8663
|
await options.saveSnapshot(absPath, oldContent);
|
|
8599
8664
|
} catch {
|
|
8600
8665
|
}
|
|
8601
8666
|
}
|
|
8602
|
-
await
|
|
8667
|
+
await fs10.rm(absPath, { recursive: false });
|
|
8603
8668
|
return `Deleted ${filePath}`;
|
|
8604
8669
|
}
|
|
8605
8670
|
};
|
|
@@ -8616,7 +8681,7 @@ var FileListTool = class extends BaseTool {
|
|
|
8616
8681
|
async execute(input, _options) {
|
|
8617
8682
|
const inputPath = input["path"] || ".";
|
|
8618
8683
|
const absPath = resolveInWorkspace(this.workspaceRoot, inputPath);
|
|
8619
|
-
const entries = await
|
|
8684
|
+
const entries = await fs10.readdir(absPath, { withFileTypes: true });
|
|
8620
8685
|
return entries.map((e) => `${e.isDirectory() ? "[DIR] " : " "}${e.name}`).join("\n") || "(empty directory)";
|
|
8621
8686
|
}
|
|
8622
8687
|
};
|
|
@@ -9029,8 +9094,8 @@ var ImageAnalyzeTool = class extends BaseTool {
|
|
|
9029
9094
|
}
|
|
9030
9095
|
};
|
|
9031
9096
|
async function fileToImageAttachment(filePath) {
|
|
9032
|
-
const data = await
|
|
9033
|
-
const ext =
|
|
9097
|
+
const data = await fs10.readFile(filePath);
|
|
9098
|
+
const ext = path26.extname(filePath).toLowerCase();
|
|
9034
9099
|
const mimeMap = {
|
|
9035
9100
|
".jpg": "image/jpeg",
|
|
9036
9101
|
".jpeg": "image/jpeg",
|
|
@@ -9064,14 +9129,14 @@ var PDFCreateTool = class extends BaseTool {
|
|
|
9064
9129
|
const filePath = input["path"];
|
|
9065
9130
|
const content = input["content"];
|
|
9066
9131
|
const title = input["title"];
|
|
9067
|
-
const dir =
|
|
9068
|
-
if (!
|
|
9069
|
-
|
|
9132
|
+
const dir = path26.dirname(filePath);
|
|
9133
|
+
if (!fs24.existsSync(dir)) {
|
|
9134
|
+
fs24.mkdirSync(dir, { recursive: true });
|
|
9070
9135
|
}
|
|
9071
9136
|
return new Promise((resolve, reject) => {
|
|
9072
9137
|
try {
|
|
9073
9138
|
const doc = new PDFDocument({ margin: 50 });
|
|
9074
|
-
const stream =
|
|
9139
|
+
const stream = fs24.createWriteStream(filePath);
|
|
9075
9140
|
doc.pipe(stream);
|
|
9076
9141
|
if (title) {
|
|
9077
9142
|
doc.info["Title"] = title;
|
|
@@ -9149,22 +9214,22 @@ var CodeInterpreterTool = class extends BaseTool {
|
|
|
9149
9214
|
}
|
|
9150
9215
|
cmdPrefix = NODE_CMD;
|
|
9151
9216
|
}
|
|
9152
|
-
const tmpDir =
|
|
9153
|
-
if (!
|
|
9154
|
-
|
|
9217
|
+
const tmpDir = path26.join(this.workspaceRoot, ".cascade", "tmp");
|
|
9218
|
+
if (!fs24.existsSync(tmpDir)) {
|
|
9219
|
+
fs24.mkdirSync(tmpDir, { recursive: true });
|
|
9155
9220
|
}
|
|
9156
9221
|
const extension = language === "python" ? "py" : "js";
|
|
9157
9222
|
const fileName = `intp_${randomUUID().slice(0, 8)}.${extension}`;
|
|
9158
|
-
const filePath =
|
|
9159
|
-
|
|
9223
|
+
const filePath = path26.join(tmpDir, fileName);
|
|
9224
|
+
fs24.writeFileSync(filePath, code, "utf-8");
|
|
9160
9225
|
const execArgs = [filePath, ...args];
|
|
9161
9226
|
return new Promise((resolve) => {
|
|
9162
9227
|
const startMs = Date.now();
|
|
9163
9228
|
execFile(cmdPrefix, execArgs, { cwd: this.workspaceRoot, timeout: 3e4 }, (error, stdout, stderr) => {
|
|
9164
9229
|
const duration = Date.now() - startMs;
|
|
9165
9230
|
try {
|
|
9166
|
-
if (
|
|
9167
|
-
|
|
9231
|
+
if (fs24.existsSync(filePath)) {
|
|
9232
|
+
fs24.unlinkSync(filePath);
|
|
9168
9233
|
}
|
|
9169
9234
|
} catch (cleanupErr) {
|
|
9170
9235
|
console.error(`Failed to cleanup interpreter script ${filePath}:`, cleanupErr);
|
|
@@ -9443,7 +9508,7 @@ var GlobTool = class extends BaseTool {
|
|
|
9443
9508
|
};
|
|
9444
9509
|
async execute(input, _options) {
|
|
9445
9510
|
const pattern = input["pattern"];
|
|
9446
|
-
const searchPath = input["path"] ?
|
|
9511
|
+
const searchPath = input["path"] ? path26.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
|
|
9447
9512
|
const matches = await glob(pattern, {
|
|
9448
9513
|
cwd: searchPath,
|
|
9449
9514
|
ignore: ["node_modules/**", ".git/**", "dist/**", "build/**"],
|
|
@@ -9456,7 +9521,7 @@ var GlobTool = class extends BaseTool {
|
|
|
9456
9521
|
const withMtime = await Promise.all(
|
|
9457
9522
|
matches.map(async (rel) => {
|
|
9458
9523
|
try {
|
|
9459
|
-
const stat = await
|
|
9524
|
+
const stat = await fs10.stat(path26.join(searchPath, rel));
|
|
9460
9525
|
return { rel, mtime: stat.mtimeMs };
|
|
9461
9526
|
} catch {
|
|
9462
9527
|
return { rel, mtime: 0 };
|
|
@@ -9505,7 +9570,7 @@ var GrepTool = class extends BaseTool {
|
|
|
9505
9570
|
};
|
|
9506
9571
|
async execute(input, _options) {
|
|
9507
9572
|
const pattern = input["pattern"];
|
|
9508
|
-
const searchPath = input["path"] ?
|
|
9573
|
+
const searchPath = input["path"] ? path26.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
|
|
9509
9574
|
const globPattern = input["glob"];
|
|
9510
9575
|
const outputMode = input["output_mode"] ?? "content";
|
|
9511
9576
|
const context = input["context"] ?? 0;
|
|
@@ -9559,15 +9624,15 @@ var GrepTool = class extends BaseTool {
|
|
|
9559
9624
|
nodir: true
|
|
9560
9625
|
});
|
|
9561
9626
|
} catch {
|
|
9562
|
-
files = [
|
|
9627
|
+
files = [path26.relative(searchPath, searchPath) || "."];
|
|
9563
9628
|
}
|
|
9564
9629
|
const results = [];
|
|
9565
9630
|
let totalCount = 0;
|
|
9566
9631
|
for (const rel of files) {
|
|
9567
|
-
const abs =
|
|
9632
|
+
const abs = path26.join(searchPath, rel);
|
|
9568
9633
|
let content;
|
|
9569
9634
|
try {
|
|
9570
|
-
content = await
|
|
9635
|
+
content = await fs10.readFile(abs, "utf-8");
|
|
9571
9636
|
} catch {
|
|
9572
9637
|
continue;
|
|
9573
9638
|
}
|
|
@@ -9926,10 +9991,10 @@ var ToolRegistry = class extends EventEmitter {
|
|
|
9926
9991
|
}
|
|
9927
9992
|
isIgnored(filePath) {
|
|
9928
9993
|
if (!filePath) return false;
|
|
9929
|
-
const abs =
|
|
9930
|
-
const rel =
|
|
9931
|
-
if (!rel || rel.startsWith("..") ||
|
|
9932
|
-
const posixRel = rel.split(
|
|
9994
|
+
const abs = path26.resolve(this.workspaceRoot, filePath);
|
|
9995
|
+
const rel = path26.relative(this.workspaceRoot, abs);
|
|
9996
|
+
if (!rel || rel.startsWith("..") || path26.isAbsolute(rel)) return true;
|
|
9997
|
+
const posixRel = rel.split(path26.sep).join("/");
|
|
9933
9998
|
return this.ignoreMatcher.ignores(posixRel);
|
|
9934
9999
|
}
|
|
9935
10000
|
};
|
|
@@ -10452,7 +10517,7 @@ var TaskAnalyzer = class {
|
|
|
10452
10517
|
analysisCache.clear();
|
|
10453
10518
|
}
|
|
10454
10519
|
};
|
|
10455
|
-
var DEFAULT_STATS_FILE =
|
|
10520
|
+
var DEFAULT_STATS_FILE = path26.join(os8.homedir(), ".cascade", "model-perf.json");
|
|
10456
10521
|
var ModelPerformanceTracker = class {
|
|
10457
10522
|
stats = /* @__PURE__ */ new Map();
|
|
10458
10523
|
featureStats = /* @__PURE__ */ new Map();
|
|
@@ -10465,7 +10530,7 @@ var ModelPerformanceTracker = class {
|
|
|
10465
10530
|
if (this.loaded) return;
|
|
10466
10531
|
this.loaded = true;
|
|
10467
10532
|
try {
|
|
10468
|
-
const raw = await
|
|
10533
|
+
const raw = await fs10.readFile(this.statsFile, "utf-8");
|
|
10469
10534
|
const parsed = JSON.parse(raw);
|
|
10470
10535
|
if (parsed.models) {
|
|
10471
10536
|
for (const [key, stat] of Object.entries(parsed.models)) this.stats.set(key, stat);
|
|
@@ -10484,12 +10549,12 @@ var ModelPerformanceTracker = class {
|
|
|
10484
10549
|
}
|
|
10485
10550
|
async save() {
|
|
10486
10551
|
try {
|
|
10487
|
-
await
|
|
10552
|
+
await fs10.mkdir(path26.dirname(this.statsFile), { recursive: true });
|
|
10488
10553
|
const modelsObj = {};
|
|
10489
10554
|
const featuresObj = {};
|
|
10490
10555
|
for (const [key, stat] of this.stats) modelsObj[key] = stat;
|
|
10491
10556
|
for (const [key, stat] of this.featureStats) featuresObj[key] = stat;
|
|
10492
|
-
await
|
|
10557
|
+
await fs10.writeFile(this.statsFile, JSON.stringify({ models: modelsObj, features: featuresObj }, null, 2), "utf-8");
|
|
10493
10558
|
} catch {
|
|
10494
10559
|
}
|
|
10495
10560
|
}
|
|
@@ -11023,9 +11088,9 @@ Required capability: ${description.slice(0, 300)}`;
|
|
|
11023
11088
|
* any dangerous action, so a silently-reloaded tool can't act without approval. */
|
|
11024
11089
|
async loadPersistedTools() {
|
|
11025
11090
|
if (!this.workspacePath || !this.persistEnabled) return;
|
|
11026
|
-
const file =
|
|
11091
|
+
const file = path26.join(this.workspacePath, ".cascade", DYNAMIC_TOOLS_FILE);
|
|
11027
11092
|
try {
|
|
11028
|
-
const raw = await
|
|
11093
|
+
const raw = await fs10.readFile(file, "utf-8");
|
|
11029
11094
|
const specs = JSON.parse(raw);
|
|
11030
11095
|
if (!Array.isArray(specs)) return;
|
|
11031
11096
|
let loaded = 0;
|
|
@@ -11047,11 +11112,11 @@ Required capability: ${description.slice(0, 300)}`;
|
|
|
11047
11112
|
}
|
|
11048
11113
|
async persist() {
|
|
11049
11114
|
if (!this.workspacePath || !this.persistEnabled) return;
|
|
11050
|
-
const dir =
|
|
11051
|
-
const file =
|
|
11115
|
+
const dir = path26.join(this.workspacePath, ".cascade");
|
|
11116
|
+
const file = path26.join(dir, DYNAMIC_TOOLS_FILE);
|
|
11052
11117
|
try {
|
|
11053
|
-
await
|
|
11054
|
-
await
|
|
11118
|
+
await fs10.mkdir(dir, { recursive: true });
|
|
11119
|
+
await fs10.writeFile(file, JSON.stringify(Array.from(this.specs.values()), null, 2), "utf-8");
|
|
11055
11120
|
} catch (err) {
|
|
11056
11121
|
this.log(`[tool-creator] Failed to persist tools: ${err instanceof Error ? err.message : String(err)}`);
|
|
11057
11122
|
}
|
|
@@ -11068,12 +11133,12 @@ var WorldStateDB = class {
|
|
|
11068
11133
|
constructor(workspacePath, debugMode = false) {
|
|
11069
11134
|
this.workspacePath = workspacePath;
|
|
11070
11135
|
this.debugMode = debugMode;
|
|
11071
|
-
const cascadeDir =
|
|
11072
|
-
if (!
|
|
11073
|
-
|
|
11136
|
+
const cascadeDir = path26.join(workspacePath, ".cascade");
|
|
11137
|
+
if (!fs24.existsSync(cascadeDir)) {
|
|
11138
|
+
fs24.mkdirSync(cascadeDir, { recursive: true });
|
|
11074
11139
|
}
|
|
11075
|
-
this.keyPath =
|
|
11076
|
-
this.dbPath =
|
|
11140
|
+
this.keyPath = path26.join(cascadeDir, "world_state.key");
|
|
11141
|
+
this.dbPath = path26.join(cascadeDir, "world_state.db");
|
|
11077
11142
|
this.initEncryptionKey();
|
|
11078
11143
|
this.db = new Database2(this.dbPath);
|
|
11079
11144
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -11103,11 +11168,11 @@ var WorldStateDB = class {
|
|
|
11103
11168
|
dbPath;
|
|
11104
11169
|
encryptionKey;
|
|
11105
11170
|
initEncryptionKey() {
|
|
11106
|
-
if (
|
|
11107
|
-
this.encryptionKey =
|
|
11171
|
+
if (fs24.existsSync(this.keyPath)) {
|
|
11172
|
+
this.encryptionKey = fs24.readFileSync(this.keyPath);
|
|
11108
11173
|
} else {
|
|
11109
11174
|
this.encryptionKey = crypto.randomBytes(32);
|
|
11110
|
-
|
|
11175
|
+
fs24.writeFileSync(this.keyPath, this.encryptionKey);
|
|
11111
11176
|
}
|
|
11112
11177
|
}
|
|
11113
11178
|
encrypt(text) {
|
|
@@ -11248,6 +11313,22 @@ var WorldStateDB = class {
|
|
|
11248
11313
|
}
|
|
11249
11314
|
return selected.slice(0, limit).map((f) => `- ${f.entity} ${f.relation} ${f.value}`).join("\n");
|
|
11250
11315
|
}
|
|
11316
|
+
/**
|
|
11317
|
+
* Delete one fact by its (normalized) entity + relation key. Returns whether
|
|
11318
|
+
* a row was actually removed. Powers the desktop Knowledge tab's per-fact
|
|
11319
|
+
* delete — users can prune what the planner remembers about their project.
|
|
11320
|
+
*/
|
|
11321
|
+
deleteFact(entity, relation) {
|
|
11322
|
+
const e = normalizeKey(entity);
|
|
11323
|
+
const r = normalizeKey(relation);
|
|
11324
|
+
if (!e || !r) return false;
|
|
11325
|
+
const result = this.db.prepare("DELETE FROM facts WHERE entity = ? AND relation = ?").run(e, r);
|
|
11326
|
+
return result.changes > 0;
|
|
11327
|
+
}
|
|
11328
|
+
/** Delete every fact. Returns how many were removed. */
|
|
11329
|
+
clearFacts() {
|
|
11330
|
+
return this.db.prepare("DELETE FROM facts").run().changes;
|
|
11331
|
+
}
|
|
11251
11332
|
// ── Export / Import ──────────────────────────
|
|
11252
11333
|
//
|
|
11253
11334
|
// Knowledge travels DECRYPTED in the export bundle (a portable plaintext
|
|
@@ -11296,9 +11377,9 @@ var WorldStateDB = class {
|
|
|
11296
11377
|
dumpDebugIfNeeded() {
|
|
11297
11378
|
if (!this.debugMode) return;
|
|
11298
11379
|
try {
|
|
11299
|
-
const dumpPath =
|
|
11380
|
+
const dumpPath = path26.join(os8.tmpdir(), "cascade_world_state_debug.json");
|
|
11300
11381
|
const entries = this.getAllEntries();
|
|
11301
|
-
|
|
11382
|
+
fs24.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
|
|
11302
11383
|
} catch (err) {
|
|
11303
11384
|
console.error("Failed to dump debug world state", err);
|
|
11304
11385
|
}
|
|
@@ -13716,10 +13797,10 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, a
|
|
|
13716
13797
|
}
|
|
13717
13798
|
} catch {
|
|
13718
13799
|
}
|
|
13719
|
-
const configPath =
|
|
13800
|
+
const configPath = path26.join(workspacePath, ".cascade", "config.json");
|
|
13720
13801
|
try {
|
|
13721
|
-
await
|
|
13722
|
-
await
|
|
13802
|
+
await fs10.mkdir(path26.dirname(configPath), { recursive: true });
|
|
13803
|
+
await fs10.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
13723
13804
|
} catch (err) {
|
|
13724
13805
|
const msg = err instanceof Error ? err.message : String(err);
|
|
13725
13806
|
dispatch({
|
|
@@ -13770,9 +13851,9 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, a
|
|
|
13770
13851
|
if (msg.includes("non-text parts")) return;
|
|
13771
13852
|
originalLog(...args);
|
|
13772
13853
|
};
|
|
13773
|
-
const store = new MemoryStore(
|
|
13854
|
+
const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
|
|
13774
13855
|
storeRef.current = store;
|
|
13775
|
-
globalStoreRef.current = new MemoryStore(
|
|
13856
|
+
globalStoreRef.current = new MemoryStore(path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE));
|
|
13776
13857
|
const identityRows = store.listIdentities().map((i) => ({ id: i.id, name: i.name, isDefault: i.isDefault }));
|
|
13777
13858
|
setIdentities(identityRows);
|
|
13778
13859
|
let initialIdentityId = config.defaultIdentityId ?? identityRows.find((i) => i.isDefault)?.id ?? identityRows[0]?.id;
|
|
@@ -13835,14 +13916,14 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, a
|
|
|
13835
13916
|
onThemeChange: (name) => setTheme(getTheme(name)),
|
|
13836
13917
|
onExport: async (fmt) => {
|
|
13837
13918
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
13838
|
-
const exportPath =
|
|
13919
|
+
const exportPath = path26.join(workspacePath, `cascade-export-${stamp}.${fmt === "json" ? "json" : "md"}`);
|
|
13839
13920
|
if (fmt === "json") {
|
|
13840
|
-
await
|
|
13921
|
+
await fs10.writeFile(exportPath, JSON.stringify({ sessionId: sessionIdRef.current, messages: state.messages }, null, 2), "utf-8");
|
|
13841
13922
|
} else {
|
|
13842
13923
|
const markdown = state.messages.map((msg) => `## ${msg.role.toUpperCase()} \u2014 ${msg.timestamp}
|
|
13843
13924
|
|
|
13844
13925
|
${msg.content}`).join("\n\n");
|
|
13845
|
-
await
|
|
13926
|
+
await fs10.writeFile(exportPath, markdown, "utf-8");
|
|
13846
13927
|
}
|
|
13847
13928
|
},
|
|
13848
13929
|
onRollback: async () => {
|
|
@@ -13852,7 +13933,7 @@ ${msg.content}`).join("\n\n");
|
|
|
13852
13933
|
if (!snapshots.length) return "No file snapshots found for this session.";
|
|
13853
13934
|
for (const { filePath, content } of snapshots) {
|
|
13854
13935
|
try {
|
|
13855
|
-
await
|
|
13936
|
+
await fs10.writeFile(filePath, content, "utf-8");
|
|
13856
13937
|
} catch (err) {
|
|
13857
13938
|
console.error(`Restore failed: ${filePath}`, err);
|
|
13858
13939
|
}
|
|
@@ -14731,9 +14812,9 @@ function stringifySlashOutput(val) {
|
|
|
14731
14812
|
}
|
|
14732
14813
|
async function searchSessionsAndMessages(query, workspacePath) {
|
|
14733
14814
|
if (!query) return "Usage: /search <query>";
|
|
14734
|
-
const dbPath =
|
|
14815
|
+
const dbPath = path26.join(workspacePath, CASCADE_DB_FILE);
|
|
14735
14816
|
try {
|
|
14736
|
-
await
|
|
14817
|
+
await fs10.access(dbPath);
|
|
14737
14818
|
} catch {
|
|
14738
14819
|
return "No database found. Start a conversation first.";
|
|
14739
14820
|
}
|
|
@@ -14765,7 +14846,7 @@ async function searchSessionsAndMessages(query, workspacePath) {
|
|
|
14765
14846
|
async function diagnoseRuntime(config, workspacePath) {
|
|
14766
14847
|
const providers = config.providers.map((p) => `${p.type}${p.apiKey ? " (key set)" : " (no key)"}`).join("\n");
|
|
14767
14848
|
const models = [`T1: ${config.models.t1 ?? "default"}`, `T2: ${config.models.t2 ?? "default"}`, `T3: ${config.models.t3 ?? "default"}`].join("\n");
|
|
14768
|
-
const store = new MemoryStore(
|
|
14849
|
+
const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
|
|
14769
14850
|
try {
|
|
14770
14851
|
const sessions = store.listSessions(void 0, 3);
|
|
14771
14852
|
return [
|
|
@@ -14784,7 +14865,7 @@ async function diagnoseRuntime(config, workspacePath) {
|
|
|
14784
14865
|
}
|
|
14785
14866
|
async function showRecentLogs(args, workspacePath) {
|
|
14786
14867
|
const limit = Number.parseInt(args[0] ?? "10", 10) || 10;
|
|
14787
|
-
const store = new MemoryStore(
|
|
14868
|
+
const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
|
|
14788
14869
|
try {
|
|
14789
14870
|
const logs = store.listRuntimeNodeLogs(void 0, void 0, limit);
|
|
14790
14871
|
if (!logs.length) return "No recent runtime logs.";
|
|
@@ -14796,7 +14877,7 @@ async function showRecentLogs(args, workspacePath) {
|
|
|
14796
14877
|
async function loadSessionSnapshot(args, workspacePath) {
|
|
14797
14878
|
const sessionId = args[0];
|
|
14798
14879
|
if (!sessionId) return "Usage: /resume <sessionId>";
|
|
14799
|
-
const store = new MemoryStore(
|
|
14880
|
+
const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
|
|
14800
14881
|
try {
|
|
14801
14882
|
const session = store.getSession(sessionId);
|
|
14802
14883
|
if (!session) return `Session not found: ${sessionId}`;
|
|
@@ -15216,10 +15297,10 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
15216
15297
|
...anyAuto ? { cascadeAuto: true } : {}
|
|
15217
15298
|
};
|
|
15218
15299
|
const config = CascadeConfigSchema.parse(rawConfig);
|
|
15219
|
-
const configDir =
|
|
15220
|
-
await
|
|
15221
|
-
const configPath =
|
|
15222
|
-
await
|
|
15300
|
+
const configDir = path26.join(workspacePath, ".cascade");
|
|
15301
|
+
await fs10.mkdir(configDir, { recursive: true });
|
|
15302
|
+
const configPath = path26.join(workspacePath, CASCADE_CONFIG_FILE);
|
|
15303
|
+
await fs10.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
15223
15304
|
savedConfigRef.current = config;
|
|
15224
15305
|
dispatchRef.current({ type: "GO_DONE" });
|
|
15225
15306
|
} catch (err) {
|
|
@@ -15559,14 +15640,14 @@ function printTelemetryBanner() {
|
|
|
15559
15640
|
async function initCommand(workspacePath = process.cwd()) {
|
|
15560
15641
|
const spin = ora({ text: "Initializing Cascade project\u2026", color: "magenta" }).start();
|
|
15561
15642
|
try {
|
|
15562
|
-
const configDir =
|
|
15563
|
-
await
|
|
15564
|
-
const mdPath =
|
|
15643
|
+
const configDir = path26.join(workspacePath, ".cascade");
|
|
15644
|
+
await fs10.mkdir(configDir, { recursive: true });
|
|
15645
|
+
const mdPath = path26.join(workspacePath, "CASCADE.md");
|
|
15565
15646
|
if (!await fileExists(mdPath)) {
|
|
15566
15647
|
await createDefaultCascadeMd(workspacePath);
|
|
15567
15648
|
spin.succeed(chalk9.green("Created CASCADE.md"));
|
|
15568
15649
|
}
|
|
15569
|
-
const ignorePath =
|
|
15650
|
+
const ignorePath = path26.join(workspacePath, ".cascadeignore");
|
|
15570
15651
|
if (!await fileExists(ignorePath)) {
|
|
15571
15652
|
await createDefaultIgnoreFile(workspacePath);
|
|
15572
15653
|
spin.succeed(chalk9.green("Created .cascadeignore"));
|
|
@@ -15575,7 +15656,7 @@ async function initCommand(workspacePath = process.cwd()) {
|
|
|
15575
15656
|
console.log();
|
|
15576
15657
|
console.log(chalk9.magenta(" \u25C8 Cascade AI \u2014 Project initialized"));
|
|
15577
15658
|
console.log();
|
|
15578
|
-
const configPath =
|
|
15659
|
+
const configPath = path26.join(workspacePath, CASCADE_CONFIG_FILE);
|
|
15579
15660
|
if (await fileExists(configPath)) {
|
|
15580
15661
|
console.log(chalk9.yellow(" .cascade/config.json already exists \u2014 launching wizard to reconfigure."));
|
|
15581
15662
|
console.log();
|
|
@@ -15594,7 +15675,7 @@ async function initCommand(workspacePath = process.cwd()) {
|
|
|
15594
15675
|
}
|
|
15595
15676
|
async function fileExists(p) {
|
|
15596
15677
|
try {
|
|
15597
|
-
await
|
|
15678
|
+
await fs10.access(p);
|
|
15598
15679
|
return true;
|
|
15599
15680
|
} catch {
|
|
15600
15681
|
return false;
|
|
@@ -15606,7 +15687,7 @@ init_constants();
|
|
|
15606
15687
|
var TOS_WARNING = "Reusing this subscription token outside its own CLI may violate the vendor\u2019s terms of service.";
|
|
15607
15688
|
async function readJson(file) {
|
|
15608
15689
|
try {
|
|
15609
|
-
const raw = await
|
|
15690
|
+
const raw = await fs10.readFile(file, "utf-8");
|
|
15610
15691
|
return JSON.parse(raw);
|
|
15611
15692
|
} catch {
|
|
15612
15693
|
return null;
|
|
@@ -15633,7 +15714,7 @@ function fromEnv(env) {
|
|
|
15633
15714
|
return out;
|
|
15634
15715
|
}
|
|
15635
15716
|
async function fromClaudeCode(home) {
|
|
15636
|
-
const file =
|
|
15717
|
+
const file = path26.join(home, ".claude", ".credentials.json");
|
|
15637
15718
|
const data = await readJson(file);
|
|
15638
15719
|
if (!data) return [];
|
|
15639
15720
|
const oauth = data["claudeAiOauth"];
|
|
@@ -15656,7 +15737,7 @@ async function fromClaudeCode(home) {
|
|
|
15656
15737
|
return [];
|
|
15657
15738
|
}
|
|
15658
15739
|
async function fromCodex(home) {
|
|
15659
|
-
const file =
|
|
15740
|
+
const file = path26.join(home, ".codex", "auth.json");
|
|
15660
15741
|
const data = await readJson(file);
|
|
15661
15742
|
if (!data) return [];
|
|
15662
15743
|
const apiKey = str(data["OPENAI_API_KEY"]);
|
|
@@ -15679,7 +15760,7 @@ async function fromCodex(home) {
|
|
|
15679
15760
|
return [];
|
|
15680
15761
|
}
|
|
15681
15762
|
async function fromGemini(home) {
|
|
15682
|
-
const file =
|
|
15763
|
+
const file = path26.join(home, ".gemini", "oauth_creds.json");
|
|
15683
15764
|
const data = await readJson(file);
|
|
15684
15765
|
const accessToken = str(data?.["access_token"]);
|
|
15685
15766
|
if (!accessToken) return [];
|
|
@@ -15695,7 +15776,7 @@ async function fromGemini(home) {
|
|
|
15695
15776
|
}
|
|
15696
15777
|
async function fromCopilot(home) {
|
|
15697
15778
|
for (const name of ["apps.json", "hosts.json"]) {
|
|
15698
|
-
const file =
|
|
15779
|
+
const file = path26.join(home, ".config", "github-copilot", name);
|
|
15699
15780
|
const data = await readJson(file);
|
|
15700
15781
|
if (!data) continue;
|
|
15701
15782
|
for (const value of Object.values(data)) {
|
|
@@ -15749,7 +15830,7 @@ async function doctorCommand() {
|
|
|
15749
15830
|
checks.push({
|
|
15750
15831
|
label: "Cascade config",
|
|
15751
15832
|
ok: true,
|
|
15752
|
-
detail: `Loaded ${
|
|
15833
|
+
detail: `Loaded ${path26.join(process.cwd(), CASCADE_CONFIG_FILE)}`
|
|
15753
15834
|
});
|
|
15754
15835
|
const providers = [
|
|
15755
15836
|
{ type: "anthropic", name: "Anthropic" },
|
|
@@ -15905,6 +15986,11 @@ function authMiddleware(secret, required = true) {
|
|
|
15905
15986
|
}
|
|
15906
15987
|
const user = verifyToken(token, secret);
|
|
15907
15988
|
if (!user) {
|
|
15989
|
+
if (!required) {
|
|
15990
|
+
req.user = void 0;
|
|
15991
|
+
next();
|
|
15992
|
+
return;
|
|
15993
|
+
}
|
|
15908
15994
|
res.status(401).json({ error: "Invalid or expired token" });
|
|
15909
15995
|
return;
|
|
15910
15996
|
}
|
|
@@ -16216,7 +16302,7 @@ function aggregateCostStats(sessions, opts = {}) {
|
|
|
16216
16302
|
}
|
|
16217
16303
|
|
|
16218
16304
|
// src/dashboard/server.ts
|
|
16219
|
-
var __dirname$1 =
|
|
16305
|
+
var __dirname$1 = path26.dirname(fileURLToPath(import.meta.url));
|
|
16220
16306
|
var DashboardServer = class {
|
|
16221
16307
|
app;
|
|
16222
16308
|
httpServer;
|
|
@@ -16450,12 +16536,17 @@ var DashboardServer = class {
|
|
|
16450
16536
|
*/
|
|
16451
16537
|
persistConfig() {
|
|
16452
16538
|
try {
|
|
16453
|
-
const configPath =
|
|
16454
|
-
|
|
16455
|
-
|
|
16539
|
+
const configPath = path26.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
16540
|
+
fs24.mkdirSync(path26.dirname(configPath), { recursive: true });
|
|
16541
|
+
fs24.writeFileSync(configPath, JSON.stringify(this.config, null, 2), "utf-8");
|
|
16456
16542
|
} catch (err) {
|
|
16457
16543
|
console.warn(`[dashboard] Failed to persist config: ${err instanceof Error ? err.message : String(err)}`);
|
|
16458
16544
|
}
|
|
16545
|
+
try {
|
|
16546
|
+
saveGlobalCredentials(path26.join(os8.homedir(), GLOBAL_CONFIG_DIR), this.config.providers ?? []);
|
|
16547
|
+
} catch (err) {
|
|
16548
|
+
console.warn(`[dashboard] Failed to sync global credentials: ${err instanceof Error ? err.message : String(err)}`);
|
|
16549
|
+
}
|
|
16459
16550
|
}
|
|
16460
16551
|
/**
|
|
16461
16552
|
* Produce a stable dashboard JWT signing secret.
|
|
@@ -16467,15 +16558,15 @@ var DashboardServer = class {
|
|
|
16467
16558
|
resolveDashboardSecret() {
|
|
16468
16559
|
const fromConfig = this.config.dashboard.secret ?? process.env["CASCADE_DASHBOARD_SECRET"];
|
|
16469
16560
|
if (fromConfig) return fromConfig;
|
|
16470
|
-
const secretPath =
|
|
16561
|
+
const secretPath = path26.join(this.workspacePath, CASCADE_DASHBOARD_SECRET_FILE);
|
|
16471
16562
|
try {
|
|
16472
|
-
if (
|
|
16473
|
-
const existing =
|
|
16563
|
+
if (fs24.existsSync(secretPath)) {
|
|
16564
|
+
const existing = fs24.readFileSync(secretPath, "utf-8").trim();
|
|
16474
16565
|
if (existing.length >= 16) return existing;
|
|
16475
16566
|
}
|
|
16476
16567
|
const generated = randomUUID();
|
|
16477
|
-
|
|
16478
|
-
|
|
16568
|
+
fs24.mkdirSync(path26.dirname(secretPath), { recursive: true });
|
|
16569
|
+
fs24.writeFileSync(secretPath, generated, { encoding: "utf-8", mode: 384 });
|
|
16479
16570
|
if (this.config.dashboard.auth) {
|
|
16480
16571
|
console.warn(
|
|
16481
16572
|
`Dashboard auth enabled with no secret configured; persisted a generated secret to ${secretPath}. Set CASCADE_DASHBOARD_SECRET or config.dashboard.secret to override.`
|
|
@@ -16502,7 +16593,7 @@ var DashboardServer = class {
|
|
|
16502
16593
|
// ── Setup ─────────────────────────────────────
|
|
16503
16594
|
getGlobalStore() {
|
|
16504
16595
|
if (!this.globalStore) {
|
|
16505
|
-
const globalDbPath =
|
|
16596
|
+
const globalDbPath = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
16506
16597
|
this.globalStore = new MemoryStore(globalDbPath);
|
|
16507
16598
|
}
|
|
16508
16599
|
return this.globalStore;
|
|
@@ -16771,12 +16862,12 @@ ${prompt}`;
|
|
|
16771
16862
|
}
|
|
16772
16863
|
}
|
|
16773
16864
|
watchRuntimeChanges() {
|
|
16774
|
-
const workspaceDbPath =
|
|
16775
|
-
const globalDbPath =
|
|
16865
|
+
const workspaceDbPath = path26.join(this.workspacePath, CASCADE_DB_FILE);
|
|
16866
|
+
const globalDbPath = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
16776
16867
|
const watchPaths = [workspaceDbPath, globalDbPath].filter((p, index, arr) => arr.indexOf(p) === index);
|
|
16777
16868
|
for (const watchPath of watchPaths) {
|
|
16778
|
-
if (!
|
|
16779
|
-
|
|
16869
|
+
if (!fs24.existsSync(watchPath)) continue;
|
|
16870
|
+
fs24.watchFile(watchPath, { interval: 3e3 }, () => {
|
|
16780
16871
|
this.throttledBroadcast(watchPath === globalDbPath ? "global" : "workspace");
|
|
16781
16872
|
});
|
|
16782
16873
|
}
|
|
@@ -16898,7 +16989,7 @@ ${prompt}`;
|
|
|
16898
16989
|
const sessionId = req.params.id;
|
|
16899
16990
|
this.store.deleteSession(sessionId);
|
|
16900
16991
|
this.store.deleteRuntimeSession(sessionId);
|
|
16901
|
-
const globalDbPath =
|
|
16992
|
+
const globalDbPath = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
16902
16993
|
const globalStore = new MemoryStore(globalDbPath);
|
|
16903
16994
|
try {
|
|
16904
16995
|
globalStore.deleteRuntimeSession(sessionId);
|
|
@@ -16991,7 +17082,7 @@ ${prompt}`;
|
|
|
16991
17082
|
});
|
|
16992
17083
|
this.app.delete("/api/sessions", auth, (req, res) => {
|
|
16993
17084
|
const body = req.body;
|
|
16994
|
-
const globalDbPath =
|
|
17085
|
+
const globalDbPath = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
16995
17086
|
if (body?.ids && Array.isArray(body.ids) && body.ids.length > 0) {
|
|
16996
17087
|
const globalStore = new MemoryStore(globalDbPath);
|
|
16997
17088
|
try {
|
|
@@ -17014,7 +17105,7 @@ ${prompt}`;
|
|
|
17014
17105
|
});
|
|
17015
17106
|
this.app.delete("/api/runtime", auth, (_req, res) => {
|
|
17016
17107
|
this.store.deleteAllRuntimeNodes();
|
|
17017
|
-
const globalDbPath =
|
|
17108
|
+
const globalDbPath = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
17018
17109
|
const globalStore = new MemoryStore(globalDbPath);
|
|
17019
17110
|
try {
|
|
17020
17111
|
globalStore.deleteAllRuntimeNodes();
|
|
@@ -17100,12 +17191,12 @@ ${prompt}`;
|
|
|
17100
17191
|
if (body["tierLimits"]) this.config.tierLimits = { ...this.config.tierLimits, ...body["tierLimits"] };
|
|
17101
17192
|
if (body["budget"]) this.config.budget = { ...this.config.budget, ...body["budget"] };
|
|
17102
17193
|
try {
|
|
17103
|
-
const configPath =
|
|
17104
|
-
const existing =
|
|
17194
|
+
const configPath = path26.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
17195
|
+
const existing = fs24.existsSync(configPath) ? JSON.parse(fs24.readFileSync(configPath, "utf-8")) : {};
|
|
17105
17196
|
const updated = { ...existing, tierLimits: this.config.tierLimits, budget: this.config.budget };
|
|
17106
17197
|
const tmp = configPath + ".tmp";
|
|
17107
|
-
|
|
17108
|
-
|
|
17198
|
+
fs24.writeFileSync(tmp, JSON.stringify(updated, null, 2), "utf-8");
|
|
17199
|
+
fs24.renameSync(tmp, configPath);
|
|
17109
17200
|
res.json({ ok: true });
|
|
17110
17201
|
} catch (err) {
|
|
17111
17202
|
res.status(500).json({ error: `Failed to save config: ${err instanceof Error ? err.message : String(err)}` });
|
|
@@ -17133,7 +17224,7 @@ ${prompt}`;
|
|
|
17133
17224
|
this.app.get("/api/runtime", auth, (req, res) => {
|
|
17134
17225
|
const scope = req.query["scope"] ?? "workspace";
|
|
17135
17226
|
if (scope === "global") {
|
|
17136
|
-
const globalDbPath =
|
|
17227
|
+
const globalDbPath = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
17137
17228
|
const globalStore = new MemoryStore(globalDbPath);
|
|
17138
17229
|
try {
|
|
17139
17230
|
res.json({
|
|
@@ -17368,6 +17459,48 @@ ${prompt}`;
|
|
|
17368
17459
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
17369
17460
|
}
|
|
17370
17461
|
});
|
|
17462
|
+
this.app.get("/api/knowledge", auth, (_req, res) => {
|
|
17463
|
+
try {
|
|
17464
|
+
const ws = new WorldStateDB(this.workspacePath);
|
|
17465
|
+
try {
|
|
17466
|
+
const facts = ws.getAllFacts();
|
|
17467
|
+
res.json({ total: facts.length, facts });
|
|
17468
|
+
} finally {
|
|
17469
|
+
ws.close();
|
|
17470
|
+
}
|
|
17471
|
+
} catch (err) {
|
|
17472
|
+
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
17473
|
+
}
|
|
17474
|
+
});
|
|
17475
|
+
this.app.delete("/api/knowledge/fact", auth, mutationLimiter, (req, res) => {
|
|
17476
|
+
const body = req.body;
|
|
17477
|
+
if (!body.entity || !body.relation) {
|
|
17478
|
+
res.status(400).json({ error: "entity and relation are required" });
|
|
17479
|
+
return;
|
|
17480
|
+
}
|
|
17481
|
+
try {
|
|
17482
|
+
const ws = new WorldStateDB(this.workspacePath);
|
|
17483
|
+
try {
|
|
17484
|
+
res.json({ ok: true, deleted: ws.deleteFact(body.entity, body.relation) });
|
|
17485
|
+
} finally {
|
|
17486
|
+
ws.close();
|
|
17487
|
+
}
|
|
17488
|
+
} catch (err) {
|
|
17489
|
+
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
17490
|
+
}
|
|
17491
|
+
});
|
|
17492
|
+
this.app.delete("/api/knowledge", auth, mutationLimiter, (_req, res) => {
|
|
17493
|
+
try {
|
|
17494
|
+
const ws = new WorldStateDB(this.workspacePath);
|
|
17495
|
+
try {
|
|
17496
|
+
res.json({ ok: true, deleted: ws.clearFacts() });
|
|
17497
|
+
} finally {
|
|
17498
|
+
ws.close();
|
|
17499
|
+
}
|
|
17500
|
+
} catch (err) {
|
|
17501
|
+
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
17502
|
+
}
|
|
17503
|
+
});
|
|
17371
17504
|
this.app.get("/api/audit-chain", auth, async (req, res) => {
|
|
17372
17505
|
try {
|
|
17373
17506
|
const limit = Math.min(500, Math.max(1, parseInt(req.query["limit"] || "200", 10) || 200));
|
|
@@ -17386,13 +17519,13 @@ ${prompt}`;
|
|
|
17386
17519
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
17387
17520
|
}
|
|
17388
17521
|
});
|
|
17389
|
-
const prodPath =
|
|
17390
|
-
const devPath =
|
|
17391
|
-
const webDistPath =
|
|
17392
|
-
if (
|
|
17522
|
+
const prodPath = path26.resolve(__dirname$1, "../web/dist");
|
|
17523
|
+
const devPath = path26.resolve(__dirname$1, "../../web/dist");
|
|
17524
|
+
const webDistPath = fs24.existsSync(prodPath) ? prodPath : devPath;
|
|
17525
|
+
if (fs24.existsSync(webDistPath)) {
|
|
17393
17526
|
this.app.use(express.static(webDistPath));
|
|
17394
17527
|
this.app.get("*", (_req, res) => {
|
|
17395
|
-
res.sendFile(
|
|
17528
|
+
res.sendFile(path26.join(webDistPath, "index.html"));
|
|
17396
17529
|
});
|
|
17397
17530
|
} else {
|
|
17398
17531
|
this.app.get("/", (_req, res) => {
|
|
@@ -17413,7 +17546,7 @@ init_constants();
|
|
|
17413
17546
|
async function dashboardCommand(config, workspacePath = process.cwd()) {
|
|
17414
17547
|
const port = config.dashboard.port ?? DEFAULT_DASHBOARD_PORT;
|
|
17415
17548
|
const spin = ora({ text: `Starting dashboard on port ${port}\u2026`, color: "magenta" }).start();
|
|
17416
|
-
const store = new MemoryStore(
|
|
17549
|
+
const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
|
|
17417
17550
|
const server = new DashboardServer(config, store, workspacePath);
|
|
17418
17551
|
server.watchRuntimeChanges();
|
|
17419
17552
|
const gThis = globalThis;
|
|
@@ -17445,7 +17578,7 @@ init_constants();
|
|
|
17445
17578
|
function makeIdentityCommand(workspacePath = process.cwd()) {
|
|
17446
17579
|
const identity = new Command("identity").alias("id").description("Manage Cascade identities");
|
|
17447
17580
|
identity.command("list").description("List all available identities").action(() => {
|
|
17448
|
-
const store = new MemoryStore(
|
|
17581
|
+
const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
|
|
17449
17582
|
try {
|
|
17450
17583
|
const identities = store.listIdentities();
|
|
17451
17584
|
console.log(chalk9.bold("\n Identities:"));
|
|
@@ -17465,7 +17598,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
|
|
|
17465
17598
|
}
|
|
17466
17599
|
});
|
|
17467
17600
|
identity.command("create <name>").description("Create a new identity").option("-d, --desc <text>", "Description of the identity").option("-s, --system <text>", "System prompt").option("--default", "Set as default identity").action((name, options) => {
|
|
17468
|
-
const store = new MemoryStore(
|
|
17601
|
+
const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
|
|
17469
17602
|
try {
|
|
17470
17603
|
if (options.default) {
|
|
17471
17604
|
const existingDefault = store.getDefaultIdentity();
|
|
@@ -17491,7 +17624,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
|
|
|
17491
17624
|
}
|
|
17492
17625
|
});
|
|
17493
17626
|
identity.command("set-default <name>").description("Set an identity as default by name or ID").action((query) => {
|
|
17494
|
-
const store = new MemoryStore(
|
|
17627
|
+
const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
|
|
17495
17628
|
try {
|
|
17496
17629
|
const identities = store.listIdentities();
|
|
17497
17630
|
const match = identities.find((i) => i.id === query || i.name.toLowerCase() === query.toLowerCase());
|
|
@@ -17619,11 +17752,11 @@ async function exportCommand(options = {}) {
|
|
|
17619
17752
|
let store;
|
|
17620
17753
|
try {
|
|
17621
17754
|
const workspacePath = options.workspacePath ?? process.cwd();
|
|
17622
|
-
const workspaceDbPath =
|
|
17623
|
-
const globalDbPath =
|
|
17755
|
+
const workspaceDbPath = path26.join(workspacePath, CASCADE_DB_FILE);
|
|
17756
|
+
const globalDbPath = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE);
|
|
17624
17757
|
let dbPath = globalDbPath;
|
|
17625
17758
|
try {
|
|
17626
|
-
await
|
|
17759
|
+
await fs10.access(workspaceDbPath);
|
|
17627
17760
|
dbPath = workspaceDbPath;
|
|
17628
17761
|
} catch {
|
|
17629
17762
|
}
|
|
@@ -17662,8 +17795,8 @@ async function exportCommand(options = {}) {
|
|
|
17662
17795
|
const ext = format === "json" ? ".json" : ".md";
|
|
17663
17796
|
const safeName = session.title.replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").slice(0, 60);
|
|
17664
17797
|
const defaultFile = `cascade-export-${safeName}${ext}`;
|
|
17665
|
-
const outPath = options.output ?
|
|
17666
|
-
await
|
|
17798
|
+
const outPath = options.output ? path26.resolve(options.output) : path26.join(process.cwd(), defaultFile);
|
|
17799
|
+
await fs10.writeFile(outPath, content, "utf-8");
|
|
17667
17800
|
spin.succeed(chalk9.green(`Exported to ${chalk9.white(outPath)}`));
|
|
17668
17801
|
const messageCount = Array.isArray(session.messages) ? session.messages.length : 0;
|
|
17669
17802
|
console.log();
|
|
@@ -17890,11 +18023,11 @@ async function statsCommand() {
|
|
|
17890
18023
|
dotenv.config();
|
|
17891
18024
|
function warnIfBuildIsStale() {
|
|
17892
18025
|
try {
|
|
17893
|
-
const here =
|
|
18026
|
+
const here = path26.dirname(fileURLToPath(import.meta.url));
|
|
17894
18027
|
for (const rel of ["..", "../.."]) {
|
|
17895
|
-
const pkgPath =
|
|
17896
|
-
if (!
|
|
17897
|
-
const pkg = JSON.parse(
|
|
18028
|
+
const pkgPath = path26.join(here, rel, "package.json");
|
|
18029
|
+
if (!fs24.existsSync(pkgPath)) continue;
|
|
18030
|
+
const pkg = JSON.parse(fs24.readFileSync(pkgPath, "utf-8"));
|
|
17898
18031
|
if (pkg.name !== "cascade-ai") continue;
|
|
17899
18032
|
if (pkg.version && pkg.version !== CASCADE_VERSION) {
|
|
17900
18033
|
console.error(
|