@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 +90 -20
- package/dist/bin.js.map +3 -3
- package/dist/index.js +90 -20
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
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.
|
|
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
|
|
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 ??
|
|
2546
|
-
|
|
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") ??
|
|
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
|
|
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 (
|
|
2771
|
+
if (existsSync5(target)) {
|
|
2712
2772
|
context.error(`${target} already exists. Pass --output to write somewhere else.`);
|
|
2713
2773
|
return 1;
|
|
2714
2774
|
}
|
|
2715
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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);
|