@north-light/crouter 0.3.184 → 0.3.186

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 (80) hide show
  1. package/dist/api/dto/broker.d.ts +0 -32
  2. package/dist/api/dto/messages.d.ts +0 -5
  3. package/dist/build-root.js +10 -3
  4. package/dist/builtin-memory/internal/plugins.md +1 -1
  5. package/dist/clients/attach/input/capabilities.js +3 -0
  6. package/dist/clients/attach/input/controller.js +3 -4
  7. package/dist/clients/attach/overlays/mcp.js +1 -3
  8. package/dist/clients/attach/slash/dispatch.js +12 -2
  9. package/dist/clients/attach/viewer.js +349 -349
  10. package/dist/commands/memory/list.js +7 -3
  11. package/dist/commands/pkg/browse/actions.d.ts +7 -0
  12. package/dist/commands/pkg/browse/actions.js +199 -0
  13. package/dist/commands/pkg/browse/catalog.d.ts +9 -0
  14. package/dist/commands/pkg/browse/catalog.js +466 -0
  15. package/dist/commands/pkg/browse/command-view.d.ts +3 -0
  16. package/dist/commands/pkg/browse/command-view.js +191 -0
  17. package/dist/commands/pkg/browse/detail-panel.d.ts +66 -0
  18. package/dist/commands/pkg/browse/detail-panel.js +402 -0
  19. package/dist/commands/pkg/browse/doc-view.d.ts +3 -0
  20. package/dist/commands/pkg/browse/doc-view.js +81 -0
  21. package/dist/commands/pkg/browse/list-panel.d.ts +28 -0
  22. package/dist/commands/pkg/browse/list-panel.js +168 -0
  23. package/dist/commands/pkg/browse/model.d.ts +230 -0
  24. package/dist/commands/pkg/browse/model.js +36 -0
  25. package/dist/commands/pkg/browse/shell.d.ts +93 -0
  26. package/dist/commands/pkg/browse/shell.js +640 -0
  27. package/dist/commands/pkg/browse.d.ts +2 -0
  28. package/dist/commands/pkg/browse.js +27 -0
  29. package/dist/commands/pkg/market-manage.js +13 -4
  30. package/dist/commands/pkg/plugin-manage.js +8 -3
  31. package/dist/commands/pkg.js +2 -1
  32. package/dist/commands/sys/doctor.js +6 -4
  33. package/dist/commands/sys/panels/broker-limits-panel.d.ts +1 -1
  34. package/dist/commands/sys/panels/broker-limits-panel.js +1 -1
  35. package/dist/commands/sys/panels/dependencies-panel.d.ts +1 -1
  36. package/dist/commands/sys/panels/dependencies-panel.js +1 -1
  37. package/dist/commands/sys/panels/keybindings-panel.js +1 -1
  38. package/dist/commands/sys/panels/models-panel.d.ts +1 -1
  39. package/dist/commands/sys/panels/models-panel.js +1 -1
  40. package/dist/commands/sys/panels/plugins-panel.d.ts +1 -1
  41. package/dist/commands/sys/panels/plugins-panel.js +1 -1
  42. package/dist/commands/sys/panels/prefs-panel.d.ts +1 -1
  43. package/dist/commands/sys/panels/prefs-panel.js +1 -1
  44. package/dist/commands/sys/panels/provider-panel.d.ts +1 -1
  45. package/dist/commands/sys/panels/provider-panel.js +1 -1
  46. package/dist/commands/sys/settings-shell.js +1 -1
  47. package/dist/commands/sys/setup-settings-runner.d.ts +7 -18
  48. package/dist/commands/sys/setup-settings-runner.js +3 -52
  49. package/dist/commands/sys/setup-wizard.js +1 -1
  50. package/dist/core/__tests__/serial/command-plugins.test.js +21 -40
  51. package/dist/core/__tests__/session-model.test.js +2 -4
  52. package/dist/core/command-manifests/registry.d.ts +10 -3
  53. package/dist/core/command-manifests/registry.js +36 -22
  54. package/dist/core/command-plugins/compose.js +1 -1
  55. package/dist/core/command-plugins/discovery.js +15 -7
  56. package/dist/core/keybindings/catalog.d.ts +1 -1
  57. package/dist/core/keybindings/catalog.js +1 -0
  58. package/dist/core/resolver.d.ts +2 -2
  59. package/dist/core/runtime/broker/event-projection.d.ts +1 -8
  60. package/dist/core/runtime/broker/event-projection.js +1 -25
  61. package/dist/core/runtime/broker/frame-dispatch.d.ts +0 -2
  62. package/dist/core/runtime/broker/frame-dispatch.js +8 -41
  63. package/dist/core/runtime/broker-protocol.d.ts +1 -46
  64. package/dist/core/runtime/broker.js +1 -8
  65. package/dist/core/runtime/interactive-deliver.d.ts +2 -5
  66. package/dist/core/runtime/interactive-deliver.js +3 -6
  67. package/dist/core/runtime/tmux-bindings.js +2 -0
  68. package/dist/core/session-model/session-state.d.ts +3 -6
  69. package/dist/core/session-model/session-state.js +1 -5
  70. package/dist/core/tui/page-host.d.ts +25 -0
  71. package/dist/core/tui/page-host.js +65 -0
  72. package/dist/daemon/api/handlers/messages.js +1 -7
  73. package/dist/pi-extensions/canvas-bash-valve.js +45 -1
  74. package/dist/types.d.ts +4 -1
  75. package/package.json +1 -1
  76. package/runtime.lock.json +2 -2
  77. package/dist/core/runtime/broker/message-ledger.d.ts +0 -53
  78. package/dist/core/runtime/broker/message-ledger.js +0 -143
  79. /package/dist/{commands/sys/panels → core/tui}/panel.d.ts +0 -0
  80. /package/dist/{commands/sys/panels → core/tui}/panel.js +0 -0
@@ -28,38 +28,6 @@ export interface BrokerWelcomeSnapshot<M = unknown> {
28
28
  export interface BrokerWelcomeFrame<M = unknown> {
29
29
  type: 'welcome';
30
30
  snapshot?: BrokerWelcomeSnapshot<M>;
31
- /** The last few id-bearing user-message dispatches from the broker's message
32
- * ledger, oldest first — so a reattaching follower can key the snapshot's
33
- * trailing user wakes by id (the id a sender minted via `message_id` on
34
- * `POST .../messages`) instead of discarding them heuristically. Live-process
35
- * memory only: a broker restart loses it. Absent on a broker pinned to an
36
- * older runtime generation. */
37
- recentUserMessages?: Array<{
38
- id: string;
39
- text: string;
40
- }>;
41
- }
42
- /**
43
- * The additive identity fields the broker sets on a relayed pi `queue_update`:
44
- * id arrays PARALLEL to pi's text arrays (`steeringIds[i]` identifies
45
- * `steering[i]`). Sender-minted when the send carried `message_id`,
46
- * broker-minted otherwise. Absent on a broker pinned to an older runtime
47
- * generation — consumers fall back to bare texts. Intersect with the pinned pi
48
- * `queue_update` event shape to read them typed.
49
- */
50
- export interface RelayedQueueUpdateIdentity {
51
- steeringIds?: string[];
52
- followUpIds?: string[];
53
- }
54
- /**
55
- * The additive identity field the broker sets on a relayed USER-role
56
- * `message_start`: the message's crouter id — the `message_id` its sender
57
- * minted, or a broker-minted one. Absent when the message carries none (an
58
- * engine-command expansion, a pre-ledger replay, an older broker). Intersect
59
- * with the pinned pi `message_start` event shape to read it typed.
60
- */
61
- export interface RelayedUserMessageStartIdentity {
62
- crtrMessageId?: string;
63
31
  }
64
32
  /**
65
33
  * The broker-control frames a relay consumer reads: `welcome` (catch-up snapshot)
@@ -26,11 +26,6 @@ export interface SendMessageRequest {
26
26
  * body only — rejected with fresh/reopen/situational_context/
27
27
  * output_schema or tier 'deferred'. Absent → durable inbox (unchanged). */
28
28
  delivery?: 'interactive';
29
- /** Sender-minted message identity for a `delivery:'interactive'` send. The
30
- * broker mirrors it through pi's queues and echoes it on the relayed user
31
- * `message_start` (`crtrMessageId`), so an optimistic sender (the gateway)
32
- * retires its pending entry by exact id. Ignored on the durable-inbox path. */
33
- message_id?: string;
34
29
  }
35
30
  /** Result of an immediate message send. */
36
31
  export interface MessageResultDTO {
@@ -14,8 +14,15 @@ const GLOBALS = [
14
14
  * into a single leaf, so it must load ONE subtree — not the attach TUI and
15
15
  * every other subtree it never touches. `crtr` cold-start is dominated by Node
16
16
  * module loading; keeping the
17
- * other subtrees off the path is the biggest, lowest-risk win. */
18
- const SUBTREE_LOADERS = {
17
+ * other subtrees off the path is the biggest, lowest-risk win.
18
+ *
19
+ * Null-prototype: `resolveRoot` indexes this by the raw first argv token, so a
20
+ * normal object literal would resolve `Object.prototype` members — `crtr
21
+ * toString` would build a root from a non-loader and report "Valid children:"
22
+ * with nothing after it, and `crtr hasOwnProperty` would die untyped. With no
23
+ * prototype every non-subtree token misses and falls through to the real
24
+ * unknown-path error. */
25
+ const SUBTREE_LOADERS = Object.assign(Object.create(null), {
19
26
  memory: async () => (await import('./commands/memory.js')).registerMemory(),
20
27
  profile: async () => (await import('./commands/profile.js')).registerProfile(),
21
28
  pkg: async () => (await import('./commands/pkg.js')).registerPkg(),
@@ -26,7 +33,7 @@ const SUBTREE_LOADERS = {
26
33
  cron: async () => (await import('./commands/cron.js')).registerCron(),
27
34
  canvas: async () => (await import('./commands/canvas.js')).registerCanvas(),
28
35
  surface: async () => (await import('./commands/surface.js')).registerSurface(),
29
- };
36
+ });
30
37
  /** Every shipped subtree name. Cheap (no module loading) — the front-door
31
38
  * recursion guard and the dispatcher's first-token routing need only names. */
32
39
  export const SUBTREE_NAMES = Object.freeze(Object.keys(SUBTREE_LOADERS));
@@ -8,7 +8,7 @@ file-read-visibility: none
8
8
 
9
9
  # Authoring crtr plugins
10
10
 
11
- A **plugin** is a directory shipping substrate docs (knowledge and preferences) and other artifact types like `rules/` and `agents/`. Plugins are how you package that content for sharing across machines, projects, and people.
11
+ A **plugin** is a directory shipping substrate docs (knowledge and preferences) and other artifact types like `rules/` and `agents/`. Plugins are how you package that content for sharing across machines, projects, and people. A human can inspect marketplaces and their plugin capability surfaces in `crtr pkg browse`; agents use the scriptable `crtr pkg market browse` and `crtr pkg plugin list` leaves.
12
12
 
13
13
  Audience: LLM agents creating or maintaining a crtr plugin.
14
14
 
@@ -73,6 +73,9 @@ export function viewerOperationForSlash(name, builtin, canvas) {
73
73
  case 'resume-node': return 'canvas-resume';
74
74
  case 'context':
75
75
  case 'node-metadata': return 'local-canvas-inspect';
76
+ // `/plugins` configures the local machine, not this node; observers and
77
+ // remote attaches can open its local popup.
78
+ case 'plugins': return undefined;
76
79
  case 'inbox': return 'canvas-inbox';
77
80
  case 'clear':
78
81
  case 'promote':
@@ -2,7 +2,6 @@
2
2
  // lifetime belongs to InputOverlayOwner; viewer permission belongs to the single
3
3
  // ViewerCapabilities value supplied by session/input-wiring.
4
4
  import { spawn } from 'node:child_process';
5
- import { randomUUID } from 'node:crypto';
6
5
  import { readFileSync, unlinkSync, writeFileSync } from 'node:fs';
7
6
  import { tmpdir } from 'node:os';
8
7
  import { join } from 'node:path';
@@ -46,7 +45,7 @@ export class InputController {
46
45
  this.emitCommand({ type: 'cycle_ladder', direction });
47
46
  }
48
47
  sendInterrupting(text) {
49
- return this.emitDrive({ type: 'prompt', text, interrupt: true, message_id: randomUUID() });
48
+ return this.emitDrive({ type: 'prompt', text, interrupt: true });
50
49
  }
51
50
  attachDialog(request) {
52
51
  this.overlays.attachDialog(request);
@@ -182,7 +181,7 @@ export class InputController {
182
181
  if (delivery !== 'steer' && delivery !== 'prompt')
183
182
  return;
184
183
  const expanded = this.expandImagePlaceholders(classification.kind === 'slash' ? text : classification.text);
185
- if (!this.emitDrive(delivery === 'steer' ? { type: 'steer', text: expanded, message_id: randomUUID() } : { type: 'prompt', text: expanded, message_id: randomUUID() }))
184
+ if (!this.emitDrive(delivery === 'steer' ? { type: 'steer', text: expanded } : { type: 'prompt', text: expanded }))
186
185
  return;
187
186
  this.editor.addToHistory(classification.trimmed);
188
187
  this.editor.setText('');
@@ -194,7 +193,7 @@ export class InputController {
194
193
  const trimmed = text.trim();
195
194
  if (!trimmed)
196
195
  return;
197
- if (!this.emitDrive({ type: 'follow_up', text: this.expandImagePlaceholders(text), message_id: randomUUID() }))
196
+ if (!this.emitDrive({ type: 'follow_up', text: this.expandImagePlaceholders(text) }))
198
197
  return;
199
198
  this.editor.addToHistory(trimmed);
200
199
  this.editor.setText('');
@@ -39,7 +39,7 @@
39
39
  // as cached but fails to reconnect). Action names verified against the
40
40
  // installed adapter (pi-mcp-adapter index.ts: "auth-start" / "auth-complete" /
41
41
  // "ui-messages"; `connect: <name>`).
42
- import { createHash, randomUUID } from 'node:crypto';
42
+ import { createHash } from 'node:crypto';
43
43
  import { existsSync } from 'node:fs';
44
44
  import { homedir } from 'node:os';
45
45
  import { join, resolve } from 'node:path';
@@ -257,7 +257,6 @@ export function startMcpServerAction(row, send, notify) {
257
257
  send({
258
258
  type: 'prompt',
259
259
  text: `Run mcp({ action: "auth-start", server: ${nameJson} }) and show me the returned OAuth URL verbatim (with any instructions). If the server does not need OAuth, run mcp({ connect: ${nameJson} }) instead and report the result.`,
260
- message_id: randomUUID(),
261
260
  });
262
261
  notify(`Asking the engine to start OAuth for ${row.name}…`);
263
262
  }
@@ -265,7 +264,6 @@ export function startMcpServerAction(row, send, notify) {
265
264
  send({
266
265
  type: 'prompt',
267
266
  text: `Run mcp({ connect: ${nameJson} }) to reconnect that MCP server and report the result. If it fails with an authentication or authorization error, run mcp({ action: "auth-start", server: ${nameJson} }) and show me the returned OAuth URL verbatim (with any instructions).`,
268
- message_id: randomUUID(),
269
267
  });
270
268
  notify(`Asking the engine to reconnect ${row.name}…`);
271
269
  }
@@ -22,7 +22,6 @@
22
22
  // SCOPED OUT (no engine method): `/login`, `/logout`, `/share`,
23
23
  // `/trust` — a brief "not supported in attach" notice, never a frame.
24
24
  import { execFile } from 'node:child_process';
25
- import { randomUUID } from 'node:crypto';
26
25
  import { BUILTIN_SLASH_COMMANDS } from '../../../core/runtime/pi-vendored.js';
27
26
  import { surfaceTmuxStyleArgs } from '../../../core/runtime/surface-bg.js';
28
27
  import { viewerOperationForSlash } from '../input/capabilities.js';
@@ -41,6 +40,7 @@ export const CANVAS_SLASH_COMMANDS = [
41
40
  { name: 'promote', description: 'Promote this node to an orchestrator — /promote, or /promote <kind> to specialize' },
42
41
  { name: 'resume-node', description: 'Open the canvas navigator (search/scope/sort/tree) and resume the chosen node' },
43
42
  { name: 'context', description: 'Browse this node + its subscriptions’ context dirs in a read-only nvim popup' },
43
+ { name: 'plugins', description: 'Open the interactive plugin browser for this machine' },
44
44
  { name: 'node-metadata', description: 'Inspect this node — identity, lifecycle, runtime, graph, artifacts, paths, and agent surfaces' },
45
45
  { name: 'mcp', description: 'MCP servers — status from the adapter’s config/cache; Enter (or /mcp <server>) connects, reconnects, or re-auths' },
46
46
  { name: 'rename', description: 'Rename this node everywhere — node row, viewer window, session chip, tmux pane: /rename <name>' },
@@ -346,6 +346,8 @@ function dispatchCanvasCommand(name, arg, ctx) {
346
346
  // A read-only nvim explorer over this node's + its subscriptions' context
347
347
  // dirs, as a self-contained popup.
348
348
  return openContext(ctx);
349
+ case 'plugins':
350
+ return openPlugins(ctx);
349
351
  case 'node-metadata':
350
352
  if (ctx.onNodeMetadata)
351
353
  ctx.onNodeMetadata();
@@ -449,7 +451,6 @@ function promoteNode(arg, ctx) {
449
451
  ctx.send({
450
452
  type: 'prompt',
451
453
  text: 'You have just been promoted to an orchestrator. Your new-role guidance is arriving — read it, author your roadmap, and start delegating.',
452
- message_id: randomUUID(),
453
454
  });
454
455
  });
455
456
  child.stdin?.end();
@@ -594,6 +595,15 @@ function openContext(ctx) {
594
595
  popup(`crtr node inspect context --node ${shellQuote(nodeId)}`);
595
596
  return true;
596
597
  }
598
+ /** `/plugins`. Opens the local plugin browser as a self-contained popup. */
599
+ function openPlugins(ctx) {
600
+ if (process.env['TMUX'] === undefined) {
601
+ ctx.notify('/plugins needs tmux');
602
+ return true;
603
+ }
604
+ popup('crtr pkg browse');
605
+ return true;
606
+ }
597
607
  /** Open `cmd` as a fire-and-forget tmux display-popup (same geometry as the canvas
598
608
  * extensions). tmux runs the trailing string through `sh -c`; the popup owns its
599
609
  * screen and closes itself when the command exits, dropping back to this pane. */