@tinycloud/cli 1.5.0-beta.21 → 1.5.0-beta.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -6
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +51 -36
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +1 -1
package/dist/legacy-entry.js
CHANGED
|
@@ -2555,6 +2555,9 @@ function isRawEncryptionPermission(permission) {
|
|
|
2555
2555
|
function isVerifiedRawEncryptionPermission(permission) {
|
|
2556
2556
|
return isRawEncryptionPermission(permission) && (permission.space === void 0 || permission.space === "encryption");
|
|
2557
2557
|
}
|
|
2558
|
+
function isDeviceDefaultDecrypt(permission) {
|
|
2559
|
+
return permission.service === ENCRYPTION_PERMISSION_SERVICE && permission.space === "default" && permission.path === "" && permission.actions.length === 1 && permission.actions[0] === "tinycloud.encryption/decrypt" && !permission.caveats?.length;
|
|
2560
|
+
}
|
|
2558
2561
|
var init_raw_encryption = __esm({
|
|
2559
2562
|
"src/lib/raw-encryption.ts"() {
|
|
2560
2563
|
"use strict";
|
|
@@ -2625,7 +2628,7 @@ var init_caveat_refusal = __esm({
|
|
|
2625
2628
|
|
|
2626
2629
|
// src/share/publishing-manifest.ts
|
|
2627
2630
|
function sharePublishingPermissions() {
|
|
2628
|
-
return SHARE_PUBLISHING_MANIFEST.permissions.map((permission) => ({
|
|
2631
|
+
return SHARE_PUBLISHING_MANIFEST.permissions.filter((permission) => permission.service !== "tinycloud.encryption").map((permission) => ({
|
|
2629
2632
|
service: permission.service,
|
|
2630
2633
|
space: SHARE_PUBLISHING_MANIFEST.space,
|
|
2631
2634
|
path: permission.path,
|
|
@@ -2665,6 +2668,12 @@ var init_publishing_manifest = __esm({
|
|
|
2665
2668
|
path: "artifacts/",
|
|
2666
2669
|
skipPrefix: true,
|
|
2667
2670
|
actions: ["tinycloud.kv/get", "tinycloud.kv/metadata", "tinycloud.kv/put", "tinycloud.kv/list"]
|
|
2671
|
+
},
|
|
2672
|
+
{
|
|
2673
|
+
service: "tinycloud.encryption",
|
|
2674
|
+
path: "",
|
|
2675
|
+
skipPrefix: true,
|
|
2676
|
+
actions: ["tinycloud.encryption/decrypt"]
|
|
2668
2677
|
}
|
|
2669
2678
|
]
|
|
2670
2679
|
};
|
|
@@ -2915,6 +2924,9 @@ async function loadPermissionRequest(source, profile) {
|
|
|
2915
2924
|
async function loadManifestPermissions(source, profile, options = {}) {
|
|
2916
2925
|
const raw = await loadManifestText(source);
|
|
2917
2926
|
const manifest = JSON.parse(raw);
|
|
2927
|
+
if (options.device && source === SHARE_PUBLISHING_MANIFEST_REF && Array.isArray(manifest.permissions)) {
|
|
2928
|
+
manifest.permissions = manifest.permissions.filter((entry) => !(entry !== null && typeof entry === "object" && normalizeService(String(entry.service ?? "")) === "tinycloud.encryption"));
|
|
2929
|
+
}
|
|
2918
2930
|
if (Array.isArray(manifest.permissions)) assertNoCaveatLimits(manifest.permissions);
|
|
2919
2931
|
if (options.device && typeof manifest.app_id === "string" && (manifest.secrets !== void 0 || manifest.space === "secrets" || typeof manifest.space === "string" && manifest.space.endsWith(":secrets") || Array.isArray(manifest.permissions) && manifest.permissions.some((entry) => entry !== null && typeof entry === "object" && normalizeService(String(entry.service ?? "")) === "tinycloud.encryption"))) {
|
|
2920
2932
|
throw new CLIError("DEVICE_AUTH_UNSUPPORTED_SCOPE", "--device cannot authorize tinycloud.encryption or the secrets space. Use browser or --paste login.", ExitCode.USAGE_ERROR);
|
|
@@ -3041,6 +3053,10 @@ async function resolvePermissionSpaces(entries, profile, options = {}) {
|
|
|
3041
3053
|
resolved.push({ ...raw, service, space: ENCRYPTION_MANIFEST_SPACE, actions });
|
|
3042
3054
|
continue;
|
|
3043
3055
|
}
|
|
3056
|
+
if (isDeviceDefaultDecrypt({ ...entry, service, actions })) {
|
|
3057
|
+
resolved.push({ service, space: ENCRYPTION_MANIFEST_SPACE, path: await defaultSecretsNetworkId(profile, options.ownerDid), actions });
|
|
3058
|
+
continue;
|
|
3059
|
+
}
|
|
3044
3060
|
let space;
|
|
3045
3061
|
try {
|
|
3046
3062
|
space = await resolveSpaceUri(entry.space, profile) ?? entry.space;
|
|
@@ -6568,20 +6584,20 @@ function effectiveAuthority2(input) {
|
|
|
6568
6584
|
}
|
|
6569
6585
|
function wrap(native) {
|
|
6570
6586
|
const statements = /* @__PURE__ */ new Map();
|
|
6571
|
-
const prepared = (
|
|
6572
|
-
let statement = statements.get(
|
|
6587
|
+
const prepared = (sql2) => {
|
|
6588
|
+
let statement = statements.get(sql2);
|
|
6573
6589
|
if (statement === void 0) {
|
|
6574
|
-
statement = native.prepare(
|
|
6575
|
-
statements.set(
|
|
6590
|
+
statement = native.prepare(sql2);
|
|
6591
|
+
statements.set(sql2, statement);
|
|
6576
6592
|
}
|
|
6577
6593
|
return statement;
|
|
6578
6594
|
};
|
|
6579
6595
|
return {
|
|
6580
|
-
exec: (
|
|
6581
|
-
run: (
|
|
6596
|
+
exec: (sql2) => native.exec(sql2),
|
|
6597
|
+
run: (sql2, ...params) => void prepared(sql2).run(...params),
|
|
6582
6598
|
// bun:sqlite answers a missing row with null, node:sqlite with undefined.
|
|
6583
|
-
get: (
|
|
6584
|
-
all: (
|
|
6599
|
+
get: (sql2, ...params) => prepared(sql2).get(...params) ?? void 0,
|
|
6600
|
+
all: (sql2, ...params) => prepared(sql2).all(...params),
|
|
6585
6601
|
close: () => {
|
|
6586
6602
|
let failure;
|
|
6587
6603
|
try {
|
|
@@ -7274,11 +7290,11 @@ CREATE TABLE IF NOT EXISTS grant_install (
|
|
|
7274
7290
|
"pending_grant_consent_anchor_at REAL",
|
|
7275
7291
|
"pending_grant_consent_anchor_source TEXT"
|
|
7276
7292
|
];
|
|
7277
|
-
normalizeSchema = (
|
|
7278
|
-
SOURCE_SCHEMA = SCHEMA.trim().split(";").map((
|
|
7279
|
-
const match = /^CREATE (TABLE|INDEX) IF NOT EXISTS ([a-z_]+)/.exec(
|
|
7293
|
+
normalizeSchema = (sql2) => sql2.replace(" IF NOT EXISTS", "").replace(/\s+/g, " ").trim();
|
|
7294
|
+
SOURCE_SCHEMA = SCHEMA.trim().split(";").map((sql2) => sql2.trim()).filter(Boolean).map((sql2) => {
|
|
7295
|
+
const match = /^CREATE (TABLE|INDEX) IF NOT EXISTS ([a-z_]+)/.exec(sql2);
|
|
7280
7296
|
if (match === null) throw new Error("Invalid source replica schema.");
|
|
7281
|
-
return { type: match[1].toLowerCase(), name: match[2], sql: normalizeSchema(
|
|
7297
|
+
return { type: match[1].toLowerCase(), name: match[2], sql: normalizeSchema(sql2) };
|
|
7282
7298
|
});
|
|
7283
7299
|
V1_SCHEMA = SOURCE_SCHEMA.filter((entry) => entry.name !== "grant_install");
|
|
7284
7300
|
POLICY_REPLICA = V1_SCHEMA.find((entry) => entry.name === "replica").sql.replace(/\) STRICT$/, `, ${POLICY_COLUMNS.join(", ")}) STRICT`);
|
|
@@ -16453,11 +16469,11 @@ function registerAccountCommand(program) {
|
|
|
16453
16469
|
return handleError(error);
|
|
16454
16470
|
}
|
|
16455
16471
|
});
|
|
16456
|
-
index.command("query <sql>").description("Query the materialized account SQLite index").option("--params <json>", "Bind parameters as a JSON array for ? placeholders").action(async (
|
|
16472
|
+
index.command("query <sql>").description("Query the materialized account SQLite index").option("--params <json>", "Bind parameters as a JSON array for ? placeholders").action(async (sql2, options, cmd) => {
|
|
16457
16473
|
try {
|
|
16458
16474
|
const node = await authenticatedNode(cmd);
|
|
16459
16475
|
const params = parseParams(options.params);
|
|
16460
|
-
const result = await node.account.index.query(
|
|
16476
|
+
const result = await node.account.index.query(sql2, params);
|
|
16461
16477
|
assertOk(result);
|
|
16462
16478
|
outputJson(result.data);
|
|
16463
16479
|
} catch (error) {
|
|
@@ -36959,14 +36975,14 @@ function verifyBytes(file, bytes) {
|
|
|
36959
36975
|
throw new AppsInstallError("INTEGRITY_FAILED", `${file.path} does not match the pinned sha256 and size`);
|
|
36960
36976
|
}
|
|
36961
36977
|
}
|
|
36962
|
-
function splitStatements(
|
|
36978
|
+
function splitStatements(sql2) {
|
|
36963
36979
|
let stripped = "";
|
|
36964
36980
|
let inQuote = false;
|
|
36965
|
-
for (let i = 0; i <
|
|
36966
|
-
const ch =
|
|
36981
|
+
for (let i = 0; i < sql2.length; i++) {
|
|
36982
|
+
const ch = sql2[i];
|
|
36967
36983
|
if (ch === "'") inQuote = !inQuote;
|
|
36968
|
-
if (!inQuote && ch === "-" &&
|
|
36969
|
-
while (i <
|
|
36984
|
+
if (!inQuote && ch === "-" && sql2[i + 1] === "-") {
|
|
36985
|
+
while (i < sql2.length && sql2[i] !== "\n") i++;
|
|
36970
36986
|
stripped += "\n";
|
|
36971
36987
|
continue;
|
|
36972
36988
|
}
|
|
@@ -38146,11 +38162,15 @@ init_errors();
|
|
|
38146
38162
|
init_formatter();
|
|
38147
38163
|
var ONBOARD_EXPIRY = "30d";
|
|
38148
38164
|
var kv = (actions) => actions.map((action) => `tinycloud.kv/${action}`);
|
|
38165
|
+
var sql = (actions) => actions.map((action) => `tinycloud.sql/${action}`);
|
|
38149
38166
|
var ONBOARD_STEPS = [
|
|
38150
38167
|
{
|
|
38151
38168
|
space: "applications",
|
|
38152
|
-
asks: "Read and
|
|
38153
|
-
permissions: [
|
|
38169
|
+
asks: "Read, change and delete your apps' data for 30 days.",
|
|
38170
|
+
permissions: [
|
|
38171
|
+
{ service: "tinycloud.kv", space: "applications", path: "", actions: kv(["get", "list", "put", "del", "metadata"]) },
|
|
38172
|
+
{ service: "tinycloud.sql", space: "applications", path: "", actions: sql(["read", "write"]) }
|
|
38173
|
+
]
|
|
38154
38174
|
},
|
|
38155
38175
|
{
|
|
38156
38176
|
space: "account",
|
|
@@ -38168,7 +38188,7 @@ var ApprovalNeeded = class extends Error {
|
|
|
38168
38188
|
}
|
|
38169
38189
|
};
|
|
38170
38190
|
function registerOnboardCommand(program) {
|
|
38171
|
-
program.command("onboard").description(`Set up this computer to act for its owner: a profile, the owner's OpenKey sign-in (or new account), and a starting grant for 30 days (read and
|
|
38191
|
+
program.command("onboard").description(`Set up this computer to act for its owner: a profile, the owner's OpenKey sign-in (or new account), and a starting grant for 30 days (read, change and delete in the applications space; read which spaces and apps exist). Each run does the next approval: it prints the approval link and exits ${ExitCode.PENDING}; run it again with the code OpenKey shows. Exits 0 when onboarding is complete.`).option("--code <code>", "The code OpenKey showed after the owner approved the last link").action(async (options, cmd) => {
|
|
38172
38192
|
try {
|
|
38173
38193
|
const globalOpts = cmd.optsWithGlobals();
|
|
38174
38194
|
const ctx = await ProfileManager.resolveContext(globalOpts, { storedSession: "unused" });
|
|
@@ -42753,12 +42773,7 @@ function shareCliError(error, operation = "publish") {
|
|
|
42753
42773
|
return new CLIError("UPLOAD_FAILED", "share source upload failed; nothing was shared", 4);
|
|
42754
42774
|
}
|
|
42755
42775
|
if (failure.kind === "decrypt-not-held") {
|
|
42756
|
-
return new CLIError(
|
|
42757
|
-
"PERMISSION_DENIED",
|
|
42758
|
-
"this sign-in can't create email-addressed shares: it has no permission to decrypt with the account's key. Nothing was shared.",
|
|
42759
|
-
5,
|
|
42760
|
-
{ hint: "Share it from https://share.tinycloud.xyz while signed in as the account owner, or ask the owner to share it. A link anyone can open works from this sign-in: --to anyone." }
|
|
42761
|
-
);
|
|
42776
|
+
return new CLIError("PERMISSION_DENIED", `Email shares need a paste sign-in: run ${CLI_NAME34} auth login --paste --manifest builtin:share-publishing`, 5);
|
|
42762
42777
|
}
|
|
42763
42778
|
if (failure.kind === "over-limit") {
|
|
42764
42779
|
return new CLIError("MAX_BYTES_EXCEEDED", ADDRESSED_SHARE_TOO_LARGE_MESSAGE, 7, { hint: `What stays behind: ${failure.remaining.join("; ")}.` });
|
|
@@ -43393,8 +43408,8 @@ import { writeFile as writeFile5 } from "fs/promises";
|
|
|
43393
43408
|
import { resolve as resolve8 } from "path";
|
|
43394
43409
|
async function dbHandle(node, dbName, spaceInput, profileName2) {
|
|
43395
43410
|
const spaceUri = await resolveSpaceUri(spaceInput, profileName2);
|
|
43396
|
-
const
|
|
43397
|
-
return { handle:
|
|
43411
|
+
const sql2 = spaceUri ? node.sqlForSpace(spaceUri) : node.sql;
|
|
43412
|
+
return { handle: sql2.db(dbName), spaceUri };
|
|
43398
43413
|
}
|
|
43399
43414
|
async function throwSqlError(error, spaceUri, profileName2, prefix) {
|
|
43400
43415
|
const storageFull = storageFullError(error, prefix);
|
|
@@ -43405,7 +43420,7 @@ async function throwSqlError(error, spaceUri, profileName2, prefix) {
|
|
|
43405
43420
|
throw cliErrorFromService(error, message);
|
|
43406
43421
|
}
|
|
43407
43422
|
function registerSqlCommand(program) {
|
|
43408
|
-
const
|
|
43423
|
+
const sql2 = program.command("sql").description("SQLite database operations for your TinyCloud space").addHelpText("after", `
|
|
43409
43424
|
|
|
43410
43425
|
TinyCloud SQL gives each space isolated SQLite databases. Use the default
|
|
43411
43426
|
database for simple apps, or pass --db to target a named database. Pass
|
|
@@ -43431,7 +43446,7 @@ Tips:
|
|
|
43431
43446
|
- --space accepts a short name ("applications") or full URI ("tinycloud:pkh:eip155:1:0x...:applications").
|
|
43432
43447
|
- Add --json for scripting-friendly output.
|
|
43433
43448
|
`);
|
|
43434
|
-
|
|
43449
|
+
sql2.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", `
|
|
43435
43450
|
|
|
43436
43451
|
Examples:
|
|
43437
43452
|
$ ${CLI_NAME36} sql query "SELECT * FROM notes ORDER BY id"
|
|
@@ -43475,7 +43490,7 @@ ${rowCount} row${rowCount === 1 ? "" : "s"} returned`) + "\n");
|
|
|
43475
43490
|
return handleError(error);
|
|
43476
43491
|
}
|
|
43477
43492
|
});
|
|
43478
|
-
|
|
43493
|
+
sql2.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", `
|
|
43479
43494
|
|
|
43480
43495
|
Examples:
|
|
43481
43496
|
$ ${CLI_NAME36} sql execute "CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT)"
|
|
@@ -43508,7 +43523,7 @@ Output:
|
|
|
43508
43523
|
return handleError(error);
|
|
43509
43524
|
}
|
|
43510
43525
|
});
|
|
43511
|
-
|
|
43526
|
+
sql2.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", `
|
|
43512
43527
|
|
|
43513
43528
|
Examples:
|
|
43514
43529
|
$ ${CLI_NAME36} sql export
|
|
@@ -43543,7 +43558,7 @@ Output:
|
|
|
43543
43558
|
return handleError(error);
|
|
43544
43559
|
}
|
|
43545
43560
|
});
|
|
43546
|
-
|
|
43561
|
+
sql2.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", `
|
|
43547
43562
|
|
|
43548
43563
|
Examples:
|
|
43549
43564
|
$ ${CLI_NAME36} sql copy --from-db com.tinycloud.conversation-sync/conversations \\
|