@persistmemory/cli 0.8.1 → 0.9.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/bin.js +235 -75
- package/dist/bin.js.map +4 -4
- package/dist/index.js +235 -75
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1469,7 +1469,8 @@ function pathsFor(home = homedir()) {
|
|
|
1469
1469
|
return {
|
|
1470
1470
|
dir,
|
|
1471
1471
|
config: join(dir, "config.json"),
|
|
1472
|
-
credentials: join(dir, "credentials.json")
|
|
1472
|
+
credentials: join(dir, "credentials.json"),
|
|
1473
|
+
agentPid: join(dir, "agent.pid")
|
|
1473
1474
|
};
|
|
1474
1475
|
}
|
|
1475
1476
|
function readConfig(paths) {
|
|
@@ -1709,7 +1710,7 @@ function shortDate(iso) {
|
|
|
1709
1710
|
}
|
|
1710
1711
|
|
|
1711
1712
|
// src/help.ts
|
|
1712
|
-
var VERSION = true ? "0.
|
|
1713
|
+
var VERSION = true ? "0.9.1" : versionFromManifest();
|
|
1713
1714
|
var PACKAGE = "@persistmemory/cli";
|
|
1714
1715
|
var HELP = `
|
|
1715
1716
|
pm \u2014 PersistMemory from your terminal
|
|
@@ -1778,9 +1779,10 @@ var HELP = `
|
|
|
1778
1779
|
approve every request first, and see the
|
|
1779
1780
|
exact path or command before you do.
|
|
1780
1781
|
Stays in the foreground; background it
|
|
1781
|
-
|
|
1782
|
+
with:
|
|
1782
1783
|
nohup pm agent </dev/null >> ~/agent.log 2>&1 &
|
|
1783
|
-
|
|
1784
|
+
agent stop stop the agent running on this machine.
|
|
1785
|
+
Lets the request in flight finish first.
|
|
1784
1786
|
agent --root <dir> [--root ...] narrow it to these folders, for this run
|
|
1785
1787
|
agent enable [hostname] turn this machine back on after it was
|
|
1786
1788
|
switched off for going quiet. Asks you to
|
|
@@ -2513,7 +2515,8 @@ async function readStdin() {
|
|
|
2513
2515
|
import { hostname } from "node:os";
|
|
2514
2516
|
import { homedir as homedir2 } from "node:os";
|
|
2515
2517
|
import { basename, join as join6, resolve as resolve3 } from "node:path";
|
|
2516
|
-
import { existsSync as existsSync5, readFileSync as readFileSync6, readdirSync, statSync as statSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2518
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6, readdirSync, statSync as statSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2519
|
+
import { execFileSync } from "node:child_process";
|
|
2517
2520
|
|
|
2518
2521
|
// src/files.ts
|
|
2519
2522
|
import { existsSync as existsSync3, readFileSync as readFileSync4, realpathSync, statSync as statSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
@@ -7287,8 +7290,8 @@ var ENDS = /,|\s+(?:and|then|so|but|because|which|where|that|with|to)\s+/i;
|
|
|
7287
7290
|
var ASKS_FOR_A_SPACE = /\b(?:make|create|start|open|add|set\s?up|setup|spin\s?up|new)\b[^.;!?]{0,40}?\b(?:work[\s-]?)?spaces?\b/gi;
|
|
7288
7291
|
var NAMED_A_SPACE = /\b(?:make|create|start|open|add|set\s?up|setup|spin\s?up|new)\b(?:\s+(?:me|us))?(?:\s+(?:a|an|the|my|our|another|one|new|second|separate|empty))*\s+(?:work[\s-]?)?spaces?\b(?:\s*(?:called|named|titled|for|about))?\s*(?:[:\-–—]\s*)?([^.;!?]*)/gi;
|
|
7289
7292
|
var SPACE_NAMED_FIRST = /\b(?:make|create|start|open|add|set\s?up|setup|spin\s?up|new)\b(?:\s+(?:a|an|the|my|our|another|one|new|second|separate|empty))*\s+((?:[\p{L}\p{N}][\p{L}\p{N}&'.-]*\s+){0,2}[\p{L}\p{N}][\p{L}\p{N}&'.-]*)\s+(?:work\s?)?spaces?\b/giu;
|
|
7290
|
-
function flatten(
|
|
7291
|
-
return
|
|
7293
|
+
function flatten(said3) {
|
|
7294
|
+
return said3.normalize("NFKC").replace(/[‘’‚‛′]/g, "'").replace(/[“”„‟″«»]/g, '"').replace(/\s+/g, " ").trim();
|
|
7292
7295
|
}
|
|
7293
7296
|
function tidy(name) {
|
|
7294
7297
|
return name.replace(/^["']+|["']+$/g, "").replace(/[.,;:!?]+$/g, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
@@ -7319,9 +7322,9 @@ function readings(tail) {
|
|
|
7319
7322
|
}
|
|
7320
7323
|
return found;
|
|
7321
7324
|
}
|
|
7322
|
-
function spacesTheyAskedToMake(
|
|
7323
|
-
if (typeof
|
|
7324
|
-
const text = flatten(
|
|
7325
|
+
function spacesTheyAskedToMake(said3) {
|
|
7326
|
+
if (typeof said3 !== "string") return [];
|
|
7327
|
+
const text = flatten(said3);
|
|
7325
7328
|
if (text === "" || text.length > AT_MOST) return [];
|
|
7326
7329
|
if ([...text.matchAll(ASKS_FOR_A_SPACE)].length !== 1) return [];
|
|
7327
7330
|
const named = [...text.matchAll(NAMED_A_SPACE)];
|
|
@@ -7334,11 +7337,40 @@ function spacesTheyAskedToMake(said2) {
|
|
|
7334
7337
|
const only = tidy((first[0]?.[1] ?? "").replace(LEADING, ""));
|
|
7335
7338
|
return nameable(only) ? [only] : [];
|
|
7336
7339
|
}
|
|
7337
|
-
function askedToMakeIt(
|
|
7338
|
-
if (typeof
|
|
7340
|
+
function askedToMakeIt(said3, name) {
|
|
7341
|
+
if (typeof said3 !== "string" || typeof name !== "string") return false;
|
|
7339
7342
|
const wanted2 = tidy(name);
|
|
7340
7343
|
if (!nameable(wanted2)) return false;
|
|
7341
|
-
return spacesTheyAskedToMake(
|
|
7344
|
+
return spacesTheyAskedToMake(said3).includes(wanted2);
|
|
7345
|
+
}
|
|
7346
|
+
var ASKS_TO_WORK_IN = /\b(?:work(?:ing)?\s+in|select|choose|pick|switch\s+to|move\s+to|use)\b[^.;!?]{0,40}?\bspaces?\b|\bwork(?:ing)?\s+in\b/gi;
|
|
7347
|
+
var WORK_IN_NAMED = /\b(?:select|choose|pick|switch\s+to|move\s+to|use|work(?:ing)?\s+in)\s+(?:the\s+|my\s+|our\s+)?spaces?\s+(?:called\s+|named\s+)?(.{1,200})$/i;
|
|
7348
|
+
var WORK_IN_FIRST = /\b(?:select|choose|pick|switch\s+to|move\s+to|use|work(?:ing)?\s+in)\s+(?:the\s+|my\s+|our\s+)?(.{1,200}?)\s+spaces?\b/i;
|
|
7349
|
+
var WORK_IN_BARE = /\bwork(?:ing)?\s+in\s+(?:the\s+|my\s+|our\s+)?(.{1,200})$/i;
|
|
7350
|
+
function spacesTheyAskedToWorkIn(said3) {
|
|
7351
|
+
if (typeof said3 !== "string") return [];
|
|
7352
|
+
const text = flatten(said3);
|
|
7353
|
+
if (text === "" || text.length > AT_MOST) return [];
|
|
7354
|
+
if ([...text.matchAll(ASKS_TO_WORK_IN)].length !== 1) return [];
|
|
7355
|
+
const first = WORK_IN_FIRST.exec(text);
|
|
7356
|
+
if (first) {
|
|
7357
|
+
const only = tidy((first[1] ?? "").replace(LEADING, ""));
|
|
7358
|
+
return nameable(only) ? [only] : [];
|
|
7359
|
+
}
|
|
7360
|
+
const named = WORK_IN_NAMED.exec(text);
|
|
7361
|
+
if (named) {
|
|
7362
|
+
const found = readings(named[1] ?? "");
|
|
7363
|
+
if (found.length > 0) return found;
|
|
7364
|
+
}
|
|
7365
|
+
const bare = WORK_IN_BARE.exec(text);
|
|
7366
|
+
if (bare) return readings(bare[1] ?? "");
|
|
7367
|
+
return [];
|
|
7368
|
+
}
|
|
7369
|
+
function askedToWorkIn(said3, name) {
|
|
7370
|
+
if (typeof said3 !== "string" || typeof name !== "string") return false;
|
|
7371
|
+
const wanted2 = tidy(name);
|
|
7372
|
+
if (!nameable(wanted2)) return false;
|
|
7373
|
+
return spacesTheyAskedToWorkIn(said3).includes(wanted2);
|
|
7342
7374
|
}
|
|
7343
7375
|
|
|
7344
7376
|
// ../tools/src/catalogue.ts
|
|
@@ -7790,10 +7822,10 @@ var TOOLS = [
|
|
|
7790
7822
|
},
|
|
7791
7823
|
...context.origin?.askedBy ? { confirmedBy: context.origin.askedBy } : {}
|
|
7792
7824
|
});
|
|
7793
|
-
const
|
|
7825
|
+
const said3 = `Forwarded to ${String(args.to)}. It went whole, with everything that was attached to it.`;
|
|
7794
7826
|
return {
|
|
7795
|
-
model: `${
|
|
7796
|
-
person: `${
|
|
7827
|
+
model: `${said3} Report what happened and stop. Do not forward anything else and do not offer to \u2014 another message, or another address, is a new decision that is theirs to make.`,
|
|
7828
|
+
person: `${said3} There is no unsending it \u2014 if it went to the wrong address, tell them.`
|
|
7797
7829
|
};
|
|
7798
7830
|
}
|
|
7799
7831
|
},
|
|
@@ -8237,15 +8269,15 @@ var TOOLS = [
|
|
|
8237
8269
|
a wider search that still finds the file beats a refusal.
|
|
8238
8270
|
*/
|
|
8239
8271
|
argsFrom: (rest) => {
|
|
8240
|
-
const
|
|
8241
|
-
if (!
|
|
8242
|
-
const at =
|
|
8272
|
+
const said3 = rest.trim();
|
|
8273
|
+
if (!said3) return void 0;
|
|
8274
|
+
const at = said3.lastIndexOf(" in ");
|
|
8243
8275
|
if (at > 0) {
|
|
8244
|
-
const what =
|
|
8245
|
-
const where =
|
|
8276
|
+
const what = said3.slice(0, at).trim();
|
|
8277
|
+
const where = said3.slice(at + 4).trim();
|
|
8246
8278
|
if (what && where && !where.includes(" ")) return { what, in: where };
|
|
8247
8279
|
}
|
|
8248
|
-
return { what:
|
|
8280
|
+
return { what: said3 };
|
|
8249
8281
|
},
|
|
8250
8282
|
missing: "Say what to look for, like: /find deployment notes in ~/Documents"
|
|
8251
8283
|
},
|
|
@@ -9112,10 +9144,10 @@ var TOOLS = [
|
|
|
9112
9144
|
...context.origin?.askedBy ? { askedBy: context.origin.askedBy } : {}
|
|
9113
9145
|
});
|
|
9114
9146
|
if (!offered.ok) return offered.error;
|
|
9115
|
-
const
|
|
9147
|
+
const said3 = `Invited ${offered.value.email} to \u201C${held.space.name}\u201D as ${offered.value.role}. They cannot see anything yet \u2014 the invitation has to be accepted first.`;
|
|
9116
9148
|
return {
|
|
9117
|
-
model: `${
|
|
9118
|
-
person: `${
|
|
9149
|
+
model: `${said3} That grant covers EVERYTHING in that Space, now and later. Report what happened and stop. Do not share anything else, and do not offer to \u2014 another Space, another address, or a wider role is a new decision that is theirs to make.`,
|
|
9150
|
+
person: `${said3} While it stands they will see everything in \u201C${held.space.name}\u201D, including memories filed into it after today. Undo it with /unshare ${held.space.name} ${offered.value.email}`
|
|
9119
9151
|
};
|
|
9120
9152
|
}
|
|
9121
9153
|
},
|
|
@@ -9260,8 +9292,8 @@ var TOOLS = [
|
|
|
9260
9292
|
...context.origin?.askedBy ? { askedBy: context.origin.askedBy } : {}
|
|
9261
9293
|
});
|
|
9262
9294
|
if (!changed.ok) return changed.error;
|
|
9263
|
-
const
|
|
9264
|
-
return changed.value.role === "owner" ? `${
|
|
9295
|
+
const said3 = `${changed.value.email} is now ${changed.value.role} on \u201C${held.space.name}\u201D.`;
|
|
9296
|
+
return changed.value.role === "owner" ? `${said3} As an owner they can share it onward and revoke anybody, including you.` : said3;
|
|
9265
9297
|
}
|
|
9266
9298
|
},
|
|
9267
9299
|
/* --------------------- which Space this is filing into --------------------- */
|
|
@@ -9437,12 +9469,14 @@ The other Spaces on this account: ${others.join(", ")}. They can move this conve
|
|
|
9437
9469
|
};
|
|
9438
9470
|
}
|
|
9439
9471
|
const to = found[0];
|
|
9472
|
+
const readable = look.spaces.shared ? await look.spaces.shared(look.userId, to.id).then((one) => one.ok ? one.value : true).catch(() => true) : true;
|
|
9440
9473
|
return {
|
|
9441
9474
|
ok: true,
|
|
9442
9475
|
facts: {
|
|
9443
9476
|
from,
|
|
9444
9477
|
to: to.name,
|
|
9445
9478
|
toId: to.id,
|
|
9479
|
+
shared: readable,
|
|
9446
9480
|
where: here.value.where,
|
|
9447
9481
|
unchanged: to.id === (here.value.chosen?.id ?? ""),
|
|
9448
9482
|
leaving: Boolean(here.value.chosen)
|
|
@@ -9515,11 +9549,13 @@ RIGHT NOW: ${where} is filing into ${from}.`;
|
|
|
9515
9549
|
Both conditions, and every unclear case is false. See
|
|
9516
9550
|
`their-own-words.ts` for what this does not cover.
|
|
9517
9551
|
*/
|
|
9518
|
-
directly(args, facts,
|
|
9519
|
-
if (facts["creating"] !== true) return false;
|
|
9552
|
+
directly(args, facts, said3, alsoRead) {
|
|
9520
9553
|
const wanted2 = oneLine2(args.space, 200);
|
|
9521
9554
|
if (wanted2 === void 0) return false;
|
|
9522
|
-
return askedToMakeIt(
|
|
9555
|
+
if (facts["creating"] === true) return askedToMakeIt(said3, wanted2);
|
|
9556
|
+
if (facts["shared"] !== false) return false;
|
|
9557
|
+
const named = (alsoRead ?? []).map((one) => one.trim().toLowerCase());
|
|
9558
|
+
return askedToWorkIn(said3, wanted2) || named.includes(wanted2.trim().toLowerCase());
|
|
9523
9559
|
}
|
|
9524
9560
|
},
|
|
9525
9561
|
description: "Files everything sent from this conversation into one Space from now on, and answers questions asked here from that Space alone. NOTHING IS KEPT AT ALL until this is done: there is no default Space and nothing picks one, so a conversation with no Space chosen refuses every note. Use it when the person says something like 'work in Acme', 'put all of this in my Work space', or when they have been told nothing is being kept and they name where it should go. Pass no Space to stop working in one, which means nothing sent here is kept until they choose again. Set `create` ONLY when they are asking for a Space that does not exist yet \u2014 usually because the account has none at all \u2014 and never to resolve a name you are unsure of. WHAT HAPPENS WHEN YOU CALL THIS DEPENDS ON THE ACT, AND THE ANSWER SAYS WHICH: moving into a Space that already exists is put in front of the person and they have to release it themselves, so say it is waiting for them and stop; making a NEW one, when they asked for it in their own message, simply happens, and the answer says so. Read what comes back and repeat what it says. Never say something is done unless it says so, never say something is waiting when it says it is done, and never invent a confirmation code.",
|
|
@@ -9939,11 +9975,31 @@ function avoided(path) {
|
|
|
9939
9975
|
return path.split(sep).some((segment) => segment.startsWith(".") || NEVER2.has(segment));
|
|
9940
9976
|
}
|
|
9941
9977
|
|
|
9942
|
-
// src/
|
|
9978
|
+
// src/said.ts
|
|
9943
9979
|
async function said(response, fallback) {
|
|
9944
|
-
const
|
|
9945
|
-
|
|
9946
|
-
|
|
9980
|
+
const raw = await response.text().catch(() => "");
|
|
9981
|
+
return reasonFrom(raw, response.status, fallback);
|
|
9982
|
+
}
|
|
9983
|
+
function reasonFrom(raw, status2, fallback) {
|
|
9984
|
+
let body;
|
|
9985
|
+
try {
|
|
9986
|
+
body = JSON.parse(raw);
|
|
9987
|
+
} catch {
|
|
9988
|
+
body = void 0;
|
|
9989
|
+
}
|
|
9990
|
+
if (typeof body?.error === "string") {
|
|
9991
|
+
const description = body.error_description?.trim();
|
|
9992
|
+
return description ? `${description} (${body.error})` : `${body.error} (${status2})`;
|
|
9993
|
+
}
|
|
9994
|
+
const message2 = typeof body?.error === "object" ? body.error?.message?.trim() : void 0;
|
|
9995
|
+
if (message2) return message2;
|
|
9996
|
+
const excerpt = raw.trim().replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").slice(0, 160);
|
|
9997
|
+
return excerpt ? `${fallback} (${status2}): ${excerpt}` : `${fallback} (${status2})`;
|
|
9998
|
+
}
|
|
9999
|
+
|
|
10000
|
+
// src/commands/agent.ts
|
|
10001
|
+
async function said2(response, fallback) {
|
|
10002
|
+
return said(response, fallback);
|
|
9947
10003
|
}
|
|
9948
10004
|
function locate(roots, requested) {
|
|
9949
10005
|
const expanded = requested.startsWith("~") ? resolve3(homedir2(), requested.slice(1).replace(/^[/\\]/, "")) : requested;
|
|
@@ -10018,7 +10074,7 @@ async function answer(context, apiUrl, token, roots, request) {
|
|
|
10018
10074
|
try {
|
|
10019
10075
|
fetched = await fetch(request.sourceUrl);
|
|
10020
10076
|
if (!fetched.ok) {
|
|
10021
|
-
return { ok: false, error: await
|
|
10077
|
+
return { ok: false, error: await said2(fetched, "The file could not be fetched") };
|
|
10022
10078
|
}
|
|
10023
10079
|
downloaded = Buffer.from(await fetched.arrayBuffer());
|
|
10024
10080
|
} catch {
|
|
@@ -10087,7 +10143,7 @@ async function upload(apiUrl, token, filename, bytes) {
|
|
|
10087
10143
|
})
|
|
10088
10144
|
});
|
|
10089
10145
|
if (!grant.ok) {
|
|
10090
|
-
return { ok: false, error: await
|
|
10146
|
+
return { ok: false, error: await said2(grant, "could not get an upload url") };
|
|
10091
10147
|
}
|
|
10092
10148
|
const { uploadUrl, contentType, maxBytes } = await grant.json();
|
|
10093
10149
|
const limit = maxBytes ?? MAX_TRANSFER_BYTES;
|
|
@@ -10143,7 +10199,7 @@ async function enableCommand(context) {
|
|
|
10143
10199
|
headers: { authorization: `Bearer ${await token()}` }
|
|
10144
10200
|
});
|
|
10145
10201
|
if (!listed.ok) {
|
|
10146
|
-
context.error(await
|
|
10202
|
+
context.error(await said2(listed, "could not read this account's machines"));
|
|
10147
10203
|
return 1;
|
|
10148
10204
|
}
|
|
10149
10205
|
const { items } = await listed.json();
|
|
@@ -10186,7 +10242,7 @@ Run this on ${machine.hostname} itself, or turn it back on from your dashboard.`
|
|
|
10186
10242
|
{ method: "POST", headers: { authorization: `Bearer ${await token()}` } }
|
|
10187
10243
|
);
|
|
10188
10244
|
if (!enabled.ok) {
|
|
10189
|
-
context.error(await
|
|
10245
|
+
context.error(await said2(enabled, "could not turn that machine back on"));
|
|
10190
10246
|
return 1;
|
|
10191
10247
|
}
|
|
10192
10248
|
const result = await enabled.json();
|
|
@@ -10199,8 +10255,94 @@ Run this on ${machine.hostname} itself, or turn it back on from your dashboard.`
|
|
|
10199
10255
|
);
|
|
10200
10256
|
return 0;
|
|
10201
10257
|
}
|
|
10258
|
+
function agentIsRunningAs(pid) {
|
|
10259
|
+
try {
|
|
10260
|
+
process.kill(pid, 0);
|
|
10261
|
+
} catch {
|
|
10262
|
+
return false;
|
|
10263
|
+
}
|
|
10264
|
+
if (process.platform === "win32") return void 0;
|
|
10265
|
+
try {
|
|
10266
|
+
const line2 = execFileSync("ps", ["-p", String(pid), "-o", "command="], {
|
|
10267
|
+
encoding: "utf8",
|
|
10268
|
+
timeout: 5e3
|
|
10269
|
+
});
|
|
10270
|
+
return /\bpm\b|persistmemory|\bagent\b/i.test(line2);
|
|
10271
|
+
} catch {
|
|
10272
|
+
return void 0;
|
|
10273
|
+
}
|
|
10274
|
+
}
|
|
10275
|
+
function processState(pid) {
|
|
10276
|
+
if (process.platform === "win32") return void 0;
|
|
10277
|
+
try {
|
|
10278
|
+
return execFileSync("ps", ["-p", String(pid), "-o", "stat="], {
|
|
10279
|
+
encoding: "utf8",
|
|
10280
|
+
timeout: 5e3
|
|
10281
|
+
}).trim();
|
|
10282
|
+
} catch {
|
|
10283
|
+
return void 0;
|
|
10284
|
+
}
|
|
10285
|
+
}
|
|
10286
|
+
async function stopCommand(context) {
|
|
10287
|
+
const file = context.paths.agentPid;
|
|
10288
|
+
if (!existsSync5(file)) {
|
|
10289
|
+
context.print("No agent is running on this machine.");
|
|
10290
|
+
context.print('If one is running from another home directory, stop it with: pkill -f "pm agent"');
|
|
10291
|
+
return 0;
|
|
10292
|
+
}
|
|
10293
|
+
const pid = Number.parseInt(readFileSync6(file, "utf8").trim(), 10);
|
|
10294
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
10295
|
+
unlinkSync2(file);
|
|
10296
|
+
context.error("The agent's pid file was unreadable, so it has been removed.");
|
|
10297
|
+
context.error('If an agent is still running, stop it with: pkill -f "pm agent"');
|
|
10298
|
+
return 1;
|
|
10299
|
+
}
|
|
10300
|
+
const mine = agentIsRunningAs(pid);
|
|
10301
|
+
if (mine === false) {
|
|
10302
|
+
unlinkSync2(file);
|
|
10303
|
+
context.print("No agent is running. Cleared a stale pid file.");
|
|
10304
|
+
return 0;
|
|
10305
|
+
}
|
|
10306
|
+
if (mine === void 0) {
|
|
10307
|
+
context.error(`Found a pid file naming process ${pid}, but could not confirm it is the agent.`);
|
|
10308
|
+
context.error("Refusing to signal a process this command cannot identify.");
|
|
10309
|
+
context.error(`If that is the agent, stop it with: kill ${pid}`);
|
|
10310
|
+
return 1;
|
|
10311
|
+
}
|
|
10312
|
+
try {
|
|
10313
|
+
process.kill(pid, "SIGCONT");
|
|
10314
|
+
} catch {
|
|
10315
|
+
}
|
|
10316
|
+
process.kill(pid, "SIGTERM");
|
|
10317
|
+
context.print(`Asked the agent (${pid}) to stop. Any request in flight will finish first.`);
|
|
10318
|
+
for (let waited = 0; waited < 1e4; waited += 250) {
|
|
10319
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
10320
|
+
if (agentIsRunningAs(pid) === false) {
|
|
10321
|
+
if (existsSync5(file)) unlinkSync2(file);
|
|
10322
|
+
context.print("Stopped.");
|
|
10323
|
+
return 0;
|
|
10324
|
+
}
|
|
10325
|
+
}
|
|
10326
|
+
if (processState(pid)?.startsWith("T")) {
|
|
10327
|
+
context.error(`The agent (${pid}) is suspended and did not respond to being resumed.`);
|
|
10328
|
+
context.error(`Stop it with: kill -CONT ${pid} && kill -9 ${pid}`);
|
|
10329
|
+
return 1;
|
|
10330
|
+
}
|
|
10331
|
+
context.print("Still finishing a request. It will exit on its own; nothing more to do.");
|
|
10332
|
+
return 0;
|
|
10333
|
+
}
|
|
10202
10334
|
async function agentCommand(context) {
|
|
10203
10335
|
if (context.args.words[1] === "enable") return enableCommand(context);
|
|
10336
|
+
if (context.args.words[1] === "stop") return stopCommand(context);
|
|
10337
|
+
const word = context.args.words[1];
|
|
10338
|
+
if (word !== void 0) {
|
|
10339
|
+
context.error(`pm agent: there is no "${word}" subcommand.`);
|
|
10340
|
+
context.error("");
|
|
10341
|
+
context.error(" pm agent start answering requests here");
|
|
10342
|
+
context.error(" pm agent stop stop the agent running here");
|
|
10343
|
+
context.error(" pm agent enable [host] turn a machine back on after it was switched off");
|
|
10344
|
+
return 2;
|
|
10345
|
+
}
|
|
10204
10346
|
const write3 = (message2, error = false) => {
|
|
10205
10347
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
10206
10348
|
for (const line2 of message2.split("\n")) {
|
|
@@ -10241,7 +10383,20 @@ async function agentCommand(context) {
|
|
|
10241
10383
|
const every = Math.max(2, asked ?? 5) * 1e3;
|
|
10242
10384
|
write3(`Answering as ${name}, from: ${roots.join(", ")}`);
|
|
10243
10385
|
write3("Nothing outside those folders can be read. Ctrl-C to stop.");
|
|
10386
|
+
writeFileSync5(context.paths.agentPid, `${process.pid}
|
|
10387
|
+
`, { mode: 384 });
|
|
10388
|
+
const forgetPid = () => {
|
|
10389
|
+
try {
|
|
10390
|
+
if (existsSync5(context.paths.agentPid)) {
|
|
10391
|
+
const held = Number.parseInt(readFileSync6(context.paths.agentPid, "utf8").trim(), 10);
|
|
10392
|
+
if (held === process.pid) unlinkSync2(context.paths.agentPid);
|
|
10393
|
+
}
|
|
10394
|
+
} catch {
|
|
10395
|
+
}
|
|
10396
|
+
};
|
|
10397
|
+
process.on("exit", forgetPid);
|
|
10244
10398
|
let running = true;
|
|
10399
|
+
let signedOut = false;
|
|
10245
10400
|
const stop = () => {
|
|
10246
10401
|
if (!running) return;
|
|
10247
10402
|
running = false;
|
|
@@ -10281,7 +10436,7 @@ async function agentCommand(context) {
|
|
|
10281
10436
|
version: VERSION
|
|
10282
10437
|
});
|
|
10283
10438
|
if (!beat.ok) {
|
|
10284
|
-
complain2(await
|
|
10439
|
+
complain2(await said2(beat, "the service refused this machine"));
|
|
10285
10440
|
} else {
|
|
10286
10441
|
const state = await beat.json();
|
|
10287
10442
|
if (state.status === "disabled") {
|
|
@@ -10298,7 +10453,7 @@ async function agentCommand(context) {
|
|
|
10298
10453
|
}
|
|
10299
10454
|
const claimed = await call("claim", { hostname: name, limit: 5 });
|
|
10300
10455
|
if (!claimed.ok) {
|
|
10301
|
-
complain2(await
|
|
10456
|
+
complain2(await said2(claimed, "could not pick up work"));
|
|
10302
10457
|
} else {
|
|
10303
10458
|
working();
|
|
10304
10459
|
const { items } = await claimed.json();
|
|
@@ -10312,18 +10467,28 @@ async function agentCommand(context) {
|
|
|
10312
10467
|
outcome.ok ? { result: { attachToken: outcome.attachToken } } : { error: outcome.error }
|
|
10313
10468
|
);
|
|
10314
10469
|
if (!done.ok) {
|
|
10315
|
-
write3(`Stored, but the service did not record it: ${await
|
|
10470
|
+
write3(`Stored, but the service did not record it: ${await said2(done, "refused")}`, true);
|
|
10316
10471
|
continue;
|
|
10317
10472
|
}
|
|
10318
10473
|
write3(outcome.ok ? `Sent ${outcome.bytes} bytes` : `Refused: ${outcome.error}`);
|
|
10319
10474
|
}
|
|
10320
10475
|
}
|
|
10321
10476
|
} catch (error) {
|
|
10322
|
-
|
|
10477
|
+
const message2 = error instanceof Error ? error.message : "connection failed";
|
|
10478
|
+
if (/invalid_grant/i.test(message2)) {
|
|
10479
|
+
write3(`This machine is signed out: ${message2}`, true);
|
|
10480
|
+
write3("Sign in again with: pm auth login", true);
|
|
10481
|
+
write3("The agent is stopping \u2014 retrying cannot renew a revoked token.", true);
|
|
10482
|
+
running = false;
|
|
10483
|
+
signedOut = true;
|
|
10484
|
+
continue;
|
|
10485
|
+
}
|
|
10486
|
+
complain2(message2);
|
|
10323
10487
|
}
|
|
10324
10488
|
if (running) await new Promise((r) => setTimeout(r, every));
|
|
10325
10489
|
}
|
|
10326
|
-
|
|
10490
|
+
forgetPid();
|
|
10491
|
+
return signedOut ? 1 : 0;
|
|
10327
10492
|
}
|
|
10328
10493
|
|
|
10329
10494
|
// src/commands/google.ts
|
|
@@ -10346,8 +10511,7 @@ async function callApi(context, path, init = {}) {
|
|
|
10346
10511
|
});
|
|
10347
10512
|
}
|
|
10348
10513
|
async function complain(context, response) {
|
|
10349
|
-
|
|
10350
|
-
context.error(body?.error?.message ?? `That failed (${response.status}).`);
|
|
10514
|
+
context.error(await said(response, "That failed"));
|
|
10351
10515
|
return response.status === 409 ? 3 : 1;
|
|
10352
10516
|
}
|
|
10353
10517
|
async function driveCommand(context) {
|
|
@@ -10548,8 +10712,7 @@ async function collectCommand(context) {
|
|
|
10548
10712
|
{ headers: { authorization: `Bearer ${credential.token}` } }
|
|
10549
10713
|
);
|
|
10550
10714
|
if (!link.ok) {
|
|
10551
|
-
|
|
10552
|
-
context.error(body?.error?.message ?? `Could not prepare that file (${link.status}).`);
|
|
10715
|
+
context.error(await said(link, "Could not prepare that file"));
|
|
10553
10716
|
return 1;
|
|
10554
10717
|
}
|
|
10555
10718
|
const { downloadUrl, filename } = await link.json();
|
|
@@ -10644,8 +10807,7 @@ async function setupCommand(context) {
|
|
|
10644
10807
|
}
|
|
10645
10808
|
async function chooseSpace(context, client, spaces) {
|
|
10646
10809
|
const { print, error } = context;
|
|
10647
|
-
const
|
|
10648
|
-
const current = found?.config.space;
|
|
10810
|
+
const current = findWorkspace()?.config.space;
|
|
10649
10811
|
const named = stringFlag(context.args, "space");
|
|
10650
10812
|
const creating = stringFlag(context.args, "new-space");
|
|
10651
10813
|
if (creating !== void 0) {
|
|
@@ -10667,48 +10829,46 @@ async function chooseSpace(context, client, spaces) {
|
|
|
10667
10829
|
print(` Pass --space "<name>" or --new-space "<name>" to pick one.`);
|
|
10668
10830
|
return 0;
|
|
10669
10831
|
}
|
|
10670
|
-
|
|
10671
|
-
|
|
10832
|
+
const live = current ? spaces.find((one) => one.id === current.id) : void 0;
|
|
10833
|
+
if (current && !live) {
|
|
10834
|
+
print(` This folder points at a Space called "${current.name}", which is no longer`);
|
|
10835
|
+
print(" in your account. Choose another one.");
|
|
10836
|
+
print("");
|
|
10837
|
+
} else if (live) {
|
|
10838
|
+
print(` This folder currently uses the Space "${live.name}".`);
|
|
10839
|
+
print("");
|
|
10840
|
+
}
|
|
10841
|
+
if (spaces.length === 0) {
|
|
10842
|
+
print(" You have no Spaces yet. Every memory is filed in one \u2014 a capture with");
|
|
10843
|
+
print(" no Space is refused, and names the Spaces you have \u2014 so this makes the first.");
|
|
10672
10844
|
print("");
|
|
10845
|
+
return finish(context, await askForNewSpace(context, client), current);
|
|
10673
10846
|
}
|
|
10674
10847
|
print(" Which Space should this folder use?");
|
|
10675
10848
|
print("");
|
|
10676
10849
|
spaces.forEach((space, index) => {
|
|
10677
|
-
const mark =
|
|
10850
|
+
const mark = live?.id === space.id ? " (current)" : "";
|
|
10678
10851
|
const count = space.memoryCount === void 0 ? "" : `, ${space.memoryCount} memories`;
|
|
10679
10852
|
print(` ${index + 1}. ${space.name}${mark} ${space.kind}${count}`);
|
|
10680
10853
|
});
|
|
10681
10854
|
const createIndex = spaces.length + 1;
|
|
10682
|
-
const noneIndex = spaces.length + 2;
|
|
10683
10855
|
print(` ${createIndex}. Create a new Space`);
|
|
10684
|
-
print(` ${noneIndex}. No Space \u2014 use everything in my account`);
|
|
10685
10856
|
print("");
|
|
10686
|
-
const fallback =
|
|
10687
|
-
const answer3 = await context.ask(` Choose 1-${
|
|
10857
|
+
const fallback = live ? "keep the current one" : String(createIndex);
|
|
10858
|
+
const answer3 = await context.ask(` Choose 1-${createIndex} [${fallback}]: `);
|
|
10688
10859
|
if (answer3 === "") {
|
|
10689
|
-
if (
|
|
10860
|
+
if (live) {
|
|
10690
10861
|
print("");
|
|
10691
|
-
print(` Keeping "${
|
|
10862
|
+
print(` Keeping "${live.name}".`);
|
|
10692
10863
|
return 0;
|
|
10693
10864
|
}
|
|
10694
10865
|
return finish(context, await askForNewSpace(context, client), current);
|
|
10695
10866
|
}
|
|
10696
10867
|
const choice = Number(answer3);
|
|
10697
|
-
if (!Number.isInteger(choice) || choice < 1 || choice >
|
|
10868
|
+
if (!Number.isInteger(choice) || choice < 1 || choice > createIndex) {
|
|
10698
10869
|
error(`"${answer3}" is not one of the choices. Nothing was changed.`);
|
|
10699
10870
|
return 2;
|
|
10700
10871
|
}
|
|
10701
|
-
if (choice === noneIndex) {
|
|
10702
|
-
if (found) {
|
|
10703
|
-
writeWorkspace(found.dir, {});
|
|
10704
|
-
print("");
|
|
10705
|
-
print(` Cleared the Space in ${WORKSPACE_FILE}. This folder now uses everything.`);
|
|
10706
|
-
} else {
|
|
10707
|
-
print("");
|
|
10708
|
-
print(" No Space. This folder uses everything in your account.");
|
|
10709
|
-
}
|
|
10710
|
-
return 0;
|
|
10711
|
-
}
|
|
10712
10872
|
if (choice === createIndex) {
|
|
10713
10873
|
return finish(context, await askForNewSpace(context, client), current);
|
|
10714
10874
|
}
|
|
@@ -11002,8 +11162,8 @@ Delete the file it runs from: ${processPath()}`
|
|
|
11002
11162
|
}
|
|
11003
11163
|
const result = run2(manager, ["uninstall", "-g", PACKAGE]);
|
|
11004
11164
|
if (result.status !== 0) {
|
|
11005
|
-
const
|
|
11006
|
-
if (
|
|
11165
|
+
const said3 = npmSaid(result);
|
|
11166
|
+
if (said3) context.error(said3);
|
|
11007
11167
|
context.error(`npm could not remove ${PACKAGE}.`);
|
|
11008
11168
|
if (alsoDisconnect) reportDisconnect(context, disconnected, stubborn);
|
|
11009
11169
|
return result.status ?? 1;
|
|
@@ -11039,8 +11199,8 @@ function reportDisconnect(context, disconnected, stubborn) {
|
|
|
11039
11199
|
}
|
|
11040
11200
|
function npmSaid(result) {
|
|
11041
11201
|
if (result.error) return result.error.message;
|
|
11042
|
-
const
|
|
11043
|
-
return
|
|
11202
|
+
const said3 = [result.stdout, result.stderr].map((stream) => (stream ?? "").trim()).filter((stream) => stream.length > 0).join("\n");
|
|
11203
|
+
return said3.length > 0 ? said3 : void 0;
|
|
11044
11204
|
}
|
|
11045
11205
|
async function deleteCommand(context) {
|
|
11046
11206
|
if (!existsSync8(context.paths.dir)) {
|
|
@@ -11476,7 +11636,7 @@ function say(context, working, profile, what, surface) {
|
|
|
11476
11636
|
what === "changed" ? `No longer working in a Space on ${where}.` : `No Space chosen on ${where}.`
|
|
11477
11637
|
);
|
|
11478
11638
|
context.print(
|
|
11479
|
-
surface === "email" ? "Mail forwarded to your ingest address is HELD and not filed \u2014 there is no default and nothing picks one. Each held message is kept whole and readable, and forwarding it again once you have chosen is what captures it." : "
|
|
11639
|
+
surface === "email" ? "Mail forwarded to your ingest address is HELD and not filed \u2014 there is no default and nothing picks one. Each held message is kept whole and readable, and forwarding it again once you have chosen is what captures it." : "`pm remember` from here is REFUSED, naming the Spaces you have \u2014 it is not silently dropped, and nothing is lost \u2014 there is no default and nothing picks one. Questions are still answered, from everything."
|
|
11480
11640
|
);
|
|
11481
11641
|
context.print(
|
|
11482
11642
|
surface === "email" ? 'Run `pm space --email "Work"` to choose, or `pm spaces create "Work"` first.' : 'Run `pm space "Work"` to choose, or `pm spaces create "Work"` if you have none.'
|