pinomad 0.11.0 → 0.12.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 +1 -1
- package/dist/{chunk-b457q0tm.js → chunk-41q0saz0.js} +11 -8
- package/dist/chunk-42nqtx5r.js +22 -0
- package/dist/{chunk-wb10efyq.js → chunk-445cevpm.js} +1 -1
- package/dist/{chunk-xqycs43g.js → chunk-47ff1d1t.js} +35 -7
- package/dist/{chunk-n1m1tjrk.js → chunk-5qf1grkk.js} +19 -13
- package/dist/{chunk-ncbqa8jk.js → chunk-7mn38n4f.js} +1 -1
- package/dist/{chunk-sd5fkxjj.js → chunk-7wj0jh3t.js} +1 -1
- package/dist/{chunk-4dcs8sbj.js → chunk-agq4tn9r.js} +43 -21
- package/dist/{chunk-as6pyf00.js → chunk-aqj9wfmb.js} +1 -1
- package/dist/{chunk-y127fe1a.js → chunk-g3ahp7b9.js} +11 -0
- package/dist/{chunk-gtdpjqa3.js → chunk-gg2dcqbg.js} +186 -45
- package/dist/{chunk-6d16c2fv.js → chunk-jeqsdcj1.js} +13 -6
- package/dist/{chunk-67gq282t.js → chunk-ksfxqd8s.js} +491 -34
- package/dist/{chunk-9h5vawnm.js → chunk-mnmqyq7d.js} +237 -27
- package/dist/{chunk-jt34g8st.js → chunk-np4jd070.js} +154 -2
- package/dist/{chunk-44pnts98.js → chunk-nppgxh6c.js} +1 -1
- package/dist/chunk-nq57fdnq.js +106 -0
- package/dist/{chunk-vvj9b1gc.js → chunk-t5abqsva.js} +174 -52
- package/dist/{chunk-tj57aqav.js → chunk-vqkb70d7.js} +2 -2
- package/dist/{chunk-h5q2y57m.js → chunk-vx7dy07y.js} +10 -4
- package/dist/pinomad.js +15 -14
- package/package.json +2 -1
- package/web/assets/astro-DOly5024.js +1 -0
- package/web/assets/c-BIGW1oBm.js +1 -0
- package/web/assets/core-DnpzcQzs.js +4 -0
- package/web/assets/cpp-DddhVWhK.js +1 -0
- package/web/assets/csharp-B_TYUkTI.js +1 -0
- package/web/assets/css-Z72CF80r.js +1 -0
- package/web/assets/dart-CtKP3W-j.js +1 -0
- package/web/assets/diff-D97Zzqfu.js +1 -0
- package/web/assets/dockerfile-BcOcwvcX.js +1 -0
- package/web/assets/elixir-DkJLSxtU.js +1 -0
- package/web/assets/engine-javascript-BHPwbUfN.js +141 -0
- package/web/assets/engine-oniguruma-CQjQPVHy.js +1 -0
- package/web/assets/fish-BvzEVeQv.js +1 -0
- package/web/assets/go-C27-OAKa.js +1 -0
- package/web/assets/graphql-ChdNCCLP.js +1 -0
- package/web/assets/haskell-Df6bDoY_.js +1 -0
- package/web/assets/hcl-BWvSN4gD.js +1 -0
- package/web/assets/highlighted-diff-Dn5ifsvK.js +175 -0
- package/web/assets/highlights-DnHv6NBM.js +4 -0
- package/web/assets/highlights-themes-CUdrlZk3.js +1 -0
- package/web/assets/html-BlS82ilI.js +1 -0
- package/web/assets/{index-DoyHMPwf.css → index-BV0ecolo.css} +1 -1
- package/web/assets/index-DNsTTgPE.js +396 -0
- package/web/assets/ini-BEwlwnbL.js +1 -0
- package/web/assets/java-CylS5w8V.js +1 -0
- package/web/assets/javascript-wDzz0qaB.js +1 -0
- package/web/assets/json-Cp-IABpG.js +1 -0
- package/web/assets/jsonc-Des-eS-w.js +1 -0
- package/web/assets/jsx-g9-lgVsj.js +1 -0
- package/web/assets/kotlin-BdnUsdx6.js +1 -0
- package/web/assets/less-B1dDrJ26.js +1 -0
- package/web/assets/lua-BaeVxFsk.js +1 -0
- package/web/assets/make-CHLpvVh8.js +1 -0
- package/web/assets/markdown-Cvjx9yec.js +1 -0
- package/web/assets/nix-CwoSXNpI.js +1 -0
- package/web/assets/objective-c-DXmwc3jG.js +1 -0
- package/web/assets/php-DoruYMF0.js +1 -0
- package/web/assets/pierre-dark-CyvmCCZW.js +1 -0
- package/web/assets/pierre-dark-VKo95ZgA.js +1 -0
- package/web/assets/pierre-light-480U9XYS.js +1 -0
- package/web/assets/pierre-light-BVSW-Xev.js +1 -0
- package/web/assets/postcss-DnzrXTVv.js +1 -0
- package/web/assets/powershell-Dd0kTxKP.js +1 -0
- package/web/assets/prisma-Vru482bI.js +1 -0
- package/web/assets/proto-C7zT0LnQ.js +1 -0
- package/web/assets/python-B6aJPvgy.js +1 -0
- package/web/assets/ruby-B43yHlf9.js +1 -0
- package/web/assets/rust-DPvrY3IQ.js +1 -0
- package/web/assets/scala-CqE71os6.js +1 -0
- package/web/assets/scss-Cw6XMfVs.js +1 -0
- package/web/assets/shellscript-Yzrsuije.js +1 -0
- package/web/assets/shiki-BuPP5HCg.js +2 -0
- package/web/assets/shiki-langs-FJk0v0Ga.js +2 -0
- package/web/assets/shiki-wasm-CgFRq91U.js +1 -0
- package/web/assets/sql-DkvoVjww.js +1 -0
- package/web/assets/stream-tokenizer-Bl6bisIa.js +4 -0
- package/web/assets/svelte-DoIXUgJc.js +1 -0
- package/web/assets/swift-C2oV4EkX.js +1 -0
- package/web/assets/terraform-BETggiCN.js +1 -0
- package/web/assets/theming-themes-BL_CC-Mm.js +1 -0
- package/web/assets/toml-vGWfd6FD.js +1 -0
- package/web/assets/tsx-COt5Ahok.js +1 -0
- package/web/assets/typescript-BPQ3VLAy.js +1 -0
- package/web/assets/vue-CwSeJimo.js +1 -0
- package/web/assets/xml-sdJ4AIDG.js +1 -0
- package/web/assets/xterm-view-6GBZ9nXN.css +32 -0
- package/web/assets/xterm-view-ZsuzJb2C.js +38 -0
- package/web/assets/yaml-Buea-lGh.js +1 -0
- package/web/assets/zig-VOosw3JB.js +1 -0
- package/web/index.html +2 -2
- package/dist/chunk-etf7ef97.js +0 -55
- package/dist/chunk-greb3dp6.js +0 -22
- package/web/assets/index-BhHrKpoo.js +0 -395
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ A self-hosted coding agent built on [Pi Durable](https://www.npmjs.com/package/@
|
|
|
17
17
|
## Quick start
|
|
18
18
|
|
|
19
19
|
1. **Create an account** at [app.pinomad.si](https://app.pinomad.si/) (sign-up is by invite).
|
|
20
|
-
2. **Add your host.** On the machine that should run the agent (macOS or Linux), run the two lines below. `pinomad up`
|
|
20
|
+
2. **Add your host.** On the machine that should run the agent (macOS or Linux), run the two lines below. `pinomad up` opens a page in your browser; sign in and confirm to add the host to your account. It then runs as a service. Without a browser on that machine (over SSH, say), it prints a code to enter on any signed-in device instead.
|
|
21
21
|
|
|
22
22
|
```sh
|
|
23
23
|
curl -fsSL https://pinomad.si/install.sh | sh
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
readRemovalNotice,
|
|
3
4
|
readServerConfig
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-nq57fdnq.js";
|
|
5
6
|
import {
|
|
6
7
|
argValue,
|
|
7
8
|
reachabilityLine,
|
|
8
9
|
serviceManager,
|
|
9
10
|
versionLine
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import"./chunk-
|
|
11
|
+
} from "./chunk-t5abqsva.js";
|
|
12
|
+
import"./chunk-np4jd070.js";
|
|
12
13
|
import"./chunk-kzbzacz9.js";
|
|
13
|
-
import"./chunk-
|
|
14
|
+
import"./chunk-47ff1d1t.js";
|
|
14
15
|
import"./chunk-pqe7q1s7.js";
|
|
15
16
|
import {
|
|
16
17
|
DEFAULT_DATA_DIR
|
|
17
18
|
} from "./chunk-ngf1zatm.js";
|
|
18
19
|
import {
|
|
19
20
|
commandHint
|
|
20
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-7wj0jh3t.js";
|
|
21
22
|
import"./chunk-df32avw6.js";
|
|
22
23
|
|
|
23
24
|
// apps/host/src/cli/status.ts
|
|
@@ -27,6 +28,7 @@ async function statusReport(args, deps) {
|
|
|
27
28
|
const dataDir = resolve(args.dataDir);
|
|
28
29
|
const manager = deps?.manager ?? serviceManager();
|
|
29
30
|
const server = await readServerConfig(dataDir);
|
|
31
|
+
const removed = server === undefined ? await readRemovalNotice(dataDir) : undefined;
|
|
30
32
|
const service = await manager.state();
|
|
31
33
|
const spec = service.installed ? await manager.read() : undefined;
|
|
32
34
|
const own = spec !== undefined && resolve(spec.dataDir) === dataDir;
|
|
@@ -34,6 +36,7 @@ async function statusReport(args, deps) {
|
|
|
34
36
|
const port = own ? argValue(spec.hostArgs, "--port") : undefined;
|
|
35
37
|
return {
|
|
36
38
|
...server === undefined ? {} : { server },
|
|
39
|
+
...removed === undefined ? {} : { removed },
|
|
37
40
|
...lanPort === undefined ? {} : { lanPort },
|
|
38
41
|
service,
|
|
39
42
|
...port === undefined ? {} : { port },
|
|
@@ -45,10 +48,10 @@ async function main(argv) {
|
|
|
45
48
|
const dataDir = resolve(values["data-dir"] ?? DEFAULT_DATA_DIR);
|
|
46
49
|
try {
|
|
47
50
|
const report = await statusReport({ dataDir });
|
|
48
|
-
const { server } = report;
|
|
49
|
-
console.log(server
|
|
51
|
+
const { server, removed } = report;
|
|
52
|
+
console.log(server !== undefined ? `server: ${server.username === undefined ? "joined" : server.username} at ${server.origin}` : removed !== undefined ? `server: removed from ${removed.username === undefined ? "its account" : `${removed.username}'s account`} at ${removed.origin} (\`${commandHint("up")}\` to join again)` : `server: not joined (\`${commandHint("up")}\` to join)`);
|
|
50
53
|
console.log(`LAN and tailnet: ${report.lanPort === undefined ? "off" : `port ${report.lanPort}`}`);
|
|
51
|
-
console.log(`service: ${report.service.installed ? report.service.
|
|
54
|
+
console.log(`service: ${!report.service.installed ? "not installed" : report.service.stopped ? `stopped (\`${commandHint("service")} start\` to turn it back on)` : report.service.detail}`);
|
|
52
55
|
if (report.otherDataDir !== undefined)
|
|
53
56
|
console.log(`note: the installed service runs ${report.otherDataDir}, not ${dataDir}`);
|
|
54
57
|
console.log(await versionLine());
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
joinServer,
|
|
4
|
+
main
|
|
5
|
+
} from "./chunk-gg2dcqbg.js";
|
|
6
|
+
import"./chunk-7mn38n4f.js";
|
|
7
|
+
import"./chunk-nppgxh6c.js";
|
|
8
|
+
import"./chunk-t91zt5f0.js";
|
|
9
|
+
import"./chunk-nq57fdnq.js";
|
|
10
|
+
import"./chunk-g3ahp7b9.js";
|
|
11
|
+
import"./chunk-t5abqsva.js";
|
|
12
|
+
import"./chunk-np4jd070.js";
|
|
13
|
+
import"./chunk-kzbzacz9.js";
|
|
14
|
+
import"./chunk-47ff1d1t.js";
|
|
15
|
+
import"./chunk-pqe7q1s7.js";
|
|
16
|
+
import"./chunk-ngf1zatm.js";
|
|
17
|
+
import"./chunk-7wj0jh3t.js";
|
|
18
|
+
import"./chunk-df32avw6.js";
|
|
19
|
+
export {
|
|
20
|
+
main,
|
|
21
|
+
joinServer
|
|
22
|
+
};
|
|
@@ -16,7 +16,7 @@ var NOT_BUILT = "Web client not built: run bun run build";
|
|
|
16
16
|
var SECURITY_HEADERS = {
|
|
17
17
|
"content-security-policy": [
|
|
18
18
|
"default-src 'self'",
|
|
19
|
-
"script-src 'self'",
|
|
19
|
+
"script-src 'self' 'wasm-unsafe-eval'",
|
|
20
20
|
"style-src 'self' 'unsafe-inline'",
|
|
21
21
|
"img-src 'self' data:",
|
|
22
22
|
"connect-src 'self' ws: wss:",
|
|
@@ -187,7 +187,7 @@ var conversationStream = (id) => `conversation:${id}`;
|
|
|
187
187
|
var docStream = (kind, id) => `doc:${kind}:${id}`;
|
|
188
188
|
var UNAUTHORIZED_CLOSE_CODE = 4401;
|
|
189
189
|
var INVALID_FRAME_CLOSE_CODE = 4400;
|
|
190
|
-
var PROTOCOL = { major: 5, minor:
|
|
190
|
+
var PROTOCOL = { major: 5, minor: 27 };
|
|
191
191
|
var CALL_SINCE = {
|
|
192
192
|
addProject: 0,
|
|
193
193
|
removeProject: 0,
|
|
@@ -229,7 +229,13 @@ var CALL_SINCE = {
|
|
|
229
229
|
beginAttachment: 19,
|
|
230
230
|
appendAttachment: 19,
|
|
231
231
|
rtcOffer: 23,
|
|
232
|
-
ping: 23
|
|
232
|
+
ping: 23,
|
|
233
|
+
openTerminal: 27,
|
|
234
|
+
terminalInput: 27,
|
|
235
|
+
resizeTerminal: 27,
|
|
236
|
+
renameTerminal: 27,
|
|
237
|
+
restartTerminal: 27,
|
|
238
|
+
closeTerminal: 27
|
|
233
239
|
};
|
|
234
240
|
function hostSupportsCall(hostMinor, method) {
|
|
235
241
|
return hostMinor >= CALL_SINCE[method];
|
|
@@ -246,7 +252,8 @@ var FEATURE_SINCE = {
|
|
|
246
252
|
attachments: 19,
|
|
247
253
|
imageVariants: 20,
|
|
248
254
|
rtc: 23,
|
|
249
|
-
submitEntry: 25
|
|
255
|
+
submitEntry: 25,
|
|
256
|
+
terminals: 27
|
|
250
257
|
};
|
|
251
258
|
function hostSupportsFeature(hostMinor, feature) {
|
|
252
259
|
return hostMinor >= FEATURE_SINCE[feature];
|
|
@@ -259,8 +266,16 @@ function parseClientFrame(value) {
|
|
|
259
266
|
if (typeof stream !== "string")
|
|
260
267
|
return { kind: "malformed" };
|
|
261
268
|
if (isStreamName(stream)) {
|
|
262
|
-
|
|
263
|
-
|
|
269
|
+
if (value.type === "unsubscribe")
|
|
270
|
+
return { kind: "frame", frame: { type: "unsubscribe", stream } };
|
|
271
|
+
if (stream.startsWith("terminal:") && value.since !== undefined) {
|
|
272
|
+
const since = value.since;
|
|
273
|
+
if (typeof since !== "number" || !Number.isSafeInteger(since) || since < 0)
|
|
274
|
+
return { kind: "malformed" };
|
|
275
|
+
return { kind: "frame", frame: { type: "subscribe", stream, since } };
|
|
276
|
+
}
|
|
277
|
+
const refs = value.images === "ref";
|
|
278
|
+
return { kind: "frame", frame: refs ? { type: "subscribe", stream, images: "ref" } : { type: "subscribe", stream } };
|
|
264
279
|
}
|
|
265
280
|
const prefix = stream.slice(0, stream.indexOf(":") === -1 ? undefined : stream.indexOf(":"));
|
|
266
281
|
if (STREAM_KINDS.has(prefix))
|
|
@@ -277,9 +292,9 @@ function parseClientFrame(value) {
|
|
|
277
292
|
return { kind: "unsupportedCall", id, method };
|
|
278
293
|
return validCallArgs(method, args) ? { kind: "frame", frame: { type: "call", id, method, args } } : { kind: "malformed" };
|
|
279
294
|
}
|
|
280
|
-
var STREAM_KINDS = new Set(["conversation", "doc", "tasks", ...HOST_STREAM_NAMES]);
|
|
295
|
+
var STREAM_KINDS = new Set(["conversation", "doc", "tasks", "terminals", "terminal", ...HOST_STREAM_NAMES]);
|
|
281
296
|
function isStreamName(stream) {
|
|
282
|
-
return stream === "tasks" || HOST_STREAM_NAMES.includes(stream) || /^(?:conversation:|doc
|
|
297
|
+
return stream === "tasks" || HOST_STREAM_NAMES.includes(stream) || /^(?:conversation:|doc:.+:|terminals:)[1-9]\d*$/.test(stream) || /^terminal:[\w-]+$/.test(stream);
|
|
283
298
|
}
|
|
284
299
|
var isCallMethod = (method) => CALL_METHODS.includes(method);
|
|
285
300
|
var CALL_METHODS = Object.keys(CALL_SINCE);
|
|
@@ -299,7 +314,20 @@ function validCallArgs(method, args) {
|
|
|
299
314
|
const skills = () => args.skills === undefined || Array.isArray(args.skills) && args.skills.every(nonempty);
|
|
300
315
|
const attachments = () => args.attachments === undefined || Array.isArray(args.attachments) && args.attachments.every(nonempty);
|
|
301
316
|
const role = () => record(args.role) && typeof args.role.name === "string" && typeof args.role.description === "string" && typeof args.role.prompt === "string" && (args.role.model === undefined || typeof args.role.model === "string") && (args.role.thinkingLevel === undefined || typeof args.role.thinkingLevel === "string");
|
|
317
|
+
const terminalId = () => nonempty(args.terminalId);
|
|
318
|
+
const size = () => [args.cols, args.rows].every((n) => typeof n === "number" && Number.isSafeInteger(n) && n >= 1);
|
|
302
319
|
switch (method) {
|
|
320
|
+
case "openTerminal":
|
|
321
|
+
return conversationId() && size();
|
|
322
|
+
case "terminalInput":
|
|
323
|
+
return terminalId() && typeof args.data === "string";
|
|
324
|
+
case "resizeTerminal":
|
|
325
|
+
case "restartTerminal":
|
|
326
|
+
return terminalId() && size();
|
|
327
|
+
case "renameTerminal":
|
|
328
|
+
return terminalId() && (args.name === null || typeof args.name === "string");
|
|
329
|
+
case "closeTerminal":
|
|
330
|
+
return terminalId();
|
|
303
331
|
case "submit":
|
|
304
332
|
return conversationId() && typeof args.text === "string" && (args.whenBusy === "steer" || args.whenBusy === "followUp") && nonempty(args.requestId) && images() && skills() && attachments();
|
|
305
333
|
case "submission":
|
|
@@ -3,31 +3,32 @@ import {
|
|
|
3
3
|
joinServer,
|
|
4
4
|
openServiceSlot,
|
|
5
5
|
writeService
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import"./chunk-
|
|
8
|
-
import"./chunk-
|
|
6
|
+
} from "./chunk-gg2dcqbg.js";
|
|
7
|
+
import"./chunk-7mn38n4f.js";
|
|
8
|
+
import"./chunk-nppgxh6c.js";
|
|
9
9
|
import"./chunk-t91zt5f0.js";
|
|
10
10
|
import {
|
|
11
11
|
normalizeOrigin,
|
|
12
|
+
readRemovalNotice,
|
|
12
13
|
readServerConfig
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import"./chunk-
|
|
14
|
+
} from "./chunk-nq57fdnq.js";
|
|
15
|
+
import"./chunk-g3ahp7b9.js";
|
|
15
16
|
import {
|
|
16
17
|
argValue,
|
|
17
18
|
serviceManager,
|
|
18
19
|
withoutArg,
|
|
19
20
|
withoutRelayArgs
|
|
20
|
-
} from "./chunk-
|
|
21
|
-
import"./chunk-
|
|
21
|
+
} from "./chunk-t5abqsva.js";
|
|
22
|
+
import"./chunk-np4jd070.js";
|
|
22
23
|
import"./chunk-kzbzacz9.js";
|
|
23
|
-
import"./chunk-
|
|
24
|
+
import"./chunk-47ff1d1t.js";
|
|
24
25
|
import"./chunk-pqe7q1s7.js";
|
|
25
26
|
import {
|
|
26
27
|
DEFAULT_DATA_DIR
|
|
27
28
|
} from "./chunk-ngf1zatm.js";
|
|
28
29
|
import {
|
|
29
30
|
commandHint
|
|
30
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-7wj0jh3t.js";
|
|
31
32
|
import"./chunk-df32avw6.js";
|
|
32
33
|
|
|
33
34
|
// apps/host/src/cli/up.ts
|
|
@@ -44,6 +45,9 @@ function resolveReach(flags, current) {
|
|
|
44
45
|
return { server: flags.server ?? current.server, lan: flags.lan ?? current.lan };
|
|
45
46
|
return { server: flags.server ?? !flags.lan, lan: flags.lan ?? !flags.server };
|
|
46
47
|
}
|
|
48
|
+
var lastRemovedOrigin = async (dataDir) => (await readRemovalNotice(dataDir).catch(() => {
|
|
49
|
+
return;
|
|
50
|
+
}))?.origin;
|
|
47
51
|
async function currentReach(dataDir, deps) {
|
|
48
52
|
const slot = await openServiceSlot(dataDir, deps?.manager ?? serviceManager());
|
|
49
53
|
const config = await readServerConfig(dataDir);
|
|
@@ -73,7 +77,7 @@ async function upHost(args, deps) {
|
|
|
73
77
|
const manager = deps?.manager ?? serviceManager();
|
|
74
78
|
const slot = await openServiceSlot(dataDir, manager);
|
|
75
79
|
const config = await readServerConfig(dataDir);
|
|
76
|
-
const origin = normalizeOrigin(args.origin ?? config?.origin ?? OFFICIAL_SERVER_ORIGIN);
|
|
80
|
+
const origin = normalizeOrigin(args.origin ?? config?.origin ?? await lastRemovedOrigin(dataDir) ?? OFFICIAL_SERVER_ORIGIN);
|
|
77
81
|
if (reach.server && config !== undefined && normalizeOrigin(config.origin) !== origin) {
|
|
78
82
|
throw new Error(`this host is joined to ${config.origin} — run \`${commandHint("leave")}\` first to join ${origin}, ` + `or leave out --origin to stay`);
|
|
79
83
|
}
|
|
@@ -81,7 +85,7 @@ async function upHost(args, deps) {
|
|
|
81
85
|
const lan = (hostArgs) => withLan(hostArgs, lanPort);
|
|
82
86
|
const notes = [];
|
|
83
87
|
if (reach.server && config === undefined) {
|
|
84
|
-
const joined = await joinServer({ origin, dataDir, editHostArgs: lan }, { ...deps, manager });
|
|
88
|
+
const joined = await joinServer({ origin, dataDir, code: args.code, editHostArgs: lan }, { ...deps, manager });
|
|
85
89
|
return {
|
|
86
90
|
joined: { origin: joined.origin, username: joined.username, fresh: true },
|
|
87
91
|
...lanPort === undefined ? {} : { lanPort },
|
|
@@ -145,6 +149,7 @@ async function main(argv, deps) {
|
|
|
145
149
|
lan: { type: "boolean" },
|
|
146
150
|
yes: { type: "boolean", short: "y" },
|
|
147
151
|
origin: { type: "string" },
|
|
152
|
+
code: { type: "boolean" },
|
|
148
153
|
"remote-port": { type: "string" },
|
|
149
154
|
"data-dir": { type: "string" }
|
|
150
155
|
}
|
|
@@ -152,7 +157,7 @@ async function main(argv, deps) {
|
|
|
152
157
|
const log = deps?.log ?? ((line) => console.log(line));
|
|
153
158
|
try {
|
|
154
159
|
const dataDir = resolve(values["data-dir"] ?? DEFAULT_DATA_DIR);
|
|
155
|
-
const origin = normalizeOrigin(values.origin ?? (await readServerConfig(dataDir))?.origin ?? OFFICIAL_SERVER_ORIGIN);
|
|
160
|
+
const origin = normalizeOrigin(values.origin ?? (await readServerConfig(dataDir))?.origin ?? await lastRemovedOrigin(dataDir) ?? OFFICIAL_SERVER_ORIGIN);
|
|
156
161
|
const current = await currentReach(dataDir, deps);
|
|
157
162
|
let reach = resolveReach({ server: values.server, lan: values.lan }, current);
|
|
158
163
|
if (reach === undefined) {
|
|
@@ -176,7 +181,8 @@ async function main(argv, deps) {
|
|
|
176
181
|
dataDir,
|
|
177
182
|
origin,
|
|
178
183
|
reach,
|
|
179
|
-
...values["remote-port"] === undefined ? {} : { remotePort: values["remote-port"] }
|
|
184
|
+
...values["remote-port"] === undefined ? {} : { remotePort: values["remote-port"] },
|
|
185
|
+
...values.code === true ? { code: true } : {}
|
|
180
186
|
}, deps);
|
|
181
187
|
if (outcome.joined !== undefined) {
|
|
182
188
|
const who = outcome.joined.username ?? "an account";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// apps/host/src/distribution.ts
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
-
var packagedVersion = "0.
|
|
3
|
+
var packagedVersion = "0.12.0";
|
|
4
4
|
var webRoot = fileURLToPath(new URL(packagedVersion === undefined ? "../../web/dist" : "../web/", import.meta.url));
|
|
5
5
|
var hostEntry = packagedVersion === undefined ? { script: fileURLToPath(new URL("./main.ts", import.meta.url)), args: [] } : { script: fileURLToPath(new URL("./pinomad.js", import.meta.url)), args: ["host"] };
|
|
6
6
|
var commandHint = (name) => packagedVersion === undefined ? `bun run ${name}` : `pinomad ${name}`;
|
|
@@ -3,32 +3,35 @@ import {
|
|
|
3
3
|
assertSecureServerOrigin,
|
|
4
4
|
probeServerAccounts,
|
|
5
5
|
runServerOperation
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-7mn38n4f.js";
|
|
7
7
|
import {
|
|
8
8
|
loadHostKey,
|
|
9
9
|
loadRelayKey
|
|
10
10
|
} from "./chunk-t91zt5f0.js";
|
|
11
11
|
import {
|
|
12
|
+
readRemovalNotice,
|
|
12
13
|
readServerConfig,
|
|
13
14
|
removeServerConfig
|
|
14
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-nq57fdnq.js";
|
|
15
16
|
import {
|
|
16
17
|
relayHostId
|
|
17
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-g3ahp7b9.js";
|
|
18
19
|
import {
|
|
19
20
|
argValue,
|
|
20
21
|
restartService,
|
|
21
22
|
serviceManager,
|
|
22
23
|
withoutRelayArgs
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import"./chunk-
|
|
24
|
+
} from "./chunk-t5abqsva.js";
|
|
25
|
+
import"./chunk-np4jd070.js";
|
|
25
26
|
import"./chunk-kzbzacz9.js";
|
|
26
|
-
import"./chunk-
|
|
27
|
+
import"./chunk-47ff1d1t.js";
|
|
27
28
|
import"./chunk-pqe7q1s7.js";
|
|
28
29
|
import {
|
|
29
30
|
DEFAULT_DATA_DIR
|
|
30
31
|
} from "./chunk-ngf1zatm.js";
|
|
31
|
-
import
|
|
32
|
+
import {
|
|
33
|
+
commandHint
|
|
34
|
+
} from "./chunk-7wj0jh3t.js";
|
|
32
35
|
import"./chunk-df32avw6.js";
|
|
33
36
|
|
|
34
37
|
// apps/host/src/cli/leave.ts
|
|
@@ -39,8 +42,13 @@ var message = (error) => error instanceof Error ? error.message : String(error);
|
|
|
39
42
|
async function leaveServer(args, deps) {
|
|
40
43
|
const dataDir = resolve(args.dataDir);
|
|
41
44
|
const config = await readServerConfig(dataDir);
|
|
42
|
-
if (config === undefined)
|
|
43
|
-
|
|
45
|
+
if (config === undefined) {
|
|
46
|
+
const removed = await readRemovalNotice(dataDir);
|
|
47
|
+
if (removed === undefined)
|
|
48
|
+
throw new Error(`not joined — no server.json in ${dataDir}`);
|
|
49
|
+
await removeServerConfig(dataDir);
|
|
50
|
+
return { origin: removed.origin, warnings: [], notes: ["the account had already removed this host"] };
|
|
51
|
+
}
|
|
44
52
|
const origin = config.origin;
|
|
45
53
|
const warnings = [];
|
|
46
54
|
const notes = [];
|
|
@@ -78,7 +86,10 @@ async function leaveServer(args, deps) {
|
|
|
78
86
|
await removeServerConfig(dataDir);
|
|
79
87
|
const manager = deps?.manager ?? serviceManager();
|
|
80
88
|
try {
|
|
81
|
-
const
|
|
89
|
+
const state = await manager.state().catch(() => {
|
|
90
|
+
return;
|
|
91
|
+
});
|
|
92
|
+
const installed = state?.installed === true;
|
|
82
93
|
const previous = installed ? await manager.read() : undefined;
|
|
83
94
|
if (!installed) {
|
|
84
95
|
notes.push("no service installed — restart any running host so it stops dialing the server");
|
|
@@ -87,19 +98,30 @@ async function leaveServer(args, deps) {
|
|
|
87
98
|
} else {
|
|
88
99
|
const stripped = previous === undefined ? undefined : { ...previous, hostArgs: withoutRelayArgs(previous.hostArgs) };
|
|
89
100
|
const rewriting = stripped !== undefined && stripped.hostArgs.length !== previous.hostArgs.length;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
argv: ["--data-dir", dataDir, ...port === undefined ? [] : ["--url", `ws://127.0.0.1:${port}`]],
|
|
94
|
-
manager,
|
|
95
|
-
...rewriting ? {
|
|
96
|
-
restart: async () => {
|
|
101
|
+
if (state.stopped) {
|
|
102
|
+
if (rewriting) {
|
|
103
|
+
try {
|
|
97
104
|
await manager.install(stripped);
|
|
105
|
+
} finally {
|
|
106
|
+
await manager.stop();
|
|
98
107
|
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
108
|
+
}
|
|
109
|
+
notes.push(`the service is stopped and stays off until \`${commandHint("service")} start\``);
|
|
110
|
+
} else {
|
|
111
|
+
const port = argValue(previous?.hostArgs ?? [], "--port");
|
|
112
|
+
const code = await restartService({
|
|
113
|
+
force: false,
|
|
114
|
+
argv: ["--data-dir", dataDir, ...port === undefined ? [] : ["--url", `ws://127.0.0.1:${port}`]],
|
|
115
|
+
manager,
|
|
116
|
+
...rewriting ? {
|
|
117
|
+
restart: async () => {
|
|
118
|
+
await manager.install(stripped);
|
|
119
|
+
}
|
|
120
|
+
} : {}
|
|
121
|
+
});
|
|
122
|
+
if (code !== 0)
|
|
123
|
+
warnings.push("the service restart did not finish — check `service status`");
|
|
124
|
+
}
|
|
103
125
|
}
|
|
104
126
|
} catch (error) {
|
|
105
127
|
warnings.push(`could not update the service definition: ${message(error)} — remove --relay from it and restart manually`);
|
|
@@ -15,6 +15,7 @@ var RELAY_CLOSE = {
|
|
|
15
15
|
handshakeTimeout: 4608,
|
|
16
16
|
replaced: 4609,
|
|
17
17
|
acceptTimeout: 4610,
|
|
18
|
+
hostRemoved: 4611,
|
|
18
19
|
tooManyConnections: 4629,
|
|
19
20
|
trafficLimit: 4630
|
|
20
21
|
};
|
|
@@ -467,6 +468,14 @@ function startRelayLink(options) {
|
|
|
467
468
|
}
|
|
468
469
|
if (stopped)
|
|
469
470
|
return;
|
|
471
|
+
if (code === RELAY_CLOSE.hostRemoved) {
|
|
472
|
+
stopped = true;
|
|
473
|
+
setState({
|
|
474
|
+
status: "removed",
|
|
475
|
+
reason: `relay ${origin}: the account removed this host (${hostId}); join it again with \`pinomad up\` (${code})`
|
|
476
|
+
});
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
470
479
|
const rejection = REJECTION_REASONS[code];
|
|
471
480
|
if (rejection !== undefined) {
|
|
472
481
|
const reason = `relay ${origin}: ${rejection(hostId)} (${code})`;
|
|
@@ -494,6 +503,8 @@ function startRelayLink(options) {
|
|
|
494
503
|
origin,
|
|
495
504
|
state: () => state,
|
|
496
505
|
sendPush(request) {
|
|
506
|
+
if (state.status === "removed")
|
|
507
|
+
return Promise.resolve({ result: "unavailable", reason: "the account removed this host" });
|
|
497
508
|
const socket = control;
|
|
498
509
|
if (stopped || socket === undefined || socket.readyState !== WebSocket.OPEN || state.status !== "registered") {
|
|
499
510
|
return Promise.resolve({ result: "retry", reason: "control disconnected" });
|