@tinycloud/cli 1.3.0-beta.23 → 1.3.0-beta.24
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/legacy-entry.js +169 -121
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +5 -5
- package/skills/tc-cli/SDK.md +8 -9
package/dist/legacy-entry.js
CHANGED
|
@@ -11231,11 +11231,12 @@ function announceFirstOwner(profileName2, expectedOwner, ownerDid) {
|
|
|
11231
11231
|
`);
|
|
11232
11232
|
}
|
|
11233
11233
|
function ownerLoginPermissions(owner) {
|
|
11234
|
-
const space = ownerSpaceId("default", owner);
|
|
11234
|
+
const space = owner === void 0 ? "default" : ownerSpaceId("default", owner);
|
|
11235
11235
|
return [
|
|
11236
11236
|
{ service: "tinycloud.kv", space, path: "", actions: ["tinycloud.kv/put", "tinycloud.kv/get", "tinycloud.kv/del", "tinycloud.kv/list", "tinycloud.kv/metadata"] },
|
|
11237
11237
|
{ service: "tinycloud.sql", space, path: "", actions: ["tinycloud.sql/read", "tinycloud.sql/write", "tinycloud.sql/admin"] },
|
|
11238
|
-
{ service: "tinycloud.capabilities", space, path: "", actions: ["tinycloud.capabilities/read"] }
|
|
11238
|
+
{ service: "tinycloud.capabilities", space, path: "", actions: ["tinycloud.capabilities/read"] },
|
|
11239
|
+
{ service: "tinycloud.delegation", space, path: "", actions: ["tinycloud.delegation/list"] }
|
|
11239
11240
|
];
|
|
11240
11241
|
}
|
|
11241
11242
|
|
|
@@ -14452,9 +14453,10 @@ async function refreshOpenKeySession(profileName2, host, options = {}) {
|
|
|
14452
14453
|
jwk: key,
|
|
14453
14454
|
host,
|
|
14454
14455
|
openkeyHost: resolveOpenKeyHost(profile),
|
|
14455
|
-
// A plain login
|
|
14456
|
-
// so OpenKey preselects its key; the signed
|
|
14457
|
-
|
|
14456
|
+
// A plain login sends the default abilities plus delegation/list explicitly. With a pinned
|
|
14457
|
+
// owner (or --owner) the space is that owner's, so OpenKey preselects its key; the signed
|
|
14458
|
+
// owner is verified below.
|
|
14459
|
+
permissions: permissions ?? ownerLoginPermissions(expectedOwner),
|
|
14458
14460
|
expiry: openKeyExpiry,
|
|
14459
14461
|
...permissions ? { reason: "Allow this TinyCloud CLI profile to use the permissions in the requested manifest." } : {}
|
|
14460
14462
|
});
|
|
@@ -14686,14 +14688,42 @@ init_formatter();
|
|
|
14686
14688
|
init_errors();
|
|
14687
14689
|
init_constants();
|
|
14688
14690
|
init_sdk();
|
|
14689
|
-
|
|
14690
|
-
|
|
14691
|
-
|
|
14692
|
-
|
|
14691
|
+
import { CLI_NAME as CLI_NAME17 } from "@tinycloud/operations/cli-name";
|
|
14692
|
+
import { PermissionNotInManifestError } from "@tinycloud/node-sdk";
|
|
14693
|
+
async function queryDelegations(node, options, until) {
|
|
14694
|
+
const found = [];
|
|
14695
|
+
let cursor;
|
|
14696
|
+
do {
|
|
14697
|
+
const page = await node.account.delegations.query({ ...options, limit: 100, cursor });
|
|
14698
|
+
if (!page.ok) {
|
|
14699
|
+
if (page.error.code === "PERMISSION_DENIED") {
|
|
14700
|
+
throw new CLIError(
|
|
14701
|
+
"PERMISSION_DENIED",
|
|
14702
|
+
`This session cannot read delegations: it lacks tinycloud.delegation/list. Run \`${CLI_NAME17} auth login\` again to grant it.`,
|
|
14703
|
+
ExitCode.PERMISSION_DENIED
|
|
14704
|
+
);
|
|
14705
|
+
}
|
|
14706
|
+
throw cliErrorFromService(page.error);
|
|
14707
|
+
}
|
|
14708
|
+
found.push(...page.data.items);
|
|
14709
|
+
cursor = page.data.nextCursor;
|
|
14710
|
+
} while (cursor && !(until && found.some((d) => d.cid === until)));
|
|
14711
|
+
return found;
|
|
14693
14712
|
}
|
|
14694
|
-
function
|
|
14695
|
-
|
|
14696
|
-
|
|
14713
|
+
function listedDelegation(d) {
|
|
14714
|
+
const resources = d.resources.map((r) => {
|
|
14715
|
+
const [space, service, ...path] = r.resource.split("/");
|
|
14716
|
+
return { space, service, path: path.join("/"), actions: r.actions };
|
|
14717
|
+
});
|
|
14718
|
+
return {
|
|
14719
|
+
cid: d.cid,
|
|
14720
|
+
delegatee: d.delegateDid,
|
|
14721
|
+
delegator: d.delegatorDid,
|
|
14722
|
+
direction: d.direction,
|
|
14723
|
+
status: d.status,
|
|
14724
|
+
expiry: d.expiresAt?.toISOString() ?? null,
|
|
14725
|
+
resources
|
|
14726
|
+
};
|
|
14697
14727
|
}
|
|
14698
14728
|
async function confirmCurrentSessionRevoke(yes) {
|
|
14699
14729
|
if (yes) return;
|
|
@@ -14724,69 +14754,87 @@ function registerDelegationCommand(program, configuration = {}) {
|
|
|
14724
14754
|
const trimmed = a.trim();
|
|
14725
14755
|
return trimmed.startsWith("tinycloud.") ? trimmed : `tinycloud.${trimmed}`;
|
|
14726
14756
|
});
|
|
14757
|
+
const byService = {};
|
|
14758
|
+
for (const action of actions) {
|
|
14759
|
+
const slash = action.indexOf("/");
|
|
14760
|
+
if (slash <= "tinycloud.".length || slash === action.length - 1) {
|
|
14761
|
+
throw new CLIError("USAGE_ERROR", `Invalid action "${action}"; use service/action, e.g. kv/get`, ExitCode.USAGE_ERROR);
|
|
14762
|
+
}
|
|
14763
|
+
(byService[action.slice(0, slash)] ??= []).push(action);
|
|
14764
|
+
}
|
|
14727
14765
|
const expiry = parseExpiry2(options.expiry);
|
|
14728
|
-
const
|
|
14729
|
-
|
|
14730
|
-
|
|
14731
|
-
|
|
14732
|
-
|
|
14766
|
+
const recipient = options.to.split("#")[0];
|
|
14767
|
+
const { delegation: created } = await node.delegateTo(
|
|
14768
|
+
recipient,
|
|
14769
|
+
Object.entries(byService).map(([service, serviceActions]) => ({
|
|
14770
|
+
service,
|
|
14771
|
+
space: "default",
|
|
14772
|
+
path: options.path,
|
|
14773
|
+
actions: serviceActions
|
|
14774
|
+
})),
|
|
14775
|
+
{ expiry: expiry.getTime() - Date.now(), requireActivation: true }
|
|
14776
|
+
).catch((error) => {
|
|
14777
|
+
if (error instanceof PermissionNotInManifestError) {
|
|
14778
|
+
const needed = error.missing.map((m) => `${m.actions.join(", ")} on space "${m.space ?? "default"}" path "${m.path}"`).join("; ");
|
|
14779
|
+
throw new CLIError(
|
|
14780
|
+
"PERMISSION_DENIED",
|
|
14781
|
+
`This session cannot delegate what was requested. Missing: ${needed}. Sign in again with these permissions.`,
|
|
14782
|
+
ExitCode.PERMISSION_DENIED
|
|
14783
|
+
);
|
|
14784
|
+
}
|
|
14785
|
+
throw error;
|
|
14733
14786
|
});
|
|
14734
|
-
|
|
14735
|
-
|
|
14787
|
+
const confirmed = (await queryDelegations(node, { direction: "granted" }, created.cid)).find((d) => d.cid === created.cid);
|
|
14788
|
+
if (confirmed?.status !== "active") {
|
|
14789
|
+
throw new CLIError(
|
|
14790
|
+
"DELEGATION_NOT_CONFIRMED",
|
|
14791
|
+
confirmed ? `The node lists delegation ${created.cid} as ${confirmed.status}, not active; treat it as not created.` : `The node accepted delegation ${created.cid} but does not list it; treat it as not created.`,
|
|
14792
|
+
ExitCode.ERROR
|
|
14793
|
+
);
|
|
14736
14794
|
}
|
|
14737
14795
|
outputJson({
|
|
14738
|
-
cid:
|
|
14739
|
-
delegateDid:
|
|
14796
|
+
cid: created.cid,
|
|
14797
|
+
delegateDid: recipient,
|
|
14798
|
+
delegatorDid: confirmed.delegatorDid,
|
|
14740
14799
|
path: options.path,
|
|
14741
14800
|
actions,
|
|
14742
|
-
expiry: expiry.toISOString()
|
|
14801
|
+
expiry: created.expiry.toISOString(),
|
|
14802
|
+
status: confirmed.status
|
|
14743
14803
|
});
|
|
14744
14804
|
} catch (error) {
|
|
14745
14805
|
return handleError(error);
|
|
14746
14806
|
}
|
|
14747
14807
|
});
|
|
14748
|
-
delegation.command("list").description("List delegations").option("--granted", "Show only delegations I've granted").option("--received", "Show only delegations I've received").action(async (options, cmd) => {
|
|
14808
|
+
delegation.command("list").description("List delegations").option("--granted", "Show only delegations I've granted").option("--received", "Show only delegations I've received").option("--space <space>", "Filter by space name or ID").action(async (options, cmd) => {
|
|
14749
14809
|
try {
|
|
14750
14810
|
const globalOpts = cmd.optsWithGlobals();
|
|
14751
14811
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
14752
14812
|
const node = await ensureAuthenticated(ctx);
|
|
14753
|
-
|
|
14754
|
-
|
|
14755
|
-
throw cliErrorFromService(result.error);
|
|
14756
|
-
}
|
|
14757
|
-
let delegations = result.data;
|
|
14758
|
-
if (options.granted) {
|
|
14759
|
-
const myDid = node.did;
|
|
14760
|
-
delegations = delegations.filter((d) => didMatches(d.delegatorDID, myDid));
|
|
14761
|
-
} else if (options.received) {
|
|
14762
|
-
const myDid = node.did;
|
|
14763
|
-
delegations = delegations.filter((d) => didMatches(d.delegateDID, myDid));
|
|
14813
|
+
if (options.granted && options.received) {
|
|
14814
|
+
throw new CLIError("USAGE_ERROR", "Use only one of --granted or --received.", ExitCode.USAGE_ERROR);
|
|
14764
14815
|
}
|
|
14816
|
+
const delegations = await queryDelegations(node, {
|
|
14817
|
+
direction: options.granted ? "granted" : options.received ? "received" : "all",
|
|
14818
|
+
space: options.space
|
|
14819
|
+
});
|
|
14765
14820
|
outputJson({
|
|
14766
|
-
delegations: delegations.map(
|
|
14767
|
-
cid: d.cid,
|
|
14768
|
-
delegatee: d.delegateDID,
|
|
14769
|
-
delegator: d.delegatorDID,
|
|
14770
|
-
path: d.path,
|
|
14771
|
-
actions: d.actions,
|
|
14772
|
-
expiry: d.expiry instanceof Date ? d.expiry.toISOString() : d.expiry
|
|
14773
|
-
})),
|
|
14821
|
+
delegations: delegations.map(listedDelegation),
|
|
14774
14822
|
count: delegations.length
|
|
14775
14823
|
});
|
|
14776
14824
|
} catch (error) {
|
|
14777
14825
|
return handleError(error);
|
|
14778
14826
|
}
|
|
14779
14827
|
});
|
|
14780
|
-
delegation.command("info <cid>").description("Get delegation details").action(async (cid,
|
|
14828
|
+
delegation.command("info <cid>").description("Get delegation details").option("--space <space>", "Filter by space name or ID").action(async (cid, options, cmd) => {
|
|
14781
14829
|
try {
|
|
14782
14830
|
const globalOpts = cmd.optsWithGlobals();
|
|
14783
14831
|
const ctx = await ProfileManager.resolveContext(globalOpts);
|
|
14784
14832
|
const node = await ensureAuthenticated(ctx);
|
|
14785
|
-
const
|
|
14786
|
-
if (!
|
|
14787
|
-
throw new CLIError("NOT_FOUND", `Delegation "${cid}" not found`, ExitCode.NOT_FOUND);
|
|
14833
|
+
const found = (await queryDelegations(node, { direction: "all", space: options.space })).find((d) => d.cid === cid);
|
|
14834
|
+
if (!found) {
|
|
14835
|
+
throw new CLIError("NOT_FOUND", `Delegation "${cid}" not found in this account's delegations`, ExitCode.NOT_FOUND);
|
|
14788
14836
|
}
|
|
14789
|
-
outputJson(
|
|
14837
|
+
outputJson(listedDelegation(found));
|
|
14790
14838
|
} catch (error) {
|
|
14791
14839
|
return handleError(error);
|
|
14792
14840
|
}
|
|
@@ -14826,7 +14874,7 @@ init_constants();
|
|
|
14826
14874
|
init_formatter();
|
|
14827
14875
|
init_theme();
|
|
14828
14876
|
init_errors();
|
|
14829
|
-
import { CLI_NAME as
|
|
14877
|
+
import { CLI_NAME as CLI_NAME18 } from "@tinycloud/operations/cli-name";
|
|
14830
14878
|
init_session_expired();
|
|
14831
14879
|
init_types();
|
|
14832
14880
|
function registerDoctorCommand(program) {
|
|
@@ -14860,7 +14908,7 @@ function registerDoctorCommand(program) {
|
|
|
14860
14908
|
const profile = await ProfileManager.getProfile(profileName2);
|
|
14861
14909
|
keyDetail = profile.did ? `${profile.did.slice(0, 20)}...` : "key found";
|
|
14862
14910
|
} else {
|
|
14863
|
-
keyDetail = `no key \u2014 run ${
|
|
14911
|
+
keyDetail = `no key \u2014 run ${CLI_NAME18} init`;
|
|
14864
14912
|
}
|
|
14865
14913
|
} catch {
|
|
14866
14914
|
keyDetail = "error reading key";
|
|
@@ -14882,7 +14930,7 @@ function registerDoctorCommand(program) {
|
|
|
14882
14930
|
} else if (state2 === "unverified") {
|
|
14883
14931
|
sessionDetail = `not usable (no signed OpenKey proof) \u2014 ${signInAgainHint(profileName2, resolveProfilePosture(profile))}`;
|
|
14884
14932
|
} else {
|
|
14885
|
-
sessionDetail = sessionOk ? "active" : `no session \u2014 run ${
|
|
14933
|
+
sessionDetail = sessionOk ? "active" : `no session \u2014 run ${CLI_NAME18} auth login`;
|
|
14886
14934
|
}
|
|
14887
14935
|
} catch {
|
|
14888
14936
|
sessionDetail = "error reading session";
|
|
@@ -14910,7 +14958,7 @@ function registerDoctorCommand(program) {
|
|
|
14910
14958
|
try {
|
|
14911
14959
|
const profile = await ProfileManager.getProfile(profileName2);
|
|
14912
14960
|
spaceOk = Boolean(profile.spaceId);
|
|
14913
|
-
spaceDetail = spaceOk ? `${profile.spaceId.slice(0, 16)}...` : `no space \u2014 run ${
|
|
14961
|
+
spaceDetail = spaceOk ? `${profile.spaceId.slice(0, 16)}...` : `no space \u2014 run ${CLI_NAME18} space create`;
|
|
14914
14962
|
} catch {
|
|
14915
14963
|
spaceDetail = "error checking space";
|
|
14916
14964
|
}
|
|
@@ -15100,7 +15148,7 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
|
|
|
15100
15148
|
}
|
|
15101
15149
|
|
|
15102
15150
|
// src/commands/enable.ts
|
|
15103
|
-
import { CLI_NAME as
|
|
15151
|
+
import { CLI_NAME as CLI_NAME19 } from "@tinycloud/operations/cli-name";
|
|
15104
15152
|
init_constants();
|
|
15105
15153
|
init_profiles();
|
|
15106
15154
|
init_errors();
|
|
@@ -15108,7 +15156,7 @@ init_formatter();
|
|
|
15108
15156
|
init_publishing_manifest();
|
|
15109
15157
|
function registerEnableCommand(program) {
|
|
15110
15158
|
const enable = program.command("enable").description("Enable a narrowly scoped TinyCloud service");
|
|
15111
|
-
enable.command("share").description(`Approve Share publishing scope through OpenKey device authorization (same as \`${
|
|
15159
|
+
enable.command("share").description(`Approve Share publishing scope through OpenKey device authorization (same as \`${CLI_NAME19} auth login --device --manifest builtin:share-publishing\`). Use a dedicated profile, e.g. \`${CLI_NAME19} init --name publisher --key-only && ${CLI_NAME19} --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) => {
|
|
15112
15160
|
try {
|
|
15113
15161
|
const globalOptions = command.optsWithGlobals();
|
|
15114
15162
|
const context = await ProfileManager.resolveContext(globalOptions, { storedSession: "unused" });
|
|
@@ -15144,7 +15192,7 @@ init_profiles();
|
|
|
15144
15192
|
init_formatter();
|
|
15145
15193
|
init_errors();
|
|
15146
15194
|
init_constants();
|
|
15147
|
-
import { CLI_NAME as
|
|
15195
|
+
import { CLI_NAME as CLI_NAME20 } from "@tinycloud/operations/cli-name";
|
|
15148
15196
|
function registerInitCommand(program) {
|
|
15149
15197
|
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) => {
|
|
15150
15198
|
try {
|
|
@@ -15162,7 +15210,7 @@ function registerInitCommand(program) {
|
|
|
15162
15210
|
if (await ProfileManager.profileExists(profileName2)) {
|
|
15163
15211
|
throw new CLIError(
|
|
15164
15212
|
"PROFILE_EXISTS",
|
|
15165
|
-
`Profile "${profileName2}" already exists. Use \`${
|
|
15213
|
+
`Profile "${profileName2}" already exists. Use \`${CLI_NAME20} profile delete ${profileName2}\` first or choose a different name.`,
|
|
15166
15214
|
ExitCode.ERROR
|
|
15167
15215
|
);
|
|
15168
15216
|
}
|
|
@@ -15512,7 +15560,7 @@ init_errors();
|
|
|
15512
15560
|
init_constants();
|
|
15513
15561
|
init_theme();
|
|
15514
15562
|
import { resolveManifestKnowledgeRoot as resolveManifestKnowledgeRoot2 } from "@tinycloud/sdk-core";
|
|
15515
|
-
import { CLI_NAME as
|
|
15563
|
+
import { CLI_NAME as CLI_NAME21 } from "@tinycloud/operations/cli-name";
|
|
15516
15564
|
import { readFile as readFile11 } from "fs/promises";
|
|
15517
15565
|
var DEFAULT_APP_SPACE = "applications";
|
|
15518
15566
|
function registerManifestCommand(program) {
|
|
@@ -15520,8 +15568,8 @@ function registerManifestCommand(program) {
|
|
|
15520
15568
|
manifest.command("resolve <source>").description("Resolve a manifest file or URL to its effective space, paths, and DB basenames").addHelpText("after", `
|
|
15521
15569
|
|
|
15522
15570
|
Examples:
|
|
15523
|
-
$ ${
|
|
15524
|
-
$ ${
|
|
15571
|
+
$ ${CLI_NAME21} manifest resolve ./manifest.json
|
|
15572
|
+
$ ${CLI_NAME21} manifest resolve https://app.example.com/manifest.json --json
|
|
15525
15573
|
|
|
15526
15574
|
What it shows:
|
|
15527
15575
|
- app_id, name, manifest_version
|
|
@@ -15531,7 +15579,7 @@ What it shows:
|
|
|
15531
15579
|
|
|
15532
15580
|
This command is read-only and does NOT contact the node \u2014 it just resolves
|
|
15533
15581
|
the manifest against the active profile's address/chain so you know which
|
|
15534
|
-
\`--space\` and \`--db\` values to pass to other ${
|
|
15582
|
+
\`--space\` and \`--db\` values to pass to other ${CLI_NAME21} commands.
|
|
15535
15583
|
`).action(async (source, _options, cmd) => {
|
|
15536
15584
|
try {
|
|
15537
15585
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -15600,7 +15648,7 @@ ${theme.heading("SQL databases")}
|
|
|
15600
15648
|
`);
|
|
15601
15649
|
}
|
|
15602
15650
|
process.stdout.write(theme.muted(`
|
|
15603
|
-
Use with: ${
|
|
15651
|
+
Use with: ${CLI_NAME21} sql query --space ${spaceName} --db <db> "..."
|
|
15604
15652
|
`));
|
|
15605
15653
|
}
|
|
15606
15654
|
if (permissions.length > 0) {
|
|
@@ -15720,7 +15768,7 @@ init_profiles();
|
|
|
15720
15768
|
init_formatter();
|
|
15721
15769
|
init_errors();
|
|
15722
15770
|
init_constants();
|
|
15723
|
-
import { CLI_NAME as
|
|
15771
|
+
import { CLI_NAME as CLI_NAME22 } from "@tinycloud/operations/cli-name";
|
|
15724
15772
|
import { createInterface as createInterface5 } from "readline";
|
|
15725
15773
|
init_theme();
|
|
15726
15774
|
init_types();
|
|
@@ -15866,9 +15914,9 @@ profile at command time. Precedence for every kv/sql command:
|
|
|
15866
15914
|
explicit --space flag > profile defaultSpace > primary space.
|
|
15867
15915
|
|
|
15868
15916
|
Examples:
|
|
15869
|
-
$ ${
|
|
15870
|
-
$ ${
|
|
15871
|
-
$ ${
|
|
15917
|
+
$ ${CLI_NAME22} profile set-default-space applications
|
|
15918
|
+
$ ${CLI_NAME22} profile set-default-space applications --profile cli-test
|
|
15919
|
+
$ ${CLI_NAME22} profile set-default-space --unset
|
|
15872
15920
|
`).action(async (name, options, cmd) => {
|
|
15873
15921
|
try {
|
|
15874
15922
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -15880,7 +15928,7 @@ Examples:
|
|
|
15880
15928
|
if (!options.unset && (name === void 0 || name === "")) {
|
|
15881
15929
|
throw new CLIError(
|
|
15882
15930
|
"USAGE_ERROR",
|
|
15883
|
-
`Provide a space name (e.g. \`${
|
|
15931
|
+
`Provide a space name (e.g. \`${CLI_NAME22} profile set-default-space applications\`) or pass --unset.`,
|
|
15884
15932
|
ExitCode.USAGE_ERROR
|
|
15885
15933
|
);
|
|
15886
15934
|
}
|
|
@@ -15939,7 +15987,7 @@ function parseOperatorType(raw) {
|
|
|
15939
15987
|
|
|
15940
15988
|
// src/commands/replica.ts
|
|
15941
15989
|
init_profile_replicas();
|
|
15942
|
-
import { CLI_NAME as
|
|
15990
|
+
import { CLI_NAME as CLI_NAME23 } from "@tinycloud/operations/cli-name";
|
|
15943
15991
|
import { randomBytes as randomBytes7 } from "crypto";
|
|
15944
15992
|
import { constants as fsConstants5 } from "fs";
|
|
15945
15993
|
import { mkdir as mkdir5, open as open9, readdir as readdir6, rename as rename4, unlink as unlink4, lstat as lstat9 } from "fs/promises";
|
|
@@ -16080,11 +16128,11 @@ var EXIT_BY_CODE = {
|
|
|
16080
16128
|
var READ_FAILURE = {
|
|
16081
16129
|
absent: { code: "KEY_ABSENT", exit: ExitCode.NOT_FOUND, what: "is not in the replica (the replica covers its whole prefix)" },
|
|
16082
16130
|
deleted: { code: "KEY_DELETED", exit: ExitCode.NOT_FOUND, what: "was deleted at the source" },
|
|
16083
|
-
content_missing: { code: "CONTENT_MISSING", exit: ExitCode.NOT_FOUND, what: `is known but its content has not been fetched yet; run ${
|
|
16131
|
+
content_missing: { code: "CONTENT_MISSING", exit: ExitCode.NOT_FOUND, what: `is known but its content has not been fetched yet; run ${CLI_NAME23} replica sync` },
|
|
16084
16132
|
coverage_incomplete: {
|
|
16085
16133
|
code: "COVERAGE_INCOMPLETE",
|
|
16086
16134
|
exit: ExitCode.NOT_FOUND,
|
|
16087
|
-
what: `is not in the replica, which has not finished its first sync; run ${
|
|
16135
|
+
what: `is not in the replica, which has not finished its first sync; run ${CLI_NAME23} replica sync`
|
|
16088
16136
|
},
|
|
16089
16137
|
not_covered: { code: "NOT_COVERED", exit: ExitCode.USAGE_ERROR, what: "is outside the replica's prefix" }
|
|
16090
16138
|
};
|
|
@@ -16202,7 +16250,7 @@ async function existingReplicaName(profile, option) {
|
|
|
16202
16250
|
const names = await replicaNames(profile);
|
|
16203
16251
|
if (names.length === 1) return names[0];
|
|
16204
16252
|
if (names.length === 0) {
|
|
16205
|
-
throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, `Profile "${profile}" has no replicas. Create one with: ${
|
|
16253
|
+
throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, `Profile "${profile}" has no replicas. Create one with: ${CLI_NAME23} replica sync --prefix <prefix>`);
|
|
16206
16254
|
}
|
|
16207
16255
|
throw new CLIError("USAGE_ERROR", `Profile "${profile}" has several replicas (${names.join(", ")}); pass --replica <name>.`, ExitCode.USAGE_ERROR);
|
|
16208
16256
|
}
|
|
@@ -16310,9 +16358,9 @@ async function storedGrants(profile) {
|
|
|
16310
16358
|
function grantHint(space, prefix) {
|
|
16311
16359
|
return [
|
|
16312
16360
|
"A replica needs a device grant with get and sync on its prefix:",
|
|
16313
|
-
` device: ${
|
|
16314
|
-
` owner: ${
|
|
16315
|
-
` device: ${
|
|
16361
|
+
` device: ${CLI_NAME23} auth request --cap tinycloud.kv:${space}:${prefix}:get,list,metadata,sync --expiry 30d --emit req.json`,
|
|
16362
|
+
` owner: ${CLI_NAME23} auth grant req.json > grant.json`,
|
|
16363
|
+
` device: ${CLI_NAME23} auth import grant.json`
|
|
16316
16364
|
].join("\n");
|
|
16317
16365
|
}
|
|
16318
16366
|
function resolveSpace(input, prefix, grants) {
|
|
@@ -16403,7 +16451,7 @@ function describeStatus(status) {
|
|
|
16403
16451
|
formatField("Size", formatBytes(status.bytes)),
|
|
16404
16452
|
formatField("Grant", status.device.delegationCid),
|
|
16405
16453
|
formatField("Last error", status.lastError ? `${status.lastError.code}: ${status.lastError.message}` : null),
|
|
16406
|
-
formatField("Purge", status.purgePending ? `incomplete: content files remain; the next ${
|
|
16454
|
+
formatField("Purge", status.purgePending ? `incomplete: content files remain; the next ${CLI_NAME23} replica command retries` : null)
|
|
16407
16455
|
].join("\n");
|
|
16408
16456
|
}
|
|
16409
16457
|
async function writeFileAtomic(path, bytes) {
|
|
@@ -16567,7 +16615,7 @@ or coverage incomplete; 5 grant expired, revoked or not yet valid; 6 network;
|
|
|
16567
16615
|
(options, cmd) => run(async () => {
|
|
16568
16616
|
const profile = await profileName(cmd);
|
|
16569
16617
|
if (options.purge) {
|
|
16570
|
-
throw new CLIError("REPORT_READ_ONLY", `Replica report is read-only. To remove a named local replica, run ${
|
|
16618
|
+
throw new CLIError("REPORT_READ_ONLY", `Replica report is read-only. To remove a named local replica, run ${CLI_NAME23} replica reset --replica <name> --purge; flag-owned purge has no equivalent yet (TC-1309).`, ExitCode.USAGE_ERROR);
|
|
16571
16619
|
}
|
|
16572
16620
|
if (options.clearPending) {
|
|
16573
16621
|
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);
|
|
@@ -16669,7 +16717,7 @@ or coverage incomplete; 5 grant expired, revoked or not yet valid; 6 network;
|
|
|
16669
16717
|
if (differs !== void 0) {
|
|
16670
16718
|
throw new ReplicaError(
|
|
16671
16719
|
ReplicaErrorCode.CONFIG_MISMATCH,
|
|
16672
|
-
`Replica ${name} already follows ${differs}. Use another --replica name, or ${
|
|
16720
|
+
`Replica ${name} already follows ${differs}. Use another --replica name, or ${CLI_NAME23} replica reset --replica ${name} --purge first.`
|
|
16673
16721
|
);
|
|
16674
16722
|
}
|
|
16675
16723
|
}
|
|
@@ -16834,7 +16882,7 @@ init_theme();
|
|
|
16834
16882
|
init_errors();
|
|
16835
16883
|
init_warnings();
|
|
16836
16884
|
init_constants();
|
|
16837
|
-
import { CLI_NAME as
|
|
16885
|
+
import { CLI_NAME as CLI_NAME25 } from "@tinycloud/operations/cli-name";
|
|
16838
16886
|
import { readFile as readFile12 } from "fs/promises";
|
|
16839
16887
|
import { join as join16 } from "path";
|
|
16840
16888
|
import { homedir, hostname, userInfo } from "os";
|
|
@@ -17045,7 +17093,7 @@ function parseStatusEnvelope(line) {
|
|
|
17045
17093
|
// src/lib/secrets-wait.ts
|
|
17046
17094
|
init_constants();
|
|
17047
17095
|
init_errors();
|
|
17048
|
-
import { CLI_NAME as
|
|
17096
|
+
import { CLI_NAME as CLI_NAME24 } from "@tinycloud/operations/cli-name";
|
|
17049
17097
|
var WAIT_BACKOFF_MS = [3e3, 4500, 6750, 10125];
|
|
17050
17098
|
var WAIT_STEADY_INTERVAL_MS = 15e3;
|
|
17051
17099
|
var DEFAULT_WAIT_TIMEOUT = "5m";
|
|
@@ -17053,7 +17101,7 @@ var MAX_WAIT_TIMEOUT_MS = 9 * 6e4;
|
|
|
17053
17101
|
var MAX_CONSECUTIVE_FAILURES = 5;
|
|
17054
17102
|
var TIMEOUT_RE = /^(0|\d+s|\d+m)$/;
|
|
17055
17103
|
var RETRYABLE_STATUS_ERRORS = { NODE_UNREACHABLE: true, NODE_ERROR: true };
|
|
17056
|
-
var EXPLICIT_KEY_HINT = `${
|
|
17104
|
+
var EXPLICIT_KEY_HINT = `${CLI_NAME24} secrets wait never signs in, and an explicit private key has no stored session. Use a local profile signed in once with ${CLI_NAME24} --profile <profile> auth login --method local, without TC_PRIVATE_KEY.`;
|
|
17057
17105
|
function abortableSleep(ms2, signal) {
|
|
17058
17106
|
return new Promise((resolve8) => {
|
|
17059
17107
|
if (signal.aborted) {
|
|
@@ -17089,7 +17137,7 @@ async function runSecretsWait(request, deps) {
|
|
|
17089
17137
|
let step = 0;
|
|
17090
17138
|
const cancelled = () => new CLIError(
|
|
17091
17139
|
"CANCELLED",
|
|
17092
|
-
`Interrupted: ${
|
|
17140
|
+
`Interrupted: ${CLI_NAME24} secrets wait stopped before the secrets were ready.`,
|
|
17093
17141
|
ExitCode.CANCELLED,
|
|
17094
17142
|
progress === void 0 ? {} : { progress }
|
|
17095
17143
|
);
|
|
@@ -17233,13 +17281,13 @@ function statusError(error, profile, warnings) {
|
|
|
17233
17281
|
if (error.code === "AUTH_REQUIRED" || error.code === "SESSION_NOT_FOUND") {
|
|
17234
17282
|
const recovery = recoveryOf(error, "sign_in");
|
|
17235
17283
|
if (recovery !== void 0) {
|
|
17236
|
-
const hint = recovery.command !== void 0 ? `Sign in once with: ${recovery.command}, then retry.` : `Sign in to profile "${profile}" once with ${
|
|
17284
|
+
const hint = recovery.command !== void 0 ? `Sign in once with: ${recovery.command}, then retry.` : `Sign in to profile "${profile}" once with ${CLI_NAME24} auth login, then retry.`;
|
|
17237
17285
|
return new CLIError("AUTH_REQUIRED", error.message, ExitCode.AUTH_REQUIRED, { hint, recovery, ...extra });
|
|
17238
17286
|
}
|
|
17239
17287
|
}
|
|
17240
17288
|
if (error.code === "PROFILE_MIGRATION_REQUIRED") {
|
|
17241
17289
|
const recovery = recoveryOf(error, "complete_migration");
|
|
17242
|
-
const hint = recovery?.command !== void 0 ? `Run ${recovery.command} once to finish this profile's one-time upgrade, then retry wait.` : `Run ${
|
|
17290
|
+
const hint = recovery?.command !== void 0 ? `Run ${recovery.command} once to finish this profile's one-time upgrade, then retry wait.` : `Run ${CLI_NAME24} auth caps once with profile "${profile}" to finish its one-time upgrade, then retry wait.`;
|
|
17243
17291
|
return new CLIError("PROFILE_MIGRATION_REQUIRED", error.message, ExitCode.ERROR, {
|
|
17244
17292
|
hint,
|
|
17245
17293
|
...recovery === void 0 ? {} : { recovery },
|
|
@@ -17431,11 +17479,11 @@ async function runSecretOperation(params) {
|
|
|
17431
17479
|
}
|
|
17432
17480
|
function secretPermissionReason(action, name) {
|
|
17433
17481
|
const target = name ? ` secret "${name}"` : " secrets";
|
|
17434
|
-
return `Allow \`${
|
|
17482
|
+
return `Allow \`${CLI_NAME25} secrets ${action}${name ? ` ${name}` : ""}\` to access${target} with the required TinyCloud permissions.`;
|
|
17435
17483
|
}
|
|
17436
17484
|
function assertOwnerApprovalPossible(profileName2, profile, action, name, missing = []) {
|
|
17437
17485
|
if (profile.authMethod !== "openkey" || process.stdin.isTTY || process.stderr.isTTY) return;
|
|
17438
|
-
const command = `${
|
|
17486
|
+
const command = `${CLI_NAME25} secrets ${action === "del" ? "delete" : action}${name ? ` ${name}` : ""}`;
|
|
17439
17487
|
throw new CLIError(
|
|
17440
17488
|
"PERMISSION_DENIED",
|
|
17441
17489
|
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.`,
|
|
@@ -17446,7 +17494,7 @@ function assertOwnerApprovalPossible(profileName2, profile, action, name, missin
|
|
|
17446
17494
|
);
|
|
17447
17495
|
}
|
|
17448
17496
|
function scopedSecretLoginHint(profileName2) {
|
|
17449
|
-
return `Have the owner approve a scoped login whose manifest names the secret: ${
|
|
17497
|
+
return `Have the owner approve a scoped login whose manifest names the secret: ${CLI_NAME25} --profile ${profileName2} auth login --method openkey --paste --manifest <manifest.json>. Pass the owner's code on stdin, newline-terminated.`;
|
|
17450
17498
|
}
|
|
17451
17499
|
async function runSecretOperationAttempt(label, operation) {
|
|
17452
17500
|
try {
|
|
@@ -17580,7 +17628,7 @@ ${message}`,
|
|
|
17580
17628
|
"AUTH_REQUIRED",
|
|
17581
17629
|
"Not signed in to TinyCloud.",
|
|
17582
17630
|
ExitCode.AUTH_REQUIRED,
|
|
17583
|
-
{ hint: `Sign in with: ${
|
|
17631
|
+
{ hint: `Sign in with: ${CLI_NAME25} --profile ${result.context.profile} auth login` }
|
|
17584
17632
|
);
|
|
17585
17633
|
}
|
|
17586
17634
|
if (result.error.code === "NODE_UNREACHABLE") {
|
|
@@ -18057,7 +18105,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
18057
18105
|
name: "Encryption network",
|
|
18058
18106
|
ok: false,
|
|
18059
18107
|
detail: `${networkName} not found`,
|
|
18060
|
-
hint: `${
|
|
18108
|
+
hint: `${CLI_NAME25} secrets network init ${networkName}`
|
|
18061
18109
|
}
|
|
18062
18110
|
];
|
|
18063
18111
|
let secret;
|
|
@@ -18103,7 +18151,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
18103
18151
|
name: "Secret access",
|
|
18104
18152
|
ok: false,
|
|
18105
18153
|
detail: notFound ? `${resolved.permissionPaths.vault} not found` : result.error.message,
|
|
18106
|
-
hint: notFound ? local ? `${
|
|
18154
|
+
hint: notFound ? local ? `${CLI_NAME25} secrets put ${resolved.name}${formatSecretScopeFlag(scopeOptions)} --stdin` : `${CLI_NAME25} secrets request ${resolved.name}${formatSecretScopeFlag(scopeOptions)}` : `Ask the owner profile to grant ${secretCapabilityAction("get")} and ${SECRET_DECRYPT_CAPABILITY}.`
|
|
18107
18155
|
});
|
|
18108
18156
|
}
|
|
18109
18157
|
} else {
|
|
@@ -18347,7 +18395,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
18347
18395
|
if (effectiveLocal(profile, options)) {
|
|
18348
18396
|
throw new CLIError(
|
|
18349
18397
|
"LOCAL_OWNER_PROFILE",
|
|
18350
|
-
`Profile "${ctx.profile}" uses a local key, which owns its secrets: store them with ${
|
|
18398
|
+
`Profile "${ctx.profile}" uses a local key, which owns its secrets: store them with ${CLI_NAME25} secrets put NAME --stdin instead of requesting them.`,
|
|
18351
18399
|
ExitCode.USAGE_ERROR
|
|
18352
18400
|
);
|
|
18353
18401
|
}
|
|
@@ -18470,7 +18518,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
18470
18518
|
|
|
18471
18519
|
// src/commands/share.ts
|
|
18472
18520
|
import { Option } from "commander";
|
|
18473
|
-
import { CLI_NAME as
|
|
18521
|
+
import { CLI_NAME as CLI_NAME26 } from "@tinycloud/operations/cli-name";
|
|
18474
18522
|
|
|
18475
18523
|
// ../share-sdk/dist/artifact.js
|
|
18476
18524
|
init_sha256();
|
|
@@ -36601,12 +36649,12 @@ function shareCliError(error, operation = "publish") {
|
|
|
36601
36649
|
const profileName2 = "profileName" in failure ? failure.profileName : void 0;
|
|
36602
36650
|
const localKey = "localKey" in failure && failure.localKey === true;
|
|
36603
36651
|
const profileHint = profileName2 === void 0 ? "" : `--profile ${profileName2} `;
|
|
36604
|
-
const loginHint = localKey ? `\`${
|
|
36652
|
+
const loginHint = localKey ? `\`${CLI_NAME26} ${profileHint}auth login --method local\`` : `\`${CLI_NAME26} ${profileHint}auth login --device --manifest builtin:share-publishing\` (or \`${CLI_NAME26} ${profileHint}enable share\`)`;
|
|
36605
36653
|
if (failure.kind === "caveated-session") {
|
|
36606
36654
|
const holder = profileName2 === void 0 ? "this session" : `profile ${profileName2}'s session`;
|
|
36607
36655
|
return new CLIError(
|
|
36608
36656
|
"PERMISSION_DENIED",
|
|
36609
|
-
`${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): \`${
|
|
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): \`${CLI_NAME26} init --name publisher --key-only && ${CLI_NAME26} --profile publisher enable share\``,
|
|
36610
36658
|
5
|
|
36611
36659
|
);
|
|
36612
36660
|
}
|
|
@@ -37011,7 +37059,7 @@ init_constants();
|
|
|
37011
37059
|
init_sdk();
|
|
37012
37060
|
init_host();
|
|
37013
37061
|
init_theme();
|
|
37014
|
-
import { CLI_NAME as
|
|
37062
|
+
import { CLI_NAME as CLI_NAME27 } from "@tinycloud/operations/cli-name";
|
|
37015
37063
|
import { ACCOUNT_REGISTRY_SPACE } from "@tinycloud/node-sdk";
|
|
37016
37064
|
import { randomBytes as randomBytes9 } from "crypto";
|
|
37017
37065
|
import { mkdir as mkdir7, writeFile as writeFile4 } from "fs/promises";
|
|
@@ -37058,7 +37106,7 @@ function registerSpaceCommand(program) {
|
|
|
37058
37106
|
} else {
|
|
37059
37107
|
const profile = await ProfileManager.getProfile(ctx.profile);
|
|
37060
37108
|
const cap = "tinycloud.kv:account:spaces/:get,list";
|
|
37061
|
-
const hint = resolveProfilePosture(profile) === "delegate-session" ? `Ask the space owner to grant it. Create the request with: ${
|
|
37109
|
+
const hint = resolveProfilePosture(profile) === "delegate-session" ? `Ask the space owner to grant it. Create the request with: ${CLI_NAME27} --profile ${ctx.profile} auth request --cap ${cap} --emit` : `Grant it to this session with: ${CLI_NAME27} --profile ${ctx.profile} auth request --grant --cap ${cap}. The default OpenKey owner sign-in covers only the default space, so it includes neither.`;
|
|
37062
37110
|
throw new CLIError(
|
|
37063
37111
|
"PERMISSION_DENIED",
|
|
37064
37112
|
`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.`,
|
|
@@ -37095,10 +37143,10 @@ function registerSpaceCommand(program) {
|
|
|
37095
37143
|
|
|
37096
37144
|
A delegate cannot host a space \u2014 only its owner (root authority) can. This
|
|
37097
37145
|
emits a tinycloud.host.request artifact naming the space and its owner so you
|
|
37098
|
-
can hand it to the owner; they run \`${
|
|
37146
|
+
can hand it to the owner; they run \`${CLI_NAME27} space host <name>\` and confirm.
|
|
37099
37147
|
|
|
37100
37148
|
If you ARE the owner of the resolved space, this refuses and tells you to host
|
|
37101
|
-
it directly with \`${
|
|
37149
|
+
it directly with \`${CLI_NAME27} space host <name>\` (no request needed).
|
|
37102
37150
|
`).action(async (name, options, cmd) => {
|
|
37103
37151
|
try {
|
|
37104
37152
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -37109,7 +37157,7 @@ it directly with \`${CLI_NAME26} space host <name>\` (no request needed).
|
|
|
37109
37157
|
if (await isRootAuthority(spaceId, ctx.profile)) {
|
|
37110
37158
|
throw new CLIError(
|
|
37111
37159
|
"ALREADY_ROOT_AUTHORITY",
|
|
37112
|
-
`You are the owner of ${spaceId}. Host it directly: ${
|
|
37160
|
+
`You are the owner of ${spaceId}. Host it directly: ${CLI_NAME27} space host ${spaceName}`,
|
|
37113
37161
|
ExitCode.USAGE_ERROR
|
|
37114
37162
|
);
|
|
37115
37163
|
}
|
|
@@ -37195,7 +37243,7 @@ init_sdk();
|
|
|
37195
37243
|
init_space();
|
|
37196
37244
|
init_host();
|
|
37197
37245
|
init_theme();
|
|
37198
|
-
import { CLI_NAME as
|
|
37246
|
+
import { CLI_NAME as CLI_NAME28 } from "@tinycloud/operations/cli-name";
|
|
37199
37247
|
import { writeFile as writeFile5 } from "fs/promises";
|
|
37200
37248
|
import { resolve as resolve7 } from "path";
|
|
37201
37249
|
async function dbHandle(node, dbName, spaceInput, profileName2) {
|
|
@@ -37219,12 +37267,12 @@ database for simple apps, or pass --db to target a named database. Pass
|
|
|
37219
37267
|
--space to target a non-primary space (e.g. the manifest "applications" space).
|
|
37220
37268
|
|
|
37221
37269
|
Common workflows:
|
|
37222
|
-
$ ${
|
|
37223
|
-
$ ${
|
|
37224
|
-
$ ${
|
|
37225
|
-
$ ${
|
|
37226
|
-
$ ${
|
|
37227
|
-
$ ${
|
|
37270
|
+
$ ${CLI_NAME28} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
|
|
37271
|
+
$ ${CLI_NAME28} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["ship docs"]'
|
|
37272
|
+
$ ${CLI_NAME28} sql query "SELECT id, body FROM notes ORDER BY id"
|
|
37273
|
+
$ ${CLI_NAME28} sql query "SELECT * FROM events WHERE type = ?" --db analytics --params '["signup"]'
|
|
37274
|
+
$ ${CLI_NAME28} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
|
|
37275
|
+
$ ${CLI_NAME28} sql export --db analytics --output analytics.db
|
|
37228
37276
|
|
|
37229
37277
|
Commands:
|
|
37230
37278
|
query Read rows with SELECT statements
|
|
@@ -37241,10 +37289,10 @@ Tips:
|
|
|
37241
37289
|
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", `
|
|
37242
37290
|
|
|
37243
37291
|
Examples:
|
|
37244
|
-
$ ${
|
|
37245
|
-
$ ${
|
|
37246
|
-
$ ${
|
|
37247
|
-
$ ${
|
|
37292
|
+
$ ${CLI_NAME28} sql query "SELECT * FROM notes ORDER BY id"
|
|
37293
|
+
$ ${CLI_NAME28} sql query "SELECT * FROM notes WHERE id = ?" --params '[42]'
|
|
37294
|
+
$ ${CLI_NAME28} sql query "SELECT count(*) AS total FROM events" --db analytics --json
|
|
37295
|
+
$ ${CLI_NAME28} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
|
|
37248
37296
|
|
|
37249
37297
|
Output:
|
|
37250
37298
|
Human output is formatted as a table. Piped output or --json returns
|
|
@@ -37285,11 +37333,11 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
|
|
|
37285
37333
|
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", `
|
|
37286
37334
|
|
|
37287
37335
|
Examples:
|
|
37288
|
-
$ ${
|
|
37289
|
-
$ ${
|
|
37290
|
-
$ ${
|
|
37291
|
-
$ ${
|
|
37292
|
-
$ ${
|
|
37336
|
+
$ ${CLI_NAME28} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
|
|
37337
|
+
$ ${CLI_NAME28} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["first note"]'
|
|
37338
|
+
$ ${CLI_NAME28} sql execute "UPDATE notes SET body = ? WHERE id = ?" --params '["edited", 1]'
|
|
37339
|
+
$ ${CLI_NAME28} sql execute "DROP TABLE old_notes" --db archive
|
|
37340
|
+
$ ${CLI_NAME28} sql execute "DELETE FROM conversation WHERE id = ?" --space applications --db xyz.tinycloud.listen/conversations --params '["abc"]'
|
|
37293
37341
|
|
|
37294
37342
|
Output:
|
|
37295
37343
|
Returns JSON with the changed row count and last inserted row id when available.
|
|
@@ -37318,9 +37366,9 @@ Output:
|
|
|
37318
37366
|
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", `
|
|
37319
37367
|
|
|
37320
37368
|
Examples:
|
|
37321
|
-
$ ${
|
|
37322
|
-
$ ${
|
|
37323
|
-
$ ${
|
|
37369
|
+
$ ${CLI_NAME28} sql export
|
|
37370
|
+
$ ${CLI_NAME28} sql export --db analytics --output analytics.db
|
|
37371
|
+
$ ${CLI_NAME28} sql export --space applications --db xyz.tinycloud.listen/conversations --output listen.db
|
|
37324
37372
|
|
|
37325
37373
|
Output:
|
|
37326
37374
|
Writes the database file locally and returns JSON with the path and size.
|
|
@@ -37353,19 +37401,19 @@ Output:
|
|
|
37353
37401
|
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", `
|
|
37354
37402
|
|
|
37355
37403
|
Examples:
|
|
37356
|
-
$ ${
|
|
37404
|
+
$ ${CLI_NAME28} sql copy --from-db com.tinycloud.conversation-sync/conversations \\
|
|
37357
37405
|
--to-db xyz.tinycloud.listen/conversations \\
|
|
37358
37406
|
--space applications --dry-run
|
|
37359
|
-
$ ${
|
|
37407
|
+
$ ${CLI_NAME28} sql copy --from-space applications --from-db com.foo/data \\
|
|
37360
37408
|
--to-space applications --to-db com.bar/data \\
|
|
37361
37409
|
--table conversation --table participant
|
|
37362
37410
|
|
|
37363
37411
|
Notes:
|
|
37364
37412
|
- Refuses to run when (resolved space, db) is identical for source and destination.
|
|
37365
|
-
- Does NOT create destination tables. Run the target app once (or use \`${
|
|
37413
|
+
- Does NOT create destination tables. Run the target app once (or use \`${CLI_NAME28} sql execute\`)
|
|
37366
37414
|
to materialize the schema before copying.
|
|
37367
37415
|
- One row at a time; suitable for small/medium datasets. Large copies should
|
|
37368
|
-
use \`${
|
|
37416
|
+
use \`${CLI_NAME28} sql export\` + bulk import.
|
|
37369
37417
|
- Authorization: the active session/delegation must cover sql/read on source
|
|
37370
37418
|
AND sql/write on destination. Otherwise the relevant operation will fail.
|
|
37371
37419
|
`).action(async (options, cmd) => {
|
|
@@ -37465,7 +37513,7 @@ init_errors();
|
|
|
37465
37513
|
init_formatter();
|
|
37466
37514
|
init_theme();
|
|
37467
37515
|
init_permissions();
|
|
37468
|
-
import { CLI_NAME as
|
|
37516
|
+
import { CLI_NAME as CLI_NAME29 } from "@tinycloud/operations/cli-name";
|
|
37469
37517
|
import {
|
|
37470
37518
|
NodeWasmBindings as NodeWasmBindings2
|
|
37471
37519
|
} from "@tinycloud/node-sdk";
|
|
@@ -37739,7 +37787,7 @@ function formatStatus(summary) {
|
|
|
37739
37787
|
lines.push(`Default profile: ${theme.value(summary.defaultProfile)}`);
|
|
37740
37788
|
lines.push("");
|
|
37741
37789
|
if (summary.profiles.length === 0) {
|
|
37742
|
-
lines.push(theme.muted(`No profiles configured. Run: ${
|
|
37790
|
+
lines.push(theme.muted(`No profiles configured. Run: ${CLI_NAME29} init`));
|
|
37743
37791
|
return `${lines.join("\n")}
|
|
37744
37792
|
`;
|
|
37745
37793
|
}
|