@asteroidai/cli 1.8.204 → 1.9.4

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
@@ -48,16 +48,17 @@ function indent(text2) {
48
48
  function text(value2) {
49
49
  return clean(isScalar(value2) ? String(value2) : JSON.stringify(value2));
50
50
  }
51
- function cell(value2) {
51
+ function cell(value2, options = {}) {
52
52
  if (value2 === null || value2 === void 0) return "";
53
53
  const line = text(value2).replace(/\s+/g, " ");
54
- return line.length > MAX_CELL ? `${line.slice(0, MAX_CELL - 1)}\u2026` : line;
54
+ if (options.full || line.length <= MAX_CELL) return line;
55
+ return `${line.slice(0, MAX_CELL - 1)}\u2026`;
55
56
  }
56
- function columns(rows) {
57
+ function columns(rows, options) {
57
58
  const scalarKeys = [];
58
59
  for (const row of rows) {
59
60
  for (const [key, value2] of Object.entries(row)) {
60
- if (isScalar(value2) && !HIDDEN.test(key) && !scalarKeys.includes(key))
61
+ if (isScalar(value2) && !HIDDEN.test(key.split(".").at(-1) ?? key) && !scalarKeys.includes(key))
61
62
  scalarKeys.push(key);
62
63
  }
63
64
  }
@@ -65,16 +66,29 @@ function columns(rows) {
65
66
  ...PREFERRED.filter((key) => scalarKeys.includes(key)),
66
67
  ...scalarKeys.filter((key) => !PREFERRED.includes(key))
67
68
  ];
68
- return ordered.slice(0, MAX_COLUMNS);
69
+ return options.full ? ordered : ordered.slice(0, MAX_COLUMNS);
69
70
  }
70
71
  function style(format, value2, options) {
71
72
  return options.styled ? styleText(format, value2, { validateStream: false }) : value2;
72
73
  }
73
- function table(rows, options = {}) {
74
- if (rows.length === 0) return "No results.";
75
- const keys = columns(rows);
74
+ function flatten(row) {
75
+ const flat = {};
76
+ for (const [key, value2] of Object.entries(row)) {
77
+ if (!isObject(value2)) {
78
+ flat[key] = value2;
79
+ continue;
80
+ }
81
+ for (const [inner, v] of Object.entries(value2))
82
+ flat[`${key}.${inner}`] = v;
83
+ }
84
+ return flat;
85
+ }
86
+ function table(input, options = {}) {
87
+ if (input.length === 0) return "No results.";
88
+ const rows = input.map(flatten);
89
+ const keys = columns(rows, options);
76
90
  if (keys.length === 0) return rows.map((row) => text(row)).join("\n");
77
- const cells = rows.map((row) => keys.map((key) => cell(row[key])));
91
+ const cells = rows.map((row) => keys.map((key) => cell(row[key], options)));
78
92
  if (options.styled) {
79
93
  const grid = new Table({
80
94
  head: keys.map((key) => style("bold", key, options)),
@@ -91,10 +105,10 @@ function table(rows, options = {}) {
91
105
  "\n"
92
106
  );
93
107
  }
94
- function summary(value2) {
108
+ function summary(value2, options) {
95
109
  if (Array.isArray(value2))
96
110
  return `[${value2.length} item${value2.length === 1 ? "" : "s"}]`;
97
- if (value2 && typeof value2 === "object") return cell(value2);
111
+ if (value2 && typeof value2 === "object") return cell(value2, options);
98
112
  return text(value2);
99
113
  }
100
114
  function record(value2, options) {
@@ -109,7 +123,11 @@ ${indent(table(v, options))}`;
109
123
  return `${label(key)} ${v.map(text).join(`
110
124
  ${" ".repeat(width + 2)}`)}`;
111
125
  }
112
- return `${label(key)} ${isScalar(v) ? text(v) : summary(v)}`;
126
+ if (isObject(v) && Object.keys(v).length > 0) {
127
+ return `${style("bold", key, options)}:
128
+ ${indent(record(v, options))}`;
129
+ }
130
+ return `${label(key)} ${isScalar(v) ? text(v) : summary(v, options)}`;
113
131
  }).join("\n");
114
132
  }
115
133
  function render(value2, options = {}) {
@@ -5312,7 +5330,7 @@ var ApiError = class extends Error {
5312
5330
  status;
5313
5331
  body;
5314
5332
  };
5315
- var VERSION = "1.8.204";
5333
+ var VERSION = "1.9.4";
5316
5334
  function problem(status, body, fallback) {
5317
5335
  if (body && typeof body === "object") {
5318
5336
  const record2 = body;
@@ -5861,7 +5879,10 @@ async function runCommand(command, parsed, deps) {
5861
5879
  }
5862
5880
  if (response.json !== void 0) {
5863
5881
  deps.stdout(
5864
- `${parsed.json ? JSON.stringify(response.json, null, 2) : render(response.json, { styled: deps.styled })}
5882
+ `${parsed.json ? JSON.stringify(response.json, null, 2) : render(response.json, {
5883
+ styled: deps.styled,
5884
+ full: !deps.stdoutIsTTY
5885
+ })}
5865
5886
  `
5866
5887
  );
5867
5888
  return 0;
@@ -5870,7 +5891,7 @@ async function runCommand(command, parsed, deps) {
5870
5891
  `);
5871
5892
  return 0;
5872
5893
  }
5873
- var DESCRIPTION = "The Asteroid CLI. Sign in with `asteroid login`, or set ASTEROID_API_KEY.";
5894
+ var DESCRIPTION = "The Asteroid CLI. Sign in with `asteroid login`, or set ASTEROID_API_KEY.\nAt a terminal, tables show up to 6 columns and cut long cells to fit; when output is piped, they show every column in full. Use `--json` for every field.";
5874
5895
  async function run(argv2, deps) {
5875
5896
  try {
5876
5897
  const invocation = await parseInvocation(argv2, {
package/dist/core.js CHANGED
@@ -4560,16 +4560,17 @@ function indent(text2) {
4560
4560
  function text(value2) {
4561
4561
  return clean(isScalar(value2) ? String(value2) : JSON.stringify(value2));
4562
4562
  }
4563
- function cell(value2) {
4563
+ function cell(value2, options = {}) {
4564
4564
  if (value2 === null || value2 === void 0) return "";
4565
4565
  const line = text(value2).replace(/\s+/g, " ");
4566
- return line.length > MAX_CELL ? `${line.slice(0, MAX_CELL - 1)}\u2026` : line;
4566
+ if (options.full || line.length <= MAX_CELL) return line;
4567
+ return `${line.slice(0, MAX_CELL - 1)}\u2026`;
4567
4568
  }
4568
- function columns(rows) {
4569
+ function columns(rows, options) {
4569
4570
  const scalarKeys = [];
4570
4571
  for (const row of rows) {
4571
4572
  for (const [key, value2] of Object.entries(row)) {
4572
- if (isScalar(value2) && !HIDDEN.test(key) && !scalarKeys.includes(key))
4573
+ if (isScalar(value2) && !HIDDEN.test(key.split(".").at(-1) ?? key) && !scalarKeys.includes(key))
4573
4574
  scalarKeys.push(key);
4574
4575
  }
4575
4576
  }
@@ -4577,16 +4578,29 @@ function columns(rows) {
4577
4578
  ...PREFERRED.filter((key) => scalarKeys.includes(key)),
4578
4579
  ...scalarKeys.filter((key) => !PREFERRED.includes(key))
4579
4580
  ];
4580
- return ordered.slice(0, MAX_COLUMNS);
4581
+ return options.full ? ordered : ordered.slice(0, MAX_COLUMNS);
4581
4582
  }
4582
4583
  function style(format, value2, options) {
4583
4584
  return options.styled ? styleText(format, value2, { validateStream: false }) : value2;
4584
4585
  }
4585
- function table(rows, options = {}) {
4586
- if (rows.length === 0) return "No results.";
4587
- const keys = columns(rows);
4586
+ function flatten(row) {
4587
+ const flat = {};
4588
+ for (const [key, value2] of Object.entries(row)) {
4589
+ if (!isObject(value2)) {
4590
+ flat[key] = value2;
4591
+ continue;
4592
+ }
4593
+ for (const [inner, v] of Object.entries(value2))
4594
+ flat[`${key}.${inner}`] = v;
4595
+ }
4596
+ return flat;
4597
+ }
4598
+ function table(input, options = {}) {
4599
+ if (input.length === 0) return "No results.";
4600
+ const rows = input.map(flatten);
4601
+ const keys = columns(rows, options);
4588
4602
  if (keys.length === 0) return rows.map((row) => text(row)).join("\n");
4589
- const cells = rows.map((row) => keys.map((key) => cell(row[key])));
4603
+ const cells = rows.map((row) => keys.map((key) => cell(row[key], options)));
4590
4604
  if (options.styled) {
4591
4605
  const grid = new Table({
4592
4606
  head: keys.map((key) => style("bold", key, options)),
@@ -4603,10 +4617,10 @@ function table(rows, options = {}) {
4603
4617
  "\n"
4604
4618
  );
4605
4619
  }
4606
- function summary(value2) {
4620
+ function summary(value2, options) {
4607
4621
  if (Array.isArray(value2))
4608
4622
  return `[${value2.length} item${value2.length === 1 ? "" : "s"}]`;
4609
- if (value2 && typeof value2 === "object") return cell(value2);
4623
+ if (value2 && typeof value2 === "object") return cell(value2, options);
4610
4624
  return text(value2);
4611
4625
  }
4612
4626
  function record(value2, options) {
@@ -4621,7 +4635,11 @@ ${indent(table(v, options))}`;
4621
4635
  return `${label(key)} ${v.map(text).join(`
4622
4636
  ${" ".repeat(width + 2)}`)}`;
4623
4637
  }
4624
- return `${label(key)} ${isScalar(v) ? text(v) : summary(v)}`;
4638
+ if (isObject(v) && Object.keys(v).length > 0) {
4639
+ return `${style("bold", key, options)}:
4640
+ ${indent(record(v, options))}`;
4641
+ }
4642
+ return `${label(key)} ${isScalar(v) ? text(v) : summary(v, options)}`;
4625
4643
  }).join("\n");
4626
4644
  }
4627
4645
  function render(value2, options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteroidai/cli",
3
- "version": "1.8.204",
3
+ "version": "1.9.4",
4
4
  "description": "The asteroid command-line interface for the Asteroid platform.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -23,4 +23,49 @@ 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
+ });
52
+
53
+ it("keeps long cells whole when asked", () => {
54
+ const secrets = `ECW(51d1db07-a6e1-4b1d-8303-a472b78923e8)[PASSWORD=##ECW_PASSWORD##]`;
55
+ const rows = [{ id: "p-1", secrets }];
56
+ expect(render(rows)).toContain("…");
57
+ expect(render(rows, { full: true })).toContain(secrets);
58
+ });
59
+
60
+ it("shows every column when asked, six otherwise", () => {
61
+ const row = Object.fromEntries(
62
+ ["id", "a", "b", "c", "d", "e", "f", "g"].map((key) => [
63
+ key,
64
+ key.toUpperCase(),
65
+ ]),
66
+ );
67
+ const header = (out: string) => out.split("\n")[0].trim().split(/\s+/);
68
+ expect(header(render([row]))).toHaveLength(6);
69
+ expect(header(render([row], { full: true }))).toHaveLength(8);
70
+ });
26
71
  });
package/src/output.ts CHANGED
@@ -22,6 +22,7 @@ type Row = Record<string, unknown>;
22
22
 
23
23
  export interface RenderOptions {
24
24
  styled?: boolean;
25
+ full?: boolean;
25
26
  }
26
27
 
27
28
  export function clean(text: string): string {
@@ -49,17 +50,22 @@ function text(value: unknown): string {
49
50
  return clean(isScalar(value) ? String(value) : JSON.stringify(value));
50
51
  }
51
52
 
52
- function cell(value: unknown): string {
53
+ function cell(value: unknown, options: RenderOptions = {}): string {
53
54
  if (value === null || value === undefined) return "";
54
55
  const line = text(value).replace(/\s+/g, " ");
55
- return line.length > MAX_CELL ? `${line.slice(0, MAX_CELL - 1)}…` : line;
56
+ if (options.full || line.length <= MAX_CELL) return line;
57
+ return `${line.slice(0, MAX_CELL - 1)}…`;
56
58
  }
57
59
 
58
- function columns(rows: Row[]): string[] {
60
+ function columns(rows: Row[], options: RenderOptions): string[] {
59
61
  const scalarKeys: string[] = [];
60
62
  for (const row of rows) {
61
63
  for (const [key, value] of Object.entries(row)) {
62
- if (isScalar(value) && !HIDDEN.test(key) && !scalarKeys.includes(key))
64
+ if (
65
+ isScalar(value) &&
66
+ !HIDDEN.test(key.split(".").at(-1) ?? key) &&
67
+ !scalarKeys.includes(key)
68
+ )
63
69
  scalarKeys.push(key);
64
70
  }
65
71
  }
@@ -67,7 +73,7 @@ function columns(rows: Row[]): string[] {
67
73
  ...PREFERRED.filter((key) => scalarKeys.includes(key)),
68
74
  ...scalarKeys.filter((key) => !PREFERRED.includes(key)),
69
75
  ];
70
- return ordered.slice(0, MAX_COLUMNS);
76
+ return options.full ? ordered : ordered.slice(0, MAX_COLUMNS);
71
77
  }
72
78
 
73
79
  function style(
@@ -80,11 +86,25 @@ function style(
80
86
  : value;
81
87
  }
82
88
 
83
- export function table(rows: Row[], options: RenderOptions = {}): string {
84
- if (rows.length === 0) return "No results.";
85
- const keys = columns(rows);
89
+ function flatten(row: Row): Row {
90
+ const flat: Row = {};
91
+ for (const [key, value] of Object.entries(row)) {
92
+ if (!isObject(value)) {
93
+ flat[key] = value;
94
+ continue;
95
+ }
96
+ for (const [inner, v] of Object.entries(value as Row))
97
+ flat[`${key}.${inner}`] = v;
98
+ }
99
+ return flat;
100
+ }
101
+
102
+ export function table(input: Row[], options: RenderOptions = {}): string {
103
+ if (input.length === 0) return "No results.";
104
+ const rows = input.map(flatten);
105
+ const keys = columns(rows, options);
86
106
  if (keys.length === 0) return rows.map((row) => text(row)).join("\n");
87
- const cells = rows.map((row) => keys.map((key) => cell(row[key])));
107
+ const cells = rows.map((row) => keys.map((key) => cell(row[key], options)));
88
108
  if (options.styled) {
89
109
  const grid = new Table({
90
110
  head: keys.map((key) => style("bold", key, options)),
@@ -106,10 +126,10 @@ export function table(rows: Row[], options: RenderOptions = {}): string {
106
126
  );
107
127
  }
108
128
 
109
- function summary(value: unknown): string {
129
+ function summary(value: unknown, options: RenderOptions): string {
110
130
  if (Array.isArray(value))
111
131
  return `[${value.length} item${value.length === 1 ? "" : "s"}]`;
112
- if (value && typeof value === "object") return cell(value);
132
+ if (value && typeof value === "object") return cell(value, options);
113
133
  return text(value);
114
134
  }
115
135
 
@@ -124,7 +144,10 @@ function record(value: Row, options: RenderOptions): string {
124
144
  if (Array.isArray(v) && v.length > 0 && v.every(isScalar)) {
125
145
  return `${label(key)} ${v.map(text).join(`\n${" ".repeat(width + 2)}`)}`;
126
146
  }
127
- return `${label(key)} ${isScalar(v) ? text(v) : summary(v)}`;
147
+ if (isObject(v) && Object.keys(v as Row).length > 0) {
148
+ return `${style("bold", key, options)}:\n${indent(record(v as Row, options))}`;
149
+ }
150
+ return `${label(key)} ${isScalar(v) ? text(v) : summary(v, options)}`;
128
151
  })
129
152
  .join("\n");
130
153
  }
package/src/reference.ts CHANGED
@@ -138,6 +138,7 @@ export function renderReference(commands: CliCommand[]): string {
138
138
  "Sign in with `asteroid login`, or set `ASTEROID_API_KEY` for CI and agents.",
139
139
  "Commands that return data print compact text. Add `--json` to get the response as JSON.",
140
140
  "The text output may change between releases. Scripts should read `--json`.",
141
+ "At a terminal, tables show up to 6 columns and cut long cells to fit; when output is piped, they show every column in full. Use `--json` for every field.",
141
142
  "Downloads write raw bytes. `login`, `logout` and `use` only report status on stderr.",
142
143
  "Exit codes: 0 ok, 1 API error, 2 usage error.",
143
144
  "",
package/src/run.test.ts CHANGED
@@ -269,6 +269,26 @@ describe("guards", () => {
269
269
  });
270
270
  });
271
271
 
272
+ describe("cell width", () => {
273
+ const long = "x".repeat(80);
274
+ const reply = () => json({ items: [{ id: "w-1", name: long }] });
275
+
276
+ it("keeps cells whole when stdout is not a terminal", async () => {
277
+ const h = harness({ ASTEROID_API_KEY: "sk_test" }, reply);
278
+ await run(["workflow", "list"], h.deps);
279
+ expect(h.out.join("")).toContain(long);
280
+ });
281
+
282
+ it("cuts long cells at a terminal", async () => {
283
+ const h = harness({ ASTEROID_API_KEY: "sk_test" }, reply);
284
+ h.deps.stdoutIsTTY = true;
285
+ await run(["workflow", "list"], h.deps);
286
+ const out = h.out.join("");
287
+ expect(out).toContain(`${"x".repeat(59)}…`);
288
+ expect(out).not.toContain(long);
289
+ });
290
+ });
291
+
272
292
  describe("terminal safety", () => {
273
293
  it("refuses to print a download to a terminal", async () => {
274
294
  const h = harness(
package/src/run.ts CHANGED
@@ -138,7 +138,14 @@ async function runCommand(
138
138
  }
139
139
  if (response.json !== undefined) {
140
140
  deps.stdout(
141
- `${parsed.json ? JSON.stringify(response.json, null, 2) : render(response.json, { styled: deps.styled })}\n`,
141
+ `${
142
+ parsed.json
143
+ ? JSON.stringify(response.json, null, 2)
144
+ : render(response.json, {
145
+ styled: deps.styled,
146
+ full: !deps.stdoutIsTTY,
147
+ })
148
+ }\n`,
142
149
  );
143
150
  return 0;
144
151
  }
@@ -147,7 +154,7 @@ async function runCommand(
147
154
  }
148
155
 
149
156
  const DESCRIPTION =
150
- "The Asteroid CLI. Sign in with `asteroid login`, or set ASTEROID_API_KEY.";
157
+ "The Asteroid CLI. Sign in with `asteroid login`, or set ASTEROID_API_KEY.\nAt a terminal, tables show up to 6 columns and cut long cells to fit; when output is piped, they show every column in full. Use `--json` for every field.";
151
158
 
152
159
  export async function run(argv: string[], deps: Deps): Promise<number> {
153
160
  try {