@aiaiai-pt/martha-cli 0.22.0 → 0.24.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.
Files changed (2) hide show
  1. package/dist/index.js +745 -94
  2. 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.22.0";
11080
+ var CLI_VERSION = "0.24.0";
11081
11081
 
11082
11082
  // src/commands/sessions.ts
11083
11083
  function relativeTime(iso) {
@@ -11538,7 +11538,387 @@ function parseHostScreenshotRequests(data) {
11538
11538
  return out;
11539
11539
  }
11540
11540
 
11541
+ // src/lib/host-browser.ts
11542
+ var DOM_CAP = 16 * 1024;
11543
+ var CONSOLE_CAP = 200;
11544
+ function parseHostBrowserRequests(data) {
11545
+ let tools;
11546
+ try {
11547
+ tools = JSON.parse(data);
11548
+ } catch {
11549
+ return [];
11550
+ }
11551
+ if (!Array.isArray(tools))
11552
+ return [];
11553
+ const valid = [
11554
+ "navigate",
11555
+ "click",
11556
+ "read_console",
11557
+ "read_dom",
11558
+ "screenshot"
11559
+ ];
11560
+ const out = [];
11561
+ for (const t of tools) {
11562
+ if (t.tool_name !== "host_browser" || t.status !== "awaiting_input")
11563
+ continue;
11564
+ if (!t.tool_call_id)
11565
+ continue;
11566
+ const action = t.args?.action;
11567
+ if (!action || !valid.includes(action))
11568
+ continue;
11569
+ out.push({
11570
+ toolCallId: t.tool_call_id,
11571
+ action,
11572
+ url: t.args?.url,
11573
+ selector: t.args?.selector,
11574
+ waitMs: typeof t.args?.wait_ms === "number" ? t.args.wait_ms : 500,
11575
+ fullPage: t.args?.full_page !== false
11576
+ });
11577
+ }
11578
+ return out;
11579
+ }
11580
+ async function applyBrowserAction(page, consoleBuffer, req) {
11581
+ try {
11582
+ switch (req.action) {
11583
+ case "navigate": {
11584
+ if (!req.url)
11585
+ return { error: true, message: "navigate requires `url`" };
11586
+ await page.goto(req.url, { waitUntil: "load", timeout: 30000 });
11587
+ if (req.waitMs > 0)
11588
+ await page.waitForTimeout(req.waitMs);
11589
+ const title = await page.title();
11590
+ const url = page.url();
11591
+ return { text: `Navigated to ${url} (title: ${title || "<none>"}).` };
11592
+ }
11593
+ case "click": {
11594
+ if (!req.selector)
11595
+ return { error: true, message: "click requires `selector`" };
11596
+ await page.click(req.selector, { timeout: 1e4 });
11597
+ if (req.waitMs > 0)
11598
+ await page.waitForTimeout(req.waitMs);
11599
+ return { text: `Clicked ${req.selector}.` };
11600
+ }
11601
+ case "read_console": {
11602
+ if (req.waitMs > 0)
11603
+ await page.waitForTimeout(req.waitMs);
11604
+ const entries = consoleBuffer.slice(-CONSOLE_CAP);
11605
+ if (entries.length === 0)
11606
+ return {
11607
+ text: "No console messages, page errors, or failed requests captured yet."
11608
+ };
11609
+ const lines = entries.map((e) => {
11610
+ if (e.kind === "console")
11611
+ return `[console.${e.level}] ${e.text}`;
11612
+ if (e.kind === "pageerror")
11613
+ return `[pageerror] ${e.text}`;
11614
+ return `[requestfailed] ${e.url} — ${e.text}`;
11615
+ });
11616
+ return {
11617
+ text: `Captured ${entries.length} console/error/network entries:
11618
+ ${lines.join(`
11619
+ `)}`
11620
+ };
11621
+ }
11622
+ case "read_dom": {
11623
+ if (req.waitMs > 0)
11624
+ await page.waitForTimeout(req.waitMs);
11625
+ let html;
11626
+ if (req.selector) {
11627
+ html = await page.evaluate((sel) => {
11628
+ const el = document.querySelector(sel);
11629
+ return el ? el.outerHTML : `__NOT_FOUND__:${sel}`;
11630
+ }, req.selector);
11631
+ if (typeof html === "string" && html.startsWith("__NOT_FOUND__:"))
11632
+ return {
11633
+ error: true,
11634
+ message: `No element matched selector: ${req.selector}`
11635
+ };
11636
+ } else {
11637
+ html = await page.content();
11638
+ }
11639
+ const truncated = html.length > DOM_CAP;
11640
+ return {
11641
+ text: (truncated ? html.slice(0, DOM_CAP) + `
11642
+ …[truncated]` : html) || ""
11643
+ };
11644
+ }
11645
+ case "screenshot": {
11646
+ if (req.waitMs > 0)
11647
+ await page.waitForTimeout(req.waitMs);
11648
+ const buf = await page.screenshot({
11649
+ fullPage: req.fullPage,
11650
+ type: "png"
11651
+ });
11652
+ return { png: buf };
11653
+ }
11654
+ default:
11655
+ return { error: true, message: `unknown action: ${req.action}` };
11656
+ }
11657
+ } catch (e) {
11658
+ return { error: true, message: `${req.action} failed: ${String(e)}` };
11659
+ }
11660
+ }
11661
+
11662
+ class BrowserSession {
11663
+ browser = null;
11664
+ page = null;
11665
+ consoleBuffer = [];
11666
+ async ensurePage() {
11667
+ if (this.page)
11668
+ return this.page;
11669
+ const moduleName = "playwright";
11670
+ let chromium;
11671
+ try {
11672
+ ({ chromium } = await import(moduleName));
11673
+ } catch {
11674
+ throw new Error("playwright is not installed on the runner. Install it to use host_browser: `npm i -D playwright && npx playwright install chromium`");
11675
+ }
11676
+ this.browser = await chromium.launch({ headless: true });
11677
+ this.page = await this.browser.newPage();
11678
+ this.page.on("console", (msg) => this.consoleBuffer.push({
11679
+ kind: "console",
11680
+ level: msg.type(),
11681
+ text: msg.text()
11682
+ }));
11683
+ this.page.on("pageerror", (err) => this.consoleBuffer.push({
11684
+ kind: "pageerror",
11685
+ text: String(err?.message ?? err)
11686
+ }));
11687
+ this.page.on("requestfailed", (request) => this.consoleBuffer.push({
11688
+ kind: "requestfailed",
11689
+ url: request.url(),
11690
+ text: request.failure()?.errorText ?? "failed"
11691
+ }));
11692
+ return this.page;
11693
+ }
11694
+ async run(req) {
11695
+ let page;
11696
+ try {
11697
+ page = await this.ensurePage();
11698
+ } catch (e) {
11699
+ return { error: true, message: String(e) };
11700
+ }
11701
+ return applyBrowserAction(page, this.consoleBuffer, req);
11702
+ }
11703
+ async close() {
11704
+ if (this.browser) {
11705
+ try {
11706
+ await this.browser.close();
11707
+ } catch {}
11708
+ this.browser = null;
11709
+ this.page = null;
11710
+ }
11711
+ }
11712
+ }
11713
+
11714
+ // src/lib/host-files.ts
11715
+ import { promises as fs6 } from "node:fs";
11716
+ import * as path5 from "node:path";
11717
+ var READ_CAP = 256 * 1024;
11718
+ function resolvePath(cwd, p) {
11719
+ return path5.isAbsolute(p) ? p : path5.resolve(cwd, p);
11720
+ }
11721
+ async function applyFileAction(cwd, req) {
11722
+ if (!req.path)
11723
+ return { error: true, message: `${req.action} requires \`path\`` };
11724
+ const abs = resolvePath(cwd, req.path);
11725
+ try {
11726
+ switch (req.action) {
11727
+ case "host_read": {
11728
+ const raw = await fs6.readFile(abs, "utf8");
11729
+ let body = raw;
11730
+ if (typeof req.offset === "number" || typeof req.limit === "number") {
11731
+ const lines = raw.split(`
11732
+ `);
11733
+ const start = Math.max(0, (req.offset ?? 1) - 1);
11734
+ const end = typeof req.limit === "number" ? start + req.limit : lines.length;
11735
+ body = lines.slice(start, end).join(`
11736
+ `);
11737
+ }
11738
+ const truncated = body.length > READ_CAP;
11739
+ if (truncated)
11740
+ body = body.slice(0, READ_CAP) + `
11741
+ …[truncated]`;
11742
+ return { text: body === "" ? "(empty file)" : body };
11743
+ }
11744
+ case "host_write": {
11745
+ if (typeof req.content !== "string")
11746
+ return { error: true, message: "host_write requires `content`" };
11747
+ await fs6.mkdir(path5.dirname(abs), { recursive: true });
11748
+ await fs6.writeFile(abs, req.content, "utf8");
11749
+ return {
11750
+ text: `Wrote ${Buffer.byteLength(req.content, "utf8")} bytes to ${req.path}.`
11751
+ };
11752
+ }
11753
+ case "host_edit": {
11754
+ if (typeof req.oldString !== "string" || typeof req.newString !== "string")
11755
+ return {
11756
+ error: true,
11757
+ message: "host_edit requires `old_string` and `new_string`"
11758
+ };
11759
+ if (req.oldString === req.newString)
11760
+ return {
11761
+ error: true,
11762
+ message: "old_string and new_string are identical"
11763
+ };
11764
+ const raw = await fs6.readFile(abs, "utf8");
11765
+ const count = req.oldString ? raw.split(req.oldString).length - 1 : 0;
11766
+ if (count === 0)
11767
+ return { error: true, message: "old_string not found in file" };
11768
+ if (count > 1 && !req.replaceAll)
11769
+ return {
11770
+ error: true,
11771
+ message: `old_string is not unique (${count} matches); pass replace_all=true to replace all`
11772
+ };
11773
+ const next = req.replaceAll ? raw.split(req.oldString).join(req.newString) : raw.replace(req.oldString, req.newString);
11774
+ await fs6.writeFile(abs, next, "utf8");
11775
+ return {
11776
+ text: `Edited ${req.path} (${req.replaceAll ? count : 1} replacement${(req.replaceAll ? count : 1) === 1 ? "" : "s"}).`
11777
+ };
11778
+ }
11779
+ default:
11780
+ return { error: true, message: `unknown file action: ${req.action}` };
11781
+ }
11782
+ } catch (e) {
11783
+ return { error: true, message: `${req.action} failed: ${String(e)}` };
11784
+ }
11785
+ }
11786
+ var VALID = ["host_read", "host_write", "host_edit"];
11787
+ function parseHostFileRequests(data) {
11788
+ let tools;
11789
+ try {
11790
+ tools = JSON.parse(data);
11791
+ } catch {
11792
+ return [];
11793
+ }
11794
+ if (!Array.isArray(tools))
11795
+ return [];
11796
+ const out = [];
11797
+ for (const t of tools) {
11798
+ if (t.status !== "awaiting_input" || !t.tool_call_id)
11799
+ continue;
11800
+ if (!VALID.includes(t.tool_name))
11801
+ continue;
11802
+ const a = t.args ?? {};
11803
+ out.push({
11804
+ toolCallId: t.tool_call_id,
11805
+ action: t.tool_name,
11806
+ path: a.path,
11807
+ offset: typeof a.offset === "number" ? a.offset : undefined,
11808
+ limit: typeof a.limit === "number" ? a.limit : undefined,
11809
+ content: typeof a.content === "string" ? a.content : undefined,
11810
+ oldString: typeof a.old_string === "string" ? a.old_string : undefined,
11811
+ newString: typeof a.new_string === "string" ? a.new_string : undefined,
11812
+ replaceAll: a.replace_all === true
11813
+ });
11814
+ }
11815
+ return out;
11816
+ }
11817
+
11818
+ // src/lib/local-agent.ts
11819
+ import { spawn as spawn2 } from "node:child_process";
11820
+ var DEFAULT_LOCAL_AGENT_CMD = "codex exec";
11821
+ var DEFAULT_LOCAL_AGENT_TIMEOUT_MS = 30 * 60 * 1000;
11822
+ var MAX_OUTPUT2 = 256 * 1024;
11823
+ function buildAgentArgv(agentCmd, task) {
11824
+ const parts = agentCmd.trim().split(/\s+/).filter(Boolean);
11825
+ if (parts.length === 0)
11826
+ throw new Error("MARTHA_LOCAL_AGENT_CMD is empty");
11827
+ return [...parts, task];
11828
+ }
11829
+ async function runLocalAgent(task, opts) {
11830
+ const argv = buildAgentArgv(opts.agentCmd, task);
11831
+ const cap = opts.maxOutputBytes ?? MAX_OUTPUT2;
11832
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_LOCAL_AGENT_TIMEOUT_MS;
11833
+ return await new Promise((resolve2, reject) => {
11834
+ let out = "";
11835
+ let settled = false;
11836
+ let child;
11837
+ try {
11838
+ child = spawn2(argv[0], argv.slice(1), {
11839
+ cwd: opts.cwd,
11840
+ shell: false,
11841
+ stdio: ["ignore", "pipe", "pipe"]
11842
+ });
11843
+ } catch (e) {
11844
+ reject(e);
11845
+ return;
11846
+ }
11847
+ const append = (chunk) => {
11848
+ if (out.length < cap)
11849
+ out += chunk.toString("utf8");
11850
+ };
11851
+ child.stdout?.on("data", append);
11852
+ child.stderr?.on("data", append);
11853
+ const timer = setTimeout(() => {
11854
+ if (settled)
11855
+ return;
11856
+ settled = true;
11857
+ child.kill("SIGKILL");
11858
+ resolve2({
11859
+ text: out.slice(0, cap),
11860
+ exit_code: -1,
11861
+ timed_out: true
11862
+ });
11863
+ }, timeoutMs);
11864
+ child.on("error", (e) => {
11865
+ if (settled)
11866
+ return;
11867
+ settled = true;
11868
+ clearTimeout(timer);
11869
+ reject(e);
11870
+ });
11871
+ child.on("close", (code) => {
11872
+ if (settled)
11873
+ return;
11874
+ settled = true;
11875
+ clearTimeout(timer);
11876
+ resolve2({ text: out.slice(0, cap), exit_code: code ?? -1 });
11877
+ });
11878
+ });
11879
+ }
11880
+ function parseLocalAgentRequests(data) {
11881
+ let tools;
11882
+ try {
11883
+ tools = JSON.parse(data);
11884
+ } catch {
11885
+ return [];
11886
+ }
11887
+ if (!Array.isArray(tools))
11888
+ return [];
11889
+ const out = [];
11890
+ for (const t of tools) {
11891
+ if (t.tool_name !== "local_agent" || t.status !== "awaiting_input")
11892
+ continue;
11893
+ if (!t.tool_call_id || !t.args?.task)
11894
+ continue;
11895
+ out.push({
11896
+ toolCallId: t.tool_call_id,
11897
+ task: t.args.task,
11898
+ cwd: typeof t.args.cwd === "string" ? t.args.cwd : undefined
11899
+ });
11900
+ }
11901
+ return out;
11902
+ }
11903
+
11541
11904
  // src/commands/chat.ts
11905
+ async function uploadImageArtifact(ctx, sessionId, png) {
11906
+ try {
11907
+ const mint = await ctx.api.post(`/api/chat/${encodeURIComponent(sessionId)}/local-artifact-upload`, {
11908
+ content_type: "image/png"
11909
+ });
11910
+ const put = await fetch(mint.upload_url, {
11911
+ method: "PUT",
11912
+ headers: { "Content-Type": "image/png" },
11913
+ body: new Uint8Array(png)
11914
+ });
11915
+ if (!put.ok)
11916
+ return { error: `image upload failed: HTTP ${put.status}` };
11917
+ return { storage_key: mint.storage_key };
11918
+ } catch (e) {
11919
+ return { error: `image upload failed: ${String(e)}` };
11920
+ }
11921
+ }
11542
11922
  async function resolveHostExec(sessionId, toolCallId, command, state) {
11543
11923
  const decision = decideFromJournal(await state.journal.lookup(sessionId, toolCallId));
11544
11924
  if (decision.kind === "replay")
@@ -11596,27 +11976,89 @@ async function resolveHostScreenshot(ctx, sessionId, req, state) {
11596
11976
  } catch (e) {
11597
11977
  return { error: true, message: `screenshot failed: ${String(e)}` };
11598
11978
  }
11979
+ const uploaded = await uploadImageArtifact(ctx, sessionId, png);
11980
+ if ("error" in uploaded)
11981
+ return { error: true, message: uploaded.error };
11982
+ return {
11983
+ text: `Screenshot of ${req.url} captured (${png.length} bytes).`,
11984
+ _images: [{ storage_key: uploaded.storage_key }]
11985
+ };
11986
+ }
11987
+ async function resolveHostBrowser(ctx, sessionId, req, state) {
11988
+ if (!state.autoYes) {
11989
+ process.stderr.write(source_default.yellow(`
11990
+ [host_browser] refused (re-run with --yes to allow): ${req.action}${req.url ? ` ${req.url}` : ""}
11991
+ `));
11992
+ return {
11993
+ declined: true,
11994
+ message: "Host browser declined; re-run with --yes to allow."
11995
+ };
11996
+ }
11997
+ process.stderr.write(source_default.dim(`
11998
+ [host_browser] ${req.action}${req.url ? ` ${req.url}` : ""}${req.selector ? ` (${req.selector})` : ""}
11999
+ `));
12000
+ if (!state.browser)
12001
+ state.browser = new BrowserSession;
12002
+ const result = await state.browser.run(req);
12003
+ if ("error" in result)
12004
+ return { error: true, message: result.message };
12005
+ if ("png" in result) {
12006
+ const uploaded = await uploadImageArtifact(ctx, sessionId, result.png);
12007
+ if ("error" in uploaded)
12008
+ return { error: true, message: uploaded.error };
12009
+ return {
12010
+ text: `Screenshot of the current page captured (${result.png.length} bytes).`,
12011
+ _images: [{ storage_key: uploaded.storage_key }]
12012
+ };
12013
+ }
12014
+ return result;
12015
+ }
12016
+ async function resolveHostFile(req, state) {
12017
+ const mutating = req.action === "host_write" || req.action === "host_edit";
12018
+ if (mutating && !state.autoYes) {
12019
+ process.stderr.write(source_default.yellow(`
12020
+ [${req.action}] refused (re-run with --yes to allow): ${req.path}
12021
+ `));
12022
+ return {
12023
+ declined: true,
12024
+ message: `${req.action} declined; re-run with --yes to allow.`
12025
+ };
12026
+ }
12027
+ process.stderr.write(source_default.dim(`
12028
+ [${req.action}] ${req.path ?? "<no path>"}
12029
+ `));
12030
+ const result = await applyFileAction(state.cwd, req);
12031
+ if ("error" in result)
12032
+ return { error: true, message: result.message };
12033
+ return result;
12034
+ }
12035
+ async function resolveLocalAgent(req, state) {
12036
+ if (!state.autoYes) {
12037
+ process.stderr.write(source_default.yellow(`
12038
+ [local_agent] refused (re-run with --yes to allow): ${req.task.slice(0, 80)}
12039
+ `));
12040
+ return {
12041
+ declined: true,
12042
+ message: "Local agent delegation declined; re-run with --yes to allow."
12043
+ };
12044
+ }
12045
+ const agentCmd = process.env.MARTHA_LOCAL_AGENT_CMD || DEFAULT_LOCAL_AGENT_CMD;
12046
+ const cwd = req.cwd ? `${state.cwd}/${req.cwd}` : state.cwd;
12047
+ process.stderr.write(source_default.dim(`
12048
+ [local_agent] (${agentCmd}) ${req.task.slice(0, 100)}
12049
+ `));
11599
12050
  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
- }
12051
+ const result = await runLocalAgent(req.task, { cwd, agentCmd });
11614
12052
  return {
11615
- text: `Screenshot of ${req.url} captured (${png.length} bytes).`,
11616
- _images: [{ storage_key: mint.storage_key }]
12053
+ text: result.text || "(local agent produced no output)",
12054
+ exit_code: result.exit_code,
12055
+ ...result.timed_out ? { timed_out: true } : {}
11617
12056
  };
11618
12057
  } catch (e) {
11619
- return { error: true, message: `image upload failed: ${String(e)}` };
12058
+ return {
12059
+ error: true,
12060
+ message: `local agent failed to start (${agentCmd}): ${String(e)}. ` + `Set MARTHA_LOCAL_AGENT_CMD to an installed agent command.`
12061
+ };
11620
12062
  }
11621
12063
  }
11622
12064
  async function _postToolResult(ctx, sessionId, toolCallId, result, label) {
@@ -11644,6 +12086,30 @@ async function handleHostExecFrame(ctx, sessionId, data, state) {
11644
12086
  const result = await resolveHostScreenshot(ctx, sessionId, req, state);
11645
12087
  await _postToolResult(ctx, sessionId, id, result, "host_screenshot");
11646
12088
  }
12089
+ for (const req of parseHostBrowserRequests(data)) {
12090
+ const id = req.toolCallId;
12091
+ if (state.handled.has(id))
12092
+ continue;
12093
+ state.handled.add(id);
12094
+ const result = await resolveHostBrowser(ctx, sessionId, req, state);
12095
+ await _postToolResult(ctx, sessionId, id, result, "host_browser");
12096
+ }
12097
+ for (const req of parseHostFileRequests(data)) {
12098
+ const id = req.toolCallId;
12099
+ if (state.handled.has(id))
12100
+ continue;
12101
+ state.handled.add(id);
12102
+ const result = await resolveHostFile(req, state);
12103
+ await _postToolResult(ctx, sessionId, id, result, req.action);
12104
+ }
12105
+ for (const req of parseLocalAgentRequests(data)) {
12106
+ const id = req.toolCallId;
12107
+ if (state.handled.has(id))
12108
+ continue;
12109
+ state.handled.add(id);
12110
+ const result = await resolveLocalAgent(req, state);
12111
+ await _postToolResult(ctx, sessionId, id, result, "local_agent");
12112
+ }
11647
12113
  }
11648
12114
  function parseSlashCommand(input) {
11649
12115
  const trimmed = input.trim();
@@ -11768,6 +12234,9 @@ async function sendMessage(ctx, sessionId, message, opts = {}) {
11768
12234
  return { response: fullResponse, aborted: true };
11769
12235
  }
11770
12236
  throw err;
12237
+ } finally {
12238
+ if (localExecState?.browser)
12239
+ await localExecState.browser.close();
11771
12240
  }
11772
12241
  } else {
11773
12242
  const text = await res.text();
@@ -11818,11 +12287,11 @@ function formatToolStatus(data) {
11818
12287
  }
11819
12288
  }
11820
12289
  function promptPick(rl, count) {
11821
- return new Promise((resolve) => {
12290
+ return new Promise((resolve2) => {
11822
12291
  rl.question(source_default.dim(`Pick 1-${count} (or Enter to cancel): `), (ans) => {
11823
12292
  const trimmed = ans.trim();
11824
12293
  if (!trimmed) {
11825
- resolve(-1);
12294
+ resolve2(-1);
11826
12295
  return;
11827
12296
  }
11828
12297
  const n = parseInt(trimmed, 10);
@@ -11830,10 +12299,10 @@ function promptPick(rl, count) {
11830
12299
  process.stderr.write(source_default.yellow(`Invalid choice.
11831
12300
 
11832
12301
  `));
11833
- resolve(-1);
12302
+ resolve2(-1);
11834
12303
  return;
11835
12304
  }
11836
- resolve(n - 1);
12305
+ resolve2(n - 1);
11837
12306
  });
11838
12307
  });
11839
12308
  }
@@ -12276,9 +12745,9 @@ init_errors();
12276
12745
  import { createInterface as createInterface3 } from "node:readline";
12277
12746
  function prompt(rl, question, defaultValue) {
12278
12747
  const suffix = defaultValue ? ` [${defaultValue}]` : "";
12279
- return new Promise((resolve) => {
12748
+ return new Promise((resolve2) => {
12280
12749
  rl.question(`${question}${suffix}: `, (answer) => {
12281
- resolve(answer.trim() || defaultValue || "");
12750
+ resolve2(answer.trim() || defaultValue || "");
12282
12751
  });
12283
12752
  });
12284
12753
  }
@@ -12574,14 +13043,14 @@ function registerConfigCommand(program2) {
12574
13043
  if (!process.stdin.isTTY) {
12575
13044
  throw new CLIError("Cannot confirm in non-interactive mode.", 4 /* Validation */, "Use --yes to skip confirmation.");
12576
13045
  }
12577
- const ok = await new Promise((resolve) => {
13046
+ const ok = await new Promise((resolve2) => {
12578
13047
  const rl = createInterface3({
12579
13048
  input: process.stdin,
12580
13049
  output: process.stderr
12581
13050
  });
12582
13051
  rl.question(`Delete profile '${name}'? (y/N) `, (answer) => {
12583
13052
  rl.close();
12584
- resolve(answer.trim().toLowerCase() === "y");
13053
+ resolve2(answer.trim().toLowerCase() === "y");
12585
13054
  });
12586
13055
  });
12587
13056
  if (!ok) {
@@ -12604,8 +13073,8 @@ function registerConfigCommand(program2) {
12604
13073
  }
12605
13074
 
12606
13075
  // src/commands/definitions-apply.ts
12607
- import fs6 from "node:fs";
12608
- import path5 from "node:path";
13076
+ import fs7 from "node:fs";
13077
+ import path6 from "node:path";
12609
13078
  init_dist();
12610
13079
  init_errors();
12611
13080
  var VALID_KINDS = new Set([
@@ -12647,20 +13116,20 @@ var SERVER_GENERATED_FIELDS = new Set([
12647
13116
  ]);
12648
13117
  var AGENT_MANAGED_FIELDS = new Set(["functions", "workflows"]);
12649
13118
  function loadDefinitions(inputPath) {
12650
- const resolved = path5.resolve(inputPath);
12651
- if (!fs6.existsSync(resolved)) {
13119
+ const resolved = path6.resolve(inputPath);
13120
+ if (!fs7.existsSync(resolved)) {
12652
13121
  throw new CLIError(`Path not found: ${inputPath}`, 1 /* Error */);
12653
13122
  }
12654
- const stat = fs6.statSync(resolved);
13123
+ const stat = fs7.statSync(resolved);
12655
13124
  if (stat.isDirectory()) {
12656
13125
  return loadDirectory(resolved);
12657
13126
  }
12658
13127
  return loadFile(resolved);
12659
13128
  }
12660
13129
  function loadDirectory(dirPath) {
12661
- const entries = fs6.readdirSync(dirPath).sort();
13130
+ const entries = fs7.readdirSync(dirPath).sort();
12662
13131
  const validExts = new Set([".yaml", ".yml", ".json"]);
12663
- const files = entries.filter((e) => validExts.has(path5.extname(e).toLowerCase())).map((e) => path5.join(dirPath, e));
13132
+ const files = entries.filter((e) => validExts.has(path6.extname(e).toLowerCase())).map((e) => path6.join(dirPath, e));
12664
13133
  if (files.length === 0) {
12665
13134
  throw new CLIError(`No YAML or JSON files found in ${dirPath}`, 4 /* Validation */);
12666
13135
  }
@@ -12671,8 +13140,8 @@ function loadDirectory(dirPath) {
12671
13140
  return results;
12672
13141
  }
12673
13142
  function loadFile(filePath) {
12674
- const content = fs6.readFileSync(filePath, "utf-8");
12675
- const ext = path5.extname(filePath).toLowerCase();
13143
+ const content = fs7.readFileSync(filePath, "utf-8");
13144
+ const ext = path6.extname(filePath).toLowerCase();
12676
13145
  if (ext === ".json") {
12677
13146
  const parsed = parseJsonFile(content, filePath);
12678
13147
  return [toLocalDefinition(parsed, filePath)];
@@ -13094,7 +13563,7 @@ Examples:
13094
13563
  }
13095
13564
 
13096
13565
  // src/commands/definitions-export.ts
13097
- import fs7 from "node:fs";
13566
+ import fs8 from "node:fs";
13098
13567
  init_errors();
13099
13568
  async function exportDefinitions(ctx, opts, isJsonMode) {
13100
13569
  const params = {};
@@ -13110,7 +13579,7 @@ async function exportDefinitions(ctx, opts, isJsonMode) {
13110
13579
  const text = await res.text();
13111
13580
  if (opts.output) {
13112
13581
  try {
13113
- fs7.writeFileSync(opts.output, text);
13582
+ fs8.writeFileSync(opts.output, text);
13114
13583
  } catch (err) {
13115
13584
  throw new CLIError(`Failed to write ${opts.output}: ${err instanceof Error ? err.message : String(err)}`, 1 /* Error */);
13116
13585
  }
@@ -13419,7 +13888,7 @@ function colorStatus(status) {
13419
13888
  return fn(status);
13420
13889
  }
13421
13890
  function sleep(ms) {
13422
- return new Promise((resolve) => setTimeout(resolve, ms));
13891
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
13423
13892
  }
13424
13893
  async function pollUntilDone(ctx, executionId, intervalMs = 2000, timeoutMs = 300000) {
13425
13894
  const deadline = Date.now() + timeoutMs;
@@ -13830,8 +14299,8 @@ function registerProjectionCommands(parentCmd, getCtx, isJson) {
13830
14299
  } catch {}
13831
14300
  }
13832
14301
  if (opts.output) {
13833
- const fs8 = await import("node:fs/promises");
13834
- await fs8.writeFile(opts.output, toWrite, "utf-8");
14302
+ const fs9 = await import("node:fs/promises");
14303
+ await fs9.writeFile(opts.output, toWrite, "utf-8");
13835
14304
  if (!isJson()) {
13836
14305
  console.error(source_default.dim(`Wrote ${format} projection of '${name}' to ${opts.output}`));
13837
14306
  }
@@ -14238,8 +14707,8 @@ Usage:
14238
14707
  });
14239
14708
  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) => {
14240
14709
  const ctx = getCtx();
14241
- const path6 = `${API_PATH}/${encodeURIComponent(agent)}`;
14242
- const current = await ctx.api.get(path6);
14710
+ const path7 = `${API_PATH}/${encodeURIComponent(agent)}`;
14711
+ const current = await ctx.api.get(path7);
14243
14712
  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;
14244
14713
  if (!mutating) {
14245
14714
  if (isJson()) {
@@ -14291,7 +14760,7 @@ Usage:
14291
14760
  roots.delete(c);
14292
14761
  body.self_grant_collection_roots = [...roots];
14293
14762
  }
14294
- const updated = await ctx.api.put(path6, body);
14763
+ const updated = await ctx.api.put(path7, body);
14295
14764
  if (isJson()) {
14296
14765
  console.log(JSON.stringify(policyView(updated), null, 2));
14297
14766
  return;
@@ -14303,6 +14772,128 @@ Usage:
14303
14772
  }
14304
14773
  };
14305
14774
 
14775
+ // src/commands/prompts.ts
14776
+ var API_PATH2 = "/api/admin/definitions/prompts";
14777
+ var promptsConfig = {
14778
+ typeName: "prompt",
14779
+ typeNamePlural: "prompts",
14780
+ apiPath: API_PATH2,
14781
+ extractList: (data) => {
14782
+ const d = data;
14783
+ if (d.items && Array.isArray(d.items)) {
14784
+ return d.items;
14785
+ }
14786
+ throw new Error("Unexpected API response shape for prompts list");
14787
+ },
14788
+ extractTotal: (data) => {
14789
+ const d = data;
14790
+ return d.total;
14791
+ },
14792
+ listColumns: [
14793
+ { header: "NAME", accessor: (item) => String(item.name ?? "") },
14794
+ {
14795
+ header: "SCOPE",
14796
+ accessor: (item) => String(item.scope ?? "-")
14797
+ },
14798
+ {
14799
+ header: "PROD VER",
14800
+ accessor: (item) => {
14801
+ const prod = item.production;
14802
+ return prod?.version ? String(prod.version) : "-";
14803
+ }
14804
+ },
14805
+ {
14806
+ header: "STAGING VER",
14807
+ accessor: (item) => {
14808
+ const staging = item.staging;
14809
+ return staging?.version ? String(staging.version) : "-";
14810
+ }
14811
+ },
14812
+ {
14813
+ header: "PROTECTED",
14814
+ accessor: (item) => item.is_protected ? "yes" : "no"
14815
+ }
14816
+ ],
14817
+ renderDetail: (prompt2) => {
14818
+ console.log(source_default.bold(`Prompt: ${prompt2.name}
14819
+ `));
14820
+ if (prompt2.description)
14821
+ console.log(` ${prompt2.description}
14822
+ `);
14823
+ console.log(` Scope: ${prompt2.scope ?? "-"}`);
14824
+ console.log(` Protected: ${prompt2.is_protected ? source_default.yellow("yes") : "no"}`);
14825
+ const prod = prompt2.production;
14826
+ const staging = prompt2.staging;
14827
+ console.log(` Prod Ver: ${prod?.version ?? "-"}`);
14828
+ console.log(` Staging Ver: ${staging?.version ?? "-"}`);
14829
+ if (prompt2.updated_at)
14830
+ console.log(` Updated: ${prompt2.updated_at}`);
14831
+ const content = prompt2.content;
14832
+ if (content) {
14833
+ console.log(`
14834
+ ${source_default.bold("Content:")}`);
14835
+ const lines = content.split(`
14836
+ `).slice(0, 10);
14837
+ for (const line of lines) {
14838
+ console.log(` ${line}`);
14839
+ }
14840
+ if (content.split(`
14841
+ `).length > 10) {
14842
+ console.log(source_default.dim(` ... (${content.split(`
14843
+ `).length - 10} more lines)`));
14844
+ }
14845
+ }
14846
+ },
14847
+ extraListOptions: (cmd) => {
14848
+ cmd.option("--scope <scope>", "Filter by scope (global, tenant, agent)").option("--protected", "Show only protected prompts");
14849
+ },
14850
+ buildListParams: (opts) => {
14851
+ const params = {};
14852
+ if (opts.scope)
14853
+ params.scope = opts.scope;
14854
+ if (opts.protected)
14855
+ params.is_protected = "true";
14856
+ return params;
14857
+ },
14858
+ extraCommands: (parentCmd, getCtx, isJson) => {
14859
+ parentCmd.command("promote <name>").description("Promote staging version to production").action(async (name) => {
14860
+ const ctx = getCtx();
14861
+ const result = await ctx.api.post(`${API_PATH2}/${encodeURIComponent(name)}/promote`, {});
14862
+ if (isJson()) {
14863
+ console.log(JSON.stringify(result, null, 2));
14864
+ return;
14865
+ }
14866
+ const prodVer = result.production_version_number ?? result.version;
14867
+ console.log(`Promoted '${name}' staging → production (version ${prodVer})`);
14868
+ });
14869
+ parentCmd.command("linked-collections <name>").description("List collections using this prompt as an extra prompt").action(async (name) => {
14870
+ const ctx = getCtx();
14871
+ const response = await ctx.api.get(`${API_PATH2}/${encodeURIComponent(name)}/linked-collections`);
14872
+ const items = response.linked_collections ?? [];
14873
+ if (isJson()) {
14874
+ console.log(JSON.stringify(response, null, 2));
14875
+ return;
14876
+ }
14877
+ if (items.length === 0) {
14878
+ console.log(source_default.dim("No collections linked to this prompt."));
14879
+ return;
14880
+ }
14881
+ const nameW = Math.max(10, ...items.map((c) => String(c.collection_name ?? "").length));
14882
+ const header = ["COLLECTION", "DOCS", "PAGES", "CHUNKS"].map((h, i) => h.padEnd([nameW, 8, 8, 8][i])).join(" ");
14883
+ console.log(source_default.bold(header));
14884
+ console.log(source_default.dim("-".repeat(header.length)));
14885
+ for (const c of items) {
14886
+ console.log([
14887
+ String(c.collection_name ?? "").padEnd(nameW),
14888
+ String(c.document_count ?? 0).padEnd(8),
14889
+ String(c.page_count ?? 0).padEnd(8),
14890
+ String(c.chunk_count ?? 0).padEnd(8)
14891
+ ].join(" "));
14892
+ }
14893
+ });
14894
+ }
14895
+ };
14896
+
14306
14897
  // src/commands/triggers.ts
14307
14898
  init_errors();
14308
14899
  function triggerKind(item) {
@@ -14416,7 +15007,7 @@ var triggersConfig = {
14416
15007
  };
14417
15008
 
14418
15009
  // src/commands/documents.ts
14419
- import fs8 from "node:fs";
15010
+ import fs9 from "node:fs";
14420
15011
  init_errors();
14421
15012
  var TERMINAL_STATUSES2 = new Set(["ready", "error"]);
14422
15013
  var SPINNER_FRAMES2 = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
@@ -14427,7 +15018,7 @@ function nextSpinner() {
14427
15018
  return frame;
14428
15019
  }
14429
15020
  function sleep2(ms) {
14430
- return new Promise((resolve) => setTimeout(resolve, ms));
15021
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
14431
15022
  }
14432
15023
  function formatBytes(bytes) {
14433
15024
  if (bytes < 1024)
@@ -14671,6 +15262,65 @@ ${items.length} collections`));
14671
15262
  if (col.updated_at)
14672
15263
  console.log(` Updated: ${col.updated_at}`);
14673
15264
  });
15265
+ cmd.command("collection-available-prompts <id>").description("List prompts available for a collection's extra prompts").action(async (id) => {
15266
+ const ctx = getCtx();
15267
+ const col = await resolveCollection(ctx, id);
15268
+ const items = await ctx.api.get(`/api/admin/collections/${encodeURIComponent(col.id)}/available-prompts`);
15269
+ if (isJson()) {
15270
+ console.log(JSON.stringify(items, null, 2));
15271
+ return;
15272
+ }
15273
+ if (items.length === 0) {
15274
+ console.log(source_default.dim("No prompts available."));
15275
+ return;
15276
+ }
15277
+ const nameW = Math.max(10, ...items.map((p) => String(p.name ?? "").length));
15278
+ const scopeW = Math.max(6, ...items.map((p) => String(p.scope ?? "").length));
15279
+ const header = ["NAME", "SCOPE", "DESCRIPTION"].map((h, i) => h.padEnd([nameW, scopeW, 40][i])).join(" ");
15280
+ console.log(source_default.bold(header));
15281
+ console.log(source_default.dim("-".repeat(header.length)));
15282
+ for (const p of items) {
15283
+ const desc = String(p.description ?? "-").slice(0, 40);
15284
+ console.log([
15285
+ String(p.name ?? "").padEnd(nameW),
15286
+ String(p.scope ?? "-").padEnd(scopeW),
15287
+ desc
15288
+ ].join(" "));
15289
+ }
15290
+ });
15291
+ cmd.command("collection-get-prompts <id>").description("Get current extra prompts for a collection").action(async (id) => {
15292
+ const ctx = getCtx();
15293
+ const col = await ctx.api.get(`/api/admin/collections/${encodeURIComponent(id)}`);
15294
+ if (isJson()) {
15295
+ console.log(JSON.stringify({ extra_prompt_ids: col.extra_prompt_ids ?? [] }, null, 2));
15296
+ return;
15297
+ }
15298
+ const promptIds = col.extra_prompt_ids ?? [];
15299
+ if (promptIds.length === 0) {
15300
+ console.log(source_default.dim("No extra prompts configured."));
15301
+ return;
15302
+ }
15303
+ console.log(source_default.bold(`Extra Prompts for '${col.name}':`));
15304
+ for (const pid of promptIds) {
15305
+ console.log(` - ${pid}`);
15306
+ }
15307
+ });
15308
+ cmd.command("collection-set-prompts <id> [prompt-ids...]").description("Set extra prompts for a collection (replaces existing)").option("--clear", "Clear all extra prompts").action(async (id, promptIds, opts) => {
15309
+ const ctx = getCtx();
15310
+ const col = await resolveCollection(ctx, id);
15311
+ const extraPromptIds = opts.clear ? [] : promptIds;
15312
+ const result = await ctx.api.put(`/api/admin/collections/${encodeURIComponent(col.id)}`, { extra_prompt_ids: extraPromptIds });
15313
+ if (isJson()) {
15314
+ console.log(JSON.stringify({ extra_prompt_ids: result.extra_prompt_ids ?? [] }, null, 2));
15315
+ return;
15316
+ }
15317
+ const newIds = result.extra_prompt_ids ?? [];
15318
+ if (newIds.length === 0) {
15319
+ console.log(`Cleared extra prompts for '${col.name}'`);
15320
+ } else {
15321
+ console.log(`Set ${newIds.length} extra prompt(s) for '${col.name}'`);
15322
+ }
15323
+ });
14674
15324
  cmd.command("create-collection").description("Create a new document collection").requiredOption("--name <name>", "Collection name").option("--description <text>", "Collection description").option("--parent <slug-or-id>", "Create as a sub-collection of the given collection (#372 PR1)").action(async (opts) => {
14675
15325
  const ctx = getCtx();
14676
15326
  const tenantId = await ctx.getTenantId();
@@ -14718,7 +15368,7 @@ ${items.length} collections`));
14718
15368
  });
14719
15369
  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) => {
14720
15370
  const ctx = getCtx();
14721
- if (!fs8.existsSync(filePath)) {
15371
+ if (!fs9.existsSync(filePath)) {
14722
15372
  throw new CLIError(`File not found: ${filePath}`, 4 /* Validation */);
14723
15373
  }
14724
15374
  const result = await ctx.api.upload(`/api/admin/collections/${encodeURIComponent(collectionId)}/documents`, filePath);
@@ -15350,18 +16000,18 @@ function registerApprovalCommands(program2) {
15350
16000
  import { readFileSync } from "node:fs";
15351
16001
  init_errors();
15352
16002
  var truncate2 = (s, n) => s.length > n ? s.slice(0, n - 1) + "…" : s;
15353
- var sleep3 = (ms, signal) => new Promise((resolve) => {
16003
+ var sleep3 = (ms, signal) => new Promise((resolve2) => {
15354
16004
  if (signal?.aborted) {
15355
- resolve();
16005
+ resolve2();
15356
16006
  return;
15357
16007
  }
15358
16008
  const t = setTimeout(() => {
15359
16009
  signal?.removeEventListener("abort", onAbort);
15360
- resolve();
16010
+ resolve2();
15361
16011
  }, ms);
15362
16012
  const onAbort = () => {
15363
16013
  clearTimeout(t);
15364
- resolve();
16014
+ resolve2();
15365
16015
  };
15366
16016
  signal?.addEventListener("abort", onAbort, { once: true });
15367
16017
  });
@@ -15821,7 +16471,7 @@ ${items.length} task(s) available`));
15821
16471
 
15822
16472
  // src/commands/teams.ts
15823
16473
  init_errors();
15824
- var API_PATH2 = "/api/admin/teams";
16474
+ var API_PATH3 = "/api/admin/teams";
15825
16475
  var fmtDate3 = (v) => typeof v === "string" ? new Date(v).toISOString().slice(0, 16).replace("T", " ") : "-";
15826
16476
  function registerTeamCommands(program2) {
15827
16477
  const cmd = program2.command("teams").description("Manage agent teams");
@@ -15845,7 +16495,7 @@ function registerTeamCommands(program2) {
15845
16495
  };
15846
16496
  if (opts.description)
15847
16497
  body.description = opts.description;
15848
- const result = await ctx.api.post(API_PATH2, body);
16498
+ const result = await ctx.api.post(API_PATH3, body);
15849
16499
  if (isJson()) {
15850
16500
  console.log(JSON.stringify(result, null, 2));
15851
16501
  return;
@@ -15856,7 +16506,7 @@ function registerTeamCommands(program2) {
15856
16506
  });
15857
16507
  cmd.command("list").description("List teams").action(async () => {
15858
16508
  const ctx = getCtx();
15859
- const items = await ctx.api.get(API_PATH2);
16509
+ const items = await ctx.api.get(API_PATH3);
15860
16510
  if (isJson()) {
15861
16511
  console.log(JSON.stringify(items, null, 2));
15862
16512
  return;
@@ -15895,7 +16545,7 @@ ${items.length} team(s)`));
15895
16545
  });
15896
16546
  cmd.command("view <nameOrId>").description("View team details with members").action(async (nameOrId) => {
15897
16547
  const ctx = getCtx();
15898
- const item = await ctx.api.get(`${API_PATH2}/${encodeURIComponent(nameOrId)}`);
16548
+ const item = await ctx.api.get(`${API_PATH3}/${encodeURIComponent(nameOrId)}`);
15899
16549
  if (isJson()) {
15900
16550
  console.log(JSON.stringify(item, null, 2));
15901
16551
  return;
@@ -15932,7 +16582,7 @@ ${items.length} team(s)`));
15932
16582
  if (Object.keys(body).length === 0) {
15933
16583
  throw new CLIError("No fields to update. Use --name, --description, or --routing.", 4 /* Validation */);
15934
16584
  }
15935
- const result = await ctx.api.put(`${API_PATH2}/${encodeURIComponent(nameOrId)}`, body);
16585
+ const result = await ctx.api.put(`${API_PATH3}/${encodeURIComponent(nameOrId)}`, body);
15936
16586
  if (isJson()) {
15937
16587
  console.log(JSON.stringify(result, null, 2));
15938
16588
  return;
@@ -15947,7 +16597,7 @@ ${items.length} team(s)`));
15947
16597
  throw new CLIError("Cancelled. Use --yes to skip confirmation.", 1 /* Error */);
15948
16598
  }
15949
16599
  }
15950
- await ctx.api.del(`${API_PATH2}/${encodeURIComponent(nameOrId)}`);
16600
+ await ctx.api.del(`${API_PATH3}/${encodeURIComponent(nameOrId)}`);
15951
16601
  if (isJson()) {
15952
16602
  console.log(JSON.stringify({ name: nameOrId, deleted: true }));
15953
16603
  return;
@@ -15960,7 +16610,7 @@ ${items.length} team(s)`));
15960
16610
  agent_name: agentName,
15961
16611
  role: opts.role
15962
16612
  };
15963
- const result = await ctx.api.post(`${API_PATH2}/${encodeURIComponent(team)}/members`, body);
16613
+ const result = await ctx.api.post(`${API_PATH3}/${encodeURIComponent(team)}/members`, body);
15964
16614
  if (isJson()) {
15965
16615
  console.log(JSON.stringify(result, null, 2));
15966
16616
  return;
@@ -15969,7 +16619,7 @@ ${items.length} team(s)`));
15969
16619
  });
15970
16620
  cmd.command("remove-member <team> <agentId>").description("Remove an agent from a team").action(async (team, agentId) => {
15971
16621
  const ctx = getCtx();
15972
- await ctx.api.del(`${API_PATH2}/${encodeURIComponent(team)}/members/${encodeURIComponent(agentId)}`);
16622
+ await ctx.api.del(`${API_PATH3}/${encodeURIComponent(team)}/members/${encodeURIComponent(agentId)}`);
15973
16623
  if (isJson()) {
15974
16624
  console.log(JSON.stringify({ team, agent_id: agentId, removed: true }));
15975
16625
  return;
@@ -16295,8 +16945,8 @@ ${data.length} spec(s)`));
16295
16945
  Resources:
16296
16946
  `));
16297
16947
  for (const r of resources) {
16298
- const path6 = r.path || `/${r.name}`;
16299
- console.log(` ${source_default.cyan(r.label || r.name)}` + source_default.dim(` → ${path6}`));
16948
+ const path7 = r.path || `/${r.name}`;
16949
+ console.log(` ${source_default.cyan(r.label || r.name)}` + source_default.dim(` → ${path7}`));
16300
16950
  }
16301
16951
  }
16302
16952
  try {
@@ -16316,14 +16966,14 @@ Functions:
16316
16966
  console.log(source_default.dim(`
16317
16967
  Proxy: martha integrations proxy ${name} GET /<path>`));
16318
16968
  });
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) => {
16969
+ 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, path7, opts) => {
16320
16970
  const ctx = getCtx();
16321
16971
  const upperMethod = method.toUpperCase();
16322
16972
  const allowed = new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
16323
16973
  if (!allowed.has(upperMethod)) {
16324
16974
  throw new CLIError(`Invalid method: ${method}`, 4 /* Validation */, "Allowed: GET, POST, PUT, PATCH, DELETE");
16325
16975
  }
16326
- const cleanPath = path6.startsWith("/") ? path6.slice(1) : path6;
16976
+ const cleanPath = path7.startsWith("/") ? path7.slice(1) : path7;
16327
16977
  const proxyUrl = `/api/admin/plugins/${encodeURIComponent(name)}/${cleanPath}`;
16328
16978
  const params = {};
16329
16979
  if (opts.query) {
@@ -16533,8 +17183,8 @@ async function resolveCredentialValue(value) {
16533
17183
  if (value === "-")
16534
17184
  return readStdin();
16535
17185
  if (value.startsWith("@")) {
16536
- const fs9 = await import("node:fs/promises");
16537
- return (await fs9.readFile(value.slice(1), "utf-8")).trim();
17186
+ const fs10 = await import("node:fs/promises");
17187
+ return (await fs10.readFile(value.slice(1), "utf-8")).trim();
16538
17188
  }
16539
17189
  return value;
16540
17190
  }
@@ -16649,8 +17299,8 @@ Notification connections`));
16649
17299
  if (credentialValue === "-") {
16650
17300
  credentialValue = await readStdin2();
16651
17301
  } else if (credentialValue?.startsWith("@")) {
16652
- const fs9 = await import("node:fs/promises");
16653
- credentialValue = await fs9.readFile(credentialValue.slice(1), "utf-8");
17302
+ const fs10 = await import("node:fs/promises");
17303
+ credentialValue = await fs10.readFile(credentialValue.slice(1), "utf-8");
16654
17304
  }
16655
17305
  if (!credentialValue) {
16656
17306
  throw new Error("--credential-value is required (use '-' for stdin or '@path' for file).");
@@ -16700,11 +17350,11 @@ async function readStdin2() {
16700
17350
 
16701
17351
  // src/commands/messaging.ts
16702
17352
  init_errors();
16703
- async function messagingFetch(baseUrl, path6, opts) {
16704
- if (/^(https?:)?\/\//i.test(path6)) {
17353
+ async function messagingFetch(baseUrl, path7, opts) {
17354
+ if (/^(https?:)?\/\//i.test(path7)) {
16705
17355
  throw new CLIError("Absolute URL paths are not allowed", 1 /* Error */);
16706
17356
  }
16707
- const url = new URL(path6, baseUrl);
17357
+ const url = new URL(path7, baseUrl);
16708
17358
  const headers = {
16709
17359
  "Content-Type": "application/json"
16710
17360
  };
@@ -17984,7 +18634,7 @@ ${models.length} models`));
17984
18634
  }
17985
18635
 
17986
18636
  // src/commands/wiki.ts
17987
- import fs9 from "node:fs";
18637
+ import fs10 from "node:fs";
17988
18638
  init_errors();
17989
18639
  function registerWikiCommands(program2) {
17990
18640
  const cmd = program2.command("wiki").description("Manage tenant wiki pages, settings, schema, recompile (#245 D5.4)");
@@ -18031,14 +18681,14 @@ function registerWikiCommands(program2) {
18031
18681
  console.log(source_default.dim(`
18032
18682
  ${pages.length} pages`));
18033
18683
  });
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) => {
18684
+ 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 (path7, opts) => {
18035
18685
  const ctx = getCtx();
18036
- const safe = path6.split("/").map(encodeURIComponent).join("/");
18686
+ const safe = path7.split("/").map(encodeURIComponent).join("/");
18037
18687
  const resp = await ctx.api.getRaw(`/api/wiki/pages/${safe}`, {
18038
18688
  headers: { Accept: "text/markdown,*/*" }
18039
18689
  });
18040
18690
  if (resp.status === 404) {
18041
- throw new CLIError(`page not found: ${path6}`, 3 /* NotFound */);
18691
+ throw new CLIError(`page not found: ${path7}`, 3 /* NotFound */);
18042
18692
  }
18043
18693
  if (!resp.ok) {
18044
18694
  const detail = await resp.text();
@@ -18046,16 +18696,16 @@ ${pages.length} pages`));
18046
18696
  }
18047
18697
  const body = await resp.text();
18048
18698
  if (opts.out) {
18049
- fs9.writeFileSync(opts.out, body);
18699
+ fs10.writeFileSync(opts.out, body);
18050
18700
  if (!isJson()) {
18051
18701
  console.log(source_default.dim(`wrote ${body.length} bytes to ${opts.out}`));
18052
18702
  } else {
18053
- console.log(JSON.stringify({ path: path6, bytes: body.length, file: opts.out }));
18703
+ console.log(JSON.stringify({ path: path7, bytes: body.length, file: opts.out }));
18054
18704
  }
18055
18705
  return;
18056
18706
  }
18057
18707
  if (isJson()) {
18058
- console.log(JSON.stringify({ path: path6, body, etag: resp.headers.get("ETag") }));
18708
+ console.log(JSON.stringify({ path: path7, body, etag: resp.headers.get("ETag") }));
18059
18709
  } else {
18060
18710
  process.stdout.write(body);
18061
18711
  }
@@ -18150,10 +18800,10 @@ ${pages.length} pages`));
18150
18800
  }
18151
18801
  if (opts.compilePromptOverrideFile) {
18152
18802
  const file = String(opts.compilePromptOverrideFile);
18153
- if (!fs9.existsSync(file)) {
18803
+ if (!fs10.existsSync(file)) {
18154
18804
  throw new CLIError(`override file not found: ${file}`, 3 /* NotFound */);
18155
18805
  }
18156
- const content = fs9.readFileSync(file, "utf8");
18806
+ const content = fs10.readFileSync(file, "utf8");
18157
18807
  const bytes = Buffer.byteLength(content, "utf8");
18158
18808
  if (bytes > 16 * 1024) {
18159
18809
  throw new CLIError(`compile prompt override exceeds 16 KB (${bytes} bytes)`, 4 /* Validation */);
@@ -18177,7 +18827,7 @@ ${pages.length} pages`));
18177
18827
  const ctx = getCtx();
18178
18828
  const resp = await ctx.api.get("/api/wiki/schema");
18179
18829
  if (opts.out) {
18180
- fs9.writeFileSync(opts.out, resp.body);
18830
+ fs10.writeFileSync(opts.out, resp.body);
18181
18831
  if (!isJson()) {
18182
18832
  console.log(source_default.dim(`wrote ${resp.body.length} bytes to ${opts.out}`));
18183
18833
  } else {
@@ -18193,10 +18843,10 @@ ${pages.length} pages`));
18193
18843
  });
18194
18844
  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) => {
18195
18845
  const ctx = getCtx();
18196
- if (!fs9.existsSync(opts.file)) {
18846
+ if (!fs10.existsSync(opts.file)) {
18197
18847
  throw new CLIError(`schema file not found: ${opts.file}`, 3 /* NotFound */);
18198
18848
  }
18199
- const body = fs9.readFileSync(opts.file, "utf8");
18849
+ const body = fs10.readFileSync(opts.file, "utf8");
18200
18850
  const bytes = Buffer.byteLength(body, "utf8");
18201
18851
  if (bytes > 64 * 1024) {
18202
18852
  throw new CLIError(`schema body exceeds 64 KB (${bytes} bytes)`, 4 /* Validation */);
@@ -18532,7 +19182,7 @@ async function pollConnectionActive(ctx, connectionId) {
18532
19182
  });
18533
19183
  if (conn?.status === "active")
18534
19184
  return;
18535
- await new Promise((resolve) => setTimeout(resolve, intervalMs));
19185
+ await new Promise((resolve2) => setTimeout(resolve2, intervalMs));
18536
19186
  }
18537
19187
  throw new CLIError(`Timed out waiting for OAuth authorization (${Math.round(timeoutMs / 1000)}s).`, 1 /* Error */, "The browser consent may not have completed. Re-run to try again, or check " + "the connection status in the admin UI.");
18538
19188
  }
@@ -18594,7 +19244,7 @@ async function pollExecution(ctx, executionId, timeoutMs) {
18594
19244
  if (["completed", "failed", "cancelled", "timeout"].includes(last.status)) {
18595
19245
  return last;
18596
19246
  }
18597
- await new Promise((resolve) => setTimeout(resolve, 1000));
19247
+ await new Promise((resolve2) => setTimeout(resolve2, 1000));
18598
19248
  }
18599
19249
  throw new CLIError(`Timed out waiting for MCP workflow execution ${executionId}`, 1 /* Error */, last ? `Last status: ${last.status}` : undefined);
18600
19250
  }
@@ -18722,8 +19372,8 @@ async function resolveCredentialFlag(value) {
18722
19372
  return Buffer.concat(chunks).toString("utf-8").trim();
18723
19373
  }
18724
19374
  if (value.startsWith("@")) {
18725
- const fs10 = await import("node:fs/promises");
18726
- return (await fs10.readFile(value.slice(1), "utf-8")).trim();
19375
+ const fs11 = await import("node:fs/promises");
19376
+ return (await fs11.readFile(value.slice(1), "utf-8")).trim();
18727
19377
  }
18728
19378
  return value;
18729
19379
  }
@@ -19909,19 +20559,19 @@ function registerDoctorCommand(program2) {
19909
20559
 
19910
20560
  // src/commands/skill.ts
19911
20561
  init_errors();
19912
- import fs10 from "node:fs";
19913
- import path6 from "node:path";
20562
+ import fs11 from "node:fs";
20563
+ import path7 from "node:path";
19914
20564
  import { fileURLToPath } from "node:url";
19915
20565
  function locateSkill() {
19916
- const here = path6.dirname(fileURLToPath(import.meta.url));
20566
+ const here = path7.dirname(fileURLToPath(import.meta.url));
19917
20567
  const candidates = [
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")
20568
+ path7.join(here, "skills", "martha-cli", "SKILL.md"),
20569
+ path7.join(here, "..", "skills", "martha-cli", "SKILL.md"),
20570
+ path7.join(here, "..", "..", "..", "skills", "martha-cli", "SKILL.md"),
20571
+ path7.join(here, "..", "..", "skills", "martha-cli", "SKILL.md")
19922
20572
  ];
19923
20573
  for (const p of candidates) {
19924
- if (fs10.existsSync(p))
20574
+ if (fs11.existsSync(p))
19925
20575
  return p;
19926
20576
  }
19927
20577
  return null;
@@ -19931,7 +20581,7 @@ async function skillCommand() {
19931
20581
  if (!skillPath) {
19932
20582
  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 */);
19933
20583
  }
19934
- const body = fs10.readFileSync(skillPath, "utf-8");
20584
+ const body = fs11.readFileSync(skillPath, "utf-8");
19935
20585
  process.stdout.write(body);
19936
20586
  }
19937
20587
  function registerSkillCommand(program2) {
@@ -20116,6 +20766,7 @@ registerConfigCommand(program2);
20116
20766
  registerDefinitionCommands(program2, functionsConfig);
20117
20767
  registerDefinitionCommands(program2, workflowsConfig);
20118
20768
  registerDefinitionCommands(program2, agentsConfig);
20769
+ registerDefinitionCommands(program2, promptsConfig);
20119
20770
  registerDefinitionCommands(program2, triggersConfig);
20120
20771
  registerDefinitionsApply(program2);
20121
20772
  registerDefinitionsExport(program2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiaiai-pt/martha-cli",
3
- "version": "0.22.0",
3
+ "version": "0.24.0",
4
4
  "description": "Terminal-first client for the Martha AI platform",
5
5
  "homepage": "https://docs.martha.nomadriver.co",
6
6
  "repository": {