@aiaiai-pt/martha-cli 0.26.0 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +473 -74
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11081,7 +11081,7 @@ init_config();
|
|
|
11081
11081
|
init_errors();
|
|
11082
11082
|
|
|
11083
11083
|
// src/version.ts
|
|
11084
|
-
var CLI_VERSION = "0.
|
|
11084
|
+
var CLI_VERSION = "0.27.0";
|
|
11085
11085
|
|
|
11086
11086
|
// src/commands/sessions.ts
|
|
11087
11087
|
function relativeTime(iso) {
|
|
@@ -11358,6 +11358,9 @@ import { spawn } from "node:child_process";
|
|
|
11358
11358
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
11359
11359
|
import { closeSync, openSync, promises as fs5 } from "node:fs";
|
|
11360
11360
|
import * as path4 from "node:path";
|
|
11361
|
+
function sleep(ms) {
|
|
11362
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
11363
|
+
}
|
|
11361
11364
|
var DEFAULT_TIMEOUT_MS = 20000;
|
|
11362
11365
|
var MAX_OUTPUT = 256 * 1024;
|
|
11363
11366
|
function runnerDir(cwd) {
|
|
@@ -11452,16 +11455,87 @@ class Journal {
|
|
|
11452
11455
|
try {
|
|
11453
11456
|
const raw = JSON.parse(await fs5.readFile(this.file(), "utf8"));
|
|
11454
11457
|
if (raw["entries"] !== undefined) {
|
|
11455
|
-
|
|
11458
|
+
const f = raw;
|
|
11459
|
+
if (!f.secrets)
|
|
11460
|
+
f.secrets = {};
|
|
11461
|
+
if (!f.jobs)
|
|
11462
|
+
f.jobs = {};
|
|
11463
|
+
return f;
|
|
11456
11464
|
}
|
|
11457
|
-
return {
|
|
11465
|
+
return {
|
|
11466
|
+
entries: raw,
|
|
11467
|
+
secrets: {},
|
|
11468
|
+
jobs: {}
|
|
11469
|
+
};
|
|
11458
11470
|
} catch {
|
|
11459
|
-
return { entries: {}, secrets: {} };
|
|
11471
|
+
return { entries: {}, secrets: {}, jobs: {} };
|
|
11460
11472
|
}
|
|
11461
11473
|
}
|
|
11462
11474
|
async save(data) {
|
|
11463
11475
|
await fs5.mkdir(runnerDir(this.cwd), { recursive: true });
|
|
11464
|
-
|
|
11476
|
+
const tmp = `${this.file()}.${randomBytes2(6).toString("hex")}.tmp`;
|
|
11477
|
+
await fs5.writeFile(tmp, JSON.stringify(data, null, 2));
|
|
11478
|
+
await fs5.rename(tmp, this.file());
|
|
11479
|
+
}
|
|
11480
|
+
chain = Promise.resolve();
|
|
11481
|
+
async acquireFileLock() {
|
|
11482
|
+
const lockPath = path4.join(runnerDir(this.cwd), "journal.lock");
|
|
11483
|
+
await fs5.mkdir(runnerDir(this.cwd), { recursive: true });
|
|
11484
|
+
const deadline = Date.now() + 5000;
|
|
11485
|
+
for (;; ) {
|
|
11486
|
+
try {
|
|
11487
|
+
const fh = await fs5.open(lockPath, "wx");
|
|
11488
|
+
await fh.write(String(process.pid));
|
|
11489
|
+
await fh.close();
|
|
11490
|
+
return async () => {
|
|
11491
|
+
await fs5.rm(lockPath, { force: true });
|
|
11492
|
+
};
|
|
11493
|
+
} catch (e) {
|
|
11494
|
+
if (e.code !== "EEXIST")
|
|
11495
|
+
throw e;
|
|
11496
|
+
try {
|
|
11497
|
+
const st = await fs5.stat(lockPath);
|
|
11498
|
+
if (Date.now() - st.mtimeMs > 1e4) {
|
|
11499
|
+
await fs5.rm(lockPath, { force: true });
|
|
11500
|
+
continue;
|
|
11501
|
+
}
|
|
11502
|
+
} catch {
|
|
11503
|
+
continue;
|
|
11504
|
+
}
|
|
11505
|
+
if (Date.now() > deadline) {
|
|
11506
|
+
return async () => {};
|
|
11507
|
+
}
|
|
11508
|
+
await sleep(15 + Math.floor(Math.random() * 25));
|
|
11509
|
+
}
|
|
11510
|
+
}
|
|
11511
|
+
}
|
|
11512
|
+
async transact(fn) {
|
|
11513
|
+
const run = async () => {
|
|
11514
|
+
const release = await this.acquireFileLock();
|
|
11515
|
+
try {
|
|
11516
|
+
const data = await this.load();
|
|
11517
|
+
const out = await fn(data);
|
|
11518
|
+
await this.save(data);
|
|
11519
|
+
return out;
|
|
11520
|
+
} finally {
|
|
11521
|
+
await release();
|
|
11522
|
+
}
|
|
11523
|
+
};
|
|
11524
|
+
const result = this.chain.then(run, run);
|
|
11525
|
+
this.chain = result.then(() => {
|
|
11526
|
+
return;
|
|
11527
|
+
}, () => {
|
|
11528
|
+
return;
|
|
11529
|
+
});
|
|
11530
|
+
return result;
|
|
11531
|
+
}
|
|
11532
|
+
async recordJob(job) {
|
|
11533
|
+
await this.transact((data) => {
|
|
11534
|
+
data.jobs[job.job_id] = job;
|
|
11535
|
+
});
|
|
11536
|
+
}
|
|
11537
|
+
async lookupJob(jobId) {
|
|
11538
|
+
return this.transact((data) => data.jobs[jobId]);
|
|
11465
11539
|
}
|
|
11466
11540
|
static key(sessionId, toolCallId) {
|
|
11467
11541
|
return `${sessionId}:${toolCallId}`;
|
|
@@ -11521,7 +11595,12 @@ function parseHostExecRequests(data) {
|
|
|
11521
11595
|
continue;
|
|
11522
11596
|
if (!t.tool_call_id)
|
|
11523
11597
|
continue;
|
|
11524
|
-
out.push({
|
|
11598
|
+
out.push({
|
|
11599
|
+
toolCallId: t.tool_call_id,
|
|
11600
|
+
command: t.args?.command ?? "",
|
|
11601
|
+
background: t.args?.background === true,
|
|
11602
|
+
cwd: typeof t.args?.cwd === "string" ? t.args.cwd : undefined
|
|
11603
|
+
});
|
|
11525
11604
|
}
|
|
11526
11605
|
return out;
|
|
11527
11606
|
}
|
|
@@ -11917,12 +11996,247 @@ function parseLocalAgentRequests(data) {
|
|
|
11917
11996
|
out.push({
|
|
11918
11997
|
toolCallId: t.tool_call_id,
|
|
11919
11998
|
task: t.args.task,
|
|
11920
|
-
cwd: typeof t.args.cwd === "string" ? t.args.cwd : undefined
|
|
11999
|
+
cwd: typeof t.args.cwd === "string" ? t.args.cwd : undefined,
|
|
12000
|
+
background: t.args.background === true
|
|
11921
12001
|
});
|
|
11922
12002
|
}
|
|
11923
12003
|
return out;
|
|
11924
12004
|
}
|
|
11925
12005
|
|
|
12006
|
+
// src/lib/local-jobs.ts
|
|
12007
|
+
import { execFileSync, spawn as spawn2 } from "node:child_process";
|
|
12008
|
+
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
12009
|
+
import { closeSync as closeSync2, openSync as openSync2, readFileSync, promises as fs7 } from "node:fs";
|
|
12010
|
+
import * as path6 from "node:path";
|
|
12011
|
+
var POLL_CAP = 64 * 1024;
|
|
12012
|
+
function mintJobId() {
|
|
12013
|
+
return `job_${randomBytes3(6).toString("hex")}`;
|
|
12014
|
+
}
|
|
12015
|
+
var SENTINEL_TAIL = `
|
|
12016
|
+
__martha_ec=$?
|
|
12017
|
+
printf '%s' "$__martha_ec" > "$MARTHA_JOB_SENTINEL.tmp" 2>/dev/null
|
|
12018
|
+
mv -f "$MARTHA_JOB_SENTINEL.tmp" "$MARTHA_JOB_SENTINEL" 2>/dev/null
|
|
12019
|
+
exit $__martha_ec`;
|
|
12020
|
+
function runnerDir2(cwd) {
|
|
12021
|
+
return path6.join(cwd, ".martha-runner");
|
|
12022
|
+
}
|
|
12023
|
+
function getProcessStartTime(pid) {
|
|
12024
|
+
try {
|
|
12025
|
+
if (process.platform === "linux") {
|
|
12026
|
+
const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
12027
|
+
const tail = stat.slice(stat.lastIndexOf(")") + 1).trim().split(/\s+/);
|
|
12028
|
+
return tail[19] ?? null;
|
|
12029
|
+
}
|
|
12030
|
+
const out = execFileSync("ps", ["-o", "lstart=", "-p", String(pid)], {
|
|
12031
|
+
encoding: "utf8"
|
|
12032
|
+
}).trim();
|
|
12033
|
+
return out || null;
|
|
12034
|
+
} catch {
|
|
12035
|
+
return null;
|
|
12036
|
+
}
|
|
12037
|
+
}
|
|
12038
|
+
function isProcessAlive(pid) {
|
|
12039
|
+
if (pid <= 0)
|
|
12040
|
+
return false;
|
|
12041
|
+
try {
|
|
12042
|
+
process.kill(pid, 0);
|
|
12043
|
+
return true;
|
|
12044
|
+
} catch (e) {
|
|
12045
|
+
return e.code === "EPERM";
|
|
12046
|
+
}
|
|
12047
|
+
}
|
|
12048
|
+
async function readSentinel(p) {
|
|
12049
|
+
try {
|
|
12050
|
+
const raw = (await fs7.readFile(p, "utf8")).trim();
|
|
12051
|
+
if (raw === "")
|
|
12052
|
+
return { present: true };
|
|
12053
|
+
const n = parseInt(raw, 10);
|
|
12054
|
+
return { present: true, code: Number.isNaN(n) ? undefined : n };
|
|
12055
|
+
} catch {
|
|
12056
|
+
return { present: false };
|
|
12057
|
+
}
|
|
12058
|
+
}
|
|
12059
|
+
async function readLogOrNull(p) {
|
|
12060
|
+
try {
|
|
12061
|
+
return await fs7.readFile(p);
|
|
12062
|
+
} catch {
|
|
12063
|
+
return null;
|
|
12064
|
+
}
|
|
12065
|
+
}
|
|
12066
|
+
async function writeSentinelAtomic(p, content) {
|
|
12067
|
+
const tmp = `${p}.${randomBytes3(4).toString("hex")}.tmp`;
|
|
12068
|
+
await fs7.writeFile(tmp, content);
|
|
12069
|
+
await fs7.rename(tmp, p);
|
|
12070
|
+
}
|
|
12071
|
+
async function dispatchBackgroundJob(command, jobId, journal, opts) {
|
|
12072
|
+
if (process.platform === "win32") {
|
|
12073
|
+
throw new Error("background jobs are not supported on Windows (POSIX-only: /bin/sh + " + "process groups). Run this command in the foreground instead.");
|
|
12074
|
+
}
|
|
12075
|
+
const dir = runnerDir2(opts.cwd);
|
|
12076
|
+
await fs7.mkdir(dir, { recursive: true });
|
|
12077
|
+
const logPath = path6.join(dir, `${jobId}.log`);
|
|
12078
|
+
const sentinelPath = path6.join(dir, `${jobId}.exit`);
|
|
12079
|
+
await fs7.writeFile(logPath, "");
|
|
12080
|
+
const fd = openSync2(logPath, "a");
|
|
12081
|
+
const env2 = { ...process.env, MARTHA_JOB_SENTINEL: sentinelPath };
|
|
12082
|
+
const stdin = opts.stdinData != null ? "pipe" : "ignore";
|
|
12083
|
+
const child = opts.argv ? spawn2("/bin/sh", ["-c", `"$@"${SENTINEL_TAIL}`, "martha-job", ...opts.argv], {
|
|
12084
|
+
shell: false,
|
|
12085
|
+
cwd: opts.cwd,
|
|
12086
|
+
detached: true,
|
|
12087
|
+
env: env2,
|
|
12088
|
+
stdio: [stdin, fd, fd]
|
|
12089
|
+
}) : spawn2("/bin/sh", ["-c", `( ${command} )${SENTINEL_TAIL}`], {
|
|
12090
|
+
shell: false,
|
|
12091
|
+
cwd: opts.cwd,
|
|
12092
|
+
detached: true,
|
|
12093
|
+
env: env2,
|
|
12094
|
+
stdio: [stdin, fd, fd]
|
|
12095
|
+
});
|
|
12096
|
+
closeSync2(fd);
|
|
12097
|
+
if (opts.stdinData != null && child.stdin) {
|
|
12098
|
+
child.stdin.on("error", () => {});
|
|
12099
|
+
child.stdin.write(opts.stdinData);
|
|
12100
|
+
child.stdin.end();
|
|
12101
|
+
}
|
|
12102
|
+
const pid = child.pid ?? -1;
|
|
12103
|
+
const start_time = pid > 0 ? getProcessStartTime(pid) : null;
|
|
12104
|
+
child.unref();
|
|
12105
|
+
const record = {
|
|
12106
|
+
job_id: jobId,
|
|
12107
|
+
command,
|
|
12108
|
+
pid,
|
|
12109
|
+
start_time,
|
|
12110
|
+
started_at: new Date().toISOString(),
|
|
12111
|
+
log_path: logPath,
|
|
12112
|
+
sentinel_path: sentinelPath,
|
|
12113
|
+
offset: 0,
|
|
12114
|
+
status: "running"
|
|
12115
|
+
};
|
|
12116
|
+
await journal.recordJob(record);
|
|
12117
|
+
return { job_id: jobId, status: "running", log_path: logPath, pid };
|
|
12118
|
+
}
|
|
12119
|
+
function startTimeMatches(job) {
|
|
12120
|
+
if (!job.start_time)
|
|
12121
|
+
return true;
|
|
12122
|
+
const live = getProcessStartTime(job.pid);
|
|
12123
|
+
if (live == null)
|
|
12124
|
+
return true;
|
|
12125
|
+
return live === job.start_time;
|
|
12126
|
+
}
|
|
12127
|
+
async function classifyAndRead(job) {
|
|
12128
|
+
const sentinel = await readSentinel(job.sentinel_path);
|
|
12129
|
+
const logBuf = await readLogOrNull(job.log_path);
|
|
12130
|
+
let status;
|
|
12131
|
+
let done;
|
|
12132
|
+
let exit_code;
|
|
12133
|
+
if (sentinel.present) {
|
|
12134
|
+
done = true;
|
|
12135
|
+
exit_code = sentinel.code;
|
|
12136
|
+
status = job.status === "killed" ? "killed" : "done";
|
|
12137
|
+
job.status = status;
|
|
12138
|
+
} else if (isProcessAlive(job.pid) && startTimeMatches(job)) {
|
|
12139
|
+
status = "running";
|
|
12140
|
+
done = false;
|
|
12141
|
+
} else if (logBuf == null) {
|
|
12142
|
+
status = "expired";
|
|
12143
|
+
done = true;
|
|
12144
|
+
} else {
|
|
12145
|
+
status = "crashed";
|
|
12146
|
+
done = true;
|
|
12147
|
+
job.status = "crashed";
|
|
12148
|
+
}
|
|
12149
|
+
let new_output = "";
|
|
12150
|
+
let bytes_remaining = 0;
|
|
12151
|
+
if (logBuf != null) {
|
|
12152
|
+
const slice = logBuf.subarray(job.offset, job.offset + POLL_CAP);
|
|
12153
|
+
new_output = slice.toString("utf8");
|
|
12154
|
+
job.offset += slice.length;
|
|
12155
|
+
bytes_remaining = Math.max(0, logBuf.length - job.offset);
|
|
12156
|
+
}
|
|
12157
|
+
return {
|
|
12158
|
+
job_id: job.job_id,
|
|
12159
|
+
status,
|
|
12160
|
+
exit_code,
|
|
12161
|
+
new_output,
|
|
12162
|
+
done,
|
|
12163
|
+
bytes_remaining
|
|
12164
|
+
};
|
|
12165
|
+
}
|
|
12166
|
+
async function pollJob(journal, jobId) {
|
|
12167
|
+
return journal.transact(async (data) => {
|
|
12168
|
+
const job = data.jobs[jobId];
|
|
12169
|
+
if (!job) {
|
|
12170
|
+
return {
|
|
12171
|
+
job_id: jobId,
|
|
12172
|
+
status: "unknown",
|
|
12173
|
+
new_output: "",
|
|
12174
|
+
done: true,
|
|
12175
|
+
bytes_remaining: 0,
|
|
12176
|
+
message: `No background job with id ${jobId} is known on this machine.`
|
|
12177
|
+
};
|
|
12178
|
+
}
|
|
12179
|
+
return classifyAndRead(job);
|
|
12180
|
+
});
|
|
12181
|
+
}
|
|
12182
|
+
async function killJob(journal, jobId) {
|
|
12183
|
+
return journal.transact(async (data) => {
|
|
12184
|
+
const job = data.jobs[jobId];
|
|
12185
|
+
if (!job) {
|
|
12186
|
+
return {
|
|
12187
|
+
job_id: jobId,
|
|
12188
|
+
status: "unknown",
|
|
12189
|
+
new_output: "",
|
|
12190
|
+
done: true,
|
|
12191
|
+
bytes_remaining: 0,
|
|
12192
|
+
message: `No background job with id ${jobId} is known on this machine.`
|
|
12193
|
+
};
|
|
12194
|
+
}
|
|
12195
|
+
const sentinel = await readSentinel(job.sentinel_path);
|
|
12196
|
+
if (!sentinel.present) {
|
|
12197
|
+
for (const sig of ["SIGTERM", "SIGKILL"]) {
|
|
12198
|
+
try {
|
|
12199
|
+
if (job.pid > 0)
|
|
12200
|
+
process.kill(-job.pid, sig);
|
|
12201
|
+
} catch {}
|
|
12202
|
+
}
|
|
12203
|
+
job.status = "killed";
|
|
12204
|
+
await writeSentinelAtomic(job.sentinel_path, "137");
|
|
12205
|
+
}
|
|
12206
|
+
const result = await classifyAndRead(job);
|
|
12207
|
+
if (result.status === "done" && job.status === "killed") {
|
|
12208
|
+
result.status = "killed";
|
|
12209
|
+
}
|
|
12210
|
+
result.message = "Background job stopped.";
|
|
12211
|
+
return result;
|
|
12212
|
+
});
|
|
12213
|
+
}
|
|
12214
|
+
function parseHostOutputRequests(data) {
|
|
12215
|
+
return parseJobToolRequests(data, "host_output");
|
|
12216
|
+
}
|
|
12217
|
+
function parseHostKillRequests(data) {
|
|
12218
|
+
return parseJobToolRequests(data, "host_kill");
|
|
12219
|
+
}
|
|
12220
|
+
function parseJobToolRequests(data, toolName) {
|
|
12221
|
+
let tools;
|
|
12222
|
+
try {
|
|
12223
|
+
tools = JSON.parse(data);
|
|
12224
|
+
} catch {
|
|
12225
|
+
return [];
|
|
12226
|
+
}
|
|
12227
|
+
if (!Array.isArray(tools))
|
|
12228
|
+
return [];
|
|
12229
|
+
const out = [];
|
|
12230
|
+
for (const t of tools) {
|
|
12231
|
+
if (t.tool_name !== toolName || t.status !== "awaiting_input")
|
|
12232
|
+
continue;
|
|
12233
|
+
if (!t.tool_call_id || !t.args?.job_id)
|
|
12234
|
+
continue;
|
|
12235
|
+
out.push({ toolCallId: t.tool_call_id, jobId: t.args.job_id });
|
|
12236
|
+
}
|
|
12237
|
+
return out;
|
|
12238
|
+
}
|
|
12239
|
+
|
|
11926
12240
|
// src/commands/chat.ts
|
|
11927
12241
|
async function uploadImageArtifact(ctx, sessionId, png) {
|
|
11928
12242
|
try {
|
|
@@ -11941,7 +12255,8 @@ async function uploadImageArtifact(ctx, sessionId, png) {
|
|
|
11941
12255
|
return { error: `image upload failed: ${String(e)}` };
|
|
11942
12256
|
}
|
|
11943
12257
|
}
|
|
11944
|
-
async function resolveHostExec(sessionId, toolCallId,
|
|
12258
|
+
async function resolveHostExec(sessionId, toolCallId, req, state) {
|
|
12259
|
+
const command = req.command;
|
|
11945
12260
|
const decision = decideFromJournal(await state.journal.lookup(sessionId, toolCallId));
|
|
11946
12261
|
if (decision.kind === "replay")
|
|
11947
12262
|
return decision.result;
|
|
@@ -11962,20 +12277,65 @@ async function resolveHostExec(sessionId, toolCallId, command, state) {
|
|
|
11962
12277
|
message: "Host execution declined; re-run with --yes to allow."
|
|
11963
12278
|
};
|
|
11964
12279
|
}
|
|
12280
|
+
const cwd = req.cwd ? `${state.cwd}/${req.cwd}` : state.cwd;
|
|
12281
|
+
if (req.background) {
|
|
12282
|
+
const jobId = mintJobId();
|
|
12283
|
+
process.stderr.write(source_default.dim(`
|
|
12284
|
+
[host_exec:bg] $ ${command}
|
|
12285
|
+
(job ${jobId}, cwd: ${cwd})
|
|
12286
|
+
`));
|
|
12287
|
+
try {
|
|
12288
|
+
const handle = await dispatchBackgroundJob(command, jobId, state.journal, {
|
|
12289
|
+
cwd
|
|
12290
|
+
});
|
|
12291
|
+
return {
|
|
12292
|
+
job_id: handle.job_id,
|
|
12293
|
+
status: "running",
|
|
12294
|
+
pid: handle.pid,
|
|
12295
|
+
log_path: handle.log_path,
|
|
12296
|
+
message: `Dispatched in the background as ${handle.job_id}. Poll host_output(` + `{job_id:"${handle.job_id}"}) for new output + status; host_kill to stop it.`
|
|
12297
|
+
};
|
|
12298
|
+
} catch (e) {
|
|
12299
|
+
return {
|
|
12300
|
+
error: true,
|
|
12301
|
+
message: `failed to dispatch background job: ${String(e)}`
|
|
12302
|
+
};
|
|
12303
|
+
}
|
|
12304
|
+
}
|
|
11965
12305
|
process.stderr.write(source_default.dim(`
|
|
11966
12306
|
[host_exec] $ ${command}
|
|
11967
|
-
(cwd: ${
|
|
12307
|
+
(cwd: ${cwd})
|
|
11968
12308
|
`));
|
|
11969
12309
|
await state.journal.recordRunning(sessionId, toolCallId, command, new Date().toISOString());
|
|
11970
12310
|
let result;
|
|
11971
12311
|
try {
|
|
11972
|
-
result = await runHostCommand(command, toolCallId, { cwd
|
|
12312
|
+
result = await runHostCommand(command, toolCallId, { cwd });
|
|
11973
12313
|
} catch (e) {
|
|
11974
12314
|
result = { stdout: "", stderr: String(e), exit_code: -1 };
|
|
11975
12315
|
}
|
|
11976
12316
|
await state.journal.recordDone(sessionId, toolCallId, result);
|
|
11977
12317
|
return result;
|
|
11978
12318
|
}
|
|
12319
|
+
async function resolveHostOutput(req, state) {
|
|
12320
|
+
process.stderr.write(source_default.dim(`
|
|
12321
|
+
[host_output] ${req.jobId}
|
|
12322
|
+
`));
|
|
12323
|
+
try {
|
|
12324
|
+
return await pollJob(state.journal, req.jobId);
|
|
12325
|
+
} catch (e) {
|
|
12326
|
+
return { error: true, message: `host_output failed: ${String(e)}` };
|
|
12327
|
+
}
|
|
12328
|
+
}
|
|
12329
|
+
async function resolveHostKill(req, state) {
|
|
12330
|
+
process.stderr.write(source_default.dim(`
|
|
12331
|
+
[host_kill] ${req.jobId}
|
|
12332
|
+
`));
|
|
12333
|
+
try {
|
|
12334
|
+
return await killJob(state.journal, req.jobId);
|
|
12335
|
+
} catch (e) {
|
|
12336
|
+
return { error: true, message: `host_kill failed: ${String(e)}` };
|
|
12337
|
+
}
|
|
12338
|
+
}
|
|
11979
12339
|
async function resolveHostScreenshot(ctx, sessionId, req, state) {
|
|
11980
12340
|
if (!state.autoYes) {
|
|
11981
12341
|
process.stderr.write(source_default.yellow(`
|
|
@@ -12082,6 +12442,27 @@ async function resolveLocalAgent(sessionId, toolCallId, req, state) {
|
|
|
12082
12442
|
}
|
|
12083
12443
|
const cwd = req.cwd ? `${state.cwd}/${req.cwd}` : state.cwd;
|
|
12084
12444
|
const { bin, args, stdin } = buildSpawn(invocation, req.task);
|
|
12445
|
+
if (req.background) {
|
|
12446
|
+
const jobId = mintJobId();
|
|
12447
|
+
process.stderr.write(source_default.dim(`
|
|
12448
|
+
[local_agent:bg] (${invocation.source}) ${req.task.slice(0, 80)} (job ${jobId})
|
|
12449
|
+
`));
|
|
12450
|
+
try {
|
|
12451
|
+
const handle = await dispatchBackgroundJob(`local_agent:${invocation.source}`, jobId, state.journal, { cwd, argv: [bin, ...args], stdinData: stdin ?? undefined });
|
|
12452
|
+
return {
|
|
12453
|
+
job_id: handle.job_id,
|
|
12454
|
+
status: "running",
|
|
12455
|
+
pid: handle.pid,
|
|
12456
|
+
log_path: handle.log_path,
|
|
12457
|
+
message: `Delegated to ${invocation.source} in the background as ${handle.job_id}. ` + `Poll host_output({job_id:"${handle.job_id}"}) for its progress + final ` + `result; host_kill to stop it.`
|
|
12458
|
+
};
|
|
12459
|
+
} catch (e) {
|
|
12460
|
+
return {
|
|
12461
|
+
error: true,
|
|
12462
|
+
message: `local agent failed to start (${invocation.source}): ${String(e)}. ` + `Pick an installed agent with --local-agent <claude|codex|cursor-agent>.`
|
|
12463
|
+
};
|
|
12464
|
+
}
|
|
12465
|
+
}
|
|
12085
12466
|
process.stderr.write(source_default.dim(`
|
|
12086
12467
|
[local_agent] (${invocation.source}) ${req.task.slice(0, 100)}
|
|
12087
12468
|
`));
|
|
@@ -12157,9 +12538,27 @@ async function handleHostExecFrame(ctx, sessionId, data, state) {
|
|
|
12157
12538
|
continue;
|
|
12158
12539
|
state.handled.add(id);
|
|
12159
12540
|
await claimGate(ctx, sessionId, id, state);
|
|
12160
|
-
const result = await resolveHostExec(sessionId, id, req
|
|
12541
|
+
const result = await resolveHostExec(sessionId, id, req, state);
|
|
12161
12542
|
await _postToolResult(ctx, sessionId, id, result, "host_exec", state.secrets.get(id));
|
|
12162
12543
|
}
|
|
12544
|
+
for (const req of parseHostOutputRequests(data)) {
|
|
12545
|
+
const id = req.toolCallId;
|
|
12546
|
+
if (state.handled.has(id))
|
|
12547
|
+
continue;
|
|
12548
|
+
state.handled.add(id);
|
|
12549
|
+
await claimGate(ctx, sessionId, id, state);
|
|
12550
|
+
const result = await resolveHostOutput(req, state);
|
|
12551
|
+
await _postToolResult(ctx, sessionId, id, result, "host_output", state.secrets.get(id));
|
|
12552
|
+
}
|
|
12553
|
+
for (const req of parseHostKillRequests(data)) {
|
|
12554
|
+
const id = req.toolCallId;
|
|
12555
|
+
if (state.handled.has(id))
|
|
12556
|
+
continue;
|
|
12557
|
+
state.handled.add(id);
|
|
12558
|
+
await claimGate(ctx, sessionId, id, state);
|
|
12559
|
+
const result = await resolveHostKill(req, state);
|
|
12560
|
+
await _postToolResult(ctx, sessionId, id, result, "host_kill", state.secrets.get(id));
|
|
12561
|
+
}
|
|
12163
12562
|
for (const req of parseHostScreenshotRequests(data)) {
|
|
12164
12563
|
const id = req.toolCallId;
|
|
12165
12564
|
if (state.handled.has(id))
|
|
@@ -13166,8 +13565,8 @@ function registerConfigCommand(program2) {
|
|
|
13166
13565
|
}
|
|
13167
13566
|
|
|
13168
13567
|
// src/commands/definitions-apply.ts
|
|
13169
|
-
import
|
|
13170
|
-
import
|
|
13568
|
+
import fs8 from "node:fs";
|
|
13569
|
+
import path7 from "node:path";
|
|
13171
13570
|
init_dist();
|
|
13172
13571
|
init_errors();
|
|
13173
13572
|
var VALID_KINDS = new Set([
|
|
@@ -13209,20 +13608,20 @@ var SERVER_GENERATED_FIELDS = new Set([
|
|
|
13209
13608
|
]);
|
|
13210
13609
|
var AGENT_MANAGED_FIELDS = new Set(["functions", "workflows"]);
|
|
13211
13610
|
function loadDefinitions(inputPath) {
|
|
13212
|
-
const resolved =
|
|
13213
|
-
if (!
|
|
13611
|
+
const resolved = path7.resolve(inputPath);
|
|
13612
|
+
if (!fs8.existsSync(resolved)) {
|
|
13214
13613
|
throw new CLIError(`Path not found: ${inputPath}`, 1 /* Error */);
|
|
13215
13614
|
}
|
|
13216
|
-
const stat =
|
|
13615
|
+
const stat = fs8.statSync(resolved);
|
|
13217
13616
|
if (stat.isDirectory()) {
|
|
13218
13617
|
return loadDirectory(resolved);
|
|
13219
13618
|
}
|
|
13220
13619
|
return loadFile(resolved);
|
|
13221
13620
|
}
|
|
13222
13621
|
function loadDirectory(dirPath) {
|
|
13223
|
-
const entries =
|
|
13622
|
+
const entries = fs8.readdirSync(dirPath).sort();
|
|
13224
13623
|
const validExts = new Set([".yaml", ".yml", ".json"]);
|
|
13225
|
-
const files = entries.filter((e) => validExts.has(
|
|
13624
|
+
const files = entries.filter((e) => validExts.has(path7.extname(e).toLowerCase())).map((e) => path7.join(dirPath, e));
|
|
13226
13625
|
if (files.length === 0) {
|
|
13227
13626
|
throw new CLIError(`No YAML or JSON files found in ${dirPath}`, 4 /* Validation */);
|
|
13228
13627
|
}
|
|
@@ -13233,8 +13632,8 @@ function loadDirectory(dirPath) {
|
|
|
13233
13632
|
return results;
|
|
13234
13633
|
}
|
|
13235
13634
|
function loadFile(filePath) {
|
|
13236
|
-
const content =
|
|
13237
|
-
const ext =
|
|
13635
|
+
const content = fs8.readFileSync(filePath, "utf-8");
|
|
13636
|
+
const ext = path7.extname(filePath).toLowerCase();
|
|
13238
13637
|
if (ext === ".json") {
|
|
13239
13638
|
const parsed = parseJsonFile(content, filePath);
|
|
13240
13639
|
return [toLocalDefinition(parsed, filePath)];
|
|
@@ -13656,7 +14055,7 @@ Examples:
|
|
|
13656
14055
|
}
|
|
13657
14056
|
|
|
13658
14057
|
// src/commands/definitions-export.ts
|
|
13659
|
-
import
|
|
14058
|
+
import fs9 from "node:fs";
|
|
13660
14059
|
init_errors();
|
|
13661
14060
|
async function exportDefinitions(ctx, opts, isJsonMode) {
|
|
13662
14061
|
const params = {};
|
|
@@ -13672,7 +14071,7 @@ async function exportDefinitions(ctx, opts, isJsonMode) {
|
|
|
13672
14071
|
const text = await res.text();
|
|
13673
14072
|
if (opts.output) {
|
|
13674
14073
|
try {
|
|
13675
|
-
|
|
14074
|
+
fs9.writeFileSync(opts.output, text);
|
|
13676
14075
|
} catch (err) {
|
|
13677
14076
|
throw new CLIError(`Failed to write ${opts.output}: ${err instanceof Error ? err.message : String(err)}`, 1 /* Error */);
|
|
13678
14077
|
}
|
|
@@ -13980,7 +14379,7 @@ function colorStatus(status) {
|
|
|
13980
14379
|
const fn = STATUS_COLORS[status] ?? source_default.white;
|
|
13981
14380
|
return fn(status);
|
|
13982
14381
|
}
|
|
13983
|
-
function
|
|
14382
|
+
function sleep2(ms) {
|
|
13984
14383
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
13985
14384
|
}
|
|
13986
14385
|
async function pollUntilDone(ctx, executionId, intervalMs = 2000, timeoutMs = 300000) {
|
|
@@ -13990,7 +14389,7 @@ async function pollUntilDone(ctx, executionId, intervalMs = 2000, timeoutMs = 30
|
|
|
13990
14389
|
if (TERMINAL_STATUSES.has(status.status)) {
|
|
13991
14390
|
return status;
|
|
13992
14391
|
}
|
|
13993
|
-
await
|
|
14392
|
+
await sleep2(intervalMs);
|
|
13994
14393
|
}
|
|
13995
14394
|
throw new CLIError(`Timed out waiting for execution ${executionId}`, 1 /* Error */);
|
|
13996
14395
|
}
|
|
@@ -14061,7 +14460,7 @@ Detached. Reattach with: martha workflows execution ${executionId} --follow`));
|
|
|
14061
14460
|
}
|
|
14062
14461
|
return;
|
|
14063
14462
|
}
|
|
14064
|
-
await
|
|
14463
|
+
await sleep2(1000);
|
|
14065
14464
|
current = await ctx.api.get(`/api/admin/executions/${encodeURIComponent(executionId)}`);
|
|
14066
14465
|
}
|
|
14067
14466
|
} finally {
|
|
@@ -14392,8 +14791,8 @@ function registerProjectionCommands(parentCmd, getCtx, isJson) {
|
|
|
14392
14791
|
} catch {}
|
|
14393
14792
|
}
|
|
14394
14793
|
if (opts.output) {
|
|
14395
|
-
const
|
|
14396
|
-
await
|
|
14794
|
+
const fs10 = await import("node:fs/promises");
|
|
14795
|
+
await fs10.writeFile(opts.output, toWrite, "utf-8");
|
|
14397
14796
|
if (!isJson()) {
|
|
14398
14797
|
console.error(source_default.dim(`Wrote ${format} projection of '${name}' to ${opts.output}`));
|
|
14399
14798
|
}
|
|
@@ -14800,8 +15199,8 @@ Usage:
|
|
|
14800
15199
|
});
|
|
14801
15200
|
parentCmd.command("self-grant <agent>").description("View or configure an agent's self-provisioning policy (request-then-approve). " + "With no flags, prints the current policy.").option("--enable", "Allow the agent to REQUEST capability grants").option("--disable", "Disallow self-provisioning").option("--scope <scope>", "Scope ceiling: none | read_only | read_write").option("--allow <ref...>", "Add allow-list refs (function:NAME or mcp:integration/name)").option("--remove <ref...>", "Remove allow-list refs").option("--collection-root <id...>", "Add collection-subtree roots the agent may self-grant collection-scoped functions into").option("--remove-collection-root <id...>", "Remove collection roots").option("--max-pending <n>", "Max concurrently-pending requests").action(async (agent, opts) => {
|
|
14802
15201
|
const ctx = getCtx();
|
|
14803
|
-
const
|
|
14804
|
-
const current = await ctx.api.get(
|
|
15202
|
+
const path8 = `${API_PATH}/${encodeURIComponent(agent)}`;
|
|
15203
|
+
const current = await ctx.api.get(path8);
|
|
14805
15204
|
const mutating = opts.enable || opts.disable || opts.scope !== undefined || (opts.allow?.length ?? 0) > 0 || (opts.remove?.length ?? 0) > 0 || (opts.collectionRoot?.length ?? 0) > 0 || (opts.removeCollectionRoot?.length ?? 0) > 0 || opts.maxPending !== undefined;
|
|
14806
15205
|
if (!mutating) {
|
|
14807
15206
|
if (isJson()) {
|
|
@@ -14853,7 +15252,7 @@ Usage:
|
|
|
14853
15252
|
roots.delete(c);
|
|
14854
15253
|
body.self_grant_collection_roots = [...roots];
|
|
14855
15254
|
}
|
|
14856
|
-
const updated = await ctx.api.put(
|
|
15255
|
+
const updated = await ctx.api.put(path8, body);
|
|
14857
15256
|
if (isJson()) {
|
|
14858
15257
|
console.log(JSON.stringify(policyView(updated), null, 2));
|
|
14859
15258
|
return;
|
|
@@ -15100,7 +15499,7 @@ var triggersConfig = {
|
|
|
15100
15499
|
};
|
|
15101
15500
|
|
|
15102
15501
|
// src/commands/documents.ts
|
|
15103
|
-
import
|
|
15502
|
+
import fs10 from "node:fs";
|
|
15104
15503
|
init_errors();
|
|
15105
15504
|
var TERMINAL_STATUSES2 = new Set(["ready", "error"]);
|
|
15106
15505
|
var SPINNER_FRAMES2 = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
@@ -15110,7 +15509,7 @@ function nextSpinner() {
|
|
|
15110
15509
|
spinnerIdx++;
|
|
15111
15510
|
return frame;
|
|
15112
15511
|
}
|
|
15113
|
-
function
|
|
15512
|
+
function sleep3(ms) {
|
|
15114
15513
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
15115
15514
|
}
|
|
15116
15515
|
function formatBytes(bytes) {
|
|
@@ -15170,7 +15569,7 @@ async function pollIngestionUntilDone(ctx, documentId, intervalMs = 2000, timeou
|
|
|
15170
15569
|
if (TERMINAL_STATUSES2.has(status.ingestion_status)) {
|
|
15171
15570
|
return status;
|
|
15172
15571
|
}
|
|
15173
|
-
await
|
|
15572
|
+
await sleep3(intervalMs);
|
|
15174
15573
|
}
|
|
15175
15574
|
throw new CLIError(`Timed out waiting for ingestion of document ${documentId}`, 1 /* Error */);
|
|
15176
15575
|
}
|
|
@@ -15245,7 +15644,7 @@ Detached. Check status with: martha documents status ${documentId}`));
|
|
|
15245
15644
|
}
|
|
15246
15645
|
return;
|
|
15247
15646
|
}
|
|
15248
|
-
await
|
|
15647
|
+
await sleep3(1000);
|
|
15249
15648
|
}
|
|
15250
15649
|
} finally {
|
|
15251
15650
|
process.removeListener("SIGINT", sigintHandler);
|
|
@@ -15461,7 +15860,7 @@ ${items.length} collections`));
|
|
|
15461
15860
|
});
|
|
15462
15861
|
cmd.command("upload <collection-id> <file>").description("Upload a document to a collection").option("--wait", "Wait for ingestion to complete").option("--follow", "Follow ingestion progress in real time").action(async (collectionId, filePath, opts) => {
|
|
15463
15862
|
const ctx = getCtx();
|
|
15464
|
-
if (!
|
|
15863
|
+
if (!fs10.existsSync(filePath)) {
|
|
15465
15864
|
throw new CLIError(`File not found: ${filePath}`, 4 /* Validation */);
|
|
15466
15865
|
}
|
|
15467
15866
|
const result = await ctx.api.upload(`/api/admin/collections/${encodeURIComponent(collectionId)}/documents`, filePath);
|
|
@@ -16090,10 +16489,10 @@ function registerApprovalCommands(program2) {
|
|
|
16090
16489
|
}
|
|
16091
16490
|
|
|
16092
16491
|
// src/commands/tasks.ts
|
|
16093
|
-
import { readFileSync } from "node:fs";
|
|
16492
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
16094
16493
|
init_errors();
|
|
16095
16494
|
var truncate2 = (s, n) => s.length > n ? s.slice(0, n - 1) + "…" : s;
|
|
16096
|
-
var
|
|
16495
|
+
var sleep4 = (ms, signal) => new Promise((resolve2) => {
|
|
16097
16496
|
if (signal?.aborted) {
|
|
16098
16497
|
resolve2();
|
|
16099
16498
|
return;
|
|
@@ -16459,7 +16858,7 @@ ${items.length} task(s) available`));
|
|
|
16459
16858
|
if (reconnects > maxReconnects) {
|
|
16460
16859
|
throw new CLIError(`Exceeded --max-reconnects (${maxReconnects})`, 1 /* Error */);
|
|
16461
16860
|
}
|
|
16462
|
-
await
|
|
16861
|
+
await sleep4(backoffMs, abort.signal);
|
|
16463
16862
|
backoffMs = Math.min(backoffMs * 2, maxBackoff);
|
|
16464
16863
|
continue;
|
|
16465
16864
|
}
|
|
@@ -16523,7 +16922,7 @@ ${items.length} task(s) available`));
|
|
|
16523
16922
|
if (reconnects > maxReconnects) {
|
|
16524
16923
|
throw new CLIError(`Exceeded --max-reconnects (${maxReconnects})`, 1 /* Error */);
|
|
16525
16924
|
}
|
|
16526
|
-
await
|
|
16925
|
+
await sleep4(backoffMs, abort.signal);
|
|
16527
16926
|
backoffMs = Math.min(backoffMs * 2, maxBackoff);
|
|
16528
16927
|
}
|
|
16529
16928
|
} finally {
|
|
@@ -16536,7 +16935,7 @@ ${items.length} task(s) available`));
|
|
|
16536
16935
|
let rawOutcome;
|
|
16537
16936
|
if (opts.outcomeFile) {
|
|
16538
16937
|
try {
|
|
16539
|
-
rawOutcome =
|
|
16938
|
+
rawOutcome = readFileSync2(opts.outcomeFile, "utf-8");
|
|
16540
16939
|
} catch {
|
|
16541
16940
|
throw new CLIError(`--outcome-file could not be read: ${opts.outcomeFile}`, 4 /* Validation */);
|
|
16542
16941
|
}
|
|
@@ -17038,8 +17437,8 @@ ${data.length} spec(s)`));
|
|
|
17038
17437
|
Resources:
|
|
17039
17438
|
`));
|
|
17040
17439
|
for (const r of resources) {
|
|
17041
|
-
const
|
|
17042
|
-
console.log(` ${source_default.cyan(r.label || r.name)}` + source_default.dim(` → ${
|
|
17440
|
+
const path8 = r.path || `/${r.name}`;
|
|
17441
|
+
console.log(` ${source_default.cyan(r.label || r.name)}` + source_default.dim(` → ${path8}`));
|
|
17043
17442
|
}
|
|
17044
17443
|
}
|
|
17045
17444
|
try {
|
|
@@ -17059,14 +17458,14 @@ Functions:
|
|
|
17059
17458
|
console.log(source_default.dim(`
|
|
17060
17459
|
Proxy: martha integrations proxy ${name} GET /<path>`));
|
|
17061
17460
|
});
|
|
17062
|
-
cmd.command("proxy <name> <method> <path>").description("Send a request through the plugin proxy").option("--data <json>", "JSON request body").option("--query <params>", "Query params as key=val&key=val").action(async (name, method,
|
|
17461
|
+
cmd.command("proxy <name> <method> <path>").description("Send a request through the plugin proxy").option("--data <json>", "JSON request body").option("--query <params>", "Query params as key=val&key=val").action(async (name, method, path8, opts) => {
|
|
17063
17462
|
const ctx = getCtx();
|
|
17064
17463
|
const upperMethod = method.toUpperCase();
|
|
17065
17464
|
const allowed = new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
|
|
17066
17465
|
if (!allowed.has(upperMethod)) {
|
|
17067
17466
|
throw new CLIError(`Invalid method: ${method}`, 4 /* Validation */, "Allowed: GET, POST, PUT, PATCH, DELETE");
|
|
17068
17467
|
}
|
|
17069
|
-
const cleanPath =
|
|
17468
|
+
const cleanPath = path8.startsWith("/") ? path8.slice(1) : path8;
|
|
17070
17469
|
const proxyUrl = `/api/admin/plugins/${encodeURIComponent(name)}/${cleanPath}`;
|
|
17071
17470
|
const params = {};
|
|
17072
17471
|
if (opts.query) {
|
|
@@ -17276,8 +17675,8 @@ async function resolveCredentialValue(value) {
|
|
|
17276
17675
|
if (value === "-")
|
|
17277
17676
|
return readStdin();
|
|
17278
17677
|
if (value.startsWith("@")) {
|
|
17279
|
-
const
|
|
17280
|
-
return (await
|
|
17678
|
+
const fs11 = await import("node:fs/promises");
|
|
17679
|
+
return (await fs11.readFile(value.slice(1), "utf-8")).trim();
|
|
17281
17680
|
}
|
|
17282
17681
|
return value;
|
|
17283
17682
|
}
|
|
@@ -17392,8 +17791,8 @@ Notification connections`));
|
|
|
17392
17791
|
if (credentialValue === "-") {
|
|
17393
17792
|
credentialValue = await readStdin2();
|
|
17394
17793
|
} else if (credentialValue?.startsWith("@")) {
|
|
17395
|
-
const
|
|
17396
|
-
credentialValue = await
|
|
17794
|
+
const fs11 = await import("node:fs/promises");
|
|
17795
|
+
credentialValue = await fs11.readFile(credentialValue.slice(1), "utf-8");
|
|
17397
17796
|
}
|
|
17398
17797
|
if (!credentialValue) {
|
|
17399
17798
|
throw new Error("--credential-value is required (use '-' for stdin or '@path' for file).");
|
|
@@ -17443,11 +17842,11 @@ async function readStdin2() {
|
|
|
17443
17842
|
|
|
17444
17843
|
// src/commands/messaging.ts
|
|
17445
17844
|
init_errors();
|
|
17446
|
-
async function messagingFetch(baseUrl,
|
|
17447
|
-
if (/^(https?:)?\/\//i.test(
|
|
17845
|
+
async function messagingFetch(baseUrl, path8, opts) {
|
|
17846
|
+
if (/^(https?:)?\/\//i.test(path8)) {
|
|
17448
17847
|
throw new CLIError("Absolute URL paths are not allowed", 1 /* Error */);
|
|
17449
17848
|
}
|
|
17450
|
-
const url = new URL(
|
|
17849
|
+
const url = new URL(path8, baseUrl);
|
|
17451
17850
|
const headers = {
|
|
17452
17851
|
"Content-Type": "application/json"
|
|
17453
17852
|
};
|
|
@@ -18727,7 +19126,7 @@ ${models.length} models`));
|
|
|
18727
19126
|
}
|
|
18728
19127
|
|
|
18729
19128
|
// src/commands/wiki.ts
|
|
18730
|
-
import
|
|
19129
|
+
import fs11 from "node:fs";
|
|
18731
19130
|
init_errors();
|
|
18732
19131
|
function registerWikiCommands(program2) {
|
|
18733
19132
|
const cmd = program2.command("wiki").description("Manage tenant wiki pages, settings, schema, recompile (#245 D5.4)");
|
|
@@ -18774,14 +19173,14 @@ function registerWikiCommands(program2) {
|
|
|
18774
19173
|
console.log(source_default.dim(`
|
|
18775
19174
|
${pages.length} pages`));
|
|
18776
19175
|
});
|
|
18777
|
-
cmd.command("get <path>").description("Fetch the raw markdown body of a wiki page").option("--out <file>", "Write body to file instead of stdout").action(async (
|
|
19176
|
+
cmd.command("get <path>").description("Fetch the raw markdown body of a wiki page").option("--out <file>", "Write body to file instead of stdout").action(async (path8, opts) => {
|
|
18778
19177
|
const ctx = getCtx();
|
|
18779
|
-
const safe =
|
|
19178
|
+
const safe = path8.split("/").map(encodeURIComponent).join("/");
|
|
18780
19179
|
const resp = await ctx.api.getRaw(`/api/wiki/pages/${safe}`, {
|
|
18781
19180
|
headers: { Accept: "text/markdown,*/*" }
|
|
18782
19181
|
});
|
|
18783
19182
|
if (resp.status === 404) {
|
|
18784
|
-
throw new CLIError(`page not found: ${
|
|
19183
|
+
throw new CLIError(`page not found: ${path8}`, 3 /* NotFound */);
|
|
18785
19184
|
}
|
|
18786
19185
|
if (!resp.ok) {
|
|
18787
19186
|
const detail = await resp.text();
|
|
@@ -18789,16 +19188,16 @@ ${pages.length} pages`));
|
|
|
18789
19188
|
}
|
|
18790
19189
|
const body = await resp.text();
|
|
18791
19190
|
if (opts.out) {
|
|
18792
|
-
|
|
19191
|
+
fs11.writeFileSync(opts.out, body);
|
|
18793
19192
|
if (!isJson()) {
|
|
18794
19193
|
console.log(source_default.dim(`wrote ${body.length} bytes to ${opts.out}`));
|
|
18795
19194
|
} else {
|
|
18796
|
-
console.log(JSON.stringify({ path:
|
|
19195
|
+
console.log(JSON.stringify({ path: path8, bytes: body.length, file: opts.out }));
|
|
18797
19196
|
}
|
|
18798
19197
|
return;
|
|
18799
19198
|
}
|
|
18800
19199
|
if (isJson()) {
|
|
18801
|
-
console.log(JSON.stringify({ path:
|
|
19200
|
+
console.log(JSON.stringify({ path: path8, body, etag: resp.headers.get("ETag") }));
|
|
18802
19201
|
} else {
|
|
18803
19202
|
process.stdout.write(body);
|
|
18804
19203
|
}
|
|
@@ -18893,10 +19292,10 @@ ${pages.length} pages`));
|
|
|
18893
19292
|
}
|
|
18894
19293
|
if (opts.compilePromptOverrideFile) {
|
|
18895
19294
|
const file = String(opts.compilePromptOverrideFile);
|
|
18896
|
-
if (!
|
|
19295
|
+
if (!fs11.existsSync(file)) {
|
|
18897
19296
|
throw new CLIError(`override file not found: ${file}`, 3 /* NotFound */);
|
|
18898
19297
|
}
|
|
18899
|
-
const content =
|
|
19298
|
+
const content = fs11.readFileSync(file, "utf8");
|
|
18900
19299
|
const bytes = Buffer.byteLength(content, "utf8");
|
|
18901
19300
|
if (bytes > 16 * 1024) {
|
|
18902
19301
|
throw new CLIError(`compile prompt override exceeds 16 KB (${bytes} bytes)`, 4 /* Validation */);
|
|
@@ -18920,7 +19319,7 @@ ${pages.length} pages`));
|
|
|
18920
19319
|
const ctx = getCtx();
|
|
18921
19320
|
const resp = await ctx.api.get("/api/wiki/schema");
|
|
18922
19321
|
if (opts.out) {
|
|
18923
|
-
|
|
19322
|
+
fs11.writeFileSync(opts.out, resp.body);
|
|
18924
19323
|
if (!isJson()) {
|
|
18925
19324
|
console.log(source_default.dim(`wrote ${resp.body.length} bytes to ${opts.out}`));
|
|
18926
19325
|
} else {
|
|
@@ -18936,10 +19335,10 @@ ${pages.length} pages`));
|
|
|
18936
19335
|
});
|
|
18937
19336
|
schemaCmd.command("set").description("Replace the tenant wiki schema with the contents of <file>").requiredOption("--file <file>", "Path to schema markdown file").action(async (opts) => {
|
|
18938
19337
|
const ctx = getCtx();
|
|
18939
|
-
if (!
|
|
19338
|
+
if (!fs11.existsSync(opts.file)) {
|
|
18940
19339
|
throw new CLIError(`schema file not found: ${opts.file}`, 3 /* NotFound */);
|
|
18941
19340
|
}
|
|
18942
|
-
const body =
|
|
19341
|
+
const body = fs11.readFileSync(opts.file, "utf8");
|
|
18943
19342
|
const bytes = Buffer.byteLength(body, "utf8");
|
|
18944
19343
|
if (bytes > 64 * 1024) {
|
|
18945
19344
|
throw new CLIError(`schema body exceeds 64 KB (${bytes} bytes)`, 4 /* Validation */);
|
|
@@ -19465,8 +19864,8 @@ async function resolveCredentialFlag(value) {
|
|
|
19465
19864
|
return Buffer.concat(chunks).toString("utf-8").trim();
|
|
19466
19865
|
}
|
|
19467
19866
|
if (value.startsWith("@")) {
|
|
19468
|
-
const
|
|
19469
|
-
return (await
|
|
19867
|
+
const fs12 = await import("node:fs/promises");
|
|
19868
|
+
return (await fs12.readFile(value.slice(1), "utf-8")).trim();
|
|
19470
19869
|
}
|
|
19471
19870
|
return value;
|
|
19472
19871
|
}
|
|
@@ -20652,19 +21051,19 @@ function registerDoctorCommand(program2) {
|
|
|
20652
21051
|
|
|
20653
21052
|
// src/commands/skill.ts
|
|
20654
21053
|
init_errors();
|
|
20655
|
-
import
|
|
20656
|
-
import
|
|
21054
|
+
import fs12 from "node:fs";
|
|
21055
|
+
import path8 from "node:path";
|
|
20657
21056
|
import { fileURLToPath } from "node:url";
|
|
20658
21057
|
function locateSkill() {
|
|
20659
|
-
const here =
|
|
21058
|
+
const here = path8.dirname(fileURLToPath(import.meta.url));
|
|
20660
21059
|
const candidates = [
|
|
20661
|
-
|
|
20662
|
-
|
|
20663
|
-
|
|
20664
|
-
|
|
21060
|
+
path8.join(here, "skills", "martha-cli", "SKILL.md"),
|
|
21061
|
+
path8.join(here, "..", "skills", "martha-cli", "SKILL.md"),
|
|
21062
|
+
path8.join(here, "..", "..", "..", "skills", "martha-cli", "SKILL.md"),
|
|
21063
|
+
path8.join(here, "..", "..", "skills", "martha-cli", "SKILL.md")
|
|
20665
21064
|
];
|
|
20666
21065
|
for (const p of candidates) {
|
|
20667
|
-
if (
|
|
21066
|
+
if (fs12.existsSync(p))
|
|
20668
21067
|
return p;
|
|
20669
21068
|
}
|
|
20670
21069
|
return null;
|
|
@@ -20674,7 +21073,7 @@ async function skillCommand() {
|
|
|
20674
21073
|
if (!skillPath) {
|
|
20675
21074
|
throw new CLIError("SKILL.md not found in the installed package. Reinstall via `npm i -g @aiaiai-pt/martha-cli` or `npx -y @aiaiai-pt/martha-cli@latest skill`.", 1 /* Error */);
|
|
20676
21075
|
}
|
|
20677
|
-
const body =
|
|
21076
|
+
const body = fs12.readFileSync(skillPath, "utf-8");
|
|
20678
21077
|
process.stdout.write(body);
|
|
20679
21078
|
}
|
|
20680
21079
|
function registerSkillCommand(program2) {
|