clawvault 1.10.2 → 1.11.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 +0 -49
- package/bin/clawvault.js +6 -343
- package/dist/{chunk-GENGXU34.js → chunk-5VYRT2XZ.js} +1 -1
- package/dist/{chunk-CC7IJJ32.js → chunk-RPMQZZPE.js} +35 -1
- package/dist/commands/doctor.js +3 -3
- package/dist/commands/observe.js +2 -2
- package/dist/commands/sleep.d.ts +0 -2
- package/dist/commands/sleep.js +5 -9
- package/dist/commands/status.js +3 -3
- package/dist/commands/wake.js +4 -4
- package/dist/index.js +2 -2
- package/package.json +2 -2
- package/dist/chunk-BBPSJL6H.js +0 -375
- package/dist/commands/cloud.d.ts +0 -63
- package/dist/commands/cloud.js +0 -117
- package/dist/types-CilEQY9w.d.ts +0 -51
package/README.md
CHANGED
|
@@ -106,32 +106,6 @@ clawvault context "what decisions were made" --budget 2000
|
|
|
106
106
|
# → fits within token budget, 🔴 items first
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
## ClawVault Cloud
|
|
110
|
-
|
|
111
|
-
ClawVault Cloud extends local memory with org-linked decision traces. The local vault stays your source of truth, and cloud sync adds cross-agent visibility plus centralized audit trails.
|
|
112
|
-
|
|
113
|
-
- **Local-first writes** - `trace emit` always appends locally before sync attempts.
|
|
114
|
-
- **Queued and retryable sync** - traces are buffered and sent with `clawvault sync`.
|
|
115
|
-
- **Org-linked vault identity** - each vault can be linked once, then monitored with status checks.
|
|
116
|
-
- **Backwards-compatible sync command** - `clawvault sync <target>` still syncs vault files to a folder.
|
|
117
|
-
|
|
118
|
-
Quick setup:
|
|
119
|
-
|
|
120
|
-
```bash
|
|
121
|
-
# 1) Save your cloud API key
|
|
122
|
-
clawvault config --cloud-key cvk_xxx
|
|
123
|
-
|
|
124
|
-
# 2) Link this local vault to your org
|
|
125
|
-
clawvault org link --vault ~/memory
|
|
126
|
-
|
|
127
|
-
# 3) Verify cloud + org link state
|
|
128
|
-
clawvault org status
|
|
129
|
-
|
|
130
|
-
# 4) Emit trace events and sync queued traces
|
|
131
|
-
clawvault trace emit --summary "Approved 20% discount for ACME"
|
|
132
|
-
clawvault sync
|
|
133
|
-
```
|
|
134
|
-
|
|
135
109
|
## Search
|
|
136
110
|
|
|
137
111
|
Use `clawvault search` / `qmd` for vault search — it indexes the **entire vault** (decisions/, people/, lessons/, observations/, etc.).
|
|
@@ -217,29 +191,6 @@ clawvault recap --brief # Token-efficient recap
|
|
|
217
191
|
clawvault doctor
|
|
218
192
|
```
|
|
219
193
|
|
|
220
|
-
### Cloud Sync
|
|
221
|
-
|
|
222
|
-
```bash
|
|
223
|
-
# Configure cloud API access
|
|
224
|
-
clawvault config --cloud-key cvk_xxx
|
|
225
|
-
clawvault config --cloud-api-url https://api.clawvault.dev # optional override
|
|
226
|
-
|
|
227
|
-
# Link local vault to cloud org/vault ID
|
|
228
|
-
clawvault org link --vault ~/memory
|
|
229
|
-
clawvault org status
|
|
230
|
-
|
|
231
|
-
# Emit decision traces (summary-only or JSON payload)
|
|
232
|
-
clawvault trace emit --summary "Approved 20% discount for ACME"
|
|
233
|
-
clawvault trace emit --trace-file ./trace.json
|
|
234
|
-
|
|
235
|
-
# Sync queued cloud traces (no target argument)
|
|
236
|
-
clawvault sync
|
|
237
|
-
clawvault sync --all
|
|
238
|
-
clawvault sync --limit 25
|
|
239
|
-
|
|
240
|
-
# Existing file sync still works when target is provided
|
|
241
|
-
clawvault sync ./obsidian # existing file sync to target folder
|
|
242
|
-
```
|
|
243
194
|
|
|
244
195
|
## Agent Setup (AGENTS.md)
|
|
245
196
|
|
package/bin/clawvault.js
CHANGED
|
@@ -103,16 +103,6 @@ function printQmdMissing() {
|
|
|
103
103
|
console.log(chalk.dim(`Install: ${QMD_INSTALL_COMMAND}`));
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
function cloudSkipReasonText(reason) {
|
|
107
|
-
const map = {
|
|
108
|
-
'empty-queue': 'No pending traces to sync.',
|
|
109
|
-
'cloud-not-configured': 'Cloud not fully configured (need API key and linked vault).',
|
|
110
|
-
'sync-disabled': 'Cloud sync disabled for this trace emit.',
|
|
111
|
-
'sync-failed': 'Cloud sync failed; traces remain queued.'
|
|
112
|
-
};
|
|
113
|
-
return map[reason] || `Skipped: ${reason}`;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
106
|
function parseBooleanInput(value, defaultValue = true) {
|
|
117
107
|
const normalized = value.trim().toLowerCase();
|
|
118
108
|
if (!normalized) {
|
|
@@ -127,125 +117,6 @@ function parseBooleanInput(value, defaultValue = true) {
|
|
|
127
117
|
return null;
|
|
128
118
|
}
|
|
129
119
|
|
|
130
|
-
async function promptDecisionTrace(initialSummary) {
|
|
131
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
132
|
-
|
|
133
|
-
const ask = async (label, defaultValue = '') => {
|
|
134
|
-
const suffix = defaultValue ? ` [${defaultValue}]` : '';
|
|
135
|
-
const answer = await rl.question(`${label}${suffix}: `);
|
|
136
|
-
const trimmed = answer.trim();
|
|
137
|
-
return trimmed || defaultValue;
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
const askRequired = async (label, defaultValue = '') => {
|
|
141
|
-
while (true) {
|
|
142
|
-
const value = await ask(label, defaultValue);
|
|
143
|
-
if (value.trim()) {
|
|
144
|
-
return value.trim();
|
|
145
|
-
}
|
|
146
|
-
console.log(chalk.yellow(`${label} is required.`));
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const askBoolean = async (label, defaultValue = true) => {
|
|
151
|
-
while (true) {
|
|
152
|
-
const defaultText = defaultValue ? 'Y/n' : 'y/N';
|
|
153
|
-
const answer = await rl.question(`${label} (${defaultText}): `);
|
|
154
|
-
const parsed = parseBooleanInput(answer, defaultValue);
|
|
155
|
-
if (parsed !== null) {
|
|
156
|
-
return parsed;
|
|
157
|
-
}
|
|
158
|
-
console.log(chalk.yellow('Please answer yes or no.'));
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
const askOptionalObject = async (label) => {
|
|
163
|
-
while (true) {
|
|
164
|
-
const raw = await rl.question(`${label} (JSON object, leave blank for none): `);
|
|
165
|
-
const trimmed = raw.trim();
|
|
166
|
-
if (!trimmed) {
|
|
167
|
-
return undefined;
|
|
168
|
-
}
|
|
169
|
-
try {
|
|
170
|
-
const parsed = JSON.parse(trimmed);
|
|
171
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
172
|
-
console.log(chalk.yellow('Please enter a JSON object (e.g. {"key":"value"}).'));
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
return parsed;
|
|
176
|
-
} catch {
|
|
177
|
-
console.log(chalk.yellow('Invalid JSON. Try again.'));
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const collectInputs = async () => {
|
|
183
|
-
const inputs = [];
|
|
184
|
-
let index = 1;
|
|
185
|
-
while (await askBoolean(`Add input #${index}?`, index === 1)) {
|
|
186
|
-
const source = await askRequired(' input.source');
|
|
187
|
-
const type = await askRequired(' input.type');
|
|
188
|
-
const id = await askRequired(' input.id');
|
|
189
|
-
const data = await askOptionalObject(' input.data');
|
|
190
|
-
inputs.push(data ? { source, type, id, data } : { source, type, id });
|
|
191
|
-
index += 1;
|
|
192
|
-
}
|
|
193
|
-
return inputs;
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
const collectPolicies = async () => {
|
|
197
|
-
const policies = [];
|
|
198
|
-
let index = 1;
|
|
199
|
-
while (await askBoolean(`Add policy #${index}?`, index === 1)) {
|
|
200
|
-
const id = await askRequired(' policy.id');
|
|
201
|
-
const name = await askRequired(' policy.name');
|
|
202
|
-
const version = await askRequired(' policy.version', '1.0');
|
|
203
|
-
const rule = await askRequired(' policy.rule');
|
|
204
|
-
const result = await askRequired(' policy.result');
|
|
205
|
-
policies.push({ id, name, version, rule, result });
|
|
206
|
-
index += 1;
|
|
207
|
-
}
|
|
208
|
-
return policies;
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
const collectExceptions = async () => {
|
|
212
|
-
const exceptions = [];
|
|
213
|
-
let index = 1;
|
|
214
|
-
while (await askBoolean(`Add exception #${index}?`, false)) {
|
|
215
|
-
const policyId = await askRequired(' exception.policyId');
|
|
216
|
-
const reason = await askRequired(' exception.reason');
|
|
217
|
-
const approvedBy = await ask(' exception.approvedBy');
|
|
218
|
-
exceptions.push(approvedBy ? { policyId, reason, approvedBy } : { policyId, reason });
|
|
219
|
-
index += 1;
|
|
220
|
-
}
|
|
221
|
-
return exceptions;
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
try {
|
|
225
|
-
console.log(chalk.cyan('\nTrace emit interactive mode\n'));
|
|
226
|
-
const summary = await askRequired('summary', initialSummary);
|
|
227
|
-
const inputs = await collectInputs();
|
|
228
|
-
const policies = await collectPolicies();
|
|
229
|
-
const exceptions = await collectExceptions();
|
|
230
|
-
|
|
231
|
-
console.log(chalk.cyan('\nOutcome\n'));
|
|
232
|
-
const action = await askRequired(' outcome.action');
|
|
233
|
-
const target = await askRequired(' outcome.target');
|
|
234
|
-
const success = await askBoolean(' outcome.success', true);
|
|
235
|
-
const data = await askOptionalObject(' outcome.data');
|
|
236
|
-
|
|
237
|
-
return {
|
|
238
|
-
summary,
|
|
239
|
-
inputs,
|
|
240
|
-
policies,
|
|
241
|
-
exceptions,
|
|
242
|
-
outcome: data ? { action, target, success, data } : { action, target, success }
|
|
243
|
-
};
|
|
244
|
-
} finally {
|
|
245
|
-
rl.close();
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
120
|
program
|
|
250
121
|
.name('clawvault')
|
|
251
122
|
.description('🐘 An elephant never forgets. Structured memory for AI agents.')
|
|
@@ -314,164 +185,6 @@ program
|
|
|
314
185
|
}
|
|
315
186
|
});
|
|
316
187
|
|
|
317
|
-
// === CONFIG ===
|
|
318
|
-
program
|
|
319
|
-
.command('config')
|
|
320
|
-
.description('Manage ClawVault cloud configuration')
|
|
321
|
-
.option('--cloud-key <key>', 'Set cloud API key')
|
|
322
|
-
.option('--cloud-api-url <url>', 'Set cloud API base URL')
|
|
323
|
-
.option('--json', 'Output as JSON')
|
|
324
|
-
.action(async (options) => {
|
|
325
|
-
try {
|
|
326
|
-
const { cloudConfigCommand } = await import('../dist/commands/cloud.js');
|
|
327
|
-
const status = await cloudConfigCommand({
|
|
328
|
-
cloudKey: options.cloudKey,
|
|
329
|
-
cloudApiUrl: options.cloudApiUrl
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
if (options.json) {
|
|
333
|
-
console.log(JSON.stringify(status, null, 2));
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
console.log(chalk.cyan('\n☁️ Cloud Config\n'));
|
|
338
|
-
console.log(chalk.dim(`API key: ${status.cloudApiKeyMasked}`));
|
|
339
|
-
console.log(chalk.dim(`Vault ID: ${status.cloudVaultId || '(not linked)'}`));
|
|
340
|
-
console.log(chalk.dim(`Org slug: ${status.cloudOrgSlug || '(not set)'}`));
|
|
341
|
-
console.log(chalk.dim(`Queue depth: ${status.queueDepth}`));
|
|
342
|
-
console.log(chalk.dim(`Configured: ${status.configured ? 'yes' : 'no'}`));
|
|
343
|
-
console.log();
|
|
344
|
-
} catch (err) {
|
|
345
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
346
|
-
process.exit(1);
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
// === ORG ===
|
|
351
|
-
const org = program
|
|
352
|
-
.command('org')
|
|
353
|
-
.description('Manage cloud organization link');
|
|
354
|
-
|
|
355
|
-
org
|
|
356
|
-
.command('link')
|
|
357
|
-
.description('Link this vault to cloud org')
|
|
358
|
-
.option('-v, --vault <path>', 'Vault path')
|
|
359
|
-
.option('-a, --agent-id <id>', 'Agent ID (default: OPENCLAW_AGENT_ID or agent-local)')
|
|
360
|
-
.option('--org-slug <slug>', 'Org slug override')
|
|
361
|
-
.option('--json', 'Output as JSON')
|
|
362
|
-
.action(async (options) => {
|
|
363
|
-
try {
|
|
364
|
-
const vaultPath = resolveVaultPath(options.vault);
|
|
365
|
-
const { orgLinkCommand } = await import('../dist/commands/cloud.js');
|
|
366
|
-
const result = await orgLinkCommand({
|
|
367
|
-
vaultPath,
|
|
368
|
-
agentId: options.agentId,
|
|
369
|
-
orgSlug: options.orgSlug
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
if (options.json) {
|
|
373
|
-
console.log(JSON.stringify(result, null, 2));
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
console.log(chalk.green(`✓ Cloud vault linked: ${result.vaultId}`));
|
|
378
|
-
console.log(chalk.dim(` Vault: ${result.vaultName}`));
|
|
379
|
-
if (result.orgSlug) {
|
|
380
|
-
console.log(chalk.dim(` Org: ${result.orgSlug}`));
|
|
381
|
-
}
|
|
382
|
-
} catch (err) {
|
|
383
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
384
|
-
process.exit(1);
|
|
385
|
-
}
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
org
|
|
389
|
-
.command('status')
|
|
390
|
-
.description('Show cloud org link status')
|
|
391
|
-
.option('--json', 'Output as JSON')
|
|
392
|
-
.action(async (options) => {
|
|
393
|
-
try {
|
|
394
|
-
const { orgStatusCommand } = await import('../dist/commands/cloud.js');
|
|
395
|
-
const status = await orgStatusCommand();
|
|
396
|
-
|
|
397
|
-
if (options.json) {
|
|
398
|
-
console.log(JSON.stringify(status, null, 2));
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
console.log(chalk.cyan('\n☁️ Org Status\n'));
|
|
403
|
-
console.log(chalk.dim(`Configured: ${status.configured ? 'yes' : 'no'}`));
|
|
404
|
-
console.log(chalk.dim(`API key set: ${status.apiKeySet ? 'yes' : 'no'}`));
|
|
405
|
-
console.log(chalk.dim(`Vault linked: ${status.vaultIdSet ? 'yes' : 'no'}`));
|
|
406
|
-
console.log(chalk.dim(`Org slug: ${status.orgSlug || '(not set)'}`));
|
|
407
|
-
console.log(chalk.dim(`Queue depth: ${status.queueDepth}`));
|
|
408
|
-
if (status.cloudApiUrl) {
|
|
409
|
-
console.log(chalk.dim(`API URL: ${status.cloudApiUrl}`));
|
|
410
|
-
}
|
|
411
|
-
console.log();
|
|
412
|
-
} catch (err) {
|
|
413
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
414
|
-
process.exit(1);
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
// === TRACE ===
|
|
419
|
-
const trace = program
|
|
420
|
-
.command('trace')
|
|
421
|
-
.description('Manage decision traces');
|
|
422
|
-
|
|
423
|
-
trace
|
|
424
|
-
.command('emit')
|
|
425
|
-
.description('Emit decision trace (interactive, local + cloud queue)')
|
|
426
|
-
.option('--summary <text>', 'Decision summary')
|
|
427
|
-
.option('--trace-json <json>', 'Decision trace JSON payload')
|
|
428
|
-
.option('--trace-file <path>', 'Path to JSON trace payload')
|
|
429
|
-
.option('--stdin', 'Read JSON trace payload from stdin')
|
|
430
|
-
.option('--no-sync', 'Skip immediate cloud sync attempt')
|
|
431
|
-
.option('--json', 'Output as JSON')
|
|
432
|
-
.action(async (options) => {
|
|
433
|
-
try {
|
|
434
|
-
const { traceEmitCommand } = await import('../dist/commands/cloud.js');
|
|
435
|
-
const hasPayloadInput = Boolean(options.traceJson || options.traceFile || options.stdin);
|
|
436
|
-
|
|
437
|
-
let tracePayload;
|
|
438
|
-
if (!hasPayloadInput) {
|
|
439
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
440
|
-
throw new Error(
|
|
441
|
-
'Interactive trace emit requires a TTY. Use --trace-json, --trace-file, or --stdin for non-interactive mode.'
|
|
442
|
-
);
|
|
443
|
-
}
|
|
444
|
-
tracePayload = await promptDecisionTrace(options.summary);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const result = await traceEmitCommand({
|
|
448
|
-
summary: options.summary,
|
|
449
|
-
traceJson: options.traceJson,
|
|
450
|
-
traceFile: options.traceFile,
|
|
451
|
-
stdin: options.stdin,
|
|
452
|
-
sync: options.sync,
|
|
453
|
-
trace: tracePayload
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
if (options.json) {
|
|
457
|
-
console.log(JSON.stringify(result, null, 2));
|
|
458
|
-
return;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
console.log(chalk.green(`✓ Trace emitted: ${result.trace.localTraceId}`));
|
|
462
|
-
console.log(chalk.dim(` Summary: ${result.trace.summary}`));
|
|
463
|
-
console.log(chalk.dim(` Queue depth: ${result.queueDepth}`));
|
|
464
|
-
if (result.sync.skippedReason) {
|
|
465
|
-
console.log(chalk.yellow(` Cloud sync: ${cloudSkipReasonText(result.sync.skippedReason)}`));
|
|
466
|
-
} else {
|
|
467
|
-
console.log(chalk.dim(` Cloud sync: sent ${result.sync.synced}, remaining ${result.sync.remaining}`));
|
|
468
|
-
}
|
|
469
|
-
} catch (err) {
|
|
470
|
-
console.error(chalk.red(`Error: ${err.message}`));
|
|
471
|
-
process.exit(1);
|
|
472
|
-
}
|
|
473
|
-
});
|
|
474
|
-
|
|
475
188
|
// === STORE ===
|
|
476
189
|
program
|
|
477
190
|
.command('store')
|
|
@@ -539,16 +252,6 @@ program
|
|
|
539
252
|
const collection = vault.getQmdCollection();
|
|
540
253
|
await runQmd(collection ? ['update', '-c', collection] : ['update']);
|
|
541
254
|
}
|
|
542
|
-
|
|
543
|
-
const { autoSyncHandoffCommand } = await import('../dist/commands/cloud.js');
|
|
544
|
-
const cloudSync = await autoSyncHandoffCommand();
|
|
545
|
-
if (!options.json) {
|
|
546
|
-
if (cloudSync.skippedReason) {
|
|
547
|
-
console.log(chalk.dim(` Cloud sync: ${cloudSkipReasonText(cloudSync.skippedReason)}`));
|
|
548
|
-
} else {
|
|
549
|
-
console.log(chalk.dim(` Cloud sync: sent ${cloudSync.synced}, remaining ${cloudSync.remaining}`));
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
255
|
} catch (err) {
|
|
553
256
|
console.error(chalk.red(`Error: ${err.message}`));
|
|
554
257
|
process.exit(1);
|
|
@@ -882,33 +585,15 @@ program
|
|
|
882
585
|
}
|
|
883
586
|
});
|
|
884
587
|
|
|
885
|
-
// === SYNC ===
|
|
588
|
+
// === SYNC (vault file sync only) ===
|
|
886
589
|
program
|
|
887
|
-
.command('sync
|
|
888
|
-
.description('Sync
|
|
590
|
+
.command('sync <target>')
|
|
591
|
+
.description('Sync vault files to target path')
|
|
889
592
|
.option('--delete', 'Delete orphan files in target')
|
|
890
593
|
.option('--dry-run', "Show what would be synced without syncing")
|
|
891
|
-
.option('--all', 'For cloud sync, send all queued traces')
|
|
892
|
-
.option('--limit <n>', 'For cloud sync, max traces to send')
|
|
893
594
|
.option('-v, --vault <path>', 'Vault path')
|
|
894
595
|
.action(async (target, options) => {
|
|
895
596
|
try {
|
|
896
|
-
if (!target) {
|
|
897
|
-
const { cloudSyncCommand } = await import('../dist/commands/cloud.js');
|
|
898
|
-
const cloudResult = await cloudSyncCommand({
|
|
899
|
-
all: options.all,
|
|
900
|
-
limit: options.limit ? parseInt(options.limit, 10) : undefined
|
|
901
|
-
});
|
|
902
|
-
|
|
903
|
-
if (cloudResult.skippedReason) {
|
|
904
|
-
console.log(chalk.yellow(cloudSkipReasonText(cloudResult.skippedReason)));
|
|
905
|
-
} else {
|
|
906
|
-
console.log(chalk.green(`✓ Synced ${cloudResult.synced} trace(s) to cloud`));
|
|
907
|
-
console.log(chalk.dim(` Remaining queued: ${cloudResult.remaining}`));
|
|
908
|
-
}
|
|
909
|
-
return;
|
|
910
|
-
}
|
|
911
|
-
|
|
912
597
|
const vault = await getVault(options.vault);
|
|
913
598
|
|
|
914
599
|
console.log(chalk.cyan(`\n🔄 Syncing to ${target}...\n`));
|
|
@@ -1128,13 +813,6 @@ program
|
|
|
1128
813
|
console.log(chalk.dim(' Git: commit skipped'));
|
|
1129
814
|
}
|
|
1130
815
|
}
|
|
1131
|
-
if (result.cloudSync) {
|
|
1132
|
-
if (result.cloudSync.skippedReason) {
|
|
1133
|
-
console.log(chalk.dim(` Cloud sync: ${cloudSkipReasonText(result.cloudSync.skippedReason)}`));
|
|
1134
|
-
} else {
|
|
1135
|
-
console.log(chalk.dim(` Cloud sync: sent ${result.cloudSync.synced}, remaining ${result.cloudSync.remaining}`));
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
816
|
if (result.observationRoutingSummary) {
|
|
1139
817
|
console.log(chalk.dim(` Observe: ${result.observationRoutingSummary}`));
|
|
1140
818
|
}
|
|
@@ -1177,7 +855,6 @@ program
|
|
|
1177
855
|
};
|
|
1178
856
|
|
|
1179
857
|
const doc = await vault.createHandoff(handoff);
|
|
1180
|
-
let cloudSync;
|
|
1181
858
|
|
|
1182
859
|
if (!options.json) {
|
|
1183
860
|
console.log(chalk.green(`✓ Handoff created: ${doc.id}`));
|
|
@@ -1190,15 +867,8 @@ program
|
|
|
1190
867
|
await runQmd(collection ? ['update', '-c', collection] : ['update']);
|
|
1191
868
|
}
|
|
1192
869
|
|
|
1193
|
-
const { autoSyncHandoffCommand } = await import('../dist/commands/cloud.js');
|
|
1194
|
-
cloudSync = await autoSyncHandoffCommand();
|
|
1195
|
-
|
|
1196
870
|
if (options.json) {
|
|
1197
|
-
console.log(JSON.stringify({ id: doc.id, path: doc.path, handoff
|
|
1198
|
-
} else if (cloudSync.skippedReason) {
|
|
1199
|
-
console.log(chalk.dim(` Cloud sync: ${cloudSkipReasonText(cloudSync.skippedReason)}`));
|
|
1200
|
-
} else {
|
|
1201
|
-
console.log(chalk.dim(` Cloud sync: sent ${cloudSync.synced}, remaining ${cloudSync.remaining}`));
|
|
871
|
+
console.log(JSON.stringify({ id: doc.id, path: doc.path, handoff }, null, 2));
|
|
1202
872
|
}
|
|
1203
873
|
} catch (err) {
|
|
1204
874
|
console.error(chalk.red(`Error: ${err.message}`));
|
|
@@ -1442,11 +1112,9 @@ program
|
|
|
1442
1112
|
blocked: options.blocked,
|
|
1443
1113
|
urgent: options.urgent
|
|
1444
1114
|
});
|
|
1445
|
-
const { autoSyncCheckpointCommand } = await import('../dist/commands/cloud.js');
|
|
1446
|
-
const cloudSync = await autoSyncCheckpointCommand();
|
|
1447
1115
|
|
|
1448
1116
|
if (options.json) {
|
|
1449
|
-
console.log(JSON.stringify(
|
|
1117
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1450
1118
|
} else {
|
|
1451
1119
|
console.log(chalk.green('✓ Checkpoint saved'));
|
|
1452
1120
|
console.log(chalk.dim(` Timestamp: ${data.timestamp}`));
|
|
@@ -1454,11 +1122,6 @@ program
|
|
|
1454
1122
|
if (data.focus) console.log(chalk.dim(` Focus: ${data.focus}`));
|
|
1455
1123
|
if (data.blocked) console.log(chalk.dim(` Blocked: ${data.blocked}`));
|
|
1456
1124
|
if (data.urgent) console.log(chalk.dim(' Urgent: yes'));
|
|
1457
|
-
if (cloudSync.skippedReason) {
|
|
1458
|
-
console.log(chalk.dim(` Cloud sync: ${cloudSkipReasonText(cloudSync.skippedReason)}`));
|
|
1459
|
-
} else {
|
|
1460
|
-
console.log(chalk.dim(` Cloud sync: sent ${cloudSync.synced}, remaining ${cloudSync.remaining}`));
|
|
1461
|
-
}
|
|
1462
1125
|
}
|
|
1463
1126
|
} catch (err) {
|
|
1464
1127
|
console.error(chalk.red(`Error: ${err.message}`));
|
|
@@ -1621,4 +1284,4 @@ program
|
|
|
1621
1284
|
});
|
|
1622
1285
|
|
|
1623
1286
|
// Parse and run
|
|
1624
|
-
program.parse();
|
|
1287
|
+
program.parse();
|
|
@@ -534,12 +534,29 @@ var Router = class {
|
|
|
534
534
|
}
|
|
535
535
|
return null;
|
|
536
536
|
}
|
|
537
|
+
normalizeForDedup(content) {
|
|
538
|
+
return content.replace(/^\d{2}:\d{2}\s+/, "").replace(/\[\[[^\]]*\]\]/g, (m) => m.replace(/\[\[|\]\]/g, "")).replace(/\s+/g, " ").trim().toLowerCase();
|
|
539
|
+
}
|
|
537
540
|
appendToCategory(category, item) {
|
|
538
541
|
const categoryDir = path.join(this.vaultPath, category);
|
|
539
542
|
fs.mkdirSync(categoryDir, { recursive: true });
|
|
540
543
|
const filePath = path.join(categoryDir, `${item.date}.md`);
|
|
541
544
|
const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf-8").trim() : "";
|
|
542
|
-
|
|
545
|
+
const normalizedNew = this.normalizeForDedup(item.content);
|
|
546
|
+
const existingLines = existing.split(/\r?\n/);
|
|
547
|
+
for (const line of existingLines) {
|
|
548
|
+
const lineContent = line.replace(/^-\s*(?:🔴|🟡|🟢)\s*/, "");
|
|
549
|
+
if (this.normalizeForDedup(lineContent) === normalizedNew) return;
|
|
550
|
+
}
|
|
551
|
+
for (const line of existingLines) {
|
|
552
|
+
const lineContent = line.replace(/^-\s*(?:🔴|🟡|🟢)\s*/, "");
|
|
553
|
+
const normalizedExisting = this.normalizeForDedup(lineContent);
|
|
554
|
+
if (normalizedExisting.length > 10 && normalizedNew.length > 10) {
|
|
555
|
+
const shorter = normalizedNew.length < normalizedExisting.length ? normalizedNew : normalizedExisting;
|
|
556
|
+
const longer = normalizedNew.length >= normalizedExisting.length ? normalizedNew : normalizedExisting;
|
|
557
|
+
if (longer.includes(shorter) || this.similarity(normalizedNew, normalizedExisting) > 0.8) return;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
543
560
|
const linkedContent = this.addWikiLinks(item.content);
|
|
544
561
|
const entry = `- ${item.priority} ${linkedContent}`;
|
|
545
562
|
const header = existing ? "" : `# ${category} \u2014 ${item.date}
|
|
@@ -705,6 +722,23 @@ ${entry}
|
|
|
705
722
|
return match;
|
|
706
723
|
});
|
|
707
724
|
}
|
|
725
|
+
/**
|
|
726
|
+
* Jaccard similarity on word bigrams — cheap approximation.
|
|
727
|
+
*/
|
|
728
|
+
similarity(a, b) {
|
|
729
|
+
const bigrams = (s) => {
|
|
730
|
+
const words = s.split(" ");
|
|
731
|
+
const bg = /* @__PURE__ */ new Set();
|
|
732
|
+
for (let i = 0; i < words.length - 1; i++) bg.add(`${words[i]} ${words[i + 1]}`);
|
|
733
|
+
return bg;
|
|
734
|
+
};
|
|
735
|
+
const setA = bigrams(a);
|
|
736
|
+
const setB = bigrams(b);
|
|
737
|
+
if (setA.size === 0 || setB.size === 0) return 0;
|
|
738
|
+
let intersection = 0;
|
|
739
|
+
for (const bg of setA) if (setB.has(bg)) intersection++;
|
|
740
|
+
return intersection / (setA.size + setB.size - intersection);
|
|
741
|
+
}
|
|
708
742
|
buildSummary(routed) {
|
|
709
743
|
if (routed.length === 0) return "No items routed to vault categories.";
|
|
710
744
|
const byCat = /* @__PURE__ */ new Map();
|
package/dist/commands/doctor.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
formatAge
|
|
3
|
-
} from "../chunk-7ZRP733D.js";
|
|
4
1
|
import {
|
|
5
2
|
ClawVault,
|
|
6
3
|
findVault
|
|
@@ -12,6 +9,9 @@ import {
|
|
|
12
9
|
scanVaultLinks
|
|
13
10
|
} from "../chunk-4VQTUVH7.js";
|
|
14
11
|
import "../chunk-J7ZWCI2C.js";
|
|
12
|
+
import {
|
|
13
|
+
formatAge
|
|
14
|
+
} from "../chunk-7ZRP733D.js";
|
|
15
15
|
|
|
16
16
|
// src/commands/doctor.ts
|
|
17
17
|
import * as fs from "fs";
|
package/dist/commands/observe.js
CHANGED
package/dist/commands/sleep.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { H as HandoffDocument, D as Document } from '../types-DMU3SuAV.js';
|
|
2
|
-
import { C as CloudSyncResult } from '../types-CilEQY9w.js';
|
|
3
2
|
|
|
4
3
|
type PromptFn = (question: string) => Promise<string>;
|
|
5
4
|
interface SleepOptions {
|
|
@@ -28,7 +27,6 @@ interface SleepResult {
|
|
|
28
27
|
handoff: HandoffDocument;
|
|
29
28
|
document: Document;
|
|
30
29
|
git?: GitCommitResult;
|
|
31
|
-
cloudSync?: CloudSyncResult;
|
|
32
30
|
observationRoutingSummary?: string;
|
|
33
31
|
}
|
|
34
32
|
declare function sleep(options: SleepOptions): Promise<SleepResult>;
|
package/dist/commands/sleep.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
clearDirtyFlag
|
|
3
|
-
} from "../chunk-MZZJLQNQ.js";
|
|
4
|
-
import {
|
|
5
|
-
autoSyncOnHandoff
|
|
6
|
-
} from "../chunk-BBPSJL6H.js";
|
|
7
1
|
import {
|
|
8
2
|
ClawVault
|
|
9
3
|
} from "../chunk-3HFB7EMU.js";
|
|
@@ -13,7 +7,10 @@ import {
|
|
|
13
7
|
import {
|
|
14
8
|
Observer,
|
|
15
9
|
parseSessionFile
|
|
16
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-RPMQZZPE.js";
|
|
11
|
+
import {
|
|
12
|
+
clearDirtyFlag
|
|
13
|
+
} from "../chunk-MZZJLQNQ.js";
|
|
17
14
|
|
|
18
15
|
// src/commands/sleep.ts
|
|
19
16
|
import * as fs from "fs";
|
|
@@ -173,7 +170,6 @@ async function sleep(options) {
|
|
|
173
170
|
cwd: options.cwd ?? process.cwd(),
|
|
174
171
|
interactive
|
|
175
172
|
});
|
|
176
|
-
const cloudSync = await autoSyncOnHandoff();
|
|
177
173
|
let observationRoutingSummary;
|
|
178
174
|
try {
|
|
179
175
|
const transcriptPath = resolveSessionTranscriptPath(options.sessionTranscript);
|
|
@@ -186,7 +182,7 @@ async function sleep(options) {
|
|
|
186
182
|
}
|
|
187
183
|
} catch {
|
|
188
184
|
}
|
|
189
|
-
return { handoff, document, git,
|
|
185
|
+
return { handoff, document, git, observationRoutingSummary };
|
|
190
186
|
}
|
|
191
187
|
export {
|
|
192
188
|
sleep
|
package/dist/commands/status.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
formatAge
|
|
3
|
-
} from "../chunk-7ZRP733D.js";
|
|
4
1
|
import {
|
|
5
2
|
ClawVault
|
|
6
3
|
} from "../chunk-3HFB7EMU.js";
|
|
@@ -12,6 +9,9 @@ import {
|
|
|
12
9
|
scanVaultLinks
|
|
13
10
|
} from "../chunk-4VQTUVH7.js";
|
|
14
11
|
import "../chunk-J7ZWCI2C.js";
|
|
12
|
+
import {
|
|
13
|
+
formatAge
|
|
14
|
+
} from "../chunk-7ZRP733D.js";
|
|
15
15
|
|
|
16
16
|
// src/commands/status.ts
|
|
17
17
|
import * as fs from "fs";
|
package/dist/commands/wake.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ClawVault
|
|
3
|
+
} from "../chunk-3HFB7EMU.js";
|
|
4
|
+
import "../chunk-MIIXBNO3.js";
|
|
1
5
|
import {
|
|
2
6
|
recover
|
|
3
7
|
} from "../chunk-MILVYUPK.js";
|
|
@@ -5,10 +9,6 @@ import {
|
|
|
5
9
|
clearDirtyFlag
|
|
6
10
|
} from "../chunk-MZZJLQNQ.js";
|
|
7
11
|
import "../chunk-7ZRP733D.js";
|
|
8
|
-
import {
|
|
9
|
-
ClawVault
|
|
10
|
-
} from "../chunk-3HFB7EMU.js";
|
|
11
|
-
import "../chunk-MIIXBNO3.js";
|
|
12
12
|
|
|
13
13
|
// src/commands/wake.ts
|
|
14
14
|
import * as fs from "fs";
|
package/dist/index.js
CHANGED
|
@@ -41,13 +41,13 @@ import {
|
|
|
41
41
|
SessionWatcher,
|
|
42
42
|
observeCommand,
|
|
43
43
|
registerObserveCommand
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-5VYRT2XZ.js";
|
|
45
45
|
import {
|
|
46
46
|
Compressor,
|
|
47
47
|
Observer,
|
|
48
48
|
Reflector,
|
|
49
49
|
parseSessionFile
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-RPMQZZPE.js";
|
|
51
51
|
|
|
52
52
|
// src/index.ts
|
|
53
53
|
import * as fs from "fs";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clawvault",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "ClawVault™ - 🐘 An elephant never forgets. Structured memory for OpenClaw agents. Context death resilience, Obsidian-compatible markdown, local semantic search.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
]
|
|
30
30
|
},
|
|
31
31
|
"scripts": {
|
|
32
|
-
"build": "tsup src/index.ts src/commands/entities.ts src/commands/link.ts src/commands/checkpoint.ts src/commands/recover.ts src/commands/status.ts src/commands/template.ts src/commands/setup.ts src/commands/context.ts src/commands/observe.ts src/commands/session-recap.ts src/commands/wake.ts src/commands/sleep.ts src/commands/doctor.ts src/commands/shell-init.ts src/commands/repair-session.ts src/
|
|
32
|
+
"build": "tsup src/index.ts src/commands/entities.ts src/commands/link.ts src/commands/checkpoint.ts src/commands/recover.ts src/commands/status.ts src/commands/template.ts src/commands/setup.ts src/commands/context.ts src/commands/observe.ts src/commands/session-recap.ts src/commands/wake.ts src/commands/sleep.ts src/commands/doctor.ts src/commands/shell-init.ts src/commands/repair-session.ts src/lib/entity-index.ts src/lib/auto-linker.ts src/lib/config.ts src/lib/template-engine.ts src/lib/session-utils.ts src/lib/session-repair.ts --format esm --dts --clean",
|
|
33
33
|
"dev": "tsup src/index.ts src/commands/*.ts src/lib/*.ts --format esm --dts --watch",
|
|
34
34
|
"lint": "eslint src",
|
|
35
35
|
"typecheck": "tsc --noEmit",
|
package/dist/chunk-BBPSJL6H.js
DELETED
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
// src/cloud/config.ts
|
|
2
|
-
import * as fs from "fs";
|
|
3
|
-
|
|
4
|
-
// src/cloud/paths.ts
|
|
5
|
-
import * as os from "os";
|
|
6
|
-
import * as path from "path";
|
|
7
|
-
var DEFAULT_HOME_DIR = ".clawvault";
|
|
8
|
-
var CONFIG_FILE = "config.json";
|
|
9
|
-
var SYNC_QUEUE_FILE = "sync-queue.json";
|
|
10
|
-
var TRACE_LOG_FILE = "traces.ndjson";
|
|
11
|
-
function getClawVaultHomeDir() {
|
|
12
|
-
const override = process.env.CLAWVAULT_HOME?.trim();
|
|
13
|
-
if (override) {
|
|
14
|
-
return path.resolve(override);
|
|
15
|
-
}
|
|
16
|
-
return path.join(os.homedir(), DEFAULT_HOME_DIR);
|
|
17
|
-
}
|
|
18
|
-
function getCloudConfigPath() {
|
|
19
|
-
return path.join(getClawVaultHomeDir(), CONFIG_FILE);
|
|
20
|
-
}
|
|
21
|
-
function getSyncQueuePath() {
|
|
22
|
-
return path.join(getClawVaultHomeDir(), SYNC_QUEUE_FILE);
|
|
23
|
-
}
|
|
24
|
-
function getTraceLogPath() {
|
|
25
|
-
return path.join(getClawVaultHomeDir(), TRACE_LOG_FILE);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// src/cloud/config.ts
|
|
29
|
-
function ensureCloudDir() {
|
|
30
|
-
const dir = getClawVaultHomeDir();
|
|
31
|
-
if (!fs.existsSync(dir)) {
|
|
32
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
function readCloudConfig() {
|
|
36
|
-
const configPath = getCloudConfigPath();
|
|
37
|
-
if (!fs.existsSync(configPath)) {
|
|
38
|
-
return {};
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
const raw = fs.readFileSync(configPath, "utf-8");
|
|
42
|
-
const parsed = JSON.parse(raw);
|
|
43
|
-
return parsed ?? {};
|
|
44
|
-
} catch {
|
|
45
|
-
return {};
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function writeCloudConfig(config) {
|
|
49
|
-
ensureCloudDir();
|
|
50
|
-
const configPath = getCloudConfigPath();
|
|
51
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
52
|
-
return config;
|
|
53
|
-
}
|
|
54
|
-
function updateCloudConfig(patch) {
|
|
55
|
-
const current = readCloudConfig();
|
|
56
|
-
const next = { ...current, ...patch };
|
|
57
|
-
return writeCloudConfig(next);
|
|
58
|
-
}
|
|
59
|
-
function maskApiKey(apiKey) {
|
|
60
|
-
if (!apiKey) return "(not set)";
|
|
61
|
-
if (apiKey.length <= 8) return "***";
|
|
62
|
-
const start = apiKey.slice(0, 4);
|
|
63
|
-
const end = apiKey.slice(-4);
|
|
64
|
-
return `${start}${"*".repeat(Math.max(4, apiKey.length - 8))}${end}`;
|
|
65
|
-
}
|
|
66
|
-
function getConfiguredCloudApiUrl(config) {
|
|
67
|
-
const value = config?.cloudApiUrl || process.env.CLAWVAULT_CLOUD_API_URL;
|
|
68
|
-
if (value && value.trim()) {
|
|
69
|
-
return value.trim().replace(/\/+$/, "");
|
|
70
|
-
}
|
|
71
|
-
if (process.env.NODE_ENV === "development") {
|
|
72
|
-
return "http://localhost:4000";
|
|
73
|
-
}
|
|
74
|
-
return "https://api.clawvault.io";
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// src/cloud/service.ts
|
|
78
|
-
import { randomUUID } from "crypto";
|
|
79
|
-
|
|
80
|
-
// src/cloud/client.ts
|
|
81
|
-
var CloudApiError = class extends Error {
|
|
82
|
-
status;
|
|
83
|
-
responseBody;
|
|
84
|
-
};
|
|
85
|
-
function parseRegisterResponse(payload) {
|
|
86
|
-
const vaultId = payload?.vaultId ?? payload?.id ?? payload?.vault?.id;
|
|
87
|
-
const orgSlug = payload?.orgSlug ?? payload?.org?.slug;
|
|
88
|
-
if (!vaultId || typeof vaultId !== "string") {
|
|
89
|
-
throw new Error("Cloud register response missing vault ID.");
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
vaultId,
|
|
93
|
-
orgSlug: typeof orgSlug === "string" ? orgSlug : void 0,
|
|
94
|
-
raw: payload
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
async function requestJson(options, config) {
|
|
98
|
-
const baseUrl = getConfiguredCloudApiUrl(config);
|
|
99
|
-
const url = `${baseUrl}${options.path.startsWith("/") ? options.path : `/${options.path}`}`;
|
|
100
|
-
const timeoutMs = options.timeoutMs ?? 1e4;
|
|
101
|
-
const controller = new AbortController();
|
|
102
|
-
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
103
|
-
try {
|
|
104
|
-
const response = await fetch(url, {
|
|
105
|
-
method: options.method,
|
|
106
|
-
headers: {
|
|
107
|
-
"Content-Type": "application/json",
|
|
108
|
-
"X-API-Key": options.apiKey
|
|
109
|
-
},
|
|
110
|
-
body: options.body ? JSON.stringify(options.body) : void 0,
|
|
111
|
-
signal: controller.signal
|
|
112
|
-
});
|
|
113
|
-
if (!response.ok) {
|
|
114
|
-
const body = await response.text();
|
|
115
|
-
const err = new CloudApiError(
|
|
116
|
-
`Cloud API request failed (${response.status}) at ${options.path}`
|
|
117
|
-
);
|
|
118
|
-
err.status = response.status;
|
|
119
|
-
err.responseBody = body;
|
|
120
|
-
throw err;
|
|
121
|
-
}
|
|
122
|
-
const text = await response.text();
|
|
123
|
-
if (!text.trim()) {
|
|
124
|
-
return {};
|
|
125
|
-
}
|
|
126
|
-
return JSON.parse(text);
|
|
127
|
-
} catch (err) {
|
|
128
|
-
if (err?.name === "AbortError") {
|
|
129
|
-
throw new Error(`Cloud API request timed out after ${timeoutMs}ms.`);
|
|
130
|
-
}
|
|
131
|
-
throw err;
|
|
132
|
-
} finally {
|
|
133
|
-
clearTimeout(timeout);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
function createCloudClient(config) {
|
|
137
|
-
if (!config.cloudApiKey) {
|
|
138
|
-
throw new Error("Cloud API key not configured. Run `clawvault config --cloud-key <key>`.");
|
|
139
|
-
}
|
|
140
|
-
return {
|
|
141
|
-
async registerVault(input) {
|
|
142
|
-
const payload = await requestJson({
|
|
143
|
-
method: "POST",
|
|
144
|
-
path: "/vaults/register",
|
|
145
|
-
apiKey: config.cloudApiKey,
|
|
146
|
-
body: {
|
|
147
|
-
name: input.name,
|
|
148
|
-
agentId: input.agentId
|
|
149
|
-
}
|
|
150
|
-
}, config);
|
|
151
|
-
return parseRegisterResponse(payload);
|
|
152
|
-
},
|
|
153
|
-
async syncTraces(vaultId, traces) {
|
|
154
|
-
return requestJson({
|
|
155
|
-
method: "POST",
|
|
156
|
-
path: `/vaults/${encodeURIComponent(vaultId)}/sync`,
|
|
157
|
-
apiKey: config.cloudApiKey,
|
|
158
|
-
body: { traces }
|
|
159
|
-
}, config);
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// src/cloud/queue.ts
|
|
165
|
-
import * as fs2 from "fs";
|
|
166
|
-
var EMPTY_QUEUE = {
|
|
167
|
-
traces: [],
|
|
168
|
-
updatedAt: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
169
|
-
};
|
|
170
|
-
function ensureCloudDir2() {
|
|
171
|
-
const dir = getClawVaultHomeDir();
|
|
172
|
-
if (!fs2.existsSync(dir)) {
|
|
173
|
-
fs2.mkdirSync(dir, { recursive: true });
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
function readQueue() {
|
|
177
|
-
const queuePath = getSyncQueuePath();
|
|
178
|
-
if (!fs2.existsSync(queuePath)) {
|
|
179
|
-
return { ...EMPTY_QUEUE };
|
|
180
|
-
}
|
|
181
|
-
try {
|
|
182
|
-
const raw = fs2.readFileSync(queuePath, "utf-8");
|
|
183
|
-
const parsed = JSON.parse(raw);
|
|
184
|
-
if (!parsed || !Array.isArray(parsed.traces)) {
|
|
185
|
-
return { ...EMPTY_QUEUE };
|
|
186
|
-
}
|
|
187
|
-
return {
|
|
188
|
-
traces: parsed.traces,
|
|
189
|
-
updatedAt: parsed.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
190
|
-
};
|
|
191
|
-
} catch {
|
|
192
|
-
return { ...EMPTY_QUEUE };
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
function writeQueue(traces) {
|
|
196
|
-
ensureCloudDir2();
|
|
197
|
-
const next = {
|
|
198
|
-
traces,
|
|
199
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
200
|
-
};
|
|
201
|
-
fs2.writeFileSync(getSyncQueuePath(), JSON.stringify(next, null, 2));
|
|
202
|
-
return next;
|
|
203
|
-
}
|
|
204
|
-
function enqueueTrace(trace) {
|
|
205
|
-
const queue = readQueue();
|
|
206
|
-
queue.traces.push(trace);
|
|
207
|
-
return writeQueue(queue.traces);
|
|
208
|
-
}
|
|
209
|
-
function appendTraceLog(trace) {
|
|
210
|
-
ensureCloudDir2();
|
|
211
|
-
fs2.appendFileSync(getTraceLogPath(), `${JSON.stringify(trace)}
|
|
212
|
-
`);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// src/cloud/service.ts
|
|
216
|
-
function normalizeTrace(input) {
|
|
217
|
-
return {
|
|
218
|
-
localTraceId: input.localTraceId || randomUUID(),
|
|
219
|
-
timestamp: input.timestamp || (/* @__PURE__ */ new Date()).toISOString(),
|
|
220
|
-
summary: input.summary,
|
|
221
|
-
inputs: Array.isArray(input.inputs) ? input.inputs : [],
|
|
222
|
-
policies: Array.isArray(input.policies) ? input.policies : [],
|
|
223
|
-
exceptions: Array.isArray(input.exceptions) ? input.exceptions : [],
|
|
224
|
-
outcome: input.outcome || {
|
|
225
|
-
action: "unspecified",
|
|
226
|
-
target: "unspecified",
|
|
227
|
-
success: true,
|
|
228
|
-
data: {}
|
|
229
|
-
},
|
|
230
|
-
entityRefs: Array.isArray(input.entityRefs) ? input.entityRefs : []
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
function getSyncWindow(traces, options) {
|
|
234
|
-
if (options.all) {
|
|
235
|
-
return traces;
|
|
236
|
-
}
|
|
237
|
-
const limit = Math.max(1, options.limit ?? 10);
|
|
238
|
-
return traces.slice(0, limit);
|
|
239
|
-
}
|
|
240
|
-
function setCloudApiKey(cloudKey) {
|
|
241
|
-
const clean = cloudKey.trim();
|
|
242
|
-
if (!clean) {
|
|
243
|
-
throw new Error("Cloud API key cannot be empty.");
|
|
244
|
-
}
|
|
245
|
-
updateCloudConfig({ cloudApiKey: clean });
|
|
246
|
-
return getCloudStatus();
|
|
247
|
-
}
|
|
248
|
-
function getCloudStatus() {
|
|
249
|
-
const config = readCloudConfig();
|
|
250
|
-
const queue = readQueue();
|
|
251
|
-
const configured = Boolean(config.cloudApiKey && config.cloudVaultId);
|
|
252
|
-
return {
|
|
253
|
-
configured,
|
|
254
|
-
cloudApiKeyMasked: maskApiKey(config.cloudApiKey),
|
|
255
|
-
cloudVaultId: config.cloudVaultId,
|
|
256
|
-
cloudOrgSlug: config.cloudOrgSlug,
|
|
257
|
-
cloudApiUrl: getConfiguredCloudApiUrl(config),
|
|
258
|
-
queueDepth: queue.traces.length
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
async function linkVaultToOrg(options) {
|
|
262
|
-
if (!options.vaultName.trim()) {
|
|
263
|
-
throw new Error("Vault name is required for org link.");
|
|
264
|
-
}
|
|
265
|
-
if (!options.agentId.trim()) {
|
|
266
|
-
throw new Error("Agent ID is required for org link.");
|
|
267
|
-
}
|
|
268
|
-
const config = readCloudConfig();
|
|
269
|
-
const client = createCloudClient(config);
|
|
270
|
-
const result = await client.registerVault({
|
|
271
|
-
name: options.vaultName.trim(),
|
|
272
|
-
agentId: options.agentId.trim()
|
|
273
|
-
});
|
|
274
|
-
updateCloudConfig({
|
|
275
|
-
cloudVaultId: result.vaultId,
|
|
276
|
-
cloudOrgSlug: options.orgSlug || result.orgSlug || config.cloudOrgSlug
|
|
277
|
-
});
|
|
278
|
-
return result;
|
|
279
|
-
}
|
|
280
|
-
async function syncQueuedTraces(options = {}) {
|
|
281
|
-
const queue = readQueue();
|
|
282
|
-
const config = readCloudConfig();
|
|
283
|
-
const attemptedWindow = getSyncWindow(queue.traces, options);
|
|
284
|
-
if (attemptedWindow.length === 0) {
|
|
285
|
-
return {
|
|
286
|
-
attempted: 0,
|
|
287
|
-
synced: 0,
|
|
288
|
-
remaining: 0,
|
|
289
|
-
skippedReason: "empty-queue"
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
if (!config.cloudApiKey || !config.cloudVaultId) {
|
|
293
|
-
return {
|
|
294
|
-
attempted: attemptedWindow.length,
|
|
295
|
-
synced: 0,
|
|
296
|
-
remaining: queue.traces.length,
|
|
297
|
-
skippedReason: "cloud-not-configured"
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
const client = createCloudClient(config);
|
|
301
|
-
await client.syncTraces(config.cloudVaultId, attemptedWindow);
|
|
302
|
-
const remaining = queue.traces.slice(attemptedWindow.length);
|
|
303
|
-
writeQueue(remaining);
|
|
304
|
-
return {
|
|
305
|
-
attempted: attemptedWindow.length,
|
|
306
|
-
synced: attemptedWindow.length,
|
|
307
|
-
remaining: remaining.length
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
async function emitTrace(input, syncNow = true) {
|
|
311
|
-
const trace = normalizeTrace(input);
|
|
312
|
-
appendTraceLog(trace);
|
|
313
|
-
const queue = enqueueTrace(trace);
|
|
314
|
-
let syncResult = {
|
|
315
|
-
attempted: 0,
|
|
316
|
-
synced: 0,
|
|
317
|
-
remaining: queue.traces.length,
|
|
318
|
-
skippedReason: "sync-disabled"
|
|
319
|
-
};
|
|
320
|
-
if (syncNow) {
|
|
321
|
-
try {
|
|
322
|
-
syncResult = await syncQueuedTraces({ all: false, limit: 25 });
|
|
323
|
-
} catch {
|
|
324
|
-
syncResult = {
|
|
325
|
-
attempted: 0,
|
|
326
|
-
synced: 0,
|
|
327
|
-
remaining: readQueue().traces.length,
|
|
328
|
-
skippedReason: "sync-failed"
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
return {
|
|
333
|
-
trace,
|
|
334
|
-
queueDepth: readQueue().traces.length,
|
|
335
|
-
sync: syncResult
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
async function autoSyncOnCheckpoint() {
|
|
339
|
-
try {
|
|
340
|
-
return await syncQueuedTraces({ all: false, limit: 10 });
|
|
341
|
-
} catch {
|
|
342
|
-
const remaining = readQueue().traces.length;
|
|
343
|
-
return {
|
|
344
|
-
attempted: 0,
|
|
345
|
-
synced: 0,
|
|
346
|
-
remaining,
|
|
347
|
-
skippedReason: "sync-failed"
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
async function autoSyncOnHandoff() {
|
|
352
|
-
try {
|
|
353
|
-
return await syncQueuedTraces({ all: true });
|
|
354
|
-
} catch {
|
|
355
|
-
const remaining = readQueue().traces.length;
|
|
356
|
-
return {
|
|
357
|
-
attempted: 0,
|
|
358
|
-
synced: 0,
|
|
359
|
-
remaining,
|
|
360
|
-
skippedReason: "sync-failed"
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
export {
|
|
366
|
-
readCloudConfig,
|
|
367
|
-
updateCloudConfig,
|
|
368
|
-
setCloudApiKey,
|
|
369
|
-
getCloudStatus,
|
|
370
|
-
linkVaultToOrg,
|
|
371
|
-
syncQueuedTraces,
|
|
372
|
-
emitTrace,
|
|
373
|
-
autoSyncOnCheckpoint,
|
|
374
|
-
autoSyncOnHandoff
|
|
375
|
-
};
|
package/dist/commands/cloud.d.ts
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { C as CloudSyncResult, D as DecisionTraceInput, a as DecisionTrace } from '../types-CilEQY9w.js';
|
|
2
|
-
|
|
3
|
-
interface CloudStatus {
|
|
4
|
-
configured: boolean;
|
|
5
|
-
cloudApiKeyMasked: string;
|
|
6
|
-
cloudVaultId?: string;
|
|
7
|
-
cloudOrgSlug?: string;
|
|
8
|
-
cloudApiUrl?: string;
|
|
9
|
-
queueDepth: number;
|
|
10
|
-
}
|
|
11
|
-
interface EmitTraceResult {
|
|
12
|
-
trace: DecisionTrace;
|
|
13
|
-
queueDepth: number;
|
|
14
|
-
sync: CloudSyncResult;
|
|
15
|
-
}
|
|
16
|
-
declare function getCloudStatus(): CloudStatus;
|
|
17
|
-
declare function syncQueuedTraces(options?: {
|
|
18
|
-
all?: boolean;
|
|
19
|
-
limit?: number;
|
|
20
|
-
}): Promise<CloudSyncResult>;
|
|
21
|
-
declare function emitTrace(input: DecisionTraceInput, syncNow?: boolean): Promise<EmitTraceResult>;
|
|
22
|
-
declare function autoSyncOnCheckpoint(): Promise<CloudSyncResult>;
|
|
23
|
-
declare function autoSyncOnHandoff(): Promise<CloudSyncResult>;
|
|
24
|
-
|
|
25
|
-
interface OrgLinkOptions {
|
|
26
|
-
vaultPath: string;
|
|
27
|
-
agentId?: string;
|
|
28
|
-
orgSlug?: string;
|
|
29
|
-
}
|
|
30
|
-
interface TraceEmitOptions {
|
|
31
|
-
summary?: string;
|
|
32
|
-
traceFile?: string;
|
|
33
|
-
traceJson?: string;
|
|
34
|
-
stdin?: boolean;
|
|
35
|
-
sync?: boolean;
|
|
36
|
-
trace?: DecisionTraceInput;
|
|
37
|
-
}
|
|
38
|
-
declare function cloudConfigCommand(options: {
|
|
39
|
-
cloudKey?: string;
|
|
40
|
-
cloudApiUrl?: string;
|
|
41
|
-
}): Promise<ReturnType<typeof getCloudStatus>>;
|
|
42
|
-
declare function orgLinkCommand(options: OrgLinkOptions): Promise<{
|
|
43
|
-
vaultName: string;
|
|
44
|
-
vaultId: string;
|
|
45
|
-
orgSlug?: string;
|
|
46
|
-
}>;
|
|
47
|
-
declare function orgStatusCommand(): Promise<{
|
|
48
|
-
configured: boolean;
|
|
49
|
-
apiKeySet: boolean;
|
|
50
|
-
vaultIdSet: boolean;
|
|
51
|
-
orgSlug?: string;
|
|
52
|
-
queueDepth: number;
|
|
53
|
-
cloudApiUrl?: string;
|
|
54
|
-
}>;
|
|
55
|
-
declare function cloudSyncCommand(options?: {
|
|
56
|
-
all?: boolean;
|
|
57
|
-
limit?: number;
|
|
58
|
-
}): Promise<Awaited<ReturnType<typeof syncQueuedTraces>>>;
|
|
59
|
-
declare function traceEmitCommand(options: TraceEmitOptions): Promise<Awaited<ReturnType<typeof emitTrace>>>;
|
|
60
|
-
declare function autoSyncCheckpointCommand(): Promise<Awaited<ReturnType<typeof autoSyncOnCheckpoint>>>;
|
|
61
|
-
declare function autoSyncHandoffCommand(): Promise<Awaited<ReturnType<typeof autoSyncOnHandoff>>>;
|
|
62
|
-
|
|
63
|
-
export { autoSyncCheckpointCommand, autoSyncHandoffCommand, cloudConfigCommand, cloudSyncCommand, orgLinkCommand, orgStatusCommand, traceEmitCommand };
|
package/dist/commands/cloud.js
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
autoSyncOnCheckpoint,
|
|
3
|
-
autoSyncOnHandoff,
|
|
4
|
-
emitTrace,
|
|
5
|
-
getCloudStatus,
|
|
6
|
-
linkVaultToOrg,
|
|
7
|
-
readCloudConfig,
|
|
8
|
-
setCloudApiKey,
|
|
9
|
-
syncQueuedTraces,
|
|
10
|
-
updateCloudConfig
|
|
11
|
-
} from "../chunk-BBPSJL6H.js";
|
|
12
|
-
|
|
13
|
-
// src/commands/cloud.ts
|
|
14
|
-
import * as fs from "fs";
|
|
15
|
-
import * as path from "path";
|
|
16
|
-
function resolveVaultName(vaultPath) {
|
|
17
|
-
const resolved = path.resolve(vaultPath);
|
|
18
|
-
const configPath = path.join(resolved, ".clawvault.json");
|
|
19
|
-
if (!fs.existsSync(configPath)) {
|
|
20
|
-
return path.basename(resolved);
|
|
21
|
-
}
|
|
22
|
-
try {
|
|
23
|
-
const parsed = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
24
|
-
return parsed.name || path.basename(resolved);
|
|
25
|
-
} catch {
|
|
26
|
-
return path.basename(resolved);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function parseTracePayload(options) {
|
|
30
|
-
let payload = null;
|
|
31
|
-
if (options.trace) {
|
|
32
|
-
payload = options.trace;
|
|
33
|
-
} else if (options.traceJson) {
|
|
34
|
-
payload = JSON.parse(options.traceJson);
|
|
35
|
-
} else if (options.traceFile) {
|
|
36
|
-
payload = JSON.parse(fs.readFileSync(options.traceFile, "utf-8"));
|
|
37
|
-
} else if (options.stdin) {
|
|
38
|
-
const raw = fs.readFileSync(0, "utf-8");
|
|
39
|
-
payload = JSON.parse(raw);
|
|
40
|
-
}
|
|
41
|
-
if (!payload) {
|
|
42
|
-
if (!options.summary) {
|
|
43
|
-
throw new Error("Trace summary is required (use --summary, --trace-json, --trace-file, or --stdin).");
|
|
44
|
-
}
|
|
45
|
-
return { summary: options.summary };
|
|
46
|
-
}
|
|
47
|
-
if (!payload.summary && options.summary) {
|
|
48
|
-
payload.summary = options.summary;
|
|
49
|
-
}
|
|
50
|
-
if (!payload.summary) {
|
|
51
|
-
throw new Error("Trace payload must include summary.");
|
|
52
|
-
}
|
|
53
|
-
return payload;
|
|
54
|
-
}
|
|
55
|
-
async function cloudConfigCommand(options) {
|
|
56
|
-
if (!options.cloudKey && !options.cloudApiUrl) {
|
|
57
|
-
return getCloudStatus();
|
|
58
|
-
}
|
|
59
|
-
if (options.cloudKey) {
|
|
60
|
-
setCloudApiKey(options.cloudKey);
|
|
61
|
-
}
|
|
62
|
-
if (options.cloudApiUrl) {
|
|
63
|
-
updateCloudConfig({ cloudApiUrl: options.cloudApiUrl.trim() });
|
|
64
|
-
}
|
|
65
|
-
return getCloudStatus();
|
|
66
|
-
}
|
|
67
|
-
async function orgLinkCommand(options) {
|
|
68
|
-
const vaultName = resolveVaultName(options.vaultPath);
|
|
69
|
-
const agentId = options.agentId || process.env.OPENCLAW_AGENT_ID || "agent-local";
|
|
70
|
-
const result = await linkVaultToOrg({
|
|
71
|
-
vaultName,
|
|
72
|
-
agentId,
|
|
73
|
-
orgSlug: options.orgSlug
|
|
74
|
-
});
|
|
75
|
-
return {
|
|
76
|
-
vaultName,
|
|
77
|
-
vaultId: result.vaultId,
|
|
78
|
-
orgSlug: options.orgSlug || result.orgSlug
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
async function orgStatusCommand() {
|
|
82
|
-
const status = getCloudStatus();
|
|
83
|
-
const config = readCloudConfig();
|
|
84
|
-
return {
|
|
85
|
-
configured: status.configured,
|
|
86
|
-
apiKeySet: Boolean(config.cloudApiKey),
|
|
87
|
-
vaultIdSet: Boolean(config.cloudVaultId),
|
|
88
|
-
orgSlug: config.cloudOrgSlug,
|
|
89
|
-
queueDepth: status.queueDepth,
|
|
90
|
-
cloudApiUrl: status.cloudApiUrl
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
async function cloudSyncCommand(options = {}) {
|
|
94
|
-
return syncQueuedTraces({
|
|
95
|
-
all: options.all,
|
|
96
|
-
limit: options.limit
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
async function traceEmitCommand(options) {
|
|
100
|
-
const payload = parseTracePayload(options);
|
|
101
|
-
return emitTrace(payload, options.sync !== false);
|
|
102
|
-
}
|
|
103
|
-
async function autoSyncCheckpointCommand() {
|
|
104
|
-
return autoSyncOnCheckpoint();
|
|
105
|
-
}
|
|
106
|
-
async function autoSyncHandoffCommand() {
|
|
107
|
-
return autoSyncOnHandoff();
|
|
108
|
-
}
|
|
109
|
-
export {
|
|
110
|
-
autoSyncCheckpointCommand,
|
|
111
|
-
autoSyncHandoffCommand,
|
|
112
|
-
cloudConfigCommand,
|
|
113
|
-
cloudSyncCommand,
|
|
114
|
-
orgLinkCommand,
|
|
115
|
-
orgStatusCommand,
|
|
116
|
-
traceEmitCommand
|
|
117
|
-
};
|
package/dist/types-CilEQY9w.d.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
interface DecisionTraceInputSource {
|
|
2
|
-
source: string;
|
|
3
|
-
type: string;
|
|
4
|
-
id: string;
|
|
5
|
-
data?: Record<string, unknown>;
|
|
6
|
-
}
|
|
7
|
-
interface DecisionTracePolicy {
|
|
8
|
-
id: string;
|
|
9
|
-
name: string;
|
|
10
|
-
version: string;
|
|
11
|
-
rule: string;
|
|
12
|
-
result: string;
|
|
13
|
-
}
|
|
14
|
-
interface DecisionTraceException {
|
|
15
|
-
policyId: string;
|
|
16
|
-
reason: string;
|
|
17
|
-
approvedBy?: string;
|
|
18
|
-
}
|
|
19
|
-
interface DecisionTraceOutcome {
|
|
20
|
-
action: string;
|
|
21
|
-
target: string;
|
|
22
|
-
data?: Record<string, unknown>;
|
|
23
|
-
success: boolean;
|
|
24
|
-
}
|
|
25
|
-
interface DecisionTraceEntityRef {
|
|
26
|
-
type: string;
|
|
27
|
-
id: string;
|
|
28
|
-
name?: string;
|
|
29
|
-
role?: string;
|
|
30
|
-
}
|
|
31
|
-
interface DecisionTrace {
|
|
32
|
-
localTraceId: string;
|
|
33
|
-
timestamp: string;
|
|
34
|
-
summary: string;
|
|
35
|
-
inputs: DecisionTraceInputSource[];
|
|
36
|
-
policies: DecisionTracePolicy[];
|
|
37
|
-
exceptions: DecisionTraceException[];
|
|
38
|
-
outcome: DecisionTraceOutcome;
|
|
39
|
-
entityRefs: DecisionTraceEntityRef[];
|
|
40
|
-
}
|
|
41
|
-
interface DecisionTraceInput extends Partial<DecisionTrace> {
|
|
42
|
-
summary: string;
|
|
43
|
-
}
|
|
44
|
-
interface CloudSyncResult {
|
|
45
|
-
attempted: number;
|
|
46
|
-
synced: number;
|
|
47
|
-
remaining: number;
|
|
48
|
-
skippedReason?: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export type { CloudSyncResult as C, DecisionTraceInput as D, DecisionTrace as a };
|