@persistmemory/cli 0.2.0 → 0.2.2

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
@@ -1525,7 +1525,7 @@ function shortDate(iso) {
1525
1525
  }
1526
1526
 
1527
1527
  // src/help.ts
1528
- var VERSION = "0.1.2";
1528
+ var VERSION = true ? "0.2.2" : versionFromManifest();
1529
1529
  var PACKAGE = "@persistmemory/cli";
1530
1530
  var HELP = `
1531
1531
  pm \u2014 PersistMemory from your terminal
@@ -1744,7 +1744,17 @@ async function startLoopback(options = {}) {
1744
1744
  // This page is one-use and holds a result; nothing should keep it.
1745
1745
  "cache-control": "no-store",
1746
1746
  // It never loads anything, so it is not allowed to.
1747
- "content-security-policy": "default-src 'none'; style-src 'unsafe-inline'"
1747
+ "content-security-policy": "default-src 'none'; style-src 'unsafe-inline'",
1748
+ /*
1749
+ Not kept alive, because a kept socket keeps the PROCESS alive.
1750
+
1751
+ `server.close()` stops new connections and leaves established ones
1752
+ open, and a browser holds this one open by default — so `pm auth login`
1753
+ printed "Signed in" and then sat there until somebody pressed Ctrl+C.
1754
+ The page is the last thing this server ever serves; there is nothing to
1755
+ reuse the connection for.
1756
+ */
1757
+ connection: "close"
1748
1758
  });
1749
1759
  response.end(donePage(callback));
1750
1760
  resolveCallback?.(callback);
@@ -1776,7 +1786,9 @@ async function startLoopback(options = {}) {
1776
1786
  },
1777
1787
  close() {
1778
1788
  clearTimeout(timer);
1789
+ server.closeAllConnections?.();
1779
1790
  server.close();
1791
+ server.unref();
1780
1792
  }
1781
1793
  };
1782
1794
  }
@@ -2062,7 +2074,7 @@ async function clientFor(resolved, deps) {
2062
2074
  });
2063
2075
  }
2064
2076
  async function currentCredential(resolved, deps) {
2065
- const credential = resolved.credential;
2077
+ const credential = resolved.fromEnvironment ? resolved.credential : readCredentials(deps.paths)[resolved.profile] ?? resolved.credential;
2066
2078
  if (!credential) throw new NotSignedIn();
2067
2079
  if (!isExpired(credential, deps.now?.() ?? Date.now())) return credential;
2068
2080
  if (!credential.refreshToken || !resolved.clientId) {
@@ -2157,8 +2169,8 @@ async function readStdin() {
2157
2169
  // src/commands/agent.ts
2158
2170
  import { hostname } from "node:os";
2159
2171
  import { homedir as homedir2 } from "node:os";
2160
- import { join as join4, resolve as resolve3 } from "node:path";
2161
- import { readFileSync as readFileSync4, readdirSync, statSync as statSync2 } from "node:fs";
2172
+ import { basename, join as join4, resolve as resolve3 } from "node:path";
2173
+ import { existsSync as existsSync4, readFileSync as readFileSync4, readdirSync, statSync as statSync2, writeFileSync as writeFileSync4 } from "node:fs";
2162
2174
 
2163
2175
  // src/files.ts
2164
2176
  import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync, statSync, writeFileSync as writeFileSync3 } from "node:fs";
@@ -2283,6 +2295,17 @@ function locate(roots, requested) {
2283
2295
  }
2284
2296
  throw new Error(`${requested} is not inside any allowed folder (${roots.join(", ")})`);
2285
2297
  }
2298
+ function uncontested(target) {
2299
+ if (!existsSync4(target)) return target;
2300
+ const dot = target.lastIndexOf(".");
2301
+ const stem = dot > target.lastIndexOf("/") && dot !== -1 ? target.slice(0, dot) : target;
2302
+ const extension = stem === target ? "" : target.slice(dot);
2303
+ for (let n = 1; n < 1e3; n += 1) {
2304
+ const candidate = `${stem} (${n})${extension}`;
2305
+ if (!existsSync4(candidate)) return candidate;
2306
+ }
2307
+ throw new Error(`${target} and a thousand names beside it are taken.`);
2308
+ }
2286
2309
  async function answer(context, apiUrl, token, roots, request) {
2287
2310
  let located;
2288
2311
  try {
@@ -2290,6 +2313,43 @@ async function answer(context, apiUrl, token, roots, request) {
2290
2313
  } catch (error) {
2291
2314
  return { ok: false, error: error instanceof Error ? error.message : "refused" };
2292
2315
  }
2316
+ if (request.kind === "write_file") {
2317
+ if (!request.sourceUrl) return { ok: false, error: "There was nothing to write." };
2318
+ let downloaded;
2319
+ let fetched;
2320
+ try {
2321
+ fetched = await fetch(request.sourceUrl);
2322
+ if (!fetched.ok) {
2323
+ return { ok: false, error: await said(fetched, "The file could not be fetched") };
2324
+ }
2325
+ downloaded = Buffer.from(await fetched.arrayBuffer());
2326
+ } catch {
2327
+ return { ok: false, error: "The file could not be fetched from the service." };
2328
+ }
2329
+ try {
2330
+ let target = located;
2331
+ if (existsSync4(located) && statSync2(located).isDirectory()) {
2332
+ const disposition = fetched.headers.get("content-disposition") ?? "";
2333
+ const named = /filename="([^"]+)"/.exec(disposition)?.[1];
2334
+ target = join4(located, basename(named ?? "file"));
2335
+ }
2336
+ target = uncontested(target);
2337
+ writeFileSync4(target, downloaded, { flag: "wx" });
2338
+ return upload(
2339
+ apiUrl,
2340
+ token,
2341
+ "written.txt",
2342
+ Buffer.from(`Saved to ${target}
2343
+ ${downloaded.length} bytes
2344
+ `, "utf8")
2345
+ );
2346
+ } catch (error) {
2347
+ return {
2348
+ ok: false,
2349
+ error: error instanceof Error ? error.message : "could not write it"
2350
+ };
2351
+ }
2352
+ }
2293
2353
  let bytes;
2294
2354
  let filename = request.path.split("/").pop() ?? "file";
2295
2355
  if (request.kind === "list_dir") {
@@ -2475,8 +2535,8 @@ async function agentCommand(context) {
2475
2535
  }
2476
2536
 
2477
2537
  // src/commands/google.ts
2478
- import { writeFileSync as writeFileSync4 } from "node:fs";
2479
- import { basename, resolve as resolve4 } from "node:path";
2538
+ import { writeFileSync as writeFileSync5 } from "node:fs";
2539
+ import { basename as basename2, resolve as resolve4 } from "node:path";
2480
2540
  import { readFileSync as readFileSync5 } from "node:fs";
2481
2541
  async function callApi(context, path, init = {}) {
2482
2542
  const credential = context.resolved.credential;
@@ -2542,8 +2602,8 @@ async function driveGet(context, fileId) {
2542
2602
  const disposition = response.headers.get("content-disposition") ?? "";
2543
2603
  const named = /filename="([^"]+)"/.exec(disposition)?.[1];
2544
2604
  const out = stringFlag(context.args, "out");
2545
- const target = resolve4(out ?? basename(named ?? fileId));
2546
- writeFileSync4(target, Buffer.from(await response.arrayBuffer()));
2605
+ const target = resolve4(out ?? basename2(named ?? fileId));
2606
+ writeFileSync5(target, Buffer.from(await response.arrayBuffer()));
2547
2607
  context.print(target);
2548
2608
  return 0;
2549
2609
  }
@@ -2559,7 +2619,7 @@ async function drivePut(context, path) {
2559
2619
  context.error(`Cannot read ${path}.`);
2560
2620
  return 1;
2561
2621
  }
2562
- const name = stringFlag(context.args, "name") ?? basename(path);
2622
+ const name = stringFlag(context.args, "name") ?? basename2(path);
2563
2623
  const response = await callApi(
2564
2624
  context,
2565
2625
  `/api/v1/google/drive/files?name=${encodeURIComponent(name)}`,
@@ -2638,7 +2698,7 @@ async function mailCommand(context) {
2638
2698
  }
2639
2699
 
2640
2700
  // src/commands/requests.ts
2641
- import { existsSync as existsSync4, writeFileSync as writeFileSync5 } from "node:fs";
2701
+ import { existsSync as existsSync5, writeFileSync as writeFileSync6 } from "node:fs";
2642
2702
  import { resolve as resolve5 } from "node:path";
2643
2703
  async function requestsCommand(context) {
2644
2704
  if (context.args.words[1] === "get") return collectCommand(context);
@@ -2708,24 +2768,24 @@ async function collectCommand(context) {
2708
2768
  }
2709
2769
  const name = stringFlag(context.args, "output", "o") ?? filename;
2710
2770
  const target = resolve5(name);
2711
- if (existsSync4(target)) {
2771
+ if (existsSync5(target)) {
2712
2772
  context.error(`${target} already exists. Pass --output to write somewhere else.`);
2713
2773
  return 1;
2714
2774
  }
2715
- writeFileSync5(target, new Uint8Array(await file.arrayBuffer()));
2775
+ writeFileSync6(target, new Uint8Array(await file.arrayBuffer()));
2716
2776
  context.print(`Wrote ${target}`);
2717
2777
  return 0;
2718
2778
  }
2719
2779
 
2720
2780
  // src/workspace.ts
2721
- import { existsSync as existsSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "node:fs";
2781
+ import { existsSync as existsSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "node:fs";
2722
2782
  import { dirname as dirname3, join as join5, resolve as resolvePath } from "node:path";
2723
2783
  var WORKSPACE_FILE = ".persistmemory.json";
2724
2784
  function findWorkspace(from = process.cwd()) {
2725
2785
  let dir = resolvePath(from);
2726
2786
  for (; ; ) {
2727
2787
  const file = join5(dir, WORKSPACE_FILE);
2728
- if (existsSync5(file)) {
2788
+ if (existsSync6(file)) {
2729
2789
  const config = readWorkspace(file);
2730
2790
  if (config) return { file, dir, config };
2731
2791
  }
@@ -2748,7 +2808,7 @@ function readWorkspace(file) {
2748
2808
  }
2749
2809
  function writeWorkspace(dir, config) {
2750
2810
  const file = join5(dir, WORKSPACE_FILE);
2751
- writeFileSync6(file, `${JSON.stringify(config, null, 2)}
2811
+ writeFileSync7(file, `${JSON.stringify(config, null, 2)}
2752
2812
  `, "utf8");
2753
2813
  return file;
2754
2814
  }
@@ -3017,7 +3077,7 @@ function message(error) {
3017
3077
  }
3018
3078
 
3019
3079
  // src/commands/maintain.ts
3020
- import { existsSync as existsSync6, rmSync } from "node:fs";
3080
+ import { existsSync as existsSync7, rmSync } from "node:fs";
3021
3081
  import { spawnSync } from "node:child_process";
3022
3082
  import { dirname as dirname4, resolve as resolve7 } from "node:path";
3023
3083
  import { fileURLToPath } from "node:url";
@@ -3061,7 +3121,7 @@ Delete the file it runs from: ${processPath()}`
3061
3121
  return 0;
3062
3122
  }
3063
3123
  async function deleteCommand(context) {
3064
- if (!existsSync6(context.paths.dir)) {
3124
+ if (!existsSync7(context.paths.dir)) {
3065
3125
  context.print(`Nothing to delete: ${context.paths.dir} does not exist.`);
3066
3126
  return 0;
3067
3127
  }
@@ -3114,7 +3174,7 @@ import { randomUUID } from "node:crypto";
3114
3174
  import { relative as relative2 } from "node:path";
3115
3175
 
3116
3176
  // src/events.ts
3117
- import { appendFileSync, existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync7 } from "node:fs";
3177
+ import { appendFileSync, existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync7 } from "node:fs";
3118
3178
  import { join as join6 } from "node:path";
3119
3179
  function openSessionLog(paths, id) {
3120
3180
  const directory = join6(paths.dir, "sessions");
@@ -3131,7 +3191,7 @@ function openSessionLog(paths, id) {
3131
3191
  }
3132
3192
  },
3133
3193
  read() {
3134
- if (!existsSync7(path)) return [];
3194
+ if (!existsSync8(path)) return [];
3135
3195
  return readFileSync7(path, "utf8").split("\n").filter((line) => line.trim() !== "").flatMap((line) => {
3136
3196
  try {
3137
3197
  return [JSON.parse(line)];
@@ -3747,6 +3807,16 @@ async function dispatch(context) {
3747
3807
  case "setup":
3748
3808
  case "init":
3749
3809
  return setupCommand(context);
3810
+ /*
3811
+ `pm version` as well as `--version`.
3812
+
3813
+ It is what people type, and answering "Unknown command" to somebody
3814
+ asking which version they are running — while a flag two characters away
3815
+ answers it — is a needless dead end.
3816
+ */
3817
+ case "version":
3818
+ context.print(VERSION);
3819
+ return 0;
3750
3820
  case "update":
3751
3821
  case "upgrade":
3752
3822
  return updateCommand(context);