@tinycloud/cli 1.3.0-beta.24 → 1.3.0-beta.26
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/index.js +20 -1
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +266 -152
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +7 -7
package/dist/legacy-entry.js
CHANGED
|
@@ -3052,6 +3052,16 @@ var init_delegation_expiry = __esm({
|
|
|
3052
3052
|
}
|
|
3053
3053
|
});
|
|
3054
3054
|
|
|
3055
|
+
// src/lib/retry-policy.ts
|
|
3056
|
+
function cliRetryPolicy() {
|
|
3057
|
+
return process.env.TINYCLOUD_RETRY === "0" ? { retryPolicy: { maxAttempts: 1 } } : {};
|
|
3058
|
+
}
|
|
3059
|
+
var init_retry_policy = __esm({
|
|
3060
|
+
"src/lib/retry-policy.ts"() {
|
|
3061
|
+
"use strict";
|
|
3062
|
+
}
|
|
3063
|
+
});
|
|
3064
|
+
|
|
3055
3065
|
// src/lib/replication-log.ts
|
|
3056
3066
|
import { CLI_NAME as CLI_NAME5 } from "@tinycloud/operations/cli-name";
|
|
3057
3067
|
import { appendFileSync, chmodSync, mkdirSync, renameSync, statSync, writeFileSync } from "fs";
|
|
@@ -9081,6 +9091,7 @@ async function storedSessionVerifiesLocally(profileName2) {
|
|
|
9081
9091
|
if (!session || typeof session.delegationCid !== "string" || typeof session.spaceId !== "string" || session.delegationHeader === null || typeof session.delegationHeader !== "object" || !("Authorization" in session.delegationHeader) || typeof session.delegationHeader.Authorization !== "string") return true;
|
|
9082
9092
|
const key = await ProfileManager.getKey(profileName2);
|
|
9083
9093
|
const node = new TinyCloudNode({
|
|
9094
|
+
...cliRetryPolicy(),
|
|
9084
9095
|
host: profile.host ?? DEFAULT_HOST,
|
|
9085
9096
|
...local ? { privateKey: profile.privateKey } : {}
|
|
9086
9097
|
});
|
|
@@ -9126,6 +9137,7 @@ async function createSDKInstance(ctx, options) {
|
|
|
9126
9137
|
process.stderr.write("[replication] Enabled, but this profile has no configured prefixes; reads use the network.\n");
|
|
9127
9138
|
}
|
|
9128
9139
|
const node2 = new TinyCloudNode({
|
|
9140
|
+
...cliRetryPolicy(),
|
|
9129
9141
|
host: ctx.host,
|
|
9130
9142
|
privateKey: effectivePrivateKey,
|
|
9131
9143
|
...replication2 ? { replication: replication2 } : {}
|
|
@@ -9166,6 +9178,7 @@ async function createSDKInstance(ctx, options) {
|
|
|
9166
9178
|
process.stderr.write("[replication] Enabled, but this profile has no configured prefixes; reads use the network.\n");
|
|
9167
9179
|
}
|
|
9168
9180
|
const node = new TinyCloudNode({
|
|
9181
|
+
...cliRetryPolicy(),
|
|
9169
9182
|
host: ctx.host,
|
|
9170
9183
|
privateKey: options?.privateKey,
|
|
9171
9184
|
...replication ? { replication } : {}
|
|
@@ -9329,6 +9342,7 @@ var init_sdk = __esm({
|
|
|
9329
9342
|
init_session_expired();
|
|
9330
9343
|
init_warnings();
|
|
9331
9344
|
init_delegation_expiry();
|
|
9345
|
+
init_retry_policy();
|
|
9332
9346
|
init_replication_config();
|
|
9333
9347
|
init_replication_registry();
|
|
9334
9348
|
init_logout_pending();
|
|
@@ -9786,18 +9800,89 @@ var init_profiles = __esm({
|
|
|
9786
9800
|
// src/commands/account.ts
|
|
9787
9801
|
init_profiles();
|
|
9788
9802
|
init_constants();
|
|
9803
|
+
import open6 from "open";
|
|
9804
|
+
import { readFile as readFile6 } from "fs/promises";
|
|
9805
|
+
|
|
9806
|
+
// src/lib/account-access.ts
|
|
9807
|
+
init_constants();
|
|
9808
|
+
init_errors();
|
|
9809
|
+
init_profiles();
|
|
9810
|
+
init_permissions();
|
|
9811
|
+
import { ACCOUNT_MANIFEST_PERMISSIONS } from "@tinycloud/sdk-core";
|
|
9812
|
+
import { CLI_NAME as CLI_NAME10 } from "@tinycloud/operations/cli-name";
|
|
9813
|
+
var PRIMARY_SPACE = "default";
|
|
9814
|
+
function accountPermissionEntries(need = "registry") {
|
|
9815
|
+
const entries = ACCOUNT_MANIFEST_PERMISSIONS.map((resource) => ({
|
|
9816
|
+
service: resource.service,
|
|
9817
|
+
space: resource.space,
|
|
9818
|
+
path: resource.path,
|
|
9819
|
+
actions: [...resource.actions]
|
|
9820
|
+
}));
|
|
9821
|
+
if (need === "remove") {
|
|
9822
|
+
for (const path of ["applications/", "spaces/"]) {
|
|
9823
|
+
entries.push({ service: "tinycloud.kv", space: "account", path, actions: ["tinycloud.kv/del"] });
|
|
9824
|
+
}
|
|
9825
|
+
}
|
|
9826
|
+
if (need === "spaces" || need === "delegations") {
|
|
9827
|
+
entries.push({ service: "tinycloud.space", space: PRIMARY_SPACE, path: "", actions: ["tinycloud.space/list"] });
|
|
9828
|
+
}
|
|
9829
|
+
if (need === "delegations") {
|
|
9830
|
+
entries.push(capabilitiesRead(PRIMARY_SPACE));
|
|
9831
|
+
}
|
|
9832
|
+
return entries;
|
|
9833
|
+
}
|
|
9834
|
+
function capabilitiesRead(space) {
|
|
9835
|
+
return { service: "tinycloud.capabilities", space, path: "", actions: ["tinycloud.capabilities/read"] };
|
|
9836
|
+
}
|
|
9837
|
+
function capSpecs(entries) {
|
|
9838
|
+
return entries.map((entry) => {
|
|
9839
|
+
const actions = entry.actions.map((action) => action.slice(action.lastIndexOf("/") + 1));
|
|
9840
|
+
return `${entry.service}:${entry.space}:${entry.path}:${actions.join(",")}`;
|
|
9841
|
+
});
|
|
9842
|
+
}
|
|
9843
|
+
function shellWord(value) {
|
|
9844
|
+
return /^[A-Za-z0-9._@:+-]+$/.test(value) ? value : `'${value.replace(/'/g, `'\\''`)}'`;
|
|
9845
|
+
}
|
|
9846
|
+
function accessRequired(profile, entries, what) {
|
|
9847
|
+
const grant = `${CLI_NAME10} --profile ${shellWord(profile)} auth request --grant ` + capSpecs(entries).map((spec) => `--cap '${spec}'`).join(" ");
|
|
9848
|
+
const approvals = new Set(entries.map((entry) => entry.space)).size;
|
|
9849
|
+
return new CLIError(
|
|
9850
|
+
"ACCOUNT_ACCESS_REQUIRED",
|
|
9851
|
+
`This session lacks account permissions this command needs (${what}). A plain OpenKey login grants only the \`default\` space.`,
|
|
9852
|
+
ExitCode.PERMISSION_DENIED,
|
|
9853
|
+
{
|
|
9854
|
+
hint: `Grant account access (${approvals} OpenKey approval${approvals === 1 ? "" : "s"}), then retry: ${grant}
|
|
9855
|
+
If the browser cannot reach the CLI, re-run with --paste: ${grant} --paste`
|
|
9856
|
+
}
|
|
9857
|
+
);
|
|
9858
|
+
}
|
|
9859
|
+
async function assertAccountAccess(node, profile, need = "registry", cid) {
|
|
9860
|
+
if (need === "revoke") {
|
|
9861
|
+
if ((await ProfileManager.getProfile(profile)).authMethod === "local") return;
|
|
9862
|
+
throw new CLIError(
|
|
9863
|
+
"ACCOUNT_REVOKE_UNSUPPORTED",
|
|
9864
|
+
"This profile signs through OpenKey, which cannot sign the raw-CID revocation `account delegations revoke` needs.",
|
|
9865
|
+
ExitCode.PERMISSION_DENIED,
|
|
9866
|
+
{ hint: `Revoke it with: ${CLI_NAME10} --profile ${shellWord(profile)} delegation revoke ${cid ?? "<cid>"}` }
|
|
9867
|
+
);
|
|
9868
|
+
}
|
|
9869
|
+
const entries = accountPermissionEntries(need);
|
|
9870
|
+
const required = await resolvePermissionSpaces(entries, profile);
|
|
9871
|
+
if (node.hasRuntimePermissions(required)) return;
|
|
9872
|
+
throw accessRequired(profile, entries, need === "registry" ? "kv applications/ and spaces/, sql account, delegation list, capabilities read on `account`" : `the \`account\` registry set plus ${need === "remove" ? "kv del on applications/ and spaces/" : "space list and capabilities read on `default`"}`);
|
|
9873
|
+
}
|
|
9874
|
+
|
|
9875
|
+
// src/commands/account.ts
|
|
9789
9876
|
init_sdk();
|
|
9790
9877
|
init_errors();
|
|
9791
9878
|
init_formatter();
|
|
9792
9879
|
init_theme();
|
|
9793
|
-
import open6 from "open";
|
|
9794
|
-
import { readFile as readFile6 } from "fs/promises";
|
|
9795
9880
|
var ACCOUNT_BILLING_URL = "https://account.tinycloud.xyz/billing";
|
|
9796
9881
|
function registerAccountCommand(program) {
|
|
9797
9882
|
const account = program.command("account").description("Account applications, spaces, delegations, and billing");
|
|
9798
9883
|
account.command("status").description("Show account status").action(async (_options, cmd) => {
|
|
9799
9884
|
try {
|
|
9800
|
-
const node = await authenticatedNode(cmd);
|
|
9885
|
+
const node = await authenticatedNode(cmd, "delegations");
|
|
9801
9886
|
const status = await node.account.status();
|
|
9802
9887
|
assertOk(status);
|
|
9803
9888
|
outputJson(status.data);
|
|
@@ -9839,7 +9924,7 @@ function registerAccountCommand(program) {
|
|
|
9839
9924
|
const spaces = account.command("spaces").description("Manage account space registry");
|
|
9840
9925
|
spaces.command("list").description("List spaces registered under account/spaces").option("--live", "Read canonical account KV records instead of the SQLite index").action(async (options, cmd) => {
|
|
9841
9926
|
try {
|
|
9842
|
-
const node = await authenticatedNode(cmd);
|
|
9927
|
+
const node = await authenticatedNode(cmd, "spaces");
|
|
9843
9928
|
const result = options.live ? await node.account.spaces.list() : await node.account.spaces.list({ preferIndex: true });
|
|
9844
9929
|
assertOk(result);
|
|
9845
9930
|
const payload = { spaces: result.data.map(formatSpace), count: result.data.length };
|
|
@@ -9896,7 +9981,7 @@ function registerAccountCommand(program) {
|
|
|
9896
9981
|
});
|
|
9897
9982
|
spaces.command("sync").description("Register currently accessible spaces into account/spaces").action(async (_options, cmd) => {
|
|
9898
9983
|
try {
|
|
9899
|
-
const node = await authenticatedNode(cmd);
|
|
9984
|
+
const node = await authenticatedNode(cmd, "spaces");
|
|
9900
9985
|
const result = await node.account.spaces.syncAccessible();
|
|
9901
9986
|
assertOk(result);
|
|
9902
9987
|
outputJson({ spaces: result.data.map(formatSpace), count: result.data.length, synced: true });
|
|
@@ -9906,7 +9991,7 @@ function registerAccountCommand(program) {
|
|
|
9906
9991
|
});
|
|
9907
9992
|
spaces.command("remove <space-id>").alias("delete").description("Remove a space registry entry").action(async (spaceId, _options, cmd) => {
|
|
9908
9993
|
try {
|
|
9909
|
-
const node = await authenticatedNode(cmd);
|
|
9994
|
+
const node = await authenticatedNode(cmd, "remove");
|
|
9910
9995
|
const result = await node.account.spaces.remove(spaceId);
|
|
9911
9996
|
assertOk(result);
|
|
9912
9997
|
outputJson({ spaceId, removed: true });
|
|
@@ -9937,7 +10022,7 @@ function registerAccountCommand(program) {
|
|
|
9937
10022
|
});
|
|
9938
10023
|
apps.command("remove <app-id>").alias("delete").description("Remove an application registry entry").action(async (appId, _options, cmd) => {
|
|
9939
10024
|
try {
|
|
9940
|
-
const node = await authenticatedNode(cmd);
|
|
10025
|
+
const node = await authenticatedNode(cmd, "remove");
|
|
9941
10026
|
const result = await node.account.applications.remove(appId);
|
|
9942
10027
|
assertOk(result);
|
|
9943
10028
|
outputJson({ appId, removed: true });
|
|
@@ -9951,7 +10036,7 @@ function registerAccountCommand(program) {
|
|
|
9951
10036
|
if (options.granted && options.received) {
|
|
9952
10037
|
throw new CLIError("USAGE_ERROR", "Use only one of --granted or --received.", ExitCode.USAGE_ERROR);
|
|
9953
10038
|
}
|
|
9954
|
-
const node = await authenticatedNode(cmd);
|
|
10039
|
+
const node = await authenticatedNode(cmd, "delegations");
|
|
9955
10040
|
const direction = options.granted ? "granted" : options.received ? "received" : "all";
|
|
9956
10041
|
const result = options.live ? await node.account.delegations.list({ direction, space: options.space }) : await node.account.delegations.list({ direction, space: options.space, preferIndex: true });
|
|
9957
10042
|
assertOk(result);
|
|
@@ -9983,7 +10068,7 @@ function registerAccountCommand(program) {
|
|
|
9983
10068
|
});
|
|
9984
10069
|
delegations.command("revoke <cid>").description("Revoke an active delegation granted by this account").requiredOption("--space <space>", "Space name or ID containing the delegation").action(async (cid, options, cmd) => {
|
|
9985
10070
|
try {
|
|
9986
|
-
const node = await authenticatedNode(cmd);
|
|
10071
|
+
const node = await authenticatedNode(cmd, "revoke", cid);
|
|
9987
10072
|
const result = await node.account.delegations.revoke({ cid, space: options.space });
|
|
9988
10073
|
assertOk(result);
|
|
9989
10074
|
outputJson({ cid, space: options.space, revoked: true });
|
|
@@ -10014,7 +10099,7 @@ function registerAccountCommand(program) {
|
|
|
10014
10099
|
});
|
|
10015
10100
|
index.command("rebuild").description("Rebuild account SQLite index from canonical account data").action(async (_options, cmd) => {
|
|
10016
10101
|
try {
|
|
10017
|
-
const node = await authenticatedNode(cmd);
|
|
10102
|
+
const node = await authenticatedNode(cmd, "delegations");
|
|
10018
10103
|
const result = await node.account.index.rebuild();
|
|
10019
10104
|
assertOk(result);
|
|
10020
10105
|
outputJson(result.data);
|
|
@@ -10047,10 +10132,12 @@ function registerAccountCommand(program) {
|
|
|
10047
10132
|
});
|
|
10048
10133
|
}
|
|
10049
10134
|
}
|
|
10050
|
-
async function authenticatedNode(cmd) {
|
|
10135
|
+
async function authenticatedNode(cmd, need = "registry", cid) {
|
|
10051
10136
|
const globalOpts = cmd.optsWithGlobals();
|
|
10052
10137
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
10053
|
-
|
|
10138
|
+
const node = await ensureAuthenticated(ctx);
|
|
10139
|
+
await assertAccountAccess(node, ctx.profile, need, cid);
|
|
10140
|
+
return node;
|
|
10054
10141
|
}
|
|
10055
10142
|
function assertOk(result) {
|
|
10056
10143
|
if (!result.ok) {
|
|
@@ -10107,7 +10194,7 @@ function formatSpace(space) {
|
|
|
10107
10194
|
init_profile_gate();
|
|
10108
10195
|
init_dist();
|
|
10109
10196
|
init_profiles();
|
|
10110
|
-
import { CLI_NAME as
|
|
10197
|
+
import { CLI_NAME as CLI_NAME15 } from "@tinycloud/operations/cli-name";
|
|
10111
10198
|
import { get as httpGet } from "http";
|
|
10112
10199
|
import { get as httpsGet } from "https";
|
|
10113
10200
|
import { spawn } from "child_process";
|
|
@@ -11067,14 +11154,14 @@ init_space();
|
|
|
11067
11154
|
import { lstat as lstat6 } from "fs/promises";
|
|
11068
11155
|
import { join as join12 } from "path";
|
|
11069
11156
|
import { REPLICA_LOGOUT_PENDING_FILE as REPLICA_LOGOUT_PENDING_FILE3 } from "@tinycloud/node-sdk";
|
|
11070
|
-
import { CLI_NAME as
|
|
11157
|
+
import { CLI_NAME as CLI_NAME11 } from "@tinycloud/operations/cli-name";
|
|
11071
11158
|
import { isLocalOwnerProfile as isLocalOwnerProfile3, ownerSessionLacksProof as ownerSessionLacksProof3 } from "@tinycloud/operations/profile";
|
|
11072
11159
|
import { ProfileLockTimeoutError as ProfileLockTimeoutError2, profilePath as profilePath7 } from "@tinycloud/operations/state";
|
|
11073
11160
|
function assertNotLocalOwner(profileName2, profile, flow) {
|
|
11074
11161
|
if (profile === null || !isLocalOwnerProfile3(profile)) return;
|
|
11075
11162
|
throw new CLIError(
|
|
11076
11163
|
"LOCAL_OWNER_PROFILE",
|
|
11077
|
-
`Profile "${profileName2}" holds a local owner key. ${flow} would turn it into an OpenKey profile while keeping that key. Use a separate profile: \`${
|
|
11164
|
+
`Profile "${profileName2}" holds a local owner key. ${flow} would turn it into an OpenKey profile while keeping that key. Use a separate profile: \`${CLI_NAME11} init --name publisher --key-only\`, then \`${CLI_NAME11} --profile publisher auth login --device --manifest ...\`.`,
|
|
11078
11165
|
ExitCode.USAGE_ERROR
|
|
11079
11166
|
);
|
|
11080
11167
|
}
|
|
@@ -11109,7 +11196,7 @@ function assertSessionReplaceable(profileName2, snapshot, ownerDid, scope, newEx
|
|
|
11109
11196
|
if (problem !== void 0) {
|
|
11110
11197
|
throw new CLIError(
|
|
11111
11198
|
"PROFILE_STATE_INCONSISTENT",
|
|
11112
|
-
`Profile "${profileName2}" is inconsistent (${problem}), possibly from an interrupted write. Nothing was saved. Check \`${
|
|
11199
|
+
`Profile "${profileName2}" is inconsistent (${problem}), possibly from an interrupted write. Nothing was saved. Check \`${CLI_NAME11} --profile ${profileName2} context\`, then pass --replace-session to replace this state, or use a new profile.`,
|
|
11113
11200
|
ExitCode.ERROR
|
|
11114
11201
|
);
|
|
11115
11202
|
}
|
|
@@ -11125,7 +11212,7 @@ function assertSessionReplaceable(profileName2, snapshot, ownerDid, scope, newEx
|
|
|
11125
11212
|
const space = typeof session.spaceId === "string" ? session.spaceId : "an unknown space";
|
|
11126
11213
|
throw new CLIError(
|
|
11127
11214
|
"SESSION_IN_USE",
|
|
11128
|
-
`Profile "${profileName2}" has a live session for ${space}${expiresAt ? ` until ${expiresAt}` : ""} that this login would ${keepsScope ? "shorten" : "narrow or replace"}, dropping that authority. Nothing was saved. Keep the user's existing profiles: use a new profile name (\`${
|
|
11215
|
+
`Profile "${profileName2}" has a live session for ${space}${expiresAt ? ` until ${expiresAt}` : ""} that this login would ${keepsScope ? "shorten" : "narrow or replace"}, dropping that authority. Nothing was saved. Keep the user's existing profiles: use a new profile name (\`${CLI_NAME11} init --name publisher --key-only\`, then \`${CLI_NAME11} --profile publisher auth login --device --manifest ...\`), or pass --replace-session to replace this session.`,
|
|
11129
11216
|
ExitCode.USAGE_ERROR
|
|
11130
11217
|
);
|
|
11131
11218
|
}
|
|
@@ -11159,7 +11246,7 @@ async function commitLogin(profileName2, snapshot, commit) {
|
|
|
11159
11246
|
if (canonicalJson(current.profile) !== canonicalJson(snapshot.profile) || canonicalJson(current.key) !== canonicalJson(snapshot.key) || canonicalJson(current.session) !== canonicalJson(snapshot.session)) {
|
|
11160
11247
|
throw new CLIError(
|
|
11161
11248
|
"PROFILE_CHANGED_DURING_LOGIN",
|
|
11162
|
-
`Profile "${profileName2}" changed while this login was in progress (another login, key rotation or logout). Nothing was saved; check \`${
|
|
11249
|
+
`Profile "${profileName2}" changed while this login was in progress (another login, key rotation or logout). Nothing was saved; check \`${CLI_NAME11} --profile ${profileName2} context\` and run the login again if it is still needed.`,
|
|
11163
11250
|
ExitCode.ERROR
|
|
11164
11251
|
);
|
|
11165
11252
|
}
|
|
@@ -11191,7 +11278,7 @@ async function commitLogin(profileName2, snapshot, commit) {
|
|
|
11191
11278
|
if (failures.length === 0) throw error;
|
|
11192
11279
|
throw new CLIError(
|
|
11193
11280
|
"PROFILE_STATE_INCONSISTENT",
|
|
11194
|
-
`Saving the login for profile "${profileName2}" failed (${errorMessage(error)}), and restoring its previous state failed too (${failures.map(errorMessage).join("; ")}). The profile's key, session and settings may not match. Check \`${
|
|
11281
|
+
`Saving the login for profile "${profileName2}" failed (${errorMessage(error)}), and restoring its previous state failed too (${failures.map(errorMessage).join("; ")}). The profile's key, session and settings may not match. Check \`${CLI_NAME11} --profile ${profileName2} context\`, then run the login again with --replace-session, or use a new profile.`,
|
|
11195
11282
|
ExitCode.ERROR
|
|
11196
11283
|
);
|
|
11197
11284
|
}
|
|
@@ -11207,7 +11294,7 @@ async function commitLogin(profileName2, snapshot, commit) {
|
|
|
11207
11294
|
|
|
11208
11295
|
// src/auth/owner-key.ts
|
|
11209
11296
|
init_theme();
|
|
11210
|
-
import { CLI_NAME as
|
|
11297
|
+
import { CLI_NAME as CLI_NAME12 } from "@tinycloud/operations/cli-name";
|
|
11211
11298
|
function openKeyPrimaryFlag(delegation) {
|
|
11212
11299
|
return typeof delegation.primary === "boolean" ? delegation.primary : void 0;
|
|
11213
11300
|
}
|
|
@@ -11218,7 +11305,7 @@ function withOwnerKeyPrimary(profile, primary) {
|
|
|
11218
11305
|
function nonPrimaryKeyWarning(ownerDid) {
|
|
11219
11306
|
const address = ownerDid.split(":")[4] ?? ownerDid;
|
|
11220
11307
|
return `${theme.warn("Warning:")} you signed in with OpenKey key ${address} (${ownerDid}), which is not your account's primary OpenKey key. This key is a separate owner with its own spaces and data, so this profile does not see the data stored under your primary key.
|
|
11221
|
-
To use your primary key, log in again and choose the primary key in OpenKey, for example into a new profile: ${
|
|
11308
|
+
To use your primary key, log in again and choose the primary key in OpenKey, for example into a new profile: ${CLI_NAME12} init --name <profile>
|
|
11222
11309
|
`;
|
|
11223
11310
|
}
|
|
11224
11311
|
function warnIfNotPrimaryKey(ownerDid, primary) {
|
|
@@ -11227,7 +11314,7 @@ function warnIfNotPrimaryKey(ownerDid, primary) {
|
|
|
11227
11314
|
}
|
|
11228
11315
|
function announceFirstOwner(profileName2, expectedOwner, ownerDid) {
|
|
11229
11316
|
if (expectedOwner !== void 0 || ownerDid === void 0) return;
|
|
11230
|
-
process.stderr.write(`Profile "${profileName2}" now belongs to ${ownerDid}. If that isn't your OpenKey account, stop using this profile and create a new one with ${
|
|
11317
|
+
process.stderr.write(`Profile "${profileName2}" now belongs to ${ownerDid}. If that isn't your OpenKey account, stop using this profile and create a new one with ${CLI_NAME12} init.
|
|
11231
11318
|
`);
|
|
11232
11319
|
}
|
|
11233
11320
|
function ownerLoginPermissions(owner) {
|
|
@@ -11929,7 +12016,7 @@ init_types();
|
|
|
11929
12016
|
init_constants();
|
|
11930
12017
|
init_permissions();
|
|
11931
12018
|
init_sdk();
|
|
11932
|
-
import { CLI_NAME as
|
|
12019
|
+
import { CLI_NAME as CLI_NAME14 } from "@tinycloud/operations/cli-name";
|
|
11933
12020
|
import { TinyCloudNode as TinyCloudNode2 } from "@tinycloud/node-sdk";
|
|
11934
12021
|
import { DelegationRequestBindingSchema, PermissionEntrySchema } from "@tinycloud/operations/artifacts";
|
|
11935
12022
|
import {
|
|
@@ -11944,7 +12031,7 @@ import {
|
|
|
11944
12031
|
// src/lib/revoke-delegation.ts
|
|
11945
12032
|
init_constants();
|
|
11946
12033
|
init_errors();
|
|
11947
|
-
import { CLI_NAME as
|
|
12034
|
+
import { CLI_NAME as CLI_NAME13 } from "@tinycloud/operations/cli-name";
|
|
11948
12035
|
import { parseSignedCompactUcanAttenuation as parseSignedCompactUcanAttenuation2 } from "@tinycloud/sdk-core";
|
|
11949
12036
|
import { inspectStoredDelegation, withoutBearer as withoutBearer2 } from "@tinycloud/operations/delegation-binding";
|
|
11950
12037
|
init_permissions();
|
|
@@ -12130,7 +12217,7 @@ async function revokeDelegationOnNode(params) {
|
|
|
12130
12217
|
"REVOKE_UNAUTHORIZED",
|
|
12131
12218
|
"The node rejected this revocation: Unauthorized Revoker.",
|
|
12132
12219
|
ExitCode.PERMISSION_DENIED,
|
|
12133
|
-
{ status: 403, hint: `Use a different profile belonging to the delegation's grantor, its recipient, or the owner of a space it covers; the rejected profile was "${ctx.profile}" (DID ${profileDid}). Retry with \`${
|
|
12220
|
+
{ status: 403, hint: `Use a different profile belonging to the delegation's grantor, its recipient, or the owner of a space it covers; the rejected profile was "${ctx.profile}" (DID ${profileDid}). Retry with \`${CLI_NAME13} --profile <profile-name> delegation revoke ${cid}\`.` }
|
|
12134
12221
|
);
|
|
12135
12222
|
}
|
|
12136
12223
|
throw cliErrorFromService(result.error);
|
|
@@ -12213,7 +12300,7 @@ function renderList(result) {
|
|
|
12213
12300
|
}
|
|
12214
12301
|
process.stdout.write(`Profile: ${result.profile}
|
|
12215
12302
|
`);
|
|
12216
|
-
process.stdout.write(`Session CID: ${result.session?.cid ?? "unavailable"}` + (result.session?.cid ? ` (current session; revoke with ${
|
|
12303
|
+
process.stdout.write(`Session CID: ${result.session?.cid ?? "unavailable"}` + (result.session?.cid ? ` (current session; revoke with ${CLI_NAME14} delegation revoke ${result.session.cid})
|
|
12217
12304
|
` : "\n"));
|
|
12218
12305
|
process.stdout.write(`Local verification: ${result.localVerification}; node validation and revocation are not checked.
|
|
12219
12306
|
`);
|
|
@@ -12311,7 +12398,7 @@ function registerAuthGrantsCommand(auth, options = {}) {
|
|
|
12311
12398
|
ExitCode.ERROR,
|
|
12312
12399
|
{
|
|
12313
12400
|
causeCode: cause.code,
|
|
12314
|
-
hint: `Do not use --revoke again. After fixing local profile storage, run: ${
|
|
12401
|
+
hint: `Do not use --revoke again. After fixing local profile storage, run: ${CLI_NAME14} --profile ${ctx.profile} auth grants remove ${cid}`
|
|
12315
12402
|
}
|
|
12316
12403
|
);
|
|
12317
12404
|
}
|
|
@@ -12356,6 +12443,19 @@ init_delegation_expiry();
|
|
|
12356
12443
|
init_space();
|
|
12357
12444
|
init_permissions();
|
|
12358
12445
|
|
|
12446
|
+
// src/lib/approval-notices.ts
|
|
12447
|
+
var spaceLabel = (group) => {
|
|
12448
|
+
const space = group[0]?.space ?? "";
|
|
12449
|
+
return `${space.split(":").pop() || space} space`;
|
|
12450
|
+
};
|
|
12451
|
+
function approvalNotices(groups) {
|
|
12452
|
+
if (groups.length < 2) return void 0;
|
|
12453
|
+
return {
|
|
12454
|
+
intro: `This needs ${groups.length} approvals in OpenKey (${groups.map(spaceLabel).join(", then ")}).`,
|
|
12455
|
+
prompts: groups.map((group, index) => `Approval ${index + 1} of ${groups.length}: ${spaceLabel(group)}...`)
|
|
12456
|
+
};
|
|
12457
|
+
}
|
|
12458
|
+
|
|
12359
12459
|
// src/auth/replication-login.ts
|
|
12360
12460
|
init_dist();
|
|
12361
12461
|
init_errors();
|
|
@@ -12649,7 +12749,7 @@ async function revokeForLogout(ctx, discardLocalChanges) {
|
|
|
12649
12749
|
"REVOCATION_FAILED",
|
|
12650
12750
|
`Revocation failed for ${cid}: ${result.error.message}. Session, key, and replica files are retained for retry; local reads stay denied.`,
|
|
12651
12751
|
ExitCode.ERROR,
|
|
12652
|
-
{ cid, hint: `Retry \`${
|
|
12752
|
+
{ cid, hint: `Retry \`${CLI_NAME15} auth logout --revoke\` when the node is available.` }
|
|
12653
12753
|
);
|
|
12654
12754
|
}
|
|
12655
12755
|
if (result.data.cid !== cid || result.data.revoked !== true) {
|
|
@@ -12668,7 +12768,7 @@ async function revokeForLogout(ctx, discardLocalChanges) {
|
|
|
12668
12768
|
"REVOCATION_FAILED",
|
|
12669
12769
|
`Revocation failed before local logout: ${error instanceof Error ? error.message : String(error)}. Session, key, and replica files are retained for retry; local reads stay denied.`,
|
|
12670
12770
|
ExitCode.ERROR,
|
|
12671
|
-
{ hint: `Retry \`${
|
|
12771
|
+
{ hint: `Retry \`${CLI_NAME15} auth logout --revoke\` when online authority is available.` }
|
|
12672
12772
|
);
|
|
12673
12773
|
}
|
|
12674
12774
|
}
|
|
@@ -12867,7 +12967,7 @@ async function finishProfileLogout(ctx, options = {}) {
|
|
|
12867
12967
|
function registerAuthCommand(program, options) {
|
|
12868
12968
|
const auth = program.command("auth").description("Authentication management");
|
|
12869
12969
|
registerAuthGrantsCommand(auth, { openKeyAcquisition: options?.openKeyAcquisition });
|
|
12870
|
-
auth.command("login").description("Authenticate with TinyCloud").option("--device", "Approve on another device (e.g. a phone) through OpenKey device authorization; requires a KV-scoped --manifest (SQL and secret decrypt need browser or --paste login)").option("--paste", "Wait up to 5 minutes for a signed code from OpenKey on stdin instead of a browser callback (Safari may require paste)").option("--no-popup", "Print the OpenKey URL without opening a browser").option("--method <method>", "Authentication method: local or openkey").option("--manifest <fileOrBase64>", `Request only this manifest's permissions (one space, plus raw encryption network entries such as a secrets decrypt grant); ${SHARE_PUBLISHING_MANIFEST_REF} covers \`${
|
|
12970
|
+
auth.command("login").description("Authenticate with TinyCloud").option("--device", "Approve on another device (e.g. a phone) through OpenKey device authorization; requires a KV-scoped --manifest (SQL and secret decrypt need browser or --paste login)").option("--paste", "Wait up to 5 minutes for a signed code from OpenKey on stdin instead of a browser callback (Safari may require paste)").option("--no-popup", "Print the OpenKey URL without opening a browser").option("--method <method>", "Authentication method: local or openkey").option("--manifest <fileOrBase64>", `Request only this manifest's permissions (one space, plus raw encryption network entries such as a secrets decrypt grant); ${SHARE_PUBLISHING_MANIFEST_REF} covers \`${CLI_NAME15} share publish\``).option("--expiry <duration>", "OpenKey session lifetime, e.g. 1h or 7d (device login: at most 30d, default 30d)").option("--owner <did>", "Sign in as this owner (did:pkh:eip155:CHAIN:ADDRESS): OpenKey preselects that key, e.g. one that is not the account's primary key, and an approval by any other identity is refused. With --manifest, also names the secrets owner for a manifest's `secrets` on a profile with no recorded owner").option("--replace-session", "Scoped or device login: replace this profile's live session even though the new scope would narrow, change or shorten it (prefer a new profile)").option("--replication-prefix <prefix>", "Add replication get+sync authority for a folder prefix ending in /, or root (repeatable)", (prefix, values = []) => [...values, prefix]).option("--replication-allow-secrets", "Allow replication of secret material (network-wide decrypt warning)").action(async (options2, cmd) => {
|
|
12871
12971
|
try {
|
|
12872
12972
|
if (options2.device && options2.paste) {
|
|
12873
12973
|
throw new CLIError("INVALID_ARGUMENT", "--device and --paste are mutually exclusive.", ExitCode.USAGE_ERROR);
|
|
@@ -13016,7 +13116,7 @@ function registerAuthCommand(program, options) {
|
|
|
13016
13116
|
authenticated: false,
|
|
13017
13117
|
replicasRemoved,
|
|
13018
13118
|
replicasKept: options2.keepReplicas === true,
|
|
13019
|
-
warning: online ? "The node confirmed revocation of the listed CIDs before local logout." : options2.keepReplicas ? "Legacy replica files remain on disk; this local logout clears the session but does not revoke node grants. Purge the files before a future sign-in if they must not be reused." : `Logout is local; delegations and grants on the node stay valid until they expire. Use \`${
|
|
13119
|
+
warning: online ? "The node confirmed revocation of the listed CIDs before local logout." : options2.keepReplicas ? "Legacy replica files remain on disk; this local logout clears the session but does not revoke node grants. Purge the files before a future sign-in if they must not be reused." : `Logout is local; delegations and grants on the node stay valid until they expire. Use \`${CLI_NAME15} delegation revoke <cid>\` to end access now.`,
|
|
13020
13120
|
...online === void 0 ? {} : {
|
|
13021
13121
|
revokedCids: online.plan.cids,
|
|
13022
13122
|
revocationExcluded: online.plan.excluded
|
|
@@ -13106,11 +13206,14 @@ ${signInAgainHint(ctx.profile, posture ?? void 0)}
|
|
|
13106
13206
|
).option("--permission <file>", 'JSON permission request: { "permissions": PermissionEntry[] }').option("--manifest <fileOrBase64>", "Manifest file, base64:<json>, or raw base64 JSON").option(
|
|
13107
13207
|
"--expiry <duration>",
|
|
13108
13208
|
`Lifetime of the granted delegation. ms-format string (e.g. "7d", "30m") or raw milliseconds. Defaults to 7d, capped by the active session's expiry.`
|
|
13109
|
-
).option("--emit [file]", "Emit the request artifact to stdout, or write it to file when provided").option("--grant", "Grant the requested permissions immediately with this owner profile").option("--yes", "Skip local-key TTY confirmation", false).option("--no-popup", "Print the OpenKey URL without opening a browser when granting with OpenKey").option("--device", "With --grant: approve on another device (e.g. a phone) through OpenKey device authorization (KV-scoped; SQL needs browser login)").action(async (options2, cmd) => {
|
|
13209
|
+
).option("--emit [file]", "Emit the request artifact to stdout, or write it to file when provided").option("--grant", "Grant the requested permissions immediately with this owner profile").option("--yes", "Skip local-key TTY confirmation", false).option("--no-popup", "Print the OpenKey URL without opening a browser when granting with OpenKey").option("--device", "With --grant: approve on another device (e.g. a phone) through OpenKey device authorization (KV-scoped; SQL needs browser login)").option("--paste", "With --grant on an OpenKey profile: wait up to 5 minutes for a signed code from OpenKey on stdin instead of a browser callback (use when the browser cannot reach this CLI)").action(async (options2, cmd) => {
|
|
13110
13210
|
try {
|
|
13111
13211
|
const globalOpts = cmd.optsWithGlobals();
|
|
13112
13212
|
const ctx = await ProfileManager.resolveContext(globalOpts, options2.grant ? {} : { storedSession: "unused" });
|
|
13113
13213
|
const profile = await ProfileManager.getProfile(ctx.profile);
|
|
13214
|
+
if (options2.paste && (!options2.grant || options2.device || resolveProfilePosture(profile) !== "owner-openkey")) {
|
|
13215
|
+
throw new CLIError("INVALID_ARGUMENT", "--paste requires --grant on an OpenKey owner profile and cannot be combined with --device.", ExitCode.USAGE_ERROR);
|
|
13216
|
+
}
|
|
13114
13217
|
if (options2.device && (!options2.grant || resolveProfilePosture(profile) !== "owner-openkey")) {
|
|
13115
13218
|
throw new CLIError("INVALID_ARGUMENT", "--device requires --grant on an OpenKey owner profile.", ExitCode.USAGE_ERROR);
|
|
13116
13219
|
}
|
|
@@ -13143,7 +13246,7 @@ ${signInAgainHint(ctx.profile, posture ?? void 0)}
|
|
|
13143
13246
|
if (profile.authMethod === "openkey" || options2.device) {
|
|
13144
13247
|
const key = await ProfileManager.getKey(ctx.profile);
|
|
13145
13248
|
if (!key) {
|
|
13146
|
-
throw new CLIError("NO_KEY", `No key found for profile "${ctx.profile}". Run \`${
|
|
13249
|
+
throw new CLIError("NO_KEY", `No key found for profile "${ctx.profile}". Run \`${CLI_NAME15} --profile ${ctx.profile} auth rotate\` to create a new key and sign in.`, ExitCode.AUTH_REQUIRED);
|
|
13147
13250
|
}
|
|
13148
13251
|
const openkeyHost = resolveOpenKeyHost(profile);
|
|
13149
13252
|
const grants = [];
|
|
@@ -13155,9 +13258,15 @@ ${signInAgainHint(ctx.profile, posture ?? void 0)}
|
|
|
13155
13258
|
expiry: expiryCap
|
|
13156
13259
|
};
|
|
13157
13260
|
const declined = [];
|
|
13158
|
-
|
|
13261
|
+
const groups = groupPermissionsBySpace(requested);
|
|
13262
|
+
const notices = approvalNotices(groups);
|
|
13263
|
+
if (notices) process.stderr.write(`${notices.intro}
|
|
13264
|
+
`);
|
|
13265
|
+
for (const [index, group] of groups.entries()) {
|
|
13266
|
+
if (notices) process.stderr.write(`${notices.prompts[index]}
|
|
13267
|
+
`);
|
|
13159
13268
|
const reason = permissionGrantReason(
|
|
13160
|
-
`Grant requested TinyCloud permissions from \`${
|
|
13269
|
+
`Grant requested TinyCloud permissions from \`${CLI_NAME15} auth request --grant\`.`,
|
|
13161
13270
|
group
|
|
13162
13271
|
);
|
|
13163
13272
|
const request = grantRequestPermissions(group, profile.spaceId ?? profile.spaceName);
|
|
@@ -13184,7 +13293,8 @@ ${signInAgainHint(ctx.profile, posture ?? void 0)}
|
|
|
13184
13293
|
reason,
|
|
13185
13294
|
openkeyHost,
|
|
13186
13295
|
expiry: expiryCap === void 0 ? void 0 : openKeyExpiryParam(expiryCap),
|
|
13187
|
-
noPopup: options2.popup === false
|
|
13296
|
+
noPopup: options2.popup === false,
|
|
13297
|
+
paste: options2.paste
|
|
13188
13298
|
});
|
|
13189
13299
|
}
|
|
13190
13300
|
const delegation = portableFromOpenKeyDelegation(delegationData, request, ctx.host, proof);
|
|
@@ -13266,7 +13376,7 @@ ${signInAgainHint(ctx.profile, posture ?? void 0)}
|
|
|
13266
13376
|
kind: parsed.kind,
|
|
13267
13377
|
requestId: parsed.requestId,
|
|
13268
13378
|
requested: parsed.requested,
|
|
13269
|
-
next: `${
|
|
13379
|
+
next: `${CLI_NAME15} auth retry ${parsed.requestId}`
|
|
13270
13380
|
});
|
|
13271
13381
|
return;
|
|
13272
13382
|
}
|
|
@@ -13817,7 +13927,7 @@ async function ensureDelegationAuthority(params) {
|
|
|
13817
13927
|
if (!key) {
|
|
13818
13928
|
throw new CLIError(
|
|
13819
13929
|
"NO_KEY",
|
|
13820
|
-
`No key found for profile "${params.ctx.profile}". Run \`${
|
|
13930
|
+
`No key found for profile "${params.ctx.profile}". Run \`${CLI_NAME15} --profile ${params.ctx.profile} auth rotate\` to create a new key and sign in.`,
|
|
13821
13931
|
ExitCode.AUTH_REQUIRED
|
|
13822
13932
|
);
|
|
13823
13933
|
}
|
|
@@ -14063,7 +14173,7 @@ async function rotateAuthKey(profileName2, host, options = {}) {
|
|
|
14063
14173
|
if (!profile.privateKey) {
|
|
14064
14174
|
throw new CLIError(
|
|
14065
14175
|
"LOCAL_OWNER_KEY_REQUIRED",
|
|
14066
|
-
`Profile "${profileName2}" does not have a local owner private key. Run \`${
|
|
14176
|
+
`Profile "${profileName2}" does not have a local owner private key. Run \`${CLI_NAME15} auth login --method local\` first.`,
|
|
14067
14177
|
ExitCode.AUTH_REQUIRED
|
|
14068
14178
|
);
|
|
14069
14179
|
}
|
|
@@ -14503,7 +14613,7 @@ async function refreshOpenKeySession(profileName2, host, options = {}) {
|
|
|
14503
14613
|
}
|
|
14504
14614
|
|
|
14505
14615
|
// src/commands/completion.ts
|
|
14506
|
-
import { CLI_NAME as
|
|
14616
|
+
import { CLI_NAME as CLI_NAME16 } from "@tinycloud/operations/cli-name";
|
|
14507
14617
|
function registerCompletionCommand(program) {
|
|
14508
14618
|
const completion = program.command("completion").description("Generate shell completions");
|
|
14509
14619
|
completion.command("bash").description("Output bash completions").action(() => {
|
|
@@ -14520,7 +14630,7 @@ function registerCompletionCommand(program) {
|
|
|
14520
14630
|
});
|
|
14521
14631
|
}
|
|
14522
14632
|
function generateBashCompletion() {
|
|
14523
|
-
return `# ${
|
|
14633
|
+
return `# ${CLI_NAME16} bash completion
|
|
14524
14634
|
_tc_completions() {
|
|
14525
14635
|
local cur prev commands subcommands
|
|
14526
14636
|
COMPREPLY=()
|
|
@@ -14545,11 +14655,11 @@ _tc_completions() {
|
|
|
14545
14655
|
COMPREPLY=( $(compgen -W "\${subcommands}" -- "\${cur}") )
|
|
14546
14656
|
fi
|
|
14547
14657
|
}
|
|
14548
|
-
complete -F _tc_completions ${
|
|
14658
|
+
complete -F _tc_completions ${CLI_NAME16}
|
|
14549
14659
|
`;
|
|
14550
14660
|
}
|
|
14551
14661
|
function generateZshCompletion() {
|
|
14552
|
-
return `#compdef ${
|
|
14662
|
+
return `#compdef ${CLI_NAME16}
|
|
14553
14663
|
|
|
14554
14664
|
_tc() {
|
|
14555
14665
|
local -a commands
|
|
@@ -14600,50 +14710,50 @@ _tc
|
|
|
14600
14710
|
`;
|
|
14601
14711
|
}
|
|
14602
14712
|
function generateFishCompletion() {
|
|
14603
|
-
return `# ${
|
|
14713
|
+
return `# ${CLI_NAME16} fish completion
|
|
14604
14714
|
set -l commands init auth kv space delegation share node profile completion
|
|
14605
14715
|
|
|
14606
14716
|
# Disable file completion by default
|
|
14607
|
-
complete -c ${
|
|
14717
|
+
complete -c ${CLI_NAME16} -f
|
|
14608
14718
|
|
|
14609
14719
|
# Top-level commands
|
|
14610
|
-
complete -c ${
|
|
14611
|
-
complete -c ${
|
|
14612
|
-
complete -c ${
|
|
14613
|
-
complete -c ${
|
|
14614
|
-
complete -c ${
|
|
14615
|
-
complete -c ${
|
|
14616
|
-
complete -c ${
|
|
14617
|
-
complete -c ${
|
|
14618
|
-
complete -c ${
|
|
14720
|
+
complete -c ${CLI_NAME16} -n "not __fish_seen_subcommand_from $commands" -a init -d "Initialize a new TinyCloud profile"
|
|
14721
|
+
complete -c ${CLI_NAME16} -n "not __fish_seen_subcommand_from $commands" -a auth -d "Authentication management"
|
|
14722
|
+
complete -c ${CLI_NAME16} -n "not __fish_seen_subcommand_from $commands" -a kv -d "Key-value store operations"
|
|
14723
|
+
complete -c ${CLI_NAME16} -n "not __fish_seen_subcommand_from $commands" -a space -d "Space management"
|
|
14724
|
+
complete -c ${CLI_NAME16} -n "not __fish_seen_subcommand_from $commands" -a delegation -d "Manage delegations"
|
|
14725
|
+
complete -c ${CLI_NAME16} -n "not __fish_seen_subcommand_from $commands" -a share -d "Share data with others"
|
|
14726
|
+
complete -c ${CLI_NAME16} -n "not __fish_seen_subcommand_from $commands" -a node -d "Node health and info"
|
|
14727
|
+
complete -c ${CLI_NAME16} -n "not __fish_seen_subcommand_from $commands" -a profile -d "Profile management"
|
|
14728
|
+
complete -c ${CLI_NAME16} -n "not __fish_seen_subcommand_from $commands" -a completion -d "Generate shell completions"
|
|
14619
14729
|
|
|
14620
14730
|
# Subcommands
|
|
14621
|
-
complete -c ${
|
|
14622
|
-
complete -c ${
|
|
14623
|
-
complete -c ${
|
|
14624
|
-
complete -c ${
|
|
14625
|
-
complete -c ${
|
|
14626
|
-
complete -c ${
|
|
14627
|
-
complete -c ${
|
|
14628
|
-
complete -c ${
|
|
14731
|
+
complete -c ${CLI_NAME16} -n "__fish_seen_subcommand_from auth" -a "login logout rotate status whoami"
|
|
14732
|
+
complete -c ${CLI_NAME16} -n "__fish_seen_subcommand_from kv" -a "get put delete list head"
|
|
14733
|
+
complete -c ${CLI_NAME16} -n "__fish_seen_subcommand_from space" -a "list create info switch"
|
|
14734
|
+
complete -c ${CLI_NAME16} -n "__fish_seen_subcommand_from delegation" -a "create list info revoke"
|
|
14735
|
+
complete -c ${CLI_NAME16} -n "__fish_seen_subcommand_from share" -a "create receive list revoke"
|
|
14736
|
+
complete -c ${CLI_NAME16} -n "__fish_seen_subcommand_from node" -a "health version status"
|
|
14737
|
+
complete -c ${CLI_NAME16} -n "__fish_seen_subcommand_from profile" -a "list create show switch delete"
|
|
14738
|
+
complete -c ${CLI_NAME16} -n "__fish_seen_subcommand_from completion" -a "bash zsh fish"
|
|
14629
14739
|
|
|
14630
14740
|
# Global options
|
|
14631
|
-
complete -c ${
|
|
14632
|
-
complete -c ${
|
|
14633
|
-
complete -c ${
|
|
14634
|
-
complete -c ${
|
|
14635
|
-
complete -c ${
|
|
14636
|
-
complete -c ${
|
|
14637
|
-
complete -c ${
|
|
14638
|
-
complete -c ${
|
|
14741
|
+
complete -c ${CLI_NAME16} -l profile -s p -d "Profile to use"
|
|
14742
|
+
complete -c ${CLI_NAME16} -l host -s H -d "TinyCloud node URL"
|
|
14743
|
+
complete -c ${CLI_NAME16} -l verbose -s v -d "Enable verbose output"
|
|
14744
|
+
complete -c ${CLI_NAME16} -l no-cache -d "Disable caching"
|
|
14745
|
+
complete -c ${CLI_NAME16} -l quiet -s q -d "Suppress non-essential output"
|
|
14746
|
+
complete -c ${CLI_NAME16} -l replication -d "Enable read-through replication"
|
|
14747
|
+
complete -c ${CLI_NAME16} -l no-replication -d "Disable replication"
|
|
14748
|
+
complete -c ${CLI_NAME16} -l replication-debug -d "Write replication diagnostics to stderr"
|
|
14639
14749
|
`;
|
|
14640
14750
|
}
|
|
14641
14751
|
|
|
14642
14752
|
// src/commands/courier.ts
|
|
14643
|
-
import { CLI_NAME as
|
|
14644
|
-
var TC_OPTIONS_BEFORE_COURIER = `${
|
|
14753
|
+
import { CLI_NAME as CLI_NAME17 } from "@tinycloud/operations/cli-name";
|
|
14754
|
+
var TC_OPTIONS_BEFORE_COURIER = `${CLI_NAME17} courier takes no ${CLI_NAME17} options and does not use ${CLI_NAME17} profiles; Courier keeps its own profile in $COURIER_HOME (default ~/.courier). Put flags after "courier", e.g. ${CLI_NAME17} courier init --host <url>.`;
|
|
14645
14755
|
function registerCourierCommand(program) {
|
|
14646
|
-
const courier = program.command("courier").description(`Agent-to-agent threads over TinyCloud; run \`${
|
|
14756
|
+
const courier = program.command("courier").description(`Agent-to-agent threads over TinyCloud; run \`${CLI_NAME17} courier --help\` (beta)`).argument("[args...]").allowUnknownOption().helpOption(false);
|
|
14647
14757
|
courier.action(() => courier.error(TC_OPTIONS_BEFORE_COURIER, { exitCode: 2, code: "tc.courier.options" }));
|
|
14648
14758
|
}
|
|
14649
14759
|
|
|
@@ -14688,7 +14798,7 @@ init_formatter();
|
|
|
14688
14798
|
init_errors();
|
|
14689
14799
|
init_constants();
|
|
14690
14800
|
init_sdk();
|
|
14691
|
-
import { CLI_NAME as
|
|
14801
|
+
import { CLI_NAME as CLI_NAME18 } from "@tinycloud/operations/cli-name";
|
|
14692
14802
|
import { PermissionNotInManifestError } from "@tinycloud/node-sdk";
|
|
14693
14803
|
async function queryDelegations(node, options, until) {
|
|
14694
14804
|
const found = [];
|
|
@@ -14699,7 +14809,7 @@ async function queryDelegations(node, options, until) {
|
|
|
14699
14809
|
if (page.error.code === "PERMISSION_DENIED") {
|
|
14700
14810
|
throw new CLIError(
|
|
14701
14811
|
"PERMISSION_DENIED",
|
|
14702
|
-
`This session cannot read delegations: it lacks tinycloud.delegation/list. Run \`${
|
|
14812
|
+
`This session cannot read delegations: it lacks tinycloud.delegation/list. Run \`${CLI_NAME18} auth login\` again to grant it.`,
|
|
14703
14813
|
ExitCode.PERMISSION_DENIED
|
|
14704
14814
|
);
|
|
14705
14815
|
}
|
|
@@ -14874,7 +14984,7 @@ init_constants();
|
|
|
14874
14984
|
init_formatter();
|
|
14875
14985
|
init_theme();
|
|
14876
14986
|
init_errors();
|
|
14877
|
-
import { CLI_NAME as
|
|
14987
|
+
import { CLI_NAME as CLI_NAME19 } from "@tinycloud/operations/cli-name";
|
|
14878
14988
|
init_session_expired();
|
|
14879
14989
|
init_types();
|
|
14880
14990
|
function registerDoctorCommand(program) {
|
|
@@ -14908,7 +15018,7 @@ function registerDoctorCommand(program) {
|
|
|
14908
15018
|
const profile = await ProfileManager.getProfile(profileName2);
|
|
14909
15019
|
keyDetail = profile.did ? `${profile.did.slice(0, 20)}...` : "key found";
|
|
14910
15020
|
} else {
|
|
14911
|
-
keyDetail = `no key \u2014 run ${
|
|
15021
|
+
keyDetail = `no key \u2014 run ${CLI_NAME19} init`;
|
|
14912
15022
|
}
|
|
14913
15023
|
} catch {
|
|
14914
15024
|
keyDetail = "error reading key";
|
|
@@ -14930,7 +15040,7 @@ function registerDoctorCommand(program) {
|
|
|
14930
15040
|
} else if (state2 === "unverified") {
|
|
14931
15041
|
sessionDetail = `not usable (no signed OpenKey proof) \u2014 ${signInAgainHint(profileName2, resolveProfilePosture(profile))}`;
|
|
14932
15042
|
} else {
|
|
14933
|
-
sessionDetail = sessionOk ? "active" : `no session \u2014 run ${
|
|
15043
|
+
sessionDetail = sessionOk ? "active" : `no session \u2014 run ${CLI_NAME19} auth login`;
|
|
14934
15044
|
}
|
|
14935
15045
|
} catch {
|
|
14936
15046
|
sessionDetail = "error reading session";
|
|
@@ -14958,7 +15068,7 @@ function registerDoctorCommand(program) {
|
|
|
14958
15068
|
try {
|
|
14959
15069
|
const profile = await ProfileManager.getProfile(profileName2);
|
|
14960
15070
|
spaceOk = Boolean(profile.spaceId);
|
|
14961
|
-
spaceDetail = spaceOk ? `${profile.spaceId.slice(0, 16)}...` : `no space \u2014 run ${
|
|
15071
|
+
spaceDetail = spaceOk ? `${profile.spaceId.slice(0, 16)}...` : `no space \u2014 run ${CLI_NAME19} space create`;
|
|
14962
15072
|
} catch {
|
|
14963
15073
|
spaceDetail = "error checking space";
|
|
14964
15074
|
}
|
|
@@ -15148,7 +15258,7 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
|
|
|
15148
15258
|
}
|
|
15149
15259
|
|
|
15150
15260
|
// src/commands/enable.ts
|
|
15151
|
-
import { CLI_NAME as
|
|
15261
|
+
import { CLI_NAME as CLI_NAME20 } from "@tinycloud/operations/cli-name";
|
|
15152
15262
|
init_constants();
|
|
15153
15263
|
init_profiles();
|
|
15154
15264
|
init_errors();
|
|
@@ -15156,7 +15266,7 @@ init_formatter();
|
|
|
15156
15266
|
init_publishing_manifest();
|
|
15157
15267
|
function registerEnableCommand(program) {
|
|
15158
15268
|
const enable = program.command("enable").description("Enable a narrowly scoped TinyCloud service");
|
|
15159
|
-
enable.command("share").description(`Approve Share publishing scope through OpenKey device authorization (same as \`${
|
|
15269
|
+
enable.command("share").description(`Approve Share publishing scope through OpenKey device authorization (same as \`${CLI_NAME20} auth login --device --manifest builtin:share-publishing\`). Use a dedicated profile, e.g. \`${CLI_NAME20} init --name publisher --key-only && ${CLI_NAME20} --profile publisher enable share\`; keep existing profiles for their apps.`).option("--replace-session", "Replace this profile's live session even though the new scope would narrow, change or shorten it (prefer a new profile)").action(async (options, command) => {
|
|
15160
15270
|
try {
|
|
15161
15271
|
const globalOptions = command.optsWithGlobals();
|
|
15162
15272
|
const context = await ProfileManager.resolveContext(globalOptions, { storedSession: "unused" });
|
|
@@ -15192,7 +15302,7 @@ init_profiles();
|
|
|
15192
15302
|
init_formatter();
|
|
15193
15303
|
init_errors();
|
|
15194
15304
|
init_constants();
|
|
15195
|
-
import { CLI_NAME as
|
|
15305
|
+
import { CLI_NAME as CLI_NAME21 } from "@tinycloud/operations/cli-name";
|
|
15196
15306
|
function registerInitCommand(program) {
|
|
15197
15307
|
program.command("init").description("Initialize a new TinyCloud profile").option("--name <profile>", "Profile name", "default").option("--key-only", "Only generate key, skip authentication").option("--host <url>", "TinyCloud node URL").option("--paste", "Use manual paste mode for authentication").option("--no-popup", "Print the OpenKey URL without opening a browser").option("--default-space <name>", "Default space used when --space is omitted (e.g. applications)").action(async (options, cmd) => {
|
|
15198
15308
|
try {
|
|
@@ -15210,7 +15320,7 @@ function registerInitCommand(program) {
|
|
|
15210
15320
|
if (await ProfileManager.profileExists(profileName2)) {
|
|
15211
15321
|
throw new CLIError(
|
|
15212
15322
|
"PROFILE_EXISTS",
|
|
15213
|
-
`Profile "${profileName2}" already exists. Use \`${
|
|
15323
|
+
`Profile "${profileName2}" already exists. Use \`${CLI_NAME21} profile delete ${profileName2}\` first or choose a different name.`,
|
|
15214
15324
|
ExitCode.ERROR
|
|
15215
15325
|
);
|
|
15216
15326
|
}
|
|
@@ -15354,6 +15464,9 @@ async function kvHandle(node, spaceInput, profileName2) {
|
|
|
15354
15464
|
const kv = spaceUri ? node.kvForSpace(spaceUri) : node.kv;
|
|
15355
15465
|
return { kv, spaceUri };
|
|
15356
15466
|
}
|
|
15467
|
+
function retryFields(retry) {
|
|
15468
|
+
return retry ? { attempts: retry.attempts, backoffMs: retry.backoffMs } : {};
|
|
15469
|
+
}
|
|
15357
15470
|
function registerKvCommand(program) {
|
|
15358
15471
|
const kv = program.command("kv").description("Key-value store operations");
|
|
15359
15472
|
kv.command("get <key>").description("Get a value by key").option("--raw", "Output raw value (no JSON wrapping)").option("-o, --output <file>", "Write value to file").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (key, options, cmd) => {
|
|
@@ -15391,7 +15504,8 @@ function registerKvCommand(program) {
|
|
|
15391
15504
|
outputJson({
|
|
15392
15505
|
key,
|
|
15393
15506
|
data,
|
|
15394
|
-
metadata
|
|
15507
|
+
metadata,
|
|
15508
|
+
...retryFields(result.data.retry)
|
|
15395
15509
|
});
|
|
15396
15510
|
} else {
|
|
15397
15511
|
const content = typeof data === "string" ? data : JSON.stringify(data);
|
|
@@ -15431,7 +15545,7 @@ function registerKvCommand(program) {
|
|
|
15431
15545
|
if (!result.ok) {
|
|
15432
15546
|
await throwKvError(result.error, spaceUri, ctx.profile);
|
|
15433
15547
|
}
|
|
15434
|
-
outputJson({ key, written: true });
|
|
15548
|
+
outputJson({ key, written: true, ...retryFields(result.data?.retry) });
|
|
15435
15549
|
} catch (error) {
|
|
15436
15550
|
return handleError(error);
|
|
15437
15551
|
}
|
|
@@ -15447,7 +15561,7 @@ function registerKvCommand(program) {
|
|
|
15447
15561
|
if (!result.ok) {
|
|
15448
15562
|
await throwKvError(result.error, spaceUri, ctx.profile);
|
|
15449
15563
|
}
|
|
15450
|
-
outputJson({ key, deleted: true });
|
|
15564
|
+
outputJson({ key, deleted: true, ...retryFields(result.data?.retry) });
|
|
15451
15565
|
} catch (error) {
|
|
15452
15566
|
return handleError(error);
|
|
15453
15567
|
}
|
|
@@ -15560,7 +15674,7 @@ init_errors();
|
|
|
15560
15674
|
init_constants();
|
|
15561
15675
|
init_theme();
|
|
15562
15676
|
import { resolveManifestKnowledgeRoot as resolveManifestKnowledgeRoot2 } from "@tinycloud/sdk-core";
|
|
15563
|
-
import { CLI_NAME as
|
|
15677
|
+
import { CLI_NAME as CLI_NAME22 } from "@tinycloud/operations/cli-name";
|
|
15564
15678
|
import { readFile as readFile11 } from "fs/promises";
|
|
15565
15679
|
var DEFAULT_APP_SPACE = "applications";
|
|
15566
15680
|
function registerManifestCommand(program) {
|
|
@@ -15568,8 +15682,8 @@ function registerManifestCommand(program) {
|
|
|
15568
15682
|
manifest.command("resolve <source>").description("Resolve a manifest file or URL to its effective space, paths, and DB basenames").addHelpText("after", `
|
|
15569
15683
|
|
|
15570
15684
|
Examples:
|
|
15571
|
-
$ ${
|
|
15572
|
-
$ ${
|
|
15685
|
+
$ ${CLI_NAME22} manifest resolve ./manifest.json
|
|
15686
|
+
$ ${CLI_NAME22} manifest resolve https://app.example.com/manifest.json --json
|
|
15573
15687
|
|
|
15574
15688
|
What it shows:
|
|
15575
15689
|
- app_id, name, manifest_version
|
|
@@ -15579,7 +15693,7 @@ What it shows:
|
|
|
15579
15693
|
|
|
15580
15694
|
This command is read-only and does NOT contact the node \u2014 it just resolves
|
|
15581
15695
|
the manifest against the active profile's address/chain so you know which
|
|
15582
|
-
\`--space\` and \`--db\` values to pass to other ${
|
|
15696
|
+
\`--space\` and \`--db\` values to pass to other ${CLI_NAME22} commands.
|
|
15583
15697
|
`).action(async (source, _options, cmd) => {
|
|
15584
15698
|
try {
|
|
15585
15699
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -15648,7 +15762,7 @@ ${theme.heading("SQL databases")}
|
|
|
15648
15762
|
`);
|
|
15649
15763
|
}
|
|
15650
15764
|
process.stdout.write(theme.muted(`
|
|
15651
|
-
Use with: ${
|
|
15765
|
+
Use with: ${CLI_NAME22} sql query --space ${spaceName} --db <db> "..."
|
|
15652
15766
|
`));
|
|
15653
15767
|
}
|
|
15654
15768
|
if (permissions.length > 0) {
|
|
@@ -15768,7 +15882,7 @@ init_profiles();
|
|
|
15768
15882
|
init_formatter();
|
|
15769
15883
|
init_errors();
|
|
15770
15884
|
init_constants();
|
|
15771
|
-
import { CLI_NAME as
|
|
15885
|
+
import { CLI_NAME as CLI_NAME23 } from "@tinycloud/operations/cli-name";
|
|
15772
15886
|
import { createInterface as createInterface5 } from "readline";
|
|
15773
15887
|
init_theme();
|
|
15774
15888
|
init_types();
|
|
@@ -15914,9 +16028,9 @@ profile at command time. Precedence for every kv/sql command:
|
|
|
15914
16028
|
explicit --space flag > profile defaultSpace > primary space.
|
|
15915
16029
|
|
|
15916
16030
|
Examples:
|
|
15917
|
-
$ ${
|
|
15918
|
-
$ ${
|
|
15919
|
-
$ ${
|
|
16031
|
+
$ ${CLI_NAME23} profile set-default-space applications
|
|
16032
|
+
$ ${CLI_NAME23} profile set-default-space applications --profile cli-test
|
|
16033
|
+
$ ${CLI_NAME23} profile set-default-space --unset
|
|
15920
16034
|
`).action(async (name, options, cmd) => {
|
|
15921
16035
|
try {
|
|
15922
16036
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -15928,7 +16042,7 @@ Examples:
|
|
|
15928
16042
|
if (!options.unset && (name === void 0 || name === "")) {
|
|
15929
16043
|
throw new CLIError(
|
|
15930
16044
|
"USAGE_ERROR",
|
|
15931
|
-
`Provide a space name (e.g. \`${
|
|
16045
|
+
`Provide a space name (e.g. \`${CLI_NAME23} profile set-default-space applications\`) or pass --unset.`,
|
|
15932
16046
|
ExitCode.USAGE_ERROR
|
|
15933
16047
|
);
|
|
15934
16048
|
}
|
|
@@ -15987,7 +16101,7 @@ function parseOperatorType(raw) {
|
|
|
15987
16101
|
|
|
15988
16102
|
// src/commands/replica.ts
|
|
15989
16103
|
init_profile_replicas();
|
|
15990
|
-
import { CLI_NAME as
|
|
16104
|
+
import { CLI_NAME as CLI_NAME24 } from "@tinycloud/operations/cli-name";
|
|
15991
16105
|
import { randomBytes as randomBytes7 } from "crypto";
|
|
15992
16106
|
import { constants as fsConstants5 } from "fs";
|
|
15993
16107
|
import { mkdir as mkdir5, open as open9, readdir as readdir6, rename as rename4, unlink as unlink4, lstat as lstat9 } from "fs/promises";
|
|
@@ -16128,11 +16242,11 @@ var EXIT_BY_CODE = {
|
|
|
16128
16242
|
var READ_FAILURE = {
|
|
16129
16243
|
absent: { code: "KEY_ABSENT", exit: ExitCode.NOT_FOUND, what: "is not in the replica (the replica covers its whole prefix)" },
|
|
16130
16244
|
deleted: { code: "KEY_DELETED", exit: ExitCode.NOT_FOUND, what: "was deleted at the source" },
|
|
16131
|
-
content_missing: { code: "CONTENT_MISSING", exit: ExitCode.NOT_FOUND, what: `is known but its content has not been fetched yet; run ${
|
|
16245
|
+
content_missing: { code: "CONTENT_MISSING", exit: ExitCode.NOT_FOUND, what: `is known but its content has not been fetched yet; run ${CLI_NAME24} replica sync` },
|
|
16132
16246
|
coverage_incomplete: {
|
|
16133
16247
|
code: "COVERAGE_INCOMPLETE",
|
|
16134
16248
|
exit: ExitCode.NOT_FOUND,
|
|
16135
|
-
what: `is not in the replica, which has not finished its first sync; run ${
|
|
16249
|
+
what: `is not in the replica, which has not finished its first sync; run ${CLI_NAME24} replica sync`
|
|
16136
16250
|
},
|
|
16137
16251
|
not_covered: { code: "NOT_COVERED", exit: ExitCode.USAGE_ERROR, what: "is outside the replica's prefix" }
|
|
16138
16252
|
};
|
|
@@ -16250,7 +16364,7 @@ async function existingReplicaName(profile, option) {
|
|
|
16250
16364
|
const names = await replicaNames(profile);
|
|
16251
16365
|
if (names.length === 1) return names[0];
|
|
16252
16366
|
if (names.length === 0) {
|
|
16253
|
-
throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, `Profile "${profile}" has no replicas. Create one with: ${
|
|
16367
|
+
throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, `Profile "${profile}" has no replicas. Create one with: ${CLI_NAME24} replica sync --prefix <prefix>`);
|
|
16254
16368
|
}
|
|
16255
16369
|
throw new CLIError("USAGE_ERROR", `Profile "${profile}" has several replicas (${names.join(", ")}); pass --replica <name>.`, ExitCode.USAGE_ERROR);
|
|
16256
16370
|
}
|
|
@@ -16358,9 +16472,9 @@ async function storedGrants(profile) {
|
|
|
16358
16472
|
function grantHint(space, prefix) {
|
|
16359
16473
|
return [
|
|
16360
16474
|
"A replica needs a device grant with get and sync on its prefix:",
|
|
16361
|
-
` device: ${
|
|
16362
|
-
` owner: ${
|
|
16363
|
-
` device: ${
|
|
16475
|
+
` device: ${CLI_NAME24} auth request --cap tinycloud.kv:${space}:${prefix}:get,list,metadata,sync --expiry 30d --emit req.json`,
|
|
16476
|
+
` owner: ${CLI_NAME24} auth grant req.json > grant.json`,
|
|
16477
|
+
` device: ${CLI_NAME24} auth import grant.json`
|
|
16364
16478
|
].join("\n");
|
|
16365
16479
|
}
|
|
16366
16480
|
function resolveSpace(input, prefix, grants) {
|
|
@@ -16451,7 +16565,7 @@ function describeStatus(status) {
|
|
|
16451
16565
|
formatField("Size", formatBytes(status.bytes)),
|
|
16452
16566
|
formatField("Grant", status.device.delegationCid),
|
|
16453
16567
|
formatField("Last error", status.lastError ? `${status.lastError.code}: ${status.lastError.message}` : null),
|
|
16454
|
-
formatField("Purge", status.purgePending ? `incomplete: content files remain; the next ${
|
|
16568
|
+
formatField("Purge", status.purgePending ? `incomplete: content files remain; the next ${CLI_NAME24} replica command retries` : null)
|
|
16455
16569
|
].join("\n");
|
|
16456
16570
|
}
|
|
16457
16571
|
async function writeFileAtomic(path, bytes) {
|
|
@@ -16615,7 +16729,7 @@ or coverage incomplete; 5 grant expired, revoked or not yet valid; 6 network;
|
|
|
16615
16729
|
(options, cmd) => run(async () => {
|
|
16616
16730
|
const profile = await profileName(cmd);
|
|
16617
16731
|
if (options.purge) {
|
|
16618
|
-
throw new CLIError("REPORT_READ_ONLY", `Replica report is read-only. To remove a named local replica, run ${
|
|
16732
|
+
throw new CLIError("REPORT_READ_ONLY", `Replica report is read-only. To remove a named local replica, run ${CLI_NAME24} replica reset --replica <name> --purge; flag-owned purge has no equivalent yet (TC-1309).`, ExitCode.USAGE_ERROR);
|
|
16619
16733
|
}
|
|
16620
16734
|
if (options.clearPending) {
|
|
16621
16735
|
throw new CLIError("REPORT_READ_ONLY", "Clearing pending replica writes has no equivalent yet; see TC-1309 for a safe replacement command.", ExitCode.USAGE_ERROR);
|
|
@@ -16717,7 +16831,7 @@ or coverage incomplete; 5 grant expired, revoked or not yet valid; 6 network;
|
|
|
16717
16831
|
if (differs !== void 0) {
|
|
16718
16832
|
throw new ReplicaError(
|
|
16719
16833
|
ReplicaErrorCode.CONFIG_MISMATCH,
|
|
16720
|
-
`Replica ${name} already follows ${differs}. Use another --replica name, or ${
|
|
16834
|
+
`Replica ${name} already follows ${differs}. Use another --replica name, or ${CLI_NAME24} replica reset --replica ${name} --purge first.`
|
|
16721
16835
|
);
|
|
16722
16836
|
}
|
|
16723
16837
|
}
|
|
@@ -16882,7 +16996,7 @@ init_theme();
|
|
|
16882
16996
|
init_errors();
|
|
16883
16997
|
init_warnings();
|
|
16884
16998
|
init_constants();
|
|
16885
|
-
import { CLI_NAME as
|
|
16999
|
+
import { CLI_NAME as CLI_NAME26 } from "@tinycloud/operations/cli-name";
|
|
16886
17000
|
import { readFile as readFile12 } from "fs/promises";
|
|
16887
17001
|
import { join as join16 } from "path";
|
|
16888
17002
|
import { homedir, hostname, userInfo } from "os";
|
|
@@ -17093,7 +17207,7 @@ function parseStatusEnvelope(line) {
|
|
|
17093
17207
|
// src/lib/secrets-wait.ts
|
|
17094
17208
|
init_constants();
|
|
17095
17209
|
init_errors();
|
|
17096
|
-
import { CLI_NAME as
|
|
17210
|
+
import { CLI_NAME as CLI_NAME25 } from "@tinycloud/operations/cli-name";
|
|
17097
17211
|
var WAIT_BACKOFF_MS = [3e3, 4500, 6750, 10125];
|
|
17098
17212
|
var WAIT_STEADY_INTERVAL_MS = 15e3;
|
|
17099
17213
|
var DEFAULT_WAIT_TIMEOUT = "5m";
|
|
@@ -17101,7 +17215,7 @@ var MAX_WAIT_TIMEOUT_MS = 9 * 6e4;
|
|
|
17101
17215
|
var MAX_CONSECUTIVE_FAILURES = 5;
|
|
17102
17216
|
var TIMEOUT_RE = /^(0|\d+s|\d+m)$/;
|
|
17103
17217
|
var RETRYABLE_STATUS_ERRORS = { NODE_UNREACHABLE: true, NODE_ERROR: true };
|
|
17104
|
-
var EXPLICIT_KEY_HINT = `${
|
|
17218
|
+
var EXPLICIT_KEY_HINT = `${CLI_NAME25} secrets wait never signs in, and an explicit private key has no stored session. Use a local profile signed in once with ${CLI_NAME25} --profile <profile> auth login --method local, without TC_PRIVATE_KEY.`;
|
|
17105
17219
|
function abortableSleep(ms2, signal) {
|
|
17106
17220
|
return new Promise((resolve8) => {
|
|
17107
17221
|
if (signal.aborted) {
|
|
@@ -17137,7 +17251,7 @@ async function runSecretsWait(request, deps) {
|
|
|
17137
17251
|
let step = 0;
|
|
17138
17252
|
const cancelled = () => new CLIError(
|
|
17139
17253
|
"CANCELLED",
|
|
17140
|
-
`Interrupted: ${
|
|
17254
|
+
`Interrupted: ${CLI_NAME25} secrets wait stopped before the secrets were ready.`,
|
|
17141
17255
|
ExitCode.CANCELLED,
|
|
17142
17256
|
progress === void 0 ? {} : { progress }
|
|
17143
17257
|
);
|
|
@@ -17281,13 +17395,13 @@ function statusError(error, profile, warnings) {
|
|
|
17281
17395
|
if (error.code === "AUTH_REQUIRED" || error.code === "SESSION_NOT_FOUND") {
|
|
17282
17396
|
const recovery = recoveryOf(error, "sign_in");
|
|
17283
17397
|
if (recovery !== void 0) {
|
|
17284
|
-
const hint = recovery.command !== void 0 ? `Sign in once with: ${recovery.command}, then retry.` : `Sign in to profile "${profile}" once with ${
|
|
17398
|
+
const hint = recovery.command !== void 0 ? `Sign in once with: ${recovery.command}, then retry.` : `Sign in to profile "${profile}" once with ${CLI_NAME25} auth login, then retry.`;
|
|
17285
17399
|
return new CLIError("AUTH_REQUIRED", error.message, ExitCode.AUTH_REQUIRED, { hint, recovery, ...extra });
|
|
17286
17400
|
}
|
|
17287
17401
|
}
|
|
17288
17402
|
if (error.code === "PROFILE_MIGRATION_REQUIRED") {
|
|
17289
17403
|
const recovery = recoveryOf(error, "complete_migration");
|
|
17290
|
-
const hint = recovery?.command !== void 0 ? `Run ${recovery.command} once to finish this profile's one-time upgrade, then retry wait.` : `Run ${
|
|
17404
|
+
const hint = recovery?.command !== void 0 ? `Run ${recovery.command} once to finish this profile's one-time upgrade, then retry wait.` : `Run ${CLI_NAME25} auth caps once with profile "${profile}" to finish its one-time upgrade, then retry wait.`;
|
|
17291
17405
|
return new CLIError("PROFILE_MIGRATION_REQUIRED", error.message, ExitCode.ERROR, {
|
|
17292
17406
|
hint,
|
|
17293
17407
|
...recovery === void 0 ? {} : { recovery },
|
|
@@ -17479,11 +17593,11 @@ async function runSecretOperation(params) {
|
|
|
17479
17593
|
}
|
|
17480
17594
|
function secretPermissionReason(action, name) {
|
|
17481
17595
|
const target = name ? ` secret "${name}"` : " secrets";
|
|
17482
|
-
return `Allow \`${
|
|
17596
|
+
return `Allow \`${CLI_NAME26} secrets ${action}${name ? ` ${name}` : ""}\` to access${target} with the required TinyCloud permissions.`;
|
|
17483
17597
|
}
|
|
17484
17598
|
function assertOwnerApprovalPossible(profileName2, profile, action, name, missing = []) {
|
|
17485
17599
|
if (profile.authMethod !== "openkey" || process.stdin.isTTY || process.stderr.isTTY) return;
|
|
17486
|
-
const command = `${
|
|
17600
|
+
const command = `${CLI_NAME26} secrets ${action === "del" ? "delete" : action}${name ? ` ${name}` : ""}`;
|
|
17487
17601
|
throw new CLIError(
|
|
17488
17602
|
"PERMISSION_DENIED",
|
|
17489
17603
|
action === "get" ? missing.length > 0 && missing.every((permission) => permission.service === "tinycloud.encryption" && Array.isArray(permission.actions) && permission.actions.length > 0 && permission.actions.every((capability) => capability === SECRET_DECRYPT_CAPABILITY)) ? `Profile "${profileName2}" lacks the scoped decrypt authority (${SECRET_DECRYPT_CAPABILITY}) needed to read secret "${name}", and requesting it needs an interactive browser approval.` : `Profile "${profileName2}" lacks a scoped read or decrypt grant needed to read secret "${name}", and requesting it needs an interactive browser approval.` : `Profile "${profileName2}" holds no grant for \`${command}\`, and requesting one needs an interactive browser approval.`,
|
|
@@ -17494,7 +17608,7 @@ function assertOwnerApprovalPossible(profileName2, profile, action, name, missin
|
|
|
17494
17608
|
);
|
|
17495
17609
|
}
|
|
17496
17610
|
function scopedSecretLoginHint(profileName2) {
|
|
17497
|
-
return `Have the owner approve a scoped login whose manifest names the secret: ${
|
|
17611
|
+
return `Have the owner approve a scoped login whose manifest names the secret: ${CLI_NAME26} --profile ${profileName2} auth login --method openkey --paste --manifest <manifest.json>. Pass the owner's code on stdin, newline-terminated.`;
|
|
17498
17612
|
}
|
|
17499
17613
|
async function runSecretOperationAttempt(label, operation) {
|
|
17500
17614
|
try {
|
|
@@ -17628,7 +17742,7 @@ ${message}`,
|
|
|
17628
17742
|
"AUTH_REQUIRED",
|
|
17629
17743
|
"Not signed in to TinyCloud.",
|
|
17630
17744
|
ExitCode.AUTH_REQUIRED,
|
|
17631
|
-
{ hint: `Sign in with: ${
|
|
17745
|
+
{ hint: `Sign in with: ${CLI_NAME26} --profile ${result.context.profile} auth login` }
|
|
17632
17746
|
);
|
|
17633
17747
|
}
|
|
17634
17748
|
if (result.error.code === "NODE_UNREACHABLE") {
|
|
@@ -18105,7 +18219,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
18105
18219
|
name: "Encryption network",
|
|
18106
18220
|
ok: false,
|
|
18107
18221
|
detail: `${networkName} not found`,
|
|
18108
|
-
hint: `${
|
|
18222
|
+
hint: `${CLI_NAME26} secrets network init ${networkName}`
|
|
18109
18223
|
}
|
|
18110
18224
|
];
|
|
18111
18225
|
let secret;
|
|
@@ -18151,7 +18265,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
18151
18265
|
name: "Secret access",
|
|
18152
18266
|
ok: false,
|
|
18153
18267
|
detail: notFound ? `${resolved.permissionPaths.vault} not found` : result.error.message,
|
|
18154
|
-
hint: notFound ? local ? `${
|
|
18268
|
+
hint: notFound ? local ? `${CLI_NAME26} secrets put ${resolved.name}${formatSecretScopeFlag(scopeOptions)} --stdin` : `${CLI_NAME26} secrets request ${resolved.name}${formatSecretScopeFlag(scopeOptions)}` : `Ask the owner profile to grant ${secretCapabilityAction("get")} and ${SECRET_DECRYPT_CAPABILITY}.`
|
|
18155
18269
|
});
|
|
18156
18270
|
}
|
|
18157
18271
|
} else {
|
|
@@ -18395,7 +18509,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
18395
18509
|
if (effectiveLocal(profile, options)) {
|
|
18396
18510
|
throw new CLIError(
|
|
18397
18511
|
"LOCAL_OWNER_PROFILE",
|
|
18398
|
-
`Profile "${ctx.profile}" uses a local key, which owns its secrets: store them with ${
|
|
18512
|
+
`Profile "${ctx.profile}" uses a local key, which owns its secrets: store them with ${CLI_NAME26} secrets put NAME --stdin instead of requesting them.`,
|
|
18399
18513
|
ExitCode.USAGE_ERROR
|
|
18400
18514
|
);
|
|
18401
18515
|
}
|
|
@@ -18518,7 +18632,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
18518
18632
|
|
|
18519
18633
|
// src/commands/share.ts
|
|
18520
18634
|
import { Option } from "commander";
|
|
18521
|
-
import { CLI_NAME as
|
|
18635
|
+
import { CLI_NAME as CLI_NAME27 } from "@tinycloud/operations/cli-name";
|
|
18522
18636
|
|
|
18523
18637
|
// ../share-sdk/dist/artifact.js
|
|
18524
18638
|
init_sha256();
|
|
@@ -36649,12 +36763,12 @@ function shareCliError(error, operation = "publish") {
|
|
|
36649
36763
|
const profileName2 = "profileName" in failure ? failure.profileName : void 0;
|
|
36650
36764
|
const localKey = "localKey" in failure && failure.localKey === true;
|
|
36651
36765
|
const profileHint = profileName2 === void 0 ? "" : `--profile ${profileName2} `;
|
|
36652
|
-
const loginHint = localKey ? `\`${
|
|
36766
|
+
const loginHint = localKey ? `\`${CLI_NAME27} ${profileHint}auth login --method local\`` : `\`${CLI_NAME27} ${profileHint}auth login --device --manifest builtin:share-publishing\` (or \`${CLI_NAME27} ${profileHint}enable share\`)`;
|
|
36653
36767
|
if (failure.kind === "caveated-session") {
|
|
36654
36768
|
const holder = profileName2 === void 0 ? "this session" : `profile ${profileName2}'s session`;
|
|
36655
36769
|
return new CLIError(
|
|
36656
36770
|
"PERMISSION_DENIED",
|
|
36657
|
-
`${holder} carries signed restrictions (caveats) on the authority an anyone-with-link share needs, so it cannot create the share link; nothing was shared. Approve Share publishing without restrictions on a new, dedicated profile (any unused name): \`${
|
|
36771
|
+
`${holder} carries signed restrictions (caveats) on the authority an anyone-with-link share needs, so it cannot create the share link; nothing was shared. Approve Share publishing without restrictions on a new, dedicated profile (any unused name): \`${CLI_NAME27} init --name publisher --key-only && ${CLI_NAME27} --profile publisher enable share\``,
|
|
36658
36772
|
5
|
|
36659
36773
|
);
|
|
36660
36774
|
}
|
|
@@ -37059,7 +37173,7 @@ init_constants();
|
|
|
37059
37173
|
init_sdk();
|
|
37060
37174
|
init_host();
|
|
37061
37175
|
init_theme();
|
|
37062
|
-
import { CLI_NAME as
|
|
37176
|
+
import { CLI_NAME as CLI_NAME28 } from "@tinycloud/operations/cli-name";
|
|
37063
37177
|
import { ACCOUNT_REGISTRY_SPACE } from "@tinycloud/node-sdk";
|
|
37064
37178
|
import { randomBytes as randomBytes9 } from "crypto";
|
|
37065
37179
|
import { mkdir as mkdir7, writeFile as writeFile4 } from "fs/promises";
|
|
@@ -37106,7 +37220,7 @@ function registerSpaceCommand(program) {
|
|
|
37106
37220
|
} else {
|
|
37107
37221
|
const profile = await ProfileManager.getProfile(ctx.profile);
|
|
37108
37222
|
const cap = "tinycloud.kv:account:spaces/:get,list";
|
|
37109
|
-
const hint = resolveProfilePosture(profile) === "delegate-session" ? `Ask the space owner to grant it. Create the request with: ${
|
|
37223
|
+
const hint = resolveProfilePosture(profile) === "delegate-session" ? `Ask the space owner to grant it. Create the request with: ${CLI_NAME28} --profile ${ctx.profile} auth request --cap ${cap} --emit` : `Grant it to this session with: ${CLI_NAME28} --profile ${ctx.profile} auth request --grant --cap ${cap}. The default OpenKey owner sign-in covers only the default space, so it includes neither.`;
|
|
37110
37224
|
throw new CLIError(
|
|
37111
37225
|
"PERMISSION_DENIED",
|
|
37112
37226
|
`Listing spaces needs tinycloud.kv/get and tinycloud.kv/list on the account space's spaces/ registry (${accountSpaceId ?? ACCOUNT_REGISTRY_SPACE}), or tinycloud.space/list. This session holds neither.`,
|
|
@@ -37143,10 +37257,10 @@ function registerSpaceCommand(program) {
|
|
|
37143
37257
|
|
|
37144
37258
|
A delegate cannot host a space \u2014 only its owner (root authority) can. This
|
|
37145
37259
|
emits a tinycloud.host.request artifact naming the space and its owner so you
|
|
37146
|
-
can hand it to the owner; they run \`${
|
|
37260
|
+
can hand it to the owner; they run \`${CLI_NAME28} space host <name>\` and confirm.
|
|
37147
37261
|
|
|
37148
37262
|
If you ARE the owner of the resolved space, this refuses and tells you to host
|
|
37149
|
-
it directly with \`${
|
|
37263
|
+
it directly with \`${CLI_NAME28} space host <name>\` (no request needed).
|
|
37150
37264
|
`).action(async (name, options, cmd) => {
|
|
37151
37265
|
try {
|
|
37152
37266
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -37157,7 +37271,7 @@ it directly with \`${CLI_NAME27} space host <name>\` (no request needed).
|
|
|
37157
37271
|
if (await isRootAuthority(spaceId, ctx.profile)) {
|
|
37158
37272
|
throw new CLIError(
|
|
37159
37273
|
"ALREADY_ROOT_AUTHORITY",
|
|
37160
|
-
`You are the owner of ${spaceId}. Host it directly: ${
|
|
37274
|
+
`You are the owner of ${spaceId}. Host it directly: ${CLI_NAME28} space host ${spaceName}`,
|
|
37161
37275
|
ExitCode.USAGE_ERROR
|
|
37162
37276
|
);
|
|
37163
37277
|
}
|
|
@@ -37243,7 +37357,7 @@ init_sdk();
|
|
|
37243
37357
|
init_space();
|
|
37244
37358
|
init_host();
|
|
37245
37359
|
init_theme();
|
|
37246
|
-
import { CLI_NAME as
|
|
37360
|
+
import { CLI_NAME as CLI_NAME29 } from "@tinycloud/operations/cli-name";
|
|
37247
37361
|
import { writeFile as writeFile5 } from "fs/promises";
|
|
37248
37362
|
import { resolve as resolve7 } from "path";
|
|
37249
37363
|
async function dbHandle(node, dbName, spaceInput, profileName2) {
|
|
@@ -37267,12 +37381,12 @@ database for simple apps, or pass --db to target a named database. Pass
|
|
|
37267
37381
|
--space to target a non-primary space (e.g. the manifest "applications" space).
|
|
37268
37382
|
|
|
37269
37383
|
Common workflows:
|
|
37270
|
-
$ ${
|
|
37271
|
-
$ ${
|
|
37272
|
-
$ ${
|
|
37273
|
-
$ ${
|
|
37274
|
-
$ ${
|
|
37275
|
-
$ ${
|
|
37384
|
+
$ ${CLI_NAME29} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
|
|
37385
|
+
$ ${CLI_NAME29} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["ship docs"]'
|
|
37386
|
+
$ ${CLI_NAME29} sql query "SELECT id, body FROM notes ORDER BY id"
|
|
37387
|
+
$ ${CLI_NAME29} sql query "SELECT * FROM events WHERE type = ?" --db analytics --params '["signup"]'
|
|
37388
|
+
$ ${CLI_NAME29} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
|
|
37389
|
+
$ ${CLI_NAME29} sql export --db analytics --output analytics.db
|
|
37276
37390
|
|
|
37277
37391
|
Commands:
|
|
37278
37392
|
query Read rows with SELECT statements
|
|
@@ -37289,10 +37403,10 @@ Tips:
|
|
|
37289
37403
|
sql.command("query <sql>").description("Run a read-only SELECT query").option("--db <name>", "SQLite database name within the current space", "default").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").option("--params <json>", "Bind parameters as a JSON array for ? placeholders").addHelpText("after", `
|
|
37290
37404
|
|
|
37291
37405
|
Examples:
|
|
37292
|
-
$ ${
|
|
37293
|
-
$ ${
|
|
37294
|
-
$ ${
|
|
37295
|
-
$ ${
|
|
37406
|
+
$ ${CLI_NAME29} sql query "SELECT * FROM notes ORDER BY id"
|
|
37407
|
+
$ ${CLI_NAME29} sql query "SELECT * FROM notes WHERE id = ?" --params '[42]'
|
|
37408
|
+
$ ${CLI_NAME29} sql query "SELECT count(*) AS total FROM events" --db analytics --json
|
|
37409
|
+
$ ${CLI_NAME29} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
|
|
37296
37410
|
|
|
37297
37411
|
Output:
|
|
37298
37412
|
Human output is formatted as a table. Piped output or --json returns
|
|
@@ -37333,11 +37447,11 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
|
|
|
37333
37447
|
sql.command("execute <sql>").description("Run a write or schema statement").option("--db <name>", "SQLite database name within the current space", "default").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").option("--params <json>", "Bind parameters as a JSON array for ? placeholders").addHelpText("after", `
|
|
37334
37448
|
|
|
37335
37449
|
Examples:
|
|
37336
|
-
$ ${
|
|
37337
|
-
$ ${
|
|
37338
|
-
$ ${
|
|
37339
|
-
$ ${
|
|
37340
|
-
$ ${
|
|
37450
|
+
$ ${CLI_NAME29} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
|
|
37451
|
+
$ ${CLI_NAME29} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["first note"]'
|
|
37452
|
+
$ ${CLI_NAME29} sql execute "UPDATE notes SET body = ? WHERE id = ?" --params '["edited", 1]'
|
|
37453
|
+
$ ${CLI_NAME29} sql execute "DROP TABLE old_notes" --db archive
|
|
37454
|
+
$ ${CLI_NAME29} sql execute "DELETE FROM conversation WHERE id = ?" --space applications --db xyz.tinycloud.listen/conversations --params '["abc"]'
|
|
37341
37455
|
|
|
37342
37456
|
Output:
|
|
37343
37457
|
Returns JSON with the changed row count and last inserted row id when available.
|
|
@@ -37366,9 +37480,9 @@ Output:
|
|
|
37366
37480
|
sql.command("export").description("Export a SQLite database as a binary .db file").option("--db <name>", "SQLite database name within the current space", "default").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").option("-o, --output <file>", "Output file path", "export.db").addHelpText("after", `
|
|
37367
37481
|
|
|
37368
37482
|
Examples:
|
|
37369
|
-
$ ${
|
|
37370
|
-
$ ${
|
|
37371
|
-
$ ${
|
|
37483
|
+
$ ${CLI_NAME29} sql export
|
|
37484
|
+
$ ${CLI_NAME29} sql export --db analytics --output analytics.db
|
|
37485
|
+
$ ${CLI_NAME29} sql export --space applications --db xyz.tinycloud.listen/conversations --output listen.db
|
|
37372
37486
|
|
|
37373
37487
|
Output:
|
|
37374
37488
|
Writes the database file locally and returns JSON with the path and size.
|
|
@@ -37401,19 +37515,19 @@ Output:
|
|
|
37401
37515
|
sql.command("copy").description("Copy rows between SQL databases (optionally across spaces)").requiredOption("--from-db <name>", "Source database name").requiredOption("--to-db <name>", "Destination database name").option("--from-space <name|uri>", "Source space (defaults to primary)").option("--to-space <name|uri>", "Destination space (defaults to primary)").option("--table <name...>", "Restrict copy to specific tables (repeat or comma-separated)").option("--dry-run", "Print the plan without writing", false).addHelpText("after", `
|
|
37402
37516
|
|
|
37403
37517
|
Examples:
|
|
37404
|
-
$ ${
|
|
37518
|
+
$ ${CLI_NAME29} sql copy --from-db com.tinycloud.conversation-sync/conversations \\
|
|
37405
37519
|
--to-db xyz.tinycloud.listen/conversations \\
|
|
37406
37520
|
--space applications --dry-run
|
|
37407
|
-
$ ${
|
|
37521
|
+
$ ${CLI_NAME29} sql copy --from-space applications --from-db com.foo/data \\
|
|
37408
37522
|
--to-space applications --to-db com.bar/data \\
|
|
37409
37523
|
--table conversation --table participant
|
|
37410
37524
|
|
|
37411
37525
|
Notes:
|
|
37412
37526
|
- Refuses to run when (resolved space, db) is identical for source and destination.
|
|
37413
|
-
- Does NOT create destination tables. Run the target app once (or use \`${
|
|
37527
|
+
- Does NOT create destination tables. Run the target app once (or use \`${CLI_NAME29} sql execute\`)
|
|
37414
37528
|
to materialize the schema before copying.
|
|
37415
37529
|
- One row at a time; suitable for small/medium datasets. Large copies should
|
|
37416
|
-
use \`${
|
|
37530
|
+
use \`${CLI_NAME29} sql export\` + bulk import.
|
|
37417
37531
|
- Authorization: the active session/delegation must cover sql/read on source
|
|
37418
37532
|
AND sql/write on destination. Otherwise the relevant operation will fail.
|
|
37419
37533
|
`).action(async (options, cmd) => {
|
|
@@ -37513,7 +37627,7 @@ init_errors();
|
|
|
37513
37627
|
init_formatter();
|
|
37514
37628
|
init_theme();
|
|
37515
37629
|
init_permissions();
|
|
37516
|
-
import { CLI_NAME as
|
|
37630
|
+
import { CLI_NAME as CLI_NAME30 } from "@tinycloud/operations/cli-name";
|
|
37517
37631
|
import {
|
|
37518
37632
|
NodeWasmBindings as NodeWasmBindings2
|
|
37519
37633
|
} from "@tinycloud/node-sdk";
|
|
@@ -37787,7 +37901,7 @@ function formatStatus(summary) {
|
|
|
37787
37901
|
lines.push(`Default profile: ${theme.value(summary.defaultProfile)}`);
|
|
37788
37902
|
lines.push("");
|
|
37789
37903
|
if (summary.profiles.length === 0) {
|
|
37790
|
-
lines.push(theme.muted(`No profiles configured. Run: ${
|
|
37904
|
+
lines.push(theme.muted(`No profiles configured. Run: ${CLI_NAME30} init`));
|
|
37791
37905
|
return `${lines.join("\n")}
|
|
37792
37906
|
`;
|
|
37793
37907
|
}
|