@rigxyz/tapd 0.3.0 → 0.4.1
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/bin.js +236 -34
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-N3KRCOBG.js → chunk-JBX2ZRMD.js} +739 -190
- package/dist/chunk-JBX2ZRMD.js.map +1 -0
- package/dist/index.d.ts +318 -19
- package/dist/index.js +35 -3
- package/package.json +12 -12
- package/LICENSE +0 -21
- package/dist/chunk-N3KRCOBG.js.map +0 -1
package/dist/bin.js
CHANGED
|
@@ -2,23 +2,29 @@
|
|
|
2
2
|
import {
|
|
3
3
|
AlreadyInitializedError,
|
|
4
4
|
AlreadyJoinedError,
|
|
5
|
+
ImportTokenScopeError,
|
|
6
|
+
ImportValidationFailed,
|
|
5
7
|
InvalidInviteUrlError,
|
|
6
8
|
NotInitializedError,
|
|
7
9
|
RelayClient,
|
|
8
10
|
STATE_KEY_CONFLICT_COUNT,
|
|
11
|
+
addImport,
|
|
9
12
|
bindingConfigFile,
|
|
10
13
|
computeLocalDiff,
|
|
11
14
|
findConflictSidecars,
|
|
15
|
+
importStateDbPath,
|
|
12
16
|
init,
|
|
13
17
|
join,
|
|
14
18
|
list,
|
|
15
19
|
loadIgnore,
|
|
20
|
+
loadImportMounts,
|
|
16
21
|
mint,
|
|
17
22
|
openStateDb,
|
|
18
23
|
readBindingConfig,
|
|
24
|
+
removeImport,
|
|
19
25
|
revoke,
|
|
20
26
|
start
|
|
21
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-JBX2ZRMD.js";
|
|
22
28
|
|
|
23
29
|
// src/bin.ts
|
|
24
30
|
import { join as pathJoin, resolve as resolve2 } from "path";
|
|
@@ -272,7 +278,7 @@ import { existsSync as existsSync4 } from "fs";
|
|
|
272
278
|
import { join as join4 } from "path";
|
|
273
279
|
|
|
274
280
|
// src/version.ts
|
|
275
|
-
var TAPD_VERSION = "0.
|
|
281
|
+
var TAPD_VERSION = "0.4.1";
|
|
276
282
|
|
|
277
283
|
// src/status.ts
|
|
278
284
|
var STATE_KEY_LAST_APPLY_ERROR = "last_apply_error";
|
|
@@ -284,9 +290,9 @@ function statusToJson(report) {
|
|
|
284
290
|
return {
|
|
285
291
|
protocolVersion: STATUS_PROTOCOL_VERSION,
|
|
286
292
|
version: TAPD_VERSION,
|
|
287
|
-
bindingId: report.config
|
|
288
|
-
relayUrl: report.config
|
|
289
|
-
deviceId: report.config
|
|
293
|
+
bindingId: report.config?.bindingId ?? null,
|
|
294
|
+
relayUrl: report.config?.relayUrl ?? null,
|
|
295
|
+
deviceId: report.config?.deviceId ?? null,
|
|
290
296
|
daemon: report.daemon,
|
|
291
297
|
cursor: report.cursor,
|
|
292
298
|
remoteCursor: report.remoteCursor,
|
|
@@ -295,13 +301,65 @@ function statusToJson(report) {
|
|
|
295
301
|
pendingUploads: report.pendingUploads,
|
|
296
302
|
conflicts: report.conflicts,
|
|
297
303
|
lastApplyError: report.lastApplyError,
|
|
298
|
-
offline: report.offline
|
|
304
|
+
offline: report.offline,
|
|
305
|
+
mounts: report.mounts
|
|
299
306
|
};
|
|
300
307
|
}
|
|
308
|
+
function readMounts(rootDir) {
|
|
309
|
+
const loaded = loadImportMounts(rootDir);
|
|
310
|
+
const mounts = [];
|
|
311
|
+
const readyPaths = [];
|
|
312
|
+
for (const spec of loaded.ready) {
|
|
313
|
+
let cursor;
|
|
314
|
+
const dbPath = importStateDbPath(rootDir, spec.name);
|
|
315
|
+
if (existsSync4(dbPath)) {
|
|
316
|
+
const db = openStateDb(dbPath);
|
|
317
|
+
try {
|
|
318
|
+
cursor = db.getCursor();
|
|
319
|
+
} finally {
|
|
320
|
+
db.close();
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
mounts.push({
|
|
324
|
+
name: spec.name,
|
|
325
|
+
path: spec.path,
|
|
326
|
+
state: "ok",
|
|
327
|
+
bindingId: spec.bindingId,
|
|
328
|
+
...cursor ? { cursor } : {}
|
|
329
|
+
});
|
|
330
|
+
readyPaths.push(spec.path);
|
|
331
|
+
}
|
|
332
|
+
for (const b of loaded.broken) {
|
|
333
|
+
mounts.push({ name: b.name, path: b.path, state: "misconfigured", reason: b.reason });
|
|
334
|
+
}
|
|
335
|
+
mounts.sort((a, b) => a.name.localeCompare(b.name));
|
|
336
|
+
return { mounts, readyPaths };
|
|
337
|
+
}
|
|
301
338
|
async function status(opts) {
|
|
302
339
|
const config = readBindingConfig(opts.rootDir);
|
|
340
|
+
const { mounts, readyPaths } = readMounts(opts.rootDir);
|
|
341
|
+
const pidfilePath = join4(opts.rootDir, DAEMON_PIDFILE_REL);
|
|
342
|
+
const livePid = readLivePid(pidfilePath);
|
|
343
|
+
const daemon = livePid !== null ? { running: true, pid: livePid } : { running: false };
|
|
303
344
|
if (!config) {
|
|
304
|
-
|
|
345
|
+
if (mounts.length === 0) {
|
|
346
|
+
throw new NotInitializedError(opts.rootDir);
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
config: null,
|
|
350
|
+
cursor: "chg_0",
|
|
351
|
+
remoteCursor: "chg_0",
|
|
352
|
+
trackedPaths: 0,
|
|
353
|
+
pendingPeek: [],
|
|
354
|
+
pendingApplies: 0,
|
|
355
|
+
pendingUploads: 0,
|
|
356
|
+
conflicts: [],
|
|
357
|
+
conflictCount: 0,
|
|
358
|
+
lastApplyError: null,
|
|
359
|
+
daemon,
|
|
360
|
+
offline: false,
|
|
361
|
+
mounts
|
|
362
|
+
};
|
|
305
363
|
}
|
|
306
364
|
const db = openStateDb(join4(opts.rootDir, ".rig", "tap", "state.local.db"));
|
|
307
365
|
try {
|
|
@@ -314,9 +372,6 @@ async function status(opts) {
|
|
|
314
372
|
const lastErrJson = db.getMeta(STATE_KEY_LAST_APPLY_ERROR);
|
|
315
373
|
const lastErrAt = db.getMeta(STATE_KEY_LAST_APPLY_ERROR_AT);
|
|
316
374
|
const lastApplyError = lastErrJson ? safeParseLastError(lastErrJson, lastErrAt) : null;
|
|
317
|
-
const pidfilePath = join4(opts.rootDir, DAEMON_PIDFILE_REL);
|
|
318
|
-
const livePid = readLivePid(pidfilePath);
|
|
319
|
-
const daemon = livePid !== null ? { running: true, pid: livePid } : { running: false };
|
|
320
375
|
const client = new RelayClient({
|
|
321
376
|
baseUrl: config.relayUrl,
|
|
322
377
|
bindingId: config.bindingId,
|
|
@@ -341,7 +396,12 @@ async function status(opts) {
|
|
|
341
396
|
let pendingUploads = 0;
|
|
342
397
|
if (existsSync4(opts.rootDir)) {
|
|
343
398
|
const ignore = loadIgnore(opts.rootDir);
|
|
344
|
-
const diff = await computeLocalDiff({
|
|
399
|
+
const diff = await computeLocalDiff({
|
|
400
|
+
rootDir: opts.rootDir,
|
|
401
|
+
stateDb: db,
|
|
402
|
+
ignore,
|
|
403
|
+
excludePaths: readyPaths
|
|
404
|
+
});
|
|
345
405
|
pendingUploads = diff.writes.length + diff.deletes.length + diff.mkdirs.length + diff.rmdirs.length;
|
|
346
406
|
}
|
|
347
407
|
const conflicts = findConflictSidecars(opts.rootDir);
|
|
@@ -357,7 +417,8 @@ async function status(opts) {
|
|
|
357
417
|
conflictCount,
|
|
358
418
|
lastApplyError,
|
|
359
419
|
daemon,
|
|
360
|
-
offline
|
|
420
|
+
offline,
|
|
421
|
+
mounts
|
|
361
422
|
};
|
|
362
423
|
} finally {
|
|
363
424
|
db.close();
|
|
@@ -506,6 +567,7 @@ ${C.bold().cyan("COMMANDS")}
|
|
|
506
567
|
${C.green("tapd invite")} mint / list / revoke invite URLs (owner only)
|
|
507
568
|
${C.green("tapd members")} list / remove / set-role for binding members (owner only for write ops)
|
|
508
569
|
${C.green("tapd join")} accept an invite URL into the current directory
|
|
570
|
+
${C.green("tapd import")} mount another binding read-only into a subdir (meta-rig)
|
|
509
571
|
${C.green("tapd start")} run the watch + apply daemon (foreground)
|
|
510
572
|
${C.green("tapd status")} show binding + cursor + pending remote events
|
|
511
573
|
${C.green("tapd history")} list ChangeEvents for a single path (recovery)
|
|
@@ -892,6 +954,129 @@ async function runJoin(positional2, flags2) {
|
|
|
892
954
|
console.log("");
|
|
893
955
|
console.log(C.dim(" Next: `tapd start` (initial apply pulls the remote manifest)."));
|
|
894
956
|
}
|
|
957
|
+
async function runImport(positional2, flags2) {
|
|
958
|
+
const json = flags2.get("json") === true;
|
|
959
|
+
const rootDir = resolve2(flags2.get("dir") ?? process.cwd());
|
|
960
|
+
const sub = positional2[0];
|
|
961
|
+
if (sub === "remove") {
|
|
962
|
+
const name = positional2[1];
|
|
963
|
+
if (!name) {
|
|
964
|
+
if (json) printJsonError("missing_argument", "usage: tapd import remove <name>");
|
|
965
|
+
else console.error(kleur.red("error:"), "usage: tapd import remove <name>");
|
|
966
|
+
process.exit(2);
|
|
967
|
+
}
|
|
968
|
+
let accessToken2 = null;
|
|
969
|
+
try {
|
|
970
|
+
accessToken2 = readHubToken();
|
|
971
|
+
} catch {
|
|
972
|
+
accessToken2 = null;
|
|
973
|
+
}
|
|
974
|
+
const result2 = await removeImport({
|
|
975
|
+
rootDir,
|
|
976
|
+
name,
|
|
977
|
+
purge: flags2.get("purge") === true,
|
|
978
|
+
accessToken: accessToken2
|
|
979
|
+
});
|
|
980
|
+
if (json) {
|
|
981
|
+
printJson({ protocolVersion: PROTOCOL_VERSION, ...result2, name });
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
const C2 = kleur;
|
|
985
|
+
if (!result2.removed) {
|
|
986
|
+
console.log(C2.dim(`no import named "${name}"`));
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
console.log(`${C2.green("\u2713")} removed import ${C2.bold(name)}${result2.purged ? C2.dim(" (purged files)") : ""}`);
|
|
990
|
+
switch (result2.revoke) {
|
|
991
|
+
case "revoked":
|
|
992
|
+
console.log(C2.dim(" grant relinquished on the relay."));
|
|
993
|
+
break;
|
|
994
|
+
case "not-creator":
|
|
995
|
+
console.log(
|
|
996
|
+
C2.yellow(" note:") + " the grant belongs to another user and remains active \u2014 only its owner can revoke it."
|
|
997
|
+
);
|
|
998
|
+
break;
|
|
999
|
+
case "no-token":
|
|
1000
|
+
console.log(C2.yellow(" note:") + " not signed in \u2014 the grant was not revoked. Run `rig login`, then re-remove, to relinquish it.");
|
|
1001
|
+
break;
|
|
1002
|
+
case "offline":
|
|
1003
|
+
console.log(C2.yellow(" note:") + " relay unreachable \u2014 local mount removed, grant not yet revoked.");
|
|
1004
|
+
break;
|
|
1005
|
+
}
|
|
1006
|
+
console.log(C2.dim(" restart the daemon to apply: `tapd start`"));
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
const inviteUrl = sub;
|
|
1010
|
+
const mountPath = flags2.get("path");
|
|
1011
|
+
if (!inviteUrl || !mountPath) {
|
|
1012
|
+
const msg = "usage: tapd import <invite-url> --path <subdir> [--name <name>]";
|
|
1013
|
+
if (json) printJsonError("missing_argument", msg);
|
|
1014
|
+
else console.error(kleur.red("error:"), msg);
|
|
1015
|
+
process.exit(2);
|
|
1016
|
+
}
|
|
1017
|
+
const anonymous = flags2.get("anonymous") === true;
|
|
1018
|
+
let accessToken = null;
|
|
1019
|
+
if (!anonymous) {
|
|
1020
|
+
try {
|
|
1021
|
+
accessToken = readHubToken();
|
|
1022
|
+
} catch (err) {
|
|
1023
|
+
if (err instanceof HubTokenMissingError) {
|
|
1024
|
+
if (json) printJsonError("not_logged_in", err.message);
|
|
1025
|
+
else {
|
|
1026
|
+
console.error(kleur.red("error:"), err.message);
|
|
1027
|
+
console.error(kleur.dim(" pass --anonymous for a pure-capability invite, or run `rig login`."));
|
|
1028
|
+
}
|
|
1029
|
+
process.exit(1);
|
|
1030
|
+
}
|
|
1031
|
+
throw err;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
let result;
|
|
1035
|
+
try {
|
|
1036
|
+
result = await addImport({
|
|
1037
|
+
rootDir,
|
|
1038
|
+
inviteUrl,
|
|
1039
|
+
path: mountPath,
|
|
1040
|
+
name: flags2.get("name"),
|
|
1041
|
+
accessToken,
|
|
1042
|
+
deviceLabel: flags2.get("device-label")
|
|
1043
|
+
});
|
|
1044
|
+
} catch (err) {
|
|
1045
|
+
if (err instanceof ImportValidationFailed) {
|
|
1046
|
+
if (json) printJsonError("import_invalid", err.message);
|
|
1047
|
+
else console.error(kleur.red("error:"), err.message);
|
|
1048
|
+
process.exit(2);
|
|
1049
|
+
}
|
|
1050
|
+
if (err instanceof ImportTokenScopeError) {
|
|
1051
|
+
if (json) printJsonError("insufficient_ops", err.message);
|
|
1052
|
+
else console.error(kleur.red("error:"), err.message);
|
|
1053
|
+
process.exit(1);
|
|
1054
|
+
}
|
|
1055
|
+
if (err instanceof InvalidInviteUrlError) {
|
|
1056
|
+
if (json) printJsonError("invalid_invite_url", err.message);
|
|
1057
|
+
else console.error(kleur.red("error:"), err.message);
|
|
1058
|
+
process.exit(2);
|
|
1059
|
+
}
|
|
1060
|
+
throw err;
|
|
1061
|
+
}
|
|
1062
|
+
if (json) {
|
|
1063
|
+
printJson({ protocolVersion: PROTOCOL_VERSION, ...result });
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
const C = kleur;
|
|
1067
|
+
console.log(C.bold().cyan("tapd import"));
|
|
1068
|
+
console.log(` name: ${C.bold(result.name)}`);
|
|
1069
|
+
console.log(` mount: ${result.path} ${C.dim("(read-only mirror)")}`);
|
|
1070
|
+
console.log(` binding: ${result.bindingId}`);
|
|
1071
|
+
console.log(` ops: ${result.ops.join(", ")}`);
|
|
1072
|
+
console.log(C.dim(` Token stored in ${rootDir}/.rig/tap/imports/${result.name}.local.json (mode 0600).`));
|
|
1073
|
+
if (result.importerBindingVerified === false) {
|
|
1074
|
+
console.log(
|
|
1075
|
+
C.yellow(" note:") + " recorded as a personal import (you \u2192 source); the rig-level edge needs membership in this meta-rig."
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
console.log(C.dim(" Next: (re)start the daemon to begin mirroring \u2014 `tapd start`."));
|
|
1079
|
+
}
|
|
895
1080
|
async function runStatus(flags2) {
|
|
896
1081
|
const json = flags2.get("json") === true;
|
|
897
1082
|
const rootDir = resolve2(flags2.get("dir") ?? process.cwd());
|
|
@@ -914,31 +1099,45 @@ async function runStatus(flags2) {
|
|
|
914
1099
|
}
|
|
915
1100
|
const C = kleur;
|
|
916
1101
|
console.log(C.bold().cyan("tapd status"));
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
1102
|
+
if (report.config) {
|
|
1103
|
+
console.log(` binding: ${C.bold(report.config.bindingId)}`);
|
|
1104
|
+
console.log(` relay: ${report.config.relayUrl}`);
|
|
1105
|
+
console.log(` device: ${C.bold(report.config.deviceId)}`);
|
|
1106
|
+
console.log(` cursor: ${report.cursor}`);
|
|
1107
|
+
console.log(` tracked: ${report.trackedPaths} paths`);
|
|
1108
|
+
if (report.pendingPeek.length === 0) {
|
|
1109
|
+
console.log(` pending: ${C.green("up to date")}`);
|
|
1110
|
+
} else {
|
|
1111
|
+
console.log(` pending: ${C.yellow(`${report.pendingPeek.length}+ events behind`)}`);
|
|
1112
|
+
for (const ev of report.pendingPeek) {
|
|
1113
|
+
console.log(C.dim(` ${ev.op.padEnd(6)} ${ev.path} (${ev.id})`));
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
if (report.conflictCount > 0) {
|
|
1117
|
+
console.log(
|
|
1118
|
+
` conflicts: ${C.yellow(String(report.conflictCount))} ${C.dim("(sidecars at *.conflict-from.*)")}`
|
|
1119
|
+
);
|
|
1120
|
+
} else {
|
|
1121
|
+
console.log(` conflicts: ${C.dim("none")}`);
|
|
1122
|
+
}
|
|
1123
|
+
if (report.lastApplyError) {
|
|
1124
|
+
const e = report.lastApplyError;
|
|
1125
|
+
console.log(
|
|
1126
|
+
` last err: ${C.red(e.reason)} ${C.dim(`(${e.op} ${e.path} @ ${e.eventId}${e.at ? `, ${e.at}` : ""})`)}`
|
|
1127
|
+
);
|
|
928
1128
|
}
|
|
929
|
-
}
|
|
930
|
-
if (report.conflictCount > 0) {
|
|
931
|
-
console.log(
|
|
932
|
-
` conflicts: ${C.yellow(String(report.conflictCount))} ${C.dim("(sidecars at *.conflict-from.*)")}`
|
|
933
|
-
);
|
|
934
1129
|
} else {
|
|
935
|
-
console.log(
|
|
1130
|
+
console.log(C.dim(" (import-only meta-rig \u2014 no primary binding)"));
|
|
936
1131
|
}
|
|
937
|
-
if (report.
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
);
|
|
1132
|
+
if (report.mounts.length > 0) {
|
|
1133
|
+
console.log(` imports:`);
|
|
1134
|
+
for (const m of report.mounts) {
|
|
1135
|
+
if (m.state === "ok") {
|
|
1136
|
+
console.log(` ${C.green("\u25CF")} ${C.bold(m.name)} \u2192 ${m.path}${m.cursor ? C.dim(` (${m.cursor})`) : ""}`);
|
|
1137
|
+
} else {
|
|
1138
|
+
console.log(` ${C.yellow("\u25CB")} ${C.bold(m.name)} ${C.yellow("misconfigured")} ${C.dim(`(${m.reason ?? "unknown"})`)}`);
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
942
1141
|
}
|
|
943
1142
|
}
|
|
944
1143
|
async function runUninit(flags2) {
|
|
@@ -1269,6 +1468,9 @@ if (flags.get("version") === true || command === "-v") {
|
|
|
1269
1468
|
case "join":
|
|
1270
1469
|
await runJoin(positional, flags);
|
|
1271
1470
|
return;
|
|
1471
|
+
case "import":
|
|
1472
|
+
await runImport(positional, flags);
|
|
1473
|
+
return;
|
|
1272
1474
|
case "start":
|
|
1273
1475
|
await runStart(flags);
|
|
1274
1476
|
return;
|