@north-light/crouter 0.3.195 → 0.3.197
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/clients/attach/render/assistant-message.d.ts +1 -1
- package/dist/clients/attach/render/assistant-message.js +3 -2
- package/dist/clients/attach/render/chat-view.d.ts +7 -17
- package/dist/clients/attach/render/chat-view.js +42 -57
- package/dist/clients/attach/render/diagram.d.ts +0 -10
- package/dist/clients/attach/render/diagram.js +0 -32
- package/dist/clients/attach/render/html-markdown.d.ts +0 -4
- package/dist/clients/attach/render/html-markdown.js +0 -22
- package/dist/clients/attach/render/markdown-source.d.ts +5 -0
- package/dist/clients/attach/render/markdown-source.js +9 -7
- package/dist/clients/attach/viewer.js +393 -393
- package/dist/commands/human/prompts.js +9 -15
- 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/package.json +1 -1
- package/runtime.lock.json +2 -2
|
@@ -10,7 +10,6 @@ import { extname, join } from 'node:path';
|
|
|
10
10
|
import { randomBytes } from 'node:crypto';
|
|
11
11
|
import { rmSync } from 'node:fs';
|
|
12
12
|
import { readConfig } from '../../core/config.js';
|
|
13
|
-
import { tmuxServerReachable } from '../../core/spawn.js';
|
|
14
13
|
import { markStopSignal } from '../../core/runtime/stop-signals.js';
|
|
15
14
|
import { atomicWriteJson } from '../../core/human/convention.js';
|
|
16
15
|
import { ticketDir } from '../../core/human/root.js';
|
|
@@ -313,22 +312,17 @@ export const humanShow = defineLeaf({
|
|
|
313
312
|
const path = input['path'];
|
|
314
313
|
const windowArg = input['window'];
|
|
315
314
|
const window = windowArg !== undefined ? windowArg : 'auto';
|
|
316
|
-
// `human show`
|
|
317
|
-
//
|
|
318
|
-
let paneId;
|
|
315
|
+
// `human show` always reports display failures in its result rather than
|
|
316
|
+
// turning a passive screen surface into a failed agent turn.
|
|
319
317
|
try {
|
|
320
|
-
const
|
|
321
|
-
paneId
|
|
318
|
+
const result = display(path, { window, maxPanes: resolveMaxPanes() });
|
|
319
|
+
return result.paneId !== undefined
|
|
320
|
+
? { pane_id: result.paneId, reason: null }
|
|
321
|
+
: { pane_id: null, reason: result.reason };
|
|
322
322
|
}
|
|
323
|
-
catch {
|
|
324
|
-
|
|
323
|
+
catch (error) {
|
|
324
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
325
|
+
return { pane_id: null, reason: `display failed: ${reason}` };
|
|
325
326
|
}
|
|
326
|
-
if (paneId !== undefined) {
|
|
327
|
-
return { pane_id: paneId, reason: null };
|
|
328
|
-
}
|
|
329
|
-
const reason = tmuxServerReachable()
|
|
330
|
-
? 'renderer unavailable (termrender/uv missing)'
|
|
331
|
-
: 'not in tmux';
|
|
332
|
-
return { pane_id: null, reason };
|
|
333
327
|
},
|
|
334
328
|
});
|
|
@@ -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
|
}
|
package/package.json
CHANGED
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/crouter",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.197",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/crouter",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.197",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|