@asteroidai/cli 1.8.197 → 1.9.2

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/asteroid.js CHANGED
@@ -57,7 +57,7 @@ function columns(rows) {
57
57
  const scalarKeys = [];
58
58
  for (const row of rows) {
59
59
  for (const [key, value2] of Object.entries(row)) {
60
- if (isScalar(value2) && !HIDDEN.test(key) && !scalarKeys.includes(key))
60
+ if (isScalar(value2) && !HIDDEN.test(key.split(".").at(-1) ?? key) && !scalarKeys.includes(key))
61
61
  scalarKeys.push(key);
62
62
  }
63
63
  }
@@ -70,8 +70,21 @@ function columns(rows) {
70
70
  function style(format, value2, options) {
71
71
  return options.styled ? styleText(format, value2, { validateStream: false }) : value2;
72
72
  }
73
- function table(rows, options = {}) {
74
- if (rows.length === 0) return "No results.";
73
+ function flatten(row) {
74
+ const flat = {};
75
+ for (const [key, value2] of Object.entries(row)) {
76
+ if (!isObject(value2)) {
77
+ flat[key] = value2;
78
+ continue;
79
+ }
80
+ for (const [inner, v] of Object.entries(value2))
81
+ flat[`${key}.${inner}`] = v;
82
+ }
83
+ return flat;
84
+ }
85
+ function table(input, options = {}) {
86
+ if (input.length === 0) return "No results.";
87
+ const rows = input.map(flatten);
75
88
  const keys = columns(rows);
76
89
  if (keys.length === 0) return rows.map((row) => text(row)).join("\n");
77
90
  const cells = rows.map((row) => keys.map((key) => cell(row[key])));
@@ -108,6 +121,10 @@ ${indent(table(v, options))}`;
108
121
  if (Array.isArray(v) && v.length > 0 && v.every(isScalar)) {
109
122
  return `${label(key)} ${v.map(text).join(`
110
123
  ${" ".repeat(width + 2)}`)}`;
124
+ }
125
+ if (isObject(v) && Object.keys(v).length > 0) {
126
+ return `${style("bold", key, options)}:
127
+ ${indent(record(v, options))}`;
111
128
  }
112
129
  return `${label(key)} ${isScalar(v) ? text(v) : summary(v)}`;
113
130
  }).join("\n");
@@ -4875,16 +4892,25 @@ function defineCommands(root, options, dotted = /* @__PURE__ */ new Map(), onInv
4875
4892
  const defaults = command.args.map(
4876
4893
  (arg) => options.defaultArg?.(command, arg)
4877
4894
  );
4878
- command.args.forEach((arg, i) => {
4895
+ const defaulted = defaults[0] !== void 0;
4896
+ command.args.forEach((arg) => {
4879
4897
  leaf.argument(
4880
- defaults[i] === void 0 ? `<${arg.flag}>` : `[${arg.flag}]`,
4898
+ defaulted ? `[${arg.flag}]` : `<${arg.flag}>`,
4881
4899
  arg.description ?? ""
4882
4900
  );
4883
4901
  });
4884
- if (defaults.some((value2) => value2 !== void 0) && options.defaultArgNote) {
4885
- leaf.addHelpText("after", `
4886
- ${options.defaultArgNote}`);
4902
+ if (defaulted) {
4903
+ const names = command.args.map(
4904
+ (arg, i) => i === 0 ? `[${arg.flag}]` : `<${arg.flag}>`
4905
+ );
4906
+ leaf.usage(`[options] ${names.join(" ")}`);
4887
4907
  }
4908
+ const notes = [
4909
+ defaults.some((value2) => value2 !== void 0) ? options.defaultArgNote : void 0,
4910
+ options.helpNote?.(command)
4911
+ ].filter(Boolean);
4912
+ if (notes.length > 0) leaf.addHelpText("after", `
4913
+ ${notes.join("\n")}`);
4888
4914
  leafOptions(leaf, command);
4889
4915
  leaf.action((...args) => {
4890
4916
  const cmd = args.at(-1);
@@ -4893,7 +4919,7 @@ ${options.defaultArgNote}`);
4893
4919
  positionals = [defaults[0], ...positionals];
4894
4920
  }
4895
4921
  if (positionals.length !== command.args.length) {
4896
- const expected = command.args.map((a) => `<${a.flag}>`).join(" ");
4922
+ const expected = command.args.map((a, i) => i === 0 && defaulted ? `[${a.flag}]` : `<${a.flag}>`).join(" ");
4897
4923
  throw new UsageError(
4898
4924
  `expected ${expected}, got ${cmd.args.length} argument(s)`
4899
4925
  );
@@ -5303,7 +5329,7 @@ var ApiError = class extends Error {
5303
5329
  status;
5304
5330
  body;
5305
5331
  };
5306
- var VERSION = "1.8.197";
5332
+ var VERSION = "1.9.2";
5307
5333
  function problem(status, body, fallback) {
5308
5334
  if (body && typeof body === "object") {
5309
5335
  const record2 = body;
package/dist/core.js CHANGED
@@ -4569,7 +4569,7 @@ function columns(rows) {
4569
4569
  const scalarKeys = [];
4570
4570
  for (const row of rows) {
4571
4571
  for (const [key, value2] of Object.entries(row)) {
4572
- if (isScalar(value2) && !HIDDEN.test(key) && !scalarKeys.includes(key))
4572
+ if (isScalar(value2) && !HIDDEN.test(key.split(".").at(-1) ?? key) && !scalarKeys.includes(key))
4573
4573
  scalarKeys.push(key);
4574
4574
  }
4575
4575
  }
@@ -4582,8 +4582,21 @@ function columns(rows) {
4582
4582
  function style(format, value2, options) {
4583
4583
  return options.styled ? styleText(format, value2, { validateStream: false }) : value2;
4584
4584
  }
4585
- function table(rows, options = {}) {
4586
- if (rows.length === 0) return "No results.";
4585
+ function flatten(row) {
4586
+ const flat = {};
4587
+ for (const [key, value2] of Object.entries(row)) {
4588
+ if (!isObject(value2)) {
4589
+ flat[key] = value2;
4590
+ continue;
4591
+ }
4592
+ for (const [inner, v] of Object.entries(value2))
4593
+ flat[`${key}.${inner}`] = v;
4594
+ }
4595
+ return flat;
4596
+ }
4597
+ function table(input, options = {}) {
4598
+ if (input.length === 0) return "No results.";
4599
+ const rows = input.map(flatten);
4587
4600
  const keys = columns(rows);
4588
4601
  if (keys.length === 0) return rows.map((row) => text(row)).join("\n");
4589
4602
  const cells = rows.map((row) => keys.map((key) => cell(row[key])));
@@ -4620,6 +4633,10 @@ ${indent(table(v, options))}`;
4620
4633
  if (Array.isArray(v) && v.length > 0 && v.every(isScalar)) {
4621
4634
  return `${label(key)} ${v.map(text).join(`
4622
4635
  ${" ".repeat(width + 2)}`)}`;
4636
+ }
4637
+ if (isObject(v) && Object.keys(v).length > 0) {
4638
+ return `${style("bold", key, options)}:
4639
+ ${indent(record(v, options))}`;
4623
4640
  }
4624
4641
  return `${label(key)} ${isScalar(v) ? text(v) : summary(v)}`;
4625
4642
  }).join("\n");
@@ -4808,16 +4825,25 @@ function defineCommands(root, options, dotted = /* @__PURE__ */ new Map(), onInv
4808
4825
  const defaults = command.args.map(
4809
4826
  (arg) => options.defaultArg?.(command, arg)
4810
4827
  );
4811
- command.args.forEach((arg, i) => {
4828
+ const defaulted = defaults[0] !== void 0;
4829
+ command.args.forEach((arg) => {
4812
4830
  leaf.argument(
4813
- defaults[i] === void 0 ? `<${arg.flag}>` : `[${arg.flag}]`,
4831
+ defaulted ? `[${arg.flag}]` : `<${arg.flag}>`,
4814
4832
  arg.description ?? ""
4815
4833
  );
4816
4834
  });
4817
- if (defaults.some((value2) => value2 !== void 0) && options.defaultArgNote) {
4818
- leaf.addHelpText("after", `
4819
- ${options.defaultArgNote}`);
4835
+ if (defaulted) {
4836
+ const names = command.args.map(
4837
+ (arg, i) => i === 0 ? `[${arg.flag}]` : `<${arg.flag}>`
4838
+ );
4839
+ leaf.usage(`[options] ${names.join(" ")}`);
4820
4840
  }
4841
+ const notes = [
4842
+ defaults.some((value2) => value2 !== void 0) ? options.defaultArgNote : void 0,
4843
+ options.helpNote?.(command)
4844
+ ].filter(Boolean);
4845
+ if (notes.length > 0) leaf.addHelpText("after", `
4846
+ ${notes.join("\n")}`);
4821
4847
  leafOptions(leaf, command);
4822
4848
  leaf.action((...args) => {
4823
4849
  const cmd = args.at(-1);
@@ -4826,7 +4852,7 @@ ${options.defaultArgNote}`);
4826
4852
  positionals = [defaults[0], ...positionals];
4827
4853
  }
4828
4854
  if (positionals.length !== command.args.length) {
4829
- const expected = command.args.map((a) => `<${a.flag}>`).join(" ");
4855
+ const expected = command.args.map((a, i) => i === 0 && defaulted ? `[${a.flag}]` : `<${a.flag}>`).join(" ");
4830
4856
  throw new UsageError(
4831
4857
  `expected ${expected}, got ${cmd.args.length} argument(s)`
4832
4858
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteroidai/cli",
3
- "version": "1.8.197",
3
+ "version": "1.9.2",
4
4
  "description": "The asteroid command-line interface for the Asteroid platform.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,4 +23,30 @@ describe("render", () => {
23
23
  expect(render(rows)).not.toContain("┌");
24
24
  expect(render(rows, { styled: true })).toContain("┌");
25
25
  });
26
+
27
+ it("prints nested objects in full, not as cut JSON", () => {
28
+ const out = render({
29
+ workflow: { id: "3cfc3c1d-e38d-4132-a5ee-8847136d491d", name: "Nebula" },
30
+ version: { version: 3, isPublished: true },
31
+ });
32
+ expect(out).toContain("3cfc3c1d-e38d-4132-a5ee-8847136d491d");
33
+ expect(out).toMatch(/workflow:\n {2}id +3cfc3c1d/);
34
+ expect(out).not.toContain("…");
35
+ });
36
+
37
+ it("flattens one level of nested fields into table columns", () => {
38
+ const out = render([
39
+ { date: "2026-10-09", outcomes: { success: 3, failure: 1 } },
40
+ ]);
41
+ expect(out).toContain("OUTCOMES.SUCCESS");
42
+ expect(out).toMatch(/2026-10-09 +3 +1/);
43
+ });
44
+
45
+ it("hides nested fields that are hidden at the top level", () => {
46
+ const out = render([
47
+ { id: "1", execution: { agentId: "a-1", status: "failed" } },
48
+ ]);
49
+ expect(out).toContain("EXECUTION.STATUS");
50
+ expect(out).not.toContain("AGENTID");
51
+ });
26
52
  });
package/src/output.ts CHANGED
@@ -59,7 +59,11 @@ function columns(rows: Row[]): string[] {
59
59
  const scalarKeys: string[] = [];
60
60
  for (const row of rows) {
61
61
  for (const [key, value] of Object.entries(row)) {
62
- if (isScalar(value) && !HIDDEN.test(key) && !scalarKeys.includes(key))
62
+ if (
63
+ isScalar(value) &&
64
+ !HIDDEN.test(key.split(".").at(-1) ?? key) &&
65
+ !scalarKeys.includes(key)
66
+ )
63
67
  scalarKeys.push(key);
64
68
  }
65
69
  }
@@ -80,8 +84,22 @@ function style(
80
84
  : value;
81
85
  }
82
86
 
83
- export function table(rows: Row[], options: RenderOptions = {}): string {
84
- if (rows.length === 0) return "No results.";
87
+ function flatten(row: Row): Row {
88
+ const flat: Row = {};
89
+ for (const [key, value] of Object.entries(row)) {
90
+ if (!isObject(value)) {
91
+ flat[key] = value;
92
+ continue;
93
+ }
94
+ for (const [inner, v] of Object.entries(value as Row))
95
+ flat[`${key}.${inner}`] = v;
96
+ }
97
+ return flat;
98
+ }
99
+
100
+ export function table(input: Row[], options: RenderOptions = {}): string {
101
+ if (input.length === 0) return "No results.";
102
+ const rows = input.map(flatten);
85
103
  const keys = columns(rows);
86
104
  if (keys.length === 0) return rows.map((row) => text(row)).join("\n");
87
105
  const cells = rows.map((row) => keys.map((key) => cell(row[key])));
@@ -124,6 +142,9 @@ function record(value: Row, options: RenderOptions): string {
124
142
  if (Array.isArray(v) && v.length > 0 && v.every(isScalar)) {
125
143
  return `${label(key)} ${v.map(text).join(`\n${" ".repeat(width + 2)}`)}`;
126
144
  }
145
+ if (isObject(v) && Object.keys(v as Row).length > 0) {
146
+ return `${style("bold", key, options)}:\n${indent(record(v as Row, options))}`;
147
+ }
127
148
  return `${label(key)} ${isScalar(v) ? text(v) : summary(v)}`;
128
149
  })
129
150
  .join("\n");
@@ -96,4 +96,41 @@ describe("parseInvocation", () => {
96
96
  );
97
97
  expect(got.parsed.positionals).toEqual(["chat-wf"]);
98
98
  });
99
+
100
+ it("adds a per-command help note", async () => {
101
+ const help = await parseInvocation(["workflow", "get", "--help"], {
102
+ commands: COMMANDS,
103
+ helpNote: (command) =>
104
+ command.tool === "workflow_get" ? "Note for get." : undefined,
105
+ });
106
+
107
+ expect(help.kind === "exit" && help.stdout).toContain("Note for get.");
108
+ });
109
+
110
+ it("fills a defaulted first argument ahead of a required one", async () => {
111
+ const options = {
112
+ commands: COMMANDS,
113
+ defaultArg: (_: unknown, arg: { name: string }) =>
114
+ arg.name === "workflowId" ? "chat-wf" : undefined,
115
+ };
116
+
117
+ const short = command(
118
+ await parseInvocation(["schedule", "get", "s-1"], options),
119
+ );
120
+ expect(short.parsed.positionals).toEqual(["chat-wf", "s-1"]);
121
+
122
+ const full = command(
123
+ await parseInvocation(["schedule", "get", "wf-2", "s-1"], options),
124
+ );
125
+ expect(full.parsed.positionals).toEqual(["wf-2", "s-1"]);
126
+
127
+ const help = await parseInvocation(["schedule", "get", "--help"], options);
128
+ expect(help.kind === "exit" && help.stdout).toContain(
129
+ "[workflow-id] <schedule-id>",
130
+ );
131
+
132
+ await expect(parseInvocation(["schedule", "get"], options)).rejects.toThrow(
133
+ "expected [workflow-id] <schedule-id>, got 0 argument(s)",
134
+ );
135
+ });
99
136
  });
package/src/program.ts CHANGED
@@ -16,6 +16,7 @@ export interface ProgramOptions {
16
16
  description?: string;
17
17
  defaultArg?: (command: CliCommand, arg: Option) => string | undefined;
18
18
  defaultArgNote?: string;
19
+ helpNote?: (command: CliCommand) => string | undefined;
19
20
  }
20
21
 
21
22
  const DOTTED = /^--([a-z0-9-]+\.[a-z0-9.-]+)(?:=(.*))?$/;
@@ -155,18 +156,26 @@ export function defineCommands(
155
156
  const defaults = command.args.map((arg) =>
156
157
  options.defaultArg?.(command, arg),
157
158
  );
158
- command.args.forEach((arg, i) => {
159
+ const defaulted = defaults[0] !== undefined;
160
+ command.args.forEach((arg) => {
159
161
  leaf.argument(
160
- defaults[i] === undefined ? `<${arg.flag}>` : `[${arg.flag}]`,
162
+ defaulted ? `[${arg.flag}]` : `<${arg.flag}>`,
161
163
  arg.description ?? "",
162
164
  );
163
165
  });
164
- if (
165
- defaults.some((value) => value !== undefined) &&
166
- options.defaultArgNote
167
- ) {
168
- leaf.addHelpText("after", `\n${options.defaultArgNote}`);
166
+ if (defaulted) {
167
+ const names = command.args.map((arg, i) =>
168
+ i === 0 ? `[${arg.flag}]` : `<${arg.flag}>`,
169
+ );
170
+ leaf.usage(`[options] ${names.join(" ")}`);
169
171
  }
172
+ const notes = [
173
+ defaults.some((value) => value !== undefined)
174
+ ? options.defaultArgNote
175
+ : undefined,
176
+ options.helpNote?.(command),
177
+ ].filter(Boolean);
178
+ if (notes.length > 0) leaf.addHelpText("after", `\n${notes.join("\n")}`);
170
179
  leafOptions(leaf, command);
171
180
  leaf.action((...args: unknown[]) => {
172
181
  const cmd = args.at(-1) as Command;
@@ -178,7 +187,9 @@ export function defineCommands(
178
187
  positionals = [defaults[0], ...positionals];
179
188
  }
180
189
  if (positionals.length !== command.args.length) {
181
- const expected = command.args.map((a) => `<${a.flag}>`).join(" ");
190
+ const expected = command.args
191
+ .map((a, i) => (i === 0 && defaulted ? `[${a.flag}]` : `<${a.flag}>`))
192
+ .join(" ");
182
193
  throw new UsageError(
183
194
  `expected ${expected}, got ${cmd.args.length} argument(s)`,
184
195
  );