@aiaiai-pt/martha-cli 0.20.0 → 0.22.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 +492 -62
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11077,7 +11077,7 @@ import { createInterface as createInterface2 } from "node:readline";
|
|
|
11077
11077
|
init_errors();
|
|
11078
11078
|
|
|
11079
11079
|
// src/version.ts
|
|
11080
|
-
var CLI_VERSION = "0.
|
|
11080
|
+
var CLI_VERSION = "0.22.0";
|
|
11081
11081
|
|
|
11082
11082
|
// src/commands/sessions.ts
|
|
11083
11083
|
function relativeTime(iso) {
|
|
@@ -11349,7 +11349,302 @@ function createApprovalTagStripper() {
|
|
|
11349
11349
|
};
|
|
11350
11350
|
}
|
|
11351
11351
|
|
|
11352
|
+
// src/lib/local-exec.ts
|
|
11353
|
+
import { spawn } from "node:child_process";
|
|
11354
|
+
import { closeSync, openSync, promises as fs5 } from "node:fs";
|
|
11355
|
+
import * as path4 from "node:path";
|
|
11356
|
+
var DEFAULT_TIMEOUT_MS = 20000;
|
|
11357
|
+
var MAX_OUTPUT = 256 * 1024;
|
|
11358
|
+
function runnerDir(cwd) {
|
|
11359
|
+
return path4.join(cwd, ".martha-runner");
|
|
11360
|
+
}
|
|
11361
|
+
async function runHostCommand(command, toolCallId, opts) {
|
|
11362
|
+
const dir = runnerDir(opts.cwd);
|
|
11363
|
+
await fs5.mkdir(dir, { recursive: true });
|
|
11364
|
+
const logPath = path4.join(dir, `${safeName(toolCallId)}.log`);
|
|
11365
|
+
await fs5.writeFile(logPath, "");
|
|
11366
|
+
const fd = openSync(logPath, "a");
|
|
11367
|
+
const cap = opts.maxOutputBytes ?? MAX_OUTPUT;
|
|
11368
|
+
const readLog = async () => {
|
|
11369
|
+
try {
|
|
11370
|
+
const buf = await fs5.readFile(logPath);
|
|
11371
|
+
return buf.subarray(-cap).toString("utf8");
|
|
11372
|
+
} catch {
|
|
11373
|
+
return "";
|
|
11374
|
+
}
|
|
11375
|
+
};
|
|
11376
|
+
return await new Promise((resolve, reject) => {
|
|
11377
|
+
let settled = false;
|
|
11378
|
+
const child = spawn(command, {
|
|
11379
|
+
shell: true,
|
|
11380
|
+
cwd: opts.cwd,
|
|
11381
|
+
detached: true,
|
|
11382
|
+
stdio: ["ignore", fd, fd]
|
|
11383
|
+
});
|
|
11384
|
+
closeSync(fd);
|
|
11385
|
+
const timer = setTimeout(async () => {
|
|
11386
|
+
if (settled)
|
|
11387
|
+
return;
|
|
11388
|
+
settled = true;
|
|
11389
|
+
child.unref();
|
|
11390
|
+
resolve({
|
|
11391
|
+
stdout: await readLog(),
|
|
11392
|
+
stderr: "",
|
|
11393
|
+
exit_code: 0,
|
|
11394
|
+
backgrounded: true,
|
|
11395
|
+
pid: child.pid
|
|
11396
|
+
});
|
|
11397
|
+
}, opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
11398
|
+
child.on("error", (e) => {
|
|
11399
|
+
if (settled)
|
|
11400
|
+
return;
|
|
11401
|
+
settled = true;
|
|
11402
|
+
clearTimeout(timer);
|
|
11403
|
+
reject(e);
|
|
11404
|
+
});
|
|
11405
|
+
child.on("close", async (code) => {
|
|
11406
|
+
if (settled)
|
|
11407
|
+
return;
|
|
11408
|
+
settled = true;
|
|
11409
|
+
clearTimeout(timer);
|
|
11410
|
+
resolve({ stdout: await readLog(), stderr: "", exit_code: code ?? -1 });
|
|
11411
|
+
});
|
|
11412
|
+
});
|
|
11413
|
+
}
|
|
11414
|
+
function safeName(s) {
|
|
11415
|
+
return s.replace(/[^a-zA-Z0-9_.-]/g, "_").slice(0, 120) || "task";
|
|
11416
|
+
}
|
|
11417
|
+
|
|
11418
|
+
class Journal {
|
|
11419
|
+
cwd;
|
|
11420
|
+
constructor(cwd) {
|
|
11421
|
+
this.cwd = cwd;
|
|
11422
|
+
}
|
|
11423
|
+
file() {
|
|
11424
|
+
return path4.join(runnerDir(this.cwd), "journal.json");
|
|
11425
|
+
}
|
|
11426
|
+
async load() {
|
|
11427
|
+
try {
|
|
11428
|
+
return JSON.parse(await fs5.readFile(this.file(), "utf8"));
|
|
11429
|
+
} catch {
|
|
11430
|
+
return {};
|
|
11431
|
+
}
|
|
11432
|
+
}
|
|
11433
|
+
async save(data) {
|
|
11434
|
+
await fs5.mkdir(runnerDir(this.cwd), { recursive: true });
|
|
11435
|
+
await fs5.writeFile(this.file(), JSON.stringify(data, null, 2));
|
|
11436
|
+
}
|
|
11437
|
+
static key(sessionId, toolCallId) {
|
|
11438
|
+
return `${sessionId}:${toolCallId}`;
|
|
11439
|
+
}
|
|
11440
|
+
async lookup(sessionId, toolCallId) {
|
|
11441
|
+
return (await this.load())[Journal.key(sessionId, toolCallId)];
|
|
11442
|
+
}
|
|
11443
|
+
async recordRunning(sessionId, toolCallId, command, now) {
|
|
11444
|
+
const data = await this.load();
|
|
11445
|
+
data[Journal.key(sessionId, toolCallId)] = {
|
|
11446
|
+
state: "running",
|
|
11447
|
+
command,
|
|
11448
|
+
started_at: now
|
|
11449
|
+
};
|
|
11450
|
+
await this.save(data);
|
|
11451
|
+
}
|
|
11452
|
+
async recordDone(sessionId, toolCallId, result) {
|
|
11453
|
+
const data = await this.load();
|
|
11454
|
+
const k = Journal.key(sessionId, toolCallId);
|
|
11455
|
+
const prev = data[k];
|
|
11456
|
+
data[k] = {
|
|
11457
|
+
state: "done",
|
|
11458
|
+
command: prev?.command ?? "",
|
|
11459
|
+
started_at: prev?.started_at ?? "",
|
|
11460
|
+
result
|
|
11461
|
+
};
|
|
11462
|
+
await this.save(data);
|
|
11463
|
+
}
|
|
11464
|
+
}
|
|
11465
|
+
function decideFromJournal(entry) {
|
|
11466
|
+
if (!entry)
|
|
11467
|
+
return { kind: "fresh" };
|
|
11468
|
+
if (entry.state === "done" && entry.result)
|
|
11469
|
+
return { kind: "replay", result: entry.result };
|
|
11470
|
+
return { kind: "crashed", command: entry.command };
|
|
11471
|
+
}
|
|
11472
|
+
function parseHostExecRequests(data) {
|
|
11473
|
+
let tools;
|
|
11474
|
+
try {
|
|
11475
|
+
tools = JSON.parse(data);
|
|
11476
|
+
} catch {
|
|
11477
|
+
return [];
|
|
11478
|
+
}
|
|
11479
|
+
if (!Array.isArray(tools))
|
|
11480
|
+
return [];
|
|
11481
|
+
const out = [];
|
|
11482
|
+
for (const t of tools) {
|
|
11483
|
+
if (t.tool_name !== "host_exec" || t.status !== "awaiting_input")
|
|
11484
|
+
continue;
|
|
11485
|
+
if (!t.tool_call_id)
|
|
11486
|
+
continue;
|
|
11487
|
+
out.push({ toolCallId: t.tool_call_id, command: t.args?.command ?? "" });
|
|
11488
|
+
}
|
|
11489
|
+
return out;
|
|
11490
|
+
}
|
|
11491
|
+
|
|
11492
|
+
// src/lib/host-screenshot.ts
|
|
11493
|
+
async function captureScreenshot(url, opts) {
|
|
11494
|
+
const moduleName = "playwright";
|
|
11495
|
+
let chromium;
|
|
11496
|
+
try {
|
|
11497
|
+
({ chromium } = await import(moduleName));
|
|
11498
|
+
} catch {
|
|
11499
|
+
throw new Error("playwright is not installed on the runner. Install it to use host_screenshot: `npm i -D playwright && npx playwright install chromium`");
|
|
11500
|
+
}
|
|
11501
|
+
const browser = await chromium.launch({ headless: true });
|
|
11502
|
+
try {
|
|
11503
|
+
const page = await browser.newPage();
|
|
11504
|
+
await page.goto(url, { waitUntil: "load", timeout: 30000 });
|
|
11505
|
+
if (opts.waitMs > 0)
|
|
11506
|
+
await page.waitForTimeout(opts.waitMs);
|
|
11507
|
+
const buf = await page.screenshot({
|
|
11508
|
+
fullPage: opts.fullPage,
|
|
11509
|
+
type: "png"
|
|
11510
|
+
});
|
|
11511
|
+
return buf;
|
|
11512
|
+
} finally {
|
|
11513
|
+
await browser.close();
|
|
11514
|
+
}
|
|
11515
|
+
}
|
|
11516
|
+
function parseHostScreenshotRequests(data) {
|
|
11517
|
+
let tools;
|
|
11518
|
+
try {
|
|
11519
|
+
tools = JSON.parse(data);
|
|
11520
|
+
} catch {
|
|
11521
|
+
return [];
|
|
11522
|
+
}
|
|
11523
|
+
if (!Array.isArray(tools))
|
|
11524
|
+
return [];
|
|
11525
|
+
const out = [];
|
|
11526
|
+
for (const t of tools) {
|
|
11527
|
+
if (t.tool_name !== "host_screenshot" || t.status !== "awaiting_input")
|
|
11528
|
+
continue;
|
|
11529
|
+
if (!t.tool_call_id || !t.args?.url)
|
|
11530
|
+
continue;
|
|
11531
|
+
out.push({
|
|
11532
|
+
toolCallId: t.tool_call_id,
|
|
11533
|
+
url: t.args.url,
|
|
11534
|
+
fullPage: t.args.full_page !== false,
|
|
11535
|
+
waitMs: typeof t.args.wait_ms === "number" ? t.args.wait_ms : 500
|
|
11536
|
+
});
|
|
11537
|
+
}
|
|
11538
|
+
return out;
|
|
11539
|
+
}
|
|
11540
|
+
|
|
11352
11541
|
// src/commands/chat.ts
|
|
11542
|
+
async function resolveHostExec(sessionId, toolCallId, command, state) {
|
|
11543
|
+
const decision = decideFromJournal(await state.journal.lookup(sessionId, toolCallId));
|
|
11544
|
+
if (decision.kind === "replay")
|
|
11545
|
+
return decision.result;
|
|
11546
|
+
if (decision.kind === "crashed") {
|
|
11547
|
+
return {
|
|
11548
|
+
error: true,
|
|
11549
|
+
exit_code: -1,
|
|
11550
|
+
message: `A previous run did not complete; not re-running: ${decision.command}`
|
|
11551
|
+
};
|
|
11552
|
+
}
|
|
11553
|
+
if (!state.autoYes) {
|
|
11554
|
+
process.stderr.write(source_default.yellow(`
|
|
11555
|
+
[host_exec] refused (re-run with --yes to allow): ${command}
|
|
11556
|
+
`));
|
|
11557
|
+
return {
|
|
11558
|
+
declined: true,
|
|
11559
|
+
exit_code: -1,
|
|
11560
|
+
message: "Host execution declined; re-run with --yes to allow."
|
|
11561
|
+
};
|
|
11562
|
+
}
|
|
11563
|
+
process.stderr.write(source_default.dim(`
|
|
11564
|
+
[host_exec] $ ${command}
|
|
11565
|
+
(cwd: ${state.cwd})
|
|
11566
|
+
`));
|
|
11567
|
+
await state.journal.recordRunning(sessionId, toolCallId, command, new Date().toISOString());
|
|
11568
|
+
let result;
|
|
11569
|
+
try {
|
|
11570
|
+
result = await runHostCommand(command, toolCallId, { cwd: state.cwd });
|
|
11571
|
+
} catch (e) {
|
|
11572
|
+
result = { stdout: "", stderr: String(e), exit_code: -1 };
|
|
11573
|
+
}
|
|
11574
|
+
await state.journal.recordDone(sessionId, toolCallId, result);
|
|
11575
|
+
return result;
|
|
11576
|
+
}
|
|
11577
|
+
async function resolveHostScreenshot(ctx, sessionId, req, state) {
|
|
11578
|
+
if (!state.autoYes) {
|
|
11579
|
+
process.stderr.write(source_default.yellow(`
|
|
11580
|
+
[host_screenshot] refused (re-run with --yes to allow): ${req.url}
|
|
11581
|
+
`));
|
|
11582
|
+
return {
|
|
11583
|
+
declined: true,
|
|
11584
|
+
message: "Host screenshot declined; re-run with --yes to allow."
|
|
11585
|
+
};
|
|
11586
|
+
}
|
|
11587
|
+
process.stderr.write(source_default.dim(`
|
|
11588
|
+
[host_screenshot] ${req.url}
|
|
11589
|
+
`));
|
|
11590
|
+
let png;
|
|
11591
|
+
try {
|
|
11592
|
+
png = await captureScreenshot(req.url, {
|
|
11593
|
+
fullPage: req.fullPage,
|
|
11594
|
+
waitMs: req.waitMs
|
|
11595
|
+
});
|
|
11596
|
+
} catch (e) {
|
|
11597
|
+
return { error: true, message: `screenshot failed: ${String(e)}` };
|
|
11598
|
+
}
|
|
11599
|
+
try {
|
|
11600
|
+
const mint = await ctx.api.post(`/api/chat/${encodeURIComponent(sessionId)}/local-artifact-upload`, {
|
|
11601
|
+
content_type: "image/png"
|
|
11602
|
+
});
|
|
11603
|
+
const put = await fetch(mint.upload_url, {
|
|
11604
|
+
method: "PUT",
|
|
11605
|
+
headers: { "Content-Type": "image/png" },
|
|
11606
|
+
body: new Uint8Array(png)
|
|
11607
|
+
});
|
|
11608
|
+
if (!put.ok) {
|
|
11609
|
+
return {
|
|
11610
|
+
error: true,
|
|
11611
|
+
message: `image upload failed: HTTP ${put.status}`
|
|
11612
|
+
};
|
|
11613
|
+
}
|
|
11614
|
+
return {
|
|
11615
|
+
text: `Screenshot of ${req.url} captured (${png.length} bytes).`,
|
|
11616
|
+
_images: [{ storage_key: mint.storage_key }]
|
|
11617
|
+
};
|
|
11618
|
+
} catch (e) {
|
|
11619
|
+
return { error: true, message: `image upload failed: ${String(e)}` };
|
|
11620
|
+
}
|
|
11621
|
+
}
|
|
11622
|
+
async function _postToolResult(ctx, sessionId, toolCallId, result, label) {
|
|
11623
|
+
try {
|
|
11624
|
+
await ctx.api.post(`/api/chat/${encodeURIComponent(sessionId)}/tool-result`, { tool_call_id: toolCallId, result });
|
|
11625
|
+
} catch (e) {
|
|
11626
|
+
process.stderr.write(source_default.red(`[${label}] failed to POST result: ${String(e)}
|
|
11627
|
+
`));
|
|
11628
|
+
}
|
|
11629
|
+
}
|
|
11630
|
+
async function handleHostExecFrame(ctx, sessionId, data, state) {
|
|
11631
|
+
for (const req of parseHostExecRequests(data)) {
|
|
11632
|
+
const id = req.toolCallId;
|
|
11633
|
+
if (state.handled.has(id))
|
|
11634
|
+
continue;
|
|
11635
|
+
state.handled.add(id);
|
|
11636
|
+
const result = await resolveHostExec(sessionId, id, req.command, state);
|
|
11637
|
+
await _postToolResult(ctx, sessionId, id, result, "host_exec");
|
|
11638
|
+
}
|
|
11639
|
+
for (const req of parseHostScreenshotRequests(data)) {
|
|
11640
|
+
const id = req.toolCallId;
|
|
11641
|
+
if (state.handled.has(id))
|
|
11642
|
+
continue;
|
|
11643
|
+
state.handled.add(id);
|
|
11644
|
+
const result = await resolveHostScreenshot(ctx, sessionId, req, state);
|
|
11645
|
+
await _postToolResult(ctx, sessionId, id, result, "host_screenshot");
|
|
11646
|
+
}
|
|
11647
|
+
}
|
|
11353
11648
|
function parseSlashCommand(input) {
|
|
11354
11649
|
const trimmed = input.trim();
|
|
11355
11650
|
if (!trimmed.startsWith("/"))
|
|
@@ -11400,7 +11695,16 @@ async function sendMessage(ctx, sessionId, message, opts = {}) {
|
|
|
11400
11695
|
params.selected_agent = opts.agentName;
|
|
11401
11696
|
reqOpts.params = params;
|
|
11402
11697
|
}
|
|
11403
|
-
const
|
|
11698
|
+
const localExecState = opts.localExec ? {
|
|
11699
|
+
cwd: opts.localExec.cwd,
|
|
11700
|
+
autoYes: opts.localExec.autoYes,
|
|
11701
|
+
handled: new Set,
|
|
11702
|
+
journal: new Journal(opts.localExec.cwd)
|
|
11703
|
+
} : null;
|
|
11704
|
+
const reqBody = { content: message };
|
|
11705
|
+
if (opts.localExec)
|
|
11706
|
+
reqBody.capability_keys = ["local_exec"];
|
|
11707
|
+
const res = await ctx.api.postRaw(`/api/chat/${encodeURIComponent(sessionId)}`, reqBody, reqOpts);
|
|
11404
11708
|
const contentType = res.headers.get("content-type") ?? "";
|
|
11405
11709
|
let fullResponse = "";
|
|
11406
11710
|
if (contentType.includes("application/json")) {
|
|
@@ -11448,6 +11752,9 @@ async function sendMessage(ctx, sessionId, message, opts = {}) {
|
|
|
11448
11752
|
case "tool_status":
|
|
11449
11753
|
if (opts.onToolStatus)
|
|
11450
11754
|
opts.onToolStatus(event.data);
|
|
11755
|
+
if (localExecState) {
|
|
11756
|
+
await handleHostExecFrame(ctx, sessionId, event.data, localExecState);
|
|
11757
|
+
}
|
|
11451
11758
|
break;
|
|
11452
11759
|
case "clear":
|
|
11453
11760
|
fullResponse = "";
|
|
@@ -11668,7 +11975,7 @@ async function showHistoryPicker(ctx, rl) {
|
|
|
11668
11975
|
}
|
|
11669
11976
|
return data.items[idx].session_id;
|
|
11670
11977
|
}
|
|
11671
|
-
async function runRepl(ctx, initialSessionId, clientId, agentName, showTools, timeoutMs) {
|
|
11978
|
+
async function runRepl(ctx, initialSessionId, clientId, agentName, showTools, timeoutMs, localExec) {
|
|
11672
11979
|
let sessionId = initialSessionId;
|
|
11673
11980
|
process.stderr.write(source_default.dim(`martha v${CLI_VERSION} | session: ${sessionId.slice(0, 8)}...
|
|
11674
11981
|
`));
|
|
@@ -11805,6 +12112,7 @@ Cancelled.
|
|
|
11805
12112
|
try {
|
|
11806
12113
|
const result = await sendMessage(ctx, sessionId, input, {
|
|
11807
12114
|
timeoutMs,
|
|
12115
|
+
localExec,
|
|
11808
12116
|
onToken: (token) => process.stdout.write(stripper.push(token)),
|
|
11809
12117
|
onToolCall: showTools ? (data) => process.stderr.write(formatToolCall(data)) : undefined,
|
|
11810
12118
|
onToolResult: (data) => {
|
|
@@ -11860,7 +12168,7 @@ Error: ${err instanceof Error ? err.message : String(err)}
|
|
|
11860
12168
|
process.stderr.write(`
|
|
11861
12169
|
`);
|
|
11862
12170
|
}
|
|
11863
|
-
async function runOneShot(ctx, sessionId, message, isJson, clientId, agentName, showTools, timeoutMs) {
|
|
12171
|
+
async function runOneShot(ctx, sessionId, message, isJson, clientId, agentName, showTools, timeoutMs, localExec) {
|
|
11864
12172
|
const toolCalls = [];
|
|
11865
12173
|
function recordToolStatus(data) {
|
|
11866
12174
|
try {
|
|
@@ -11884,6 +12192,7 @@ async function runOneShot(ctx, sessionId, message, isJson, clientId, agentName,
|
|
|
11884
12192
|
clientId,
|
|
11885
12193
|
agentName,
|
|
11886
12194
|
timeoutMs,
|
|
12195
|
+
localExec,
|
|
11887
12196
|
onToolStatus: isJson ? recordToolStatus : showTools ? (data) => {
|
|
11888
12197
|
recordToolStatus(data);
|
|
11889
12198
|
process.stderr.write(formatToolStatus(data));
|
|
@@ -11907,7 +12216,7 @@ async function runOneShot(ctx, sessionId, message, isJson, clientId, agentName,
|
|
|
11907
12216
|
}
|
|
11908
12217
|
}
|
|
11909
12218
|
function registerChatCommand(program2) {
|
|
11910
|
-
program2.command("chat").description("Chat with a Martha agent").option("--session <id>", "Resume an existing session").option("--client <nameOrId>", "Use a specific client (name or ID)").option("--agent <name>", "Drive the chat with a specific copilot agent (defaults to profile.default_agent)").option("--message <text>", "Send a single message (non-interactive)").option("--show-tools", "Show tool calls and results during chat").option("--timeout <seconds>", "Per-message HTTP timeout in seconds (default: 600)").action(async (opts) => {
|
|
12219
|
+
program2.command("chat").description("Chat with a Martha agent").option("--session <id>", "Resume an existing session").option("--client <nameOrId>", "Use a specific client (name or ID)").option("--agent <name>", "Drive the chat with a specific copilot agent (defaults to profile.default_agent)").option("--message <text>", "Send a single message (non-interactive)").option("--show-tools", "Show tool calls and results during chat").option("--local-exec", "Allow the agent to run host tools on THIS machine (#568). Requires the server to have LOCAL_EXEC_ENABLED=1.").option("--yes", "Auto-approve host tool execution (required with --local-exec for non-interactive runs).").option("--timeout <seconds>", "Per-message HTTP timeout in seconds (default: 600)").action(async (opts) => {
|
|
11911
12220
|
const ctx = createContext({
|
|
11912
12221
|
profileOverride: program2.opts().profile,
|
|
11913
12222
|
verbose: program2.opts().verbose
|
|
@@ -11942,13 +12251,14 @@ function registerChatCommand(program2) {
|
|
|
11942
12251
|
}
|
|
11943
12252
|
const showTools = !!opts.showTools;
|
|
11944
12253
|
const timeoutMs = opts.timeout ? parseTimeoutSeconds(opts.timeout) * 1000 : undefined;
|
|
12254
|
+
const localExec = opts.localExec ? { cwd: process.cwd(), autoYes: !!opts.yes } : undefined;
|
|
11945
12255
|
if (opts.message) {
|
|
11946
|
-
await runOneShot(ctx, sessionId, opts.message, isJson, clientId, agentName, showTools, timeoutMs);
|
|
12256
|
+
await runOneShot(ctx, sessionId, opts.message, isJson, clientId, agentName, showTools, timeoutMs, localExec);
|
|
11947
12257
|
} else {
|
|
11948
12258
|
if (isJson) {
|
|
11949
12259
|
throw new CLIError("The --json flag requires --message for non-interactive mode.", 4 /* Validation */, 'Example: martha --json chat --message "hello"');
|
|
11950
12260
|
}
|
|
11951
|
-
await runRepl(ctx, sessionId, clientId, agentName, showTools, timeoutMs);
|
|
12261
|
+
await runRepl(ctx, sessionId, clientId, agentName, showTools, timeoutMs, localExec);
|
|
11952
12262
|
}
|
|
11953
12263
|
});
|
|
11954
12264
|
}
|
|
@@ -12294,20 +12604,27 @@ function registerConfigCommand(program2) {
|
|
|
12294
12604
|
}
|
|
12295
12605
|
|
|
12296
12606
|
// src/commands/definitions-apply.ts
|
|
12297
|
-
import
|
|
12298
|
-
import
|
|
12607
|
+
import fs6 from "node:fs";
|
|
12608
|
+
import path5 from "node:path";
|
|
12299
12609
|
init_dist();
|
|
12300
12610
|
init_errors();
|
|
12301
|
-
var VALID_KINDS = new Set([
|
|
12611
|
+
var VALID_KINDS = new Set([
|
|
12612
|
+
"Function",
|
|
12613
|
+
"Workflow",
|
|
12614
|
+
"Agent",
|
|
12615
|
+
"Trigger"
|
|
12616
|
+
]);
|
|
12302
12617
|
var KIND_TO_API_PATH = {
|
|
12303
12618
|
Function: "/api/admin/definitions/functions",
|
|
12304
12619
|
Workflow: "/api/admin/definitions/workflows",
|
|
12305
|
-
Agent: "/api/admin/definitions/agents"
|
|
12620
|
+
Agent: "/api/admin/definitions/agents",
|
|
12621
|
+
Trigger: "/api/admin/triggers"
|
|
12306
12622
|
};
|
|
12307
12623
|
var KIND_TO_PLURAL = {
|
|
12308
12624
|
Function: "Functions",
|
|
12309
12625
|
Workflow: "Workflows",
|
|
12310
|
-
Agent: "Agents"
|
|
12626
|
+
Agent: "Agents",
|
|
12627
|
+
Trigger: "Triggers"
|
|
12311
12628
|
};
|
|
12312
12629
|
var SERVER_GENERATED_FIELDS = new Set([
|
|
12313
12630
|
"id",
|
|
@@ -12330,20 +12647,20 @@ var SERVER_GENERATED_FIELDS = new Set([
|
|
|
12330
12647
|
]);
|
|
12331
12648
|
var AGENT_MANAGED_FIELDS = new Set(["functions", "workflows"]);
|
|
12332
12649
|
function loadDefinitions(inputPath) {
|
|
12333
|
-
const resolved =
|
|
12334
|
-
if (!
|
|
12650
|
+
const resolved = path5.resolve(inputPath);
|
|
12651
|
+
if (!fs6.existsSync(resolved)) {
|
|
12335
12652
|
throw new CLIError(`Path not found: ${inputPath}`, 1 /* Error */);
|
|
12336
12653
|
}
|
|
12337
|
-
const stat =
|
|
12654
|
+
const stat = fs6.statSync(resolved);
|
|
12338
12655
|
if (stat.isDirectory()) {
|
|
12339
12656
|
return loadDirectory(resolved);
|
|
12340
12657
|
}
|
|
12341
12658
|
return loadFile(resolved);
|
|
12342
12659
|
}
|
|
12343
12660
|
function loadDirectory(dirPath) {
|
|
12344
|
-
const entries =
|
|
12661
|
+
const entries = fs6.readdirSync(dirPath).sort();
|
|
12345
12662
|
const validExts = new Set([".yaml", ".yml", ".json"]);
|
|
12346
|
-
const files = entries.filter((e) => validExts.has(
|
|
12663
|
+
const files = entries.filter((e) => validExts.has(path5.extname(e).toLowerCase())).map((e) => path5.join(dirPath, e));
|
|
12347
12664
|
if (files.length === 0) {
|
|
12348
12665
|
throw new CLIError(`No YAML or JSON files found in ${dirPath}`, 4 /* Validation */);
|
|
12349
12666
|
}
|
|
@@ -12354,8 +12671,8 @@ function loadDirectory(dirPath) {
|
|
|
12354
12671
|
return results;
|
|
12355
12672
|
}
|
|
12356
12673
|
function loadFile(filePath) {
|
|
12357
|
-
const content =
|
|
12358
|
-
const ext =
|
|
12674
|
+
const content = fs6.readFileSync(filePath, "utf-8");
|
|
12675
|
+
const ext = path5.extname(filePath).toLowerCase();
|
|
12359
12676
|
if (ext === ".json") {
|
|
12360
12677
|
const parsed = parseJsonFile(content, filePath);
|
|
12361
12678
|
return [toLocalDefinition(parsed, filePath)];
|
|
@@ -12777,7 +13094,7 @@ Examples:
|
|
|
12777
13094
|
}
|
|
12778
13095
|
|
|
12779
13096
|
// src/commands/definitions-export.ts
|
|
12780
|
-
import
|
|
13097
|
+
import fs7 from "node:fs";
|
|
12781
13098
|
init_errors();
|
|
12782
13099
|
async function exportDefinitions(ctx, opts, isJsonMode) {
|
|
12783
13100
|
const params = {};
|
|
@@ -12793,7 +13110,7 @@ async function exportDefinitions(ctx, opts, isJsonMode) {
|
|
|
12793
13110
|
const text = await res.text();
|
|
12794
13111
|
if (opts.output) {
|
|
12795
13112
|
try {
|
|
12796
|
-
|
|
13113
|
+
fs7.writeFileSync(opts.output, text);
|
|
12797
13114
|
} catch (err) {
|
|
12798
13115
|
throw new CLIError(`Failed to write ${opts.output}: ${err instanceof Error ? err.message : String(err)}`, 1 /* Error */);
|
|
12799
13116
|
}
|
|
@@ -13513,8 +13830,8 @@ function registerProjectionCommands(parentCmd, getCtx, isJson) {
|
|
|
13513
13830
|
} catch {}
|
|
13514
13831
|
}
|
|
13515
13832
|
if (opts.output) {
|
|
13516
|
-
const
|
|
13517
|
-
await
|
|
13833
|
+
const fs8 = await import("node:fs/promises");
|
|
13834
|
+
await fs8.writeFile(opts.output, toWrite, "utf-8");
|
|
13518
13835
|
if (!isJson()) {
|
|
13519
13836
|
console.error(source_default.dim(`Wrote ${format} projection of '${name}' to ${opts.output}`));
|
|
13520
13837
|
}
|
|
@@ -13921,8 +14238,8 @@ Usage:
|
|
|
13921
14238
|
});
|
|
13922
14239
|
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) => {
|
|
13923
14240
|
const ctx = getCtx();
|
|
13924
|
-
const
|
|
13925
|
-
const current = await ctx.api.get(
|
|
14241
|
+
const path6 = `${API_PATH}/${encodeURIComponent(agent)}`;
|
|
14242
|
+
const current = await ctx.api.get(path6);
|
|
13926
14243
|
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;
|
|
13927
14244
|
if (!mutating) {
|
|
13928
14245
|
if (isJson()) {
|
|
@@ -13974,7 +14291,7 @@ Usage:
|
|
|
13974
14291
|
roots.delete(c);
|
|
13975
14292
|
body.self_grant_collection_roots = [...roots];
|
|
13976
14293
|
}
|
|
13977
|
-
const updated = await ctx.api.put(
|
|
14294
|
+
const updated = await ctx.api.put(path6, body);
|
|
13978
14295
|
if (isJson()) {
|
|
13979
14296
|
console.log(JSON.stringify(policyView(updated), null, 2));
|
|
13980
14297
|
return;
|
|
@@ -13986,8 +14303,120 @@ Usage:
|
|
|
13986
14303
|
}
|
|
13987
14304
|
};
|
|
13988
14305
|
|
|
14306
|
+
// src/commands/triggers.ts
|
|
14307
|
+
init_errors();
|
|
14308
|
+
function triggerKind(item) {
|
|
14309
|
+
if (item.schedule_config)
|
|
14310
|
+
return "schedule";
|
|
14311
|
+
if (item.deadman_config)
|
|
14312
|
+
return "deadman";
|
|
14313
|
+
return "event";
|
|
14314
|
+
}
|
|
14315
|
+
function scheduleSummary(sched) {
|
|
14316
|
+
return String(sched.cron ?? sched.interval ?? sched.spec ?? "schedule");
|
|
14317
|
+
}
|
|
14318
|
+
var triggersConfig = {
|
|
14319
|
+
typeName: "trigger",
|
|
14320
|
+
typeNamePlural: "triggers",
|
|
14321
|
+
apiPath: "/api/admin/triggers",
|
|
14322
|
+
extractList: (data) => {
|
|
14323
|
+
if (!Array.isArray(data)) {
|
|
14324
|
+
throw new Error("Unexpected API response shape for triggers list");
|
|
14325
|
+
}
|
|
14326
|
+
return data;
|
|
14327
|
+
},
|
|
14328
|
+
listColumns: [
|
|
14329
|
+
{ header: "NAME", accessor: (item) => String(item.name ?? "") },
|
|
14330
|
+
{ header: "KIND", accessor: (item) => triggerKind(item) },
|
|
14331
|
+
{
|
|
14332
|
+
header: "EVENT / SCHEDULE",
|
|
14333
|
+
accessor: (item) => {
|
|
14334
|
+
const sched = item.schedule_config;
|
|
14335
|
+
if (sched)
|
|
14336
|
+
return scheduleSummary(sched);
|
|
14337
|
+
return String(item.event_type ?? "-");
|
|
14338
|
+
}
|
|
14339
|
+
},
|
|
14340
|
+
{ header: "TARGET", accessor: (item) => String(item.target_name ?? "-") },
|
|
14341
|
+
{
|
|
14342
|
+
header: "STATUS",
|
|
14343
|
+
accessor: (item) => item.is_active !== false ? "active" : "inactive"
|
|
14344
|
+
}
|
|
14345
|
+
],
|
|
14346
|
+
renderDetail: (t) => {
|
|
14347
|
+
console.log(source_default.bold(`Trigger: ${t.name}
|
|
14348
|
+
`));
|
|
14349
|
+
if (t.description)
|
|
14350
|
+
console.log(` ${t.description}
|
|
14351
|
+
`);
|
|
14352
|
+
console.log(` Kind: ${triggerKind(t)}`);
|
|
14353
|
+
console.log(` Status: ${t.is_active !== false ? source_default.green("active") : source_default.dim("inactive")}`);
|
|
14354
|
+
if (t.event_type)
|
|
14355
|
+
console.log(` Event type: ${t.event_type}`);
|
|
14356
|
+
const filter = t.event_filter;
|
|
14357
|
+
if (filter && Object.keys(filter).length)
|
|
14358
|
+
console.log(` Event filter: ${JSON.stringify(filter)}`);
|
|
14359
|
+
const sched = t.schedule_config;
|
|
14360
|
+
if (sched)
|
|
14361
|
+
console.log(` Schedule: ${scheduleSummary(sched)} ${source_default.dim(JSON.stringify(sched))}`);
|
|
14362
|
+
const deadman = t.deadman_config;
|
|
14363
|
+
if (deadman)
|
|
14364
|
+
console.log(` Deadman: ${JSON.stringify(deadman)}`);
|
|
14365
|
+
console.log(` Target: ${t.target_type ?? "workflow"} → ${t.target_name ?? "-"}`);
|
|
14366
|
+
const mapping = t.input_mapping;
|
|
14367
|
+
if (mapping && Object.keys(mapping).length) {
|
|
14368
|
+
console.log(`
|
|
14369
|
+
${source_default.bold("Input mapping:")}`);
|
|
14370
|
+
for (const [k, v] of Object.entries(mapping)) {
|
|
14371
|
+
console.log(` ${k.padEnd(20)} ${source_default.dim(String(v))}`);
|
|
14372
|
+
}
|
|
14373
|
+
}
|
|
14374
|
+
if (t.dedup_key)
|
|
14375
|
+
console.log(`
|
|
14376
|
+
Dedup key: ${t.dedup_key}`);
|
|
14377
|
+
if (t.version)
|
|
14378
|
+
console.log(` Version: ${t.version}`);
|
|
14379
|
+
if (t.updated_at)
|
|
14380
|
+
console.log(` Updated: ${t.updated_at}`);
|
|
14381
|
+
},
|
|
14382
|
+
extraListOptions: (cmd) => {
|
|
14383
|
+
cmd.option("--active", "Only active triggers");
|
|
14384
|
+
},
|
|
14385
|
+
buildListParams: (opts) => {
|
|
14386
|
+
const params = {};
|
|
14387
|
+
if (opts.active)
|
|
14388
|
+
params.active = "true";
|
|
14389
|
+
return params;
|
|
14390
|
+
},
|
|
14391
|
+
extraCommands: (parentCmd, getCtx, isJson) => {
|
|
14392
|
+
parentCmd.command("test <name>").description("Dry-run a trigger against a sample event (no workflow started)").requiredOption("--event <json>", "Sample CloudEvent JSON to match against").action(async (name, opts) => {
|
|
14393
|
+
let event;
|
|
14394
|
+
try {
|
|
14395
|
+
event = JSON.parse(opts.event);
|
|
14396
|
+
} catch {
|
|
14397
|
+
throw new CLIError("--event must be valid JSON", 4 /* Validation */, `Example: martha triggers test ${name} --event '{"type":"facility_booking.booking.created","data":{}}'`);
|
|
14398
|
+
}
|
|
14399
|
+
const ctx = getCtx();
|
|
14400
|
+
const res = await ctx.api.post(`/api/admin/triggers/${encodeURIComponent(name)}/test`, { event });
|
|
14401
|
+
if (isJson()) {
|
|
14402
|
+
console.log(JSON.stringify(res, null, 2));
|
|
14403
|
+
return;
|
|
14404
|
+
}
|
|
14405
|
+
console.log(res.matched ? source_default.green(`✓ trigger '${name}' WOULD fire for this event`) : source_default.yellow(`✗ trigger '${name}' would NOT fire for this event`));
|
|
14406
|
+
console.log(` Event-type / filter match: ${res.filter_result ? source_default.green("yes") : source_default.red("no")}`);
|
|
14407
|
+
if (res.input_mapping_result) {
|
|
14408
|
+
console.log(`
|
|
14409
|
+
${source_default.bold("Resolved input mapping:")}`);
|
|
14410
|
+
for (const [k, v] of Object.entries(res.input_mapping_result)) {
|
|
14411
|
+
console.log(` ${k.padEnd(20)} ${source_default.dim(JSON.stringify(v))}`);
|
|
14412
|
+
}
|
|
14413
|
+
}
|
|
14414
|
+
});
|
|
14415
|
+
}
|
|
14416
|
+
};
|
|
14417
|
+
|
|
13989
14418
|
// src/commands/documents.ts
|
|
13990
|
-
import
|
|
14419
|
+
import fs8 from "node:fs";
|
|
13991
14420
|
init_errors();
|
|
13992
14421
|
var TERMINAL_STATUSES2 = new Set(["ready", "error"]);
|
|
13993
14422
|
var SPINNER_FRAMES2 = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
@@ -14289,7 +14718,7 @@ ${items.length} collections`));
|
|
|
14289
14718
|
});
|
|
14290
14719
|
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) => {
|
|
14291
14720
|
const ctx = getCtx();
|
|
14292
|
-
if (!
|
|
14721
|
+
if (!fs8.existsSync(filePath)) {
|
|
14293
14722
|
throw new CLIError(`File not found: ${filePath}`, 4 /* Validation */);
|
|
14294
14723
|
}
|
|
14295
14724
|
const result = await ctx.api.upload(`/api/admin/collections/${encodeURIComponent(collectionId)}/documents`, filePath);
|
|
@@ -15866,8 +16295,8 @@ ${data.length} spec(s)`));
|
|
|
15866
16295
|
Resources:
|
|
15867
16296
|
`));
|
|
15868
16297
|
for (const r of resources) {
|
|
15869
|
-
const
|
|
15870
|
-
console.log(` ${source_default.cyan(r.label || r.name)}` + source_default.dim(` → ${
|
|
16298
|
+
const path6 = r.path || `/${r.name}`;
|
|
16299
|
+
console.log(` ${source_default.cyan(r.label || r.name)}` + source_default.dim(` → ${path6}`));
|
|
15871
16300
|
}
|
|
15872
16301
|
}
|
|
15873
16302
|
try {
|
|
@@ -15887,14 +16316,14 @@ Functions:
|
|
|
15887
16316
|
console.log(source_default.dim(`
|
|
15888
16317
|
Proxy: martha integrations proxy ${name} GET /<path>`));
|
|
15889
16318
|
});
|
|
15890
|
-
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,
|
|
16319
|
+
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, path6, opts) => {
|
|
15891
16320
|
const ctx = getCtx();
|
|
15892
16321
|
const upperMethod = method.toUpperCase();
|
|
15893
16322
|
const allowed = new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
|
|
15894
16323
|
if (!allowed.has(upperMethod)) {
|
|
15895
16324
|
throw new CLIError(`Invalid method: ${method}`, 4 /* Validation */, "Allowed: GET, POST, PUT, PATCH, DELETE");
|
|
15896
16325
|
}
|
|
15897
|
-
const cleanPath =
|
|
16326
|
+
const cleanPath = path6.startsWith("/") ? path6.slice(1) : path6;
|
|
15898
16327
|
const proxyUrl = `/api/admin/plugins/${encodeURIComponent(name)}/${cleanPath}`;
|
|
15899
16328
|
const params = {};
|
|
15900
16329
|
if (opts.query) {
|
|
@@ -16104,8 +16533,8 @@ async function resolveCredentialValue(value) {
|
|
|
16104
16533
|
if (value === "-")
|
|
16105
16534
|
return readStdin();
|
|
16106
16535
|
if (value.startsWith("@")) {
|
|
16107
|
-
const
|
|
16108
|
-
return (await
|
|
16536
|
+
const fs9 = await import("node:fs/promises");
|
|
16537
|
+
return (await fs9.readFile(value.slice(1), "utf-8")).trim();
|
|
16109
16538
|
}
|
|
16110
16539
|
return value;
|
|
16111
16540
|
}
|
|
@@ -16220,8 +16649,8 @@ Notification connections`));
|
|
|
16220
16649
|
if (credentialValue === "-") {
|
|
16221
16650
|
credentialValue = await readStdin2();
|
|
16222
16651
|
} else if (credentialValue?.startsWith("@")) {
|
|
16223
|
-
const
|
|
16224
|
-
credentialValue = await
|
|
16652
|
+
const fs9 = await import("node:fs/promises");
|
|
16653
|
+
credentialValue = await fs9.readFile(credentialValue.slice(1), "utf-8");
|
|
16225
16654
|
}
|
|
16226
16655
|
if (!credentialValue) {
|
|
16227
16656
|
throw new Error("--credential-value is required (use '-' for stdin or '@path' for file).");
|
|
@@ -16271,11 +16700,11 @@ async function readStdin2() {
|
|
|
16271
16700
|
|
|
16272
16701
|
// src/commands/messaging.ts
|
|
16273
16702
|
init_errors();
|
|
16274
|
-
async function messagingFetch(baseUrl,
|
|
16275
|
-
if (/^(https?:)?\/\//i.test(
|
|
16703
|
+
async function messagingFetch(baseUrl, path6, opts) {
|
|
16704
|
+
if (/^(https?:)?\/\//i.test(path6)) {
|
|
16276
16705
|
throw new CLIError("Absolute URL paths are not allowed", 1 /* Error */);
|
|
16277
16706
|
}
|
|
16278
|
-
const url = new URL(
|
|
16707
|
+
const url = new URL(path6, baseUrl);
|
|
16279
16708
|
const headers = {
|
|
16280
16709
|
"Content-Type": "application/json"
|
|
16281
16710
|
};
|
|
@@ -17555,7 +17984,7 @@ ${models.length} models`));
|
|
|
17555
17984
|
}
|
|
17556
17985
|
|
|
17557
17986
|
// src/commands/wiki.ts
|
|
17558
|
-
import
|
|
17987
|
+
import fs9 from "node:fs";
|
|
17559
17988
|
init_errors();
|
|
17560
17989
|
function registerWikiCommands(program2) {
|
|
17561
17990
|
const cmd = program2.command("wiki").description("Manage tenant wiki pages, settings, schema, recompile (#245 D5.4)");
|
|
@@ -17602,14 +18031,14 @@ function registerWikiCommands(program2) {
|
|
|
17602
18031
|
console.log(source_default.dim(`
|
|
17603
18032
|
${pages.length} pages`));
|
|
17604
18033
|
});
|
|
17605
|
-
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 (
|
|
18034
|
+
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 (path6, opts) => {
|
|
17606
18035
|
const ctx = getCtx();
|
|
17607
|
-
const safe =
|
|
18036
|
+
const safe = path6.split("/").map(encodeURIComponent).join("/");
|
|
17608
18037
|
const resp = await ctx.api.getRaw(`/api/wiki/pages/${safe}`, {
|
|
17609
18038
|
headers: { Accept: "text/markdown,*/*" }
|
|
17610
18039
|
});
|
|
17611
18040
|
if (resp.status === 404) {
|
|
17612
|
-
throw new CLIError(`page not found: ${
|
|
18041
|
+
throw new CLIError(`page not found: ${path6}`, 3 /* NotFound */);
|
|
17613
18042
|
}
|
|
17614
18043
|
if (!resp.ok) {
|
|
17615
18044
|
const detail = await resp.text();
|
|
@@ -17617,16 +18046,16 @@ ${pages.length} pages`));
|
|
|
17617
18046
|
}
|
|
17618
18047
|
const body = await resp.text();
|
|
17619
18048
|
if (opts.out) {
|
|
17620
|
-
|
|
18049
|
+
fs9.writeFileSync(opts.out, body);
|
|
17621
18050
|
if (!isJson()) {
|
|
17622
18051
|
console.log(source_default.dim(`wrote ${body.length} bytes to ${opts.out}`));
|
|
17623
18052
|
} else {
|
|
17624
|
-
console.log(JSON.stringify({ path:
|
|
18053
|
+
console.log(JSON.stringify({ path: path6, bytes: body.length, file: opts.out }));
|
|
17625
18054
|
}
|
|
17626
18055
|
return;
|
|
17627
18056
|
}
|
|
17628
18057
|
if (isJson()) {
|
|
17629
|
-
console.log(JSON.stringify({ path:
|
|
18058
|
+
console.log(JSON.stringify({ path: path6, body, etag: resp.headers.get("ETag") }));
|
|
17630
18059
|
} else {
|
|
17631
18060
|
process.stdout.write(body);
|
|
17632
18061
|
}
|
|
@@ -17721,10 +18150,10 @@ ${pages.length} pages`));
|
|
|
17721
18150
|
}
|
|
17722
18151
|
if (opts.compilePromptOverrideFile) {
|
|
17723
18152
|
const file = String(opts.compilePromptOverrideFile);
|
|
17724
|
-
if (!
|
|
18153
|
+
if (!fs9.existsSync(file)) {
|
|
17725
18154
|
throw new CLIError(`override file not found: ${file}`, 3 /* NotFound */);
|
|
17726
18155
|
}
|
|
17727
|
-
const content =
|
|
18156
|
+
const content = fs9.readFileSync(file, "utf8");
|
|
17728
18157
|
const bytes = Buffer.byteLength(content, "utf8");
|
|
17729
18158
|
if (bytes > 16 * 1024) {
|
|
17730
18159
|
throw new CLIError(`compile prompt override exceeds 16 KB (${bytes} bytes)`, 4 /* Validation */);
|
|
@@ -17748,7 +18177,7 @@ ${pages.length} pages`));
|
|
|
17748
18177
|
const ctx = getCtx();
|
|
17749
18178
|
const resp = await ctx.api.get("/api/wiki/schema");
|
|
17750
18179
|
if (opts.out) {
|
|
17751
|
-
|
|
18180
|
+
fs9.writeFileSync(opts.out, resp.body);
|
|
17752
18181
|
if (!isJson()) {
|
|
17753
18182
|
console.log(source_default.dim(`wrote ${resp.body.length} bytes to ${opts.out}`));
|
|
17754
18183
|
} else {
|
|
@@ -17764,10 +18193,10 @@ ${pages.length} pages`));
|
|
|
17764
18193
|
});
|
|
17765
18194
|
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) => {
|
|
17766
18195
|
const ctx = getCtx();
|
|
17767
|
-
if (!
|
|
18196
|
+
if (!fs9.existsSync(opts.file)) {
|
|
17768
18197
|
throw new CLIError(`schema file not found: ${opts.file}`, 3 /* NotFound */);
|
|
17769
18198
|
}
|
|
17770
|
-
const body =
|
|
18199
|
+
const body = fs9.readFileSync(opts.file, "utf8");
|
|
17771
18200
|
const bytes = Buffer.byteLength(body, "utf8");
|
|
17772
18201
|
if (bytes > 64 * 1024) {
|
|
17773
18202
|
throw new CLIError(`schema body exceeds 64 KB (${bytes} bytes)`, 4 /* Validation */);
|
|
@@ -18293,8 +18722,8 @@ async function resolveCredentialFlag(value) {
|
|
|
18293
18722
|
return Buffer.concat(chunks).toString("utf-8").trim();
|
|
18294
18723
|
}
|
|
18295
18724
|
if (value.startsWith("@")) {
|
|
18296
|
-
const
|
|
18297
|
-
return (await
|
|
18725
|
+
const fs10 = await import("node:fs/promises");
|
|
18726
|
+
return (await fs10.readFile(value.slice(1), "utf-8")).trim();
|
|
18298
18727
|
}
|
|
18299
18728
|
return value;
|
|
18300
18729
|
}
|
|
@@ -19480,19 +19909,19 @@ function registerDoctorCommand(program2) {
|
|
|
19480
19909
|
|
|
19481
19910
|
// src/commands/skill.ts
|
|
19482
19911
|
init_errors();
|
|
19483
|
-
import
|
|
19484
|
-
import
|
|
19912
|
+
import fs10 from "node:fs";
|
|
19913
|
+
import path6 from "node:path";
|
|
19485
19914
|
import { fileURLToPath } from "node:url";
|
|
19486
19915
|
function locateSkill() {
|
|
19487
|
-
const here =
|
|
19916
|
+
const here = path6.dirname(fileURLToPath(import.meta.url));
|
|
19488
19917
|
const candidates = [
|
|
19489
|
-
|
|
19490
|
-
|
|
19491
|
-
|
|
19492
|
-
|
|
19918
|
+
path6.join(here, "skills", "martha-cli", "SKILL.md"),
|
|
19919
|
+
path6.join(here, "..", "skills", "martha-cli", "SKILL.md"),
|
|
19920
|
+
path6.join(here, "..", "..", "..", "skills", "martha-cli", "SKILL.md"),
|
|
19921
|
+
path6.join(here, "..", "..", "skills", "martha-cli", "SKILL.md")
|
|
19493
19922
|
];
|
|
19494
19923
|
for (const p of candidates) {
|
|
19495
|
-
if (
|
|
19924
|
+
if (fs10.existsSync(p))
|
|
19496
19925
|
return p;
|
|
19497
19926
|
}
|
|
19498
19927
|
return null;
|
|
@@ -19502,7 +19931,7 @@ async function skillCommand() {
|
|
|
19502
19931
|
if (!skillPath) {
|
|
19503
19932
|
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 */);
|
|
19504
19933
|
}
|
|
19505
|
-
const body =
|
|
19934
|
+
const body = fs10.readFileSync(skillPath, "utf-8");
|
|
19506
19935
|
process.stdout.write(body);
|
|
19507
19936
|
}
|
|
19508
19937
|
function registerSkillCommand(program2) {
|
|
@@ -19687,6 +20116,7 @@ registerConfigCommand(program2);
|
|
|
19687
20116
|
registerDefinitionCommands(program2, functionsConfig);
|
|
19688
20117
|
registerDefinitionCommands(program2, workflowsConfig);
|
|
19689
20118
|
registerDefinitionCommands(program2, agentsConfig);
|
|
20119
|
+
registerDefinitionCommands(program2, triggersConfig);
|
|
19690
20120
|
registerDefinitionsApply(program2);
|
|
19691
20121
|
registerDefinitionsExport(program2);
|
|
19692
20122
|
registerDocumentCommands(program2);
|