@kanadego/dsh-heartbeat 1.9.0 → 1.9.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/README.md +58 -71
- package/assets/frontwin.ps1 +1 -0
- package/assets/idle.ps1 +1 -0
- package/assets/screenpulse.ps1 +4 -2
- package/config/profile-schema.json +306 -306
- package/cordis.patch.yml +18 -39
- package/dist/{bindings-XPPSKILN.js → bindings-225SI7KC.js} +3 -2
- package/dist/{chunk-63FUJLIF.js → chunk-3MDBU6WY.js} +17 -2
- package/dist/chunk-3MDBU6WY.js.map +1 -0
- package/dist/chunk-AJ5HLAL7.js +2477 -0
- package/dist/chunk-AJ5HLAL7.js.map +1 -0
- package/dist/{chunk-VIZNIQLK.js → chunk-CUOSICYJ.js} +82 -74
- package/dist/chunk-CUOSICYJ.js.map +1 -0
- package/dist/chunk-IG6YMUHW.js +569 -0
- package/dist/chunk-IG6YMUHW.js.map +1 -0
- package/dist/chunk-IV2ZWQA3.js +56 -0
- package/dist/chunk-IV2ZWQA3.js.map +1 -0
- package/dist/{chunk-PQTSXW4F.js → chunk-JDH2LDFS.js} +97 -71
- package/dist/chunk-JDH2LDFS.js.map +1 -0
- package/dist/chunk-K5Y6JP2B.js +399 -0
- package/dist/chunk-K5Y6JP2B.js.map +1 -0
- package/dist/{chunk-RB3NLLGF.js → chunk-KB5SMG3F.js} +3 -3
- package/dist/{chunk-J6ZTRFFW.js → chunk-TY6UP74X.js} +6 -2
- package/dist/chunk-TY6UP74X.js.map +1 -0
- package/dist/chunk-WRUTATW4.js +47 -0
- package/dist/chunk-WRUTATW4.js.map +1 -0
- package/dist/{chunk-IPOZFZPY.js → chunk-XFNCTOW7.js} +48 -10
- package/dist/chunk-XFNCTOW7.js.map +1 -0
- package/dist/chunk-ZLV4NMFX.js +333 -0
- package/dist/chunk-ZLV4NMFX.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +565 -497
- package/dist/cli/index.js.map +1 -1
- package/dist/{inbox-57NHQHY3.js → inbox-P26DDMWC.js} +5 -3
- package/dist/index.d.ts +550 -0
- package/dist/index.js +665 -2730
- package/dist/index.js.map +1 -1
- package/dist/{migrate-N6NXMZ3G.js → migrate-D4VKAKYT.js} +4 -3
- package/dist/orchestrator-EUTZ3KG6.js +62 -0
- package/dist/{pool-7I4VACG2.js → pool-UTXQJJ5C.js} +5 -3
- package/dist/{snapshot-ZBCSX67Y.js → snapshot-K5VHETTK.js} +5 -4
- package/dist/snapshot-K5VHETTK.js.map +1 -0
- package/package.json +83 -76
- package/config/watchlist.json +0 -9
- package/dist/chunk-2SYXKKZF.js +0 -837
- package/dist/chunk-2SYXKKZF.js.map +0 -1
- package/dist/chunk-63FUJLIF.js.map +0 -1
- package/dist/chunk-IFTFDHZX.js +0 -203
- package/dist/chunk-IFTFDHZX.js.map +0 -1
- package/dist/chunk-IPOZFZPY.js.map +0 -1
- package/dist/chunk-J6ZTRFFW.js.map +0 -1
- package/dist/chunk-PQTSXW4F.js.map +0 -1
- package/dist/chunk-VIZNIQLK.js.map +0 -1
- /package/dist/{bindings-XPPSKILN.js.map → bindings-225SI7KC.js.map} +0 -0
- /package/dist/{chunk-RB3NLLGF.js.map → chunk-KB5SMG3F.js.map} +0 -0
- /package/dist/{inbox-57NHQHY3.js.map → inbox-P26DDMWC.js.map} +0 -0
- /package/dist/{migrate-N6NXMZ3G.js.map → migrate-D4VKAKYT.js.map} +0 -0
- /package/dist/{pool-7I4VACG2.js.map → orchestrator-EUTZ3KG6.js.map} +0 -0
- /package/dist/{snapshot-ZBCSX67Y.js.map → pool-UTXQJJ5C.js.map} +0 -0
package/dist/cli/index.js
CHANGED
|
@@ -1,28 +1,34 @@
|
|
|
1
|
+
import {
|
|
2
|
+
applyMigrationEntries,
|
|
3
|
+
collectMigrationEntries,
|
|
4
|
+
decryptContainer,
|
|
5
|
+
encryptContainer
|
|
6
|
+
} from "../chunk-XFNCTOW7.js";
|
|
1
7
|
import {
|
|
2
8
|
BUNDLED_PRESET_ID,
|
|
9
|
+
conventionalUserPresetRoot,
|
|
10
|
+
createPathGuard,
|
|
11
|
+
describeInstall,
|
|
12
|
+
installBundledPreset,
|
|
13
|
+
loadPolicy,
|
|
14
|
+
presetStatus
|
|
15
|
+
} from "../chunk-ZLV4NMFX.js";
|
|
16
|
+
import {
|
|
3
17
|
adviseWander,
|
|
4
18
|
appendEntry,
|
|
5
19
|
browseStatus,
|
|
6
|
-
checkWatchlist,
|
|
7
20
|
completeWander,
|
|
8
|
-
conventionalUserPresetRoot,
|
|
9
|
-
createPathGuard,
|
|
10
|
-
describeInstall,
|
|
11
21
|
ensureRegistered,
|
|
12
|
-
installBundledPreset,
|
|
13
22
|
ledgerFilePath,
|
|
14
23
|
listWeeklyReports,
|
|
15
24
|
loadInterests,
|
|
16
|
-
loadPolicy,
|
|
17
|
-
loadWatchlist,
|
|
18
25
|
markDone,
|
|
19
|
-
presetStatus,
|
|
20
26
|
readLedger,
|
|
21
27
|
readWeeklyReport,
|
|
22
28
|
scanPending,
|
|
23
29
|
sendNewMessageHint,
|
|
24
30
|
weeklyDirPath
|
|
25
|
-
} from "../chunk-
|
|
31
|
+
} from "../chunk-IG6YMUHW.js";
|
|
26
32
|
import {
|
|
27
33
|
activeSeeds,
|
|
28
34
|
addSeed,
|
|
@@ -32,27 +38,24 @@ import {
|
|
|
32
38
|
loadPool,
|
|
33
39
|
seedsFilePath,
|
|
34
40
|
surfaceSeed
|
|
35
|
-
} from "../chunk-
|
|
41
|
+
} from "../chunk-JDH2LDFS.js";
|
|
42
|
+
import "../chunk-IV2ZWQA3.js";
|
|
36
43
|
import {
|
|
37
44
|
appendAuditLine,
|
|
38
45
|
loadProfile,
|
|
39
46
|
profileFilePath,
|
|
40
47
|
pruneAuditFile,
|
|
41
48
|
rebuildProfile,
|
|
42
|
-
shredFileSync,
|
|
43
49
|
verifyProfile
|
|
44
|
-
} from "../chunk-
|
|
45
|
-
import {
|
|
46
|
-
applyMigrationEntries,
|
|
47
|
-
collectMigrationEntries,
|
|
48
|
-
decryptContainer,
|
|
49
|
-
encryptContainer
|
|
50
|
-
} from "../chunk-IPOZFZPY.js";
|
|
50
|
+
} from "../chunk-CUOSICYJ.js";
|
|
51
51
|
import {
|
|
52
52
|
initWorkspace,
|
|
53
|
-
loadEncryptedText,
|
|
54
53
|
writeText
|
|
55
|
-
} from "../chunk-
|
|
54
|
+
} from "../chunk-K5Y6JP2B.js";
|
|
55
|
+
import {
|
|
56
|
+
atomicWriteFileSync,
|
|
57
|
+
shredFileSync
|
|
58
|
+
} from "../chunk-WRUTATW4.js";
|
|
56
59
|
|
|
57
60
|
// src/cli/index.ts
|
|
58
61
|
import { spawn } from "child_process";
|
|
@@ -67,6 +70,9 @@ var BURN_LIST = [
|
|
|
67
70
|
{ file: "profile.json", note: "\u753B\u50CF\u7269\u5316\u89C6\u56FE" },
|
|
68
71
|
{ file: "profile_inbox.jsonl", note: "\u89C2\u5BDF\u6536\u4EF6\u7BB1" },
|
|
69
72
|
{ file: "profile_journal.jsonl", note: "\u753B\u50CF\u64CD\u4F5C\u6D41\u6C34" },
|
|
73
|
+
// H-19: the rotation shards hold the whole profile history and are written
|
|
74
|
+
// in PLAINTEXT — a burn that leaves them behind leaves the memory readable.
|
|
75
|
+
{ prefix: "profile_journal.archive-", note: "\u753B\u50CF\u6D41\u6C34\u5F52\u6863\u5206\u7247\uFF08\u660E\u6587\uFF1Bjournal \u8F6E\u8F6C\u4EA7\u7269\uFF09" },
|
|
70
76
|
{ file: "profile_rhythm.json", note: "\u4F5C\u606F\u805A\u5408\uFF08\u7EAF\u7EDF\u8BA1\uFF09" },
|
|
71
77
|
{ file: "screen.json", note: "\u5C4F\u5E55\u5FEB\u7167\uFF08\u52A0\u5BC6\uFF09" },
|
|
72
78
|
{ file: "screen.jpg", note: "\u622A\u56FE\uFF08\u52A0\u5BC6\uFF09" },
|
|
@@ -76,22 +82,40 @@ var BURN_LIST = [
|
|
|
76
82
|
{ file: "ledger.md", note: "\u8D26\u672C\uFF08\u660E\u6587\uFF0C\u4EBA\u53EF\u8BFB\u662F\u8BBE\u8BA1\u76EE\u6807\uFF09" },
|
|
77
83
|
{ file: "envpulse.json", note: "\u73AF\u5883\u6E29\u5EA6\u8BA1\u5FEB\u7167" },
|
|
78
84
|
{ file: "gate.json", note: "\u95F8\u95E8\u8FD0\u884C\u65F6\u8BA1\u6570" },
|
|
85
|
+
// v1.9.0 completion (H-19): these carry memory just as much as the rows
|
|
86
|
+
// above — the persona report log records WHAT SHE SAID (spoken / seed_ids /
|
|
87
|
+
// profile_ids / reason), so a burn that skips it is a burn that lies.
|
|
88
|
+
{ file: "profile_snapshot.json", note: "\u753B\u50CF\u5FEB\u7167\uFF08journal \u8F6E\u8F6C\u57FA\u7EBF\uFF09" },
|
|
89
|
+
{ file: "cursors.json", note: "\u89C2\u5BDF\u6E38\u6807\uFF08\u4F1A\u8BDD\u4E8B\u4EF6\u4F4D\u70B9\uFF09" },
|
|
90
|
+
{ file: "seed_report.jsonl", note: "\u6295\u9012\u62A5\u8D26\u65E5\u5FD7\uFF08\u52A0\u5BC6\uFF09" },
|
|
91
|
+
{ file: "preference.json", note: "\u8BDD\u9898\u504F\u597D\u7EDF\u8BA1" },
|
|
92
|
+
{ dir: "weekly", note: "\u5468\u62A5\uFF08\u753B\u50CF\u4E0E\u7D20\u6750\u6563\u6587\uFF09" },
|
|
79
93
|
{ dir: "tmp", note: "\u89E3\u9501\u4E34\u65F6\u6587\u4EF6\uFF08\u5D29\u6E83\u6B8B\u7559\u7684\u89E3\u5BC6\u660E\u6587\u662F\u6700\u8BE5\u70E7\u7684\u4E1C\u897F\uFF09" },
|
|
80
94
|
{ dir: "logs", note: "\u5BA1\u8BA1\u4E0E\u51B3\u7B56\u65E5\u5FD7" },
|
|
81
95
|
{ dir: "exports", note: "\u5BFC\u51FA\u4EA7\u7269" }
|
|
82
96
|
];
|
|
83
97
|
var SETTINGS_DIR = "settings";
|
|
98
|
+
function prefixMatches(guard, paths, prefix) {
|
|
99
|
+
try {
|
|
100
|
+
return fs.readdirSync(guard.assert(paths.dataDir)).filter((f) => f.startsWith(prefix)).sort();
|
|
101
|
+
} catch {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
84
105
|
function planBurn(guard, paths, all = false) {
|
|
85
106
|
const plan = [];
|
|
86
107
|
for (const t of BURN_LIST) {
|
|
87
|
-
const rel = t.file ?? t.dir;
|
|
108
|
+
const rel = t.file ?? t.dir ?? t.prefix ?? "";
|
|
88
109
|
if (!all && rel === SETTINGS_DIR) continue;
|
|
89
|
-
const abs = path.join(paths.dataDir, rel);
|
|
90
110
|
let exists = false;
|
|
91
|
-
|
|
92
|
-
exists =
|
|
93
|
-
}
|
|
94
|
-
|
|
111
|
+
if (t.prefix) {
|
|
112
|
+
exists = prefixMatches(guard, paths, t.prefix).length > 0;
|
|
113
|
+
} else {
|
|
114
|
+
try {
|
|
115
|
+
exists = fs.existsSync(guard.assert(path.join(paths.dataDir, rel)));
|
|
116
|
+
} catch {
|
|
117
|
+
exists = false;
|
|
118
|
+
}
|
|
95
119
|
}
|
|
96
120
|
plan.push({ target: t, exists });
|
|
97
121
|
}
|
|
@@ -105,6 +129,21 @@ function executeBurn(guard, paths, opts = {}) {
|
|
|
105
129
|
const missing = [];
|
|
106
130
|
const passes = opts.shredPasses ?? 3;
|
|
107
131
|
for (const t of BURN_LIST) {
|
|
132
|
+
if (t.prefix) {
|
|
133
|
+
const matches = prefixMatches(guard, paths, t.prefix);
|
|
134
|
+
if (matches.length === 0) {
|
|
135
|
+
missing.push(`${t.prefix}*`);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
for (const f of matches) {
|
|
139
|
+
try {
|
|
140
|
+
shredFileSync(guard.assert(path.join(paths.dataDir, f)), passes);
|
|
141
|
+
burned.push(f);
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
108
147
|
const rel = t.file ?? t.dir;
|
|
109
148
|
const abs = path.join(paths.dataDir, rel);
|
|
110
149
|
let absCanon;
|
|
@@ -159,9 +198,8 @@ function usage() {
|
|
|
159
198
|
" ledger done <id|substring> mark an entry done",
|
|
160
199
|
" ledger open open ledger.md in the default editor",
|
|
161
200
|
" logs cleanup [--dry-run] apply log retention now",
|
|
162
|
-
" browse status browse state summary (
|
|
201
|
+
" browse status browse state summary (wander)",
|
|
163
202
|
" browse dry wander adjudication with forced noon window",
|
|
164
|
-
" browse watch run watchlist check now (network)",
|
|
165
203
|
" browse done <focus> CODE-side registration after a wander visit",
|
|
166
204
|
" bind list list session bindings (D13)",
|
|
167
205
|
" bind add <sessionId> [--observe] bind a session (deliver by default)",
|
|
@@ -194,6 +232,13 @@ function flag(argv, name) {
|
|
|
194
232
|
const i = argv.indexOf(name);
|
|
195
233
|
return i > -1 ? argv[i + 1] : void 0;
|
|
196
234
|
}
|
|
235
|
+
var SEED_TAGS = ["news", "fandom", "scene", "promise"];
|
|
236
|
+
var SEED_SOURCES = ["chat", "screen", "browse", "hand", "profile"];
|
|
237
|
+
function flagOneOf(argv, name, allowed) {
|
|
238
|
+
const v = flag(argv, name);
|
|
239
|
+
if (v === void 0) return void 0;
|
|
240
|
+
return allowed.includes(v) ? v : void 0;
|
|
241
|
+
}
|
|
197
242
|
async function readLines(prompts) {
|
|
198
243
|
const readline = await import("readline/promises");
|
|
199
244
|
const it = readline.createInterface({ input: process.stdin });
|
|
@@ -202,521 +247,544 @@ async function readLines(prompts) {
|
|
|
202
247
|
it.close();
|
|
203
248
|
return out;
|
|
204
249
|
}
|
|
205
|
-
async function
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const seedAudit =
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
250
|
+
async function verbStatus(c) {
|
|
251
|
+
const { paths, policy } = c;
|
|
252
|
+
console.log(JSON.stringify({
|
|
253
|
+
dataDir: paths.dataDir,
|
|
254
|
+
heartbeatIntervalMin: policy.heartbeat.intervalMin,
|
|
255
|
+
maxDailySend: policy.gate.maxDailySend,
|
|
256
|
+
seedsMaxActive: policy.seeds.maxActive,
|
|
257
|
+
psyEnabled: policy.profile.psyEnabled
|
|
258
|
+
}, null, 2));
|
|
259
|
+
return 0;
|
|
260
|
+
}
|
|
261
|
+
async function verbSeeds(c) {
|
|
262
|
+
const { guard, policy, sub, rest, seedsFile, seedAudit } = c;
|
|
263
|
+
switch (sub) {
|
|
264
|
+
case "add": {
|
|
265
|
+
let text = rest[0]?.trim() ?? "";
|
|
266
|
+
if (text === "-") text = readStdinText();
|
|
267
|
+
if (!text) {
|
|
268
|
+
console.error("usage: seeds add <text|-> [--tag t] [--source s] [--topic x] [--confidence n]");
|
|
269
|
+
return 1;
|
|
270
|
+
}
|
|
271
|
+
const result = addSeed(guard, seedsFile, policy, {
|
|
272
|
+
text,
|
|
273
|
+
tag: flagOneOf(rest, "--tag", SEED_TAGS),
|
|
274
|
+
source: flagOneOf(rest, "--source", SEED_SOURCES),
|
|
275
|
+
topic: flag(rest, "--topic"),
|
|
276
|
+
confidence: flag(rest, "--confidence") ? Number(flag(rest, "--confidence")) : void 0
|
|
277
|
+
}, Date.now(), seedAudit);
|
|
278
|
+
if (result.kind === "duplicate") {
|
|
279
|
+
console.log(`DUPLICATE: active seed ${result.seed.id} has the same text`);
|
|
280
|
+
return 0;
|
|
281
|
+
}
|
|
282
|
+
console.log(`${result.kind.toUpperCase()} ${result.seed.id} (${activeSeeds(loadPool(guard, seedsFile)).length}/${policy.seeds.maxActive})` + (result.evicted ? ` [evicted ${result.evicted.id}: ${result.evicted.retireReason}]` : ""));
|
|
232
283
|
return 0;
|
|
233
284
|
}
|
|
234
|
-
case "
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
console.error("usage: seeds add <text|-> [--tag t] [--source s] [--topic x] [--confidence n]");
|
|
241
|
-
return 1;
|
|
242
|
-
}
|
|
243
|
-
const result = addSeed(guard, seedsFile, policy, {
|
|
244
|
-
text,
|
|
245
|
-
tag: flag(rest, "--tag"),
|
|
246
|
-
source: flag(rest, "--source"),
|
|
247
|
-
topic: flag(rest, "--topic"),
|
|
248
|
-
confidence: flag(rest, "--confidence") ? Number(flag(rest, "--confidence")) : void 0
|
|
249
|
-
}, Date.now(), seedAudit);
|
|
250
|
-
if (result.kind === "duplicate") {
|
|
251
|
-
console.log(`DUPLICATE: active seed ${result.seed.id} has the same text`);
|
|
252
|
-
return 0;
|
|
253
|
-
}
|
|
254
|
-
console.log(`${result.kind.toUpperCase()} ${result.seed.id} (${activeSeeds(loadPool(guard, seedsFile)).length}/${policy.seeds.maxActive})` + (result.evicted ? ` [evicted ${result.evicted.id}: ${result.evicted.retireReason}]` : ""));
|
|
255
|
-
return 0;
|
|
256
|
-
}
|
|
257
|
-
case "list": {
|
|
258
|
-
const db = loadPool(guard, seedsFile);
|
|
259
|
-
const items = rest.includes("--archived") ? archivedSeeds(db) : activeSeeds(db);
|
|
260
|
-
for (const s of items) {
|
|
261
|
-
const ageDays = Math.floor((Date.now() - Date.parse(s.bornAt)) / 864e5);
|
|
262
|
-
console.log(`${s.id} [${s.tag}/${s.source}] d${ageDays} used:${s.used}${s.protected ? " *" : ""} ${s.text.slice(0, 60)}`);
|
|
263
|
-
}
|
|
264
|
-
if (items.length === 0) console.log("(empty)");
|
|
265
|
-
return 0;
|
|
266
|
-
}
|
|
267
|
-
case "surface": {
|
|
268
|
-
const s = surfaceSeed(guard, seedsFile, policy, rest[0] ?? "", Date.now(), seedAudit);
|
|
269
|
-
if (!s) {
|
|
270
|
-
console.log("NOT_FOUND");
|
|
271
|
-
return 1;
|
|
272
|
-
}
|
|
273
|
-
console.log(`SURFACED ${s.id} used:${s.used}${s.status === "archived" ? " -> archived (consumed)" : ""}`);
|
|
274
|
-
return 0;
|
|
275
|
-
}
|
|
276
|
-
case "archive": {
|
|
277
|
-
const s = archiveSeedById(guard, seedsFile, rest[0] ?? "", "completed", Date.now(), seedAudit);
|
|
278
|
-
if (!s) {
|
|
279
|
-
console.log("NOT_FOUND");
|
|
280
|
-
return 1;
|
|
281
|
-
}
|
|
282
|
-
console.log(`ARCHIVED ${s.id}`);
|
|
283
|
-
return 0;
|
|
284
|
-
}
|
|
285
|
-
case "gc": {
|
|
286
|
-
const report = gcPool(guard, seedsFile, policy, Date.now(), seedAudit);
|
|
287
|
-
console.log(`GC consumed:${report.consumed} expired:${report.expired} cold_bench:${report.coldBench} archive_trimmed:${report.archiveTrimmed} -> active ${report.activeAfter}/${policy.seeds.maxActive} archive ${archivedSeeds(loadPool(guard, seedsFile)).length}/${policy.seeds.archiveCap}`);
|
|
288
|
-
return 0;
|
|
289
|
-
}
|
|
290
|
-
case "stats": {
|
|
291
|
-
const db = loadPool(guard, seedsFile);
|
|
292
|
-
console.log(JSON.stringify({
|
|
293
|
-
active: activeSeeds(db).length,
|
|
294
|
-
archived: archivedSeeds(db).length,
|
|
295
|
-
cap: policy.seeds.maxActive,
|
|
296
|
-
seq: db.seq
|
|
297
|
-
}, null, 2));
|
|
298
|
-
return 0;
|
|
299
|
-
}
|
|
300
|
-
default:
|
|
301
|
-
console.error(`unknown seeds subcommand: ${sub}`);
|
|
302
|
-
return 1;
|
|
285
|
+
case "list": {
|
|
286
|
+
const db = loadPool(guard, seedsFile);
|
|
287
|
+
const items = rest.includes("--archived") ? archivedSeeds(db) : activeSeeds(db);
|
|
288
|
+
for (const s of items) {
|
|
289
|
+
const ageDays = Math.floor((Date.now() - Date.parse(s.bornAt)) / 864e5);
|
|
290
|
+
console.log(`${s.id} [${s.tag}/${s.source}] d${ageDays} used:${s.used}${s.protected ? " *" : ""} ${s.text.slice(0, 60)}`);
|
|
303
291
|
}
|
|
292
|
+
if (items.length === 0) console.log("(empty)");
|
|
293
|
+
return 0;
|
|
304
294
|
}
|
|
305
|
-
case "
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
if (!text) {
|
|
311
|
-
console.error("usage: ledger add <text|->");
|
|
312
|
-
return 1;
|
|
313
|
-
}
|
|
314
|
-
const e = appendEntry(guard, ledgerFile, text);
|
|
315
|
-
console.log(`OPEN #${e.id} ${e.text.slice(0, 60)}`);
|
|
316
|
-
return 0;
|
|
317
|
-
}
|
|
318
|
-
case "list": {
|
|
319
|
-
const { entries } = readLedger(guard, ledgerFile);
|
|
320
|
-
const items = rest.includes("--pending") ? scanPending(guard, ledgerFile) : entries;
|
|
321
|
-
for (const e of items) console.log(`${e.status === "open" ? " " : "x"} #${e.id} ${e.date} ${e.text.slice(0, 60)}`);
|
|
322
|
-
if (items.length === 0) console.log("(empty)");
|
|
323
|
-
return 0;
|
|
324
|
-
}
|
|
325
|
-
case "done": {
|
|
326
|
-
const key = rest.join(" ").trim();
|
|
327
|
-
if (!key) {
|
|
328
|
-
console.error("usage: ledger done <id|substring>");
|
|
329
|
-
return 1;
|
|
330
|
-
}
|
|
331
|
-
const e = markDone(guard, ledgerFile, key);
|
|
332
|
-
if (!e) {
|
|
333
|
-
console.log("NOT_FOUND");
|
|
334
|
-
return 1;
|
|
335
|
-
}
|
|
336
|
-
console.log(`DONE #${e.id}`);
|
|
337
|
-
return 0;
|
|
338
|
-
}
|
|
339
|
-
case "open": {
|
|
340
|
-
const f = ledgerFile;
|
|
341
|
-
if (!fs2.existsSync(f)) {
|
|
342
|
-
console.log(`(ledger will be created at ${f})`);
|
|
343
|
-
}
|
|
344
|
-
spawn("cmd", ["/c", "start", "", f], { detached: true, stdio: "ignore" }).unref();
|
|
345
|
-
console.log(`opened ${f}`);
|
|
346
|
-
return 0;
|
|
347
|
-
}
|
|
348
|
-
default:
|
|
349
|
-
console.error(`unknown ledger subcommand: ${sub}`);
|
|
350
|
-
return 1;
|
|
295
|
+
case "surface": {
|
|
296
|
+
const s = surfaceSeed(guard, seedsFile, policy, rest[0] ?? "", Date.now(), seedAudit);
|
|
297
|
+
if (!s) {
|
|
298
|
+
console.log("NOT_FOUND");
|
|
299
|
+
return 1;
|
|
351
300
|
}
|
|
301
|
+
console.log(`SURFACED ${s.id} used:${s.used}${s.status === "archived" ? " -> archived (consumed)" : ""}`);
|
|
302
|
+
return 0;
|
|
352
303
|
}
|
|
353
|
-
case "
|
|
354
|
-
const
|
|
355
|
-
if (
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
return 0;
|
|
359
|
-
}
|
|
360
|
-
if (sub === "show") {
|
|
361
|
-
const file = rest[0] ?? reports[0]?.file;
|
|
362
|
-
if (!file) {
|
|
363
|
-
console.log("(no reports yet)");
|
|
364
|
-
return 1;
|
|
365
|
-
}
|
|
366
|
-
const rep = readWeeklyReport(guard, paths.dataDir, file);
|
|
367
|
-
if (!rep) {
|
|
368
|
-
console.error(`no such report: ${file} (see: weekly list; files live in ${weeklyDirPath(paths.dataDir)})`);
|
|
369
|
-
return 1;
|
|
370
|
-
}
|
|
371
|
-
console.log(`# ${rep.start.slice(0, 10)} ~ ${rep.end.slice(0, 10)} [${rep.source}]`);
|
|
372
|
-
console.log(rep.text);
|
|
373
|
-
return 0;
|
|
304
|
+
case "archive": {
|
|
305
|
+
const s = archiveSeedById(guard, seedsFile, rest[0] ?? "", "completed", Date.now(), seedAudit);
|
|
306
|
+
if (!s) {
|
|
307
|
+
console.log("NOT_FOUND");
|
|
308
|
+
return 1;
|
|
374
309
|
}
|
|
375
|
-
console.
|
|
310
|
+
console.log(`ARCHIVED ${s.id}`);
|
|
311
|
+
return 0;
|
|
312
|
+
}
|
|
313
|
+
case "gc": {
|
|
314
|
+
const report = gcPool(guard, seedsFile, policy, Date.now(), seedAudit);
|
|
315
|
+
console.log(`GC consumed:${report.consumed} expired:${report.expired} cold_bench:${report.coldBench} archive_trimmed:${report.archiveTrimmed} -> active ${report.activeAfter}/${policy.seeds.maxActive} archive ${archivedSeeds(loadPool(guard, seedsFile)).length}/${policy.seeds.archiveCap}`);
|
|
316
|
+
return 0;
|
|
317
|
+
}
|
|
318
|
+
case "stats": {
|
|
319
|
+
const db = loadPool(guard, seedsFile);
|
|
320
|
+
console.log(JSON.stringify({
|
|
321
|
+
active: activeSeeds(db).length,
|
|
322
|
+
archived: archivedSeeds(db).length,
|
|
323
|
+
cap: policy.seeds.maxActive,
|
|
324
|
+
seq: db.seq
|
|
325
|
+
}, null, 2));
|
|
326
|
+
return 0;
|
|
327
|
+
}
|
|
328
|
+
default:
|
|
329
|
+
console.error(`unknown seeds subcommand: ${sub}`);
|
|
376
330
|
return 1;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async function verbLedger(c) {
|
|
334
|
+
const { guard, sub, rest, ledgerFile } = c;
|
|
335
|
+
switch (sub) {
|
|
336
|
+
case "add": {
|
|
337
|
+
let text = rest.join(" ").trim();
|
|
338
|
+
if (text === "-") text = readStdinText();
|
|
339
|
+
if (!text) {
|
|
340
|
+
console.error("usage: ledger add <text|->");
|
|
341
|
+
return 1;
|
|
342
|
+
}
|
|
343
|
+
const e = appendEntry(guard, ledgerFile, text);
|
|
344
|
+
console.log(`OPEN #${e.id} ${e.text.slice(0, 60)}`);
|
|
345
|
+
return 0;
|
|
346
|
+
}
|
|
347
|
+
case "list": {
|
|
348
|
+
const { entries } = readLedger(guard, ledgerFile);
|
|
349
|
+
const items = rest.includes("--pending") ? scanPending(guard, ledgerFile) : entries;
|
|
350
|
+
for (const e of items) console.log(`${e.status === "open" ? " " : "x"} #${e.id} ${e.date} ${e.text.slice(0, 60)}`);
|
|
351
|
+
if (items.length === 0) console.log("(empty)");
|
|
352
|
+
return 0;
|
|
377
353
|
}
|
|
378
|
-
case "
|
|
379
|
-
|
|
380
|
-
|
|
354
|
+
case "done": {
|
|
355
|
+
const key = rest.join(" ").trim();
|
|
356
|
+
if (!key) {
|
|
357
|
+
console.error("usage: ledger done <id|substring>");
|
|
381
358
|
return 1;
|
|
382
359
|
}
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
if (dry) {
|
|
388
|
-
console.log(`(dry-run) would prune ${envPulse} to ${policy.retention.envPulseHours}h and ${decisionLog} to ${policy.retention.decisionLogDays}d`);
|
|
389
|
-
return 0;
|
|
360
|
+
const e = markDone(guard, ledgerFile, key);
|
|
361
|
+
if (!e) {
|
|
362
|
+
console.log("NOT_FOUND");
|
|
363
|
+
return 1;
|
|
390
364
|
}
|
|
391
|
-
|
|
392
|
-
const b = pruneAuditFile(decisionLog, cut2);
|
|
393
|
-
appendAuditLine(decisionLog, { event: "retention", pruned_envpulse: a, pruned_decision: b });
|
|
394
|
-
console.log(`PRUNED envpulse:${a} decision:${b}`);
|
|
365
|
+
console.log(`DONE #${e.id}`);
|
|
395
366
|
return 0;
|
|
396
367
|
}
|
|
397
|
-
case "
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
const interests = loadInterests(paths);
|
|
402
|
-
console.log(JSON.stringify({
|
|
403
|
-
watchTargets: loadWatchlist(paths).targets?.length ?? 0,
|
|
404
|
-
lastCheckAt: st.last_check_at ? new Date(st.last_check_at).toISOString() : null,
|
|
405
|
-
lastWanderAt: st.wander.last_wander_at ? new Date(st.wander.last_wander_at).toISOString() : null,
|
|
406
|
-
focusCount: st.wander.focusCount,
|
|
407
|
-
interestCount: interests.interests?.length ?? 0
|
|
408
|
-
}, null, 2));
|
|
409
|
-
return 0;
|
|
410
|
-
}
|
|
411
|
-
case "dry": {
|
|
412
|
-
const forced = /* @__PURE__ */ new Date();
|
|
413
|
-
forced.setHours(12, 0, 0, 0);
|
|
414
|
-
const advice = adviseWander(guard, paths, policy, forced);
|
|
415
|
-
console.log(JSON.stringify(advice, null, 2));
|
|
416
|
-
return 0;
|
|
417
|
-
}
|
|
418
|
-
case "watch": {
|
|
419
|
-
const report = await checkWatchlist(guard, paths, { throttleOk: true });
|
|
420
|
-
console.log(JSON.stringify({ checked: report.checked, updates: report.items.length, errors: report.errors }, null, 2));
|
|
421
|
-
for (const item of report.items) console.log(`NEWS: ${item.text}`);
|
|
422
|
-
return 0;
|
|
423
|
-
}
|
|
424
|
-
case "done": {
|
|
425
|
-
const focus = rest.join(" ").trim();
|
|
426
|
-
if (!focus) {
|
|
427
|
-
console.error("usage: browse done <focus>");
|
|
428
|
-
return 1;
|
|
429
|
-
}
|
|
430
|
-
const r = completeWander(guard, paths, focus);
|
|
431
|
-
console.log(JSON.stringify(r));
|
|
432
|
-
return 0;
|
|
433
|
-
}
|
|
434
|
-
default:
|
|
435
|
-
console.error(`unknown browse subcommand: ${sub}`);
|
|
436
|
-
return 1;
|
|
368
|
+
case "open": {
|
|
369
|
+
const f = ledgerFile;
|
|
370
|
+
if (!fs2.existsSync(f)) {
|
|
371
|
+
console.log(`(ledger will be created at ${f})`);
|
|
437
372
|
}
|
|
373
|
+
spawn("cmd", ["/c", "start", "", f], { detached: true, stdio: "ignore" }).unref();
|
|
374
|
+
console.log(`opened ${f}`);
|
|
375
|
+
return 0;
|
|
438
376
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
377
|
+
default:
|
|
378
|
+
console.error(`unknown ledger subcommand: ${sub}`);
|
|
379
|
+
return 1;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
async function verbWeekly(c) {
|
|
383
|
+
const { guard, paths, sub, rest } = c;
|
|
384
|
+
const reports = listWeeklyReports(guard, paths.dataDir);
|
|
385
|
+
if (sub === "list" || sub === void 0) {
|
|
386
|
+
if (reports.length === 0) console.log("(no reports yet \u2014 heartbeat generates one every 7 days)");
|
|
387
|
+
for (const r of reports) console.log(`${r.file} ${r.start.slice(0, 10)} ~ ${r.end.slice(0, 10)} [${r.source}]`);
|
|
388
|
+
return 0;
|
|
389
|
+
}
|
|
390
|
+
if (sub === "show") {
|
|
391
|
+
const file = rest[0] ?? reports[0]?.file;
|
|
392
|
+
if (!file) {
|
|
393
|
+
console.log("(no reports yet)");
|
|
394
|
+
return 1;
|
|
395
|
+
}
|
|
396
|
+
const rep = readWeeklyReport(guard, paths.dataDir, file);
|
|
397
|
+
if (!rep) {
|
|
398
|
+
console.error(`no such report: ${file} (see: weekly list; files live in ${weeklyDirPath(paths.dataDir)})`);
|
|
399
|
+
return 1;
|
|
400
|
+
}
|
|
401
|
+
console.log(`# ${rep.start.slice(0, 10)} ~ ${rep.end.slice(0, 10)} [${rep.source}]`);
|
|
402
|
+
console.log(rep.text);
|
|
403
|
+
return 0;
|
|
404
|
+
}
|
|
405
|
+
console.error(`unknown weekly subcommand: ${sub}`);
|
|
406
|
+
return 1;
|
|
407
|
+
}
|
|
408
|
+
async function verbLogs(c) {
|
|
409
|
+
const { paths, policy, sub, rest, decisionLog } = c;
|
|
410
|
+
if (sub !== "cleanup") {
|
|
411
|
+
console.error("usage: logs cleanup [--dry-run]");
|
|
412
|
+
return 1;
|
|
413
|
+
}
|
|
414
|
+
const dry = rest.includes("--dry-run");
|
|
415
|
+
const envPulse = path2.join(paths.logsDir, "envpulse.jsonl");
|
|
416
|
+
const cut1 = policy.retention.envPulseHours * 36e5;
|
|
417
|
+
const cut2 = policy.retention.decisionLogDays * 864e5;
|
|
418
|
+
if (dry) {
|
|
419
|
+
console.log(`(dry-run) would prune ${envPulse} to ${policy.retention.envPulseHours}h and ${decisionLog} to ${policy.retention.decisionLogDays}d`);
|
|
420
|
+
return 0;
|
|
421
|
+
}
|
|
422
|
+
const a = pruneAuditFile(envPulse, cut1);
|
|
423
|
+
const b = pruneAuditFile(decisionLog, cut2);
|
|
424
|
+
appendAuditLine(decisionLog, { event: "retention", pruned_envpulse: a, pruned_decision: b });
|
|
425
|
+
console.log(`PRUNED envpulse:${a} decision:${b}`);
|
|
426
|
+
return 0;
|
|
427
|
+
}
|
|
428
|
+
async function verbBrowse(c) {
|
|
429
|
+
const { guard, paths, policy, sub, rest } = c;
|
|
430
|
+
switch (sub) {
|
|
431
|
+
case "status": {
|
|
432
|
+
const st = browseStatus(guard, paths);
|
|
433
|
+
const interests = loadInterests(paths);
|
|
434
|
+
console.log(JSON.stringify({
|
|
435
|
+
lastWanderAt: st.wander.last_wander_at ? new Date(st.wander.last_wander_at).toISOString() : null,
|
|
436
|
+
focusCount: st.wander.focusCount,
|
|
437
|
+
interestCount: interests.interests?.length ?? 0
|
|
438
|
+
}, null, 2));
|
|
439
|
+
return 0;
|
|
440
|
+
}
|
|
441
|
+
case "dry": {
|
|
442
|
+
const forced = /* @__PURE__ */ new Date();
|
|
443
|
+
forced.setHours(12, 0, 0, 0);
|
|
444
|
+
const advice = adviseWander(guard, paths, policy, forced);
|
|
445
|
+
console.log(JSON.stringify(advice, null, 2));
|
|
446
|
+
return 0;
|
|
447
|
+
}
|
|
448
|
+
case "done": {
|
|
449
|
+
const focus = rest.join(" ").trim();
|
|
450
|
+
if (!focus) {
|
|
451
|
+
console.error("usage: browse done <focus>");
|
|
452
|
+
return 1;
|
|
454
453
|
}
|
|
454
|
+
const r = completeWander(guard, paths, focus);
|
|
455
|
+
console.log(JSON.stringify(r));
|
|
456
|
+
return 0;
|
|
455
457
|
}
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
case "rebuild": {
|
|
492
|
-
const r = rebuildProfile(guard, paths.dataDir, { check: rest.includes("--check") });
|
|
493
|
-
console.log(JSON.stringify(r, null, 2));
|
|
494
|
-
return r.ok ? 0 : 1;
|
|
495
|
-
}
|
|
496
|
-
case "wipe": {
|
|
497
|
-
if (!rest.includes("--yes")) {
|
|
498
|
-
console.log("REFUSED: add --yes to wipe profile data (profile.json/inbox/journal)");
|
|
499
|
-
return 1;
|
|
500
|
-
}
|
|
501
|
-
for (const f of ["profile.json", "profile_inbox.jsonl", "profile_journal.jsonl"]) {
|
|
502
|
-
const abs = guard.assert(path2.join(paths.dataDir, f));
|
|
503
|
-
if (fs2.existsSync(abs)) fs2.rmSync(abs, { force: true });
|
|
504
|
-
}
|
|
505
|
-
console.log("WIPED (settings preserved; use burn for full shredding)");
|
|
506
|
-
return 0;
|
|
458
|
+
default:
|
|
459
|
+
console.error(`unknown browse subcommand: ${sub}`);
|
|
460
|
+
return 1;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
async function verbNotify(c) {
|
|
464
|
+
const { paths, sub } = c;
|
|
465
|
+
switch (sub) {
|
|
466
|
+
case "check":
|
|
467
|
+
console.log(await ensureRegistered(paths) ? "REGISTERED: yes" : "REGISTERED: no");
|
|
468
|
+
return 0;
|
|
469
|
+
case "register":
|
|
470
|
+
await ensureRegistered(paths);
|
|
471
|
+
console.log("register attempted");
|
|
472
|
+
return 0;
|
|
473
|
+
case "send":
|
|
474
|
+
console.log(await sendNewMessageHint(paths) ? "SENT" : "FAILED");
|
|
475
|
+
return 0;
|
|
476
|
+
default:
|
|
477
|
+
console.error("usage: notify check|register|send");
|
|
478
|
+
return 1;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
async function verbProfile(c) {
|
|
482
|
+
const { guard, paths, sub, rest } = c;
|
|
483
|
+
const doc = loadProfile(guard, profileFilePath(paths.dataDir));
|
|
484
|
+
const all = rest.includes("--all");
|
|
485
|
+
switch (sub) {
|
|
486
|
+
case "list": {
|
|
487
|
+
let n = 0;
|
|
488
|
+
for (const p of ["interest", "projects", "comm", "psy"]) {
|
|
489
|
+
for (const e of doc.partitions[p].entries) {
|
|
490
|
+
if (!all && e.validTo !== null) continue;
|
|
491
|
+
n += 1;
|
|
492
|
+
console.log(`${e.id} [${e.partition}/${e.topic}/${e.subTopic}] ${e.temporal} conf=${e.confidence.toFixed(2)}${e.lowActivity ? " \u4E45\u672A\u9A8C\u8BC1" : ""}${e.validTo ? " [\u5931\u6548]" : ""}: ${e.content.slice(0, 60)}`);
|
|
507
493
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
if (
|
|
518
|
-
|
|
519
|
-
return 0;
|
|
520
|
-
}
|
|
521
|
-
console.log(`\u5DF2\u6298\u53E0 ${report.folded} \u6761 \u2192 \u5FEB\u7167\uFF08baseline ${report.baselineTs}\uFF09\uFF0C\u5F52\u6863 ${report.archiveFile}\uFF0Clive journal \u5DF2\u6E05\u7A7A\u3002`);
|
|
522
|
-
console.log("verify/rebuild \u81EA\u52A8\u4ECE\u5FEB\u7167\u57FA\u70B9\u91CD\u653E\uFF0C\u65E0\u9700\u989D\u5916\u64CD\u4F5C\u3002");
|
|
523
|
-
return 0;
|
|
494
|
+
}
|
|
495
|
+
if (n === 0) console.log("(empty)");
|
|
496
|
+
return 0;
|
|
497
|
+
}
|
|
498
|
+
case "export": {
|
|
499
|
+
const lines = [`# \u753B\u50CF\u5BFC\u51FA ${(/* @__PURE__ */ new Date()).toISOString()}`, ""];
|
|
500
|
+
for (const p of ["interest", "projects", "comm", "psy"]) {
|
|
501
|
+
lines.push(`## ${p}`);
|
|
502
|
+
for (const e of doc.partitions[p].entries) {
|
|
503
|
+
if (e.validTo !== null) continue;
|
|
504
|
+
lines.push(`- [${e.topic}/${e.subTopic}] ${e.content} (conf ${e.confidence.toFixed(2)}, ${e.temporal})`);
|
|
524
505
|
}
|
|
525
|
-
default:
|
|
526
|
-
console.error(`unknown profile subcommand: ${sub}`);
|
|
527
|
-
return 1;
|
|
528
506
|
}
|
|
507
|
+
const out = path2.join(paths.exportsDir, `profile-export-${Date.now()}.md`);
|
|
508
|
+
writeText(guard, out, lines.join("\n") + "\n");
|
|
509
|
+
console.log(`EXPORTED: ${out}`);
|
|
510
|
+
return 0;
|
|
529
511
|
}
|
|
530
|
-
case "
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
512
|
+
case "verify": {
|
|
513
|
+
const r = verifyProfile(guard, paths.dataDir);
|
|
514
|
+
console.log(JSON.stringify(r, null, 2));
|
|
515
|
+
return r.ok ? 0 : 1;
|
|
516
|
+
}
|
|
517
|
+
case "rebuild": {
|
|
518
|
+
const r = rebuildProfile(guard, paths.dataDir, { check: rest.includes("--check") });
|
|
519
|
+
console.log(JSON.stringify(r, null, 2));
|
|
520
|
+
return r.ok ? 0 : 1;
|
|
521
|
+
}
|
|
522
|
+
case "wipe": {
|
|
523
|
+
if (!rest.includes("--yes")) {
|
|
524
|
+
console.log("REFUSED: add --yes to wipe profile data (profile.json/inbox/journal)");
|
|
525
|
+
return 1;
|
|
539
526
|
}
|
|
540
|
-
const
|
|
541
|
-
|
|
527
|
+
for (const f of ["profile.json", "profile_inbox.jsonl", "profile_journal.jsonl"]) {
|
|
528
|
+
const abs = guard.assert(path2.join(paths.dataDir, f));
|
|
529
|
+
if (fs2.existsSync(abs)) fs2.rmSync(abs, { force: true });
|
|
530
|
+
}
|
|
531
|
+
console.log("WIPED (settings preserved; use burn for full shredding)");
|
|
542
532
|
return 0;
|
|
543
533
|
}
|
|
544
|
-
case "
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
outCanon = guard.assert(outfile);
|
|
551
|
-
} catch {
|
|
552
|
-
console.error(`\u5BFC\u51FA\u6587\u4EF6\u5FC5\u987B\u843D\u5728\u63D2\u4EF6 data/ \u76EE\u5F55\u5185\uFF08\u8DEF\u5F84\u5B88\u536B\u62D2\u7EDD\uFF09\uFF1A${outfile}
|
|
553
|
-
\u9ED8\u8BA4\u4F4D\u7F6E\uFF1A${defaultOut}`);
|
|
554
|
-
return 1;
|
|
555
|
-
}
|
|
556
|
-
const [pw1, pw2] = await readLines(["\u8BBE\u7F6E\u53E3\u4EE4\uFF08\u7528\u4E8E\u52A0\u5BC6\u8FC1\u79FB\u5305\uFF09: ", "\u518D\u8F93\u5165\u4E00\u6B21\u786E\u8BA4: "]);
|
|
557
|
-
if (!pw1 || pw1 !== pw2) {
|
|
558
|
-
console.error("\u4E24\u6B21\u8F93\u5165\u4E0D\u4E00\u81F4\u6216\u4E3A\u7A7A\uFF0C\u5DF2\u53D6\u6D88\u3002");
|
|
559
|
-
return 1;
|
|
560
|
-
}
|
|
561
|
-
const { entries, progress } = collectMigrationEntries(guard, paths);
|
|
562
|
-
if (entries.length === 0) {
|
|
563
|
-
console.log("\u6CA1\u6709\u53EF\u6253\u5305\u7684\u8BB0\u5FC6\u6587\u4EF6\uFF08data/ \u662F\u7A7A\u7684\uFF09\u3002");
|
|
564
|
-
return 1;
|
|
565
|
-
}
|
|
566
|
-
fs2.mkdirSync(path2.dirname(outCanon), { recursive: true });
|
|
567
|
-
fs2.writeFileSync(outCanon, encryptContainer(entries, pw1), "utf8");
|
|
568
|
-
console.log(`\u5DF2\u6253\u5305 ${entries.length} \u4E2A\u6587\u4EF6 \u2192 ${outCanon}`);
|
|
569
|
-
console.log(` \u6253\u5305\uFF1A${progress.packed.join(", ")}`);
|
|
570
|
-
if (progress.missing.length > 0) console.log(` \u8DF3\u8FC7\uFF08\u4E0D\u5B58\u5728\uFF09\uFF1A${progress.missing.join(", ")}`);
|
|
571
|
-
console.log("\u5BB9\u5668\u662F\u53E3\u4EE4\u52A0\u5BC6\u7684\uFF0C\u53EF\u5B89\u5168\u62F7\u8D1D\u5230\u65B0\u673A\u5668\uFF1BDPAPI \u4F1A\u5728\u5BFC\u5165\u65F6\u7528\u65B0\u673A\u5668\u91CD\u65B0\u52A0\u5BC6\u3002");
|
|
534
|
+
case "snapshot": {
|
|
535
|
+
const { snapshotDue, snapshotProfile, SNAPSHOT_THRESHOLD } = await import("../snapshot-K5VHETTK.js");
|
|
536
|
+
const { needed, lines } = snapshotDue(guard, paths.dataDir);
|
|
537
|
+
console.log(`journal ${lines} \u6761\uFF08\u9608\u503C ${SNAPSHOT_THRESHOLD}\uFF09`);
|
|
538
|
+
if (!needed && !rest.includes("--force")) {
|
|
539
|
+
console.log("\u672A\u5230\u9608\u503C\uFF0C\u672A\u6267\u884C\uFF08\u52A0 --force \u5F3A\u5236\u6298\u53E0\uFF09\u3002");
|
|
572
540
|
return 0;
|
|
573
541
|
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
console.error("usage: migrate import <container-file>");
|
|
578
|
-
return 1;
|
|
579
|
-
}
|
|
580
|
-
const [pw] = await readLines(["\u8F93\u5165\u8FC1\u79FB\u5305\u53E3\u4EE4: "]);
|
|
581
|
-
let text;
|
|
582
|
-
try {
|
|
583
|
-
text = fs2.readFileSync(infile, "utf8");
|
|
584
|
-
} catch {
|
|
585
|
-
console.error(`\u8BFB\u4E0D\u5230\u8FC1\u79FB\u5305\uFF1A${infile}`);
|
|
586
|
-
return 1;
|
|
587
|
-
}
|
|
588
|
-
let files;
|
|
589
|
-
try {
|
|
590
|
-
files = decryptContainer(text, pw ?? "");
|
|
591
|
-
} catch (e) {
|
|
592
|
-
console.error(String(e instanceof Error ? e.message : e));
|
|
593
|
-
return 1;
|
|
594
|
-
}
|
|
595
|
-
const result = applyMigrationEntries(guard, paths, files.files);
|
|
596
|
-
console.log(`\u6062\u590D ${result.restored.length} \u4E2A\u6587\u4EF6\uFF1B\u5907\u4EFD ${result.backedUp.length} \u4E2A\u88AB\u8986\u76D6\u6587\u4EF6\uFF1B\u8DF3\u8FC7 ${result.skipped.length} \u4E2A\u3002`);
|
|
597
|
-
for (const r of result.restored) console.log(` \u6062\u590D: ${r}`);
|
|
598
|
-
for (const b of result.backedUp) console.log(` \u5907\u4EFD: ${b}.bak-migrate-*`);
|
|
599
|
-
if (result.skipped.length > 0) console.log(` \u8DF3\u8FC7: ${result.skipped.join(", ")}`);
|
|
600
|
-
console.log("\u91CD\u542F DSH \u540E\u751F\u6548\u3002");
|
|
542
|
+
const report = snapshotProfile(guard, paths.dataDir);
|
|
543
|
+
if (!report.ok) {
|
|
544
|
+
console.log(`\u672A\u6267\u884C\uFF1A${report.reason}`);
|
|
601
545
|
return 0;
|
|
602
546
|
}
|
|
603
|
-
console.
|
|
547
|
+
console.log(`\u5DF2\u6298\u53E0 ${report.folded} \u6761 \u2192 \u5FEB\u7167\uFF08baseline ${report.baselineTs}\uFF09\uFF0C\u5F52\u6863 ${report.archiveFile}\uFF0Clive journal \u5DF2\u6E05\u7A7A\u3002`);
|
|
548
|
+
console.log("verify/rebuild \u81EA\u52A8\u4ECE\u5FEB\u7167\u57FA\u70B9\u91CD\u653E\uFF0C\u65E0\u9700\u989D\u5916\u64CD\u4F5C\u3002");
|
|
549
|
+
return 0;
|
|
550
|
+
}
|
|
551
|
+
default:
|
|
552
|
+
console.error(`unknown profile subcommand: ${sub}`);
|
|
553
|
+
return 1;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
async function verbBurn(c) {
|
|
557
|
+
const { guard, paths, rest } = c;
|
|
558
|
+
const yes = rest.includes("--yes");
|
|
559
|
+
const all = rest.includes("--all");
|
|
560
|
+
const plan = planBurn(guard, paths, all);
|
|
561
|
+
console.log("\u9884\u6F14\uFF08\u4E0D\u4F1A\u6267\u884C\uFF09:");
|
|
562
|
+
for (const p of plan) console.log(` [${p.exists ? "\u5B58\u5728" : "\u65E0 "}] ${p.target.file ?? p.target.dir} ${p.target.note}`);
|
|
563
|
+
if (!yes) {
|
|
564
|
+
console.log("\n\u6B64\u64CD\u4F5C\u4F1A\u8BA9\u5FC3\u8DF3 agent \u5931\u5FC6\u3002\u786E\u8BA4\u6267\u884C\u8BF7\u52A0 --yes\uFF08" + (all ? "\u542B --all \u8FDE\u7528\u6237\u8BBE\u5B9A" : "\u7528\u6237\u8BBE\u5B9A\u4FDD\u7559") + "\uFF09");
|
|
565
|
+
return 0;
|
|
566
|
+
}
|
|
567
|
+
const result = executeBurn(guard, paths, { all });
|
|
568
|
+
console.log(`BURNED: ${result.burned.length} \u9879\uFF1BMISSING: ${result.missing.length} \u9879`);
|
|
569
|
+
return 0;
|
|
570
|
+
}
|
|
571
|
+
async function verbMigrate(c) {
|
|
572
|
+
const { guard, paths, sub, rest } = c;
|
|
573
|
+
if (sub === "export") {
|
|
574
|
+
const defaultOut = path2.join(paths.exportsDir, `heartbeat-memory-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.hbmig`);
|
|
575
|
+
const outfile = rest[0] ?? defaultOut;
|
|
576
|
+
let outCanon;
|
|
577
|
+
try {
|
|
578
|
+
outCanon = guard.assert(outfile);
|
|
579
|
+
} catch {
|
|
580
|
+
console.error(`\u5BFC\u51FA\u6587\u4EF6\u5FC5\u987B\u843D\u5728\u63D2\u4EF6 data/ \u76EE\u5F55\u5185\uFF08\u8DEF\u5F84\u5B88\u536B\u62D2\u7EDD\uFF09\uFF1A${outfile}
|
|
581
|
+
\u9ED8\u8BA4\u4F4D\u7F6E\uFF1A${defaultOut}`);
|
|
604
582
|
return 1;
|
|
605
583
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
584
|
+
const [pw1, pw2] = await readLines(["\u8BBE\u7F6E\u53E3\u4EE4\uFF08\u7528\u4E8E\u52A0\u5BC6\u8FC1\u79FB\u5305\uFF09: ", "\u518D\u8F93\u5165\u4E00\u6B21\u786E\u8BA4: "]);
|
|
585
|
+
if (!pw1 || pw1 !== pw2) {
|
|
586
|
+
console.error("\u4E24\u6B21\u8F93\u5165\u4E0D\u4E00\u81F4\u6216\u4E3A\u7A7A\uFF0C\u5DF2\u53D6\u6D88\u3002");
|
|
587
|
+
return 1;
|
|
588
|
+
}
|
|
589
|
+
const { entries, progress } = collectMigrationEntries(guard, paths);
|
|
590
|
+
if (entries.length === 0) {
|
|
591
|
+
console.log("\u6CA1\u6709\u53EF\u6253\u5305\u7684\u8BB0\u5FC6\u6587\u4EF6\uFF08data/ \u662F\u7A7A\u7684\uFF09\u3002");
|
|
592
|
+
return 1;
|
|
593
|
+
}
|
|
594
|
+
fs2.mkdirSync(path2.dirname(outCanon), { recursive: true });
|
|
595
|
+
atomicWriteFileSync(outCanon, encryptContainer(entries, pw1));
|
|
596
|
+
console.log(`\u5DF2\u6253\u5305 ${entries.length} \u4E2A\u6587\u4EF6 \u2192 ${outCanon}`);
|
|
597
|
+
console.log(` \u6253\u5305\uFF1A${progress.packed.join(", ")}`);
|
|
598
|
+
if (progress.missing.length > 0) console.log(` \u8DF3\u8FC7\uFF08\u4E0D\u5B58\u5728\uFF09\uFF1A${progress.missing.join(", ")}`);
|
|
599
|
+
console.log("\u5BB9\u5668\u662F\u53E3\u4EE4\u52A0\u5BC6\u7684\uFF0C\u53EF\u5B89\u5168\u62F7\u8D1D\u5230\u65B0\u673A\u5668\uFF1BDPAPI \u4F1A\u5728\u5BFC\u5165\u65F6\u7528\u65B0\u673A\u5668\u91CD\u65B0\u52A0\u5BC6\u3002");
|
|
600
|
+
return 0;
|
|
601
|
+
}
|
|
602
|
+
if (sub === "import") {
|
|
603
|
+
const infile = rest[0];
|
|
604
|
+
if (!infile) {
|
|
605
|
+
console.error("usage: migrate import <container-file>");
|
|
606
|
+
return 1;
|
|
607
|
+
}
|
|
608
|
+
const [pw] = await readLines(["\u8F93\u5165\u8FC1\u79FB\u5305\u53E3\u4EE4: "]);
|
|
609
|
+
let text;
|
|
610
|
+
try {
|
|
611
|
+
text = fs2.readFileSync(infile, "utf8");
|
|
612
|
+
} catch {
|
|
613
|
+
console.error(`\u8BFB\u4E0D\u5230\u8FC1\u79FB\u5305\uFF1A${infile}`);
|
|
614
|
+
return 1;
|
|
615
|
+
}
|
|
616
|
+
let files;
|
|
617
|
+
try {
|
|
618
|
+
files = decryptContainer(text, pw ?? "");
|
|
619
|
+
} catch (e) {
|
|
620
|
+
console.error(String(e instanceof Error ? e.message : e));
|
|
621
|
+
return 1;
|
|
622
|
+
}
|
|
623
|
+
const result = applyMigrationEntries(guard, paths, files.files);
|
|
624
|
+
console.log(`\u6062\u590D ${result.restored.length} \u4E2A\u6587\u4EF6\uFF1B\u5907\u4EFD ${result.backedUp.length} \u4E2A\u88AB\u8986\u76D6\u6587\u4EF6\uFF1B\u8DF3\u8FC7 ${result.skipped.length} \u4E2A\u3002`);
|
|
625
|
+
for (const r of result.restored) console.log(` \u6062\u590D: ${r}`);
|
|
626
|
+
for (const b of result.backedUp) console.log(` \u5907\u4EFD: ${b}.bak-migrate-*`);
|
|
627
|
+
if (result.skipped.length > 0) console.log(` \u8DF3\u8FC7: ${result.skipped.join(", ")}`);
|
|
628
|
+
if (result.error) console.log(` \u5931\u8D25\u5E76\u5DF2\u56DE\u6EDA\uFF1A${result.error}`);
|
|
629
|
+
console.log("\u91CD\u542F DSH \u540E\u751F\u6548\u3002");
|
|
630
|
+
return result.error ? 1 : 0;
|
|
631
|
+
}
|
|
632
|
+
console.error(`unknown migrate subcommand: ${sub}`);
|
|
633
|
+
return 1;
|
|
634
|
+
}
|
|
635
|
+
async function verbSessions(c) {
|
|
636
|
+
const { guard, paths } = c;
|
|
637
|
+
const root = path2.join(os.homedir(), ".dsh", "sessions");
|
|
638
|
+
let found = 0;
|
|
639
|
+
if (fs2.existsSync(root)) {
|
|
640
|
+
const { homeSessionId } = await import("../orchestrator-EUTZ3KG6.js");
|
|
641
|
+
const own = homeSessionId(guard, paths);
|
|
642
|
+
for (const slug of fs2.readdirSync(root)) {
|
|
643
|
+
for (const id of fs2.readdirSync(path2.join(root, slug))) {
|
|
644
|
+
found += 1;
|
|
645
|
+
const mark = id === own ? " \u2190 \u5FC3\u8DF3\u6B63\u8EAB" : "";
|
|
646
|
+
console.log(`${id} [${slug}]${mark}`);
|
|
622
647
|
}
|
|
623
|
-
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if (found === 0) console.log("(no persisted sessions found)");
|
|
651
|
+
return 0;
|
|
652
|
+
}
|
|
653
|
+
async function verbBind(c) {
|
|
654
|
+
const { guard, paths, sub, rest } = c;
|
|
655
|
+
const { loadBindings, addBinding, removeBinding } = await import("../bindings-225SI7KC.js");
|
|
656
|
+
switch (sub) {
|
|
657
|
+
case "list": {
|
|
658
|
+
const data = loadBindings(guard, paths.settingsDir);
|
|
659
|
+
const { homeSessionId } = await import("../orchestrator-EUTZ3KG6.js");
|
|
660
|
+
const own = homeSessionId(guard, paths);
|
|
661
|
+
if (own) console.log(`\u5FC3\u8DF3\u6B63\u8EAB: ${own}\uFF08\u51B3\u7B56\u8F6E\u6B21\u53D1\u751F\u5730\uFF1Bbind remove \u5B83 = \u91CD\u7F6E\u6B63\u8EAB\uFF09`);
|
|
662
|
+
for (const b of data.bindings) {
|
|
663
|
+
console.log(`${b.sessionId} deliver:${b.deliver ? "\u221A" : "\xD7"} observe:${b.observe ? "\u221A" : "\xD7"}`);
|
|
664
|
+
}
|
|
665
|
+
if (data.bindings.length === 0 && !own) console.log("(no bindings \u2014 expressions stay in the dedicated heartbeat session)");
|
|
624
666
|
return 0;
|
|
625
667
|
}
|
|
626
|
-
case "
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
let own = null;
|
|
632
|
-
try {
|
|
633
|
-
own = JSON.parse(loadEncryptedText(guard, path2.join(paths.dataDir, "gate.json")) ?? "{}").sessionId ?? null;
|
|
634
|
-
} catch {
|
|
635
|
-
}
|
|
636
|
-
if (own) console.log(`\u5FC3\u8DF3\u6B63\u8EAB: ${own}\uFF08\u51B3\u7B56\u8F6E\u6B21\u53D1\u751F\u5730\uFF1Bbind remove \u5B83 = \u91CD\u7F6E\u6B63\u8EAB\uFF09`);
|
|
637
|
-
for (const b of data.bindings) {
|
|
638
|
-
console.log(`${b.sessionId} deliver:${b.deliver ? "\u221A" : "\xD7"} observe:${b.observe ? "\u221A" : "\xD7"}`);
|
|
639
|
-
}
|
|
640
|
-
if (data.bindings.length === 0 && !own) console.log("(no bindings \u2014 expressions stay in the dedicated heartbeat session)");
|
|
641
|
-
return 0;
|
|
642
|
-
}
|
|
643
|
-
case "add": {
|
|
644
|
-
const id = rest[0];
|
|
645
|
-
if (!id || !id.startsWith("session-")) {
|
|
646
|
-
console.error("usage: bind add <sessionId> [--observe] [--no-deliver] (see: sessions list)");
|
|
647
|
-
return 1;
|
|
648
|
-
}
|
|
649
|
-
const observe = rest.includes("--observe") || rest.includes("--observe-only");
|
|
650
|
-
const deliver = !rest.includes("--no-deliver") && !rest.includes("--observe-only");
|
|
651
|
-
const b = addBinding(guard, paths.settingsDir, id, { deliver, observe });
|
|
652
|
-
console.log(`BOUND ${b.sessionId} deliver:${b.deliver} observe:${b.observe}`);
|
|
653
|
-
return 0;
|
|
654
|
-
}
|
|
655
|
-
case "remove": {
|
|
656
|
-
const id = rest[0];
|
|
657
|
-
if (!id) {
|
|
658
|
-
console.error("usage: bind remove <sessionId>");
|
|
659
|
-
return 1;
|
|
660
|
-
}
|
|
661
|
-
console.log(removeBinding(guard, paths.settingsDir, id) ? `UNBOUND ${id}` : "NOT_FOUND");
|
|
662
|
-
try {
|
|
663
|
-
const state = JSON.parse(loadEncryptedText(guard, path2.join(paths.dataDir, "gate.json")) ?? "{}");
|
|
664
|
-
if (state.sessionId === id) {
|
|
665
|
-
fs2.rmSync(guard.assert(path2.join(paths.dataDir, "gate.json")), { force: true });
|
|
666
|
-
console.log("\u6CE8\u610F\uFF1A\u8FD9\u662F\u5FC3\u8DF3\u6B63\u8EAB\u4F1A\u8BDD\u3002\u5DF2\u91CD\u7F6E\u2014\u2014\u4E0B\u6B21\u5FC3\u8DF3\u5C06\u521B\u5EFA\u65B0\u7684\u6B63\u8EAB\u4F1A\u8BDD\uFF08\u65E7\u4F1A\u8BDD\u4E0D\u518D\u6709\u5FC3\u8DF3\uFF09");
|
|
667
|
-
appendAuditLine(paths.logsDir + "/heartbeat.jsonl", { event: "home_reset", oldSessionId: id });
|
|
668
|
-
}
|
|
669
|
-
} catch {
|
|
670
|
-
}
|
|
671
|
-
return 0;
|
|
672
|
-
}
|
|
673
|
-
default:
|
|
674
|
-
console.error("usage: bind list | add <sessionId> | remove <sessionId>");
|
|
675
|
-
return 1;
|
|
668
|
+
case "add": {
|
|
669
|
+
const id = rest[0];
|
|
670
|
+
if (!id || !id.startsWith("session-")) {
|
|
671
|
+
console.error("usage: bind add <sessionId> [--observe] [--no-deliver] (see: sessions list)");
|
|
672
|
+
return 1;
|
|
676
673
|
}
|
|
674
|
+
const observe = rest.includes("--observe") || rest.includes("--observe-only");
|
|
675
|
+
const deliver = !rest.includes("--no-deliver") && !rest.includes("--observe-only");
|
|
676
|
+
const b = addBinding(guard, paths.settingsDir, id, { deliver, observe });
|
|
677
|
+
console.log(`BOUND ${b.sessionId} deliver:${b.deliver} observe:${b.observe}`);
|
|
678
|
+
return 0;
|
|
677
679
|
}
|
|
678
|
-
case "
|
|
679
|
-
const id =
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
console.log(`preset id : ${status.id}`);
|
|
684
|
-
console.log(`installed : ${status.installed ? "yes" : "no"}`);
|
|
685
|
-
console.log(`target dir : ${status.dir}`);
|
|
686
|
-
console.log(`bundled at : ${status.bundledDir ?? "(not found next to the plugin)"}`);
|
|
687
|
-
if (status.installed) {
|
|
688
|
-
console.log(
|
|
689
|
-
`composition : ${status.compositionMatches ? "matches the bundled template" : "differs from the bundled template (hand-edited, or an older version)"}`
|
|
690
|
-
);
|
|
691
|
-
}
|
|
692
|
-
if (!status.installed) {
|
|
693
|
-
console.log("hint: the plugin installs this itself on the next DSH start (installPreset=true, default);");
|
|
694
|
-
console.log(" or run `preset install` now.");
|
|
695
|
-
}
|
|
696
|
-
return status.installed ? 0 : 1;
|
|
680
|
+
case "remove": {
|
|
681
|
+
const id = rest[0];
|
|
682
|
+
if (!id) {
|
|
683
|
+
console.error("usage: bind remove <sessionId>");
|
|
684
|
+
return 1;
|
|
697
685
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
root,
|
|
703
|
-
force: rest.includes("--force")
|
|
704
|
-
});
|
|
705
|
-
console.log(describeInstall(result));
|
|
706
|
-
return result.action === "error" ? 1 : 0;
|
|
686
|
+
console.log(removeBinding(guard, paths.settingsDir, id) ? `UNBOUND ${id}` : "NOT_FOUND");
|
|
687
|
+
const { resetHomeSession } = await import("../orchestrator-EUTZ3KG6.js");
|
|
688
|
+
if (resetHomeSession(guard, paths, id)) {
|
|
689
|
+
console.log("\u6CE8\u610F\uFF1A\u8FD9\u662F\u5FC3\u8DF3\u6B63\u8EAB\u4F1A\u8BDD\u3002\u5DF2\u91CD\u7F6E\u2014\u2014\u4E0B\u6B21\u5FC3\u8DF3\u5C06\u521B\u5EFA\u65B0\u7684\u6B63\u8EAB\u4F1A\u8BDD\uFF08\u65E7\u4F1A\u8BDD\u4E0D\u518D\u6709\u5FC3\u8DF3\uFF09");
|
|
707
690
|
}
|
|
708
|
-
|
|
709
|
-
return 1;
|
|
691
|
+
return 0;
|
|
710
692
|
}
|
|
711
693
|
default:
|
|
712
|
-
console.error(
|
|
713
|
-
console.log(usage());
|
|
694
|
+
console.error("usage: bind list | add <sessionId> | remove <sessionId>");
|
|
714
695
|
return 1;
|
|
715
696
|
}
|
|
716
697
|
}
|
|
698
|
+
async function verbPreset(c) {
|
|
699
|
+
const { sub, rest, argv } = c;
|
|
700
|
+
const id = flag(argv, "--id") ?? BUNDLED_PRESET_ID;
|
|
701
|
+
const root = conventionalUserPresetRoot();
|
|
702
|
+
const status = presetStatus(import.meta.url, id, root);
|
|
703
|
+
if (sub === void 0 || sub === "status") {
|
|
704
|
+
console.log(`preset id : ${status.id}`);
|
|
705
|
+
console.log(`installed : ${status.installed ? "yes" : "no"}`);
|
|
706
|
+
console.log(`target dir : ${status.dir}`);
|
|
707
|
+
console.log(`bundled at : ${status.bundledDir ?? "(not found next to the plugin)"}`);
|
|
708
|
+
if (status.installed) {
|
|
709
|
+
console.log(
|
|
710
|
+
`composition : ${status.compositionMatches ? "matches the bundled template" : "differs from the bundled template (hand-edited, or an older version)"}`
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
if (!status.installed) {
|
|
714
|
+
console.log("hint: the plugin installs this itself on the next DSH start (installPreset=true, default);");
|
|
715
|
+
console.log(" or run `preset install` now.");
|
|
716
|
+
}
|
|
717
|
+
return status.installed ? 0 : 1;
|
|
718
|
+
}
|
|
719
|
+
if (sub === "install") {
|
|
720
|
+
const result = installBundledPreset({
|
|
721
|
+
moduleUrl: import.meta.url,
|
|
722
|
+
id,
|
|
723
|
+
root,
|
|
724
|
+
force: rest.includes("--force")
|
|
725
|
+
});
|
|
726
|
+
console.log(describeInstall(result));
|
|
727
|
+
return result.action === "error" ? 1 : 0;
|
|
728
|
+
}
|
|
729
|
+
console.error("usage: preset status | install [--force] [--id <presetId>]");
|
|
730
|
+
return 1;
|
|
731
|
+
}
|
|
732
|
+
var VERBS = {
|
|
733
|
+
status: verbStatus,
|
|
734
|
+
seeds: verbSeeds,
|
|
735
|
+
ledger: verbLedger,
|
|
736
|
+
weekly: verbWeekly,
|
|
737
|
+
logs: verbLogs,
|
|
738
|
+
browse: verbBrowse,
|
|
739
|
+
notify: verbNotify,
|
|
740
|
+
profile: verbProfile,
|
|
741
|
+
burn: verbBurn,
|
|
742
|
+
migrate: verbMigrate,
|
|
743
|
+
sessions: verbSessions,
|
|
744
|
+
bind: verbBind,
|
|
745
|
+
preset: verbPreset
|
|
746
|
+
};
|
|
747
|
+
async function main(argv) {
|
|
748
|
+
const [cmd, sub, ...rest] = argv;
|
|
749
|
+
if (!cmd || cmd === "help" || cmd === "--help") {
|
|
750
|
+
console.log(usage());
|
|
751
|
+
return 0;
|
|
752
|
+
}
|
|
753
|
+
const verb = VERBS[cmd];
|
|
754
|
+
if (!verb) {
|
|
755
|
+
console.error(`unknown command: ${cmd}`);
|
|
756
|
+
console.log(usage());
|
|
757
|
+
return 1;
|
|
758
|
+
}
|
|
759
|
+
const paths = initWorkspace();
|
|
760
|
+
const guard = createPathGuard(paths.dataDir);
|
|
761
|
+
const policy = loadPolicy(guard, paths.configDir, paths.settingsDir);
|
|
762
|
+
const decisionLog = path2.join(paths.logsDir, "heartbeat.jsonl");
|
|
763
|
+
const seedAudit = (entry) => {
|
|
764
|
+
try {
|
|
765
|
+
appendAuditLine(decisionLog, entry);
|
|
766
|
+
} catch {
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
return verb({
|
|
770
|
+
paths,
|
|
771
|
+
guard,
|
|
772
|
+
policy,
|
|
773
|
+
sub,
|
|
774
|
+
rest,
|
|
775
|
+
argv,
|
|
776
|
+
seedsFile: seedsFilePath(paths.dataDir),
|
|
777
|
+
ledgerFile: ledgerFilePath(paths.dataDir),
|
|
778
|
+
decisionLog,
|
|
779
|
+
seedAudit
|
|
780
|
+
});
|
|
781
|
+
}
|
|
717
782
|
if (process.argv[1] && import.meta.url === new URL(`file:///${process.argv[1].replace(/\\/g, "/")}`).href) {
|
|
718
783
|
void main(process.argv.slice(2)).then((code) => {
|
|
719
784
|
process.exitCode = code;
|
|
785
|
+
}).catch((e) => {
|
|
786
|
+
console.error(e);
|
|
787
|
+
process.exitCode = 1;
|
|
720
788
|
});
|
|
721
789
|
}
|
|
722
790
|
export {
|