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