@north-light/crouter 0.3.175 → 0.3.177
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 +4 -4
- package/dist/api/client.js +2 -2
- package/dist/api/dto/lifecycle.d.ts +1 -1
- package/dist/api/dto/nodes.d.ts +7 -1
- package/dist/api/dto/profiles.d.ts +3 -0
- package/dist/api/dto/reports.d.ts +1 -1
- package/dist/clients/attach/__tests__/group-activity.test.js +2 -2
- package/dist/clients/attach/config.js +5 -5
- package/dist/clients/attach/input/clipboard-image.js +2 -2
- package/dist/clients/attach/input/ref-autocomplete.js +1 -1
- package/dist/clients/attach/input/titled-editor.d.ts +12 -13
- package/dist/clients/attach/input/titled-editor.js +14 -15
- package/dist/clients/attach/overlays/dialogs.d.ts +1 -1
- package/dist/clients/attach/overlays/dialogs.js +2 -2
- package/dist/clients/attach/overlays/mcp.js +5 -12
- package/dist/clients/attach/render/chat-view.d.ts +1 -1
- package/dist/clients/attach/render/chat-view.js +2 -2
- package/dist/clients/attach/render/crtr-output.d.ts +3 -3
- package/dist/clients/attach/render/crtr-output.js +25 -13
- package/dist/clients/attach/render/group-recap.js +7 -0
- package/dist/clients/attach/session/context.js +3 -3
- package/dist/clients/attach/session/editor-inventory.js +6 -6
- package/dist/clients/attach/session/input-wiring.js +1 -1
- package/dist/clients/attach/session/teardown.js +7 -9
- package/dist/clients/attach/slash/dispatch.d.ts +1 -2
- package/dist/clients/attach/slash/dispatch.js +4 -8
- package/dist/clients/attach/viewer.js +536 -566
- package/dist/clients/inbox/controller.js +3 -2
- package/dist/clients/inbox/deck-adapter.js +3 -2
- package/dist/clients/surfaces/host.d.ts +1 -1
- package/dist/commands/api-client.d.ts +12 -13
- package/dist/commands/api-client.js +15 -16
- package/dist/commands/chord.js +1 -2
- package/dist/commands/human/queue.js +6 -5
- package/dist/commands/memory/find.js +3 -3
- package/dist/commands/node/create.js +7 -3
- package/dist/commands/node/lifecycle.js +1 -1
- package/dist/commands/node-lifecycle-revive.d.ts +1 -1
- package/dist/commands/node-lifecycle-revive.js +3 -3
- package/dist/commands/profile/list.js +3 -1
- package/dist/commands/profile/new.js +4 -1
- package/dist/commands/profile/project.js +23 -9
- package/dist/commands/profile/show.js +3 -1
- package/dist/commands/profile.js +2 -2
- package/dist/commands/push.js +1 -1
- package/dist/commands/surface/node/placement.d.ts +1 -1
- package/dist/commands/surface/node/placement.js +1 -1
- package/dist/commands/surface-edit.js +1 -4
- package/dist/commands/sys/logs.js +4 -2
- package/dist/commands/sys/panels/panel.d.ts +3 -3
- package/dist/commands/sys/panels/panel.js +3 -3
- package/dist/commands/sys/setup-core.js +1 -7
- package/dist/commands/sys/sync-deps.js +1 -3
- package/dist/commands/sys/sync-project-guidance.js +2 -11
- package/dist/commands/sys/sysprompt.js +1 -3
- package/dist/core/__tests__/branded-host-concurrency.test.js +1 -1
- package/dist/core/__tests__/fixtures/fake-engine.js +1 -1
- package/dist/core/__tests__/helpers/harness.d.ts +1 -1
- package/dist/core/__tests__/helpers/harness.js +1 -1
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +1 -1
- package/dist/core/__tests__/on-read-dedup-resume.test.js +1 -1
- package/dist/core/canvas/boot.js +1 -1
- package/dist/core/canvas/browse/app.js +2 -2
- package/dist/core/canvas/canvas.d.ts +3 -3
- package/dist/core/canvas/canvas.js +3 -3
- package/dist/core/canvas/daemon-owner.js +1 -2
- package/dist/core/canvas/db.js +7 -8
- package/dist/core/canvas/extensions.d.ts +1 -1
- package/dist/core/canvas/extensions.js +7 -1
- package/dist/core/canvas/index.js +2 -2
- package/dist/core/canvas/pid.d.ts +12 -14
- package/dist/core/canvas/pid.js +16 -20
- package/dist/core/canvas/remote-transport.js +2 -2
- package/dist/core/canvas/status-glyph.d.ts +1 -1
- package/dist/core/canvas/status-glyph.js +1 -1
- package/dist/core/canvas/types.d.ts +1 -1
- package/dist/core/command-manifests/manifest.js +4 -6
- package/dist/core/command-manifests/schema.js +10 -12
- package/dist/core/command-plugins/transport/exec-invoke.js +5 -7
- package/dist/core/command-plugins/transport/http-invoke.js +14 -8
- package/dist/core/command.js +3 -1
- package/dist/core/config.d.ts +1 -1
- package/dist/core/config.js +6 -29
- package/dist/core/events/read.js +1 -3
- package/dist/core/exclusive-lock.d.ts +14 -0
- package/dist/core/exclusive-lock.js +124 -0
- package/dist/core/fault-classifier.js +1 -3
- package/dist/core/fs-utils.d.ts +7 -0
- package/dist/core/fs-utils.js +29 -2
- package/dist/core/host-exports/export.d.ts +4 -5
- package/dist/core/host-exports/export.js +4 -5
- package/dist/core/human/claim.js +4 -2
- package/dist/core/human/convention.d.ts +0 -11
- package/dist/core/human/convention.js +1 -131
- package/dist/core/human/scan.js +5 -4
- package/dist/core/io.d.ts +23 -0
- package/dist/core/io.js +60 -0
- package/dist/core/preview-registry.d.ts +10 -5
- package/dist/core/preview-registry.js +136 -2537
- package/dist/core/profiles/manifest.d.ts +13 -2
- package/dist/core/profiles/manifest.js +58 -49
- package/dist/core/review/store.d.ts +4 -1
- package/dist/core/review/store.js +10 -0
- package/dist/core/review/types.d.ts +1 -1
- package/dist/core/review/types.js +1 -1
- package/dist/core/runtime/branded-host.js +6 -84
- package/dist/core/runtime/broker/frame-dispatch.js +4 -4
- package/dist/core/runtime/broker-cli.js +1 -1
- package/dist/core/runtime/broker-extension-render.js +1 -1
- package/dist/core/runtime/broker-protocol.d.ts +9 -10
- package/dist/core/runtime/broker-protocol.js +9 -10
- package/dist/core/runtime/broker-sdk.d.ts +1 -1
- package/dist/core/runtime/broker.js +6 -6
- package/dist/core/runtime/canvas-extensions.d.ts +1 -0
- package/dist/core/runtime/canvas-extensions.js +2 -0
- package/dist/core/runtime/host.js +14 -14
- package/dist/core/runtime/launch.d.ts +1 -1
- package/dist/core/runtime/launch.js +1 -1
- package/dist/core/runtime/lifecycle.js +16 -21
- package/dist/core/runtime/node-read.js +2 -3
- package/dist/core/runtime/package-health.js +1 -8
- package/dist/core/runtime/persona.js +2 -2
- package/dist/core/runtime/pi-vendored.d.ts +4 -2
- package/dist/core/runtime/pi-vendored.js +5 -16
- package/dist/core/runtime/promote.js +1 -1
- package/dist/core/runtime/reopen.js +2 -2
- package/dist/core/runtime/revive-all.d.ts +3 -3
- package/dist/core/runtime/revive-all.js +6 -6
- package/dist/core/runtime/revive.js +1 -1
- package/dist/core/runtime/spawn.d.ts +1 -2
- package/dist/core/runtime/spawn.js +2 -3
- package/dist/core/runtime/structured-output.js +4 -6
- package/dist/core/runtime/tmux-bindings.js +1 -1
- package/dist/core/runtime/tmux-driver.d.ts +2 -2
- package/dist/core/runtime/tmux-driver.js +3 -5
- package/dist/core/runtime/tool-group-summary.js +5 -4
- package/dist/core/runtime/warm-pool.d.ts +9 -8
- package/dist/core/runtime/warm-pool.js +58 -62
- package/dist/core/scope.js +2 -9
- package/dist/core/self-update.js +3 -3
- package/dist/core/spawn.d.ts +0 -1
- package/dist/core/spawn.js +1 -4
- package/dist/core/substrate/index.d.ts +2 -1
- package/dist/core/substrate/index.js +2 -1
- package/dist/core/substrate/on-read-node.d.ts +7 -0
- package/dist/core/substrate/on-read-node.js +37 -0
- package/dist/core/substrate/on-read.d.ts +7 -5
- package/dist/core/substrate/on-read.js +16 -32
- package/dist/core/termrender/termrender.js +16 -96
- package/dist/core/wake.js +2 -2
- package/dist/core/worktree.d.ts +3 -3
- package/dist/core/worktree.js +16 -107
- package/dist/daemon/api/bridge.js +1 -1
- package/dist/daemon/api/handlers/broker-ops.js +1 -2
- package/dist/daemon/api/handlers/messages.js +2 -2
- package/dist/daemon/api/handlers/nodes.js +33 -10
- package/dist/daemon/api/handlers/reports.js +1 -1
- package/dist/daemon/api/handlers/review-comments.js +5 -44
- package/dist/daemon/api/handlers/reviews.js +5 -53
- package/dist/daemon/api/handlers/validate.d.ts +23 -0
- package/dist/daemon/api/handlers/validate.js +58 -0
- package/dist/daemon/api/map.js +1 -0
- package/dist/daemon/cron-run.js +7 -7
- package/dist/daemon/human/finish.js +3 -2
- package/dist/daemon/manage.d.ts +6 -6
- package/dist/daemon/manage.js +15 -16
- package/dist/daemon/review/finish.js +1 -10
- package/dist/pi-extensions/canvas-context-intro.js +1 -14
- package/dist/pi-extensions/canvas-doc-substrate.js +2 -2
- package/dist/pi-extensions/canvas-preview-result.d.ts +22 -0
- package/dist/pi-extensions/canvas-preview-result.js +60 -0
- package/dist/pi-extensions/canvas-review-boundary.js +1 -14
- package/dist/pi-extensions/truncate.d.ts +5 -0
- package/dist/pi-extensions/truncate.js +14 -0
- package/dist/shared/predicates.d.ts +2 -0
- package/dist/shared/predicates.js +4 -0
- package/dist/shared/shell-quote.d.ts +3 -0
- package/dist/shared/shell-quote.js +5 -0
- package/dist/shared/tool-groups.d.ts +4 -2
- package/dist/shared/tool-groups.js +3 -3
- package/dist/types.d.ts +8 -5
- package/package.json +1 -1
- package/runtime.lock.json +2 -2
- package/dist/core/substrate/on-read-pure.d.ts +0 -7
- package/dist/core/substrate/on-read-pure.js +0 -272
|
@@ -7,7 +7,8 @@ import { scanInbox } from '../../core/human/scan.js';
|
|
|
7
7
|
import { watchInboxActivity } from '../../core/human/root.js';
|
|
8
8
|
import { claimTicket, heartbeatClaim, releaseClaim } from '../../core/human/claim.js';
|
|
9
9
|
import { readTicketResult } from '../../core/human/tickets.js';
|
|
10
|
-
import { clearProgress, deckPath
|
|
10
|
+
import { clearProgress, deckPath } from '../../core/human/convention.js';
|
|
11
|
+
import { readJsonOrNull } from '../../core/fs-utils.js';
|
|
11
12
|
import { DeckAdapter } from './deck-adapter.js';
|
|
12
13
|
import { validateDeck } from '../../core/human/deck-schema.js';
|
|
13
14
|
import { ReviewAdapter } from './review-adapter.js';
|
|
@@ -258,7 +259,7 @@ export class InboxController {
|
|
|
258
259
|
this.markDirty();
|
|
259
260
|
}
|
|
260
261
|
readDeck(dir) {
|
|
261
|
-
const deck =
|
|
262
|
+
const deck = readJsonOrNull(deckPath(dir));
|
|
262
263
|
if (deck === null)
|
|
263
264
|
return undefined;
|
|
264
265
|
try {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { progressPath
|
|
1
|
+
import { progressPath } from '../../core/human/convention.js';
|
|
2
|
+
import { readJsonOrNull } from '../../core/fs-utils.js';
|
|
2
3
|
import { mountPanel } from './tui/panel.js';
|
|
3
4
|
/** Embeds the single deck renderer in a controller-owned rectangle. */
|
|
4
5
|
export class DeckAdapter {
|
|
@@ -50,7 +51,7 @@ function notificationsAcknowledged(deck, responses) {
|
|
|
50
51
|
});
|
|
51
52
|
}
|
|
52
53
|
function initialResponses(deck, dir) {
|
|
53
|
-
const saved =
|
|
54
|
+
const saved = readJsonOrNull(progressPath(dir))?.responses;
|
|
54
55
|
if (Array.isArray(saved))
|
|
55
56
|
return saved;
|
|
56
57
|
return deck.interactions.flatMap((interaction) => {
|
|
@@ -12,7 +12,7 @@ export interface SurfaceComponent extends Component {
|
|
|
12
12
|
/** Consume wheel input when applicable. */
|
|
13
13
|
handleWheel?(direction: 'up' | 'down', lines: number): boolean;
|
|
14
14
|
}
|
|
15
|
-
/** The identities used for mounted
|
|
15
|
+
/** The identities used for mounted surfaces. */
|
|
16
16
|
export type SurfaceId = 'inbox' | 'review';
|
|
17
17
|
/** How long a notice stays up. */
|
|
18
18
|
export interface SurfaceNoticeOptions {
|
|
@@ -20,14 +20,14 @@ export declare function rethrowAsCliError(err: unknown, next?: string): never;
|
|
|
20
20
|
* 404-only→null mapping as `ApiCanvasSource.orNull`, but returning the raw DTO
|
|
21
21
|
* and rethrowing non-404s as CLI errors rather than swallowing them. */
|
|
22
22
|
export declare function getNodeOrNull(nodeId: string): Promise<NodeDetailDTO | null>;
|
|
23
|
-
/** Bounded tail of crtrd.err surfaced on a cold-start `/healthz` timeout
|
|
24
|
-
*
|
|
23
|
+
/** Bounded tail of crtrd.err surfaced on a cold-start `/healthz` timeout.
|
|
24
|
+
* Large enough to catch a real startup failure/stack trace,
|
|
25
25
|
* small enough that a runaway daemon log can never leak unbounded into a CLI
|
|
26
26
|
* error message. */
|
|
27
27
|
export declare const COLD_START_DIAGNOSTIC_TAIL_BYTES = 4096;
|
|
28
28
|
/** Read a bounded tail of crtrd's stderr log to enrich a `daemon_unavailable`
|
|
29
|
-
* cold-start timeout
|
|
30
|
-
*
|
|
29
|
+
* cold-start timeout, so the actual startup failure is not discarded behind a
|
|
30
|
+
* bare message. Bounds the I/O itself — opens the file and reads only the
|
|
31
31
|
* final `COLD_START_DIAGNOSTIC_TAIL_BYTES` bytes via `fstatSync`+`readSync`,
|
|
32
32
|
* never `readFileSync`-ing the whole append-only log — then decodes/filters
|
|
33
33
|
* that bounded slice. Best-effort and synchronous per the `coldStartDiagnostic`
|
|
@@ -42,15 +42,14 @@ export declare function readColdStartDiagnostic(): string | undefined;
|
|
|
42
42
|
* autostart is disabled — the client throws `daemon_unavailable` (spec §7.1,
|
|
43
43
|
* §8). This is the ONLY CLI reference to `ensureDaemon`.
|
|
44
44
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* `
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* `daemon_unavailable` at 10s. Passing the SAME `DAEMON_VERIFY_WINDOW_MS`
|
|
45
|
+
* `onColdSocket` is fire-and-forget — `ensureDaemon()` spawns crtrd and
|
|
46
|
+
* returns immediately, so the client's OWN `/healthz` poll is the only
|
|
47
|
+
* deadline the initiating CLI invocation actually waits on. It must never be
|
|
48
|
+
* narrower than the window `spawnDaemon`'s `verifyDaemonStartup` allows (which
|
|
49
|
+
* `ensureDaemon`'s spawn transitively awaits, in the detached child, on the
|
|
50
|
+
* SAME cold-start path): a daemon that takes 12s is a valid, verified startup,
|
|
51
|
+
* and a 10s client poll would still give up and report `daemon_unavailable`
|
|
52
|
+
* against it. Passing the SAME `DAEMON_VERIFY_WINDOW_MS`
|
|
54
53
|
* here keeps the two in lockstep — one authoritative window, not two that can
|
|
55
54
|
* drift apart — without adding any retry/fallback poll loop. */
|
|
56
55
|
export declare function cliClient(opts?: {
|
|
@@ -65,15 +65,15 @@ export async function getNodeOrNull(nodeId) {
|
|
|
65
65
|
function autostartDisabled() {
|
|
66
66
|
return process.env['CRTR_NO_DAEMON_AUTOSTART'] === '1' || process.argv.includes('--no-autostart');
|
|
67
67
|
}
|
|
68
|
-
/** Bounded tail of crtrd.err surfaced on a cold-start `/healthz` timeout
|
|
69
|
-
*
|
|
68
|
+
/** Bounded tail of crtrd.err surfaced on a cold-start `/healthz` timeout.
|
|
69
|
+
* Large enough to catch a real startup failure/stack trace,
|
|
70
70
|
* small enough that a runaway daemon log can never leak unbounded into a CLI
|
|
71
71
|
* error message. */
|
|
72
72
|
export const COLD_START_DIAGNOSTIC_TAIL_BYTES = 4_096;
|
|
73
73
|
const COLD_START_DIAGNOSTIC_TAIL_LINES = 20;
|
|
74
74
|
/** Read a bounded tail of crtrd's stderr log to enrich a `daemon_unavailable`
|
|
75
|
-
* cold-start timeout
|
|
76
|
-
*
|
|
75
|
+
* cold-start timeout, so the actual startup failure is not discarded behind a
|
|
76
|
+
* bare message. Bounds the I/O itself — opens the file and reads only the
|
|
77
77
|
* final `COLD_START_DIAGNOSTIC_TAIL_BYTES` bytes via `fstatSync`+`readSync`,
|
|
78
78
|
* never `readFileSync`-ing the whole append-only log — then decodes/filters
|
|
79
79
|
* that bounded slice. Best-effort and synchronous per the `coldStartDiagnostic`
|
|
@@ -116,15 +116,14 @@ export function readColdStartDiagnostic() {
|
|
|
116
116
|
* autostart is disabled — the client throws `daemon_unavailable` (spec §7.1,
|
|
117
117
|
* §8). This is the ONLY CLI reference to `ensureDaemon`.
|
|
118
118
|
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* `
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* `daemon_unavailable` at 10s. Passing the SAME `DAEMON_VERIFY_WINDOW_MS`
|
|
119
|
+
* `onColdSocket` is fire-and-forget — `ensureDaemon()` spawns crtrd and
|
|
120
|
+
* returns immediately, so the client's OWN `/healthz` poll is the only
|
|
121
|
+
* deadline the initiating CLI invocation actually waits on. It must never be
|
|
122
|
+
* narrower than the window `spawnDaemon`'s `verifyDaemonStartup` allows (which
|
|
123
|
+
* `ensureDaemon`'s spawn transitively awaits, in the detached child, on the
|
|
124
|
+
* SAME cold-start path): a daemon that takes 12s is a valid, verified startup,
|
|
125
|
+
* and a 10s client poll would still give up and report `daemon_unavailable`
|
|
126
|
+
* against it. Passing the SAME `DAEMON_VERIFY_WINDOW_MS`
|
|
128
127
|
* here keeps the two in lockstep — one authoritative window, not two that can
|
|
129
128
|
* drift apart — without adding any retry/fallback poll loop. */
|
|
130
129
|
export function cliClient(opts) {
|
|
@@ -140,7 +139,7 @@ export function cliClient(opts) {
|
|
|
140
139
|
ensureDaemon();
|
|
141
140
|
},
|
|
142
141
|
// On a cold-start timeout, enrich the bare `daemon_unavailable` with the
|
|
143
|
-
// tail of crtrd's own stderr log
|
|
142
|
+
// tail of crtrd's own stderr log instead of discarding it.
|
|
144
143
|
coldStartDiagnostic: readColdStartDiagnostic,
|
|
145
144
|
coldStartPollWindowMs: DAEMON_VERIFY_WINDOW_MS,
|
|
146
145
|
});
|
|
@@ -153,8 +152,8 @@ export function cliClient(opts) {
|
|
|
153
152
|
// performs no lossy legacy reconstruction or invented local-presence values.
|
|
154
153
|
// The attach viewer's recurring subtree walk (chrome/canvas-panels.ts + overlays/graph.ts
|
|
155
154
|
// computeActivity/visitActivity) calls subscriptionsOf/subscribersOf once per
|
|
156
|
-
// visited node on every poll tick
|
|
157
|
-
//
|
|
155
|
+
// visited node on every poll tick, and a `GET /v1/nodes/:id` per call just to
|
|
156
|
+
// read the edge list off the detail DTO costs one round trip per visited node.
|
|
158
157
|
// This short-TTL cache backs those two methods with the lean roster
|
|
159
158
|
// (`GET /v1/canvas/roster`) instead — one shared fetch per TTL window covers
|
|
160
159
|
// every topology walk in that window, comfortably under the attach viewer's 5s
|
package/dist/commands/chord.js
CHANGED
|
@@ -19,8 +19,7 @@ const pexec = promisify(execFile);
|
|
|
19
19
|
* here rather than importing node.ts's `nodeInPane` (which reaches openDb via the
|
|
20
20
|
* canvas accessors) so this dispatcher's import graph stays db-free. Under the
|
|
21
21
|
* headless-broker model every engine runs DETACHED (window=null), so the pane tag
|
|
22
|
-
* is the ONLY node handle
|
|
23
|
-
* matched legacy in-pane engines, which no longer exist. A stale tag is ignored
|
|
22
|
+
* is the ONLY node handle. A stale tag is ignored
|
|
24
23
|
* (the tagged node must still be live), validated through the daemon API. */
|
|
25
24
|
async function nodeInPane(pane) {
|
|
26
25
|
if (pane === undefined || pane === '')
|
|
@@ -5,7 +5,8 @@ import { defineLeaf } from '../../core/command.js';
|
|
|
5
5
|
import { ticketDir } from '../../core/human/root.js';
|
|
6
6
|
import { scanInbox } from '../../core/human/scan.js';
|
|
7
7
|
import { parseDeck } from '../../core/human/deck-schema.js';
|
|
8
|
-
import { deckPath, isResolved,
|
|
8
|
+
import { deckPath, isResolved, reviewPath } from '../../core/human/convention.js';
|
|
9
|
+
import { readJsonOrNull } from '../../core/fs-utils.js';
|
|
9
10
|
import { InputError } from '../../core/io.js';
|
|
10
11
|
import { paginate } from '../../core/pagination.js';
|
|
11
12
|
import { cliClient, rethrowAsCliError } from '../api-client.js';
|
|
@@ -14,9 +15,9 @@ function toRef(n) {
|
|
|
14
15
|
return { node_id: n.node_id, name: n.name, cwd: n.cwd, parent: n.parent, status: n.status };
|
|
15
16
|
}
|
|
16
17
|
/** The canvas roster, fetched ONCE per leaf run through the API and threaded
|
|
17
|
-
* into the sync provenance helpers below (
|
|
18
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* into the sync provenance helpers below (the API surface is async, so the
|
|
19
|
+
* fetch is hoisted to the leaf and the helpers read the pre-fetched
|
|
20
|
+
* array/map). */
|
|
20
21
|
async function fetchNodeRefs() {
|
|
21
22
|
return (await cliClient().listNodes()).map(toRef);
|
|
22
23
|
}
|
|
@@ -67,7 +68,7 @@ function summarizeTicket(summary, nodes, byId) {
|
|
|
67
68
|
return null;
|
|
68
69
|
const asking = askingNodeFor(jobId, byId);
|
|
69
70
|
const conversation = resolveConversation(asking, nodes);
|
|
70
|
-
const interactionCount = summary.kind === 'deck' ?
|
|
71
|
+
const interactionCount = summary.kind === 'deck' ? readJsonOrNull(deckPath(summary.dir))?.interactions.length : undefined;
|
|
71
72
|
const sourceLabel = readableSourceLabel(summary.source);
|
|
72
73
|
return {
|
|
73
74
|
id: summary.id,
|
|
@@ -155,9 +155,9 @@ export const findLeaf = defineLeaf({
|
|
|
155
155
|
// Sort comparator MUST match keyOf exactly (score desc, then scope asc,
|
|
156
156
|
// then name asc as the final deterministic identity component — the
|
|
157
157
|
// candidate dedup keys on scope+name, so this pair is always unique).
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
//
|
|
158
|
+
// Sorting equal-score hits from different scopes by name alone while the
|
|
159
|
+
// cursor key orders by scope first would let the array disagree with its
|
|
160
|
+
// own keys, and paginate() would silently skip a later item.
|
|
161
161
|
hits.sort((a, b) => {
|
|
162
162
|
if (b.score !== a.score)
|
|
163
163
|
return b.score - a.score;
|
|
@@ -71,7 +71,7 @@ function nodeNewParams() {
|
|
|
71
71
|
{ kind: 'stdin', name: 'prompt', required: false, constraint: 'First user message. Required unless --no-kickoff is given.' },
|
|
72
72
|
{ kind: 'flag', name: 'kind', type: 'string', required: false, default: 'general', constraint: 'Persona kind. The <kinds> list below names every top-level installable kind.' },
|
|
73
73
|
{ kind: 'flag', name: 'mode', type: 'enum', choices: ['base', 'orchestrator'], required: false, default: 'base', constraint: 'Persona mode. base for hands-on work; orchestrator when the unit itself needs decomposition across children.' },
|
|
74
|
-
{ kind: 'flag', name: 'cwd', type: 'path', required: false, constraint: '
|
|
74
|
+
{ kind: 'flag', name: 'cwd', type: 'path', required: false, constraint: 'Pin the node to this directory. Defaults to the spawner\u2019s directory for a managed child, and to the profile home for a root (`crtr profile show <profile>`).' },
|
|
75
75
|
{ kind: 'flag', name: 'name', type: 'string', required: false, constraint: 'Display name. Defaults to the kind.' },
|
|
76
76
|
{ kind: 'flag', name: 'parent', type: 'string', required: false, constraint: 'Parent node id. Defaults to the calling node.' },
|
|
77
77
|
{ kind: 'flag', name: 'node-id', type: 'string', required: false, constraint: 'Exact id for the new node; lowercase letters, digits, and hyphens only, at most 128 characters. Rejected if already present.' },
|
|
@@ -106,7 +106,7 @@ async function runNodeCreation(input) {
|
|
|
106
106
|
}
|
|
107
107
|
const kind = input['kind'] ?? 'general';
|
|
108
108
|
const mode = (input['mode'] ?? 'base');
|
|
109
|
-
const
|
|
109
|
+
const pinCwd = input['cwd']?.trim();
|
|
110
110
|
const name = input['name'];
|
|
111
111
|
const parent = input['parent'];
|
|
112
112
|
const root = input['root'] === true;
|
|
@@ -140,7 +140,11 @@ async function runNodeCreation(input) {
|
|
|
140
140
|
const detail = await cliClient().createNode({
|
|
141
141
|
kind,
|
|
142
142
|
mode,
|
|
143
|
-
|
|
143
|
+
// The caller's directory is the profile-SELECTION context; where the node
|
|
144
|
+
// actually lands is resolved server-side (pin > spawner's cwd > profile
|
|
145
|
+
// home). Only `--cwd` pins.
|
|
146
|
+
cwd: process.cwd(),
|
|
147
|
+
...(pinCwd !== undefined && pinCwd !== '' ? { pin_cwd: pinCwd } : {}),
|
|
144
148
|
...(name !== undefined ? { name } : {}),
|
|
145
149
|
prompt,
|
|
146
150
|
...(root ? { root: true } : { parent: parentId ?? null }),
|
|
@@ -375,7 +375,7 @@ export const nodeYield = defineLeaf({
|
|
|
375
375
|
{ kind: 'flag', name: 'promote', type: 'bool', required: false, constraint: 'Also become an orchestrator as you refresh. Pass only when (1) the remaining work contains independent units that can run in parallel and (2) the task is large enough that parallel execution will materially improve intelligence, productivity, or elapsed throughput after coordination and synthesis. Task length, context exhaustion, sequential phases, or one helper are not enough; keep hands-on work in base mode and yield again as needed. Promote when coordinating and integrating children becomes your primary job. Already an orchestrator? This is not an error: your mode stays orchestrator and a missing roadmap is seeded.' },
|
|
376
376
|
{ kind: 'flag', name: 'kind', type: 'string', required: false, constraint: 'Respecialize as you refresh. The <kinds> list below names every top-level available kind and when to choose it; a sub-kind (e.g. plan/reviewers/security) is valid too by exact path but not listed here. Defaults to your current kind. With --promote it sets the orchestrator kind you become; without --promote it changes the kind while preserving your current mode.' },
|
|
377
377
|
{ kind: 'flag', name: 'model', type: 'enum', choices: ['ultra', 'strong'], required: false, constraint: 'Change the model your fresh revive runs on, by capability tier: `ultra` (frontier — reserve for high-taste judgment or enormous work: speccing, planning something large, e2e-testing something hard to test) or `strong` (opus — regular dev work). Omit to keep your current model. A node steering work is never weaker than opus, so these are the only two choices; the change is durable across future revives.' },
|
|
378
|
-
// Deliberate fail-first contract
|
|
378
|
+
// Deliberate fail-first contract: builtin orchestration guidance shows bare
|
|
379
379
|
// `crtr node yield`; that invocation fails fast and surfaces yield's full help just in
|
|
380
380
|
// time, at the point of use. Keep the note required rather than pre-documenting stdin
|
|
381
381
|
// in kernel guidance.
|
|
@@ -3,7 +3,7 @@ import type { Fault } from '../core/runtime/fault.js';
|
|
|
3
3
|
/** --now eligibility for a live, non-busy fault: a daemon-owned AUTO retry
|
|
4
4
|
* (kick it early instead of waiting out the schedule), or any fault the
|
|
5
5
|
* canvas dashboard flags "needs you" (`faultNeedsYou`, status-glyph.ts) — the
|
|
6
|
-
* disposition that otherwise has NO sanctioned recovery verb
|
|
6
|
+
* disposition that otherwise has NO sanctioned recovery verb.
|
|
7
7
|
* Both are scoped to `pi→provider`: refuses a client-owned auto retry
|
|
8
8
|
* (redialing) already in flight, and refuses any OTHER link — notably a
|
|
9
9
|
* `daemon→node` wedge fault, which --now can't clear (SIGTERM doesn't touch
|
|
@@ -40,7 +40,7 @@ import { waitForViewSocketReady } from '../core/runtime/view-socket.js';
|
|
|
40
40
|
/** --now eligibility for a live, non-busy fault: a daemon-owned AUTO retry
|
|
41
41
|
* (kick it early instead of waiting out the schedule), or any fault the
|
|
42
42
|
* canvas dashboard flags "needs you" (`faultNeedsYou`, status-glyph.ts) — the
|
|
43
|
-
* disposition that otherwise has NO sanctioned recovery verb
|
|
43
|
+
* disposition that otherwise has NO sanctioned recovery verb.
|
|
44
44
|
* Both are scoped to `pi→provider`: refuses a client-owned auto retry
|
|
45
45
|
* (redialing) already in flight, and refuses any OTHER link — notably a
|
|
46
46
|
* `daemon→node` wedge fault, which --now can't clear (SIGTERM doesn't touch
|
|
@@ -143,7 +143,7 @@ export const nodeReviveLeaf = defineLeaf({
|
|
|
143
143
|
// above) so it can't be used to nuke a healthy node. This whole path stays
|
|
144
144
|
// LOCAL: the kick is a direct SIGTERM to the broker pid (there is no revive
|
|
145
145
|
// API call), and the node detail comes from the daemon read (`getNode`). The
|
|
146
|
-
// reopen gate
|
|
146
|
+
// reopen gate is NOT invoked here — --now targets a LIVE broker, and a
|
|
147
147
|
// finalized node has none (its own live-pid check below refuses it anyway).
|
|
148
148
|
if (now) {
|
|
149
149
|
// The detail DTO projects `pi_pid_identity` (the launch-time process-identity
|
|
@@ -201,7 +201,7 @@ export const nodeReviveLeaf = defineLeaf({
|
|
|
201
201
|
};
|
|
202
202
|
}
|
|
203
203
|
// Ordinary revive: the daemon relaunches the broker (the ONLY sanctioned
|
|
204
|
-
// launcher). Node-existence validation (`not_found`) and the
|
|
204
|
+
// launcher). Node-existence validation (`not_found`) and the reopen gate
|
|
205
205
|
// are both enforced server-side in `handleRevive`; a thrown gate/not-found
|
|
206
206
|
// error round-trips through `ApiError.details` and is reconstructed into the
|
|
207
207
|
// identical `InputError` by the shared `apiErrorToCliError` (via
|
|
@@ -4,7 +4,7 @@ import { listProfiles } from '../../core/profiles/manifest.js';
|
|
|
4
4
|
export const listLeaf = defineLeaf({
|
|
5
5
|
name: 'list',
|
|
6
6
|
description: 'inventory every profile',
|
|
7
|
-
whenToUse: 'browse every profile that exists — one line each with its id, name, project count, and last-used time. Reach for `profile show` once you have picked one, for its full project list.',
|
|
7
|
+
whenToUse: 'browse every profile that exists — one line each with its id, name, project count, home directory, and last-used time. Reach for `profile show` once you have picked one, for its full project list.',
|
|
8
8
|
help: {
|
|
9
9
|
name: 'profile list',
|
|
10
10
|
summary: 'every profile as JSONL, one line each, sorted by name then profile_id',
|
|
@@ -12,6 +12,7 @@ export const listLeaf = defineLeaf({
|
|
|
12
12
|
{ name: 'profile_id', type: 'string', required: true, constraint: 'Stable directory id.' },
|
|
13
13
|
{ name: 'name', type: 'string', required: true, constraint: 'Manifest name.' },
|
|
14
14
|
{ name: 'projects_count', type: 'int', required: true, constraint: 'Number of project directories on the manifest.' },
|
|
15
|
+
{ name: 'home', type: 'string | null', required: true, constraint: 'Directory the profile\u2019s nodes are pinned to, or null when it owns no projects.' },
|
|
15
16
|
{ name: 'last_used_at', type: 'string | null', required: true, constraint: 'ISO 8601 timestamp of the most recent selection, or null if never selected.' },
|
|
16
17
|
],
|
|
17
18
|
outputKind: 'jsonl',
|
|
@@ -27,6 +28,7 @@ export const listLeaf = defineLeaf({
|
|
|
27
28
|
profile_id: profileId,
|
|
28
29
|
name: manifest.name,
|
|
29
30
|
projects_count: manifest.projects.length,
|
|
31
|
+
home: manifest.home,
|
|
30
32
|
last_used_at: manifest.last_used_at,
|
|
31
33
|
});
|
|
32
34
|
}
|
|
@@ -28,7 +28,7 @@ export const newLeaf = defineLeaf({
|
|
|
28
28
|
name: 'project',
|
|
29
29
|
type: 'path',
|
|
30
30
|
required: false,
|
|
31
|
-
constraint:
|
|
31
|
+
constraint: "One initial project directory. Must already exist and be a directory; resolved to its absolute real path before storing. Also becomes the profile's home — where its nodes are pinned. Singular — add more with `crtr profile project add` after creation.",
|
|
32
32
|
},
|
|
33
33
|
],
|
|
34
34
|
output: [
|
|
@@ -36,6 +36,7 @@ export const newLeaf = defineLeaf({
|
|
|
36
36
|
{ name: 'name', type: 'string', required: true, constraint: 'The manifest name as given.' },
|
|
37
37
|
{ name: 'path', type: 'string', required: true, constraint: 'Absolute path to the profile directory.' },
|
|
38
38
|
{ name: 'projects', type: 'string[]', required: true, constraint: 'Resolved absolute project directories, in manifest order. Empty when --project was omitted.' },
|
|
39
|
+
{ name: 'home', type: 'string | null', required: true, constraint: 'Directory the profile\u2019s nodes are pinned to — the initial project, or null when none was given.' },
|
|
39
40
|
{ name: 'created_at', type: 'string', required: true, constraint: 'ISO 8601 creation timestamp.' },
|
|
40
41
|
{ name: 'follow_up', type: 'string', required: true, constraint: 'Concrete next commands.' },
|
|
41
42
|
],
|
|
@@ -56,6 +57,7 @@ export const newLeaf = defineLeaf({
|
|
|
56
57
|
name: manifest.name,
|
|
57
58
|
path: profileRoot(profileId),
|
|
58
59
|
projects: manifest.projects,
|
|
60
|
+
home: manifest.home,
|
|
59
61
|
created_at: manifest.created_at,
|
|
60
62
|
follow_up: `Root profile ensured. Spawn nodes under it with \`--profile ${profileId}\`, or widen its purview with \`crtr profile project add ${profileId} --dir <path>\`.`,
|
|
61
63
|
};
|
|
@@ -68,6 +70,7 @@ export const newLeaf = defineLeaf({
|
|
|
68
70
|
name: manifest.name,
|
|
69
71
|
path: profileRoot(profileId),
|
|
70
72
|
projects: manifest.projects,
|
|
73
|
+
home: manifest.home,
|
|
71
74
|
created_at: manifest.created_at,
|
|
72
75
|
follow_up: `Show it with \`crtr profile show ${profileId}\`, add another project with \`crtr profile project add ${profileId} --dir <path>\`, or list every profile with \`crtr profile list\`.`,
|
|
73
76
|
};
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
// `crtr profile project` — the project-purview sub-noun of `profile`. A
|
|
2
|
-
// profile's `projects` list is what it can SEE (memory + config resolution)
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// profile's `projects` list is what it can SEE (memory + config resolution),
|
|
3
|
+
// and its `home` — one of those dirs — is where its nodes actually run. These
|
|
4
|
+
// two verbs widen or narrow that list after creation and re-point the home;
|
|
5
|
+
// `crtr profile show` reads both back.
|
|
5
6
|
import { defineLeaf, defineBranch } from '../../core/command.js';
|
|
6
7
|
import { addProfileProject, removeProfileProject, loadProfileManifest } from '../../core/profiles/manifest.js';
|
|
7
8
|
const addLeaf = defineLeaf({
|
|
8
9
|
name: 'add',
|
|
9
10
|
description: "add a project directory to a profile's purview",
|
|
10
|
-
whenToUse: "a profile needs to see (memory + config from) another project directory — extend its purview after creation, including mid-session from a node already running under that profile.",
|
|
11
|
+
whenToUse: "a profile needs to see (memory + config from) another project directory — extend its purview after creation, including mid-session from a node already running under that profile. Also the way to re-point the profile's home, with --home.",
|
|
11
12
|
help: {
|
|
12
13
|
name: 'profile project add',
|
|
13
14
|
summary: "append a project directory to a profile's manifest, deduped by real path",
|
|
@@ -25,21 +26,32 @@ const addLeaf = defineLeaf({
|
|
|
25
26
|
required: true,
|
|
26
27
|
constraint: 'Directory to add. Must exist and be a directory; resolved to its absolute real path before storing. A path already on the manifest is a no-op, not an error.',
|
|
27
28
|
},
|
|
29
|
+
{
|
|
30
|
+
kind: 'flag',
|
|
31
|
+
name: 'home',
|
|
32
|
+
type: 'bool',
|
|
33
|
+
required: false,
|
|
34
|
+
constraint: "Also make this directory the profile's home — where every node under it is pinned unless the create passes an explicit --cwd. Defaults to the first project dir; pass this on an already-listed dir to re-point it.",
|
|
35
|
+
},
|
|
28
36
|
],
|
|
29
37
|
output: [
|
|
30
38
|
{ name: 'profile_id', type: 'string', required: true, constraint: 'Stable directory id.' },
|
|
31
39
|
{ name: 'projects', type: 'string[]', required: true, constraint: 'The updated, deduped project list in manifest order.' },
|
|
40
|
+
{ name: 'home', type: 'string | null', required: true, constraint: "The profile's home directory after the change." },
|
|
32
41
|
{ name: 'follow_up', type: 'string', required: true, constraint: 'Concrete next commands.' },
|
|
33
42
|
],
|
|
34
43
|
outputKind: 'object',
|
|
35
|
-
effects: ["Appends to the profile manifest's `projects` array. Invalidates the process scope cache."],
|
|
44
|
+
effects: ["Appends to the profile manifest's `projects` array, and rewrites `home` when --home is given. Invalidates the process scope cache."],
|
|
36
45
|
},
|
|
37
46
|
run: async (input) => {
|
|
38
47
|
const { profileId } = loadProfileManifest(input['profile']);
|
|
39
|
-
const { manifest } = addProfileProject(profileId, input['dir']
|
|
48
|
+
const { manifest } = addProfileProject(profileId, input['dir'], {
|
|
49
|
+
home: input['home'] === true,
|
|
50
|
+
});
|
|
40
51
|
return {
|
|
41
52
|
profile_id: profileId,
|
|
42
53
|
projects: manifest.projects,
|
|
54
|
+
home: manifest.home,
|
|
43
55
|
follow_up: `Show the full manifest with \`crtr profile show ${profileId}\`.`,
|
|
44
56
|
};
|
|
45
57
|
},
|
|
@@ -63,16 +75,17 @@ const removeLeaf = defineLeaf({
|
|
|
63
75
|
name: 'dir',
|
|
64
76
|
type: 'path',
|
|
65
77
|
required: true,
|
|
66
|
-
constraint: 'Directory to remove, matched by its resolved real path against the manifest. Does not require the directory to still exist on disk.',
|
|
78
|
+
constraint: 'Directory to remove, matched by its resolved real path against the manifest. Does not require the directory to still exist on disk. Removing the home dir re-points the home at the first remaining project.',
|
|
67
79
|
},
|
|
68
80
|
],
|
|
69
81
|
output: [
|
|
70
82
|
{ name: 'profile_id', type: 'string', required: true, constraint: 'Stable directory id.' },
|
|
71
83
|
{ name: 'projects', type: 'string[]', required: true, constraint: 'The updated project list in manifest order.' },
|
|
84
|
+
{ name: 'home', type: 'string | null', required: true, constraint: "The profile's home directory after the change." },
|
|
72
85
|
{ name: 'follow_up', type: 'string', required: true, constraint: 'Concrete next commands.' },
|
|
73
86
|
],
|
|
74
87
|
outputKind: 'object',
|
|
75
|
-
effects: ["Removes an entry from the profile manifest's `projects` array. Invalidates the process scope cache."],
|
|
88
|
+
effects: ["Removes an entry from the profile manifest's `projects` array, re-pointing `home` when that entry was it. Invalidates the process scope cache."],
|
|
76
89
|
},
|
|
77
90
|
run: async (input) => {
|
|
78
91
|
const { profileId } = loadProfileManifest(input['profile']);
|
|
@@ -80,6 +93,7 @@ const removeLeaf = defineLeaf({
|
|
|
80
93
|
return {
|
|
81
94
|
profile_id: profileId,
|
|
82
95
|
projects: manifest.projects,
|
|
96
|
+
home: manifest.home,
|
|
83
97
|
follow_up: `Show the full manifest with \`crtr profile show ${profileId}\`.`,
|
|
84
98
|
};
|
|
85
99
|
},
|
|
@@ -91,7 +105,7 @@ export const projectBranch = defineBranch({
|
|
|
91
105
|
help: {
|
|
92
106
|
name: 'profile project',
|
|
93
107
|
summary: "manage a profile's project purview — the directories it resolves memory and config from",
|
|
94
|
-
model: "A profile's `projects` list is the set of directories it inherits memory and config from. `add` widens that purview by one directory (deduped by real path); `remove` narrows it. Read
|
|
108
|
+
model: "A profile's `projects` list is the set of directories it inherits memory and config from, and its `home` — the first project unless `add --home` says otherwise — is the directory its nodes are pinned to. `add` widens that purview by one directory (deduped by real path) and optionally re-points the home; `remove` narrows it, re-pointing the home when it removed it. Read both back with `crtr profile show`. Purview is independent of a per-cwd default pin (`crtr profile default`) — widening purview never changes which profile a directory defaults to.",
|
|
95
109
|
},
|
|
96
110
|
children: [addLeaf, removeLeaf],
|
|
97
111
|
});
|
|
@@ -6,7 +6,7 @@ import { CONFIG_FILE } from '../../types.js';
|
|
|
6
6
|
export const showLeaf = defineLeaf({
|
|
7
7
|
name: 'show',
|
|
8
8
|
description: "one profile's manifest in full",
|
|
9
|
-
whenToUse: "you need a profile's full project list, id, and timestamps — after `profile list` narrows to a name, or to inspect what a node's bearings named as its profile.",
|
|
9
|
+
whenToUse: "you need a profile's full project list, home directory, id, and timestamps — after `profile list` narrows to a name, to find where its nodes will run, or to inspect what a node's bearings named as its profile.",
|
|
10
10
|
help: {
|
|
11
11
|
name: 'profile show',
|
|
12
12
|
summary: "print one profile's manifest plus its on-disk path and config.json location",
|
|
@@ -22,6 +22,7 @@ export const showLeaf = defineLeaf({
|
|
|
22
22
|
{ name: 'profile_id', type: 'string', required: true, constraint: 'Stable directory id.' },
|
|
23
23
|
{ name: 'name', type: 'string', required: true, constraint: 'Manifest name.' },
|
|
24
24
|
{ name: 'projects', type: 'string[]', required: true, constraint: 'Absolute project directories, in manifest order.' },
|
|
25
|
+
{ name: 'home', type: 'string | null', required: true, constraint: 'The directory every node under this profile is pinned to unless its create passes an explicit --cwd. Defaults to the first project; re-point it with `profile project add --home`. Null when the profile owns no projects (the root profile), in which case nodes fall back to the creating directory.' },
|
|
25
26
|
{ name: 'created_at', type: 'string', required: true, constraint: 'ISO 8601 creation timestamp.' },
|
|
26
27
|
{ name: 'last_used_at', type: 'string | null', required: true, constraint: 'ISO 8601 timestamp of the most recent selection, or null if never selected.' },
|
|
27
28
|
{ name: 'path', type: 'string', required: true, constraint: 'Absolute path to the profile directory.' },
|
|
@@ -39,6 +40,7 @@ export const showLeaf = defineLeaf({
|
|
|
39
40
|
profile_id: profileId,
|
|
40
41
|
name: manifest.name,
|
|
41
42
|
projects: manifest.projects,
|
|
43
|
+
home: manifest.home,
|
|
42
44
|
created_at: manifest.created_at,
|
|
43
45
|
last_used_at: manifest.last_used_at,
|
|
44
46
|
path: profileRoot(profileId),
|
package/dist/commands/profile.js
CHANGED
|
@@ -23,14 +23,14 @@ export function registerProfile() {
|
|
|
23
23
|
return defineBranch({
|
|
24
24
|
name: 'profile',
|
|
25
25
|
rootEntry: {
|
|
26
|
-
concept: 'an agent identity defined at the user root — a manifest naming project directories plus its own memory store',
|
|
26
|
+
concept: 'an agent identity defined at the user root — a manifest naming project directories and the home its nodes run in, plus its own memory store',
|
|
27
27
|
desc: 'create, inspect, and manage profiles',
|
|
28
28
|
useWhen: 'creating a new agent identity, inspecting or listing existing ones, changing a profile\u2019s project purview, pinning which profile a directory defaults to at startup, or managing a profile\u2019s stored env vars. Selecting a profile for a one-off run (`--profile`) lives elsewhere.',
|
|
29
29
|
},
|
|
30
30
|
help: {
|
|
31
31
|
name: 'profile',
|
|
32
32
|
summary: 'create, inspect, and manage profiles \u2014 agent identities with their own project purview and memory store',
|
|
33
|
-
model: '`new` creates one from a name (+ at most one initial `--project
|
|
33
|
+
model: 'A profile owns a `home` — the first of its project dirs unless `project add --home` re-points it — and every node created under the profile is pinned there, so cwd selects the profile and the profile then supplies where its nodes run. Only `node new --cwd` (or inheriting a spawner already elsewhere, notably inside a managed worktree) overrides that; the root profile has no home, so nodes under it stay in the directory that created them. `new` creates one from a name (+ at most one initial `--project`, which becomes its home). `list` inventories every profile as JSONL. `show` prints one manifest in full. `rename` changes only the manifest name \u2014 the profile_id is stable across a rename. `delete` removes a profile\u2019s directory (manifest + its own memory store) entirely and does not touch the project directories it pointed at. `project` widens/narrows a profile\u2019s project purview (the dirs it resolves memory + config from). `default` pins which profile a directory defaults to at startup so the chooser stops asking \u2014 directory-scoped, independent of purview. `env` manages a profile-scoped 0600 store of env vars injected directly into every broker launched under that profile \u2014 set on stdin, list names only (never values), or remove one; no `spawnEnv.allow` entry needed. Every manifest verb but `new`/`list` takes <profile> as an exact id or a unique manifest name; an ambiguous name fails listing every matching id.',
|
|
34
34
|
},
|
|
35
35
|
children: [newLeaf, listLeaf, showLeaf, renameLeaf, deleteLeaf, projectBranch, defaultBranch, envBranch],
|
|
36
36
|
});
|
package/dist/commands/push.js
CHANGED
|
@@ -73,7 +73,7 @@ function makeTierLeaf(tier) {
|
|
|
73
73
|
...(tier === 'final'
|
|
74
74
|
? [
|
|
75
75
|
'Marks the node done (status + intent) server-side; its engine shuts down on next stop.',
|
|
76
|
-
'When this node owns a managed worktree with zero commits ahead of its base and no uncommitted changes, the server auto-drops it instead of blocking on `node worktree close
|
|
76
|
+
'When this node owns a managed worktree with zero commits ahead of its base and no uncommitted changes, the server auto-drops it instead of blocking on `node worktree close`; a worktree with real commits or real uncommitted changes still blocks with open_managed_worktree.',
|
|
77
77
|
]
|
|
78
78
|
: []),
|
|
79
79
|
],
|
|
@@ -39,7 +39,7 @@ export declare function openSpawnViewer(nodeId: string, cwd: string, name: strin
|
|
|
39
39
|
* A `crtr surface attach` viewer self-tags its pane with the node it views
|
|
40
40
|
* (`@crtr_node`) — the tag IS the handle (a broker engine runs DETACHED with
|
|
41
41
|
* window=null, so there is no window→node fallback). Kept SYNC + tag-only: the
|
|
42
|
-
*
|
|
42
|
+
* CLI does not read canvas state to confirm the tagged node is
|
|
43
43
|
* still live, so a stale tag (attach SIGKILLed without clearing) resolves until
|
|
44
44
|
* the pane is respawned. Callers that mutate then re-validate the id via the
|
|
45
45
|
* API (getNode 404 → clean error). */
|
|
@@ -111,7 +111,7 @@ export function openSpawnViewer(nodeId, cwd, name) {
|
|
|
111
111
|
* A `crtr surface attach` viewer self-tags its pane with the node it views
|
|
112
112
|
* (`@crtr_node`) — the tag IS the handle (a broker engine runs DETACHED with
|
|
113
113
|
* window=null, so there is no window→node fallback). Kept SYNC + tag-only: the
|
|
114
|
-
*
|
|
114
|
+
* CLI does not read canvas state to confirm the tagged node is
|
|
115
115
|
* still live, so a stale tag (attach SIGKILLed without clearing) resolves until
|
|
116
116
|
* the pane is respawned. Callers that mutate then re-validate the id via the
|
|
117
117
|
* API (getNode 404 → clean error). */
|
|
@@ -33,6 +33,7 @@ import { isAbsolute, resolve } from 'node:path';
|
|
|
33
33
|
import { defineLeaf } from '../core/command.js';
|
|
34
34
|
import { InputError } from '../core/io.js';
|
|
35
35
|
import { currentTmux, splitWindow, paneLocation, paneOfNode, } from '../core/runtime/placement-tmux.js';
|
|
36
|
+
import { shellQuote } from '../shared/shell-quote.js';
|
|
36
37
|
/** Resolve the pane the editor should open BESIDE — the pane that opened this
|
|
37
38
|
* command: explicit --pane, the caller's own $TMUX_PANE, the pane where THIS
|
|
38
39
|
* node is being watched (its `@crtr_node`-tagged pane, whatever viewer put it
|
|
@@ -57,10 +58,6 @@ function resolveTargetPane(explicit) {
|
|
|
57
58
|
}
|
|
58
59
|
return currentTmux()?.pane ?? null;
|
|
59
60
|
}
|
|
60
|
-
/** POSIX single-quote a shell word so paths survive spaces/specials. */
|
|
61
|
-
function shellQuote(s) {
|
|
62
|
-
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
63
|
-
}
|
|
64
61
|
/** The command the new pane runs. The pane INHERITS the tmux server environment
|
|
65
62
|
* (like every crtr chrome pane — the subagent viewer panes get theirs the same
|
|
66
63
|
* way), and a tmux server started from the human's interactive shell captured
|
|
@@ -3,7 +3,7 @@ import { isSafeNodeId } from '../../core/canvas/paths.js';
|
|
|
3
3
|
import { usage } from '../../core/errors.js';
|
|
4
4
|
import { validateOperationId } from '../../core/events/operation-id.js';
|
|
5
5
|
import { readLogSnapshot } from '../../core/events/read.js';
|
|
6
|
-
import { emitLine, isJsonOutput } from '../../core/io.js';
|
|
6
|
+
import { emitLine, isJsonOutput, recordPreviewLine } from '../../core/io.js';
|
|
7
7
|
const ISO_TIMESTAMP = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/u;
|
|
8
8
|
const MAX_PATTERN_BYTES = 4_096;
|
|
9
9
|
function invalidFlag(flag, received, expected) {
|
|
@@ -116,8 +116,10 @@ export const sysLogsLeaf = defineLeaf({
|
|
|
116
116
|
if (item.kind === 'event') {
|
|
117
117
|
if (json)
|
|
118
118
|
emitLine({ ...item.envelope });
|
|
119
|
-
else
|
|
119
|
+
else {
|
|
120
|
+
recordPreviewLine({ ...item.envelope });
|
|
120
121
|
process.stdout.write(`${renderEvent(item.envelope)}\n`);
|
|
122
|
+
}
|
|
121
123
|
}
|
|
122
124
|
else if (!json) {
|
|
123
125
|
process.stdout.write(`RAW ${item.source} ${new Date(item.effectiveTsMs).toISOString()} ${JSON.stringify(item.content)}\n`);
|
|
@@ -7,9 +7,9 @@ export interface SetupPanel {
|
|
|
7
7
|
type ThemeFgRole = "border" | "accent" | "dim" | "success" | "warning" | "muted" | "mdLink" | "mdLinkUrl";
|
|
8
8
|
export declare const theme: {
|
|
9
9
|
fg(role: ThemeFgRole, text: string): string;
|
|
10
|
-
/** A real filled selection band. `selectedBg`
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
/** A real filled selection band. An identity `selectedBg` would leave the
|
|
11
|
+
* active tab and the active row with no background at all — reverse video is
|
|
12
|
+
* the one background every terminal theme honours. */
|
|
13
13
|
bg(_role: "selectedBg", text: string): string;
|
|
14
14
|
bold(text: string): string;
|
|
15
15
|
};
|
|
@@ -23,9 +23,9 @@ export const theme = {
|
|
|
23
23
|
return ansiYellow(text);
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
|
-
/** A real filled selection band. `selectedBg`
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
/** A real filled selection band. An identity `selectedBg` would leave the
|
|
27
|
+
* active tab and the active row with no background at all — reverse video is
|
|
28
|
+
* the one background every terminal theme honours. */
|
|
29
29
|
bg(_role, text) {
|
|
30
30
|
return `\x1b[7m${text}\x1b[27m`;
|
|
31
31
|
},
|