@tinycloud/cli 1.5.0-beta.0 → 1.5.0-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/dist/legacy-entry.js +302 -252
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +1 -1
package/dist/legacy-entry.js
CHANGED
|
@@ -16456,7 +16456,7 @@ function formatSpace(space) {
|
|
|
16456
16456
|
init_profile_gate();
|
|
16457
16457
|
init_dist();
|
|
16458
16458
|
init_profiles();
|
|
16459
|
-
import { CLI_NAME as
|
|
16459
|
+
import { CLI_NAME as CLI_NAME18 } from "@tinycloud/operations/cli-name";
|
|
16460
16460
|
import { get as httpGet } from "http";
|
|
16461
16461
|
import { get as httpsGet } from "https";
|
|
16462
16462
|
import { spawn } from "child_process";
|
|
@@ -31318,6 +31318,7 @@ init_errors();
|
|
|
31318
31318
|
init_constants();
|
|
31319
31319
|
import { NodeWasmBindings, signedCompactUcanExpiry as signedCompactUcanExpiry2 } from "@tinycloud/node-sdk";
|
|
31320
31320
|
import { isLocalOwnerProfile as isLocalOwnerProfile2, ownerSessionLacksProof as ownerSessionLacksProof2 } from "@tinycloud/operations/profile";
|
|
31321
|
+
import { CLI_NAME as CLI_NAME12 } from "@tinycloud/operations/cli-name";
|
|
31321
31322
|
|
|
31322
31323
|
// src/lib/duration.ts
|
|
31323
31324
|
function parseDuration(input) {
|
|
@@ -31353,6 +31354,87 @@ init_manifest();
|
|
|
31353
31354
|
init_raw_encryption();
|
|
31354
31355
|
init_owner_did();
|
|
31355
31356
|
init_caveat_refusal();
|
|
31357
|
+
|
|
31358
|
+
// src/lib/named-keys.ts
|
|
31359
|
+
init_constants();
|
|
31360
|
+
init_errors();
|
|
31361
|
+
import { extractRecapAttenuations } from "@tinycloud/sdk-core";
|
|
31362
|
+
import {
|
|
31363
|
+
EncryptionResourceError,
|
|
31364
|
+
KEY_CREATE_ABILITY,
|
|
31365
|
+
formatPrefixResource,
|
|
31366
|
+
nodeSupportsNamedKeys
|
|
31367
|
+
} from "@tinycloud/sdk-services/encryption/resource";
|
|
31368
|
+
import { DECRYPT_ACTION } from "@tinycloud/sdk-services";
|
|
31369
|
+
var NAMED_KEYS_NETWORK = "default";
|
|
31370
|
+
function namedKeysNetworkId(ownerDid) {
|
|
31371
|
+
return `urn:tinycloud:encryption:${ownerDid}:${NAMED_KEYS_NETWORK}`;
|
|
31372
|
+
}
|
|
31373
|
+
function parseKeysPrefix(input, ownerDid) {
|
|
31374
|
+
const prefix = input.endsWith("/") ? input.slice(0, -1) : input;
|
|
31375
|
+
const invalid3 = (reason) => new CLIError("INVALID_KEY_PREFIX", `Invalid \`--keys\` folder "${input}": ${reason}`, ExitCode.USAGE_ERROR);
|
|
31376
|
+
if (prefix.length === 0) throw invalid3("the folder can't be empty.");
|
|
31377
|
+
if (prefix === "apps" || prefix.startsWith("apps/")) {
|
|
31378
|
+
throw invalid3("folders under `apps/` belong to apps and can't be used from the CLI.");
|
|
31379
|
+
}
|
|
31380
|
+
try {
|
|
31381
|
+
return { prefix, resource: formatPrefixResource(namedKeysNetworkId(ownerDid), prefix) };
|
|
31382
|
+
} catch (error) {
|
|
31383
|
+
if (error instanceof EncryptionResourceError) throw invalid3(error.message);
|
|
31384
|
+
throw error;
|
|
31385
|
+
}
|
|
31386
|
+
}
|
|
31387
|
+
function keyAbilitiesFor(keys) {
|
|
31388
|
+
return { [keys.resource]: [KEY_CREATE_ABILITY, DECRYPT_ACTION] };
|
|
31389
|
+
}
|
|
31390
|
+
function keyPathInPrefix(prefix, path) {
|
|
31391
|
+
return path.startsWith(`${prefix}/`) && path.length > prefix.length + 1;
|
|
31392
|
+
}
|
|
31393
|
+
async function assertNodeSupportsNamedKeys(host, fetchImpl = fetch) {
|
|
31394
|
+
let info = null;
|
|
31395
|
+
try {
|
|
31396
|
+
const response = await fetchImpl(`${host}/info`);
|
|
31397
|
+
if (response.ok) info = await response.json();
|
|
31398
|
+
} catch {
|
|
31399
|
+
info = null;
|
|
31400
|
+
}
|
|
31401
|
+
if (!nodeSupportsNamedKeys(info)) {
|
|
31402
|
+
throw new CLIError(
|
|
31403
|
+
"NAMED_KEYS_UNAVAILABLE",
|
|
31404
|
+
`${host} doesn't support named keys, so \`--keys\` can't be used there. Nothing was signed.`,
|
|
31405
|
+
ExitCode.NODE_ERROR
|
|
31406
|
+
);
|
|
31407
|
+
}
|
|
31408
|
+
}
|
|
31409
|
+
function partialKeysGrantError() {
|
|
31410
|
+
return new CLIError(
|
|
31411
|
+
"KEYS_PARTIAL_GRANT",
|
|
31412
|
+
"Named keys need both permissions (create and use), but only one was approved, so this sign-in wasn't saved. Sign in again and approve both.",
|
|
31413
|
+
ExitCode.PERMISSION_DENIED
|
|
31414
|
+
);
|
|
31415
|
+
}
|
|
31416
|
+
function namedKeysGrantIn(siwe, keys) {
|
|
31417
|
+
let recap = {};
|
|
31418
|
+
try {
|
|
31419
|
+
recap = siwe === void 0 ? {} : extractRecapAttenuations(siwe);
|
|
31420
|
+
} catch {
|
|
31421
|
+
return "none";
|
|
31422
|
+
}
|
|
31423
|
+
const row = recap[keys.resource] ?? {};
|
|
31424
|
+
const wanted = keyAbilitiesFor(keys)[keys.resource];
|
|
31425
|
+
const granted = wanted.filter((ability) => ability in row);
|
|
31426
|
+
if (granted.length === 0) return "none";
|
|
31427
|
+
return granted.length === wanted.length && Object.keys(row).length === wanted.length ? "both" : "partial";
|
|
31428
|
+
}
|
|
31429
|
+
function withVerifiedNamedKeys(profile, session) {
|
|
31430
|
+
if (profile.namedKeys === void 0) return profile;
|
|
31431
|
+
const siwe = typeof session === "object" && session !== null ? session.siwe : void 0;
|
|
31432
|
+
if (typeof siwe === "string" && namedKeysGrantIn(siwe, profile.namedKeys) === "both") return profile;
|
|
31433
|
+
const { namedKeys: _dropped, ...rest } = profile;
|
|
31434
|
+
return rest;
|
|
31435
|
+
}
|
|
31436
|
+
|
|
31437
|
+
// src/auth/scoped-login.ts
|
|
31356
31438
|
var CAPABILITIES_READ = "tinycloud.capabilities/read";
|
|
31357
31439
|
var CLOCK_SKEW_MS = 3e4;
|
|
31358
31440
|
var SIGNED_RECAP = "signed-recap";
|
|
@@ -31605,6 +31687,35 @@ function verifySignedSession(data, key, sessionDid, expected = {}) {
|
|
|
31605
31687
|
}
|
|
31606
31688
|
return { ownerDid, permissions, expiresAt };
|
|
31607
31689
|
}
|
|
31690
|
+
function withoutRequestedKeyRow(signed, keys, signerDid) {
|
|
31691
|
+
const mismatch = () => new CLIError(
|
|
31692
|
+
"OPENKEY_SCOPE_MISMATCH",
|
|
31693
|
+
`The key permission in the approval didn't match what was asked for "<folder>/", so this sign-in wasn't saved. Sign in again with \`${CLI_NAME12} auth login --keys <folder>\`.`,
|
|
31694
|
+
ExitCode.PERMISSION_DENIED
|
|
31695
|
+
);
|
|
31696
|
+
let expectedResource;
|
|
31697
|
+
try {
|
|
31698
|
+
expectedResource = parseKeysPrefix(keys.prefix, canonicalOwnerDid(signerDid)).resource;
|
|
31699
|
+
} catch {
|
|
31700
|
+
throw mismatch();
|
|
31701
|
+
}
|
|
31702
|
+
if (keys.resource !== expectedResource) throw mismatch();
|
|
31703
|
+
const rest = [];
|
|
31704
|
+
const actions = /* @__PURE__ */ new Set();
|
|
31705
|
+
for (const permission of signed) {
|
|
31706
|
+
if (!isVerifiedRawEncryptionPermission(permission) || permission.path !== expectedResource) {
|
|
31707
|
+
rest.push(permission);
|
|
31708
|
+
continue;
|
|
31709
|
+
}
|
|
31710
|
+
if (permission.caveats?.some((caveat) => Object.keys(caveat).length > 0)) throw mismatch();
|
|
31711
|
+
for (const action of permission.actions) actions.add(action);
|
|
31712
|
+
}
|
|
31713
|
+
if (actions.size === 0) return rest;
|
|
31714
|
+
const wanted = keyAbilitiesFor(keys)[expectedResource];
|
|
31715
|
+
if ([...actions].some((action) => !wanted.includes(action))) throw mismatch();
|
|
31716
|
+
if (actions.size !== wanted.length) throw partialKeysGrantError();
|
|
31717
|
+
return rest;
|
|
31718
|
+
}
|
|
31608
31719
|
function verifyScopedLogin(data, key, sessionDid, requested, expected = {}) {
|
|
31609
31720
|
const notStored = expected.purpose === "grant" ? "No grant was stored." : "No scoped session was saved.";
|
|
31610
31721
|
let signed;
|
|
@@ -31630,10 +31741,11 @@ function verifyScopedLogin(data, key, sessionDid, requested, expected = {}) {
|
|
|
31630
31741
|
}
|
|
31631
31742
|
const legacyNested = [];
|
|
31632
31743
|
const approved = [];
|
|
31633
|
-
|
|
31744
|
+
const keyRowRest = expected.namedKeyRow === void 0 ? signed.permissions : withoutRequestedKeyRow(signed.permissions, expected.namedKeyRow, signed.ownerDid);
|
|
31745
|
+
for (const permission of keyRowRest) {
|
|
31634
31746
|
(isLegacyNestedDecrypt(permission, requested, signed.ownerDid, spaceId) ? legacyNested : approved).push(permission);
|
|
31635
31747
|
}
|
|
31636
|
-
for (const permission of
|
|
31748
|
+
for (const permission of keyRowRest) {
|
|
31637
31749
|
if (isVerifiedRawEncryptionPermission(permission) && !rawEncryptionOwnerMatches(permission.path, signed.ownerDid)) {
|
|
31638
31750
|
throw new CLIError("OPENKEY_SCOPE_MISMATCH", `OpenKey signed decrypt for a network not owned by the approving identity. ${notStored}`, ExitCode.PERMISSION_DENIED);
|
|
31639
31751
|
}
|
|
@@ -31650,80 +31762,6 @@ function verifyScopedLogin(data, key, sessionDid, requested, expected = {}) {
|
|
|
31650
31762
|
|
|
31651
31763
|
// src/auth/login-commit.ts
|
|
31652
31764
|
import { lstat as lstat6 } from "fs/promises";
|
|
31653
|
-
|
|
31654
|
-
// src/lib/named-keys.ts
|
|
31655
|
-
init_constants();
|
|
31656
|
-
init_errors();
|
|
31657
|
-
import { extractRecapAttenuations } from "@tinycloud/sdk-core";
|
|
31658
|
-
import {
|
|
31659
|
-
EncryptionResourceError,
|
|
31660
|
-
KEY_CREATE_ABILITY,
|
|
31661
|
-
formatPrefixResource,
|
|
31662
|
-
nodeSupportsNamedKeys
|
|
31663
|
-
} from "@tinycloud/sdk-services/encryption/resource";
|
|
31664
|
-
import { DECRYPT_ACTION } from "@tinycloud/sdk-services";
|
|
31665
|
-
var NAMED_KEYS_NETWORK = "default";
|
|
31666
|
-
function namedKeysNetworkId(ownerDid) {
|
|
31667
|
-
return `urn:tinycloud:encryption:${ownerDid}:${NAMED_KEYS_NETWORK}`;
|
|
31668
|
-
}
|
|
31669
|
-
function parseKeysPrefix(input, ownerDid) {
|
|
31670
|
-
const prefix = input.endsWith("/") ? input.slice(0, -1) : input;
|
|
31671
|
-
const invalid3 = (reason) => new CLIError("INVALID_KEY_PREFIX", `Invalid \`--keys\` folder "${input}": ${reason}`, ExitCode.USAGE_ERROR);
|
|
31672
|
-
if (prefix.length === 0) throw invalid3("the folder can't be empty.");
|
|
31673
|
-
if (prefix === "apps" || prefix.startsWith("apps/")) {
|
|
31674
|
-
throw invalid3("folders under `apps/` belong to apps and can't be used from the CLI.");
|
|
31675
|
-
}
|
|
31676
|
-
try {
|
|
31677
|
-
return { prefix, resource: formatPrefixResource(namedKeysNetworkId(ownerDid), prefix) };
|
|
31678
|
-
} catch (error) {
|
|
31679
|
-
if (error instanceof EncryptionResourceError) throw invalid3(error.message);
|
|
31680
|
-
throw error;
|
|
31681
|
-
}
|
|
31682
|
-
}
|
|
31683
|
-
function keyAbilitiesFor(keys) {
|
|
31684
|
-
return { [keys.resource]: [KEY_CREATE_ABILITY, DECRYPT_ACTION] };
|
|
31685
|
-
}
|
|
31686
|
-
function keyPathInPrefix(prefix, path) {
|
|
31687
|
-
return path.startsWith(`${prefix}/`) && path.length > prefix.length + 1;
|
|
31688
|
-
}
|
|
31689
|
-
async function assertNodeSupportsNamedKeys(host, fetchImpl = fetch) {
|
|
31690
|
-
let info = null;
|
|
31691
|
-
try {
|
|
31692
|
-
const response = await fetchImpl(`${host}/info`);
|
|
31693
|
-
if (response.ok) info = await response.json();
|
|
31694
|
-
} catch {
|
|
31695
|
-
info = null;
|
|
31696
|
-
}
|
|
31697
|
-
if (!nodeSupportsNamedKeys(info)) {
|
|
31698
|
-
throw new CLIError(
|
|
31699
|
-
"NAMED_KEYS_UNAVAILABLE",
|
|
31700
|
-
`${host} doesn't support named keys, so \`--keys\` can't be used there. Nothing was signed.`,
|
|
31701
|
-
ExitCode.NODE_ERROR
|
|
31702
|
-
);
|
|
31703
|
-
}
|
|
31704
|
-
}
|
|
31705
|
-
function namedKeysGrantIn(siwe, keys) {
|
|
31706
|
-
let recap = {};
|
|
31707
|
-
try {
|
|
31708
|
-
recap = siwe === void 0 ? {} : extractRecapAttenuations(siwe);
|
|
31709
|
-
} catch {
|
|
31710
|
-
return "none";
|
|
31711
|
-
}
|
|
31712
|
-
const row = recap[keys.resource] ?? {};
|
|
31713
|
-
const wanted = keyAbilitiesFor(keys)[keys.resource];
|
|
31714
|
-
const granted = wanted.filter((ability) => ability in row);
|
|
31715
|
-
if (granted.length === 0) return "none";
|
|
31716
|
-
return granted.length === wanted.length && Object.keys(row).length === wanted.length ? "both" : "partial";
|
|
31717
|
-
}
|
|
31718
|
-
function withVerifiedNamedKeys(profile, session) {
|
|
31719
|
-
if (profile.namedKeys === void 0) return profile;
|
|
31720
|
-
const siwe = typeof session === "object" && session !== null ? session.siwe : void 0;
|
|
31721
|
-
if (typeof siwe === "string" && namedKeysGrantIn(siwe, profile.namedKeys) === "both") return profile;
|
|
31722
|
-
const { namedKeys: _dropped, ...rest } = profile;
|
|
31723
|
-
return rest;
|
|
31724
|
-
}
|
|
31725
|
-
|
|
31726
|
-
// src/auth/login-commit.ts
|
|
31727
31765
|
init_constants();
|
|
31728
31766
|
init_profiles();
|
|
31729
31767
|
init_storage();
|
|
@@ -31731,14 +31769,14 @@ init_errors();
|
|
|
31731
31769
|
init_space();
|
|
31732
31770
|
import { join as join13 } from "path";
|
|
31733
31771
|
import { REPLICA_LOGOUT_PENDING_FILE as REPLICA_LOGOUT_PENDING_FILE3 } from "@tinycloud/node-sdk";
|
|
31734
|
-
import { CLI_NAME as
|
|
31772
|
+
import { CLI_NAME as CLI_NAME13 } from "@tinycloud/operations/cli-name";
|
|
31735
31773
|
import { isLocalOwnerProfile as isLocalOwnerProfile3, ownerSessionLacksProof as ownerSessionLacksProof3 } from "@tinycloud/operations/profile";
|
|
31736
31774
|
import { ProfileLockTimeoutError as ProfileLockTimeoutError2, profilePath as profilePath7 } from "@tinycloud/operations/state";
|
|
31737
31775
|
function assertNotLocalOwner(profileName2, profile, flow) {
|
|
31738
31776
|
if (profile === null || !isLocalOwnerProfile3(profile)) return;
|
|
31739
31777
|
throw new CLIError(
|
|
31740
31778
|
"LOCAL_OWNER_PROFILE",
|
|
31741
|
-
`Profile "${profileName2}" holds a local owner key. ${flow} would turn it into an OpenKey profile while keeping that key. Use a separate profile: \`${
|
|
31779
|
+
`Profile "${profileName2}" holds a local owner key. ${flow} would turn it into an OpenKey profile while keeping that key. Use a separate profile: \`${CLI_NAME13} init --name publisher --key-only\`, then \`${CLI_NAME13} --profile publisher auth login --device --manifest ...\`.`,
|
|
31742
31780
|
ExitCode.USAGE_ERROR
|
|
31743
31781
|
);
|
|
31744
31782
|
}
|
|
@@ -31773,7 +31811,7 @@ function assertSessionReplaceable(profileName2, snapshot, ownerDid, scope, newEx
|
|
|
31773
31811
|
if (problem !== void 0) {
|
|
31774
31812
|
throw new CLIError(
|
|
31775
31813
|
"PROFILE_STATE_INCONSISTENT",
|
|
31776
|
-
`Profile "${profileName2}" is inconsistent (${problem}), possibly from an interrupted write. Nothing was saved. Check \`${
|
|
31814
|
+
`Profile "${profileName2}" is inconsistent (${problem}), possibly from an interrupted write. Nothing was saved. Check \`${CLI_NAME13} --profile ${profileName2} context\`, then pass --replace-session to replace this state, or use a new profile.`,
|
|
31777
31815
|
ExitCode.ERROR
|
|
31778
31816
|
);
|
|
31779
31817
|
}
|
|
@@ -31789,7 +31827,7 @@ function assertSessionReplaceable(profileName2, snapshot, ownerDid, scope, newEx
|
|
|
31789
31827
|
const space = typeof session.spaceId === "string" ? session.spaceId : "an unknown space";
|
|
31790
31828
|
throw new CLIError(
|
|
31791
31829
|
"SESSION_IN_USE",
|
|
31792
|
-
`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 (\`${
|
|
31830
|
+
`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_NAME13} init --name publisher --key-only\`, then \`${CLI_NAME13} --profile publisher auth login --device --manifest ...\`), or pass --replace-session to replace this session.`,
|
|
31793
31831
|
ExitCode.USAGE_ERROR
|
|
31794
31832
|
);
|
|
31795
31833
|
}
|
|
@@ -31823,7 +31861,7 @@ async function commitLogin(profileName2, snapshot, commit) {
|
|
|
31823
31861
|
if (canonicalJson(current.profile) !== canonicalJson(snapshot.profile) || canonicalJson(current.key) !== canonicalJson(snapshot.key) || canonicalJson(current.session) !== canonicalJson(snapshot.session)) {
|
|
31824
31862
|
throw new CLIError(
|
|
31825
31863
|
"PROFILE_CHANGED_DURING_LOGIN",
|
|
31826
|
-
`Profile "${profileName2}" changed while this login was in progress (another login, key rotation or logout). Nothing was saved; check \`${
|
|
31864
|
+
`Profile "${profileName2}" changed while this login was in progress (another login, key rotation or logout). Nothing was saved; check \`${CLI_NAME13} --profile ${profileName2} context\` and run the login again if it is still needed.`,
|
|
31827
31865
|
ExitCode.ERROR
|
|
31828
31866
|
);
|
|
31829
31867
|
}
|
|
@@ -31856,7 +31894,7 @@ async function commitLogin(profileName2, snapshot, commit) {
|
|
|
31856
31894
|
if (failures.length === 0) throw error;
|
|
31857
31895
|
throw new CLIError(
|
|
31858
31896
|
"PROFILE_STATE_INCONSISTENT",
|
|
31859
|
-
`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 \`${
|
|
31897
|
+
`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_NAME13} --profile ${profileName2} context\`, then run the login again with --replace-session, or use a new profile.`,
|
|
31860
31898
|
ExitCode.ERROR
|
|
31861
31899
|
);
|
|
31862
31900
|
}
|
|
@@ -31872,7 +31910,7 @@ async function commitLogin(profileName2, snapshot, commit) {
|
|
|
31872
31910
|
|
|
31873
31911
|
// src/auth/owner-key.ts
|
|
31874
31912
|
init_theme();
|
|
31875
|
-
import { CLI_NAME as
|
|
31913
|
+
import { CLI_NAME as CLI_NAME14 } from "@tinycloud/operations/cli-name";
|
|
31876
31914
|
function openKeyPrimaryFlag(delegation) {
|
|
31877
31915
|
return typeof delegation.primary === "boolean" ? delegation.primary : void 0;
|
|
31878
31916
|
}
|
|
@@ -31883,7 +31921,7 @@ function withOwnerKeyPrimary(profile, primary) {
|
|
|
31883
31921
|
function nonPrimaryKeyWarning(ownerDid) {
|
|
31884
31922
|
const address = ownerDid.split(":")[4] ?? ownerDid;
|
|
31885
31923
|
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.
|
|
31886
|
-
To use your primary key, log in again and choose the primary key in OpenKey, for example into a new profile: ${
|
|
31924
|
+
To use your primary key, log in again and choose the primary key in OpenKey, for example into a new profile: ${CLI_NAME14} init --name <profile>
|
|
31887
31925
|
`;
|
|
31888
31926
|
}
|
|
31889
31927
|
function warnIfNotPrimaryKey(ownerDid, primary) {
|
|
@@ -31892,7 +31930,7 @@ function warnIfNotPrimaryKey(ownerDid, primary) {
|
|
|
31892
31930
|
}
|
|
31893
31931
|
function announceFirstOwner(profileName2, expectedOwner, ownerDid) {
|
|
31894
31932
|
if (expectedOwner !== void 0 || ownerDid === void 0) return;
|
|
31895
|
-
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 ${
|
|
31933
|
+
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_NAME14} init.
|
|
31896
31934
|
`);
|
|
31897
31935
|
}
|
|
31898
31936
|
function ownerLoginPermissions(owner) {
|
|
@@ -32723,7 +32761,7 @@ init_types();
|
|
|
32723
32761
|
init_constants();
|
|
32724
32762
|
init_permissions();
|
|
32725
32763
|
init_sdk();
|
|
32726
|
-
import { CLI_NAME as
|
|
32764
|
+
import { CLI_NAME as CLI_NAME16 } from "@tinycloud/operations/cli-name";
|
|
32727
32765
|
import { TinyCloudNode as TinyCloudNode2 } from "@tinycloud/node-sdk";
|
|
32728
32766
|
import { DelegationRequestBindingSchema, PermissionEntrySchema } from "@tinycloud/operations/artifacts";
|
|
32729
32767
|
import {
|
|
@@ -32738,7 +32776,7 @@ import {
|
|
|
32738
32776
|
// src/lib/revoke-delegation.ts
|
|
32739
32777
|
init_constants();
|
|
32740
32778
|
init_errors();
|
|
32741
|
-
import { CLI_NAME as
|
|
32779
|
+
import { CLI_NAME as CLI_NAME15 } from "@tinycloud/operations/cli-name";
|
|
32742
32780
|
import { parseSignedCompactUcanAttenuation as parseSignedCompactUcanAttenuation3 } from "@tinycloud/sdk-core";
|
|
32743
32781
|
import { inspectStoredDelegation, withoutBearer as withoutBearer3 } from "@tinycloud/operations/delegation-binding";
|
|
32744
32782
|
init_permissions();
|
|
@@ -33002,7 +33040,7 @@ async function revokeDelegationOnNode(params) {
|
|
|
33002
33040
|
"REVOKE_UNAUTHORIZED",
|
|
33003
33041
|
"The node rejected this revocation: Unauthorized Revoker.",
|
|
33004
33042
|
ExitCode.PERMISSION_DENIED,
|
|
33005
|
-
{ 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 \`${
|
|
33043
|
+
{ 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_NAME15} --profile <profile-name> delegation revoke ${cid}\`.` }
|
|
33006
33044
|
);
|
|
33007
33045
|
}
|
|
33008
33046
|
throw cliErrorFromService(result.error);
|
|
@@ -33085,7 +33123,7 @@ function renderList(result) {
|
|
|
33085
33123
|
}
|
|
33086
33124
|
process.stdout.write(`Profile: ${result.profile}
|
|
33087
33125
|
`);
|
|
33088
|
-
process.stdout.write(`Session CID: ${result.session?.cid ?? "unavailable"}` + (result.session?.cid ? ` (current session; revoke with ${
|
|
33126
|
+
process.stdout.write(`Session CID: ${result.session?.cid ?? "unavailable"}` + (result.session?.cid ? ` (current session; revoke with ${CLI_NAME16} delegation revoke ${result.session.cid})
|
|
33089
33127
|
` : "\n"));
|
|
33090
33128
|
process.stdout.write(`Local verification: ${result.localVerification}; node validation and revocation are not checked.
|
|
33091
33129
|
`);
|
|
@@ -33183,7 +33221,7 @@ function registerAuthGrantsCommand(auth, options = {}) {
|
|
|
33183
33221
|
ExitCode.ERROR,
|
|
33184
33222
|
{
|
|
33185
33223
|
causeCode: cause.code,
|
|
33186
|
-
hint: `Do not use --revoke again. After fixing local profile storage, run: ${
|
|
33224
|
+
hint: `Do not use --revoke again. After fixing local profile storage, run: ${CLI_NAME16} --profile ${ctx.profile} auth grants remove ${cid}`
|
|
33187
33225
|
}
|
|
33188
33226
|
);
|
|
33189
33227
|
}
|
|
@@ -33248,7 +33286,7 @@ init_safe_text();
|
|
|
33248
33286
|
init_raw_encryption();
|
|
33249
33287
|
init_space();
|
|
33250
33288
|
import { DEFAULT_EXPIRY as DEFAULT_EXPIRY2, parseExpiry as parseExpiry3, SERVICE_LONG_TO_SHORT as SERVICE_LONG_TO_SHORT3, VAULT_PERMISSION_SERVICE as VAULT_PERMISSION_SERVICE2 } from "@tinycloud/sdk-core";
|
|
33251
|
-
import { CLI_NAME as
|
|
33289
|
+
import { CLI_NAME as CLI_NAME17 } from "@tinycloud/operations/cli-name";
|
|
33252
33290
|
var APPS_AGENT_PRESET = "apps-agent";
|
|
33253
33291
|
var APPS_AGENT_SPACE = "applications";
|
|
33254
33292
|
var APPS_AGENT_EXPIRY_DAYS = 7;
|
|
@@ -33330,7 +33368,7 @@ function lifetimePhrase(ms2) {
|
|
|
33330
33368
|
return `${hours} hour${hours === 1 ? "" : "s"}`;
|
|
33331
33369
|
}
|
|
33332
33370
|
function revokeSentence(lifetimeMs2) {
|
|
33333
|
-
return `It lasts up to ${lifetimePhrase(lifetimeMs2)}; revoke it any time with \`${
|
|
33371
|
+
return `It lasts up to ${lifetimePhrase(lifetimeMs2)}; revoke it any time with \`${CLI_NAME17} delegation revoke <id>\`.`;
|
|
33334
33372
|
}
|
|
33335
33373
|
function appsAgentScopeNotice(mode, appIds = []) {
|
|
33336
33374
|
const variant = APPS_AGENT_VARIANTS[mode];
|
|
@@ -33525,7 +33563,7 @@ function describeGrantScope(requested, requestedExpiry, applicationsSpace, maxLi
|
|
|
33525
33563
|
const durations = [lifetimeMs(requestedExpiry), ...entryLifetimes].filter((ms2) => ms2 !== void 0).map((ms2) => Math.min(ms2, maxLifetimeMs ?? Number.POSITIVE_INFINITY));
|
|
33526
33564
|
const longest = Math.max(...durations);
|
|
33527
33565
|
lines.push(
|
|
33528
|
-
durations.length === 0 || !Number.isFinite(longest) ? "Its lifetime could not be read from the request." : `It lasts up to ${lifetimePhrase(longest)}${requestedExpiry === void 0 ? " (no lifetime was requested; this is the default)" : ""}; revoke it any time with \`${
|
|
33566
|
+
durations.length === 0 || !Number.isFinite(longest) ? "Its lifetime could not be read from the request." : `It lasts up to ${lifetimePhrase(longest)}${requestedExpiry === void 0 ? " (no lifetime was requested; this is the default)" : ""}; revoke it any time with \`${CLI_NAME17} delegation revoke <id>\`.`
|
|
33529
33567
|
);
|
|
33530
33568
|
return { text: lines.join("\n"), entries: requested, extras };
|
|
33531
33569
|
}
|
|
@@ -33833,7 +33871,7 @@ async function revokeForLogout(ctx, discardLocalChanges) {
|
|
|
33833
33871
|
"REVOCATION_FAILED",
|
|
33834
33872
|
`Revocation failed for ${cid}: ${result.error.message}. Session, key, and replica files are retained for retry; local reads stay denied.`,
|
|
33835
33873
|
ExitCode.ERROR,
|
|
33836
|
-
{ cid, hint: `Retry \`${
|
|
33874
|
+
{ cid, hint: `Retry \`${CLI_NAME18} auth logout --revoke\` when the node is available.` }
|
|
33837
33875
|
);
|
|
33838
33876
|
}
|
|
33839
33877
|
if (result.data.cid !== cid || result.data.revoked !== true) {
|
|
@@ -33852,7 +33890,7 @@ async function revokeForLogout(ctx, discardLocalChanges) {
|
|
|
33852
33890
|
"REVOCATION_FAILED",
|
|
33853
33891
|
`Revocation failed before local logout: ${error instanceof Error ? error.message : String(error)}. Session, key, and replica files are retained for retry; local reads stay denied.`,
|
|
33854
33892
|
ExitCode.ERROR,
|
|
33855
|
-
{ hint: `Retry \`${
|
|
33893
|
+
{ hint: `Retry \`${CLI_NAME18} auth logout --revoke\` when online authority is available.` }
|
|
33856
33894
|
);
|
|
33857
33895
|
}
|
|
33858
33896
|
}
|
|
@@ -34051,7 +34089,7 @@ async function finishProfileLogout(ctx, options = {}) {
|
|
|
34051
34089
|
function registerAuthCommand(program, options) {
|
|
34052
34090
|
const auth = program.command("auth").description("Authentication management");
|
|
34053
34091
|
registerAuthGrantsCommand(auth, { openKeyAcquisition: options?.openKeyAcquisition });
|
|
34054
|
-
auth.command("login").description("Authenticate with TinyCloud").option("--device", "Approve on another device (e.g. a phone) through OpenKey device authorization; requires a KV-scoped --manifest (SQL and secret decrypt need browser or --paste login)").option("--paste", "Wait up to 5 minutes for a signed code from OpenKey on stdin instead of a browser callback (Safari may require paste)").option("--no-popup", "Print the OpenKey URL without opening a browser").option("--method <method>", "Authentication method: local or openkey").option("--manifest <fileOrBase64>", `Request only this manifest's permissions (one space, plus raw encryption network entries such as a secrets decrypt grant); ${SHARE_PUBLISHING_MANIFEST_REF} covers \`${
|
|
34092
|
+
auth.command("login").description("Authenticate with TinyCloud").option("--device", "Approve on another device (e.g. a phone) through OpenKey device authorization; requires a KV-scoped --manifest (SQL and secret decrypt need browser or --paste login)").option("--paste", "Wait up to 5 minutes for a signed code from OpenKey on stdin instead of a browser callback (Safari may require paste)").option("--no-popup", "Print the OpenKey URL without opening a browser").option("--method <method>", "Authentication method: local or openkey").option("--manifest <fileOrBase64>", `Request only this manifest's permissions (one space, plus raw encryption network entries such as a secrets decrypt grant); ${SHARE_PUBLISHING_MANIFEST_REF} covers \`${CLI_NAME18} 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("--force", "Replica-enabled OpenKey login: replace the stored session key even when recorded shares could not be checked or hold unexpired shares that need the current key").option("--keys <folder>", "Also let this sign-in create and use encryption keys in this folder (for example `qa`). The node must support named keys. With OpenKey, the first time on a profile also needs `--owner`.").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) => {
|
|
34055
34093
|
try {
|
|
34056
34094
|
if (options2.device && options2.paste) {
|
|
34057
34095
|
throw new CLIError("INVALID_ARGUMENT", "--device and --paste are mutually exclusive.", ExitCode.USAGE_ERROR);
|
|
@@ -34209,7 +34247,7 @@ function registerAuthCommand(program, options) {
|
|
|
34209
34247
|
authenticated: false,
|
|
34210
34248
|
replicasRemoved,
|
|
34211
34249
|
replicasKept: options2.keepReplicas === true,
|
|
34212
|
-
warning: online ? "The node confirmed revocation of the listed CIDs before local logout." : options2.keepReplicas ? "Legacy replica files remain on disk; this local logout clears the session but does not revoke node grants. Purge the files before a future sign-in if they must not be reused." : `Logout is local; delegations and grants on the node stay valid until they expire. Use \`${
|
|
34250
|
+
warning: online ? "The node confirmed revocation of the listed CIDs before local logout." : options2.keepReplicas ? "Legacy replica files remain on disk; this local logout clears the session but does not revoke node grants. Purge the files before a future sign-in if they must not be reused." : `Logout is local; delegations and grants on the node stay valid until they expire. Use \`${CLI_NAME18} delegation revoke <cid>\` to end access now.`,
|
|
34213
34251
|
...online === void 0 ? {} : {
|
|
34214
34252
|
revokedCids: online.plan.cids,
|
|
34215
34253
|
revocationExcluded: online.plan.excluded
|
|
@@ -34354,7 +34392,7 @@ ${appsAgentScopeNotice(preset.mode, preset.appIds)}
|
|
|
34354
34392
|
if (profile.authMethod === "openkey" || options2.device) {
|
|
34355
34393
|
const key = await ProfileManager.getKey(ctx.profile);
|
|
34356
34394
|
if (!key) {
|
|
34357
|
-
throw new CLIError("NO_KEY", `No key found for profile "${ctx.profile}". Run \`${
|
|
34395
|
+
throw new CLIError("NO_KEY", `No key found for profile "${ctx.profile}". Run \`${CLI_NAME18} --profile ${ctx.profile} auth rotate\` to create a new key and sign in.`, ExitCode.AUTH_REQUIRED);
|
|
34358
34396
|
}
|
|
34359
34397
|
const openkeyHost = resolveOpenKeyHost(profile);
|
|
34360
34398
|
const grants = [];
|
|
@@ -34374,7 +34412,7 @@ ${appsAgentScopeNotice(preset.mode, preset.appIds)}
|
|
|
34374
34412
|
if (notices) process.stderr.write(`${notices.prompts[index]}
|
|
34375
34413
|
`);
|
|
34376
34414
|
const reason = permissionGrantReason(
|
|
34377
|
-
`Grant requested TinyCloud permissions from \`${
|
|
34415
|
+
`Grant requested TinyCloud permissions from \`${CLI_NAME18} auth request --grant\`.`,
|
|
34378
34416
|
group
|
|
34379
34417
|
);
|
|
34380
34418
|
const request = grantRequestPermissions(group, profile.spaceId ?? profile.spaceName);
|
|
@@ -34484,7 +34522,7 @@ ${appsAgentScopeNotice(preset.mode, preset.appIds)}
|
|
|
34484
34522
|
kind: parsed.kind,
|
|
34485
34523
|
requestId: parsed.requestId,
|
|
34486
34524
|
requested: parsed.requested,
|
|
34487
|
-
next: `${
|
|
34525
|
+
next: `${CLI_NAME18} auth retry ${parsed.requestId}`
|
|
34488
34526
|
});
|
|
34489
34527
|
return;
|
|
34490
34528
|
}
|
|
@@ -35093,7 +35131,7 @@ async function acquireOpenKeyApproval(params) {
|
|
|
35093
35131
|
if (!key) {
|
|
35094
35132
|
throw new CLIError(
|
|
35095
35133
|
"NO_KEY",
|
|
35096
|
-
`No key found for profile "${params.ctx.profile}". Run \`${
|
|
35134
|
+
`No key found for profile "${params.ctx.profile}". Run \`${CLI_NAME18} --profile ${params.ctx.profile} auth rotate\` to create a new key and sign in.`,
|
|
35097
35135
|
ExitCode.AUTH_REQUIRED
|
|
35098
35136
|
);
|
|
35099
35137
|
}
|
|
@@ -35166,7 +35204,7 @@ async function ensureDelegationAuthority(params) {
|
|
|
35166
35204
|
if (!key) {
|
|
35167
35205
|
throw new CLIError(
|
|
35168
35206
|
"NO_KEY",
|
|
35169
|
-
`No key found for profile "${params.ctx.profile}". Run \`${
|
|
35207
|
+
`No key found for profile "${params.ctx.profile}". Run \`${CLI_NAME18} --profile ${params.ctx.profile} auth rotate\` to create a new key and sign in.`,
|
|
35170
35208
|
ExitCode.AUTH_REQUIRED
|
|
35171
35209
|
);
|
|
35172
35210
|
}
|
|
@@ -35420,7 +35458,7 @@ async function rotateAuthKey(profileName2, host, options = {}) {
|
|
|
35420
35458
|
if (!profile.privateKey) {
|
|
35421
35459
|
throw new CLIError(
|
|
35422
35460
|
"LOCAL_OWNER_KEY_REQUIRED",
|
|
35423
|
-
`Profile "${profileName2}" does not have a local owner private key. Run \`${
|
|
35461
|
+
`Profile "${profileName2}" does not have a local owner private key. Run \`${CLI_NAME18} auth login --method local\` first.`,
|
|
35424
35462
|
ExitCode.AUTH_REQUIRED
|
|
35425
35463
|
);
|
|
35426
35464
|
}
|
|
@@ -35678,6 +35716,8 @@ async function refreshPreparedOpenKeySession(params) {
|
|
|
35678
35716
|
if (expectedOwner && normalizePkhIdentifier(owner) !== normalizePkhIdentifier(expectedOwner)) {
|
|
35679
35717
|
throw new CLIError("OPENKEY_OWNER_MISMATCH", "The verified owner differs from this profile. No approval was started.", ExitCode.PERMISSION_DENIED);
|
|
35680
35718
|
}
|
|
35719
|
+
const namedKeys = options.keys === void 0 ? void 0 : parseKeysPrefix(options.keys, canonicalOwnerDid(owner));
|
|
35720
|
+
if (namedKeys !== void 0) await assertNodeSupportsNamedKeys(host);
|
|
35681
35721
|
if (options.force !== true) await assertShareHistoryAllowsKeyDrop(profileName2, "login");
|
|
35682
35722
|
const replicationConfig = createCliReplicationConfig(profileName2, replication, { debug: false, quiet: true }, true);
|
|
35683
35723
|
if (!replicationConfig) throw new CLIError("V2_REQUIRED", "Replica configuration is unavailable; no approval was started.", ExitCode.AUTH_REQUIRED);
|
|
@@ -35698,12 +35738,15 @@ async function refreshPreparedOpenKeySession(params) {
|
|
|
35698
35738
|
capabilityRequest,
|
|
35699
35739
|
includeAccountRegistryPermissions: false,
|
|
35700
35740
|
autoBootstrapAccount: false,
|
|
35701
|
-
replication: replicationConfig
|
|
35741
|
+
replication: replicationConfig,
|
|
35742
|
+
...namedKeys === void 0 ? {} : { additionalRawAbilities: keyAbilitiesFor(namedKeys) }
|
|
35702
35743
|
});
|
|
35703
35744
|
let handoff;
|
|
35704
35745
|
let preparedDid;
|
|
35746
|
+
let handedBackSiwe;
|
|
35705
35747
|
const gateWasActive = await readGateSnapshot(profilePath8(profileName2)).then((gate) => gate !== void 0 && gate.principalHash !== null, () => true);
|
|
35706
35748
|
try {
|
|
35749
|
+
if (namedKeys !== void 0) process.stderr.write(KEYS_APPROVAL_NOTE(namedKeys.prefix));
|
|
35707
35750
|
await node.signInWithOpenKey(async (prepared) => {
|
|
35708
35751
|
preparedDid = prepared.verificationMethod;
|
|
35709
35752
|
handoff = await startPreparedOpenKeyHandoff({
|
|
@@ -35712,14 +35755,23 @@ async function refreshPreparedOpenKeySession(params) {
|
|
|
35712
35755
|
noPopup: options.noPopup,
|
|
35713
35756
|
paste: options.paste
|
|
35714
35757
|
});
|
|
35715
|
-
return handoff.result
|
|
35758
|
+
return handoff.result.then((result) => {
|
|
35759
|
+
handedBackSiwe = result.signedMessage;
|
|
35760
|
+
return result;
|
|
35761
|
+
});
|
|
35716
35762
|
});
|
|
35717
35763
|
const active = node.restorableSession;
|
|
35718
35764
|
if (!active || !preparedDid || node.sessionDid !== preparedDid || active.verificationMethod !== preparedDid) {
|
|
35719
35765
|
throw new CLIError("OPENKEY_SESSION_MISMATCH", "OpenKey signed a different SDK session key; no profile or session was saved.", ExitCode.PERMISSION_DENIED);
|
|
35720
35766
|
}
|
|
35721
35767
|
const key = active.jwk;
|
|
35722
|
-
const verified = verifyScopedLogin({ ...active }, key, preparedDid, permissions, {
|
|
35768
|
+
const verified = verifyScopedLogin({ ...active }, key, preparedDid, permissions, {
|
|
35769
|
+
expectedOwner: owner,
|
|
35770
|
+
expiry,
|
|
35771
|
+
...namedKeys === void 0 ? {} : { namedKeyRow: namedKeys }
|
|
35772
|
+
});
|
|
35773
|
+
const grant = namedKeys === void 0 ? "none" : namedKeysGrantIn(active.siwe, namedKeys);
|
|
35774
|
+
if (grant === "partial") throw partialKeysGrantError();
|
|
35723
35775
|
const session = {
|
|
35724
35776
|
...verified.session,
|
|
35725
35777
|
...node.replicaStamp ? { replicaStamp: node.replicaStamp } : {},
|
|
@@ -35740,6 +35792,10 @@ async function refreshPreparedOpenKeySession(params) {
|
|
|
35740
35792
|
...replication.allowSecrets === true ? { allowSecrets: true } : {}
|
|
35741
35793
|
}
|
|
35742
35794
|
};
|
|
35795
|
+
if (namedKeys !== void 0) {
|
|
35796
|
+
delete updatedProfile.namedKeys;
|
|
35797
|
+
if (grant === "both") updatedProfile.namedKeys = namedKeys;
|
|
35798
|
+
}
|
|
35743
35799
|
await commitLogin(profileName2, snapshot, {
|
|
35744
35800
|
key,
|
|
35745
35801
|
session,
|
|
@@ -35749,6 +35805,10 @@ async function refreshPreparedOpenKeySession(params) {
|
|
|
35749
35805
|
});
|
|
35750
35806
|
await handoff?.complete(true);
|
|
35751
35807
|
announceFirstOwner(profileName2, expectedOwner, owner);
|
|
35808
|
+
if (namedKeys !== void 0) {
|
|
35809
|
+
if (grant === "both") await readyNamedKeysNetwork(node);
|
|
35810
|
+
else process.stderr.write(keysNotGrantedAfterLogin(profileName2));
|
|
35811
|
+
}
|
|
35752
35812
|
return {
|
|
35753
35813
|
profile: updatedProfile,
|
|
35754
35814
|
delegationData: session,
|
|
@@ -35758,14 +35818,14 @@ async function refreshPreparedOpenKeySession(params) {
|
|
|
35758
35818
|
} catch (error) {
|
|
35759
35819
|
await handoff?.complete(false);
|
|
35760
35820
|
await undoPreparedAttempt(profileName2, node, owner, gateWasActive).catch(() => void 0);
|
|
35761
|
-
throw error;
|
|
35821
|
+
throw namedKeys !== void 0 && isNarrowedRecapMismatch(error) && namedKeysGrantIn(handedBackSiwe, namedKeys) === "partial" ? partialKeysGrantError() : error;
|
|
35762
35822
|
}
|
|
35763
35823
|
}
|
|
35764
35824
|
var KEYS_APPROVAL_NOTE = (folder) => `On the OpenKey page, the row that mentions tinycloud.encryption lets this profile create and use keys in the folder "${folder}/". Keep every row ticked. To sign in without keys, untick that whole row and nothing else; any other change makes OpenKey refuse the sign-in.
|
|
35765
35825
|
`;
|
|
35766
|
-
var keysNotGrantedAfterLogin = (profileName2) => `You're signed in, but without named keys: the tinycloud.encryption permission wasn't approved. To add them, sign in again with \`${
|
|
35826
|
+
var keysNotGrantedAfterLogin = (profileName2) => `You're signed in, but without named keys: the tinycloud.encryption permission wasn't approved. To add them, sign in again with \`${CLI_NAME18} --profile ${profileName2} auth login --keys <folder>\` and leave the row that mentions tinycloud.encryption ticked.
|
|
35767
35827
|
`;
|
|
35768
|
-
var KEYS_FOLDER_NOT_READY = `You're signed in, but your encryption keys aren't set up yet. Try \`${
|
|
35828
|
+
var KEYS_FOLDER_NOT_READY = `You're signed in, but your encryption keys aren't set up yet. Try \`${CLI_NAME18} keys ensure <folder>/<name>\` again in a minute.
|
|
35769
35829
|
`;
|
|
35770
35830
|
async function readyNamedKeysNetwork(node) {
|
|
35771
35831
|
try {
|
|
@@ -35792,7 +35852,7 @@ async function refreshPreparedKeysSession(params) {
|
|
|
35792
35852
|
if (expectedOwner && normalizePkhIdentifier(owner) !== normalizePkhIdentifier(expectedOwner)) {
|
|
35793
35853
|
throw new CLIError("OPENKEY_OWNER_MISMATCH", "The verified owner differs from this profile. No approval was started.", ExitCode.PERMISSION_DENIED);
|
|
35794
35854
|
}
|
|
35795
|
-
const namedKeys = parseKeysPrefix(options.keys, owner);
|
|
35855
|
+
const namedKeys = parseKeysPrefix(options.keys, canonicalOwnerDid(owner));
|
|
35796
35856
|
await assertNodeSupportsNamedKeys(host);
|
|
35797
35857
|
if (options.force !== true) await assertShareHistoryAllowsKeyDrop(profileName2, "login");
|
|
35798
35858
|
const node = new TinyCloudNode3({
|
|
@@ -35805,6 +35865,7 @@ async function refreshPreparedKeysSession(params) {
|
|
|
35805
35865
|
});
|
|
35806
35866
|
let handoff;
|
|
35807
35867
|
let preparedDid;
|
|
35868
|
+
let handedBackSiwe;
|
|
35808
35869
|
try {
|
|
35809
35870
|
process.stderr.write(KEYS_APPROVAL_NOTE(namedKeys.prefix));
|
|
35810
35871
|
await node.signInWithOpenKey(async (prepared) => {
|
|
@@ -35815,7 +35876,10 @@ async function refreshPreparedKeysSession(params) {
|
|
|
35815
35876
|
noPopup: options.noPopup,
|
|
35816
35877
|
paste: options.paste
|
|
35817
35878
|
});
|
|
35818
|
-
return handoff.result
|
|
35879
|
+
return handoff.result.then((result) => {
|
|
35880
|
+
handedBackSiwe = result.signedMessage;
|
|
35881
|
+
return result;
|
|
35882
|
+
});
|
|
35819
35883
|
});
|
|
35820
35884
|
const active = node.restorableSession;
|
|
35821
35885
|
if (!active || !preparedDid || node.sessionDid !== preparedDid || active.verificationMethod !== preparedDid) {
|
|
@@ -35849,16 +35913,9 @@ async function refreshPreparedKeysSession(params) {
|
|
|
35849
35913
|
} catch (error) {
|
|
35850
35914
|
await handoff?.complete(false);
|
|
35851
35915
|
await node.replication?.close();
|
|
35852
|
-
throw isNarrowedRecapMismatch(error) ? partialKeysGrantError() : error;
|
|
35916
|
+
throw isNarrowedRecapMismatch(error) && namedKeysGrantIn(handedBackSiwe, namedKeys) === "partial" ? partialKeysGrantError() : error;
|
|
35853
35917
|
}
|
|
35854
35918
|
}
|
|
35855
|
-
function partialKeysGrantError() {
|
|
35856
|
-
return new CLIError(
|
|
35857
|
-
"KEYS_PARTIAL_GRANT",
|
|
35858
|
-
"Named keys need both permissions (create and use), but only one was approved, so this sign-in wasn't saved. Sign in again and approve both.",
|
|
35859
|
-
ExitCode.PERMISSION_DENIED
|
|
35860
|
-
);
|
|
35861
|
-
}
|
|
35862
35919
|
async function undoPreparedAttempt(profileName2, node, owner, gateWasActive) {
|
|
35863
35920
|
if (!gateWasActive) {
|
|
35864
35921
|
const undone = await ProfileManager.withLock(profileName2, async () => {
|
|
@@ -35897,14 +35954,7 @@ async function refreshOpenKeySession(profileName2, host, options = {}) {
|
|
|
35897
35954
|
const estimatedExpiry = expiry === void 0 ? void 0 : new Date(Date.now() + expiry.durationMs).toISOString();
|
|
35898
35955
|
assertSessionReplaceable(profileName2, snapshot, expectedOwner, permissions, estimatedExpiry);
|
|
35899
35956
|
}
|
|
35900
|
-
if (options.keys !== void 0) {
|
|
35901
|
-
if (replication.prefixes.length > 0) {
|
|
35902
|
-
throw new CLIError(
|
|
35903
|
-
"INVALID_ARGUMENT",
|
|
35904
|
-
`With OpenKey sign-in, \`--keys\` can't be used on a profile that has replication prefixes (given with \`--replication-prefix\` or saved from an earlier sign-in). Nothing was started. Use a separate profile for named keys: \`${CLI_NAME17} --profile <name> auth login --owner did:pkh:eip155:<chain>:<address> --keys <folder>\`.`,
|
|
35905
|
-
ExitCode.USAGE_ERROR
|
|
35906
|
-
);
|
|
35907
|
-
}
|
|
35957
|
+
if (options.keys !== void 0 && replication.prefixes.length === 0) {
|
|
35908
35958
|
return refreshPreparedKeysSession({ profileName: profileName2, host, profile, snapshot, options, expectedOwner, expiry });
|
|
35909
35959
|
}
|
|
35910
35960
|
if (replication.prefixes.length > 0) {
|
|
@@ -35982,7 +36032,7 @@ async function refreshOpenKeySession(profileName2, host, options = {}) {
|
|
|
35982
36032
|
}
|
|
35983
36033
|
|
|
35984
36034
|
// src/commands/completion.ts
|
|
35985
|
-
import { CLI_NAME as
|
|
36035
|
+
import { CLI_NAME as CLI_NAME19 } from "@tinycloud/operations/cli-name";
|
|
35986
36036
|
function registerCompletionCommand(program) {
|
|
35987
36037
|
const completion = program.command("completion").description("Generate shell completions");
|
|
35988
36038
|
completion.command("bash").description("Output bash completions").action(() => {
|
|
@@ -35999,7 +36049,7 @@ function registerCompletionCommand(program) {
|
|
|
35999
36049
|
});
|
|
36000
36050
|
}
|
|
36001
36051
|
function generateBashCompletion() {
|
|
36002
|
-
return `# ${
|
|
36052
|
+
return `# ${CLI_NAME19} bash completion
|
|
36003
36053
|
_tc_completions() {
|
|
36004
36054
|
local cur prev commands subcommands
|
|
36005
36055
|
COMPREPLY=()
|
|
@@ -36024,11 +36074,11 @@ _tc_completions() {
|
|
|
36024
36074
|
COMPREPLY=( $(compgen -W "\${subcommands}" -- "\${cur}") )
|
|
36025
36075
|
fi
|
|
36026
36076
|
}
|
|
36027
|
-
complete -F _tc_completions ${
|
|
36077
|
+
complete -F _tc_completions ${CLI_NAME19}
|
|
36028
36078
|
`;
|
|
36029
36079
|
}
|
|
36030
36080
|
function generateZshCompletion() {
|
|
36031
|
-
return `#compdef ${
|
|
36081
|
+
return `#compdef ${CLI_NAME19}
|
|
36032
36082
|
|
|
36033
36083
|
_tc() {
|
|
36034
36084
|
local -a commands
|
|
@@ -36079,50 +36129,50 @@ _tc
|
|
|
36079
36129
|
`;
|
|
36080
36130
|
}
|
|
36081
36131
|
function generateFishCompletion() {
|
|
36082
|
-
return `# ${
|
|
36132
|
+
return `# ${CLI_NAME19} fish completion
|
|
36083
36133
|
set -l commands init auth kv space delegation share node profile completion
|
|
36084
36134
|
|
|
36085
36135
|
# Disable file completion by default
|
|
36086
|
-
complete -c ${
|
|
36136
|
+
complete -c ${CLI_NAME19} -f
|
|
36087
36137
|
|
|
36088
36138
|
# Top-level commands
|
|
36089
|
-
complete -c ${
|
|
36090
|
-
complete -c ${
|
|
36091
|
-
complete -c ${
|
|
36092
|
-
complete -c ${
|
|
36093
|
-
complete -c ${
|
|
36094
|
-
complete -c ${
|
|
36095
|
-
complete -c ${
|
|
36096
|
-
complete -c ${
|
|
36097
|
-
complete -c ${
|
|
36139
|
+
complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a init -d "Initialize a new TinyCloud profile"
|
|
36140
|
+
complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a auth -d "Authentication management"
|
|
36141
|
+
complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a kv -d "Key-value store operations"
|
|
36142
|
+
complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a space -d "Space management"
|
|
36143
|
+
complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a delegation -d "Manage delegations"
|
|
36144
|
+
complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a share -d "Share data with others"
|
|
36145
|
+
complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a node -d "Node health and info"
|
|
36146
|
+
complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a profile -d "Profile management"
|
|
36147
|
+
complete -c ${CLI_NAME19} -n "not __fish_seen_subcommand_from $commands" -a completion -d "Generate shell completions"
|
|
36098
36148
|
|
|
36099
36149
|
# Subcommands
|
|
36100
|
-
complete -c ${
|
|
36101
|
-
complete -c ${
|
|
36102
|
-
complete -c ${
|
|
36103
|
-
complete -c ${
|
|
36104
|
-
complete -c ${
|
|
36105
|
-
complete -c ${
|
|
36106
|
-
complete -c ${
|
|
36107
|
-
complete -c ${
|
|
36150
|
+
complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from auth" -a "login logout rotate status whoami"
|
|
36151
|
+
complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from kv" -a "get put delete list head"
|
|
36152
|
+
complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from space" -a "list create info switch"
|
|
36153
|
+
complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from delegation" -a "create list info revoke"
|
|
36154
|
+
complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from share" -a "create receive list revoke"
|
|
36155
|
+
complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from node" -a "health version status"
|
|
36156
|
+
complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from profile" -a "list create show switch delete"
|
|
36157
|
+
complete -c ${CLI_NAME19} -n "__fish_seen_subcommand_from completion" -a "bash zsh fish"
|
|
36108
36158
|
|
|
36109
36159
|
# Global options
|
|
36110
|
-
complete -c ${
|
|
36111
|
-
complete -c ${
|
|
36112
|
-
complete -c ${
|
|
36113
|
-
complete -c ${
|
|
36114
|
-
complete -c ${
|
|
36115
|
-
complete -c ${
|
|
36116
|
-
complete -c ${
|
|
36117
|
-
complete -c ${
|
|
36160
|
+
complete -c ${CLI_NAME19} -l profile -s p -d "Profile to use"
|
|
36161
|
+
complete -c ${CLI_NAME19} -l host -s H -d "TinyCloud node URL"
|
|
36162
|
+
complete -c ${CLI_NAME19} -l verbose -s v -d "Enable verbose output"
|
|
36163
|
+
complete -c ${CLI_NAME19} -l no-cache -d "Disable caching"
|
|
36164
|
+
complete -c ${CLI_NAME19} -l quiet -s q -d "Suppress non-essential output"
|
|
36165
|
+
complete -c ${CLI_NAME19} -l replication -d "Enable read-through replication"
|
|
36166
|
+
complete -c ${CLI_NAME19} -l no-replication -d "Disable replication"
|
|
36167
|
+
complete -c ${CLI_NAME19} -l replication-debug -d "Write replication diagnostics to stderr"
|
|
36118
36168
|
`;
|
|
36119
36169
|
}
|
|
36120
36170
|
|
|
36121
36171
|
// src/commands/courier.ts
|
|
36122
|
-
import { CLI_NAME as
|
|
36123
|
-
var TC_OPTIONS_BEFORE_COURIER = `${
|
|
36172
|
+
import { CLI_NAME as CLI_NAME20 } from "@tinycloud/operations/cli-name";
|
|
36173
|
+
var TC_OPTIONS_BEFORE_COURIER = `${CLI_NAME20} courier takes no ${CLI_NAME20} options and does not use ${CLI_NAME20} profiles; Courier keeps its own profile in $COURIER_HOME (default ~/.courier). Put flags after "courier", e.g. ${CLI_NAME20} courier init --host <url>.`;
|
|
36124
36174
|
function registerCourierCommand(program) {
|
|
36125
|
-
const courier = program.command("courier").description(`Agent-to-agent threads over TinyCloud; run \`${
|
|
36175
|
+
const courier = program.command("courier").description(`Agent-to-agent threads over TinyCloud; run \`${CLI_NAME20} courier --help\` (beta)`).argument("[args...]").allowUnknownOption().helpOption(false);
|
|
36126
36176
|
courier.action(() => courier.error(TC_OPTIONS_BEFORE_COURIER, { exitCode: 2, code: "tc.courier.options" }));
|
|
36127
36177
|
}
|
|
36128
36178
|
|
|
@@ -36167,7 +36217,7 @@ init_formatter();
|
|
|
36167
36217
|
init_errors();
|
|
36168
36218
|
init_constants();
|
|
36169
36219
|
init_sdk();
|
|
36170
|
-
import { CLI_NAME as
|
|
36220
|
+
import { CLI_NAME as CLI_NAME21 } from "@tinycloud/operations/cli-name";
|
|
36171
36221
|
import { PermissionNotInManifestError } from "@tinycloud/node-sdk";
|
|
36172
36222
|
async function queryDelegations(node, options, until) {
|
|
36173
36223
|
const found = [];
|
|
@@ -36178,7 +36228,7 @@ async function queryDelegations(node, options, until) {
|
|
|
36178
36228
|
if (page.error.code === "PERMISSION_DENIED") {
|
|
36179
36229
|
throw new CLIError(
|
|
36180
36230
|
"PERMISSION_DENIED",
|
|
36181
|
-
`This session cannot read delegations: it lacks tinycloud.delegation/list. Run \`${
|
|
36231
|
+
`This session cannot read delegations: it lacks tinycloud.delegation/list. Run \`${CLI_NAME21} auth login\` again to grant it.`,
|
|
36182
36232
|
ExitCode.PERMISSION_DENIED
|
|
36183
36233
|
);
|
|
36184
36234
|
}
|
|
@@ -36353,7 +36403,7 @@ init_constants();
|
|
|
36353
36403
|
init_formatter();
|
|
36354
36404
|
init_theme();
|
|
36355
36405
|
init_errors();
|
|
36356
|
-
import { CLI_NAME as
|
|
36406
|
+
import { CLI_NAME as CLI_NAME22 } from "@tinycloud/operations/cli-name";
|
|
36357
36407
|
init_session_expired();
|
|
36358
36408
|
init_types();
|
|
36359
36409
|
function registerDoctorCommand(program) {
|
|
@@ -36387,7 +36437,7 @@ function registerDoctorCommand(program) {
|
|
|
36387
36437
|
const profile = await ProfileManager.getProfile(profileName2);
|
|
36388
36438
|
keyDetail = profile.did ? `${profile.did.slice(0, 20)}...` : "key found";
|
|
36389
36439
|
} else {
|
|
36390
|
-
keyDetail = `no key \u2014 run ${
|
|
36440
|
+
keyDetail = `no key \u2014 run ${CLI_NAME22} init`;
|
|
36391
36441
|
}
|
|
36392
36442
|
} catch {
|
|
36393
36443
|
keyDetail = "error reading key";
|
|
@@ -36409,7 +36459,7 @@ function registerDoctorCommand(program) {
|
|
|
36409
36459
|
} else if (state2 === "unverified") {
|
|
36410
36460
|
sessionDetail = `not usable (no signed OpenKey proof) \u2014 ${signInAgainHint(profileName2, resolveProfilePosture(profile))}`;
|
|
36411
36461
|
} else {
|
|
36412
|
-
sessionDetail = sessionOk ? "active" : `no session \u2014 run ${
|
|
36462
|
+
sessionDetail = sessionOk ? "active" : `no session \u2014 run ${CLI_NAME22} auth login`;
|
|
36413
36463
|
}
|
|
36414
36464
|
} catch {
|
|
36415
36465
|
sessionDetail = "error reading session";
|
|
@@ -36437,7 +36487,7 @@ function registerDoctorCommand(program) {
|
|
|
36437
36487
|
try {
|
|
36438
36488
|
const profile = await ProfileManager.getProfile(profileName2);
|
|
36439
36489
|
spaceOk = Boolean(profile.spaceId);
|
|
36440
|
-
spaceDetail = spaceOk ? `${profile.spaceId.slice(0, 16)}...` : `no space \u2014 run ${
|
|
36490
|
+
spaceDetail = spaceOk ? `${profile.spaceId.slice(0, 16)}...` : `no space \u2014 run ${CLI_NAME22} space create`;
|
|
36441
36491
|
} catch {
|
|
36442
36492
|
spaceDetail = "error checking space";
|
|
36443
36493
|
}
|
|
@@ -36627,7 +36677,7 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
|
|
|
36627
36677
|
}
|
|
36628
36678
|
|
|
36629
36679
|
// src/commands/enable.ts
|
|
36630
|
-
import { CLI_NAME as
|
|
36680
|
+
import { CLI_NAME as CLI_NAME23 } from "@tinycloud/operations/cli-name";
|
|
36631
36681
|
init_constants();
|
|
36632
36682
|
init_profiles();
|
|
36633
36683
|
init_errors();
|
|
@@ -36635,7 +36685,7 @@ init_formatter();
|
|
|
36635
36685
|
init_publishing_manifest();
|
|
36636
36686
|
function registerEnableCommand(program) {
|
|
36637
36687
|
const enable = program.command("enable").description("Enable a narrowly scoped TinyCloud service");
|
|
36638
|
-
enable.command("share").description(`Approve Share publishing scope through OpenKey device authorization (same as \`${
|
|
36688
|
+
enable.command("share").description(`Approve Share publishing scope through OpenKey device authorization (same as \`${CLI_NAME23} auth login --device --manifest builtin:share-publishing\`). Use a dedicated profile, e.g. \`${CLI_NAME23} init --name publisher --key-only && ${CLI_NAME23} --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) => {
|
|
36639
36689
|
try {
|
|
36640
36690
|
const globalOptions = command.optsWithGlobals();
|
|
36641
36691
|
const context = await ProfileManager.resolveContext(globalOptions, { storedSession: "unused" });
|
|
@@ -36670,7 +36720,7 @@ function registerEnableCommand(program) {
|
|
|
36670
36720
|
init_profiles();
|
|
36671
36721
|
init_constants();
|
|
36672
36722
|
init_sdk();
|
|
36673
|
-
import { CLI_NAME as
|
|
36723
|
+
import { CLI_NAME as CLI_NAME24 } from "@tinycloud/operations/cli-name";
|
|
36674
36724
|
import { operationSpaceResolver as operationSpaceResolver2 } from "@tinycloud/operations/delegation-binding";
|
|
36675
36725
|
|
|
36676
36726
|
// src/lib/knowledge-input.ts
|
|
@@ -36936,7 +36986,7 @@ function registerAppsCommand(program) {
|
|
|
36936
36986
|
"after",
|
|
36937
36987
|
`
|
|
36938
36988
|
Examples:
|
|
36939
|
-
$ ${
|
|
36989
|
+
$ ${CLI_NAME24} apps install xyz.tinycloud.sample.healthlog
|
|
36940
36990
|
`
|
|
36941
36991
|
).action(async (appIdInput, _options, cmd) => {
|
|
36942
36992
|
try {
|
|
@@ -37028,7 +37078,7 @@ init_profiles();
|
|
|
37028
37078
|
init_formatter();
|
|
37029
37079
|
init_errors();
|
|
37030
37080
|
init_constants();
|
|
37031
|
-
import { CLI_NAME as
|
|
37081
|
+
import { CLI_NAME as CLI_NAME25 } from "@tinycloud/operations/cli-name";
|
|
37032
37082
|
function registerInitCommand(program) {
|
|
37033
37083
|
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) => {
|
|
37034
37084
|
try {
|
|
@@ -37046,7 +37096,7 @@ function registerInitCommand(program) {
|
|
|
37046
37096
|
if (await ProfileManager.profileExists(profileName2)) {
|
|
37047
37097
|
throw new CLIError(
|
|
37048
37098
|
"PROFILE_EXISTS",
|
|
37049
|
-
`Profile "${profileName2}" already exists. Use \`${
|
|
37099
|
+
`Profile "${profileName2}" already exists. Use \`${CLI_NAME25} profile delete ${profileName2}\` first or choose a different name.`,
|
|
37050
37100
|
ExitCode.ERROR
|
|
37051
37101
|
);
|
|
37052
37102
|
}
|
|
@@ -37102,7 +37152,7 @@ init_session_expired();
|
|
|
37102
37152
|
init_constants();
|
|
37103
37153
|
init_profiles();
|
|
37104
37154
|
import { invokeOperation as invokeOperation2 } from "@tinycloud/operations";
|
|
37105
|
-
import { CLI_NAME as
|
|
37155
|
+
import { CLI_NAME as CLI_NAME26 } from "@tinycloud/operations/cli-name";
|
|
37106
37156
|
init_permissions();
|
|
37107
37157
|
init_errors();
|
|
37108
37158
|
init_formatter();
|
|
@@ -37205,7 +37255,7 @@ function knowledgeFailure(result, profile, appId) {
|
|
|
37205
37255
|
case "error": {
|
|
37206
37256
|
if (result.error.code === "SESSION_NOT_FOUND" || result.error.code === "PROFILE_POSTURE_NOT_ALLOWED" && result.context.posture === "unauthenticated") {
|
|
37207
37257
|
return new CLIError("AUTH_REQUIRED", "Not signed in to TinyCloud.", ExitCode.AUTH_REQUIRED, {
|
|
37208
|
-
hint: `Sign in with: ${
|
|
37258
|
+
hint: `Sign in with: ${CLI_NAME26} --profile ${result.context.profile} auth login`
|
|
37209
37259
|
});
|
|
37210
37260
|
}
|
|
37211
37261
|
if (result.error.code === "NODE_UNREACHABLE") {
|
|
@@ -37235,7 +37285,7 @@ function knowledgeFailure(result, profile, appId) {
|
|
|
37235
37285
|
}
|
|
37236
37286
|
}
|
|
37237
37287
|
function missingGrantHint(profile, appId) {
|
|
37238
|
-
const request = `${
|
|
37288
|
+
const request = `${CLI_NAME26} --profile ${profile} auth request`;
|
|
37239
37289
|
const lines = appId === void 0 ? [
|
|
37240
37290
|
` ${request} --preset ${APPS_AGENT_PRESET} --emit req.json (read-only, all installed apps)`,
|
|
37241
37291
|
` ${request} --cap tinycloud.kv:${APPS_AGENT_SPACE}::get,list --emit req.json (all files, no table access)`,
|
|
@@ -37248,7 +37298,7 @@ function missingGrantHint(profile, appId) {
|
|
|
37248
37298
|
return [
|
|
37249
37299
|
"Ask the owner for a grant, narrowest first. Create the request:",
|
|
37250
37300
|
...lines,
|
|
37251
|
-
`The owner runs \`${
|
|
37301
|
+
`The owner runs \`${CLI_NAME26} auth grant req.json > grant.json\`; then import the result with \`${CLI_NAME26} --profile ${profile} auth import grant.json\`.`
|
|
37252
37302
|
].join("\n");
|
|
37253
37303
|
}
|
|
37254
37304
|
|
|
@@ -37555,7 +37605,7 @@ init_errors();
|
|
|
37555
37605
|
init_sdk();
|
|
37556
37606
|
import { readFile as readFile12 } from "fs/promises";
|
|
37557
37607
|
import { NamedKeyError, createNamedKeyClient } from "@tinycloud/node-sdk/encryption-keys";
|
|
37558
|
-
import { CLI_NAME as
|
|
37608
|
+
import { CLI_NAME as CLI_NAME27 } from "@tinycloud/operations/cli-name";
|
|
37559
37609
|
async function readStdin3() {
|
|
37560
37610
|
const chunks = [];
|
|
37561
37611
|
for await (const chunk of process.stdin) {
|
|
@@ -37579,7 +37629,7 @@ function cliErrorFromNamedKey(error, command, path) {
|
|
|
37579
37629
|
case "KEY_NOT_FOUND":
|
|
37580
37630
|
return new CLIError(
|
|
37581
37631
|
"NOT_FOUND",
|
|
37582
|
-
command === "decrypt" ? "The key this envelope needs doesn't exist on this node, so it can't be decrypted here." : `There's no key at ${path} yet. Create it with \`${
|
|
37632
|
+
command === "decrypt" ? "The key this envelope needs doesn't exist on this node, so it can't be decrypted here." : `There's no key at ${path} yet. Create it with \`${CLI_NAME27} keys ensure ${path}\`.`,
|
|
37583
37633
|
ExitCode.NOT_FOUND
|
|
37584
37634
|
);
|
|
37585
37635
|
case "KEY_BLOCKED":
|
|
@@ -37589,7 +37639,7 @@ function cliErrorFromNamedKey(error, command, path) {
|
|
|
37589
37639
|
}
|
|
37590
37640
|
if (error.status === 401 || error.status === 403) {
|
|
37591
37641
|
return new CLIError("PERMISSION_DENIED", error.message, ExitCode.PERMISSION_DENIED, {
|
|
37592
|
-
hint: `Sign in with \`${
|
|
37642
|
+
hint: `Sign in with \`${CLI_NAME27} auth login --keys <folder>\` for this key's folder.`
|
|
37593
37643
|
});
|
|
37594
37644
|
}
|
|
37595
37645
|
return new CLIError(error.code, error.message, error.status === void 0 ? ExitCode.ERROR : ExitCode.NODE_ERROR);
|
|
@@ -37609,7 +37659,7 @@ async function grantedPrefix(deps, profileName2) {
|
|
|
37609
37659
|
if (keys === void 0) {
|
|
37610
37660
|
throw new CLIError(
|
|
37611
37661
|
"KEYS_NOT_GRANTED",
|
|
37612
|
-
`Profile "${profileName2}" can't use named keys yet. Sign in with \`${
|
|
37662
|
+
`Profile "${profileName2}" can't use named keys yet. Sign in with \`${CLI_NAME27} --profile ${profileName2} auth login --keys <folder>\`.`,
|
|
37613
37663
|
ExitCode.PERMISSION_DENIED
|
|
37614
37664
|
);
|
|
37615
37665
|
}
|
|
@@ -37619,7 +37669,7 @@ function assertInGrantedPrefix(prefix, path, profileName2) {
|
|
|
37619
37669
|
if (!keyPathInPrefix(prefix, path)) {
|
|
37620
37670
|
throw new CLIError(
|
|
37621
37671
|
"KEY_OUTSIDE_GRANTED_PREFIX",
|
|
37622
|
-
`Key "${path}" is outside the folder "${prefix}/" this sign-in covers. To use it, sign in with \`${
|
|
37672
|
+
`Key "${path}" is outside the folder "${prefix}/" this sign-in covers. To use it, sign in with \`${CLI_NAME27} --profile ${profileName2} auth login --method local --keys <folder>\`.`,
|
|
37623
37673
|
ExitCode.PERMISSION_DENIED
|
|
37624
37674
|
);
|
|
37625
37675
|
}
|
|
@@ -37647,7 +37697,7 @@ var defaultDeps = {
|
|
|
37647
37697
|
};
|
|
37648
37698
|
function registerKeysCommand(program, deps = defaultDeps) {
|
|
37649
37699
|
const { outputJson: outputJson2, handleError: handleError2 } = deps;
|
|
37650
|
-
const keys = program.command("keys").description(`Named encryption keys (sign in with \`${
|
|
37700
|
+
const keys = program.command("keys").description(`Named encryption keys (sign in with \`${CLI_NAME27} auth login --keys <folder>\` first)`);
|
|
37651
37701
|
keys.command("ensure <path>").description("Create the named key if it doesn't exist yet, then show it").action(async (path, _options, cmd) => {
|
|
37652
37702
|
try {
|
|
37653
37703
|
const ctx = await deps.resolveContext(cmd.optsWithGlobals());
|
|
@@ -37725,7 +37775,7 @@ init_errors();
|
|
|
37725
37775
|
init_constants();
|
|
37726
37776
|
init_theme();
|
|
37727
37777
|
import { resolveManifestKnowledgeRoot as resolveManifestKnowledgeRoot2 } from "@tinycloud/sdk-core";
|
|
37728
|
-
import { CLI_NAME as
|
|
37778
|
+
import { CLI_NAME as CLI_NAME28 } from "@tinycloud/operations/cli-name";
|
|
37729
37779
|
import { readFile as readFile13 } from "fs/promises";
|
|
37730
37780
|
var DEFAULT_APP_SPACE = "applications";
|
|
37731
37781
|
function registerManifestCommand(program) {
|
|
@@ -37733,8 +37783,8 @@ function registerManifestCommand(program) {
|
|
|
37733
37783
|
manifest.command("resolve <source>").description("Resolve a manifest file or URL to its effective space, paths, and DB basenames").addHelpText("after", `
|
|
37734
37784
|
|
|
37735
37785
|
Examples:
|
|
37736
|
-
$ ${
|
|
37737
|
-
$ ${
|
|
37786
|
+
$ ${CLI_NAME28} manifest resolve ./manifest.json
|
|
37787
|
+
$ ${CLI_NAME28} manifest resolve https://app.example.com/manifest.json --json
|
|
37738
37788
|
|
|
37739
37789
|
What it shows:
|
|
37740
37790
|
- app_id, name, manifest_version
|
|
@@ -37744,7 +37794,7 @@ What it shows:
|
|
|
37744
37794
|
|
|
37745
37795
|
This command is read-only and does NOT contact the node \u2014 it just resolves
|
|
37746
37796
|
the manifest against the active profile's address/chain so you know which
|
|
37747
|
-
\`--space\` and \`--db\` values to pass to other ${
|
|
37797
|
+
\`--space\` and \`--db\` values to pass to other ${CLI_NAME28} commands.
|
|
37748
37798
|
`).action(async (source, _options, cmd) => {
|
|
37749
37799
|
try {
|
|
37750
37800
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -37813,7 +37863,7 @@ ${theme.heading("SQL databases")}
|
|
|
37813
37863
|
`);
|
|
37814
37864
|
}
|
|
37815
37865
|
process.stdout.write(theme.muted(`
|
|
37816
|
-
Use with: ${
|
|
37866
|
+
Use with: ${CLI_NAME28} sql query --space ${spaceName} --db <db> "..."
|
|
37817
37867
|
`));
|
|
37818
37868
|
}
|
|
37819
37869
|
if (permissions.length > 0) {
|
|
@@ -37933,7 +37983,7 @@ init_profiles();
|
|
|
37933
37983
|
init_formatter();
|
|
37934
37984
|
init_errors();
|
|
37935
37985
|
init_constants();
|
|
37936
|
-
import { CLI_NAME as
|
|
37986
|
+
import { CLI_NAME as CLI_NAME29 } from "@tinycloud/operations/cli-name";
|
|
37937
37987
|
import { createInterface as createInterface5 } from "readline";
|
|
37938
37988
|
init_theme();
|
|
37939
37989
|
init_types();
|
|
@@ -38079,9 +38129,9 @@ profile at command time. Precedence for every kv/sql command:
|
|
|
38079
38129
|
explicit --space flag > profile defaultSpace > primary space.
|
|
38080
38130
|
|
|
38081
38131
|
Examples:
|
|
38082
|
-
$ ${
|
|
38083
|
-
$ ${
|
|
38084
|
-
$ ${
|
|
38132
|
+
$ ${CLI_NAME29} profile set-default-space applications
|
|
38133
|
+
$ ${CLI_NAME29} profile set-default-space applications --profile cli-test
|
|
38134
|
+
$ ${CLI_NAME29} profile set-default-space --unset
|
|
38085
38135
|
`).action(async (name, options, cmd) => {
|
|
38086
38136
|
try {
|
|
38087
38137
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -38093,7 +38143,7 @@ Examples:
|
|
|
38093
38143
|
if (!options.unset && (name === void 0 || name === "")) {
|
|
38094
38144
|
throw new CLIError(
|
|
38095
38145
|
"USAGE_ERROR",
|
|
38096
|
-
`Provide a space name (e.g. \`${
|
|
38146
|
+
`Provide a space name (e.g. \`${CLI_NAME29} profile set-default-space applications\`) or pass --unset.`,
|
|
38097
38147
|
ExitCode.USAGE_ERROR
|
|
38098
38148
|
);
|
|
38099
38149
|
}
|
|
@@ -38152,7 +38202,7 @@ function parseOperatorType(raw) {
|
|
|
38152
38202
|
|
|
38153
38203
|
// src/commands/replica.ts
|
|
38154
38204
|
init_profile_replicas();
|
|
38155
|
-
import { CLI_NAME as
|
|
38205
|
+
import { CLI_NAME as CLI_NAME30 } from "@tinycloud/operations/cli-name";
|
|
38156
38206
|
import { randomBytes as randomBytes7 } from "crypto";
|
|
38157
38207
|
import { constants as fsConstants5 } from "fs";
|
|
38158
38208
|
import { mkdir as mkdir5, open as open11, readdir as readdir6, rename as rename4, unlink as unlink5, lstat as lstat9 } from "fs/promises";
|
|
@@ -38293,11 +38343,11 @@ var EXIT_BY_CODE = {
|
|
|
38293
38343
|
var READ_FAILURE = {
|
|
38294
38344
|
absent: { code: "KEY_ABSENT", exit: ExitCode.NOT_FOUND, what: "is not in the replica (the replica covers its whole prefix)" },
|
|
38295
38345
|
deleted: { code: "KEY_DELETED", exit: ExitCode.NOT_FOUND, what: "was deleted at the source" },
|
|
38296
|
-
content_missing: { code: "CONTENT_MISSING", exit: ExitCode.NOT_FOUND, what: `is known but its content has not been fetched yet; run ${
|
|
38346
|
+
content_missing: { code: "CONTENT_MISSING", exit: ExitCode.NOT_FOUND, what: `is known but its content has not been fetched yet; run ${CLI_NAME30} replica sync` },
|
|
38297
38347
|
coverage_incomplete: {
|
|
38298
38348
|
code: "COVERAGE_INCOMPLETE",
|
|
38299
38349
|
exit: ExitCode.NOT_FOUND,
|
|
38300
|
-
what: `is not in the replica, which has not finished its first sync; run ${
|
|
38350
|
+
what: `is not in the replica, which has not finished its first sync; run ${CLI_NAME30} replica sync`
|
|
38301
38351
|
},
|
|
38302
38352
|
not_covered: { code: "NOT_COVERED", exit: ExitCode.USAGE_ERROR, what: "is outside the replica's prefix" }
|
|
38303
38353
|
};
|
|
@@ -38415,7 +38465,7 @@ async function existingReplicaName(profile, option) {
|
|
|
38415
38465
|
const names = await replicaNames(profile);
|
|
38416
38466
|
if (names.length === 1) return names[0];
|
|
38417
38467
|
if (names.length === 0) {
|
|
38418
|
-
throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, `Profile "${profile}" has no replicas. Create one with: ${
|
|
38468
|
+
throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, `Profile "${profile}" has no replicas. Create one with: ${CLI_NAME30} replica sync --prefix <prefix>`);
|
|
38419
38469
|
}
|
|
38420
38470
|
throw new CLIError("USAGE_ERROR", `Profile "${profile}" has several replicas (${names.join(", ")}); pass --replica <name>.`, ExitCode.USAGE_ERROR);
|
|
38421
38471
|
}
|
|
@@ -38523,9 +38573,9 @@ async function storedGrants(profile) {
|
|
|
38523
38573
|
function grantHint(space, prefix) {
|
|
38524
38574
|
return [
|
|
38525
38575
|
"A replica needs a device grant with get and sync on its prefix:",
|
|
38526
|
-
` device: ${
|
|
38527
|
-
` owner: ${
|
|
38528
|
-
` device: ${
|
|
38576
|
+
` device: ${CLI_NAME30} auth request --cap tinycloud.kv:${space}:${prefix}:get,list,metadata,sync --expiry 30d --emit req.json`,
|
|
38577
|
+
` owner: ${CLI_NAME30} auth grant req.json > grant.json`,
|
|
38578
|
+
` device: ${CLI_NAME30} auth import grant.json`
|
|
38529
38579
|
].join("\n");
|
|
38530
38580
|
}
|
|
38531
38581
|
function resolveSpace(input, prefix, grants) {
|
|
@@ -38616,7 +38666,7 @@ function describeStatus(status) {
|
|
|
38616
38666
|
formatField("Size", formatBytes(status.bytes)),
|
|
38617
38667
|
formatField("Grant", status.device.delegationCid),
|
|
38618
38668
|
formatField("Last error", status.lastError ? `${status.lastError.code}: ${status.lastError.message}` : null),
|
|
38619
|
-
formatField("Purge", status.purgePending ? `incomplete: content files remain; the next ${
|
|
38669
|
+
formatField("Purge", status.purgePending ? `incomplete: content files remain; the next ${CLI_NAME30} replica command retries` : null)
|
|
38620
38670
|
].join("\n");
|
|
38621
38671
|
}
|
|
38622
38672
|
async function writeFileAtomic(path, bytes) {
|
|
@@ -38780,7 +38830,7 @@ or coverage incomplete; 5 grant expired, revoked or not yet valid; 6 network;
|
|
|
38780
38830
|
(options, cmd) => run(async () => {
|
|
38781
38831
|
const profile = await profileName(cmd);
|
|
38782
38832
|
if (options.purge) {
|
|
38783
|
-
throw new CLIError("REPORT_READ_ONLY", `Replica report is read-only. To remove a named local replica, run ${
|
|
38833
|
+
throw new CLIError("REPORT_READ_ONLY", `Replica report is read-only. To remove a named local replica, run ${CLI_NAME30} replica reset --replica <name> --purge; flag-owned purge has no equivalent yet (TC-1309).`, ExitCode.USAGE_ERROR);
|
|
38784
38834
|
}
|
|
38785
38835
|
if (options.clearPending) {
|
|
38786
38836
|
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);
|
|
@@ -38882,7 +38932,7 @@ or coverage incomplete; 5 grant expired, revoked or not yet valid; 6 network;
|
|
|
38882
38932
|
if (differs !== void 0) {
|
|
38883
38933
|
throw new ReplicaError(
|
|
38884
38934
|
ReplicaErrorCode.CONFIG_MISMATCH,
|
|
38885
|
-
`Replica ${name} already follows ${differs}. Use another --replica name, or ${
|
|
38935
|
+
`Replica ${name} already follows ${differs}. Use another --replica name, or ${CLI_NAME30} replica reset --replica ${name} --purge first.`
|
|
38886
38936
|
);
|
|
38887
38937
|
}
|
|
38888
38938
|
}
|
|
@@ -39047,7 +39097,7 @@ init_theme();
|
|
|
39047
39097
|
init_errors();
|
|
39048
39098
|
init_warnings();
|
|
39049
39099
|
init_constants();
|
|
39050
|
-
import { CLI_NAME as
|
|
39100
|
+
import { CLI_NAME as CLI_NAME32 } from "@tinycloud/operations/cli-name";
|
|
39051
39101
|
import { readFile as readFile14 } from "fs/promises";
|
|
39052
39102
|
import { join as join17 } from "path";
|
|
39053
39103
|
import { homedir, hostname, userInfo } from "os";
|
|
@@ -39258,7 +39308,7 @@ function parseStatusEnvelope(line) {
|
|
|
39258
39308
|
// src/lib/secrets-wait.ts
|
|
39259
39309
|
init_constants();
|
|
39260
39310
|
init_errors();
|
|
39261
|
-
import { CLI_NAME as
|
|
39311
|
+
import { CLI_NAME as CLI_NAME31 } from "@tinycloud/operations/cli-name";
|
|
39262
39312
|
var WAIT_BACKOFF_MS = [3e3, 4500, 6750, 10125];
|
|
39263
39313
|
var WAIT_STEADY_INTERVAL_MS = 15e3;
|
|
39264
39314
|
var DEFAULT_WAIT_TIMEOUT = "5m";
|
|
@@ -39266,7 +39316,7 @@ var MAX_WAIT_TIMEOUT_MS = 9 * 6e4;
|
|
|
39266
39316
|
var MAX_CONSECUTIVE_FAILURES = 5;
|
|
39267
39317
|
var TIMEOUT_RE = /^(0|\d+s|\d+m)$/;
|
|
39268
39318
|
var RETRYABLE_STATUS_ERRORS = { NODE_UNREACHABLE: true, NODE_ERROR: true };
|
|
39269
|
-
var EXPLICIT_KEY_HINT = `${
|
|
39319
|
+
var EXPLICIT_KEY_HINT = `${CLI_NAME31} secrets wait never signs in, and an explicit private key has no stored session. Use a local profile signed in once with ${CLI_NAME31} --profile <profile> auth login --method local, without TC_PRIVATE_KEY.`;
|
|
39270
39320
|
function abortableSleep(ms2, signal) {
|
|
39271
39321
|
return new Promise((resolve9) => {
|
|
39272
39322
|
if (signal.aborted) {
|
|
@@ -39302,7 +39352,7 @@ async function runSecretsWait(request, deps) {
|
|
|
39302
39352
|
let step = 0;
|
|
39303
39353
|
const cancelled = () => new CLIError(
|
|
39304
39354
|
"CANCELLED",
|
|
39305
|
-
`Interrupted: ${
|
|
39355
|
+
`Interrupted: ${CLI_NAME31} secrets wait stopped before the secrets were ready.`,
|
|
39306
39356
|
ExitCode.CANCELLED,
|
|
39307
39357
|
progress === void 0 ? {} : { progress }
|
|
39308
39358
|
);
|
|
@@ -39446,13 +39496,13 @@ function statusError(error, profile, warnings) {
|
|
|
39446
39496
|
if (error.code === "AUTH_REQUIRED" || error.code === "SESSION_NOT_FOUND") {
|
|
39447
39497
|
const recovery = recoveryOf(error, "sign_in");
|
|
39448
39498
|
if (recovery !== void 0) {
|
|
39449
|
-
const hint = recovery.command !== void 0 ? `Sign in once with: ${recovery.command}, then retry.` : `Sign in to profile "${profile}" once with ${
|
|
39499
|
+
const hint = recovery.command !== void 0 ? `Sign in once with: ${recovery.command}, then retry.` : `Sign in to profile "${profile}" once with ${CLI_NAME31} auth login, then retry.`;
|
|
39450
39500
|
return new CLIError("AUTH_REQUIRED", error.message, ExitCode.AUTH_REQUIRED, { hint, recovery, ...extra });
|
|
39451
39501
|
}
|
|
39452
39502
|
}
|
|
39453
39503
|
if (error.code === "PROFILE_MIGRATION_REQUIRED") {
|
|
39454
39504
|
const recovery = recoveryOf(error, "complete_migration");
|
|
39455
|
-
const hint = recovery?.command !== void 0 ? `Run ${recovery.command} once to finish this profile's one-time upgrade, then retry wait.` : `Run ${
|
|
39505
|
+
const hint = recovery?.command !== void 0 ? `Run ${recovery.command} once to finish this profile's one-time upgrade, then retry wait.` : `Run ${CLI_NAME31} auth caps once with profile "${profile}" to finish its one-time upgrade, then retry wait.`;
|
|
39456
39506
|
return new CLIError("PROFILE_MIGRATION_REQUIRED", error.message, ExitCode.ERROR, {
|
|
39457
39507
|
hint,
|
|
39458
39508
|
...recovery === void 0 ? {} : { recovery },
|
|
@@ -39644,11 +39694,11 @@ async function runSecretOperation(params) {
|
|
|
39644
39694
|
}
|
|
39645
39695
|
function secretPermissionReason(action, name) {
|
|
39646
39696
|
const target = name ? ` secret "${name}"` : " secrets";
|
|
39647
|
-
return `Allow \`${
|
|
39697
|
+
return `Allow \`${CLI_NAME32} secrets ${action}${name ? ` ${name}` : ""}\` to access${target} with the required TinyCloud permissions.`;
|
|
39648
39698
|
}
|
|
39649
39699
|
function assertOwnerApprovalPossible(profileName2, profile, action, name, missing = []) {
|
|
39650
39700
|
if (profile.authMethod !== "openkey" || process.stdin.isTTY || process.stderr.isTTY) return;
|
|
39651
|
-
const command = `${
|
|
39701
|
+
const command = `${CLI_NAME32} secrets ${action === "del" ? "delete" : action}${name ? ` ${name}` : ""}`;
|
|
39652
39702
|
throw new CLIError(
|
|
39653
39703
|
"PERMISSION_DENIED",
|
|
39654
39704
|
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.`,
|
|
@@ -39659,7 +39709,7 @@ function assertOwnerApprovalPossible(profileName2, profile, action, name, missin
|
|
|
39659
39709
|
);
|
|
39660
39710
|
}
|
|
39661
39711
|
function scopedSecretLoginHint(profileName2) {
|
|
39662
|
-
return `Have the owner approve a scoped login whose manifest names the secret: ${
|
|
39712
|
+
return `Have the owner approve a scoped login whose manifest names the secret: ${CLI_NAME32} --profile ${profileName2} auth login --method openkey --paste --manifest <manifest.json>. Pass the owner's code on stdin, newline-terminated.`;
|
|
39663
39713
|
}
|
|
39664
39714
|
async function runSecretOperationAttempt(label, operation) {
|
|
39665
39715
|
try {
|
|
@@ -39793,7 +39843,7 @@ ${message}`,
|
|
|
39793
39843
|
"AUTH_REQUIRED",
|
|
39794
39844
|
"Not signed in to TinyCloud.",
|
|
39795
39845
|
ExitCode.AUTH_REQUIRED,
|
|
39796
|
-
{ hint: `Sign in with: ${
|
|
39846
|
+
{ hint: `Sign in with: ${CLI_NAME32} --profile ${result.context.profile} auth login` }
|
|
39797
39847
|
);
|
|
39798
39848
|
}
|
|
39799
39849
|
if (result.error.code === "NODE_UNREACHABLE") {
|
|
@@ -40270,7 +40320,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
40270
40320
|
name: "Encryption network",
|
|
40271
40321
|
ok: false,
|
|
40272
40322
|
detail: `${networkName} not found`,
|
|
40273
|
-
hint: `${
|
|
40323
|
+
hint: `${CLI_NAME32} secrets network init ${networkName}`
|
|
40274
40324
|
}
|
|
40275
40325
|
];
|
|
40276
40326
|
let secret;
|
|
@@ -40316,7 +40366,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
40316
40366
|
name: "Secret access",
|
|
40317
40367
|
ok: false,
|
|
40318
40368
|
detail: notFound ? `${resolved.permissionPaths.vault} not found` : result.error.message,
|
|
40319
|
-
hint: notFound ? local ? `${
|
|
40369
|
+
hint: notFound ? local ? `${CLI_NAME32} secrets put ${resolved.name}${formatSecretScopeFlag(scopeOptions)} --stdin` : `${CLI_NAME32} secrets request ${resolved.name}${formatSecretScopeFlag(scopeOptions)}` : `Ask the owner profile to grant ${secretCapabilityAction("get")} and ${SECRET_DECRYPT_CAPABILITY}.`
|
|
40320
40370
|
});
|
|
40321
40371
|
}
|
|
40322
40372
|
} else {
|
|
@@ -40560,7 +40610,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
40560
40610
|
if (effectiveLocal(profile, options)) {
|
|
40561
40611
|
throw new CLIError(
|
|
40562
40612
|
"LOCAL_OWNER_PROFILE",
|
|
40563
|
-
`Profile "${ctx.profile}" uses a local key, which owns its secrets: store them with ${
|
|
40613
|
+
`Profile "${ctx.profile}" uses a local key, which owns its secrets: store them with ${CLI_NAME32} secrets put NAME --stdin instead of requesting them.`,
|
|
40564
40614
|
ExitCode.USAGE_ERROR
|
|
40565
40615
|
);
|
|
40566
40616
|
}
|
|
@@ -40683,7 +40733,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
40683
40733
|
|
|
40684
40734
|
// src/commands/share.ts
|
|
40685
40735
|
import { Option } from "commander";
|
|
40686
|
-
import { CLI_NAME as
|
|
40736
|
+
import { CLI_NAME as CLI_NAME33 } from "@tinycloud/operations/cli-name";
|
|
40687
40737
|
import { ProfileLockTimeoutError as ProfileLockTimeoutError3 } from "@tinycloud/operations/state";
|
|
40688
40738
|
init_errors();
|
|
40689
40739
|
|
|
@@ -42144,7 +42194,7 @@ function shareCliError(error, operation = "publish") {
|
|
|
42144
42194
|
"SHARE_HISTORY_UNREADABLE",
|
|
42145
42195
|
"sender history cannot be read with the current session key",
|
|
42146
42196
|
1,
|
|
42147
|
-
{ hint: `Run \`${
|
|
42197
|
+
{ hint: `Run \`${CLI_NAME33} share history recover\` to move the unreadable file aside. The old file is kept, not deleted.` }
|
|
42148
42198
|
);
|
|
42149
42199
|
}
|
|
42150
42200
|
if (error instanceof SharePublishAuthorityError) {
|
|
@@ -42152,12 +42202,12 @@ function shareCliError(error, operation = "publish") {
|
|
|
42152
42202
|
const profileName2 = "profileName" in failure ? failure.profileName : void 0;
|
|
42153
42203
|
const localKey = "localKey" in failure && failure.localKey === true;
|
|
42154
42204
|
const profileHint = profileName2 === void 0 ? "" : `--profile ${profileName2} `;
|
|
42155
|
-
const loginHint = localKey ? `\`${
|
|
42205
|
+
const loginHint = localKey ? `\`${CLI_NAME33} ${profileHint}auth login --method local\`` : `\`${CLI_NAME33} ${profileHint}auth login --device --manifest builtin:share-publishing\` (or \`${CLI_NAME33} ${profileHint}enable share\`)`;
|
|
42156
42206
|
if (failure.kind === "caveated-session") {
|
|
42157
42207
|
const holder = profileName2 === void 0 ? "this session" : `profile ${profileName2}'s session`;
|
|
42158
42208
|
return new CLIError(
|
|
42159
42209
|
"PERMISSION_DENIED",
|
|
42160
|
-
`${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): \`${
|
|
42210
|
+
`${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_NAME33} init --name publisher --key-only && ${CLI_NAME33} --profile publisher enable share\``,
|
|
42161
42211
|
5
|
|
42162
42212
|
);
|
|
42163
42213
|
}
|
|
@@ -42258,7 +42308,7 @@ function shareCliError(error, operation = "publish") {
|
|
|
42258
42308
|
if (nodeCode === "ENOENT") return new CLIError("INVALID_ARGUMENT", "share input was not found", 2);
|
|
42259
42309
|
if (nodeCode === "EISDIR") return new CLIError("INVALID_ARGUMENT", "share input must be a Markdown file", 2);
|
|
42260
42310
|
const mapped = typeof nodeCode === "string" && known[nodeCode] !== void 0 ? known[nodeCode] : known[message];
|
|
42261
|
-
const classified = mapped === void 0 ? classifyShareFailure(error, `\`${
|
|
42311
|
+
const classified = mapped === void 0 ? classifyShareFailure(error, `\`${CLI_NAME33} auth login\``) : void 0;
|
|
42262
42312
|
if (classified !== void 0) return Object.assign(new CLIError(classified.code, classified.reason, classified.exitCode), { cause: error });
|
|
42263
42313
|
if (error instanceof TypeError) return new CLIError("INVALID_ARGUMENT", "share input is invalid", 2);
|
|
42264
42314
|
return new CLIError(mapped?.code ?? "INVALID_ARGUMENT", mapped ? mapped.code : "share operation failed", mapped?.exit ?? 2);
|
|
@@ -42668,7 +42718,7 @@ init_constants();
|
|
|
42668
42718
|
init_sdk();
|
|
42669
42719
|
init_host();
|
|
42670
42720
|
init_theme();
|
|
42671
|
-
import { CLI_NAME as
|
|
42721
|
+
import { CLI_NAME as CLI_NAME34 } from "@tinycloud/operations/cli-name";
|
|
42672
42722
|
import { ACCOUNT_REGISTRY_SPACE } from "@tinycloud/node-sdk";
|
|
42673
42723
|
import { randomBytes as randomBytes9 } from "crypto";
|
|
42674
42724
|
import { mkdir as mkdir7, writeFile as writeFile4 } from "fs/promises";
|
|
@@ -42715,7 +42765,7 @@ function registerSpaceCommand(program) {
|
|
|
42715
42765
|
} else {
|
|
42716
42766
|
const profile = await ProfileManager.getProfile(ctx.profile);
|
|
42717
42767
|
const cap = "tinycloud.kv:account:spaces/:get,list";
|
|
42718
|
-
const hint = resolveProfilePosture(profile) === "delegate-session" ? `Ask the space owner to grant it. Create the request with: ${
|
|
42768
|
+
const hint = resolveProfilePosture(profile) === "delegate-session" ? `Ask the space owner to grant it. Create the request with: ${CLI_NAME34} --profile ${ctx.profile} auth request --cap ${cap} --emit` : `Grant it to this session with: ${CLI_NAME34} --profile ${ctx.profile} auth request --grant --cap ${cap}. The default OpenKey owner sign-in covers only the default space, so it includes neither.`;
|
|
42719
42769
|
throw new CLIError(
|
|
42720
42770
|
"PERMISSION_DENIED",
|
|
42721
42771
|
`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.`,
|
|
@@ -42752,10 +42802,10 @@ function registerSpaceCommand(program) {
|
|
|
42752
42802
|
|
|
42753
42803
|
A delegate cannot host a space \u2014 only its owner (root authority) can. This
|
|
42754
42804
|
emits a tinycloud.host.request artifact naming the space and its owner so you
|
|
42755
|
-
can hand it to the owner; they run \`${
|
|
42805
|
+
can hand it to the owner; they run \`${CLI_NAME34} space host <name>\` and confirm.
|
|
42756
42806
|
|
|
42757
42807
|
If you ARE the owner of the resolved space, this refuses and tells you to host
|
|
42758
|
-
it directly with \`${
|
|
42808
|
+
it directly with \`${CLI_NAME34} space host <name>\` (no request needed).
|
|
42759
42809
|
`).action(async (name, options, cmd) => {
|
|
42760
42810
|
try {
|
|
42761
42811
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -42766,7 +42816,7 @@ it directly with \`${CLI_NAME33} space host <name>\` (no request needed).
|
|
|
42766
42816
|
if (await isRootAuthority(spaceId, ctx.profile)) {
|
|
42767
42817
|
throw new CLIError(
|
|
42768
42818
|
"ALREADY_ROOT_AUTHORITY",
|
|
42769
|
-
`You are the owner of ${spaceId}. Host it directly: ${
|
|
42819
|
+
`You are the owner of ${spaceId}. Host it directly: ${CLI_NAME34} space host ${spaceName}`,
|
|
42770
42820
|
ExitCode.USAGE_ERROR
|
|
42771
42821
|
);
|
|
42772
42822
|
}
|
|
@@ -42852,7 +42902,7 @@ init_sdk();
|
|
|
42852
42902
|
init_space();
|
|
42853
42903
|
init_host();
|
|
42854
42904
|
init_theme();
|
|
42855
|
-
import { CLI_NAME as
|
|
42905
|
+
import { CLI_NAME as CLI_NAME35 } from "@tinycloud/operations/cli-name";
|
|
42856
42906
|
import { writeFile as writeFile5 } from "fs/promises";
|
|
42857
42907
|
import { resolve as resolve8 } from "path";
|
|
42858
42908
|
async function dbHandle(node, dbName, spaceInput, profileName2) {
|
|
@@ -42876,12 +42926,12 @@ database for simple apps, or pass --db to target a named database. Pass
|
|
|
42876
42926
|
--space to target a non-primary space (e.g. the manifest "applications" space).
|
|
42877
42927
|
|
|
42878
42928
|
Common workflows:
|
|
42879
|
-
$ ${
|
|
42880
|
-
$ ${
|
|
42881
|
-
$ ${
|
|
42882
|
-
$ ${
|
|
42883
|
-
$ ${
|
|
42884
|
-
$ ${
|
|
42929
|
+
$ ${CLI_NAME35} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
|
|
42930
|
+
$ ${CLI_NAME35} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["ship docs"]'
|
|
42931
|
+
$ ${CLI_NAME35} sql query "SELECT id, body FROM notes ORDER BY id"
|
|
42932
|
+
$ ${CLI_NAME35} sql query "SELECT * FROM events WHERE type = ?" --db analytics --params '["signup"]'
|
|
42933
|
+
$ ${CLI_NAME35} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
|
|
42934
|
+
$ ${CLI_NAME35} sql export --db analytics --output analytics.db
|
|
42885
42935
|
|
|
42886
42936
|
Commands:
|
|
42887
42937
|
query Read rows with SELECT statements
|
|
@@ -42898,10 +42948,10 @@ Tips:
|
|
|
42898
42948
|
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", `
|
|
42899
42949
|
|
|
42900
42950
|
Examples:
|
|
42901
|
-
$ ${
|
|
42902
|
-
$ ${
|
|
42903
|
-
$ ${
|
|
42904
|
-
$ ${
|
|
42951
|
+
$ ${CLI_NAME35} sql query "SELECT * FROM notes ORDER BY id"
|
|
42952
|
+
$ ${CLI_NAME35} sql query "SELECT * FROM notes WHERE id = ?" --params '[42]'
|
|
42953
|
+
$ ${CLI_NAME35} sql query "SELECT count(*) AS total FROM events" --db analytics --json
|
|
42954
|
+
$ ${CLI_NAME35} sql query "SELECT count(*) FROM conversation" --space applications --db xyz.tinycloud.listen/conversations
|
|
42905
42955
|
|
|
42906
42956
|
Output:
|
|
42907
42957
|
Human output is formatted as a table. Piped output or --json returns
|
|
@@ -42942,11 +42992,11 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
|
|
|
42942
42992
|
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", `
|
|
42943
42993
|
|
|
42944
42994
|
Examples:
|
|
42945
|
-
$ ${
|
|
42946
|
-
$ ${
|
|
42947
|
-
$ ${
|
|
42948
|
-
$ ${
|
|
42949
|
-
$ ${
|
|
42995
|
+
$ ${CLI_NAME35} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
|
|
42996
|
+
$ ${CLI_NAME35} sql execute "INSERT INTO notes (body) VALUES (?)" --params '["first note"]'
|
|
42997
|
+
$ ${CLI_NAME35} sql execute "UPDATE notes SET body = ? WHERE id = ?" --params '["edited", 1]'
|
|
42998
|
+
$ ${CLI_NAME35} sql execute "DROP TABLE old_notes" --db archive
|
|
42999
|
+
$ ${CLI_NAME35} sql execute "DELETE FROM conversation WHERE id = ?" --space applications --db xyz.tinycloud.listen/conversations --params '["abc"]'
|
|
42950
43000
|
|
|
42951
43001
|
Output:
|
|
42952
43002
|
Returns JSON with the changed row count and last inserted row id when available.
|
|
@@ -42975,9 +43025,9 @@ Output:
|
|
|
42975
43025
|
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", `
|
|
42976
43026
|
|
|
42977
43027
|
Examples:
|
|
42978
|
-
$ ${
|
|
42979
|
-
$ ${
|
|
42980
|
-
$ ${
|
|
43028
|
+
$ ${CLI_NAME35} sql export
|
|
43029
|
+
$ ${CLI_NAME35} sql export --db analytics --output analytics.db
|
|
43030
|
+
$ ${CLI_NAME35} sql export --space applications --db xyz.tinycloud.listen/conversations --output listen.db
|
|
42981
43031
|
|
|
42982
43032
|
Output:
|
|
42983
43033
|
Writes the database file locally and returns JSON with the path and size.
|
|
@@ -43010,19 +43060,19 @@ Output:
|
|
|
43010
43060
|
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", `
|
|
43011
43061
|
|
|
43012
43062
|
Examples:
|
|
43013
|
-
$ ${
|
|
43063
|
+
$ ${CLI_NAME35} sql copy --from-db com.tinycloud.conversation-sync/conversations \\
|
|
43014
43064
|
--to-db xyz.tinycloud.listen/conversations \\
|
|
43015
43065
|
--space applications --dry-run
|
|
43016
|
-
$ ${
|
|
43066
|
+
$ ${CLI_NAME35} sql copy --from-space applications --from-db com.foo/data \\
|
|
43017
43067
|
--to-space applications --to-db com.bar/data \\
|
|
43018
43068
|
--table conversation --table participant
|
|
43019
43069
|
|
|
43020
43070
|
Notes:
|
|
43021
43071
|
- Refuses to run when (resolved space, db) is identical for source and destination.
|
|
43022
|
-
- Does NOT create destination tables. Run the target app once (or use \`${
|
|
43072
|
+
- Does NOT create destination tables. Run the target app once (or use \`${CLI_NAME35} sql execute\`)
|
|
43023
43073
|
to materialize the schema before copying.
|
|
43024
43074
|
- One row at a time; suitable for small/medium datasets. Large copies should
|
|
43025
|
-
use \`${
|
|
43075
|
+
use \`${CLI_NAME35} sql export\` + bulk import.
|
|
43026
43076
|
- Authorization: the active session/delegation must cover sql/read on source
|
|
43027
43077
|
AND sql/write on destination. Otherwise the relevant operation will fail.
|
|
43028
43078
|
`).action(async (options, cmd) => {
|
|
@@ -43122,7 +43172,7 @@ init_errors();
|
|
|
43122
43172
|
init_formatter();
|
|
43123
43173
|
init_theme();
|
|
43124
43174
|
init_permissions();
|
|
43125
|
-
import { CLI_NAME as
|
|
43175
|
+
import { CLI_NAME as CLI_NAME36 } from "@tinycloud/operations/cli-name";
|
|
43126
43176
|
import {
|
|
43127
43177
|
NodeWasmBindings as NodeWasmBindings2
|
|
43128
43178
|
} from "@tinycloud/node-sdk";
|
|
@@ -43396,7 +43446,7 @@ function formatStatus(summary) {
|
|
|
43396
43446
|
lines.push(`Default profile: ${theme.value(summary.defaultProfile)}`);
|
|
43397
43447
|
lines.push("");
|
|
43398
43448
|
if (summary.profiles.length === 0) {
|
|
43399
|
-
lines.push(theme.muted(`No profiles configured. Run: ${
|
|
43449
|
+
lines.push(theme.muted(`No profiles configured. Run: ${CLI_NAME36} init`));
|
|
43400
43450
|
return `${lines.join("\n")}
|
|
43401
43451
|
`;
|
|
43402
43452
|
}
|