@persistmemory/cli 0.8.1 → 0.9.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/dist/bin.js +201 -74
- package/dist/bin.js.map +4 -4
- package/dist/index.js +201 -74
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1470,7 +1470,8 @@ function pathsFor(home = homedir()) {
|
|
|
1470
1470
|
return {
|
|
1471
1471
|
dir,
|
|
1472
1472
|
config: join(dir, "config.json"),
|
|
1473
|
-
credentials: join(dir, "credentials.json")
|
|
1473
|
+
credentials: join(dir, "credentials.json"),
|
|
1474
|
+
agentPid: join(dir, "agent.pid")
|
|
1474
1475
|
};
|
|
1475
1476
|
}
|
|
1476
1477
|
function readConfig(paths) {
|
|
@@ -1710,7 +1711,7 @@ function shortDate(iso) {
|
|
|
1710
1711
|
}
|
|
1711
1712
|
|
|
1712
1713
|
// src/help.ts
|
|
1713
|
-
var VERSION = true ? "0.
|
|
1714
|
+
var VERSION = true ? "0.9.0" : versionFromManifest();
|
|
1714
1715
|
var PACKAGE = "@persistmemory/cli";
|
|
1715
1716
|
var HELP = `
|
|
1716
1717
|
pm \u2014 PersistMemory from your terminal
|
|
@@ -1779,9 +1780,10 @@ var HELP = `
|
|
|
1779
1780
|
approve every request first, and see the
|
|
1780
1781
|
exact path or command before you do.
|
|
1781
1782
|
Stays in the foreground; background it
|
|
1782
|
-
|
|
1783
|
+
with:
|
|
1783
1784
|
nohup pm agent </dev/null >> ~/agent.log 2>&1 &
|
|
1784
|
-
|
|
1785
|
+
agent stop stop the agent running on this machine.
|
|
1786
|
+
Lets the request in flight finish first.
|
|
1785
1787
|
agent --root <dir> [--root ...] narrow it to these folders, for this run
|
|
1786
1788
|
agent enable [hostname] turn this machine back on after it was
|
|
1787
1789
|
switched off for going quiet. Asks you to
|
|
@@ -2514,7 +2516,8 @@ async function readStdin() {
|
|
|
2514
2516
|
import { hostname } from "node:os";
|
|
2515
2517
|
import { homedir as homedir2 } from "node:os";
|
|
2516
2518
|
import { basename, join as join6, resolve as resolve3 } from "node:path";
|
|
2517
|
-
import { existsSync as existsSync5, readFileSync as readFileSync6, readdirSync, statSync as statSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2519
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6, readdirSync, statSync as statSync5, unlinkSync as unlinkSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2520
|
+
import { execFileSync } from "node:child_process";
|
|
2518
2521
|
|
|
2519
2522
|
// src/files.ts
|
|
2520
2523
|
import { existsSync as existsSync3, readFileSync as readFileSync4, realpathSync, statSync as statSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
@@ -7288,8 +7291,8 @@ var ENDS = /,|\s+(?:and|then|so|but|because|which|where|that|with|to)\s+/i;
|
|
|
7288
7291
|
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;
|
|
7289
7292
|
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;
|
|
7290
7293
|
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;
|
|
7291
|
-
function flatten(
|
|
7292
|
-
return
|
|
7294
|
+
function flatten(said3) {
|
|
7295
|
+
return said3.normalize("NFKC").replace(/[‘’‚‛′]/g, "'").replace(/[“”„‟″«»]/g, '"').replace(/\s+/g, " ").trim();
|
|
7293
7296
|
}
|
|
7294
7297
|
function tidy(name) {
|
|
7295
7298
|
return name.replace(/^["']+|["']+$/g, "").replace(/[.,;:!?]+$/g, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
@@ -7320,9 +7323,9 @@ function readings(tail) {
|
|
|
7320
7323
|
}
|
|
7321
7324
|
return found;
|
|
7322
7325
|
}
|
|
7323
|
-
function spacesTheyAskedToMake(
|
|
7324
|
-
if (typeof
|
|
7325
|
-
const text = flatten(
|
|
7326
|
+
function spacesTheyAskedToMake(said3) {
|
|
7327
|
+
if (typeof said3 !== "string") return [];
|
|
7328
|
+
const text = flatten(said3);
|
|
7326
7329
|
if (text === "" || text.length > AT_MOST) return [];
|
|
7327
7330
|
if ([...text.matchAll(ASKS_FOR_A_SPACE)].length !== 1) return [];
|
|
7328
7331
|
const named = [...text.matchAll(NAMED_A_SPACE)];
|
|
@@ -7335,11 +7338,11 @@ function spacesTheyAskedToMake(said2) {
|
|
|
7335
7338
|
const only = tidy((first[0]?.[1] ?? "").replace(LEADING, ""));
|
|
7336
7339
|
return nameable(only) ? [only] : [];
|
|
7337
7340
|
}
|
|
7338
|
-
function askedToMakeIt(
|
|
7339
|
-
if (typeof
|
|
7341
|
+
function askedToMakeIt(said3, name) {
|
|
7342
|
+
if (typeof said3 !== "string" || typeof name !== "string") return false;
|
|
7340
7343
|
const wanted2 = tidy(name);
|
|
7341
7344
|
if (!nameable(wanted2)) return false;
|
|
7342
|
-
return spacesTheyAskedToMake(
|
|
7345
|
+
return spacesTheyAskedToMake(said3).includes(wanted2);
|
|
7343
7346
|
}
|
|
7344
7347
|
|
|
7345
7348
|
// ../tools/src/catalogue.ts
|
|
@@ -7791,10 +7794,10 @@ var TOOLS = [
|
|
|
7791
7794
|
},
|
|
7792
7795
|
...context.origin?.askedBy ? { confirmedBy: context.origin.askedBy } : {}
|
|
7793
7796
|
});
|
|
7794
|
-
const
|
|
7797
|
+
const said3 = `Forwarded to ${String(args.to)}. It went whole, with everything that was attached to it.`;
|
|
7795
7798
|
return {
|
|
7796
|
-
model: `${
|
|
7797
|
-
person: `${
|
|
7799
|
+
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.`,
|
|
7800
|
+
person: `${said3} There is no unsending it \u2014 if it went to the wrong address, tell them.`
|
|
7798
7801
|
};
|
|
7799
7802
|
}
|
|
7800
7803
|
},
|
|
@@ -8238,15 +8241,15 @@ var TOOLS = [
|
|
|
8238
8241
|
a wider search that still finds the file beats a refusal.
|
|
8239
8242
|
*/
|
|
8240
8243
|
argsFrom: (rest) => {
|
|
8241
|
-
const
|
|
8242
|
-
if (!
|
|
8243
|
-
const at =
|
|
8244
|
+
const said3 = rest.trim();
|
|
8245
|
+
if (!said3) return void 0;
|
|
8246
|
+
const at = said3.lastIndexOf(" in ");
|
|
8244
8247
|
if (at > 0) {
|
|
8245
|
-
const what =
|
|
8246
|
-
const where =
|
|
8248
|
+
const what = said3.slice(0, at).trim();
|
|
8249
|
+
const where = said3.slice(at + 4).trim();
|
|
8247
8250
|
if (what && where && !where.includes(" ")) return { what, in: where };
|
|
8248
8251
|
}
|
|
8249
|
-
return { what:
|
|
8252
|
+
return { what: said3 };
|
|
8250
8253
|
},
|
|
8251
8254
|
missing: "Say what to look for, like: /find deployment notes in ~/Documents"
|
|
8252
8255
|
},
|
|
@@ -9113,10 +9116,10 @@ var TOOLS = [
|
|
|
9113
9116
|
...context.origin?.askedBy ? { askedBy: context.origin.askedBy } : {}
|
|
9114
9117
|
});
|
|
9115
9118
|
if (!offered.ok) return offered.error;
|
|
9116
|
-
const
|
|
9119
|
+
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.`;
|
|
9117
9120
|
return {
|
|
9118
|
-
model: `${
|
|
9119
|
-
person: `${
|
|
9121
|
+
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.`,
|
|
9122
|
+
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}`
|
|
9120
9123
|
};
|
|
9121
9124
|
}
|
|
9122
9125
|
},
|
|
@@ -9261,8 +9264,8 @@ var TOOLS = [
|
|
|
9261
9264
|
...context.origin?.askedBy ? { askedBy: context.origin.askedBy } : {}
|
|
9262
9265
|
});
|
|
9263
9266
|
if (!changed.ok) return changed.error;
|
|
9264
|
-
const
|
|
9265
|
-
return changed.value.role === "owner" ? `${
|
|
9267
|
+
const said3 = `${changed.value.email} is now ${changed.value.role} on \u201C${held.space.name}\u201D.`;
|
|
9268
|
+
return changed.value.role === "owner" ? `${said3} As an owner they can share it onward and revoke anybody, including you.` : said3;
|
|
9266
9269
|
}
|
|
9267
9270
|
},
|
|
9268
9271
|
/* --------------------- which Space this is filing into --------------------- */
|
|
@@ -9516,11 +9519,11 @@ RIGHT NOW: ${where} is filing into ${from}.`;
|
|
|
9516
9519
|
Both conditions, and every unclear case is false. See
|
|
9517
9520
|
`their-own-words.ts` for what this does not cover.
|
|
9518
9521
|
*/
|
|
9519
|
-
directly(args, facts,
|
|
9522
|
+
directly(args, facts, said3) {
|
|
9520
9523
|
if (facts["creating"] !== true) return false;
|
|
9521
9524
|
const wanted2 = oneLine2(args.space, 200);
|
|
9522
9525
|
if (wanted2 === void 0) return false;
|
|
9523
|
-
return askedToMakeIt(
|
|
9526
|
+
return askedToMakeIt(said3, wanted2);
|
|
9524
9527
|
}
|
|
9525
9528
|
},
|
|
9526
9529
|
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.",
|
|
@@ -9940,11 +9943,31 @@ function avoided(path) {
|
|
|
9940
9943
|
return path.split(sep).some((segment) => segment.startsWith(".") || NEVER2.has(segment));
|
|
9941
9944
|
}
|
|
9942
9945
|
|
|
9943
|
-
// src/
|
|
9946
|
+
// src/said.ts
|
|
9944
9947
|
async function said(response, fallback) {
|
|
9945
|
-
const
|
|
9946
|
-
|
|
9947
|
-
|
|
9948
|
+
const raw = await response.text().catch(() => "");
|
|
9949
|
+
return reasonFrom(raw, response.status, fallback);
|
|
9950
|
+
}
|
|
9951
|
+
function reasonFrom(raw, status2, fallback) {
|
|
9952
|
+
let body;
|
|
9953
|
+
try {
|
|
9954
|
+
body = JSON.parse(raw);
|
|
9955
|
+
} catch {
|
|
9956
|
+
body = void 0;
|
|
9957
|
+
}
|
|
9958
|
+
if (typeof body?.error === "string") {
|
|
9959
|
+
const description = body.error_description?.trim();
|
|
9960
|
+
return description ? `${description} (${body.error})` : `${body.error} (${status2})`;
|
|
9961
|
+
}
|
|
9962
|
+
const message2 = typeof body?.error === "object" ? body.error?.message?.trim() : void 0;
|
|
9963
|
+
if (message2) return message2;
|
|
9964
|
+
const excerpt = raw.trim().replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").slice(0, 160);
|
|
9965
|
+
return excerpt ? `${fallback} (${status2}): ${excerpt}` : `${fallback} (${status2})`;
|
|
9966
|
+
}
|
|
9967
|
+
|
|
9968
|
+
// src/commands/agent.ts
|
|
9969
|
+
async function said2(response, fallback) {
|
|
9970
|
+
return said(response, fallback);
|
|
9948
9971
|
}
|
|
9949
9972
|
function locate(roots, requested) {
|
|
9950
9973
|
const expanded = requested.startsWith("~") ? resolve3(homedir2(), requested.slice(1).replace(/^[/\\]/, "")) : requested;
|
|
@@ -10019,7 +10042,7 @@ async function answer(context, apiUrl, token, roots, request) {
|
|
|
10019
10042
|
try {
|
|
10020
10043
|
fetched = await fetch(request.sourceUrl);
|
|
10021
10044
|
if (!fetched.ok) {
|
|
10022
|
-
return { ok: false, error: await
|
|
10045
|
+
return { ok: false, error: await said2(fetched, "The file could not be fetched") };
|
|
10023
10046
|
}
|
|
10024
10047
|
downloaded = Buffer.from(await fetched.arrayBuffer());
|
|
10025
10048
|
} catch {
|
|
@@ -10088,7 +10111,7 @@ async function upload(apiUrl, token, filename, bytes) {
|
|
|
10088
10111
|
})
|
|
10089
10112
|
});
|
|
10090
10113
|
if (!grant.ok) {
|
|
10091
|
-
return { ok: false, error: await
|
|
10114
|
+
return { ok: false, error: await said2(grant, "could not get an upload url") };
|
|
10092
10115
|
}
|
|
10093
10116
|
const { uploadUrl, contentType, maxBytes } = await grant.json();
|
|
10094
10117
|
const limit = maxBytes ?? MAX_TRANSFER_BYTES;
|
|
@@ -10144,7 +10167,7 @@ async function enableCommand(context) {
|
|
|
10144
10167
|
headers: { authorization: `Bearer ${await token()}` }
|
|
10145
10168
|
});
|
|
10146
10169
|
if (!listed.ok) {
|
|
10147
|
-
context.error(await
|
|
10170
|
+
context.error(await said2(listed, "could not read this account's machines"));
|
|
10148
10171
|
return 1;
|
|
10149
10172
|
}
|
|
10150
10173
|
const { items } = await listed.json();
|
|
@@ -10187,7 +10210,7 @@ Run this on ${machine.hostname} itself, or turn it back on from your dashboard.`
|
|
|
10187
10210
|
{ method: "POST", headers: { authorization: `Bearer ${await token()}` } }
|
|
10188
10211
|
);
|
|
10189
10212
|
if (!enabled.ok) {
|
|
10190
|
-
context.error(await
|
|
10213
|
+
context.error(await said2(enabled, "could not turn that machine back on"));
|
|
10191
10214
|
return 1;
|
|
10192
10215
|
}
|
|
10193
10216
|
const result = await enabled.json();
|
|
@@ -10200,8 +10223,94 @@ Run this on ${machine.hostname} itself, or turn it back on from your dashboard.`
|
|
|
10200
10223
|
);
|
|
10201
10224
|
return 0;
|
|
10202
10225
|
}
|
|
10226
|
+
function agentIsRunningAs(pid) {
|
|
10227
|
+
try {
|
|
10228
|
+
process.kill(pid, 0);
|
|
10229
|
+
} catch {
|
|
10230
|
+
return false;
|
|
10231
|
+
}
|
|
10232
|
+
if (process.platform === "win32") return void 0;
|
|
10233
|
+
try {
|
|
10234
|
+
const line2 = execFileSync("ps", ["-p", String(pid), "-o", "command="], {
|
|
10235
|
+
encoding: "utf8",
|
|
10236
|
+
timeout: 5e3
|
|
10237
|
+
});
|
|
10238
|
+
return /\bpm\b|persistmemory|\bagent\b/i.test(line2);
|
|
10239
|
+
} catch {
|
|
10240
|
+
return void 0;
|
|
10241
|
+
}
|
|
10242
|
+
}
|
|
10243
|
+
function processState(pid) {
|
|
10244
|
+
if (process.platform === "win32") return void 0;
|
|
10245
|
+
try {
|
|
10246
|
+
return execFileSync("ps", ["-p", String(pid), "-o", "stat="], {
|
|
10247
|
+
encoding: "utf8",
|
|
10248
|
+
timeout: 5e3
|
|
10249
|
+
}).trim();
|
|
10250
|
+
} catch {
|
|
10251
|
+
return void 0;
|
|
10252
|
+
}
|
|
10253
|
+
}
|
|
10254
|
+
async function stopCommand(context) {
|
|
10255
|
+
const file = context.paths.agentPid;
|
|
10256
|
+
if (!existsSync5(file)) {
|
|
10257
|
+
context.print("No agent is running on this machine.");
|
|
10258
|
+
context.print('If one is running from another home directory, stop it with: pkill -f "pm agent"');
|
|
10259
|
+
return 0;
|
|
10260
|
+
}
|
|
10261
|
+
const pid = Number.parseInt(readFileSync6(file, "utf8").trim(), 10);
|
|
10262
|
+
if (!Number.isInteger(pid) || pid <= 0) {
|
|
10263
|
+
unlinkSync2(file);
|
|
10264
|
+
context.error("The agent's pid file was unreadable, so it has been removed.");
|
|
10265
|
+
context.error('If an agent is still running, stop it with: pkill -f "pm agent"');
|
|
10266
|
+
return 1;
|
|
10267
|
+
}
|
|
10268
|
+
const mine = agentIsRunningAs(pid);
|
|
10269
|
+
if (mine === false) {
|
|
10270
|
+
unlinkSync2(file);
|
|
10271
|
+
context.print("No agent is running. Cleared a stale pid file.");
|
|
10272
|
+
return 0;
|
|
10273
|
+
}
|
|
10274
|
+
if (mine === void 0) {
|
|
10275
|
+
context.error(`Found a pid file naming process ${pid}, but could not confirm it is the agent.`);
|
|
10276
|
+
context.error("Refusing to signal a process this command cannot identify.");
|
|
10277
|
+
context.error(`If that is the agent, stop it with: kill ${pid}`);
|
|
10278
|
+
return 1;
|
|
10279
|
+
}
|
|
10280
|
+
try {
|
|
10281
|
+
process.kill(pid, "SIGCONT");
|
|
10282
|
+
} catch {
|
|
10283
|
+
}
|
|
10284
|
+
process.kill(pid, "SIGTERM");
|
|
10285
|
+
context.print(`Asked the agent (${pid}) to stop. Any request in flight will finish first.`);
|
|
10286
|
+
for (let waited = 0; waited < 1e4; waited += 250) {
|
|
10287
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
10288
|
+
if (agentIsRunningAs(pid) === false) {
|
|
10289
|
+
if (existsSync5(file)) unlinkSync2(file);
|
|
10290
|
+
context.print("Stopped.");
|
|
10291
|
+
return 0;
|
|
10292
|
+
}
|
|
10293
|
+
}
|
|
10294
|
+
if (processState(pid)?.startsWith("T")) {
|
|
10295
|
+
context.error(`The agent (${pid}) is suspended and did not respond to being resumed.`);
|
|
10296
|
+
context.error(`Stop it with: kill -CONT ${pid} && kill -9 ${pid}`);
|
|
10297
|
+
return 1;
|
|
10298
|
+
}
|
|
10299
|
+
context.print("Still finishing a request. It will exit on its own; nothing more to do.");
|
|
10300
|
+
return 0;
|
|
10301
|
+
}
|
|
10203
10302
|
async function agentCommand(context) {
|
|
10204
10303
|
if (context.args.words[1] === "enable") return enableCommand(context);
|
|
10304
|
+
if (context.args.words[1] === "stop") return stopCommand(context);
|
|
10305
|
+
const word = context.args.words[1];
|
|
10306
|
+
if (word !== void 0) {
|
|
10307
|
+
context.error(`pm agent: there is no "${word}" subcommand.`);
|
|
10308
|
+
context.error("");
|
|
10309
|
+
context.error(" pm agent start answering requests here");
|
|
10310
|
+
context.error(" pm agent stop stop the agent running here");
|
|
10311
|
+
context.error(" pm agent enable [host] turn a machine back on after it was switched off");
|
|
10312
|
+
return 2;
|
|
10313
|
+
}
|
|
10205
10314
|
const write3 = (message2, error = false) => {
|
|
10206
10315
|
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
10207
10316
|
for (const line2 of message2.split("\n")) {
|
|
@@ -10242,7 +10351,20 @@ async function agentCommand(context) {
|
|
|
10242
10351
|
const every = Math.max(2, asked ?? 5) * 1e3;
|
|
10243
10352
|
write3(`Answering as ${name}, from: ${roots.join(", ")}`);
|
|
10244
10353
|
write3("Nothing outside those folders can be read. Ctrl-C to stop.");
|
|
10354
|
+
writeFileSync5(context.paths.agentPid, `${process.pid}
|
|
10355
|
+
`, { mode: 384 });
|
|
10356
|
+
const forgetPid = () => {
|
|
10357
|
+
try {
|
|
10358
|
+
if (existsSync5(context.paths.agentPid)) {
|
|
10359
|
+
const held = Number.parseInt(readFileSync6(context.paths.agentPid, "utf8").trim(), 10);
|
|
10360
|
+
if (held === process.pid) unlinkSync2(context.paths.agentPid);
|
|
10361
|
+
}
|
|
10362
|
+
} catch {
|
|
10363
|
+
}
|
|
10364
|
+
};
|
|
10365
|
+
process.on("exit", forgetPid);
|
|
10245
10366
|
let running = true;
|
|
10367
|
+
let signedOut = false;
|
|
10246
10368
|
const stop = () => {
|
|
10247
10369
|
if (!running) return;
|
|
10248
10370
|
running = false;
|
|
@@ -10282,7 +10404,7 @@ async function agentCommand(context) {
|
|
|
10282
10404
|
version: VERSION
|
|
10283
10405
|
});
|
|
10284
10406
|
if (!beat.ok) {
|
|
10285
|
-
complain2(await
|
|
10407
|
+
complain2(await said2(beat, "the service refused this machine"));
|
|
10286
10408
|
} else {
|
|
10287
10409
|
const state = await beat.json();
|
|
10288
10410
|
if (state.status === "disabled") {
|
|
@@ -10299,7 +10421,7 @@ async function agentCommand(context) {
|
|
|
10299
10421
|
}
|
|
10300
10422
|
const claimed = await call("claim", { hostname: name, limit: 5 });
|
|
10301
10423
|
if (!claimed.ok) {
|
|
10302
|
-
complain2(await
|
|
10424
|
+
complain2(await said2(claimed, "could not pick up work"));
|
|
10303
10425
|
} else {
|
|
10304
10426
|
working();
|
|
10305
10427
|
const { items } = await claimed.json();
|
|
@@ -10313,18 +10435,28 @@ async function agentCommand(context) {
|
|
|
10313
10435
|
outcome.ok ? { result: { attachToken: outcome.attachToken } } : { error: outcome.error }
|
|
10314
10436
|
);
|
|
10315
10437
|
if (!done.ok) {
|
|
10316
|
-
write3(`Stored, but the service did not record it: ${await
|
|
10438
|
+
write3(`Stored, but the service did not record it: ${await said2(done, "refused")}`, true);
|
|
10317
10439
|
continue;
|
|
10318
10440
|
}
|
|
10319
10441
|
write3(outcome.ok ? `Sent ${outcome.bytes} bytes` : `Refused: ${outcome.error}`);
|
|
10320
10442
|
}
|
|
10321
10443
|
}
|
|
10322
10444
|
} catch (error) {
|
|
10323
|
-
|
|
10445
|
+
const message2 = error instanceof Error ? error.message : "connection failed";
|
|
10446
|
+
if (/invalid_grant/i.test(message2)) {
|
|
10447
|
+
write3(`This machine is signed out: ${message2}`, true);
|
|
10448
|
+
write3("Sign in again with: pm auth login", true);
|
|
10449
|
+
write3("The agent is stopping \u2014 retrying cannot renew a revoked token.", true);
|
|
10450
|
+
running = false;
|
|
10451
|
+
signedOut = true;
|
|
10452
|
+
continue;
|
|
10453
|
+
}
|
|
10454
|
+
complain2(message2);
|
|
10324
10455
|
}
|
|
10325
10456
|
if (running) await new Promise((r) => setTimeout(r, every));
|
|
10326
10457
|
}
|
|
10327
|
-
|
|
10458
|
+
forgetPid();
|
|
10459
|
+
return signedOut ? 1 : 0;
|
|
10328
10460
|
}
|
|
10329
10461
|
|
|
10330
10462
|
// src/commands/google.ts
|
|
@@ -10347,8 +10479,7 @@ async function callApi(context, path, init = {}) {
|
|
|
10347
10479
|
});
|
|
10348
10480
|
}
|
|
10349
10481
|
async function complain(context, response) {
|
|
10350
|
-
|
|
10351
|
-
context.error(body?.error?.message ?? `That failed (${response.status}).`);
|
|
10482
|
+
context.error(await said(response, "That failed"));
|
|
10352
10483
|
return response.status === 409 ? 3 : 1;
|
|
10353
10484
|
}
|
|
10354
10485
|
async function driveCommand(context) {
|
|
@@ -10549,8 +10680,7 @@ async function collectCommand(context) {
|
|
|
10549
10680
|
{ headers: { authorization: `Bearer ${credential.token}` } }
|
|
10550
10681
|
);
|
|
10551
10682
|
if (!link.ok) {
|
|
10552
|
-
|
|
10553
|
-
context.error(body?.error?.message ?? `Could not prepare that file (${link.status}).`);
|
|
10683
|
+
context.error(await said(link, "Could not prepare that file"));
|
|
10554
10684
|
return 1;
|
|
10555
10685
|
}
|
|
10556
10686
|
const { downloadUrl, filename } = await link.json();
|
|
@@ -10645,8 +10775,7 @@ async function setupCommand(context) {
|
|
|
10645
10775
|
}
|
|
10646
10776
|
async function chooseSpace(context, client, spaces) {
|
|
10647
10777
|
const { print, error } = context;
|
|
10648
|
-
const
|
|
10649
|
-
const current = found?.config.space;
|
|
10778
|
+
const current = findWorkspace()?.config.space;
|
|
10650
10779
|
const named = stringFlag(context.args, "space");
|
|
10651
10780
|
const creating = stringFlag(context.args, "new-space");
|
|
10652
10781
|
if (creating !== void 0) {
|
|
@@ -10668,48 +10797,46 @@ async function chooseSpace(context, client, spaces) {
|
|
|
10668
10797
|
print(` Pass --space "<name>" or --new-space "<name>" to pick one.`);
|
|
10669
10798
|
return 0;
|
|
10670
10799
|
}
|
|
10671
|
-
|
|
10672
|
-
|
|
10800
|
+
const live = current ? spaces.find((one) => one.id === current.id) : void 0;
|
|
10801
|
+
if (current && !live) {
|
|
10802
|
+
print(` This folder points at a Space called "${current.name}", which is no longer`);
|
|
10803
|
+
print(" in your account. Choose another one.");
|
|
10804
|
+
print("");
|
|
10805
|
+
} else if (live) {
|
|
10806
|
+
print(` This folder currently uses the Space "${live.name}".`);
|
|
10807
|
+
print("");
|
|
10808
|
+
}
|
|
10809
|
+
if (spaces.length === 0) {
|
|
10810
|
+
print(" You have no Spaces yet. Every memory is filed in one \u2014 a capture with");
|
|
10811
|
+
print(" no Space is refused, and names the Spaces you have \u2014 so this makes the first.");
|
|
10673
10812
|
print("");
|
|
10813
|
+
return finish(context, await askForNewSpace(context, client), current);
|
|
10674
10814
|
}
|
|
10675
10815
|
print(" Which Space should this folder use?");
|
|
10676
10816
|
print("");
|
|
10677
10817
|
spaces.forEach((space, index) => {
|
|
10678
|
-
const mark =
|
|
10818
|
+
const mark = live?.id === space.id ? " (current)" : "";
|
|
10679
10819
|
const count = space.memoryCount === void 0 ? "" : `, ${space.memoryCount} memories`;
|
|
10680
10820
|
print(` ${index + 1}. ${space.name}${mark} ${space.kind}${count}`);
|
|
10681
10821
|
});
|
|
10682
10822
|
const createIndex = spaces.length + 1;
|
|
10683
|
-
const noneIndex = spaces.length + 2;
|
|
10684
10823
|
print(` ${createIndex}. Create a new Space`);
|
|
10685
|
-
print(` ${noneIndex}. No Space \u2014 use everything in my account`);
|
|
10686
10824
|
print("");
|
|
10687
|
-
const fallback =
|
|
10688
|
-
const answer3 = await context.ask(` Choose 1-${
|
|
10825
|
+
const fallback = live ? "keep the current one" : String(createIndex);
|
|
10826
|
+
const answer3 = await context.ask(` Choose 1-${createIndex} [${fallback}]: `);
|
|
10689
10827
|
if (answer3 === "") {
|
|
10690
|
-
if (
|
|
10828
|
+
if (live) {
|
|
10691
10829
|
print("");
|
|
10692
|
-
print(` Keeping "${
|
|
10830
|
+
print(` Keeping "${live.name}".`);
|
|
10693
10831
|
return 0;
|
|
10694
10832
|
}
|
|
10695
10833
|
return finish(context, await askForNewSpace(context, client), current);
|
|
10696
10834
|
}
|
|
10697
10835
|
const choice = Number(answer3);
|
|
10698
|
-
if (!Number.isInteger(choice) || choice < 1 || choice >
|
|
10836
|
+
if (!Number.isInteger(choice) || choice < 1 || choice > createIndex) {
|
|
10699
10837
|
error(`"${answer3}" is not one of the choices. Nothing was changed.`);
|
|
10700
10838
|
return 2;
|
|
10701
10839
|
}
|
|
10702
|
-
if (choice === noneIndex) {
|
|
10703
|
-
if (found) {
|
|
10704
|
-
writeWorkspace(found.dir, {});
|
|
10705
|
-
print("");
|
|
10706
|
-
print(` Cleared the Space in ${WORKSPACE_FILE}. This folder now uses everything.`);
|
|
10707
|
-
} else {
|
|
10708
|
-
print("");
|
|
10709
|
-
print(" No Space. This folder uses everything in your account.");
|
|
10710
|
-
}
|
|
10711
|
-
return 0;
|
|
10712
|
-
}
|
|
10713
10840
|
if (choice === createIndex) {
|
|
10714
10841
|
return finish(context, await askForNewSpace(context, client), current);
|
|
10715
10842
|
}
|
|
@@ -11003,8 +11130,8 @@ Delete the file it runs from: ${processPath()}`
|
|
|
11003
11130
|
}
|
|
11004
11131
|
const result = run2(manager, ["uninstall", "-g", PACKAGE]);
|
|
11005
11132
|
if (result.status !== 0) {
|
|
11006
|
-
const
|
|
11007
|
-
if (
|
|
11133
|
+
const said3 = npmSaid(result);
|
|
11134
|
+
if (said3) context.error(said3);
|
|
11008
11135
|
context.error(`npm could not remove ${PACKAGE}.`);
|
|
11009
11136
|
if (alsoDisconnect) reportDisconnect(context, disconnected, stubborn);
|
|
11010
11137
|
return result.status ?? 1;
|
|
@@ -11040,8 +11167,8 @@ function reportDisconnect(context, disconnected, stubborn) {
|
|
|
11040
11167
|
}
|
|
11041
11168
|
function npmSaid(result) {
|
|
11042
11169
|
if (result.error) return result.error.message;
|
|
11043
|
-
const
|
|
11044
|
-
return
|
|
11170
|
+
const said3 = [result.stdout, result.stderr].map((stream) => (stream ?? "").trim()).filter((stream) => stream.length > 0).join("\n");
|
|
11171
|
+
return said3.length > 0 ? said3 : void 0;
|
|
11045
11172
|
}
|
|
11046
11173
|
async function deleteCommand(context) {
|
|
11047
11174
|
if (!existsSync8(context.paths.dir)) {
|
|
@@ -11477,7 +11604,7 @@ function say(context, working, profile, what, surface) {
|
|
|
11477
11604
|
what === "changed" ? `No longer working in a Space on ${where}.` : `No Space chosen on ${where}.`
|
|
11478
11605
|
);
|
|
11479
11606
|
context.print(
|
|
11480
|
-
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." : "
|
|
11607
|
+
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."
|
|
11481
11608
|
);
|
|
11482
11609
|
context.print(
|
|
11483
11610
|
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.'
|