@softov/ahpc 0.1.0
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/LICENSE +21 -0
- package/README.md +296 -0
- package/dist/src/ahp/channels.d.ts +146 -0
- package/dist/src/ahp/channels.js +423 -0
- package/dist/src/ahp/connection.d.ts +656 -0
- package/dist/src/ahp/connection.js +1 -0
- package/dist/src/ahp/fake.d.ts +69 -0
- package/dist/src/ahp/fake.js +1996 -0
- package/dist/src/ahp/live.d.ts +160 -0
- package/dist/src/ahp/live.js +2641 -0
- package/dist/src/ahp/operate.d.ts +36 -0
- package/dist/src/ahp/operate.js +56 -0
- package/dist/src/ahp/publish.d.ts +39 -0
- package/dist/src/ahp/publish.js +370 -0
- package/dist/src/ahp/status.d.ts +23 -0
- package/dist/src/ahp/status.js +23 -0
- package/dist/src/ahp/types.d.ts +745 -0
- package/dist/src/ahp/types.js +27 -0
- package/dist/src/app.d.ts +54 -0
- package/dist/src/app.js +369 -0
- package/dist/src/blocks.d.ts +88 -0
- package/dist/src/blocks.js +102 -0
- package/dist/src/cli/main.d.ts +13 -0
- package/dist/src/cli/main.js +1537 -0
- package/dist/src/cli/render.d.ts +43 -0
- package/dist/src/cli/render.js +91 -0
- package/dist/src/config.d.ts +63 -0
- package/dist/src/config.js +45 -0
- package/dist/src/connect.d.ts +53 -0
- package/dist/src/connect.js +69 -0
- package/dist/src/control.d.ts +189 -0
- package/dist/src/control.js +2241 -0
- package/dist/src/diff.d.ts +44 -0
- package/dist/src/diff.js +111 -0
- package/dist/src/flags.d.ts +28 -0
- package/dist/src/flags.js +76 -0
- package/dist/src/main.d.ts +10 -0
- package/dist/src/main.js +54 -0
- package/dist/src/schedule.d.ts +46 -0
- package/dist/src/schedule.js +140 -0
- package/dist/src/screens.d.ts +79 -0
- package/dist/src/screens.js +1322 -0
- package/dist/src/state.d.ts +355 -0
- package/dist/src/state.js +531 -0
- package/dist/src/terminal.d.ts +44 -0
- package/dist/src/terminal.js +101 -0
- package/dist/src/tui.d.ts +92 -0
- package/dist/src/tui.js +372 -0
- package/dist/src/view/automations.d.ts +31 -0
- package/dist/src/view/automations.js +76 -0
- package/dist/src/view/bood/art.d.ts +90 -0
- package/dist/src/view/bood/art.js +152 -0
- package/dist/src/view/bood/beetle.d.ts +7 -0
- package/dist/src/view/bood/beetle.js +123 -0
- package/dist/src/view/bood/bunny.d.ts +8 -0
- package/dist/src/view/bood/bunny.js +228 -0
- package/dist/src/view/bood/cat.d.ts +9 -0
- package/dist/src/view/bood/cat.js +358 -0
- package/dist/src/view/bood/crab.d.ts +7 -0
- package/dist/src/view/bood/crab.js +121 -0
- package/dist/src/view/bood/index.d.ts +29 -0
- package/dist/src/view/bood/index.js +25 -0
- package/dist/src/view/bood/motion.d.ts +178 -0
- package/dist/src/view/bood/motion.js +298 -0
- package/dist/src/view/bood/owl.d.ts +9 -0
- package/dist/src/view/bood/owl.js +285 -0
- package/dist/src/view/bood/registry.d.ts +36 -0
- package/dist/src/view/bood/registry.js +218 -0
- package/dist/src/view/bood/render.d.ts +31 -0
- package/dist/src/view/bood/render.js +196 -0
- package/dist/src/view/bood/sprite.d.ts +18 -0
- package/dist/src/view/bood/sprite.js +130 -0
- package/dist/src/view/bood/sprout.d.ts +8 -0
- package/dist/src/view/bood/sprout.js +121 -0
- package/dist/src/view/bood/types.d.ts +158 -0
- package/dist/src/view/bood/types.js +57 -0
- package/dist/src/view/bubble.d.ts +75 -0
- package/dist/src/view/bubble.js +86 -0
- package/dist/src/view/changes.d.ts +35 -0
- package/dist/src/view/changes.js +22 -0
- package/dist/src/view/composer.d.ts +64 -0
- package/dist/src/view/composer.js +192 -0
- package/dist/src/view/controls.d.ts +44 -0
- package/dist/src/view/controls.js +49 -0
- package/dist/src/view/creature.d.ts +69 -0
- package/dist/src/view/creature.js +83 -0
- package/dist/src/view/customizations.d.ts +28 -0
- package/dist/src/view/customizations.js +53 -0
- package/dist/src/view/details.d.ts +65 -0
- package/dist/src/view/details.js +65 -0
- package/dist/src/view/filediff.d.ts +29 -0
- package/dist/src/view/filediff.js +24 -0
- package/dist/src/view/files.d.ts +31 -0
- package/dist/src/view/files.js +39 -0
- package/dist/src/view/hitl.d.ts +43 -0
- package/dist/src/view/hitl.js +171 -0
- package/dist/src/view/icons.d.ts +13 -0
- package/dist/src/view/icons.js +71 -0
- package/dist/src/view/picker.d.ts +42 -0
- package/dist/src/view/picker.js +71 -0
- package/dist/src/view/sessionhead.d.ts +41 -0
- package/dist/src/view/sessionhead.js +60 -0
- package/dist/src/view/sessions.d.ts +34 -0
- package/dist/src/view/sessions.js +61 -0
- package/dist/src/view/terminal.d.ts +30 -0
- package/dist/src/view/terminal.js +32 -0
- package/dist/src/view/toolcall.d.ts +27 -0
- package/dist/src/view/toolcall.js +48 -0
- package/dist/src/view/transcript.d.ts +50 -0
- package/dist/src/view/transcript.js +60 -0
- package/package.json +72 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { BoxProps, RenderOutput } from '@textui/core';
|
|
2
|
+
import type { ToolCall } from '../ahp/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* A tool call, as a row.
|
|
5
|
+
*
|
|
6
|
+
* Twenty of these in a turn look identical unless the *command* is on the row,
|
|
7
|
+
* so the input is the row and the display name is a prefix. What it meant to
|
|
8
|
+
* do is markdown, like everything else a host writes for a person; what came
|
|
9
|
+
* back is not, and is shown as it arrived.
|
|
10
|
+
*
|
|
11
|
+
* The status is a glyph and a colour together. A 16-colour session, a piped
|
|
12
|
+
* log and a colourblind reader all lose the colour and keep the glyph.
|
|
13
|
+
*
|
|
14
|
+
* It sits at the turn's own left edge rather than inside the gutter, with its
|
|
15
|
+
* status glyph where the header's bullet is. A tool call is something the
|
|
16
|
+
* agent *did*; indenting it inside the rule filed it under what the agent was
|
|
17
|
+
* saying, which is the one thing it is not.
|
|
18
|
+
*/
|
|
19
|
+
export interface ToolCallRowProps extends BoxProps {
|
|
20
|
+
call: ToolCall;
|
|
21
|
+
expanded?: boolean;
|
|
22
|
+
/** The transcript's cursor is on this row. */
|
|
23
|
+
active?: boolean;
|
|
24
|
+
/** Clicking the row opens it. */
|
|
25
|
+
onToggle?(): void;
|
|
26
|
+
}
|
|
27
|
+
export declare const ToolCallRow: (props: ToolCallRowProps) => RenderOutput;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@textui/core/jsx-runtime";
|
|
2
|
+
import { defineComponent, useTheme } from '@textui/core';
|
|
3
|
+
import { Badge, Column, MarkdownView, Row } from '@textui/widgets';
|
|
4
|
+
const LOOK = {
|
|
5
|
+
pending: { tone: 'muted', glyph: 'bulletHollow' },
|
|
6
|
+
'pending-confirmation': { tone: 'warning', glyph: 'bulletHalf' },
|
|
7
|
+
running: { tone: 'accent', glyph: 'bulletFilled' },
|
|
8
|
+
completed: { tone: 'success', glyph: 'check' },
|
|
9
|
+
failed: { tone: 'danger', glyph: 'cross' },
|
|
10
|
+
cancelled: { tone: 'muted', glyph: 'cross' },
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* The input, as one line.
|
|
14
|
+
*
|
|
15
|
+
* A tool's arguments are often JSON, and JSON arrives with newlines in it. Put
|
|
16
|
+
* straight on the row, a three-line object makes the row three lines tall and
|
|
17
|
+
* every other cell in it vertically centred - so the name floats beside the
|
|
18
|
+
* middle line of a brace-delimited block. The whole thing is on its own lines
|
|
19
|
+
* once the row is opened; this is the part that fits beside a name.
|
|
20
|
+
*/
|
|
21
|
+
function oneLine(text) {
|
|
22
|
+
return text.replace(/\s+/g, ' ').trim();
|
|
23
|
+
}
|
|
24
|
+
export const ToolCallRow = defineComponent('ToolCallRow', (props) => {
|
|
25
|
+
const { call, expanded, active, onToggle, ...rest } = props;
|
|
26
|
+
const theme = useTheme();
|
|
27
|
+
const look = LOOK[call.status] ?? { tone: 'muted', glyph: 'bulletHollow' };
|
|
28
|
+
const glyph = theme.glyphs[look.glyph];
|
|
29
|
+
const chevron = expanded ? theme.glyphs.chevronDown : theme.glyphs.chevronRight;
|
|
30
|
+
const failed = call.status === 'failed' || (call.exitCode !== undefined && call.exitCode !== 0);
|
|
31
|
+
const summary = oneLine(call.input ?? call.intention ?? '');
|
|
32
|
+
// Only when there is something under it. A chevron on a row that opens on
|
|
33
|
+
// to nothing is a promise the row cannot keep.
|
|
34
|
+
const opens = Boolean(call.intention ?? call.input ?? call.output ?? call.outcome
|
|
35
|
+
?? (call.files && call.files.length > 0));
|
|
36
|
+
return (_jsxs(Column, { ...rest, ...(active ? { bg: 'selected' } : {}), children: [_jsxs(Row, { gap: 1, ...(opens && onToggle ? { onClick: onToggle } : {}),
|
|
37
|
+
// The whole row lights up, not the glyph the pointer happens to be
|
|
38
|
+
// over: the row is the thing that opens.
|
|
39
|
+
style: { hover: { bg: 'hover' } }, children: [_jsx("text", { content: glyph, fg: look.tone }), _jsx("text", { content: call.name, bold: true }), _jsx("text", { content: summary, fg: "muted", flex: 1, truncate: "middle" }), call.status === 'pending-confirmation' ? _jsx(Badge, { label: "asks", tone: "warning", icon: theme.glyphs.warning }) : null, failed ? _jsx(Badge, { label: `exit ${call.exitCode ?? 1}`, tone: "danger" }) : null, opens ? _jsx("text", { content: chevron, fg: "subtle" }) : null] }), expanded ? (
|
|
40
|
+
// Indented to the row's own text, which starts one glyph and one gap
|
|
41
|
+
// in - so what opened out of a row lines up under it.
|
|
42
|
+
_jsxs(Column, { padding: [0, 0, 0, 2], gap: 0, children: [call.intention && call.intention !== call.name && call.intention !== call.input
|
|
43
|
+
? _jsx(MarkdownView, { content: call.intention, quiet: true })
|
|
44
|
+
: null, call.input ? (
|
|
45
|
+
// On its own lines, wrapped as written. This is where the JSON
|
|
46
|
+
// goes: whole, and not sharing a row with the name.
|
|
47
|
+
_jsx(Column, { bg: "surfaceAlt", padding: [0, 1], children: call.input.split('\n').map((line, i) => (_jsx("text", { content: line, fg: "text", wrap: "word" }, i))) })) : null, call.output ? (_jsxs(Column, { children: [call.output.split('\n').slice(0, 12).map((line, i) => (_jsx("text", { content: line, fg: "muted", truncate: "end" }, i))), call.output.split('\n').length > 12 ? (_jsx("text", { content: `${theme.glyphs.ellipsis} ${call.output.split('\n').length - 12} more lines`, fg: "subtle" })) : null] })) : null, call.files && call.files.length > 0 ? (_jsx(Column, { children: call.files.map((file) => (_jsx("text", { content: `${theme.glyphs.chevronRight} ${file}`, fg: "info" }, file))) })) : null, call.outcome ? _jsx("text", { content: call.outcome, fg: "subtle" }) : null] })) : null] }));
|
|
48
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { BoxProps, RenderOutput } from '@textui/core';
|
|
2
|
+
import type { Block } from '../blocks.js';
|
|
3
|
+
/**
|
|
4
|
+
* The conversation, as blocks in a feed.
|
|
5
|
+
*
|
|
6
|
+
* There is no scrolling in this file. `Feed` owns the viewport, the cursor and
|
|
7
|
+
* the tail it follows, because none of that is about chat: a transcript, an
|
|
8
|
+
* activity stream and a list of search results with snippets are the same
|
|
9
|
+
* problem, which is "entries that are not one line tall". What is left here is
|
|
10
|
+
* the only part that *is* about chat - which block draws as what.
|
|
11
|
+
*/
|
|
12
|
+
export interface ChatTranscriptProps extends BoxProps {
|
|
13
|
+
blocks: Block[];
|
|
14
|
+
expanded: Record<string, boolean>;
|
|
15
|
+
onToggle(id: string): void;
|
|
16
|
+
/** Which block the cursor is on. Held by the screen, like every other state. */
|
|
17
|
+
cursor?: number;
|
|
18
|
+
onCursor?(index: number): void;
|
|
19
|
+
/**
|
|
20
|
+
* What the find box is looking for.
|
|
21
|
+
*
|
|
22
|
+
* Passed down to be coloured where it appears, not to decide what is drawn:
|
|
23
|
+
* every block stays where it was and the ones holding the term light up, so
|
|
24
|
+
* a reader keeps the conversation around a hit instead of a filtered list
|
|
25
|
+
* of the lines that matched.
|
|
26
|
+
*/
|
|
27
|
+
match?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Keep the cursor in view rather than only when it moves.
|
|
30
|
+
*
|
|
31
|
+
* For the find box, which drives the cursor: its first hit is often the
|
|
32
|
+
* block the cursor is already on, and a feed that only scrolls on a change
|
|
33
|
+
* would leave that one off screen while the box counted it.
|
|
34
|
+
*/
|
|
35
|
+
pinCursor?: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* What this conversation is, as the first thing in it.
|
|
38
|
+
*
|
|
39
|
+
* Inside the scrolling region rather than pinned above it: a caption outside
|
|
40
|
+
* costs a row of the conversation on every screen for ever, so it has to
|
|
41
|
+
* earn each one - which is what forces it down to a line and then down to
|
|
42
|
+
* less than it was for. Here it costs nothing after the first screen.
|
|
43
|
+
*
|
|
44
|
+
* It is not a block. The cursor walks the conversation and there is nothing
|
|
45
|
+
* to do to a caption, so it sits ahead of the indices rather than in them.
|
|
46
|
+
*/
|
|
47
|
+
head?: RenderOutput;
|
|
48
|
+
focusId?: string;
|
|
49
|
+
}
|
|
50
|
+
export declare const ChatTranscript: (props: ChatTranscriptProps) => RenderOutput;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@textui/core/jsx-runtime";
|
|
2
|
+
import { defineComponent, useTheme } from '@textui/core';
|
|
3
|
+
import { Feed, Row } from '@textui/widgets';
|
|
4
|
+
import { ChatBubble, Gutter, ReasoningBlock, StreamingText } from './bubble.js';
|
|
5
|
+
import { ToolCallRow } from './toolcall.js';
|
|
6
|
+
export const ChatTranscript = defineComponent('ChatTranscript', (props) => {
|
|
7
|
+
const { blocks, expanded, onToggle, cursor, onCursor, head, match, pinCursor, focusId = 'chat.transcript', ...rest } = props;
|
|
8
|
+
// The caption is an entry the feed scrolls and the cursor does not visit,
|
|
9
|
+
// so every index the feed reports is one further along than the block it
|
|
10
|
+
// stands for. Converted here, once, rather than at each of the three
|
|
11
|
+
// places that would otherwise each have to remember.
|
|
12
|
+
const lead = head ? 1 : 0;
|
|
13
|
+
return (_jsxs(Feed, { focusId: focusId,
|
|
14
|
+
// Page up from the composer means the conversation above it. There is
|
|
15
|
+
// nothing else on this screen those keys could be for, and taking the
|
|
16
|
+
// keyboard off the field to use them is what a reader is avoiding.
|
|
17
|
+
pageKeys: "always", ...(cursor !== undefined ? { selectedIndex: cursor + lead } : {}), ...(pinCursor ? { pinSelection: true } : {}), ...(onCursor ? { onSelect: (index) => onCursor(Math.max(0, index - lead)) } : {}), onActivate: (index) => {
|
|
18
|
+
const block = blocks[index - lead];
|
|
19
|
+
if (block)
|
|
20
|
+
onToggle(block.id);
|
|
21
|
+
}, ...rest, children: [head ?? null, blocks.map((block) => (_jsx(BlockView, { block: block, expanded: expanded[block.id] ?? false, active: cursor !== undefined && blocks[cursor]?.id === block.id, ...(match ? { match } : {}), onToggle: () => onToggle(block.id) }, block.id)))] }));
|
|
22
|
+
});
|
|
23
|
+
const BlockView = defineComponent('ChatBlockView', ({ block, expanded, active, match, onToggle }) => {
|
|
24
|
+
const theme = useTheme();
|
|
25
|
+
// Spread rather than passed, so a block with no search over it carries no
|
|
26
|
+
// extra prop and its text node is compared unchanged.
|
|
27
|
+
const hit = match ? { match } : {};
|
|
28
|
+
switch (block.kind) {
|
|
29
|
+
case 'said':
|
|
30
|
+
// The blank row is the turn boundary. A uniform gap between every block
|
|
31
|
+
// would space a paragraph from the sentence it belongs to just as much
|
|
32
|
+
// as it spaces one speaker from the next.
|
|
33
|
+
return (_jsx(ChatBubble, { speaker: "user", padding: [1, 0, 0, 0], children: _jsx("text", { content: block.text, wrap: "word", ...hit }) }));
|
|
34
|
+
case 'header':
|
|
35
|
+
return (_jsxs(Row, { gap: 1, padding: [1, 0, 0, 0], children: [_jsx("text", { content: theme.glyphs.bulletFilled, fg: block.state === 'running' ? 'accent' : 'muted' }), _jsx("text", { content: block.model ?? 'agent', bold: true, fg: "accent" }), block.settings ? _jsx("text", { content: block.settings, fg: "subtle" }) : null, _jsx("text", { content: block.meta, fg: "subtle", flex: 1 }), block.state === 'cancelled' ? _jsx("text", { content: "stopped", fg: "warning" }) : null, block.state === 'failed' ? _jsx("text", { content: "failed", fg: "danger" }) : null] }));
|
|
36
|
+
case 'prose':
|
|
37
|
+
return (_jsxs(Row, { gap: 1, children: [_jsx(Gutter, {}), _jsx(StreamingText, { content: block.content, streaming: block.streaming, flex: 1, ...hit })] }));
|
|
38
|
+
case 'reasoning':
|
|
39
|
+
return (_jsxs(Row, { gap: 1, children: [_jsx(Gutter, {}), _jsx(ReasoningBlock, { content: block.content, expanded: expanded, streaming: block.streaming, flex: 1, ...hit, ...(active ? { bg: 'selected' } : {}) })] }));
|
|
40
|
+
case 'notice':
|
|
41
|
+
return (_jsxs(Row, { gap: 1, children: [_jsx("text", { content: theme.glyphs.info, fg: "info" }), _jsx("text", { content: block.content, fg: "muted", wrap: "word", flex: 1, ...hit })] }));
|
|
42
|
+
// Not a notice. A notice is the harness saying something in passing, and
|
|
43
|
+
// this is the turn stopping - so it takes the danger tone and says whether
|
|
44
|
+
// there is anything to carry on from.
|
|
45
|
+
case 'failure':
|
|
46
|
+
return (_jsxs(Row, { gap: 1, children: [_jsx("text", { content: theme.glyphs.cross, fg: "danger" }), _jsx("text", { content: block.content, fg: "danger", wrap: "word", flex: 1, ...hit }), block.resumable ? _jsx("text", { content: "resumable", fg: "subtle" }) : null] }));
|
|
47
|
+
case 'tool':
|
|
48
|
+
// No gutter. A tool call is something the agent *did*, not something it
|
|
49
|
+
// said, so it sits at the turn's own left edge with a status glyph where
|
|
50
|
+
// the header's bullet is - rather than indented inside the rule as
|
|
51
|
+
// though it were a paragraph of the answer.
|
|
52
|
+
return _jsx(ToolCallRow, { call: block.call, expanded: expanded, active: active, onToggle: onToggle });
|
|
53
|
+
case 'queued':
|
|
54
|
+
// Not sent. It reads as a message unless it says so, and "I typed that
|
|
55
|
+
// and nothing happened" is the complaint that follows.
|
|
56
|
+
return (_jsxs(Row, { gap: 1, children: [_jsx("text", { content: theme.glyphs.chevronRight, fg: active ? 'accent' : 'subtle' }), _jsx("text", { content: block.text, fg: "subtle", italic: true, wrap: "word", flex: 1, ...hit }), _jsx("text", { content: active ? 'enter drops it' : 'queued', fg: "warning" })] }));
|
|
57
|
+
default:
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@softov/ahpc",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "A terminal client for the Agent Host Protocol: sessions, a streaming transcript, and interactive prompts",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"ahp",
|
|
8
|
+
"agent-host-protocol",
|
|
9
|
+
"agent-host",
|
|
10
|
+
"agent",
|
|
11
|
+
"ai",
|
|
12
|
+
"protocol",
|
|
13
|
+
"json-rpc",
|
|
14
|
+
"websocket",
|
|
15
|
+
"cli",
|
|
16
|
+
"command-line",
|
|
17
|
+
"tui",
|
|
18
|
+
"terminal",
|
|
19
|
+
"client"
|
|
20
|
+
],
|
|
21
|
+
"author": "Luiz Fernando Softov <lfs@softov.dev>",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"homepage": "https://github.com/softov/ahpc#readme",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/softov/ahpc.git"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/softov/ahpc/issues"
|
|
30
|
+
},
|
|
31
|
+
"bin": {
|
|
32
|
+
"ahpc": "./dist/src/main.js"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist/src",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"bench": "vitest bench --run",
|
|
44
|
+
"build": "tsc -p tsconfig.build.json",
|
|
45
|
+
"build:watch": "tsc -p tsconfig.json --watch",
|
|
46
|
+
"dev": "npm run build && node dist/src/main.js",
|
|
47
|
+
"dev:bun": "bun src/main.tsx",
|
|
48
|
+
"prepack": "tsc -p tsconfig.build.json",
|
|
49
|
+
"schema": "node tools/schema.mjs",
|
|
50
|
+
"start": "node dist/src/main.js",
|
|
51
|
+
"test": "vitest run",
|
|
52
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
53
|
+
"wire": "node tools/validate.mjs"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"@microsoft/agent-host-protocol": "^0.9.0",
|
|
57
|
+
"@textui/core": "^0.5.0",
|
|
58
|
+
"@textui/terminal": "^0.5.0",
|
|
59
|
+
"@textui/widgets": "^0.5.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@textui/testing": "^0.5.0",
|
|
63
|
+
"@types/node": "^22.10.2",
|
|
64
|
+
"ajv": "^8.17.1",
|
|
65
|
+
"ajv-formats": "^3.0.1",
|
|
66
|
+
"typescript": "^5.9.3",
|
|
67
|
+
"vitest": "^3.0.0"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=22"
|
|
71
|
+
}
|
|
72
|
+
}
|