@openagt/servers 0.0.0-stage → 0.0.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.
@@ -0,0 +1,94 @@
1
+ import { type Exec, type Session } from './ssh.js';
2
+ import { type LoginKind, type Server } from './store.js';
3
+ /**
4
+ * The two logins a server works with, and how each gets there: over SSH, as the install's own
5
+ * scripts do, never through the browser and never through the server's door.
6
+ *
7
+ * GitHub's is this computer's own, read from `gh` and copied. Claude's is made new for the
8
+ * server: `claude setup-token` asks the person in this computer's browser and shows a login
9
+ * that lasts a year; this computer's own Claude login is never read. Neither is written
10
+ * anywhere on this computer, and what a server said is kept without them.
11
+ *
12
+ * Taking a login off the server deletes it there. It does not end the login: GitHub's is still
13
+ * this computer's own, and Claude's is good until its year is over.
14
+ */
15
+ export type Failed = {
16
+ ok: false;
17
+ error: string;
18
+ };
19
+ /** The GitHub account `gh` is logged in to on this computer: whose login a copy would be. */
20
+ export declare function ownGithubAccount(run?: Exec): Promise<{
21
+ ok: true;
22
+ account: string;
23
+ } | Failed>;
24
+ /** `text` without the login it may repeat. */
25
+ export declare function scrubbed(text: string, login: string): string;
26
+ export type GithubGiven = {
27
+ ok: true;
28
+ account: string;
29
+ } | (Failed & {
30
+ /** The server was sent the login. Its script leaves no login behind when it fails; a line that broke half-way leaves that unknown. */
31
+ reached: boolean;
32
+ });
33
+ /** Copy this computer's GitHub login to the server on a line that is already open. */
34
+ export declare function giveGithub(session: Session, run?: Exec): Promise<GithubGiven>;
35
+ /** Write down how a copy of the GitHub login went. */
36
+ export declare function noteGithub(kept: Server, given: GithubGiven): void;
37
+ /** The login among what Claude Code showed, once all of it is there. */
38
+ export declare function mintedIn(shown: string): string | undefined;
39
+ /** Why Claude Code ended without a login, from what it showed: its last lines, never a part of a login. */
40
+ export declare function notMinted(shown: string): string;
41
+ export interface MintOptions {
42
+ signal?: AbortSignal;
43
+ /** How long the person has to say yes in the browser. */
44
+ timeoutMs?: number;
45
+ /** The command that makes the login; Claude Code's own when unsaid. */
46
+ command?: string;
47
+ }
48
+ /** How a Claude login is made for a server. */
49
+ export type Mint = (opts?: MintOptions) => Promise<{
50
+ ok: true;
51
+ login: string;
52
+ } | Failed>;
53
+ /**
54
+ * Make a Claude login that lasts a year, with Claude Code's own `claude setup-token`: it opens
55
+ * this computer's browser, the person says yes there, and it shows the login once. It keeps
56
+ * nothing, and neither does this.
57
+ *
58
+ * Claude Code draws on a terminal's screen and this process has none, so the system's `script`
59
+ * lends it one, wide enough that the login stays on one line. Its input is a pipe nothing ever
60
+ * writes to. `script` gives its program a group of its own, so that program first says which
61
+ * process it is: at the end both groups are ended, whether or not Claude Code heeds a terminal
62
+ * that went away.
63
+ */
64
+ export declare const mintClaude: Mint;
65
+ /** One change to one login of a server. */
66
+ export interface Job {
67
+ kind: LoginKind;
68
+ /** Take the login off the server, where otherwise the server is given one. */
69
+ remove: boolean;
70
+ }
71
+ export interface LoginOptions extends Job {
72
+ env: NodeJS.ProcessEnv;
73
+ address: string;
74
+ exec?: Exec;
75
+ mint?: Mint;
76
+ /** Stops waiting for the person. What was already on its way to the server arrives all the same. */
77
+ signal?: AbortSignal;
78
+ /** How often the file is told the work still goes on. */
79
+ beatMs?: number;
80
+ }
81
+ /** Refused: the login is already being changed, in another process. */
82
+ export declare class AlreadyAtWork extends Error {
83
+ constructor(address: string, kind: LoginKind);
84
+ }
85
+ /**
86
+ * Change one login of a server: give it one, or take it off. Run as a process of its own: a
87
+ * person asked in the browser takes a while, a server may too, and a command a page runs has
88
+ * seconds. How it ended is written to the file, for the page.
89
+ *
90
+ * Once the server is being told, it is told to the end: a line cut half-way would leave on the
91
+ * server what this computer does not know of.
92
+ */
93
+ export declare function changeLogin(opts: LoginOptions): Promise<void>;
94
+ //# sourceMappingURL=logins.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logins.d.ts","sourceRoot":"","sources":["../src/logins.ts"],"names":[],"mappings":"AACA,OAAO,EAAuD,KAAK,IAAI,EAAa,KAAK,OAAO,EAAE,MAAM,UAAU,CAAA;AAElH,OAAO,EAAwD,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,CAAA;AAE9G;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,MAAM,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAUjD,6FAA6F;AAC7F,wBAAsB,gBAAgB,CAAC,GAAG,GAAE,IAAc,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAC,CAI3G;AAUD,8CAA8C;AAC9C,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5D;AAWD,MAAM,MAAM,WAAW,GACnB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC7B,CAAC,MAAM,GAAG;IACR,sIAAsI;IACtI,OAAO,EAAE,OAAO,CAAA;CACjB,CAAC,CAAA;AAEN,sFAAsF;AACtF,wBAAsB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,GAAE,IAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAO5F;AAKD,sDAAsD;AACtD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CAIjE;AAUD,wEAAwE;AACxE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE1D;AAED,2GAA2G;AAC3G,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,yDAAyD;IACzD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,+CAA+C;AAC/C,MAAM,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAAC,CAAA;AAQxF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,EAAE,IAyCrB,CAAA;AAEJ,2CAA2C;AAC3C,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,SAAS,CAAA;IACf,8EAA8E;IAC9E,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,MAAM,WAAW,YAAa,SAAQ,GAAG;IACvC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAA;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,oGAAoG;IACpG,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAID,uEAAuE;AACvE,qBAAa,aAAc,SAAQ,KAAK;IACtC,YAAY,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAE3C;CACF;AAcD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAmCnE"}
package/dist/logins.js ADDED
@@ -0,0 +1,205 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { exec as ownExec, lastLines, openSession, withAdvice } from './ssh.js';
3
+ import { LOGIN_TIMEOUT_MS, claudeLoginScript, claudeLogoutScript, githubLoginScript, githubLogoutScript } from './steps.js';
4
+ import { BEAT_MS, atWork, changeServer, isAccount } from './store.js';
5
+ /** How long one of this computer's own commands gets to answer. */
6
+ const OWN_TIMEOUT_MS = 15_000;
7
+ /** Why this computer's `gh` gave nothing, in words: never its own, which could hold what it was asked for. */
8
+ function noGh(said, otherwise) {
9
+ return { ok: false, error: `There is no GitHub login to copy from this computer. ${/could not start/.test(said.err) ? '`gh` is not on this computer.' : otherwise}` };
10
+ }
11
+ /** The GitHub account `gh` is logged in to on this computer: whose login a copy would be. */
12
+ export async function ownGithubAccount(run = ownExec) {
13
+ const said = await run('gh', ['api', '--hostname', 'github.com', 'user', '--jq', '.login'], '', { timeoutMs: OWN_TIMEOUT_MS });
14
+ const account = said.out.trim();
15
+ return said.code === 0 && isAccount(account) ? { ok: true, account } : noGh(said, '`gh` is not logged in to github.com here, or GitHub does not answer: see `gh auth status`.');
16
+ }
17
+ /** This computer's GitHub login, as `gh` keeps it. */
18
+ async function ownGithubLogin(run) {
19
+ const said = await run('gh', ['auth', 'token', '--hostname', 'github.com'], '', { timeoutMs: OWN_TIMEOUT_MS });
20
+ const login = said.out.trim();
21
+ // What it reads as is what GitHub hands out; anything else is not sent anywhere.
22
+ return said.code === 0 && /^[A-Za-z0-9_]{20,255}$/.test(login) ? { ok: true, login } : noGh(said, '`gh` is not logged in to github.com here: run `gh auth login`.');
23
+ }
24
+ /** `text` without the login it may repeat. */
25
+ export function scrubbed(text, login) {
26
+ return login ? text.replaceAll(login, '•••') : text;
27
+ }
28
+ /** Run a login's script on the server. What comes back never holds the login. */
29
+ async function send(session, { as, script }, login = '') {
30
+ const said = await session.run(as, script, LOGIN_TIMEOUT_MS);
31
+ const clean = { out: scrubbed(said.out, login), err: scrubbed(said.err, login) };
32
+ return said.code === 0 ? { ok: true, out: clean.out } : { ok: false, error: lastLines(clean) || 'The server did not answer.' };
33
+ }
34
+ /** Copy this computer's GitHub login to the server on a line that is already open. */
35
+ export async function giveGithub(session, run = ownExec) {
36
+ const own = await ownGithubLogin(run);
37
+ if (!own.ok)
38
+ return { ...own, reached: false };
39
+ const sent = await send(session, githubLoginScript(own.login), own.login);
40
+ if (!sent.ok)
41
+ return { ...sent, reached: true };
42
+ const account = sent.out.trim().split('\n').at(-1)?.trim();
43
+ return isAccount(account) ? { ok: true, account } : { ok: false, error: 'The server took the login and did not say whose it is.', reached: true };
44
+ }
45
+ /** What is known of a login beyond a try: since when it is there, and whose it is. */
46
+ const known = (login) => ({ ...(login?.since ? { since: login.since } : {}), ...(login?.account ? { account: login.account } : {}) });
47
+ /** Write down how a copy of the GitHub login went. */
48
+ export function noteGithub(kept, given) {
49
+ if (given.ok)
50
+ kept.github = { since: new Date().toISOString(), account: given.account };
51
+ // One that was sent and did not arrive replaced what the server had: this computer no longer counts on a login there.
52
+ else
53
+ kept.github = { ...(given.reached ? {} : known(kept.github)), failed: given.error };
54
+ }
55
+ /** What Claude Code writes before and after the login it made, and what such a login reads as. */
56
+ const MINTED = /Your OAuth token[^\n]*:\s*(sk-ant-[A-Za-z0-9_-]{40,400})\s+Store this token securely/;
57
+ /** What a terminal is told beside words: colours, where the cursor goes, a link's address. */
58
+ const TERMINAL = /\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|\x1b[@-Z\\-_]/g;
59
+ /** The words Claude Code showed, without what only a terminal reads. */
60
+ const words = (shown) => shown.replace(TERMINAL, '').replaceAll('\r', '\n');
61
+ /** The login among what Claude Code showed, once all of it is there. */
62
+ export function mintedIn(shown) {
63
+ return MINTED.exec(words(shown))?.[1];
64
+ }
65
+ /** Why Claude Code ended without a login, from what it showed: its last lines, never a part of a login. */
66
+ export function notMinted(shown) {
67
+ const text = words(shown);
68
+ if (/Your OAuth token/.test(text))
69
+ return 'Claude Code made a login, and this OpenAgent could not read it off what Claude Code showed.';
70
+ const lines = [...new Set(text.split('\n').map(line => line.trim()).filter(line => line && !STARTED_AS.test(line)))];
71
+ return ['Claude Code ended without a login.', ...lines.slice(-5)].join('\n');
72
+ }
73
+ const MINT_TIMEOUT_MS = 10 * 60_000;
74
+ /** How much of what Claude Code showed is kept: its end, where the login is. */
75
+ const SHOWN = 256 * 1024;
76
+ /** The line the lent terminal's first program writes of itself, before it becomes Claude Code. */
77
+ const STARTED_AS = /^started as (\d+)$/m;
78
+ /**
79
+ * Make a Claude login that lasts a year, with Claude Code's own `claude setup-token`: it opens
80
+ * this computer's browser, the person says yes there, and it shows the login once. It keeps
81
+ * nothing, and neither does this.
82
+ *
83
+ * Claude Code draws on a terminal's screen and this process has none, so the system's `script`
84
+ * lends it one, wide enough that the login stays on one line. Its input is a pipe nothing ever
85
+ * writes to. `script` gives its program a group of its own, so that program first says which
86
+ * process it is: at the end both groups are ended, whether or not Claude Code heeds a terminal
87
+ * that went away.
88
+ */
89
+ export const mintClaude = ({ signal, timeoutMs = MINT_TIMEOUT_MS, command = 'claude setup-token' } = {}) => new Promise(resolve => {
90
+ const inner = `echo "started as $$"; stty cols 250 rows 50; command -v ${command.split(' ')[0]} >/dev/null || { echo "Claude Code is not on this computer."; exit 1; }; exec ${command}`;
91
+ const lent = process.platform === 'darwin' ? 'script -q /dev/null sh -c "$0"' : 'script -q -e -c "$0" /dev/null';
92
+ // On Linux `script` starts its program with the person's own shell, and the line above is written for sh.
93
+ const child = spawn('sh', ['-c', `tail -f /dev/null | { ${lent}; kill 0; }`, inner], { stdio: ['ignore', 'pipe', 'pipe'], detached: true, env: { ...process.env, SHELL: '/bin/sh' } });
94
+ let shown = '';
95
+ let lentTo;
96
+ let ended = false;
97
+ const end = (answer) => {
98
+ if (ended)
99
+ return;
100
+ ended = true;
101
+ clearTimeout(timer);
102
+ signal?.removeEventListener('abort', stopped);
103
+ for (const group of [lentTo, child.pid]) {
104
+ if (group === undefined || group <= 1)
105
+ continue;
106
+ try {
107
+ process.kill(-group, 'SIGKILL');
108
+ }
109
+ catch {
110
+ // Gone already.
111
+ }
112
+ }
113
+ child.stdout.destroy();
114
+ child.stderr.destroy();
115
+ resolve(answer);
116
+ };
117
+ const stopped = () => end({ ok: false, error: 'The login was stopped.' });
118
+ const timer = setTimeout(() => end({ ok: false, error: `No yes came from the browser in ${Math.round(timeoutMs / 60_000)} minutes.` }), timeoutMs);
119
+ signal?.addEventListener('abort', stopped, { once: true });
120
+ if (signal?.aborted)
121
+ stopped();
122
+ const read = (said) => {
123
+ shown = (shown + said).slice(-SHOWN);
124
+ // Read once, as it comes: it is the first thing written, and a long wait scrolls it out of what is kept.
125
+ lentTo ??= Number(STARTED_AS.exec(words(shown))?.[1]) || undefined;
126
+ const login = mintedIn(shown);
127
+ if (login)
128
+ end({ ok: true, login });
129
+ };
130
+ child.stdout.setEncoding('utf8').on('data', read);
131
+ child.stderr.setEncoding('utf8').on('data', read);
132
+ child.once('error', error => end({ ok: false, error: `Claude Code could not be started: ${error.message}` }));
133
+ child.once('close', () => end({ ok: false, error: notMinted(shown) }));
134
+ });
135
+ const NAMES = { github: 'GitHub', claude: 'Claude' };
136
+ /** Refused: the login is already being changed, in another process. */
137
+ export class AlreadyAtWork extends Error {
138
+ constructor(address, kind) {
139
+ super(`The ${NAMES[kind]} login of ${address} is being changed right now`);
140
+ }
141
+ }
142
+ /** Open a line to the server, meet it, and do `work` on it; a server that cannot be met is told with what to do about it. */
143
+ async function onServer(server, run, work) {
144
+ const session = await openSession(server, run ? { exec: run } : {});
145
+ try {
146
+ const met = await session.meet();
147
+ if (!met.ok)
148
+ return { ok: false, error: withAdvice(server, met.said || 'The server did not answer.'), reached: false };
149
+ return await work(session);
150
+ }
151
+ finally {
152
+ await session.close();
153
+ }
154
+ }
155
+ /**
156
+ * Change one login of a server: give it one, or take it off. Run as a process of its own: a
157
+ * person asked in the browser takes a while, a server may too, and a command a page runs has
158
+ * seconds. How it ended is written to the file, for the page.
159
+ *
160
+ * Once the server is being told, it is told to the end: a line cut half-way would leave on the
161
+ * server what this computer does not know of.
162
+ */
163
+ export async function changeLogin(opts) {
164
+ const { env, address, kind, remove } = opts;
165
+ const mine = (kept) => kept[kind]?.worker?.pid === process.pid;
166
+ const beat = () => ({ pid: process.pid, seen: new Date().toISOString() });
167
+ // This process's from here on; the command that started it held it under its own name until now.
168
+ const server = await changeServer(env, address, kept => {
169
+ const worker = kept[kind]?.worker;
170
+ if (atWork(worker) && worker.pid !== process.pid && worker.pid !== process.ppid)
171
+ throw new AlreadyAtWork(address, kind);
172
+ kept[kind] = { ...known(kept[kind]), worker: beat() };
173
+ return { address: kept.address, user: kept.user };
174
+ });
175
+ if (!server)
176
+ throw new Error(`no server at ${address}`);
177
+ const beating = setInterval(() => void changeServer(env, address, kept => void (mine(kept) && (kept[kind].worker = beat()))).catch(() => { }), opts.beatMs ?? BEAT_MS);
178
+ const failed = (error) => (kept) => void (kept[kind] = { ...known(kept[kind]), failed: error });
179
+ let after = failed('The work stopped before it ended.');
180
+ try {
181
+ if (remove) {
182
+ const gone = await onServer(server, opts.exec, session => send(session, kind === 'github' ? githubLogoutScript() : claudeLogoutScript()));
183
+ after = gone.ok ? kept => void delete kept[kind] : failed(gone.error);
184
+ }
185
+ else if (kind === 'github') {
186
+ const given = await onServer(server, opts.exec, session => giveGithub(session, opts.exec));
187
+ after = kept => noteGithub(kept, given);
188
+ }
189
+ else {
190
+ await changeServer(env, address, kept => void (mine(kept) && (kept.claude.asking = true)));
191
+ const minted = await (opts.mint ?? mintClaude)(opts.signal ? { signal: opts.signal } : {});
192
+ await changeServer(env, address, kept => void (mine(kept) && delete kept.claude.asking));
193
+ if (!minted.ok)
194
+ return void (after = failed(minted.error));
195
+ const given = await onServer(server, opts.exec, session => send(session, claudeLoginScript(minted.login), minted.login));
196
+ after = given.ok ? kept => void (kept.claude = { since: new Date().toISOString() }) : failed(given.error);
197
+ }
198
+ }
199
+ finally {
200
+ clearInterval(beating);
201
+ // One that was started over is another process's by now: nothing of it is this one's to write.
202
+ await changeServer(env, address, kept => void (mine(kept) && after(kept))).catch(() => { });
203
+ }
204
+ }
205
+ //# sourceMappingURL=logins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logins.js","sourceRoot":"","sources":["../src/logins.ts"],"names":[],"mappings":"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"}
package/dist/ssh.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ import { SERVER_USER } from './steps.js';
2
+ /**
3
+ * A script run on the server, over SSH, with this computer's own `ssh`: its keys, its agent and
4
+ * its settings are the person's, and OpenAgent keeps none of its own. Nothing is ever asked: a
5
+ * key that needs a password typed, or a server that wants one, is a failure told in words.
6
+ *
7
+ * The first time this computer meets a server its key is taken as it is and remembered, as when
8
+ * a person answers "yes" in a terminal. A server that later shows another key is refused by
9
+ * `ssh` itself, before anything is sent.
10
+ */
11
+ /** How a command ended, and what it said. */
12
+ export interface Said {
13
+ /** The exit code; `null` for a command that was stopped, or could not start. */
14
+ code: number | null;
15
+ out: string;
16
+ err: string;
17
+ }
18
+ /** How one command is run and fed a text: the real one when unsaid. */
19
+ export type Exec = (bin: string, args: readonly string[], stdin: string, opts: {
20
+ timeoutMs: number;
21
+ signal?: AbortSignal;
22
+ }) => Promise<Said>;
23
+ export declare const exec: Exec;
24
+ /** The end of what a command said, its errors last: where the reason of a failure is. */
25
+ export declare function lastLines(said: Pick<Said, 'out' | 'err'>): string;
26
+ /**
27
+ * What to do about a failure to log in to the server, which `ssh` and `sudo` word their own way,
28
+ * after those words. Only for the first thing asked of a server: later a server's own words
29
+ * could read like one of these.
30
+ */
31
+ export declare function withAdvice(server: {
32
+ address: string;
33
+ user: string;
34
+ }, said: string): string;
35
+ export interface Session {
36
+ /**
37
+ * Meet the server: log in, and answer what its key is known by. `undefined` when `ssh` did not
38
+ * say, which changes nothing: the key is the one `ssh` remembers either way.
39
+ */
40
+ meet(): Promise<{
41
+ ok: true;
42
+ fingerprint?: string;
43
+ } | {
44
+ ok: false;
45
+ said: string;
46
+ }>;
47
+ /** Run `script` on the server as root or as the server's own user. */
48
+ run(as: 'root' | typeof SERVER_USER, script: string, timeoutMs: number): Promise<Said>;
49
+ /** Hang up. */
50
+ close(): Promise<void>;
51
+ }
52
+ /**
53
+ * A line to one server, kept open between the scripts, so the keys are asked for once (a key
54
+ * that wants a touch or a confirmation each time it is used would otherwise ask at every step).
55
+ */
56
+ export declare function openSession(server: {
57
+ address: string;
58
+ user: string;
59
+ }, opts?: {
60
+ exec?: Exec;
61
+ signal?: AbortSignal;
62
+ }): Promise<Session>;
63
+ //# sourceMappingURL=ssh.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh.d.ts","sourceRoot":"","sources":["../src/ssh.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAExC;;;;;;;;GAQG;AAEH,6CAA6C;AAC7C,MAAM,WAAW,IAAI;IACnB,gFAAgF;IAChF,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,uEAAuE;AACvE,MAAM,MAAM,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAK5I,eAAO,MAAM,IAAI,EAAE,IAoCf,CAAA;AAKJ,yFAAyF;AACzF,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,KAAK,CAAC,GAAG,MAAM,CAGjE;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAS1F;AAED,MAAM,WAAW,OAAO;IACtB;;;OAGG;IACH,IAAI,IAAI,OAAO,CAAC;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACjF,sEAAsE;IACtE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtF,eAAe;IACf,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAKD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,IAAI,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAqC/I"}
package/dist/ssh.js ADDED
@@ -0,0 +1,113 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { mkdtemp, rm } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { SERVER_USER } from './steps.js';
6
+ /** How much of what a command says is kept: its end, where a failure tells its reason. */
7
+ const KEPT = 64 * 1024;
8
+ export const exec = (bin, args, stdin, { timeoutMs, signal }) => new Promise(resolve => {
9
+ const child = spawn(bin, [...args], { stdio: ['pipe', 'pipe', 'pipe'] });
10
+ let out = '';
11
+ let err = '';
12
+ let stopped;
13
+ const stop = (why) => {
14
+ stopped ??= why;
15
+ child.kill('SIGKILL');
16
+ };
17
+ const timer = setTimeout(() => stop(`No answer for ${Math.round(timeoutMs / 60_000)} minutes.`), timeoutMs);
18
+ const aborted = () => stop('The install was stopped.');
19
+ signal?.addEventListener('abort', aborted, { once: true });
20
+ if (signal?.aborted)
21
+ aborted();
22
+ child.stdout.setEncoding('utf8').on('data', (said) => (out = (out + said).slice(-KEPT)));
23
+ child.stderr.setEncoding('utf8').on('data', (said) => (err = (err + said).slice(-KEPT)));
24
+ let ended = false;
25
+ const done = (code, more = '') => {
26
+ if (ended)
27
+ return;
28
+ ended = true;
29
+ clearTimeout(timer);
30
+ signal?.removeEventListener('abort', aborted);
31
+ // Whatever still holds the command's output open is not listened to any longer.
32
+ child.stdout.destroy();
33
+ child.stderr.destroy();
34
+ resolve({ code: stopped === undefined ? code : null, out, err: [err.trimEnd(), stopped, more].filter(Boolean).join('\n') });
35
+ };
36
+ child.once('error', error => done(null, `${bin} could not start: ${error.message}`));
37
+ child.once('close', code => done(code));
38
+ // `ssh` leaves a process behind that keeps the line open for the next script, and that one
39
+ // may hold this command's output open long after the command ended: its end is the exit,
40
+ // a moment later so what it said last has arrived.
41
+ child.once('exit', code => void setTimeout(() => done(code), 250));
42
+ // A command that ended before it read its text is told by its exit, not by a broken pipe here.
43
+ child.stdin.on('error', () => { });
44
+ child.stdin.end(stdin);
45
+ });
46
+ /** How many of a command's last lines are kept for the page. */
47
+ const SAID_LINES = 15;
48
+ /** The end of what a command said, its errors last: where the reason of a failure is. */
49
+ export function lastLines(said) {
50
+ const lines = [said.out, said.err].flatMap(text => text.split('\n').map(line => line.trimEnd()).filter(Boolean));
51
+ return lines.slice(-SAID_LINES).join('\n');
52
+ }
53
+ /**
54
+ * What to do about a failure to log in to the server, which `ssh` and `sudo` word their own way,
55
+ * after those words. Only for the first thing asked of a server: later a server's own words
56
+ * could read like one of these.
57
+ */
58
+ export function withAdvice(server, said) {
59
+ const advice = /REMOTE HOST IDENTIFICATION HAS CHANGED|Host key verification failed/.test(said)
60
+ ? 'This server shows another key than the one this computer remembers for it. Nothing was sent to it.'
61
+ : /Permission denied/.test(said)
62
+ ? `OpenAgent logs in with this computer's own SSH keys. Check that "ssh ${server.user}@${server.address}" works in a terminal without asking for anything.`
63
+ : /sudo: a password is required|sudo: a terminal is required|sudo: command not found|sudo: not found/.test(said)
64
+ ? `The user ${server.user} cannot become root without a password. Use root, or a user whose sudo asks for none.`
65
+ : undefined;
66
+ return [said, advice].filter(Boolean).join('\n');
67
+ }
68
+ /** What `ssh -v` prints about itself, which is nothing the server said. */
69
+ const NOISE = /^(debug\d:|OpenSSH_|Authenticated to |Transferred: |Bytes per second: )/;
70
+ /**
71
+ * A line to one server, kept open between the scripts, so the keys are asked for once (a key
72
+ * that wants a touch or a confirmation each time it is used would otherwise ask at every step).
73
+ */
74
+ export async function openSession(server, opts = {}) {
75
+ const run = opts.exec ?? exec;
76
+ // The line's own socket, in a folder only this user reads; a short path, which a socket needs.
77
+ const dir = await mkdtemp(join(tmpdir(), 'oas-'));
78
+ const options = [
79
+ ['BatchMode', 'yes'],
80
+ ['StrictHostKeyChecking', 'accept-new'],
81
+ ['ConnectTimeout', '20'],
82
+ ['ServerAliveInterval', '15'],
83
+ ['ServerAliveCountMax', '4'],
84
+ ['ControlMaster', 'auto'],
85
+ ['ControlPath', join(dir, 's')],
86
+ ['ControlPersist', '120'],
87
+ // Whatever this computer's own settings say for every server: a server met a moment ago gets no way back into this computer.
88
+ ['ForwardAgent', 'no'],
89
+ ['ForwardX11', 'no'],
90
+ ['ClearAllForwardings', 'yes'],
91
+ ['PermitLocalCommand', 'no'],
92
+ ['RequestTTY', 'no'],
93
+ ].flatMap(([name, value]) => ['-o', `${name}=${value}`]);
94
+ // `--` ends the options: an address is never read as one.
95
+ const args = (remote, more = []) => [...options, ...more, '-l', server.user, '--', server.address, remote];
96
+ const root = server.user === 'root' ? '' : 'sudo -n ';
97
+ const signal = opts.signal ? { signal: opts.signal } : {};
98
+ return {
99
+ async meet() {
100
+ const said = await run('ssh', args('true', ['-v']), '', { timeoutMs: 60_000, ...signal });
101
+ if (said.code !== 0)
102
+ return { ok: false, said: said.err.split('\n').filter(line => !NOISE.test(line)).join('\n').trim() };
103
+ const fingerprint = /^debug1: Server host key: \S+ (SHA256:\S+)/m.exec(said.err)?.[1];
104
+ return { ok: true, ...(fingerprint ? { fingerprint } : {}) };
105
+ },
106
+ run: (as, script, timeoutMs) => run('ssh', args(as === 'root' ? `${root}bash -s` : `${root}runuser -l ${SERVER_USER} -c 'bash -s'`), script, { timeoutMs, ...signal }),
107
+ async close() {
108
+ await run('ssh', ['-o', `ControlPath=${join(dir, 's')}`, '-O', 'exit', '--', server.address], '', { timeoutMs: 5_000 }).catch(() => { });
109
+ await rm(dir, { recursive: true, force: true });
110
+ },
111
+ };
112
+ }
113
+ //# sourceMappingURL=ssh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh.js","sourceRoot":"","sources":["../src/ssh.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * The steps of an install, in order: what each is called on the page, who runs it on the server,
3
+ * and the script itself. A step first looks whether its work is already done and then does
4
+ * nothing, so the same list is the first install, a second try after a failure, and an update.
5
+ *
6
+ * `root` is only who installs. What runs afterwards runs as the server's own user `openagent`:
7
+ * the programs are root's, so an agent that works there cannot change OpenAgent itself.
8
+ */
9
+ export declare const STEP_IDS: readonly ['login', 'user', 'tools', 'openagent', 'agent', 'service', 'door'];
10
+ export type StepId = (typeof STEP_IDS)[number];
11
+ /** What a script is written for. */
12
+ export interface Facts {
13
+ /** The server's number or name: what its certificate is for. */
14
+ address: string;
15
+ /** The OpenAgent the server gets: the one that runs on this computer. */
16
+ version: string;
17
+ }
18
+ export interface StepDef {
19
+ id: StepId;
20
+ /** The step's line on the page. */
21
+ label: string;
22
+ as: 'root' | 'openagent';
23
+ /** How long the step may take: a server that stopped answering is not waited for longer. */
24
+ timeoutMs: number;
25
+ script(facts: Facts): string;
26
+ }
27
+ /** The server's user everything runs as once it is installed. */
28
+ export declare const SERVER_USER = "openagent";
29
+ /** Where the server keeps the Claude login: root's file, which the service reads as it starts. An agent on the server works with the login, and so can read it; it cannot change the file. */
30
+ export declare const CLAUDE_FILE = "/etc/openagent/claude.env";
31
+ /** `value` as one word of a shell script, whatever it holds. */
32
+ export declare function quoted(value: string): string;
33
+ /** What the service file holds. `KillMode=process`: a run is a process of its own and goes on when OpenAgent starts again, as on a laptop. */
34
+ export declare const SERVICE_UNIT = "[Unit]\nDescription=OpenAgent\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nUser=openagent\nWorkingDirectory=/home/openagent\nEnvironment=PATH=/home/openagent/.local/bin:/opt/openagent/node/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\nEnvironmentFile=-/etc/openagent/claude.env\nExecStart=/opt/openagent/node/bin/openagent\nRestart=always\nRestartSec=5\nKillMode=process\nAmbientCapabilities=CAP_NET_BIND_SERVICE\n\n[Install]\nWantedBy=multi-user.target";
35
+ export declare const STEPS: readonly StepDef[];
36
+ /** The script that asks the server's door for a link a new machine gets in with. Run as the server's own user. */
37
+ export declare function linkScript(): string;
38
+ /** A script that puts a login on the server or takes it off, and who runs it there. */
39
+ export interface LoginScript {
40
+ as: StepDef['as'];
41
+ script: string;
42
+ }
43
+ /** How long a login's script may take: a server that stopped answering is not waited for longer. */
44
+ export declare const LOGIN_TIMEOUT_MS: number;
45
+ /**
46
+ * Give the server a GitHub login: `gh` keeps it in its own file, and git asks `gh` for it. The
47
+ * server then holds this one login, and answers whose it is; when any of that fails, it holds none.
48
+ *
49
+ * The login is a word of the script, which the server's shell reads where a program would read:
50
+ * it is on no command line, and no program the script starts finds it among what it was given.
51
+ */
52
+ export declare function githubLoginScript(login: string): LoginScript;
53
+ /** Take the GitHub login off the server. */
54
+ export declare function githubLogoutScript(): LoginScript;
55
+ /**
56
+ * Give the server a Claude login: Claude Code is first asked, as the server's own user, whether
57
+ * it reads the login; then the login is written to root's file and OpenAgent is started again
58
+ * with it. A run that is at work goes on with the login it started with. When OpenAgent does not
59
+ * start with it, the file is put back as it was.
60
+ *
61
+ * Claude Code is the server user's own program: root asks it as that user, for half a minute at
62
+ * most, and without the lock root holds, which a program that never ended would hold with it.
63
+ */
64
+ export declare function claudeLoginScript(login: string): LoginScript;
65
+ /** Take the Claude login off the server, and start OpenAgent again without it. */
66
+ export declare function claudeLogoutScript(): LoginScript;
67
+ //# sourceMappingURL=steps.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"steps.d.ts","sourceRoot":"","sources":["../src/steps.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,eAAO,MAAM,QAAQ,YAAI,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAU,CAAA;AACpG,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAA;AAE9C,oCAAoC;AACpC,MAAM,WAAW,KAAK;IACpB,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAA;IACf,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAA;IACb,EAAE,EAAE,MAAM,GAAG,WAAW,CAAA;IACxB,4FAA4F;IAC5F,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAA;CAC7B;AAED,iEAAiE;AACjE,eAAO,MAAM,WAAW,cAAc,CAAA;AAOtC,8LAA8L;AAC9L,eAAO,MAAM,WAAW,8BAA8B,CAAA;AAOtD,gEAAgE;AAChE,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5C;AAgDD,8IAA8I;AAC9I,eAAO,MAAM,YAAY,yfAiBE,CAAA;AAE3B,eAAO,MAAM,KAAK,EAAE,SAAS,OAAO,EAqMnC,CAAA;AAED,kHAAkH;AAClH,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,uFAAuF;AACvF,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;CACf;AAED,oGAAoG;AACpG,eAAO,MAAM,gBAAgB,QAAa,CAAA;AAM1C;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CAmB5D;AAED,4CAA4C;AAC5C,wBAAgB,kBAAkB,IAAI,WAAW,CAYhD;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CA8B5D;AAED,kFAAkF;AAClF,wBAAgB,kBAAkB,IAAI,WAAW,CAchD"}