@miadi/chronicle-tui 0.6.0 → 0.8.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/README.md +47 -0
- package/dist/App.d.ts.map +1 -1
- package/dist/App.js +252 -3
- package/dist/App.js.map +1 -1
- package/dist/actions.d.ts +54 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +76 -1
- package/dist/actions.js.map +1 -1
- package/dist/api.d.ts +10 -2
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +19 -10
- package/dist/api.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/inquiries.d.ts +76 -0
- package/dist/inquiries.d.ts.map +1 -0
- package/dist/inquiries.js +77 -0
- package/dist/inquiries.js.map +1 -0
- package/dist/sessions.d.ts +76 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +148 -0
- package/dist/sessions.js.map +1 -0
- package/dist/tide.d.ts +79 -0
- package/dist/tide.d.ts.map +1 -0
- package/dist/tide.js +102 -0
- package/dist/tide.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tmux sessions beside the chronicle (jgwill/Miadi#706, P4).
|
|
3
|
+
*
|
|
4
|
+
* Inside tmux, a session opens in a split of the browser's own window: tmux
|
|
5
|
+
* draws it, a nested client of the same server (`env -u TMUX tmux attach`), so
|
|
6
|
+
* the agent's session stays where it is and goes on when the split closes. Its
|
|
7
|
+
* own keys take the prefix twice; the prefix once moves between the panes.
|
|
8
|
+
* Outside tmux, the browser hands the terminal to `tmux attach` and takes it
|
|
9
|
+
* back when the person detaches.
|
|
10
|
+
*
|
|
11
|
+
* Only the person's keys reach the session. The browser never types into it.
|
|
12
|
+
*/
|
|
13
|
+
import type { EpisodeRow } from "./model.js";
|
|
14
|
+
export interface TmuxSession {
|
|
15
|
+
name: string;
|
|
16
|
+
/** Clients attached to it now. */
|
|
17
|
+
attached: number;
|
|
18
|
+
/** Last activity, seconds since the epoch. */
|
|
19
|
+
activity: number;
|
|
20
|
+
/** What its active pane runs. */
|
|
21
|
+
command: string;
|
|
22
|
+
/** Where its active pane is. */
|
|
23
|
+
path: string;
|
|
24
|
+
paneId: string;
|
|
25
|
+
}
|
|
26
|
+
/** A session name the browser passes to tmux; anything else is listed but not opened. */
|
|
27
|
+
export declare const SESSION_NAME: RegExp;
|
|
28
|
+
/** The environment a tmux command gets: no token and no secret, whatever the browser holds. */
|
|
29
|
+
export declare function tmuxEnv(env?: NodeJS.ProcessEnv, withheld?: readonly string[]): NodeJS.ProcessEnv;
|
|
30
|
+
/** The browser runs inside tmux, in a pane it can split. */
|
|
31
|
+
export declare function insideTmux(env?: NodeJS.ProcessEnv): boolean;
|
|
32
|
+
/** One row per session, from its active pane: `tmux list-panes -a` with FORMAT. */
|
|
33
|
+
export declare function parseSessions(stdout: string): TmuxSession[];
|
|
34
|
+
/** The sessions on this machine's tmux server, most recently active first. None when no server runs. */
|
|
35
|
+
export declare function listSessions(env?: NodeJS.ProcessEnv): Promise<TmuxSession[]>;
|
|
36
|
+
/** The session the browser itself runs in, so it is never opened inside itself. */
|
|
37
|
+
export declare function ownSession(env?: NodeJS.ProcessEnv): Promise<string | undefined>;
|
|
38
|
+
/**
|
|
39
|
+
* The episode a session works in: the vessel its active pane is in, else the
|
|
40
|
+
* number its name carries (`episode-339-mia-companion`, `ep140-bosf`) when
|
|
41
|
+
* exactly one visible episode has it.
|
|
42
|
+
*/
|
|
43
|
+
export declare function sessionEpisode(session: Pick<TmuxSession, "name" | "path">, rows: readonly EpisodeRow[], root: string): string | undefined;
|
|
44
|
+
/**
|
|
45
|
+
* Open a session in a split beside `pane` (the browser's own, by default),
|
|
46
|
+
* leaving the focus where it is. Returns the split's pane id.
|
|
47
|
+
*/
|
|
48
|
+
export declare function openBeside(name: string, options?: {
|
|
49
|
+
pane?: string;
|
|
50
|
+
size?: string;
|
|
51
|
+
env?: NodeJS.ProcessEnv;
|
|
52
|
+
}): Promise<string>;
|
|
53
|
+
/** The split is still there. */
|
|
54
|
+
export declare function paneAlive(paneId: string, env?: NodeJS.ProcessEnv): Promise<boolean>;
|
|
55
|
+
export declare function focusPane(paneId: string, env?: NodeJS.ProcessEnv): Promise<void>;
|
|
56
|
+
/** Close the split: the nested client goes, the session it showed goes on. */
|
|
57
|
+
export declare function closePane(paneId: string, env?: NodeJS.ProcessEnv): Promise<void>;
|
|
58
|
+
/** Outside tmux: attach in this terminal until the person detaches. */
|
|
59
|
+
export declare function attachHere(name: string, env?: NodeJS.ProcessEnv): {
|
|
60
|
+
ok: boolean;
|
|
61
|
+
message?: string;
|
|
62
|
+
};
|
|
63
|
+
/** "12s", "4m", "3h", "2d" since the last activity. */
|
|
64
|
+
export declare function idleFor(activity: number, now?: number): string;
|
|
65
|
+
export interface SessionRow {
|
|
66
|
+
session: TmuxSession;
|
|
67
|
+
/** The episode it works in, when one is known. */
|
|
68
|
+
episode?: string;
|
|
69
|
+
/** It works in the episode in focus. */
|
|
70
|
+
here: boolean;
|
|
71
|
+
/** It is the browser's own session. */
|
|
72
|
+
own: boolean;
|
|
73
|
+
}
|
|
74
|
+
/** The sessions as the overlay lists them: the episode in focus's first, then the rest, each most recent first. */
|
|
75
|
+
export declare function orderSessions(sessions: readonly TmuxSession[], rows: readonly EpisodeRow[], root: string, focus?: string, own?: string): SessionRow[];
|
|
76
|
+
//# sourceMappingURL=sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
|
package/dist/sessions.js
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tmux sessions beside the chronicle (jgwill/Miadi#706, P4).
|
|
3
|
+
*
|
|
4
|
+
* Inside tmux, a session opens in a split of the browser's own window: tmux
|
|
5
|
+
* draws it, a nested client of the same server (`env -u TMUX tmux attach`), so
|
|
6
|
+
* the agent's session stays where it is and goes on when the split closes. Its
|
|
7
|
+
* own keys take the prefix twice; the prefix once moves between the panes.
|
|
8
|
+
* Outside tmux, the browser hands the terminal to `tmux attach` and takes it
|
|
9
|
+
* back when the person detaches.
|
|
10
|
+
*
|
|
11
|
+
* Only the person's keys reach the session. The browser never types into it.
|
|
12
|
+
*/
|
|
13
|
+
import { execFile, spawnSync } from "node:child_process";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
/** A session name the browser passes to tmux; anything else is listed but not opened. */
|
|
16
|
+
export const SESSION_NAME = /^[A-Za-z0-9._:@+-]{1,200}$/;
|
|
17
|
+
/** The environment a tmux command gets: no token and no secret, whatever the browser holds. */
|
|
18
|
+
export function tmuxEnv(env = process.env, withheld = []) {
|
|
19
|
+
const out = {};
|
|
20
|
+
for (const [key, value] of Object.entries(env)) {
|
|
21
|
+
if (/TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|API_KEY|PRIVATE_KEY/i.test(key) || withheld.includes(key))
|
|
22
|
+
continue;
|
|
23
|
+
out[key] = value;
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
/** The browser runs inside tmux, in a pane it can split. */
|
|
28
|
+
export function insideTmux(env = process.env) {
|
|
29
|
+
return Boolean(env.TMUX && env.TMUX_PANE);
|
|
30
|
+
}
|
|
31
|
+
function tmux(args, env) {
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
execFile("tmux", args, { env, timeout: 10_000, maxBuffer: 4 * 1024 * 1024 }, (error, stdout, stderr) => {
|
|
34
|
+
if (error)
|
|
35
|
+
reject(new Error(String(stderr || error.message).trim()));
|
|
36
|
+
else
|
|
37
|
+
resolve(stdout);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
const FORMAT = ["#{session_name}", "#{session_attached}", "#{session_activity}", "#{pane_current_command}", "#{pane_current_path}", "#{pane_id}", "#{window_active}", "#{pane_active}"].join("\t");
|
|
42
|
+
/** One row per session, from its active pane: `tmux list-panes -a` with FORMAT. */
|
|
43
|
+
export function parseSessions(stdout) {
|
|
44
|
+
const sessions = new Map();
|
|
45
|
+
for (const line of stdout.split("\n")) {
|
|
46
|
+
const [name, attached, activity, command, where, paneId, windowActive, paneActive] = line.split("\t");
|
|
47
|
+
if (!name || windowActive !== "1" || paneActive !== "1" || sessions.has(name))
|
|
48
|
+
continue;
|
|
49
|
+
sessions.set(name, { name, attached: Number(attached) || 0, activity: Number(activity) || 0, command: command ?? "", path: where ?? "", paneId: paneId ?? "" });
|
|
50
|
+
}
|
|
51
|
+
return [...sessions.values()];
|
|
52
|
+
}
|
|
53
|
+
/** The sessions on this machine's tmux server, most recently active first. None when no server runs. */
|
|
54
|
+
export async function listSessions(env = tmuxEnv()) {
|
|
55
|
+
try {
|
|
56
|
+
return parseSessions(await tmux(["list-panes", "-a", "-F", FORMAT], env)).sort((a, b) => b.activity - a.activity);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** The session the browser itself runs in, so it is never opened inside itself. */
|
|
63
|
+
export async function ownSession(env = process.env) {
|
|
64
|
+
if (!insideTmux(env))
|
|
65
|
+
return undefined;
|
|
66
|
+
try {
|
|
67
|
+
return (await tmux(["display-message", "-p", "-t", env.TMUX_PANE, "#{session_name}"], tmuxEnv(env))).trim() || undefined;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The episode a session works in: the vessel its active pane is in, else the
|
|
75
|
+
* number its name carries (`episode-339-mia-companion`, `ep140-bosf`) when
|
|
76
|
+
* exactly one visible episode has it.
|
|
77
|
+
*/
|
|
78
|
+
export function sessionEpisode(session, rows, root) {
|
|
79
|
+
const relative = path.relative(root, session.path);
|
|
80
|
+
if (relative && !relative.startsWith("..") && !path.isAbsolute(relative)) {
|
|
81
|
+
const folder = relative.split(path.sep)[0];
|
|
82
|
+
if (rows.some((row) => row.name === folder))
|
|
83
|
+
return folder;
|
|
84
|
+
}
|
|
85
|
+
const number = /^ep(?:isode)?[-_]?0*(\d+)(?:\D|$)/i.exec(session.name)?.[1];
|
|
86
|
+
if (!number)
|
|
87
|
+
return undefined;
|
|
88
|
+
const matches = rows.filter((row) => row.number === Number(number));
|
|
89
|
+
return matches.length === 1 ? matches[0].name : undefined;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Open a session in a split beside `pane` (the browser's own, by default),
|
|
93
|
+
* leaving the focus where it is. Returns the split's pane id.
|
|
94
|
+
*/
|
|
95
|
+
export async function openBeside(name, options = {}) {
|
|
96
|
+
if (!SESSION_NAME.test(name))
|
|
97
|
+
throw new Error(`The browser opens sessions named with letters, digits and ._:@+- only: ${JSON.stringify(name)}`);
|
|
98
|
+
const env = options.env ?? tmuxEnv();
|
|
99
|
+
const pane = options.pane ?? process.env.TMUX_PANE;
|
|
100
|
+
if (!pane)
|
|
101
|
+
throw new Error("Not inside tmux: there is no pane to split.");
|
|
102
|
+
// More than one argument after the options: tmux runs them as they are, with no shell between.
|
|
103
|
+
const out = await tmux(["split-window", "-h", "-d", "-l", options.size ?? "60%", "-t", pane, "-P", "-F", "#{pane_id}", "env", "-u", "TMUX", "tmux", "attach-session", "-t", `=${name}`], env);
|
|
104
|
+
return out.trim();
|
|
105
|
+
}
|
|
106
|
+
/** The split is still there. */
|
|
107
|
+
export async function paneAlive(paneId, env = tmuxEnv()) {
|
|
108
|
+
try {
|
|
109
|
+
return (await tmux(["display-message", "-p", "-t", paneId, "#{pane_id}"], env)).trim() === paneId;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
export async function focusPane(paneId, env = tmuxEnv()) {
|
|
116
|
+
await tmux(["select-pane", "-t", paneId], env);
|
|
117
|
+
}
|
|
118
|
+
/** Close the split: the nested client goes, the session it showed goes on. */
|
|
119
|
+
export async function closePane(paneId, env = tmuxEnv()) {
|
|
120
|
+
await tmux(["kill-pane", "-t", paneId], env).catch(() => { });
|
|
121
|
+
}
|
|
122
|
+
/** Outside tmux: attach in this terminal until the person detaches. */
|
|
123
|
+
export function attachHere(name, env = tmuxEnv()) {
|
|
124
|
+
if (!SESSION_NAME.test(name))
|
|
125
|
+
return { ok: false, message: `Not a session name the browser opens: ${JSON.stringify(name)}` };
|
|
126
|
+
const result = spawnSync("tmux", ["attach-session", "-t", `=${name}`], { stdio: "inherit", env });
|
|
127
|
+
return result.status === 0 ? { ok: true } : { ok: false, message: result.error?.message ?? `tmux attach ended with ${result.status}` };
|
|
128
|
+
}
|
|
129
|
+
/** "12s", "4m", "3h", "2d" since the last activity. */
|
|
130
|
+
export function idleFor(activity, now = Date.now() / 1000) {
|
|
131
|
+
const seconds = Math.max(0, Math.round(now - activity));
|
|
132
|
+
if (seconds < 60)
|
|
133
|
+
return `${seconds}s`;
|
|
134
|
+
if (seconds < 3600)
|
|
135
|
+
return `${Math.floor(seconds / 60)}m`;
|
|
136
|
+
if (seconds < 86_400)
|
|
137
|
+
return `${Math.floor(seconds / 3600)}h`;
|
|
138
|
+
return `${Math.floor(seconds / 86_400)}d`;
|
|
139
|
+
}
|
|
140
|
+
/** The sessions as the overlay lists them: the episode in focus's first, then the rest, each most recent first. */
|
|
141
|
+
export function orderSessions(sessions, rows, root, focus, own) {
|
|
142
|
+
const listed = sessions.map((session) => {
|
|
143
|
+
const episode = sessionEpisode(session, rows, root);
|
|
144
|
+
return { session, episode, here: Boolean(focus && episode === focus), own: session.name === own };
|
|
145
|
+
});
|
|
146
|
+
return [...listed.filter((row) => row.here), ...listed.filter((row) => !row.here)];
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
|
package/dist/tide.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tide cockpit from the terminal, starting with an episode's companion
|
|
3
|
+
* seat (jgwill/Miadi#714, in P4 of jgwill/Miadi#706).
|
|
4
|
+
*
|
|
5
|
+
* `POST /api/tide/companion` runs `tide companion open --format json`. Without
|
|
6
|
+
* `run` it answers the plan; with `run` it carries the plan out and answers the
|
|
7
|
+
* steps taken. The browser does what the cockpit's "Open Mia for this episode"
|
|
8
|
+
* does (`components/tide/companion-opener.tsx`): it always shows the plan
|
|
9
|
+
* first, and offers to run only the plan on screen, only when that plan would
|
|
10
|
+
* act, and only once the person confirms it. Archiving the existing session is
|
|
11
|
+
* the person's word, so it is a choice they make, and choosing it asks again.
|
|
12
|
+
*
|
|
13
|
+
* Opening a seat starts an agent with permissions bypassed in a tmux session,
|
|
14
|
+
* on the app's host. The browser never types into that pane: the command does,
|
|
15
|
+
* and it waits until the seat is idle.
|
|
16
|
+
*/
|
|
17
|
+
import { type MiadiApi } from "./api.js";
|
|
18
|
+
export interface CompanionPlan {
|
|
19
|
+
state?: string;
|
|
20
|
+
session?: string;
|
|
21
|
+
launch?: string;
|
|
22
|
+
commands?: string[];
|
|
23
|
+
starter_prompt?: string;
|
|
24
|
+
archive_name?: string | null;
|
|
25
|
+
episode?: {
|
|
26
|
+
number?: number;
|
|
27
|
+
title?: string;
|
|
28
|
+
folder?: string;
|
|
29
|
+
};
|
|
30
|
+
seat?: {
|
|
31
|
+
key?: string;
|
|
32
|
+
};
|
|
33
|
+
agent?: {
|
|
34
|
+
state?: string;
|
|
35
|
+
status?: string;
|
|
36
|
+
} | null;
|
|
37
|
+
listener?: {
|
|
38
|
+
state?: string;
|
|
39
|
+
};
|
|
40
|
+
run?: {
|
|
41
|
+
steps?: Array<{
|
|
42
|
+
at: string;
|
|
43
|
+
step: string;
|
|
44
|
+
}>;
|
|
45
|
+
warnings?: string[];
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export interface CompanionAnswer {
|
|
49
|
+
/** tide's exit: 0 done (or nothing to do), 3 done with warnings, 4 refused, 2 not in controller mode. */
|
|
50
|
+
exitCode?: number;
|
|
51
|
+
plan?: CompanionPlan | null;
|
|
52
|
+
message?: string;
|
|
53
|
+
error?: string;
|
|
54
|
+
/** The HTTP status, when it was not 200. */
|
|
55
|
+
status?: number;
|
|
56
|
+
}
|
|
57
|
+
/** The plans that do nothing, as the cockpit lists them: nothing is offered to run. */
|
|
58
|
+
export declare const NOTHING_TO_RUN: readonly string[];
|
|
59
|
+
/** A plan that would act: a state that is not a refusal or a no-op, and commands to carry out. */
|
|
60
|
+
export declare function planActs(answer: CompanionAnswer | undefined): boolean;
|
|
61
|
+
/** The plan for an episode's companion seat. Acts on nothing. */
|
|
62
|
+
export declare function companionPlan(api: MiadiApi, episode: string, options?: {
|
|
63
|
+
archive?: boolean;
|
|
64
|
+
seat?: string;
|
|
65
|
+
}): Promise<CompanionAnswer>;
|
|
66
|
+
/** Carry the plan out. Opening a seat waits for its prompt and its listener: up to seven minutes. */
|
|
67
|
+
export declare function runCompanion(api: MiadiApi, episode: string, options?: {
|
|
68
|
+
archive?: boolean;
|
|
69
|
+
seat?: string;
|
|
70
|
+
}): Promise<CompanionAnswer>;
|
|
71
|
+
export interface PlanLine {
|
|
72
|
+
text: string;
|
|
73
|
+
tone?: "state" | "meta" | "warn" | "error" | "step";
|
|
74
|
+
}
|
|
75
|
+
/** What the person reads before deciding: the session, the state, what would be done, or what was done. */
|
|
76
|
+
export declare function companionLines(answer: CompanionAnswer): PlanLine[];
|
|
77
|
+
/** One line for the status bar once the plan ran. */
|
|
78
|
+
export declare function ranMessage(answer: CompanionAnswer): string;
|
|
79
|
+
//# sourceMappingURL=tide.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tide.d.ts","sourceRoot":"","sources":["../src/tide.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAA;AAGlD,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9D,IAAI,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACvB,KAAK,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAClD,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7B,GAAG,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,KAAK,CAAC;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;CAC3E;AAED,MAAM,WAAW,eAAe;IAC9B,yGAAyG;IACzG,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,aAAa,GAAG,IAAI,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,uFAAuF;AACvF,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAK3C,CAAA;AAED,kGAAkG;AAClG,wBAAgB,QAAQ,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,GAAG,OAAO,CAKrE;AAYD,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAE1I;AAED,qGAAqG;AACrG,wBAAgB,YAAY,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,eAAe,CAAC,CAEzI;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAA;CACpD;AAED,2GAA2G;AAC3G,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,QAAQ,EAAE,CAuBlE;AAED,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAO1D"}
|
package/dist/tide.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tide cockpit from the terminal, starting with an episode's companion
|
|
3
|
+
* seat (jgwill/Miadi#714, in P4 of jgwill/Miadi#706).
|
|
4
|
+
*
|
|
5
|
+
* `POST /api/tide/companion` runs `tide companion open --format json`. Without
|
|
6
|
+
* `run` it answers the plan; with `run` it carries the plan out and answers the
|
|
7
|
+
* steps taken. The browser does what the cockpit's "Open Mia for this episode"
|
|
8
|
+
* does (`components/tide/companion-opener.tsx`): it always shows the plan
|
|
9
|
+
* first, and offers to run only the plan on screen, only when that plan would
|
|
10
|
+
* act, and only once the person confirms it. Archiving the existing session is
|
|
11
|
+
* the person's word, so it is a choice they make, and choosing it asks again.
|
|
12
|
+
*
|
|
13
|
+
* Opening a seat starts an agent with permissions bypassed in a tmux session,
|
|
14
|
+
* on the app's host. The browser never types into that pane: the command does,
|
|
15
|
+
* and it waits until the seat is idle.
|
|
16
|
+
*/
|
|
17
|
+
import { ApiError } from "./api.js";
|
|
18
|
+
import { clean, cleanLine } from "./text.js";
|
|
19
|
+
/** The plans that do nothing, as the cockpit lists them: nothing is offered to run. */
|
|
20
|
+
export const NOTHING_TO_RUN = [
|
|
21
|
+
"re-arm: already listening",
|
|
22
|
+
"re-arm: another seat listens on this episode",
|
|
23
|
+
"re-arm: pane runs something else",
|
|
24
|
+
"archive refused: the seat still listens",
|
|
25
|
+
];
|
|
26
|
+
/** A plan that would act: a state that is not a refusal or a no-op, and commands to carry out. */
|
|
27
|
+
export function planActs(answer) {
|
|
28
|
+
const state = answer?.plan?.state;
|
|
29
|
+
if (!state || answer?.plan?.run)
|
|
30
|
+
return false;
|
|
31
|
+
if (NOTHING_TO_RUN.includes(state) || state.startsWith("archive refused"))
|
|
32
|
+
return false;
|
|
33
|
+
return (answer.plan?.commands?.length ?? 0) > 0;
|
|
34
|
+
}
|
|
35
|
+
async function companion(api, body, timeoutMs) {
|
|
36
|
+
try {
|
|
37
|
+
return await api.post("/api/tide/companion", body, { timeoutMs });
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
// A refusal (409), tide's controller gate (423) and a bad episode (400) still answer in JSON.
|
|
41
|
+
if (error instanceof ApiError && error.body && typeof error.body === "object")
|
|
42
|
+
return { ...error.body, status: error.status };
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** The plan for an episode's companion seat. Acts on nothing. */
|
|
47
|
+
export function companionPlan(api, episode, options = {}) {
|
|
48
|
+
return companion(api, { episode, archive: options.archive === true, ...(options.seat ? { seat: options.seat } : {}) }, 60_000);
|
|
49
|
+
}
|
|
50
|
+
/** Carry the plan out. Opening a seat waits for its prompt and its listener: up to seven minutes. */
|
|
51
|
+
export function runCompanion(api, episode, options = {}) {
|
|
52
|
+
return companion(api, { episode, archive: options.archive === true, run: true, ...(options.seat ? { seat: options.seat } : {}) }, 450_000);
|
|
53
|
+
}
|
|
54
|
+
/** What the person reads before deciding: the session, the state, what would be done, or what was done. */
|
|
55
|
+
export function companionLines(answer) {
|
|
56
|
+
const lines = [];
|
|
57
|
+
if (answer.error)
|
|
58
|
+
lines.push({ text: cleanLine(answer.error), tone: "error" });
|
|
59
|
+
if (answer.message)
|
|
60
|
+
lines.push({ text: cleanLine(answer.message), tone: "error" });
|
|
61
|
+
const plan = answer.plan;
|
|
62
|
+
if (!plan)
|
|
63
|
+
return lines;
|
|
64
|
+
if (plan.session)
|
|
65
|
+
lines.push({ text: `session: ${cleanLine(plan.session)}` });
|
|
66
|
+
if (plan.state) {
|
|
67
|
+
const listener = plan.listener?.state ? ` · listener: ${cleanLine(plan.listener.state)}` : "";
|
|
68
|
+
// A listening seat reports its agent as `shell`, not idle (#714, F1).
|
|
69
|
+
const agent = plan.agent?.status ? ` · agent: ${cleanLine(plan.agent.status)}` : "";
|
|
70
|
+
lines.push({ text: `state: ${cleanLine(plan.state)}${listener}${agent}`, tone: "state" });
|
|
71
|
+
}
|
|
72
|
+
if (plan.archive_name)
|
|
73
|
+
lines.push({ text: `archives the running session as ${cleanLine(plan.archive_name)}`, tone: "warn" });
|
|
74
|
+
if (plan.run) {
|
|
75
|
+
for (const step of plan.run.steps ?? [])
|
|
76
|
+
lines.push({ text: `${cleanLine(step.at).slice(11, 19)} ${cleanLine(step.step)}`, tone: "step" });
|
|
77
|
+
for (const warning of plan.run.warnings ?? [])
|
|
78
|
+
lines.push({ text: `warning: ${cleanLine(warning)}`, tone: "warn" });
|
|
79
|
+
if (plan.session)
|
|
80
|
+
lines.push({ text: `tmux attach -t ${cleanLine(plan.session)}`, tone: "meta" });
|
|
81
|
+
return lines;
|
|
82
|
+
}
|
|
83
|
+
;
|
|
84
|
+
(plan.commands ?? []).forEach((command, index) => lines.push({ text: `${index + 1}. ${cleanLine(command)}` }));
|
|
85
|
+
if (planActs(answer) && plan.starter_prompt)
|
|
86
|
+
lines.push({ text: `starter prompt: ${clean(plan.starter_prompt).replace(/\s+/g, " ").trim()}`, tone: "meta" });
|
|
87
|
+
return lines;
|
|
88
|
+
}
|
|
89
|
+
/** One line for the status bar once the plan ran. */
|
|
90
|
+
export function ranMessage(answer) {
|
|
91
|
+
const session = answer.plan?.session ? cleanLine(answer.plan.session) : "the seat";
|
|
92
|
+
if (answer.exitCode === 0)
|
|
93
|
+
return `Opened ${session}: tmux attach -t ${session}`;
|
|
94
|
+
if (answer.exitCode === 3)
|
|
95
|
+
return `Opened ${session}, with warnings.`;
|
|
96
|
+
if (answer.exitCode === 4)
|
|
97
|
+
return `tide refused: ${cleanLine(answer.plan?.state ?? answer.message ?? "see the plan")}`;
|
|
98
|
+
if (answer.exitCode === 2)
|
|
99
|
+
return "tide is not in controller mode on the app's host.";
|
|
100
|
+
return cleanLine(answer.error ?? answer.message ?? `tide answered ${answer.exitCode ?? answer.status ?? "nothing"}`);
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=tide.js.map
|
package/dist/tide.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tide.js","sourceRoot":"","sources":["../src/tide.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@miadi/chronicle-tui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Browse the Miadi Chronicle from a terminal and take part in its talking circles: list and filter every episode, search their text, read a vessel's files with their references as chips, open the room, the wheel or the vessel as clickable links or a QR code, answer attention, relate episodes, and speak, witness, keep the diary and close a ceremony as yourself. Every operation is an action in one registry, so writes join the same list later.",
|
|
3
|
+
"version": "0.8.0",
|
|
4
|
+
"description": "Browse the Miadi Chronicle from a terminal and take part in its talking circles: list and filter every episode, search their text, read a vessel's files with their references as chips, open the room, the wheel or the vessel as clickable links or a QR code, answer attention, relate episodes, attach inquiries, open an episode's companion seat, open a tmux session beside the chronicle, and speak, witness, keep the diary and close a ceremony as yourself. Every operation is an action in one registry, so writes join the same list later.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|