chainlesschain 0.37.8 → 0.37.10
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 +403 -8
- package/bin/chainlesschain.js +4 -0
- package/package.json +7 -2
- package/src/commands/agent.js +30 -0
- package/src/commands/ask.js +114 -0
- package/src/commands/audit.js +286 -0
- package/src/commands/auth.js +387 -0
- package/src/commands/browse.js +184 -0
- package/src/commands/chat.js +35 -0
- package/src/commands/db.js +152 -0
- package/src/commands/did.js +376 -0
- package/src/commands/encrypt.js +233 -0
- package/src/commands/export.js +125 -0
- package/src/commands/git.js +215 -0
- package/src/commands/import.js +259 -0
- package/src/commands/instinct.js +202 -0
- package/src/commands/llm.js +288 -0
- package/src/commands/mcp.js +302 -0
- package/src/commands/memory.js +282 -0
- package/src/commands/note.js +489 -0
- package/src/commands/org.js +505 -0
- package/src/commands/p2p.js +274 -0
- package/src/commands/plugin.js +398 -0
- package/src/commands/search.js +237 -0
- package/src/commands/session.js +238 -0
- package/src/commands/skill.js +479 -0
- package/src/commands/sync.js +249 -0
- package/src/commands/tokens.js +214 -0
- package/src/commands/wallet.js +416 -0
- package/src/index.js +65 -0
- package/src/lib/audit-logger.js +364 -0
- package/src/lib/bm25-search.js +322 -0
- package/src/lib/browser-automation.js +216 -0
- package/src/lib/crypto-manager.js +246 -0
- package/src/lib/did-manager.js +270 -0
- package/src/lib/ensure-utf8.js +59 -0
- package/src/lib/git-integration.js +220 -0
- package/src/lib/instinct-manager.js +190 -0
- package/src/lib/knowledge-exporter.js +302 -0
- package/src/lib/knowledge-importer.js +293 -0
- package/src/lib/llm-providers.js +325 -0
- package/src/lib/mcp-client.js +413 -0
- package/src/lib/memory-manager.js +211 -0
- package/src/lib/note-versioning.js +244 -0
- package/src/lib/org-manager.js +424 -0
- package/src/lib/p2p-manager.js +317 -0
- package/src/lib/pdf-parser.js +96 -0
- package/src/lib/permission-engine.js +374 -0
- package/src/lib/plan-mode.js +333 -0
- package/src/lib/platform.js +15 -0
- package/src/lib/plugin-manager.js +312 -0
- package/src/lib/response-cache.js +156 -0
- package/src/lib/session-manager.js +189 -0
- package/src/lib/sync-manager.js +347 -0
- package/src/lib/token-tracker.js +200 -0
- package/src/lib/wallet-manager.js +348 -0
- package/src/repl/agent-repl.js +912 -0
- package/src/repl/chat-repl.js +262 -0
- package/src/runtime/bootstrap.js +159 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DID management commands
|
|
3
|
+
* chainlesschain did create|show|list|resolve|sign|verify|export|delete|set-default
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { logger } from "../lib/logger.js";
|
|
8
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
9
|
+
import {
|
|
10
|
+
createIdentity,
|
|
11
|
+
getIdentity,
|
|
12
|
+
getAllIdentities,
|
|
13
|
+
setDefaultIdentity,
|
|
14
|
+
deleteIdentity,
|
|
15
|
+
signMessage,
|
|
16
|
+
verifyWithDID,
|
|
17
|
+
exportIdentity,
|
|
18
|
+
resolveDID,
|
|
19
|
+
} from "../lib/did-manager.js";
|
|
20
|
+
|
|
21
|
+
export function registerDidCommand(program) {
|
|
22
|
+
const did = program
|
|
23
|
+
.command("did")
|
|
24
|
+
.description("Decentralized Identity (DID) management");
|
|
25
|
+
|
|
26
|
+
// did create
|
|
27
|
+
did
|
|
28
|
+
.command("create")
|
|
29
|
+
.description("Create a new DID identity")
|
|
30
|
+
.option("--name <name>", "Display name for the identity")
|
|
31
|
+
.option("--json", "Output as JSON")
|
|
32
|
+
.action(async (options) => {
|
|
33
|
+
try {
|
|
34
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
35
|
+
if (!ctx.db) {
|
|
36
|
+
logger.error("Database not available");
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
const db = ctx.db.getDatabase();
|
|
40
|
+
const identity = createIdentity(db, options.name);
|
|
41
|
+
|
|
42
|
+
if (options.json) {
|
|
43
|
+
console.log(JSON.stringify(identity, null, 2));
|
|
44
|
+
} else {
|
|
45
|
+
logger.success("DID identity created");
|
|
46
|
+
logger.log(` ${chalk.bold("DID:")} ${chalk.cyan(identity.did)}`);
|
|
47
|
+
if (identity.displayName) {
|
|
48
|
+
logger.log(` ${chalk.bold("Name:")} ${identity.displayName}`);
|
|
49
|
+
}
|
|
50
|
+
logger.log(
|
|
51
|
+
` ${chalk.bold("Default:")} ${identity.isDefault ? chalk.green("yes") : "no"}`,
|
|
52
|
+
);
|
|
53
|
+
logger.log(
|
|
54
|
+
` ${chalk.bold("PubKey:")} ${chalk.gray(identity.publicKey.slice(0, 40))}...`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
await shutdown();
|
|
59
|
+
} catch (err) {
|
|
60
|
+
logger.error(`Failed: ${err.message}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// did show
|
|
66
|
+
did
|
|
67
|
+
.command("show")
|
|
68
|
+
.description("Show a specific DID identity")
|
|
69
|
+
.argument("<did>", "DID or prefix")
|
|
70
|
+
.option("--json", "Output as JSON")
|
|
71
|
+
.action(async (didArg, options) => {
|
|
72
|
+
try {
|
|
73
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
74
|
+
if (!ctx.db) {
|
|
75
|
+
logger.error("Database not available");
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
const db = ctx.db.getDatabase();
|
|
79
|
+
const identity = getIdentity(db, didArg);
|
|
80
|
+
|
|
81
|
+
if (!identity) {
|
|
82
|
+
logger.error(`Identity not found: ${didArg}`);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (options.json) {
|
|
87
|
+
console.log(
|
|
88
|
+
JSON.stringify(
|
|
89
|
+
{
|
|
90
|
+
did: identity.did,
|
|
91
|
+
displayName: identity.display_name,
|
|
92
|
+
publicKey: identity.public_key,
|
|
93
|
+
isDefault: identity.is_default === 1,
|
|
94
|
+
document: JSON.parse(identity.did_document || "{}"),
|
|
95
|
+
createdAt: identity.created_at,
|
|
96
|
+
},
|
|
97
|
+
null,
|
|
98
|
+
2,
|
|
99
|
+
),
|
|
100
|
+
);
|
|
101
|
+
} else {
|
|
102
|
+
logger.log(chalk.bold("DID Identity:\n"));
|
|
103
|
+
logger.log(
|
|
104
|
+
` ${chalk.bold("DID:")} ${chalk.cyan(identity.did)}`,
|
|
105
|
+
);
|
|
106
|
+
logger.log(
|
|
107
|
+
` ${chalk.bold("Name:")} ${identity.display_name || chalk.gray("(none)")}`,
|
|
108
|
+
);
|
|
109
|
+
logger.log(
|
|
110
|
+
` ${chalk.bold("Default:")} ${identity.is_default ? chalk.green("yes") : "no"}`,
|
|
111
|
+
);
|
|
112
|
+
logger.log(
|
|
113
|
+
` ${chalk.bold("PubKey:")} ${chalk.gray(identity.public_key)}`,
|
|
114
|
+
);
|
|
115
|
+
logger.log(` ${chalk.bold("Created:")} ${identity.created_at}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
await shutdown();
|
|
119
|
+
} catch (err) {
|
|
120
|
+
logger.error(`Failed: ${err.message}`);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// did list
|
|
126
|
+
did
|
|
127
|
+
.command("list", { isDefault: true })
|
|
128
|
+
.description("List all DID identities")
|
|
129
|
+
.option("--json", "Output as JSON")
|
|
130
|
+
.action(async (options) => {
|
|
131
|
+
try {
|
|
132
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
133
|
+
if (!ctx.db) {
|
|
134
|
+
logger.error("Database not available");
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
const db = ctx.db.getDatabase();
|
|
138
|
+
const identities = getAllIdentities(db);
|
|
139
|
+
|
|
140
|
+
if (options.json) {
|
|
141
|
+
console.log(
|
|
142
|
+
JSON.stringify(
|
|
143
|
+
identities.map((i) => ({
|
|
144
|
+
did: i.did,
|
|
145
|
+
displayName: i.display_name,
|
|
146
|
+
isDefault: i.is_default === 1,
|
|
147
|
+
createdAt: i.created_at,
|
|
148
|
+
})),
|
|
149
|
+
null,
|
|
150
|
+
2,
|
|
151
|
+
),
|
|
152
|
+
);
|
|
153
|
+
} else if (identities.length === 0) {
|
|
154
|
+
logger.info(
|
|
155
|
+
'No DID identities. Create one with "chainlesschain did create"',
|
|
156
|
+
);
|
|
157
|
+
} else {
|
|
158
|
+
logger.log(chalk.bold(`DID Identities (${identities.length}):\n`));
|
|
159
|
+
for (const id of identities) {
|
|
160
|
+
const def = id.is_default ? chalk.green(" [default]") : "";
|
|
161
|
+
const name = id.display_name ? ` (${id.display_name})` : "";
|
|
162
|
+
logger.log(` ${chalk.cyan(id.did)}${name}${def}`);
|
|
163
|
+
logger.log(` ${chalk.gray(`created: ${id.created_at}`)}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
await shutdown();
|
|
168
|
+
} catch (err) {
|
|
169
|
+
logger.error(`Failed: ${err.message}`);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// did resolve
|
|
175
|
+
did
|
|
176
|
+
.command("resolve")
|
|
177
|
+
.description("Resolve a DID to its DID Document")
|
|
178
|
+
.argument("<did>", "DID to resolve")
|
|
179
|
+
.action(async (didArg) => {
|
|
180
|
+
try {
|
|
181
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
182
|
+
if (!ctx.db) {
|
|
183
|
+
logger.error("Database not available");
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
const db = ctx.db.getDatabase();
|
|
187
|
+
const doc = resolveDID(db, didArg);
|
|
188
|
+
|
|
189
|
+
if (!doc) {
|
|
190
|
+
logger.error(`DID not found: ${didArg}`);
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
console.log(JSON.stringify(doc, null, 2));
|
|
195
|
+
await shutdown();
|
|
196
|
+
} catch (err) {
|
|
197
|
+
logger.error(`Failed: ${err.message}`);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// did sign
|
|
203
|
+
did
|
|
204
|
+
.command("sign")
|
|
205
|
+
.description("Sign a message with a DID identity")
|
|
206
|
+
.argument("<message>", "Message to sign")
|
|
207
|
+
.option("--did <did>", "DID to sign with (default: default identity)")
|
|
208
|
+
.option("--json", "Output as JSON")
|
|
209
|
+
.action(async (message, options) => {
|
|
210
|
+
try {
|
|
211
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
212
|
+
if (!ctx.db) {
|
|
213
|
+
logger.error("Database not available");
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
const db = ctx.db.getDatabase();
|
|
217
|
+
|
|
218
|
+
const didStr =
|
|
219
|
+
options.did ||
|
|
220
|
+
(() => {
|
|
221
|
+
const def = getIdentity(db, "did:chainless:");
|
|
222
|
+
if (!def) throw new Error("No identities found. Create one first.");
|
|
223
|
+
return def.did;
|
|
224
|
+
})();
|
|
225
|
+
|
|
226
|
+
const signature = signMessage(db, didStr, message);
|
|
227
|
+
|
|
228
|
+
if (options.json) {
|
|
229
|
+
console.log(
|
|
230
|
+
JSON.stringify({ did: didStr, message, signature }, null, 2),
|
|
231
|
+
);
|
|
232
|
+
} else {
|
|
233
|
+
logger.success("Message signed");
|
|
234
|
+
logger.log(` ${chalk.bold("Signature:")} ${chalk.gray(signature)}`);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
await shutdown();
|
|
238
|
+
} catch (err) {
|
|
239
|
+
logger.error(`Failed: ${err.message}`);
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// did verify
|
|
245
|
+
did
|
|
246
|
+
.command("verify")
|
|
247
|
+
.description("Verify a signed message")
|
|
248
|
+
.argument("<message>", "Original message")
|
|
249
|
+
.argument("<signature>", "Signature to verify (hex)")
|
|
250
|
+
.option("--did <did>", "DID that signed the message")
|
|
251
|
+
.action(async (message, signature, options) => {
|
|
252
|
+
try {
|
|
253
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
254
|
+
if (!ctx.db) {
|
|
255
|
+
logger.error("Database not available");
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
const db = ctx.db.getDatabase();
|
|
259
|
+
|
|
260
|
+
if (!options.did) {
|
|
261
|
+
logger.error("--did is required for verification");
|
|
262
|
+
process.exit(1);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const valid = verifyWithDID(db, options.did, message, signature);
|
|
266
|
+
if (valid) {
|
|
267
|
+
logger.success("Signature is VALID");
|
|
268
|
+
} else {
|
|
269
|
+
logger.error("Signature is INVALID");
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
await shutdown();
|
|
274
|
+
} catch (err) {
|
|
275
|
+
logger.error(`Failed: ${err.message}`);
|
|
276
|
+
process.exit(1);
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// did export
|
|
281
|
+
did
|
|
282
|
+
.command("export")
|
|
283
|
+
.description("Export DID identity (public data only)")
|
|
284
|
+
.argument("<did>", "DID to export")
|
|
285
|
+
.action(async (didArg) => {
|
|
286
|
+
try {
|
|
287
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
288
|
+
if (!ctx.db) {
|
|
289
|
+
logger.error("Database not available");
|
|
290
|
+
process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
const db = ctx.db.getDatabase();
|
|
293
|
+
const exported = exportIdentity(db, didArg);
|
|
294
|
+
|
|
295
|
+
if (!exported) {
|
|
296
|
+
logger.error(`Identity not found: ${didArg}`);
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
console.log(JSON.stringify(exported, null, 2));
|
|
301
|
+
await shutdown();
|
|
302
|
+
} catch (err) {
|
|
303
|
+
logger.error(`Failed: ${err.message}`);
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// did set-default
|
|
309
|
+
did
|
|
310
|
+
.command("set-default")
|
|
311
|
+
.description("Set a DID as the default identity")
|
|
312
|
+
.argument("<did>", "DID to set as default")
|
|
313
|
+
.action(async (didArg) => {
|
|
314
|
+
try {
|
|
315
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
316
|
+
if (!ctx.db) {
|
|
317
|
+
logger.error("Database not available");
|
|
318
|
+
process.exit(1);
|
|
319
|
+
}
|
|
320
|
+
const db = ctx.db.getDatabase();
|
|
321
|
+
const ok = setDefaultIdentity(db, didArg);
|
|
322
|
+
|
|
323
|
+
if (ok) {
|
|
324
|
+
logger.success("Default identity updated");
|
|
325
|
+
} else {
|
|
326
|
+
logger.error(`Identity not found: ${didArg}`);
|
|
327
|
+
process.exit(1);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
await shutdown();
|
|
331
|
+
} catch (err) {
|
|
332
|
+
logger.error(`Failed: ${err.message}`);
|
|
333
|
+
process.exit(1);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// did delete
|
|
338
|
+
did
|
|
339
|
+
.command("delete")
|
|
340
|
+
.description("Delete a DID identity")
|
|
341
|
+
.argument("<did>", "DID to delete")
|
|
342
|
+
.option("--force", "Skip confirmation")
|
|
343
|
+
.action(async (didArg, options) => {
|
|
344
|
+
try {
|
|
345
|
+
if (!options.force) {
|
|
346
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
347
|
+
const ok = await confirm({
|
|
348
|
+
message: "Delete this DID identity? The secret key will be lost.",
|
|
349
|
+
});
|
|
350
|
+
if (!ok) {
|
|
351
|
+
logger.info("Cancelled");
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
357
|
+
if (!ctx.db) {
|
|
358
|
+
logger.error("Database not available");
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
const db = ctx.db.getDatabase();
|
|
362
|
+
const ok = deleteIdentity(db, didArg);
|
|
363
|
+
|
|
364
|
+
if (ok) {
|
|
365
|
+
logger.success("Identity deleted");
|
|
366
|
+
} else {
|
|
367
|
+
logger.error(`Identity not found: ${didArg}`);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
await shutdown();
|
|
371
|
+
} catch (err) {
|
|
372
|
+
logger.error(`Failed: ${err.message}`);
|
|
373
|
+
process.exit(1);
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encryption commands
|
|
3
|
+
* chainlesschain encrypt <file> | decrypt <file> | db encrypt | db decrypt | info <file>
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from "chalk";
|
|
7
|
+
import { logger } from "../lib/logger.js";
|
|
8
|
+
import { bootstrap, shutdown } from "../runtime/bootstrap.js";
|
|
9
|
+
import {
|
|
10
|
+
encryptFile,
|
|
11
|
+
decryptFile,
|
|
12
|
+
isEncryptedFile,
|
|
13
|
+
getEncryptedFileInfo,
|
|
14
|
+
getDbEncryptionStatus,
|
|
15
|
+
setDbEncryptionStatus,
|
|
16
|
+
hashPassword,
|
|
17
|
+
} from "../lib/crypto-manager.js";
|
|
18
|
+
|
|
19
|
+
async function promptPassword(message = "Password:") {
|
|
20
|
+
const { password } = await import("@inquirer/prompts");
|
|
21
|
+
return password({ message });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function registerEncryptCommand(program) {
|
|
25
|
+
const enc = program
|
|
26
|
+
.command("encrypt")
|
|
27
|
+
.description("File encryption and database encryption management");
|
|
28
|
+
|
|
29
|
+
// encrypt file
|
|
30
|
+
enc
|
|
31
|
+
.command("file")
|
|
32
|
+
.description("Encrypt a file with AES-256-GCM")
|
|
33
|
+
.argument("<path>", "File to encrypt")
|
|
34
|
+
.option("-o, --output <path>", "Output file path (default: <file>.enc)")
|
|
35
|
+
.option("-p, --password <pass>", "Password (prompted if not given)")
|
|
36
|
+
.action(async (filePath, options) => {
|
|
37
|
+
try {
|
|
38
|
+
const pass =
|
|
39
|
+
options.password || (await promptPassword("Encryption password:"));
|
|
40
|
+
if (!pass) {
|
|
41
|
+
logger.error("Password is required");
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Confirm password
|
|
46
|
+
if (!options.password) {
|
|
47
|
+
const confirm = await promptPassword("Confirm password:");
|
|
48
|
+
if (pass !== confirm) {
|
|
49
|
+
logger.error("Passwords do not match");
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const result = encryptFile(filePath, pass, options.output);
|
|
55
|
+
logger.success("File encrypted");
|
|
56
|
+
logger.log(
|
|
57
|
+
` ${chalk.bold("Input:")} ${result.inputPath} (${result.originalSize} bytes)`,
|
|
58
|
+
);
|
|
59
|
+
logger.log(
|
|
60
|
+
` ${chalk.bold("Output:")} ${result.outputPath} (${result.encryptedSize} bytes)`,
|
|
61
|
+
);
|
|
62
|
+
} catch (err) {
|
|
63
|
+
logger.error(`Failed: ${err.message}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// decrypt file
|
|
69
|
+
const dec = program
|
|
70
|
+
.command("decrypt")
|
|
71
|
+
.description("Decrypt an encrypted file");
|
|
72
|
+
|
|
73
|
+
dec
|
|
74
|
+
.command("file")
|
|
75
|
+
.description("Decrypt an AES-256-GCM encrypted file")
|
|
76
|
+
.argument("<path>", "File to decrypt (.enc)")
|
|
77
|
+
.option("-o, --output <path>", "Output file path")
|
|
78
|
+
.option("-p, --password <pass>", "Password (prompted if not given)")
|
|
79
|
+
.action(async (filePath, options) => {
|
|
80
|
+
try {
|
|
81
|
+
const pass =
|
|
82
|
+
options.password || (await promptPassword("Decryption password:"));
|
|
83
|
+
if (!pass) {
|
|
84
|
+
logger.error("Password is required");
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const result = decryptFile(filePath, pass, options.output);
|
|
89
|
+
logger.success("File decrypted");
|
|
90
|
+
logger.log(
|
|
91
|
+
` ${chalk.bold("Input:")} ${result.inputPath} (${result.encryptedSize} bytes)`,
|
|
92
|
+
);
|
|
93
|
+
logger.log(
|
|
94
|
+
` ${chalk.bold("Output:")} ${result.outputPath} (${result.decryptedSize} bytes)`,
|
|
95
|
+
);
|
|
96
|
+
} catch (err) {
|
|
97
|
+
logger.error(`Failed: ${err.message}`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// encrypt db
|
|
103
|
+
enc
|
|
104
|
+
.command("db")
|
|
105
|
+
.description("Enable database encryption tracking")
|
|
106
|
+
.option("-p, --password <pass>", "Password (prompted if not given)")
|
|
107
|
+
.action(async (options) => {
|
|
108
|
+
try {
|
|
109
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
110
|
+
if (!ctx.db) {
|
|
111
|
+
logger.error("Database not available");
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
const db = ctx.db.getDatabase();
|
|
115
|
+
|
|
116
|
+
if (getDbEncryptionStatus(db)) {
|
|
117
|
+
logger.info("Database is already marked as encrypted");
|
|
118
|
+
await shutdown();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const pass =
|
|
123
|
+
options.password ||
|
|
124
|
+
(await promptPassword("Database encryption password:"));
|
|
125
|
+
if (!pass) {
|
|
126
|
+
logger.error("Password is required");
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const { hash, salt } = hashPassword(pass);
|
|
131
|
+
setDbEncryptionStatus(db, true, hash, salt);
|
|
132
|
+
logger.success(
|
|
133
|
+
"Database encryption status set. Note: full SQLCipher encryption requires the desktop app.",
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
await shutdown();
|
|
137
|
+
} catch (err) {
|
|
138
|
+
logger.error(`Failed: ${err.message}`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// decrypt db
|
|
144
|
+
dec
|
|
145
|
+
.command("db")
|
|
146
|
+
.description("Disable database encryption tracking")
|
|
147
|
+
.option("--force", "Skip confirmation")
|
|
148
|
+
.action(async (options) => {
|
|
149
|
+
try {
|
|
150
|
+
if (!options.force) {
|
|
151
|
+
const { confirm } = await import("@inquirer/prompts");
|
|
152
|
+
const ok = await confirm({
|
|
153
|
+
message: "Disable database encryption tracking?",
|
|
154
|
+
});
|
|
155
|
+
if (!ok) {
|
|
156
|
+
logger.info("Cancelled");
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
162
|
+
if (!ctx.db) {
|
|
163
|
+
logger.error("Database not available");
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
const db = ctx.db.getDatabase();
|
|
167
|
+
setDbEncryptionStatus(db, false);
|
|
168
|
+
logger.success("Database encryption status cleared");
|
|
169
|
+
|
|
170
|
+
await shutdown();
|
|
171
|
+
} catch (err) {
|
|
172
|
+
logger.error(`Failed: ${err.message}`);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// encrypt info
|
|
178
|
+
enc
|
|
179
|
+
.command("info")
|
|
180
|
+
.description("Check if a file is encrypted")
|
|
181
|
+
.argument("<path>", "File to check")
|
|
182
|
+
.option("--json", "Output as JSON")
|
|
183
|
+
.action(async (filePath, options) => {
|
|
184
|
+
try {
|
|
185
|
+
const info = getEncryptedFileInfo(filePath);
|
|
186
|
+
|
|
187
|
+
if (options.json) {
|
|
188
|
+
console.log(JSON.stringify(info, null, 2));
|
|
189
|
+
} else {
|
|
190
|
+
logger.log(chalk.bold("File Info:\n"));
|
|
191
|
+
logger.log(` ${chalk.bold("Path:")} ${info.path}`);
|
|
192
|
+
logger.log(` ${chalk.bold("Size:")} ${info.size} bytes`);
|
|
193
|
+
logger.log(
|
|
194
|
+
` ${chalk.bold("Encrypted:")} ${info.encrypted ? chalk.green("yes (ChainlessChain format)") : chalk.gray("no")}`,
|
|
195
|
+
);
|
|
196
|
+
logger.log(` ${chalk.bold("Modified:")} ${info.modified}`);
|
|
197
|
+
}
|
|
198
|
+
} catch (err) {
|
|
199
|
+
logger.error(`Failed: ${err.message}`);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// encrypt status (db status)
|
|
205
|
+
enc
|
|
206
|
+
.command("status")
|
|
207
|
+
.description("Show database encryption status")
|
|
208
|
+
.option("--json", "Output as JSON")
|
|
209
|
+
.action(async (options) => {
|
|
210
|
+
try {
|
|
211
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
212
|
+
if (!ctx.db) {
|
|
213
|
+
logger.error("Database not available");
|
|
214
|
+
process.exit(1);
|
|
215
|
+
}
|
|
216
|
+
const db = ctx.db.getDatabase();
|
|
217
|
+
const encrypted = getDbEncryptionStatus(db);
|
|
218
|
+
|
|
219
|
+
if (options.json) {
|
|
220
|
+
console.log(JSON.stringify({ encrypted }, null, 2));
|
|
221
|
+
} else {
|
|
222
|
+
logger.log(
|
|
223
|
+
`Database encryption: ${encrypted ? chalk.green("enabled") : chalk.gray("not enabled")}`,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
await shutdown();
|
|
228
|
+
} catch (err) {
|
|
229
|
+
logger.error(`Failed: ${err.message}`);
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|