@tinycloud/cli 1.3.0-beta.50 → 1.3.0-beta.52
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 +11 -0
- package/dist/courier-entry.js +79 -19
- package/dist/courier-entry.js.map +1 -1
- package/dist/index.js +2529 -2431
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +22660 -22292
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +2 -2
- package/skills/tc-cli/AUTH.md +3 -1
- package/skills/tc-cli/REFERENCE.md +2 -1
- package/skills/tc-cli/SKILL.md +2 -2
package/README.md
CHANGED
|
@@ -242,6 +242,17 @@ record (for example, restored from an older copy) refuses logout; switch the
|
|
|
242
242
|
default profile if needed, then run `tc profile delete <name>` and sign in
|
|
243
243
|
again.
|
|
244
244
|
|
|
245
|
+
`auth logout`, `auth rotate`, and replica-enabled `auth login` that replaces the
|
|
246
|
+
profile's share-history key check the sender share history first. If it records
|
|
247
|
+
unexpired shares, or cannot be read, the command refuses with exit 5
|
|
248
|
+
(`SHARE_HISTORY_UNEXPIRED` or `SHARE_HISTORY_UNAVAILABLE`) and changes nothing.
|
|
249
|
+
Pass `--force` to proceed anyway; `auth logout --revoke` also skips the check. A
|
|
250
|
+
plain re-login that keeps the key is not checked, and local-owner profiles are not
|
|
251
|
+
checked. `tc share history recover` moves an unreadable history file aside to
|
|
252
|
+
`share-history-v2.json.unreadable-<UTC timestamp>` and reports the new path. It
|
|
253
|
+
never deletes or overwrites a file. If the file exists but cannot be read,
|
|
254
|
+
recover exits 1 with `SHARE_HISTORY_RECOVER_FAILED` and moves nothing.
|
|
255
|
+
|
|
245
256
|
`tc auth grants list` labels the signed-in delegation CID `(current session)`.
|
|
246
257
|
`tc delegation revoke <cid>` still accepts any eligible exact CID, but when
|
|
247
258
|
`<cid>` is the current session it requires `--yes` or an interactive confirmation
|
package/dist/courier-entry.js
CHANGED
|
@@ -9070,8 +9070,9 @@ function loadPeers(path21, ownerUid2 = process.getuid?.()) {
|
|
|
9070
9070
|
const stat = fs14.fstatSync(fd);
|
|
9071
9071
|
if (!stat.isFile())
|
|
9072
9072
|
throw new CourierError("insecure-peers", `${path21} is not a regular file.`);
|
|
9073
|
-
if (ownerUid2 !== void 0 && stat.uid !== ownerUid2)
|
|
9074
|
-
throw new CourierError("insecure-peers", `${path21} is not owned by the
|
|
9073
|
+
if (ownerUid2 !== void 0 && stat.uid !== ownerUid2) {
|
|
9074
|
+
throw new CourierError("insecure-peers", `${path21} is not owned by the peers owner (uid ${ownerUid2}); pass --peers-owner or chown the file.`);
|
|
9075
|
+
}
|
|
9075
9076
|
if ((stat.mode & 18) !== 0) {
|
|
9076
9077
|
throw new CourierError("insecure-peers", `${path21} is writable by group or others; restrict it to the owner (chmod 600).`);
|
|
9077
9078
|
}
|
|
@@ -9093,6 +9094,53 @@ function loadPeers(path21, ownerUid2 = process.getuid?.()) {
|
|
|
9093
9094
|
function policyOf(peers, peerId) {
|
|
9094
9095
|
return Object.hasOwn(peers, peerId) ? peers[peerId] : { ...ABSENT };
|
|
9095
9096
|
}
|
|
9097
|
+
var UID_MAX = 4294967294;
|
|
9098
|
+
var PASSWD = "/etc/passwd";
|
|
9099
|
+
function uidOfName(name2, passwdPath) {
|
|
9100
|
+
let text;
|
|
9101
|
+
try {
|
|
9102
|
+
text = fs14.readFileSync(passwdPath, "utf8");
|
|
9103
|
+
} catch {
|
|
9104
|
+
throw new CourierError("usage", `cannot read ${passwdPath} to resolve "${name2}"; pass a numeric uid instead.`);
|
|
9105
|
+
}
|
|
9106
|
+
const uids = /* @__PURE__ */ new Set();
|
|
9107
|
+
for (const line of text.split("\n")) {
|
|
9108
|
+
if (line === "" || line.startsWith("#") || line.startsWith("+") || line.startsWith("-"))
|
|
9109
|
+
continue;
|
|
9110
|
+
const fields = line.split(":");
|
|
9111
|
+
if (fields.length !== 7 || !/^(0|[1-9]\d*)$/.test(fields[2]) || Number(fields[2]) > UID_MAX) {
|
|
9112
|
+
throw new CourierError("usage", `${passwdPath} has a malformed line; fix it or pass a numeric uid.`);
|
|
9113
|
+
}
|
|
9114
|
+
if (fields[0] === name2)
|
|
9115
|
+
uids.add(Number(fields[2]));
|
|
9116
|
+
}
|
|
9117
|
+
if (uids.size === 0)
|
|
9118
|
+
throw new CourierError("usage", `no account named "${name2}"; pass a numeric uid instead.`);
|
|
9119
|
+
if (uids.size > 1)
|
|
9120
|
+
throw new CourierError("usage", `"${name2}" maps to more than one uid in ${passwdPath}; pass a numeric uid.`);
|
|
9121
|
+
return [...uids][0];
|
|
9122
|
+
}
|
|
9123
|
+
function resolvePeersOwner(flag, env, passwdPath = PASSWD) {
|
|
9124
|
+
const value2 = flag ?? env;
|
|
9125
|
+
if (value2 === void 0) {
|
|
9126
|
+
const uid = process.getuid?.();
|
|
9127
|
+
if (uid === void 0)
|
|
9128
|
+
throw new CourierError("unsupported", "peers ownership needs a POSIX platform.");
|
|
9129
|
+
return uid;
|
|
9130
|
+
}
|
|
9131
|
+
if (value2 === "true")
|
|
9132
|
+
throw new CourierError("usage", "--peers-owner needs a user name or numeric uid.");
|
|
9133
|
+
if (/^(0|[1-9]\d*)$/.test(value2)) {
|
|
9134
|
+
const uid = Number(value2);
|
|
9135
|
+
if (uid > UID_MAX)
|
|
9136
|
+
throw new CourierError("usage", `peers owner uid ${value2} is out of range.`);
|
|
9137
|
+
return uid;
|
|
9138
|
+
}
|
|
9139
|
+
if (!/^[A-Za-z_][A-Za-z0-9._-]{0,31}$/.test(value2)) {
|
|
9140
|
+
throw new CourierError("usage", `peers owner "${value2}" is neither a user name nor a numeric uid.`);
|
|
9141
|
+
}
|
|
9142
|
+
return uidOfName(value2, passwdPath);
|
|
9143
|
+
}
|
|
9096
9144
|
|
|
9097
9145
|
// ../courier/dist/intake/queue.js
|
|
9098
9146
|
import fs15 from "fs";
|
|
@@ -9801,13 +9849,29 @@ function intervalSeconds(raw) {
|
|
|
9801
9849
|
throw new CourierError("usage", "--interval must be an integer from 5 to 3600");
|
|
9802
9850
|
return n;
|
|
9803
9851
|
}
|
|
9804
|
-
|
|
9852
|
+
function reloadPeers(peersFile, peersOwner, last, log) {
|
|
9853
|
+
try {
|
|
9854
|
+
return loadPeers(peersFile, peersOwner);
|
|
9855
|
+
} catch (error) {
|
|
9856
|
+
log("intake-error", { code: errInfo(error).code ?? "bad-peers" });
|
|
9857
|
+
return last;
|
|
9858
|
+
}
|
|
9859
|
+
}
|
|
9860
|
+
var defaultLoopDeps = {
|
|
9861
|
+
loadProfile,
|
|
9862
|
+
courierNode,
|
|
9863
|
+
reloadPeers,
|
|
9864
|
+
intakeCycle,
|
|
9865
|
+
sleep: (ms2) => new Promise((resolve) => setTimeout(resolve, ms2))
|
|
9866
|
+
};
|
|
9867
|
+
async function cmdIntake(args, deps = defaultLoopDeps) {
|
|
9805
9868
|
const base = path18.join(profileDir(), "intake");
|
|
9806
9869
|
const queueRoot = args.get("queue-dir") ?? path18.join(base, "queue");
|
|
9807
9870
|
const doneRoot = args.get("done-dir") ?? path18.join(base, "done");
|
|
9808
9871
|
const overflowRoot = args.get("overflow-dir") ?? path18.join(base, "overflow");
|
|
9809
9872
|
const stateDir = args.get("state-dir") ?? path18.join(base, "state");
|
|
9810
9873
|
const peersFile = args.get("peers-file") ?? path18.join(base, "peers.json");
|
|
9874
|
+
const peersOwner = resolvePeersOwner(args.get("peers-owner"), process.env.COURIER_PEERS_OWNER);
|
|
9811
9875
|
const once = args.has("once");
|
|
9812
9876
|
const intervalMs = intervalSeconds(args.get("interval")) * 1e3;
|
|
9813
9877
|
const log = jsonLineLog();
|
|
@@ -9824,18 +9888,14 @@ async function cmdIntake(args) {
|
|
|
9824
9888
|
return;
|
|
9825
9889
|
}
|
|
9826
9890
|
const state = readState(path18.join(stateDir, STATE_FILE));
|
|
9827
|
-
let peers = loadPeers(peersFile);
|
|
9891
|
+
let peers = loadPeers(peersFile, peersOwner);
|
|
9828
9892
|
log("intake-start", { once, intervalSeconds: intervalMs / 1e3 });
|
|
9829
9893
|
for (; ; ) {
|
|
9830
9894
|
try {
|
|
9831
|
-
const profile = loadProfile();
|
|
9832
|
-
const node = await courierNode(profile);
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
} catch (error) {
|
|
9836
|
-
log("intake-error", { code: errInfo(error).code ?? "bad-peers" });
|
|
9837
|
-
}
|
|
9838
|
-
const queued = await intakeCycle({
|
|
9895
|
+
const profile = deps.loadProfile();
|
|
9896
|
+
const node = await deps.courierNode(profile);
|
|
9897
|
+
peers = deps.reloadPeers(peersFile, peersOwner, peers, log);
|
|
9898
|
+
const queued = await deps.intakeCycle({
|
|
9839
9899
|
kv: node.kv,
|
|
9840
9900
|
profile,
|
|
9841
9901
|
peers,
|
|
@@ -9855,9 +9915,7 @@ async function cmdIntake(args) {
|
|
|
9855
9915
|
}
|
|
9856
9916
|
if (once)
|
|
9857
9917
|
return;
|
|
9858
|
-
await
|
|
9859
|
-
setTimeout(resolve, intervalMs);
|
|
9860
|
-
});
|
|
9918
|
+
await deps.sleep(intervalMs);
|
|
9861
9919
|
}
|
|
9862
9920
|
} finally {
|
|
9863
9921
|
release();
|
|
@@ -9896,7 +9954,7 @@ var Refusal = class extends Error {
|
|
|
9896
9954
|
}
|
|
9897
9955
|
};
|
|
9898
9956
|
var ADD_USAGE = `Usage: ${cmd("outbox add <peer> (--thread <id> | --new <subject>) [--text t | --file f | stdin] [--outbox-dir d]")}`;
|
|
9899
|
-
var SERVE_USAGE = `Usage: ${cmd("outbox serve --pipe <socket> [--outbox-dir d] [--socket-group g] [--peers-file f] [--sent-dir d] [--once] [--dry-run]")}`;
|
|
9957
|
+
var SERVE_USAGE = `Usage: ${cmd("outbox serve --pipe <socket> [--outbox-dir d] [--socket-group g] [--peers-file f] [--peers-owner u] [--sent-dir d] [--once] [--dry-run]")}`;
|
|
9900
9958
|
var OUTBOX_USAGE = `Usage: ${cmd("outbox add <peer> ... | outbox serve ...")}`;
|
|
9901
9959
|
function lstatOrUndefined(file) {
|
|
9902
9960
|
try {
|
|
@@ -9928,6 +9986,7 @@ var LOGGABLE_CODES = /* @__PURE__ */ new Set([
|
|
|
9928
9986
|
"ECONNRESET",
|
|
9929
9987
|
"bad-marker",
|
|
9930
9988
|
"bad-peers",
|
|
9989
|
+
"insecure-peers",
|
|
9931
9990
|
"socket-group-denied",
|
|
9932
9991
|
"unknown-group",
|
|
9933
9992
|
"unsafe-dir",
|
|
@@ -10230,7 +10289,7 @@ async function startOutbox(opts) {
|
|
|
10230
10289
|
let peersNow;
|
|
10231
10290
|
function currentPeers() {
|
|
10232
10291
|
try {
|
|
10233
|
-
peersNow = loadPeers(opts.peersFile);
|
|
10292
|
+
peersNow = loadPeers(opts.peersFile, opts.peersOwnerUid);
|
|
10234
10293
|
} catch (error) {
|
|
10235
10294
|
limited("outbox.peers-invalid", { code: safeCode(error) });
|
|
10236
10295
|
}
|
|
@@ -10396,6 +10455,7 @@ async function cmdOutboxServe(args) {
|
|
|
10396
10455
|
pipe: configValue(args, "pipe", "COURIER_OUTBOX_PIPE", SERVE_USAGE),
|
|
10397
10456
|
socketGroup: configValue(args, "socket-group", "COURIER_OUTBOX_SOCKET_GROUP", SERVE_USAGE, DEFAULT_SOCKET_GROUP),
|
|
10398
10457
|
peersFile: args.get("peers-file") ?? path19.join(base, "intake", "peers.json"),
|
|
10458
|
+
peersOwnerUid: resolvePeersOwner(args.get("peers-owner"), process.env.COURIER_PEERS_OWNER),
|
|
10399
10459
|
sentDir: args.get("sent-dir") ?? path19.join(base, "outbox-sent"),
|
|
10400
10460
|
once: args.has("once"),
|
|
10401
10461
|
dryRun: args.has("dry-run")
|
|
@@ -10995,14 +11055,14 @@ Delivery (one watcher per person per machine; never calls a model):
|
|
|
10995
11055
|
watch \u2026 --install-launchd | --install-systemd [--dry-run] [--name n]
|
|
10996
11056
|
Install the same watch command as a macOS launchd agent or a Linux
|
|
10997
11057
|
systemd --user unit. --dry-run prints the exact file and commands.
|
|
10998
|
-
intake [--queue-dir d] [--peers-file f] [--state-dir d] [--interval 60] [--once]
|
|
11058
|
+
intake [--queue-dir d] [--peers-file f] [--peers-owner u] [--state-dir d] [--interval 60] [--once]
|
|
10999
11059
|
Intake reader: copy paired peers' mirrored messages into the local queue
|
|
11000
11060
|
(one <msgId>.json per message) with cheap checks. Never calls a model.
|
|
11001
11061
|
outbox add <peer> (--thread <id> | --new <subject>) [--text t | --file f | stdin] [--outbox-dir d]
|
|
11002
11062
|
Queue one message for a paired peer as a single-use outbox item. Text limit:
|
|
11003
11063
|
2000 characters (8 KiB UTF-8); subject limit: 200 characters.
|
|
11004
11064
|
outbox serve --pipe <socket> [--outbox-dir d] [--socket-group courier-sender] [--peers-file f]
|
|
11005
|
-
[--sent-dir d] [--once] [--dry-run]
|
|
11065
|
+
[--peers-owner u] [--sent-dir d] [--once] [--dry-run]
|
|
11006
11066
|
Sender: accept one-line item requests over a Unix socket and send the
|
|
11007
11067
|
named outbox item. Replies are ok or refused:<code>.
|
|
11008
11068
|
|