@crouton-kit/crouter 0.3.65 → 0.3.67
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/builtin-memory/05-kinds/developer/00-base.md +1 -1
- package/dist/builtin-memory/05-kinds/developer/01-orchestrator.md +2 -0
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/__tests__/provider-rotation.test.ts +24 -12
- package/dist/builtin-pi-packages/pi-crtr-extensions/extensions/provider-rotation.ts +41 -16
- package/dist/clients/attach/__tests__/attach-chrome-remote.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/attach-chrome-remote.test.js +371 -0
- package/dist/clients/attach/__tests__/attach-remote-readonly.test.d.ts +1 -0
- package/dist/clients/attach/__tests__/attach-remote-readonly.test.js +191 -0
- package/dist/clients/attach/__tests__/transport-relay.test.js +38 -1
- package/dist/clients/attach/attach-cmd.d.ts +30 -2
- package/dist/clients/attach/attach-cmd.js +632 -632
- package/dist/clients/attach/canvas-panels.js +9 -3
- package/dist/clients/attach/graph-overlay.d.ts +5 -0
- package/dist/clients/attach/graph-overlay.js +65 -11
- package/dist/clients/attach/input-controller.d.ts +9 -0
- package/dist/clients/attach/input-controller.js +47 -8
- package/dist/clients/attach/slash-commands.d.ts +11 -4
- package/dist/clients/attach/slash-commands.js +44 -9
- package/dist/clients/attach/transport-relay.d.ts +5 -5
- package/dist/clients/attach/transport-relay.js +3 -30
- package/dist/commands/__tests__/canvas-config.test.d.ts +1 -0
- package/dist/commands/__tests__/canvas-config.test.js +107 -0
- package/dist/commands/canvas-browse.js +26 -1
- package/dist/commands/canvas-config.d.ts +2 -0
- package/dist/commands/canvas-config.js +210 -0
- package/dist/commands/canvas-use.d.ts +1 -0
- package/dist/commands/canvas-use.js +63 -0
- package/dist/commands/canvas.js +4 -2
- package/dist/commands/node-lifecycle-revive.js +9 -2
- package/dist/commands/node.js +41 -11
- package/dist/core/__tests__/on-read-crouter-home-fence.test.d.ts +1 -0
- package/dist/core/__tests__/on-read-crouter-home-fence.test.js +67 -0
- package/dist/core/__tests__/remote-canvas-target.test.d.ts +1 -0
- package/dist/core/__tests__/remote-canvas-target.test.js +87 -0
- package/dist/core/__tests__/secrets.test.d.ts +1 -0
- package/dist/core/__tests__/secrets.test.js +55 -0
- package/dist/core/canvas/__tests__/remote-canvas-source.test.d.ts +1 -0
- package/dist/core/canvas/__tests__/remote-canvas-source.test.js +371 -0
- package/dist/core/canvas/__tests__/remote-event-stream.test.d.ts +1 -0
- package/dist/core/canvas/__tests__/remote-event-stream.test.js +144 -0
- package/dist/core/canvas/__tests__/render-remote.test.d.ts +1 -0
- package/dist/core/canvas/__tests__/render-remote.test.js +221 -0
- package/dist/core/canvas/__tests__/source-resolve.test.d.ts +1 -0
- package/dist/core/canvas/__tests__/source-resolve.test.js +77 -0
- package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.d.ts +1 -0
- package/dist/core/canvas/browse/__tests__/rebuild-coalescer.test.js +117 -0
- package/dist/core/canvas/browse/app.d.ts +14 -0
- package/dist/core/canvas/browse/app.js +97 -15
- package/dist/core/canvas/browse/rebuild-coalescer.d.ts +7 -0
- package/dist/core/canvas/browse/rebuild-coalescer.js +61 -0
- package/dist/core/canvas/browse/render.d.ts +4 -0
- package/dist/core/canvas/browse/render.js +3 -1
- package/dist/core/canvas/nav-model.d.ts +19 -10
- package/dist/core/canvas/nav-model.js +30 -12
- package/dist/core/canvas/pid.d.ts +71 -13
- package/dist/core/canvas/pid.js +73 -29
- package/dist/core/canvas/remote-canvas-source.d.ts +62 -0
- package/dist/core/canvas/remote-canvas-source.js +222 -0
- package/dist/core/canvas/remote-event-stream.d.ts +24 -0
- package/dist/core/canvas/remote-event-stream.js +94 -0
- package/dist/core/canvas/render.d.ts +13 -1
- package/dist/core/canvas/render.js +56 -37
- package/dist/core/canvas/source.d.ts +9 -0
- package/dist/core/canvas/source.js +15 -0
- package/dist/core/config.js +4 -3
- package/dist/core/runtime/__tests__/node-env.test.d.ts +1 -0
- package/dist/core/runtime/__tests__/node-env.test.js +91 -0
- package/dist/core/runtime/nodes.js +10 -0
- package/dist/core/runtime/revive-all.js +8 -2
- package/dist/core/runtime/revive.js +18 -8
- package/dist/core/secrets.d.ts +25 -0
- package/dist/core/secrets.js +55 -0
- package/dist/core/substrate/on-read.js +13 -2
- package/dist/core/view/__tests__/transport-remote.test.d.ts +1 -0
- package/dist/core/view/__tests__/transport-remote.test.js +95 -0
- package/dist/core/view/remote-canvas-target.d.ts +2 -1
- package/dist/core/view/remote-canvas-target.js +12 -7
- package/dist/core/view/transport-remote.d.ts +2 -0
- package/dist/core/view/transport-remote.js +53 -0
- package/dist/daemon/crtrd.js +18 -8
- package/dist/types.d.ts +19 -11
- package/dist/types.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// transport-remote.test.ts — `createRemoteExec` (`../transport-remote.ts`), the
|
|
2
|
+
// Node-side POST transport `RemoteCanvasSource` shells every remote read
|
|
3
|
+
// through. Covers the one thing a bare `fetch(...)` with no
|
|
4
|
+
// AbortController/timeout doesn't give you for free: a hung endpoint fails
|
|
5
|
+
// CLOSED within a bound instead of wedging the caller forever. WITHOUT the
|
|
6
|
+
// timeout, the "stalled endpoint" test below would hang past its own
|
|
7
|
+
// assertion deadline (the promise the transport returns never settles).
|
|
8
|
+
import { test } from 'node:test';
|
|
9
|
+
import assert from 'node:assert/strict';
|
|
10
|
+
import { createServer } from 'node:http';
|
|
11
|
+
import { createRemoteExec } from '../transport-remote.js';
|
|
12
|
+
function startStalledServer() {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
// Accept the connection but never write a response — the request just
|
|
15
|
+
// hangs, exactly what a wedged in-guest `/__crtr/source` handler looks
|
|
16
|
+
// like from the caller's side.
|
|
17
|
+
const server = createServer(() => { });
|
|
18
|
+
server.listen(0, '127.0.0.1', () => {
|
|
19
|
+
const addr = server.address();
|
|
20
|
+
const port = typeof addr === 'object' && addr !== null ? addr.port : 0;
|
|
21
|
+
resolve({
|
|
22
|
+
url: `http://127.0.0.1:${port}`,
|
|
23
|
+
close: () => new Promise((res) => server.close(() => res())),
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
test('a stalled endpoint returns a timeout failure within the configured bound instead of hanging forever', async () => {
|
|
29
|
+
const server = await startStalledServer();
|
|
30
|
+
const exec = createRemoteExec(server.url, 'tok', 100); // small timeout for the test
|
|
31
|
+
const started = Date.now();
|
|
32
|
+
// Race against a generous outer deadline: if the transport doesn't
|
|
33
|
+
// self-bound, this whole test times out instead of asserting cleanly —
|
|
34
|
+
// still proves the point, but assert explicitly for a clear failure message.
|
|
35
|
+
const result = await Promise.race([
|
|
36
|
+
exec('crtr', ['--json', 'node', 'inspect', 'list']),
|
|
37
|
+
new Promise((_resolve, reject) => setTimeout(() => reject(new Error('transport did not resolve within the outer deadline — no timeout is wired')), 2000)),
|
|
38
|
+
]);
|
|
39
|
+
const elapsed = Date.now() - started;
|
|
40
|
+
assert.equal(result.ok, false);
|
|
41
|
+
assert.match(result.stderr, /timeout/i);
|
|
42
|
+
assert.ok(elapsed < 1000, `expected the timeout to fire near the configured 100ms bound, took ${elapsed}ms`);
|
|
43
|
+
await server.close();
|
|
44
|
+
});
|
|
45
|
+
test('a normal fast response is unaffected by the timeout wiring', async () => {
|
|
46
|
+
const server = createServer((req, res) => {
|
|
47
|
+
const chunks = [];
|
|
48
|
+
req.on('data', (c) => chunks.push(c));
|
|
49
|
+
req.on('end', () => {
|
|
50
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
51
|
+
res.end(JSON.stringify({ ok: true, exitCode: 0, stdout: JSON.stringify({ nodes: [] }), stderr: '' }));
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
await new Promise((r) => server.listen(0, '127.0.0.1', r));
|
|
55
|
+
const addr = server.address();
|
|
56
|
+
const port = typeof addr === 'object' && addr !== null ? addr.port : 0;
|
|
57
|
+
const exec = createRemoteExec(`http://127.0.0.1:${port}`, 'tok', 5000);
|
|
58
|
+
const result = await exec('crtr', ['--json', 'node', 'inspect', 'list']);
|
|
59
|
+
assert.equal(result.ok, true);
|
|
60
|
+
assert.equal(result.stdout, JSON.stringify({ nodes: [] }));
|
|
61
|
+
await new Promise((r) => server.close(() => r()));
|
|
62
|
+
});
|
|
63
|
+
// --- Phase 3 review Minor 1: no token bytes may ever reach returned stderr ---
|
|
64
|
+
test('an unreachable endpoint returns a flat generic network-error message, never the raw exception text', async () => {
|
|
65
|
+
const exec = createRemoteExec('http://127.0.0.1:1', 'valid-token-123');
|
|
66
|
+
const result = await exec('crtr', ['--json', 'node', 'inspect', 'list']);
|
|
67
|
+
assert.equal(result.ok, false);
|
|
68
|
+
assert.equal(result.stderr, 'remote source: network error');
|
|
69
|
+
});
|
|
70
|
+
test('a malformed token (contains a newline) is rejected up front with a generic message, never reaching fetch/Headers', async () => {
|
|
71
|
+
const exec = createRemoteExec('http://127.0.0.1:1', 'bad\ntoken');
|
|
72
|
+
const result = await exec('crtr', ['--json', 'node', 'inspect', 'list']);
|
|
73
|
+
assert.equal(result.ok, false);
|
|
74
|
+
assert.equal(result.stderr, 'remote source: invalid stored relay token');
|
|
75
|
+
assert.ok(!result.stderr.includes('bad\ntoken'));
|
|
76
|
+
assert.ok(!result.stderr.includes('bad'));
|
|
77
|
+
});
|
|
78
|
+
test('a valid token containing typical bearer-token punctuation (-, _, .) still round-trips against a real server', async () => {
|
|
79
|
+
const server = createServer((req, res) => {
|
|
80
|
+
const chunks = [];
|
|
81
|
+
req.on('data', (c) => chunks.push(c));
|
|
82
|
+
req.on('end', () => {
|
|
83
|
+
res.writeHead(200, { 'content-type': 'application/json' });
|
|
84
|
+
res.end(JSON.stringify({ ok: true, exitCode: 0, stdout: JSON.stringify({ nodes: [] }), stderr: '' }));
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
await new Promise((r) => server.listen(0, '127.0.0.1', r));
|
|
88
|
+
const addr = server.address();
|
|
89
|
+
const port = typeof addr === 'object' && addr !== null ? addr.port : 0;
|
|
90
|
+
const exec = createRemoteExec(`http://127.0.0.1:${port}`, 'a1b2c3-real-token_ABC.789', 5000);
|
|
91
|
+
const result = await exec('crtr', ['--json', 'node', 'inspect', 'list']);
|
|
92
|
+
assert.equal(result.ok, true);
|
|
93
|
+
assert.equal(result.stdout, JSON.stringify({ nodes: [] }));
|
|
94
|
+
await new Promise((r) => server.close(() => r()));
|
|
95
|
+
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import type { Scope } from '../../types.js';
|
|
1
|
+
import type { Scope, RemoteCanvasTarget } from '../../types.js';
|
|
2
2
|
import type { RelayTarget } from '../../clients/attach/transport-relay.js';
|
|
3
|
+
export declare function getRemoteCanvasTarget(name: string, scope?: Scope): RemoteCanvasTarget | undefined;
|
|
3
4
|
export declare function resolveRemoteCanvasTarget(name: string, scope?: Scope): RelayTarget;
|
|
@@ -2,12 +2,17 @@
|
|
|
2
2
|
// config entry (see `RemoteCanvasTarget`, `src/types.ts`) into a `RelayTarget`
|
|
3
3
|
// ready for `RelayTransport` (`src/clients/attach/transport-relay.ts`).
|
|
4
4
|
//
|
|
5
|
-
// The raw bearer token NEVER lives in config
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
5
|
+
// The raw bearer token NEVER lives in config.json — only a ref
|
|
6
|
+
// (`relayTokenRef`) into the 0600 secrets store (`src/core/secrets.ts`),
|
|
7
|
+
// resolved here at connect time via `getRelayToken`. Targets are registered
|
|
8
|
+
// and the token stored via `crtr canvas config add` (`--relay-token -` reads
|
|
9
|
+
// it from stdin, the preferred form to keep it out of shell history).
|
|
9
10
|
import { usage } from '../errors.js';
|
|
10
11
|
import { readConfig } from '../config.js';
|
|
12
|
+
import { getRelayToken } from '../secrets.js';
|
|
13
|
+
export function getRemoteCanvasTarget(name, scope = 'user') {
|
|
14
|
+
return readConfig(scope).remoteCanvas.targets[name];
|
|
15
|
+
}
|
|
11
16
|
export function resolveRemoteCanvasTarget(name, scope = 'user') {
|
|
12
17
|
const cfg = readConfig(scope);
|
|
13
18
|
const target = cfg.remoteCanvas.targets[name];
|
|
@@ -15,11 +20,11 @@ export function resolveRemoteCanvasTarget(name, scope = 'user') {
|
|
|
15
20
|
const known = Object.keys(cfg.remoteCanvas.targets);
|
|
16
21
|
throw usage(`no remote canvas target named '${name}'` +
|
|
17
22
|
(known.length > 0 ? ` — configured: ${known.join(', ')}` : ' — none configured yet') +
|
|
18
|
-
`. Add
|
|
23
|
+
`. Add one with \`crtr canvas config add ${name} --preview <url> --relay-token -\`.`);
|
|
19
24
|
}
|
|
20
|
-
const relayToken =
|
|
25
|
+
const relayToken = getRelayToken(target.relayTokenRef, scope);
|
|
21
26
|
if (relayToken === undefined || relayToken === '') {
|
|
22
|
-
throw usage(`
|
|
27
|
+
throw usage(`target '${name}' has no stored relay token — add it with \`crtr canvas config add ${name} --relay-token -\`.`);
|
|
23
28
|
}
|
|
24
29
|
return { previewEndpoint: target.previewEndpoint, relayToken };
|
|
25
30
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// transport-remote.ts — the Node-side remote exec transport for a
|
|
2
|
+
// RemoteCanvasSource. POSTs a SourceRequest to a Blaxel preview endpoint's
|
|
3
|
+
// `/__crtr/source` route (the SAME in-guest source-fulfillment endpoint the
|
|
4
|
+
// browser/web transport already speaks), bearer-authed with the relay token.
|
|
5
|
+
//
|
|
6
|
+
// This is the ONLY way RemoteCanvasSource reaches the remote canvas: it never
|
|
7
|
+
// reads a remote SQLite file, and it never invokes an in-guest command that
|
|
8
|
+
// doesn't already exist. Every argv it sends is `crtr --json <existing
|
|
9
|
+
// subcommand>` — read-only.
|
|
10
|
+
/** The remote read plane crosses a network boundary — a stalled TCP connection
|
|
11
|
+
* or a hung `/__crtr/source` handler must fail closed, not wedge `node
|
|
12
|
+
* inspect`/`canvas browse`/attach forever. Low enough that a genuinely hung
|
|
13
|
+
* endpoint surfaces as a failed read within one interactive frame, not a
|
|
14
|
+
* silent hang. */
|
|
15
|
+
const DEFAULT_TIMEOUT_MS = 5_000;
|
|
16
|
+
export function createRemoteExec(previewEndpoint, relayToken, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
17
|
+
// A malformed stored token (control chars / non-Latin1 bytes) would throw
|
|
18
|
+
// from the Headers constructor with the invalid value IN the thrown
|
|
19
|
+
// message — validate up front so that never happens, and fail with a
|
|
20
|
+
// GENERIC message instead (Phase 3 review Minor 1: no token bytes may ever
|
|
21
|
+
// reach returned stderr). HTTP header values are ISO-8859-1; reject
|
|
22
|
+
// anything outside `/^[\x20-\x7e]+$/` (printable ASCII, no CR/LF/control).
|
|
23
|
+
const tokenValid = /^[\x20-\x7e]+$/.test(relayToken);
|
|
24
|
+
return async function exec(bin, args) {
|
|
25
|
+
if (!tokenValid) {
|
|
26
|
+
return { ok: false, stdout: '', stderr: 'remote source: invalid stored relay token' };
|
|
27
|
+
}
|
|
28
|
+
const req = { kind: 'exec', bin, args };
|
|
29
|
+
const controller = new AbortController();
|
|
30
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
31
|
+
let res;
|
|
32
|
+
try {
|
|
33
|
+
res = await fetch(new URL('/__crtr/source', previewEndpoint), {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${relayToken}` },
|
|
36
|
+
body: JSON.stringify(req),
|
|
37
|
+
signal: controller.signal,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
if (controller.signal.aborted) {
|
|
42
|
+
return { ok: false, stdout: '', stderr: `remote source: timeout after ${timeoutMs}ms` };
|
|
43
|
+
}
|
|
44
|
+
return { ok: false, stdout: '', stderr: 'remote source: network error' };
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
clearTimeout(timer);
|
|
48
|
+
}
|
|
49
|
+
if (!res.ok)
|
|
50
|
+
return { ok: false, stdout: '', stderr: `remote source: HTTP ${res.status} ${res.statusText}` };
|
|
51
|
+
return (await res.json());
|
|
52
|
+
};
|
|
53
|
+
}
|
package/dist/daemon/crtrd.js
CHANGED
|
@@ -64,7 +64,7 @@ import { FAULT_QUIET_MS, readFault, recordFault } from '../core/runtime/fault.js
|
|
|
64
64
|
import { fanDoctrineWake } from '../core/runtime/close.js';
|
|
65
65
|
import { logger } from '../core/log.js';
|
|
66
66
|
export { FAULT_QUIET_MS };
|
|
67
|
-
import { isPidAlive, isRecordedPidAlive } from '../core/canvas/pid.js';
|
|
67
|
+
import { isPidAlive, isRecordedPidAlive, recordedPidLiveness } from '../core/canvas/pid.js';
|
|
68
68
|
import { reconcileBootLiveness } from '../core/canvas/boot.js';
|
|
69
69
|
import { listLivePanes, tearDownNode } from '../core/runtime/placement.js';
|
|
70
70
|
import { reviveNode } from '../core/runtime/revive.js';
|
|
@@ -705,14 +705,24 @@ async function handleNodeLiveness(row, now, revivedThisTick) {
|
|
|
705
705
|
// its turn is over, and the engine never exited — past the grace it kills the
|
|
706
706
|
// engine so the dead-pid refresh branch below revives it fresh.
|
|
707
707
|
//
|
|
708
|
-
// Liveness here is IDENTITY-aware
|
|
708
|
+
// Liveness here is IDENTITY-aware AND requires CONFIRMED alive, not a bare
|
|
709
|
+
// isPidAlive or the fail-open-to-alive boolean adapter: on a heavily-
|
|
709
710
|
// forking host (esp. Linux, where low pids recycle fast) a just-dead
|
|
710
|
-
// broker's pid can be reused by an unrelated process
|
|
711
|
-
//
|
|
712
|
-
// alive"
|
|
713
|
-
//
|
|
714
|
-
// the
|
|
715
|
-
|
|
711
|
+
// broker's pid can be reused by an unrelated process within milliseconds,
|
|
712
|
+
// AND the `ps` probe confirming identity can itself intermittently fail.
|
|
713
|
+
// Either misread as "alive" here doesn't just misreport this one tick — it
|
|
714
|
+
// clears `unhealthySince` (the grace clock), discarding the daemon's memory
|
|
715
|
+
// that the node had already been observed dead, so the VERY NEXT tick treats
|
|
716
|
+
// a dead-for-20s+ node as a brand-new first observation and restarts the
|
|
717
|
+
// whole grace window from scratch (the confirmed Round-2 crash-revive wedge:
|
|
718
|
+
// a single `probe-null` read on a Linux CI runner wedged
|
|
719
|
+
// broker-crash-teardown.test.ts's 'CRASH → grace-revive RESUME'). So only a
|
|
720
|
+
// POSITIVELY CONFIRMED-alive read (`recordedPidLiveness === 'alive'`) may
|
|
721
|
+
// clear the clock; `'dead'` AND `'indeterminate'` both fall through to the
|
|
722
|
+
// ordinary not-alive path below, which now CONTRIBUTES to the grace window
|
|
723
|
+
// instead of zeroing it — robust to a flicker, and still revives promptly
|
|
724
|
+
// once a node is truly gone for the full grace duration.
|
|
725
|
+
if (pid != null && recordedPidLiveness(pid, row.pi_pid_identity) === 'alive') {
|
|
716
726
|
unhealthySince.delete(id);
|
|
717
727
|
clearStrandedRelaunchState(id); // a live engine means the stranding resolved
|
|
718
728
|
handleYieldStall(row, pid, now);
|
package/dist/types.d.ts
CHANGED
|
@@ -138,10 +138,10 @@ export interface ScopeConfig {
|
|
|
138
138
|
* entries at the same scope precedence as the rest of `ScopeConfig`. */
|
|
139
139
|
kinds: Record<string, KindConfig>;
|
|
140
140
|
/** Named remote-canvas targets for `crtr surface attach to <id> --canvas
|
|
141
|
-
* <name
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* `TOP_LEVEL_KEYS`. */
|
|
141
|
+
* <name>`, `crtr node inspect list/show --canvas <name>`, and `crtr canvas
|
|
142
|
+
* browse --canvas <name>` — registered/managed via `crtr canvas config
|
|
143
|
+
* add/list/show/remove` and selected durably via `crtr canvas use`. Unset
|
|
144
|
+
* via `sys config set` — not in `TOP_LEVEL_KEYS`. */
|
|
145
145
|
remoteCanvas: RemoteCanvasConfig;
|
|
146
146
|
/** Extra env-var names/globs a scope explicitly admits across the broker
|
|
147
147
|
* spawn-env boundary (`buildBrokerEnv`, `core/runtime/spawn-env.ts`) — the
|
|
@@ -154,14 +154,17 @@ export interface ScopeConfig {
|
|
|
154
154
|
};
|
|
155
155
|
}
|
|
156
156
|
/** One remote canvas target: where to relay-attach and how to find its
|
|
157
|
-
* bearer token. The token itself is NEVER stored here — only
|
|
158
|
-
*
|
|
159
|
-
*
|
|
157
|
+
* bearer token. The token itself is NEVER stored here — only a ref into the
|
|
158
|
+
* 0600 secrets store, resolved at connect time by `resolveRemoteCanvasTarget`
|
|
159
|
+
* (`src/core/view/remote-canvas-target.ts`) via `getRelayToken`
|
|
160
|
+
* (`src/core/secrets.ts`). */
|
|
160
161
|
export interface RemoteCanvasTarget {
|
|
161
162
|
previewEndpoint: string;
|
|
162
|
-
/** Name
|
|
163
|
-
*
|
|
164
|
-
|
|
163
|
+
/** Name/ref under which the raw token is stored in secrets.json — NEVER the
|
|
164
|
+
* token itself. Resolved via `getRelayToken` (`core/secrets.ts`) at connect
|
|
165
|
+
* time. Defaults to the target name at creation but is a distinct field so
|
|
166
|
+
* a future rename doesn't orphan the secret. */
|
|
167
|
+
relayTokenRef: string;
|
|
165
168
|
cpOrigin?: string;
|
|
166
169
|
}
|
|
167
170
|
export interface RemoteCanvasConfig {
|
|
@@ -176,6 +179,11 @@ export interface ScopeState {
|
|
|
176
179
|
}>;
|
|
177
180
|
last_self_check?: string;
|
|
178
181
|
bootstrap_done?: boolean;
|
|
182
|
+
/** The name of the remote canvas target `crtr canvas use` last selected
|
|
183
|
+
* (see `RemoteCanvasConfig`), or `null`/omitted for local. Read by
|
|
184
|
+
* `resolveCanvasSource` (`core/canvas/source.ts`) as the durable fallback
|
|
185
|
+
* when a command's own `--canvas` flag is absent. */
|
|
186
|
+
activeCanvas?: string | null;
|
|
179
187
|
}
|
|
180
188
|
export interface SubagentFrontmatter {
|
|
181
189
|
name: string;
|
|
@@ -244,7 +252,7 @@ export declare const PROFILE_DIR = "profiles";
|
|
|
244
252
|
export declare const DEFAULT_MAX_PANES_PER_WINDOW = 3;
|
|
245
253
|
export declare function defaultScopeConfig(): ScopeConfig;
|
|
246
254
|
/** No remote canvas targets are configured out of the box — every one is
|
|
247
|
-
*
|
|
255
|
+
* registered via `crtr canvas config add` (see `RemoteCanvasConfig`). */
|
|
248
256
|
export declare function defaultRemoteCanvasConfig(): RemoteCanvasConfig;
|
|
249
257
|
/** The builtin kind registry (spec §1.5): the built-in defaults for every
|
|
250
258
|
* top-level kind and its sub-persona kinds. `whenToUse`/`model` are the
|
package/dist/types.js
CHANGED
|
@@ -36,7 +36,7 @@ export function defaultScopeConfig() {
|
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
/** No remote canvas targets are configured out of the box — every one is
|
|
39
|
-
*
|
|
39
|
+
* registered via `crtr canvas config add` (see `RemoteCanvasConfig`). */
|
|
40
40
|
export function defaultRemoteCanvasConfig() {
|
|
41
41
|
return { targets: {} };
|
|
42
42
|
}
|