@persistmemory/cli 0.1.1 → 0.1.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 +215 -49
- package/dist/bin.js.map +4 -4
- package/dist/index.js +215 -49
- package/dist/index.js.map +4 -4
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -344,7 +344,7 @@ var SignalFired = class extends Error {
|
|
|
344
344
|
};
|
|
345
345
|
function untilAborted(work, signal) {
|
|
346
346
|
work.catch(() => void 0);
|
|
347
|
-
return new Promise((
|
|
347
|
+
return new Promise((resolve7, reject) => {
|
|
348
348
|
if (signal.aborted) {
|
|
349
349
|
reject(new SignalFired());
|
|
350
350
|
return;
|
|
@@ -354,7 +354,7 @@ function untilAborted(work, signal) {
|
|
|
354
354
|
work.then(
|
|
355
355
|
(value) => {
|
|
356
356
|
signal.removeEventListener("abort", onAbort);
|
|
357
|
-
|
|
357
|
+
resolve7(value);
|
|
358
358
|
},
|
|
359
359
|
(error) => {
|
|
360
360
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -364,14 +364,14 @@ function untilAborted(work, signal) {
|
|
|
364
364
|
});
|
|
365
365
|
}
|
|
366
366
|
function defaultSleep(ms, signal) {
|
|
367
|
-
return new Promise((
|
|
367
|
+
return new Promise((resolve7, reject) => {
|
|
368
368
|
if (signal?.aborted) {
|
|
369
369
|
reject(new AbortError());
|
|
370
370
|
return;
|
|
371
371
|
}
|
|
372
372
|
const timer = setTimeout(() => {
|
|
373
373
|
signal?.removeEventListener("abort", onAbort);
|
|
374
|
-
|
|
374
|
+
resolve7();
|
|
375
375
|
}, ms);
|
|
376
376
|
function onAbort() {
|
|
377
377
|
clearTimeout(timer);
|
|
@@ -558,6 +558,58 @@ var Spaces = class {
|
|
|
558
558
|
async create(params, options) {
|
|
559
559
|
return this.#http.post("/api/v1/spaces", params, options);
|
|
560
560
|
}
|
|
561
|
+
/**
|
|
562
|
+
* Deletes a Space. You must say what happens to what is in it.
|
|
563
|
+
*
|
|
564
|
+
* There is no default, here or in the API, and that is deliberate: "delete
|
|
565
|
+
* this Space" means the label to some people and everything inside it to
|
|
566
|
+
* others, and a client that guessed would destroy or keep somebody's
|
|
567
|
+
* material without being asked.
|
|
568
|
+
*
|
|
569
|
+
* `delete` never destroys a memory that is filed in another Space as well —
|
|
570
|
+
* that one is detached and left alone. `deleted` and `kept` come back so you
|
|
571
|
+
* can say what actually happened.
|
|
572
|
+
*/
|
|
573
|
+
async delete(id, params, options) {
|
|
574
|
+
return this.#http.delete(
|
|
575
|
+
`/api/v1/spaces/${encodeURIComponent(id)}`,
|
|
576
|
+
params,
|
|
577
|
+
options
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Merges Spaces into a NEW one, leaving every source exactly as it was.
|
|
582
|
+
*
|
|
583
|
+
* Additive, not destructive: a memory ends up in the sources AND the result,
|
|
584
|
+
* every existing search over a source returns what it did before, and
|
|
585
|
+
* undoing it is deleting the Space this returns. A memory in two sources is
|
|
586
|
+
* filed once.
|
|
587
|
+
*/
|
|
588
|
+
async merge(params, options) {
|
|
589
|
+
return this.#http.post(
|
|
590
|
+
"/api/v1/spaces/merge",
|
|
591
|
+
params,
|
|
592
|
+
options
|
|
593
|
+
);
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* The Space this account files into when a capture names none.
|
|
597
|
+
*
|
|
598
|
+
* `{}` — an object with no `space` — means there is no default, which is the
|
|
599
|
+
* normal state rather than a gap. It is also what comes back after the Space
|
|
600
|
+
* somebody chose has been deleted.
|
|
601
|
+
*/
|
|
602
|
+
async getDefault(options) {
|
|
603
|
+
return this.#http.get("/api/v1/spaces/default", void 0, options);
|
|
604
|
+
}
|
|
605
|
+
/** `null` clears it. Not the same as omitting it, which is why the type says so. */
|
|
606
|
+
async setDefault(spaceId, options) {
|
|
607
|
+
return this.#http.patch(
|
|
608
|
+
"/api/v1/spaces/default",
|
|
609
|
+
{ spaceId },
|
|
610
|
+
options
|
|
611
|
+
);
|
|
612
|
+
}
|
|
561
613
|
/** Renaming, retention, and archiving - `archived` is a field, not a verb. */
|
|
562
614
|
async update(id, params, options) {
|
|
563
615
|
return this.#http.patch(`/api/v1/spaces/${encodeURIComponent(id)}`, params, options);
|
|
@@ -920,6 +972,42 @@ var Health = class {
|
|
|
920
972
|
return this.#http.get("/health/ready", void 0, options);
|
|
921
973
|
}
|
|
922
974
|
};
|
|
975
|
+
var Agent = class {
|
|
976
|
+
#http;
|
|
977
|
+
constructor(http) {
|
|
978
|
+
this.#http = http;
|
|
979
|
+
}
|
|
980
|
+
/** The row, including whether it finished and how large the result is. */
|
|
981
|
+
async request(id, options) {
|
|
982
|
+
return this.#http.get(
|
|
983
|
+
`/api/v1/agent/request/${encodeURIComponent(id)}`,
|
|
984
|
+
void 0,
|
|
985
|
+
options
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* A short-lived link to the bytes of a finished request.
|
|
990
|
+
*
|
|
991
|
+
* Returns the URL rather than the file, and that is a deliberate limit of
|
|
992
|
+
* this package rather than an oversight. The transport under every other
|
|
993
|
+
* method parses JSON, retries, and attaches the API key; none of those is
|
|
994
|
+
* right for a hundred-megabyte binary body, and building a second request
|
|
995
|
+
* path inside the SDK to serve one method is how a client ends up with two
|
|
996
|
+
* retry policies that differ only during an outage. Fetch the URL with
|
|
997
|
+
* whatever already streams in your runtime - it needs no credential, which
|
|
998
|
+
* is the whole reason it is signed.
|
|
999
|
+
*
|
|
1000
|
+
* Treat the URL as the file. It is a bearer credential for exactly one
|
|
1001
|
+
* object, it expires in minutes, and it should not be logged or stored.
|
|
1002
|
+
*/
|
|
1003
|
+
async downloadLink(id, options) {
|
|
1004
|
+
return this.#http.get(
|
|
1005
|
+
`/api/v1/agent/request/${encodeURIComponent(id)}/download`,
|
|
1006
|
+
void 0,
|
|
1007
|
+
options
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
923
1011
|
var PersistMemory = class {
|
|
924
1012
|
memories;
|
|
925
1013
|
search;
|
|
@@ -933,6 +1021,7 @@ var PersistMemory = class {
|
|
|
933
1021
|
conversations;
|
|
934
1022
|
integrations;
|
|
935
1023
|
health;
|
|
1024
|
+
agent;
|
|
936
1025
|
#http;
|
|
937
1026
|
constructor(options) {
|
|
938
1027
|
this.#http = new HttpClient(options);
|
|
@@ -948,6 +1037,7 @@ var PersistMemory = class {
|
|
|
948
1037
|
this.conversations = new Conversations(this.#http);
|
|
949
1038
|
this.integrations = new Integrations(this.#http);
|
|
950
1039
|
this.health = new Health(this.#http);
|
|
1040
|
+
this.agent = new Agent(this.#http);
|
|
951
1041
|
}
|
|
952
1042
|
/**
|
|
953
1043
|
* An escape hatch for an endpoint this package has not caught up with.
|
|
@@ -1289,7 +1379,7 @@ function shortDate(iso) {
|
|
|
1289
1379
|
}
|
|
1290
1380
|
|
|
1291
1381
|
// src/help.ts
|
|
1292
|
-
var VERSION = "0.1.
|
|
1382
|
+
var VERSION = "0.1.2";
|
|
1293
1383
|
var PACKAGE = "@persistmemory/cli";
|
|
1294
1384
|
var HELP = `
|
|
1295
1385
|
pm \u2014 PersistMemory from your terminal
|
|
@@ -1336,6 +1426,7 @@ var HELP = `
|
|
|
1336
1426
|
|
|
1337
1427
|
status is the service healthy
|
|
1338
1428
|
requests file requests waiting for you to approve
|
|
1429
|
+
requests get <id> write a finished one to a file here
|
|
1339
1430
|
|
|
1340
1431
|
update install the newest version
|
|
1341
1432
|
uninstall remove pm from this machine
|
|
@@ -1479,8 +1570,8 @@ async function startLoopback(options = {}) {
|
|
|
1479
1570
|
const timeoutMs = options.timeoutMs ?? 5 * 60 * 1e3;
|
|
1480
1571
|
let resolveCallback;
|
|
1481
1572
|
let rejectCallback;
|
|
1482
|
-
const received = new Promise((
|
|
1483
|
-
resolveCallback =
|
|
1573
|
+
const received = new Promise((resolve7, reject) => {
|
|
1574
|
+
resolveCallback = resolve7;
|
|
1484
1575
|
rejectCallback = reject;
|
|
1485
1576
|
});
|
|
1486
1577
|
const server = createServer((request, response) => {
|
|
@@ -1506,9 +1597,9 @@ async function startLoopback(options = {}) {
|
|
|
1506
1597
|
response.end(donePage(callback));
|
|
1507
1598
|
resolveCallback?.(callback);
|
|
1508
1599
|
});
|
|
1509
|
-
await new Promise((
|
|
1600
|
+
await new Promise((resolve7, reject) => {
|
|
1510
1601
|
server.once("error", reject);
|
|
1511
|
-
server.listen(0, "127.0.0.1",
|
|
1602
|
+
server.listen(0, "127.0.0.1", resolve7);
|
|
1512
1603
|
});
|
|
1513
1604
|
const address = server.address();
|
|
1514
1605
|
if (address === null || typeof address === "string") {
|
|
@@ -1770,7 +1861,7 @@ function safeEqual(a, b) {
|
|
|
1770
1861
|
}
|
|
1771
1862
|
async function openBrowser(url) {
|
|
1772
1863
|
const [command, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", url]] : ["xdg-open", [url]];
|
|
1773
|
-
await new Promise((
|
|
1864
|
+
await new Promise((resolve7, reject) => {
|
|
1774
1865
|
const child = spawn(command, args, {
|
|
1775
1866
|
stdio: "ignore",
|
|
1776
1867
|
// Detached so closing the terminal does not close the browser, and so
|
|
@@ -1779,7 +1870,7 @@ async function openBrowser(url) {
|
|
|
1779
1870
|
});
|
|
1780
1871
|
child.once("error", reject);
|
|
1781
1872
|
child.unref();
|
|
1782
|
-
|
|
1873
|
+
resolve7();
|
|
1783
1874
|
});
|
|
1784
1875
|
}
|
|
1785
1876
|
async function describe(response) {
|
|
@@ -1841,13 +1932,13 @@ async function currentCredential(resolved, deps) {
|
|
|
1841
1932
|
// src/context.ts
|
|
1842
1933
|
import { createInterface } from "node:readline";
|
|
1843
1934
|
async function askOnTty(prompt) {
|
|
1844
|
-
return new Promise((
|
|
1935
|
+
return new Promise((resolve7) => {
|
|
1845
1936
|
const readline = createInterface({ input: process.stdin, output: process.stdout });
|
|
1846
1937
|
readline.question(prompt, (answer3) => {
|
|
1847
1938
|
readline.close();
|
|
1848
|
-
|
|
1939
|
+
resolve7(answer3.trim());
|
|
1849
1940
|
});
|
|
1850
|
-
readline.once("close", () =>
|
|
1941
|
+
readline.once("close", () => resolve7(""));
|
|
1851
1942
|
});
|
|
1852
1943
|
}
|
|
1853
1944
|
var ETX = "";
|
|
@@ -1856,13 +1947,13 @@ var BACKSPACE = "\b";
|
|
|
1856
1947
|
async function readSecretFromTty(prompt) {
|
|
1857
1948
|
const input = process.stdin;
|
|
1858
1949
|
if (!input.isTTY) {
|
|
1859
|
-
return new Promise((
|
|
1950
|
+
return new Promise((resolve7) => {
|
|
1860
1951
|
const readline = createInterface({ input });
|
|
1861
1952
|
readline.once("line", (line) => {
|
|
1862
1953
|
readline.close();
|
|
1863
|
-
|
|
1954
|
+
resolve7(line.trim());
|
|
1864
1955
|
});
|
|
1865
|
-
readline.once("close", () =>
|
|
1956
|
+
readline.once("close", () => resolve7(""));
|
|
1866
1957
|
});
|
|
1867
1958
|
}
|
|
1868
1959
|
process.stdout.write(prompt);
|
|
@@ -1870,14 +1961,14 @@ async function readSecretFromTty(prompt) {
|
|
|
1870
1961
|
input.setRawMode?.(true);
|
|
1871
1962
|
input.resume();
|
|
1872
1963
|
input.setEncoding("utf8");
|
|
1873
|
-
return new Promise((
|
|
1964
|
+
return new Promise((resolve7) => {
|
|
1874
1965
|
let value = "";
|
|
1875
1966
|
const finish2 = () => {
|
|
1876
1967
|
input.removeListener("data", onData);
|
|
1877
1968
|
input.setRawMode?.(previouslyRaw);
|
|
1878
1969
|
input.pause();
|
|
1879
1970
|
process.stdout.write("\n");
|
|
1880
|
-
|
|
1971
|
+
resolve7(value.trim());
|
|
1881
1972
|
};
|
|
1882
1973
|
const onData = (chunk) => {
|
|
1883
1974
|
for (const character of chunk) {
|
|
@@ -1914,8 +2005,8 @@ async function readStdin() {
|
|
|
1914
2005
|
// src/commands/agent.ts
|
|
1915
2006
|
import { hostname } from "node:os";
|
|
1916
2007
|
import { homedir as homedir2 } from "node:os";
|
|
1917
|
-
import { resolve as resolve3 } from "node:path";
|
|
1918
|
-
import { readFileSync as readFileSync4, statSync as statSync2 } from "node:fs";
|
|
2008
|
+
import { join as join4, resolve as resolve3 } from "node:path";
|
|
2009
|
+
import { readFileSync as readFileSync4, readdirSync, statSync as statSync2 } from "node:fs";
|
|
1919
2010
|
|
|
1920
2011
|
// src/files.ts
|
|
1921
2012
|
import { existsSync as existsSync3, readFileSync as readFileSync3, realpathSync, statSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
@@ -1928,11 +2019,13 @@ var OutsideWorkspace = class extends Error {
|
|
|
1928
2019
|
};
|
|
1929
2020
|
var TooLarge = class extends Error {
|
|
1930
2021
|
constructor(path, bytes, limit) {
|
|
1931
|
-
|
|
2022
|
+
const say = (value) => value >= 1024 * 1024 ? `${(value / (1024 * 1024)).toFixed(1)} MB` : `${Math.round(value / 1024)} KB`;
|
|
2023
|
+
super(`${path} is ${say(bytes)}, over the ${say(limit)} limit.`);
|
|
1932
2024
|
this.name = "TooLarge";
|
|
1933
2025
|
}
|
|
1934
2026
|
};
|
|
1935
2027
|
var MAX_READ_BYTES = 512 * 1024;
|
|
2028
|
+
var MAX_TRANSFER_BYTES = 100 * 1024 * 1024;
|
|
1936
2029
|
function realLocation(absolute) {
|
|
1937
2030
|
let existing = absolute;
|
|
1938
2031
|
const trailing = [];
|
|
@@ -2046,19 +2139,41 @@ async function answer(context, apiUrl, token, roots, request) {
|
|
|
2046
2139
|
return { ok: false, error: error instanceof Error ? error.message : "refused" };
|
|
2047
2140
|
}
|
|
2048
2141
|
let bytes;
|
|
2142
|
+
let filename = request.path.split("/").pop() ?? "file";
|
|
2143
|
+
if (request.kind === "list_dir") {
|
|
2144
|
+
try {
|
|
2145
|
+
const stats = statSync2(located);
|
|
2146
|
+
if (!stats.isDirectory()) return { ok: false, error: `${request.path} is not a folder.` };
|
|
2147
|
+
const entries = readdirSync(located, { withFileTypes: true }).filter((entry) => !entry.name.startsWith(".")).slice(0, MAX_LISTED).map((entry) => {
|
|
2148
|
+
if (entry.isDirectory()) return `${entry.name}/`;
|
|
2149
|
+
try {
|
|
2150
|
+
return `${entry.name} ${sizeOf(join4(located, entry.name))}`;
|
|
2151
|
+
} catch {
|
|
2152
|
+
return entry.name;
|
|
2153
|
+
}
|
|
2154
|
+
}).sort();
|
|
2155
|
+
const listing = entries.length > 0 ? entries.join("\n") : "(empty)";
|
|
2156
|
+
bytes = Buffer.from(`${request.path}
|
|
2157
|
+
|
|
2158
|
+
${listing}
|
|
2159
|
+
`, "utf8");
|
|
2160
|
+
filename = `${request.path.split("/").filter(Boolean).pop() ?? "listing"}.txt`;
|
|
2161
|
+
const grant = await upload(apiUrl, token, filename, bytes);
|
|
2162
|
+
return grant;
|
|
2163
|
+
} catch (error) {
|
|
2164
|
+
return { ok: false, error: error instanceof Error ? error.message : "could not list it" };
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2049
2167
|
try {
|
|
2050
2168
|
const stats = statSync2(located);
|
|
2051
2169
|
if (!stats.isFile()) return { ok: false, error: `${request.path} is not a file.` };
|
|
2052
|
-
if (stats.size > MAX_READ_BYTES) {
|
|
2053
|
-
return {
|
|
2054
|
-
ok: false,
|
|
2055
|
-
error: new TooLarge(request.path, stats.size, MAX_READ_BYTES).message
|
|
2056
|
-
};
|
|
2057
|
-
}
|
|
2058
2170
|
bytes = readFileSync4(located);
|
|
2059
2171
|
} catch (error) {
|
|
2060
2172
|
return { ok: false, error: error instanceof Error ? error.message : "could not read it" };
|
|
2061
2173
|
}
|
|
2174
|
+
return upload(apiUrl, token, filename, bytes);
|
|
2175
|
+
}
|
|
2176
|
+
async function upload(apiUrl, token, filename, bytes) {
|
|
2062
2177
|
const grant = await fetch(`${apiUrl}/api/v1/agent/upload-url`, {
|
|
2063
2178
|
method: "POST",
|
|
2064
2179
|
headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
|
|
@@ -2070,13 +2185,17 @@ async function answer(context, apiUrl, token, roots, request) {
|
|
|
2070
2185
|
// No content type is sent: this machine has a path, not a declaration.
|
|
2071
2186
|
// The server resolves it from the name against the one table that knows
|
|
2072
2187
|
// which types it can read, and tells us below what it decided.
|
|
2073
|
-
filename
|
|
2188
|
+
filename
|
|
2074
2189
|
})
|
|
2075
2190
|
});
|
|
2076
2191
|
if (!grant.ok) {
|
|
2077
2192
|
return { ok: false, error: await said(grant, "could not get an upload url") };
|
|
2078
2193
|
}
|
|
2079
|
-
const { uploadUrl, contentType } = await grant.json();
|
|
2194
|
+
const { uploadUrl, contentType, maxBytes } = await grant.json();
|
|
2195
|
+
const limit = maxBytes ?? MAX_TRANSFER_BYTES;
|
|
2196
|
+
if (bytes.length > limit) {
|
|
2197
|
+
return { ok: false, error: new TooLarge(filename, bytes.length, limit).message };
|
|
2198
|
+
}
|
|
2080
2199
|
const put = await fetch(uploadUrl, {
|
|
2081
2200
|
method: "PUT",
|
|
2082
2201
|
// The type the grant was signed for. Anything else is refused.
|
|
@@ -2088,6 +2207,13 @@ async function answer(context, apiUrl, token, roots, request) {
|
|
|
2088
2207
|
if (!stored.attachToken) return { ok: false, error: "the upload returned no reference" };
|
|
2089
2208
|
return { ok: true, attachToken: stored.attachToken, bytes: bytes.length };
|
|
2090
2209
|
}
|
|
2210
|
+
var MAX_LISTED = 200;
|
|
2211
|
+
function sizeOf(path) {
|
|
2212
|
+
const size = statSync2(path).size;
|
|
2213
|
+
if (size < 1024) return `${size} B`;
|
|
2214
|
+
if (size < 1024 * 1024) return `${Math.round(size / 1024)} KB`;
|
|
2215
|
+
return `${(size / (1024 * 1024)).toFixed(1)} MB`;
|
|
2216
|
+
}
|
|
2091
2217
|
async function agentCommand(context) {
|
|
2092
2218
|
const credential = context.resolved.credential;
|
|
2093
2219
|
if (!credential) {
|
|
@@ -2197,7 +2323,10 @@ async function agentCommand(context) {
|
|
|
2197
2323
|
}
|
|
2198
2324
|
|
|
2199
2325
|
// src/commands/requests.ts
|
|
2326
|
+
import { existsSync as existsSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2327
|
+
import { resolve as resolve4 } from "node:path";
|
|
2200
2328
|
async function requestsCommand(context) {
|
|
2329
|
+
if (context.args.words[1] === "get") return collectCommand(context);
|
|
2201
2330
|
const credential = context.resolved.credential;
|
|
2202
2331
|
if (!credential) {
|
|
2203
2332
|
context.error("Sign in first: pm auth login");
|
|
@@ -2235,16 +2364,53 @@ async function requestsCommand(context) {
|
|
|
2235
2364
|
context.print("They cannot be approved from here \u2014 see `pm help requests`.");
|
|
2236
2365
|
return 0;
|
|
2237
2366
|
}
|
|
2367
|
+
async function collectCommand(context) {
|
|
2368
|
+
const credential = context.resolved.credential;
|
|
2369
|
+
if (!credential) {
|
|
2370
|
+
context.error("Sign in first: pm auth login");
|
|
2371
|
+
return 1;
|
|
2372
|
+
}
|
|
2373
|
+
const id = context.args.words[2];
|
|
2374
|
+
if (!id) {
|
|
2375
|
+
context.error("Which request? `pm requests` lists them with their ids.");
|
|
2376
|
+
return 2;
|
|
2377
|
+
}
|
|
2378
|
+
const apiUrl = context.resolved.apiUrl.replace(/\/+$/, "");
|
|
2379
|
+
const link = await fetch(
|
|
2380
|
+
`${apiUrl}/api/v1/agent/request/${encodeURIComponent(id)}/download`,
|
|
2381
|
+
{ headers: { authorization: `Bearer ${credential.token}` } }
|
|
2382
|
+
);
|
|
2383
|
+
if (!link.ok) {
|
|
2384
|
+
const body = await link.json().catch(() => void 0);
|
|
2385
|
+
context.error(body?.error?.message ?? `Could not prepare that file (${link.status}).`);
|
|
2386
|
+
return 1;
|
|
2387
|
+
}
|
|
2388
|
+
const { downloadUrl, filename } = await link.json();
|
|
2389
|
+
const file = await fetch(downloadUrl);
|
|
2390
|
+
if (!file.ok) {
|
|
2391
|
+
context.error(`The download refused it (${file.status}). Links expire in minutes \u2014 try again.`);
|
|
2392
|
+
return 1;
|
|
2393
|
+
}
|
|
2394
|
+
const name = stringFlag(context.args, "output", "o") ?? filename;
|
|
2395
|
+
const target = resolve4(name);
|
|
2396
|
+
if (existsSync4(target)) {
|
|
2397
|
+
context.error(`${target} already exists. Pass --output to write somewhere else.`);
|
|
2398
|
+
return 1;
|
|
2399
|
+
}
|
|
2400
|
+
writeFileSync4(target, new Uint8Array(await file.arrayBuffer()));
|
|
2401
|
+
context.print(`Wrote ${target}`);
|
|
2402
|
+
return 0;
|
|
2403
|
+
}
|
|
2238
2404
|
|
|
2239
2405
|
// src/workspace.ts
|
|
2240
|
-
import { existsSync as
|
|
2241
|
-
import { dirname as dirname3, join as
|
|
2406
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2407
|
+
import { dirname as dirname3, join as join5, resolve as resolvePath } from "node:path";
|
|
2242
2408
|
var WORKSPACE_FILE = ".persistmemory.json";
|
|
2243
2409
|
function findWorkspace(from = process.cwd()) {
|
|
2244
2410
|
let dir = resolvePath(from);
|
|
2245
2411
|
for (; ; ) {
|
|
2246
|
-
const file =
|
|
2247
|
-
if (
|
|
2412
|
+
const file = join5(dir, WORKSPACE_FILE);
|
|
2413
|
+
if (existsSync5(file)) {
|
|
2248
2414
|
const config = readWorkspace(file);
|
|
2249
2415
|
if (config) return { file, dir, config };
|
|
2250
2416
|
}
|
|
@@ -2266,8 +2432,8 @@ function readWorkspace(file) {
|
|
|
2266
2432
|
}
|
|
2267
2433
|
}
|
|
2268
2434
|
function writeWorkspace(dir, config) {
|
|
2269
|
-
const file =
|
|
2270
|
-
|
|
2435
|
+
const file = join5(dir, WORKSPACE_FILE);
|
|
2436
|
+
writeFileSync5(file, `${JSON.stringify(config, null, 2)}
|
|
2271
2437
|
`, "utf8");
|
|
2272
2438
|
return file;
|
|
2273
2439
|
}
|
|
@@ -2536,9 +2702,9 @@ function message(error) {
|
|
|
2536
2702
|
}
|
|
2537
2703
|
|
|
2538
2704
|
// src/commands/maintain.ts
|
|
2539
|
-
import { existsSync as
|
|
2705
|
+
import { existsSync as existsSync6, rmSync } from "node:fs";
|
|
2540
2706
|
import { spawnSync } from "node:child_process";
|
|
2541
|
-
import { dirname as dirname4, resolve as
|
|
2707
|
+
import { dirname as dirname4, resolve as resolve6 } from "node:path";
|
|
2542
2708
|
import { fileURLToPath } from "node:url";
|
|
2543
2709
|
async function updateCommand(context) {
|
|
2544
2710
|
const manager = installer();
|
|
@@ -2580,7 +2746,7 @@ Delete the file it runs from: ${processPath()}`
|
|
|
2580
2746
|
return 0;
|
|
2581
2747
|
}
|
|
2582
2748
|
async function deleteCommand(context) {
|
|
2583
|
-
if (!
|
|
2749
|
+
if (!existsSync6(context.paths.dir)) {
|
|
2584
2750
|
context.print(`Nothing to delete: ${context.paths.dir} does not exist.`);
|
|
2585
2751
|
return 0;
|
|
2586
2752
|
}
|
|
@@ -2609,7 +2775,7 @@ async function deleteCommand(context) {
|
|
|
2609
2775
|
return 0;
|
|
2610
2776
|
}
|
|
2611
2777
|
function removeEverything(context) {
|
|
2612
|
-
const dir =
|
|
2778
|
+
const dir = resolve6(context.paths.dir);
|
|
2613
2779
|
if (dir === "/" || dir.split("/").filter(Boolean).length < 2) {
|
|
2614
2780
|
context.error(`Refusing to delete ${dir}: that does not look like a data directory.`);
|
|
2615
2781
|
return;
|
|
@@ -2621,7 +2787,7 @@ function installer() {
|
|
|
2621
2787
|
}
|
|
2622
2788
|
function processPath() {
|
|
2623
2789
|
try {
|
|
2624
|
-
return
|
|
2790
|
+
return resolve6(dirname4(fileURLToPath(import.meta.url)));
|
|
2625
2791
|
} catch {
|
|
2626
2792
|
return process.argv[1] ?? "";
|
|
2627
2793
|
}
|
|
@@ -2633,12 +2799,12 @@ import { randomUUID } from "node:crypto";
|
|
|
2633
2799
|
import { relative as relative2 } from "node:path";
|
|
2634
2800
|
|
|
2635
2801
|
// src/events.ts
|
|
2636
|
-
import { appendFileSync, existsSync as
|
|
2637
|
-
import { join as
|
|
2802
|
+
import { appendFileSync, existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync6 } from "node:fs";
|
|
2803
|
+
import { join as join6 } from "node:path";
|
|
2638
2804
|
function openSessionLog(paths, id) {
|
|
2639
|
-
const directory =
|
|
2805
|
+
const directory = join6(paths.dir, "sessions");
|
|
2640
2806
|
mkdirSync3(directory, { recursive: true, mode: 448 });
|
|
2641
|
-
const path =
|
|
2807
|
+
const path = join6(directory, `${id}.jsonl`);
|
|
2642
2808
|
return {
|
|
2643
2809
|
id,
|
|
2644
2810
|
path,
|
|
@@ -2650,7 +2816,7 @@ function openSessionLog(paths, id) {
|
|
|
2650
2816
|
}
|
|
2651
2817
|
},
|
|
2652
2818
|
read() {
|
|
2653
|
-
if (!
|
|
2819
|
+
if (!existsSync7(path)) return [];
|
|
2654
2820
|
return readFileSync6(path, "utf8").split("\n").filter((line) => line.trim() !== "").flatMap((line) => {
|
|
2655
2821
|
try {
|
|
2656
2822
|
return [JSON.parse(line)];
|
|
@@ -2728,9 +2894,9 @@ async function sessionCommand(context) {
|
|
|
2728
2894
|
context.print(` Ask anything. /help for commands, /exit to leave.
|
|
2729
2895
|
`);
|
|
2730
2896
|
const readline = createInterface2({ input: process.stdin, output: process.stdout });
|
|
2731
|
-
const ask = (prompt) => new Promise((
|
|
2732
|
-
readline.question(prompt,
|
|
2733
|
-
readline.once("close", () =>
|
|
2897
|
+
const ask = (prompt) => new Promise((resolve7) => {
|
|
2898
|
+
readline.question(prompt, resolve7);
|
|
2899
|
+
readline.once("close", () => resolve7(void 0));
|
|
2734
2900
|
});
|
|
2735
2901
|
const root = process.cwd();
|
|
2736
2902
|
let running = true;
|