codemem 0.21.1 → 0.22.0-alpha.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/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/index.js +225 -34
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/commands/db.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/commands/db.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4CpC,eAAO,MAAM,SAAS,SAEe,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAqCH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA0JpC,eAAO,MAAM,WAAW,SAE+B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { DEFAULT_COORDINATOR_DB_PATH, MemoryStore, ObserverClient, RawEventSweeper, SyncRetentionRunner, VERSION, backfillTagsText, backfillVectors, buildRawEventEnvelopeFromHook,
|
|
2
|
+
import { DEFAULT_COORDINATOR_DB_PATH, MemoryStore, ObserverClient, RawEventSweeper, SyncRetentionRunner, VERSION, backfillTagsText, backfillVectors, buildRawEventEnvelopeFromHook, connect, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorDisableDeviceAction, coordinatorEnrollDeviceAction, coordinatorImportInviteAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorReviewJoinRequestAction, createCoordinatorApp, deactivateLowSignalMemories, deactivateLowSignalObservations, ensureDeviceIdentity, exportMemories, fingerprintPublicKey, getRawEventStatus, importMemories, initDatabase, isEmbeddingDisabled, loadPublicKey, loadSqliteVec, planReplicationOpsAgePrune, pruneReplicationOpsUntilCaughtUp, rawEventsGate, readCodememConfigFile, readCoordinatorSyncConfig, readImportPayload, resolveDbPath, resolveProject, retryRawEventFailures, runSyncDaemon, runSyncPass, schema, setPeerProjectFilter, stripJsonComments, stripPrivateObj, stripTrailingCommas, syncPassPreflight, updatePeerAddresses, vacuumDatabase, writeCodememConfigFile } from "@codemem/core";
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import omelette from "omelette";
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
@@ -232,7 +232,7 @@ dbCommand.addCommand(new Command("init").configureHelp(helpStyle).description("V
|
|
|
232
232
|
const beforeBytes = estimateReplicationOpsBytes(db);
|
|
233
233
|
p.intro("codemem db prune-replication-ops");
|
|
234
234
|
p.log.info(`Replication ops size: ${formatBytes(beforeBytes)}`);
|
|
235
|
-
p.log.info(`Policy: approximately
|
|
235
|
+
p.log.info(`Policy: approximately prune oldest-first toward <= ${maxSizeMb} MB while removing history older than ${maxAgeDays} day(s), subject to per-pass batch/runtime limits`);
|
|
236
236
|
const agePlan = planReplicationOpsAgePrune(db, maxAgeDays, batchOps);
|
|
237
237
|
if (agePlan.candidate_ops > 0) p.log.info(`Age pass plan: ${agePlan.candidate_ops.toLocaleString()} ops, ~${formatBytes(agePlan.estimated_candidate_bytes)} in ~${agePlan.estimated_batches.toLocaleString()} batch(es), cutoff ${agePlan.cutoff_cursor}`);
|
|
238
238
|
else p.log.info("Age pass plan: no ops older than cutoff");
|
|
@@ -240,35 +240,21 @@ dbCommand.addCommand(new Command("init").configureHelp(helpStyle).description("V
|
|
|
240
240
|
p.outro("Dry run only; no changes made");
|
|
241
241
|
return;
|
|
242
242
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
maxSizeBytes: maxSizeMb * 1024 * 1024,
|
|
259
|
-
maxDeleteOps: batchOps,
|
|
260
|
-
maxRuntimeMs: batchRuntimeMs
|
|
261
|
-
});
|
|
262
|
-
batches += 1;
|
|
263
|
-
totalDeleted += result.deleted;
|
|
264
|
-
lastFloor = result.retained_floor_cursor;
|
|
265
|
-
afterBytes = result.estimated_bytes_after ?? estimateReplicationOpsBytes(db);
|
|
266
|
-
p.log.step(`Batch ${batches}: deleted ${result.deleted.toLocaleString()} ops, remaining size ~${formatBytes(afterBytes)}`);
|
|
267
|
-
if (result.deleted === 0 || !result.stopped_by_budget) break;
|
|
268
|
-
}
|
|
269
|
-
p.log.info(`Deleted ops: ${totalDeleted.toLocaleString()}`);
|
|
270
|
-
p.log.info(`Estimated replication ops size after prune: ${formatBytes(afterBytes)} (approximate)`);
|
|
271
|
-
if (lastFloor) p.log.info(`Retained floor: ${lastFloor}`);
|
|
243
|
+
const loopResult = pruneReplicationOpsUntilCaughtUp(db, {
|
|
244
|
+
maxAgeDays,
|
|
245
|
+
maxSizeBytes: maxSizeMb * 1024 * 1024,
|
|
246
|
+
maxDeleteOps: batchOps,
|
|
247
|
+
maxRuntimeMs: batchRuntimeMs,
|
|
248
|
+
onPass: (pass) => {
|
|
249
|
+
if (pass.deleted === 0 && !pass.stoppedByBudget) return;
|
|
250
|
+
const suffix = pass.stoppedByBudget ? " (budget-limited)" : "";
|
|
251
|
+
p.log.step(`Pass ${pass.passNumber}: deleted ${pass.deleted.toLocaleString()} ops, remaining size ~${formatBytes(pass.afterBytes)}${suffix}`);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
p.log.info(`Deleted ops: ${loopResult.totalDeleted.toLocaleString()}`);
|
|
255
|
+
p.log.info(`Estimated replication ops size after prune: ${formatBytes(loopResult.afterBytes)} (approximate)`);
|
|
256
|
+
if (loopResult.lastFloor) p.log.info(`Retained floor: ${loopResult.lastFloor}`);
|
|
257
|
+
if (loopResult.stoppedByBudget) p.log.warn("Prune stopped because the current batch/runtime budget was exhausted before retention caught up. Re-run with higher --batch-ops or --batch-runtime-ms for faster catch-up.");
|
|
272
258
|
if (opts.vacuum) {
|
|
273
259
|
p.log.step("Running VACUUM as requested...");
|
|
274
260
|
db.close();
|
|
@@ -1748,6 +1734,33 @@ function parseAttemptsLimit(value) {
|
|
|
1748
1734
|
if (!/^\d+$/.test(value.trim())) throw new Error(`Invalid --limit: ${value}`);
|
|
1749
1735
|
return Number.parseInt(value, 10);
|
|
1750
1736
|
}
|
|
1737
|
+
function resolvePeerMatch(db, peerRef) {
|
|
1738
|
+
const trimmed = peerRef.trim();
|
|
1739
|
+
if (!trimmed) return null;
|
|
1740
|
+
const byId = db.select({
|
|
1741
|
+
peer_device_id: schema.syncPeers.peer_device_id,
|
|
1742
|
+
name: schema.syncPeers.name
|
|
1743
|
+
}).from(schema.syncPeers).where(eq(schema.syncPeers.peer_device_id, trimmed)).get();
|
|
1744
|
+
if (byId) return byId;
|
|
1745
|
+
const byName = db.select({
|
|
1746
|
+
peer_device_id: schema.syncPeers.peer_device_id,
|
|
1747
|
+
name: schema.syncPeers.name
|
|
1748
|
+
}).from(schema.syncPeers).where(eq(schema.syncPeers.name, trimmed)).all();
|
|
1749
|
+
if (byName.length > 1) return "ambiguous";
|
|
1750
|
+
return byName[0] ?? null;
|
|
1751
|
+
}
|
|
1752
|
+
function readCoordinatorPublicKey(opts) {
|
|
1753
|
+
const inline = String(opts.publicKey ?? "").trim();
|
|
1754
|
+
const filePath = String(opts.publicKeyFile ?? "").trim();
|
|
1755
|
+
if (inline && filePath) throw new Error("Use only one of --public-key or --public-key-file");
|
|
1756
|
+
if (filePath) {
|
|
1757
|
+
const text = readFileSync(filePath, "utf8").trim();
|
|
1758
|
+
if (!text) throw new Error(`Public key file is empty: ${filePath}`);
|
|
1759
|
+
return text;
|
|
1760
|
+
}
|
|
1761
|
+
if (!inline) throw new Error("Public key required via --public-key or --public-key-file");
|
|
1762
|
+
return inline;
|
|
1763
|
+
}
|
|
1751
1764
|
async function portOpen(host, port) {
|
|
1752
1765
|
return new Promise((resolve) => {
|
|
1753
1766
|
const socket = net.createConnection({
|
|
@@ -2147,7 +2160,7 @@ syncCommand.addCommand(new Command("disable").configureHelp(helpStyle).descripti
|
|
|
2147
2160
|
p.intro("codemem sync disable");
|
|
2148
2161
|
p.outro("Sync disabled — restart `codemem serve` to take effect");
|
|
2149
2162
|
}));
|
|
2150
|
-
|
|
2163
|
+
var peersCommand = new Command("peers").configureHelp(helpStyle).description("List known sync peers").option("--db <path>", "database path").option("--db-path <path>", "database path").option("--json", "output as JSON").action((opts) => {
|
|
2151
2164
|
const store = new MemoryStore(resolveDbPath(opts.db ?? opts.dbPath));
|
|
2152
2165
|
try {
|
|
2153
2166
|
const peers = drizzle(store.db, { schema }).select({
|
|
@@ -2175,7 +2188,41 @@ syncCommand.addCommand(new Command("peers").configureHelp(helpStyle).description
|
|
|
2175
2188
|
} finally {
|
|
2176
2189
|
store.close();
|
|
2177
2190
|
}
|
|
2191
|
+
});
|
|
2192
|
+
peersCommand.addCommand(new Command("remove").configureHelp(helpStyle).description("Remove a sync peer by device id or exact name").argument("<peer>", "peer device id or exact name").option("--db <path>", "database path").option("--db-path <path>", "database path").option("--json", "output as JSON").action((peerRef, opts) => {
|
|
2193
|
+
const store = new MemoryStore(resolveDbPath(opts.db ?? opts.dbPath));
|
|
2194
|
+
try {
|
|
2195
|
+
const d = drizzle(store.db, { schema });
|
|
2196
|
+
const match = resolvePeerMatch(d, peerRef);
|
|
2197
|
+
if (match === "ambiguous") {
|
|
2198
|
+
p.log.error(`Peer name is ambiguous: ${peerRef.trim()}`);
|
|
2199
|
+
process.exitCode = 1;
|
|
2200
|
+
return;
|
|
2201
|
+
}
|
|
2202
|
+
if (!match) {
|
|
2203
|
+
p.log.error(`Peer not found: ${peerRef.trim()}`);
|
|
2204
|
+
process.exitCode = 1;
|
|
2205
|
+
return;
|
|
2206
|
+
}
|
|
2207
|
+
d.delete(schema.replicationCursors).where(eq(schema.replicationCursors.peer_device_id, match.peer_device_id)).run();
|
|
2208
|
+
d.delete(schema.syncPeers).where(eq(schema.syncPeers.peer_device_id, match.peer_device_id)).run();
|
|
2209
|
+
const payload = {
|
|
2210
|
+
ok: true,
|
|
2211
|
+
peer_device_id: match.peer_device_id,
|
|
2212
|
+
name: match.name
|
|
2213
|
+
};
|
|
2214
|
+
if (opts.json) {
|
|
2215
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
2216
|
+
return;
|
|
2217
|
+
}
|
|
2218
|
+
p.intro("codemem sync peers remove");
|
|
2219
|
+
p.log.success(`Removed peer ${match.name || match.peer_device_id}`);
|
|
2220
|
+
p.outro(match.peer_device_id);
|
|
2221
|
+
} finally {
|
|
2222
|
+
store.close();
|
|
2223
|
+
}
|
|
2178
2224
|
}));
|
|
2225
|
+
syncCommand.addCommand(peersCommand);
|
|
2179
2226
|
syncCommand.addCommand(new Command("connect").configureHelp(helpStyle).description("Configure coordinator URL for cloud sync").argument("<url>", "coordinator URL (e.g. https://coordinator.example.com)").option("--group <group>", "sync group ID").action((url, opts) => {
|
|
2180
2227
|
const config = readCodememConfigFile();
|
|
2181
2228
|
config.sync_coordinator_url = url.trim();
|
|
@@ -2187,9 +2234,152 @@ syncCommand.addCommand(new Command("connect").configureHelp(helpStyle).descripti
|
|
|
2187
2234
|
p.outro("Restart `codemem serve` to activate coordinator sync");
|
|
2188
2235
|
}));
|
|
2189
2236
|
var coordinatorCommand = new Command("coordinator").configureHelp(helpStyle).description("Manage coordinator invites, join requests, and relay server");
|
|
2190
|
-
coordinatorCommand.addCommand(new Command("
|
|
2237
|
+
coordinatorCommand.addCommand(new Command("group-create").configureHelp(helpStyle).description("Create a coordinator group in the local store").argument("<group>", "group id").option("--name <name>", "display name override").option("--db <path>", "coordinator database path").option("--db-path <path>", "coordinator database path").option("--json", "output as JSON").action(async (groupId, opts) => {
|
|
2238
|
+
const group = coordinatorCreateGroupAction({
|
|
2239
|
+
groupId,
|
|
2240
|
+
displayName: opts.name?.trim() || null,
|
|
2241
|
+
dbPath: opts.db ?? opts.dbPath ?? null
|
|
2242
|
+
});
|
|
2243
|
+
if (opts.json) {
|
|
2244
|
+
console.log(JSON.stringify(group, null, 2));
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
2247
|
+
p.intro("codemem sync coordinator group-create");
|
|
2248
|
+
p.log.success(`Group ready: ${groupId.trim()}`);
|
|
2249
|
+
p.outro(String(group.display_name ?? group.group_id ?? groupId.trim()));
|
|
2250
|
+
}));
|
|
2251
|
+
coordinatorCommand.addCommand(new Command("list-groups").configureHelp(helpStyle).description("List coordinator groups from the local store").option("--db <path>", "coordinator database path").option("--db-path <path>", "coordinator database path").option("--json", "output as JSON").action((opts) => {
|
|
2252
|
+
const groups = coordinatorListGroupsAction({ dbPath: opts.db ?? opts.dbPath ?? null });
|
|
2253
|
+
if (opts.json) {
|
|
2254
|
+
console.log(JSON.stringify(groups, null, 2));
|
|
2255
|
+
return;
|
|
2256
|
+
}
|
|
2257
|
+
p.intro("codemem sync coordinator list-groups");
|
|
2258
|
+
if (groups.length === 0) {
|
|
2259
|
+
p.outro("No coordinator groups found");
|
|
2260
|
+
return;
|
|
2261
|
+
}
|
|
2262
|
+
for (const group of groups) p.log.message(`- ${String(group.group_id ?? "")}${group.display_name ? ` (${String(group.display_name)})` : ""}`);
|
|
2263
|
+
p.outro(`${groups.length} group(s)`);
|
|
2264
|
+
}));
|
|
2265
|
+
coordinatorCommand.addCommand(new Command("enroll-device").configureHelp(helpStyle).description("Enroll a device in a local coordinator group").argument("<group>", "group id").argument("<device-id>", "device id").option("--fingerprint <fingerprint>", "device fingerprint").option("--public-key <key>", "device public key").option("--public-key-file <path>", "path to device public key").option("--name <name>", "display name").option("--db <path>", "coordinator database path").option("--db-path <path>", "coordinator database path").option("--json", "output as JSON").action(async (groupId, deviceId, opts) => {
|
|
2266
|
+
const publicKey = readCoordinatorPublicKey(opts);
|
|
2267
|
+
const fingerprint = String(opts.fingerprint ?? "").trim();
|
|
2268
|
+
if (!fingerprint) {
|
|
2269
|
+
p.log.error("Fingerprint required via --fingerprint");
|
|
2270
|
+
process.exitCode = 1;
|
|
2271
|
+
return;
|
|
2272
|
+
}
|
|
2273
|
+
if (fingerprintPublicKey(publicKey) !== fingerprint) {
|
|
2274
|
+
p.log.error("Fingerprint does not match the provided public key");
|
|
2275
|
+
process.exitCode = 1;
|
|
2276
|
+
return;
|
|
2277
|
+
}
|
|
2278
|
+
const enrollment = coordinatorEnrollDeviceAction({
|
|
2279
|
+
groupId,
|
|
2280
|
+
deviceId,
|
|
2281
|
+
fingerprint,
|
|
2282
|
+
publicKey,
|
|
2283
|
+
displayName: opts.name?.trim() || null,
|
|
2284
|
+
dbPath: opts.db ?? opts.dbPath ?? null
|
|
2285
|
+
});
|
|
2286
|
+
if (opts.json) {
|
|
2287
|
+
console.log(JSON.stringify(enrollment, null, 2));
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2290
|
+
p.intro("codemem sync coordinator enroll-device");
|
|
2291
|
+
p.log.success(`Enrolled ${deviceId.trim()} in ${groupId.trim()}`);
|
|
2292
|
+
p.outro(String(enrollment.display_name ?? enrollment.device_id ?? deviceId.trim()));
|
|
2293
|
+
}));
|
|
2294
|
+
coordinatorCommand.addCommand(new Command("list-devices").configureHelp(helpStyle).description("List enrolled devices in a local coordinator group").argument("<group>", "group id").option("--include-disabled", "include disabled devices").option("--db <path>", "coordinator database path").option("--db-path <path>", "coordinator database path").option("--json", "output as JSON").action((groupId, opts) => {
|
|
2295
|
+
const rows = coordinatorListDevicesAction({
|
|
2296
|
+
groupId,
|
|
2297
|
+
includeDisabled: opts.includeDisabled === true,
|
|
2298
|
+
dbPath: opts.db ?? opts.dbPath ?? null
|
|
2299
|
+
});
|
|
2300
|
+
if (opts.json) {
|
|
2301
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
2302
|
+
return;
|
|
2303
|
+
}
|
|
2304
|
+
p.intro("codemem sync coordinator list-devices");
|
|
2305
|
+
if (rows.length === 0) {
|
|
2306
|
+
p.outro(`No enrolled devices for ${groupId.trim()}`);
|
|
2307
|
+
return;
|
|
2308
|
+
}
|
|
2309
|
+
for (const row of rows) {
|
|
2310
|
+
const label = String(row.display_name ?? row.device_id ?? "").trim() || String(row.device_id ?? "");
|
|
2311
|
+
const enabled = Number(row.enabled ?? 1) === 1 ? "enabled" : "disabled";
|
|
2312
|
+
p.log.message(`- ${label} (${String(row.device_id ?? "")}) ${enabled}`);
|
|
2313
|
+
}
|
|
2314
|
+
p.outro(`${rows.length} device(s)`);
|
|
2315
|
+
}));
|
|
2316
|
+
coordinatorCommand.addCommand(new Command("rename-device").configureHelp(helpStyle).description("Rename an enrolled device in the local coordinator store").argument("<group>", "group id").argument("<device-id>", "device id").requiredOption("--name <name>", "display name").option("--db <path>", "coordinator database path").option("--db-path <path>", "coordinator database path").option("--json", "output as JSON").action((groupId, deviceId, opts) => {
|
|
2317
|
+
const result = coordinatorRenameDeviceAction({
|
|
2318
|
+
groupId,
|
|
2319
|
+
deviceId,
|
|
2320
|
+
displayName: opts.name.trim(),
|
|
2321
|
+
dbPath: opts.db ?? opts.dbPath ?? null
|
|
2322
|
+
});
|
|
2323
|
+
if (!result) {
|
|
2324
|
+
p.log.error(`Device not found: ${deviceId.trim()}`);
|
|
2325
|
+
process.exitCode = 1;
|
|
2326
|
+
return;
|
|
2327
|
+
}
|
|
2328
|
+
if (opts.json) {
|
|
2329
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2330
|
+
return;
|
|
2331
|
+
}
|
|
2332
|
+
p.intro("codemem sync coordinator rename-device");
|
|
2333
|
+
p.log.success(`Renamed ${deviceId.trim()} in ${groupId.trim()}`);
|
|
2334
|
+
p.outro(String(result.display_name ?? result.device_id ?? deviceId.trim()));
|
|
2335
|
+
}));
|
|
2336
|
+
coordinatorCommand.addCommand(new Command("disable-device").configureHelp(helpStyle).description("Disable an enrolled device in the local coordinator store").argument("<group>", "group id").argument("<device-id>", "device id").option("--db <path>", "coordinator database path").option("--db-path <path>", "coordinator database path").option("--json", "output as JSON").action((groupId, deviceId, opts) => {
|
|
2337
|
+
if (!coordinatorDisableDeviceAction({
|
|
2338
|
+
groupId,
|
|
2339
|
+
deviceId,
|
|
2340
|
+
dbPath: opts.db ?? opts.dbPath ?? null
|
|
2341
|
+
})) {
|
|
2342
|
+
p.log.error(`Device not found: ${deviceId.trim()}`);
|
|
2343
|
+
process.exitCode = 1;
|
|
2344
|
+
return;
|
|
2345
|
+
}
|
|
2346
|
+
if (opts.json) {
|
|
2347
|
+
console.log(JSON.stringify({
|
|
2348
|
+
ok: true,
|
|
2349
|
+
group_id: groupId.trim(),
|
|
2350
|
+
device_id: deviceId.trim()
|
|
2351
|
+
}, null, 2));
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
p.intro("codemem sync coordinator disable-device");
|
|
2355
|
+
p.log.success(`Disabled ${deviceId.trim()} in ${groupId.trim()}`);
|
|
2356
|
+
p.outro("disabled");
|
|
2357
|
+
}));
|
|
2358
|
+
coordinatorCommand.addCommand(new Command("remove-device").configureHelp(helpStyle).description("Remove an enrolled device from the local coordinator store").argument("<group>", "group id").argument("<device-id>", "device id").option("--db <path>", "coordinator database path").option("--db-path <path>", "coordinator database path").option("--json", "output as JSON").action((groupId, deviceId, opts) => {
|
|
2359
|
+
if (!coordinatorRemoveDeviceAction({
|
|
2360
|
+
groupId,
|
|
2361
|
+
deviceId,
|
|
2362
|
+
dbPath: opts.db ?? opts.dbPath ?? null
|
|
2363
|
+
})) {
|
|
2364
|
+
p.log.error(`Device not found: ${deviceId.trim()}`);
|
|
2365
|
+
process.exitCode = 1;
|
|
2366
|
+
return;
|
|
2367
|
+
}
|
|
2368
|
+
if (opts.json) {
|
|
2369
|
+
console.log(JSON.stringify({
|
|
2370
|
+
ok: true,
|
|
2371
|
+
group_id: groupId.trim(),
|
|
2372
|
+
device_id: deviceId.trim()
|
|
2373
|
+
}, null, 2));
|
|
2374
|
+
return;
|
|
2375
|
+
}
|
|
2376
|
+
p.intro("codemem sync coordinator remove-device");
|
|
2377
|
+
p.log.success(`Removed ${deviceId.trim()} from ${groupId.trim()}`);
|
|
2378
|
+
p.outro("removed");
|
|
2379
|
+
}));
|
|
2380
|
+
coordinatorCommand.addCommand(new Command("serve").configureHelp(helpStyle).description("Run the coordinator relay HTTP server").option("--db <path>", "coordinator database path").option("--db-path <path>", "coordinator database path").option("--host <host>", "bind host", "127.0.0.1").option("--port <port>", "bind port", "7347").action(async (opts) => {
|
|
2191
2381
|
const host = String(opts.host ?? "127.0.0.1").trim() || "127.0.0.1";
|
|
2192
|
-
const port = Number.parseInt(String(opts.port ?? "
|
|
2382
|
+
const port = Number.parseInt(String(opts.port ?? "7347"), 10);
|
|
2193
2383
|
const dbPath = opts.db ?? opts.dbPath ?? DEFAULT_COORDINATOR_DB_PATH;
|
|
2194
2384
|
const app = createCoordinatorApp({ dbPath });
|
|
2195
2385
|
p.intro("codemem sync coordinator serve");
|
|
@@ -2222,6 +2412,7 @@ coordinatorCommand.addCommand(new Command("create-invite").configureHelp(helpSty
|
|
|
2222
2412
|
p.log.success(`Invite created for ${groupId}`);
|
|
2223
2413
|
if (typeof result.link === "string") p.log.message(`- link: ${result.link}`);
|
|
2224
2414
|
if (typeof result.encoded === "string") p.log.message(`- invite: ${result.encoded}`);
|
|
2415
|
+
for (const warning of Array.isArray(result.warnings) ? result.warnings : []) p.log.warn(String(warning));
|
|
2225
2416
|
p.outro("Invite ready");
|
|
2226
2417
|
}));
|
|
2227
2418
|
coordinatorCommand.addCommand(new Command("import-invite").configureHelp(helpStyle).description("Import a coordinator invite").argument("<invite>", "invite value or link").option("--db <path>", "database path").option("--db-path <path>", "database path").option("--keys-dir <path>", "keys directory").option("--config <path>", "config path").option("--json", "output as JSON").action(async (invite, opts) => {
|