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