@persistmemory/cli 0.4.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 CHANGED
@@ -1404,7 +1404,7 @@ function shortDate(iso) {
1404
1404
  }
1405
1405
 
1406
1406
  // src/help.ts
1407
- var VERSION = true ? "0.4.0" : versionFromManifest();
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
@@ -1446,14 +1446,18 @@ var HELP = `
1446
1446
  remember - capture whatever is piped in
1447
1447
  remember --file <path> capture a file's contents
1448
1448
 
1449
- agent answer file requests from this machine.
1450
- Reads ~/Desktop, ~/Documents and
1451
- ~/Downloads by default, and nothing
1452
- outside them. It stays in the foreground.
1453
- Background it, and stop it later, with:
1449
+ agent answer file and command requests from this
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:
1454
1455
  nohup pm agent > ~/agent.log 2>&1 &
1455
1456
  pkill -f "pm agent"
1456
- agent --root <dir> [--root ...] choose the folders yourself instead
1457
+ agent --root <dir> [--root ...] narrow it to these folders, for this run
1458
+
1459
+ A command that reaches the network, or that runs a language, is refused by
1460
+ this machine whatever anybody approves.
1457
1461
 
1458
1462
  search <query> search your memory
1459
1463
  list memories the most recent memories
@@ -2067,8 +2071,8 @@ async function readStdin() {
2067
2071
  // src/commands/agent.ts
2068
2072
  import { hostname } from "node:os";
2069
2073
  import { homedir as homedir2 } from "node:os";
2070
- import { basename, join as join4, resolve as resolve3 } from "node:path";
2071
- import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync, statSync as statSync2, writeFileSync as writeFileSync4 } from "node:fs";
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";
2072
2076
 
2073
2077
  // src/files.ts
2074
2078
  import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync, statSync, writeFileSync as writeFileSync3 } from "node:fs";
@@ -2176,6 +2180,250 @@ ${head.join("\n")}`;
2176
2180
  ].join("\n");
2177
2181
  }
2178
2182
 
2183
+ // src/commands/run-command.ts
2184
+ import { spawn as spawn2 } from "node:child_process";
2185
+ import { existsSync as existsSync4, readFileSync as readFileSync4, statSync as statSync2 } from "node:fs";
2186
+ import { isAbsolute as isAbsolute2, join as join4, resolve as resolvePath } from "node:path";
2187
+ var CLASSES = new Map([
2188
+ // Reads. Report on the filesystem and change nothing.
2189
+ ...[
2190
+ "ls",
2191
+ "cat",
2192
+ "head",
2193
+ "tail",
2194
+ "wc",
2195
+ "file",
2196
+ "stat",
2197
+ "du",
2198
+ "df",
2199
+ "find",
2200
+ "grep",
2201
+ "rg",
2202
+ "fd",
2203
+ "tree",
2204
+ "pwd",
2205
+ "date",
2206
+ "uname",
2207
+ "which",
2208
+ "echo",
2209
+ "sort",
2210
+ "uniq",
2211
+ "diff",
2212
+ "basename",
2213
+ "dirname",
2214
+ "realpath",
2215
+ "ps",
2216
+ "env"
2217
+ ].map((name) => [name, "read"]),
2218
+ // Writes. Recoverable or not, they change the machine.
2219
+ ...[
2220
+ "rm",
2221
+ "mv",
2222
+ "cp",
2223
+ "mkdir",
2224
+ "rmdir",
2225
+ "touch",
2226
+ "chmod",
2227
+ "chown",
2228
+ "ln",
2229
+ "tee",
2230
+ "truncate",
2231
+ "dd",
2232
+ "kill",
2233
+ "killall",
2234
+ "make",
2235
+ "cargo",
2236
+ "swift",
2237
+ "xcodebuild",
2238
+ "gradle",
2239
+ "docker",
2240
+ "brew",
2241
+ "apt",
2242
+ "yum"
2243
+ ].map((name) => [name, "write"]),
2244
+ // A way out. See the trifecta note above.
2245
+ ...["curl", "wget", "nc", "ncat", "telnet", "ssh", "scp", "rsync", "ftp", "http", "httpie"].map((name) => [name, "network"]),
2246
+ // Every command at once, wearing one name.
2247
+ ...["sh", "bash", "zsh", "fish", "python", "python3", "node", "ruby", "perl", "osascript", "eval"].map((name) => [name, "interpreter"])
2248
+ ]);
2249
+ var SUBCOMMAND_READS = /* @__PURE__ */ new Map([
2250
+ [
2251
+ "git",
2252
+ /* @__PURE__ */ new Set([
2253
+ "status",
2254
+ "log",
2255
+ "diff",
2256
+ "show",
2257
+ "branch",
2258
+ "remote",
2259
+ "describe",
2260
+ "blame",
2261
+ "shortlog",
2262
+ "ls-files",
2263
+ "rev-parse",
2264
+ "config"
2265
+ ])
2266
+ ],
2267
+ ["npm", /* @__PURE__ */ new Set(["ls", "list", "view", "outdated", "why", "config"])],
2268
+ ["yarn", /* @__PURE__ */ new Set(["list", "why", "info", "config"])],
2269
+ ["docker", /* @__PURE__ */ new Set(["ps", "images", "logs", "inspect"])]
2270
+ ]);
2271
+ var MAX_OUTPUT_BYTES = 256 * 1024;
2272
+ var TIMEOUT_MS = 2e4;
2273
+ function describe2(argv) {
2274
+ return argv.join(" ");
2275
+ }
2276
+ function programOf(argv) {
2277
+ const first = argv[0] ?? "";
2278
+ return first.split("/").pop() ?? first;
2279
+ }
2280
+ function classify(argv, policy) {
2281
+ const program = programOf(argv);
2282
+ if (policy.allow.includes(program)) return "read";
2283
+ const reads = SUBCOMMAND_READS.get(program);
2284
+ if (reads) {
2285
+ const sub = argv.slice(1).find((one) => !one.startsWith("-"));
2286
+ return sub && reads.has(sub) ? "read" : "write";
2287
+ }
2288
+ return CLASSES.get(program) ?? "unknown";
2289
+ }
2290
+ function judge(argv, policy) {
2291
+ const program = programOf(argv);
2292
+ if (!argv[0]) {
2293
+ return { commandClass: "unknown", automatic: false, refusal: "No command was given.", reason: "empty" };
2294
+ }
2295
+ if (policy.deny.includes(program)) {
2296
+ return {
2297
+ commandClass: "unknown",
2298
+ automatic: false,
2299
+ refusal: `This machine refuses "${program}".`,
2300
+ reason: "on this machine's deny list"
2301
+ };
2302
+ }
2303
+ const commandClass = classify(argv, policy);
2304
+ if (commandClass === "network") {
2305
+ return {
2306
+ commandClass,
2307
+ automatic: false,
2308
+ 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.`,
2309
+ reason: "reads private data and has a way out"
2310
+ };
2311
+ }
2312
+ if (commandClass === "interpreter") {
2313
+ return {
2314
+ commandClass,
2315
+ automatic: false,
2316
+ refusal: `"${program}" runs a language, which is every command at once. Ask for the specific command instead.`,
2317
+ reason: "an interpreter is not one command"
2318
+ };
2319
+ }
2320
+ const outside = pathOutsideRoots(argv, policy.roots);
2321
+ if (outside) {
2322
+ return {
2323
+ commandClass,
2324
+ automatic: false,
2325
+ refusal: `${outside} is outside the folders this machine may read.`,
2326
+ reason: "outside the roots"
2327
+ };
2328
+ }
2329
+ if (policy.mode === "plan") {
2330
+ return {
2331
+ commandClass,
2332
+ automatic: false,
2333
+ reason: "this machine is in plan mode and runs nothing"
2334
+ };
2335
+ }
2336
+ return {
2337
+ commandClass,
2338
+ automatic: policy.mode === "auto-read" && commandClass === "read",
2339
+ reason: commandClass === "read" ? "reads and changes nothing" : commandClass === "write" ? "changes this machine" : "not a command this machine recognises"
2340
+ };
2341
+ }
2342
+ function pathOutsideRoots(argv, roots) {
2343
+ for (const argument of argv.slice(1)) {
2344
+ if (argument.startsWith("-")) continue;
2345
+ if (!argument.startsWith("/") && !argument.startsWith("~") && !argument.startsWith(".") && !argument.includes("/")) {
2346
+ continue;
2347
+ }
2348
+ const resolved = expand(argument, roots[0] ?? process.cwd());
2349
+ if (!roots.some((root) => resolved === root || resolved.startsWith(`${root}/`))) {
2350
+ return argument;
2351
+ }
2352
+ }
2353
+ return void 0;
2354
+ }
2355
+ function expand(argument, base) {
2356
+ const home = process.env["HOME"] ?? "";
2357
+ const withHome = argument.startsWith("~") ? join4(home, argument.slice(1)) : argument;
2358
+ return isAbsolute2(withHome) ? resolvePath(withHome) : resolvePath(base, withHome);
2359
+ }
2360
+ async function runCommand(argv, policy) {
2361
+ const verdict = judge(argv, policy);
2362
+ if (verdict.refusal) return { ok: false, text: verdict.refusal };
2363
+ if (policy.mode === "plan") {
2364
+ return { ok: false, text: `Plan mode: this machine did not run \`${describe2(argv)}\`.` };
2365
+ }
2366
+ const cwd = policy.roots[0];
2367
+ if (!cwd) return { ok: false, text: "This machine has no folders it may read." };
2368
+ try {
2369
+ if (!statSync2(cwd).isDirectory()) return { ok: false, text: `${cwd} is not a folder.` };
2370
+ } catch {
2371
+ return { ok: false, text: `${cwd} does not exist.` };
2372
+ }
2373
+ return new Promise((resolve8) => {
2374
+ const child = spawn2(argv[0], argv.slice(1), {
2375
+ cwd,
2376
+ // NO shell. With one, every character a model can produce is a character
2377
+ // the shell can act on, and the argument list stops meaning anything.
2378
+ shell: false,
2379
+ /*
2380
+ A bare environment.
2381
+
2382
+ This process holds the credential that authorises the machine, and
2383
+ handing it to a subprocess a model chose would mean `env` — or anything
2384
+ that prints its environment — returning that key.
2385
+ */
2386
+ env: {
2387
+ PATH: process.env["PATH"] ?? "/usr/bin:/bin",
2388
+ HOME: process.env["HOME"] ?? "",
2389
+ LANG: process.env["LANG"] ?? "C"
2390
+ }
2391
+ });
2392
+ let output = "";
2393
+ let truncated = false;
2394
+ const collect = (chunk) => {
2395
+ if (truncated) return;
2396
+ output += chunk.toString("utf8");
2397
+ if (output.length > MAX_OUTPUT_BYTES) {
2398
+ output = output.slice(0, MAX_OUTPUT_BYTES);
2399
+ truncated = true;
2400
+ child.kill("SIGKILL");
2401
+ }
2402
+ };
2403
+ child.stdout.on("data", collect);
2404
+ child.stderr.on("data", collect);
2405
+ const timer = setTimeout(() => child.kill("SIGKILL"), TIMEOUT_MS);
2406
+ child.on("error", (error) => {
2407
+ clearTimeout(timer);
2408
+ resolve8({ ok: false, text: `Could not run it: ${error.message}` });
2409
+ });
2410
+ child.on("close", (code) => {
2411
+ clearTimeout(timer);
2412
+ const notes = [
2413
+ truncated ? `
2414
+
2415
+ [output cut off at ${MAX_OUTPUT_BYTES} bytes]` : "",
2416
+ code !== 0 && code !== null ? `
2417
+
2418
+ [exit code ${code}]` : ""
2419
+ ].join("");
2420
+ resolve8({ ok: code === 0, text: `$ ${describe2(argv)}
2421
+
2422
+ ${output}${notes}` });
2423
+ });
2424
+ });
2425
+ }
2426
+
2179
2427
  // src/commands/agent.ts
2180
2428
  async function said(response, fallback) {
2181
2429
  const body = await response.json().catch(() => void 0);
@@ -2199,18 +2447,32 @@ function locate(roots, requested) {
2199
2447
  throw new Error(`${requested} is not inside any allowed folder (${roots.join(", ")})`);
2200
2448
  }
2201
2449
  function uncontested(target) {
2202
- if (!existsSync4(target)) return target;
2450
+ if (!existsSync5(target)) return target;
2203
2451
  const dot = target.lastIndexOf(".");
2204
2452
  const stem = dot > target.lastIndexOf("/") && dot !== -1 ? target.slice(0, dot) : target;
2205
2453
  const extension = stem === target ? "" : target.slice(dot);
2206
2454
  for (let n = 1; n < 1e3; n += 1) {
2207
2455
  const candidate = `${stem} (${n})${extension}`;
2208
- if (!existsSync4(candidate)) return candidate;
2456
+ if (!existsSync5(candidate)) return candidate;
2209
2457
  }
2210
2458
  throw new Error(`${target} and a thousand names beside it are taken.`);
2211
2459
  }
2212
2460
  async function answer(context, apiUrl, token, roots, request) {
2213
2461
  let located;
2462
+ if (request.kind === "run_command") {
2463
+ if (!request.argv || request.argv.length === 0) {
2464
+ return { ok: false, error: "No command was given." };
2465
+ }
2466
+ const policy = { mode: "ask", allow: [], deny: [], roots };
2467
+ const outcome = await runCommand(request.argv, policy);
2468
+ if (!outcome.ok) return { ok: false, error: outcome.text };
2469
+ return upload(
2470
+ apiUrl,
2471
+ token,
2472
+ `${(request.argv[0] ?? "output").split("/").pop()}.txt`,
2473
+ Buffer.from(outcome.text, "utf8")
2474
+ );
2475
+ }
2214
2476
  try {
2215
2477
  located = locate(roots, request.path);
2216
2478
  } catch (error) {
@@ -2231,10 +2493,10 @@ async function answer(context, apiUrl, token, roots, request) {
2231
2493
  }
2232
2494
  try {
2233
2495
  let target = located;
2234
- if (existsSync4(located) && statSync2(located).isDirectory()) {
2496
+ if (existsSync5(located) && statSync3(located).isDirectory()) {
2235
2497
  const disposition = fetched.headers.get("content-disposition") ?? "";
2236
2498
  const named = /filename="([^"]+)"/.exec(disposition)?.[1];
2237
- target = join4(located, basename(named ?? "file"));
2499
+ target = join5(located, basename(named ?? "file"));
2238
2500
  }
2239
2501
  target = uncontested(target);
2240
2502
  writeFileSync4(target, downloaded, { flag: "wx" });
@@ -2257,12 +2519,12 @@ ${downloaded.length} bytes
2257
2519
  let filename = request.path.split("/").pop() ?? "file";
2258
2520
  if (request.kind === "list_dir") {
2259
2521
  try {
2260
- const stats = statSync2(located);
2522
+ const stats = statSync3(located);
2261
2523
  if (!stats.isDirectory()) return { ok: false, error: `${request.path} is not a folder.` };
2262
2524
  const entries = readdirSync(located, { withFileTypes: true }).filter((entry) => !entry.name.startsWith(".")).slice(0, MAX_LISTED).map((entry) => {
2263
2525
  if (entry.isDirectory()) return `${entry.name}/`;
2264
2526
  try {
2265
- return `${entry.name} ${sizeOf(join4(located, entry.name))}`;
2527
+ return `${entry.name} ${sizeOf(join5(located, entry.name))}`;
2266
2528
  } catch {
2267
2529
  return entry.name;
2268
2530
  }
@@ -2280,9 +2542,9 @@ ${listing}
2280
2542
  }
2281
2543
  }
2282
2544
  try {
2283
- const stats = statSync2(located);
2545
+ const stats = statSync3(located);
2284
2546
  if (!stats.isFile()) return { ok: false, error: `${request.path} is not a file.` };
2285
- bytes = readFileSync4(located);
2547
+ bytes = readFileSync5(located);
2286
2548
  } catch (error) {
2287
2549
  return { ok: false, error: error instanceof Error ? error.message : "could not read it" };
2288
2550
  }
@@ -2322,18 +2584,9 @@ async function upload(apiUrl, token, filename, bytes) {
2322
2584
  if (!stored.attachToken) return { ok: false, error: "the upload returned no reference" };
2323
2585
  return { ok: true, attachToken: stored.attachToken, bytes: bytes.length };
2324
2586
  }
2325
- function defaultRoots(home = homedir2(), isDirectory = (path) => {
2326
- try {
2327
- return statSync2(path).isDirectory();
2328
- } catch {
2329
- return false;
2330
- }
2331
- }) {
2332
- return ["Desktop", "Documents", "Downloads"].map((name) => join4(home, name)).filter(isDirectory);
2333
- }
2334
2587
  var MAX_LISTED = 200;
2335
2588
  function sizeOf(path) {
2336
- const size = statSync2(path).size;
2589
+ const size = statSync3(path).size;
2337
2590
  if (size < 1024) return `${size} B`;
2338
2591
  if (size < 1024 * 1024) return `${Math.round(size / 1024)} KB`;
2339
2592
  return `${(size / (1024 * 1024)).toFixed(1)} MB`;
@@ -2347,24 +2600,14 @@ async function agentCommand(context) {
2347
2600
  const roots = (listFlag(context.args, "root") ?? []).map(
2348
2601
  (one) => resolve3(one.startsWith("~") ? resolve3(homedir2(), one.slice(1).replace(/^[/\\]/, "")) : one)
2349
2602
  );
2350
- if (roots.length === 0) {
2351
- const usual = defaultRoots();
2352
- if (usual.length === 0) {
2353
- context.error(
2354
- "This machine has no Desktop, Documents or Downloads folder, so there is nothing to offer by default. Say which folders it may read from:"
2355
- );
2356
- context.error("");
2357
- context.error(" pm agent --root ~/projects");
2358
- return 1;
2359
- }
2360
- roots.push(...usual);
2361
- context.print(
2362
- `Reading from ${usual.map((path) => path.replace(homedir2(), "~")).join(", ")} \u2014 nothing outside them. Use --root to choose your own.`
2363
- );
2364
- }
2603
+ const everywhere = roots.length === 0;
2604
+ if (everywhere) roots.push("/");
2605
+ context.print(
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.`
2607
+ );
2365
2608
  for (const root of roots) {
2366
2609
  try {
2367
- if (!statSync2(root).isDirectory()) {
2610
+ if (!statSync3(root).isDirectory()) {
2368
2611
  context.error(`${root} is not a folder.`);
2369
2612
  return 1;
2370
2613
  }
@@ -2461,7 +2704,7 @@ async function agentCommand(context) {
2461
2704
  // src/commands/google.ts
2462
2705
  import { writeFileSync as writeFileSync5 } from "node:fs";
2463
2706
  import { basename as basename2, resolve as resolve4 } from "node:path";
2464
- import { readFileSync as readFileSync5 } from "node:fs";
2707
+ import { readFileSync as readFileSync6 } from "node:fs";
2465
2708
  async function callApi(context, path, init = {}) {
2466
2709
  const credential = context.resolved.credential;
2467
2710
  if (!credential) {
@@ -2538,7 +2781,7 @@ async function drivePut(context, path) {
2538
2781
  }
2539
2782
  let bytes;
2540
2783
  try {
2541
- bytes = readFileSync5(resolve4(path));
2784
+ bytes = readFileSync6(resolve4(path));
2542
2785
  } catch {
2543
2786
  context.error(`Cannot read ${path}.`);
2544
2787
  return 1;
@@ -2622,7 +2865,7 @@ async function mailCommand(context) {
2622
2865
  }
2623
2866
 
2624
2867
  // src/commands/requests.ts
2625
- import { existsSync as existsSync5, writeFileSync as writeFileSync6 } from "node:fs";
2868
+ import { existsSync as existsSync6, writeFileSync as writeFileSync6 } from "node:fs";
2626
2869
  import { resolve as resolve5 } from "node:path";
2627
2870
  async function requestsCommand(context) {
2628
2871
  if (context.args.words[1] === "get") return collectCommand(context);
@@ -2692,7 +2935,7 @@ async function collectCommand(context) {
2692
2935
  }
2693
2936
  const name = stringFlag(context.args, "output", "o") ?? filename;
2694
2937
  const target = resolve5(name);
2695
- if (existsSync5(target)) {
2938
+ if (existsSync6(target)) {
2696
2939
  context.error(`${target} already exists. Pass --output to write somewhere else.`);
2697
2940
  return 1;
2698
2941
  }
@@ -2702,14 +2945,14 @@ async function collectCommand(context) {
2702
2945
  }
2703
2946
 
2704
2947
  // src/workspace.ts
2705
- import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "node:fs";
2706
- import { dirname as dirname3, join as join5, resolve as resolvePath } from "node:path";
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";
2707
2950
  var WORKSPACE_FILE = ".persistmemory.json";
2708
2951
  function findWorkspace(from = process.cwd()) {
2709
- let dir = resolvePath(from);
2952
+ let dir = resolvePath2(from);
2710
2953
  for (; ; ) {
2711
- const file = join5(dir, WORKSPACE_FILE);
2712
- if (existsSync6(file)) {
2954
+ const file = join6(dir, WORKSPACE_FILE);
2955
+ if (existsSync7(file)) {
2713
2956
  const config = readWorkspace(file);
2714
2957
  if (config) return { file, dir, config };
2715
2958
  }
@@ -2720,7 +2963,7 @@ function findWorkspace(from = process.cwd()) {
2720
2963
  }
2721
2964
  function readWorkspace(file) {
2722
2965
  try {
2723
- const parsed = JSON.parse(readFileSync6(file, "utf8"));
2966
+ const parsed = JSON.parse(readFileSync7(file, "utf8"));
2724
2967
  const space = parsed.space;
2725
2968
  if (space && typeof space === "object" && typeof space.id === "string" && space.id !== "" && typeof space.name === "string") {
2726
2969
  return { space: { id: space.id, name: space.name } };
@@ -2731,7 +2974,7 @@ function readWorkspace(file) {
2731
2974
  }
2732
2975
  }
2733
2976
  function writeWorkspace(dir, config) {
2734
- const file = join5(dir, WORKSPACE_FILE);
2977
+ const file = join6(dir, WORKSPACE_FILE);
2735
2978
  writeFileSync7(file, `${JSON.stringify(config, null, 2)}
2736
2979
  `, "utf8");
2737
2980
  return file;
@@ -3001,7 +3244,7 @@ function message(error) {
3001
3244
  }
3002
3245
 
3003
3246
  // src/commands/maintain.ts
3004
- import { existsSync as existsSync7, rmSync } from "node:fs";
3247
+ import { existsSync as existsSync8, rmSync } from "node:fs";
3005
3248
  import { spawnSync } from "node:child_process";
3006
3249
  import { dirname as dirname4, resolve as resolve7 } from "node:path";
3007
3250
  import { fileURLToPath } from "node:url";
@@ -3067,7 +3310,7 @@ Delete the file it runs from: ${processPath()}`
3067
3310
  return 0;
3068
3311
  }
3069
3312
  async function deleteCommand(context) {
3070
- if (!existsSync7(context.paths.dir)) {
3313
+ if (!existsSync8(context.paths.dir)) {
3071
3314
  context.print(`Nothing to delete: ${context.paths.dir} does not exist.`);
3072
3315
  return 0;
3073
3316
  }
@@ -3120,12 +3363,12 @@ import { randomUUID } from "node:crypto";
3120
3363
  import { relative as relative2 } from "node:path";
3121
3364
 
3122
3365
  // src/events.ts
3123
- import { appendFileSync, existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync7 } from "node:fs";
3124
- import { join as join6 } from "node:path";
3366
+ import { appendFileSync, existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync8 } from "node:fs";
3367
+ import { join as join7 } from "node:path";
3125
3368
  function openSessionLog(paths, id) {
3126
- const directory = join6(paths.dir, "sessions");
3369
+ const directory = join7(paths.dir, "sessions");
3127
3370
  mkdirSync3(directory, { recursive: true, mode: 448 });
3128
- const path = join6(directory, `${id}.jsonl`);
3371
+ const path = join7(directory, `${id}.jsonl`);
3129
3372
  return {
3130
3373
  id,
3131
3374
  path,
@@ -3137,8 +3380,8 @@ function openSessionLog(paths, id) {
3137
3380
  }
3138
3381
  },
3139
3382
  read() {
3140
- if (!existsSync8(path)) return [];
3141
- return readFileSync7(path, "utf8").split("\n").filter((line) => line.trim() !== "").flatMap((line) => {
3383
+ if (!existsSync9(path)) return [];
3384
+ return readFileSync8(path, "utf8").split("\n").filter((line) => line.trim() !== "").flatMap((line) => {
3142
3385
  try {
3143
3386
  return [JSON.parse(line)];
3144
3387
  } catch {
@@ -3401,7 +3644,7 @@ async function write2(args) {
3401
3644
  }
3402
3645
 
3403
3646
  // src/commands/memory.ts
3404
- import { readFileSync as readFileSync8 } from "node:fs";
3647
+ import { readFileSync as readFileSync9 } from "node:fs";
3405
3648
 
3406
3649
  // src/spaces.ts
3407
3650
  async function spacesFor(context, client, env = process.env) {
@@ -3465,7 +3708,7 @@ async function rememberCommand(context) {
3465
3708
  let text;
3466
3709
  if (file) {
3467
3710
  try {
3468
- text = readFileSync8(file, "utf8");
3711
+ text = readFileSync9(file, "utf8");
3469
3712
  } catch {
3470
3713
  context.error(`Could not read ${file}.`);
3471
3714
  return 1;