@mnmal-ai/substrate-tui 1.0.0-alpha.1 → 1.0.0-alpha.11

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @mnmal-ai/substrate-tui
2
2
 
3
- A terminal UI for Substrate: ask questions in natural language, browse the federated schema, watch service status and metrics, and, with an unlocked admin identity, run admin operations. Install it with `pnpm add -g @mnmal-ai/substrate-tui`, then run `substrate-tui` in an interactive terminal with `SUBSTRATE_URL` set.
3
+ A terminal UI for Substrate: ask questions in natural language, browse the federated schema, watch service status and metrics, and, with an unlocked admin identity, run admin operations. Install it with `pnpm add -g @mnmal-ai/substrate-tui@next`, then run `substrate-tui` in an interactive terminal with `SUBSTRATE_URL` set.
4
4
 
5
5
  Without an admin identity it is read-only.
6
6
 
@@ -25,7 +25,7 @@ Without an admin identity it is read-only.
25
25
 
26
26
  ## Status row
27
27
 
28
- `● LIVE`, `● OFFLINE` or `● RECONNECTING` (polled from `/health` every 10 s), the service version, the agent kid (`anon` when unauthenticated, `anon?` when whoami fails) and the active namespace.
28
+ `● LIVE`, `● OFFLINE` or `● RECONNECTING` (polled from `/health` every 10 s), the service version, the agent kid (key id; `anon` when unauthenticated, `anon?` when whoami fails) and the active namespace.
29
29
 
30
30
  ## Environment
31
31
 
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { i as isKeyRejection, t as adminCapabilityOf } from "./session-C1D5Ermr.js";
3
3
  import { resolve } from "node:path";
4
- import { AdminBackfillError, AuthError, CURRENT_MARK, NetworkError, STEP_MARKS, adminClaimCapabilities, capabilitiesFor, checkRevocable, checkUpdatable, currentStep, decodeList, describeGrant, diagnose, encodeGrant, flattenGrant, grantEntriesConfer, isActiveAdminKeyRow, isEncryptedKey, makeTokenProvider, parseGrantEntries, planOnboarding, resolveAdminCredential } from "@mnmal-ai/substrate/client";
4
+ import { AdminBackfillError, AuthError, CURRENT_MARK, NetworkError, STEP_MARKS, adminClaimCapabilities, capabilitiesFor, checkRevocable, checkUpdatable, currentStep, decodeList, describeGrant, diagnose, encodeGrant, flattenGrant, grantEntriesConfer, isActiveAdminKeyRow, isEncryptedKey, makeTokenProvider, parseGrantEntries, planOnboarding, resolveAdminCredential, tokenAudience } from "@mnmal-ai/substrate/client";
5
5
  import { createApp, defineModule, sanitise } from "@mnmal-ai/surface-core";
6
6
  import { marathon } from "@mnmal-ai/surface-theme";
7
7
  import { complete } from "@mnmal-ai/substrate-nl/complete";
@@ -76,6 +76,7 @@ function adminModule(deps, io = {}) {
76
76
  kid
77
77
  }, {
78
78
  capabilities: adminClaimCapabilities(deps.adminEnv),
79
+ audience: tokenAudience(deps.endpoint),
79
80
  ...passphrase !== void 0 ? { passphrase } : {}
80
81
  });
81
82
  return verifyAndUnlock(() => `Bearer ${getToken()}`, keyPath, kid, getToken);
@@ -223,6 +224,7 @@ const KIND = {
223
224
  recall: "substrate.recall",
224
225
  clarify: "substrate.clarify",
225
226
  refuse: "substrate.refuse",
227
+ unknown: "substrate.unknown",
226
228
  error: "substrate.error"
227
229
  };
228
230
  const str = (v) => v === void 0 || v === null ? "" : sanitise(String(v));
@@ -231,9 +233,10 @@ const errorLines = (errors) => (errors ?? []).map((e) => {
231
233
  const x = e;
232
234
  return sanitise(`${x.code ?? "error"}${x.message !== void 0 ? `: ${x.message}` : ""}`);
233
235
  });
236
+ const droppedLines = (dropped) => (dropped ?? []).map((d) => sanitise(`dropped "${d.span}": ${d.reason}`));
234
237
  function outcomeChips(o) {
235
238
  if (o.kind !== "rows") return [];
236
- const rejected = !Array.isArray(o.data);
239
+ const rejected = o.data === void 0;
237
240
  return [
238
241
  {
239
242
  label: `▸ ${frameKey(o)}`,
@@ -244,7 +247,7 @@ function outcomeChips(o) {
244
247
  tone: o.source === "handoff" ? "warn" : "dim"
245
248
  },
246
249
  {
247
- label: rejected ? "rejected" : `${o.data.length} rows`,
250
+ label: rejected ? "rejected" : `${o.count ?? (Array.isArray(o.data) ? o.data.length : 0)} rows`,
248
251
  tone: rejected ? "error" : "dim"
249
252
  }
250
253
  ];
@@ -260,6 +263,17 @@ const titleWidth = (columns) => Math.max(COLS.minTitle, columns - (COLS.id + COL
260
263
  const rows = ({ data, theme, size }) => {
261
264
  const titleCols = titleWidth(size.columns);
262
265
  const o = data;
266
+ const lost = droppedLines(o.dropped).map((l, i) => /* @__PURE__ */ jsx(Text, {
267
+ color: theme.color.warn,
268
+ children: ` ${l}`
269
+ }, `d${i}`));
270
+ if (!Array.isArray(o.data) && typeof o.count === "number") return /* @__PURE__ */ jsxs(Box, {
271
+ flexDirection: "column",
272
+ children: [/* @__PURE__ */ jsx(Text, {
273
+ color: theme.color.text,
274
+ children: o.count
275
+ }), lost]
276
+ });
263
277
  if (!Array.isArray(o.data)) return /* @__PURE__ */ jsxs(Box, {
264
278
  flexDirection: "column",
265
279
  children: [/* @__PURE__ */ jsxs(Text, {
@@ -271,33 +285,53 @@ const rows = ({ data, theme, size }) => {
271
285
  }, i))]
272
286
  });
273
287
  const list = o.data;
274
- if (list.length === 0) return /* @__PURE__ */ jsx(Text, {
288
+ if (list.length === 0 && lost.length === 0) return /* @__PURE__ */ jsx(Text, {
275
289
  color: theme.color.dim,
276
290
  children: "no rows"
277
291
  });
278
- return /* @__PURE__ */ jsx(Box, {
292
+ return /* @__PURE__ */ jsxs(Box, {
279
293
  flexDirection: "column",
280
- children: list.map((r, i) => /* @__PURE__ */ jsxs(Text, { children: [
281
- /* @__PURE__ */ jsx(Text, {
294
+ children: [
295
+ list.length === 0 ? /* @__PURE__ */ jsx(Text, {
282
296
  color: theme.color.dim,
283
- children: padCells(str(r.shortId ?? r.id), COLS.id, true)
284
- }),
285
- " ",
286
- /* @__PURE__ */ jsx(Text, {
287
- color: theme.color.text,
288
- children: padCells(str(r.title ?? r.synopsis ?? r.name ?? r.topic ?? r.subject), titleCols)
289
- }),
290
- " ",
291
- /* @__PURE__ */ jsx(Text, {
292
- color: theme.color.info,
293
- children: padCells(str(r.status), COLS.status)
294
- }),
295
- " ",
296
- /* @__PURE__ */ jsx(Text, {
297
- color: theme.color.dimMid,
298
- children: padCells(str(r.project), COLS.project)
299
- })
300
- ] }, i))
297
+ children: "no rows"
298
+ }) : null,
299
+ list.map((r, i) => /* @__PURE__ */ jsxs(Text, { children: [
300
+ /* @__PURE__ */ jsx(Text, {
301
+ color: theme.color.dim,
302
+ children: padCells(str(r.shortId ?? r.id), COLS.id, true)
303
+ }),
304
+ " ",
305
+ /* @__PURE__ */ jsx(Text, {
306
+ color: theme.color.text,
307
+ children: padCells(str(r.title ?? r.synopsis ?? r.name ?? r.topic ?? r.subject), titleCols)
308
+ }),
309
+ " ",
310
+ /* @__PURE__ */ jsx(Text, {
311
+ color: theme.color.info,
312
+ children: padCells(str(r.status), COLS.status)
313
+ }),
314
+ " ",
315
+ /* @__PURE__ */ jsx(Text, {
316
+ color: theme.color.dimMid,
317
+ children: padCells(str(r.project), COLS.project)
318
+ })
319
+ ] }, i)),
320
+ lost
321
+ ]
322
+ });
323
+ };
324
+ const unknown = ({ data, theme }) => {
325
+ const o = data;
326
+ return /* @__PURE__ */ jsxs(Box, {
327
+ flexDirection: "column",
328
+ children: [/* @__PURE__ */ jsx(Text, {
329
+ color: theme.color.warn,
330
+ children: "? unknown: not answered, because part of it was dropped"
331
+ }), droppedLines(o.dropped).map((l, i) => /* @__PURE__ */ jsx(Text, {
332
+ color: theme.color.warn,
333
+ children: ` ${l}`
334
+ }, i))]
301
335
  });
302
336
  };
303
337
  const recall = ({ data, theme }) => {
@@ -385,6 +419,7 @@ const renderers = {
385
419
  [KIND.recall]: recall,
386
420
  [KIND.clarify]: clarify,
387
421
  [KIND.refuse]: refuse,
422
+ [KIND.unknown]: unknown,
388
423
  [KIND.error]: error
389
424
  };
390
425
 
@@ -524,6 +559,7 @@ function askModule(deps) {
524
559
  case "recall":
525
560
  case "clarify":
526
561
  case "refuse":
562
+ case "unknown":
527
563
  last = { lines };
528
564
  if (ev.event === "clarify" && ev.data.options.length > 0) ctx.open("clarify", ev.data);
529
565
  else ctx.close();
@@ -3660,6 +3696,14 @@ function surgeryModule(deps, io = {}) {
3660
3696
 
3661
3697
  //#endregion
3662
3698
  //#region src/app.ts
3699
+ /** marathon with no overlay fill: an empty `raised` means Ink paints no background, so panels sit on the ground. */
3700
+ const substrateTheme = {
3701
+ ...marathon,
3702
+ color: {
3703
+ ...marathon.color,
3704
+ raised: ""
3705
+ }
3706
+ };
3663
3707
  function buildModules(deps, opts = {}) {
3664
3708
  return [
3665
3709
  statusModule(deps, opts),
@@ -3674,11 +3718,12 @@ function buildModules(deps, opts = {}) {
3674
3718
  }
3675
3719
  function substrateAppOptions(deps, opts = {}) {
3676
3720
  return {
3677
- theme: marathon,
3721
+ theme: substrateTheme,
3678
3722
  modules: buildModules(deps, opts),
3679
3723
  defaultTurn: "substrate.ask:ask",
3680
3724
  band: true,
3681
3725
  gutter: 2,
3726
+ anchored: true,
3682
3727
  terminal: false
3683
3728
  };
3684
3729
  }
@@ -3687,4 +3732,4 @@ function createTui(deps, opts = {}) {
3687
3732
  }
3688
3733
 
3689
3734
  //#endregion
3690
- export { buildModules, createTui, substrateAppOptions };
3735
+ export { buildModules, createTui, substrateAppOptions, substrateTheme };
@@ -1,12 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as colorDepthEnv } from "../terminal-XfKFiD7r.js";
3
3
  import { basename } from "node:path";
4
- import { DEFAULT_CAPABILITIES, makeTokenProvider } from "@mnmal-ai/substrate/client";
4
+ import { DEFAULT_CAPABILITIES, makeTokenProvider, tokenAudience } from "@mnmal-ai/substrate/client";
5
+ import { trimTrailingSlashes } from "@mnmal-ai/substrate/sdk";
5
6
 
6
7
  //#region src/config.ts
7
8
  const message = (err) => err instanceof Error ? err.message : String(err);
8
9
  function readConfig(env, cwd) {
9
- const endpoint = (env.SUBSTRATE_URL ?? "").replace(/\/+$/, "");
10
+ const endpoint = trimTrailingSlashes(env.SUBSTRATE_URL ?? "");
10
11
  if (endpoint === "") return { error: "SUBSTRATE_URL is required (e.g. https://substrate.example.com:3100)" };
11
12
  const namespace = env.SUBSTRATE_NAMESPACE ?? "";
12
13
  if (namespace === "") return { error: "SUBSTRATE_NAMESPACE is required (the namespace to watch)" };
@@ -19,7 +20,10 @@ function readConfig(env, cwd) {
19
20
  token = makeTokenProvider({
20
21
  keyPath,
21
22
  kid
22
- }, { capabilities: raw !== void 0 && raw.length > 0 ? raw : DEFAULT_CAPABILITIES });
23
+ }, {
24
+ capabilities: raw !== void 0 && raw.length > 0 ? raw : DEFAULT_CAPABILITIES,
25
+ audience: tokenAudience(endpoint)
26
+ });
23
27
  } catch (err) {
24
28
  return { error: `agent identity: ${message(err)}` };
25
29
  }
@@ -47,7 +51,7 @@ async function main() {
47
51
  }
48
52
  Object.assign(process.env, colorDepthEnv(process.env));
49
53
  const [{ createTui }, { makeDeps }, { prepareTerminal }, { terminalOptions }] = await Promise.all([
50
- import("../app-BLOyJcBh.js"),
54
+ import("../app-xXFNoDVu.js"),
51
55
  import("../deps-WzqsHHiC.js"),
52
56
  import("@mnmal-ai/surface-core/pure"),
53
57
  import("../terminal-CVX2CKWu.js")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mnmal-ai/substrate-tui",
3
- "version": "1.0.0-alpha.1",
3
+ "version": "1.0.0-alpha.11",
4
4
  "description": "Terminal UI for Substrate: ask in natural language, browse the schema, see the service status.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "type": "module",
12
12
  "bin": {
13
- "substrate-tui": "dist/bin/tui.js"
13
+ "substrate-tui": "dist/bin/substrate-tui.js"
14
14
  },
15
15
  "files": [
16
16
  "dist"
@@ -20,17 +20,17 @@
20
20
  "access": "public"
21
21
  },
22
22
  "dependencies": {
23
- "@mnmal-ai/substrate": "1.0.0-alpha.1",
24
- "@mnmal-ai/substrate-nl": "1.0.0-alpha.1",
25
- "@mnmal-ai/surface-core": "0.14.5",
26
- "@mnmal-ai/surface-kit": "0.14.5",
27
- "@mnmal-ai/surface-theme": "0.14.5",
28
- "ink": "^7.1.1",
23
+ "@mnmal-ai/substrate": "1.0.0-alpha.11",
24
+ "@mnmal-ai/substrate-nl": "1.0.0-alpha.11",
25
+ "@mnmal-ai/surface-core": "0.15.0",
26
+ "@mnmal-ai/surface-kit": "0.15.0",
27
+ "@mnmal-ai/surface-theme": "0.15.0",
28
+ "ink": "^8.0.0",
29
29
  "react": "^19.3.0"
30
30
  },
31
31
  "devDependencies": {
32
- "@biomejs/biome": "^2.5.15",
33
- "@mnmal-ai/substrate-config": "1.0.0-alpha.1",
32
+ "@biomejs/biome": "2.6.4",
33
+ "@mnmal-ai/substrate-config": "1.0.0-alpha.11",
34
34
  "@types/node": "^26.6.4",
35
35
  "@types/react": "^19.2.0",
36
36
  "ink-testing-library": "^4.0.0",
@@ -40,7 +40,7 @@
40
40
  "vitest": "^5.0.3"
41
41
  },
42
42
  "engines": {
43
- "node": ">=26.2.0"
43
+ "node": ">=26.12.1"
44
44
  },
45
45
  "scripts": {
46
46
  "build": "tsdown",