@modeloslab/modelcode 0.1.5 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs
CHANGED
|
@@ -48,11 +48,12 @@ import {
|
|
|
48
48
|
removeCard,
|
|
49
49
|
removeJob,
|
|
50
50
|
renderBoard,
|
|
51
|
-
|
|
51
|
+
renderDiffPlain,
|
|
52
52
|
route,
|
|
53
53
|
runProc,
|
|
54
54
|
runSubagent,
|
|
55
55
|
runTeam,
|
|
56
|
+
saveConfig,
|
|
56
57
|
searchFacts,
|
|
57
58
|
searchSessions,
|
|
58
59
|
shellInvocation,
|
|
@@ -64,7 +65,7 @@ import {
|
|
|
64
65
|
validateMnemonic,
|
|
65
66
|
walletFromMnemonic,
|
|
66
67
|
which
|
|
67
|
-
} from "./main-
|
|
68
|
+
} from "./main-pj08pbgh.mjs";
|
|
68
69
|
import"./main-p2xnn95s.mjs";
|
|
69
70
|
import {
|
|
70
71
|
__require
|
|
@@ -93,6 +94,7 @@ var ConfigSchema = exports_external.object({
|
|
|
93
94
|
autoSummary: exports_external.boolean().default(true),
|
|
94
95
|
theme: exports_external.enum(["amber", "mono", "ocean"]).default("amber"),
|
|
95
96
|
teamMemoryUrl: exports_external.string().optional(),
|
|
97
|
+
autoFixLaunchErrors: exports_external.boolean().default(false),
|
|
96
98
|
spendCapMdl: exports_external.number().default(0),
|
|
97
99
|
providers: exports_external.record(exports_external.object({ baseUrl: exports_external.string(), apiKey: exports_external.string().optional(), model: exports_external.string().optional() })).default({})
|
|
98
100
|
});
|
|
@@ -119,7 +121,7 @@ function loadConfig(cwd = process.cwd()) {
|
|
|
119
121
|
raw.apiKey = envKey;
|
|
120
122
|
return ConfigSchema.parse(raw);
|
|
121
123
|
}
|
|
122
|
-
function
|
|
124
|
+
function saveConfig2(patch) {
|
|
123
125
|
const p = join(globalDir2(), "config.json");
|
|
124
126
|
let cur = {};
|
|
125
127
|
try {
|
|
@@ -155,7 +157,7 @@ var bash = {
|
|
|
155
157
|
async run(args, ctx) {
|
|
156
158
|
const cmd = String(args.command ?? "");
|
|
157
159
|
const timeout = Number(args.timeout_ms) || 120000;
|
|
158
|
-
const r = await runProc(shellInvocation(cmd), { cwd: ctx.cwd, timeoutMs: timeout, onChunk: (s) => ctx.onStream?.(s) });
|
|
160
|
+
const r = await runProc(shellInvocation(cmd), { cwd: ctx.cwd, timeoutMs: timeout, signal: ctx.signal, onChunk: (s) => ctx.onStream?.(s) });
|
|
159
161
|
return `exit ${r.code}
|
|
160
162
|
${(r.stdout + (r.stderr ? `
|
|
161
163
|
[stderr]
|
|
@@ -226,7 +228,7 @@ var edit = {
|
|
|
226
228
|
const updated = args.replace_all ? src.split(oldS).join(newS) : src.replace(oldS, newS);
|
|
227
229
|
writeFileSync2(p, updated);
|
|
228
230
|
return `edited ${args.path} (${count} replacement${count === 1 ? "" : "s"})
|
|
229
|
-
${
|
|
231
|
+
${renderDiffPlain(oldS, newS)}`;
|
|
230
232
|
}
|
|
231
233
|
};
|
|
232
234
|
var glob = {
|
|
@@ -379,6 +381,15 @@ Tool discipline (important): call each tool ONCE for a given action — never re
|
|
|
379
381
|
A tool result is authoritative: once a write/edit succeeds, trust it and move on. Do NOT claim a task is
|
|
380
382
|
done in the same message where you call the tool to do it — call the tool first, then confirm only after
|
|
381
383
|
you see its result. Keep confirmations to one short sentence.`;
|
|
384
|
+
var LAUNCH_RE = /\b(npm|pnpm|yarn|bun)\s+(run\s+)?(dev|start|serve)\b|\b(vite|next|nuxt|astro|remix|webpack[\- ]dev[\- ]server|nodemon|tsx\s+watch|ts-node-dev)\b|\b(uvicorn|gunicorn|flask\s+run|manage\.py\s+runserver|rails\s+s(erver)?|go\s+run|cargo\s+run|dotnet\s+run|php\s+artisan\s+serve|air|nest\s+start)\b/i;
|
|
385
|
+
function isLaunchCommand(cmd) {
|
|
386
|
+
return LAUNCH_RE.test(cmd);
|
|
387
|
+
}
|
|
388
|
+
function looksLikeLaunchError(result) {
|
|
389
|
+
if (!/^exit\s+([1-9]\d*|124)\b/m.test(result))
|
|
390
|
+
return false;
|
|
391
|
+
return /\b(error|exception|cannot find module|module not found|eaddrinuse|syntaxerror|typeerror|referenceerror|failed to compile|command not found|traceback|panic:|address already in use|unexpected token|ENOENT|MODULE_NOT_FOUND)\b/i.test(result);
|
|
392
|
+
}
|
|
382
393
|
|
|
383
394
|
class Agent {
|
|
384
395
|
cfg;
|
|
@@ -501,6 +512,7 @@ ${summary}` : "Earlier conversation was auto-compacted to free context (summary
|
|
|
501
512
|
recordTurn(this.ctx.cwd, "user", userText);
|
|
502
513
|
newTurn();
|
|
503
514
|
this._doneCalls.clear();
|
|
515
|
+
this._launchHandled.clear();
|
|
504
516
|
const ac = this.abortController = new AbortController;
|
|
505
517
|
const signal = ac.signal;
|
|
506
518
|
let turnGrains = 0;
|
|
@@ -550,6 +562,26 @@ ${tail}`;
|
|
|
550
562
|
}
|
|
551
563
|
this.history.push({ role: "tool", tool_call_id: call.id, name: call.function.name, content });
|
|
552
564
|
toolCount++;
|
|
565
|
+
if (call.function.name === "bash" && !this._launchHandled.has(call.id)) {
|
|
566
|
+
let bashCmd = "";
|
|
567
|
+
try {
|
|
568
|
+
bashCmd = String(JSON.parse(call.function.arguments || "{}").command ?? "");
|
|
569
|
+
} catch {}
|
|
570
|
+
if (isLaunchCommand(bashCmd) && looksLikeLaunchError(content)) {
|
|
571
|
+
this._launchHandled.add(call.id);
|
|
572
|
+
let decision = this.cfg.autoFixLaunchErrors ? "always" : "no";
|
|
573
|
+
if (!this.cfg.autoFixLaunchErrors && this.h.onLaunchError) {
|
|
574
|
+
decision = await this.h.onLaunchError(bashCmd);
|
|
575
|
+
}
|
|
576
|
+
if (decision === "always") {
|
|
577
|
+
this.cfg.autoFixLaunchErrors = true;
|
|
578
|
+
saveConfig({ autoFixLaunchErrors: true });
|
|
579
|
+
}
|
|
580
|
+
if (decision === "yes" || decision === "always") {
|
|
581
|
+
this.history.push({ role: "system", content: `The launch command \`${bashCmd}\` failed to start. Diagnose the root cause from the error output above, fix it (install missing deps, fix config/code, free the port, etc.), then re-run the launch to verify it starts cleanly.` });
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
553
585
|
}
|
|
554
586
|
}
|
|
555
587
|
} catch (e) {
|
|
@@ -577,6 +609,7 @@ ${tail}`;
|
|
|
577
609
|
}
|
|
578
610
|
}
|
|
579
611
|
_suggested = false;
|
|
612
|
+
_launchHandled = new Set;
|
|
580
613
|
_doneCalls = new Map;
|
|
581
614
|
async execToolCall(call) {
|
|
582
615
|
const tool = getTool(call.function.name);
|
|
@@ -602,11 +635,11 @@ ${tail}`;
|
|
|
602
635
|
if (!gate.allowed) {
|
|
603
636
|
result = `blocked by hook: ${gate.reason}`;
|
|
604
637
|
} else if (needsConfirm && !await this.h.confirm(tool.name, args)) {
|
|
605
|
-
result = "
|
|
638
|
+
result = "declined by user";
|
|
606
639
|
} else {
|
|
607
640
|
if (MUTATING_TOOLS.has(tool.name) && typeof args.path === "string")
|
|
608
641
|
snapshot(this.ctx.cwd, args.path);
|
|
609
|
-
const runCtx = { ...this.ctx, onStream: (c) => this.h.onToolStream?.(c), onCost: (g) => this.h.onCost(g) };
|
|
642
|
+
const runCtx = { ...this.ctx, onStream: (c) => this.h.onToolStream?.(c), onCost: (g) => this.h.onCost(g), signal: this.abortController?.signal };
|
|
610
643
|
try {
|
|
611
644
|
result = await tool.run(args, runCtx);
|
|
612
645
|
} catch (e) {
|
|
@@ -625,7 +658,7 @@ ${tail}`;
|
|
|
625
658
|
}
|
|
626
659
|
}
|
|
627
660
|
this.h.onToolResult(tool.name, result);
|
|
628
|
-
if (!result.startsWith("error") && !result.startsWith("
|
|
661
|
+
if (!result.startsWith("error") && !result.startsWith("declined") && !result.startsWith("blocked"))
|
|
629
662
|
this._doneCalls.set(sig, result);
|
|
630
663
|
return { content: result, edited };
|
|
631
664
|
}
|
|
@@ -1310,9 +1343,6 @@ function registerTaskTools() {
|
|
|
1310
1343
|
register(schedule);
|
|
1311
1344
|
}
|
|
1312
1345
|
|
|
1313
|
-
// src/tips/tips.ts
|
|
1314
|
-
var FREQUENCY = Math.max(1, Number(process.env.MODELCODE_TIP_FREQUENCY || "1"));
|
|
1315
|
-
|
|
1316
1346
|
// src/wallet/store.ts
|
|
1317
1347
|
import { join as join5 } from "path";
|
|
1318
1348
|
import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
@@ -1382,7 +1412,7 @@ async function interactiveLogin(rl, cfg) {
|
|
|
1382
1412
|
`);
|
|
1383
1413
|
return false;
|
|
1384
1414
|
}
|
|
1385
|
-
|
|
1415
|
+
saveConfig2({ apiKey: key });
|
|
1386
1416
|
cfg.apiKey = key;
|
|
1387
1417
|
stdout.write(`${C.green}\u2713 logged in${C.reset} (key saved to ~/.modelcode/config.json)
|
|
1388
1418
|
`);
|
|
@@ -1410,7 +1440,7 @@ async function interactiveLogin(rl, cfg) {
|
|
|
1410
1440
|
}
|
|
1411
1441
|
try {
|
|
1412
1442
|
const { apiKey, depositAddr } = await registerApiKey(address);
|
|
1413
|
-
|
|
1443
|
+
saveConfig2({ apiKey });
|
|
1414
1444
|
cfg.apiKey = apiKey;
|
|
1415
1445
|
stdout.write(`${C.green}\u2713 API key created + saved${C.reset}
|
|
1416
1446
|
` + `${C.dim}To buy credits: send MDL to your wallet, then run ${C.reset}modelcode credits fund <mdl>${C.dim} ` + `(it funds the API bank ${depositAddr ? depositAddr.slice(0, 16) + "\u2026" : ""} and claims credits).${C.reset}
|
|
@@ -1440,7 +1470,7 @@ async function maybeAutoKey(address) {
|
|
|
1440
1470
|
return;
|
|
1441
1471
|
try {
|
|
1442
1472
|
const { apiKey, depositAddr } = await registerApiKey(address);
|
|
1443
|
-
|
|
1473
|
+
saveConfig2({ apiKey });
|
|
1444
1474
|
stdout.write(`${C.green}API key created & saved${C.reset} (${apiKey.slice(0, 12)}\u2026)
|
|
1445
1475
|
` + `deposit address: ${depositAddr}
|
|
1446
1476
|
fund it in one command: ${C.cyan}modelcode credits fund <mdl>${C.reset}
|
|
@@ -1609,7 +1639,7 @@ async function main() {
|
|
|
1609
1639
|
s.resume();
|
|
1610
1640
|
await new Promise((r) => setImmediate(r));
|
|
1611
1641
|
}
|
|
1612
|
-
const { runTui } = await import("./tui-
|
|
1642
|
+
const { runTui } = await import("./tui-pgwd6eyk.mjs");
|
|
1613
1643
|
return runTui(cfg, resume);
|
|
1614
1644
|
}
|
|
1615
1645
|
if (cmd === "tui")
|
|
@@ -7921,6 +7921,7 @@ var ConfigSchema = exports_external.object({
|
|
|
7921
7921
|
autoSummary: exports_external.boolean().default(true),
|
|
7922
7922
|
theme: exports_external.enum(["amber", "mono", "ocean"]).default("amber"),
|
|
7923
7923
|
teamMemoryUrl: exports_external.string().optional(),
|
|
7924
|
+
autoFixLaunchErrors: exports_external.boolean().default(false),
|
|
7924
7925
|
spendCapMdl: exports_external.number().default(0),
|
|
7925
7926
|
providers: exports_external.record(exports_external.object({ baseUrl: exports_external.string(), apiKey: exports_external.string().optional(), model: exports_external.string().optional() })).default({})
|
|
7926
7927
|
});
|
|
@@ -8108,6 +8109,19 @@ function runProc(cmd, opts = {}) {
|
|
|
8108
8109
|
finish(124);
|
|
8109
8110
|
}, 2000);
|
|
8110
8111
|
}, opts.timeoutMs);
|
|
8112
|
+
if (opts.signal) {
|
|
8113
|
+
if (opts.signal.aborted) {
|
|
8114
|
+
killTree("SIGKILL");
|
|
8115
|
+
finish(130);
|
|
8116
|
+
} else
|
|
8117
|
+
opts.signal.addEventListener("abort", () => {
|
|
8118
|
+
killTree("SIGTERM");
|
|
8119
|
+
setTimeout(() => {
|
|
8120
|
+
killTree("SIGKILL");
|
|
8121
|
+
finish(130);
|
|
8122
|
+
}, 500);
|
|
8123
|
+
}, { once: true });
|
|
8124
|
+
}
|
|
8111
8125
|
p.stdout?.on("data", (d) => {
|
|
8112
8126
|
const s = dOut.write(d);
|
|
8113
8127
|
stdout += s;
|
|
@@ -15067,37 +15081,43 @@ function toolSchemas() {
|
|
|
15067
15081
|
|
|
15068
15082
|
// src/ui/diff.ts
|
|
15069
15083
|
var A = { red: "\x1B[31m", green: "\x1B[32m", gray: "\x1B[90m", cyan: "\x1B[36m", yellow: "\x1B[33m", mag: "\x1B[35m", reset: "\x1B[0m" };
|
|
15070
|
-
function
|
|
15084
|
+
function renderDiffPlain(before, after) {
|
|
15071
15085
|
const a = before.split(`
|
|
15072
15086
|
`), b = after.split(`
|
|
15073
15087
|
`);
|
|
15074
|
-
const n = Math.min(a.length,
|
|
15088
|
+
const n = Math.min(a.length, 4000), m = Math.min(b.length, 4000);
|
|
15075
15089
|
const dp = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0));
|
|
15076
15090
|
for (let i2 = n - 1;i2 >= 0; i2--)
|
|
15077
15091
|
for (let j2 = m - 1;j2 >= 0; j2--)
|
|
15078
15092
|
dp[i2][j2] = a[i2] === b[j2] ? dp[i2 + 1][j2 + 1] + 1 : Math.max(dp[i2 + 1][j2], dp[i2][j2 + 1]);
|
|
15079
|
-
const out =
|
|
15093
|
+
const out = [];
|
|
15080
15094
|
let i = 0, j = 0;
|
|
15081
15095
|
while (i < n && j < m) {
|
|
15082
15096
|
if (a[i] === b[j]) {
|
|
15083
|
-
out.push(
|
|
15097
|
+
out.push(` ${a[i]}`);
|
|
15084
15098
|
i++;
|
|
15085
15099
|
j++;
|
|
15086
15100
|
} else if (dp[i + 1][j] >= dp[i][j + 1]) {
|
|
15087
|
-
out.push(
|
|
15101
|
+
out.push(`- ${a[i]}`);
|
|
15088
15102
|
i++;
|
|
15089
15103
|
} else {
|
|
15090
|
-
out.push(
|
|
15104
|
+
out.push(`+ ${b[j]}`);
|
|
15091
15105
|
j++;
|
|
15092
15106
|
}
|
|
15093
15107
|
}
|
|
15094
15108
|
while (i < n)
|
|
15095
|
-
out.push(
|
|
15109
|
+
out.push(`- ${a[i++]}`);
|
|
15096
15110
|
while (j < m)
|
|
15097
|
-
out.push(
|
|
15111
|
+
out.push(`+ ${b[j++]}`);
|
|
15098
15112
|
return out.join(`
|
|
15099
15113
|
`);
|
|
15100
15114
|
}
|
|
15115
|
+
var KEYWORDS = /\b(const|let|var|function|class|interface|type|import|export|return|if|else|for|while|async|await|def|fn|struct|enum|public|private|func|package|use|impl|match|new)\b/g;
|
|
15116
|
+
var STRINGS = /("[^"]*"|'[^']*'|`[^`]*`)/g;
|
|
15117
|
+
var COMMENTS = /(\/\/[^\n]*|#[^\n]*)/g;
|
|
15118
|
+
function highlight(code) {
|
|
15119
|
+
return code.replace(COMMENTS, (m) => `${A.gray}${m}${A.reset}`).replace(STRINGS, (m) => `${A.yellow}${m}${A.reset}`).replace(KEYWORDS, (m) => `${A.mag}${m}${A.reset}`).replace(/\b(\d+(?:\.\d+)?)\b/g, (m) => `${A.cyan}${m}${A.reset}`);
|
|
15120
|
+
}
|
|
15101
15121
|
|
|
15102
15122
|
// src/memory/store.ts
|
|
15103
15123
|
import { join as join7 } from "path";
|
|
@@ -24335,4 +24355,4 @@ async function decryptWallet(enc, password, network = MAINNET) {
|
|
|
24335
24355
|
}
|
|
24336
24356
|
return w;
|
|
24337
24357
|
}
|
|
24338
|
-
export { exports_external, register, getTool, toolSchemas,
|
|
24358
|
+
export { exports_external, register, getTool, toolSchemas, renderDiffPlain, highlight, shellInvocation, shellName, globMatch, runProc, which, spawnProc, globalDir, loadConfig, saveConfig, rememberFact, allFacts, searchFacts, deleteFact, recordTurn, searchSessions, parseFrontmatter, loadSubagents, chat2 as chat, route, loadProjectContext, addEntity, addRelation, query, indexFile, indexCodebaseIncremental, indexCodebase, loadSkills, buildTurnContext, preToolUse, postToolUse, userPromptSubmit, fireEvent, runSubagent, runTeam, isImagePath, imageToDataUrl, estimateTokens, contextStatus, compactionThreshold, findCompactionCut, newTurn, snapshot, undo, depth, MUTATING_TOOLS, isProtectedBuiltin, recordUse, recordPatch, recordCreate, getUsage, allUsage, activityCount, analyzeImpact, impactSummary, lspFor, closeAllLsp, COLUMNS, addCard, moveCard, removeCard, renderBoard, addJob, listJobs, removeJob, startScheduler, MAINNET, generateMnemonic2 as generateMnemonic, validateMnemonic2 as validateMnemonic, walletFromMnemonic, importWalletHex, encryptWallet, decryptWallet, createTransfer };
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
getUsage,
|
|
24
24
|
globMatch,
|
|
25
25
|
globalDir,
|
|
26
|
+
highlight,
|
|
26
27
|
imageToDataUrl,
|
|
27
28
|
impactSummary,
|
|
28
29
|
indexCodebase,
|
|
@@ -51,7 +52,7 @@ import {
|
|
|
51
52
|
removeCard,
|
|
52
53
|
removeJob,
|
|
53
54
|
renderBoard,
|
|
54
|
-
|
|
55
|
+
renderDiffPlain,
|
|
55
56
|
route,
|
|
56
57
|
runProc,
|
|
57
58
|
runSubagent,
|
|
@@ -68,7 +69,7 @@ import {
|
|
|
68
69
|
undo,
|
|
69
70
|
userPromptSubmit,
|
|
70
71
|
which
|
|
71
|
-
} from "./main-
|
|
72
|
+
} from "./main-pj08pbgh.mjs";
|
|
72
73
|
import"./main-p2xnn95s.mjs";
|
|
73
74
|
import {
|
|
74
75
|
__commonJS,
|
|
@@ -21593,7 +21594,7 @@ function formatMarkdown(src) {
|
|
|
21593
21594
|
continue;
|
|
21594
21595
|
}
|
|
21595
21596
|
if (inFence) {
|
|
21596
|
-
out.push(
|
|
21597
|
+
out.push(` ${highlight(line)}`);
|
|
21597
21598
|
continue;
|
|
21598
21599
|
}
|
|
21599
21600
|
const h = line.match(/^(#{1,6})\s+(.*)$/);
|
|
@@ -21692,6 +21693,11 @@ function Chip({ label, color }) {
|
|
|
21692
21693
|
]
|
|
21693
21694
|
}, undefined, true, undefined, this);
|
|
21694
21695
|
}
|
|
21696
|
+
function truncPath(p, max2 = 60) {
|
|
21697
|
+
if (p.length <= max2)
|
|
21698
|
+
return p;
|
|
21699
|
+
return "…" + p.slice(p.length - max2 + 1);
|
|
21700
|
+
}
|
|
21695
21701
|
function C_KEY(color, k) {
|
|
21696
21702
|
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21697
21703
|
color,
|
|
@@ -21703,7 +21709,7 @@ function C_KEY(color, k) {
|
|
|
21703
21709
|
]
|
|
21704
21710
|
}, undefined, true, undefined, this);
|
|
21705
21711
|
}
|
|
21706
|
-
function App2({ theme, model, mdlUsd, lines, streaming, sessionMdl, contextPct, walletMdl, mode, busy, toolStream, turnStart, turnTokens, confirmReq, history, onConfirm, onSubmit, onInterrupt, onExit }) {
|
|
21712
|
+
function App2({ theme, model, mdlUsd, lines, streaming, sessionMdl, contextPct, walletMdl, mode, busy, toolStream, turnStart, turnTokens, confirmReq, launchReq, history, onConfirm, onLaunchDecision, onSubmit, onInterrupt, onExit }) {
|
|
21707
21713
|
const t = getTheme(theme);
|
|
21708
21714
|
const [input, setInput] = import_react34.useState("");
|
|
21709
21715
|
const [histIdx, setHistIdx] = import_react34.useState(-1);
|
|
@@ -21737,6 +21743,16 @@ function App2({ theme, model, mdlUsd, lines, streaming, sessionMdl, contextPct,
|
|
|
21737
21743
|
armExit();
|
|
21738
21744
|
return;
|
|
21739
21745
|
}
|
|
21746
|
+
if (launchReq) {
|
|
21747
|
+
const k = ch.toLowerCase();
|
|
21748
|
+
if (k === "y")
|
|
21749
|
+
onLaunchDecision("yes");
|
|
21750
|
+
else if (k === "a")
|
|
21751
|
+
onLaunchDecision("always");
|
|
21752
|
+
else if (k === "n" || key.return || key.escape)
|
|
21753
|
+
onLaunchDecision("no");
|
|
21754
|
+
return;
|
|
21755
|
+
}
|
|
21740
21756
|
if (confirmReq) {
|
|
21741
21757
|
const k = ch.toLowerCase();
|
|
21742
21758
|
if (k === "y")
|
|
@@ -21859,6 +21875,55 @@ function App2({ theme, model, mdlUsd, lines, streaming, sessionMdl, contextPct,
|
|
|
21859
21875
|
}, undefined, false, undefined, this)
|
|
21860
21876
|
]
|
|
21861
21877
|
}, i, true, undefined, this);
|
|
21878
|
+
if (l.kind === "diff") {
|
|
21879
|
+
const rows = l.text.split(`
|
|
21880
|
+
`);
|
|
21881
|
+
const header = rows[0] && !/^[+\- ] /.test(rows[0]) ? rows.shift() : null;
|
|
21882
|
+
let ln = 0;
|
|
21883
|
+
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
21884
|
+
flexDirection: "column",
|
|
21885
|
+
children: [
|
|
21886
|
+
header ? /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21887
|
+
children: [
|
|
21888
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21889
|
+
color: t.tool,
|
|
21890
|
+
children: "⏺ "
|
|
21891
|
+
}, undefined, false, undefined, this),
|
|
21892
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21893
|
+
children: header
|
|
21894
|
+
}, undefined, false, undefined, this)
|
|
21895
|
+
]
|
|
21896
|
+
}, undefined, true, undefined, this) : null,
|
|
21897
|
+
rows.slice(0, 60).map((row, k) => {
|
|
21898
|
+
const add2 = row.startsWith("+ "), del = row.startsWith("- ");
|
|
21899
|
+
if (!del)
|
|
21900
|
+
ln++;
|
|
21901
|
+
const num = String(ln).padStart(4);
|
|
21902
|
+
const body = row.slice(2);
|
|
21903
|
+
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21904
|
+
children: [
|
|
21905
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21906
|
+
color: t.dim,
|
|
21907
|
+
children: ` ${add2 ? " " : num} `
|
|
21908
|
+
}, undefined, false, undefined, this),
|
|
21909
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21910
|
+
backgroundColor: add2 ? "#10301a" : del ? "#3a1418" : undefined,
|
|
21911
|
+
color: add2 ? t.ok : del ? t.warn : t.dim,
|
|
21912
|
+
children: [
|
|
21913
|
+
add2 ? "+ " : del ? "- " : " ",
|
|
21914
|
+
body
|
|
21915
|
+
]
|
|
21916
|
+
}, undefined, true, undefined, this)
|
|
21917
|
+
]
|
|
21918
|
+
}, k, true, undefined, this);
|
|
21919
|
+
}),
|
|
21920
|
+
rows.length > 60 ? /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21921
|
+
color: t.dim,
|
|
21922
|
+
children: ` … +${rows.length - 60} more diff lines`
|
|
21923
|
+
}, undefined, false, undefined, this) : null
|
|
21924
|
+
]
|
|
21925
|
+
}, i, true, undefined, this);
|
|
21926
|
+
}
|
|
21862
21927
|
if (l.kind === "error")
|
|
21863
21928
|
return /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
21864
21929
|
flexDirection: "column",
|
|
@@ -21888,21 +21953,36 @@ function App2({ theme, model, mdlUsd, lines, streaming, sessionMdl, contextPct,
|
|
|
21888
21953
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
21889
21954
|
borderStyle: "round",
|
|
21890
21955
|
borderColor: t.accent,
|
|
21891
|
-
paddingX:
|
|
21892
|
-
|
|
21956
|
+
paddingX: 2,
|
|
21957
|
+
paddingY: 1,
|
|
21958
|
+
flexDirection: "column",
|
|
21959
|
+
alignItems: "center",
|
|
21893
21960
|
children: [
|
|
21894
21961
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21895
21962
|
bold: true,
|
|
21896
21963
|
color: t.accent,
|
|
21897
|
-
children: "
|
|
21964
|
+
children: "✻ modelcode"
|
|
21898
21965
|
}, undefined, false, undefined, this),
|
|
21899
21966
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21900
21967
|
color: t.dim,
|
|
21968
|
+
children: "the AI coding agent on modelOS"
|
|
21969
|
+
}, undefined, false, undefined, this),
|
|
21970
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21901
21971
|
children: [
|
|
21902
|
-
|
|
21903
|
-
|
|
21972
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21973
|
+
color: t.user,
|
|
21974
|
+
children: model
|
|
21975
|
+
}, undefined, false, undefined, this),
|
|
21976
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21977
|
+
color: t.dim,
|
|
21978
|
+
children: " · pay-per-use in MDL · no rate limits"
|
|
21979
|
+
}, undefined, false, undefined, this)
|
|
21904
21980
|
]
|
|
21905
|
-
}, undefined, true, undefined, this)
|
|
21981
|
+
}, undefined, true, undefined, this),
|
|
21982
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21983
|
+
color: t.dim,
|
|
21984
|
+
children: truncPath(process.cwd())
|
|
21985
|
+
}, undefined, false, undefined, this)
|
|
21906
21986
|
]
|
|
21907
21987
|
}, undefined, true, undefined, this),
|
|
21908
21988
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Static, {
|
|
@@ -21962,6 +22042,34 @@ function App2({ theme, model, mdlUsd, lines, streaming, sessionMdl, contextPct,
|
|
|
21962
22042
|
]
|
|
21963
22043
|
}, c2.cmd, true, undefined, this))
|
|
21964
22044
|
}, undefined, false, undefined, this) : null,
|
|
22045
|
+
launchReq ? /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
22046
|
+
flexDirection: "column",
|
|
22047
|
+
borderStyle: "round",
|
|
22048
|
+
borderColor: t.warn,
|
|
22049
|
+
paddingX: 1,
|
|
22050
|
+
marginTop: 1,
|
|
22051
|
+
children: [
|
|
22052
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
22053
|
+
color: t.warn,
|
|
22054
|
+
bold: true,
|
|
22055
|
+
children: "launch failed — fix it?"
|
|
22056
|
+
}, undefined, false, undefined, this),
|
|
22057
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
22058
|
+
color: t.dim,
|
|
22059
|
+
children: launchReq.cmd
|
|
22060
|
+
}, undefined, false, undefined, this),
|
|
22061
|
+
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
22062
|
+
children: [
|
|
22063
|
+
C_KEY(t.user, "y"),
|
|
22064
|
+
" accept ",
|
|
22065
|
+
C_KEY(t.user, "n"),
|
|
22066
|
+
" decline ",
|
|
22067
|
+
C_KEY(t.accent, "a"),
|
|
22068
|
+
" at all times (every project)"
|
|
22069
|
+
]
|
|
22070
|
+
}, undefined, true, undefined, this)
|
|
22071
|
+
]
|
|
22072
|
+
}, undefined, true, undefined, this) : null,
|
|
21965
22073
|
confirmReq ? /* @__PURE__ */ jsx_dev_runtime.jsxDEV(Box_default, {
|
|
21966
22074
|
flexDirection: "column",
|
|
21967
22075
|
borderStyle: "round",
|
|
@@ -21985,13 +22093,13 @@ function App2({ theme, model, mdlUsd, lines, streaming, sessionMdl, contextPct,
|
|
|
21985
22093
|
/* @__PURE__ */ jsx_dev_runtime.jsxDEV(Text, {
|
|
21986
22094
|
children: [
|
|
21987
22095
|
C_KEY(t.user, "y"),
|
|
21988
|
-
"
|
|
22096
|
+
" accept ",
|
|
21989
22097
|
C_KEY(t.user, "a"),
|
|
21990
22098
|
" always (project) ",
|
|
21991
22099
|
C_KEY(t.user, "g"),
|
|
21992
|
-
" global ",
|
|
22100
|
+
" always (global) ",
|
|
21993
22101
|
C_KEY(t.warn, "n"),
|
|
21994
|
-
"
|
|
22102
|
+
" decline"
|
|
21995
22103
|
]
|
|
21996
22104
|
}, undefined, true, undefined, this)
|
|
21997
22105
|
]
|
|
@@ -22086,6 +22194,15 @@ Tool discipline (important): call each tool ONCE for a given action — never re
|
|
|
22086
22194
|
A tool result is authoritative: once a write/edit succeeds, trust it and move on. Do NOT claim a task is
|
|
22087
22195
|
done in the same message where you call the tool to do it — call the tool first, then confirm only after
|
|
22088
22196
|
you see its result. Keep confirmations to one short sentence.`;
|
|
22197
|
+
var LAUNCH_RE = /\b(npm|pnpm|yarn|bun)\s+(run\s+)?(dev|start|serve)\b|\b(vite|next|nuxt|astro|remix|webpack[\- ]dev[\- ]server|nodemon|tsx\s+watch|ts-node-dev)\b|\b(uvicorn|gunicorn|flask\s+run|manage\.py\s+runserver|rails\s+s(erver)?|go\s+run|cargo\s+run|dotnet\s+run|php\s+artisan\s+serve|air|nest\s+start)\b/i;
|
|
22198
|
+
function isLaunchCommand(cmd) {
|
|
22199
|
+
return LAUNCH_RE.test(cmd);
|
|
22200
|
+
}
|
|
22201
|
+
function looksLikeLaunchError(result2) {
|
|
22202
|
+
if (!/^exit\s+([1-9]\d*|124)\b/m.test(result2))
|
|
22203
|
+
return false;
|
|
22204
|
+
return /\b(error|exception|cannot find module|module not found|eaddrinuse|syntaxerror|typeerror|referenceerror|failed to compile|command not found|traceback|panic:|address already in use|unexpected token|ENOENT|MODULE_NOT_FOUND)\b/i.test(result2);
|
|
22205
|
+
}
|
|
22089
22206
|
|
|
22090
22207
|
class Agent {
|
|
22091
22208
|
cfg;
|
|
@@ -22208,6 +22325,7 @@ ${summary}` : "Earlier conversation was auto-compacted to free context (summary
|
|
|
22208
22325
|
recordTurn(this.ctx.cwd, "user", userText);
|
|
22209
22326
|
newTurn();
|
|
22210
22327
|
this._doneCalls.clear();
|
|
22328
|
+
this._launchHandled.clear();
|
|
22211
22329
|
const ac = this.abortController = new AbortController;
|
|
22212
22330
|
const signal = ac.signal;
|
|
22213
22331
|
let turnGrains = 0;
|
|
@@ -22257,6 +22375,26 @@ ${tail2}`;
|
|
|
22257
22375
|
}
|
|
22258
22376
|
this.history.push({ role: "tool", tool_call_id: call.id, name: call.function.name, content });
|
|
22259
22377
|
toolCount++;
|
|
22378
|
+
if (call.function.name === "bash" && !this._launchHandled.has(call.id)) {
|
|
22379
|
+
let bashCmd = "";
|
|
22380
|
+
try {
|
|
22381
|
+
bashCmd = String(JSON.parse(call.function.arguments || "{}").command ?? "");
|
|
22382
|
+
} catch {}
|
|
22383
|
+
if (isLaunchCommand(bashCmd) && looksLikeLaunchError(content)) {
|
|
22384
|
+
this._launchHandled.add(call.id);
|
|
22385
|
+
let decision = this.cfg.autoFixLaunchErrors ? "always" : "no";
|
|
22386
|
+
if (!this.cfg.autoFixLaunchErrors && this.h.onLaunchError) {
|
|
22387
|
+
decision = await this.h.onLaunchError(bashCmd);
|
|
22388
|
+
}
|
|
22389
|
+
if (decision === "always") {
|
|
22390
|
+
this.cfg.autoFixLaunchErrors = true;
|
|
22391
|
+
saveConfig({ autoFixLaunchErrors: true });
|
|
22392
|
+
}
|
|
22393
|
+
if (decision === "yes" || decision === "always") {
|
|
22394
|
+
this.history.push({ role: "system", content: `The launch command \`${bashCmd}\` failed to start. Diagnose the root cause from the error output above, fix it (install missing deps, fix config/code, free the port, etc.), then re-run the launch to verify it starts cleanly.` });
|
|
22395
|
+
}
|
|
22396
|
+
}
|
|
22397
|
+
}
|
|
22260
22398
|
}
|
|
22261
22399
|
}
|
|
22262
22400
|
} catch (e) {
|
|
@@ -22284,6 +22422,7 @@ ${tail2}`;
|
|
|
22284
22422
|
}
|
|
22285
22423
|
}
|
|
22286
22424
|
_suggested = false;
|
|
22425
|
+
_launchHandled = new Set;
|
|
22287
22426
|
_doneCalls = new Map;
|
|
22288
22427
|
async execToolCall(call) {
|
|
22289
22428
|
const tool = getTool(call.function.name);
|
|
@@ -22309,11 +22448,11 @@ ${tail2}`;
|
|
|
22309
22448
|
if (!gate.allowed) {
|
|
22310
22449
|
result2 = `blocked by hook: ${gate.reason}`;
|
|
22311
22450
|
} else if (needsConfirm && !await this.h.confirm(tool.name, args)) {
|
|
22312
|
-
result2 = "
|
|
22451
|
+
result2 = "declined by user";
|
|
22313
22452
|
} else {
|
|
22314
22453
|
if (MUTATING_TOOLS.has(tool.name) && typeof args.path === "string")
|
|
22315
22454
|
snapshot(this.ctx.cwd, args.path);
|
|
22316
|
-
const runCtx = { ...this.ctx, onStream: (c2) => this.h.onToolStream?.(c2), onCost: (g) => this.h.onCost(g) };
|
|
22455
|
+
const runCtx = { ...this.ctx, onStream: (c2) => this.h.onToolStream?.(c2), onCost: (g) => this.h.onCost(g), signal: this.abortController?.signal };
|
|
22317
22456
|
try {
|
|
22318
22457
|
result2 = await tool.run(args, runCtx);
|
|
22319
22458
|
} catch (e) {
|
|
@@ -22332,7 +22471,7 @@ ${tail2}`;
|
|
|
22332
22471
|
}
|
|
22333
22472
|
}
|
|
22334
22473
|
this.h.onToolResult(tool.name, result2);
|
|
22335
|
-
if (!result2.startsWith("error") && !result2.startsWith("
|
|
22474
|
+
if (!result2.startsWith("error") && !result2.startsWith("declined") && !result2.startsWith("blocked"))
|
|
22336
22475
|
this._doneCalls.set(sig, result2);
|
|
22337
22476
|
return { content: result2, edited };
|
|
22338
22477
|
}
|
|
@@ -23039,7 +23178,7 @@ var bash = {
|
|
|
23039
23178
|
async run(args, ctx) {
|
|
23040
23179
|
const cmd = String(args.command ?? "");
|
|
23041
23180
|
const timeout = Number(args.timeout_ms) || 120000;
|
|
23042
|
-
const r = await runProc(shellInvocation(cmd), { cwd: ctx.cwd, timeoutMs: timeout, onChunk: (s) => ctx.onStream?.(s) });
|
|
23181
|
+
const r = await runProc(shellInvocation(cmd), { cwd: ctx.cwd, timeoutMs: timeout, signal: ctx.signal, onChunk: (s) => ctx.onStream?.(s) });
|
|
23043
23182
|
return `exit ${r.code}
|
|
23044
23183
|
${(r.stdout + (r.stderr ? `
|
|
23045
23184
|
[stderr]
|
|
@@ -23110,7 +23249,7 @@ var edit = {
|
|
|
23110
23249
|
const updated = args.replace_all ? src.split(oldS).join(newS) : src.replace(oldS, newS);
|
|
23111
23250
|
writeFileSync6(p, updated);
|
|
23112
23251
|
return `edited ${args.path} (${count} replacement${count === 1 ? "" : "s"})
|
|
23113
|
-
${
|
|
23252
|
+
${renderDiffPlain(oldS, newS)}`;
|
|
23114
23253
|
}
|
|
23115
23254
|
};
|
|
23116
23255
|
var glob = {
|
|
@@ -23875,6 +24014,7 @@ function Root({ cfg, resume, mcpCount }) {
|
|
|
23875
24014
|
const [walletMdl, setWalletMdl] = import_react35.useState(null);
|
|
23876
24015
|
const [mode, setMode] = import_react35.useState("default");
|
|
23877
24016
|
const [confirmReq, setConfirmReq] = import_react35.useState(null);
|
|
24017
|
+
const [launchReq, setLaunchReq] = import_react35.useState(null);
|
|
23878
24018
|
const [history, setHistory] = import_react35.useState([]);
|
|
23879
24019
|
const [toolStream, setToolStream] = import_react35.useState("");
|
|
23880
24020
|
const [turnStart, setTurnStart] = import_react35.useState(0);
|
|
@@ -23887,6 +24027,7 @@ function Root({ cfg, resume, mcpCount }) {
|
|
|
23887
24027
|
const busyRef = import_react35.useRef(false);
|
|
23888
24028
|
const queueRef = import_react35.useRef([]);
|
|
23889
24029
|
const confirmResolve = import_react35.useRef(null);
|
|
24030
|
+
const launchResolve = import_react35.useRef(null);
|
|
23890
24031
|
const sessionIdRef = import_react35.useRef(newSessionId());
|
|
23891
24032
|
const sessionMdlRef = import_react35.useRef(0);
|
|
23892
24033
|
const mcpCountRef = import_react35.useRef(mcpCount);
|
|
@@ -23915,6 +24056,17 @@ function Root({ cfg, resume, mcpCount }) {
|
|
|
23915
24056
|
confirmResolve.current?.(decision !== "no");
|
|
23916
24057
|
confirmResolve.current = null;
|
|
23917
24058
|
}, [confirmReq, cwd2]);
|
|
24059
|
+
const onLaunchError = import_react35.useCallback((cmd) => {
|
|
24060
|
+
setLaunchReq({ cmd });
|
|
24061
|
+
return new Promise((resolve3) => {
|
|
24062
|
+
launchResolve.current = resolve3;
|
|
24063
|
+
});
|
|
24064
|
+
}, []);
|
|
24065
|
+
const onLaunchDecision = import_react35.useCallback((d) => {
|
|
24066
|
+
setLaunchReq(null);
|
|
24067
|
+
launchResolve.current?.(d);
|
|
24068
|
+
launchResolve.current = null;
|
|
24069
|
+
}, []);
|
|
23918
24070
|
const agent = import_react35.useRef(new Agent(cfg, { cwd: cwd2 }, {
|
|
23919
24071
|
onAssistantDelta: (txt) => {
|
|
23920
24072
|
streamBuf.current += txt;
|
|
@@ -23926,10 +24078,13 @@ function Root({ cfg, resume, mcpCount }) {
|
|
|
23926
24078
|
setToolStream("");
|
|
23927
24079
|
add2({ kind: "tool", text: toolUseLabel(name, args) });
|
|
23928
24080
|
},
|
|
23929
|
-
onToolResult: (
|
|
24081
|
+
onToolResult: (name, r) => {
|
|
23930
24082
|
toolStreamRef.current = "";
|
|
23931
24083
|
setToolStream("");
|
|
23932
|
-
|
|
24084
|
+
if ((name === "edit" || name === "write") && /^[+\-] /m.test(r))
|
|
24085
|
+
add2({ kind: "diff", text: r });
|
|
24086
|
+
else
|
|
24087
|
+
add2({ kind: "result", text: capLines(r, 8) });
|
|
23933
24088
|
},
|
|
23934
24089
|
onToolStream: (chunk2) => {
|
|
23935
24090
|
toolStreamRef.current = (toolStreamRef.current + chunk2).slice(-2000);
|
|
@@ -23940,6 +24095,7 @@ function Root({ cfg, resume, mcpCount }) {
|
|
|
23940
24095
|
return v;
|
|
23941
24096
|
}),
|
|
23942
24097
|
confirm,
|
|
24098
|
+
onLaunchError,
|
|
23943
24099
|
onSuggestSkill: (n) => add2({ kind: "system", text: `\uD83D\uDCA1 that took ${n} steps — /skill-create to save it` }),
|
|
23944
24100
|
onCompact: (cs) => add2({ kind: "system", text: `\uD83D\uDDDC context auto-compacted (now ~${cs.pct}% full)` })
|
|
23945
24101
|
})).current;
|
|
@@ -24112,8 +24268,10 @@ function Root({ cfg, resume, mcpCount }) {
|
|
|
24112
24268
|
turnStart,
|
|
24113
24269
|
turnTokens,
|
|
24114
24270
|
confirmReq,
|
|
24271
|
+
launchReq,
|
|
24115
24272
|
history,
|
|
24116
24273
|
onConfirm,
|
|
24274
|
+
onLaunchDecision,
|
|
24117
24275
|
onSubmit,
|
|
24118
24276
|
onInterrupt,
|
|
24119
24277
|
onExit
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modeloslab/modelcode",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "modelOS-native AI coding agent CLI — remembers like Hermes, codes like Claude Code, runs on modelOS (pay-per-use in MDL, no rate limits). Knowledge-graph memory, subagents, MCP, vision, spend caps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|