@north-light/crouter 0.3.196 → 0.3.198
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/api/client.d.ts +2 -2
- package/dist/api/dto/inbox.d.ts +46 -0
- package/dist/builtin-memory/00-runtime-base.md +3 -3
- package/dist/builtin-memory/internal/nodes-and-canvas.md +1 -1
- package/dist/builtin-memory/internal/storage-tiers.md +1 -1
- package/dist/builtin-memory/wedged-child-on-runaway-bash.md +1 -1
- package/dist/clients/attach/render/chat-view.d.ts +3 -8
- package/dist/clients/attach/render/chat-view.js +3 -24
- package/dist/clients/attach/render/markdown-source.js +0 -6
- package/dist/clients/attach/render/tool-calls.js +16 -2
- package/dist/clients/attach/viewer.js +613 -602
- package/dist/clients/inbox/__tests__/serial/inbox-controller.test.js +2 -2
- package/dist/clients/inbox/__tests__/serial/mount-panel.test.js +11 -4
- package/dist/clients/inbox/controller.js +4 -4
- package/dist/clients/inbox/page-adapter.d.ts +2 -1
- package/dist/clients/inbox/tui/render.js +46 -25
- package/dist/clients/inbox/tui/slots.d.ts +1 -3
- package/dist/clients/inbox/tui/slots.js +8 -37
- package/dist/clients/inbox/tui/types.d.ts +4 -3
- package/dist/commands/__tests__/human.test.js +160 -23
- package/dist/commands/canvas-browse.js +1 -1
- package/dist/commands/human/doc.js +21 -74
- package/dist/commands/human/html.d.ts +2 -0
- package/dist/commands/human/html.js +81 -0
- package/dist/commands/human/prompts.d.ts +5 -4
- package/dist/commands/human/prompts.js +90 -168
- package/dist/commands/human/shared.d.ts +5 -6
- package/dist/commands/human/shared.js +13 -61
- package/dist/commands/human.js +7 -2
- package/dist/core/__tests__/daemon-boot.test.js +7 -4
- package/dist/core/__tests__/dead-node-policy-table.test.js +20 -36
- package/dist/core/__tests__/human-deliver.test.js +28 -37
- package/dist/core/__tests__/seam/broker-crash-teardown.test.js +13 -11
- package/dist/core/__tests__/seam/dormancy-release.test.js +63 -4
- package/dist/core/__tests__/serial/human-deliver-e2e.test.js +3 -13
- package/dist/core/canvas/__tests__/attention.test.js +2 -2
- package/dist/core/canvas/__tests__/render-remote.test.js +2 -2
- package/dist/core/canvas/browse/app.d.ts +1 -1
- package/dist/core/canvas/browse/app.js +21 -3
- package/dist/core/canvas/browse/render.d.ts +4 -0
- package/dist/core/canvas/browse/render.js +5 -1
- package/dist/core/canvas/canvas.d.ts +26 -0
- package/dist/core/canvas/canvas.js +55 -0
- package/dist/core/command.js +6 -11
- package/dist/core/config.js +3 -2
- package/dist/core/help.d.ts +3 -0
- package/dist/core/human/__tests__/html-markdown.test.d.ts +1 -0
- package/dist/core/human/__tests__/html-markdown.test.js +52 -0
- package/dist/core/human/__tests__/page-catalog.test.d.ts +1 -0
- package/dist/core/human/__tests__/page-catalog.test.js +16 -0
- package/dist/core/human/__tests__/page-render.test.js +40 -23
- package/dist/core/human/__tests__/page-tickets.test.js +48 -27
- package/dist/core/human/__tests__/page.test.js +48 -65
- package/dist/core/human/__tests__/serial/inbox-core.test.js +5 -15
- package/dist/core/human/answer-text.d.ts +3 -0
- package/dist/core/human/answer-text.js +98 -0
- package/dist/core/human/answer.d.ts +51 -0
- package/dist/core/human/answer.js +125 -0
- package/dist/core/human/component-docs.d.ts +14 -0
- package/dist/core/human/component-docs.js +161 -0
- package/dist/core/human/html-markdown.d.ts +3 -0
- package/dist/core/human/html-markdown.js +288 -0
- package/dist/core/human/page-catalog.d.ts +19 -3
- package/dist/core/human/page-catalog.js +73 -16
- package/dist/core/human/page-render.d.ts +3 -2
- package/dist/core/human/page-render.js +19 -45
- package/dist/core/human/page-schema.d.ts +108 -8
- package/dist/core/human/page-schema.js +88 -31
- package/dist/core/human/page-synth.d.ts +2 -4
- package/dist/core/human/page-synth.js +23 -7
- package/dist/core/human/page.d.ts +14 -7
- package/dist/core/human/page.js +178 -90
- package/dist/core/human/review-schema.d.ts +1 -0
- package/dist/core/human/review-schema.js +1 -1
- package/dist/core/human/scan.d.ts +3 -2
- package/dist/core/human/scan.js +7 -8
- package/dist/core/human/summary.d.ts +4 -7
- package/dist/core/human/summary.js +4 -60
- package/dist/core/human/tickets.d.ts +10 -9
- package/dist/core/human/tickets.js +5 -3
- package/dist/core/human/types.d.ts +4 -0
- package/dist/core/keybindings/catalog.d.ts +2 -2
- package/dist/core/keybindings/catalog.js +1 -0
- package/dist/core/review/realize.js +9 -1
- package/dist/core/runtime/boot-root.js +6 -1
- package/dist/core/runtime/broker.js +8 -6
- package/dist/core/runtime/busy.d.ts +4 -3
- package/dist/core/runtime/busy.js +4 -3
- package/dist/core/runtime/spawn.js +4 -4
- package/dist/core/termrender/display.d.ts +7 -3
- package/dist/core/termrender/display.js +45 -9
- package/dist/core/termrender/termrender.d.ts +3 -12
- package/dist/core/termrender/termrender.js +6 -37
- package/dist/core/user-settings.d.ts +17 -2
- package/dist/core/user-settings.js +32 -11
- package/dist/daemon/api/handlers/human.js +1 -1
- package/dist/daemon/api/handlers/inbox.js +22 -7
- package/dist/daemon/cron-run.js +16 -2
- package/dist/daemon/crtrd.js +1 -1
- package/dist/daemon/fleet.d.ts +22 -10
- package/dist/daemon/fleet.js +43 -24
- package/dist/daemon/human/finish.d.ts +2 -1
- package/dist/daemon/human/finish.js +45 -28
- package/dist/daemon/human/sweep.js +4 -2
- package/dist/daemon/reconcilers/broker-supervision.js +11 -8
- package/dist/daemon/reconcilers/dormant-inbox.js +11 -6
- package/dist/daemon/reconcilers/live-obligation.d.ts +21 -1
- package/dist/daemon/reconcilers/live-obligation.js +30 -2
- package/dist/daemon/reconcilers/storage-maintenance.d.ts +5 -0
- package/dist/daemon/reconcilers/storage-maintenance.js +28 -1
- package/dist/daemon/review/sweep.js +12 -1
- package/dist/pages/bundle.css +1 -1
- package/dist/pages/bundle.js +1008 -577
- package/dist/pages/comments.d.ts +29 -0
- package/dist/pages/comments.js +51 -0
- package/dist/pages/controls.d.ts +98 -0
- package/dist/pages/controls.js +261 -0
- package/dist/pages/elements/cards.d.ts +15 -4
- package/dist/pages/elements/cards.js +299 -186
- package/dist/pages/elements/chart.d.ts +22 -0
- package/dist/pages/elements/chart.js +107 -91
- package/dist/pages/elements/options.d.ts +9 -0
- package/dist/pages/elements/options.js +268 -155
- package/dist/pages/elements/pages.d.ts +6 -0
- package/dist/pages/elements/pages.js +60 -38
- package/dist/pages/elements/slot.d.ts +5 -0
- package/dist/pages/elements/slot.js +17 -10
- package/dist/pages/elements/table.d.ts +17 -0
- package/dist/pages/elements/table.js +336 -228
- package/dist/pages/elements/text.d.ts +9 -3
- package/dist/pages/elements/text.js +350 -143
- package/dist/pages/host.d.ts +0 -24
- package/dist/pages/host.js +0 -17
- package/dist/pages/readonly.d.ts +24 -0
- package/dist/pages/readonly.js +31 -0
- package/dist/pages/responses.d.ts +37 -0
- package/dist/pages/responses.js +56 -0
- package/dist/pi-extensions/canvas-bash-valve.d.ts +9 -1
- package/dist/pi-extensions/canvas-bash-valve.js +12 -8
- package/dist/types.d.ts +19 -5
- package/dist/types.js +2 -1
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
- package/scripts/install-runtime.mjs +19 -24
- package/dist/clients/attach/render/html-markdown.d.ts +0 -13
- package/dist/clients/attach/render/html-markdown.js +0 -206
- package/dist/core/human/markdown-html.d.ts +0 -8
- package/dist/core/human/markdown-html.js +0 -581
|
@@ -5,7 +5,7 @@ import { z } from 'zod';
|
|
|
5
5
|
import { buildPageSummary } from './summary.js';
|
|
6
6
|
import { atomicWriteJson, clearProgress, claimPath, pageManifestPath, pagePath, progressPath, publishJsonExclusive, requireTicket, responsePath, reviewPath } from './convention.js';
|
|
7
7
|
import { validateReviewDescriptor, validateReviewProjection } from './review-schema.js';
|
|
8
|
-
import { derivePageManifestFromDocument, parsePage, PageAuthoringError } from './page.js';
|
|
8
|
+
import { derivePageManifestFromDocument, parsePage, PageAuthoringError, snapshotPageSources } from './page.js';
|
|
9
9
|
import { validatePageResponses, validatePartialPageResponses } from './page-schema.js';
|
|
10
10
|
import { signalInboxActivity } from './root.js';
|
|
11
11
|
import { readTicketClaim, releaseClaimLocked, withTicketLock } from './claim.js';
|
|
@@ -62,7 +62,9 @@ export function preparePage(opts) {
|
|
|
62
62
|
throw new PageAuthoringError(`page source file does not exist: ${opts.sourceFile}`);
|
|
63
63
|
const document = readFileSync(opts.sourceFile, 'utf8');
|
|
64
64
|
const stamped = { ...(opts.source ?? {}), blockedSince: opts.source?.blockedSince ?? new Date().toISOString() };
|
|
65
|
-
|
|
65
|
+
const authored = derivePageManifestFromDocument(document, opts.dialect, opts.productKinds ?? [], stamped);
|
|
66
|
+
const manifest = snapshotPageSources(authored, opts.productKinds ?? []);
|
|
67
|
+
return { dialect: opts.dialect, document, manifest };
|
|
66
68
|
}
|
|
67
69
|
/** Publish one prepared page ticket: the authored source plus its manifest, exclusively. */
|
|
68
70
|
export function publishPage(dir, prepared) {
|
|
@@ -170,7 +172,7 @@ function completePageLocked(dir, responses, claimToken, opts) {
|
|
|
170
172
|
requireClaimOwnership(dir, claimToken);
|
|
171
173
|
const manifest = requirePage(dir, opts.productKinds ?? []);
|
|
172
174
|
const parsedResponses = validatePageResponses(manifest, responses);
|
|
173
|
-
const result = { schema: 'crtr.page-response/v1', kind: 'page', responses: parsedResponses, summary: buildPageSummary(manifest, parsedResponses), completedAt: opts.completedAt ?? new Date().toISOString() };
|
|
175
|
+
const result = { schema: 'crtr.page-response/v1', kind: 'page', responses: parsedResponses, summary: buildPageSummary(manifest, parsedResponses, opts.productKinds), completedAt: opts.completedAt ?? new Date().toISOString() };
|
|
174
176
|
const won = exclusiveResult(dir, result);
|
|
175
177
|
clearOwnedWork(dir, claimToken);
|
|
176
178
|
return { won, result: won ? result : readTicketResult(dir) ?? result };
|
|
@@ -4,6 +4,8 @@ export interface TicketSource {
|
|
|
4
4
|
sessionName?: string;
|
|
5
5
|
askedBy?: string;
|
|
6
6
|
blockedSince?: string;
|
|
7
|
+
/** Profile name the originating node runs under. Product consumers use this stable name to resolve the applet or worker identity behind the ticket. */
|
|
8
|
+
profileName?: string;
|
|
7
9
|
/** Originating canvas node id (CRTR_NODE_ID) when the ask was raised inside a crouter canvas node. */
|
|
8
10
|
nodeId?: string;
|
|
9
11
|
}
|
|
@@ -48,6 +50,8 @@ export interface ReviewTicketSummary extends TicketSummaryBase {
|
|
|
48
50
|
/** Page triage fields: a client picks a transcript rendering without a second fetch. */
|
|
49
51
|
export interface PageTicketSummary extends TicketSummaryBase {
|
|
50
52
|
kind: 'page';
|
|
53
|
+
/** Present on resolved history entries; the stored one-line response digest. */
|
|
54
|
+
answerDigest?: string;
|
|
51
55
|
dialect: 'md' | 'html';
|
|
52
56
|
placement: 'inline' | 'panel';
|
|
53
57
|
steps: number;
|
|
@@ -3,13 +3,13 @@ import type { AttachBindingId, AttachMenuBindingId } from './attach-control.js';
|
|
|
3
3
|
type TmuxMenuAction = 'open' | 'attach' | 'show' | 'view-settings' | 'fold-tools' | 'metadata' | 'promote' | 'demote-mode' | 'resume' | 'fork' | 'demote' | 'make-resident' | 'detach' | 'close-subtree' | 'background-bash' | 'file-review' | 'pin' | 'reload-viewer' | 'recap' | 'context' | 'providers' | 'plugins' | 'graph' | 'focus-manager' | 'copy-id' | 'issues';
|
|
4
4
|
type TmuxMenuEntryAction = Exclude<TmuxMenuAction, 'open' | 'attach'> | `issue.${'idea' | 'bug'}`;
|
|
5
5
|
type GraphAction = 'down' | 'up' | 'first' | 'last' | 'collapse-or-manager' | 'expand-or-child' | 'focus' | 'revive' | 'focus-manager' | 'close-subtree' | 'dismiss';
|
|
6
|
-
type BrowseAction = 'quit' | 'down' | 'up' | 'first' | 'last' | 'expand-or-child' | 'collapse-or-parent' | 'toggle-fold' | 'tab-next' | 'tab-previous' | `tab.${1 | 2 | 3 | 4 | 5}` | 'close-subtree' | 'confirm-close' | 'sort' | 'scope' | 'profile' | 'residents' | 'preview' | 'search' | 'revive-now' | 'focus' | 'focus-background';
|
|
6
|
+
type BrowseAction = 'quit' | 'down' | 'up' | 'first' | 'last' | 'expand-or-child' | 'collapse-or-parent' | 'toggle-fold' | 'tab-next' | 'tab-previous' | `tab.${1 | 2 | 3 | 4 | 5}` | 'close-subtree' | 'confirm-close' | 'sort' | 'scope' | 'profile' | 'residents' | 'preview' | 'search' | 'revive-now' | 'focus' | 'focus-background' | 'yank-id';
|
|
7
7
|
type ProfileMenuAction = 'up' | 'down' | 'select' | 'accept-default' | 'abort' | `slot.${1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9}` | 'create' | 'search-existing' | 'use-root' | 'set-default' | 'add-directory' | 'unpin';
|
|
8
8
|
type SetupAction = 'cancel' | 'tab-next' | 'tab-previous' | 'open' | 'list-down' | 'list-up' | 'toggle' | 'details' | 'advance' | 'cell-previous' | 'cell-next';
|
|
9
9
|
type SetupKeybindingAction = 'edit' | 'commit-edit' | 'cancel-edit' | 'unbind' | 'reset-row' | 'reset-all' | 'save' | 'discard' | 'page-up' | 'page-down' | 'filter' | 'filter-delete' | 'filter-clear';
|
|
10
10
|
type SetupModelsAction = 'open' | 'commit' | 'cancel' | 'scope-next';
|
|
11
11
|
export type BindingId = `crtr.tmux.menu.${TmuxMenuAction}` | `crtr.tmux.menu.issue.${'idea' | 'bug'}` | AttachMenuBindingId | `crtr.tmux.menu-global.${TmuxMenuEntryAction}` | `crtr.tmux.node.${'next' | 'previous'}` | 'crtr.tmux.inbox.toggle' | `crtr.attach.scroll.${'up' | 'down' | 'page-up' | 'page-down' | 'live'}` | 'crtr.attach.mouse.toggle' | 'crtr.attach.copy.message' | 'crtr.attach.help.toggle' | AttachBindingId | `crtr.graph.${GraphAction}` | `crtr.graph.confirm.${'accept' | 'cancel'}` | `crtr.inspector.${'force-quit' | 'down' | 'up' | 'section-next' | 'section-prev' | 'page-down' | 'page-up' | 'toggle-detail' | 'cancel-item' | 'open-log' | 'confirm' | 'dismiss' | 'retarget' | 'refresh' | 'quit'}` | `crtr.browse.${BrowseAction}` | `crtr.browse.search.${'cancel' | 'commit' | 'delete' | 'quit'}` | `crtr.profile.menu.${ProfileMenuAction}` | `crtr.profile.search.${'up' | 'down' | 'pick' | 'back' | 'delete' | 'abort'}` | `crtr.setup.${SetupAction}` | `crtr.setup.confirm.${'accept' | 'back'}` | `crtr.setup.keybindings.${SetupKeybindingAction}` | `crtr.setup.models.${SetupModelsAction}`;
|
|
12
|
-
export declare const BINDING_CATALOG: readonly [BindingDefinition<"crtr.tmux.menu.open">, BindingDefinition<"crtr.tmux.menu.attach">, ...(BindingDefinition<BindingId> | BindingDefinition<"crtr.tmux.node.next"> | BindingDefinition<"crtr.tmux.node.previous"> | BindingDefinition<"crtr.tmux.inbox.toggle"> | BindingDefinition<"crtr.attach.scroll.up"> | BindingDefinition<"crtr.attach.scroll.down"> | BindingDefinition<"crtr.attach.scroll.page-up"> | BindingDefinition<"crtr.attach.scroll.page-down"> | BindingDefinition<"crtr.attach.scroll.live"> | BindingDefinition<"crtr.attach.mouse.toggle"> | BindingDefinition<"crtr.attach.copy.message"> | BindingDefinition<"crtr.graph.down"> | BindingDefinition<"crtr.graph.up"> | BindingDefinition<"crtr.graph.first"> | BindingDefinition<"crtr.graph.last"> | BindingDefinition<"crtr.graph.collapse-or-manager"> | BindingDefinition<"crtr.graph.expand-or-child"> | BindingDefinition<"crtr.graph.focus"> | BindingDefinition<"crtr.graph.revive"> | BindingDefinition<"crtr.graph.focus-manager"> | BindingDefinition<"crtr.graph.close-subtree"> | BindingDefinition<"crtr.graph.dismiss"> | BindingDefinition<"crtr.graph.confirm.accept"> | BindingDefinition<"crtr.graph.confirm.cancel"> | BindingDefinition<"crtr.inspector.force-quit"> | BindingDefinition<"crtr.inspector.down"> | BindingDefinition<"crtr.inspector.up"> | BindingDefinition<"crtr.inspector.section-next"> | BindingDefinition<"crtr.inspector.section-prev"> | BindingDefinition<"crtr.inspector.page-down"> | BindingDefinition<"crtr.inspector.page-up"> | BindingDefinition<"crtr.inspector.toggle-detail"> | BindingDefinition<"crtr.inspector.cancel-item"> | BindingDefinition<"crtr.inspector.open-log"> | BindingDefinition<"crtr.inspector.confirm"> | BindingDefinition<"crtr.inspector.dismiss"> | BindingDefinition<"crtr.inspector.retarget"> | BindingDefinition<"crtr.inspector.refresh"> | BindingDefinition<"crtr.inspector.quit"> | BindingDefinition<"crtr.browse.quit"> | BindingDefinition<"crtr.browse.down"> | BindingDefinition<"crtr.browse.up"> | BindingDefinition<"crtr.browse.first"> | BindingDefinition<"crtr.browse.last"> | BindingDefinition<"crtr.browse.expand-or-child"> | BindingDefinition<"crtr.browse.collapse-or-parent"> | BindingDefinition<"crtr.browse.toggle-fold"> | BindingDefinition<"crtr.browse.tab-next"> | BindingDefinition<"crtr.browse.tab-previous"> | BindingDefinition<"crtr.browse.tab.2" | "crtr.browse.tab.1" | "crtr.browse.tab.3" | "crtr.browse.tab.4" | "crtr.browse.tab.5"> | BindingDefinition<"crtr.browse.close-subtree"> | BindingDefinition<"crtr.browse.confirm-close"> | BindingDefinition<"crtr.browse.sort"> | BindingDefinition<"crtr.browse.scope"> | BindingDefinition<"crtr.browse.profile"> | BindingDefinition<"crtr.browse.residents"> | BindingDefinition<"crtr.browse.preview"> | BindingDefinition<"crtr.browse.search"> | BindingDefinition<"crtr.browse.revive-now"> | BindingDefinition<"crtr.browse.focus"> | BindingDefinition<"crtr.browse.focus-background"> | BindingDefinition<"crtr.browse.search.cancel"> | BindingDefinition<"crtr.browse.search.commit"> | BindingDefinition<"crtr.browse.search.delete"> | BindingDefinition<"crtr.browse.search.quit"> | BindingDefinition<"crtr.profile.menu.up"> | BindingDefinition<"crtr.profile.menu.down"> | BindingDefinition<"crtr.profile.menu.select"> | BindingDefinition<"crtr.profile.menu.accept-default"> | BindingDefinition<"crtr.profile.menu.abort"> | BindingDefinition<"crtr.profile.menu.slot.2" | "crtr.profile.menu.slot.1" | "crtr.profile.menu.slot.3" | "crtr.profile.menu.slot.4" | "crtr.profile.menu.slot.5" | "crtr.profile.menu.slot.6" | "crtr.profile.menu.slot.7" | "crtr.profile.menu.slot.8" | "crtr.profile.menu.slot.9">)[], BindingDefinition<"crtr.profile.menu.create">, BindingDefinition<"crtr.profile.menu.search-existing">, BindingDefinition<"crtr.profile.menu.use-root">, BindingDefinition<"crtr.profile.menu.set-default">, BindingDefinition<"crtr.profile.menu.add-directory">, BindingDefinition<"crtr.profile.menu.unpin">, BindingDefinition<"crtr.profile.search.up">, BindingDefinition<"crtr.profile.search.down">, BindingDefinition<"crtr.profile.search.pick">, BindingDefinition<"crtr.profile.search.back">, BindingDefinition<"crtr.profile.search.delete">, BindingDefinition<"crtr.profile.search.abort">, BindingDefinition<"crtr.setup.cancel">, BindingDefinition<"crtr.setup.tab-next">, BindingDefinition<"crtr.setup.tab-previous">, BindingDefinition<"crtr.setup.open">, BindingDefinition<"crtr.setup.list-down">, BindingDefinition<"crtr.setup.list-up">, BindingDefinition<"crtr.setup.toggle">, BindingDefinition<"crtr.setup.details">, BindingDefinition<"crtr.setup.advance">, BindingDefinition<"crtr.setup.confirm.accept">, BindingDefinition<"crtr.setup.confirm.back">, BindingDefinition<"crtr.setup.keybindings.edit">, BindingDefinition<"crtr.setup.keybindings.commit-edit">, BindingDefinition<"crtr.setup.keybindings.cancel-edit">, BindingDefinition<"crtr.setup.keybindings.unbind">, BindingDefinition<"crtr.setup.keybindings.reset-row">, BindingDefinition<"crtr.setup.keybindings.reset-all">, BindingDefinition<"crtr.setup.keybindings.save">, BindingDefinition<"crtr.setup.keybindings.discard">, BindingDefinition<"crtr.setup.keybindings.page-up">, BindingDefinition<"crtr.setup.keybindings.page-down">, BindingDefinition<"crtr.setup.keybindings.filter">, BindingDefinition<"crtr.setup.keybindings.filter-delete">, BindingDefinition<"crtr.setup.keybindings.filter-clear">, BindingDefinition<"crtr.setup.cell-previous">, BindingDefinition<"crtr.setup.cell-next">, BindingDefinition<"crtr.setup.models.open">, BindingDefinition<"crtr.setup.models.scope-next">, BindingDefinition<"crtr.setup.models.commit">, BindingDefinition<"crtr.setup.models.cancel">];
|
|
12
|
+
export declare const BINDING_CATALOG: readonly [BindingDefinition<"crtr.tmux.menu.open">, BindingDefinition<"crtr.tmux.menu.attach">, ...(BindingDefinition<BindingId> | BindingDefinition<"crtr.tmux.node.next"> | BindingDefinition<"crtr.tmux.node.previous"> | BindingDefinition<"crtr.tmux.inbox.toggle"> | BindingDefinition<"crtr.attach.scroll.up"> | BindingDefinition<"crtr.attach.scroll.down"> | BindingDefinition<"crtr.attach.scroll.page-up"> | BindingDefinition<"crtr.attach.scroll.page-down"> | BindingDefinition<"crtr.attach.scroll.live"> | BindingDefinition<"crtr.attach.mouse.toggle"> | BindingDefinition<"crtr.attach.copy.message"> | BindingDefinition<"crtr.graph.down"> | BindingDefinition<"crtr.graph.up"> | BindingDefinition<"crtr.graph.first"> | BindingDefinition<"crtr.graph.last"> | BindingDefinition<"crtr.graph.collapse-or-manager"> | BindingDefinition<"crtr.graph.expand-or-child"> | BindingDefinition<"crtr.graph.focus"> | BindingDefinition<"crtr.graph.revive"> | BindingDefinition<"crtr.graph.focus-manager"> | BindingDefinition<"crtr.graph.close-subtree"> | BindingDefinition<"crtr.graph.dismiss"> | BindingDefinition<"crtr.graph.confirm.accept"> | BindingDefinition<"crtr.graph.confirm.cancel"> | BindingDefinition<"crtr.inspector.force-quit"> | BindingDefinition<"crtr.inspector.down"> | BindingDefinition<"crtr.inspector.up"> | BindingDefinition<"crtr.inspector.section-next"> | BindingDefinition<"crtr.inspector.section-prev"> | BindingDefinition<"crtr.inspector.page-down"> | BindingDefinition<"crtr.inspector.page-up"> | BindingDefinition<"crtr.inspector.toggle-detail"> | BindingDefinition<"crtr.inspector.cancel-item"> | BindingDefinition<"crtr.inspector.open-log"> | BindingDefinition<"crtr.inspector.confirm"> | BindingDefinition<"crtr.inspector.dismiss"> | BindingDefinition<"crtr.inspector.retarget"> | BindingDefinition<"crtr.inspector.refresh"> | BindingDefinition<"crtr.inspector.quit"> | BindingDefinition<"crtr.browse.quit"> | BindingDefinition<"crtr.browse.down"> | BindingDefinition<"crtr.browse.up"> | BindingDefinition<"crtr.browse.first"> | BindingDefinition<"crtr.browse.last"> | BindingDefinition<"crtr.browse.expand-or-child"> | BindingDefinition<"crtr.browse.collapse-or-parent"> | BindingDefinition<"crtr.browse.toggle-fold"> | BindingDefinition<"crtr.browse.tab-next"> | BindingDefinition<"crtr.browse.tab-previous"> | BindingDefinition<"crtr.browse.tab.2" | "crtr.browse.tab.1" | "crtr.browse.tab.3" | "crtr.browse.tab.4" | "crtr.browse.tab.5"> | BindingDefinition<"crtr.browse.close-subtree"> | BindingDefinition<"crtr.browse.confirm-close"> | BindingDefinition<"crtr.browse.sort"> | BindingDefinition<"crtr.browse.scope"> | BindingDefinition<"crtr.browse.profile"> | BindingDefinition<"crtr.browse.residents"> | BindingDefinition<"crtr.browse.preview"> | BindingDefinition<"crtr.browse.search"> | BindingDefinition<"crtr.browse.revive-now"> | BindingDefinition<"crtr.browse.focus"> | BindingDefinition<"crtr.browse.focus-background"> | BindingDefinition<"crtr.browse.yank-id"> | BindingDefinition<"crtr.browse.search.cancel"> | BindingDefinition<"crtr.browse.search.commit"> | BindingDefinition<"crtr.browse.search.delete"> | BindingDefinition<"crtr.browse.search.quit"> | BindingDefinition<"crtr.profile.menu.up"> | BindingDefinition<"crtr.profile.menu.down"> | BindingDefinition<"crtr.profile.menu.select"> | BindingDefinition<"crtr.profile.menu.accept-default"> | BindingDefinition<"crtr.profile.menu.abort"> | BindingDefinition<"crtr.profile.menu.slot.2" | "crtr.profile.menu.slot.1" | "crtr.profile.menu.slot.3" | "crtr.profile.menu.slot.4" | "crtr.profile.menu.slot.5" | "crtr.profile.menu.slot.6" | "crtr.profile.menu.slot.7" | "crtr.profile.menu.slot.8" | "crtr.profile.menu.slot.9">)[], BindingDefinition<"crtr.profile.menu.create">, BindingDefinition<"crtr.profile.menu.search-existing">, BindingDefinition<"crtr.profile.menu.use-root">, BindingDefinition<"crtr.profile.menu.set-default">, BindingDefinition<"crtr.profile.menu.add-directory">, BindingDefinition<"crtr.profile.menu.unpin">, BindingDefinition<"crtr.profile.search.up">, BindingDefinition<"crtr.profile.search.down">, BindingDefinition<"crtr.profile.search.pick">, BindingDefinition<"crtr.profile.search.back">, BindingDefinition<"crtr.profile.search.delete">, BindingDefinition<"crtr.profile.search.abort">, BindingDefinition<"crtr.setup.cancel">, BindingDefinition<"crtr.setup.tab-next">, BindingDefinition<"crtr.setup.tab-previous">, BindingDefinition<"crtr.setup.open">, BindingDefinition<"crtr.setup.list-down">, BindingDefinition<"crtr.setup.list-up">, BindingDefinition<"crtr.setup.toggle">, BindingDefinition<"crtr.setup.details">, BindingDefinition<"crtr.setup.advance">, BindingDefinition<"crtr.setup.confirm.accept">, BindingDefinition<"crtr.setup.confirm.back">, BindingDefinition<"crtr.setup.keybindings.edit">, BindingDefinition<"crtr.setup.keybindings.commit-edit">, BindingDefinition<"crtr.setup.keybindings.cancel-edit">, BindingDefinition<"crtr.setup.keybindings.unbind">, BindingDefinition<"crtr.setup.keybindings.reset-row">, BindingDefinition<"crtr.setup.keybindings.reset-all">, BindingDefinition<"crtr.setup.keybindings.save">, BindingDefinition<"crtr.setup.keybindings.discard">, BindingDefinition<"crtr.setup.keybindings.page-up">, BindingDefinition<"crtr.setup.keybindings.page-down">, BindingDefinition<"crtr.setup.keybindings.filter">, BindingDefinition<"crtr.setup.keybindings.filter-delete">, BindingDefinition<"crtr.setup.keybindings.filter-clear">, BindingDefinition<"crtr.setup.cell-previous">, BindingDefinition<"crtr.setup.cell-next">, BindingDefinition<"crtr.setup.models.open">, BindingDefinition<"crtr.setup.models.scope-next">, BindingDefinition<"crtr.setup.models.commit">, BindingDefinition<"crtr.setup.models.cancel">];
|
|
13
13
|
export declare const BINDING_IDS: ReadonlySet<string>;
|
|
14
14
|
/** True for an action whose direct gesture is handled inside an attach viewer.
|
|
15
15
|
* Tmux menu selectors and root shortcuts are chrome, not pane input. */
|
|
@@ -142,6 +142,7 @@ function browseBindings() {
|
|
|
142
142
|
b('crtr.browse.revive-now', 'Browse and pickers', 'Revive now', 'Revive the selected node now.', ['shift+k'], 'terminal', [nav]),
|
|
143
143
|
b('crtr.browse.focus', 'Browse and pickers', 'Focus node', 'Focus the selected node.', ['enter', 'o'], 'terminal', [nav]),
|
|
144
144
|
b('crtr.browse.focus-background', 'Browse and pickers', 'Focus node (keep browsing)', 'Focus the selected node without leaving the navigator.', ['shift+o'], 'terminal', [nav]),
|
|
145
|
+
b('crtr.browse.yank-id', 'Browse and pickers', 'Yank node id', 'Copy the selected node id to the system clipboard.', ['y'], 'terminal', [nav]),
|
|
145
146
|
b('crtr.browse.search.cancel', 'Browse and pickers', 'Cancel search', 'Cancel browse search.', ['escape'], 'terminal', ['browse.search'], 'dismiss-modal'),
|
|
146
147
|
b('crtr.browse.search.commit', 'Browse and pickers', 'Commit search', 'Apply browse search.', ['enter'], 'terminal', ['browse.search']),
|
|
147
148
|
b('crtr.browse.search.delete', 'Browse and pickers', 'Delete search text', 'Delete the previous search character.', ['backspace'], 'terminal', ['browse.search']),
|
|
@@ -6,6 +6,7 @@ import { atomicWriteJson } from '../human/convention.js';
|
|
|
6
6
|
import { ticketDir } from '../human/root.js';
|
|
7
7
|
import { submitReview } from '../human/tickets.js';
|
|
8
8
|
import { NodeIdConflictError, spawnNode } from '../runtime/nodes.js';
|
|
9
|
+
import { loadProfileManifest } from '../profiles/manifest.js';
|
|
9
10
|
import { transition } from '../runtime/lifecycle.js';
|
|
10
11
|
import { reviveNode } from '../runtime/revive.js';
|
|
11
12
|
import { awaitCompanionBind, spawnReviewCompanion } from './companion.js';
|
|
@@ -126,6 +127,10 @@ function publishTicket(review) {
|
|
|
126
127
|
throw bridgeUnsubscribed(review, '');
|
|
127
128
|
if (review.subtitle === null)
|
|
128
129
|
throw new Error(`ticket review has no subtitle: ${review.review_id}`);
|
|
130
|
+
const origin = requireOrigin(review);
|
|
131
|
+
const profileName = origin.profile_id == null
|
|
132
|
+
? undefined
|
|
133
|
+
: loadProfileManifest(origin.profile_id).manifest.name;
|
|
129
134
|
submitReview({
|
|
130
135
|
dir: ticketDir(review.bridge_node_id),
|
|
131
136
|
review: {
|
|
@@ -133,7 +138,10 @@ function publishTicket(review) {
|
|
|
133
138
|
...(review.output_path !== null ? { output: review.output_path } : {}),
|
|
134
139
|
title: basename(review.file),
|
|
135
140
|
subtitle: review.subtitle,
|
|
136
|
-
source: {
|
|
141
|
+
source: {
|
|
142
|
+
nodeId: review.origin_node_id,
|
|
143
|
+
...(profileName === undefined ? {} : { profileName }),
|
|
144
|
+
},
|
|
137
145
|
blockedSince: review.created,
|
|
138
146
|
},
|
|
139
147
|
});
|
|
@@ -89,7 +89,12 @@ export async function bootRoot(opts) {
|
|
|
89
89
|
// user's OWN pre-existing shell pane (not one crtr split open): on a
|
|
90
90
|
// detach-reap of an empty root it must delete the node WITHOUT kill-pane, else
|
|
91
91
|
// a plain ctrl+c closes the user's whole tmux pane.
|
|
92
|
-
const attachEnv = {
|
|
92
|
+
const attachEnv = {
|
|
93
|
+
...process.env,
|
|
94
|
+
...viewerSplitEnv(),
|
|
95
|
+
CRTR_ATTACH_WAIT_MS: '30000',
|
|
96
|
+
...(opts.prompt !== undefined && opts.prompt !== '' ? { CRTR_ATTACH_INITIAL_WHIP: '1' } : {}),
|
|
97
|
+
};
|
|
93
98
|
if (here !== null)
|
|
94
99
|
attachEnv['CRTR_ATTACH_BORROWED_PANE'] = here.pane;
|
|
95
100
|
mark('boot_root.viewer_spawn');
|
|
@@ -120,7 +120,8 @@ export function disposeActiveSession() {
|
|
|
120
120
|
// ---------------------------------------------------------------------------
|
|
121
121
|
export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), startupTs = new Date().toISOString()) {
|
|
122
122
|
// A clean-abort certificate belongs only to the dead broker that wrote it.
|
|
123
|
-
// Fleet classification consumes it
|
|
123
|
+
// Fleet classification consumes it as proof that the dead broker had a turn
|
|
124
|
+
// in flight even though Pi's settled abort already cleared the busy marker.
|
|
124
125
|
clearCleanAbort(nodeId);
|
|
125
126
|
// The canonical broker event source (job/log.jsonl) is bound before any other
|
|
126
127
|
// work in this function — no path/env/state setup may precede it, and the
|
|
@@ -594,9 +595,10 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
594
595
|
// A daemon fleet bounce gives a busy Pi a short, bounded chance to persist
|
|
595
596
|
// its aborted turn before teardown. `session.abort()` resolves only after Pi
|
|
596
597
|
// reaches idle (and thus after its message_end persistence); only then may we
|
|
597
|
-
// leave the durable clean-abort certificate that
|
|
598
|
-
//
|
|
599
|
-
//
|
|
598
|
+
// leave the durable clean-abort certificate that preserves the otherwise
|
|
599
|
+
// cleared evidence that this broker had a turn in flight. A timeout/rejection
|
|
600
|
+
// deliberately leaves no certificate: the surviving busy marker carries the
|
|
601
|
+
// same interrupted-turn fact through the fleet's shared SIGKILL deadline.
|
|
600
602
|
const ABORT_BEFORE_EXIT_MS = 2_500;
|
|
601
603
|
let signalExitInFlight = false;
|
|
602
604
|
const abortAndExit = async (reason) => {
|
|
@@ -633,8 +635,8 @@ export async function runBroker(nodeId, startupAt = process.hrtime.bigint(), sta
|
|
|
633
635
|
}
|
|
634
636
|
else if (frameDispatch.pendingPromptDispatches() > 0 || rebind.isAgentRunActive()) {
|
|
635
637
|
// The engine never reached an abortable persisted turn in the budget.
|
|
636
|
-
// Preserve the
|
|
637
|
-
//
|
|
638
|
+
// Preserve the dirty interrupted-turn witness so recovery resumes the
|
|
639
|
+
// saved transcript and explicitly re-drives the stopped work.
|
|
638
640
|
markBusy(nodeId);
|
|
639
641
|
}
|
|
640
642
|
}
|
|
@@ -4,12 +4,13 @@ export declare function markBusy(nodeId: string): void;
|
|
|
4
4
|
/** Clear the mid-run marker after Pi settles, however it routed. Best-effort. */
|
|
5
5
|
export declare function clearBusy(nodeId: string): void;
|
|
6
6
|
/** Certify that this broker aborted a busy turn and Pi persisted its settled
|
|
7
|
-
* aborted entry before teardown.
|
|
8
|
-
*
|
|
7
|
+
* aborted entry before teardown. Settling clears the busy marker, so the daemon
|
|
8
|
+
* consumes this durable certificate as the surviving interrupted-turn witness
|
|
9
|
+
* and resumes with a continuation. A dirty death instead leaves `busy` behind. */
|
|
9
10
|
export declare function markCleanAbort(nodeId: string): void;
|
|
10
11
|
/** Remove a consumed clean-abort certificate as a broker boots. */
|
|
11
12
|
export declare function clearCleanAbort(nodeId: string): void;
|
|
12
|
-
/** Whether the dead broker left a persisted clean-abort
|
|
13
|
+
/** Whether the dead broker left a persisted clean-abort turn witness. */
|
|
13
14
|
export declare function hasCleanAbort(nodeId: string): boolean;
|
|
14
15
|
/** Is the node currently inside a turn? AND this with `pidAlive` at the call
|
|
15
16
|
* site — a stale marker from a crashed pi is harmless because the dead pid
|
|
@@ -44,8 +44,9 @@ export function clearBusy(nodeId) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
/** Certify that this broker aborted a busy turn and Pi persisted its settled
|
|
47
|
-
* aborted entry before teardown.
|
|
48
|
-
*
|
|
47
|
+
* aborted entry before teardown. Settling clears the busy marker, so the daemon
|
|
48
|
+
* consumes this durable certificate as the surviving interrupted-turn witness
|
|
49
|
+
* and resumes with a continuation. A dirty death instead leaves `busy` behind. */
|
|
49
50
|
export function markCleanAbort(nodeId) {
|
|
50
51
|
try {
|
|
51
52
|
mkdirSync(jobDir(nodeId), { recursive: true });
|
|
@@ -64,7 +65,7 @@ export function clearCleanAbort(nodeId) {
|
|
|
64
65
|
/* best-effort */
|
|
65
66
|
}
|
|
66
67
|
}
|
|
67
|
-
/** Whether the dead broker left a persisted clean-abort
|
|
68
|
+
/** Whether the dead broker left a persisted clean-abort turn witness. */
|
|
68
69
|
export function hasCleanAbort(nodeId) {
|
|
69
70
|
return existsSync(cleanAbortPath(nodeId));
|
|
70
71
|
}
|
|
@@ -401,15 +401,15 @@ export async function spawnChildPrepared(opts, beforeBrokerLaunch) {
|
|
|
401
401
|
// have bound view.sock yet. Hand the attach pane CRTR_ATTACH_WAIT_MS so
|
|
402
402
|
// it self-polls the socket until it accepts instead of connecting once,
|
|
403
403
|
// finding a cold socket, and closing the pane instantly — the exact
|
|
404
|
-
// mechanism bootRoot uses.
|
|
405
|
-
// viewer
|
|
406
|
-
//
|
|
404
|
+
// mechanism bootRoot uses. A child with a kickoff prompt marks its
|
|
405
|
+
// initial viewer for whip_mode; ordinary focus/attach paths stay
|
|
406
|
+
// silent. No pre-wait in this CLI subprocess.
|
|
407
407
|
const viewer = openViewerWindow(meta.node_id, parentViewer.session ?? '', {
|
|
408
408
|
name: fullName(meta),
|
|
409
409
|
cwd: opts.cwd,
|
|
410
410
|
besidePane: parentViewer.pane,
|
|
411
411
|
waitForBroker: true,
|
|
412
|
-
initialWhip:
|
|
412
|
+
initialWhip: hasKickoff,
|
|
413
413
|
});
|
|
414
414
|
if (viewer !== null && viewer.pane !== null && viewer.pane !== '') {
|
|
415
415
|
session = viewer.session;
|
|
@@ -6,7 +6,11 @@ export interface DisplayOpts {
|
|
|
6
6
|
/** Pane budget per window before `'auto'` opens a new window. Default 3. */
|
|
7
7
|
maxPanes?: number;
|
|
8
8
|
}
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
export type DisplayResult = {
|
|
10
|
+
paneId: string;
|
|
11
|
+
reason?: never;
|
|
12
|
+
} | {
|
|
13
|
+
paneId?: never;
|
|
14
|
+
reason: string;
|
|
12
15
|
};
|
|
16
|
+
export declare function display(path: string, opts?: DisplayOpts): DisplayResult;
|
|
@@ -1,19 +1,55 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { breakPane, closePane, currentTmux, paneOfNode, splitWindow, } from '../runtime/placement-tmux.js';
|
|
4
|
+
import { documentWatchCommand } from './termrender.js';
|
|
5
|
+
function targetPane() {
|
|
6
|
+
const callerPane = process.env['TMUX_PANE'];
|
|
7
|
+
if (callerPane !== undefined && callerPane !== '')
|
|
8
|
+
return callerPane;
|
|
9
|
+
const nodeId = process.env['CRTR_NODE_ID'];
|
|
10
|
+
if (nodeId !== undefined && nodeId !== '') {
|
|
11
|
+
const viewerPane = paneOfNode(nodeId);
|
|
12
|
+
if (viewerPane !== null)
|
|
13
|
+
return viewerPane;
|
|
14
|
+
}
|
|
15
|
+
return currentTmux()?.pane ?? null;
|
|
16
|
+
}
|
|
17
|
+
function countPanes(target) {
|
|
18
|
+
const result = spawnSync('tmux', ['list-panes', '-t', target, '-F', '#{pane_id}'], {
|
|
6
19
|
encoding: 'utf8',
|
|
7
20
|
});
|
|
8
21
|
if (result.status !== 0)
|
|
9
|
-
return
|
|
22
|
+
return null;
|
|
10
23
|
return result.stdout.split('\n').filter((line) => line.trim() !== '').length;
|
|
11
24
|
}
|
|
12
25
|
export function display(path, opts) {
|
|
13
26
|
const window = (opts?.window === 'split' || opts?.window === 'new') ? opts.window : 'auto';
|
|
14
27
|
const maxPanes = (opts?.maxPanes !== undefined && opts.maxPanes > 0) ? opts.maxPanes : 3;
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
28
|
+
const command = documentWatchCommand(resolve(path));
|
|
29
|
+
if (command === null)
|
|
30
|
+
return { reason: 'renderer unavailable' };
|
|
31
|
+
const target = targetPane();
|
|
32
|
+
if (target === null)
|
|
33
|
+
return { reason: 'no pane on screen' };
|
|
34
|
+
let newWindow = window === 'new';
|
|
35
|
+
if (window === 'auto') {
|
|
36
|
+
const paneCount = countPanes(target);
|
|
37
|
+
if (paneCount === null)
|
|
38
|
+
return { reason: `tmux could not inspect target pane ${target}` };
|
|
39
|
+
newWindow = paneCount >= maxPanes;
|
|
40
|
+
}
|
|
41
|
+
const pane = splitWindow(target, {
|
|
42
|
+
cwd: process.cwd(),
|
|
43
|
+
env: {},
|
|
44
|
+
command,
|
|
45
|
+
});
|
|
46
|
+
if (pane === null)
|
|
47
|
+
return { reason: `tmux could not split target pane ${target}` };
|
|
48
|
+
if (!newWindow)
|
|
49
|
+
return { paneId: pane };
|
|
50
|
+
const opened = breakPane(pane);
|
|
51
|
+
if (opened !== null)
|
|
52
|
+
return { paneId: opened.pane };
|
|
53
|
+
closePane(pane);
|
|
54
|
+
return { reason: 'tmux could not move the document pane into a new window' };
|
|
19
55
|
}
|
|
@@ -59,15 +59,6 @@ export declare function checkMarkdown(md: string): {
|
|
|
59
59
|
ok: false;
|
|
60
60
|
error: string;
|
|
61
61
|
};
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Spawn termrender into a live tmux pane. The pane-budget policy (whether to
|
|
68
|
-
* split vs open a new window) is decided by the caller (`src/surfaces/
|
|
69
|
-
* display.ts`); this is the thin managed-binary spawn it delegates to.
|
|
70
|
-
*/
|
|
71
|
-
export declare function displayInPane(path: string, opts?: DisplayInPaneOpts): {
|
|
72
|
-
paneId?: string;
|
|
73
|
-
};
|
|
62
|
+
/** Shell command for a pane that live-renders `path` with crouter's pinned
|
|
63
|
+
* termrender. Null means the managed renderer could not be provisioned. */
|
|
64
|
+
export declare function documentWatchCommand(path: string): string | null;
|
|
@@ -3,6 +3,7 @@ import { existsSync, readFileSync, writeFileSync, statSync, mkdirSync, unlinkSyn
|
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { join, resolve } from 'node:path';
|
|
5
5
|
import stringWidth from 'string-width';
|
|
6
|
+
import { shellQuote } from '../../shared/shell-quote.js';
|
|
6
7
|
import { withExclusiveDirectoryLock } from '../exclusive-lock.js';
|
|
7
8
|
import { TERMRENDER_VERSION } from './version.js';
|
|
8
9
|
// ── The sole org-wide termrender binding ─────────────────────────────────────
|
|
@@ -703,43 +704,11 @@ export function checkMarkdown(md) {
|
|
|
703
704
|
}
|
|
704
705
|
return { ok: true };
|
|
705
706
|
}
|
|
706
|
-
/**
|
|
707
|
-
*
|
|
708
|
-
|
|
709
|
-
* display.ts`); this is the thin managed-binary spawn it delegates to.
|
|
710
|
-
*/
|
|
711
|
-
export function displayInPane(path, opts = {}) {
|
|
707
|
+
/** Shell command for a pane that live-renders `path` with crouter's pinned
|
|
708
|
+
* termrender. Null means the managed renderer could not be provisioned. */
|
|
709
|
+
export function documentWatchCommand(path) {
|
|
712
710
|
ensureRenderer();
|
|
713
711
|
if (rendererState !== 'ready')
|
|
714
|
-
return
|
|
715
|
-
|
|
716
|
-
const args = ['pane', 'open', path, '--watch'];
|
|
717
|
-
args.push('--window', opts.newWindow ? 'new' : 'split');
|
|
718
|
-
const result = spawnSync(VENV_BIN, args, {
|
|
719
|
-
encoding: 'utf-8',
|
|
720
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
721
|
-
});
|
|
722
|
-
// A spawn fault (binary broke) invalidates readiness; a non-zero exit (e.g.
|
|
723
|
-
// tmux refused, bad path) is not an environment fault and must not.
|
|
724
|
-
if (result.error) {
|
|
725
|
-
invalidateRenderer('pane open');
|
|
726
|
-
return {};
|
|
727
|
-
}
|
|
728
|
-
if (result.status !== 0)
|
|
729
|
-
return {};
|
|
730
|
-
// `encoding: 'utf-8'` makes spawnSync return stdout as a string.
|
|
731
|
-
const rawStdout = result.stdout;
|
|
732
|
-
if (!rawStdout)
|
|
733
|
-
return {};
|
|
734
|
-
let parsed = null;
|
|
735
|
-
try {
|
|
736
|
-
parsed = JSON.parse(rawStdout.trim());
|
|
737
|
-
}
|
|
738
|
-
catch {
|
|
739
|
-
return {};
|
|
740
|
-
}
|
|
741
|
-
if (parsed && typeof parsed.pane_id === 'string' && parsed.pane_id) {
|
|
742
|
-
return { paneId: parsed.pane_id };
|
|
743
|
-
}
|
|
744
|
-
return {};
|
|
712
|
+
return null;
|
|
713
|
+
return `${shellQuote(VENV_BIN)} doc watch ${shellQuote(path)}`;
|
|
745
714
|
}
|
|
@@ -147,6 +147,21 @@ export declare const CONFIG_SETTINGS_COVERAGE: {
|
|
|
147
147
|
setFromString: (config: ScopeConfig, raw: string) => string | null;
|
|
148
148
|
};
|
|
149
149
|
};
|
|
150
|
+
html_surface: {
|
|
151
|
+
surface: "settings";
|
|
152
|
+
setting: {
|
|
153
|
+
key: "html_surface";
|
|
154
|
+
label: string;
|
|
155
|
+
description: string;
|
|
156
|
+
valueConstraint: string;
|
|
157
|
+
applies: string;
|
|
158
|
+
group: "display";
|
|
159
|
+
order: number;
|
|
160
|
+
format: (config: ScopeConfig) => "on" | "off";
|
|
161
|
+
cycle: (config: ScopeConfig) => void;
|
|
162
|
+
setFromString: (config: ScopeConfig, raw: string) => string | null;
|
|
163
|
+
};
|
|
164
|
+
};
|
|
150
165
|
whip_messages: {
|
|
151
166
|
surface: "specialized";
|
|
152
167
|
owner: string;
|
|
@@ -196,10 +211,10 @@ export declare const CONFIG_SETTINGS_COVERAGE: {
|
|
|
196
211
|
setFromString: (config: ScopeConfig, raw: string) => string | null;
|
|
197
212
|
};
|
|
198
213
|
};
|
|
199
|
-
|
|
214
|
+
bash_tool_purpose: {
|
|
200
215
|
surface: "settings";
|
|
201
216
|
setting: {
|
|
202
|
-
key: "
|
|
217
|
+
key: "bash_tool_purpose";
|
|
203
218
|
label: string;
|
|
204
219
|
description: string;
|
|
205
220
|
valueConstraint: string;
|
|
@@ -176,7 +176,7 @@ export const CONFIG_SETTINGS_COVERAGE = {
|
|
|
176
176
|
setting: {
|
|
177
177
|
key: 'whip_mode',
|
|
178
178
|
label: 'Whip mode',
|
|
179
|
-
description: 'Play the whip animation when you send a prompt and when an
|
|
179
|
+
description: 'Play the whip animation when you send a prompt and when a viewer opens for an agent with an initial prompt.',
|
|
180
180
|
valueConstraint: 'true|false',
|
|
181
181
|
applies: 'Applies on the next attach.',
|
|
182
182
|
group: 'display',
|
|
@@ -192,6 +192,27 @@ export const CONFIG_SETTINGS_COVERAGE = {
|
|
|
192
192
|
},
|
|
193
193
|
},
|
|
194
194
|
},
|
|
195
|
+
html_surface: {
|
|
196
|
+
surface: 'settings',
|
|
197
|
+
setting: {
|
|
198
|
+
key: 'html_surface',
|
|
199
|
+
label: 'HTML human surface',
|
|
200
|
+
description: 'Prompt agents to use HTML pages by default for structured or interactive content shown to you.',
|
|
201
|
+
valueConstraint: 'true|false',
|
|
202
|
+
applies: 'Applies when human command help is assembled.',
|
|
203
|
+
group: 'display',
|
|
204
|
+
order: 37,
|
|
205
|
+
format: (config) => config.html_surface ? 'on' : 'off',
|
|
206
|
+
cycle: (config) => { config.html_surface = !config.html_surface; },
|
|
207
|
+
setFromString: (config, raw) => {
|
|
208
|
+
const value = booleanFromString(raw, 'html_surface');
|
|
209
|
+
if (typeof value === 'string')
|
|
210
|
+
return value;
|
|
211
|
+
config.html_surface = value;
|
|
212
|
+
return null;
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
},
|
|
195
216
|
whip_messages: { surface: 'specialized', owner: 'sys config set whip_messages' },
|
|
196
217
|
mouse_mode_default: {
|
|
197
218
|
surface: 'settings',
|
|
@@ -254,23 +275,23 @@ export const CONFIG_SETTINGS_COVERAGE = {
|
|
|
254
275
|
},
|
|
255
276
|
},
|
|
256
277
|
},
|
|
257
|
-
|
|
278
|
+
bash_tool_purpose: {
|
|
258
279
|
surface: 'settings',
|
|
259
280
|
setting: {
|
|
260
|
-
key: '
|
|
261
|
-
label: '
|
|
262
|
-
description: '
|
|
281
|
+
key: 'bash_tool_purpose',
|
|
282
|
+
label: 'Bash purpose field',
|
|
283
|
+
description: 'Expose the optional purpose label to agents in the bash tool schema. Off removes the field without disabling the bash valve.',
|
|
263
284
|
valueConstraint: 'true|false',
|
|
264
|
-
applies: 'Applies on the next
|
|
285
|
+
applies: 'Applies on the next node session.',
|
|
265
286
|
group: 'display',
|
|
266
|
-
order:
|
|
267
|
-
format: (config) => config.
|
|
268
|
-
cycle: (config) => { config.
|
|
287
|
+
order: 25,
|
|
288
|
+
format: (config) => config.bash_tool_purpose ? 'on' : 'off',
|
|
289
|
+
cycle: (config) => { config.bash_tool_purpose = !config.bash_tool_purpose; },
|
|
269
290
|
setFromString: (config, raw) => {
|
|
270
|
-
const value = booleanFromString(raw, '
|
|
291
|
+
const value = booleanFromString(raw, 'bash_tool_purpose');
|
|
271
292
|
if (typeof value === 'string')
|
|
272
293
|
return value;
|
|
273
|
-
config.
|
|
294
|
+
config.bash_tool_purpose = value;
|
|
274
295
|
return null;
|
|
275
296
|
},
|
|
276
297
|
},
|
|
@@ -93,7 +93,7 @@ async function handleResolve(ctx) {
|
|
|
93
93
|
const request = requireResolveRequest(ctx);
|
|
94
94
|
const dir = ticketDir(nodeId);
|
|
95
95
|
requirePageTicket(dir);
|
|
96
|
-
const productKinds = readConfig('user').page_components
|
|
96
|
+
const productKinds = readConfig('user').page_components;
|
|
97
97
|
const responses = preflightResolve(() => validatePageTicketResponses(dir, request.responses, productKinds));
|
|
98
98
|
await resolvePageTicket(nodeId, responses, productKinds);
|
|
99
99
|
return { status: 200, body: { delivered: true, kind: 'page' } };
|