@mtreeai/msapling-cli 2.3.6-beta.30 → 2.3.6-beta.31
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/index.js +953 -846
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -295,7 +295,15 @@ var init_localLlm = __esm({
|
|
|
295
295
|
|
|
296
296
|
// ../api-client/src/journal.ts
|
|
297
297
|
import { homedir as homedir2 } from "os";
|
|
298
|
-
import { join as join2 } from "path";
|
|
298
|
+
import { join as join2, dirname } from "path";
|
|
299
|
+
import {
|
|
300
|
+
appendFileSync,
|
|
301
|
+
existsSync,
|
|
302
|
+
mkdirSync,
|
|
303
|
+
readFileSync,
|
|
304
|
+
renameSync,
|
|
305
|
+
writeFileSync
|
|
306
|
+
} from "fs";
|
|
299
307
|
function getJournal() {
|
|
300
308
|
if (!journalInstance) {
|
|
301
309
|
journalInstance = new Journal();
|
|
@@ -365,10 +373,39 @@ var init_journal = __esm({
|
|
|
365
373
|
}
|
|
366
374
|
}
|
|
367
375
|
loadJsonl() {
|
|
376
|
+
try {
|
|
377
|
+
mkdirSync(dirname(this.jsonlPath), { recursive: true });
|
|
378
|
+
} catch (e) {
|
|
379
|
+
console.warn("[Journal] Failed to ensure journal dir", e);
|
|
380
|
+
}
|
|
381
|
+
if (!existsSync(this.jsonlPath)) return;
|
|
382
|
+
try {
|
|
383
|
+
const content = readFileSync(this.jsonlPath, "utf-8");
|
|
384
|
+
let malformed = 0;
|
|
385
|
+
for (const line of content.split("\n")) {
|
|
386
|
+
const trimmed = line.trim();
|
|
387
|
+
if (!trimmed) continue;
|
|
388
|
+
try {
|
|
389
|
+
this.jsonlEntries.push(JSON.parse(trimmed));
|
|
390
|
+
} catch {
|
|
391
|
+
malformed++;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
if (malformed > 0) {
|
|
395
|
+
console.warn(`[Journal] Skipped ${malformed} malformed JSONL line(s)`);
|
|
396
|
+
}
|
|
397
|
+
} catch (e) {
|
|
398
|
+
console.warn("[Journal] Failed to load JSONL fallback", e);
|
|
399
|
+
}
|
|
368
400
|
}
|
|
369
401
|
async journalAppend(entry) {
|
|
370
402
|
if (this.useJsonl) {
|
|
371
403
|
this.jsonlEntries.push(entry);
|
|
404
|
+
try {
|
|
405
|
+
appendFileSync(this.jsonlPath, JSON.stringify(entry) + "\n", "utf8");
|
|
406
|
+
} catch (e) {
|
|
407
|
+
console.warn("[Journal] Failed to append JSONL entry", e);
|
|
408
|
+
}
|
|
372
409
|
} else if (this.db) {
|
|
373
410
|
try {
|
|
374
411
|
const stmt = this.db.prepare(`
|
|
@@ -424,9 +461,18 @@ var init_journal = __esm({
|
|
|
424
461
|
async journalMarkSynced(ids) {
|
|
425
462
|
if (this.useJsonl) {
|
|
426
463
|
const now = Date.now();
|
|
464
|
+
const idSet = new Set(ids);
|
|
427
465
|
this.jsonlEntries = this.jsonlEntries.map(
|
|
428
|
-
(e) =>
|
|
466
|
+
(e) => idSet.has(e.id) ? { ...e, synced_at: now } : e
|
|
429
467
|
);
|
|
468
|
+
try {
|
|
469
|
+
const tmpPath = this.jsonlPath + ".tmp";
|
|
470
|
+
const body = this.jsonlEntries.map((e) => JSON.stringify(e)).join("\n") + (this.jsonlEntries.length > 0 ? "\n" : "");
|
|
471
|
+
writeFileSync(tmpPath, body, "utf8");
|
|
472
|
+
renameSync(tmpPath, this.jsonlPath);
|
|
473
|
+
} catch (e) {
|
|
474
|
+
console.warn("[Journal] Failed to persist mark-synced", e);
|
|
475
|
+
}
|
|
430
476
|
return;
|
|
431
477
|
}
|
|
432
478
|
if (!this.db) return;
|
|
@@ -1309,11 +1355,16 @@ var init_src = __esm({
|
|
|
1309
1355
|
body: JSON.stringify(params)
|
|
1310
1356
|
});
|
|
1311
1357
|
}
|
|
1312
|
-
async *streamChat(params) {
|
|
1358
|
+
async *streamChat(params, externalSignal) {
|
|
1313
1359
|
if (!params.frame_id) {
|
|
1314
1360
|
params = { ...params, frame_id: "earth_surface" };
|
|
1315
1361
|
}
|
|
1316
1362
|
const controller = new AbortController();
|
|
1363
|
+
const externalAbortHandler = () => controller.abort();
|
|
1364
|
+
if (externalSignal) {
|
|
1365
|
+
if (externalSignal.aborted) controller.abort();
|
|
1366
|
+
else externalSignal.addEventListener("abort", externalAbortHandler);
|
|
1367
|
+
}
|
|
1317
1368
|
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
1318
1369
|
try {
|
|
1319
1370
|
const headers = {
|
|
@@ -1378,11 +1429,15 @@ var init_src = __esm({
|
|
|
1378
1429
|
}
|
|
1379
1430
|
} catch (e) {
|
|
1380
1431
|
if (e.name === "AbortError") {
|
|
1432
|
+
if (externalSignal?.aborted) throw e;
|
|
1381
1433
|
throw new MSaplingError("Request timed out after 30s.", 408, "timeout");
|
|
1382
1434
|
}
|
|
1383
1435
|
throw e;
|
|
1384
1436
|
} finally {
|
|
1385
1437
|
clearTimeout(timeout);
|
|
1438
|
+
if (externalSignal) {
|
|
1439
|
+
externalSignal.removeEventListener("abort", externalAbortHandler);
|
|
1440
|
+
}
|
|
1386
1441
|
}
|
|
1387
1442
|
}
|
|
1388
1443
|
/**
|
|
@@ -1420,7 +1475,7 @@ var init_BaseTool = __esm({
|
|
|
1420
1475
|
// ../core/src/tools/ReadFileTool.ts
|
|
1421
1476
|
import { resolve, normalize, relative, isAbsolute } from "path";
|
|
1422
1477
|
import { readFile as readFile2 } from "fs/promises";
|
|
1423
|
-
import { existsSync } from "fs";
|
|
1478
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1424
1479
|
var ReadFileTool;
|
|
1425
1480
|
var init_ReadFileTool = __esm({
|
|
1426
1481
|
"../core/src/tools/ReadFileTool.ts"() {
|
|
@@ -1463,7 +1518,7 @@ var init_ReadFileTool = __esm({
|
|
|
1463
1518
|
};
|
|
1464
1519
|
}
|
|
1465
1520
|
const fullPath = normalizedTarget;
|
|
1466
|
-
if (!
|
|
1521
|
+
if (!existsSync2(fullPath)) {
|
|
1467
1522
|
return { content: `Error: file not found: ${args2.path}`, isError: true };
|
|
1468
1523
|
}
|
|
1469
1524
|
const offset = args2.offset ? parseInt(args2.offset) : 0;
|
|
@@ -1545,7 +1600,7 @@ var init_EditFileTool = __esm({
|
|
|
1545
1600
|
// ../core/src/tools/WriteFileTool.ts
|
|
1546
1601
|
import { resolve as resolve2, normalize as normalize2, relative as relative2, isAbsolute as isAbsolute2, join as join4 } from "path";
|
|
1547
1602
|
import { writeFile, readFile as readFile3, mkdir } from "fs/promises";
|
|
1548
|
-
import { existsSync as
|
|
1603
|
+
import { existsSync as existsSync3 } from "fs";
|
|
1549
1604
|
import { homedir as homedir3 } from "os";
|
|
1550
1605
|
import { randomBytes } from "crypto";
|
|
1551
1606
|
var MAX_CONTENT_BYTES, WriteFileTool;
|
|
@@ -1620,7 +1675,7 @@ var init_WriteFileTool = __esm({
|
|
|
1620
1675
|
}
|
|
1621
1676
|
let backedUpTo = null;
|
|
1622
1677
|
try {
|
|
1623
|
-
if (
|
|
1678
|
+
if (existsSync3(resolvedTarget)) {
|
|
1624
1679
|
const existingContent = await readFile3(resolvedTarget, "utf8");
|
|
1625
1680
|
const filename = resolvedTarget.split(/[\\/]/).pop() ?? "file";
|
|
1626
1681
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -2281,7 +2336,7 @@ var init_SubShellTool = __esm({
|
|
|
2281
2336
|
// ../core/src/tools/SearchTools.ts
|
|
2282
2337
|
import { spawn as spawn4 } from "child_process";
|
|
2283
2338
|
import { readFile as readFile4, readdir, stat } from "fs/promises";
|
|
2284
|
-
import { existsSync as
|
|
2339
|
+
import { existsSync as existsSync4, statSync } from "fs";
|
|
2285
2340
|
import { join as join6, relative as relative4, sep, resolve as resolve4, normalize as normalize3, isAbsolute as isAbsolute4 } from "path";
|
|
2286
2341
|
function globToRegExp(pattern) {
|
|
2287
2342
|
const normalised = pattern.replace(/\\/g, "/");
|
|
@@ -2463,7 +2518,7 @@ var init_SearchTools = __esm({
|
|
|
2463
2518
|
};
|
|
2464
2519
|
}
|
|
2465
2520
|
const candidate = normalizedTarget;
|
|
2466
|
-
if (
|
|
2521
|
+
if (existsSync4(candidate) && statSync(candidate).isDirectory()) {
|
|
2467
2522
|
scanRoot = candidate;
|
|
2468
2523
|
} else {
|
|
2469
2524
|
return {
|
|
@@ -2531,7 +2586,7 @@ var init_SearchTools = __esm({
|
|
|
2531
2586
|
};
|
|
2532
2587
|
}
|
|
2533
2588
|
const searchPath = args2?.path ? join6(projectRoot, String(args2.path)) : projectRoot;
|
|
2534
|
-
if (!
|
|
2589
|
+
if (!existsSync4(searchPath)) {
|
|
2535
2590
|
return {
|
|
2536
2591
|
content: `Error: path "${args2.path}" does not exist.`,
|
|
2537
2592
|
isError: true
|
|
@@ -2591,7 +2646,7 @@ var init_SearchTools = __esm({
|
|
|
2591
2646
|
|
|
2592
2647
|
// ../core/src/tools/ListDirectoryTool.ts
|
|
2593
2648
|
import { resolve as resolve5, normalize as normalize4, relative as relative5, isAbsolute as isAbsolute5, join as join7, sep as sep2 } from "path";
|
|
2594
|
-
import { readdirSync, statSync as statSync2, existsSync as
|
|
2649
|
+
import { readdirSync, statSync as statSync2, existsSync as existsSync5 } from "fs";
|
|
2595
2650
|
function humanSize(bytes) {
|
|
2596
2651
|
if (bytes < 1024) return `${bytes} B`;
|
|
2597
2652
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
@@ -2686,7 +2741,7 @@ var init_ListDirectoryTool = __esm({
|
|
|
2686
2741
|
isError: true
|
|
2687
2742
|
};
|
|
2688
2743
|
}
|
|
2689
|
-
if (!
|
|
2744
|
+
if (!existsSync5(normalizedTarget)) {
|
|
2690
2745
|
return {
|
|
2691
2746
|
content: `Error: path "${rawPath}" does not exist.`,
|
|
2692
2747
|
isError: true
|
|
@@ -2928,7 +2983,7 @@ var init_TodoTools = __esm({
|
|
|
2928
2983
|
// ../core/src/tools/PatchFileTool.ts
|
|
2929
2984
|
import { resolve as resolve6, normalize as normalize5, relative as relative6, isAbsolute as isAbsolute6, join as join8 } from "path";
|
|
2930
2985
|
import { readFile as readFile5, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
2931
|
-
import { existsSync as
|
|
2986
|
+
import { existsSync as existsSync6 } from "fs";
|
|
2932
2987
|
import { homedir as homedir4 } from "os";
|
|
2933
2988
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
2934
2989
|
var PatchFileTool;
|
|
@@ -2983,7 +3038,7 @@ var init_PatchFileTool = __esm({
|
|
|
2983
3038
|
};
|
|
2984
3039
|
}
|
|
2985
3040
|
const resolvedTarget = normalizedTarget;
|
|
2986
|
-
if (!
|
|
3041
|
+
if (!existsSync6(resolvedTarget)) {
|
|
2987
3042
|
return {
|
|
2988
3043
|
content: `Error: file not found: ${args2.path}. Use write_file to create it first.`,
|
|
2989
3044
|
isError: true
|
|
@@ -3672,7 +3727,7 @@ ${stderr}`;
|
|
|
3672
3727
|
// ../core/src/tools/NotebookReadTool.ts
|
|
3673
3728
|
import { resolve as resolve8, normalize as normalize7, relative as relative8, isAbsolute as isAbsolute8, extname } from "path";
|
|
3674
3729
|
import { readFile as readFile6 } from "fs/promises";
|
|
3675
|
-
import { existsSync as
|
|
3730
|
+
import { existsSync as existsSync7 } from "fs";
|
|
3676
3731
|
function joinSource(source) {
|
|
3677
3732
|
return Array.isArray(source) ? source.join("") : source ?? "";
|
|
3678
3733
|
}
|
|
@@ -3833,7 +3888,7 @@ var init_NotebookReadTool = __esm({
|
|
|
3833
3888
|
isError: true
|
|
3834
3889
|
};
|
|
3835
3890
|
}
|
|
3836
|
-
if (!
|
|
3891
|
+
if (!existsSync7(absPath)) {
|
|
3837
3892
|
return { content: `Error: file not found: ${absPath}`, isError: true };
|
|
3838
3893
|
}
|
|
3839
3894
|
let raw;
|
|
@@ -3897,7 +3952,7 @@ _(Note: notebook has ${nb.cells.length} cells; only first ${MAX_CELLS} shown.)_`
|
|
|
3897
3952
|
// ../core/src/tools/NotebookEditTool.ts
|
|
3898
3953
|
import { resolve as resolve9, normalize as normalize8, relative as relative9, isAbsolute as isAbsolute9, extname as extname2, join as join10 } from "path";
|
|
3899
3954
|
import { readFile as readFile7, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
3900
|
-
import { existsSync as
|
|
3955
|
+
import { existsSync as existsSync8 } from "fs";
|
|
3901
3956
|
import { homedir as homedir5 } from "os";
|
|
3902
3957
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
3903
3958
|
function normaliseSource(source) {
|
|
@@ -4027,7 +4082,7 @@ var init_NotebookEditTool = __esm({
|
|
|
4027
4082
|
isError: true
|
|
4028
4083
|
};
|
|
4029
4084
|
}
|
|
4030
|
-
if (!
|
|
4085
|
+
if (!existsSync8(absPath)) {
|
|
4031
4086
|
return { content: `Error: file not found: ${absPath}`, isError: true };
|
|
4032
4087
|
}
|
|
4033
4088
|
let rawJson;
|
|
@@ -4156,7 +4211,7 @@ Backup: ${backedUpTo}`;
|
|
|
4156
4211
|
// ../core/src/tools/MultiEditFileTool.ts
|
|
4157
4212
|
import { resolve as resolve10, normalize as normalize9, relative as relative10, isAbsolute as isAbsolute10, join as join11 } from "path";
|
|
4158
4213
|
import { readFile as readFile8, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
4159
|
-
import { existsSync as
|
|
4214
|
+
import { existsSync as existsSync9 } from "fs";
|
|
4160
4215
|
import { homedir as homedir6 } from "os";
|
|
4161
4216
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
4162
4217
|
var MAX_EDITS, MultiEditFileTool;
|
|
@@ -4260,7 +4315,7 @@ var init_MultiEditFileTool = __esm({
|
|
|
4260
4315
|
};
|
|
4261
4316
|
}
|
|
4262
4317
|
const resolvedTarget = normalizedTarget;
|
|
4263
|
-
if (!
|
|
4318
|
+
if (!existsSync9(resolvedTarget)) {
|
|
4264
4319
|
return {
|
|
4265
4320
|
content: `Error: file not found: ${args2.path}. Use write_file to create it first.`,
|
|
4266
4321
|
isError: true
|
|
@@ -4355,9 +4410,9 @@ Pre-edit content backed up to: ${backedUpTo}`;
|
|
|
4355
4410
|
});
|
|
4356
4411
|
|
|
4357
4412
|
// ../core/src/tools/MoveFileTool.ts
|
|
4358
|
-
import { resolve as resolve11, normalize as normalize10, relative as relative11, isAbsolute as isAbsolute11, dirname } from "path";
|
|
4413
|
+
import { resolve as resolve11, normalize as normalize10, relative as relative11, isAbsolute as isAbsolute11, dirname as dirname2 } from "path";
|
|
4359
4414
|
import { rename, mkdir as mkdir5, copyFile, rm, stat as stat2, readdir as readdir2 } from "fs/promises";
|
|
4360
|
-
import { existsSync as
|
|
4415
|
+
import { existsSync as existsSync10, statSync as statSync3 } from "fs";
|
|
4361
4416
|
import { randomBytes as randomBytes5 } from "crypto";
|
|
4362
4417
|
function containedPath(p, root) {
|
|
4363
4418
|
const abs = isAbsolute11(p) ? normalize10(p) : resolve11(root, p.trim());
|
|
@@ -4448,7 +4503,7 @@ var init_MoveFileTool = __esm({
|
|
|
4448
4503
|
return { content: `Security Block: ${srcCheck.reason}.`, isError: true };
|
|
4449
4504
|
}
|
|
4450
4505
|
const absSrc = srcCheck.abs;
|
|
4451
|
-
if (!
|
|
4506
|
+
if (!existsSync10(absSrc)) {
|
|
4452
4507
|
return {
|
|
4453
4508
|
content: `Error: source path "${args2.source}" does not exist.`,
|
|
4454
4509
|
isError: true
|
|
@@ -4462,7 +4517,7 @@ var init_MoveFileTool = __esm({
|
|
|
4462
4517
|
return { content: `Security Block: ${dstPrelimCheck.reason}.`, isError: true };
|
|
4463
4518
|
}
|
|
4464
4519
|
let absDst = dstPrelimCheck.abs;
|
|
4465
|
-
if (
|
|
4520
|
+
if (existsSync10(absDst) && statSync3(absDst).isDirectory() && !srcIsDir) {
|
|
4466
4521
|
const srcName = absSrc.split(/[\\/]/).pop();
|
|
4467
4522
|
absDst = resolve11(absDst, srcName);
|
|
4468
4523
|
const rel2 = relative11(projectRoot, absDst);
|
|
@@ -4480,7 +4535,7 @@ var init_MoveFileTool = __esm({
|
|
|
4480
4535
|
};
|
|
4481
4536
|
}
|
|
4482
4537
|
let backedUpTo = null;
|
|
4483
|
-
if (
|
|
4538
|
+
if (existsSync10(absDst)) {
|
|
4484
4539
|
if (!overwrite) {
|
|
4485
4540
|
return {
|
|
4486
4541
|
content: `Error: destination "${args2.destination}" already exists. Pass \`overwrite: true\` to replace it.`,
|
|
@@ -4492,7 +4547,7 @@ var init_MoveFileTool = __esm({
|
|
|
4492
4547
|
}
|
|
4493
4548
|
}
|
|
4494
4549
|
try {
|
|
4495
|
-
await mkdir5(
|
|
4550
|
+
await mkdir5(dirname2(absDst), { recursive: true });
|
|
4496
4551
|
} catch (e) {
|
|
4497
4552
|
return {
|
|
4498
4553
|
content: `Error: failed to create destination parent directory: ${e.message}`,
|
|
@@ -5176,119 +5231,428 @@ var init_Hooks = __esm({
|
|
|
5176
5231
|
}
|
|
5177
5232
|
});
|
|
5178
5233
|
|
|
5179
|
-
// ../core/src/
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
this.registerTool(new ListDirectoryTool());
|
|
5252
|
-
this.registerTool(new TodoWriteTool());
|
|
5253
|
-
this.registerTool(new TodoReadTool());
|
|
5254
|
-
this.registerTool(new PatchFileTool());
|
|
5255
|
-
this.registerTool(new DispatchAgentTool({
|
|
5256
|
-
client,
|
|
5257
|
-
projectRoot
|
|
5258
|
-
}));
|
|
5259
|
-
this.registerTool(new WebFetchTool());
|
|
5260
|
-
this.registerTool(new BashTool());
|
|
5261
|
-
this.registerTool(new NotebookReadTool());
|
|
5262
|
-
this.registerTool(new NotebookEditTool());
|
|
5263
|
-
this.registerTool(new MultiEditFileTool());
|
|
5264
|
-
this.registerTool(new MoveFileTool());
|
|
5265
|
-
this.registerTool(new DeleteFileTool());
|
|
5266
|
-
}
|
|
5267
|
-
setToolsEnabled(enabled) {
|
|
5268
|
-
this.toolsEnabled = enabled;
|
|
5269
|
-
}
|
|
5270
|
-
setMode(mode) {
|
|
5271
|
-
this.mode = mode;
|
|
5272
|
-
}
|
|
5273
|
-
getMode() {
|
|
5274
|
-
return this.mode;
|
|
5275
|
-
}
|
|
5276
|
-
setApprovalCallback(cb) {
|
|
5277
|
-
this.approvalCallback = cb;
|
|
5234
|
+
// ../core/src/diagnostics/specs.ts
|
|
5235
|
+
var specs_exports = {};
|
|
5236
|
+
__export(specs_exports, {
|
|
5237
|
+
collectSpecs: () => collectSpecs
|
|
5238
|
+
});
|
|
5239
|
+
import { cpus, totalmem, freemem, platform, arch } from "os";
|
|
5240
|
+
import { execSync } from "child_process";
|
|
5241
|
+
async function collectSpecs() {
|
|
5242
|
+
const cpuList = cpus();
|
|
5243
|
+
const totalMemBytes = totalmem();
|
|
5244
|
+
const freeMemBytes = freemem();
|
|
5245
|
+
const specs = {
|
|
5246
|
+
cpu: {
|
|
5247
|
+
cores: cpuList.length,
|
|
5248
|
+
model: cpuList[0]?.model ?? "Unknown",
|
|
5249
|
+
speed: cpuList[0]?.speed ?? 0
|
|
5250
|
+
// MHz -> convert below
|
|
5251
|
+
},
|
|
5252
|
+
memory: {
|
|
5253
|
+
totalGB: Math.round(totalMemBytes / 1024 ** 3 * 10) / 10,
|
|
5254
|
+
freeGB: Math.round(freeMemBytes / 1024 ** 3 * 10) / 10
|
|
5255
|
+
},
|
|
5256
|
+
gpu: [],
|
|
5257
|
+
disk: {
|
|
5258
|
+
freeGB: 0,
|
|
5259
|
+
totalGB: 0
|
|
5260
|
+
},
|
|
5261
|
+
platform: platform(),
|
|
5262
|
+
arch: arch(),
|
|
5263
|
+
nodeVersion: process.version.slice(1)
|
|
5264
|
+
// Remove 'v' prefix
|
|
5265
|
+
};
|
|
5266
|
+
specs.cpu.speed = Math.round(specs.cpu.speed / 1e3 * 10) / 10;
|
|
5267
|
+
try {
|
|
5268
|
+
const { statfs } = await import("fs/promises");
|
|
5269
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || "/";
|
|
5270
|
+
const stat5 = await statfs(homeDir);
|
|
5271
|
+
specs.disk.freeGB = Math.round(stat5.bavail * stat5.bsize / 1024 ** 3 * 10) / 10;
|
|
5272
|
+
specs.disk.totalGB = Math.round(stat5.blocks * stat5.bsize / 1024 ** 3 * 10) / 10;
|
|
5273
|
+
} catch {
|
|
5274
|
+
specs.disk.freeGB = -1;
|
|
5275
|
+
specs.disk.totalGB = -1;
|
|
5276
|
+
}
|
|
5277
|
+
specs.gpu = await detectGpu();
|
|
5278
|
+
return specs;
|
|
5279
|
+
}
|
|
5280
|
+
async function detectGpu() {
|
|
5281
|
+
const currentPlatform = platform();
|
|
5282
|
+
if (currentPlatform === "win32") {
|
|
5283
|
+
return detectGpuWindows();
|
|
5284
|
+
} else if (currentPlatform === "darwin") {
|
|
5285
|
+
return detectGpuMacOS();
|
|
5286
|
+
} else {
|
|
5287
|
+
return detectGpuLinux();
|
|
5288
|
+
}
|
|
5289
|
+
}
|
|
5290
|
+
function detectGpuWindows() {
|
|
5291
|
+
try {
|
|
5292
|
+
const output = execSync(
|
|
5293
|
+
'powershell -Command "Get-CimInstance Win32_VideoController | Select-Object Name, AdapterRAM"',
|
|
5294
|
+
{ timeout: 2e3, encoding: "utf-8" }
|
|
5295
|
+
);
|
|
5296
|
+
const gpus = [];
|
|
5297
|
+
const lines = output.split("\n");
|
|
5298
|
+
for (const line of lines) {
|
|
5299
|
+
const parts = line.trim().split(/\s{2,}/);
|
|
5300
|
+
if (parts[0] && parts[0] !== "Name") {
|
|
5301
|
+
const memBytes = parseInt(parts[1] || "0");
|
|
5302
|
+
gpus.push({
|
|
5303
|
+
name: parts[0],
|
|
5304
|
+
memoryGB: memBytes > 0 ? Math.round(memBytes / 1024 ** 3 * 10) / 10 : void 0
|
|
5305
|
+
});
|
|
5278
5306
|
}
|
|
5279
|
-
|
|
5280
|
-
|
|
5307
|
+
}
|
|
5308
|
+
return gpus;
|
|
5309
|
+
} catch {
|
|
5310
|
+
return [];
|
|
5311
|
+
}
|
|
5312
|
+
}
|
|
5313
|
+
function detectGpuMacOS() {
|
|
5314
|
+
try {
|
|
5315
|
+
const output = execSync("system_profiler SPDisplaysDataType -json", {
|
|
5316
|
+
timeout: 2e3,
|
|
5317
|
+
encoding: "utf-8"
|
|
5318
|
+
});
|
|
5319
|
+
const data = JSON.parse(output);
|
|
5320
|
+
const gpus = [];
|
|
5321
|
+
const displays = data.SPDisplaysDataType || [];
|
|
5322
|
+
for (const display of displays) {
|
|
5323
|
+
const chips = display["sppci_model_name"];
|
|
5324
|
+
if (chips) {
|
|
5325
|
+
gpus.push({ name: chips });
|
|
5281
5326
|
}
|
|
5282
|
-
|
|
5283
|
-
|
|
5327
|
+
}
|
|
5328
|
+
return gpus;
|
|
5329
|
+
} catch {
|
|
5330
|
+
return [];
|
|
5331
|
+
}
|
|
5332
|
+
}
|
|
5333
|
+
function detectGpuLinux() {
|
|
5334
|
+
const gpus = [];
|
|
5335
|
+
try {
|
|
5336
|
+
const output = execSync(
|
|
5337
|
+
"nvidia-smi --query-gpu=name,memory.total --format=csv,noheader",
|
|
5338
|
+
{ timeout: 2e3, encoding: "utf-8" }
|
|
5339
|
+
);
|
|
5340
|
+
const lines = output.trim().split("\n");
|
|
5341
|
+
for (const line of lines) {
|
|
5342
|
+
const [name, mem] = line.split(",");
|
|
5343
|
+
const memGB = mem ? Math.round(parseInt(mem) / 1024 * 10) / 10 : void 0;
|
|
5344
|
+
gpus.push({ name: name.trim(), memoryGB: memGB });
|
|
5345
|
+
}
|
|
5346
|
+
return gpus;
|
|
5347
|
+
} catch {
|
|
5348
|
+
}
|
|
5349
|
+
try {
|
|
5350
|
+
const output = execSync("rocm-smi --showproductname", {
|
|
5351
|
+
timeout: 2e3,
|
|
5352
|
+
encoding: "utf-8"
|
|
5353
|
+
});
|
|
5354
|
+
const lines = output.trim().split("\n");
|
|
5355
|
+
for (const line of lines) {
|
|
5356
|
+
if (line.includes("GPU")) {
|
|
5357
|
+
const match = line.match(/:\s*(.+)/);
|
|
5358
|
+
if (match) gpus.push({ name: match[1].trim() });
|
|
5284
5359
|
}
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5360
|
+
}
|
|
5361
|
+
return gpus;
|
|
5362
|
+
} catch {
|
|
5363
|
+
}
|
|
5364
|
+
try {
|
|
5365
|
+
const output = execSync("lspci | grep -i vga", {
|
|
5366
|
+
timeout: 2e3,
|
|
5367
|
+
encoding: "utf-8"
|
|
5368
|
+
});
|
|
5369
|
+
const lines = output.trim().split("\n");
|
|
5370
|
+
for (const line of lines) {
|
|
5371
|
+
const match = line.match(/:\s*(.+)/);
|
|
5372
|
+
if (match) gpus.push({ name: match[1].trim() });
|
|
5373
|
+
}
|
|
5374
|
+
return gpus;
|
|
5375
|
+
} catch {
|
|
5376
|
+
return [];
|
|
5377
|
+
}
|
|
5378
|
+
}
|
|
5379
|
+
var init_specs = __esm({
|
|
5380
|
+
"../core/src/diagnostics/specs.ts"() {
|
|
5381
|
+
"use strict";
|
|
5382
|
+
init_esm_shims();
|
|
5383
|
+
}
|
|
5384
|
+
});
|
|
5385
|
+
|
|
5386
|
+
// ../core/src/governor/ResourceGovernor.ts
|
|
5387
|
+
import { freemem as freemem2 } from "os";
|
|
5388
|
+
import { homedir as homedir7 } from "os";
|
|
5389
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
5390
|
+
import { join as join14 } from "path";
|
|
5391
|
+
function determineTier(specs) {
|
|
5392
|
+
const memGB = specs.memory.totalGB;
|
|
5393
|
+
const cores = specs.cpu.cores;
|
|
5394
|
+
if (memGB < 8 || cores < 4) return "T1";
|
|
5395
|
+
if (memGB < 16 || cores < 8) return "T2";
|
|
5396
|
+
if (memGB < 32 || cores < 16) return "T3";
|
|
5397
|
+
return "T4";
|
|
5398
|
+
}
|
|
5399
|
+
function recommendLimits(specs) {
|
|
5400
|
+
const tier = determineTier(specs);
|
|
5401
|
+
return tierTable[tier];
|
|
5402
|
+
}
|
|
5403
|
+
async function readConfigOverrides() {
|
|
5404
|
+
try {
|
|
5405
|
+
const configPath = join14(homedir7(), ".msapling", "config.json");
|
|
5406
|
+
const content = await readFile9(configPath, "utf-8");
|
|
5407
|
+
const config = JSON.parse(content);
|
|
5408
|
+
return config.limits ?? null;
|
|
5409
|
+
} catch {
|
|
5410
|
+
return null;
|
|
5411
|
+
}
|
|
5412
|
+
}
|
|
5413
|
+
async function createResourceGovernor(specs) {
|
|
5414
|
+
const recommended = recommendLimits(specs);
|
|
5415
|
+
const overrides = await readConfigOverrides();
|
|
5416
|
+
return new ResourceGovernor(recommended, overrides ?? void 0);
|
|
5417
|
+
}
|
|
5418
|
+
async function getGlobalGovernor() {
|
|
5419
|
+
if (globalGovernor) return globalGovernor;
|
|
5420
|
+
if (!globalGovernorPromise) {
|
|
5421
|
+
globalGovernorPromise = (async () => {
|
|
5422
|
+
const { collectSpecs: collectSpecs2 } = await Promise.resolve().then(() => (init_specs(), specs_exports));
|
|
5423
|
+
const specs = await collectSpecs2();
|
|
5424
|
+
const g = await createResourceGovernor(specs);
|
|
5425
|
+
globalGovernor = g;
|
|
5426
|
+
return g;
|
|
5427
|
+
})();
|
|
5428
|
+
}
|
|
5429
|
+
return globalGovernorPromise;
|
|
5430
|
+
}
|
|
5431
|
+
var tierTable, ResourceGovernor, globalGovernor, globalGovernorPromise;
|
|
5432
|
+
var init_ResourceGovernor = __esm({
|
|
5433
|
+
"../core/src/governor/ResourceGovernor.ts"() {
|
|
5434
|
+
"use strict";
|
|
5435
|
+
init_esm_shims();
|
|
5436
|
+
tierTable = {
|
|
5437
|
+
T1: { maxAgents: 1, maxParallelTools: 1, maxFileWatchers: 50, localLlmTier: "3B" },
|
|
5438
|
+
T2: { maxAgents: 2, maxParallelTools: 4, maxFileWatchers: 200, localLlmTier: "7B" },
|
|
5439
|
+
T3: { maxAgents: 4, maxParallelTools: 8, maxFileWatchers: 500, localLlmTier: "13B" },
|
|
5440
|
+
T4: { maxAgents: 8, maxParallelTools: 16, maxFileWatchers: 2e3, localLlmTier: "32B+" }
|
|
5441
|
+
};
|
|
5442
|
+
ResourceGovernor = class {
|
|
5443
|
+
activeAgents = 0;
|
|
5444
|
+
maxAgents;
|
|
5445
|
+
activeTool = 0;
|
|
5446
|
+
maxParallelTools;
|
|
5447
|
+
maxFileWatchers;
|
|
5448
|
+
memoryWarningShown = false;
|
|
5449
|
+
memoryCheckInterval = null;
|
|
5450
|
+
minFreeMemGB = 1.5;
|
|
5451
|
+
agentWaiters = [];
|
|
5452
|
+
toolWaiters = [];
|
|
5453
|
+
constructor(limits, overrides) {
|
|
5454
|
+
this.maxAgents = overrides?.maxAgents ?? limits.maxAgents;
|
|
5455
|
+
this.maxParallelTools = overrides?.maxParallelTools ?? limits.maxParallelTools;
|
|
5456
|
+
this.maxFileWatchers = overrides?.maxFileWatchers ?? limits.maxFileWatchers;
|
|
5457
|
+
this.startMemoryMonitor();
|
|
5458
|
+
}
|
|
5459
|
+
/**
|
|
5460
|
+
* Monitor free memory every 5s; block new acquires if below threshold
|
|
5461
|
+
*/
|
|
5462
|
+
startMemoryMonitor() {
|
|
5463
|
+
this.memoryCheckInterval = setInterval(() => {
|
|
5464
|
+
const freeMemBytes = freemem2();
|
|
5465
|
+
const freeMemGB = freeMemBytes / 1024 ** 3;
|
|
5466
|
+
if (freeMemGB < this.minFreeMemGB && !this.memoryWarningShown) {
|
|
5467
|
+
console.warn(
|
|
5468
|
+
`[ResourceGovernor] Low memory: ${freeMemGB.toFixed(2)}GB free (threshold: ${this.minFreeMemGB}GB)`
|
|
5469
|
+
);
|
|
5470
|
+
this.memoryWarningShown = true;
|
|
5471
|
+
} else if (freeMemGB >= this.minFreeMemGB) {
|
|
5472
|
+
this.memoryWarningShown = false;
|
|
5473
|
+
}
|
|
5474
|
+
}, 5e3);
|
|
5475
|
+
}
|
|
5476
|
+
/**
|
|
5477
|
+
* Acquire an agent slot, awaiting if the cap is currently full.
|
|
5478
|
+
* Low-memory state is logged via the monitor but does not block — the
|
|
5479
|
+
* single-warning behavior is intentional (see startMemoryMonitor).
|
|
5480
|
+
*/
|
|
5481
|
+
async acquireAgent() {
|
|
5482
|
+
if (this.activeAgents < this.maxAgents) {
|
|
5483
|
+
this.activeAgents++;
|
|
5484
|
+
return;
|
|
5485
|
+
}
|
|
5486
|
+
await new Promise((resolve19) => this.agentWaiters.push(resolve19));
|
|
5487
|
+
this.activeAgents++;
|
|
5488
|
+
}
|
|
5489
|
+
/**
|
|
5490
|
+
* Release an agent slot
|
|
5491
|
+
*/
|
|
5492
|
+
releaseAgent() {
|
|
5493
|
+
this.activeAgents = Math.max(0, this.activeAgents - 1);
|
|
5494
|
+
const next = this.agentWaiters.shift();
|
|
5495
|
+
if (next) next();
|
|
5496
|
+
}
|
|
5497
|
+
/**
|
|
5498
|
+
* Acquire a tool execution slot, awaiting if the cap is currently full.
|
|
5499
|
+
*/
|
|
5500
|
+
async acquireTool() {
|
|
5501
|
+
if (this.activeTool < this.maxParallelTools) {
|
|
5502
|
+
this.activeTool++;
|
|
5503
|
+
return;
|
|
5504
|
+
}
|
|
5505
|
+
await new Promise((resolve19) => this.toolWaiters.push(resolve19));
|
|
5506
|
+
this.activeTool++;
|
|
5507
|
+
}
|
|
5508
|
+
/**
|
|
5509
|
+
* Release a tool execution slot
|
|
5510
|
+
*/
|
|
5511
|
+
releaseTool() {
|
|
5512
|
+
this.activeTool = Math.max(0, this.activeTool - 1);
|
|
5513
|
+
const next = this.toolWaiters.shift();
|
|
5514
|
+
if (next) next();
|
|
5515
|
+
}
|
|
5516
|
+
/**
|
|
5517
|
+
* Get current active counts
|
|
5518
|
+
*/
|
|
5519
|
+
getStatus() {
|
|
5520
|
+
return {
|
|
5521
|
+
activeAgents: this.activeAgents,
|
|
5522
|
+
activeTool: this.activeTool,
|
|
5523
|
+
maxAgents: this.maxAgents,
|
|
5524
|
+
maxParallelTools: this.maxParallelTools
|
|
5525
|
+
};
|
|
5526
|
+
}
|
|
5527
|
+
/**
|
|
5528
|
+
* Clean up monitor interval
|
|
5529
|
+
*/
|
|
5530
|
+
destroy() {
|
|
5531
|
+
if (this.memoryCheckInterval) {
|
|
5532
|
+
clearInterval(this.memoryCheckInterval);
|
|
5533
|
+
this.memoryCheckInterval = null;
|
|
5534
|
+
}
|
|
5535
|
+
}
|
|
5536
|
+
};
|
|
5537
|
+
globalGovernor = null;
|
|
5538
|
+
globalGovernorPromise = null;
|
|
5539
|
+
}
|
|
5540
|
+
});
|
|
5541
|
+
|
|
5542
|
+
// ../core/src/agent/ToolExecutor.ts
|
|
5543
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
5544
|
+
var APPROVAL_GATED, ToolExecutor;
|
|
5545
|
+
var init_ToolExecutor = __esm({
|
|
5546
|
+
"../core/src/agent/ToolExecutor.ts"() {
|
|
5547
|
+
"use strict";
|
|
5548
|
+
init_esm_shims();
|
|
5549
|
+
init_ReadFileTool();
|
|
5550
|
+
init_EditFileTool();
|
|
5551
|
+
init_WriteFileTool();
|
|
5552
|
+
init_RunCommandTool();
|
|
5553
|
+
init_LSPTool();
|
|
5554
|
+
init_SubShellTool();
|
|
5555
|
+
init_SearchTools();
|
|
5556
|
+
init_ListDirectoryTool();
|
|
5557
|
+
init_TodoTools();
|
|
5558
|
+
init_PatchFileTool();
|
|
5559
|
+
init_DispatchAgentTool();
|
|
5560
|
+
init_WebFetchTool();
|
|
5561
|
+
init_BashTool();
|
|
5562
|
+
init_NotebookReadTool();
|
|
5563
|
+
init_NotebookEditTool();
|
|
5564
|
+
init_MultiEditFileTool();
|
|
5565
|
+
init_MoveFileTool();
|
|
5566
|
+
init_DeleteFileTool();
|
|
5567
|
+
init_MDrive();
|
|
5568
|
+
init_Sandbox();
|
|
5569
|
+
init_Voice();
|
|
5570
|
+
init_ShadowService();
|
|
5571
|
+
init_Hooks();
|
|
5572
|
+
init_ResourceGovernor();
|
|
5573
|
+
APPROVAL_GATED = /* @__PURE__ */ new Set(["run_command", "bash_command", "edit_file", "write_file", "patch_file", "multi_edit_file", "notebook_edit_cell", "move_file", "delete_file", "open_sub_shell"]);
|
|
5574
|
+
ToolExecutor = class {
|
|
5575
|
+
tools = /* @__PURE__ */ new Map();
|
|
5576
|
+
sandbox;
|
|
5577
|
+
mdrive;
|
|
5578
|
+
voice;
|
|
5579
|
+
shadow;
|
|
5580
|
+
// CLI-TOOLS-ENABLED-DEFAULT-ON-01 (Iter 30): default to true so the CLI's
|
|
5581
|
+
// 19 client-side tools are actually advertised to the backend in
|
|
5582
|
+
// ChatMessagePayload.tools. Previously false, and nothing in the CLI ever
|
|
5583
|
+
// called setToolsEnabled(true), so getToolSchemas() always returned [] and
|
|
5584
|
+
// the LLM had no idea tools existed — which is why every chat refused
|
|
5585
|
+
// file/shell access. Side effects are still gated by the permission mode
|
|
5586
|
+
// (default prompts before each call) and the persistent trust store.
|
|
5587
|
+
// Opt out via MSAPLING_TOOLS_ENABLED=false.
|
|
5588
|
+
toolsEnabled = process.env.MSAPLING_TOOLS_ENABLED !== "false";
|
|
5589
|
+
mode = "default";
|
|
5590
|
+
approvalCallback = null;
|
|
5591
|
+
/**
|
|
5592
|
+
* CLI-PARITY-37: Two-tier trust set.
|
|
5593
|
+
* `trustStore` — optional persistent store loaded from ~/.msapling/settings.json;
|
|
5594
|
+
* provides cross-session memory for "always" decisions.
|
|
5595
|
+
* `sessionTrust` — in-memory fallback for the current session when no
|
|
5596
|
+
* TrustStore is wired (preserves old behaviour exactly).
|
|
5597
|
+
*/
|
|
5598
|
+
trustStore = null;
|
|
5599
|
+
sessionTrust = /* @__PURE__ */ new Set();
|
|
5600
|
+
mcpRegistry = null;
|
|
5601
|
+
hooks = null;
|
|
5602
|
+
constructor(client, projectRoot) {
|
|
5603
|
+
this.sandbox = new Sandbox(projectRoot);
|
|
5604
|
+
this.mdrive = new MDriveService(client);
|
|
5605
|
+
this.voice = new VoiceService();
|
|
5606
|
+
this.shadow = new ShadowService(client);
|
|
5607
|
+
this.registerTool(new ReadFileTool());
|
|
5608
|
+
this.registerTool(new EditFileTool());
|
|
5609
|
+
this.registerTool(new WriteFileTool());
|
|
5610
|
+
this.registerTool(new RunCommandTool());
|
|
5611
|
+
this.registerTool(new LSPTool());
|
|
5612
|
+
this.registerTool(new SubShellTool());
|
|
5613
|
+
this.registerTool(new GlobFilesTool());
|
|
5614
|
+
this.registerTool(new GrepSearchTool());
|
|
5615
|
+
this.registerTool(new ListDirectoryTool());
|
|
5616
|
+
this.registerTool(new TodoWriteTool());
|
|
5617
|
+
this.registerTool(new TodoReadTool());
|
|
5618
|
+
this.registerTool(new PatchFileTool());
|
|
5619
|
+
this.registerTool(new DispatchAgentTool({
|
|
5620
|
+
client,
|
|
5621
|
+
projectRoot
|
|
5622
|
+
}));
|
|
5623
|
+
this.registerTool(new WebFetchTool());
|
|
5624
|
+
this.registerTool(new BashTool());
|
|
5625
|
+
this.registerTool(new NotebookReadTool());
|
|
5626
|
+
this.registerTool(new NotebookEditTool());
|
|
5627
|
+
this.registerTool(new MultiEditFileTool());
|
|
5628
|
+
this.registerTool(new MoveFileTool());
|
|
5629
|
+
this.registerTool(new DeleteFileTool());
|
|
5630
|
+
}
|
|
5631
|
+
setToolsEnabled(enabled) {
|
|
5632
|
+
this.toolsEnabled = enabled;
|
|
5633
|
+
}
|
|
5634
|
+
setMode(mode) {
|
|
5635
|
+
this.mode = mode;
|
|
5636
|
+
}
|
|
5637
|
+
getMode() {
|
|
5638
|
+
return this.mode;
|
|
5639
|
+
}
|
|
5640
|
+
setApprovalCallback(cb) {
|
|
5641
|
+
this.approvalCallback = cb;
|
|
5642
|
+
}
|
|
5643
|
+
setMCPRegistry(registry) {
|
|
5644
|
+
this.mcpRegistry = registry;
|
|
5645
|
+
}
|
|
5646
|
+
setHookRunner(runner) {
|
|
5647
|
+
this.hooks = runner;
|
|
5648
|
+
}
|
|
5649
|
+
/**
|
|
5650
|
+
* CLI-PARITY-37: Wire in a pre-loaded TrustStore so that "always" approval
|
|
5651
|
+
* decisions survive CLI restarts. Call this once at startup after `load()`
|
|
5652
|
+
* has been awaited.
|
|
5653
|
+
*/
|
|
5654
|
+
setTrustStore(store2) {
|
|
5655
|
+
this.trustStore = store2;
|
|
5292
5656
|
if (store2) {
|
|
5293
5657
|
for (const key of this.sessionTrust) {
|
|
5294
5658
|
store2.add(key).catch(() => {
|
|
@@ -5428,7 +5792,7 @@ ${blocker.stderr || "(empty)"}`,
|
|
|
5428
5792
|
}
|
|
5429
5793
|
await this.voice.speak(`Executing ${toolName}`, "natural");
|
|
5430
5794
|
if (toolName === "edit_file") {
|
|
5431
|
-
const content = await
|
|
5795
|
+
const content = await readFile10(args2.path, "utf-8");
|
|
5432
5796
|
const parentHash = await this.mdrive.getHash(content);
|
|
5433
5797
|
const block = await this.mdrive.proposeRemoteEdit(
|
|
5434
5798
|
args2.path,
|
|
@@ -5442,7 +5806,14 @@ ${blocker.stderr || "(empty)"}`,
|
|
|
5442
5806
|
Please approve the diff in the UI to sync this change locally.`
|
|
5443
5807
|
};
|
|
5444
5808
|
}
|
|
5445
|
-
const
|
|
5809
|
+
const governor = await getGlobalGovernor();
|
|
5810
|
+
await governor.acquireTool();
|
|
5811
|
+
let result;
|
|
5812
|
+
try {
|
|
5813
|
+
result = await tool.execute(args2, projectRoot);
|
|
5814
|
+
} finally {
|
|
5815
|
+
governor.releaseTool();
|
|
5816
|
+
}
|
|
5446
5817
|
if (this.hooks) {
|
|
5447
5818
|
this.hooks.fire({
|
|
5448
5819
|
event: "post-tool-use",
|
|
@@ -5515,14 +5886,14 @@ var init_Safety = __esm({
|
|
|
5515
5886
|
});
|
|
5516
5887
|
|
|
5517
5888
|
// ../core/src/ProjectConfig.ts
|
|
5518
|
-
import { homedir as
|
|
5519
|
-
import { join as
|
|
5520
|
-
import { existsSync as
|
|
5521
|
-
import { readFile as
|
|
5889
|
+
import { homedir as homedir8 } from "os";
|
|
5890
|
+
import { join as join15, dirname as dirname3, parse as parsePath } from "path";
|
|
5891
|
+
import { existsSync as existsSync11 } from "fs";
|
|
5892
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
5522
5893
|
async function readIfExists(path2) {
|
|
5523
5894
|
try {
|
|
5524
|
-
if (!
|
|
5525
|
-
const text = await
|
|
5895
|
+
if (!existsSync11(path2)) return null;
|
|
5896
|
+
const text = await readFile11(path2, "utf8");
|
|
5526
5897
|
return text.length > TRUNCATE_AT ? text.slice(0, TRUNCATE_AT) + "\n[...truncated]" : text;
|
|
5527
5898
|
} catch {
|
|
5528
5899
|
return null;
|
|
@@ -5530,7 +5901,7 @@ async function readIfExists(path2) {
|
|
|
5530
5901
|
}
|
|
5531
5902
|
async function findInDir(dir) {
|
|
5532
5903
|
for (const filename of FILENAMES) {
|
|
5533
|
-
const path2 =
|
|
5904
|
+
const path2 = join15(dir, filename);
|
|
5534
5905
|
const content = await readIfExists(path2);
|
|
5535
5906
|
if (content !== null) {
|
|
5536
5907
|
return { path: path2, filename, content };
|
|
@@ -5543,7 +5914,7 @@ async function findProjectConfig(start) {
|
|
|
5543
5914
|
for (let i = 0; i < 64; i++) {
|
|
5544
5915
|
const hit = await findInDir(dir);
|
|
5545
5916
|
if (hit) return hit;
|
|
5546
|
-
const parent =
|
|
5917
|
+
const parent = dirname3(dir);
|
|
5547
5918
|
if (parent === dir || parent === parsePath(dir).root) {
|
|
5548
5919
|
const rootHit = await findInDir(parent);
|
|
5549
5920
|
return rootHit;
|
|
@@ -5553,9 +5924,9 @@ async function findProjectConfig(start) {
|
|
|
5553
5924
|
return null;
|
|
5554
5925
|
}
|
|
5555
5926
|
async function findUserConfig() {
|
|
5556
|
-
const home =
|
|
5927
|
+
const home = homedir8();
|
|
5557
5928
|
if (!home) return null;
|
|
5558
|
-
const userDir =
|
|
5929
|
+
const userDir = join15(home, ".msapling");
|
|
5559
5930
|
return findInDir(userDir);
|
|
5560
5931
|
}
|
|
5561
5932
|
function buildCombined(user, project) {
|
|
@@ -5689,6 +6060,7 @@ var init_Agent = __esm({
|
|
|
5689
6060
|
init_Hooks();
|
|
5690
6061
|
init_ContextBudget();
|
|
5691
6062
|
init_ToolExecutor();
|
|
6063
|
+
init_ResourceGovernor();
|
|
5692
6064
|
Agent = class {
|
|
5693
6065
|
client;
|
|
5694
6066
|
executor;
|
|
@@ -5784,6 +6156,15 @@ var init_Agent = __esm({
|
|
|
5784
6156
|
* old backend versions or local/offline models that omit billing data).
|
|
5785
6157
|
*/
|
|
5786
6158
|
async runWorkerTurn(chatId, prompt4, model, onContent) {
|
|
6159
|
+
const governor = await getGlobalGovernor();
|
|
6160
|
+
await governor.acquireAgent();
|
|
6161
|
+
try {
|
|
6162
|
+
return await this._runWorkerTurnInner(chatId, prompt4, model, onContent);
|
|
6163
|
+
} finally {
|
|
6164
|
+
governor.releaseAgent();
|
|
6165
|
+
}
|
|
6166
|
+
}
|
|
6167
|
+
async _runWorkerTurnInner(chatId, prompt4, model, onContent) {
|
|
5787
6168
|
if (this.hooks) {
|
|
5788
6169
|
const outcomes = await this.hooks.fire({
|
|
5789
6170
|
event: "user-prompt-submit",
|
|
@@ -5909,10 +6290,14 @@ ${next}`;
|
|
|
5909
6290
|
const journal = getJournal();
|
|
5910
6291
|
let backendReachable = false;
|
|
5911
6292
|
let connectionTimeout = null;
|
|
6293
|
+
const controller = new AbortController();
|
|
6294
|
+
let timedOut = false;
|
|
5912
6295
|
try {
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
6296
|
+
connectionTimeout = setTimeout(() => {
|
|
6297
|
+
timedOut = true;
|
|
6298
|
+
controller.abort();
|
|
6299
|
+
}, 3e3);
|
|
6300
|
+
const backendStream = this.client.streamChat(params, controller.signal);
|
|
5916
6301
|
for await (const chunk of backendStream) {
|
|
5917
6302
|
if (connectionTimeout) {
|
|
5918
6303
|
clearTimeout(connectionTimeout);
|
|
@@ -5922,11 +6307,8 @@ ${next}`;
|
|
|
5922
6307
|
yield chunk;
|
|
5923
6308
|
}
|
|
5924
6309
|
} catch (error) {
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
connectionTimeout = null;
|
|
5928
|
-
}
|
|
5929
|
-
const isNetworkError = error?.code === "ECONNREFUSED" || error?.code === "ECONNRESET" || error?.code === "ETIMEDOUT" || error?.status >= 500 || error?.message?.includes("timeout");
|
|
6310
|
+
const isAbortTimeout = timedOut || error?.name === "AbortError" && controller.signal.aborted;
|
|
6311
|
+
const isNetworkError = isAbortTimeout || error?.code === "ECONNREFUSED" || error?.code === "ECONNRESET" || error?.code === "ETIMEDOUT" || error?.status >= 500;
|
|
5930
6312
|
if (!isNetworkError) {
|
|
5931
6313
|
throw error;
|
|
5932
6314
|
}
|
|
@@ -6006,6 +6388,11 @@ ${next}`;
|
|
|
6006
6388
|
source: `offline_${dialect}`
|
|
6007
6389
|
};
|
|
6008
6390
|
await journal.journalAppend(assistantEntry);
|
|
6391
|
+
} finally {
|
|
6392
|
+
if (connectionTimeout) {
|
|
6393
|
+
clearTimeout(connectionTimeout);
|
|
6394
|
+
connectionTimeout = null;
|
|
6395
|
+
}
|
|
6009
6396
|
}
|
|
6010
6397
|
if (backendReachable) {
|
|
6011
6398
|
const pending = await journal.journalListPending();
|
|
@@ -6055,9 +6442,9 @@ ${next}`;
|
|
|
6055
6442
|
});
|
|
6056
6443
|
|
|
6057
6444
|
// ../core/src/HardwareMonitor.ts
|
|
6058
|
-
import { cpus, totalmem, freemem, platform, arch, release } from "os";
|
|
6445
|
+
import { cpus as cpus2, totalmem as totalmem2, freemem as freemem3, platform as platform2, arch as arch2, release as release2 } from "os";
|
|
6059
6446
|
function cpuPercent() {
|
|
6060
|
-
const all =
|
|
6447
|
+
const all = cpus2();
|
|
6061
6448
|
let idle = 0;
|
|
6062
6449
|
let total = 0;
|
|
6063
6450
|
for (const c of all) {
|
|
@@ -6069,12 +6456,12 @@ function cpuPercent() {
|
|
|
6069
6456
|
return Math.round((1 - idle / total) * 100);
|
|
6070
6457
|
}
|
|
6071
6458
|
function takeSnapshot() {
|
|
6072
|
-
const total =
|
|
6073
|
-
const free =
|
|
6074
|
-
const cpuList =
|
|
6459
|
+
const total = totalmem2();
|
|
6460
|
+
const free = freemem3();
|
|
6461
|
+
const cpuList = cpus2();
|
|
6075
6462
|
return {
|
|
6076
|
-
platform:
|
|
6077
|
-
arch:
|
|
6463
|
+
platform: platform2(),
|
|
6464
|
+
arch: arch2(),
|
|
6078
6465
|
cores: cpuList.length,
|
|
6079
6466
|
cpuModel: cpuList[0]?.model ?? "unknown",
|
|
6080
6467
|
ramGiB: Math.round(total / 1073741824 * 10) / 10,
|
|
@@ -6084,18 +6471,18 @@ function takeSnapshot() {
|
|
|
6084
6471
|
};
|
|
6085
6472
|
}
|
|
6086
6473
|
function buildHwContext() {
|
|
6087
|
-
const cpuList =
|
|
6088
|
-
const totalMemBytes =
|
|
6474
|
+
const cpuList = cpus2();
|
|
6475
|
+
const totalMemBytes = totalmem2();
|
|
6089
6476
|
const totalMemGb = Math.round(totalMemBytes / 1073741824 * 100) / 100;
|
|
6090
6477
|
return {
|
|
6091
|
-
platform:
|
|
6092
|
-
arch:
|
|
6478
|
+
platform: platform2(),
|
|
6479
|
+
arch: arch2(),
|
|
6093
6480
|
cpu_model: cpuList[0]?.model ?? "unknown",
|
|
6094
6481
|
cpu_count: cpuList.length,
|
|
6095
6482
|
total_mem_gb: totalMemGb,
|
|
6096
6483
|
node_version: process.version,
|
|
6097
6484
|
bun_version: process.versions.bun,
|
|
6098
|
-
os_release:
|
|
6485
|
+
os_release: release2()
|
|
6099
6486
|
};
|
|
6100
6487
|
}
|
|
6101
6488
|
var init_HardwareMonitor = __esm({
|
|
@@ -6144,17 +6531,17 @@ var init_Mutex = __esm({
|
|
|
6144
6531
|
});
|
|
6145
6532
|
|
|
6146
6533
|
// ../core/src/TrustStore.ts
|
|
6147
|
-
import { join as
|
|
6148
|
-
import { homedir as
|
|
6149
|
-
import { existsSync as
|
|
6150
|
-
import { readFile as
|
|
6534
|
+
import { join as join16 } from "path";
|
|
6535
|
+
import { homedir as homedir9, platform as platform3 } from "os";
|
|
6536
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync2 } from "fs";
|
|
6537
|
+
import { readFile as readFile12, writeFile as writeFile5, chmod } from "fs/promises";
|
|
6151
6538
|
var USER_SETTINGS_PATH, TrustStore;
|
|
6152
6539
|
var init_TrustStore = __esm({
|
|
6153
6540
|
"../core/src/TrustStore.ts"() {
|
|
6154
6541
|
"use strict";
|
|
6155
6542
|
init_esm_shims();
|
|
6156
6543
|
init_Mutex();
|
|
6157
|
-
USER_SETTINGS_PATH =
|
|
6544
|
+
USER_SETTINGS_PATH = join16(homedir9(), ".msapling", "settings.json");
|
|
6158
6545
|
TrustStore = class {
|
|
6159
6546
|
/** Current in-memory set of trusted `tool:command` keys. */
|
|
6160
6547
|
trusted = /* @__PURE__ */ new Set();
|
|
@@ -6171,8 +6558,8 @@ var init_TrustStore = __esm({
|
|
|
6171
6558
|
*/
|
|
6172
6559
|
async readSettings() {
|
|
6173
6560
|
try {
|
|
6174
|
-
if (!
|
|
6175
|
-
const text = await
|
|
6561
|
+
if (!existsSync12(this.settingsPath)) return {};
|
|
6562
|
+
const text = await readFile12(this.settingsPath, "utf8");
|
|
6176
6563
|
if (!text.trim()) return {};
|
|
6177
6564
|
return JSON.parse(text);
|
|
6178
6565
|
} catch {
|
|
@@ -6184,10 +6571,10 @@ var init_TrustStore = __esm({
|
|
|
6184
6571
|
* updating `trustedCommands`.
|
|
6185
6572
|
*/
|
|
6186
6573
|
async writeSettings(settings) {
|
|
6187
|
-
const dir =
|
|
6188
|
-
if (!
|
|
6574
|
+
const dir = join16(homedir9(), ".msapling");
|
|
6575
|
+
if (!existsSync12(dir)) mkdirSync2(dir, { recursive: true });
|
|
6189
6576
|
await writeFile5(this.settingsPath, JSON.stringify(settings, null, 2), "utf8");
|
|
6190
|
-
if (
|
|
6577
|
+
if (platform3() !== "win32") {
|
|
6191
6578
|
try {
|
|
6192
6579
|
await chmod(this.settingsPath, 384);
|
|
6193
6580
|
} catch {
|
|
@@ -7922,10 +8309,10 @@ var require_keytar2 = __commonJS({
|
|
|
7922
8309
|
});
|
|
7923
8310
|
|
|
7924
8311
|
// ../core/src/Storage.ts
|
|
7925
|
-
import { join as
|
|
7926
|
-
import { homedir as
|
|
7927
|
-
import { chmodSync, existsSync as
|
|
7928
|
-
import { mkdir as mkdir6, writeFile as writeFile6, readFile as
|
|
8312
|
+
import { join as join17 } from "path";
|
|
8313
|
+
import { homedir as homedir10 } from "os";
|
|
8314
|
+
import { chmodSync, existsSync as existsSync13, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
8315
|
+
import { mkdir as mkdir6, writeFile as writeFile6, readFile as readFile13, appendFile } from "fs/promises";
|
|
7929
8316
|
import { randomBytes as randomBytes7, createHash as createHash3 } from "crypto";
|
|
7930
8317
|
function hashLine(line) {
|
|
7931
8318
|
return createHash3("sha256").update(line, "utf8").digest("hex");
|
|
@@ -7956,7 +8343,7 @@ var init_Storage = __esm({
|
|
|
7956
8343
|
*/
|
|
7957
8344
|
_ready;
|
|
7958
8345
|
constructor() {
|
|
7959
|
-
this.baseDir =
|
|
8346
|
+
this.baseDir = join17(homedir10(), ".msapling");
|
|
7960
8347
|
this._ready = this.ensureDirs();
|
|
7961
8348
|
}
|
|
7962
8349
|
async ensureDirs() {
|
|
@@ -7975,7 +8362,7 @@ var init_Storage = __esm({
|
|
|
7975
8362
|
"cache/recipes/objects"
|
|
7976
8363
|
];
|
|
7977
8364
|
for (const sub of subdirs) {
|
|
7978
|
-
const path2 =
|
|
8365
|
+
const path2 = join17(this.baseDir, sub);
|
|
7979
8366
|
await mkdir6(path2, { recursive: true });
|
|
7980
8367
|
}
|
|
7981
8368
|
if (process.platform !== "win32") {
|
|
@@ -7993,11 +8380,11 @@ var init_Storage = __esm({
|
|
|
7993
8380
|
await this._ready;
|
|
7994
8381
|
const KEYCHAIN_SERVICE = "msapling-cli";
|
|
7995
8382
|
const KEYCHAIN_ACCOUNT = "auth_token";
|
|
7996
|
-
const filePath =
|
|
8383
|
+
const filePath = join17(this.baseDir, "vault", "token");
|
|
7997
8384
|
try {
|
|
7998
8385
|
await keytar.setPassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT, token);
|
|
7999
8386
|
try {
|
|
8000
|
-
if (
|
|
8387
|
+
if (existsSync13(filePath)) {
|
|
8001
8388
|
unlinkSync(filePath);
|
|
8002
8389
|
}
|
|
8003
8390
|
} catch {
|
|
@@ -8013,7 +8400,7 @@ var init_Storage = __esm({
|
|
|
8013
8400
|
async loadToken() {
|
|
8014
8401
|
const KEYCHAIN_SERVICE = "msapling-cli";
|
|
8015
8402
|
const KEYCHAIN_ACCOUNT = "auth_token";
|
|
8016
|
-
const filePath =
|
|
8403
|
+
const filePath = join17(this.baseDir, "vault", "token");
|
|
8017
8404
|
try {
|
|
8018
8405
|
const keychainToken = await keytar.getPassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
|
|
8019
8406
|
if (keychainToken) {
|
|
@@ -8022,8 +8409,8 @@ var init_Storage = __esm({
|
|
|
8022
8409
|
} catch (e) {
|
|
8023
8410
|
console.debug(`Keychain unavailable (${e instanceof Error ? e.message : String(e)}), falling back to file storage`);
|
|
8024
8411
|
}
|
|
8025
|
-
if (
|
|
8026
|
-
const text = await
|
|
8412
|
+
if (existsSync13(filePath)) {
|
|
8413
|
+
const text = await readFile13(filePath, "utf8");
|
|
8027
8414
|
return text.trim();
|
|
8028
8415
|
}
|
|
8029
8416
|
return null;
|
|
@@ -8034,14 +8421,14 @@ var init_Storage = __esm({
|
|
|
8034
8421
|
async clearToken() {
|
|
8035
8422
|
const KEYCHAIN_SERVICE = "msapling-cli";
|
|
8036
8423
|
const KEYCHAIN_ACCOUNT = "auth_token";
|
|
8037
|
-
const filePath =
|
|
8424
|
+
const filePath = join17(this.baseDir, "vault", "token");
|
|
8038
8425
|
try {
|
|
8039
8426
|
await keytar.deletePassword(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
|
|
8040
8427
|
} catch (e) {
|
|
8041
8428
|
console.debug(`Keychain unavailable for deletion (${e instanceof Error ? e.message : String(e)})`);
|
|
8042
8429
|
}
|
|
8043
8430
|
try {
|
|
8044
|
-
if (
|
|
8431
|
+
if (existsSync13(filePath)) {
|
|
8045
8432
|
const fs3 = await import("fs/promises");
|
|
8046
8433
|
await fs3.unlink(filePath);
|
|
8047
8434
|
}
|
|
@@ -8073,15 +8460,15 @@ var init_Storage = __esm({
|
|
|
8073
8460
|
async writeVaultRef(label, value) {
|
|
8074
8461
|
await this._ready;
|
|
8075
8462
|
const hash = createHash3("sha256").update(value, "utf8").digest("hex");
|
|
8076
|
-
const objectPath =
|
|
8077
|
-
const refPath =
|
|
8463
|
+
const objectPath = join17(this.baseDir, "vault", "objects", hash);
|
|
8464
|
+
const refPath = join17(this.baseDir, "vault", "refs", label);
|
|
8078
8465
|
const refTmp = `${refPath}.tmp`;
|
|
8079
8466
|
await writeFile6(objectPath, value, "utf8");
|
|
8080
8467
|
if (process.platform !== "win32") {
|
|
8081
8468
|
chmodSync(objectPath, 384);
|
|
8082
8469
|
}
|
|
8083
8470
|
await writeFile6(refTmp, hash, "utf8");
|
|
8084
|
-
|
|
8471
|
+
renameSync2(refTmp, refPath);
|
|
8085
8472
|
return hash;
|
|
8086
8473
|
}
|
|
8087
8474
|
/**
|
|
@@ -8090,12 +8477,12 @@ var init_Storage = __esm({
|
|
|
8090
8477
|
*/
|
|
8091
8478
|
async readVaultRef(label) {
|
|
8092
8479
|
await this._ready;
|
|
8093
|
-
const refPath =
|
|
8094
|
-
if (!
|
|
8095
|
-
const hash = (await
|
|
8096
|
-
const objectPath =
|
|
8097
|
-
if (!
|
|
8098
|
-
return
|
|
8480
|
+
const refPath = join17(this.baseDir, "vault", "refs", label);
|
|
8481
|
+
if (!existsSync13(refPath)) return null;
|
|
8482
|
+
const hash = (await readFile13(refPath, "utf8")).trim();
|
|
8483
|
+
const objectPath = join17(this.baseDir, "vault", "objects", hash);
|
|
8484
|
+
if (!existsSync13(objectPath)) return null;
|
|
8485
|
+
return readFile13(objectPath, "utf8");
|
|
8099
8486
|
}
|
|
8100
8487
|
// ─────────────────────────────────────────────────────────────────────────
|
|
8101
8488
|
// CLI-ARCH-RECIPE-AT-HASH-URI-01 — recipe hash registry helpers
|
|
@@ -8118,23 +8505,23 @@ var init_Storage = __esm({
|
|
|
8118
8505
|
async registerRecipe(name, content) {
|
|
8119
8506
|
await this._ready;
|
|
8120
8507
|
const hash = createHash3("sha256").update(content, "utf8").digest("hex");
|
|
8121
|
-
const objectPath =
|
|
8122
|
-
const indexPath =
|
|
8508
|
+
const objectPath = join17(this.baseDir, "cache", "recipes", "objects", hash);
|
|
8509
|
+
const indexPath = join17(this.baseDir, "cache", "recipes", "index.json");
|
|
8123
8510
|
const indexTmp = `${indexPath}.tmp`;
|
|
8124
|
-
if (!
|
|
8511
|
+
if (!existsSync13(objectPath)) {
|
|
8125
8512
|
await writeFile6(objectPath, content, "utf8");
|
|
8126
8513
|
}
|
|
8127
8514
|
let index = {};
|
|
8128
|
-
if (
|
|
8515
|
+
if (existsSync13(indexPath)) {
|
|
8129
8516
|
try {
|
|
8130
|
-
index = JSON.parse(await
|
|
8517
|
+
index = JSON.parse(await readFile13(indexPath, "utf8"));
|
|
8131
8518
|
} catch {
|
|
8132
8519
|
index = {};
|
|
8133
8520
|
}
|
|
8134
8521
|
}
|
|
8135
8522
|
index[name] = hash;
|
|
8136
8523
|
await writeFile6(indexTmp, JSON.stringify(index, null, 2), "utf8");
|
|
8137
|
-
|
|
8524
|
+
renameSync2(indexTmp, indexPath);
|
|
8138
8525
|
return hash;
|
|
8139
8526
|
}
|
|
8140
8527
|
/**
|
|
@@ -8143,26 +8530,26 @@ var init_Storage = __esm({
|
|
|
8143
8530
|
*/
|
|
8144
8531
|
async resolveRecipe(nameOrRef) {
|
|
8145
8532
|
await this._ready;
|
|
8146
|
-
const indexPath =
|
|
8533
|
+
const indexPath = join17(this.baseDir, "cache", "recipes", "index.json");
|
|
8147
8534
|
const atIdx = nameOrRef.indexOf("@");
|
|
8148
8535
|
if (atIdx !== -1) {
|
|
8149
8536
|
const hash2 = nameOrRef.slice(atIdx + 1);
|
|
8150
|
-
const objectPath2 =
|
|
8151
|
-
if (!
|
|
8152
|
-
return { hash: hash2, content: await
|
|
8537
|
+
const objectPath2 = join17(this.baseDir, "cache", "recipes", "objects", hash2);
|
|
8538
|
+
if (!existsSync13(objectPath2)) return null;
|
|
8539
|
+
return { hash: hash2, content: await readFile13(objectPath2, "utf8") };
|
|
8153
8540
|
}
|
|
8154
|
-
if (!
|
|
8541
|
+
if (!existsSync13(indexPath)) return null;
|
|
8155
8542
|
let index;
|
|
8156
8543
|
try {
|
|
8157
|
-
index = JSON.parse(await
|
|
8544
|
+
index = JSON.parse(await readFile13(indexPath, "utf8"));
|
|
8158
8545
|
} catch {
|
|
8159
8546
|
return null;
|
|
8160
8547
|
}
|
|
8161
8548
|
const hash = index[nameOrRef];
|
|
8162
8549
|
if (!hash) return null;
|
|
8163
|
-
const objectPath =
|
|
8164
|
-
if (!
|
|
8165
|
-
return { hash, content: await
|
|
8550
|
+
const objectPath = join17(this.baseDir, "cache", "recipes", "objects", hash);
|
|
8551
|
+
if (!existsSync13(objectPath)) return null;
|
|
8552
|
+
return { hash, content: await readFile13(objectPath, "utf8") };
|
|
8166
8553
|
}
|
|
8167
8554
|
// ─────────────────────────────────────────────────────────────────────────
|
|
8168
8555
|
// CLI-ARCH-HASH-CHAIN-HISTORY-01 — append-only NDJSON history with hash chain
|
|
@@ -8184,18 +8571,18 @@ var init_Storage = __esm({
|
|
|
8184
8571
|
*/
|
|
8185
8572
|
async appendHistoryEntry(content) {
|
|
8186
8573
|
await this._ready;
|
|
8187
|
-
const path2 =
|
|
8574
|
+
const path2 = join17(this.baseDir, "history", "shell_history.jsonl");
|
|
8188
8575
|
return this.historyMutex.run(async () => {
|
|
8189
8576
|
let release3 = null;
|
|
8190
8577
|
try {
|
|
8191
|
-
if (!
|
|
8578
|
+
if (!existsSync13(path2)) {
|
|
8192
8579
|
await writeFile6(path2, "", "utf8");
|
|
8193
8580
|
}
|
|
8194
8581
|
release3 = await lockfile.lock(path2, { retries: 5, retryWait: 50 });
|
|
8195
8582
|
let prevHash = null;
|
|
8196
8583
|
let seq = 1;
|
|
8197
|
-
if (
|
|
8198
|
-
const raw = (await
|
|
8584
|
+
if (existsSync13(path2)) {
|
|
8585
|
+
const raw = (await readFile13(path2, "utf8")).trimEnd();
|
|
8199
8586
|
if (raw.length > 0) {
|
|
8200
8587
|
const lines = raw.split("\n");
|
|
8201
8588
|
const lastLine = lines[lines.length - 1];
|
|
@@ -8234,9 +8621,9 @@ var init_Storage = __esm({
|
|
|
8234
8621
|
*/
|
|
8235
8622
|
async loadHistoryEntries() {
|
|
8236
8623
|
await this._ready;
|
|
8237
|
-
const path2 =
|
|
8238
|
-
if (!
|
|
8239
|
-
const raw = await
|
|
8624
|
+
const path2 = join17(this.baseDir, "history", "shell_history.jsonl");
|
|
8625
|
+
if (!existsSync13(path2)) return [];
|
|
8626
|
+
const raw = await readFile13(path2, "utf8");
|
|
8240
8627
|
const entries = [];
|
|
8241
8628
|
for (const line of raw.split("\n")) {
|
|
8242
8629
|
if (!line.trim()) continue;
|
|
@@ -8284,20 +8671,20 @@ var init_Storage = __esm({
|
|
|
8284
8671
|
*/
|
|
8285
8672
|
async saveHistory(history) {
|
|
8286
8673
|
await this._ready;
|
|
8287
|
-
const path2 =
|
|
8674
|
+
const path2 = join17(this.baseDir, "history", "shell_history.json");
|
|
8288
8675
|
let release3;
|
|
8289
8676
|
try {
|
|
8290
|
-
if (!
|
|
8677
|
+
if (!existsSync13(path2)) writeFileSync2(path2, "[]", "utf8");
|
|
8291
8678
|
release3 = await lockfile.lock(path2, { realpath: false, retries: 5, retryWait: 50 });
|
|
8292
8679
|
await this.historyMutex.run(async () => {
|
|
8293
8680
|
const tmpPath = `${path2}.tmp`;
|
|
8294
8681
|
const content = JSON.stringify(history, null, 2);
|
|
8295
8682
|
await writeFile6(tmpPath, content, "utf8");
|
|
8296
8683
|
try {
|
|
8297
|
-
|
|
8684
|
+
renameSync2(tmpPath, path2);
|
|
8298
8685
|
} catch (e) {
|
|
8299
8686
|
try {
|
|
8300
|
-
if (
|
|
8687
|
+
if (existsSync13(tmpPath)) {
|
|
8301
8688
|
const fs3 = await import("fs/promises");
|
|
8302
8689
|
await fs3.unlink(tmpPath);
|
|
8303
8690
|
}
|
|
@@ -8325,27 +8712,27 @@ var init_Storage = __esm({
|
|
|
8325
8712
|
*/
|
|
8326
8713
|
async loadHistory() {
|
|
8327
8714
|
await this._ready;
|
|
8328
|
-
const path2 =
|
|
8329
|
-
if (!
|
|
8715
|
+
const path2 = join17(this.baseDir, "history", "shell_history.json");
|
|
8716
|
+
if (!existsSync13(path2)) return [];
|
|
8330
8717
|
let release3;
|
|
8331
8718
|
try {
|
|
8332
8719
|
release3 = await lockfile.lock(path2, { realpath: false, retries: 5, retryWait: 50 });
|
|
8333
8720
|
return this.historyMutex.run(async () => {
|
|
8334
|
-
if (
|
|
8335
|
-
const text = await
|
|
8721
|
+
if (existsSync13(path2)) {
|
|
8722
|
+
const text = await readFile13(path2, "utf8");
|
|
8336
8723
|
try {
|
|
8337
8724
|
return JSON.parse(text);
|
|
8338
8725
|
} catch (parseErr) {
|
|
8339
8726
|
const filename = path2.split("/").pop() || "shell_history.json";
|
|
8340
8727
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8341
8728
|
const suffix = randomBytes7(4).toString("hex");
|
|
8342
|
-
const corruptBackupPath =
|
|
8729
|
+
const corruptBackupPath = join17(
|
|
8343
8730
|
this.baseDir,
|
|
8344
8731
|
"history",
|
|
8345
8732
|
`${filename}.corrupt.${stamp}-${suffix}.bak`
|
|
8346
8733
|
);
|
|
8347
8734
|
try {
|
|
8348
|
-
|
|
8735
|
+
renameSync2(path2, corruptBackupPath);
|
|
8349
8736
|
console.warn(`History file was corrupt; backed up to ${corruptBackupPath}`);
|
|
8350
8737
|
} catch (backupErr) {
|
|
8351
8738
|
console.error(`Failed to backup corrupt history file: ${backupErr}`);
|
|
@@ -8370,20 +8757,20 @@ var init_Storage = __esm({
|
|
|
8370
8757
|
* R20-CLI-2: Typed with PermissionState from Sandbox.ts.
|
|
8371
8758
|
*/
|
|
8372
8759
|
async savePermissions(permissions) {
|
|
8373
|
-
const path2 =
|
|
8760
|
+
const path2 = join17(this.baseDir, "vault", "permissions.json");
|
|
8374
8761
|
let release3;
|
|
8375
8762
|
try {
|
|
8376
|
-
if (!
|
|
8763
|
+
if (!existsSync13(path2)) writeFileSync2(path2, "{}", "utf8");
|
|
8377
8764
|
release3 = await lockfile.lock(path2, { realpath: false, retries: 5, retryWait: 50 });
|
|
8378
8765
|
await this.permissionsMutex.run(async () => {
|
|
8379
8766
|
const tmpPath = `${path2}.tmp`;
|
|
8380
8767
|
const content = JSON.stringify(permissions, null, 2);
|
|
8381
8768
|
await writeFile6(tmpPath, content, "utf8");
|
|
8382
8769
|
try {
|
|
8383
|
-
|
|
8770
|
+
renameSync2(tmpPath, path2);
|
|
8384
8771
|
} catch (e) {
|
|
8385
8772
|
try {
|
|
8386
|
-
if (
|
|
8773
|
+
if (existsSync13(tmpPath)) {
|
|
8387
8774
|
const fs3 = await import("fs/promises");
|
|
8388
8775
|
await fs3.unlink(tmpPath);
|
|
8389
8776
|
}
|
|
@@ -8407,27 +8794,27 @@ var init_Storage = __esm({
|
|
|
8407
8794
|
* R20-CLI-2: Typed with PermissionState from Sandbox.ts.
|
|
8408
8795
|
*/
|
|
8409
8796
|
async loadPermissions() {
|
|
8410
|
-
const path2 =
|
|
8411
|
-
if (!
|
|
8797
|
+
const path2 = join17(this.baseDir, "vault", "permissions.json");
|
|
8798
|
+
if (!existsSync13(path2)) return { trustedCommands: [], trustedPaths: [] };
|
|
8412
8799
|
let release3;
|
|
8413
8800
|
try {
|
|
8414
8801
|
release3 = await lockfile.lock(path2, { realpath: false, retries: 5, retryWait: 50 });
|
|
8415
8802
|
return this.permissionsMutex.run(async () => {
|
|
8416
|
-
if (
|
|
8417
|
-
const text = await
|
|
8803
|
+
if (existsSync13(path2)) {
|
|
8804
|
+
const text = await readFile13(path2, "utf8");
|
|
8418
8805
|
try {
|
|
8419
8806
|
return JSON.parse(text);
|
|
8420
8807
|
} catch (parseErr) {
|
|
8421
8808
|
const filename = path2.split("/").pop() || "permissions.json";
|
|
8422
8809
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8423
8810
|
const suffix = randomBytes7(4).toString("hex");
|
|
8424
|
-
const corruptBackupPath =
|
|
8811
|
+
const corruptBackupPath = join17(
|
|
8425
8812
|
this.baseDir,
|
|
8426
8813
|
"vault",
|
|
8427
8814
|
`${filename}.corrupt.${stamp}-${suffix}.bak`
|
|
8428
8815
|
);
|
|
8429
8816
|
try {
|
|
8430
|
-
|
|
8817
|
+
renameSync2(path2, corruptBackupPath);
|
|
8431
8818
|
console.warn(`Permissions file was corrupt; backed up to ${corruptBackupPath}`);
|
|
8432
8819
|
} catch (backupErr) {
|
|
8433
8820
|
console.error(`Failed to backup corrupt permissions file: ${backupErr}`);
|
|
@@ -8458,7 +8845,7 @@ var init_Storage = __esm({
|
|
|
8458
8845
|
const filename = filePath.split("/").pop() || "file";
|
|
8459
8846
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
8460
8847
|
const suffix = randomBytes7(4).toString("hex");
|
|
8461
|
-
const backupPath =
|
|
8848
|
+
const backupPath = join17(this.baseDir, "backups", `${filename}.backup-${stamp}-${suffix}.bak`);
|
|
8462
8849
|
await writeFile6(backupPath, content, "utf8");
|
|
8463
8850
|
if (process.platform !== "win32") {
|
|
8464
8851
|
chmodSync(backupPath, 384);
|
|
@@ -8470,11 +8857,11 @@ var init_Storage = __esm({
|
|
|
8470
8857
|
});
|
|
8471
8858
|
|
|
8472
8859
|
// ../core/src/Settings.ts
|
|
8473
|
-
import { homedir as
|
|
8474
|
-
import { join as
|
|
8475
|
-
import { existsSync as
|
|
8860
|
+
import { homedir as homedir11 } from "os";
|
|
8861
|
+
import { join as join18 } from "path";
|
|
8862
|
+
import { existsSync as existsSync14 } from "fs";
|
|
8476
8863
|
import * as fs from "fs";
|
|
8477
|
-
import { readFile as
|
|
8864
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
8478
8865
|
import { randomBytes as randomBytes8 } from "crypto";
|
|
8479
8866
|
function ensureConfigDir(p) {
|
|
8480
8867
|
try {
|
|
@@ -8495,8 +8882,8 @@ function ensureConfigDir(p) {
|
|
|
8495
8882
|
}
|
|
8496
8883
|
async function readJson(path2) {
|
|
8497
8884
|
try {
|
|
8498
|
-
if (!
|
|
8499
|
-
const text = await
|
|
8885
|
+
if (!existsSync14(path2)) return null;
|
|
8886
|
+
const text = await readFile14(path2, "utf8");
|
|
8500
8887
|
if (!text.trim()) return null;
|
|
8501
8888
|
return JSON.parse(text);
|
|
8502
8889
|
} catch {
|
|
@@ -8550,8 +8937,8 @@ function mergeSettings(base, override) {
|
|
|
8550
8937
|
}
|
|
8551
8938
|
async function loadSettings(cwd = process.cwd(), env = process.env, warn) {
|
|
8552
8939
|
const sources = [];
|
|
8553
|
-
const userPath =
|
|
8554
|
-
const projectPath =
|
|
8940
|
+
const userPath = join18(homedir11() || ".", ".msapling", "settings.json");
|
|
8941
|
+
const projectPath = join18(cwd, ".msapling", "settings.json");
|
|
8555
8942
|
const [user, project] = await Promise.all([readJson(userPath), readJson(projectPath)]);
|
|
8556
8943
|
if (user) sources.push(userPath);
|
|
8557
8944
|
if (project) sources.push(projectPath);
|
|
@@ -9218,10 +9605,10 @@ var init_unlock = __esm({
|
|
|
9218
9605
|
});
|
|
9219
9606
|
|
|
9220
9607
|
// src/commands/doctor.ts
|
|
9221
|
-
import { homedir as
|
|
9222
|
-
import { join as
|
|
9223
|
-
import { existsSync as
|
|
9224
|
-
import { readFile as
|
|
9608
|
+
import { homedir as homedir12 } from "os";
|
|
9609
|
+
import { join as join19 } from "path";
|
|
9610
|
+
import { existsSync as existsSync15 } from "fs";
|
|
9611
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
9225
9612
|
async function checkApiHealth(client) {
|
|
9226
9613
|
try {
|
|
9227
9614
|
if (typeof client.request !== "function") {
|
|
@@ -9246,12 +9633,12 @@ async function checkAuthStatus(client) {
|
|
|
9246
9633
|
}
|
|
9247
9634
|
}
|
|
9248
9635
|
async function checkSettingsFile() {
|
|
9249
|
-
const settingsPath =
|
|
9636
|
+
const settingsPath = join19(homedir12(), ".msapling", "settings.json");
|
|
9250
9637
|
try {
|
|
9251
|
-
if (!
|
|
9638
|
+
if (!existsSync15(settingsPath)) {
|
|
9252
9639
|
return { ok: false, message: `Not found: ${settingsPath}` };
|
|
9253
9640
|
}
|
|
9254
|
-
const text = await
|
|
9641
|
+
const text = await readFile15(settingsPath, "utf8");
|
|
9255
9642
|
const parsed = JSON.parse(text);
|
|
9256
9643
|
return { ok: true, message: `Valid JSON (${settingsPath})`, settings: parsed };
|
|
9257
9644
|
} catch (e) {
|
|
@@ -9935,8 +10322,8 @@ var init_memories = __esm({
|
|
|
9935
10322
|
});
|
|
9936
10323
|
|
|
9937
10324
|
// src/commands/mdrive.ts
|
|
9938
|
-
import { readFile as
|
|
9939
|
-
import { existsSync as
|
|
10325
|
+
import { readFile as readFile16, writeFile as writeFile7 } from "fs/promises";
|
|
10326
|
+
import { existsSync as existsSync16 } from "fs";
|
|
9940
10327
|
import { basename, resolve as resolve14 } from "path";
|
|
9941
10328
|
function formatBytes(b) {
|
|
9942
10329
|
if (!b) return "0";
|
|
@@ -9989,11 +10376,11 @@ var init_mdrive = __esm({
|
|
|
9989
10376
|
return;
|
|
9990
10377
|
}
|
|
9991
10378
|
const absLocal = resolve14(local);
|
|
9992
|
-
if (!
|
|
10379
|
+
if (!existsSync16(absLocal)) {
|
|
9993
10380
|
context.addMessage("error", `Local file not found: ${absLocal}`);
|
|
9994
10381
|
return;
|
|
9995
10382
|
}
|
|
9996
|
-
const content = await
|
|
10383
|
+
const content = await readFile16(absLocal, "utf8");
|
|
9997
10384
|
const res = await context.client.mdriveWrite(remote, content);
|
|
9998
10385
|
context.addMessage("system", `Uploaded ${absLocal} \u2192 mdrive:${remote} (${formatBytes(content.length)}, hash=${res?.hash?.slice(0, 12) ?? "?"})`);
|
|
9999
10386
|
return;
|
|
@@ -10086,15 +10473,15 @@ var init_clear = __esm({
|
|
|
10086
10473
|
});
|
|
10087
10474
|
|
|
10088
10475
|
// src/commands/mode.ts
|
|
10089
|
-
import { homedir as
|
|
10090
|
-
import { join as
|
|
10091
|
-
import { existsSync as
|
|
10092
|
-
import { readFile as
|
|
10476
|
+
import { homedir as homedir13 } from "os";
|
|
10477
|
+
import { join as join20 } from "path";
|
|
10478
|
+
import { existsSync as existsSync17 } from "fs";
|
|
10479
|
+
import { readFile as readFile17, writeFile as writeFile8, mkdir as mkdir7 } from "fs/promises";
|
|
10093
10480
|
async function persistApprovalMode(mode, ttlMs) {
|
|
10094
10481
|
try {
|
|
10095
10482
|
let existing = {};
|
|
10096
|
-
if (
|
|
10097
|
-
const text = await
|
|
10483
|
+
if (existsSync17(SETTINGS_PATH)) {
|
|
10484
|
+
const text = await readFile17(SETTINGS_PATH, "utf8");
|
|
10098
10485
|
if (text.trim()) {
|
|
10099
10486
|
existing = JSON.parse(text);
|
|
10100
10487
|
}
|
|
@@ -10105,8 +10492,8 @@ async function persistApprovalMode(mode, ttlMs) {
|
|
|
10105
10492
|
...ttlMs && { ttlMs }
|
|
10106
10493
|
};
|
|
10107
10494
|
existing.approvalMode = entry;
|
|
10108
|
-
const settingsDir =
|
|
10109
|
-
if (!
|
|
10495
|
+
const settingsDir = join20(homedir13(), ".msapling");
|
|
10496
|
+
if (!existsSync17(settingsDir)) {
|
|
10110
10497
|
await mkdir7(settingsDir, { recursive: true });
|
|
10111
10498
|
}
|
|
10112
10499
|
await writeFile8(SETTINGS_PATH, JSON.stringify(existing, null, 2), "utf8");
|
|
@@ -10118,7 +10505,7 @@ var init_mode = __esm({
|
|
|
10118
10505
|
"src/commands/mode.ts"() {
|
|
10119
10506
|
"use strict";
|
|
10120
10507
|
init_esm_shims();
|
|
10121
|
-
SETTINGS_PATH =
|
|
10508
|
+
SETTINGS_PATH = join20(homedir13(), ".msapling", "settings.json");
|
|
10122
10509
|
modeCommand = {
|
|
10123
10510
|
name: "mode",
|
|
10124
10511
|
args: "[default|plan|acceptEdits|bypassPermissions] [...options]",
|
|
@@ -10520,8 +10907,8 @@ var init_compact = __esm({
|
|
|
10520
10907
|
});
|
|
10521
10908
|
|
|
10522
10909
|
// src/commands/init.ts
|
|
10523
|
-
import { join as
|
|
10524
|
-
import { existsSync as
|
|
10910
|
+
import { join as join21 } from "path";
|
|
10911
|
+
import { existsSync as existsSync18 } from "fs";
|
|
10525
10912
|
import { writeFile as writeFile9 } from "fs/promises";
|
|
10526
10913
|
var initCommand;
|
|
10527
10914
|
var init_init = __esm({
|
|
@@ -10535,8 +10922,8 @@ var init_init = __esm({
|
|
|
10535
10922
|
handler: async (args2, context) => {
|
|
10536
10923
|
try {
|
|
10537
10924
|
const cwd = process.cwd();
|
|
10538
|
-
const path2 =
|
|
10539
|
-
if (
|
|
10925
|
+
const path2 = join21(cwd, "MSAPLING.md");
|
|
10926
|
+
if (existsSync18(path2)) {
|
|
10540
10927
|
context.addMessage("error", "MSAPLING.md already exists in current directory.");
|
|
10541
10928
|
return;
|
|
10542
10929
|
}
|
|
@@ -10562,8 +10949,8 @@ var init_init = __esm({
|
|
|
10562
10949
|
});
|
|
10563
10950
|
|
|
10564
10951
|
// src/commands/review.ts
|
|
10565
|
-
import { existsSync as
|
|
10566
|
-
import { readFile as
|
|
10952
|
+
import { existsSync as existsSync19 } from "fs";
|
|
10953
|
+
import { readFile as readFile18 } from "fs/promises";
|
|
10567
10954
|
var reviewCommand;
|
|
10568
10955
|
var init_review = __esm({
|
|
10569
10956
|
"src/commands/review.ts"() {
|
|
@@ -10582,8 +10969,8 @@ var init_review = __esm({
|
|
|
10582
10969
|
}
|
|
10583
10970
|
let content = "";
|
|
10584
10971
|
try {
|
|
10585
|
-
if (
|
|
10586
|
-
content = await
|
|
10972
|
+
if (existsSync19(target)) {
|
|
10973
|
+
content = await readFile18(target, "utf8");
|
|
10587
10974
|
} else {
|
|
10588
10975
|
content = `Review target: ${target}`;
|
|
10589
10976
|
}
|
|
@@ -10676,15 +11063,15 @@ var init_swarm = __esm({
|
|
|
10676
11063
|
|
|
10677
11064
|
// src/commands/recipe.ts
|
|
10678
11065
|
import { parse as parseYaml } from "yaml";
|
|
10679
|
-
import { existsSync as
|
|
10680
|
-
import { readFile as
|
|
10681
|
-
import { join as
|
|
11066
|
+
import { existsSync as existsSync20 } from "fs";
|
|
11067
|
+
import { readFile as readFile19 } from "fs/promises";
|
|
11068
|
+
import { join as join22 } from "path";
|
|
10682
11069
|
function findRecipe(name, cwd) {
|
|
10683
11070
|
for (const dir of RECIPE_DIRS) {
|
|
10684
11071
|
for (const suffix of NAME_SUFFIXES) {
|
|
10685
11072
|
for (const ext of FILE_EXTS) {
|
|
10686
|
-
const p =
|
|
10687
|
-
if (
|
|
11073
|
+
const p = join22(cwd, dir, `${name}${suffix}${ext}`);
|
|
11074
|
+
if (existsSync20(p)) return p;
|
|
10688
11075
|
}
|
|
10689
11076
|
}
|
|
10690
11077
|
}
|
|
@@ -10743,7 +11130,7 @@ var init_recipe = __esm({
|
|
|
10743
11130
|
let text;
|
|
10744
11131
|
let recipe;
|
|
10745
11132
|
try {
|
|
10746
|
-
text = await
|
|
11133
|
+
text = await readFile19(path2, "utf8");
|
|
10747
11134
|
recipe = parseYaml(text);
|
|
10748
11135
|
} catch (e) {
|
|
10749
11136
|
context.addMessage("error", `Failed to load ${path2}: ${e.message}`);
|
|
@@ -10797,13 +11184,13 @@ ${rendered}` : rendered;
|
|
|
10797
11184
|
});
|
|
10798
11185
|
|
|
10799
11186
|
// src/commands/skill.ts
|
|
10800
|
-
import { existsSync as
|
|
10801
|
-
import { readFile as
|
|
10802
|
-
import { join as
|
|
11187
|
+
import { existsSync as existsSync21, readdirSync as readdirSync2, statSync as statSync5 } from "fs";
|
|
11188
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
11189
|
+
import { join as join23, resolve as resolve15 } from "path";
|
|
10803
11190
|
function findSkillsRoot(cwd) {
|
|
10804
11191
|
for (const candidate of SKILLS_DIRS) {
|
|
10805
11192
|
const full = resolve15(cwd, candidate);
|
|
10806
|
-
if (
|
|
11193
|
+
if (existsSync21(full) && statSync5(full).isDirectory()) return full;
|
|
10807
11194
|
}
|
|
10808
11195
|
return null;
|
|
10809
11196
|
}
|
|
@@ -10816,7 +11203,7 @@ function listAllSkills(root) {
|
|
|
10816
11203
|
return out;
|
|
10817
11204
|
}
|
|
10818
11205
|
for (const domain of domains) {
|
|
10819
|
-
const dir =
|
|
11206
|
+
const dir = join23(root, domain);
|
|
10820
11207
|
let s;
|
|
10821
11208
|
try {
|
|
10822
11209
|
s = statSync5(dir);
|
|
@@ -10832,7 +11219,7 @@ function listAllSkills(root) {
|
|
|
10832
11219
|
}
|
|
10833
11220
|
for (const f of files) {
|
|
10834
11221
|
if (!f.endsWith(".md")) continue;
|
|
10835
|
-
out.push({ domain, name: f.slice(0, -3), path:
|
|
11222
|
+
out.push({ domain, name: f.slice(0, -3), path: join23(dir, f) });
|
|
10836
11223
|
}
|
|
10837
11224
|
}
|
|
10838
11225
|
return out.sort(
|
|
@@ -10897,7 +11284,7 @@ var init_skill = __esm({
|
|
|
10897
11284
|
}
|
|
10898
11285
|
let body;
|
|
10899
11286
|
try {
|
|
10900
|
-
body = await
|
|
11287
|
+
body = await readFile20(skill.path, "utf8");
|
|
10901
11288
|
} catch (e) {
|
|
10902
11289
|
context.addMessage("error", `Failed to load skill ${skill.path}: ${e.message}`);
|
|
10903
11290
|
return;
|
|
@@ -10921,9 +11308,9 @@ ${prompt4}`;
|
|
|
10921
11308
|
});
|
|
10922
11309
|
|
|
10923
11310
|
// src/commands/benchmark.ts
|
|
10924
|
-
import { homedir as
|
|
10925
|
-
import { join as
|
|
10926
|
-
import { mkdirSync as
|
|
11311
|
+
import { homedir as homedir14 } from "os";
|
|
11312
|
+
import { join as join24 } from "path";
|
|
11313
|
+
import { mkdirSync as mkdirSync5 } from "fs";
|
|
10927
11314
|
import * as fs2 from "fs";
|
|
10928
11315
|
function parseArgs(args2) {
|
|
10929
11316
|
let models = null;
|
|
@@ -11042,10 +11429,10 @@ HW at start: ${hw.cores}-core ${hw.platform} | CPU ${hw.cpuPct}% | RAM ${hw.ramP
|
|
|
11042
11429
|
`[HW at run time: CPU ${hwAtEnd.cpuPct}% / RAM ${hwAtEnd.ramPct}% | ${hw.ramGiB} GiB RAM, ${hw.cores} cores]`
|
|
11043
11430
|
);
|
|
11044
11431
|
try {
|
|
11045
|
-
const dir =
|
|
11046
|
-
|
|
11432
|
+
const dir = join24(homedir14(), ".msapling", "benchmarks");
|
|
11433
|
+
mkdirSync5(dir, { recursive: true });
|
|
11047
11434
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 16);
|
|
11048
|
-
const file =
|
|
11435
|
+
const file = join24(dir, `${ts}.json`);
|
|
11049
11436
|
const run = {
|
|
11050
11437
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11051
11438
|
rounds,
|
|
@@ -11291,22 +11678,22 @@ var init_theme = __esm({
|
|
|
11291
11678
|
});
|
|
11292
11679
|
|
|
11293
11680
|
// src/commands/theme.ts
|
|
11294
|
-
import { join as
|
|
11295
|
-
import { homedir as
|
|
11296
|
-
import { existsSync as
|
|
11297
|
-
import { readFile as
|
|
11681
|
+
import { join as join25 } from "path";
|
|
11682
|
+
import { homedir as homedir15 } from "os";
|
|
11683
|
+
import { existsSync as existsSync22 } from "fs";
|
|
11684
|
+
import { readFile as readFile21, writeFile as writeFile10 } from "fs/promises";
|
|
11298
11685
|
async function persistTheme(storage, themeName) {
|
|
11299
|
-
const settingsPath =
|
|
11686
|
+
const settingsPath = join25(homedir15(), ".msapling", "settings.json");
|
|
11300
11687
|
let existing = {};
|
|
11301
11688
|
try {
|
|
11302
|
-
if (
|
|
11303
|
-
const text = await
|
|
11689
|
+
if (existsSync22(settingsPath)) {
|
|
11690
|
+
const text = await readFile21(settingsPath, "utf8");
|
|
11304
11691
|
if (text.trim()) existing = JSON.parse(text);
|
|
11305
11692
|
}
|
|
11306
11693
|
} catch {
|
|
11307
11694
|
}
|
|
11308
11695
|
existing["theme"] = themeName;
|
|
11309
|
-
ensureConfigDir(
|
|
11696
|
+
ensureConfigDir(join25(homedir15(), ".msapling"));
|
|
11310
11697
|
await writeFile10(settingsPath, JSON.stringify(existing, null, 2), "utf8");
|
|
11311
11698
|
}
|
|
11312
11699
|
var VALID_THEMES, themeCommand;
|
|
@@ -11378,7 +11765,7 @@ var init_version = __esm({
|
|
|
11378
11765
|
description: "Show version information for CLI and core packages",
|
|
11379
11766
|
category: "debug",
|
|
11380
11767
|
handler: async (_args, context) => {
|
|
11381
|
-
const cliVersion = true ? "2.3.6-beta.
|
|
11768
|
+
const cliVersion = true ? "2.3.6-beta.31" : "(dev)";
|
|
11382
11769
|
const coreVersion = true ? "2.3.2" : "(dev)";
|
|
11383
11770
|
const runtime = process.version;
|
|
11384
11771
|
context.addMessage("system", "MSapling Version Info");
|
|
@@ -11387,7 +11774,7 @@ var init_version = __esm({
|
|
|
11387
11774
|
context.addMessage("system", row2("Core (@msapling/core)", coreVersion));
|
|
11388
11775
|
context.addMessage("system", row2("Runtime (Node/Bun)", runtime));
|
|
11389
11776
|
try {
|
|
11390
|
-
const ts = "2026-05-
|
|
11777
|
+
const ts = "2026-05-30T19:14:27.313Z";
|
|
11391
11778
|
if (ts && ts !== "__BUILD_TIMESTAMP__") {
|
|
11392
11779
|
context.addMessage("system", row2("Build Timestamp", ts));
|
|
11393
11780
|
}
|
|
@@ -11400,15 +11787,15 @@ var init_version = __esm({
|
|
|
11400
11787
|
});
|
|
11401
11788
|
|
|
11402
11789
|
// src/commands/feedback.ts
|
|
11403
|
-
import { join as
|
|
11404
|
-
import { existsSync as
|
|
11405
|
-
import { readFile as
|
|
11790
|
+
import { join as join26 } from "path";
|
|
11791
|
+
import { existsSync as existsSync23 } from "fs";
|
|
11792
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
11406
11793
|
async function readCliVersion() {
|
|
11407
11794
|
try {
|
|
11408
11795
|
const baseDir = typeof __dirname !== "undefined" ? __dirname : process.cwd();
|
|
11409
|
-
const pkgPath =
|
|
11410
|
-
if (!
|
|
11411
|
-
const text = await
|
|
11796
|
+
const pkgPath = join26(baseDir, "..", "..", "package.json");
|
|
11797
|
+
if (!existsSync23(pkgPath)) return "unknown";
|
|
11798
|
+
const text = await readFile22(pkgPath, "utf8");
|
|
11412
11799
|
const json = JSON.parse(text);
|
|
11413
11800
|
return json.version ?? "unknown";
|
|
11414
11801
|
} catch {
|
|
@@ -11448,8 +11835,8 @@ var init_feedback = __esm({
|
|
|
11448
11835
|
});
|
|
11449
11836
|
|
|
11450
11837
|
// src/commands/export.ts
|
|
11451
|
-
import { homedir as
|
|
11452
|
-
import { join as
|
|
11838
|
+
import { homedir as homedir16 } from "os";
|
|
11839
|
+
import { join as join27 } from "path";
|
|
11453
11840
|
import { writeFile as writeFile11, mkdir as mkdir8 } from "fs/promises";
|
|
11454
11841
|
function formatTimestamp(date) {
|
|
11455
11842
|
return date.toISOString().replace(/[:.]/g, "-").replace("T", "_").slice(0, 19);
|
|
@@ -11499,10 +11886,10 @@ var init_export = __esm({
|
|
|
11499
11886
|
let outputPath;
|
|
11500
11887
|
let content;
|
|
11501
11888
|
if (arg === "" || arg === "json") {
|
|
11502
|
-
outputPath =
|
|
11889
|
+
outputPath = join27(homedir16(), `msapling-export-${timestamp}.json`);
|
|
11503
11890
|
content = buildJsonExport(history);
|
|
11504
11891
|
} else if (arg === "markdown" || arg === "md") {
|
|
11505
|
-
outputPath =
|
|
11892
|
+
outputPath = join27(homedir16(), `msapling-export-${timestamp}.md`);
|
|
11506
11893
|
content = buildMarkdownExport(history);
|
|
11507
11894
|
} else {
|
|
11508
11895
|
outputPath = arg;
|
|
@@ -11514,7 +11901,7 @@ var init_export = __esm({
|
|
|
11514
11901
|
}
|
|
11515
11902
|
}
|
|
11516
11903
|
try {
|
|
11517
|
-
const dir =
|
|
11904
|
+
const dir = join27(outputPath, "..");
|
|
11518
11905
|
await mkdir8(dir, { recursive: true });
|
|
11519
11906
|
await writeFile11(outputPath, content, "utf8");
|
|
11520
11907
|
context.addMessage("system", `Exported to: ${outputPath}`);
|
|
@@ -11709,17 +12096,17 @@ var init_plan = __esm({
|
|
|
11709
12096
|
});
|
|
11710
12097
|
|
|
11711
12098
|
// src/commands/note.ts
|
|
11712
|
-
import { homedir as
|
|
11713
|
-
import { join as
|
|
11714
|
-
import { existsSync as
|
|
11715
|
-
import { readFile as
|
|
12099
|
+
import { homedir as homedir17 } from "os";
|
|
12100
|
+
import { join as join28 } from "path";
|
|
12101
|
+
import { existsSync as existsSync24 } from "fs";
|
|
12102
|
+
import { readFile as readFile23, writeFile as writeFile12 } from "fs/promises";
|
|
11716
12103
|
function getNotesFilePath() {
|
|
11717
|
-
return
|
|
12104
|
+
return join28(homedir17(), ".msapling", "notes.json");
|
|
11718
12105
|
}
|
|
11719
12106
|
async function readNotes(filePath = getNotesFilePath()) {
|
|
11720
12107
|
try {
|
|
11721
|
-
if (!
|
|
11722
|
-
const raw = await
|
|
12108
|
+
if (!existsSync24(filePath)) return [];
|
|
12109
|
+
const raw = await readFile23(filePath, "utf8");
|
|
11723
12110
|
const parsed = JSON.parse(raw);
|
|
11724
12111
|
if (!Array.isArray(parsed)) return [];
|
|
11725
12112
|
return parsed;
|
|
@@ -11728,7 +12115,7 @@ async function readNotes(filePath = getNotesFilePath()) {
|
|
|
11728
12115
|
}
|
|
11729
12116
|
}
|
|
11730
12117
|
async function writeNotes(notes, filePath = getNotesFilePath()) {
|
|
11731
|
-
const dir =
|
|
12118
|
+
const dir = join28(homedir17(), ".msapling");
|
|
11732
12119
|
ensureConfigDir(dir);
|
|
11733
12120
|
await writeFile12(filePath, JSON.stringify(notes, null, 2), "utf8");
|
|
11734
12121
|
}
|
|
@@ -11874,14 +12261,14 @@ var init_todo = __esm({
|
|
|
11874
12261
|
});
|
|
11875
12262
|
|
|
11876
12263
|
// src/commands/outputStyle.ts
|
|
11877
|
-
import { homedir as
|
|
11878
|
-
import { join as
|
|
11879
|
-
import { existsSync as
|
|
12264
|
+
import { homedir as homedir18 } from "os";
|
|
12265
|
+
import { join as join29, basename as basename2, extname as extname3 } from "path";
|
|
12266
|
+
import { existsSync as existsSync25, mkdirSync as mkdirSync6, readdirSync as readdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
11880
12267
|
function stylesDir() {
|
|
11881
|
-
return
|
|
12268
|
+
return join29(homedir18(), ".msapling", "output-styles");
|
|
11882
12269
|
}
|
|
11883
12270
|
function activeFile() {
|
|
11884
|
-
return
|
|
12271
|
+
return join29(stylesDir(), ".active");
|
|
11885
12272
|
}
|
|
11886
12273
|
function parseStyleFile(text) {
|
|
11887
12274
|
const fm = text.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/);
|
|
@@ -11902,13 +12289,13 @@ function parseStyleFile(text) {
|
|
|
11902
12289
|
}
|
|
11903
12290
|
function listUserStyles() {
|
|
11904
12291
|
const dir = stylesDir();
|
|
11905
|
-
if (!
|
|
12292
|
+
if (!existsSync25(dir)) return [];
|
|
11906
12293
|
const out = [];
|
|
11907
12294
|
for (const entry of readdirSync3(dir)) {
|
|
11908
12295
|
if (extname3(entry).toLowerCase() !== ".md") continue;
|
|
11909
|
-
const full =
|
|
12296
|
+
const full = join29(dir, entry);
|
|
11910
12297
|
try {
|
|
11911
|
-
const text =
|
|
12298
|
+
const text = readFileSync2(full, "utf8");
|
|
11912
12299
|
const { description, body } = parseStyleFile(text);
|
|
11913
12300
|
out.push({
|
|
11914
12301
|
name: basename2(entry, ".md"),
|
|
@@ -11934,16 +12321,16 @@ function findStyle(name) {
|
|
|
11934
12321
|
function getActiveStyleName() {
|
|
11935
12322
|
try {
|
|
11936
12323
|
const f = activeFile();
|
|
11937
|
-
if (!
|
|
11938
|
-
return
|
|
12324
|
+
if (!existsSync25(f)) return "default";
|
|
12325
|
+
return readFileSync2(f, "utf8").trim() || "default";
|
|
11939
12326
|
} catch {
|
|
11940
12327
|
return "default";
|
|
11941
12328
|
}
|
|
11942
12329
|
}
|
|
11943
12330
|
function setActiveStyleName(name) {
|
|
11944
12331
|
const dir = stylesDir();
|
|
11945
|
-
if (!
|
|
11946
|
-
|
|
12332
|
+
if (!existsSync25(dir)) mkdirSync6(dir, { recursive: true });
|
|
12333
|
+
writeFileSync3(activeFile(), `${name}
|
|
11947
12334
|
`, "utf8");
|
|
11948
12335
|
}
|
|
11949
12336
|
function getActiveStyle() {
|
|
@@ -11955,14 +12342,14 @@ function createUserStyle(name, description, body) {
|
|
|
11955
12342
|
throw new Error(`Invalid style name "${name}" \u2014 use letters, digits, _ and - only.`);
|
|
11956
12343
|
}
|
|
11957
12344
|
const dir = stylesDir();
|
|
11958
|
-
if (!
|
|
11959
|
-
const target =
|
|
12345
|
+
if (!existsSync25(dir)) mkdirSync6(dir, { recursive: true });
|
|
12346
|
+
const target = join29(dir, `${name}.md`);
|
|
11960
12347
|
const frontmatter = `---
|
|
11961
12348
|
description: ${description.replace(/\n/g, " ")}
|
|
11962
12349
|
---
|
|
11963
12350
|
|
|
11964
12351
|
`;
|
|
11965
|
-
|
|
12352
|
+
writeFileSync3(target, frontmatter + body.trim() + "\n", "utf8");
|
|
11966
12353
|
return target;
|
|
11967
12354
|
}
|
|
11968
12355
|
var BUILTIN_STYLES, outputStyleCommand;
|
|
@@ -12470,515 +12857,236 @@ var init_mfa = __esm({
|
|
|
12470
12857
|
}
|
|
12471
12858
|
};
|
|
12472
12859
|
}
|
|
12473
|
-
});
|
|
12474
|
-
|
|
12475
|
-
// src/commands/mcp.ts
|
|
12476
|
-
var mcpCommand;
|
|
12477
|
-
var init_mcp = __esm({
|
|
12478
|
-
"src/commands/mcp.ts"() {
|
|
12479
|
-
"use strict";
|
|
12480
|
-
init_esm_shims();
|
|
12481
|
-
mcpCommand = {
|
|
12482
|
-
name: "mcp",
|
|
12483
|
-
args: "[connect|invoke|tools|disconnect] [...args]",
|
|
12484
|
-
description: "MCP client surface: connect, invoke, list tools, disconnect",
|
|
12485
|
-
category: "config",
|
|
12486
|
-
handler: async (args2, context) => {
|
|
12487
|
-
const sub = (args2[0] ?? "tools").toLowerCase();
|
|
12488
|
-
const rest = args2.slice(1);
|
|
12489
|
-
try {
|
|
12490
|
-
if (sub === "connect") {
|
|
12491
|
-
const url = rest[0];
|
|
12492
|
-
if (!url) {
|
|
12493
|
-
context.addMessage("system", "Usage: /mcp connect <server-url>");
|
|
12494
|
-
return;
|
|
12495
|
-
}
|
|
12496
|
-
const result = await context.client.mcpConnect(url);
|
|
12497
|
-
context.addMessage("system", `MCP Connected: ${result.status ?? "ok"}`);
|
|
12498
|
-
return;
|
|
12499
|
-
}
|
|
12500
|
-
if (sub === "tools") {
|
|
12501
|
-
const tools = await context.client.mcpTools();
|
|
12502
|
-
context.addMessage("system", "Available MCP Tools:");
|
|
12503
|
-
if (Array.isArray(tools)) {
|
|
12504
|
-
for (const tool of tools) {
|
|
12505
|
-
context.addMessage("system", ` - ${tool.name}: ${tool.description ?? ""}`);
|
|
12506
|
-
}
|
|
12507
|
-
} else if (typeof tools === "object") {
|
|
12508
|
-
context.addMessage("system", JSON.stringify(tools, null, 2));
|
|
12509
|
-
}
|
|
12510
|
-
return;
|
|
12511
|
-
}
|
|
12512
|
-
if (sub === "invoke") {
|
|
12513
|
-
const method = rest[0];
|
|
12514
|
-
if (!method) {
|
|
12515
|
-
context.addMessage("system", "Usage: /mcp invoke <method> [args...]");
|
|
12516
|
-
return;
|
|
12517
|
-
}
|
|
12518
|
-
const argsStr = rest.slice(1).join(" ");
|
|
12519
|
-
let argsObj;
|
|
12520
|
-
try {
|
|
12521
|
-
argsObj = argsStr ? JSON.parse(argsStr) : void 0;
|
|
12522
|
-
} catch {
|
|
12523
|
-
argsObj = { raw: argsStr };
|
|
12524
|
-
}
|
|
12525
|
-
const result = await context.client.mcpInvoke(method, argsObj);
|
|
12526
|
-
context.addMessage("system", `MCP Result: ${JSON.stringify(result)}`);
|
|
12527
|
-
return;
|
|
12528
|
-
}
|
|
12529
|
-
if (sub === "disconnect") {
|
|
12530
|
-
const result = await context.client.mcpDisconnect();
|
|
12531
|
-
context.addMessage("system", `MCP Disconnected: ${result.status ?? "ok"}`);
|
|
12532
|
-
return;
|
|
12533
|
-
}
|
|
12534
|
-
context.addMessage("error", `Unknown /mcp subcommand '${sub}'. Try: connect, tools, invoke, disconnect.`);
|
|
12535
|
-
} catch (e) {
|
|
12536
|
-
context.addMessage("error", `MCP: ${e.message}`);
|
|
12537
|
-
}
|
|
12538
|
-
}
|
|
12539
|
-
};
|
|
12540
|
-
}
|
|
12541
|
-
});
|
|
12542
|
-
|
|
12543
|
-
// src/commands/webhook.ts
|
|
12544
|
-
var webhookCommand;
|
|
12545
|
-
var init_webhook = __esm({
|
|
12546
|
-
"src/commands/webhook.ts"() {
|
|
12547
|
-
"use strict";
|
|
12548
|
-
init_esm_shims();
|
|
12549
|
-
webhookCommand = {
|
|
12550
|
-
name: "webhook",
|
|
12551
|
-
args: "[list|add|rm|deliveries|retry] [...args]",
|
|
12552
|
-
description: "Manage webhooks: create, list, delete, and view delivery logs",
|
|
12553
|
-
category: "project",
|
|
12554
|
-
handler: async (args2, context) => {
|
|
12555
|
-
const sub = (args2[0] ?? "list").toLowerCase();
|
|
12556
|
-
const rest = args2.slice(1);
|
|
12557
|
-
try {
|
|
12558
|
-
if (sub === "list") {
|
|
12559
|
-
const webhooks = await context.client.listWebhooks();
|
|
12560
|
-
context.addMessage("system", "Webhooks:");
|
|
12561
|
-
if (Array.isArray(webhooks) && webhooks.length > 0) {
|
|
12562
|
-
for (const wh of webhooks) {
|
|
12563
|
-
const id = wh.id ?? wh.webhook_id ?? "?";
|
|
12564
|
-
const url = wh.url ?? "?";
|
|
12565
|
-
const events = wh.events ? wh.events.join(", ") : "";
|
|
12566
|
-
context.addMessage("system", ` - ${id}: ${url} (${events})`);
|
|
12567
|
-
}
|
|
12568
|
-
} else {
|
|
12569
|
-
context.addMessage("system", " (no webhooks)");
|
|
12570
|
-
}
|
|
12571
|
-
return;
|
|
12572
|
-
}
|
|
12573
|
-
if (sub === "add") {
|
|
12574
|
-
const url = rest[0];
|
|
12575
|
-
const events = rest.slice(1);
|
|
12576
|
-
if (!url || events.length === 0) {
|
|
12577
|
-
context.addMessage("system", "Usage: /webhook add <url> <event1> [event2] ...");
|
|
12578
|
-
return;
|
|
12579
|
-
}
|
|
12580
|
-
const result = await context.client.createWebhook(url, events);
|
|
12581
|
-
const id = result.id ?? result.webhook_id ?? "?";
|
|
12582
|
-
context.addMessage("system", `Webhook created: ${id} \u2192 ${url}`);
|
|
12583
|
-
return;
|
|
12584
|
-
}
|
|
12585
|
-
if (sub === "rm" || sub === "delete") {
|
|
12586
|
-
const id = rest[0];
|
|
12587
|
-
if (!id) {
|
|
12588
|
-
context.addMessage("system", "Usage: /webhook rm <id>");
|
|
12589
|
-
return;
|
|
12590
|
-
}
|
|
12591
|
-
await context.client.deleteWebhook(id);
|
|
12592
|
-
context.addMessage("system", `Deleted webhook: ${id}`);
|
|
12593
|
-
return;
|
|
12594
|
-
}
|
|
12595
|
-
if (sub === "deliveries") {
|
|
12596
|
-
const webhookId = rest[0] || void 0;
|
|
12597
|
-
const deliveries = await context.client.listWebhookDeliveries(webhookId);
|
|
12598
|
-
const title = webhookId ? `Deliveries for webhook ${webhookId}:` : "Recent deliveries:";
|
|
12599
|
-
context.addMessage("system", title);
|
|
12600
|
-
if (Array.isArray(deliveries) && deliveries.length > 0) {
|
|
12601
|
-
for (const d of deliveries) {
|
|
12602
|
-
const id = d.id ?? d.delivery_id ?? "?";
|
|
12603
|
-
const status = d.status ?? "?";
|
|
12604
|
-
const at = d.delivered_at ?? d.created_at ?? "?";
|
|
12605
|
-
context.addMessage("system", ` - ${id}: ${status} (${at})`);
|
|
12606
|
-
}
|
|
12607
|
-
} else {
|
|
12608
|
-
context.addMessage("system", " (no deliveries)");
|
|
12609
|
-
}
|
|
12610
|
-
return;
|
|
12611
|
-
}
|
|
12612
|
-
if (sub === "retry") {
|
|
12613
|
-
const deliveryId = rest[0];
|
|
12614
|
-
if (!deliveryId) {
|
|
12615
|
-
context.addMessage("system", "Usage: /webhook retry <delivery-id>");
|
|
12616
|
-
return;
|
|
12617
|
-
}
|
|
12618
|
-
const result = await context.client.retryWebhookDelivery(deliveryId);
|
|
12619
|
-
context.addMessage("system", `Retrying delivery: ${deliveryId} (${result.status ?? "queued"})`);
|
|
12620
|
-
return;
|
|
12621
|
-
}
|
|
12622
|
-
context.addMessage("error", `Unknown /webhook subcommand '${sub}'. Try: list, add, rm, deliveries, retry.`);
|
|
12623
|
-
} catch (e) {
|
|
12624
|
-
context.addMessage("error", `Webhook: ${e.message}`);
|
|
12625
|
-
}
|
|
12626
|
-
}
|
|
12627
|
-
};
|
|
12628
|
-
}
|
|
12629
|
-
});
|
|
12630
|
-
|
|
12631
|
-
// src/commands/sync.ts
|
|
12632
|
-
var syncCommand;
|
|
12633
|
-
var init_sync = __esm({
|
|
12634
|
-
"src/commands/sync.ts"() {
|
|
12860
|
+
});
|
|
12861
|
+
|
|
12862
|
+
// src/commands/mcp.ts
|
|
12863
|
+
var mcpCommand;
|
|
12864
|
+
var init_mcp = __esm({
|
|
12865
|
+
"src/commands/mcp.ts"() {
|
|
12635
12866
|
"use strict";
|
|
12636
12867
|
init_esm_shims();
|
|
12637
|
-
|
|
12638
|
-
|
|
12639
|
-
|
|
12640
|
-
description: "
|
|
12641
|
-
args: "[status|push]",
|
|
12868
|
+
mcpCommand = {
|
|
12869
|
+
name: "mcp",
|
|
12870
|
+
args: "[connect|invoke|tools|disconnect] [...args]",
|
|
12871
|
+
description: "MCP client surface: connect, invoke, list tools, disconnect",
|
|
12642
12872
|
category: "config",
|
|
12643
12873
|
handler: async (args2, context) => {
|
|
12644
|
-
const
|
|
12645
|
-
const
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
const
|
|
12649
|
-
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
` Pending sync: ${pending}`,
|
|
12653
|
-
` Synced: ${total - pending}`,
|
|
12654
|
-
...pending > 0 ? [``, `Run /sync push to sync pending messages to the backend.`] : []
|
|
12655
|
-
].join("\n"));
|
|
12656
|
-
break;
|
|
12657
|
-
}
|
|
12658
|
-
case "push": {
|
|
12659
|
-
const pending = await journal.journalListPending();
|
|
12660
|
-
if (pending.length === 0) {
|
|
12661
|
-
context.addMessage("assistant", "No pending messages to sync.");
|
|
12662
|
-
break;
|
|
12874
|
+
const sub = (args2[0] ?? "tools").toLowerCase();
|
|
12875
|
+
const rest = args2.slice(1);
|
|
12876
|
+
try {
|
|
12877
|
+
if (sub === "connect") {
|
|
12878
|
+
const url = rest[0];
|
|
12879
|
+
if (!url) {
|
|
12880
|
+
context.addMessage("system", "Usage: /mcp connect <server-url>");
|
|
12881
|
+
return;
|
|
12663
12882
|
}
|
|
12664
|
-
|
|
12665
|
-
|
|
12666
|
-
|
|
12667
|
-
|
|
12668
|
-
|
|
12883
|
+
const result = await context.client.mcpConnect(url);
|
|
12884
|
+
context.addMessage("system", `MCP Connected: ${result.status ?? "ok"}`);
|
|
12885
|
+
return;
|
|
12886
|
+
}
|
|
12887
|
+
if (sub === "tools") {
|
|
12888
|
+
const tools = await context.client.mcpTools();
|
|
12889
|
+
context.addMessage("system", "Available MCP Tools:");
|
|
12890
|
+
if (Array.isArray(tools)) {
|
|
12891
|
+
for (const tool of tools) {
|
|
12892
|
+
context.addMessage("system", ` - ${tool.name}: ${tool.description ?? ""}`);
|
|
12669
12893
|
}
|
|
12670
|
-
|
|
12894
|
+
} else if (typeof tools === "object") {
|
|
12895
|
+
context.addMessage("system", JSON.stringify(tools, null, 2));
|
|
12671
12896
|
}
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
|
|
12677
|
-
|
|
12678
|
-
|
|
12679
|
-
chat_id: chatId,
|
|
12680
|
-
messages: batch.map((e) => ({
|
|
12681
|
-
role: e.role,
|
|
12682
|
-
content: e.content,
|
|
12683
|
-
model: e.model
|
|
12684
|
-
}))
|
|
12685
|
-
});
|
|
12686
|
-
await journal.journalMarkSynced(batch.map((e) => e.id));
|
|
12687
|
-
synced += batch.length;
|
|
12688
|
-
} catch (e) {
|
|
12689
|
-
context.addMessage(
|
|
12690
|
-
"error",
|
|
12691
|
-
`Error syncing batch from chat ${chatId}: ${String(e).slice(0, 100)}`
|
|
12692
|
-
);
|
|
12693
|
-
failed += batch.length;
|
|
12694
|
-
}
|
|
12695
|
-
}
|
|
12897
|
+
return;
|
|
12898
|
+
}
|
|
12899
|
+
if (sub === "invoke") {
|
|
12900
|
+
const method = rest[0];
|
|
12901
|
+
if (!method) {
|
|
12902
|
+
context.addMessage("system", "Usage: /mcp invoke <method> [args...]");
|
|
12903
|
+
return;
|
|
12696
12904
|
}
|
|
12697
|
-
|
|
12698
|
-
|
|
12905
|
+
const argsStr = rest.slice(1).join(" ");
|
|
12906
|
+
let argsObj;
|
|
12907
|
+
try {
|
|
12908
|
+
argsObj = argsStr ? JSON.parse(argsStr) : void 0;
|
|
12909
|
+
} catch {
|
|
12910
|
+
argsObj = { raw: argsStr };
|
|
12911
|
+
}
|
|
12912
|
+
const result = await context.client.mcpInvoke(method, argsObj);
|
|
12913
|
+
context.addMessage("system", `MCP Result: ${JSON.stringify(result)}`);
|
|
12914
|
+
return;
|
|
12699
12915
|
}
|
|
12700
|
-
|
|
12701
|
-
|
|
12702
|
-
|
|
12916
|
+
if (sub === "disconnect") {
|
|
12917
|
+
const result = await context.client.mcpDisconnect();
|
|
12918
|
+
context.addMessage("system", `MCP Disconnected: ${result.status ?? "ok"}`);
|
|
12919
|
+
return;
|
|
12920
|
+
}
|
|
12921
|
+
context.addMessage("error", `Unknown /mcp subcommand '${sub}'. Try: connect, tools, invoke, disconnect.`);
|
|
12922
|
+
} catch (e) {
|
|
12923
|
+
context.addMessage("error", `MCP: ${e.message}`);
|
|
12703
12924
|
}
|
|
12704
12925
|
}
|
|
12705
12926
|
};
|
|
12706
12927
|
}
|
|
12707
12928
|
});
|
|
12708
12929
|
|
|
12709
|
-
//
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
const cpuList = cpus2();
|
|
12714
|
-
const totalMemBytes = totalmem2();
|
|
12715
|
-
const freeMemBytes = freemem2();
|
|
12716
|
-
const specs = {
|
|
12717
|
-
cpu: {
|
|
12718
|
-
cores: cpuList.length,
|
|
12719
|
-
model: cpuList[0]?.model ?? "Unknown",
|
|
12720
|
-
speed: cpuList[0]?.speed ?? 0
|
|
12721
|
-
// MHz -> convert below
|
|
12722
|
-
},
|
|
12723
|
-
memory: {
|
|
12724
|
-
totalGB: Math.round(totalMemBytes / 1024 ** 3 * 10) / 10,
|
|
12725
|
-
freeGB: Math.round(freeMemBytes / 1024 ** 3 * 10) / 10
|
|
12726
|
-
},
|
|
12727
|
-
gpu: [],
|
|
12728
|
-
disk: {
|
|
12729
|
-
freeGB: 0,
|
|
12730
|
-
totalGB: 0
|
|
12731
|
-
},
|
|
12732
|
-
platform: platform3(),
|
|
12733
|
-
arch: arch2(),
|
|
12734
|
-
nodeVersion: process.version.slice(1)
|
|
12735
|
-
// Remove 'v' prefix
|
|
12736
|
-
};
|
|
12737
|
-
specs.cpu.speed = Math.round(specs.cpu.speed / 1e3 * 10) / 10;
|
|
12738
|
-
try {
|
|
12739
|
-
const { statfs } = await import("fs/promises");
|
|
12740
|
-
const homeDir = process.env.HOME || process.env.USERPROFILE || "/";
|
|
12741
|
-
const stat5 = await statfs(homeDir);
|
|
12742
|
-
specs.disk.freeGB = Math.round(stat5.bavail * stat5.bsize / 1024 ** 3 * 10) / 10;
|
|
12743
|
-
specs.disk.totalGB = Math.round(stat5.blocks * stat5.bsize / 1024 ** 3 * 10) / 10;
|
|
12744
|
-
} catch {
|
|
12745
|
-
specs.disk.freeGB = -1;
|
|
12746
|
-
specs.disk.totalGB = -1;
|
|
12747
|
-
}
|
|
12748
|
-
specs.gpu = await detectGpu();
|
|
12749
|
-
return specs;
|
|
12750
|
-
}
|
|
12751
|
-
async function detectGpu() {
|
|
12752
|
-
const currentPlatform = platform3();
|
|
12753
|
-
if (currentPlatform === "win32") {
|
|
12754
|
-
return detectGpuWindows();
|
|
12755
|
-
} else if (currentPlatform === "darwin") {
|
|
12756
|
-
return detectGpuMacOS();
|
|
12757
|
-
} else {
|
|
12758
|
-
return detectGpuLinux();
|
|
12759
|
-
}
|
|
12760
|
-
}
|
|
12761
|
-
function detectGpuWindows() {
|
|
12762
|
-
try {
|
|
12763
|
-
const output = execSync(
|
|
12764
|
-
'powershell -Command "Get-CimInstance Win32_VideoController | Select-Object Name, AdapterRAM"',
|
|
12765
|
-
{ timeout: 2e3, encoding: "utf-8" }
|
|
12766
|
-
);
|
|
12767
|
-
const gpus = [];
|
|
12768
|
-
const lines = output.split("\n");
|
|
12769
|
-
for (const line of lines) {
|
|
12770
|
-
const parts = line.trim().split(/\s{2,}/);
|
|
12771
|
-
if (parts[0] && parts[0] !== "Name") {
|
|
12772
|
-
const memBytes = parseInt(parts[1] || "0");
|
|
12773
|
-
gpus.push({
|
|
12774
|
-
name: parts[0],
|
|
12775
|
-
memoryGB: memBytes > 0 ? Math.round(memBytes / 1024 ** 3 * 10) / 10 : void 0
|
|
12776
|
-
});
|
|
12777
|
-
}
|
|
12778
|
-
}
|
|
12779
|
-
return gpus;
|
|
12780
|
-
} catch {
|
|
12781
|
-
return [];
|
|
12782
|
-
}
|
|
12783
|
-
}
|
|
12784
|
-
function detectGpuMacOS() {
|
|
12785
|
-
try {
|
|
12786
|
-
const output = execSync("system_profiler SPDisplaysDataType -json", {
|
|
12787
|
-
timeout: 2e3,
|
|
12788
|
-
encoding: "utf-8"
|
|
12789
|
-
});
|
|
12790
|
-
const data = JSON.parse(output);
|
|
12791
|
-
const gpus = [];
|
|
12792
|
-
const displays = data.SPDisplaysDataType || [];
|
|
12793
|
-
for (const display of displays) {
|
|
12794
|
-
const chips = display["sppci_model_name"];
|
|
12795
|
-
if (chips) {
|
|
12796
|
-
gpus.push({ name: chips });
|
|
12797
|
-
}
|
|
12798
|
-
}
|
|
12799
|
-
return gpus;
|
|
12800
|
-
} catch {
|
|
12801
|
-
return [];
|
|
12802
|
-
}
|
|
12803
|
-
}
|
|
12804
|
-
function detectGpuLinux() {
|
|
12805
|
-
const gpus = [];
|
|
12806
|
-
try {
|
|
12807
|
-
const output = execSync(
|
|
12808
|
-
"nvidia-smi --query-gpu=name,memory.total --format=csv,noheader",
|
|
12809
|
-
{ timeout: 2e3, encoding: "utf-8" }
|
|
12810
|
-
);
|
|
12811
|
-
const lines = output.trim().split("\n");
|
|
12812
|
-
for (const line of lines) {
|
|
12813
|
-
const [name, mem] = line.split(",");
|
|
12814
|
-
const memGB = mem ? Math.round(parseInt(mem) / 1024 * 10) / 10 : void 0;
|
|
12815
|
-
gpus.push({ name: name.trim(), memoryGB: memGB });
|
|
12816
|
-
}
|
|
12817
|
-
return gpus;
|
|
12818
|
-
} catch {
|
|
12819
|
-
}
|
|
12820
|
-
try {
|
|
12821
|
-
const output = execSync("rocm-smi --showproductname", {
|
|
12822
|
-
timeout: 2e3,
|
|
12823
|
-
encoding: "utf-8"
|
|
12824
|
-
});
|
|
12825
|
-
const lines = output.trim().split("\n");
|
|
12826
|
-
for (const line of lines) {
|
|
12827
|
-
if (line.includes("GPU")) {
|
|
12828
|
-
const match = line.match(/:\s*(.+)/);
|
|
12829
|
-
if (match) gpus.push({ name: match[1].trim() });
|
|
12830
|
-
}
|
|
12831
|
-
}
|
|
12832
|
-
return gpus;
|
|
12833
|
-
} catch {
|
|
12834
|
-
}
|
|
12835
|
-
try {
|
|
12836
|
-
const output = execSync("lspci | grep -i vga", {
|
|
12837
|
-
timeout: 2e3,
|
|
12838
|
-
encoding: "utf-8"
|
|
12839
|
-
});
|
|
12840
|
-
const lines = output.trim().split("\n");
|
|
12841
|
-
for (const line of lines) {
|
|
12842
|
-
const match = line.match(/:\s*(.+)/);
|
|
12843
|
-
if (match) gpus.push({ name: match[1].trim() });
|
|
12844
|
-
}
|
|
12845
|
-
return gpus;
|
|
12846
|
-
} catch {
|
|
12847
|
-
return [];
|
|
12848
|
-
}
|
|
12849
|
-
}
|
|
12850
|
-
var init_specs = __esm({
|
|
12851
|
-
"../core/src/diagnostics/specs.ts"() {
|
|
12930
|
+
// src/commands/webhook.ts
|
|
12931
|
+
var webhookCommand;
|
|
12932
|
+
var init_webhook = __esm({
|
|
12933
|
+
"src/commands/webhook.ts"() {
|
|
12852
12934
|
"use strict";
|
|
12853
12935
|
init_esm_shims();
|
|
12936
|
+
webhookCommand = {
|
|
12937
|
+
name: "webhook",
|
|
12938
|
+
args: "[list|add|rm|deliveries|retry] [...args]",
|
|
12939
|
+
description: "Manage webhooks: create, list, delete, and view delivery logs",
|
|
12940
|
+
category: "project",
|
|
12941
|
+
handler: async (args2, context) => {
|
|
12942
|
+
const sub = (args2[0] ?? "list").toLowerCase();
|
|
12943
|
+
const rest = args2.slice(1);
|
|
12944
|
+
try {
|
|
12945
|
+
if (sub === "list") {
|
|
12946
|
+
const webhooks = await context.client.listWebhooks();
|
|
12947
|
+
context.addMessage("system", "Webhooks:");
|
|
12948
|
+
if (Array.isArray(webhooks) && webhooks.length > 0) {
|
|
12949
|
+
for (const wh of webhooks) {
|
|
12950
|
+
const id = wh.id ?? wh.webhook_id ?? "?";
|
|
12951
|
+
const url = wh.url ?? "?";
|
|
12952
|
+
const events = wh.events ? wh.events.join(", ") : "";
|
|
12953
|
+
context.addMessage("system", ` - ${id}: ${url} (${events})`);
|
|
12954
|
+
}
|
|
12955
|
+
} else {
|
|
12956
|
+
context.addMessage("system", " (no webhooks)");
|
|
12957
|
+
}
|
|
12958
|
+
return;
|
|
12959
|
+
}
|
|
12960
|
+
if (sub === "add") {
|
|
12961
|
+
const url = rest[0];
|
|
12962
|
+
const events = rest.slice(1);
|
|
12963
|
+
if (!url || events.length === 0) {
|
|
12964
|
+
context.addMessage("system", "Usage: /webhook add <url> <event1> [event2] ...");
|
|
12965
|
+
return;
|
|
12966
|
+
}
|
|
12967
|
+
const result = await context.client.createWebhook(url, events);
|
|
12968
|
+
const id = result.id ?? result.webhook_id ?? "?";
|
|
12969
|
+
context.addMessage("system", `Webhook created: ${id} \u2192 ${url}`);
|
|
12970
|
+
return;
|
|
12971
|
+
}
|
|
12972
|
+
if (sub === "rm" || sub === "delete") {
|
|
12973
|
+
const id = rest[0];
|
|
12974
|
+
if (!id) {
|
|
12975
|
+
context.addMessage("system", "Usage: /webhook rm <id>");
|
|
12976
|
+
return;
|
|
12977
|
+
}
|
|
12978
|
+
await context.client.deleteWebhook(id);
|
|
12979
|
+
context.addMessage("system", `Deleted webhook: ${id}`);
|
|
12980
|
+
return;
|
|
12981
|
+
}
|
|
12982
|
+
if (sub === "deliveries") {
|
|
12983
|
+
const webhookId = rest[0] || void 0;
|
|
12984
|
+
const deliveries = await context.client.listWebhookDeliveries(webhookId);
|
|
12985
|
+
const title = webhookId ? `Deliveries for webhook ${webhookId}:` : "Recent deliveries:";
|
|
12986
|
+
context.addMessage("system", title);
|
|
12987
|
+
if (Array.isArray(deliveries) && deliveries.length > 0) {
|
|
12988
|
+
for (const d of deliveries) {
|
|
12989
|
+
const id = d.id ?? d.delivery_id ?? "?";
|
|
12990
|
+
const status = d.status ?? "?";
|
|
12991
|
+
const at = d.delivered_at ?? d.created_at ?? "?";
|
|
12992
|
+
context.addMessage("system", ` - ${id}: ${status} (${at})`);
|
|
12993
|
+
}
|
|
12994
|
+
} else {
|
|
12995
|
+
context.addMessage("system", " (no deliveries)");
|
|
12996
|
+
}
|
|
12997
|
+
return;
|
|
12998
|
+
}
|
|
12999
|
+
if (sub === "retry") {
|
|
13000
|
+
const deliveryId = rest[0];
|
|
13001
|
+
if (!deliveryId) {
|
|
13002
|
+
context.addMessage("system", "Usage: /webhook retry <delivery-id>");
|
|
13003
|
+
return;
|
|
13004
|
+
}
|
|
13005
|
+
const result = await context.client.retryWebhookDelivery(deliveryId);
|
|
13006
|
+
context.addMessage("system", `Retrying delivery: ${deliveryId} (${result.status ?? "queued"})`);
|
|
13007
|
+
return;
|
|
13008
|
+
}
|
|
13009
|
+
context.addMessage("error", `Unknown /webhook subcommand '${sub}'. Try: list, add, rm, deliveries, retry.`);
|
|
13010
|
+
} catch (e) {
|
|
13011
|
+
context.addMessage("error", `Webhook: ${e.message}`);
|
|
13012
|
+
}
|
|
13013
|
+
}
|
|
13014
|
+
};
|
|
12854
13015
|
}
|
|
12855
13016
|
});
|
|
12856
13017
|
|
|
12857
|
-
//
|
|
12858
|
-
|
|
12859
|
-
|
|
12860
|
-
|
|
12861
|
-
import { join as join29 } from "path";
|
|
12862
|
-
function determineTier(specs) {
|
|
12863
|
-
const memGB = specs.memory.totalGB;
|
|
12864
|
-
const cores = specs.cpu.cores;
|
|
12865
|
-
if (memGB < 8 || cores < 4) return "T1";
|
|
12866
|
-
if (memGB < 16 || cores < 8) return "T2";
|
|
12867
|
-
if (memGB < 32 || cores < 16) return "T3";
|
|
12868
|
-
return "T4";
|
|
12869
|
-
}
|
|
12870
|
-
function recommendLimits(specs) {
|
|
12871
|
-
const tier = determineTier(specs);
|
|
12872
|
-
return tierTable[tier];
|
|
12873
|
-
}
|
|
12874
|
-
async function readConfigOverrides() {
|
|
12875
|
-
try {
|
|
12876
|
-
const configPath = join29(homedir18(), ".msapling", "config.json");
|
|
12877
|
-
const content = await readFile23(configPath, "utf-8");
|
|
12878
|
-
const config = JSON.parse(content);
|
|
12879
|
-
return config.limits ?? null;
|
|
12880
|
-
} catch {
|
|
12881
|
-
return null;
|
|
12882
|
-
}
|
|
12883
|
-
}
|
|
12884
|
-
async function createResourceGovernor(specs) {
|
|
12885
|
-
const recommended = recommendLimits(specs);
|
|
12886
|
-
const overrides = await readConfigOverrides();
|
|
12887
|
-
return new ResourceGovernor(recommended, overrides ?? void 0);
|
|
12888
|
-
}
|
|
12889
|
-
var tierTable, ResourceGovernor;
|
|
12890
|
-
var init_ResourceGovernor = __esm({
|
|
12891
|
-
"../core/src/governor/ResourceGovernor.ts"() {
|
|
13018
|
+
// src/commands/sync.ts
|
|
13019
|
+
var syncCommand;
|
|
13020
|
+
var init_sync = __esm({
|
|
13021
|
+
"src/commands/sync.ts"() {
|
|
12892
13022
|
"use strict";
|
|
12893
13023
|
init_esm_shims();
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
12911
|
-
|
|
12912
|
-
|
|
12913
|
-
|
|
12914
|
-
}
|
|
12915
|
-
/**
|
|
12916
|
-
* Monitor free memory every 5s; block new acquires if below threshold
|
|
12917
|
-
*/
|
|
12918
|
-
startMemoryMonitor() {
|
|
12919
|
-
this.memoryCheckInterval = setInterval(() => {
|
|
12920
|
-
const freeMemBytes = freemem3();
|
|
12921
|
-
const freeMemGB = freeMemBytes / 1024 ** 3;
|
|
12922
|
-
if (freeMemGB < this.minFreeMemGB && !this.memoryWarningShown) {
|
|
12923
|
-
console.warn(
|
|
12924
|
-
`[ResourceGovernor] Low memory: ${freeMemGB.toFixed(2)}GB free (threshold: ${this.minFreeMemGB}GB)`
|
|
12925
|
-
);
|
|
12926
|
-
this.memoryWarningShown = true;
|
|
12927
|
-
} else if (freeMemGB >= this.minFreeMemGB) {
|
|
12928
|
-
this.memoryWarningShown = false;
|
|
13024
|
+
init_src();
|
|
13025
|
+
syncCommand = {
|
|
13026
|
+
name: "sync",
|
|
13027
|
+
description: "Manage offline message sync (status|push)",
|
|
13028
|
+
args: "[status|push]",
|
|
13029
|
+
category: "config",
|
|
13030
|
+
handler: async (args2, context) => {
|
|
13031
|
+
const subcommand = (args2[0] || "status").toLowerCase();
|
|
13032
|
+
const journal = getJournal();
|
|
13033
|
+
switch (subcommand) {
|
|
13034
|
+
case "status": {
|
|
13035
|
+
const { pending, total } = await journal.journalCount();
|
|
13036
|
+
context.addMessage("assistant", [
|
|
13037
|
+
`Offline messages journal:`,
|
|
13038
|
+
` Total: ${total}`,
|
|
13039
|
+
` Pending sync: ${pending}`,
|
|
13040
|
+
` Synced: ${total - pending}`,
|
|
13041
|
+
...pending > 0 ? [``, `Run /sync push to sync pending messages to the backend.`] : []
|
|
13042
|
+
].join("\n"));
|
|
13043
|
+
break;
|
|
12929
13044
|
}
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
|
|
12933
|
-
|
|
12934
|
-
|
|
12935
|
-
|
|
12936
|
-
|
|
12937
|
-
|
|
12938
|
-
|
|
12939
|
-
|
|
12940
|
-
|
|
12941
|
-
|
|
12942
|
-
|
|
12943
|
-
|
|
12944
|
-
|
|
12945
|
-
|
|
12946
|
-
|
|
12947
|
-
|
|
12948
|
-
|
|
12949
|
-
|
|
12950
|
-
|
|
12951
|
-
|
|
12952
|
-
|
|
12953
|
-
|
|
12954
|
-
|
|
12955
|
-
|
|
12956
|
-
|
|
12957
|
-
|
|
12958
|
-
|
|
12959
|
-
|
|
12960
|
-
|
|
12961
|
-
|
|
12962
|
-
|
|
12963
|
-
|
|
12964
|
-
|
|
12965
|
-
|
|
12966
|
-
|
|
12967
|
-
|
|
12968
|
-
|
|
12969
|
-
|
|
12970
|
-
|
|
12971
|
-
|
|
12972
|
-
|
|
12973
|
-
|
|
12974
|
-
|
|
12975
|
-
/**
|
|
12976
|
-
* Clean up monitor interval
|
|
12977
|
-
*/
|
|
12978
|
-
destroy() {
|
|
12979
|
-
if (this.memoryCheckInterval) {
|
|
12980
|
-
clearInterval(this.memoryCheckInterval);
|
|
12981
|
-
this.memoryCheckInterval = null;
|
|
13045
|
+
case "push": {
|
|
13046
|
+
const pending = await journal.journalListPending();
|
|
13047
|
+
if (pending.length === 0) {
|
|
13048
|
+
context.addMessage("assistant", "No pending messages to sync.");
|
|
13049
|
+
break;
|
|
13050
|
+
}
|
|
13051
|
+
context.addMessage("assistant", `Syncing ${pending.length} message(s)...`);
|
|
13052
|
+
const byChat = /* @__PURE__ */ new Map();
|
|
13053
|
+
for (const entry of pending) {
|
|
13054
|
+
if (!byChat.has(entry.chat_id)) {
|
|
13055
|
+
byChat.set(entry.chat_id, []);
|
|
13056
|
+
}
|
|
13057
|
+
byChat.get(entry.chat_id).push(entry);
|
|
13058
|
+
}
|
|
13059
|
+
let synced = 0;
|
|
13060
|
+
let failed = 0;
|
|
13061
|
+
for (const [chatId, entries] of byChat.entries()) {
|
|
13062
|
+
for (let i = 0; i < entries.length; i += 50) {
|
|
13063
|
+
const batch = entries.slice(i, i + 50);
|
|
13064
|
+
try {
|
|
13065
|
+
await context.client.importChat({
|
|
13066
|
+
chat_id: chatId,
|
|
13067
|
+
messages: batch.map((e) => ({
|
|
13068
|
+
role: e.role,
|
|
13069
|
+
content: e.content,
|
|
13070
|
+
model: e.model
|
|
13071
|
+
}))
|
|
13072
|
+
});
|
|
13073
|
+
await journal.journalMarkSynced(batch.map((e) => e.id));
|
|
13074
|
+
synced += batch.length;
|
|
13075
|
+
} catch (e) {
|
|
13076
|
+
context.addMessage(
|
|
13077
|
+
"error",
|
|
13078
|
+
`Error syncing batch from chat ${chatId}: ${String(e).slice(0, 100)}`
|
|
13079
|
+
);
|
|
13080
|
+
failed += batch.length;
|
|
13081
|
+
}
|
|
13082
|
+
}
|
|
13083
|
+
}
|
|
13084
|
+
context.addMessage("assistant", `Sync complete: ${synced} synced, ${failed} failed`);
|
|
13085
|
+
break;
|
|
13086
|
+
}
|
|
13087
|
+
default:
|
|
13088
|
+
context.addMessage("error", `Unknown sync subcommand: ${subcommand}
|
|
13089
|
+
Usage: /sync [status|push]`);
|
|
12982
13090
|
}
|
|
12983
13091
|
}
|
|
12984
13092
|
};
|
|
@@ -12992,9 +13100,8 @@ async function handler(args2, ctx) {
|
|
|
12992
13100
|
const specs = await collectSpecs();
|
|
12993
13101
|
const recommended = recommendLimits(specs);
|
|
12994
13102
|
const tier = determineTier(specs);
|
|
12995
|
-
const governor = await
|
|
13103
|
+
const governor = await getGlobalGovernor();
|
|
12996
13104
|
const status = governor.getStatus();
|
|
12997
|
-
governor.destroy();
|
|
12998
13105
|
if (jsonMode) {
|
|
12999
13106
|
const output = {
|
|
13000
13107
|
specs,
|
|
@@ -13250,7 +13357,7 @@ var exec_exports = {};
|
|
|
13250
13357
|
__export(exec_exports, {
|
|
13251
13358
|
runExec: () => runExec
|
|
13252
13359
|
});
|
|
13253
|
-
import { existsSync as
|
|
13360
|
+
import { existsSync as existsSync27 } from "fs";
|
|
13254
13361
|
import { readFile as readFile25 } from "fs/promises";
|
|
13255
13362
|
import { homedir as homedir19 } from "os";
|
|
13256
13363
|
import { join as join30 } from "path";
|
|
@@ -13258,7 +13365,7 @@ async function loadPersistedSettings() {
|
|
|
13258
13365
|
const out = { mode: "default", theme: null };
|
|
13259
13366
|
try {
|
|
13260
13367
|
const p = join30(homedir19(), ".msapling", "settings.json");
|
|
13261
|
-
if (!
|
|
13368
|
+
if (!existsSync27(p)) return out;
|
|
13262
13369
|
const raw = JSON.parse(await readFile25(p, "utf8"));
|
|
13263
13370
|
const parsed = parseApprovalMode(raw, Date.now());
|
|
13264
13371
|
if (parsed.kind === "ok") out.mode = parsed.mode;
|
|
@@ -14136,7 +14243,7 @@ __export(doctor_exports, {
|
|
|
14136
14243
|
});
|
|
14137
14244
|
import { homedir as homedir20, platform as platform4 } from "os";
|
|
14138
14245
|
import { join as join31 } from "path";
|
|
14139
|
-
import { existsSync as
|
|
14246
|
+
import { existsSync as existsSync28, statSync as statSync6, accessSync } from "fs";
|
|
14140
14247
|
import { readdir as readdir3 } from "fs/promises";
|
|
14141
14248
|
import { exec } from "child_process";
|
|
14142
14249
|
import { promisify } from "util";
|
|
@@ -14160,7 +14267,7 @@ async function checkNodeVersion() {
|
|
|
14160
14267
|
}
|
|
14161
14268
|
async function checkConfigDir() {
|
|
14162
14269
|
const configDir = join31(homedir20(), ".msapling");
|
|
14163
|
-
if (!
|
|
14270
|
+
if (!existsSync28(configDir)) {
|
|
14164
14271
|
return {
|
|
14165
14272
|
name: "Config directory",
|
|
14166
14273
|
status: "WARN",
|
|
@@ -14226,7 +14333,7 @@ async function checkPathConflicts() {
|
|
|
14226
14333
|
const paths = pathEnv.split(platform4() === "win32" ? ";" : ":");
|
|
14227
14334
|
const conflicts = [];
|
|
14228
14335
|
for (const dir of paths) {
|
|
14229
|
-
if (!dir || !
|
|
14336
|
+
if (!dir || !existsSync28(dir)) continue;
|
|
14230
14337
|
try {
|
|
14231
14338
|
const files = await readdir3(dir);
|
|
14232
14339
|
for (const file of files) {
|
|
@@ -15996,7 +16103,7 @@ __export(server_exports, {
|
|
|
15996
16103
|
runStdio: () => runStdio,
|
|
15997
16104
|
runStdioWithRegistry: () => runStdioWithRegistry
|
|
15998
16105
|
});
|
|
15999
|
-
import { readdirSync as readdirSync4, readFileSync as
|
|
16106
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync3, statSync as statSync7 } from "fs";
|
|
16000
16107
|
import { join as join32, relative as relative14, resolve as resolve18 } from "path";
|
|
16001
16108
|
function asResult2(text, isError = false) {
|
|
16002
16109
|
return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
|
|
@@ -16041,7 +16148,7 @@ function readFilesAsContext(root, files, maxKB) {
|
|
|
16041
16148
|
for (const f of files) {
|
|
16042
16149
|
let body;
|
|
16043
16150
|
try {
|
|
16044
|
-
body =
|
|
16151
|
+
body = readFileSync3(f, "utf8");
|
|
16045
16152
|
} catch {
|
|
16046
16153
|
continue;
|
|
16047
16154
|
}
|
|
@@ -16783,7 +16890,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
16783
16890
|
var Header = () => /* @__PURE__ */ jsxs(Box, { borderStyle: "single", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [
|
|
16784
16891
|
/* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
|
|
16785
16892
|
"\u25CF MSapling CLI v",
|
|
16786
|
-
"2.3.6-beta.
|
|
16893
|
+
"2.3.6-beta.31"
|
|
16787
16894
|
] }),
|
|
16788
16895
|
/* @__PURE__ */ jsx(Box, { marginLeft: 2, children: /* @__PURE__ */ jsx(Text, { color: "gray", children: "Platinum Tier Architecture" }) })
|
|
16789
16896
|
] });
|
|
@@ -17306,9 +17413,9 @@ ${prompt4}` : prompt4;
|
|
|
17306
17413
|
for (const mention of fileMentions) {
|
|
17307
17414
|
const filePath = mention.slice(1);
|
|
17308
17415
|
try {
|
|
17309
|
-
const { existsSync:
|
|
17416
|
+
const { existsSync: existsSync29 } = await import("fs");
|
|
17310
17417
|
const { readFile: readFile26 } = await import("fs/promises");
|
|
17311
|
-
if (
|
|
17418
|
+
if (existsSync29(filePath)) {
|
|
17312
17419
|
const content = await readFile26(filePath, "utf8");
|
|
17313
17420
|
const MAX_LEN = 32768;
|
|
17314
17421
|
const truncated = content.length > MAX_LEN ? content.slice(0, MAX_LEN) + "\n...[TRUNCATED]" : content;
|
|
@@ -17362,7 +17469,7 @@ init_esm_shims();
|
|
|
17362
17469
|
init_src3();
|
|
17363
17470
|
init_parseApprovalMode();
|
|
17364
17471
|
import { readFile as readFile24 } from "fs/promises";
|
|
17365
|
-
import { existsSync as
|
|
17472
|
+
import { existsSync as existsSync26 } from "fs";
|
|
17366
17473
|
async function initSession(ctx) {
|
|
17367
17474
|
try {
|
|
17368
17475
|
const { settings } = await loadSettings(
|
|
@@ -17379,7 +17486,7 @@ async function initSession(ctx) {
|
|
|
17379
17486
|
const { homedir: homedir21 } = await import("os");
|
|
17380
17487
|
const { join: join34 } = await import("path");
|
|
17381
17488
|
const userSettingsPath = join34(homedir21(), ".msapling", "settings.json");
|
|
17382
|
-
if (
|
|
17489
|
+
if (existsSync26(userSettingsPath)) {
|
|
17383
17490
|
const userText = await readFile24(userSettingsPath, "utf8");
|
|
17384
17491
|
let parsed;
|
|
17385
17492
|
try {
|
|
@@ -17683,14 +17790,14 @@ var App = ({ compact: compact2 = false }) => {
|
|
|
17683
17790
|
|
|
17684
17791
|
// src/runtime/bootstrap.ts
|
|
17685
17792
|
init_esm_shims();
|
|
17686
|
-
import { readFileSync as
|
|
17793
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
17687
17794
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
17688
|
-
import { dirname as
|
|
17795
|
+
import { dirname as dirname4, join as join33 } from "path";
|
|
17689
17796
|
function readCliVersion2() {
|
|
17690
|
-
const here =
|
|
17797
|
+
const here = dirname4(fileURLToPath2(import.meta.url));
|
|
17691
17798
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
17692
17799
|
try {
|
|
17693
|
-
const pkg = JSON.parse(
|
|
17800
|
+
const pkg = JSON.parse(readFileSync4(join33(here, rel), "utf8"));
|
|
17694
17801
|
if (pkg.name && pkg.version) {
|
|
17695
17802
|
return { name: pkg.name, version: pkg.version };
|
|
17696
17803
|
}
|