@persistmemory/cli 0.3.3 → 0.5.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 +457 -80
- package/dist/bin.js.map +4 -4
- package/dist/index.js +457 -80
- 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((resolve9, 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
|
+
resolve9(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((resolve9, 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
|
+
resolve9();
|
|
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.5.0" : versionFromManifest();
|
|
1408
1408
|
var PACKAGE = "@persistmemory/cli";
|
|
1409
1409
|
var HELP = `
|
|
1410
1410
|
pm \u2014 PersistMemory from your terminal
|
|
@@ -1446,12 +1446,24 @@ var HELP = `
|
|
|
1446
1446
|
remember - capture whatever is piped in
|
|
1447
1447
|
remember --file <path> capture a file's contents
|
|
1448
1448
|
|
|
1449
|
-
agent
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1449
|
+
agent answer file and command requests from this
|
|
1450
|
+
machine. Reads the folders in your home
|
|
1451
|
+
directory \u2014 never anything hidden \u2014 and
|
|
1452
|
+
nothing outside them. It says which ones
|
|
1453
|
+
when it starts. Stays in the foreground;
|
|
1454
|
+
background it and stop it later with:
|
|
1455
|
+
nohup pm agent > ~/agent.log 2>&1 &
|
|
1454
1456
|
pkill -f "pm agent"
|
|
1457
|
+
agent --root <dir> [--root ...] choose the folders for this run
|
|
1458
|
+
|
|
1459
|
+
Settings live in ~/.persistmemory/agent.json, and apply every time:
|
|
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
|
+
}
|
|
1455
1467
|
|
|
1456
1468
|
search <query> search your memory
|
|
1457
1469
|
list memories the most recent memories
|
|
@@ -1610,8 +1622,8 @@ async function startLoopback(options = {}) {
|
|
|
1610
1622
|
const timeoutMs = options.timeoutMs ?? 5 * 60 * 1e3;
|
|
1611
1623
|
let resolveCallback;
|
|
1612
1624
|
let rejectCallback;
|
|
1613
|
-
const received = new Promise((
|
|
1614
|
-
resolveCallback =
|
|
1625
|
+
const received = new Promise((resolve9, reject) => {
|
|
1626
|
+
resolveCallback = resolve9;
|
|
1615
1627
|
rejectCallback = reject;
|
|
1616
1628
|
});
|
|
1617
1629
|
const server = createServer((request, response) => {
|
|
@@ -1647,9 +1659,9 @@ async function startLoopback(options = {}) {
|
|
|
1647
1659
|
response.end(donePage(callback));
|
|
1648
1660
|
resolveCallback?.(callback);
|
|
1649
1661
|
});
|
|
1650
|
-
await new Promise((
|
|
1662
|
+
await new Promise((resolve9, reject) => {
|
|
1651
1663
|
server.once("error", reject);
|
|
1652
|
-
server.listen(0, "127.0.0.1",
|
|
1664
|
+
server.listen(0, "127.0.0.1", resolve9);
|
|
1653
1665
|
});
|
|
1654
1666
|
const address = server.address();
|
|
1655
1667
|
if (address === null || typeof address === "string") {
|
|
@@ -1913,7 +1925,7 @@ function safeEqual(a, b) {
|
|
|
1913
1925
|
}
|
|
1914
1926
|
async function openBrowser(url) {
|
|
1915
1927
|
const [command, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
1916
|
-
await new Promise((
|
|
1928
|
+
await new Promise((resolve9, reject) => {
|
|
1917
1929
|
const child = spawn(command, args, {
|
|
1918
1930
|
stdio: "ignore",
|
|
1919
1931
|
// Detached so closing the terminal does not close the browser, and so
|
|
@@ -1922,7 +1934,7 @@ async function openBrowser(url) {
|
|
|
1922
1934
|
});
|
|
1923
1935
|
child.once("error", reject);
|
|
1924
1936
|
child.unref();
|
|
1925
|
-
|
|
1937
|
+
resolve9();
|
|
1926
1938
|
});
|
|
1927
1939
|
}
|
|
1928
1940
|
async function describe(response) {
|
|
@@ -1984,16 +1996,16 @@ async function currentCredential(resolved, deps) {
|
|
|
1984
1996
|
// src/context.ts
|
|
1985
1997
|
import { createInterface } from "node:readline";
|
|
1986
1998
|
async function askOnTty(prompt, input = process.stdin, output = process.stdout) {
|
|
1987
|
-
return new Promise((
|
|
1999
|
+
return new Promise((resolve9) => {
|
|
1988
2000
|
const readline = createInterface({ input, output });
|
|
1989
2001
|
let answered = false;
|
|
1990
2002
|
readline.question(prompt, (answer3) => {
|
|
1991
2003
|
answered = true;
|
|
1992
|
-
|
|
2004
|
+
resolve9(answer3.trim());
|
|
1993
2005
|
readline.close();
|
|
1994
2006
|
});
|
|
1995
2007
|
readline.once("close", () => {
|
|
1996
|
-
if (!answered)
|
|
2008
|
+
if (!answered) resolve9("");
|
|
1997
2009
|
});
|
|
1998
2010
|
});
|
|
1999
2011
|
}
|
|
@@ -2003,16 +2015,16 @@ var BACKSPACE = "\b";
|
|
|
2003
2015
|
async function readSecretFromTty(prompt) {
|
|
2004
2016
|
const input = process.stdin;
|
|
2005
2017
|
if (!input.isTTY) {
|
|
2006
|
-
return new Promise((
|
|
2018
|
+
return new Promise((resolve9) => {
|
|
2007
2019
|
const readline = createInterface({ input });
|
|
2008
2020
|
let answered = false;
|
|
2009
2021
|
readline.once("line", (line) => {
|
|
2010
2022
|
answered = true;
|
|
2011
|
-
|
|
2023
|
+
resolve9(line.trim());
|
|
2012
2024
|
readline.close();
|
|
2013
2025
|
});
|
|
2014
2026
|
readline.once("close", () => {
|
|
2015
|
-
if (!answered)
|
|
2027
|
+
if (!answered) resolve9("");
|
|
2016
2028
|
});
|
|
2017
2029
|
});
|
|
2018
2030
|
}
|
|
@@ -2021,14 +2033,14 @@ async function readSecretFromTty(prompt) {
|
|
|
2021
2033
|
input.setRawMode?.(true);
|
|
2022
2034
|
input.resume();
|
|
2023
2035
|
input.setEncoding("utf8");
|
|
2024
|
-
return new Promise((
|
|
2036
|
+
return new Promise((resolve9) => {
|
|
2025
2037
|
let value = "";
|
|
2026
2038
|
const finish2 = () => {
|
|
2027
2039
|
input.removeListener("data", onData);
|
|
2028
2040
|
input.setRawMode?.(previouslyRaw);
|
|
2029
2041
|
input.pause();
|
|
2030
2042
|
process.stdout.write("\n");
|
|
2031
|
-
|
|
2043
|
+
resolve9(value.trim());
|
|
2032
2044
|
};
|
|
2033
2045
|
const onData = (chunk) => {
|
|
2034
2046
|
for (const character of chunk) {
|
|
@@ -2064,9 +2076,9 @@ async function readStdin() {
|
|
|
2064
2076
|
|
|
2065
2077
|
// src/commands/agent.ts
|
|
2066
2078
|
import { hostname } from "node:os";
|
|
2067
|
-
import { homedir as
|
|
2068
|
-
import { basename, join as
|
|
2069
|
-
import { existsSync as
|
|
2079
|
+
import { homedir as homedir3 } from "node:os";
|
|
2080
|
+
import { basename, join as join6, resolve as resolve4 } from "node:path";
|
|
2081
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6, readdirSync as readdirSync2, statSync as statSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2070
2082
|
|
|
2071
2083
|
// src/files.ts
|
|
2072
2084
|
import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync, statSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
@@ -2174,6 +2186,335 @@ ${head.join("\n")}`;
|
|
|
2174
2186
|
].join("\n");
|
|
2175
2187
|
}
|
|
2176
2188
|
|
|
2189
|
+
// src/commands/run-command.ts
|
|
2190
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
2191
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, statSync as statSync2 } from "node:fs";
|
|
2192
|
+
import { isAbsolute as isAbsolute2, join as join4, resolve as resolvePath } from "node:path";
|
|
2193
|
+
var CLASSES = new Map([
|
|
2194
|
+
// Reads. Report on the filesystem and change nothing.
|
|
2195
|
+
...[
|
|
2196
|
+
"ls",
|
|
2197
|
+
"cat",
|
|
2198
|
+
"head",
|
|
2199
|
+
"tail",
|
|
2200
|
+
"wc",
|
|
2201
|
+
"file",
|
|
2202
|
+
"stat",
|
|
2203
|
+
"du",
|
|
2204
|
+
"df",
|
|
2205
|
+
"find",
|
|
2206
|
+
"grep",
|
|
2207
|
+
"rg",
|
|
2208
|
+
"fd",
|
|
2209
|
+
"tree",
|
|
2210
|
+
"pwd",
|
|
2211
|
+
"date",
|
|
2212
|
+
"uname",
|
|
2213
|
+
"which",
|
|
2214
|
+
"echo",
|
|
2215
|
+
"sort",
|
|
2216
|
+
"uniq",
|
|
2217
|
+
"diff",
|
|
2218
|
+
"basename",
|
|
2219
|
+
"dirname",
|
|
2220
|
+
"realpath",
|
|
2221
|
+
"ps",
|
|
2222
|
+
"env"
|
|
2223
|
+
].map((name) => [name, "read"]),
|
|
2224
|
+
// Writes. Recoverable or not, they change the machine.
|
|
2225
|
+
...[
|
|
2226
|
+
"rm",
|
|
2227
|
+
"mv",
|
|
2228
|
+
"cp",
|
|
2229
|
+
"mkdir",
|
|
2230
|
+
"rmdir",
|
|
2231
|
+
"touch",
|
|
2232
|
+
"chmod",
|
|
2233
|
+
"chown",
|
|
2234
|
+
"ln",
|
|
2235
|
+
"tee",
|
|
2236
|
+
"truncate",
|
|
2237
|
+
"dd",
|
|
2238
|
+
"kill",
|
|
2239
|
+
"killall",
|
|
2240
|
+
"make",
|
|
2241
|
+
"cargo",
|
|
2242
|
+
"swift",
|
|
2243
|
+
"xcodebuild",
|
|
2244
|
+
"gradle",
|
|
2245
|
+
"docker",
|
|
2246
|
+
"brew",
|
|
2247
|
+
"apt",
|
|
2248
|
+
"yum"
|
|
2249
|
+
].map((name) => [name, "write"]),
|
|
2250
|
+
// A way out. See the trifecta note above.
|
|
2251
|
+
...["curl", "wget", "nc", "ncat", "telnet", "ssh", "scp", "rsync", "ftp", "http", "httpie"].map((name) => [name, "network"]),
|
|
2252
|
+
// Every command at once, wearing one name.
|
|
2253
|
+
...["sh", "bash", "zsh", "fish", "python", "python3", "node", "ruby", "perl", "osascript", "eval"].map((name) => [name, "interpreter"])
|
|
2254
|
+
]);
|
|
2255
|
+
var SUBCOMMAND_READS = /* @__PURE__ */ new Map([
|
|
2256
|
+
[
|
|
2257
|
+
"git",
|
|
2258
|
+
/* @__PURE__ */ new Set([
|
|
2259
|
+
"status",
|
|
2260
|
+
"log",
|
|
2261
|
+
"diff",
|
|
2262
|
+
"show",
|
|
2263
|
+
"branch",
|
|
2264
|
+
"remote",
|
|
2265
|
+
"describe",
|
|
2266
|
+
"blame",
|
|
2267
|
+
"shortlog",
|
|
2268
|
+
"ls-files",
|
|
2269
|
+
"rev-parse",
|
|
2270
|
+
"config"
|
|
2271
|
+
])
|
|
2272
|
+
],
|
|
2273
|
+
["npm", /* @__PURE__ */ new Set(["ls", "list", "view", "outdated", "why", "config"])],
|
|
2274
|
+
["yarn", /* @__PURE__ */ new Set(["list", "why", "info", "config"])],
|
|
2275
|
+
["docker", /* @__PURE__ */ new Set(["ps", "images", "logs", "inspect"])]
|
|
2276
|
+
]);
|
|
2277
|
+
var MAX_OUTPUT_BYTES = 256 * 1024;
|
|
2278
|
+
var TIMEOUT_MS = 2e4;
|
|
2279
|
+
function describe2(argv) {
|
|
2280
|
+
return argv.join(" ");
|
|
2281
|
+
}
|
|
2282
|
+
function programOf(argv) {
|
|
2283
|
+
const first = argv[0] ?? "";
|
|
2284
|
+
return first.split("/").pop() ?? first;
|
|
2285
|
+
}
|
|
2286
|
+
function classify(argv, policy) {
|
|
2287
|
+
const program = programOf(argv);
|
|
2288
|
+
if (policy.allow.includes(program)) return "read";
|
|
2289
|
+
const reads = SUBCOMMAND_READS.get(program);
|
|
2290
|
+
if (reads) {
|
|
2291
|
+
const sub = argv.slice(1).find((one) => !one.startsWith("-"));
|
|
2292
|
+
return sub && reads.has(sub) ? "read" : "write";
|
|
2293
|
+
}
|
|
2294
|
+
return CLASSES.get(program) ?? "unknown";
|
|
2295
|
+
}
|
|
2296
|
+
function judge(argv, policy) {
|
|
2297
|
+
const program = programOf(argv);
|
|
2298
|
+
if (!argv[0]) {
|
|
2299
|
+
return { commandClass: "unknown", automatic: false, refusal: "No command was given.", reason: "empty" };
|
|
2300
|
+
}
|
|
2301
|
+
if (policy.deny.includes(program)) {
|
|
2302
|
+
return {
|
|
2303
|
+
commandClass: "unknown",
|
|
2304
|
+
automatic: false,
|
|
2305
|
+
refusal: `This machine refuses "${program}".`,
|
|
2306
|
+
reason: "on this machine's deny list"
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
const commandClass = classify(argv, policy);
|
|
2310
|
+
if (commandClass === "network") {
|
|
2311
|
+
return {
|
|
2312
|
+
commandClass,
|
|
2313
|
+
automatic: false,
|
|
2314
|
+
refusal: `"${program}" can reach the network. This machine will not run it, and no approval enables it: a command that reads private files and can also send them is the one combination nobody can review by looking at it.`,
|
|
2315
|
+
reason: "reads private data and has a way out"
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
if (commandClass === "interpreter") {
|
|
2319
|
+
return {
|
|
2320
|
+
commandClass,
|
|
2321
|
+
automatic: false,
|
|
2322
|
+
refusal: `"${program}" runs a language, which is every command at once. Ask for the specific command instead.`,
|
|
2323
|
+
reason: "an interpreter is not one command"
|
|
2324
|
+
};
|
|
2325
|
+
}
|
|
2326
|
+
const outside = pathOutsideRoots(argv, policy.roots);
|
|
2327
|
+
if (outside) {
|
|
2328
|
+
return {
|
|
2329
|
+
commandClass,
|
|
2330
|
+
automatic: false,
|
|
2331
|
+
refusal: `${outside} is outside the folders this machine may read.`,
|
|
2332
|
+
reason: "outside the roots"
|
|
2333
|
+
};
|
|
2334
|
+
}
|
|
2335
|
+
if (policy.mode === "plan") {
|
|
2336
|
+
return {
|
|
2337
|
+
commandClass,
|
|
2338
|
+
automatic: false,
|
|
2339
|
+
reason: "this machine is in plan mode and runs nothing"
|
|
2340
|
+
};
|
|
2341
|
+
}
|
|
2342
|
+
return {
|
|
2343
|
+
commandClass,
|
|
2344
|
+
automatic: policy.mode === "auto-read" && commandClass === "read",
|
|
2345
|
+
reason: commandClass === "read" ? "reads and changes nothing" : commandClass === "write" ? "changes this machine" : "not a command this machine recognises"
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
function pathOutsideRoots(argv, roots) {
|
|
2349
|
+
for (const argument of argv.slice(1)) {
|
|
2350
|
+
if (argument.startsWith("-")) continue;
|
|
2351
|
+
if (!argument.startsWith("/") && !argument.startsWith("~") && !argument.startsWith(".") && !argument.includes("/")) {
|
|
2352
|
+
continue;
|
|
2353
|
+
}
|
|
2354
|
+
const resolved = expand(argument, roots[0] ?? process.cwd());
|
|
2355
|
+
if (!roots.some((root) => resolved === root || resolved.startsWith(`${root}/`))) {
|
|
2356
|
+
return argument;
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
return void 0;
|
|
2360
|
+
}
|
|
2361
|
+
function expand(argument, base) {
|
|
2362
|
+
const home = process.env["HOME"] ?? "";
|
|
2363
|
+
const withHome = argument.startsWith("~") ? join4(home, argument.slice(1)) : argument;
|
|
2364
|
+
return isAbsolute2(withHome) ? resolvePath(withHome) : resolvePath(base, withHome);
|
|
2365
|
+
}
|
|
2366
|
+
async function runCommand(argv, policy) {
|
|
2367
|
+
const verdict = judge(argv, policy);
|
|
2368
|
+
if (verdict.refusal) return { ok: false, text: verdict.refusal };
|
|
2369
|
+
if (policy.mode === "plan") {
|
|
2370
|
+
return { ok: false, text: `Plan mode: this machine did not run \`${describe2(argv)}\`.` };
|
|
2371
|
+
}
|
|
2372
|
+
const cwd = policy.roots[0];
|
|
2373
|
+
if (!cwd) return { ok: false, text: "This machine has no folders it may read." };
|
|
2374
|
+
try {
|
|
2375
|
+
if (!statSync2(cwd).isDirectory()) return { ok: false, text: `${cwd} is not a folder.` };
|
|
2376
|
+
} catch {
|
|
2377
|
+
return { ok: false, text: `${cwd} does not exist.` };
|
|
2378
|
+
}
|
|
2379
|
+
return new Promise((resolve9) => {
|
|
2380
|
+
const child = spawn2(argv[0], argv.slice(1), {
|
|
2381
|
+
cwd,
|
|
2382
|
+
// NO shell. With one, every character a model can produce is a character
|
|
2383
|
+
// the shell can act on, and the argument list stops meaning anything.
|
|
2384
|
+
shell: false,
|
|
2385
|
+
/*
|
|
2386
|
+
A bare environment.
|
|
2387
|
+
|
|
2388
|
+
This process holds the credential that authorises the machine, and
|
|
2389
|
+
handing it to a subprocess a model chose would mean `env` — or anything
|
|
2390
|
+
that prints its environment — returning that key.
|
|
2391
|
+
*/
|
|
2392
|
+
env: {
|
|
2393
|
+
PATH: process.env["PATH"] ?? "/usr/bin:/bin",
|
|
2394
|
+
HOME: process.env["HOME"] ?? "",
|
|
2395
|
+
LANG: process.env["LANG"] ?? "C"
|
|
2396
|
+
}
|
|
2397
|
+
});
|
|
2398
|
+
let output = "";
|
|
2399
|
+
let truncated = false;
|
|
2400
|
+
const collect = (chunk) => {
|
|
2401
|
+
if (truncated) return;
|
|
2402
|
+
output += chunk.toString("utf8");
|
|
2403
|
+
if (output.length > MAX_OUTPUT_BYTES) {
|
|
2404
|
+
output = output.slice(0, MAX_OUTPUT_BYTES);
|
|
2405
|
+
truncated = true;
|
|
2406
|
+
child.kill("SIGKILL");
|
|
2407
|
+
}
|
|
2408
|
+
};
|
|
2409
|
+
child.stdout.on("data", collect);
|
|
2410
|
+
child.stderr.on("data", collect);
|
|
2411
|
+
const timer = setTimeout(() => child.kill("SIGKILL"), TIMEOUT_MS);
|
|
2412
|
+
child.on("error", (error) => {
|
|
2413
|
+
clearTimeout(timer);
|
|
2414
|
+
resolve9({ ok: false, text: `Could not run it: ${error.message}` });
|
|
2415
|
+
});
|
|
2416
|
+
child.on("close", (code) => {
|
|
2417
|
+
clearTimeout(timer);
|
|
2418
|
+
const notes = [
|
|
2419
|
+
truncated ? `
|
|
2420
|
+
|
|
2421
|
+
[output cut off at ${MAX_OUTPUT_BYTES} bytes]` : "",
|
|
2422
|
+
code !== 0 && code !== null ? `
|
|
2423
|
+
|
|
2424
|
+
[exit code ${code}]` : ""
|
|
2425
|
+
].join("");
|
|
2426
|
+
resolve9({ ok: code === 0, text: `$ ${describe2(argv)}
|
|
2427
|
+
|
|
2428
|
+
${output}${notes}` });
|
|
2429
|
+
});
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
|
|
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
|
+
|
|
2177
2518
|
// src/commands/agent.ts
|
|
2178
2519
|
async function said(response, fallback) {
|
|
2179
2520
|
const body = await response.json().catch(() => void 0);
|
|
@@ -2181,7 +2522,7 @@ async function said(response, fallback) {
|
|
|
2181
2522
|
return message2 ?? `${fallback} (${response.status})`;
|
|
2182
2523
|
}
|
|
2183
2524
|
function locate(roots, requested) {
|
|
2184
|
-
const expanded = requested.startsWith("~") ?
|
|
2525
|
+
const expanded = requested.startsWith("~") ? resolve4(homedir3(), requested.slice(1).replace(/^[/\\]/, "")) : requested;
|
|
2185
2526
|
if (!expanded.includes("/") && !expanded.includes("\\")) {
|
|
2186
2527
|
const wanted2 = expanded.trim().toLowerCase();
|
|
2187
2528
|
const named = roots.find((root) => basename(root).toLowerCase() === wanted2);
|
|
@@ -2197,18 +2538,38 @@ function locate(roots, requested) {
|
|
|
2197
2538
|
throw new Error(`${requested} is not inside any allowed folder (${roots.join(", ")})`);
|
|
2198
2539
|
}
|
|
2199
2540
|
function uncontested(target) {
|
|
2200
|
-
if (!
|
|
2541
|
+
if (!existsSync6(target)) return target;
|
|
2201
2542
|
const dot = target.lastIndexOf(".");
|
|
2202
2543
|
const stem = dot > target.lastIndexOf("/") && dot !== -1 ? target.slice(0, dot) : target;
|
|
2203
2544
|
const extension = stem === target ? "" : target.slice(dot);
|
|
2204
2545
|
for (let n = 1; n < 1e3; n += 1) {
|
|
2205
2546
|
const candidate = `${stem} (${n})${extension}`;
|
|
2206
|
-
if (!
|
|
2547
|
+
if (!existsSync6(candidate)) return candidate;
|
|
2207
2548
|
}
|
|
2208
2549
|
throw new Error(`${target} and a thousand names beside it are taken.`);
|
|
2209
2550
|
}
|
|
2210
|
-
async function answer(context, apiUrl, token,
|
|
2551
|
+
async function answer(context, apiUrl, token, settings, request) {
|
|
2552
|
+
const roots = settings.roots;
|
|
2211
2553
|
let located;
|
|
2554
|
+
if (request.kind === "run_command") {
|
|
2555
|
+
if (!request.argv || request.argv.length === 0) {
|
|
2556
|
+
return { ok: false, error: "No command was given." };
|
|
2557
|
+
}
|
|
2558
|
+
const policy = {
|
|
2559
|
+
mode: settings.mode,
|
|
2560
|
+
allow: settings.allow,
|
|
2561
|
+
deny: settings.deny,
|
|
2562
|
+
roots
|
|
2563
|
+
};
|
|
2564
|
+
const outcome = await runCommand(request.argv, policy);
|
|
2565
|
+
if (!outcome.ok) return { ok: false, error: outcome.text };
|
|
2566
|
+
return upload(
|
|
2567
|
+
apiUrl,
|
|
2568
|
+
token,
|
|
2569
|
+
`${(request.argv[0] ?? "output").split("/").pop()}.txt`,
|
|
2570
|
+
Buffer.from(outcome.text, "utf8")
|
|
2571
|
+
);
|
|
2572
|
+
}
|
|
2212
2573
|
try {
|
|
2213
2574
|
located = locate(roots, request.path);
|
|
2214
2575
|
} catch (error) {
|
|
@@ -2229,10 +2590,10 @@ async function answer(context, apiUrl, token, roots, request) {
|
|
|
2229
2590
|
}
|
|
2230
2591
|
try {
|
|
2231
2592
|
let target = located;
|
|
2232
|
-
if (
|
|
2593
|
+
if (existsSync6(located) && statSync4(located).isDirectory()) {
|
|
2233
2594
|
const disposition = fetched.headers.get("content-disposition") ?? "";
|
|
2234
2595
|
const named = /filename="([^"]+)"/.exec(disposition)?.[1];
|
|
2235
|
-
target =
|
|
2596
|
+
target = join6(located, basename(named ?? "file"));
|
|
2236
2597
|
}
|
|
2237
2598
|
target = uncontested(target);
|
|
2238
2599
|
writeFileSync4(target, downloaded, { flag: "wx" });
|
|
@@ -2255,12 +2616,12 @@ ${downloaded.length} bytes
|
|
|
2255
2616
|
let filename = request.path.split("/").pop() ?? "file";
|
|
2256
2617
|
if (request.kind === "list_dir") {
|
|
2257
2618
|
try {
|
|
2258
|
-
const stats =
|
|
2619
|
+
const stats = statSync4(located);
|
|
2259
2620
|
if (!stats.isDirectory()) return { ok: false, error: `${request.path} is not a folder.` };
|
|
2260
|
-
const entries =
|
|
2621
|
+
const entries = readdirSync2(located, { withFileTypes: true }).filter((entry) => !entry.name.startsWith(".")).slice(0, MAX_LISTED).map((entry) => {
|
|
2261
2622
|
if (entry.isDirectory()) return `${entry.name}/`;
|
|
2262
2623
|
try {
|
|
2263
|
-
return `${entry.name} ${sizeOf(
|
|
2624
|
+
return `${entry.name} ${sizeOf(join6(located, entry.name))}`;
|
|
2264
2625
|
} catch {
|
|
2265
2626
|
return entry.name;
|
|
2266
2627
|
}
|
|
@@ -2278,9 +2639,9 @@ ${listing}
|
|
|
2278
2639
|
}
|
|
2279
2640
|
}
|
|
2280
2641
|
try {
|
|
2281
|
-
const stats =
|
|
2642
|
+
const stats = statSync4(located);
|
|
2282
2643
|
if (!stats.isFile()) return { ok: false, error: `${request.path} is not a file.` };
|
|
2283
|
-
bytes =
|
|
2644
|
+
bytes = readFileSync6(located);
|
|
2284
2645
|
} catch (error) {
|
|
2285
2646
|
return { ok: false, error: error instanceof Error ? error.message : "could not read it" };
|
|
2286
2647
|
}
|
|
@@ -2322,7 +2683,7 @@ async function upload(apiUrl, token, filename, bytes) {
|
|
|
2322
2683
|
}
|
|
2323
2684
|
var MAX_LISTED = 200;
|
|
2324
2685
|
function sizeOf(path) {
|
|
2325
|
-
const size =
|
|
2686
|
+
const size = statSync4(path).size;
|
|
2326
2687
|
if (size < 1024) return `${size} B`;
|
|
2327
2688
|
if (size < 1024 * 1024) return `${Math.round(size / 1024)} KB`;
|
|
2328
2689
|
return `${(size / (1024 * 1024)).toFixed(1)} MB`;
|
|
@@ -2334,25 +2695,41 @@ async function agentCommand(context) {
|
|
|
2334
2695
|
return 1;
|
|
2335
2696
|
}
|
|
2336
2697
|
const roots = (listFlag(context.args, "root") ?? []).map(
|
|
2337
|
-
(one) =>
|
|
2698
|
+
(one) => resolve4(one.startsWith("~") ? resolve4(homedir3(), one.slice(1).replace(/^[/\\]/, "")) : one)
|
|
2338
2699
|
);
|
|
2339
|
-
|
|
2700
|
+
const configPath = join6(context.paths.dir, "agent.json");
|
|
2701
|
+
const stored = readAgentConfig(configPath);
|
|
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) {
|
|
2340
2712
|
context.error(
|
|
2341
|
-
"
|
|
2713
|
+
"There is nothing in your home directory this machine would offer, so say which folders it may read from:"
|
|
2342
2714
|
);
|
|
2343
2715
|
context.error("");
|
|
2344
|
-
context.error(" pm agent --root ~/
|
|
2345
|
-
context.error("");
|
|
2346
|
-
context.error("It stays in the foreground. To leave it running:");
|
|
2716
|
+
context.error(" pm agent --root ~/projects");
|
|
2347
2717
|
context.error("");
|
|
2348
|
-
context.error(
|
|
2718
|
+
context.error(`Or write them once in ${configPath}:`);
|
|
2349
2719
|
context.error("");
|
|
2350
|
-
context.error('
|
|
2720
|
+
context.error(' { "roots": ["~/projects"], "mode": "ask" }');
|
|
2351
2721
|
return 1;
|
|
2352
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)";
|
|
2727
|
+
context.print(
|
|
2728
|
+
`Reading ${shown.join(", ")} \u2014 nothing outside them${where}. Mode: ${settings.mode}.`
|
|
2729
|
+
);
|
|
2353
2730
|
for (const root of roots) {
|
|
2354
2731
|
try {
|
|
2355
|
-
if (!
|
|
2732
|
+
if (!statSync4(root).isDirectory()) {
|
|
2356
2733
|
context.error(`${root} is not a folder.`);
|
|
2357
2734
|
return 1;
|
|
2358
2735
|
}
|
|
@@ -2426,7 +2803,7 @@ async function agentCommand(context) {
|
|
|
2426
2803
|
const { items } = await claimed.json();
|
|
2427
2804
|
for (const request of items) {
|
|
2428
2805
|
context.print(`Reading ${request.path}`);
|
|
2429
|
-
const outcome = await answer(context, apiUrl, await authorization(),
|
|
2806
|
+
const outcome = await answer(context, apiUrl, await authorization(), settings, request);
|
|
2430
2807
|
const done = await call(
|
|
2431
2808
|
`complete/${encodeURIComponent(request.id)}`,
|
|
2432
2809
|
outcome.ok ? { result: { attachToken: outcome.attachToken } } : { error: outcome.error }
|
|
@@ -2448,8 +2825,8 @@ async function agentCommand(context) {
|
|
|
2448
2825
|
|
|
2449
2826
|
// src/commands/google.ts
|
|
2450
2827
|
import { writeFileSync as writeFileSync5 } from "node:fs";
|
|
2451
|
-
import { basename as basename2, resolve as
|
|
2452
|
-
import { readFileSync as
|
|
2828
|
+
import { basename as basename2, resolve as resolve5 } from "node:path";
|
|
2829
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
2453
2830
|
async function callApi(context, path, init = {}) {
|
|
2454
2831
|
const credential = context.resolved.credential;
|
|
2455
2832
|
if (!credential) {
|
|
@@ -2514,7 +2891,7 @@ async function driveGet(context, fileId) {
|
|
|
2514
2891
|
const disposition = response.headers.get("content-disposition") ?? "";
|
|
2515
2892
|
const named = /filename="([^"]+)"/.exec(disposition)?.[1];
|
|
2516
2893
|
const out = stringFlag(context.args, "out");
|
|
2517
|
-
const target =
|
|
2894
|
+
const target = resolve5(out ?? basename2(named ?? fileId));
|
|
2518
2895
|
writeFileSync5(target, Buffer.from(await response.arrayBuffer()));
|
|
2519
2896
|
context.print(target);
|
|
2520
2897
|
return 0;
|
|
@@ -2526,7 +2903,7 @@ async function drivePut(context, path) {
|
|
|
2526
2903
|
}
|
|
2527
2904
|
let bytes;
|
|
2528
2905
|
try {
|
|
2529
|
-
bytes =
|
|
2906
|
+
bytes = readFileSync7(resolve5(path));
|
|
2530
2907
|
} catch {
|
|
2531
2908
|
context.error(`Cannot read ${path}.`);
|
|
2532
2909
|
return 1;
|
|
@@ -2610,8 +2987,8 @@ async function mailCommand(context) {
|
|
|
2610
2987
|
}
|
|
2611
2988
|
|
|
2612
2989
|
// src/commands/requests.ts
|
|
2613
|
-
import { existsSync as
|
|
2614
|
-
import { resolve as
|
|
2990
|
+
import { existsSync as existsSync7, writeFileSync as writeFileSync6 } from "node:fs";
|
|
2991
|
+
import { resolve as resolve6 } from "node:path";
|
|
2615
2992
|
async function requestsCommand(context) {
|
|
2616
2993
|
if (context.args.words[1] === "get") return collectCommand(context);
|
|
2617
2994
|
const credential = context.resolved.credential;
|
|
@@ -2679,8 +3056,8 @@ async function collectCommand(context) {
|
|
|
2679
3056
|
return 1;
|
|
2680
3057
|
}
|
|
2681
3058
|
const name = stringFlag(context.args, "output", "o") ?? filename;
|
|
2682
|
-
const target =
|
|
2683
|
-
if (
|
|
3059
|
+
const target = resolve6(name);
|
|
3060
|
+
if (existsSync7(target)) {
|
|
2684
3061
|
context.error(`${target} already exists. Pass --output to write somewhere else.`);
|
|
2685
3062
|
return 1;
|
|
2686
3063
|
}
|
|
@@ -2690,14 +3067,14 @@ async function collectCommand(context) {
|
|
|
2690
3067
|
}
|
|
2691
3068
|
|
|
2692
3069
|
// src/workspace.ts
|
|
2693
|
-
import { existsSync as
|
|
2694
|
-
import { dirname as dirname3, join as
|
|
3070
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "node:fs";
|
|
3071
|
+
import { dirname as dirname3, join as join7, resolve as resolvePath2 } from "node:path";
|
|
2695
3072
|
var WORKSPACE_FILE = ".persistmemory.json";
|
|
2696
3073
|
function findWorkspace(from = process.cwd()) {
|
|
2697
|
-
let dir =
|
|
3074
|
+
let dir = resolvePath2(from);
|
|
2698
3075
|
for (; ; ) {
|
|
2699
|
-
const file =
|
|
2700
|
-
if (
|
|
3076
|
+
const file = join7(dir, WORKSPACE_FILE);
|
|
3077
|
+
if (existsSync8(file)) {
|
|
2701
3078
|
const config = readWorkspace(file);
|
|
2702
3079
|
if (config) return { file, dir, config };
|
|
2703
3080
|
}
|
|
@@ -2708,7 +3085,7 @@ function findWorkspace(from = process.cwd()) {
|
|
|
2708
3085
|
}
|
|
2709
3086
|
function readWorkspace(file) {
|
|
2710
3087
|
try {
|
|
2711
|
-
const parsed = JSON.parse(
|
|
3088
|
+
const parsed = JSON.parse(readFileSync8(file, "utf8"));
|
|
2712
3089
|
const space = parsed.space;
|
|
2713
3090
|
if (space && typeof space === "object" && typeof space.id === "string" && space.id !== "" && typeof space.name === "string") {
|
|
2714
3091
|
return { space: { id: space.id, name: space.name } };
|
|
@@ -2719,7 +3096,7 @@ function readWorkspace(file) {
|
|
|
2719
3096
|
}
|
|
2720
3097
|
}
|
|
2721
3098
|
function writeWorkspace(dir, config) {
|
|
2722
|
-
const file =
|
|
3099
|
+
const file = join7(dir, WORKSPACE_FILE);
|
|
2723
3100
|
writeFileSync7(file, `${JSON.stringify(config, null, 2)}
|
|
2724
3101
|
`, "utf8");
|
|
2725
3102
|
return file;
|
|
@@ -2989,9 +3366,9 @@ function message(error) {
|
|
|
2989
3366
|
}
|
|
2990
3367
|
|
|
2991
3368
|
// src/commands/maintain.ts
|
|
2992
|
-
import { existsSync as
|
|
3369
|
+
import { existsSync as existsSync9, rmSync } from "node:fs";
|
|
2993
3370
|
import { spawnSync } from "node:child_process";
|
|
2994
|
-
import { dirname as dirname4, resolve as
|
|
3371
|
+
import { dirname as dirname4, resolve as resolve8 } from "node:path";
|
|
2995
3372
|
import { fileURLToPath } from "node:url";
|
|
2996
3373
|
function installArgs(pkg = PACKAGE) {
|
|
2997
3374
|
return ["install", "-g", "--prefer-online", `${pkg}@latest`];
|
|
@@ -3055,7 +3432,7 @@ Delete the file it runs from: ${processPath()}`
|
|
|
3055
3432
|
return 0;
|
|
3056
3433
|
}
|
|
3057
3434
|
async function deleteCommand(context) {
|
|
3058
|
-
if (!
|
|
3435
|
+
if (!existsSync9(context.paths.dir)) {
|
|
3059
3436
|
context.print(`Nothing to delete: ${context.paths.dir} does not exist.`);
|
|
3060
3437
|
return 0;
|
|
3061
3438
|
}
|
|
@@ -3084,7 +3461,7 @@ async function deleteCommand(context) {
|
|
|
3084
3461
|
return 0;
|
|
3085
3462
|
}
|
|
3086
3463
|
function removeEverything(context) {
|
|
3087
|
-
const dir =
|
|
3464
|
+
const dir = resolve8(context.paths.dir);
|
|
3088
3465
|
if (dir === "/" || dir.split("/").filter(Boolean).length < 2) {
|
|
3089
3466
|
context.error(`Refusing to delete ${dir}: that does not look like a data directory.`);
|
|
3090
3467
|
return;
|
|
@@ -3096,7 +3473,7 @@ function installer() {
|
|
|
3096
3473
|
}
|
|
3097
3474
|
function processPath() {
|
|
3098
3475
|
try {
|
|
3099
|
-
return
|
|
3476
|
+
return resolve8(dirname4(fileURLToPath(import.meta.url)));
|
|
3100
3477
|
} catch {
|
|
3101
3478
|
return process.argv[1] ?? "";
|
|
3102
3479
|
}
|
|
@@ -3108,12 +3485,12 @@ import { randomUUID } from "node:crypto";
|
|
|
3108
3485
|
import { relative as relative2 } from "node:path";
|
|
3109
3486
|
|
|
3110
3487
|
// src/events.ts
|
|
3111
|
-
import { appendFileSync, existsSync as
|
|
3112
|
-
import { join as
|
|
3488
|
+
import { appendFileSync, existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync9 } from "node:fs";
|
|
3489
|
+
import { join as join8 } from "node:path";
|
|
3113
3490
|
function openSessionLog(paths, id) {
|
|
3114
|
-
const directory =
|
|
3491
|
+
const directory = join8(paths.dir, "sessions");
|
|
3115
3492
|
mkdirSync3(directory, { recursive: true, mode: 448 });
|
|
3116
|
-
const path =
|
|
3493
|
+
const path = join8(directory, `${id}.jsonl`);
|
|
3117
3494
|
return {
|
|
3118
3495
|
id,
|
|
3119
3496
|
path,
|
|
@@ -3125,8 +3502,8 @@ function openSessionLog(paths, id) {
|
|
|
3125
3502
|
}
|
|
3126
3503
|
},
|
|
3127
3504
|
read() {
|
|
3128
|
-
if (!
|
|
3129
|
-
return
|
|
3505
|
+
if (!existsSync10(path)) return [];
|
|
3506
|
+
return readFileSync9(path, "utf8").split("\n").filter((line) => line.trim() !== "").flatMap((line) => {
|
|
3130
3507
|
try {
|
|
3131
3508
|
return [JSON.parse(line)];
|
|
3132
3509
|
} catch {
|
|
@@ -3203,9 +3580,9 @@ async function sessionCommand(context) {
|
|
|
3203
3580
|
context.print(` Ask anything. /help for commands, /exit to leave.
|
|
3204
3581
|
`);
|
|
3205
3582
|
const readline = createInterface2({ input: process.stdin, output: process.stdout });
|
|
3206
|
-
const ask = (prompt) => new Promise((
|
|
3207
|
-
readline.question(prompt,
|
|
3208
|
-
readline.once("close", () =>
|
|
3583
|
+
const ask = (prompt) => new Promise((resolve9) => {
|
|
3584
|
+
readline.question(prompt, resolve9);
|
|
3585
|
+
readline.once("close", () => resolve9(void 0));
|
|
3209
3586
|
});
|
|
3210
3587
|
const root = process.cwd();
|
|
3211
3588
|
let running = true;
|
|
@@ -3389,7 +3766,7 @@ async function write2(args) {
|
|
|
3389
3766
|
}
|
|
3390
3767
|
|
|
3391
3768
|
// src/commands/memory.ts
|
|
3392
|
-
import { readFileSync as
|
|
3769
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
3393
3770
|
|
|
3394
3771
|
// src/spaces.ts
|
|
3395
3772
|
async function spacesFor(context, client, env = process.env) {
|
|
@@ -3453,7 +3830,7 @@ async function rememberCommand(context) {
|
|
|
3453
3830
|
let text;
|
|
3454
3831
|
if (file) {
|
|
3455
3832
|
try {
|
|
3456
|
-
text =
|
|
3833
|
+
text = readFileSync10(file, "utf8");
|
|
3457
3834
|
} catch {
|
|
3458
3835
|
context.error(`Could not read ${file}.`);
|
|
3459
3836
|
return 1;
|