@aiaiai-pt/martha-cli 0.30.0 → 0.32.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 +104 -43
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3528,6 +3528,7 @@ var require_stringify = __commonJS((exports) => {
3528
3528
  nullStr: "null",
3529
3529
  simpleKeys: false,
3530
3530
  singleQuote: null,
3531
+ trailingComma: false,
3531
3532
  trueStr: "true",
3532
3533
  verifyAliasOrder: true
3533
3534
  }, doc.schema.toStringOptions, options);
@@ -4036,13 +4037,20 @@ ${indent}${line}` : `
4036
4037
  if (comment)
4037
4038
  reqNewline = true;
4038
4039
  let str = stringify.stringify(item, itemCtx, () => comment = null);
4039
- if (i < items.length - 1)
4040
+ reqNewline || (reqNewline = lines.length > linesAtValue || str.includes(`
4041
+ `));
4042
+ if (i < items.length - 1) {
4040
4043
  str += ",";
4044
+ } else if (ctx.options.trailingComma) {
4045
+ if (ctx.options.lineWidth > 0) {
4046
+ reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
4047
+ }
4048
+ if (reqNewline) {
4049
+ str += ",";
4050
+ }
4051
+ }
4041
4052
  if (comment)
4042
4053
  str += stringifyComment.lineComment(str, itemIndent, commentString(comment));
4043
- if (!reqNewline && (lines.length > linesAtValue || str.includes(`
4044
- `)))
4045
- reqNewline = true;
4046
4054
  lines.push(str);
4047
4055
  linesAtValue = lines.length;
4048
4056
  }
@@ -6837,17 +6845,22 @@ var require_compose_node = __commonJS((exports) => {
6837
6845
  case "block-map":
6838
6846
  case "block-seq":
6839
6847
  case "flow-collection":
6840
- node = composeCollection.composeCollection(CN, ctx, token, props, onError);
6841
- if (anchor)
6842
- node.anchor = anchor.source.substring(1);
6848
+ try {
6849
+ node = composeCollection.composeCollection(CN, ctx, token, props, onError);
6850
+ if (anchor)
6851
+ node.anchor = anchor.source.substring(1);
6852
+ } catch (error) {
6853
+ const message = error instanceof Error ? error.message : String(error);
6854
+ onError(token, "RESOURCE_EXHAUSTION", message);
6855
+ }
6843
6856
  break;
6844
6857
  default: {
6845
6858
  const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`;
6846
6859
  onError(token, "UNEXPECTED_TOKEN", message);
6847
- node = composeEmptyNode(ctx, token.offset, undefined, null, props, onError);
6848
6860
  isSrcToken = false;
6849
6861
  }
6850
6862
  }
6863
+ node ?? (node = composeEmptyNode(ctx, token.offset, undefined, null, props, onError));
6851
6864
  if (anchor && node.anchor === "")
6852
6865
  onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string");
6853
6866
  if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) {
@@ -11063,7 +11076,7 @@ init_config();
11063
11076
  init_errors();
11064
11077
 
11065
11078
  // src/version.ts
11066
- var CLI_VERSION = "0.30.0";
11079
+ var CLI_VERSION = "0.32.0";
11067
11080
 
11068
11081
  // src/commands/sessions.ts
11069
11082
  function relativeTime(iso) {
@@ -16738,6 +16751,52 @@ function registerAskCommands(program2) {
16738
16751
  import { readFileSync as readFileSync2 } from "node:fs";
16739
16752
  init_errors();
16740
16753
  var truncate2 = (s, n) => s.length > n ? s.slice(0, n - 1) + "…" : s;
16754
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
16755
+ async function resolveAgentId(ctx, ref) {
16756
+ if (UUID_RE.test(ref))
16757
+ return ref;
16758
+ try {
16759
+ const agent = await ctx.api.get(`/api/admin/definitions/agents/${encodeURIComponent(ref)}`);
16760
+ return agent.id;
16761
+ } catch {
16762
+ throw new CLIError(`Agent '${ref}' not found (pass a valid agent name or UUID)`, 4 /* Validation */);
16763
+ }
16764
+ }
16765
+ async function resolveTeamId(ctx, ref) {
16766
+ if (UUID_RE.test(ref))
16767
+ return ref;
16768
+ const teams = await ctx.api.get("/api/admin/teams");
16769
+ const match = (Array.isArray(teams) ? teams : []).find((t) => t.name === ref);
16770
+ if (!match) {
16771
+ throw new CLIError(`Team '${ref}' not found (pass a valid team name or UUID)`, 4 /* Validation */);
16772
+ }
16773
+ return match.id;
16774
+ }
16775
+ async function buildCreateTaskBody(ctx, opts) {
16776
+ if (opts.agent && opts.team) {
16777
+ throw new CLIError("--agent and --team are mutually exclusive — a task has one assignee", 4 /* Validation */);
16778
+ }
16779
+ const body = {
16780
+ goal: opts.goal,
16781
+ priority: opts.priority
16782
+ };
16783
+ if (opts.agent)
16784
+ body.agent_id = await resolveAgentId(ctx, opts.agent);
16785
+ if (opts.team)
16786
+ body.team_id = await resolveTeamId(ctx, opts.team);
16787
+ if (opts.title)
16788
+ body.title = opts.title;
16789
+ if (opts.context) {
16790
+ try {
16791
+ body.context_data = JSON.parse(opts.context);
16792
+ } catch {
16793
+ throw new CLIError("--context must be valid JSON", 4 /* Validation */);
16794
+ }
16795
+ }
16796
+ if (opts.sticky)
16797
+ body.sticky_assignment = true;
16798
+ return body;
16799
+ }
16741
16800
  var sleep4 = (ms, signal) => new Promise((resolve2) => {
16742
16801
  if (signal?.aborted) {
16743
16802
  resolve2();
@@ -16876,30 +16935,13 @@ ${items.length} task(s)`));
16876
16935
  }
16877
16936
  console.log(`Open: ${source_default.yellow(data.open ?? 0)} ` + `Running: ${source_default.blue(data.running ?? 0)} ` + `Claimed: ${source_default.magenta(data.claimed ?? 0)} ` + `Completed: ${source_default.green(data.completed ?? 0)} ` + `Failed: ${source_default.red(data.failed ?? 0)} ` + `Cancelled: ${source_default.dim(data.cancelled ?? 0)}`);
16878
16937
  });
16879
- cmd.command("create").description("Create a new task").requiredOption("--agent <name-or-id>", "Agent definition ID").requiredOption("--goal <text>", "Goal / instruction for the agent").option("--title <text>", "Short title for the task").option("--priority <priority>", "Priority (low, medium, high, urgent)", "medium").option("--context <json>", "Structured context as JSON string").option("--team <team-id>", "Assign task to a team").option("--assigned-agent <agent-id>", "Assign task to a specific agent").option("--sticky", "Keep assignment even if agent goes offline").option("--no-auto-execute", "Create the task as claimable (open) for an external runner to pull, instead of auto-running it via the cloud workflow").action(async (opts) => {
16938
+ cmd.command("create").description("Create a task. Assign it to an agent or a team to dispatch it, or omit " + "both to leave it in the backlog (#774: assignment is the dispatch moment).").requiredOption("--goal <text>", "Goal / instruction for the agent").option("--agent <name-or-id>", "Assign to an agent (dispatches; venue derives from its type)").option("--team <name-or-id>", "Assign to a team").option("--title <text>", "Short title for the task").option("--priority <priority>", "Priority (low, medium, high, urgent)", "medium").option("--context <json>", "Structured context as JSON string").option("--sticky", "Keep assignment even if agent goes offline").option("--no-auto-execute", "Deprecated (#774): venue now derives from the assignee. Accepted and ignored.").action(async (opts) => {
16880
16939
  const ctx = getCtx();
16881
- const body = {
16882
- agent_definition_id: opts.agent,
16883
- goal: opts.goal,
16884
- priority: opts.priority
16885
- };
16886
- if (opts.title)
16887
- body.title = opts.title;
16888
- if (opts.context) {
16889
- try {
16890
- body.context_data = JSON.parse(opts.context);
16891
- } catch {
16892
- throw new CLIError("--context must be valid JSON", 4 /* Validation */);
16893
- }
16940
+ if (opts.autoExecute === false) {
16941
+ process.stderr.write("Warning: --no-auto-execute is deprecated (#774) — the venue derives " + `from the assignee. Ignoring.
16942
+ `);
16894
16943
  }
16895
- if (opts.team)
16896
- body.assigned_team_id = opts.team;
16897
- if (opts.assignedAgent)
16898
- body.assigned_agent_id = opts.assignedAgent;
16899
- if (opts.sticky)
16900
- body.sticky_assignment = true;
16901
- if (opts.autoExecute === false)
16902
- body.auto_execute = false;
16944
+ const body = await buildCreateTaskBody(ctx, opts);
16903
16945
  const result = await ctx.api.post("/api/admin/tasks", body);
16904
16946
  if (isJson()) {
16905
16947
  console.log(JSON.stringify(result, null, 2));
@@ -16959,7 +17001,7 @@ ${source_default.red("Error:")}`);
16959
17001
  }
16960
17002
  console.log(`Cancelled task ${id}`);
16961
17003
  });
16962
- cmd.command("poll").description("Poll for open tasks available for claiming").option("--agent <id>", "Filter by agent definition ID").option("--limit <n>", "Max results", "10").action(async (opts) => {
17004
+ cmd.command("poll").description("Poll for open tasks available for claiming").option("--agent <id>", "Filter by agent definition ID (human tokens only; ignored under agent auth)").option("--team <name>", "Poll a team's queue (name or UUID)").option("--limit <n>", "Max results", "10").action(async (opts) => {
16963
17005
  const ctx = getCtx();
16964
17006
  const limitN = parseInt(opts.limit, 10);
16965
17007
  if (isNaN(limitN) || limitN < 1) {
@@ -16968,6 +17010,8 @@ ${source_default.red("Error:")}`);
16968
17010
  const params = { limit: String(limitN) };
16969
17011
  if (opts.agent)
16970
17012
  params.agent_id = opts.agent;
17013
+ if (opts.team)
17014
+ params.team_id = opts.team;
16971
17015
  const items = await ctx.api.get("/api/tasks/poll", { params });
16972
17016
  if (isJson()) {
16973
17017
  console.log(JSON.stringify(items, null, 2));
@@ -17226,16 +17270,18 @@ function heartbeatIntervalMs(leaseTimeoutS) {
17226
17270
  return Math.min(lease / 3, 60) * 1000;
17227
17271
  }
17228
17272
  async function runOnce(ctx, opts, handled) {
17229
- const tasks = await ctx.api.get("/api/tasks/poll", {
17230
- params: { agent_id: opts.agent, limit: String(opts.pollLimit ?? 5) }
17231
- });
17273
+ const params = { limit: String(opts.pollLimit ?? 5) };
17274
+ if (opts.team)
17275
+ params.team_id = opts.team;
17276
+ const tasks = await ctx.api.get("/api/tasks/poll", { params });
17232
17277
  const candidates = Array.isArray(tasks) ? tasks : [];
17233
- const task = candidates.find((t) => (t.status ?? "open") === "open" && !handled?.has(t.id));
17278
+ const matchesAgent = (t) => !opts.agent || t.assigned_agent_id === opts.agent || t.agent_definition_id === opts.agent;
17279
+ const task = candidates.find((t) => (t.status ?? "open") === "open" && !handled?.has(t.id) && matchesAgent(t));
17234
17280
  if (!task)
17235
17281
  return { ran: false };
17236
- if (!opts.autoYes) {
17282
+ if (!opts.allowHostExec) {
17237
17283
  process.stderr.write(source_default.yellow(`
17238
- [runner] refused task ${task.id} — re-run with --yes to allow host execution
17284
+ [runner] refused task ${task.id} — re-run with --allow-host-exec to allow host execution
17239
17285
  `));
17240
17286
  return { ran: false, taskId: task.id, status: "refused" };
17241
17287
  }
@@ -17297,8 +17343,14 @@ async function runOnce(ctx, opts, handled) {
17297
17343
  `));
17298
17344
  return { ran: true, taskId: task.id, status, exitCode: exit_code };
17299
17345
  }
17346
+ function startupEchoLine(who, opts) {
17347
+ const identity2 = who ? who.kind === "agent" ? `agent ${who.agent_name ?? "?"} (${(who.agent_id ?? "").slice(0, 8)}…)` : `${who.kind} ${who.sub ?? ""}`.trim() : "identity unknown (whoami unavailable)";
17348
+ const scope = opts.team ? `team:${opts.team}` : "direct";
17349
+ const exec = opts.allowHostExec ? "host exec allowed" : "host exec refused (pass --allow-host-exec)";
17350
+ return `[runner] authenticated as ${identity2}, scope ${scope}, cwd ${opts.cwd}, ${exec}${opts.once ? " — once" : ""}`;
17351
+ }
17300
17352
  function registerRunnerCommand(program2) {
17301
- program2.command("runner").description("Run as an external agent's executor: claim tasks and run them on this host.").requiredOption("--agent <id>", "Agent definition id this runner serves").option("--once", "Claim + run a single task, then exit").option("--poll-interval <ms>", "Poll interval when idle (ms)", "3000").option("--cwd <dir>", "Scoped workspace for task execution", process.cwd()).option("--yes", "Consent to run host commands (required to execute)").action(async (opts) => {
17353
+ program2.command("runner").description("Run as an external agent's executor: claim tasks and run them on this host.").option("--agent <id>", "Deprecated: identity comes from the API token; applied as a client-side filter only").option("--team <name>", "Poll this team's queue (name or UUID) instead of direct-only").option("--once", "Claim + run a single task, then exit").option("--poll-interval <ms>", "Poll interval when idle (ms)", "3000").option("--cwd <dir>", "Scoped workspace for task execution", process.cwd()).option("--allow-host-exec", "Consent to run host commands (required to execute)").option("--yes", "Deprecated alias for --allow-host-exec").action(async (opts) => {
17302
17354
  const ctx = createContext({
17303
17355
  profileOverride: program2.opts().profile,
17304
17356
  verbose: program2.opts().verbose
@@ -17309,12 +17361,21 @@ function registerRunnerCommand(program2) {
17309
17361
  if (isNaN(pollInterval) || pollInterval < 250) {
17310
17362
  throw new CLIError("--poll-interval must be >= 250 (ms)", 4 /* Validation */);
17311
17363
  }
17364
+ if (opts.agent) {
17365
+ process.stderr.write(source_default.yellow(`[runner] --agent is deprecated — identity comes from the API token; the value is applied as a client-side filter only
17366
+ `));
17367
+ }
17312
17368
  const runOpts = {
17313
17369
  agent: opts.agent,
17370
+ team: opts.team,
17314
17371
  cwd: opts.cwd,
17315
- autoYes: !!opts.yes
17372
+ allowHostExec: !!(opts.allowHostExec || opts.yes)
17316
17373
  };
17317
- process.stderr.write(source_default.dim(`[runner] serving agent ${opts.agent} (cwd ${opts.cwd})${opts.once ? " — once" : ""}
17374
+ let who;
17375
+ try {
17376
+ who = await ctx.api.get("/api/tasks/whoami");
17377
+ } catch {}
17378
+ process.stderr.write(source_default.dim(`${startupEchoLine(who, { team: opts.team, cwd: opts.cwd, allowHostExec: runOpts.allowHostExec, once: !!opts.once })}
17318
17379
  `));
17319
17380
  if (opts.once) {
17320
17381
  await runOnce(ctx, runOpts);
@@ -20392,9 +20453,9 @@ init_errors();
20392
20453
 
20393
20454
  // src/lib/collections.ts
20394
20455
  init_errors();
20395
- var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
20456
+ var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
20396
20457
  async function resolveCollectionIdForGrant(ctx, ref) {
20397
- if (UUID_RE.test(ref)) {
20458
+ if (UUID_RE2.test(ref)) {
20398
20459
  return ref;
20399
20460
  }
20400
20461
  const all = await ctx.api.get("/api/admin/collections", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiaiai-pt/martha-cli",
3
- "version": "0.30.0",
3
+ "version": "0.32.0",
4
4
  "description": "Terminal-first client for the Martha AI platform",
5
5
  "homepage": "https://docs.martha.nomadriver.co",
6
6
  "repository": {