@persistmemory/cli 0.5.0 → 0.6.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 +82 -204
- package/dist/bin.js.map +4 -4
- package/dist/index.js +82 -204
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -344,7 +344,7 @@ var SignalFired = class extends Error {
|
|
|
344
344
|
};
|
|
345
345
|
function untilAborted(work, signal) {
|
|
346
346
|
work.catch(() => void 0);
|
|
347
|
-
return new Promise((
|
|
347
|
+
return new Promise((resolve8, reject) => {
|
|
348
348
|
if (signal.aborted) {
|
|
349
349
|
reject(new SignalFired());
|
|
350
350
|
return;
|
|
@@ -354,7 +354,7 @@ function untilAborted(work, signal) {
|
|
|
354
354
|
work.then(
|
|
355
355
|
(value) => {
|
|
356
356
|
signal.removeEventListener("abort", onAbort);
|
|
357
|
-
|
|
357
|
+
resolve8(value);
|
|
358
358
|
},
|
|
359
359
|
(error) => {
|
|
360
360
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -364,14 +364,14 @@ function untilAborted(work, signal) {
|
|
|
364
364
|
});
|
|
365
365
|
}
|
|
366
366
|
function defaultSleep(ms, signal) {
|
|
367
|
-
return new Promise((
|
|
367
|
+
return new Promise((resolve8, reject) => {
|
|
368
368
|
if (signal?.aborted) {
|
|
369
369
|
reject(new AbortError());
|
|
370
370
|
return;
|
|
371
371
|
}
|
|
372
372
|
const timer = setTimeout(() => {
|
|
373
373
|
signal?.removeEventListener("abort", onAbort);
|
|
374
|
-
|
|
374
|
+
resolve8();
|
|
375
375
|
}, ms);
|
|
376
376
|
function onAbort() {
|
|
377
377
|
clearTimeout(timer);
|
|
@@ -1404,7 +1404,7 @@ function shortDate(iso) {
|
|
|
1404
1404
|
}
|
|
1405
1405
|
|
|
1406
1406
|
// src/help.ts
|
|
1407
|
-
var VERSION = true ? "0.
|
|
1407
|
+
var VERSION = true ? "0.6.0" : versionFromManifest();
|
|
1408
1408
|
var PACKAGE = "@persistmemory/cli";
|
|
1409
1409
|
var HELP = `
|
|
1410
1410
|
pm \u2014 PersistMemory from your terminal
|
|
@@ -1447,23 +1447,17 @@ var HELP = `
|
|
|
1447
1447
|
remember --file <path> capture a file's contents
|
|
1448
1448
|
|
|
1449
1449
|
agent answer file and command requests from this
|
|
1450
|
-
machine. Reads
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1450
|
+
machine. Reads anywhere on it \u2014 you
|
|
1451
|
+
approve every request first, and see the
|
|
1452
|
+
exact path or command before you do.
|
|
1453
|
+
Stays in the foreground; background it
|
|
1454
|
+
and stop it later with:
|
|
1455
1455
|
nohup pm agent > ~/agent.log 2>&1 &
|
|
1456
1456
|
pkill -f "pm agent"
|
|
1457
|
-
agent --root <dir> [--root ...]
|
|
1457
|
+
agent --root <dir> [--root ...] narrow it to these folders, for this run
|
|
1458
1458
|
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
{
|
|
1462
|
-
"roots": ["~/code", "~/Documents"],
|
|
1463
|
-
"mode": "ask", ask | auto-read | plan
|
|
1464
|
-
"allow": ["swift"], programs this machine treats as reads
|
|
1465
|
-
"deny": ["rm"] programs it refuses, whatever is approved
|
|
1466
|
-
}
|
|
1459
|
+
A command that reaches the network, or that runs a language, is refused by
|
|
1460
|
+
this machine whatever anybody approves.
|
|
1467
1461
|
|
|
1468
1462
|
search <query> search your memory
|
|
1469
1463
|
list memories the most recent memories
|
|
@@ -1622,8 +1616,8 @@ async function startLoopback(options = {}) {
|
|
|
1622
1616
|
const timeoutMs = options.timeoutMs ?? 5 * 60 * 1e3;
|
|
1623
1617
|
let resolveCallback;
|
|
1624
1618
|
let rejectCallback;
|
|
1625
|
-
const received = new Promise((
|
|
1626
|
-
resolveCallback =
|
|
1619
|
+
const received = new Promise((resolve8, reject) => {
|
|
1620
|
+
resolveCallback = resolve8;
|
|
1627
1621
|
rejectCallback = reject;
|
|
1628
1622
|
});
|
|
1629
1623
|
const server = createServer((request, response) => {
|
|
@@ -1659,9 +1653,9 @@ async function startLoopback(options = {}) {
|
|
|
1659
1653
|
response.end(donePage(callback));
|
|
1660
1654
|
resolveCallback?.(callback);
|
|
1661
1655
|
});
|
|
1662
|
-
await new Promise((
|
|
1656
|
+
await new Promise((resolve8, reject) => {
|
|
1663
1657
|
server.once("error", reject);
|
|
1664
|
-
server.listen(0, "127.0.0.1",
|
|
1658
|
+
server.listen(0, "127.0.0.1", resolve8);
|
|
1665
1659
|
});
|
|
1666
1660
|
const address = server.address();
|
|
1667
1661
|
if (address === null || typeof address === "string") {
|
|
@@ -1925,7 +1919,7 @@ function safeEqual(a, b) {
|
|
|
1925
1919
|
}
|
|
1926
1920
|
async function openBrowser(url) {
|
|
1927
1921
|
const [command, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
1928
|
-
await new Promise((
|
|
1922
|
+
await new Promise((resolve8, reject) => {
|
|
1929
1923
|
const child = spawn(command, args, {
|
|
1930
1924
|
stdio: "ignore",
|
|
1931
1925
|
// Detached so closing the terminal does not close the browser, and so
|
|
@@ -1934,7 +1928,7 @@ async function openBrowser(url) {
|
|
|
1934
1928
|
});
|
|
1935
1929
|
child.once("error", reject);
|
|
1936
1930
|
child.unref();
|
|
1937
|
-
|
|
1931
|
+
resolve8();
|
|
1938
1932
|
});
|
|
1939
1933
|
}
|
|
1940
1934
|
async function describe(response) {
|
|
@@ -1996,16 +1990,16 @@ async function currentCredential(resolved, deps) {
|
|
|
1996
1990
|
// src/context.ts
|
|
1997
1991
|
import { createInterface } from "node:readline";
|
|
1998
1992
|
async function askOnTty(prompt, input = process.stdin, output = process.stdout) {
|
|
1999
|
-
return new Promise((
|
|
1993
|
+
return new Promise((resolve8) => {
|
|
2000
1994
|
const readline = createInterface({ input, output });
|
|
2001
1995
|
let answered = false;
|
|
2002
1996
|
readline.question(prompt, (answer3) => {
|
|
2003
1997
|
answered = true;
|
|
2004
|
-
|
|
1998
|
+
resolve8(answer3.trim());
|
|
2005
1999
|
readline.close();
|
|
2006
2000
|
});
|
|
2007
2001
|
readline.once("close", () => {
|
|
2008
|
-
if (!answered)
|
|
2002
|
+
if (!answered) resolve8("");
|
|
2009
2003
|
});
|
|
2010
2004
|
});
|
|
2011
2005
|
}
|
|
@@ -2015,16 +2009,16 @@ var BACKSPACE = "\b";
|
|
|
2015
2009
|
async function readSecretFromTty(prompt) {
|
|
2016
2010
|
const input = process.stdin;
|
|
2017
2011
|
if (!input.isTTY) {
|
|
2018
|
-
return new Promise((
|
|
2012
|
+
return new Promise((resolve8) => {
|
|
2019
2013
|
const readline = createInterface({ input });
|
|
2020
2014
|
let answered = false;
|
|
2021
2015
|
readline.once("line", (line) => {
|
|
2022
2016
|
answered = true;
|
|
2023
|
-
|
|
2017
|
+
resolve8(line.trim());
|
|
2024
2018
|
readline.close();
|
|
2025
2019
|
});
|
|
2026
2020
|
readline.once("close", () => {
|
|
2027
|
-
if (!answered)
|
|
2021
|
+
if (!answered) resolve8("");
|
|
2028
2022
|
});
|
|
2029
2023
|
});
|
|
2030
2024
|
}
|
|
@@ -2033,14 +2027,14 @@ async function readSecretFromTty(prompt) {
|
|
|
2033
2027
|
input.setRawMode?.(true);
|
|
2034
2028
|
input.resume();
|
|
2035
2029
|
input.setEncoding("utf8");
|
|
2036
|
-
return new Promise((
|
|
2030
|
+
return new Promise((resolve8) => {
|
|
2037
2031
|
let value = "";
|
|
2038
2032
|
const finish2 = () => {
|
|
2039
2033
|
input.removeListener("data", onData);
|
|
2040
2034
|
input.setRawMode?.(previouslyRaw);
|
|
2041
2035
|
input.pause();
|
|
2042
2036
|
process.stdout.write("\n");
|
|
2043
|
-
|
|
2037
|
+
resolve8(value.trim());
|
|
2044
2038
|
};
|
|
2045
2039
|
const onData = (chunk) => {
|
|
2046
2040
|
for (const character of chunk) {
|
|
@@ -2076,9 +2070,9 @@ async function readStdin() {
|
|
|
2076
2070
|
|
|
2077
2071
|
// src/commands/agent.ts
|
|
2078
2072
|
import { hostname } from "node:os";
|
|
2079
|
-
import { homedir as
|
|
2080
|
-
import { basename, join as
|
|
2081
|
-
import { existsSync as
|
|
2073
|
+
import { homedir as homedir2 } from "node:os";
|
|
2074
|
+
import { basename, join as join5, resolve as resolve3 } from "node:path";
|
|
2075
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, readdirSync, statSync as statSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2082
2076
|
|
|
2083
2077
|
// src/files.ts
|
|
2084
2078
|
import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync, statSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
@@ -2376,7 +2370,7 @@ async function runCommand(argv, policy) {
|
|
|
2376
2370
|
} catch {
|
|
2377
2371
|
return { ok: false, text: `${cwd} does not exist.` };
|
|
2378
2372
|
}
|
|
2379
|
-
return new Promise((
|
|
2373
|
+
return new Promise((resolve8) => {
|
|
2380
2374
|
const child = spawn2(argv[0], argv.slice(1), {
|
|
2381
2375
|
cwd,
|
|
2382
2376
|
// NO shell. With one, every character a model can produce is a character
|
|
@@ -2411,7 +2405,7 @@ async function runCommand(argv, policy) {
|
|
|
2411
2405
|
const timer = setTimeout(() => child.kill("SIGKILL"), TIMEOUT_MS);
|
|
2412
2406
|
child.on("error", (error) => {
|
|
2413
2407
|
clearTimeout(timer);
|
|
2414
|
-
|
|
2408
|
+
resolve8({ ok: false, text: `Could not run it: ${error.message}` });
|
|
2415
2409
|
});
|
|
2416
2410
|
child.on("close", (code) => {
|
|
2417
2411
|
clearTimeout(timer);
|
|
@@ -2423,98 +2417,13 @@ async function runCommand(argv, policy) {
|
|
|
2423
2417
|
|
|
2424
2418
|
[exit code ${code}]` : ""
|
|
2425
2419
|
].join("");
|
|
2426
|
-
|
|
2420
|
+
resolve8({ ok: code === 0, text: `$ ${describe2(argv)}
|
|
2427
2421
|
|
|
2428
2422
|
${output}${notes}` });
|
|
2429
2423
|
});
|
|
2430
2424
|
});
|
|
2431
2425
|
}
|
|
2432
2426
|
|
|
2433
|
-
// src/commands/agent-config.ts
|
|
2434
|
-
import { existsSync as existsSync5, readFileSync as readFileSync5, readdirSync, statSync as statSync3 } from "node:fs";
|
|
2435
|
-
import { homedir as homedir2 } from "node:os";
|
|
2436
|
-
import { join as join5, resolve as resolve3 } from "node:path";
|
|
2437
|
-
var NOT_MATERIAL = /* @__PURE__ */ new Set([
|
|
2438
|
-
"Library",
|
|
2439
|
-
"Applications",
|
|
2440
|
-
"System",
|
|
2441
|
-
"Public",
|
|
2442
|
-
"node_modules",
|
|
2443
|
-
"go",
|
|
2444
|
-
"Parallels",
|
|
2445
|
-
"VirtualBox VMs"
|
|
2446
|
-
]);
|
|
2447
|
-
function discoverRoots(home = homedir2(), list = (path) => {
|
|
2448
|
-
try {
|
|
2449
|
-
return readdirSync(path);
|
|
2450
|
-
} catch {
|
|
2451
|
-
return [];
|
|
2452
|
-
}
|
|
2453
|
-
}, isDirectory = (path) => {
|
|
2454
|
-
try {
|
|
2455
|
-
return statSync3(path).isDirectory();
|
|
2456
|
-
} catch {
|
|
2457
|
-
return false;
|
|
2458
|
-
}
|
|
2459
|
-
}) {
|
|
2460
|
-
return list(home).filter((name) => !name.startsWith(".")).filter((name) => !NOT_MATERIAL.has(name)).map((name) => join5(home, name)).filter(isDirectory).sort();
|
|
2461
|
-
}
|
|
2462
|
-
function expandHome(path, home = homedir2()) {
|
|
2463
|
-
return path.startsWith("~") ? resolve3(home, path.slice(1).replace(/^[/\\]/, "")) : resolve3(path);
|
|
2464
|
-
}
|
|
2465
|
-
var MODES = /* @__PURE__ */ new Set(["ask", "auto-read", "plan"]);
|
|
2466
|
-
function readAgentConfig(path, read2 = (at) => readFileSync5(at, "utf8"), exists = (at) => existsSync5(at), home = homedir2()) {
|
|
2467
|
-
if (!exists(path)) return {};
|
|
2468
|
-
let parsed;
|
|
2469
|
-
try {
|
|
2470
|
-
parsed = JSON.parse(read2(path));
|
|
2471
|
-
} catch (error) {
|
|
2472
|
-
return { error: `${path} is not valid JSON: ${error instanceof Error ? error.message : ""}` };
|
|
2473
|
-
}
|
|
2474
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
2475
|
-
return { error: `${path} should hold an object, like {"roots": ["~/code"]}.` };
|
|
2476
|
-
}
|
|
2477
|
-
const held = parsed;
|
|
2478
|
-
const config = {};
|
|
2479
|
-
if (held["roots"] !== void 0) {
|
|
2480
|
-
if (!Array.isArray(held["roots"]) || held["roots"].some((one) => typeof one !== "string")) {
|
|
2481
|
-
return { error: `${path}: "roots" should be a list of folders.` };
|
|
2482
|
-
}
|
|
2483
|
-
config.roots = held["roots"].map((one) => expandHome(one, home));
|
|
2484
|
-
}
|
|
2485
|
-
if (held["mode"] !== void 0) {
|
|
2486
|
-
if (typeof held["mode"] !== "string" || !MODES.has(held["mode"])) {
|
|
2487
|
-
return { error: `${path}: "mode" should be "ask", "auto-read" or "plan".` };
|
|
2488
|
-
}
|
|
2489
|
-
config.mode = held["mode"];
|
|
2490
|
-
}
|
|
2491
|
-
for (const key of ["allow", "deny"]) {
|
|
2492
|
-
if (held[key] === void 0) continue;
|
|
2493
|
-
if (!Array.isArray(held[key]) || held[key].some((one) => typeof one !== "string")) {
|
|
2494
|
-
return { error: `${path}: "${key}" should be a list of program names.` };
|
|
2495
|
-
}
|
|
2496
|
-
config[key] = held[key];
|
|
2497
|
-
}
|
|
2498
|
-
return { config };
|
|
2499
|
-
}
|
|
2500
|
-
function resolveAgentConfig(args) {
|
|
2501
|
-
const roots = args.flagRoots && args.flagRoots.length > 0 ? args.flagRoots : args.stored?.roots && args.stored.roots.length > 0 ? args.stored.roots : args.discovered ?? [];
|
|
2502
|
-
return {
|
|
2503
|
-
roots,
|
|
2504
|
-
/*
|
|
2505
|
-
`ask` unless the owner said otherwise.
|
|
2506
|
-
|
|
2507
|
-
The mode decides how much happens with nobody watching, so the default
|
|
2508
|
-
is the one that does least. `auto-read` is a reasonable thing to choose
|
|
2509
|
-
and an unreasonable thing to be given.
|
|
2510
|
-
*/
|
|
2511
|
-
mode: args.stored?.mode ?? "ask",
|
|
2512
|
-
allow: args.stored?.allow ?? [],
|
|
2513
|
-
deny: args.stored?.deny ?? [],
|
|
2514
|
-
source: args.flagRoots && args.flagRoots.length > 0 ? "flags" : args.stored?.roots && args.stored.roots.length > 0 ? "config" : "discovered"
|
|
2515
|
-
};
|
|
2516
|
-
}
|
|
2517
|
-
|
|
2518
2427
|
// src/commands/agent.ts
|
|
2519
2428
|
async function said(response, fallback) {
|
|
2520
2429
|
const body = await response.json().catch(() => void 0);
|
|
@@ -2522,7 +2431,7 @@ async function said(response, fallback) {
|
|
|
2522
2431
|
return message2 ?? `${fallback} (${response.status})`;
|
|
2523
2432
|
}
|
|
2524
2433
|
function locate(roots, requested) {
|
|
2525
|
-
const expanded = requested.startsWith("~") ?
|
|
2434
|
+
const expanded = requested.startsWith("~") ? resolve3(homedir2(), requested.slice(1).replace(/^[/\\]/, "")) : requested;
|
|
2526
2435
|
if (!expanded.includes("/") && !expanded.includes("\\")) {
|
|
2527
2436
|
const wanted2 = expanded.trim().toLowerCase();
|
|
2528
2437
|
const named = roots.find((root) => basename(root).toLowerCase() === wanted2);
|
|
@@ -2538,29 +2447,23 @@ function locate(roots, requested) {
|
|
|
2538
2447
|
throw new Error(`${requested} is not inside any allowed folder (${roots.join(", ")})`);
|
|
2539
2448
|
}
|
|
2540
2449
|
function uncontested(target) {
|
|
2541
|
-
if (!
|
|
2450
|
+
if (!existsSync5(target)) return target;
|
|
2542
2451
|
const dot = target.lastIndexOf(".");
|
|
2543
2452
|
const stem = dot > target.lastIndexOf("/") && dot !== -1 ? target.slice(0, dot) : target;
|
|
2544
2453
|
const extension = stem === target ? "" : target.slice(dot);
|
|
2545
2454
|
for (let n = 1; n < 1e3; n += 1) {
|
|
2546
2455
|
const candidate = `${stem} (${n})${extension}`;
|
|
2547
|
-
if (!
|
|
2456
|
+
if (!existsSync5(candidate)) return candidate;
|
|
2548
2457
|
}
|
|
2549
2458
|
throw new Error(`${target} and a thousand names beside it are taken.`);
|
|
2550
2459
|
}
|
|
2551
|
-
async function answer(context, apiUrl, token,
|
|
2552
|
-
const roots = settings.roots;
|
|
2460
|
+
async function answer(context, apiUrl, token, roots, request) {
|
|
2553
2461
|
let located;
|
|
2554
2462
|
if (request.kind === "run_command") {
|
|
2555
2463
|
if (!request.argv || request.argv.length === 0) {
|
|
2556
2464
|
return { ok: false, error: "No command was given." };
|
|
2557
2465
|
}
|
|
2558
|
-
const policy = {
|
|
2559
|
-
mode: settings.mode,
|
|
2560
|
-
allow: settings.allow,
|
|
2561
|
-
deny: settings.deny,
|
|
2562
|
-
roots
|
|
2563
|
-
};
|
|
2466
|
+
const policy = { mode: "ask", allow: [], deny: [], roots };
|
|
2564
2467
|
const outcome = await runCommand(request.argv, policy);
|
|
2565
2468
|
if (!outcome.ok) return { ok: false, error: outcome.text };
|
|
2566
2469
|
return upload(
|
|
@@ -2590,10 +2493,10 @@ async function answer(context, apiUrl, token, settings, request) {
|
|
|
2590
2493
|
}
|
|
2591
2494
|
try {
|
|
2592
2495
|
let target = located;
|
|
2593
|
-
if (
|
|
2496
|
+
if (existsSync5(located) && statSync3(located).isDirectory()) {
|
|
2594
2497
|
const disposition = fetched.headers.get("content-disposition") ?? "";
|
|
2595
2498
|
const named = /filename="([^"]+)"/.exec(disposition)?.[1];
|
|
2596
|
-
target =
|
|
2499
|
+
target = join5(located, basename(named ?? "file"));
|
|
2597
2500
|
}
|
|
2598
2501
|
target = uncontested(target);
|
|
2599
2502
|
writeFileSync4(target, downloaded, { flag: "wx" });
|
|
@@ -2616,12 +2519,12 @@ ${downloaded.length} bytes
|
|
|
2616
2519
|
let filename = request.path.split("/").pop() ?? "file";
|
|
2617
2520
|
if (request.kind === "list_dir") {
|
|
2618
2521
|
try {
|
|
2619
|
-
const stats =
|
|
2522
|
+
const stats = statSync3(located);
|
|
2620
2523
|
if (!stats.isDirectory()) return { ok: false, error: `${request.path} is not a folder.` };
|
|
2621
|
-
const entries =
|
|
2524
|
+
const entries = readdirSync(located, { withFileTypes: true }).filter((entry) => !entry.name.startsWith(".")).slice(0, MAX_LISTED).map((entry) => {
|
|
2622
2525
|
if (entry.isDirectory()) return `${entry.name}/`;
|
|
2623
2526
|
try {
|
|
2624
|
-
return `${entry.name} ${sizeOf(
|
|
2527
|
+
return `${entry.name} ${sizeOf(join5(located, entry.name))}`;
|
|
2625
2528
|
} catch {
|
|
2626
2529
|
return entry.name;
|
|
2627
2530
|
}
|
|
@@ -2639,9 +2542,9 @@ ${listing}
|
|
|
2639
2542
|
}
|
|
2640
2543
|
}
|
|
2641
2544
|
try {
|
|
2642
|
-
const stats =
|
|
2545
|
+
const stats = statSync3(located);
|
|
2643
2546
|
if (!stats.isFile()) return { ok: false, error: `${request.path} is not a file.` };
|
|
2644
|
-
bytes =
|
|
2547
|
+
bytes = readFileSync5(located);
|
|
2645
2548
|
} catch (error) {
|
|
2646
2549
|
return { ok: false, error: error instanceof Error ? error.message : "could not read it" };
|
|
2647
2550
|
}
|
|
@@ -2683,7 +2586,7 @@ async function upload(apiUrl, token, filename, bytes) {
|
|
|
2683
2586
|
}
|
|
2684
2587
|
var MAX_LISTED = 200;
|
|
2685
2588
|
function sizeOf(path) {
|
|
2686
|
-
const size =
|
|
2589
|
+
const size = statSync3(path).size;
|
|
2687
2590
|
if (size < 1024) return `${size} B`;
|
|
2688
2591
|
if (size < 1024 * 1024) return `${Math.round(size / 1024)} KB`;
|
|
2689
2592
|
return `${(size / (1024 * 1024)).toFixed(1)} MB`;
|
|
@@ -2695,41 +2598,16 @@ async function agentCommand(context) {
|
|
|
2695
2598
|
return 1;
|
|
2696
2599
|
}
|
|
2697
2600
|
const roots = (listFlag(context.args, "root") ?? []).map(
|
|
2698
|
-
(one) =>
|
|
2601
|
+
(one) => resolve3(one.startsWith("~") ? resolve3(homedir2(), one.slice(1).replace(/^[/\\]/, "")) : one)
|
|
2699
2602
|
);
|
|
2700
|
-
const
|
|
2701
|
-
|
|
2702
|
-
if (stored.error) {
|
|
2703
|
-
context.error(stored.error);
|
|
2704
|
-
return 1;
|
|
2705
|
-
}
|
|
2706
|
-
const settings = resolveAgentConfig({
|
|
2707
|
-
flagRoots: roots,
|
|
2708
|
-
...stored.config ? { stored: stored.config } : {},
|
|
2709
|
-
discovered: discoverRoots()
|
|
2710
|
-
});
|
|
2711
|
-
if (settings.roots.length === 0) {
|
|
2712
|
-
context.error(
|
|
2713
|
-
"There is nothing in your home directory this machine would offer, so say which folders it may read from:"
|
|
2714
|
-
);
|
|
2715
|
-
context.error("");
|
|
2716
|
-
context.error(" pm agent --root ~/projects");
|
|
2717
|
-
context.error("");
|
|
2718
|
-
context.error(`Or write them once in ${configPath}:`);
|
|
2719
|
-
context.error("");
|
|
2720
|
-
context.error(' { "roots": ["~/projects"], "mode": "ask" }');
|
|
2721
|
-
return 1;
|
|
2722
|
-
}
|
|
2723
|
-
roots.length = 0;
|
|
2724
|
-
roots.push(...settings.roots);
|
|
2725
|
-
const shown = settings.roots.map((path) => path.replace(homedir3(), "~"));
|
|
2726
|
-
const where = settings.source === "flags" ? "" : settings.source === "config" ? ` (from ${configPath})` : " (found in your home directory)";
|
|
2603
|
+
const everywhere = roots.length === 0;
|
|
2604
|
+
if (everywhere) roots.push("/");
|
|
2727
2605
|
context.print(
|
|
2728
|
-
`Reading ${
|
|
2606
|
+
everywhere ? "Reading anywhere on this machine. You approve every request first, and see the exact path or command before you do. Narrow it with --root if you want to." : `Reading ${roots.map((path) => path.replace(homedir2(), "~")).join(", ")} \u2014 nothing outside them.`
|
|
2729
2607
|
);
|
|
2730
2608
|
for (const root of roots) {
|
|
2731
2609
|
try {
|
|
2732
|
-
if (!
|
|
2610
|
+
if (!statSync3(root).isDirectory()) {
|
|
2733
2611
|
context.error(`${root} is not a folder.`);
|
|
2734
2612
|
return 1;
|
|
2735
2613
|
}
|
|
@@ -2803,7 +2681,7 @@ async function agentCommand(context) {
|
|
|
2803
2681
|
const { items } = await claimed.json();
|
|
2804
2682
|
for (const request of items) {
|
|
2805
2683
|
context.print(`Reading ${request.path}`);
|
|
2806
|
-
const outcome = await answer(context, apiUrl, await authorization(),
|
|
2684
|
+
const outcome = await answer(context, apiUrl, await authorization(), roots, request);
|
|
2807
2685
|
const done = await call(
|
|
2808
2686
|
`complete/${encodeURIComponent(request.id)}`,
|
|
2809
2687
|
outcome.ok ? { result: { attachToken: outcome.attachToken } } : { error: outcome.error }
|
|
@@ -2825,8 +2703,8 @@ async function agentCommand(context) {
|
|
|
2825
2703
|
|
|
2826
2704
|
// src/commands/google.ts
|
|
2827
2705
|
import { writeFileSync as writeFileSync5 } from "node:fs";
|
|
2828
|
-
import { basename as basename2, resolve as
|
|
2829
|
-
import { readFileSync as
|
|
2706
|
+
import { basename as basename2, resolve as resolve4 } from "node:path";
|
|
2707
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
2830
2708
|
async function callApi(context, path, init = {}) {
|
|
2831
2709
|
const credential = context.resolved.credential;
|
|
2832
2710
|
if (!credential) {
|
|
@@ -2891,7 +2769,7 @@ async function driveGet(context, fileId) {
|
|
|
2891
2769
|
const disposition = response.headers.get("content-disposition") ?? "";
|
|
2892
2770
|
const named = /filename="([^"]+)"/.exec(disposition)?.[1];
|
|
2893
2771
|
const out = stringFlag(context.args, "out");
|
|
2894
|
-
const target =
|
|
2772
|
+
const target = resolve4(out ?? basename2(named ?? fileId));
|
|
2895
2773
|
writeFileSync5(target, Buffer.from(await response.arrayBuffer()));
|
|
2896
2774
|
context.print(target);
|
|
2897
2775
|
return 0;
|
|
@@ -2903,7 +2781,7 @@ async function drivePut(context, path) {
|
|
|
2903
2781
|
}
|
|
2904
2782
|
let bytes;
|
|
2905
2783
|
try {
|
|
2906
|
-
bytes =
|
|
2784
|
+
bytes = readFileSync6(resolve4(path));
|
|
2907
2785
|
} catch {
|
|
2908
2786
|
context.error(`Cannot read ${path}.`);
|
|
2909
2787
|
return 1;
|
|
@@ -2987,8 +2865,8 @@ async function mailCommand(context) {
|
|
|
2987
2865
|
}
|
|
2988
2866
|
|
|
2989
2867
|
// src/commands/requests.ts
|
|
2990
|
-
import { existsSync as
|
|
2991
|
-
import { resolve as
|
|
2868
|
+
import { existsSync as existsSync6, writeFileSync as writeFileSync6 } from "node:fs";
|
|
2869
|
+
import { resolve as resolve5 } from "node:path";
|
|
2992
2870
|
async function requestsCommand(context) {
|
|
2993
2871
|
if (context.args.words[1] === "get") return collectCommand(context);
|
|
2994
2872
|
const credential = context.resolved.credential;
|
|
@@ -3056,8 +2934,8 @@ async function collectCommand(context) {
|
|
|
3056
2934
|
return 1;
|
|
3057
2935
|
}
|
|
3058
2936
|
const name = stringFlag(context.args, "output", "o") ?? filename;
|
|
3059
|
-
const target =
|
|
3060
|
-
if (
|
|
2937
|
+
const target = resolve5(name);
|
|
2938
|
+
if (existsSync6(target)) {
|
|
3061
2939
|
context.error(`${target} already exists. Pass --output to write somewhere else.`);
|
|
3062
2940
|
return 1;
|
|
3063
2941
|
}
|
|
@@ -3067,14 +2945,14 @@ async function collectCommand(context) {
|
|
|
3067
2945
|
}
|
|
3068
2946
|
|
|
3069
2947
|
// src/workspace.ts
|
|
3070
|
-
import { existsSync as
|
|
3071
|
-
import { dirname as dirname3, join as
|
|
2948
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "node:fs";
|
|
2949
|
+
import { dirname as dirname3, join as join6, resolve as resolvePath2 } from "node:path";
|
|
3072
2950
|
var WORKSPACE_FILE = ".persistmemory.json";
|
|
3073
2951
|
function findWorkspace(from = process.cwd()) {
|
|
3074
2952
|
let dir = resolvePath2(from);
|
|
3075
2953
|
for (; ; ) {
|
|
3076
|
-
const file =
|
|
3077
|
-
if (
|
|
2954
|
+
const file = join6(dir, WORKSPACE_FILE);
|
|
2955
|
+
if (existsSync7(file)) {
|
|
3078
2956
|
const config = readWorkspace(file);
|
|
3079
2957
|
if (config) return { file, dir, config };
|
|
3080
2958
|
}
|
|
@@ -3085,7 +2963,7 @@ function findWorkspace(from = process.cwd()) {
|
|
|
3085
2963
|
}
|
|
3086
2964
|
function readWorkspace(file) {
|
|
3087
2965
|
try {
|
|
3088
|
-
const parsed = JSON.parse(
|
|
2966
|
+
const parsed = JSON.parse(readFileSync7(file, "utf8"));
|
|
3089
2967
|
const space = parsed.space;
|
|
3090
2968
|
if (space && typeof space === "object" && typeof space.id === "string" && space.id !== "" && typeof space.name === "string") {
|
|
3091
2969
|
return { space: { id: space.id, name: space.name } };
|
|
@@ -3096,7 +2974,7 @@ function readWorkspace(file) {
|
|
|
3096
2974
|
}
|
|
3097
2975
|
}
|
|
3098
2976
|
function writeWorkspace(dir, config) {
|
|
3099
|
-
const file =
|
|
2977
|
+
const file = join6(dir, WORKSPACE_FILE);
|
|
3100
2978
|
writeFileSync7(file, `${JSON.stringify(config, null, 2)}
|
|
3101
2979
|
`, "utf8");
|
|
3102
2980
|
return file;
|
|
@@ -3366,9 +3244,9 @@ function message(error) {
|
|
|
3366
3244
|
}
|
|
3367
3245
|
|
|
3368
3246
|
// src/commands/maintain.ts
|
|
3369
|
-
import { existsSync as
|
|
3247
|
+
import { existsSync as existsSync8, rmSync } from "node:fs";
|
|
3370
3248
|
import { spawnSync } from "node:child_process";
|
|
3371
|
-
import { dirname as dirname4, resolve as
|
|
3249
|
+
import { dirname as dirname4, resolve as resolve7 } from "node:path";
|
|
3372
3250
|
import { fileURLToPath } from "node:url";
|
|
3373
3251
|
function installArgs(pkg = PACKAGE) {
|
|
3374
3252
|
return ["install", "-g", "--prefer-online", `${pkg}@latest`];
|
|
@@ -3432,7 +3310,7 @@ Delete the file it runs from: ${processPath()}`
|
|
|
3432
3310
|
return 0;
|
|
3433
3311
|
}
|
|
3434
3312
|
async function deleteCommand(context) {
|
|
3435
|
-
if (!
|
|
3313
|
+
if (!existsSync8(context.paths.dir)) {
|
|
3436
3314
|
context.print(`Nothing to delete: ${context.paths.dir} does not exist.`);
|
|
3437
3315
|
return 0;
|
|
3438
3316
|
}
|
|
@@ -3461,7 +3339,7 @@ async function deleteCommand(context) {
|
|
|
3461
3339
|
return 0;
|
|
3462
3340
|
}
|
|
3463
3341
|
function removeEverything(context) {
|
|
3464
|
-
const dir =
|
|
3342
|
+
const dir = resolve7(context.paths.dir);
|
|
3465
3343
|
if (dir === "/" || dir.split("/").filter(Boolean).length < 2) {
|
|
3466
3344
|
context.error(`Refusing to delete ${dir}: that does not look like a data directory.`);
|
|
3467
3345
|
return;
|
|
@@ -3473,7 +3351,7 @@ function installer() {
|
|
|
3473
3351
|
}
|
|
3474
3352
|
function processPath() {
|
|
3475
3353
|
try {
|
|
3476
|
-
return
|
|
3354
|
+
return resolve7(dirname4(fileURLToPath(import.meta.url)));
|
|
3477
3355
|
} catch {
|
|
3478
3356
|
return process.argv[1] ?? "";
|
|
3479
3357
|
}
|
|
@@ -3485,12 +3363,12 @@ import { randomUUID } from "node:crypto";
|
|
|
3485
3363
|
import { relative as relative2 } from "node:path";
|
|
3486
3364
|
|
|
3487
3365
|
// src/events.ts
|
|
3488
|
-
import { appendFileSync, existsSync as
|
|
3489
|
-
import { join as
|
|
3366
|
+
import { appendFileSync, existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync8 } from "node:fs";
|
|
3367
|
+
import { join as join7 } from "node:path";
|
|
3490
3368
|
function openSessionLog(paths, id) {
|
|
3491
|
-
const directory =
|
|
3369
|
+
const directory = join7(paths.dir, "sessions");
|
|
3492
3370
|
mkdirSync3(directory, { recursive: true, mode: 448 });
|
|
3493
|
-
const path =
|
|
3371
|
+
const path = join7(directory, `${id}.jsonl`);
|
|
3494
3372
|
return {
|
|
3495
3373
|
id,
|
|
3496
3374
|
path,
|
|
@@ -3502,8 +3380,8 @@ function openSessionLog(paths, id) {
|
|
|
3502
3380
|
}
|
|
3503
3381
|
},
|
|
3504
3382
|
read() {
|
|
3505
|
-
if (!
|
|
3506
|
-
return
|
|
3383
|
+
if (!existsSync9(path)) return [];
|
|
3384
|
+
return readFileSync8(path, "utf8").split("\n").filter((line) => line.trim() !== "").flatMap((line) => {
|
|
3507
3385
|
try {
|
|
3508
3386
|
return [JSON.parse(line)];
|
|
3509
3387
|
} catch {
|
|
@@ -3580,9 +3458,9 @@ async function sessionCommand(context) {
|
|
|
3580
3458
|
context.print(` Ask anything. /help for commands, /exit to leave.
|
|
3581
3459
|
`);
|
|
3582
3460
|
const readline = createInterface2({ input: process.stdin, output: process.stdout });
|
|
3583
|
-
const ask = (prompt) => new Promise((
|
|
3584
|
-
readline.question(prompt,
|
|
3585
|
-
readline.once("close", () =>
|
|
3461
|
+
const ask = (prompt) => new Promise((resolve8) => {
|
|
3462
|
+
readline.question(prompt, resolve8);
|
|
3463
|
+
readline.once("close", () => resolve8(void 0));
|
|
3586
3464
|
});
|
|
3587
3465
|
const root = process.cwd();
|
|
3588
3466
|
let running = true;
|
|
@@ -3766,7 +3644,7 @@ async function write2(args) {
|
|
|
3766
3644
|
}
|
|
3767
3645
|
|
|
3768
3646
|
// src/commands/memory.ts
|
|
3769
|
-
import { readFileSync as
|
|
3647
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
3770
3648
|
|
|
3771
3649
|
// src/spaces.ts
|
|
3772
3650
|
async function spacesFor(context, client, env = process.env) {
|
|
@@ -3830,7 +3708,7 @@ async function rememberCommand(context) {
|
|
|
3830
3708
|
let text;
|
|
3831
3709
|
if (file) {
|
|
3832
3710
|
try {
|
|
3833
|
-
text =
|
|
3711
|
+
text = readFileSync9(file, "utf8");
|
|
3834
3712
|
} catch {
|
|
3835
3713
|
context.error(`Could not read ${file}.`);
|
|
3836
3714
|
return 1;
|