team 0.0.1 → 0.1.1
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 +310 -0
- package/dist/approve/approval.d.ts +38 -0
- package/dist/approve/approval.js +78 -0
- package/dist/approve/diff.d.ts +13 -0
- package/dist/approve/diff.js +47 -0
- package/dist/approve/fingerprint.d.ts +44 -0
- package/dist/approve/fingerprint.js +109 -0
- package/dist/args.d.ts +7 -0
- package/dist/args.js +22 -0
- package/dist/budgets/checks.d.ts +35 -0
- package/dist/budgets/checks.js +77 -0
- package/dist/budgets/gate.d.ts +24 -0
- package/dist/budgets/gate.js +127 -0
- package/dist/budgets/readings.d.ts +69 -0
- package/dist/budgets/readings.js +156 -0
- package/dist/budgets/run.d.ts +66 -0
- package/dist/budgets/run.js +119 -0
- package/dist/budgets/table.d.ts +24 -0
- package/dist/budgets/table.js +90 -0
- package/dist/caller.d.ts +48 -0
- package/dist/caller.js +126 -0
- package/dist/check/config.d.ts +51 -0
- package/dist/check/config.js +50 -0
- package/dist/check/git.d.ts +27 -0
- package/dist/check/git.js +97 -0
- package/dist/check/message.d.ts +27 -0
- package/dist/check/message.js +101 -0
- package/dist/check/run.d.ts +31 -0
- package/dist/check/run.js +77 -0
- package/dist/check/signature.d.ts +10 -0
- package/dist/check/signature.js +22 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.js +76 -0
- package/dist/clis.d.ts +7 -0
- package/dist/clis.js +9 -0
- package/dist/commands/add.d.ts +23 -0
- package/dist/commands/add.js +445 -0
- package/dist/commands/approve.d.ts +11 -0
- package/dist/commands/approve.js +146 -0
- package/dist/commands/check.d.ts +17 -0
- package/dist/commands/check.js +103 -0
- package/dist/commands/doctor.d.ts +38 -0
- package/dist/commands/doctor.js +232 -0
- package/dist/commands/down.d.ts +37 -0
- package/dist/commands/down.js +258 -0
- package/dist/commands/init.d.ts +9 -0
- package/dist/commands/init.js +138 -0
- package/dist/commands/remove.d.ts +23 -0
- package/dist/commands/remove.js +235 -0
- package/dist/commands/status.d.ts +41 -0
- package/dist/commands/status.js +147 -0
- package/dist/commands/up.d.ts +49 -0
- package/dist/commands/up.js +362 -0
- package/dist/commands/watch.d.ts +35 -0
- package/dist/commands/watch.js +324 -0
- package/dist/commands/worktree.d.ts +12 -0
- package/dist/commands/worktree.js +388 -0
- package/dist/end/condition.d.ts +28 -0
- package/dist/end/condition.js +77 -0
- package/dist/file/current.d.ts +13 -0
- package/dist/file/current.js +53 -0
- package/dist/file/lines.d.ts +25 -0
- package/dist/file/lines.js +277 -0
- package/dist/file/load.d.ts +7 -0
- package/dist/file/load.js +90 -0
- package/dist/file/paths.d.ts +6 -0
- package/dist/file/paths.js +77 -0
- package/dist/file/secrets.d.ts +6 -0
- package/dist/file/secrets.js +54 -0
- package/dist/file/signature.d.ts +13 -0
- package/dist/file/signature.js +36 -0
- package/dist/file/types.d.ts +130 -0
- package/dist/file/types.js +1 -0
- package/dist/file/validate.d.ts +13 -0
- package/dist/file/validate.js +702 -0
- package/dist/file/write.d.ts +11 -0
- package/dist/file/write.js +13 -0
- package/dist/herdr.d.ts +38 -0
- package/dist/herdr.js +227 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +6 -0
- package/dist/io.d.ts +10 -0
- package/dist/io.js +1 -0
- package/dist/launch/deliver.d.ts +10 -0
- package/dist/launch/deliver.js +39 -0
- package/dist/launch/execute.d.ts +46 -0
- package/dist/launch/execute.js +284 -0
- package/dist/launch/plan.d.ts +185 -0
- package/dist/launch/plan.js +280 -0
- package/dist/launch/rules.d.ts +25 -0
- package/dist/launch/rules.js +36 -0
- package/dist/log.d.ts +1 -0
- package/dist/log.js +24 -0
- package/dist/profiles/antigravity.yaml +66 -0
- package/dist/profiles/claude-code.yaml +69 -0
- package/dist/profiles/codex.yaml +77 -0
- package/dist/profiles/cursor.yaml +68 -0
- package/dist/profiles/index.d.ts +1 -0
- package/dist/profiles/index.js +1 -0
- package/dist/profiles/profile.d.ts +60 -0
- package/dist/profiles/profile.js +313 -0
- package/dist/profiles/quota.d.ts +27 -0
- package/dist/profiles/quota.js +57 -0
- package/dist/state.d.ts +61 -0
- package/dist/state.js +127 -0
- package/dist/status/compare.d.ts +26 -0
- package/dist/status/compare.js +110 -0
- package/dist/status/statusline.d.ts +9 -0
- package/dist/status/statusline.js +26 -0
- package/dist/store/store.d.ts +62 -0
- package/dist/store/store.js +116 -0
- package/dist/watch/check.d.ts +82 -0
- package/dist/watch/check.js +29 -0
- package/dist/watch/checks/approval.d.ts +2 -0
- package/dist/watch/checks/approval.js +16 -0
- package/dist/watch/checks/attention.d.ts +2 -0
- package/dist/watch/checks/attention.js +23 -0
- package/dist/watch/checks/budget.d.ts +2 -0
- package/dist/watch/checks/budget.js +141 -0
- package/dist/watch/checks/disk.d.ts +2 -0
- package/dist/watch/checks/disk.js +13 -0
- package/dist/watch/checks/extra.d.ts +2 -0
- package/dist/watch/checks/extra.js +18 -0
- package/dist/watch/checks/idle.d.ts +2 -0
- package/dist/watch/checks/idle.js +32 -0
- package/dist/watch/checks/load.d.ts +2 -0
- package/dist/watch/checks/load.js +11 -0
- package/dist/watch/checks/memory.d.ts +2 -0
- package/dist/watch/checks/memory.js +11 -0
- package/dist/watch/checks/missing.d.ts +2 -0
- package/dist/watch/checks/missing.js +12 -0
- package/dist/watch/checks/model-drift.d.ts +2 -0
- package/dist/watch/checks/model-drift.js +14 -0
- package/dist/watch/checks/swap-free.d.ts +2 -0
- package/dist/watch/checks/swap-free.js +12 -0
- package/dist/watch/checks/swap-growth.d.ts +2 -0
- package/dist/watch/checks/swap-growth.js +18 -0
- package/dist/watch/checks/team-idle.d.ts +2 -0
- package/dist/watch/checks/team-idle.js +18 -0
- package/dist/watch/checks/unsent.d.ts +2 -0
- package/dist/watch/checks/unsent.js +18 -0
- package/dist/watch/close.d.ts +19 -0
- package/dist/watch/close.js +40 -0
- package/dist/watch/dialect.d.ts +5 -0
- package/dist/watch/dialect.js +430 -0
- package/dist/watch/end.d.ts +11 -0
- package/dist/watch/end.js +17 -0
- package/dist/watch/machine.d.ts +38 -0
- package/dist/watch/machine.js +137 -0
- package/dist/watch/notify.d.ts +1 -0
- package/dist/watch/notify.js +15 -0
- package/dist/watch/pass.d.ts +42 -0
- package/dist/watch/pass.js +242 -0
- package/dist/watch/screen-core.d.ts +20 -0
- package/dist/watch/screen-core.js +356 -0
- package/dist/watch/screen-data.d.ts +65 -0
- package/dist/watch/screen-data.js +1 -0
- package/dist/watch/screen-file.d.ts +2 -0
- package/dist/watch/screen-file.js +269 -0
- package/dist/watch/screen.d.ts +24 -0
- package/dist/watch/screen.js +47 -0
- package/dist/worktree/place.d.ts +18 -0
- package/dist/worktree/place.js +126 -0
- package/dist/yaml.d.ts +30 -0
- package/dist/yaml.js +380 -0
- package/examples/checks/codex-quota +217 -0
- package/examples/team.yaml +76 -0
- package/package.json +29 -10
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Problem } from './types.ts';
|
|
2
|
+
/**
|
|
3
|
+
* The one write of a team file. The text is validated first. An invalid edit is refused and
|
|
4
|
+
* the file is left untouched. A valid one is written by a temporary file and a rename.
|
|
5
|
+
*/
|
|
6
|
+
export declare function writeTeamFile(path: string, text: string): {
|
|
7
|
+
ok: true;
|
|
8
|
+
} | {
|
|
9
|
+
ok: false;
|
|
10
|
+
errors: Problem[];
|
|
11
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { writeAtomic } from "../state.js";
|
|
2
|
+
import { validateTeamFile } from "./validate.js";
|
|
3
|
+
/**
|
|
4
|
+
* The one write of a team file. The text is validated first. An invalid edit is refused and
|
|
5
|
+
* the file is left untouched. A valid one is written by a temporary file and a rename.
|
|
6
|
+
*/
|
|
7
|
+
export function writeTeamFile(path, text) {
|
|
8
|
+
const parsed = validateTeamFile(text);
|
|
9
|
+
if (!parsed.ok)
|
|
10
|
+
return { ok: false, errors: parsed.errors };
|
|
11
|
+
writeAtomic(path, text);
|
|
12
|
+
return { ok: true };
|
|
13
|
+
}
|
package/dist/herdr.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export type HerdrAgent = {
|
|
2
|
+
name: string | null;
|
|
3
|
+
agent: string | null;
|
|
4
|
+
pane: string;
|
|
5
|
+
workspace: string;
|
|
6
|
+
status: string;
|
|
7
|
+
cwd: string | null;
|
|
8
|
+
};
|
|
9
|
+
export declare function agentList(session?: string): HerdrAgent[] | null;
|
|
10
|
+
export declare function paneRootPid(pane: string, session?: string): number | null;
|
|
11
|
+
export declare function paneForeground(pane: string, session?: string): string[] | null;
|
|
12
|
+
export type HerdrWorkspace = {
|
|
13
|
+
id: string;
|
|
14
|
+
label: string;
|
|
15
|
+
};
|
|
16
|
+
export declare function workspaceList(session?: string): HerdrWorkspace[] | null;
|
|
17
|
+
export type SessionState = 'running' | 'stopped' | 'absent';
|
|
18
|
+
export declare function sessionState(name: string): SessionState | null;
|
|
19
|
+
export declare function sessionRunning(name: string): boolean | null;
|
|
20
|
+
export declare function startServer(session: string): boolean;
|
|
21
|
+
export declare function workspaceCreate(cwd: string, label: string, session?: string): {
|
|
22
|
+
pane: string;
|
|
23
|
+
workspace: string;
|
|
24
|
+
} | null;
|
|
25
|
+
export declare function paneRun(pane: string, command: string, session?: string): boolean;
|
|
26
|
+
export declare function agentRename(pane: string, name: string, session?: string): boolean;
|
|
27
|
+
export declare function workspaceClose(workspace: string, session?: string): boolean;
|
|
28
|
+
export declare function sessionStop(name: string): boolean;
|
|
29
|
+
export declare function paneRead(pane: string, lines: number, session?: string): string | null;
|
|
30
|
+
export declare function herdrCommand(session: string | undefined, ...args: string[]): string;
|
|
31
|
+
export declare function typeText(pane: string, text: string, session?: string): boolean;
|
|
32
|
+
export declare function pressEnter(pane: string, session?: string): boolean;
|
|
33
|
+
export declare function agentStatus(pane: string, session?: string): string | null;
|
|
34
|
+
export declare const HERDR_TESTED: {
|
|
35
|
+
from: string;
|
|
36
|
+
to: string;
|
|
37
|
+
};
|
|
38
|
+
export declare function herdrVersion(): string | null;
|
package/dist/herdr.js
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { execFileSync, spawn } from 'node:child_process';
|
|
2
|
+
function run(args, session) {
|
|
3
|
+
const full = session ? ['--session', session, ...args] : args;
|
|
4
|
+
const out = execFileSync('herdr', full, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 10_000 });
|
|
5
|
+
return JSON.parse(out).result;
|
|
6
|
+
}
|
|
7
|
+
// The agents herdr lists, or null when herdr can't be reached.
|
|
8
|
+
export function agentList(session) {
|
|
9
|
+
try {
|
|
10
|
+
const result = run(['agent', 'list'], session);
|
|
11
|
+
return (result.agents ?? []).map((agent) => ({
|
|
12
|
+
name: typeof agent.name === 'string' && agent.name ? agent.name : null,
|
|
13
|
+
agent: typeof agent.agent === 'string' ? agent.agent : null,
|
|
14
|
+
pane: String(agent.pane_id),
|
|
15
|
+
workspace: String(agent.workspace_id),
|
|
16
|
+
status: typeof agent.agent_status === 'string' ? agent.agent_status : 'unknown',
|
|
17
|
+
cwd: typeof agent.cwd === 'string' ? agent.cwd : null,
|
|
18
|
+
}));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
// The pid of a pane's root process, or null. Always by `--pane`: herdr 0.7.1's `--current`
|
|
25
|
+
// answered for another pane than the caller's.
|
|
26
|
+
export function paneRootPid(pane, session) {
|
|
27
|
+
try {
|
|
28
|
+
const result = run(['pane', 'process-info', '--pane', pane], session);
|
|
29
|
+
const pid = result.process_info?.shell_pid;
|
|
30
|
+
return typeof pid === 'number' ? pid : null;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// argv0 of each foreground process, or null when the pane can't be read. After a CLI exits,
|
|
37
|
+
// herdr keeps the pane and the foreground process is the shell.
|
|
38
|
+
export function paneForeground(pane, session) {
|
|
39
|
+
try {
|
|
40
|
+
const result = run(['pane', 'process-info', '--pane', pane], session);
|
|
41
|
+
const list = result.process_info?.foreground_processes;
|
|
42
|
+
if (!Array.isArray(list))
|
|
43
|
+
return null;
|
|
44
|
+
return list.map((proc) => (typeof proc.argv0 === 'string' ? proc.argv0 : ''));
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function workspaceList(session) {
|
|
51
|
+
try {
|
|
52
|
+
const result = run(['workspace', 'list'], session);
|
|
53
|
+
return (result.workspaces ?? []).map((workspace) => ({
|
|
54
|
+
id: String(workspace.workspace_id),
|
|
55
|
+
label: typeof workspace.label === 'string' ? workspace.label : '',
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Whether a session exists and runs. A stopped session stays listed with running false; a deleted
|
|
63
|
+
// one is absent. Null when herdr can't be reached. "default" is herdr's own.
|
|
64
|
+
export function sessionState(name) {
|
|
65
|
+
try {
|
|
66
|
+
const out = execFileSync('herdr', ['session', 'list', '--json'], {
|
|
67
|
+
encoding: 'utf8',
|
|
68
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
69
|
+
timeout: 10_000,
|
|
70
|
+
});
|
|
71
|
+
const sessions = JSON.parse(out).sessions ?? [];
|
|
72
|
+
const found = sessions.find((session) => session.name === name);
|
|
73
|
+
if (!found)
|
|
74
|
+
return 'absent';
|
|
75
|
+
return found.running === true ? 'running' : 'stopped';
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Whether a session exists and runs: null when herdr can't be reached.
|
|
82
|
+
export function sessionRunning(name) {
|
|
83
|
+
const state = sessionState(name);
|
|
84
|
+
return state === null ? null : state === 'running';
|
|
85
|
+
}
|
|
86
|
+
// What a herdr command printed, or null when it did not run. Exit status is not the answer: a
|
|
87
|
+
// command aimed at a session that does not exist can print an error and exit 0.
|
|
88
|
+
function capture(args, session) {
|
|
89
|
+
const full = [...(session ? ['--session', session] : []), ...args];
|
|
90
|
+
try {
|
|
91
|
+
return execFileSync('herdr', full, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: 15_000 });
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function parsed(out) {
|
|
98
|
+
try {
|
|
99
|
+
return JSON.parse(out);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function resultOf(args, session) {
|
|
106
|
+
const out = capture(args, session);
|
|
107
|
+
if (out === null)
|
|
108
|
+
return null;
|
|
109
|
+
const body = parsed(out);
|
|
110
|
+
if (!body || body.type === 'error')
|
|
111
|
+
return null;
|
|
112
|
+
return body.result ?? null;
|
|
113
|
+
}
|
|
114
|
+
const SERVER_ENV = ['HOME', 'USER', 'LOGNAME', 'PATH', 'SHELL', 'TERM', 'LANG'];
|
|
115
|
+
// Starts a session's server in a clean environment. Refuses unless the session is absent: a
|
|
116
|
+
// second server for a session that already runs has been seen to drop it.
|
|
117
|
+
export function startServer(session) {
|
|
118
|
+
if (sessionState(session) !== 'absent')
|
|
119
|
+
return false;
|
|
120
|
+
const env = {};
|
|
121
|
+
for (const name of SERVER_ENV) {
|
|
122
|
+
const value = process.env[name];
|
|
123
|
+
if (value !== undefined)
|
|
124
|
+
env[name] = value;
|
|
125
|
+
}
|
|
126
|
+
if (process.env.SSH_AUTH_SOCK)
|
|
127
|
+
env.SSH_AUTH_SOCK = process.env.SSH_AUTH_SOCK;
|
|
128
|
+
const args = session === 'default' ? ['server'] : ['--session', session, 'server'];
|
|
129
|
+
try {
|
|
130
|
+
const child = spawn('herdr', args, { env, detached: true, stdio: 'ignore' });
|
|
131
|
+
child.unref();
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export function workspaceCreate(cwd, label, session) {
|
|
139
|
+
const result = resultOf(['workspace', 'create', '--cwd', cwd, '--label', label, '--no-focus'], session);
|
|
140
|
+
const pane = result?.root_pane?.pane_id;
|
|
141
|
+
const workspace = result?.workspace?.workspace_id;
|
|
142
|
+
if (typeof pane !== 'string' || typeof workspace !== 'string')
|
|
143
|
+
return null;
|
|
144
|
+
return { pane, workspace };
|
|
145
|
+
}
|
|
146
|
+
// Types a command into a pane and sends it. For a seat's launch, at a shell. An exit into an
|
|
147
|
+
// agent is `typeText` and then `pressEnter`, and only after the screen was read as idle again.
|
|
148
|
+
export function paneRun(pane, command, session) {
|
|
149
|
+
const out = capture(['pane', 'run', pane, command], session);
|
|
150
|
+
if (out === null)
|
|
151
|
+
return false;
|
|
152
|
+
const body = parsed(out);
|
|
153
|
+
return body === null || body.type !== 'error';
|
|
154
|
+
}
|
|
155
|
+
// Not proof an agent is there: herdr answers with agent_info for a pane that has none.
|
|
156
|
+
export function agentRename(pane, name, session) {
|
|
157
|
+
const out = capture(['agent', 'rename', pane, name], session);
|
|
158
|
+
if (out === null)
|
|
159
|
+
return false;
|
|
160
|
+
const body = parsed(out);
|
|
161
|
+
return body === null || body.type !== 'error';
|
|
162
|
+
}
|
|
163
|
+
export function workspaceClose(workspace, session) {
|
|
164
|
+
const out = capture(['workspace', 'close', workspace], session);
|
|
165
|
+
if (out === null)
|
|
166
|
+
return false;
|
|
167
|
+
const body = parsed(out);
|
|
168
|
+
return body !== null && body.type !== 'error';
|
|
169
|
+
}
|
|
170
|
+
// Stops a session. Never deletes one: clearing a stopped session is the owner's command to run.
|
|
171
|
+
export function sessionStop(name) {
|
|
172
|
+
const out = capture(['session', 'stop', name]);
|
|
173
|
+
if (out === null)
|
|
174
|
+
return false;
|
|
175
|
+
const body = parsed(out);
|
|
176
|
+
return body === null || body.type !== 'error';
|
|
177
|
+
}
|
|
178
|
+
// The visible lines of a pane, or null. `session` undefined reaches the caller's own server.
|
|
179
|
+
export function paneRead(pane, lines, session) {
|
|
180
|
+
try {
|
|
181
|
+
const full = [...(session ? ['--session', session] : []), 'pane', 'read', pane, '--source', 'visible', '--lines', String(lines)];
|
|
182
|
+
return execFileSync('herdr', full, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 10_000 });
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// The words to type for a herdr command, for a repair line.
|
|
189
|
+
export function herdrCommand(session, ...args) {
|
|
190
|
+
return ['herdr', ...(session && session !== 'default' ? ['--session', session] : []), ...args].join(' ');
|
|
191
|
+
}
|
|
192
|
+
// The two functions that type. Their callers have just seen the pane at an empty idle prompt,
|
|
193
|
+
// look at it again between the text and the Enter, and type nothing anywhere else.
|
|
194
|
+
export function typeText(pane, text, session) {
|
|
195
|
+
try {
|
|
196
|
+
execFileSync('herdr', [...(session ? ['--session', session] : []), 'pane', 'send-text', pane, text], { stdio: 'ignore', timeout: 10_000 });
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
export function pressEnter(pane, session) {
|
|
204
|
+
try {
|
|
205
|
+
execFileSync('herdr', [...(session ? ['--session', session] : []), 'pane', 'send-keys', pane, 'enter'], { stdio: 'ignore', timeout: 10_000 });
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// The status herdr reports for one pane's agent, or null.
|
|
213
|
+
export function agentStatus(pane, session) {
|
|
214
|
+
return agentList(session)?.find((agent) => agent.pane === pane)?.status ?? null;
|
|
215
|
+
}
|
|
216
|
+
// The herdr versions this version of `team` was run with.
|
|
217
|
+
export const HERDR_TESTED = { from: '0.7.1', to: '0.7.1' };
|
|
218
|
+
// What `herdr --version` prints, without the name, or null when herdr can't be run.
|
|
219
|
+
export function herdrVersion() {
|
|
220
|
+
try {
|
|
221
|
+
const out = execFileSync('herdr', ['--version'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 10_000 });
|
|
222
|
+
return out.trim().replace(/^herdr\s+/, '') || null;
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { parseYaml, toValue, YamlError } from './yaml.ts';
|
|
2
|
+
export type { YamlEntry, YamlNode, YamlScalar, YamlValue } from './yaml.ts';
|
|
3
|
+
export { validateTeamFile } from './file/validate.ts';
|
|
4
|
+
export { findRoot, loadTeamFile, TEAM_FILE } from './file/load.ts';
|
|
5
|
+
export { DEFAULT_COMMIT_TEMPLATE, DEFAULT_FORBIDDEN, DEFAULT_PR_TEMPLATE, renderSignature } from './file/signature.ts';
|
|
6
|
+
export type { LoadResult, Mode, Position, Problem, Seat, TeamFile, ValidateResult } from './file/types.ts';
|
|
7
|
+
export { callerOf, currentCaller, describeCaller, isOwner, mayChangeTeam, placeCaller, readAncestors } from './caller.ts';
|
|
8
|
+
export type { Caller, CallerSources, Process } from './caller.ts';
|
|
9
|
+
export { CLIS, PROFILES } from './clis.ts';
|
|
10
|
+
export type { Command, Io } from './io.ts';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { parseYaml, toValue, YamlError } from "./yaml.js";
|
|
2
|
+
export { validateTeamFile } from "./file/validate.js";
|
|
3
|
+
export { findRoot, loadTeamFile, TEAM_FILE } from "./file/load.js";
|
|
4
|
+
export { DEFAULT_COMMIT_TEMPLATE, DEFAULT_FORBIDDEN, DEFAULT_PR_TEMPLATE, renderSignature } from "./file/signature.js";
|
|
5
|
+
export { callerOf, currentCaller, describeCaller, isOwner, mayChangeTeam, placeCaller, readAncestors } from "./caller.js";
|
|
6
|
+
export { CLIS, PROFILES } from "./clis.js";
|
package/dist/io.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Caller } from './caller.ts';
|
|
2
|
+
export type Io = {
|
|
3
|
+
stdout(text: string): void;
|
|
4
|
+
stderr(text: string): void;
|
|
5
|
+
cwd: string;
|
|
6
|
+
env: Record<string, string | undefined>;
|
|
7
|
+
stdinIsTTY: boolean;
|
|
8
|
+
caller?: Caller;
|
|
9
|
+
};
|
|
10
|
+
export type Command = (argv: string[], io: Io) => Promise<number>;
|
package/dist/io.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface Delivery {
|
|
2
|
+
screen(): string | undefined;
|
|
3
|
+
status(): string | null;
|
|
4
|
+
type(text: string): boolean;
|
|
5
|
+
enter(): boolean;
|
|
6
|
+
now(): number;
|
|
7
|
+
sleep(ms: number): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/** A first message is accepted only after working is observed with the composer empty again. */
|
|
10
|
+
export declare function deliverRules(cli: string, text: string, seconds: number, io: Delivery): Promise<boolean>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { classifyComposer, readScreen } from "../watch/screen.js";
|
|
2
|
+
/** A first message is accepted only after working is observed with the composer empty again. */
|
|
3
|
+
export async function deliverRules(cli, text, seconds, io) {
|
|
4
|
+
const free = () => ['idle', 'done'].includes(io.status() ?? '');
|
|
5
|
+
if (!free() || readScreen(cli, io.screen()).kind !== 'idle' || !io.type(text))
|
|
6
|
+
return false;
|
|
7
|
+
const deadline = io.now() + seconds * 1000;
|
|
8
|
+
// Terminal rendering can lag send-text. Never send Enter until the pasted text is visible.
|
|
9
|
+
for (;;) {
|
|
10
|
+
const kind = readScreen(cli, io.screen()).kind;
|
|
11
|
+
if (!free())
|
|
12
|
+
return false;
|
|
13
|
+
if (kind === 'unsent')
|
|
14
|
+
break;
|
|
15
|
+
if (kind !== 'idle' || io.now() >= deadline)
|
|
16
|
+
return false;
|
|
17
|
+
const before = io.now();
|
|
18
|
+
await io.sleep(100);
|
|
19
|
+
if (io.now() <= before)
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
// Re-read immediately before Enter; a dialog that appeared after the paste gets no key.
|
|
23
|
+
if (!free() || readScreen(cli, io.screen()).kind !== 'unsent' || !io.enter())
|
|
24
|
+
return false;
|
|
25
|
+
for (;;) {
|
|
26
|
+
const status = io.status();
|
|
27
|
+
const screen = io.screen();
|
|
28
|
+
const composer = screen === undefined ? { kind: 'unknown' } : classifyComposer(cli, screen.split('\n'));
|
|
29
|
+
if (status === 'working' && composer.kind === 'idle')
|
|
30
|
+
return true;
|
|
31
|
+
const kind = readScreen(cli, io.screen()).kind;
|
|
32
|
+
if (kind === 'trust' || kind === 'permission' || kind === 'question' || io.now() >= deadline)
|
|
33
|
+
return false;
|
|
34
|
+
const before = io.now();
|
|
35
|
+
await io.sleep(100);
|
|
36
|
+
if (io.now() <= before)
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Step } from './plan.ts';
|
|
2
|
+
export type ScreenKind = 'idle' | 'working' | 'permission' | 'trust' | 'question' | 'unsent' | 'unknown';
|
|
3
|
+
/** What a live `up` or `down` can do, apart from deciding it. Tests stand in for all of it. */
|
|
4
|
+
export type Host = {
|
|
5
|
+
startServer(session: string): boolean;
|
|
6
|
+
sessionUp(session: string): boolean | null;
|
|
7
|
+
/** Makes a folder and the parents it needs. Present on `up` and `add`, whose plans carry a lobby. */
|
|
8
|
+
makeDir?(path: string): boolean;
|
|
9
|
+
createWorkspace(session: string, cwd: string, label: string): {
|
|
10
|
+
pane: string;
|
|
11
|
+
workspace: string;
|
|
12
|
+
} | null;
|
|
13
|
+
paneRun(session: string, pane: string, command: string): boolean;
|
|
14
|
+
typeLine(session: string, pane: string, text: string): boolean;
|
|
15
|
+
deliverRules?(session: string, pane: string, cli: string, text: string, seconds: number): Promise<boolean>;
|
|
16
|
+
renameAgent(session: string, pane: string, name: string): boolean;
|
|
17
|
+
closeWorkspace(session: string, workspace: string): boolean;
|
|
18
|
+
stopSession(session: string): boolean;
|
|
19
|
+
kill(pid: number): boolean;
|
|
20
|
+
/** Pane ids herdr lists an agent in, or null when the list can't be read. */
|
|
21
|
+
agentPanes(session: string): string[] | null;
|
|
22
|
+
classify(session: string, pane: string, cli: string): ScreenKind;
|
|
23
|
+
sleep(ms: number): Promise<void>;
|
|
24
|
+
now(): number;
|
|
25
|
+
/** Null when this seat may launch. A string is the reason it may not. */
|
|
26
|
+
allow(seat: string): string | null;
|
|
27
|
+
record(seat: string, patch: {
|
|
28
|
+
stage: 'launched' | 'named' | 'ready';
|
|
29
|
+
pane?: string;
|
|
30
|
+
workspace?: string;
|
|
31
|
+
rules?: 'option' | 'message';
|
|
32
|
+
}): void;
|
|
33
|
+
/** The seat is really running, so the next seat's ceiling check counts it. */
|
|
34
|
+
running(seat: string): void;
|
|
35
|
+
drop(seat: string): void;
|
|
36
|
+
say(line: string): void;
|
|
37
|
+
log(who: string, what: string): void;
|
|
38
|
+
};
|
|
39
|
+
export type Report = {
|
|
40
|
+
serverFailed: boolean;
|
|
41
|
+
watchFailed: boolean;
|
|
42
|
+
/** A seat or the watch was left behind, so the session must not be stopped. */
|
|
43
|
+
held: boolean;
|
|
44
|
+
};
|
|
45
|
+
/** Runs a plan. Steps carry the operation beside the command the dry run prints. */
|
|
46
|
+
export declare function executePlan(steps: readonly Step[], session: string, host: Host): Promise<Report>;
|