chainlesschain 0.37.9 → 0.37.11
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 +309 -19
- package/bin/chainlesschain.js +4 -0
- package/package.json +1 -1
- package/src/commands/a2a.js +374 -0
- package/src/commands/audit.js +286 -0
- package/src/commands/auth.js +387 -0
- package/src/commands/bi.js +240 -0
- package/src/commands/browse.js +184 -0
- package/src/commands/cowork.js +317 -0
- package/src/commands/did.js +376 -0
- package/src/commands/economy.js +375 -0
- package/src/commands/encrypt.js +233 -0
- package/src/commands/evolution.js +398 -0
- package/src/commands/export.js +125 -0
- package/src/commands/git.js +215 -0
- package/src/commands/hmemory.js +273 -0
- package/src/commands/hook.js +260 -0
- package/src/commands/import.js +259 -0
- package/src/commands/init.js +184 -0
- package/src/commands/instinct.js +202 -0
- package/src/commands/llm.js +155 -4
- package/src/commands/lowcode.js +320 -0
- package/src/commands/mcp.js +302 -0
- package/src/commands/memory.js +282 -0
- package/src/commands/note.js +187 -0
- package/src/commands/org.js +505 -0
- package/src/commands/p2p.js +274 -0
- package/src/commands/plugin.js +451 -0
- package/src/commands/sandbox.js +366 -0
- package/src/commands/search.js +237 -0
- package/src/commands/session.js +238 -0
- package/src/commands/skill.js +254 -201
- package/src/commands/sync.js +249 -0
- package/src/commands/tokens.js +214 -0
- package/src/commands/wallet.js +416 -0
- package/src/commands/workflow.js +359 -0
- package/src/commands/zkp.js +277 -0
- package/src/index.js +93 -1
- package/src/lib/a2a-protocol.js +371 -0
- package/src/lib/agent-coordinator.js +273 -0
- package/src/lib/agent-economy.js +369 -0
- package/src/lib/app-builder.js +377 -0
- package/src/lib/audit-logger.js +364 -0
- package/src/lib/bi-engine.js +299 -0
- package/src/lib/bm25-search.js +322 -0
- package/src/lib/browser-automation.js +216 -0
- package/src/lib/cowork/ab-comparator-cli.js +180 -0
- package/src/lib/cowork/code-knowledge-graph-cli.js +232 -0
- package/src/lib/cowork/debate-review-cli.js +144 -0
- package/src/lib/cowork/decision-kb-cli.js +153 -0
- package/src/lib/cowork/project-style-analyzer-cli.js +168 -0
- package/src/lib/cowork-adapter.js +106 -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/evolution-system.js +508 -0
- package/src/lib/git-integration.js +220 -0
- package/src/lib/hierarchical-memory.js +471 -0
- package/src/lib/hook-manager.js +387 -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/plugin-manager.js +430 -0
- package/src/lib/project-detector.js +53 -0
- package/src/lib/response-cache.js +156 -0
- package/src/lib/sandbox-v2.js +503 -0
- package/src/lib/service-container.js +183 -0
- package/src/lib/session-manager.js +189 -0
- package/src/lib/skill-loader.js +274 -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/lib/workflow-engine.js +503 -0
- package/src/lib/zkp-engine.js +241 -0
- package/src/repl/agent-repl.js +259 -124
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Economy commands
|
|
3
|
+
* chainlesschain economy price|pay|balance|channel|market|trade|nft|revenue|contribute
|
|
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
|
+
ensureEconomyTables,
|
|
11
|
+
priceService,
|
|
12
|
+
getServicePrice,
|
|
13
|
+
pay,
|
|
14
|
+
getBalance,
|
|
15
|
+
openChannel,
|
|
16
|
+
closeChannel,
|
|
17
|
+
listResource,
|
|
18
|
+
getMarketListings,
|
|
19
|
+
tradeResource,
|
|
20
|
+
mintNFT,
|
|
21
|
+
recordContribution,
|
|
22
|
+
getContributions,
|
|
23
|
+
distributeRevenue,
|
|
24
|
+
} from "../lib/agent-economy.js";
|
|
25
|
+
|
|
26
|
+
export function registerEconomyCommand(program) {
|
|
27
|
+
const economy = program
|
|
28
|
+
.command("economy")
|
|
29
|
+
.description("Agent economy — payments, channels, marketplace, NFTs");
|
|
30
|
+
|
|
31
|
+
// economy price
|
|
32
|
+
economy
|
|
33
|
+
.command("price <service-id> <price>")
|
|
34
|
+
.description("Set a service price")
|
|
35
|
+
.action(async (serviceId, price) => {
|
|
36
|
+
try {
|
|
37
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
38
|
+
if (!ctx.db) {
|
|
39
|
+
logger.error("Database not available");
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const db = ctx.db.getDatabase();
|
|
43
|
+
ensureEconomyTables(db);
|
|
44
|
+
|
|
45
|
+
const result = priceService(db, serviceId, parseFloat(price), {});
|
|
46
|
+
logger.success(
|
|
47
|
+
`Price set for ${chalk.cyan(serviceId)}: ${chalk.bold(result.price)}`,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
await shutdown();
|
|
51
|
+
} catch (err) {
|
|
52
|
+
logger.error(`Failed: ${err.message}`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// economy pay
|
|
58
|
+
economy
|
|
59
|
+
.command("pay <from> <to> <amount>")
|
|
60
|
+
.description("Make a payment between agents")
|
|
61
|
+
.option("--description <text>", "Payment description")
|
|
62
|
+
.action(async (from, to, amount, options) => {
|
|
63
|
+
try {
|
|
64
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
65
|
+
if (!ctx.db) {
|
|
66
|
+
logger.error("Database not available");
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
const db = ctx.db.getDatabase();
|
|
70
|
+
ensureEconomyTables(db);
|
|
71
|
+
|
|
72
|
+
const result = pay(
|
|
73
|
+
db,
|
|
74
|
+
from,
|
|
75
|
+
to,
|
|
76
|
+
parseFloat(amount),
|
|
77
|
+
options.description,
|
|
78
|
+
);
|
|
79
|
+
logger.success("Payment complete");
|
|
80
|
+
logger.log(` ${chalk.bold("Tx ID:")} ${chalk.cyan(result.txId)}`);
|
|
81
|
+
logger.log(` ${chalk.bold("From:")} ${result.from}`);
|
|
82
|
+
logger.log(` ${chalk.bold("To:")} ${result.to}`);
|
|
83
|
+
logger.log(` ${chalk.bold("Amount:")} ${result.amount}`);
|
|
84
|
+
logger.log(` ${chalk.bold("Balance:")} ${result.balance}`);
|
|
85
|
+
|
|
86
|
+
await shutdown();
|
|
87
|
+
} catch (err) {
|
|
88
|
+
logger.error(`Failed: ${err.message}`);
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// economy balance
|
|
94
|
+
economy
|
|
95
|
+
.command("balance <agent-id>")
|
|
96
|
+
.description("Show agent balance")
|
|
97
|
+
.option("--json", "Output as JSON")
|
|
98
|
+
.action(async (agentId, options) => {
|
|
99
|
+
try {
|
|
100
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
101
|
+
if (!ctx.db) {
|
|
102
|
+
logger.error("Database not available");
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
const db = ctx.db.getDatabase();
|
|
106
|
+
ensureEconomyTables(db);
|
|
107
|
+
|
|
108
|
+
const bal = getBalance(agentId);
|
|
109
|
+
if (options.json) {
|
|
110
|
+
console.log(JSON.stringify({ agentId, ...bal }, null, 2));
|
|
111
|
+
} else {
|
|
112
|
+
logger.log(` ${chalk.bold("Agent:")} ${chalk.cyan(agentId)}`);
|
|
113
|
+
logger.log(` ${chalk.bold("Balance:")} ${bal.balance}`);
|
|
114
|
+
logger.log(` ${chalk.bold("Locked:")} ${bal.locked}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
await shutdown();
|
|
118
|
+
} catch (err) {
|
|
119
|
+
logger.error(`Failed: ${err.message}`);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// economy channel open / close
|
|
125
|
+
const channel = economy
|
|
126
|
+
.command("channel")
|
|
127
|
+
.description("State channel management");
|
|
128
|
+
|
|
129
|
+
channel
|
|
130
|
+
.command("open <party-a> <party-b>")
|
|
131
|
+
.description("Open a state channel")
|
|
132
|
+
.option("--deposit <amount>", "Initial deposit from party A", "0")
|
|
133
|
+
.action(async (partyA, partyB, options) => {
|
|
134
|
+
try {
|
|
135
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
136
|
+
if (!ctx.db) {
|
|
137
|
+
logger.error("Database not available");
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
const db = ctx.db.getDatabase();
|
|
141
|
+
ensureEconomyTables(db);
|
|
142
|
+
|
|
143
|
+
const ch = openChannel(db, partyA, partyB, parseFloat(options.deposit));
|
|
144
|
+
logger.success("Channel opened");
|
|
145
|
+
logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(ch.id)}`);
|
|
146
|
+
logger.log(` ${chalk.bold("Party A:")} ${ch.partyA} (${ch.balanceA})`);
|
|
147
|
+
logger.log(` ${chalk.bold("Party B:")} ${ch.partyB} (${ch.balanceB})`);
|
|
148
|
+
|
|
149
|
+
await shutdown();
|
|
150
|
+
} catch (err) {
|
|
151
|
+
logger.error(`Failed: ${err.message}`);
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
channel
|
|
157
|
+
.command("close <channel-id>")
|
|
158
|
+
.description("Close and settle a state channel")
|
|
159
|
+
.action(async (channelId) => {
|
|
160
|
+
try {
|
|
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
|
+
ensureEconomyTables(db);
|
|
168
|
+
|
|
169
|
+
const ch = closeChannel(db, channelId);
|
|
170
|
+
logger.success("Channel closed and settled");
|
|
171
|
+
logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(ch.id)}`);
|
|
172
|
+
logger.log(` ${chalk.bold("Status:")} ${ch.status}`);
|
|
173
|
+
|
|
174
|
+
await shutdown();
|
|
175
|
+
} catch (err) {
|
|
176
|
+
logger.error(`Failed: ${err.message}`);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// economy market list / browse
|
|
182
|
+
const market = economy.command("market").description("Resource marketplace");
|
|
183
|
+
|
|
184
|
+
market
|
|
185
|
+
.command("list <type> <provider> <price> <amount>")
|
|
186
|
+
.description("List a resource on the marketplace")
|
|
187
|
+
.action(async (type, provider, price, amount) => {
|
|
188
|
+
try {
|
|
189
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
190
|
+
if (!ctx.db) {
|
|
191
|
+
logger.error("Database not available");
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
const db = ctx.db.getDatabase();
|
|
195
|
+
ensureEconomyTables(db);
|
|
196
|
+
|
|
197
|
+
const listing = listResource(
|
|
198
|
+
db,
|
|
199
|
+
type,
|
|
200
|
+
provider,
|
|
201
|
+
parseFloat(price),
|
|
202
|
+
parseFloat(amount),
|
|
203
|
+
"unit",
|
|
204
|
+
);
|
|
205
|
+
logger.success("Resource listed");
|
|
206
|
+
logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(listing.id)}`);
|
|
207
|
+
logger.log(` ${chalk.bold("Type:")} ${listing.resourceType}`);
|
|
208
|
+
logger.log(` ${chalk.bold("Provider:")} ${listing.provider}`);
|
|
209
|
+
logger.log(` ${chalk.bold("Price:")} ${listing.price}`);
|
|
210
|
+
logger.log(` ${chalk.bold("Available:")} ${listing.available}`);
|
|
211
|
+
|
|
212
|
+
await shutdown();
|
|
213
|
+
} catch (err) {
|
|
214
|
+
logger.error(`Failed: ${err.message}`);
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
market
|
|
220
|
+
.command("browse")
|
|
221
|
+
.description("Browse marketplace listings")
|
|
222
|
+
.option("--type <filter>", "Filter by resource type")
|
|
223
|
+
.option("--json", "Output as JSON")
|
|
224
|
+
.action(async (options) => {
|
|
225
|
+
try {
|
|
226
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
227
|
+
if (!ctx.db) {
|
|
228
|
+
logger.error("Database not available");
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
const db = ctx.db.getDatabase();
|
|
232
|
+
ensureEconomyTables(db);
|
|
233
|
+
|
|
234
|
+
const filter = options.type ? { type: options.type } : undefined;
|
|
235
|
+
const listings = getMarketListings(filter);
|
|
236
|
+
|
|
237
|
+
if (options.json) {
|
|
238
|
+
console.log(JSON.stringify(listings, null, 2));
|
|
239
|
+
} else {
|
|
240
|
+
if (listings.length === 0) {
|
|
241
|
+
logger.log("No active listings");
|
|
242
|
+
} else {
|
|
243
|
+
for (const l of listings) {
|
|
244
|
+
logger.log(
|
|
245
|
+
` ${chalk.cyan(l.id.slice(0, 8))} ${l.resourceType} by ${l.provider} — ${l.price}/${l.unit} (${l.available} avail)`,
|
|
246
|
+
);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
await shutdown();
|
|
252
|
+
} catch (err) {
|
|
253
|
+
logger.error(`Failed: ${err.message}`);
|
|
254
|
+
process.exit(1);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// economy trade
|
|
259
|
+
economy
|
|
260
|
+
.command("trade <listing-id> <buyer> <quantity>")
|
|
261
|
+
.description("Buy a resource from the marketplace")
|
|
262
|
+
.action(async (listingId, buyer, quantity) => {
|
|
263
|
+
try {
|
|
264
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
265
|
+
if (!ctx.db) {
|
|
266
|
+
logger.error("Database not available");
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
269
|
+
const db = ctx.db.getDatabase();
|
|
270
|
+
ensureEconomyTables(db);
|
|
271
|
+
|
|
272
|
+
const result = tradeResource(listingId, buyer, parseFloat(quantity));
|
|
273
|
+
logger.success("Trade complete");
|
|
274
|
+
logger.log(` ${chalk.bold("Cost:")} ${result.cost}`);
|
|
275
|
+
logger.log(` ${chalk.bold("Quantity:")} ${result.quantity}`);
|
|
276
|
+
logger.log(` ${chalk.bold("Remaining:")} ${result.remaining}`);
|
|
277
|
+
|
|
278
|
+
await shutdown();
|
|
279
|
+
} catch (err) {
|
|
280
|
+
logger.error(`Failed: ${err.message}`);
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
// economy nft mint
|
|
286
|
+
const nft = economy.command("nft").description("NFT management");
|
|
287
|
+
|
|
288
|
+
nft
|
|
289
|
+
.command("mint <owner> <type>")
|
|
290
|
+
.description("Mint a new NFT")
|
|
291
|
+
.option("--metadata <json>", "NFT metadata as JSON")
|
|
292
|
+
.action(async (owner, type, options) => {
|
|
293
|
+
try {
|
|
294
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
295
|
+
if (!ctx.db) {
|
|
296
|
+
logger.error("Database not available");
|
|
297
|
+
process.exit(1);
|
|
298
|
+
}
|
|
299
|
+
const db = ctx.db.getDatabase();
|
|
300
|
+
ensureEconomyTables(db);
|
|
301
|
+
|
|
302
|
+
const metadata = options.metadata ? JSON.parse(options.metadata) : {};
|
|
303
|
+
const nftObj = mintNFT(db, owner, type, metadata);
|
|
304
|
+
logger.success("NFT minted");
|
|
305
|
+
logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(nftObj.id)}`);
|
|
306
|
+
logger.log(` ${chalk.bold("Owner:")} ${nftObj.owner}`);
|
|
307
|
+
logger.log(` ${chalk.bold("Type:")} ${nftObj.type}`);
|
|
308
|
+
|
|
309
|
+
await shutdown();
|
|
310
|
+
} catch (err) {
|
|
311
|
+
logger.error(`Failed: ${err.message}`);
|
|
312
|
+
process.exit(1);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// economy revenue
|
|
317
|
+
economy
|
|
318
|
+
.command("revenue <pool> <agent-ids>")
|
|
319
|
+
.description("Distribute revenue pool (comma-separated agent IDs)")
|
|
320
|
+
.action(async (pool, agentIds) => {
|
|
321
|
+
try {
|
|
322
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
323
|
+
if (!ctx.db) {
|
|
324
|
+
logger.error("Database not available");
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
const db = ctx.db.getDatabase();
|
|
328
|
+
ensureEconomyTables(db);
|
|
329
|
+
|
|
330
|
+
const ids = agentIds.split(",").map((s) => s.trim());
|
|
331
|
+
const results = distributeRevenue(db, parseFloat(pool), ids);
|
|
332
|
+
logger.success(
|
|
333
|
+
`Revenue distributed: ${pool} among ${ids.length} agents`,
|
|
334
|
+
);
|
|
335
|
+
for (const r of results) {
|
|
336
|
+
logger.log(
|
|
337
|
+
` ${chalk.cyan(r.agentId)}: +${r.share} → ${r.newBalance}`,
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
await shutdown();
|
|
342
|
+
} catch (err) {
|
|
343
|
+
logger.error(`Failed: ${err.message}`);
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// economy contribute
|
|
349
|
+
economy
|
|
350
|
+
.command("contribute <agent-id> <type> <value>")
|
|
351
|
+
.description("Record agent contribution")
|
|
352
|
+
.action(async (agentId, type, value) => {
|
|
353
|
+
try {
|
|
354
|
+
const ctx = await bootstrap({ verbose: program.opts().verbose });
|
|
355
|
+
if (!ctx.db) {
|
|
356
|
+
logger.error("Database not available");
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
const db = ctx.db.getDatabase();
|
|
360
|
+
ensureEconomyTables(db);
|
|
361
|
+
|
|
362
|
+
const c = recordContribution(db, agentId, type, parseFloat(value), "");
|
|
363
|
+
logger.success("Contribution recorded");
|
|
364
|
+
logger.log(` ${chalk.bold("ID:")} ${chalk.cyan(c.id)}`);
|
|
365
|
+
logger.log(` ${chalk.bold("Agent:")} ${c.agentId}`);
|
|
366
|
+
logger.log(` ${chalk.bold("Type:")} ${c.type}`);
|
|
367
|
+
logger.log(` ${chalk.bold("Value:")} ${c.value}`);
|
|
368
|
+
|
|
369
|
+
await shutdown();
|
|
370
|
+
} catch (err) {
|
|
371
|
+
logger.error(`Failed: ${err.message}`);
|
|
372
|
+
process.exit(1);
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
}
|
|
@@ -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
|
+
}
|