@join-pantheon/cli 0.29.0 → 0.29.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/dist/cli.js +1 -0
- package/dist/machine.js +13 -6
- package/dist/quickstart.js +6 -1
- package/dist/relay.js +54 -9
- package/dist/service.js +21 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -794,6 +794,7 @@ async function main() {
|
|
|
794
794
|
}
|
|
795
795
|
case "relay": {
|
|
796
796
|
process.on("unhandledRejection", (e) => { console.error(`! unhandled rejection: ${e?.stack ?? String(e)}`); });
|
|
797
|
+
process.on("uncaughtException", (e) => { console.error(`! uncaught exception (kept serving): ${e?.stack ?? String(e)}`); });
|
|
797
798
|
const relay = new Relay({
|
|
798
799
|
port: Number(flag("port", process.env.PORT ?? "8787")),
|
|
799
800
|
host: flag("host", "0.0.0.0"),
|
package/dist/machine.js
CHANGED
|
@@ -148,15 +148,22 @@ export function logFileFor(kind, proc) {
|
|
|
148
148
|
export function dataSummary(dir) {
|
|
149
149
|
if (!dir || !existsSync(dir))
|
|
150
150
|
return "no ledger yet";
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
let files = [];
|
|
152
|
+
try {
|
|
153
|
+
files = readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => join(dir, f));
|
|
154
|
+
}
|
|
155
|
+
catch { }
|
|
156
|
+
if (!files.length)
|
|
153
157
|
return `${dir} (empty)`;
|
|
154
158
|
let acts = 0;
|
|
155
|
-
for (const f of files
|
|
156
|
-
|
|
157
|
-
|
|
159
|
+
for (const f of files) {
|
|
160
|
+
try {
|
|
161
|
+
const t = readFileSync(f, "utf8");
|
|
162
|
+
acts += t.split("\n").filter((l) => l.trim()).length;
|
|
163
|
+
}
|
|
164
|
+
catch { }
|
|
158
165
|
}
|
|
159
|
-
const spaces = files.
|
|
166
|
+
const spaces = files.length;
|
|
160
167
|
return `${acts} acts across ${spaces} space${spaces > 1 ? "s" : ""}`;
|
|
161
168
|
}
|
|
162
169
|
const defaultStopIo = (port = 8787) => ({
|
package/dist/quickstart.js
CHANGED
|
@@ -102,7 +102,12 @@ export function wireUp(opts, deps = {}) {
|
|
|
102
102
|
}
|
|
103
103
|
else if (opts.unattended) {
|
|
104
104
|
const project = opts.unattended;
|
|
105
|
-
const isRepo =
|
|
105
|
+
const isRepo = (() => { try {
|
|
106
|
+
return execFileSync("git", ["-C", project, "rev-parse", "--is-inside-work-tree"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 10_000 }).trim() === "true";
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return false;
|
|
110
|
+
} })();
|
|
106
111
|
if (!isRepo && how === "explicit") {
|
|
107
112
|
tools.push({ tool: "worker", status: `not installed — ${project} is not a git repo` });
|
|
108
113
|
warnings.push(`--unattended needs a git repo to work in; pass --project <a clone>. Nothing was installed.`);
|
package/dist/relay.js
CHANGED
|
@@ -47,8 +47,14 @@ function cookieVal(req, name) {
|
|
|
47
47
|
return "";
|
|
48
48
|
for (const part of raw.split(";")) {
|
|
49
49
|
const eq = part.indexOf("=");
|
|
50
|
-
if (eq > -1 && part.slice(0, eq).trim() === name)
|
|
51
|
-
|
|
50
|
+
if (eq > -1 && part.slice(0, eq).trim() === name) {
|
|
51
|
+
try {
|
|
52
|
+
return decodeURIComponent(part.slice(eq + 1).trim());
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
52
58
|
}
|
|
53
59
|
return "";
|
|
54
60
|
}
|
|
@@ -162,7 +168,30 @@ export class Relay {
|
|
|
162
168
|
spaces = new Map();
|
|
163
169
|
dataDir;
|
|
164
170
|
log;
|
|
165
|
-
http = createServer((req, res) =>
|
|
171
|
+
http = createServer((req, res) => {
|
|
172
|
+
const fail = (e) => {
|
|
173
|
+
const bad = e instanceof TypeError || e instanceof URIError;
|
|
174
|
+
if (!bad)
|
|
175
|
+
this.log(`! http ${req.method} ${String(req.url ?? "").slice(0, 120)} failed: ${e?.message ?? e}`);
|
|
176
|
+
try {
|
|
177
|
+
if (!res.headersSent)
|
|
178
|
+
this.json(res, bad ? 400 : 500, { error: bad ? "bad request" : "the relay could not handle that request" });
|
|
179
|
+
else
|
|
180
|
+
res.end();
|
|
181
|
+
}
|
|
182
|
+
catch { }
|
|
183
|
+
};
|
|
184
|
+
try {
|
|
185
|
+
if (!URL.canParse(req.url ?? "/", "http://x"))
|
|
186
|
+
return fail(new TypeError("unparseable request target"));
|
|
187
|
+
const r = this.onHttp(req, res);
|
|
188
|
+
if (r && typeof r.catch === "function")
|
|
189
|
+
r.catch(fail);
|
|
190
|
+
}
|
|
191
|
+
catch (e) {
|
|
192
|
+
fail(e);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
166
195
|
wss = new WebSocketServer({ noServer: true, maxPayload: 256 * 1024 });
|
|
167
196
|
heartbeat;
|
|
168
197
|
compactedAt = new Map();
|
|
@@ -2179,7 +2208,7 @@ pantheon signup --email you@work.com --username you</code></pre>
|
|
|
2179
2208
|
catch { }
|
|
2180
2209
|
}
|
|
2181
2210
|
});
|
|
2182
|
-
|
|
2211
|
+
const closeWork = (code) => {
|
|
2183
2212
|
if (dbSession) {
|
|
2184
2213
|
this.accounts?.endSession(dbSession);
|
|
2185
2214
|
dbSession = null;
|
|
@@ -2211,7 +2240,13 @@ pantheon signup --email you@work.com --username you</code></pre>
|
|
|
2211
2240
|
this.pushObligations(space);
|
|
2212
2241
|
this.broadcast(space, { t: "roster", roster: this.roster(space) });
|
|
2213
2242
|
this.log(`[${space.name}] - ${me.id}/${sessionId}`);
|
|
2214
|
-
}
|
|
2243
|
+
};
|
|
2244
|
+
ws.on("close", (code) => { try {
|
|
2245
|
+
closeWork(code);
|
|
2246
|
+
}
|
|
2247
|
+
catch (e) {
|
|
2248
|
+
this.log(`! cleanup after a disconnect failed: ${e.message}`);
|
|
2249
|
+
} });
|
|
2215
2250
|
ws.on("error", () => { });
|
|
2216
2251
|
}
|
|
2217
2252
|
sweep() {
|
|
@@ -2229,11 +2264,21 @@ pantheon signup --email you@work.com --username you</code></pre>
|
|
|
2229
2264
|
}
|
|
2230
2265
|
const now = Date.now();
|
|
2231
2266
|
for (const sp of this.spaces.values()) {
|
|
2232
|
-
|
|
2233
|
-
this.
|
|
2234
|
-
|
|
2267
|
+
try {
|
|
2268
|
+
if (now - (this.compactedAt.get(sp.name) ?? 0) > LIMITS.ledgerCompactEveryMs)
|
|
2269
|
+
this.compactSpace(sp);
|
|
2270
|
+
this.lapseClaims(sp, now);
|
|
2271
|
+
}
|
|
2272
|
+
catch (e) {
|
|
2273
|
+
this.log(`! [${sp.name}] housekeeping failed: ${e.message}`);
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
try {
|
|
2277
|
+
this.evictSpaces();
|
|
2278
|
+
}
|
|
2279
|
+
catch (e) {
|
|
2280
|
+
this.log(`! evicting idle spaces failed: ${e.message}`);
|
|
2235
2281
|
}
|
|
2236
|
-
this.evictSpaces();
|
|
2237
2282
|
}
|
|
2238
2283
|
lapseClaims(sp, now) {
|
|
2239
2284
|
const live = new Set();
|
package/dist/service.js
CHANGED
|
@@ -8,9 +8,28 @@ import { launcher } from "./install.js";
|
|
|
8
8
|
import { CONFIG_DIR } from "./config.js";
|
|
9
9
|
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
10
10
|
const LABEL = (kind) => `dev.pantheon.${kind}`;
|
|
11
|
+
export function borrowedHome() {
|
|
12
|
+
if (process.env.PANTHEON_SERVICES_ANY_HOME === "1")
|
|
13
|
+
return false;
|
|
14
|
+
try {
|
|
15
|
+
return resolve(homedir()) !== resolve(userInfo().homedir);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const READ_ONLY = (c, a) => c === "launchctl" ? ["print", "print-disabled", "list"].includes(a[0] ?? "")
|
|
22
|
+
: c === "systemctl" ? a.some((x) => ["is-enabled", "is-active", "status", "show", "show-environment"].includes(x))
|
|
23
|
+
: true;
|
|
24
|
+
function guardedExecFile(c, a) {
|
|
25
|
+
if ((c === "launchctl" || c === "systemctl") && !READ_ONLY(c, a) && borrowedHome()) {
|
|
26
|
+
throw new Error(`refused: ${c} ${a[0] ?? ""} would change this user's real services, but HOME (${homedir()}) is not the user's home — a test or a sandbox. Set PANTHEON_SERVICES_ANY_HOME=1 to allow it.`);
|
|
27
|
+
}
|
|
28
|
+
return execFileSync(c, a, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }) ?? "";
|
|
29
|
+
}
|
|
11
30
|
export const realSys = () => ({
|
|
12
31
|
platform: platform(),
|
|
13
|
-
exec:
|
|
32
|
+
exec: guardedExecFile,
|
|
14
33
|
uid: process.getuid?.() ?? 501,
|
|
15
34
|
user: (() => { try {
|
|
16
35
|
return userInfo().username;
|
|
@@ -239,7 +258,7 @@ export function disableService(kind, sys = realSys()) {
|
|
|
239
258
|
}
|
|
240
259
|
sys.exec("systemctl", ["--user", "disable", "--now", `pantheon-${kind}`]);
|
|
241
260
|
}
|
|
242
|
-
const realExec =
|
|
261
|
+
const realExec = guardedExecFile;
|
|
243
262
|
function outOf(exec, c, a) {
|
|
244
263
|
try {
|
|
245
264
|
return { out: String(exec(c, a) ?? ""), ok: true };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@join-pantheon/cli",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.1",
|
|
4
4
|
"description": "Let your coding agent collaborate directly with your teammates' agents — ask, agree an interface, and deliver, without a human relaying messages.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|