@tinycloud/cli 1.2.2 → 1.2.3-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/bin/tinycloud +3 -0
- package/dist/courier-entry.js +7 -2
- package/dist/courier-entry.js.map +1 -1
- package/dist/index.js +53 -37
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +212 -152
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +5 -4
- package/skills/tc-cli/SKILL.md +3 -3
package/dist/legacy-entry.js
CHANGED
|
@@ -319,13 +319,14 @@ var init_warnings = __esm({
|
|
|
319
319
|
|
|
320
320
|
// src/output/formatter.ts
|
|
321
321
|
import ora from "ora";
|
|
322
|
+
import { CLI_NAME } from "@tinycloud/operations/cli-name";
|
|
322
323
|
function outputJson(data) {
|
|
323
324
|
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
324
325
|
}
|
|
325
326
|
function warningLine(warnings) {
|
|
326
327
|
const which = warnings.map((warning) => `${warning.grantCid ?? "unidentified"}: ${warning.reason}`).join(", ");
|
|
327
328
|
const subject = warnings.length === 1 ? "A stored grant was" : `${warnings.length} stored grants were`;
|
|
328
|
-
return `${subject} not used (${which}). Approve access again when a command needs it; remove the old grant with
|
|
329
|
+
return `${subject} not used (${which}). Approve access again when a command needs it; remove the old grant with ${CLI_NAME} auth grants remove <cid>.`;
|
|
329
330
|
}
|
|
330
331
|
function outputError(code3, message, hint, details = {}) {
|
|
331
332
|
const { meta, warnings = [], setup, recovery, progress } = details;
|
|
@@ -432,14 +433,6 @@ function formatBytes(bytes) {
|
|
|
432
433
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
433
434
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
434
435
|
}
|
|
435
|
-
function formatTimeAgo(date) {
|
|
436
|
-
const d = typeof date === "string" ? new Date(date) : date;
|
|
437
|
-
const seconds = Math.floor((Date.now() - d.getTime()) / 1e3);
|
|
438
|
-
if (seconds < 60) return "just now";
|
|
439
|
-
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
|
|
440
|
-
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
|
|
441
|
-
return `${Math.floor(seconds / 86400)}d ago`;
|
|
442
|
-
}
|
|
443
436
|
var WARNING_CODE, WARNING_REASON, DELEGATION_CID, JSON_REQUESTED;
|
|
444
437
|
var init_formatter = __esm({
|
|
445
438
|
"src/output/formatter.ts"() {
|
|
@@ -538,6 +531,7 @@ import { readFileSync, readdirSync } from "fs";
|
|
|
538
531
|
import { join as join2 } from "path";
|
|
539
532
|
import { ProfileDeletedError, ProfileLockTimeoutError } from "@tinycloud/operations/state";
|
|
540
533
|
import { isSafeRecovery } from "@tinycloud/operations/recovery";
|
|
534
|
+
import { CLI_NAME as CLI_NAME2 } from "@tinycloud/operations/cli-name";
|
|
541
535
|
import {
|
|
542
536
|
buildSecretRequestUrl,
|
|
543
537
|
isSafeRequestAccessRecovery,
|
|
@@ -616,7 +610,7 @@ function missingPrivateKeyError() {
|
|
|
616
610
|
"AUTH_REQUIRED",
|
|
617
611
|
`Profile "${profileName2}" cannot restore its session because its private key material is missing.`,
|
|
618
612
|
ExitCode.AUTH_REQUIRED,
|
|
619
|
-
{ hint: `Sign in again with:
|
|
613
|
+
{ hint: `Sign in again with: ${CLI_NAME2} --profile ${profileName2} auth login --method openkey` }
|
|
620
614
|
);
|
|
621
615
|
}
|
|
622
616
|
function storageFullError(error, progress) {
|
|
@@ -707,7 +701,7 @@ function buildAuthHint(error) {
|
|
|
707
701
|
if (!spec) return void 0;
|
|
708
702
|
return [
|
|
709
703
|
"The active session is missing a TinyCloud capability.",
|
|
710
|
-
`Request it with:
|
|
704
|
+
`Request it with: ${CLI_NAME2} auth request --cap '${spec.replaceAll("'", "'\\''")}'`,
|
|
711
705
|
"Then retry the original command."
|
|
712
706
|
].join("\n");
|
|
713
707
|
}
|
|
@@ -750,12 +744,12 @@ function buildNetworkHint() {
|
|
|
750
744
|
const lines = [];
|
|
751
745
|
lines.push(activeHost ? `Active profile "${activeName}" \u2192 ${activeHost}` : `Active profile "${activeName}"`);
|
|
752
746
|
if (others.length === 0) {
|
|
753
|
-
lines.push(`No other profiles configured. Run
|
|
747
|
+
lines.push(`No other profiles configured. Run \`${CLI_NAME2} profile create <name>\` or \`${CLI_NAME2} init\`.`);
|
|
754
748
|
} else {
|
|
755
749
|
lines.push(`Switch to a reachable profile:`);
|
|
756
750
|
const longest = Math.max(...others.map((p) => p.name.length));
|
|
757
751
|
for (const { name, host } of others) {
|
|
758
|
-
lines.push(`
|
|
752
|
+
lines.push(` ${CLI_NAME2} profile switch ${name.padEnd(longest)} # ${host}`);
|
|
759
753
|
}
|
|
760
754
|
}
|
|
761
755
|
lines.push(`Or override per-command with --host or TC_HOST.`);
|
|
@@ -815,6 +809,7 @@ var init_types = __esm({
|
|
|
815
809
|
});
|
|
816
810
|
|
|
817
811
|
// src/lib/space.ts
|
|
812
|
+
import { CLI_NAME as CLI_NAME3 } from "@tinycloud/operations/cli-name";
|
|
818
813
|
function canonicalizeAddress(address) {
|
|
819
814
|
const trimmed = address.trim();
|
|
820
815
|
return trimmed.startsWith("0x") ? `0x${trimmed.slice(2).toLowerCase()}` : trimmed.toLowerCase();
|
|
@@ -862,7 +857,7 @@ function resolveAddress(profile, session) {
|
|
|
862
857
|
}
|
|
863
858
|
throw new CLIError(
|
|
864
859
|
"ADDRESS_UNKNOWN",
|
|
865
|
-
`Cannot determine Ethereum address for profile "${profile.name}". Run
|
|
860
|
+
`Cannot determine Ethereum address for profile "${profile.name}". Run \`${CLI_NAME3} auth login\` to refresh the session.`,
|
|
866
861
|
ExitCode.AUTH_REQUIRED
|
|
867
862
|
);
|
|
868
863
|
}
|
|
@@ -14783,6 +14778,7 @@ var init_permissions = __esm({
|
|
|
14783
14778
|
|
|
14784
14779
|
// src/auth/session-expired.ts
|
|
14785
14780
|
import { signInRecovery } from "@tinycloud/operations/recovery";
|
|
14781
|
+
import { CLI_NAME as CLI_NAME4 } from "@tinycloud/operations/cli-name";
|
|
14786
14782
|
function signInAgainHint(profileName2, posture) {
|
|
14787
14783
|
const delegate = posture === "delegate-session";
|
|
14788
14784
|
const { command } = signInRecovery(
|
|
@@ -14790,7 +14786,7 @@ function signInAgainHint(profileName2, posture) {
|
|
|
14790
14786
|
delegate ? "delegate-session" : posture === "local-owner-key" ? "local-owner-key" : "owner-openkey"
|
|
14791
14787
|
);
|
|
14792
14788
|
if (command === void 0) {
|
|
14793
|
-
return delegate ? `Have the owner approve a new scoped login for profile ${JSON.stringify(profileName2)} with
|
|
14789
|
+
return delegate ? `Have the owner approve a new scoped login for profile ${JSON.stringify(profileName2)} with ${CLI_NAME4} auth login --method openkey --paste --manifest <manifest.json>.` : `Sign in again to profile ${JSON.stringify(profileName2)} with ${CLI_NAME4} auth login.`;
|
|
14794
14790
|
}
|
|
14795
14791
|
return delegate ? `Have the owner approve a new scoped login: ${command}. Pass the owner's code on stdin, newline-terminated.` : `Sign in again with: ${command}`;
|
|
14796
14792
|
}
|
|
@@ -14835,6 +14831,7 @@ var init_delegation_expiry = __esm({
|
|
|
14835
14831
|
});
|
|
14836
14832
|
|
|
14837
14833
|
// src/lib/replication-log.ts
|
|
14834
|
+
import { CLI_NAME as CLI_NAME5 } from "@tinycloud/operations/cli-name";
|
|
14838
14835
|
import { appendFileSync, chmodSync, mkdirSync, renameSync, statSync, writeFileSync } from "fs";
|
|
14839
14836
|
import { dirname as dirname2, join as join4 } from "path";
|
|
14840
14837
|
function appendReplicationEvent(root, event) {
|
|
@@ -14897,7 +14894,7 @@ function createReplicationEventSink(profileRoot, options) {
|
|
|
14897
14894
|
}
|
|
14898
14895
|
if (event.type === "replication.read" && (event.pendingState === "in_flight" || event.pendingState === "ambiguous") && !options.quiet && !pinnedNotices.has(event.key)) {
|
|
14899
14896
|
pinnedNotices.add(event.key);
|
|
14900
|
-
process.stderr.write(`[replication] ${event.key} reads from the network: an earlier write's outcome is unknown (${event.pendingState}${event.code ? `, ${event.code}` : ""}). Review:
|
|
14897
|
+
process.stderr.write(`[replication] ${event.key} reads from the network: an earlier write's outcome is unknown (${event.pendingState}${event.code ? `, ${event.code}` : ""}). Review: ${CLI_NAME5} replica report
|
|
14901
14898
|
`);
|
|
14902
14899
|
}
|
|
14903
14900
|
};
|
|
@@ -17737,6 +17734,7 @@ var init_dist3 = __esm({
|
|
|
17737
17734
|
|
|
17738
17735
|
// src/lib/replication-config.ts
|
|
17739
17736
|
import { join as join5 } from "path";
|
|
17737
|
+
import { CLI_NAME as CLI_NAME6 } from "@tinycloud/operations/cli-name";
|
|
17740
17738
|
import { profilePath, withProfileLock as withProfileLock2 } from "@tinycloud/operations/state";
|
|
17741
17739
|
import { sqliteReplicaStorage } from "@tinycloud/node-sdk";
|
|
17742
17740
|
function createCliReplicationConfig(profile, settings, output2, enabled) {
|
|
@@ -17751,7 +17749,7 @@ function createCliReplicationConfig(profile, settings, output2, enabled) {
|
|
|
17751
17749
|
"INVALID_REPLICATION_PREFIX",
|
|
17752
17750
|
error instanceof Error ? error.message : String(error),
|
|
17753
17751
|
ExitCode.USAGE_ERROR,
|
|
17754
|
-
{ hint: `Replace the saved replication prefix with
|
|
17752
|
+
{ hint: `Replace the saved replication prefix with \`${CLI_NAME6} auth login --replication-prefix ${replacement}\`; purging a replica alone does not change this profile setting.` }
|
|
17755
17753
|
);
|
|
17756
17754
|
}
|
|
17757
17755
|
}
|
|
@@ -17797,6 +17795,7 @@ __export(sdk_exports, {
|
|
|
17797
17795
|
storedSessionVerifiesLocally: () => storedSessionVerifiesLocally
|
|
17798
17796
|
});
|
|
17799
17797
|
import { TinyCloudNode, signedCompactUcanExpiry } from "@tinycloud/node-sdk";
|
|
17798
|
+
import { CLI_NAME as CLI_NAME7 } from "@tinycloud/operations/cli-name";
|
|
17800
17799
|
import { ownerSessionLacksProof } from "@tinycloud/operations/profile";
|
|
17801
17800
|
function jwkHasPrivateParameter(jwk) {
|
|
17802
17801
|
if (!jwk || typeof jwk !== "object") return false;
|
|
@@ -17819,7 +17818,7 @@ function signerJwkForProfile(profileName2, sessionJwk, key) {
|
|
|
17819
17818
|
`Profile "${profileName2}" cannot restore its session because its private key material is missing.`,
|
|
17820
17819
|
ExitCode.AUTH_REQUIRED,
|
|
17821
17820
|
{
|
|
17822
|
-
hint: `Sign in again with:
|
|
17821
|
+
hint: `Sign in again with: ${CLI_NAME7} --profile ${profileName2} auth login --method openkey`
|
|
17823
17822
|
}
|
|
17824
17823
|
);
|
|
17825
17824
|
}
|
|
@@ -17906,7 +17905,7 @@ async function createSDKInstance(ctx, options) {
|
|
|
17906
17905
|
if (!key && !effectivePrivateKey && !(profile?.authMethod === "openkey" && session !== null)) {
|
|
17907
17906
|
throw new CLIError(
|
|
17908
17907
|
"AUTH_REQUIRED",
|
|
17909
|
-
`No key found for profile "${ctx.profile}". Run
|
|
17908
|
+
`No key found for profile "${ctx.profile}". Run \`${CLI_NAME7} --profile ${ctx.profile} auth rotate\` to create a new key and sign in.`,
|
|
17910
17909
|
ExitCode.AUTH_REQUIRED
|
|
17911
17910
|
);
|
|
17912
17911
|
}
|
|
@@ -18025,7 +18024,7 @@ async function bootstrapDelegatedSession(ctx, delegation) {
|
|
|
18025
18024
|
return restoreBeforeBootstrap(ctx.profile, written.previousProfile);
|
|
18026
18025
|
}, { timeoutMs: PROFILE_COMMIT_LOCK_TIMEOUT_MS }).catch((error) => (
|
|
18027
18026
|
// The lock or a read failed: the import's error stays the one reported.
|
|
18028
|
-
`Rolling back the provisional session of profile "${ctx.profile}" could not run (${failureName(error)}); check
|
|
18027
|
+
`Rolling back the provisional session of profile "${ctx.profile}" could not run (${failureName(error)}); check \`${CLI_NAME7} --profile ${ctx.profile} context\`.`
|
|
18029
18028
|
));
|
|
18030
18029
|
throw annotate(reported, note);
|
|
18031
18030
|
};
|
|
@@ -18055,7 +18054,7 @@ async function restoreBeforeBootstrap(profileName2, previousProfile) {
|
|
|
18055
18054
|
});
|
|
18056
18055
|
}
|
|
18057
18056
|
if (failures.length === 0) return void 0;
|
|
18058
|
-
return `Rolling back the provisional session of profile "${profileName2}" failed too (${failures.join("; ")}); check
|
|
18057
|
+
return `Rolling back the provisional session of profile "${profileName2}" failed too (${failures.join("; ")}); check \`${CLI_NAME7} --profile ${profileName2} context\`.`;
|
|
18059
18058
|
}
|
|
18060
18059
|
function failureName(error) {
|
|
18061
18060
|
if (error instanceof SyntaxError) return "a profile file is not valid JSON";
|
|
@@ -18079,7 +18078,7 @@ async function ensureAuthenticated(ctx, options) {
|
|
|
18079
18078
|
if (!session) {
|
|
18080
18079
|
throw new CLIError(
|
|
18081
18080
|
"AUTH_REQUIRED",
|
|
18082
|
-
`Not authenticated. Run
|
|
18081
|
+
`Not authenticated. Run \`${CLI_NAME7} auth login\` or \`${CLI_NAME7} init\` first.`,
|
|
18083
18082
|
ExitCode.AUTH_REQUIRED
|
|
18084
18083
|
);
|
|
18085
18084
|
}
|
|
@@ -18116,6 +18115,7 @@ __export(host_exports, {
|
|
|
18116
18115
|
spaceNameFromUri: () => spaceNameFromUri,
|
|
18117
18116
|
unhostedSpaceError: () => unhostedSpaceError
|
|
18118
18117
|
});
|
|
18118
|
+
import { CLI_NAME as CLI_NAME8 } from "@tinycloud/operations/cli-name";
|
|
18119
18119
|
import {
|
|
18120
18120
|
discoverLocalTinyCloudNode
|
|
18121
18121
|
} from "@tinycloud/sdk-core";
|
|
@@ -18209,13 +18209,13 @@ async function unhostedSpaceError(error, spaceUri, profileName2) {
|
|
|
18209
18209
|
const owner = await isRootAuthority(spaceUri, profileName2);
|
|
18210
18210
|
const hint = owner ? [
|
|
18211
18211
|
"You are the owner. Host it once:",
|
|
18212
|
-
`
|
|
18212
|
+
` ${CLI_NAME8} space host ${spaceName}`,
|
|
18213
18213
|
"Then retry."
|
|
18214
18214
|
].join("\n") : [
|
|
18215
18215
|
"You are a delegate and CANNOT host this space \u2014 only its owner can.",
|
|
18216
18216
|
"Emit a host request:",
|
|
18217
|
-
`
|
|
18218
|
-
|
|
18217
|
+
` ${CLI_NAME8} space host-request ${spaceName} --emit ./host-request.json`,
|
|
18218
|
+
`Send it to the owner; they run \`${CLI_NAME8} space host\` and confirm. Then retry.`
|
|
18219
18219
|
].join("\n");
|
|
18220
18220
|
const message = owner ? `Space '${spaceName}' (${spaceUri}) is not hosted.` : `Space '${spaceName}' (owner ${ownerDidFromSpaceUri(spaceUri) ?? spaceUri}) is not hosted.`;
|
|
18221
18221
|
return new CLIError("SPACE_NOT_HOSTED", message, ExitCode.ERROR, { hint });
|
|
@@ -18240,6 +18240,7 @@ var init_host = __esm({
|
|
|
18240
18240
|
|
|
18241
18241
|
// src/config/profiles.ts
|
|
18242
18242
|
import { chmod as chmod2, lstat, readdir as readdir2, rm as rm2, rmdir } from "fs/promises";
|
|
18243
|
+
import { CLI_NAME as CLI_NAME9 } from "@tinycloud/operations/cli-name";
|
|
18243
18244
|
import { join as join6 } from "path";
|
|
18244
18245
|
import {
|
|
18245
18246
|
profilePath as profilePath2,
|
|
@@ -18325,7 +18326,7 @@ var init_profiles = __esm({
|
|
|
18325
18326
|
if (!profile) {
|
|
18326
18327
|
throw new CLIError(
|
|
18327
18328
|
"PROFILE_NOT_FOUND",
|
|
18328
|
-
`Profile "${name}" does not exist. Create it with
|
|
18329
|
+
`Profile "${name}" does not exist. Create it with \`${CLI_NAME9} init --name ${name}\`.`
|
|
18329
18330
|
);
|
|
18330
18331
|
}
|
|
18331
18332
|
return profile;
|
|
@@ -18383,7 +18384,7 @@ var init_profiles = __esm({
|
|
|
18383
18384
|
if (config.defaultProfile === name) {
|
|
18384
18385
|
throw new CLIError(
|
|
18385
18386
|
"PROFILE_DELETE_DEFAULT",
|
|
18386
|
-
`Cannot delete the default profile "${name}". Change the default first with
|
|
18387
|
+
`Cannot delete the default profile "${name}". Change the default first with \`${CLI_NAME9} profile default <other>\`.`
|
|
18387
18388
|
);
|
|
18388
18389
|
}
|
|
18389
18390
|
const kind = await lstat(profileDir).then((stats) => stats.isSymbolicLink() ? "link" : "present", (error) => {
|
|
@@ -18840,6 +18841,7 @@ function formatSpace(space) {
|
|
|
18840
18841
|
}
|
|
18841
18842
|
|
|
18842
18843
|
// src/commands/auth.ts
|
|
18844
|
+
import { CLI_NAME as CLI_NAME14 } from "@tinycloud/operations/cli-name";
|
|
18843
18845
|
import { get as httpGet } from "http";
|
|
18844
18846
|
import { get as httpsGet } from "https";
|
|
18845
18847
|
import { spawn } from "child_process";
|
|
@@ -20310,7 +20312,8 @@ async function startAuthFlow(did, options = {}) {
|
|
|
20310
20312
|
}
|
|
20311
20313
|
try {
|
|
20312
20314
|
return await callbackFlow(did, options);
|
|
20313
|
-
} catch {
|
|
20315
|
+
} catch (error) {
|
|
20316
|
+
if (error instanceof CLIError && error.code === "CANCELLED") throw error;
|
|
20314
20317
|
if (process.stdin.isTTY) {
|
|
20315
20318
|
console.error("Could not open browser. Falling back to manual paste mode.");
|
|
20316
20319
|
return pasteFlow(did, options);
|
|
@@ -20555,6 +20558,7 @@ If the browser can't connect back, enter the short code or paste the full delega
|
|
|
20555
20558
|
resolving = false;
|
|
20556
20559
|
}
|
|
20557
20560
|
});
|
|
20561
|
+
rl.on("SIGINT", () => settle({ error: loginCancelled() }));
|
|
20558
20562
|
}
|
|
20559
20563
|
});
|
|
20560
20564
|
timeout = setTimeout(() => {
|
|
@@ -20594,10 +20598,19 @@ Open this URL in a browser to authenticate:
|
|
|
20594
20598
|
}
|
|
20595
20599
|
));
|
|
20596
20600
|
});
|
|
20601
|
+
rl.on("SIGINT", () => {
|
|
20602
|
+
if (answered) return;
|
|
20603
|
+
answered = true;
|
|
20604
|
+
rl.close();
|
|
20605
|
+
reject(loginCancelled());
|
|
20606
|
+
});
|
|
20597
20607
|
rl.setPrompt("Enter short code or paste full delegation code: ");
|
|
20598
20608
|
rl.prompt();
|
|
20599
20609
|
});
|
|
20600
20610
|
}
|
|
20611
|
+
function loginCancelled() {
|
|
20612
|
+
return new CLIError("CANCELLED", "Interrupted: tc auth login stopped before approval. No session was saved.", ExitCode.CANCELLED);
|
|
20613
|
+
}
|
|
20601
20614
|
|
|
20602
20615
|
// src/auth/device-auth.ts
|
|
20603
20616
|
init_constants();
|
|
@@ -21023,13 +21036,14 @@ init_constants();
|
|
|
21023
21036
|
init_profiles();
|
|
21024
21037
|
init_errors();
|
|
21025
21038
|
init_space();
|
|
21039
|
+
import { CLI_NAME as CLI_NAME10 } from "@tinycloud/operations/cli-name";
|
|
21026
21040
|
import { isLocalOwnerProfile as isLocalOwnerProfile3, ownerSessionLacksProof as ownerSessionLacksProof3 } from "@tinycloud/operations/profile";
|
|
21027
21041
|
import { ProfileLockTimeoutError as ProfileLockTimeoutError2 } from "@tinycloud/operations/state";
|
|
21028
21042
|
function assertNotLocalOwner(profileName2, profile, flow) {
|
|
21029
21043
|
if (profile === null || !isLocalOwnerProfile3(profile)) return;
|
|
21030
21044
|
throw new CLIError(
|
|
21031
21045
|
"LOCAL_OWNER_PROFILE",
|
|
21032
|
-
`Profile "${profileName2}" holds a local owner key. ${flow} would turn it into an OpenKey profile while keeping that key. Use a separate profile:
|
|
21046
|
+
`Profile "${profileName2}" holds a local owner key. ${flow} would turn it into an OpenKey profile while keeping that key. Use a separate profile: \`${CLI_NAME10} init --name publisher --key-only\`, then \`${CLI_NAME10} --profile publisher auth login --device --manifest ...\`.`,
|
|
21033
21047
|
ExitCode.USAGE_ERROR
|
|
21034
21048
|
);
|
|
21035
21049
|
}
|
|
@@ -21064,7 +21078,7 @@ function assertSessionReplaceable(profileName2, snapshot, ownerDid, scope, newEx
|
|
|
21064
21078
|
if (problem !== void 0) {
|
|
21065
21079
|
throw new CLIError(
|
|
21066
21080
|
"PROFILE_STATE_INCONSISTENT",
|
|
21067
|
-
`Profile "${profileName2}" is inconsistent (${problem}), possibly from an interrupted write. Nothing was saved. Check
|
|
21081
|
+
`Profile "${profileName2}" is inconsistent (${problem}), possibly from an interrupted write. Nothing was saved. Check \`${CLI_NAME10} --profile ${profileName2} context\`, then pass --replace-session to replace this state, or use a new profile.`,
|
|
21068
21082
|
ExitCode.ERROR
|
|
21069
21083
|
);
|
|
21070
21084
|
}
|
|
@@ -21080,7 +21094,7 @@ function assertSessionReplaceable(profileName2, snapshot, ownerDid, scope, newEx
|
|
|
21080
21094
|
const space = typeof session.spaceId === "string" ? session.spaceId : "an unknown space";
|
|
21081
21095
|
throw new CLIError(
|
|
21082
21096
|
"SESSION_IN_USE",
|
|
21083
|
-
`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 (
|
|
21097
|
+
`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_NAME10} init --name publisher --key-only\`, then \`${CLI_NAME10} --profile publisher auth login --device --manifest ...\`), or pass --replace-session to replace this session.`,
|
|
21084
21098
|
ExitCode.USAGE_ERROR
|
|
21085
21099
|
);
|
|
21086
21100
|
}
|
|
@@ -21109,7 +21123,7 @@ async function commitLogin(profileName2, snapshot, commit) {
|
|
|
21109
21123
|
if (canonicalJson(current.profile) !== canonicalJson(snapshot.profile) || canonicalJson(current.key) !== canonicalJson(snapshot.key) || canonicalJson(current.session) !== canonicalJson(snapshot.session)) {
|
|
21110
21124
|
throw new CLIError(
|
|
21111
21125
|
"PROFILE_CHANGED_DURING_LOGIN",
|
|
21112
|
-
`Profile "${profileName2}" changed while this login was in progress (another login, key rotation or logout). Nothing was saved; check
|
|
21126
|
+
`Profile "${profileName2}" changed while this login was in progress (another login, key rotation or logout). Nothing was saved; check \`${CLI_NAME10} --profile ${profileName2} context\` and run the login again if it is still needed.`,
|
|
21113
21127
|
ExitCode.ERROR
|
|
21114
21128
|
);
|
|
21115
21129
|
}
|
|
@@ -21126,7 +21140,7 @@ async function commitLogin(profileName2, snapshot, commit) {
|
|
|
21126
21140
|
if (failures.length === 0) throw error;
|
|
21127
21141
|
throw new CLIError(
|
|
21128
21142
|
"PROFILE_STATE_INCONSISTENT",
|
|
21129
|
-
`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
|
|
21143
|
+
`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_NAME10} --profile ${profileName2} context\`, then run the login again with --replace-session, or use a new profile.`,
|
|
21130
21144
|
ExitCode.ERROR
|
|
21131
21145
|
);
|
|
21132
21146
|
}
|
|
@@ -21142,6 +21156,7 @@ async function commitLogin(profileName2, snapshot, commit) {
|
|
|
21142
21156
|
|
|
21143
21157
|
// src/auth/owner-key.ts
|
|
21144
21158
|
init_theme();
|
|
21159
|
+
import { CLI_NAME as CLI_NAME11 } from "@tinycloud/operations/cli-name";
|
|
21145
21160
|
function openKeyPrimaryFlag(delegation) {
|
|
21146
21161
|
return typeof delegation.primary === "boolean" ? delegation.primary : void 0;
|
|
21147
21162
|
}
|
|
@@ -21152,7 +21167,7 @@ function withOwnerKeyPrimary(profile, primary) {
|
|
|
21152
21167
|
function nonPrimaryKeyWarning(ownerDid) {
|
|
21153
21168
|
const address = ownerDid.split(":")[4] ?? ownerDid;
|
|
21154
21169
|
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.
|
|
21155
|
-
To use your primary key, log in again and choose the primary key in OpenKey, for example into a new profile:
|
|
21170
|
+
To use your primary key, log in again and choose the primary key in OpenKey, for example into a new profile: ${CLI_NAME11} init --name <profile>
|
|
21156
21171
|
`;
|
|
21157
21172
|
}
|
|
21158
21173
|
function warnIfNotPrimaryKey(ownerDid, primary) {
|
|
@@ -21161,7 +21176,7 @@ function warnIfNotPrimaryKey(ownerDid, primary) {
|
|
|
21161
21176
|
}
|
|
21162
21177
|
function announceFirstOwner(profileName2, expectedOwner, ownerDid) {
|
|
21163
21178
|
if (expectedOwner !== void 0 || ownerDid === void 0) return;
|
|
21164
|
-
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
|
|
21179
|
+
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_NAME11} init.
|
|
21165
21180
|
`);
|
|
21166
21181
|
}
|
|
21167
21182
|
function ownerLoginPermissions(owner) {
|
|
@@ -21585,6 +21600,7 @@ init_types();
|
|
|
21585
21600
|
init_constants();
|
|
21586
21601
|
init_permissions();
|
|
21587
21602
|
init_sdk();
|
|
21603
|
+
import { CLI_NAME as CLI_NAME13 } from "@tinycloud/operations/cli-name";
|
|
21588
21604
|
import { TinyCloudNode as TinyCloudNode2 } from "@tinycloud/node-sdk";
|
|
21589
21605
|
import { DelegationRequestBindingSchema, PermissionEntrySchema } from "@tinycloud/operations/artifacts";
|
|
21590
21606
|
import {
|
|
@@ -21599,6 +21615,7 @@ import {
|
|
|
21599
21615
|
// src/lib/revoke-delegation.ts
|
|
21600
21616
|
init_constants();
|
|
21601
21617
|
init_errors();
|
|
21618
|
+
import { CLI_NAME as CLI_NAME12 } from "@tinycloud/operations/cli-name";
|
|
21602
21619
|
import { parseSignedCompactUcanAttenuation } from "@tinycloud/sdk-core";
|
|
21603
21620
|
import { inspectStoredDelegation, withoutBearer } from "@tinycloud/operations/delegation-binding";
|
|
21604
21621
|
init_permissions();
|
|
@@ -21784,7 +21801,7 @@ async function revokeDelegationOnNode(params) {
|
|
|
21784
21801
|
"REVOKE_UNAUTHORIZED",
|
|
21785
21802
|
"The node rejected this revocation: Unauthorized Revoker.",
|
|
21786
21803
|
ExitCode.PERMISSION_DENIED,
|
|
21787
|
-
{ 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
|
|
21804
|
+
{ 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_NAME12} --profile <profile-name> delegation revoke ${cid}\`.` }
|
|
21788
21805
|
);
|
|
21789
21806
|
}
|
|
21790
21807
|
throw cliErrorFromService(result.error);
|
|
@@ -21867,7 +21884,7 @@ function renderList(result) {
|
|
|
21867
21884
|
}
|
|
21868
21885
|
process.stdout.write(`Profile: ${result.profile}
|
|
21869
21886
|
`);
|
|
21870
|
-
process.stdout.write(`Session CID: ${result.session?.cid ?? "unavailable"}` + (result.session?.cid ? ` (revoke with
|
|
21887
|
+
process.stdout.write(`Session CID: ${result.session?.cid ?? "unavailable"}` + (result.session?.cid ? ` (revoke with ${CLI_NAME13} delegation revoke ${result.session.cid})
|
|
21871
21888
|
` : "\n"));
|
|
21872
21889
|
process.stdout.write(`Local verification: ${result.localVerification}; node validation and revocation are not checked.
|
|
21873
21890
|
`);
|
|
@@ -21963,7 +21980,7 @@ function registerAuthGrantsCommand(auth, options = {}) {
|
|
|
21963
21980
|
ExitCode.ERROR,
|
|
21964
21981
|
{
|
|
21965
21982
|
causeCode: cause.code,
|
|
21966
|
-
hint: `Do not use --revoke again. After fixing local profile storage, run:
|
|
21983
|
+
hint: `Do not use --revoke again. After fixing local profile storage, run: ${CLI_NAME13} --profile ${ctx.profile} auth grants remove ${cid}`
|
|
21967
21984
|
}
|
|
21968
21985
|
);
|
|
21969
21986
|
}
|
|
@@ -22124,7 +22141,7 @@ async function promptAuthMethod() {
|
|
|
22124
22141
|
function registerAuthCommand(program, options) {
|
|
22125
22142
|
const auth = program.command("auth").description("Authentication management");
|
|
22126
22143
|
registerAuthGrantsCommand(auth, { openKeyAcquisition: options?.openKeyAcquisition });
|
|
22127
|
-
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", "Use manual paste mode instead of browser callback").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
|
|
22144
|
+
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", "Use manual paste mode instead of browser callback").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_NAME14} 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) => {
|
|
22128
22145
|
try {
|
|
22129
22146
|
if (options2.device && options2.paste) {
|
|
22130
22147
|
throw new CLIError("INVALID_ARGUMENT", "--device and --paste are mutually exclusive.", ExitCode.USAGE_ERROR);
|
|
@@ -22232,7 +22249,7 @@ function registerAuthCommand(program, options) {
|
|
|
22232
22249
|
return handleError(error);
|
|
22233
22250
|
}
|
|
22234
22251
|
});
|
|
22235
|
-
auth.command("logout").description(
|
|
22252
|
+
auth.command("logout").description(`Clear the local session and remove local replicas. Logout is local; delegations and grants on the node stay valid until they expire. Use \`${CLI_NAME14} delegation revoke <cid>\` to end access now.`).option("--keep-replicas", "Keep this profile's local replicas").action(async (options2, cmd) => {
|
|
22236
22253
|
try {
|
|
22237
22254
|
const globalOpts = cmd.optsWithGlobals();
|
|
22238
22255
|
const ctx = await ProfileManager.resolveContext(globalOpts, { storedSession: "unused" });
|
|
@@ -22244,7 +22261,7 @@ function registerAuthCommand(program, options) {
|
|
|
22244
22261
|
authenticated: false,
|
|
22245
22262
|
replicasRemoved,
|
|
22246
22263
|
replicasKept: options2.keepReplicas === true,
|
|
22247
|
-
warning:
|
|
22264
|
+
warning: `Logout is local; delegations and grants on the node stay valid until they expire. Use \`${CLI_NAME14} delegation revoke <cid>\` to end access now.`
|
|
22248
22265
|
});
|
|
22249
22266
|
} catch (error) {
|
|
22250
22267
|
return handleError(error);
|
|
@@ -22366,7 +22383,7 @@ ${signInAgainHint(ctx.profile, posture ?? void 0)}
|
|
|
22366
22383
|
if (profile.authMethod === "openkey" || options2.device) {
|
|
22367
22384
|
const key = await ProfileManager.getKey(ctx.profile);
|
|
22368
22385
|
if (!key) {
|
|
22369
|
-
throw new CLIError("NO_KEY", `No key found for profile "${ctx.profile}". Run
|
|
22386
|
+
throw new CLIError("NO_KEY", `No key found for profile "${ctx.profile}". Run \`${CLI_NAME14} --profile ${ctx.profile} auth rotate\` to create a new key and sign in.`, ExitCode.AUTH_REQUIRED);
|
|
22370
22387
|
}
|
|
22371
22388
|
const openkeyHost = resolveOpenKeyHost(profile);
|
|
22372
22389
|
const grants = [];
|
|
@@ -22380,7 +22397,7 @@ ${signInAgainHint(ctx.profile, posture ?? void 0)}
|
|
|
22380
22397
|
const declined = [];
|
|
22381
22398
|
for (const group of groupPermissionsBySpace(requested)) {
|
|
22382
22399
|
const reason = permissionGrantReason(
|
|
22383
|
-
|
|
22400
|
+
`Grant requested TinyCloud permissions from \`${CLI_NAME14} auth request --grant\`.`,
|
|
22384
22401
|
group
|
|
22385
22402
|
);
|
|
22386
22403
|
const request = grantRequestPermissions(group, profile.spaceId ?? profile.spaceName);
|
|
@@ -22489,7 +22506,7 @@ ${signInAgainHint(ctx.profile, posture ?? void 0)}
|
|
|
22489
22506
|
kind: parsed.kind,
|
|
22490
22507
|
requestId: parsed.requestId,
|
|
22491
22508
|
requested: parsed.requested,
|
|
22492
|
-
next:
|
|
22509
|
+
next: `${CLI_NAME14} auth retry ${parsed.requestId}`
|
|
22493
22510
|
});
|
|
22494
22511
|
return;
|
|
22495
22512
|
}
|
|
@@ -23040,7 +23057,7 @@ async function ensureDelegationAuthority(params) {
|
|
|
23040
23057
|
if (!key) {
|
|
23041
23058
|
throw new CLIError(
|
|
23042
23059
|
"NO_KEY",
|
|
23043
|
-
`No key found for profile "${params.ctx.profile}". Run
|
|
23060
|
+
`No key found for profile "${params.ctx.profile}". Run \`${CLI_NAME14} --profile ${params.ctx.profile} auth rotate\` to create a new key and sign in.`,
|
|
23044
23061
|
ExitCode.AUTH_REQUIRED
|
|
23045
23062
|
);
|
|
23046
23063
|
}
|
|
@@ -23285,7 +23302,7 @@ async function rotateAuthKey(profileName2, host, options = {}) {
|
|
|
23285
23302
|
if (!profile.privateKey) {
|
|
23286
23303
|
throw new CLIError(
|
|
23287
23304
|
"LOCAL_OWNER_KEY_REQUIRED",
|
|
23288
|
-
`Profile "${profileName2}" does not have a local owner private key. Run
|
|
23305
|
+
`Profile "${profileName2}" does not have a local owner private key. Run \`${CLI_NAME14} auth login --method local\` first.`,
|
|
23289
23306
|
ExitCode.AUTH_REQUIRED
|
|
23290
23307
|
);
|
|
23291
23308
|
}
|
|
@@ -23508,7 +23525,7 @@ async function refreshOpenKeySession(profileName2, host, options = {}) {
|
|
|
23508
23525
|
if (!key) {
|
|
23509
23526
|
throw new CLIError(
|
|
23510
23527
|
"NO_KEY",
|
|
23511
|
-
`No key found for profile "${profileName2}". Run
|
|
23528
|
+
`No key found for profile "${profileName2}". Run \`${CLI_NAME14} --profile ${profileName2} auth rotate\` to create a new key and sign in.`,
|
|
23512
23529
|
ExitCode.AUTH_REQUIRED
|
|
23513
23530
|
);
|
|
23514
23531
|
}
|
|
@@ -23589,6 +23606,7 @@ async function refreshOpenKeySession(profileName2, host, options = {}) {
|
|
|
23589
23606
|
}
|
|
23590
23607
|
|
|
23591
23608
|
// src/commands/completion.ts
|
|
23609
|
+
import { CLI_NAME as CLI_NAME15 } from "@tinycloud/operations/cli-name";
|
|
23592
23610
|
function registerCompletionCommand(program) {
|
|
23593
23611
|
const completion = program.command("completion").description("Generate shell completions");
|
|
23594
23612
|
completion.command("bash").description("Output bash completions").action(() => {
|
|
@@ -23605,7 +23623,7 @@ function registerCompletionCommand(program) {
|
|
|
23605
23623
|
});
|
|
23606
23624
|
}
|
|
23607
23625
|
function generateBashCompletion() {
|
|
23608
|
-
return `#
|
|
23626
|
+
return `# ${CLI_NAME15} bash completion
|
|
23609
23627
|
_tc_completions() {
|
|
23610
23628
|
local cur prev commands subcommands
|
|
23611
23629
|
COMPREPLY=()
|
|
@@ -23630,11 +23648,11 @@ _tc_completions() {
|
|
|
23630
23648
|
COMPREPLY=( $(compgen -W "\${subcommands}" -- "\${cur}") )
|
|
23631
23649
|
fi
|
|
23632
23650
|
}
|
|
23633
|
-
complete -F _tc_completions
|
|
23651
|
+
complete -F _tc_completions ${CLI_NAME15}
|
|
23634
23652
|
`;
|
|
23635
23653
|
}
|
|
23636
23654
|
function generateZshCompletion() {
|
|
23637
|
-
return `#compdef
|
|
23655
|
+
return `#compdef ${CLI_NAME15}
|
|
23638
23656
|
|
|
23639
23657
|
_tc() {
|
|
23640
23658
|
local -a commands
|
|
@@ -23685,49 +23703,50 @@ _tc
|
|
|
23685
23703
|
`;
|
|
23686
23704
|
}
|
|
23687
23705
|
function generateFishCompletion() {
|
|
23688
|
-
return `#
|
|
23706
|
+
return `# ${CLI_NAME15} fish completion
|
|
23689
23707
|
set -l commands init auth kv space delegation share node profile completion
|
|
23690
23708
|
|
|
23691
23709
|
# Disable file completion by default
|
|
23692
|
-
complete -c
|
|
23710
|
+
complete -c ${CLI_NAME15} -f
|
|
23693
23711
|
|
|
23694
23712
|
# Top-level commands
|
|
23695
|
-
complete -c
|
|
23696
|
-
complete -c
|
|
23697
|
-
complete -c
|
|
23698
|
-
complete -c
|
|
23699
|
-
complete -c
|
|
23700
|
-
complete -c
|
|
23701
|
-
complete -c
|
|
23702
|
-
complete -c
|
|
23703
|
-
complete -c
|
|
23713
|
+
complete -c ${CLI_NAME15} -n "not __fish_seen_subcommand_from $commands" -a init -d "Initialize a new TinyCloud profile"
|
|
23714
|
+
complete -c ${CLI_NAME15} -n "not __fish_seen_subcommand_from $commands" -a auth -d "Authentication management"
|
|
23715
|
+
complete -c ${CLI_NAME15} -n "not __fish_seen_subcommand_from $commands" -a kv -d "Key-value store operations"
|
|
23716
|
+
complete -c ${CLI_NAME15} -n "not __fish_seen_subcommand_from $commands" -a space -d "Space management"
|
|
23717
|
+
complete -c ${CLI_NAME15} -n "not __fish_seen_subcommand_from $commands" -a delegation -d "Manage delegations"
|
|
23718
|
+
complete -c ${CLI_NAME15} -n "not __fish_seen_subcommand_from $commands" -a share -d "Share data with others"
|
|
23719
|
+
complete -c ${CLI_NAME15} -n "not __fish_seen_subcommand_from $commands" -a node -d "Node health and info"
|
|
23720
|
+
complete -c ${CLI_NAME15} -n "not __fish_seen_subcommand_from $commands" -a profile -d "Profile management"
|
|
23721
|
+
complete -c ${CLI_NAME15} -n "not __fish_seen_subcommand_from $commands" -a completion -d "Generate shell completions"
|
|
23704
23722
|
|
|
23705
23723
|
# Subcommands
|
|
23706
|
-
complete -c
|
|
23707
|
-
complete -c
|
|
23708
|
-
complete -c
|
|
23709
|
-
complete -c
|
|
23710
|
-
complete -c
|
|
23711
|
-
complete -c
|
|
23712
|
-
complete -c
|
|
23713
|
-
complete -c
|
|
23724
|
+
complete -c ${CLI_NAME15} -n "__fish_seen_subcommand_from auth" -a "login logout rotate status whoami"
|
|
23725
|
+
complete -c ${CLI_NAME15} -n "__fish_seen_subcommand_from kv" -a "get put delete list head"
|
|
23726
|
+
complete -c ${CLI_NAME15} -n "__fish_seen_subcommand_from space" -a "list create info switch"
|
|
23727
|
+
complete -c ${CLI_NAME15} -n "__fish_seen_subcommand_from delegation" -a "create list info revoke"
|
|
23728
|
+
complete -c ${CLI_NAME15} -n "__fish_seen_subcommand_from share" -a "create receive list revoke"
|
|
23729
|
+
complete -c ${CLI_NAME15} -n "__fish_seen_subcommand_from node" -a "health version status"
|
|
23730
|
+
complete -c ${CLI_NAME15} -n "__fish_seen_subcommand_from profile" -a "list create show switch delete"
|
|
23731
|
+
complete -c ${CLI_NAME15} -n "__fish_seen_subcommand_from completion" -a "bash zsh fish"
|
|
23714
23732
|
|
|
23715
23733
|
# Global options
|
|
23716
|
-
complete -c
|
|
23717
|
-
complete -c
|
|
23718
|
-
complete -c
|
|
23719
|
-
complete -c
|
|
23720
|
-
complete -c
|
|
23721
|
-
complete -c
|
|
23722
|
-
complete -c
|
|
23723
|
-
complete -c
|
|
23734
|
+
complete -c ${CLI_NAME15} -l profile -s p -d "Profile to use"
|
|
23735
|
+
complete -c ${CLI_NAME15} -l host -s H -d "TinyCloud node URL"
|
|
23736
|
+
complete -c ${CLI_NAME15} -l verbose -s v -d "Enable verbose output"
|
|
23737
|
+
complete -c ${CLI_NAME15} -l no-cache -d "Disable caching"
|
|
23738
|
+
complete -c ${CLI_NAME15} -l quiet -s q -d "Suppress non-essential output"
|
|
23739
|
+
complete -c ${CLI_NAME15} -l replication -d "Enable read-through replication"
|
|
23740
|
+
complete -c ${CLI_NAME15} -l no-replication -d "Disable replication"
|
|
23741
|
+
complete -c ${CLI_NAME15} -l replication-debug -d "Write replication diagnostics to stderr"
|
|
23724
23742
|
`;
|
|
23725
23743
|
}
|
|
23726
23744
|
|
|
23727
23745
|
// src/commands/courier.ts
|
|
23728
|
-
|
|
23746
|
+
import { CLI_NAME as CLI_NAME16 } from "@tinycloud/operations/cli-name";
|
|
23747
|
+
var TC_OPTIONS_BEFORE_COURIER = `${CLI_NAME16} courier takes no ${CLI_NAME16} options and does not use ${CLI_NAME16} profiles; Courier keeps its own profile in $COURIER_HOME (default ~/.courier). Put flags after "courier", e.g. ${CLI_NAME16} courier init --host <url>.`;
|
|
23729
23748
|
function registerCourierCommand(program) {
|
|
23730
|
-
const courier = program.command("courier").description(
|
|
23749
|
+
const courier = program.command("courier").description(`Agent-to-agent threads over TinyCloud; run \`${CLI_NAME16} courier --help\` (beta)`).argument("[args...]").allowUnknownOption().helpOption(false);
|
|
23731
23750
|
courier.action(() => courier.error(TC_OPTIONS_BEFORE_COURIER, { exitCode: 2, code: "tc.courier.options" }));
|
|
23732
23751
|
}
|
|
23733
23752
|
|
|
@@ -23884,6 +23903,7 @@ init_constants();
|
|
|
23884
23903
|
init_formatter();
|
|
23885
23904
|
init_theme();
|
|
23886
23905
|
init_errors();
|
|
23906
|
+
import { CLI_NAME as CLI_NAME17 } from "@tinycloud/operations/cli-name";
|
|
23887
23907
|
init_session_expired();
|
|
23888
23908
|
init_types();
|
|
23889
23909
|
function registerDoctorCommand(program) {
|
|
@@ -23917,7 +23937,7 @@ function registerDoctorCommand(program) {
|
|
|
23917
23937
|
const profile = await ProfileManager.getProfile(profileName2);
|
|
23918
23938
|
keyDetail = profile.did ? `${profile.did.slice(0, 20)}...` : "key found";
|
|
23919
23939
|
} else {
|
|
23920
|
-
keyDetail =
|
|
23940
|
+
keyDetail = `no key \u2014 run ${CLI_NAME17} init`;
|
|
23921
23941
|
}
|
|
23922
23942
|
} catch {
|
|
23923
23943
|
keyDetail = "error reading key";
|
|
@@ -23939,7 +23959,7 @@ function registerDoctorCommand(program) {
|
|
|
23939
23959
|
} else if (state2 === "unverified") {
|
|
23940
23960
|
sessionDetail = `not usable (no signed OpenKey proof) \u2014 ${signInAgainHint(profileName2, resolveProfilePosture(profile))}`;
|
|
23941
23961
|
} else {
|
|
23942
|
-
sessionDetail = sessionOk ? "active" :
|
|
23962
|
+
sessionDetail = sessionOk ? "active" : `no session \u2014 run ${CLI_NAME17} auth login`;
|
|
23943
23963
|
}
|
|
23944
23964
|
} catch {
|
|
23945
23965
|
sessionDetail = "error reading session";
|
|
@@ -23967,7 +23987,7 @@ function registerDoctorCommand(program) {
|
|
|
23967
23987
|
try {
|
|
23968
23988
|
const profile = await ProfileManager.getProfile(profileName2);
|
|
23969
23989
|
spaceOk = Boolean(profile.spaceId);
|
|
23970
|
-
spaceDetail = spaceOk ? `${profile.spaceId.slice(0, 16)}...` :
|
|
23990
|
+
spaceDetail = spaceOk ? `${profile.spaceId.slice(0, 16)}...` : `no space \u2014 run ${CLI_NAME17} space create`;
|
|
23971
23991
|
} catch {
|
|
23972
23992
|
spaceDetail = "error checking space";
|
|
23973
23993
|
}
|
|
@@ -24157,6 +24177,7 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
|
|
|
24157
24177
|
}
|
|
24158
24178
|
|
|
24159
24179
|
// src/commands/enable.ts
|
|
24180
|
+
import { CLI_NAME as CLI_NAME18 } from "@tinycloud/operations/cli-name";
|
|
24160
24181
|
init_constants();
|
|
24161
24182
|
init_profiles();
|
|
24162
24183
|
init_errors();
|
|
@@ -24164,7 +24185,7 @@ init_formatter();
|
|
|
24164
24185
|
init_publishing_manifest();
|
|
24165
24186
|
function registerEnableCommand(program) {
|
|
24166
24187
|
const enable = program.command("enable").description("Enable a narrowly scoped TinyCloud service");
|
|
24167
|
-
enable.command("share").description(
|
|
24188
|
+
enable.command("share").description(`Approve Share publishing scope through OpenKey device authorization (same as \`${CLI_NAME18} auth login --device --manifest builtin:share-publishing\`). Use a dedicated profile, e.g. \`${CLI_NAME18} init --name publisher --key-only && ${CLI_NAME18} --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) => {
|
|
24168
24189
|
try {
|
|
24169
24190
|
const globalOptions = command.optsWithGlobals();
|
|
24170
24191
|
const context = await ProfileManager.resolveContext(globalOptions, { storedSession: "unused" });
|
|
@@ -24200,6 +24221,7 @@ init_profiles();
|
|
|
24200
24221
|
init_formatter();
|
|
24201
24222
|
init_errors();
|
|
24202
24223
|
init_constants();
|
|
24224
|
+
import { CLI_NAME as CLI_NAME19 } from "@tinycloud/operations/cli-name";
|
|
24203
24225
|
function registerInitCommand(program) {
|
|
24204
24226
|
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) => {
|
|
24205
24227
|
try {
|
|
@@ -24217,7 +24239,7 @@ function registerInitCommand(program) {
|
|
|
24217
24239
|
if (await ProfileManager.profileExists(profileName2)) {
|
|
24218
24240
|
throw new CLIError(
|
|
24219
24241
|
"PROFILE_EXISTS",
|
|
24220
|
-
`Profile "${profileName2}" already exists. Use
|
|
24242
|
+
`Profile "${profileName2}" already exists. Use \`${CLI_NAME19} profile delete ${profileName2}\` first or choose a different name.`,
|
|
24221
24243
|
ExitCode.ERROR
|
|
24222
24244
|
);
|
|
24223
24245
|
}
|
|
@@ -24459,37 +24481,64 @@ function registerKvCommand(program) {
|
|
|
24459
24481
|
return handleError(error);
|
|
24460
24482
|
}
|
|
24461
24483
|
});
|
|
24462
|
-
kv.command("list").description("List keys").option("--prefix <prefix>", "Filter by key prefix").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").action(async (options, cmd) => {
|
|
24484
|
+
kv.command("list").description("List keys").option("--prefix <prefix>", "Filter by key prefix").option("--space <name|uri>", "Target a non-primary space (short name or full URI)").option("--limit <n>", "Keys per page, 1-1000 (the node's default otherwise)").option("--cursor <cursor>", "Continue from a previous page's nextCursor").option("--all", "Follow nextCursor until the listing is complete").action(async (options, cmd) => {
|
|
24463
24485
|
try {
|
|
24464
24486
|
if (options.prefix) assertAddressableKey(options.prefix);
|
|
24487
|
+
const limit = options.limit === void 0 ? void 0 : Number(options.limit);
|
|
24488
|
+
if (limit !== void 0 && (!Number.isInteger(limit) || limit < 1 || limit > 1e3)) {
|
|
24489
|
+
throw new CLIError("USAGE_ERROR", "--limit must be an integer from 1 through 1000", ExitCode.USAGE_ERROR);
|
|
24490
|
+
}
|
|
24465
24491
|
const globalOpts = cmd.optsWithGlobals();
|
|
24466
24492
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
24467
24493
|
const node = await ensureAuthenticated(ctx);
|
|
24468
24494
|
const { kv: kv2, spaceUri } = await kvHandle(node, options.space, ctx.profile);
|
|
24469
|
-
const
|
|
24470
|
-
|
|
24471
|
-
|
|
24472
|
-
|
|
24495
|
+
const keyList = [];
|
|
24496
|
+
let cursor = options.cursor;
|
|
24497
|
+
let truncated;
|
|
24498
|
+
const seenCursors = /* @__PURE__ */ new Set();
|
|
24499
|
+
for (; ; ) {
|
|
24500
|
+
const result = await withSpinner("Listing keys...", () => kv2.list({
|
|
24501
|
+
...options.prefix ? { prefix: options.prefix } : {},
|
|
24502
|
+
...limit === void 0 ? {} : { limit },
|
|
24503
|
+
...cursor === void 0 ? {} : { cursor }
|
|
24504
|
+
}));
|
|
24505
|
+
if (!result.ok) {
|
|
24506
|
+
await throwKvError(result.error, spaceUri, ctx.profile);
|
|
24507
|
+
break;
|
|
24508
|
+
}
|
|
24509
|
+
for (const key of result.data.keys) keyList.push(key);
|
|
24510
|
+
truncated = result.data.truncated;
|
|
24511
|
+
cursor = truncated === true ? result.data.nextCursor : void 0;
|
|
24512
|
+
if (options.all && truncated === true && cursor === void 0) {
|
|
24513
|
+
throw new CLIError("KV_LIST_CURSOR_MISSING", "The node reported more keys but no cursor to continue from, so the listing can't be completed.", ExitCode.ERROR);
|
|
24514
|
+
}
|
|
24515
|
+
if (!options.all || cursor === void 0) break;
|
|
24516
|
+
if (seenCursors.has(cursor)) {
|
|
24517
|
+
throw new CLIError("KV_LIST_CURSOR_STALLED", "The node returned a list cursor it had already returned, so the listing can't be completed.", ExitCode.ERROR);
|
|
24518
|
+
}
|
|
24519
|
+
seenCursors.add(cursor);
|
|
24473
24520
|
}
|
|
24474
|
-
const rawData = result.data.data ?? result.data;
|
|
24475
|
-
const keyList = Array.isArray(rawData) ? rawData : rawData?.keys ?? [];
|
|
24476
24521
|
if (shouldOutputJson()) {
|
|
24477
24522
|
outputJson({
|
|
24478
24523
|
keys: keyList,
|
|
24479
24524
|
count: keyList.length,
|
|
24480
|
-
prefix: options.prefix ?? null
|
|
24525
|
+
prefix: options.prefix ?? null,
|
|
24526
|
+
// null when the node doesn't report truncation.
|
|
24527
|
+
truncated: truncated ?? null,
|
|
24528
|
+
nextCursor: cursor ?? null
|
|
24481
24529
|
});
|
|
24482
24530
|
} else {
|
|
24483
24531
|
if (keyList.length === 0) {
|
|
24484
24532
|
process.stdout.write(theme.muted("No keys found.") + "\n");
|
|
24485
24533
|
} else {
|
|
24486
|
-
const rows = keyList.map((
|
|
24487
|
-
e.key || e,
|
|
24488
|
-
e.contentLength ? formatBytes(e.contentLength) : "\u2014",
|
|
24489
|
-
e.updatedAt ? formatTimeAgo(e.updatedAt) : "\u2014"
|
|
24490
|
-
]);
|
|
24534
|
+
const rows = keyList.map((key) => [key, "\u2014", "\u2014"]);
|
|
24491
24535
|
process.stdout.write(formatTable(["Key", "Size", "Updated"], rows) + "\n");
|
|
24492
24536
|
}
|
|
24537
|
+
if (cursor !== void 0) {
|
|
24538
|
+
process.stdout.write(theme.muted(`More keys follow. Run again with --cursor ${cursor}, or use --all.`) + "\n");
|
|
24539
|
+
} else if (truncated === true) {
|
|
24540
|
+
process.stdout.write(theme.muted("More keys follow, but the node gave no cursor to continue from.") + "\n");
|
|
24541
|
+
}
|
|
24493
24542
|
}
|
|
24494
24543
|
} catch (error) {
|
|
24495
24544
|
return handleError(error);
|
|
@@ -24532,6 +24581,7 @@ init_errors();
|
|
|
24532
24581
|
init_constants();
|
|
24533
24582
|
init_theme();
|
|
24534
24583
|
import { resolveManifestKnowledgeRoot as resolveManifestKnowledgeRoot2 } from "@tinycloud/sdk-core";
|
|
24584
|
+
import { CLI_NAME as CLI_NAME20 } from "@tinycloud/operations/cli-name";
|
|
24535
24585
|
import { readFile as readFile8 } from "fs/promises";
|
|
24536
24586
|
var DEFAULT_APP_SPACE = "applications";
|
|
24537
24587
|
function registerManifestCommand(program) {
|
|
@@ -24539,8 +24589,8 @@ function registerManifestCommand(program) {
|
|
|
24539
24589
|
manifest.command("resolve <source>").description("Resolve a manifest file or URL to its effective space, paths, and DB basenames").addHelpText("after", `
|
|
24540
24590
|
|
|
24541
24591
|
Examples:
|
|
24542
|
-
$
|
|
24543
|
-
$
|
|
24592
|
+
$ ${CLI_NAME20} manifest resolve ./manifest.json
|
|
24593
|
+
$ ${CLI_NAME20} manifest resolve https://app.example.com/manifest.json --json
|
|
24544
24594
|
|
|
24545
24595
|
What it shows:
|
|
24546
24596
|
- app_id, name, manifest_version
|
|
@@ -24550,7 +24600,7 @@ What it shows:
|
|
|
24550
24600
|
|
|
24551
24601
|
This command is read-only and does NOT contact the node \u2014 it just resolves
|
|
24552
24602
|
the manifest against the active profile's address/chain so you know which
|
|
24553
|
-
\`--space\` and \`--db\` values to pass to other
|
|
24603
|
+
\`--space\` and \`--db\` values to pass to other ${CLI_NAME20} commands.
|
|
24554
24604
|
`).action(async (source, _options, cmd) => {
|
|
24555
24605
|
try {
|
|
24556
24606
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -24619,7 +24669,7 @@ ${theme.heading("SQL databases")}
|
|
|
24619
24669
|
`);
|
|
24620
24670
|
}
|
|
24621
24671
|
process.stdout.write(theme.muted(`
|
|
24622
|
-
Use with:
|
|
24672
|
+
Use with: ${CLI_NAME20} sql query --space ${spaceName} --db <db> "..."
|
|
24623
24673
|
`));
|
|
24624
24674
|
}
|
|
24625
24675
|
if (permissions.length > 0) {
|
|
@@ -24739,6 +24789,7 @@ init_profiles();
|
|
|
24739
24789
|
init_formatter();
|
|
24740
24790
|
init_errors();
|
|
24741
24791
|
init_constants();
|
|
24792
|
+
import { CLI_NAME as CLI_NAME21 } from "@tinycloud/operations/cli-name";
|
|
24742
24793
|
import { createInterface as createInterface3 } from "readline";
|
|
24743
24794
|
init_theme();
|
|
24744
24795
|
init_types();
|
|
@@ -24884,9 +24935,9 @@ profile at command time. Precedence for every kv/sql command:
|
|
|
24884
24935
|
explicit --space flag > profile defaultSpace > primary space.
|
|
24885
24936
|
|
|
24886
24937
|
Examples:
|
|
24887
|
-
$
|
|
24888
|
-
$
|
|
24889
|
-
$
|
|
24938
|
+
$ ${CLI_NAME21} profile set-default-space applications
|
|
24939
|
+
$ ${CLI_NAME21} profile set-default-space applications --profile cli-test
|
|
24940
|
+
$ ${CLI_NAME21} profile set-default-space --unset
|
|
24890
24941
|
`).action(async (name, options, cmd) => {
|
|
24891
24942
|
try {
|
|
24892
24943
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -24898,7 +24949,7 @@ Examples:
|
|
|
24898
24949
|
if (!options.unset && (name === void 0 || name === "")) {
|
|
24899
24950
|
throw new CLIError(
|
|
24900
24951
|
"USAGE_ERROR",
|
|
24901
|
-
|
|
24952
|
+
`Provide a space name (e.g. \`${CLI_NAME21} profile set-default-space applications\`) or pass --unset.`,
|
|
24902
24953
|
ExitCode.USAGE_ERROR
|
|
24903
24954
|
);
|
|
24904
24955
|
}
|
|
@@ -24958,6 +25009,7 @@ function parseOperatorType(raw) {
|
|
|
24958
25009
|
// src/commands/replica.ts
|
|
24959
25010
|
init_sdk();
|
|
24960
25011
|
init_replication_registry();
|
|
25012
|
+
import { CLI_NAME as CLI_NAME22 } from "@tinycloud/operations/cli-name";
|
|
24961
25013
|
import { randomBytes as randomBytes6 } from "crypto";
|
|
24962
25014
|
import { constants as fsConstants2 } from "fs";
|
|
24963
25015
|
import { open as open4, readdir as readdir5, rename as rename3, stat as stat3, unlink as unlink2, readFile as readFile9, lstat as lstat4 } from "fs/promises";
|
|
@@ -25097,11 +25149,11 @@ var EXIT_BY_CODE = {
|
|
|
25097
25149
|
var READ_FAILURE = {
|
|
25098
25150
|
absent: { code: "KEY_ABSENT", exit: ExitCode.NOT_FOUND, what: "is not in the replica (the replica covers its whole prefix)" },
|
|
25099
25151
|
deleted: { code: "KEY_DELETED", exit: ExitCode.NOT_FOUND, what: "was deleted at the source" },
|
|
25100
|
-
content_missing: { code: "CONTENT_MISSING", exit: ExitCode.NOT_FOUND, what:
|
|
25152
|
+
content_missing: { code: "CONTENT_MISSING", exit: ExitCode.NOT_FOUND, what: `is known but its content has not been fetched yet; run ${CLI_NAME22} replica sync` },
|
|
25101
25153
|
coverage_incomplete: {
|
|
25102
25154
|
code: "COVERAGE_INCOMPLETE",
|
|
25103
25155
|
exit: ExitCode.NOT_FOUND,
|
|
25104
|
-
what:
|
|
25156
|
+
what: `is not in the replica, which has not finished its first sync; run ${CLI_NAME22} replica sync`
|
|
25105
25157
|
},
|
|
25106
25158
|
not_covered: { code: "NOT_COVERED", exit: ExitCode.USAGE_ERROR, what: "is outside the replica's prefix" }
|
|
25107
25159
|
};
|
|
@@ -25152,7 +25204,7 @@ async function existingReplicaName(profile, option) {
|
|
|
25152
25204
|
const names = await replicaNames(profile);
|
|
25153
25205
|
if (names.length === 1) return names[0];
|
|
25154
25206
|
if (names.length === 0) {
|
|
25155
|
-
throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, `Profile "${profile}" has no replicas. Create one with:
|
|
25207
|
+
throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, `Profile "${profile}" has no replicas. Create one with: ${CLI_NAME22} replica sync --prefix <prefix>`);
|
|
25156
25208
|
}
|
|
25157
25209
|
throw new CLIError("USAGE_ERROR", `Profile "${profile}" has several replicas (${names.join(", ")}); pass --replica <name>.`, ExitCode.USAGE_ERROR);
|
|
25158
25210
|
}
|
|
@@ -25211,9 +25263,9 @@ async function storedGrants(profile) {
|
|
|
25211
25263
|
function grantHint(space, prefix) {
|
|
25212
25264
|
return [
|
|
25213
25265
|
"A replica needs a device grant with get and sync on its prefix:",
|
|
25214
|
-
` device:
|
|
25215
|
-
|
|
25216
|
-
|
|
25266
|
+
` device: ${CLI_NAME22} auth request --cap tinycloud.kv:${space}:${prefix}:get,list,metadata,sync --expiry 30d --emit req.json`,
|
|
25267
|
+
` owner: ${CLI_NAME22} auth grant req.json > grant.json`,
|
|
25268
|
+
` device: ${CLI_NAME22} auth import grant.json`
|
|
25217
25269
|
].join("\n");
|
|
25218
25270
|
}
|
|
25219
25271
|
function resolveSpace(input, prefix, grants) {
|
|
@@ -25304,7 +25356,7 @@ function describeStatus(status) {
|
|
|
25304
25356
|
formatField("Size", formatBytes(status.bytes)),
|
|
25305
25357
|
formatField("Grant", status.device.delegationCid),
|
|
25306
25358
|
formatField("Last error", status.lastError ? `${status.lastError.code}: ${status.lastError.message}` : null),
|
|
25307
|
-
formatField("Purge", status.purgePending ?
|
|
25359
|
+
formatField("Purge", status.purgePending ? `incomplete: content files remain; the next ${CLI_NAME22} replica command retries` : null)
|
|
25308
25360
|
].join("\n");
|
|
25309
25361
|
}
|
|
25310
25362
|
async function writeFileAtomic(path, bytes) {
|
|
@@ -25476,7 +25528,7 @@ or coverage incomplete; 5 grant expired, revoked or not yet valid; 6 network;
|
|
|
25476
25528
|
await store.close();
|
|
25477
25529
|
throw new ReplicaError(
|
|
25478
25530
|
ReplicaErrorCode.CONFIG_MISMATCH,
|
|
25479
|
-
`Replica ${name} already follows ${differs}. Use another --replica name, or
|
|
25531
|
+
`Replica ${name} already follows ${differs}. Use another --replica name, or ${CLI_NAME22} replica reset --replica ${name} --purge first.`
|
|
25480
25532
|
);
|
|
25481
25533
|
}
|
|
25482
25534
|
} else {
|
|
@@ -25654,6 +25706,7 @@ init_theme();
|
|
|
25654
25706
|
init_errors();
|
|
25655
25707
|
init_warnings();
|
|
25656
25708
|
init_constants();
|
|
25709
|
+
import { CLI_NAME as CLI_NAME24 } from "@tinycloud/operations/cli-name";
|
|
25657
25710
|
import { readFile as readFile10 } from "fs/promises";
|
|
25658
25711
|
import { join as join11 } from "path";
|
|
25659
25712
|
import { homedir, hostname, userInfo } from "os";
|
|
@@ -25864,6 +25917,7 @@ function parseStatusEnvelope(line) {
|
|
|
25864
25917
|
// src/lib/secrets-wait.ts
|
|
25865
25918
|
init_constants();
|
|
25866
25919
|
init_errors();
|
|
25920
|
+
import { CLI_NAME as CLI_NAME23 } from "@tinycloud/operations/cli-name";
|
|
25867
25921
|
var WAIT_BACKOFF_MS = [3e3, 4500, 6750, 10125];
|
|
25868
25922
|
var WAIT_STEADY_INTERVAL_MS = 15e3;
|
|
25869
25923
|
var DEFAULT_WAIT_TIMEOUT = "5m";
|
|
@@ -25871,7 +25925,7 @@ var MAX_WAIT_TIMEOUT_MS = 9 * 6e4;
|
|
|
25871
25925
|
var MAX_CONSECUTIVE_FAILURES = 5;
|
|
25872
25926
|
var TIMEOUT_RE = /^(0|\d+s|\d+m)$/;
|
|
25873
25927
|
var RETRYABLE_STATUS_ERRORS = { NODE_UNREACHABLE: true, NODE_ERROR: true };
|
|
25874
|
-
var EXPLICIT_KEY_HINT =
|
|
25928
|
+
var EXPLICIT_KEY_HINT = `${CLI_NAME23} secrets wait never signs in, and an explicit private key has no stored session. Use a local profile signed in once with ${CLI_NAME23} --profile <profile> auth login --method local, without TC_PRIVATE_KEY.`;
|
|
25875
25929
|
function abortableSleep(ms2, signal) {
|
|
25876
25930
|
return new Promise((resolve5) => {
|
|
25877
25931
|
if (signal.aborted) {
|
|
@@ -25907,7 +25961,7 @@ async function runSecretsWait(request, deps) {
|
|
|
25907
25961
|
let step = 0;
|
|
25908
25962
|
const cancelled = () => new CLIError(
|
|
25909
25963
|
"CANCELLED",
|
|
25910
|
-
|
|
25964
|
+
`Interrupted: ${CLI_NAME23} secrets wait stopped before the secrets were ready.`,
|
|
25911
25965
|
ExitCode.CANCELLED,
|
|
25912
25966
|
progress === void 0 ? {} : { progress }
|
|
25913
25967
|
);
|
|
@@ -26051,13 +26105,13 @@ function statusError(error, profile, warnings) {
|
|
|
26051
26105
|
if (error.code === "AUTH_REQUIRED" || error.code === "SESSION_NOT_FOUND") {
|
|
26052
26106
|
const recovery = recoveryOf(error, "sign_in");
|
|
26053
26107
|
if (recovery !== void 0) {
|
|
26054
|
-
const hint = recovery.command !== void 0 ? `Sign in once with: ${recovery.command}, then retry.` : `Sign in to profile "${profile}" once with
|
|
26108
|
+
const hint = recovery.command !== void 0 ? `Sign in once with: ${recovery.command}, then retry.` : `Sign in to profile "${profile}" once with ${CLI_NAME23} auth login, then retry.`;
|
|
26055
26109
|
return new CLIError("AUTH_REQUIRED", error.message, ExitCode.AUTH_REQUIRED, { hint, recovery, ...extra });
|
|
26056
26110
|
}
|
|
26057
26111
|
}
|
|
26058
26112
|
if (error.code === "PROFILE_MIGRATION_REQUIRED") {
|
|
26059
26113
|
const recovery = recoveryOf(error, "complete_migration");
|
|
26060
|
-
const hint = recovery?.command !== void 0 ? `Run ${recovery.command} once to finish this profile's one-time upgrade, then retry wait.` : `Run
|
|
26114
|
+
const hint = recovery?.command !== void 0 ? `Run ${recovery.command} once to finish this profile's one-time upgrade, then retry wait.` : `Run ${CLI_NAME23} auth caps once with profile "${profile}" to finish its one-time upgrade, then retry wait.`;
|
|
26061
26115
|
return new CLIError("PROFILE_MIGRATION_REQUIRED", error.message, ExitCode.ERROR, {
|
|
26062
26116
|
hint,
|
|
26063
26117
|
...recovery === void 0 ? {} : { recovery },
|
|
@@ -26249,11 +26303,11 @@ async function runSecretOperation(params) {
|
|
|
26249
26303
|
}
|
|
26250
26304
|
function secretPermissionReason(action, name) {
|
|
26251
26305
|
const target = name ? ` secret "${name}"` : " secrets";
|
|
26252
|
-
return `Allow
|
|
26306
|
+
return `Allow \`${CLI_NAME24} secrets ${action}${name ? ` ${name}` : ""}\` to access${target} with the required TinyCloud permissions.`;
|
|
26253
26307
|
}
|
|
26254
26308
|
function assertOwnerApprovalPossible(profileName2, profile, action, name, missing = []) {
|
|
26255
26309
|
if (profile.authMethod !== "openkey" || process.stdin.isTTY || process.stderr.isTTY) return;
|
|
26256
|
-
const command =
|
|
26310
|
+
const command = `${CLI_NAME24} secrets ${action === "del" ? "delete" : action}${name ? ` ${name}` : ""}`;
|
|
26257
26311
|
throw new CLIError(
|
|
26258
26312
|
"PERMISSION_DENIED",
|
|
26259
26313
|
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.`,
|
|
@@ -26264,7 +26318,7 @@ function assertOwnerApprovalPossible(profileName2, profile, action, name, missin
|
|
|
26264
26318
|
);
|
|
26265
26319
|
}
|
|
26266
26320
|
function scopedSecretLoginHint(profileName2) {
|
|
26267
|
-
return `Have the owner approve a scoped login whose manifest names the secret:
|
|
26321
|
+
return `Have the owner approve a scoped login whose manifest names the secret: ${CLI_NAME24} --profile ${profileName2} auth login --method openkey --paste --manifest <manifest.json>. Pass the owner's code on stdin, newline-terminated.`;
|
|
26268
26322
|
}
|
|
26269
26323
|
async function runSecretOperationAttempt(label, operation) {
|
|
26270
26324
|
try {
|
|
@@ -26398,7 +26452,7 @@ ${message}`,
|
|
|
26398
26452
|
"AUTH_REQUIRED",
|
|
26399
26453
|
"Not signed in to TinyCloud.",
|
|
26400
26454
|
ExitCode.AUTH_REQUIRED,
|
|
26401
|
-
{ hint: `Sign in with:
|
|
26455
|
+
{ hint: `Sign in with: ${CLI_NAME24} --profile ${result.context.profile} auth login` }
|
|
26402
26456
|
);
|
|
26403
26457
|
}
|
|
26404
26458
|
if (result.error.code === "NODE_UNREACHABLE") {
|
|
@@ -26875,7 +26929,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
26875
26929
|
name: "Encryption network",
|
|
26876
26930
|
ok: false,
|
|
26877
26931
|
detail: `${networkName} not found`,
|
|
26878
|
-
hint:
|
|
26932
|
+
hint: `${CLI_NAME24} secrets network init ${networkName}`
|
|
26879
26933
|
}
|
|
26880
26934
|
];
|
|
26881
26935
|
let secret;
|
|
@@ -26921,7 +26975,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
26921
26975
|
name: "Secret access",
|
|
26922
26976
|
ok: false,
|
|
26923
26977
|
detail: notFound ? `${resolved.permissionPaths.vault} not found` : result.error.message,
|
|
26924
|
-
hint: notFound ? local ?
|
|
26978
|
+
hint: notFound ? local ? `${CLI_NAME24} secrets put ${resolved.name}${formatSecretScopeFlag(scopeOptions)} --stdin` : `${CLI_NAME24} secrets request ${resolved.name}${formatSecretScopeFlag(scopeOptions)}` : `Ask the owner profile to grant ${secretCapabilityAction("get")} and ${SECRET_DECRYPT_CAPABILITY}.`
|
|
26925
26979
|
});
|
|
26926
26980
|
}
|
|
26927
26981
|
} else {
|
|
@@ -27165,7 +27219,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
27165
27219
|
if (effectiveLocal(profile, options)) {
|
|
27166
27220
|
throw new CLIError(
|
|
27167
27221
|
"LOCAL_OWNER_PROFILE",
|
|
27168
|
-
`Profile "${ctx.profile}" uses a local key, which owns its secrets: store them with
|
|
27222
|
+
`Profile "${ctx.profile}" uses a local key, which owns its secrets: store them with ${CLI_NAME24} secrets put NAME --stdin instead of requesting them.`,
|
|
27169
27223
|
ExitCode.USAGE_ERROR
|
|
27170
27224
|
);
|
|
27171
27225
|
}
|
|
@@ -27286,6 +27340,9 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
27286
27340
|
});
|
|
27287
27341
|
}
|
|
27288
27342
|
|
|
27343
|
+
// src/commands/share.ts
|
|
27344
|
+
import { CLI_NAME as CLI_NAME25 } from "@tinycloud/operations/cli-name";
|
|
27345
|
+
|
|
27289
27346
|
// ../share-sdk/dist/index.js
|
|
27290
27347
|
init_sha2();
|
|
27291
27348
|
init_ed25519();
|
|
@@ -35765,12 +35822,12 @@ function shareCliError(error, operation = "publish") {
|
|
|
35765
35822
|
const profileName2 = "profileName" in failure ? failure.profileName : void 0;
|
|
35766
35823
|
const localKey = "localKey" in failure && failure.localKey === true;
|
|
35767
35824
|
const profileHint = profileName2 === void 0 ? "" : `--profile ${profileName2} `;
|
|
35768
|
-
const loginHint = localKey ?
|
|
35825
|
+
const loginHint = localKey ? `\`${CLI_NAME25} ${profileHint}auth login --method local\`` : `\`${CLI_NAME25} ${profileHint}auth login --device --manifest builtin:share-publishing\` (or \`${CLI_NAME25} ${profileHint}enable share\`)`;
|
|
35769
35826
|
if (failure.kind === "caveated-session") {
|
|
35770
35827
|
const holder = profileName2 === void 0 ? "this session" : `profile ${profileName2}'s session`;
|
|
35771
35828
|
return new CLIError(
|
|
35772
35829
|
"PERMISSION_DENIED",
|
|
35773
|
-
`${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):
|
|
35830
|
+
`${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_NAME25} init --name publisher --key-only && ${CLI_NAME25} --profile publisher enable share\``,
|
|
35774
35831
|
5
|
|
35775
35832
|
);
|
|
35776
35833
|
}
|
|
@@ -36131,6 +36188,7 @@ init_constants();
|
|
|
36131
36188
|
init_sdk();
|
|
36132
36189
|
init_host();
|
|
36133
36190
|
init_theme();
|
|
36191
|
+
import { CLI_NAME as CLI_NAME26 } from "@tinycloud/operations/cli-name";
|
|
36134
36192
|
import { randomBytes as randomBytes8 } from "crypto";
|
|
36135
36193
|
import { mkdir as mkdir5, writeFile as writeFile4 } from "fs/promises";
|
|
36136
36194
|
import { dirname as dirname6 } from "path";
|
|
@@ -36182,10 +36240,10 @@ function registerSpaceCommand(program) {
|
|
|
36182
36240
|
|
|
36183
36241
|
A delegate cannot host a space \u2014 only its owner (root authority) can. This
|
|
36184
36242
|
emits a tinycloud.host.request artifact naming the space and its owner so you
|
|
36185
|
-
can hand it to the owner; they run
|
|
36243
|
+
can hand it to the owner; they run \`${CLI_NAME26} space host <name>\` and confirm.
|
|
36186
36244
|
|
|
36187
36245
|
If you ARE the owner of the resolved space, this refuses and tells you to host
|
|
36188
|
-
it directly with
|
|
36246
|
+
it directly with \`${CLI_NAME26} space host <name>\` (no request needed).
|
|
36189
36247
|
`).action(async (name, options, cmd) => {
|
|
36190
36248
|
try {
|
|
36191
36249
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -36196,7 +36254,7 @@ it directly with \`tc space host <name>\` (no request needed).
|
|
|
36196
36254
|
if (await isRootAuthority(spaceId, ctx.profile)) {
|
|
36197
36255
|
throw new CLIError(
|
|
36198
36256
|
"ALREADY_ROOT_AUTHORITY",
|
|
36199
|
-
`You are the owner of ${spaceId}. Host it directly:
|
|
36257
|
+
`You are the owner of ${spaceId}. Host it directly: ${CLI_NAME26} space host ${spaceName}`,
|
|
36200
36258
|
ExitCode.USAGE_ERROR
|
|
36201
36259
|
);
|
|
36202
36260
|
}
|
|
@@ -36282,6 +36340,7 @@ init_sdk();
|
|
|
36282
36340
|
init_space();
|
|
36283
36341
|
init_host();
|
|
36284
36342
|
init_theme();
|
|
36343
|
+
import { CLI_NAME as CLI_NAME27 } from "@tinycloud/operations/cli-name";
|
|
36285
36344
|
import { writeFile as writeFile5 } from "fs/promises";
|
|
36286
36345
|
import { resolve as resolve4 } from "path";
|
|
36287
36346
|
async function dbHandle(node, dbName, spaceInput, profileName2) {
|
|
@@ -36305,12 +36364,12 @@ database for simple apps, or pass --db to target a named database. Pass
|
|
|
36305
36364
|
--space to target a non-primary space (e.g. the manifest "applications" space).
|
|
36306
36365
|
|
|
36307
36366
|
Common workflows:
|
|
36308
|
-
$
|
|
36309
|
-
$
|
|
36310
|
-
$
|
|
36311
|
-
$
|
|
36312
|
-
$
|
|
36313
|
-
$
|
|
36367
|
+
$ ${CLI_NAME27} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
|
|
36368
|
+
$ ${CLI_NAME27} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["ship docs"]'
|
|
36369
|
+
$ ${CLI_NAME27} sql query "SELECT id, body FROM notes ORDER BY id"
|
|
36370
|
+
$ ${CLI_NAME27} sql query "SELECT * FROM events WHERE type = ?" --db analytics --params '["signup"]'
|
|
36371
|
+
$ ${CLI_NAME27} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
|
|
36372
|
+
$ ${CLI_NAME27} sql export --db analytics --output analytics.db
|
|
36314
36373
|
|
|
36315
36374
|
Commands:
|
|
36316
36375
|
query Read rows with SELECT statements
|
|
@@ -36327,10 +36386,10 @@ Tips:
|
|
|
36327
36386
|
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", `
|
|
36328
36387
|
|
|
36329
36388
|
Examples:
|
|
36330
|
-
$
|
|
36331
|
-
$
|
|
36332
|
-
$
|
|
36333
|
-
$
|
|
36389
|
+
$ ${CLI_NAME27} sql query "SELECT * FROM notes ORDER BY id"
|
|
36390
|
+
$ ${CLI_NAME27} sql query "SELECT * FROM notes WHERE id = ?" --params '[42]'
|
|
36391
|
+
$ ${CLI_NAME27} sql query "SELECT count(*) AS total FROM events" --db analytics --json
|
|
36392
|
+
$ ${CLI_NAME27} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
|
|
36334
36393
|
|
|
36335
36394
|
Output:
|
|
36336
36395
|
Human output is formatted as a table. Piped output or --json returns
|
|
@@ -36371,11 +36430,11 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
|
|
|
36371
36430
|
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", `
|
|
36372
36431
|
|
|
36373
36432
|
Examples:
|
|
36374
|
-
$
|
|
36375
|
-
$
|
|
36376
|
-
$
|
|
36377
|
-
$
|
|
36378
|
-
$
|
|
36433
|
+
$ ${CLI_NAME27} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
|
|
36434
|
+
$ ${CLI_NAME27} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["first note"]'
|
|
36435
|
+
$ ${CLI_NAME27} sql execute "UPDATE notes SET body = ? WHERE id = ?" --params '["edited", 1]'
|
|
36436
|
+
$ ${CLI_NAME27} sql execute "DROP TABLE old_notes" --db archive
|
|
36437
|
+
$ ${CLI_NAME27} sql execute "DELETE FROM conversation WHERE id = ?" --space applications --db xyz.tinycloud.listen/conversations --params '["abc"]'
|
|
36379
36438
|
|
|
36380
36439
|
Output:
|
|
36381
36440
|
Returns JSON with the changed row count and last inserted row id when available.
|
|
@@ -36404,9 +36463,9 @@ Output:
|
|
|
36404
36463
|
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", `
|
|
36405
36464
|
|
|
36406
36465
|
Examples:
|
|
36407
|
-
$
|
|
36408
|
-
$
|
|
36409
|
-
$
|
|
36466
|
+
$ ${CLI_NAME27} sql export
|
|
36467
|
+
$ ${CLI_NAME27} sql export --db analytics --output analytics.db
|
|
36468
|
+
$ ${CLI_NAME27} sql export --space applications --db xyz.tinycloud.listen/conversations --output listen.db
|
|
36410
36469
|
|
|
36411
36470
|
Output:
|
|
36412
36471
|
Writes the database file locally and returns JSON with the path and size.
|
|
@@ -36439,19 +36498,19 @@ Output:
|
|
|
36439
36498
|
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", `
|
|
36440
36499
|
|
|
36441
36500
|
Examples:
|
|
36442
|
-
$
|
|
36501
|
+
$ ${CLI_NAME27} sql copy --from-db com.tinycloud.conversation-sync/conversations \\
|
|
36443
36502
|
--to-db xyz.tinycloud.listen/conversations \\
|
|
36444
36503
|
--space applications --dry-run
|
|
36445
|
-
$
|
|
36504
|
+
$ ${CLI_NAME27} sql copy --from-space applications --from-db com.foo/data \\
|
|
36446
36505
|
--to-space applications --to-db com.bar/data \\
|
|
36447
36506
|
--table conversation --table participant
|
|
36448
36507
|
|
|
36449
36508
|
Notes:
|
|
36450
36509
|
- Refuses to run when (resolved space, db) is identical for source and destination.
|
|
36451
|
-
- Does NOT create destination tables. Run the target app once (or use
|
|
36510
|
+
- Does NOT create destination tables. Run the target app once (or use \`${CLI_NAME27} sql execute\`)
|
|
36452
36511
|
to materialize the schema before copying.
|
|
36453
36512
|
- One row at a time; suitable for small/medium datasets. Large copies should
|
|
36454
|
-
use
|
|
36513
|
+
use \`${CLI_NAME27} sql export\` + bulk import.
|
|
36455
36514
|
- Authorization: the active session/delegation must cover sql/read on source
|
|
36456
36515
|
AND sql/write on destination. Otherwise the relevant operation will fail.
|
|
36457
36516
|
`).action(async (options, cmd) => {
|
|
@@ -36551,6 +36610,7 @@ init_errors();
|
|
|
36551
36610
|
init_formatter();
|
|
36552
36611
|
init_theme();
|
|
36553
36612
|
init_permissions();
|
|
36613
|
+
import { CLI_NAME as CLI_NAME28 } from "@tinycloud/operations/cli-name";
|
|
36554
36614
|
import {
|
|
36555
36615
|
NodeWasmBindings as NodeWasmBindings2
|
|
36556
36616
|
} from "@tinycloud/node-sdk";
|
|
@@ -36824,7 +36884,7 @@ function formatStatus(summary) {
|
|
|
36824
36884
|
lines.push(`Default profile: ${theme.value(summary.defaultProfile)}`);
|
|
36825
36885
|
lines.push("");
|
|
36826
36886
|
if (summary.profiles.length === 0) {
|
|
36827
|
-
lines.push(theme.muted(
|
|
36887
|
+
lines.push(theme.muted(`No profiles configured. Run: ${CLI_NAME28} init`));
|
|
36828
36888
|
return `${lines.join("\n")}
|
|
36829
36889
|
`;
|
|
36830
36890
|
}
|