clawvault 3.1.0 → 3.2.0
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 +422 -141
- package/bin/clawvault.js +10 -2
- package/bin/command-registration.test.js +3 -1
- package/bin/command-runtime.js +9 -1
- package/bin/register-core-commands.js +23 -28
- package/bin/register-maintenance-commands.js +39 -3
- package/bin/register-query-commands.js +58 -29
- package/bin/register-tailscale-commands.js +106 -0
- package/bin/register-task-commands.js +18 -1
- package/bin/register-task-commands.test.js +16 -0
- package/bin/register-vault-operations-commands.js +29 -1
- package/bin/register-workgraph-commands.js +1368 -0
- package/dashboard/lib/graph-diff.js +104 -0
- package/dashboard/lib/graph-diff.test.js +75 -0
- package/dashboard/lib/vault-parser.js +556 -0
- package/dashboard/lib/vault-parser.test.js +254 -0
- package/dashboard/public/app.js +796 -0
- package/dashboard/public/index.html +52 -0
- package/dashboard/public/styles.css +221 -0
- package/dashboard/server.js +374 -0
- package/dist/{chunk-F2JEUD4J.js → chunk-23YDQ3QU.js} +6 -8
- package/dist/{chunk-C7OK5WKP.js → chunk-2JQ3O2YL.js} +4 -4
- package/dist/{chunk-VR5NE7PZ.js → chunk-2RAZ4ZFE.js} +1 -1
- package/dist/chunk-2ZDO52B4.js +52 -0
- package/dist/{chunk-ZZA73MFY.js → chunk-33DOSHTA.js} +176 -36
- package/dist/chunk-33VSQP4J.js +37 -0
- package/dist/chunk-4BQTQMJP.js +93 -0
- package/dist/{chunk-GUKMRGM7.js → chunk-4OXMU5S2.js} +1 -1
- package/dist/{chunk-62YTUT6J.js → chunk-4PY655YM.js} +15 -3
- package/dist/chunk-6FH3IULF.js +352 -0
- package/dist/{chunk-3NSBOUT3.js → chunk-77Q5CSPJ.js} +404 -80
- package/dist/{chunk-4VQTUVH7.js → chunk-7YZWHM36.js} +52 -26
- package/dist/chunk-BSJ6RIT7.js +447 -0
- package/dist/chunk-BUEW6IIK.js +364 -0
- package/dist/{chunk-LI4O6NVK.js → chunk-CLJTREDS.js} +74 -14
- package/dist/chunk-EK6S23ZB.js +469 -0
- package/dist/{chunk-LNJA2UGL.js → chunk-ESFLMDRB.js} +9 -86
- package/dist/{chunk-H34S76MB.js → chunk-ESVS6K2B.js} +6 -6
- package/dist/{chunk-WAZ3NLWL.js → chunk-F55HGNU4.js} +0 -47
- package/dist/{chunk-QK3UCXWL.js → chunk-FHFUXL6G.js} +2 -2
- package/dist/{chunk-H62BP7RI.js → chunk-GAOWA7GR.js} +212 -46
- package/dist/chunk-GGA32J2R.js +784 -0
- package/dist/chunk-GNJL4YGR.js +79 -0
- package/dist/chunk-IVRIKYFE.js +520 -0
- package/dist/chunk-MDIH26GC.js +183 -0
- package/dist/{chunk-LYHGEHXG.js → chunk-MFAWT5O5.js} +0 -1
- package/dist/chunk-MM6QGW3P.js +207 -0
- package/dist/{chunk-P5EPF6MB.js → chunk-MW5C6ZQA.js} +110 -13
- package/dist/chunk-NCKFNBHJ.js +257 -0
- package/dist/{chunk-QBLMXKF2.js → chunk-OIWVQYQF.js} +1 -1
- package/dist/{chunk-42MXU7A6.js → chunk-P62WHA27.js} +58 -47
- package/dist/chunk-PBACDKKP.js +66 -0
- package/dist/{chunk-VGLOTGAS.js → chunk-QSHD36LH.js} +2 -2
- package/dist/{chunk-OZ7RIXTO.js → chunk-QSRRMEYM.js} +2 -2
- package/dist/chunk-QVEERJSP.js +152 -0
- package/dist/{chunk-N2AXRYLC.js → chunk-QWQ3TIKS.js} +1 -1
- package/dist/{chunk-3DHXQHYG.js → chunk-R2MIW5G7.js} +1 -1
- package/dist/{chunk-SJSFRIYS.js → chunk-SLXOR3CC.js} +2 -2
- package/dist/chunk-SS4B7P7V.js +99 -0
- package/dist/{chunk-JY6FYXIT.js → chunk-STCQGCEQ.js} +6 -11
- package/dist/chunk-TIGW564L.js +628 -0
- package/dist/chunk-U4O6C46S.js +154 -0
- package/dist/{chunk-ITPEXLHA.js → chunk-URXDAUVH.js} +24 -5
- package/dist/chunk-VSL7KY3M.js +189 -0
- package/dist/{chunk-U55BGUAU.js → chunk-W4SPAEE7.js} +6 -6
- package/dist/chunk-WMGIIABP.js +15 -0
- package/dist/{chunk-33UGEQRT.js → chunk-X3SPPUFG.js} +151 -64
- package/dist/chunk-Y6VJKXGL.js +373 -0
- package/dist/{chunk-3WRJEKN4.js → chunk-ZN54U2OZ.js} +123 -10
- package/dist/cli/index.js +34 -24
- package/dist/commands/archive.js +3 -3
- package/dist/commands/backlog.js +3 -3
- package/dist/commands/blocked.js +3 -3
- package/dist/commands/canvas.d.ts +15 -0
- package/dist/commands/canvas.js +200 -0
- package/dist/commands/checkpoint.js +2 -2
- package/dist/commands/compat.js +2 -2
- package/dist/commands/context.js +8 -6
- package/dist/commands/doctor.d.ts +11 -7
- package/dist/commands/doctor.js +18 -16
- package/dist/commands/embed.js +5 -6
- package/dist/commands/entities.js +2 -2
- package/dist/commands/graph.js +4 -4
- package/dist/commands/inject.d.ts +1 -1
- package/dist/commands/inject.js +5 -6
- package/dist/commands/kanban.js +4 -4
- package/dist/commands/link.js +5 -5
- package/dist/commands/migrate-observations.js +4 -4
- package/dist/commands/observe.d.ts +0 -1
- package/dist/commands/observe.js +14 -13
- package/dist/commands/project.js +5 -5
- package/dist/commands/rebuild-embeddings.d.ts +21 -0
- package/dist/commands/rebuild-embeddings.js +91 -0
- package/dist/commands/rebuild.js +12 -11
- package/dist/commands/recover.js +3 -3
- package/dist/commands/reflect.js +6 -7
- package/dist/commands/repair-session.js +1 -1
- package/dist/commands/replay.js +14 -14
- package/dist/commands/session-recap.js +1 -1
- package/dist/commands/setup.d.ts +2 -89
- package/dist/commands/setup.js +3 -21
- package/dist/commands/shell-init.js +1 -1
- package/dist/commands/sleep.d.ts +1 -1
- package/dist/commands/sleep.js +20 -19
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +57 -35
- package/dist/commands/sync-bd.d.ts +10 -0
- package/dist/commands/sync-bd.js +10 -0
- package/dist/commands/tailscale.d.ts +52 -0
- package/dist/commands/tailscale.js +26 -0
- package/dist/commands/task.js +4 -4
- package/dist/commands/template.js +2 -2
- package/dist/commands/wake.d.ts +1 -1
- package/dist/commands/wake.js +11 -10
- package/dist/commands/workgraph.d.ts +124 -0
- package/dist/commands/workgraph.js +38 -0
- package/dist/index.d.ts +341 -191
- package/dist/index.js +446 -116
- package/dist/{inject-Bzi5E-By.d.ts → inject-DYUrDqQO.d.ts} +3 -3
- package/dist/ledger-B7g7jhqG.d.ts +44 -0
- package/dist/lib/auto-linker.js +2 -2
- package/dist/lib/canvas-layout.d.ts +115 -0
- package/dist/lib/canvas-layout.js +35 -0
- package/dist/lib/config.d.ts +27 -3
- package/dist/lib/config.js +4 -2
- package/dist/lib/entity-index.js +1 -1
- package/dist/lib/project-utils.js +4 -4
- package/dist/lib/session-repair.js +1 -1
- package/dist/lib/session-utils.js +1 -1
- package/dist/lib/tailscale.d.ts +225 -0
- package/dist/lib/tailscale.js +50 -0
- package/dist/lib/task-utils.js +3 -3
- package/dist/lib/template-engine.js +1 -1
- package/dist/lib/webdav.d.ts +109 -0
- package/dist/lib/webdav.js +35 -0
- package/dist/onnxruntime_binding-5QEF3SUC.node +0 -0
- package/dist/onnxruntime_binding-BKPKNEGC.node +0 -0
- package/dist/onnxruntime_binding-FMOXGIUT.node +0 -0
- package/dist/onnxruntime_binding-OI2KMXC5.node +0 -0
- package/dist/onnxruntime_binding-UX44MLAZ.node +0 -0
- package/dist/onnxruntime_binding-Y2W7N7WY.node +0 -0
- package/dist/openclaw-plugin.d.ts +8 -0
- package/dist/openclaw-plugin.js +14 -0
- package/dist/registry-BR4326o0.d.ts +30 -0
- package/dist/store-CA-6sKCJ.d.ts +34 -0
- package/dist/thread-B9LhXNU0.d.ts +41 -0
- package/dist/transformers.node-A2ZRORSQ.js +46775 -0
- package/dist/{types-Y2_Um2Ls.d.ts → types-BbWJoC1c.d.ts} +1 -44
- package/dist/workgraph/index.d.ts +5 -0
- package/dist/workgraph/index.js +23 -0
- package/dist/workgraph/ledger.d.ts +2 -0
- package/dist/workgraph/ledger.js +25 -0
- package/dist/workgraph/registry.d.ts +2 -0
- package/dist/workgraph/registry.js +19 -0
- package/dist/workgraph/store.d.ts +2 -0
- package/dist/workgraph/store.js +25 -0
- package/dist/workgraph/thread.d.ts +2 -0
- package/dist/workgraph/thread.js +25 -0
- package/dist/workgraph/types.d.ts +54 -0
- package/dist/workgraph/types.js +7 -0
- package/hooks/clawvault/HOOK.md +113 -0
- package/hooks/clawvault/handler.js +1561 -0
- package/hooks/clawvault/handler.test.js +510 -0
- package/hooks/clawvault/openclaw.plugin.json +72 -0
- package/openclaw.plugin.json +65 -38
- package/package.json +25 -22
- package/dist/chunk-3RG5ZIWI.js +0 -10
- package/dist/chunk-3ZIH425O.js +0 -871
- package/dist/chunk-6U6MK36V.js +0 -205
- package/dist/chunk-CMB7UL7C.js +0 -327
- package/dist/chunk-D2H45LON.js +0 -1074
- package/dist/chunk-E7MFQB6D.js +0 -163
- package/dist/chunk-GQSLDZTS.js +0 -560
- package/dist/chunk-MFM6K7PU.js +0 -374
- package/dist/chunk-MXSSG3QU.js +0 -42
- package/dist/chunk-OCGVIN3L.js +0 -88
- package/dist/chunk-PAH27GSN.js +0 -108
- package/dist/chunk-YCUNCH2I.js +0 -78
- package/dist/cli/index.cjs +0 -8584
- package/dist/cli/index.d.cts +0 -5
- package/dist/commands/archive.cjs +0 -287
- package/dist/commands/archive.d.cts +0 -11
- package/dist/commands/backlog.cjs +0 -721
- package/dist/commands/backlog.d.cts +0 -53
- package/dist/commands/blocked.cjs +0 -204
- package/dist/commands/blocked.d.cts +0 -26
- package/dist/commands/checkpoint.cjs +0 -244
- package/dist/commands/checkpoint.d.cts +0 -41
- package/dist/commands/compat.cjs +0 -294
- package/dist/commands/compat.d.cts +0 -28
- package/dist/commands/context.cjs +0 -2990
- package/dist/commands/context.d.cts +0 -2
- package/dist/commands/doctor.cjs +0 -2986
- package/dist/commands/doctor.d.cts +0 -21
- package/dist/commands/embed.cjs +0 -232
- package/dist/commands/embed.d.cts +0 -17
- package/dist/commands/entities.cjs +0 -141
- package/dist/commands/entities.d.cts +0 -7
- package/dist/commands/graph.cjs +0 -501
- package/dist/commands/graph.d.cts +0 -21
- package/dist/commands/inject.cjs +0 -1636
- package/dist/commands/inject.d.cts +0 -2
- package/dist/commands/kanban.cjs +0 -884
- package/dist/commands/kanban.d.cts +0 -63
- package/dist/commands/link.cjs +0 -965
- package/dist/commands/link.d.cts +0 -11
- package/dist/commands/migrate-observations.cjs +0 -362
- package/dist/commands/migrate-observations.d.cts +0 -19
- package/dist/commands/observe.cjs +0 -4099
- package/dist/commands/observe.d.cts +0 -23
- package/dist/commands/project.cjs +0 -1341
- package/dist/commands/project.d.cts +0 -85
- package/dist/commands/rebuild.cjs +0 -3136
- package/dist/commands/rebuild.d.cts +0 -11
- package/dist/commands/recover.cjs +0 -361
- package/dist/commands/recover.d.cts +0 -38
- package/dist/commands/reflect.cjs +0 -1008
- package/dist/commands/reflect.d.cts +0 -11
- package/dist/commands/repair-session.cjs +0 -457
- package/dist/commands/repair-session.d.cts +0 -38
- package/dist/commands/replay.cjs +0 -4103
- package/dist/commands/replay.d.cts +0 -16
- package/dist/commands/session-recap.cjs +0 -353
- package/dist/commands/session-recap.d.cts +0 -27
- package/dist/commands/setup.cjs +0 -1278
- package/dist/commands/setup.d.cts +0 -99
- package/dist/commands/shell-init.cjs +0 -75
- package/dist/commands/shell-init.d.cts +0 -7
- package/dist/commands/sleep.cjs +0 -6029
- package/dist/commands/sleep.d.cts +0 -36
- package/dist/commands/status.cjs +0 -2737
- package/dist/commands/status.d.cts +0 -52
- package/dist/commands/task.cjs +0 -1236
- package/dist/commands/task.d.cts +0 -97
- package/dist/commands/template.cjs +0 -457
- package/dist/commands/template.d.cts +0 -36
- package/dist/commands/wake.cjs +0 -2627
- package/dist/commands/wake.d.cts +0 -22
- package/dist/context-BUGaWpyL.d.cts +0 -46
- package/dist/index.cjs +0 -12373
- package/dist/index.d.cts +0 -854
- package/dist/inject-Bzi5E-By.d.cts +0 -137
- package/dist/lib/auto-linker.cjs +0 -176
- package/dist/lib/auto-linker.d.cts +0 -26
- package/dist/lib/config.cjs +0 -78
- package/dist/lib/config.d.cts +0 -11
- package/dist/lib/entity-index.cjs +0 -84
- package/dist/lib/entity-index.d.cts +0 -26
- package/dist/lib/project-utils.cjs +0 -864
- package/dist/lib/project-utils.d.cts +0 -97
- package/dist/lib/session-repair.cjs +0 -239
- package/dist/lib/session-repair.d.cts +0 -110
- package/dist/lib/session-utils.cjs +0 -209
- package/dist/lib/session-utils.d.cts +0 -63
- package/dist/lib/task-utils.cjs +0 -1137
- package/dist/lib/task-utils.d.cts +0 -208
- package/dist/lib/template-engine.cjs +0 -47
- package/dist/lib/template-engine.d.cts +0 -11
- package/dist/plugin/index.cjs +0 -1907
- package/dist/plugin/index.d.cts +0 -36
- package/dist/plugin/index.d.ts +0 -36
- package/dist/plugin/index.js +0 -572
- package/dist/plugin/inject.cjs +0 -356
- package/dist/plugin/inject.d.cts +0 -54
- package/dist/plugin/inject.d.ts +0 -54
- package/dist/plugin/inject.js +0 -17
- package/dist/plugin/observe.cjs +0 -631
- package/dist/plugin/observe.d.cts +0 -39
- package/dist/plugin/observe.d.ts +0 -39
- package/dist/plugin/observe.js +0 -18
- package/dist/plugin/templates.cjs +0 -593
- package/dist/plugin/templates.d.cts +0 -52
- package/dist/plugin/templates.d.ts +0 -52
- package/dist/plugin/templates.js +0 -25
- package/dist/plugin/types.cjs +0 -18
- package/dist/plugin/types.d.cts +0 -209
- package/dist/plugin/types.d.ts +0 -209
- package/dist/plugin/types.js +0 -0
- package/dist/plugin/vault.cjs +0 -927
- package/dist/plugin/vault.d.cts +0 -68
- package/dist/plugin/vault.d.ts +0 -68
- package/dist/plugin/vault.js +0 -22
- package/dist/types-Y2_Um2Ls.d.cts +0 -205
- package/templates/memory-event.md +0 -67
- package/templates/party.md +0 -63
- package/templates/primitive-registry.yaml +0 -551
- package/templates/run.md +0 -68
- package/templates/trigger.md +0 -68
- package/templates/workspace.md +0 -50
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
// src/lib/llm-provider.ts
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import * as os from "os";
|
|
5
|
+
var DEFAULT_MODELS = {
|
|
6
|
+
anthropic: "claude-3-5-haiku-latest",
|
|
7
|
+
openai: "gpt-4o-mini",
|
|
8
|
+
gemini: "gemini-2.0-flash",
|
|
9
|
+
xai: "grok-2-latest",
|
|
10
|
+
openclaw: "gpt-4o-mini"
|
|
11
|
+
};
|
|
12
|
+
var XAI_BASE_URL = "https://api.x.ai/v1";
|
|
13
|
+
function resolveOpenClawHome() {
|
|
14
|
+
return process.env.OPENCLAW_HOME?.trim() || path.join(os.homedir(), ".openclaw");
|
|
15
|
+
}
|
|
16
|
+
function resolveOpenClawGatewayProvider() {
|
|
17
|
+
try {
|
|
18
|
+
const configPath = path.join(resolveOpenClawHome(), "openclaw.json");
|
|
19
|
+
if (!fs.existsSync(configPath)) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const raw = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
23
|
+
const enabled = raw.gateway?.http?.endpoints?.chatCompletions?.enabled === true;
|
|
24
|
+
const apiKey = raw.gateway?.auth?.token?.trim();
|
|
25
|
+
const port = raw.gateway?.port;
|
|
26
|
+
if (!enabled || !apiKey || typeof port !== "number" || !Number.isFinite(port) || port <= 0) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const defaultModel = raw.agents?.defaults?.model?.trim() || DEFAULT_MODELS.openclaw;
|
|
30
|
+
const host = raw.gateway?.bind === "loopback" ? "127.0.0.1" : "127.0.0.1";
|
|
31
|
+
return {
|
|
32
|
+
baseUrl: `http://${host}:${port}/v1`,
|
|
33
|
+
apiKey,
|
|
34
|
+
api: "openai-completions",
|
|
35
|
+
defaultModel
|
|
36
|
+
};
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function resolveOpenClawProvider() {
|
|
42
|
+
const gatewayProvider = resolveOpenClawGatewayProvider();
|
|
43
|
+
if (gatewayProvider) {
|
|
44
|
+
return gatewayProvider;
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
const modelsPath = path.join(resolveOpenClawHome(), "agents", "main", "agent", "models.json");
|
|
48
|
+
if (!fs.existsSync(modelsPath)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const raw = JSON.parse(fs.readFileSync(modelsPath, "utf-8"));
|
|
52
|
+
if (!raw.providers || typeof raw.providers !== "object") {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
for (const [, provider] of Object.entries(raw.providers)) {
|
|
56
|
+
if (provider.baseUrl && provider.apiKey) {
|
|
57
|
+
const defaultModel = provider.models?.[0]?.id ?? "gpt-4o-mini";
|
|
58
|
+
return {
|
|
59
|
+
baseUrl: provider.baseUrl.replace(/\/+$/, ""),
|
|
60
|
+
apiKey: provider.apiKey,
|
|
61
|
+
api: provider.api ?? "openai-completions",
|
|
62
|
+
defaultModel
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
function resolveLlmProvider() {
|
|
71
|
+
if (process.env.CLAWVAULT_NO_LLM) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
if (resolveOpenClawProvider()) {
|
|
75
|
+
return "openclaw";
|
|
76
|
+
}
|
|
77
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
78
|
+
return "anthropic";
|
|
79
|
+
}
|
|
80
|
+
if (process.env.OPENAI_API_KEY) {
|
|
81
|
+
return "openai";
|
|
82
|
+
}
|
|
83
|
+
if (process.env.GEMINI_API_KEY) {
|
|
84
|
+
return "gemini";
|
|
85
|
+
}
|
|
86
|
+
if (process.env.XAI_API_KEY) {
|
|
87
|
+
return "xai";
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
async function requestLlmCompletion(options) {
|
|
92
|
+
const provider = options.provider ?? resolveLlmProvider();
|
|
93
|
+
if (!provider) {
|
|
94
|
+
return "";
|
|
95
|
+
}
|
|
96
|
+
if (provider === "openclaw") {
|
|
97
|
+
return callOpenClaw(options);
|
|
98
|
+
}
|
|
99
|
+
if (provider === "anthropic") {
|
|
100
|
+
return callAnthropic(options, provider);
|
|
101
|
+
}
|
|
102
|
+
if (provider === "gemini") {
|
|
103
|
+
return callGemini(options, provider);
|
|
104
|
+
}
|
|
105
|
+
if (provider === "xai") {
|
|
106
|
+
return callXAI(options, provider);
|
|
107
|
+
}
|
|
108
|
+
return callOpenAI(options, provider);
|
|
109
|
+
}
|
|
110
|
+
async function callAnthropic(options, provider) {
|
|
111
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
112
|
+
if (!apiKey) {
|
|
113
|
+
return "";
|
|
114
|
+
}
|
|
115
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
116
|
+
const response = await fetchImpl("https://api.anthropic.com/v1/messages", {
|
|
117
|
+
method: "POST",
|
|
118
|
+
headers: {
|
|
119
|
+
"content-type": "application/json",
|
|
120
|
+
"x-api-key": apiKey,
|
|
121
|
+
"anthropic-version": "2023-06-01"
|
|
122
|
+
},
|
|
123
|
+
body: JSON.stringify({
|
|
124
|
+
model: options.model ?? DEFAULT_MODELS[provider],
|
|
125
|
+
temperature: options.temperature ?? 0.1,
|
|
126
|
+
max_tokens: options.maxTokens ?? 1200,
|
|
127
|
+
messages: [{ role: "user", content: options.prompt }]
|
|
128
|
+
})
|
|
129
|
+
});
|
|
130
|
+
if (!response.ok) {
|
|
131
|
+
throw new Error(`Anthropic request failed (${response.status})`);
|
|
132
|
+
}
|
|
133
|
+
const payload = await response.json();
|
|
134
|
+
return payload.content?.filter((entry) => entry.type === "text" && entry.text).map((entry) => entry.text).join("\n").trim() ?? "";
|
|
135
|
+
}
|
|
136
|
+
async function callOpenAI(options, provider) {
|
|
137
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
138
|
+
if (!apiKey) {
|
|
139
|
+
return "";
|
|
140
|
+
}
|
|
141
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
142
|
+
const messages = [];
|
|
143
|
+
if (options.systemPrompt?.trim()) {
|
|
144
|
+
messages.push({ role: "system", content: options.systemPrompt.trim() });
|
|
145
|
+
}
|
|
146
|
+
messages.push({ role: "user", content: options.prompt });
|
|
147
|
+
const response = await fetchImpl("https://api.openai.com/v1/chat/completions", {
|
|
148
|
+
method: "POST",
|
|
149
|
+
headers: {
|
|
150
|
+
"content-type": "application/json",
|
|
151
|
+
authorization: `Bearer ${apiKey}`
|
|
152
|
+
},
|
|
153
|
+
body: JSON.stringify({
|
|
154
|
+
model: options.model ?? DEFAULT_MODELS[provider],
|
|
155
|
+
temperature: options.temperature ?? 0.1,
|
|
156
|
+
max_tokens: options.maxTokens ?? 1200,
|
|
157
|
+
messages
|
|
158
|
+
})
|
|
159
|
+
});
|
|
160
|
+
if (!response.ok) {
|
|
161
|
+
throw new Error(`OpenAI request failed (${response.status})`);
|
|
162
|
+
}
|
|
163
|
+
const payload = await response.json();
|
|
164
|
+
return payload.choices?.[0]?.message?.content?.trim() ?? "";
|
|
165
|
+
}
|
|
166
|
+
async function callXAI(options, provider) {
|
|
167
|
+
const apiKey = process.env.XAI_API_KEY;
|
|
168
|
+
if (!apiKey) {
|
|
169
|
+
return "";
|
|
170
|
+
}
|
|
171
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
172
|
+
const messages = [];
|
|
173
|
+
if (options.systemPrompt?.trim()) {
|
|
174
|
+
messages.push({ role: "system", content: options.systemPrompt.trim() });
|
|
175
|
+
}
|
|
176
|
+
messages.push({ role: "user", content: options.prompt });
|
|
177
|
+
const response = await fetchImpl(`${XAI_BASE_URL}/chat/completions`, {
|
|
178
|
+
method: "POST",
|
|
179
|
+
headers: {
|
|
180
|
+
"content-type": "application/json",
|
|
181
|
+
authorization: `Bearer ${apiKey}`
|
|
182
|
+
},
|
|
183
|
+
body: JSON.stringify({
|
|
184
|
+
model: options.model ?? DEFAULT_MODELS[provider],
|
|
185
|
+
temperature: options.temperature ?? 0.1,
|
|
186
|
+
max_tokens: options.maxTokens ?? 1200,
|
|
187
|
+
messages
|
|
188
|
+
})
|
|
189
|
+
});
|
|
190
|
+
if (!response.ok) {
|
|
191
|
+
throw new Error(`xAI request failed (${response.status})`);
|
|
192
|
+
}
|
|
193
|
+
const payload = await response.json();
|
|
194
|
+
return payload.choices?.[0]?.message?.content?.trim() ?? "";
|
|
195
|
+
}
|
|
196
|
+
async function callGemini(options, provider) {
|
|
197
|
+
const apiKey = process.env.GEMINI_API_KEY;
|
|
198
|
+
if (!apiKey) {
|
|
199
|
+
return "";
|
|
200
|
+
}
|
|
201
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
202
|
+
const model = options.model ?? DEFAULT_MODELS[provider];
|
|
203
|
+
const response = await fetchImpl(
|
|
204
|
+
`https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent`,
|
|
205
|
+
{
|
|
206
|
+
method: "POST",
|
|
207
|
+
headers: { "content-type": "application/json", "x-goog-api-key": apiKey },
|
|
208
|
+
body: JSON.stringify({
|
|
209
|
+
contents: [{ parts: [{ text: options.prompt }] }],
|
|
210
|
+
generationConfig: {
|
|
211
|
+
temperature: options.temperature ?? 0.1,
|
|
212
|
+
maxOutputTokens: options.maxTokens ?? 1200
|
|
213
|
+
}
|
|
214
|
+
})
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
if (!response.ok) {
|
|
218
|
+
throw new Error(`Gemini request failed (${response.status})`);
|
|
219
|
+
}
|
|
220
|
+
const payload = await response.json();
|
|
221
|
+
return payload.candidates?.[0]?.content?.parts?.[0]?.text?.trim() ?? "";
|
|
222
|
+
}
|
|
223
|
+
async function callOpenClaw(options) {
|
|
224
|
+
const config = resolveOpenClawProvider();
|
|
225
|
+
if (!config) {
|
|
226
|
+
return "";
|
|
227
|
+
}
|
|
228
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
229
|
+
const messages = [];
|
|
230
|
+
if (options.systemPrompt?.trim()) {
|
|
231
|
+
messages.push({ role: "system", content: options.systemPrompt.trim() });
|
|
232
|
+
}
|
|
233
|
+
messages.push({ role: "user", content: options.prompt });
|
|
234
|
+
const response = await fetchImpl(`${config.baseUrl}/chat/completions`, {
|
|
235
|
+
method: "POST",
|
|
236
|
+
headers: {
|
|
237
|
+
"content-type": "application/json",
|
|
238
|
+
authorization: `Bearer ${config.apiKey}`
|
|
239
|
+
},
|
|
240
|
+
body: JSON.stringify({
|
|
241
|
+
model: options.model ?? config.defaultModel,
|
|
242
|
+
temperature: options.temperature ?? 0.1,
|
|
243
|
+
max_tokens: options.maxTokens ?? 1200,
|
|
244
|
+
messages
|
|
245
|
+
})
|
|
246
|
+
});
|
|
247
|
+
if (!response.ok) {
|
|
248
|
+
throw new Error(`OpenClaw provider request failed (${response.status})`);
|
|
249
|
+
}
|
|
250
|
+
const payload = await response.json();
|
|
251
|
+
return payload.choices?.[0]?.message?.content?.trim() ?? "";
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export {
|
|
255
|
+
resolveLlmProvider,
|
|
256
|
+
requestLlmCompletion
|
|
257
|
+
};
|
|
@@ -14,7 +14,7 @@ function findProtectedRanges(content) {
|
|
|
14
14
|
while ((match = codeBlockRegex.exec(content)) !== null) {
|
|
15
15
|
ranges.push({ start: match.index, end: match.index + match[0].length });
|
|
16
16
|
}
|
|
17
|
-
const inlineCodeRegex =
|
|
17
|
+
const inlineCodeRegex = /(?<!`)`[^`\n]+`(?!`)/g;
|
|
18
18
|
while ((match = inlineCodeRegex.exec(content)) !== null) {
|
|
19
19
|
ranges.push({ start: match.index, end: match.index + match[0].length });
|
|
20
20
|
}
|
|
@@ -26,10 +26,51 @@ function findProtectedRanges(content) {
|
|
|
26
26
|
while ((match = urlRegex.exec(content)) !== null) {
|
|
27
27
|
ranges.push({ start: match.index, end: match.index + match[0].length });
|
|
28
28
|
}
|
|
29
|
+
const filePathRegex = /(?:^|[\s([{"'])((?:~|\/)[^\s`<>\])}"']+|[A-Za-z]:\\[^\s`<>\])}"']+)/g;
|
|
30
|
+
while ((match = filePathRegex.exec(content)) !== null) {
|
|
31
|
+
const fullMatch = match[0];
|
|
32
|
+
const pathValue = match[1];
|
|
33
|
+
const start = match.index + fullMatch.indexOf(pathValue);
|
|
34
|
+
ranges.push({ start, end: start + pathValue.length });
|
|
35
|
+
}
|
|
29
36
|
return ranges;
|
|
30
37
|
}
|
|
31
|
-
function
|
|
32
|
-
return ranges.some((
|
|
38
|
+
function isProtectedRange(start, end, ranges) {
|
|
39
|
+
return ranges.some((range) => start < range.end && end > range.start);
|
|
40
|
+
}
|
|
41
|
+
function createAliasRegex(alias) {
|
|
42
|
+
const escapedAlias = alias.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
43
|
+
return new RegExp(`\\b${escapedAlias}\\b`, "gi");
|
|
44
|
+
}
|
|
45
|
+
function formatWikiLink(path, originalText) {
|
|
46
|
+
return originalText.toLowerCase() === path.split("/").pop()?.toLowerCase() ? `[[${path}]]` : `[[${path}|${originalText}]]`;
|
|
47
|
+
}
|
|
48
|
+
function planLinks(content, index, protectedRanges) {
|
|
49
|
+
const sortedAliases = getSortedAliases(index);
|
|
50
|
+
const linkedEntities = /* @__PURE__ */ new Set();
|
|
51
|
+
const claimedRanges = [];
|
|
52
|
+
const plannedLinks = [];
|
|
53
|
+
for (const { alias, path } of sortedAliases) {
|
|
54
|
+
if (linkedEntities.has(path)) continue;
|
|
55
|
+
const regex = createAliasRegex(alias);
|
|
56
|
+
let match;
|
|
57
|
+
while ((match = regex.exec(content)) !== null) {
|
|
58
|
+
const start = match.index;
|
|
59
|
+
const end = start + match[0].length;
|
|
60
|
+
if (isProtectedRange(start, end, protectedRanges)) continue;
|
|
61
|
+
if (isProtectedRange(start, end, claimedRanges)) continue;
|
|
62
|
+
plannedLinks.push({
|
|
63
|
+
start,
|
|
64
|
+
end,
|
|
65
|
+
originalText: match[0],
|
|
66
|
+
path
|
|
67
|
+
});
|
|
68
|
+
claimedRanges.push({ start, end });
|
|
69
|
+
linkedEntities.add(path);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return plannedLinks;
|
|
33
74
|
}
|
|
34
75
|
function createLineLookup(content) {
|
|
35
76
|
const lines = content.split("\n");
|
|
@@ -48,56 +89,24 @@ function createLineLookup(content) {
|
|
|
48
89
|
}
|
|
49
90
|
function autoLink(content, index) {
|
|
50
91
|
const protectedRanges = findProtectedRanges(content);
|
|
51
|
-
const
|
|
52
|
-
const linkedEntities = /* @__PURE__ */ new Set();
|
|
92
|
+
const plannedLinks = planLinks(content, index, protectedRanges);
|
|
53
93
|
let result = content;
|
|
54
|
-
|
|
55
|
-
for (const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const regex = new RegExp(`\\b${escapedAlias}\\b`, "gi");
|
|
59
|
-
let match;
|
|
60
|
-
while ((match = regex.exec(content)) !== null) {
|
|
61
|
-
const originalPos = match.index;
|
|
62
|
-
const adjustedPos = originalPos + offset;
|
|
63
|
-
if (isProtected(originalPos, protectedRanges)) continue;
|
|
64
|
-
const beforeMatch = result.substring(0, adjustedPos);
|
|
65
|
-
const openBrackets = (beforeMatch.match(/\[\[/g) || []).length;
|
|
66
|
-
const closeBrackets = (beforeMatch.match(/\]\]/g) || []).length;
|
|
67
|
-
if (openBrackets > closeBrackets) continue;
|
|
68
|
-
const originalText = match[0];
|
|
69
|
-
const replacement = originalText.toLowerCase() === path.split("/").pop()?.toLowerCase() ? `[[${path}]]` : `[[${path}|${originalText}]]`;
|
|
70
|
-
result = result.substring(0, adjustedPos) + replacement + result.substring(adjustedPos + originalText.length);
|
|
71
|
-
offset += replacement.length - originalText.length;
|
|
72
|
-
linkedEntities.add(path);
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
94
|
+
const sortedByPosition = plannedLinks.slice().sort((a, b) => b.start - a.start);
|
|
95
|
+
for (const planned of sortedByPosition) {
|
|
96
|
+
const replacement = formatWikiLink(planned.path, planned.originalText);
|
|
97
|
+
result = result.substring(0, planned.start) + replacement + result.substring(planned.end);
|
|
75
98
|
}
|
|
76
99
|
return result;
|
|
77
100
|
}
|
|
78
101
|
function dryRunLink(content, index) {
|
|
79
102
|
const protectedRanges = findProtectedRanges(content);
|
|
80
|
-
const
|
|
81
|
-
const linkedEntities = /* @__PURE__ */ new Set();
|
|
82
|
-
const matches = [];
|
|
103
|
+
const plannedLinks = planLinks(content, index, protectedRanges);
|
|
83
104
|
const getLineNumber = createLineLookup(content);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
while ((match = regex.exec(content)) !== null) {
|
|
90
|
-
if (isProtected(match.index, protectedRanges)) continue;
|
|
91
|
-
matches.push({
|
|
92
|
-
alias: match[0],
|
|
93
|
-
path,
|
|
94
|
-
line: getLineNumber(match.index)
|
|
95
|
-
});
|
|
96
|
-
linkedEntities.add(path);
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return matches;
|
|
105
|
+
return plannedLinks.map((planned) => ({
|
|
106
|
+
alias: planned.originalText,
|
|
107
|
+
path: planned.path,
|
|
108
|
+
line: getLineNumber(planned.start)
|
|
109
|
+
}));
|
|
101
110
|
}
|
|
102
111
|
function findUnlinkedMentions(content, index) {
|
|
103
112
|
const protectedRanges = findProtectedRanges(content);
|
|
@@ -111,7 +120,9 @@ function findUnlinkedMentions(content, index) {
|
|
|
111
120
|
const regex = new RegExp(`\\b${escapedAlias}\\b`, "gi");
|
|
112
121
|
let match;
|
|
113
122
|
while ((match = regex.exec(content)) !== null) {
|
|
114
|
-
|
|
123
|
+
const start = match.index;
|
|
124
|
+
const end = start + match[0].length;
|
|
125
|
+
if (isProtectedRange(start, end, protectedRanges)) continue;
|
|
115
126
|
matches.push({
|
|
116
127
|
alias: match[0],
|
|
117
128
|
path,
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import {
|
|
2
|
+
loadVaultQmdConfig,
|
|
3
|
+
recoverQmdEmbeddingIfNeeded,
|
|
4
|
+
runCrashSafeQmdEmbed
|
|
5
|
+
} from "./chunk-6FH3IULF.js";
|
|
6
|
+
import {
|
|
7
|
+
QmdUnavailableError,
|
|
8
|
+
hasQmd
|
|
9
|
+
} from "./chunk-EK6S23ZB.js";
|
|
10
|
+
import {
|
|
11
|
+
resolveVaultPath
|
|
12
|
+
} from "./chunk-GNJL4YGR.js";
|
|
13
|
+
|
|
14
|
+
// src/commands/embed.ts
|
|
15
|
+
async function embedCommand(options = {}) {
|
|
16
|
+
if (!hasQmd()) {
|
|
17
|
+
throw new QmdUnavailableError();
|
|
18
|
+
}
|
|
19
|
+
const vaultPath = resolveVaultPath({ explicitPath: options.vaultPath });
|
|
20
|
+
const qmdConfig = loadVaultQmdConfig(vaultPath);
|
|
21
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
22
|
+
const recovery = recoverQmdEmbeddingIfNeeded({
|
|
23
|
+
vaultPath,
|
|
24
|
+
collection: qmdConfig.qmdCollection,
|
|
25
|
+
rootPath: qmdConfig.qmdRoot,
|
|
26
|
+
mode: "marker-or-empty",
|
|
27
|
+
onLog: options.quiet ? void 0 : (message) => console.log(message)
|
|
28
|
+
});
|
|
29
|
+
if (!options.quiet && recovery.recovered) {
|
|
30
|
+
const reasonLabel = recovery.reason === "interrupted_wal" ? "interrupted run" : "empty vector state";
|
|
31
|
+
console.log(`\u2713 Automatic qmd recovery completed (${reasonLabel}).`);
|
|
32
|
+
}
|
|
33
|
+
if (!options.quiet) {
|
|
34
|
+
console.log(
|
|
35
|
+
`Embedding pending documents for collection "${qmdConfig.qmdCollection}" (root: ${qmdConfig.qmdRoot})...`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
runCrashSafeQmdEmbed({
|
|
39
|
+
vaultPath,
|
|
40
|
+
collection: qmdConfig.qmdCollection,
|
|
41
|
+
rootPath: qmdConfig.qmdRoot
|
|
42
|
+
});
|
|
43
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
44
|
+
if (!options.quiet) {
|
|
45
|
+
console.log(`\u2713 Embedding complete for "${qmdConfig.qmdCollection}"`);
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
vaultPath,
|
|
49
|
+
qmdCollection: qmdConfig.qmdCollection,
|
|
50
|
+
qmdRoot: qmdConfig.qmdRoot,
|
|
51
|
+
startedAt,
|
|
52
|
+
finishedAt
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function registerEmbedCommand(program) {
|
|
56
|
+
program.command("embed").description("Run qmd embedding for pending vault documents").option("-v, --vault <path>", "Vault path").action(async (rawOptions) => {
|
|
57
|
+
await embedCommand({
|
|
58
|
+
vaultPath: rawOptions.vault
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export {
|
|
64
|
+
embedCommand,
|
|
65
|
+
registerEmbedCommand
|
|
66
|
+
};
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveVaultPath
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-GNJL4YGR.js";
|
|
4
4
|
import {
|
|
5
5
|
buildOrUpdateMemoryGraphIndex,
|
|
6
6
|
loadMemoryGraphIndex
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-33DOSHTA.js";
|
|
8
8
|
|
|
9
9
|
// src/commands/graph.ts
|
|
10
10
|
function formatGraphSummary(summary) {
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import {
|
|
2
|
+
append
|
|
3
|
+
} from "./chunk-4BQTQMJP.js";
|
|
4
|
+
import {
|
|
5
|
+
getType,
|
|
6
|
+
loadRegistry
|
|
7
|
+
} from "./chunk-MM6QGW3P.js";
|
|
8
|
+
import {
|
|
9
|
+
__export
|
|
10
|
+
} from "./chunk-2ZDO52B4.js";
|
|
11
|
+
|
|
12
|
+
// src/workgraph/store.ts
|
|
13
|
+
var store_exports = {};
|
|
14
|
+
__export(store_exports, {
|
|
15
|
+
activeThreads: () => activeThreads,
|
|
16
|
+
blockedThreads: () => blockedThreads,
|
|
17
|
+
create: () => create,
|
|
18
|
+
findByField: () => findByField,
|
|
19
|
+
list: () => list,
|
|
20
|
+
openThreads: () => openThreads,
|
|
21
|
+
read: () => read,
|
|
22
|
+
remove: () => remove,
|
|
23
|
+
update: () => update
|
|
24
|
+
});
|
|
25
|
+
import fs from "fs";
|
|
26
|
+
import path from "path";
|
|
27
|
+
import matter from "gray-matter";
|
|
28
|
+
function create(vaultPath, typeName, fields, body, actor) {
|
|
29
|
+
const typeDef = getType(vaultPath, typeName);
|
|
30
|
+
if (!typeDef) {
|
|
31
|
+
throw new Error(`Unknown primitive type "${typeName}". Run \`clawvault primitive list\` to see available types, or \`clawvault primitive define\` to create one.`);
|
|
32
|
+
}
|
|
33
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
34
|
+
const merged = applyDefaults(typeDef, {
|
|
35
|
+
...fields,
|
|
36
|
+
created: fields.created ?? now,
|
|
37
|
+
updated: now
|
|
38
|
+
});
|
|
39
|
+
const slug = slugify(String(merged.title ?? merged.name ?? typeName));
|
|
40
|
+
const relDir = typeDef.directory;
|
|
41
|
+
const relPath = `${relDir}/${slug}.md`;
|
|
42
|
+
const absDir = path.join(vaultPath, relDir);
|
|
43
|
+
const absPath = path.join(vaultPath, relPath);
|
|
44
|
+
if (!fs.existsSync(absDir)) fs.mkdirSync(absDir, { recursive: true });
|
|
45
|
+
if (fs.existsSync(absPath)) {
|
|
46
|
+
throw new Error(`File already exists: ${relPath}. Use update instead.`);
|
|
47
|
+
}
|
|
48
|
+
const content = matter.stringify(body, stripUndefined(merged));
|
|
49
|
+
fs.writeFileSync(absPath, content, "utf-8");
|
|
50
|
+
append(vaultPath, actor, "create", relPath, typeName, {
|
|
51
|
+
title: merged.title ?? slug
|
|
52
|
+
});
|
|
53
|
+
return { path: relPath, type: typeName, fields: merged, body };
|
|
54
|
+
}
|
|
55
|
+
function read(vaultPath, relPath) {
|
|
56
|
+
const absPath = path.join(vaultPath, relPath);
|
|
57
|
+
if (!fs.existsSync(absPath)) return null;
|
|
58
|
+
const raw = fs.readFileSync(absPath, "utf-8");
|
|
59
|
+
const { data, content } = matter(raw);
|
|
60
|
+
const typeName = inferType(vaultPath, relPath);
|
|
61
|
+
return { path: relPath, type: typeName, fields: data, body: content.trim() };
|
|
62
|
+
}
|
|
63
|
+
function list(vaultPath, typeName) {
|
|
64
|
+
const typeDef = getType(vaultPath, typeName);
|
|
65
|
+
if (!typeDef) return [];
|
|
66
|
+
const dir = path.join(vaultPath, typeDef.directory);
|
|
67
|
+
if (!fs.existsSync(dir)) return [];
|
|
68
|
+
const files = fs.readdirSync(dir).filter((f) => f.endsWith(".md"));
|
|
69
|
+
const instances = [];
|
|
70
|
+
for (const file of files) {
|
|
71
|
+
const relPath = `${typeDef.directory}/${file}`;
|
|
72
|
+
const inst = read(vaultPath, relPath);
|
|
73
|
+
if (inst) instances.push(inst);
|
|
74
|
+
}
|
|
75
|
+
return instances;
|
|
76
|
+
}
|
|
77
|
+
function update(vaultPath, relPath, fieldUpdates, bodyUpdate, actor) {
|
|
78
|
+
const existing = read(vaultPath, relPath);
|
|
79
|
+
if (!existing) throw new Error(`Not found: ${relPath}`);
|
|
80
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
81
|
+
const newFields = { ...existing.fields, ...fieldUpdates, updated: now };
|
|
82
|
+
const newBody = bodyUpdate ?? existing.body;
|
|
83
|
+
const absPath = path.join(vaultPath, relPath);
|
|
84
|
+
const content = matter.stringify(newBody, stripUndefined(newFields));
|
|
85
|
+
fs.writeFileSync(absPath, content, "utf-8");
|
|
86
|
+
append(vaultPath, actor, "update", relPath, existing.type, {
|
|
87
|
+
changed: Object.keys(fieldUpdates)
|
|
88
|
+
});
|
|
89
|
+
return { path: relPath, type: existing.type, fields: newFields, body: newBody };
|
|
90
|
+
}
|
|
91
|
+
function remove(vaultPath, relPath, actor) {
|
|
92
|
+
const absPath = path.join(vaultPath, relPath);
|
|
93
|
+
if (!fs.existsSync(absPath)) throw new Error(`Not found: ${relPath}`);
|
|
94
|
+
const archiveDir = path.join(vaultPath, ".clawvault", "archive");
|
|
95
|
+
if (!fs.existsSync(archiveDir)) fs.mkdirSync(archiveDir, { recursive: true });
|
|
96
|
+
const archivePath = path.join(archiveDir, path.basename(relPath));
|
|
97
|
+
fs.renameSync(absPath, archivePath);
|
|
98
|
+
const typeName = inferType(vaultPath, relPath);
|
|
99
|
+
append(vaultPath, actor, "delete", relPath, typeName);
|
|
100
|
+
}
|
|
101
|
+
function findByField(vaultPath, typeName, field, value) {
|
|
102
|
+
return list(vaultPath, typeName).filter((inst) => inst.fields[field] === value);
|
|
103
|
+
}
|
|
104
|
+
function openThreads(vaultPath) {
|
|
105
|
+
return findByField(vaultPath, "thread", "status", "open");
|
|
106
|
+
}
|
|
107
|
+
function activeThreads(vaultPath) {
|
|
108
|
+
return findByField(vaultPath, "thread", "status", "active");
|
|
109
|
+
}
|
|
110
|
+
function blockedThreads(vaultPath) {
|
|
111
|
+
return findByField(vaultPath, "thread", "status", "blocked");
|
|
112
|
+
}
|
|
113
|
+
function slugify(text) {
|
|
114
|
+
return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 80);
|
|
115
|
+
}
|
|
116
|
+
function applyDefaults(typeDef, fields) {
|
|
117
|
+
const result = { ...fields };
|
|
118
|
+
for (const [key, def] of Object.entries(typeDef.fields)) {
|
|
119
|
+
if (result[key] === void 0 && def.default !== void 0) {
|
|
120
|
+
result[key] = def.default;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
function stripUndefined(obj) {
|
|
126
|
+
const result = {};
|
|
127
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
128
|
+
if (v !== void 0) result[k] = v;
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
function inferType(vaultPath, relPath) {
|
|
133
|
+
const registry = loadRegistry(vaultPath);
|
|
134
|
+
const dir = relPath.split("/")[0];
|
|
135
|
+
for (const typeDef of Object.values(registry.types)) {
|
|
136
|
+
if (typeDef.directory === dir) return typeDef.name;
|
|
137
|
+
}
|
|
138
|
+
return "unknown";
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export {
|
|
142
|
+
create,
|
|
143
|
+
read,
|
|
144
|
+
list,
|
|
145
|
+
update,
|
|
146
|
+
remove,
|
|
147
|
+
findByField,
|
|
148
|
+
openThreads,
|
|
149
|
+
activeThreads,
|
|
150
|
+
blockedThreads,
|
|
151
|
+
store_exports
|
|
152
|
+
};
|