@openagt/servers 0.0.0-stage → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/store.js ADDED
@@ -0,0 +1,248 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import { mkdir, readFile, rename, rm, stat, writeFile } from 'node:fs/promises';
3
+ import { isIP } from 'node:net';
4
+ import { dirname, join } from 'node:path';
5
+ import { STEP_IDS } from './steps.js';
6
+ /** The two logins a server works with. */
7
+ export const LOGIN_KINDS = ['github', 'claude'];
8
+ /** A cap, so a file edited by hand cannot bloat it. */
9
+ export const MAX_SERVERS = 50;
10
+ const MAX_SAID = 4_000;
11
+ const FILE = 'openagent-servers.json';
12
+ /** The file's path: beside OpenAgent's own home file, by the same rule. */
13
+ export function storePath(env) {
14
+ if (env.XDG_CONFIG_HOME)
15
+ return join(env.XDG_CONFIG_HOME, FILE);
16
+ return join(env.HOME ?? '', '.' + FILE);
17
+ }
18
+ /**
19
+ * `said` as a server's address is kept, or `undefined` for what is none: a number, or a name, in
20
+ * the one form the server's door keeps it in too, so the two always read the same. Nothing that
21
+ * could be read as an option of a command passes.
22
+ */
23
+ export function addressOf(said) {
24
+ if (said.length > 253)
25
+ return undefined;
26
+ if (isIP(said) === 4)
27
+ return said;
28
+ if (isIP(said) === 6) {
29
+ if (said.includes('%') || said.includes('.'))
30
+ return undefined;
31
+ return new URL(`http://[${said}]`).hostname.slice(1, -1);
32
+ }
33
+ return /^(?=.*[a-z])[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$/i.test(said) ? said.toLowerCase() : undefined;
34
+ }
35
+ /** Whether `said` is a user name a Linux server has. */
36
+ export const isUser = (said) => typeof said === 'string' && /^[a-z_][a-z0-9_-]{0,31}$/.test(said);
37
+ /** Whether `said` is a version npm keeps a package under. */
38
+ export const isVersion = (said) => typeof said === 'string' && said.length <= 64 && /^\d+\.\d+\.\d+(-[0-9A-Za-z.-]+)?$/.test(said);
39
+ /** Whether `said` reads as the code GitHub gives a device to show: two groups of four. */
40
+ export const isGithubCode = (said) => typeof said === 'string' && /^[A-Z0-9]{4}-[A-Z0-9]{4}$/.test(said);
41
+ /** How often the install's process says it still works, and after how long of silence it is taken for gone. */
42
+ export const BEAT_MS = 10_000;
43
+ export const SILENT_MS = 45_000;
44
+ /**
45
+ * Say again and again that a work is alive: `beat` every `everyMs`, one at a time, a failed one
46
+ * left at that. `stop` ends it and waits for the one on its way: a beat changes the file, and
47
+ * one that landed after the work's own last word would write when the work has ended.
48
+ */
49
+ export function heartbeat(beat, everyMs) {
50
+ let onItsWay;
51
+ const timer = setInterval(() => {
52
+ onItsWay ??= Promise.resolve()
53
+ .then(beat)
54
+ .catch(() => { })
55
+ .then(() => void (onItsWay = undefined));
56
+ }, everyMs);
57
+ return {
58
+ stop: async () => {
59
+ clearInterval(timer);
60
+ await onItsWay;
61
+ },
62
+ };
63
+ }
64
+ /**
65
+ * Whether the server's install runs right now: its process is there, and said so a moment ago.
66
+ * The process's number alone is no proof: one that died without a word (a laptop that was
67
+ * switched off) leaves its number in the file, and the system gives it to another program later.
68
+ */
69
+ export function working(server, now = Date.now()) {
70
+ return atWork(server.worker, now);
71
+ }
72
+ /** Whether a process that wrote itself to the file is still at work: it is there, and said so a moment ago. */
73
+ export function atWork(worker, now = Date.now()) {
74
+ return worker !== undefined && pidAlive(worker.pid) && now - Date.parse(worker.seen) < SILENT_MS;
75
+ }
76
+ /** Whether `said` is a GitHub account's name. */
77
+ export const isAccount = (said) => typeof said === 'string' && /^[A-Za-z\d](?:[A-Za-z\d-]{0,38})$/.test(said);
78
+ /** Where the door of the server at `address` answers: what OpenAgent's machines know it by. */
79
+ export function doorUrl(address) {
80
+ return `https://${address.includes(':') ? `[${address}]` : address}`;
81
+ }
82
+ /** Every step, not started. */
83
+ export const freshSteps = () => STEP_IDS.map(id => ({ id, state: 'waiting' }));
84
+ const STATES = ['waiting', 'running', 'done', 'failed'];
85
+ function readWorker(value) {
86
+ if (!value || typeof value !== 'object')
87
+ return undefined;
88
+ const { pid, seen } = value;
89
+ return Number.isInteger(pid) && pid > 0 && typeof seen === 'string' && !Number.isNaN(Date.parse(seen)) ? { worker: { pid: pid, seen } } : undefined;
90
+ }
91
+ function readLogin(value) {
92
+ if (!value || typeof value !== 'object')
93
+ return undefined;
94
+ const { since, account, wanted, failed, worker, asking, code } = value;
95
+ const login = {
96
+ ...(typeof since === 'string' && !Number.isNaN(Date.parse(since)) ? { since } : {}),
97
+ ...(isAccount(account) ? { account } : {}),
98
+ ...(wanted === true ? { wanted } : {}),
99
+ ...(typeof failed === 'string' && failed ? { failed: failed.slice(-MAX_SAID) } : {}),
100
+ ...(readWorker(worker) ?? {}),
101
+ ...(asking === true ? { asking } : {}),
102
+ ...(isGithubCode(code) ? { code } : {}),
103
+ };
104
+ return Object.keys(login).length ? login : undefined;
105
+ }
106
+ /** Read what a file holds, forgivingly: anything malformed is left out, never a reason to fail. */
107
+ export function sanitize(value) {
108
+ const obj = value && typeof value === 'object' && !Array.isArray(value) ? value : {};
109
+ const servers = [];
110
+ for (const entry of Array.isArray(obj.servers) ? obj.servers : []) {
111
+ if (servers.length >= MAX_SERVERS)
112
+ break;
113
+ if (!entry || typeof entry !== 'object')
114
+ continue;
115
+ const { address, user, fingerprint, steps, worker, adding, github, claude } = entry;
116
+ const [githubLogin, claudeLogin] = [readLogin(github), readLogin(claude)];
117
+ if (typeof address !== 'string' || addressOf(address) !== address || !isUser(user))
118
+ continue;
119
+ if (servers.some(server => server.address === address))
120
+ continue;
121
+ // Every step, in the install's own order: a step the file does not know has not started.
122
+ const known = new Map();
123
+ for (const step of Array.isArray(steps) ? steps : []) {
124
+ if (!step || typeof step !== 'object')
125
+ continue;
126
+ const { id, state, said } = step;
127
+ if (typeof id !== 'string' || !STATES.includes(state))
128
+ continue;
129
+ known.set(id, { id: id, state: state, ...(typeof said === 'string' && said ? { said: said.slice(-MAX_SAID) } : {}) });
130
+ }
131
+ servers.push({
132
+ address,
133
+ user,
134
+ ...(typeof fingerprint === 'string' && /^SHA256:[A-Za-z0-9+/=]{20,64}$/.test(fingerprint) ? { fingerprint } : {}),
135
+ steps: STEP_IDS.map(id => known.get(id) ?? { id, state: 'waiting' }),
136
+ ...(readWorker(worker) ?? {}),
137
+ adding: adding === true,
138
+ ...(githubLogin ? { github: githubLogin } : {}),
139
+ ...(claudeLogin ? { claude: claudeLogin } : {}),
140
+ });
141
+ }
142
+ return { servers };
143
+ }
144
+ /** The file is there and cannot be read as what it should hold: a typo made by hand, a disk that failed. */
145
+ export class Unreadable extends Error {
146
+ constructor(path) {
147
+ super(`${path} cannot be read: correct it, or delete it to start with no server`);
148
+ }
149
+ }
150
+ /** The file as it stands; no file reads as no server. Throws {@link Unreadable} for a file that is there and cannot be read. */
151
+ export async function readStore(env) {
152
+ const path = storePath(env);
153
+ let text;
154
+ try {
155
+ text = await readFile(path, 'utf8');
156
+ }
157
+ catch (err) {
158
+ if (err.code === 'ENOENT')
159
+ return sanitize(undefined);
160
+ throw new Unreadable(path);
161
+ }
162
+ try {
163
+ const value = JSON.parse(text);
164
+ if (value && typeof value === 'object' && !Array.isArray(value))
165
+ return sanitize(value);
166
+ }
167
+ catch {
168
+ // Told below, as any file that holds something else.
169
+ }
170
+ throw new Unreadable(path);
171
+ }
172
+ /** How long a writer waits for the lock, and how old a lock is taken over whoever holds it: a change takes milliseconds. */
173
+ const LOCK_WAIT_MS = 5_000;
174
+ const LOCK_OLD_MS = 30_000;
175
+ /** Whether the lock is one left behind: its writer is gone, or it is far older than any change takes. */
176
+ async function leftBehind(lock) {
177
+ const [held, age] = await Promise.all([readFile(lock, 'utf8').catch(() => undefined), stat(lock).then(s => Date.now() - s.mtimeMs, () => 0)]);
178
+ if (held === undefined)
179
+ return false;
180
+ const pid = Number(held.split(':')[0]);
181
+ return age > LOCK_OLD_MS || !Number.isInteger(pid) || !pidAlive(pid);
182
+ }
183
+ async function withLock(path, run) {
184
+ const lock = `${path}.lock`;
185
+ // The writer's name on the lock: a writer never removes a lock that is not its own.
186
+ const mine = `${process.pid}:${randomBytes(8).toString('hex')}`;
187
+ await mkdir(dirname(path), { recursive: true });
188
+ const deadline = Date.now() + LOCK_WAIT_MS;
189
+ for (;;) {
190
+ try {
191
+ await writeFile(lock, mine, { flag: 'wx', mode: 0o600 });
192
+ break;
193
+ }
194
+ catch (err) {
195
+ if (err.code !== 'EEXIST')
196
+ throw err;
197
+ // A writer that died holding the lock must not shut every later one out. Looked at twice,
198
+ // so a lock another waiter just took over is not taken from it.
199
+ if ((await leftBehind(lock)) && (await leftBehind(lock)))
200
+ await rm(lock, { force: true });
201
+ else if (Date.now() > deadline)
202
+ throw new Error('the servers file is held by another writer');
203
+ else
204
+ await new Promise(resolve => setTimeout(resolve, 20));
205
+ }
206
+ }
207
+ try {
208
+ return await run();
209
+ }
210
+ finally {
211
+ if ((await readFile(lock, 'utf8').catch(() => undefined)) === mine)
212
+ await rm(lock, { force: true });
213
+ }
214
+ }
215
+ /**
216
+ * Change the file: read it, apply `change`, write it back, with no other writer in between.
217
+ * Written to a temporary file and renamed, so a reader never sees half a file, owner-only.
218
+ * `change` answers what the caller wants to know of the change.
219
+ */
220
+ export async function update(env, change) {
221
+ const path = storePath(env);
222
+ return withLock(path, async () => {
223
+ const store = await readStore(env);
224
+ const answer = change(store);
225
+ const tmp = `${path}.${process.pid}.tmp`;
226
+ await writeFile(tmp, JSON.stringify(sanitize(store), null, 2) + '\n', { mode: 0o600 });
227
+ await rename(tmp, path);
228
+ return answer;
229
+ });
230
+ }
231
+ /** Change the one server at `address`, when the file still holds it; answers what `apply` did, or `undefined` for a server that is gone. */
232
+ export function changeServer(env, address, apply) {
233
+ return update(env, store => {
234
+ const kept = store.servers.find(candidate => candidate.address === address);
235
+ return kept ? apply(kept) : undefined;
236
+ });
237
+ }
238
+ /** Whether a process with this id is running. */
239
+ export function pidAlive(pid) {
240
+ try {
241
+ process.kill(pid, 0);
242
+ return true;
243
+ }
244
+ catch (err) {
245
+ return err.code === 'EPERM';
246
+ }
247
+ }
248
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,50 @@
1
1
  {
2
2
  "name": "@openagt/servers",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.2",
4
+ "description": "Connect your VPS: give a server's address and a user name, and OpenAgent installs itself there over SSH, with this computer's own SSH keys. The server then starts OpenAgent by itself, opens its door over HTTPS, and is a machine a run can be sent to. The server gets its two logins from this computer, over SSH: your GitHub login, copied when you tick it, and a Claude login made for it with claude setup-token. It brings the Servers section of the dashboard's Settings page.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/openagt/openagent.git",
9
+ "directory": "packages/servers"
10
+ },
11
+ "type": "module",
12
+ "engines": {
13
+ "node": ">=22.12.0"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "bin/servers"
18
+ ],
19
+ "bin": {
20
+ "servers": "bin/servers"
21
+ },
22
+ "exports": {
23
+ "./package.json": "./package.json",
24
+ "./dashboard": "./dist/dashboard/dashboard.js"
25
+ },
26
+ "devDependencies": {
27
+ "@tailwindcss/vite": "^4.0.0",
28
+ "@testing-library/dom": "^10.4.1",
29
+ "@testing-library/react": "^16.3.2",
30
+ "@types/node": "^20.0.0",
31
+ "@types/react": "^19.2.0",
32
+ "@vitejs/plugin-react": "^6.0.3",
33
+ "jsdom": "^29.1.1",
34
+ "react": "^19.2.0",
35
+ "react-dom": "^19.2.0",
36
+ "tailwindcss": "^4.0.0",
37
+ "typescript": "^7.0.2",
38
+ "vite": "^8.0.0",
39
+ "vitest": "^4.1.10"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "scripts": {
45
+ "build": "tsc -p tsconfig.build.json && vite build -c dashboard/vite.config.ts",
46
+ "typecheck": "tsc --noEmit && tsc --noEmit -p dashboard/tsconfig.json",
47
+ "test": "tsc -p tsconfig.test.json && node --test --test-timeout=60000 'dist-test/**/*.test.js' && cd dashboard && vitest run",
48
+ "clean": "rm -rf dist dist-test"
49
+ }
6
50
  }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.