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