@tinycloud/cli 1.5.0-beta.23 → 1.5.0-beta.25
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/courier-entry.js +8859 -503
- package/dist/courier-entry.js.map +1 -1
- package/dist/index.js +413 -35
- package/dist/index.js.map +1 -1
- package/dist/legacy-entry.js +457 -96
- package/dist/legacy-entry.js.map +1 -1
- package/package.json +9 -9
- package/skills/courier/SKILL.md +21 -0
- package/skills/courier/start.md +17 -0
package/dist/legacy-entry.js
CHANGED
|
@@ -1901,11 +1901,13 @@ function expandEncryptionPermissionEntry(entry) {
|
|
|
1901
1901
|
`tinycloud.encryption entries require path to be a networkId URN (got ${JSON.stringify(entry.path)})`
|
|
1902
1902
|
);
|
|
1903
1903
|
}
|
|
1904
|
-
|
|
1904
|
+
const sharePrefix = entry.path.endsWith("/share/");
|
|
1905
|
+
if (entry.path.includes("/") && !sharePrefix) {
|
|
1905
1906
|
throw new ManifestValidationError("named keys go in manifest.keys");
|
|
1906
1907
|
}
|
|
1907
1908
|
try {
|
|
1908
|
-
parseNetworkId(entry.path);
|
|
1909
|
+
const network = parseNetworkId(sharePrefix ? entry.path.slice(0, -"/share/".length) : entry.path);
|
|
1910
|
+
if (sharePrefix && network.name !== "default") throw new Error("share prefix requires default network");
|
|
1909
1911
|
} catch {
|
|
1910
1912
|
throw new ManifestValidationError("tinycloud.encryption entries require a valid networkId URN");
|
|
1911
1913
|
}
|
|
@@ -1940,6 +1942,9 @@ function expandEncryptionPermissionEntry(entry) {
|
|
|
1940
1942
|
seen.add(a);
|
|
1941
1943
|
}
|
|
1942
1944
|
}
|
|
1945
|
+
if (sharePrefix && dedupedActions.some((action) => action !== "tinycloud.encryption/decrypt")) {
|
|
1946
|
+
throw new ManifestValidationError("share prefix only supports decrypt");
|
|
1947
|
+
}
|
|
1943
1948
|
return [
|
|
1944
1949
|
{
|
|
1945
1950
|
service: ENCRYPTION_PERMISSION_SERVICE,
|
|
@@ -2555,6 +2560,9 @@ function isRawEncryptionPermission(permission) {
|
|
|
2555
2560
|
function isVerifiedRawEncryptionPermission(permission) {
|
|
2556
2561
|
return isRawEncryptionPermission(permission) && (permission.space === void 0 || permission.space === "encryption");
|
|
2557
2562
|
}
|
|
2563
|
+
function isSharePublishingPrefixDecrypt(permission) {
|
|
2564
|
+
return permission.service === ENCRYPTION_PERMISSION_SERVICE && permission.space === "default" && permission.path === "share/" && permission.actions.length === 1 && permission.actions[0] === "tinycloud.encryption/decrypt" && !permission.caveats?.length;
|
|
2565
|
+
}
|
|
2558
2566
|
function isDeviceDefaultDecrypt(permission) {
|
|
2559
2567
|
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
2568
|
}
|
|
@@ -2579,7 +2587,7 @@ function canonicalNetworkUrn(path) {
|
|
|
2579
2587
|
return match ? `urn:tinycloud:encryption:${canonicalOwnerDid(match[1])}:${match[2]}` : path;
|
|
2580
2588
|
}
|
|
2581
2589
|
function rawEncryptionOwnerMatches(path, ownerDid) {
|
|
2582
|
-
const match = /^urn:tinycloud:encryption:(did:pkh:eip155
|
|
2590
|
+
const match = /^urn:tinycloud:encryption:(did:pkh:eip155:[1-9]\d*:0x[0-9a-fA-F]{40}):([a-z0-9][a-z0-9-]*)(?:\/share\/(?:[0-9a-f]{32})?)?$/.exec(path);
|
|
2583
2591
|
if (!match) return false;
|
|
2584
2592
|
try {
|
|
2585
2593
|
return canonicalOwnerDid(match[1]) === canonicalOwnerDid(ownerDid);
|
|
@@ -2671,7 +2679,7 @@ var init_publishing_manifest = __esm({
|
|
|
2671
2679
|
},
|
|
2672
2680
|
{
|
|
2673
2681
|
service: "tinycloud.encryption",
|
|
2674
|
-
path: "",
|
|
2682
|
+
path: "share/",
|
|
2675
2683
|
skipPrefix: true,
|
|
2676
2684
|
actions: ["tinycloud.encryption/decrypt"]
|
|
2677
2685
|
}
|
|
@@ -3053,6 +3061,10 @@ async function resolvePermissionSpaces(entries, profile, options = {}) {
|
|
|
3053
3061
|
resolved.push({ ...raw, service, space: ENCRYPTION_MANIFEST_SPACE, actions });
|
|
3054
3062
|
continue;
|
|
3055
3063
|
}
|
|
3064
|
+
if (isSharePublishingPrefixDecrypt({ ...entry, service, actions })) {
|
|
3065
|
+
resolved.push({ service, space: ENCRYPTION_MANIFEST_SPACE, path: `${await defaultSecretsNetworkId(profile, options.ownerDid)}/share/`, actions });
|
|
3066
|
+
continue;
|
|
3067
|
+
}
|
|
3056
3068
|
if (isDeviceDefaultDecrypt({ ...entry, service, actions })) {
|
|
3057
3069
|
resolved.push({ service, space: ENCRYPTION_MANIFEST_SPACE, path: await defaultSecretsNetworkId(profile, options.ownerDid), actions });
|
|
3058
3070
|
continue;
|
|
@@ -21938,6 +21950,23 @@ var unifiedEncryptionNetworkSchema = external_exports.string().refine((value) =>
|
|
|
21938
21950
|
const network = separator < 0 ? "" : rest.slice(separator + 1);
|
|
21939
21951
|
return owner.startsWith("did:") && owner.length > 4 && network.length > 0 && !/[:/%?#\s]/.test(network);
|
|
21940
21952
|
}, { message: "expected canonical TinyCloud encryption network" });
|
|
21953
|
+
var unifiedEncryptionResourceSchema = external_exports.string().refine((value) => {
|
|
21954
|
+
const prefix = "urn:tinycloud:encryption:";
|
|
21955
|
+
if (!value.startsWith(prefix)) return false;
|
|
21956
|
+
const match = /^(did:.+):([^/:?#\s]+)(?:\/(.+))?$/.exec(value.slice(prefix.length));
|
|
21957
|
+
if (match === null) return false;
|
|
21958
|
+
const path = match[3];
|
|
21959
|
+
return path === void 0 || path.split("/").every(
|
|
21960
|
+
(segment) => segment.length > 0 && segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment)
|
|
21961
|
+
);
|
|
21962
|
+
}, { message: "expected a canonical TinyCloud encryption resource" });
|
|
21963
|
+
var unifiedEncryptionKeyPathSchema = external_exports.string().regex(/^share\/[^/]+$/).refine(
|
|
21964
|
+
(value) => {
|
|
21965
|
+
const segment = value.slice("share/".length);
|
|
21966
|
+
return segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment);
|
|
21967
|
+
},
|
|
21968
|
+
{ message: "expected an exact share key path" }
|
|
21969
|
+
);
|
|
21941
21970
|
var unifiedKvCapabilitySchema = external_exports.object({
|
|
21942
21971
|
kind: external_exports.literal("kv"),
|
|
21943
21972
|
resource: unifiedResourceSchema,
|
|
@@ -21951,7 +21980,7 @@ var unifiedKvCapabilitySchema = external_exports.object({
|
|
|
21951
21980
|
}).strict();
|
|
21952
21981
|
var unifiedEncryptionCapabilitySchema = external_exports.object({
|
|
21953
21982
|
kind: external_exports.literal("encryption"),
|
|
21954
|
-
resource:
|
|
21983
|
+
resource: unifiedEncryptionResourceSchema,
|
|
21955
21984
|
action: external_exports.literal("tinycloud.encryption/decrypt")
|
|
21956
21985
|
}).strict();
|
|
21957
21986
|
var unifiedCapabilitySchema = external_exports.union([unifiedKvCapabilitySchema, unifiedEncryptionCapabilitySchema]);
|
|
@@ -21960,6 +21989,7 @@ var unifiedContentSourceSchema = external_exports.object({
|
|
|
21960
21989
|
kvResource: unifiedResourceSchema,
|
|
21961
21990
|
selector: external_exports.union([external_exports.literal("exact"), external_exports.literal("prefix")]),
|
|
21962
21991
|
encryptionNetwork: unifiedEncryptionNetworkSchema,
|
|
21992
|
+
encryptionKeyPath: unifiedEncryptionKeyPathSchema.optional(),
|
|
21963
21993
|
encryptedSymmetricKeyDigestHex: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
21964
21994
|
keyVersion: external_exports.number().int().positive(),
|
|
21965
21995
|
mode: external_exports.union([external_exports.literal("mutable"), external_exports.literal("immutable")]),
|
|
@@ -22077,16 +22107,21 @@ function validateV3Invariants(value, ctx) {
|
|
|
22077
22107
|
if (value.attestedEnforcerBinding.nodeAudience !== value.target.nodeAudience || value.attestedEnforcerBinding.signature.signerDid !== value.attestedEnforcerBinding.nodeAudience || Date.parse(value.attestedEnforcerBinding.expiresAt) < Date.parse(value.expiry)) {
|
|
22078
22108
|
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["attestedEnforcerBinding"], message: "enforcer binding does not cover the target and share lifetime" });
|
|
22079
22109
|
}
|
|
22080
|
-
|
|
22110
|
+
const encryptionKeyPath = value.contentSource.encryptionKeyPath;
|
|
22111
|
+
if (encryptionKeyPath !== void 0 && encryptionKeyPath !== `share/${value.contentSource.shareId}`) {
|
|
22112
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["contentSource", "encryptionKeyPath"], message: "share key path must name this share" });
|
|
22113
|
+
}
|
|
22114
|
+
if (value.policy.contentSource.shareId !== value.contentSource.shareId || value.policy.contentSource.kvResource !== value.contentSource.kvResource || value.policy.contentSource.selector !== value.contentSource.selector || value.policy.contentSource.encryptionNetwork !== value.encryptionNetwork || value.policy.contentSource.encryptionKeyPath !== encryptionKeyPath || value.policy.contentSource.encryptedSymmetricKeyDigestHex !== value.contentSource.encryptedSymmetricKeyDigestHex || value.policy.contentSource.keyVersion !== value.contentSource.keyVersion || value.policy.contentSource.mode !== value.contentSource.mode || value.policy.contentSource.initialCiphertextDigestHex !== value.contentSource.initialCiphertextDigestHex) {
|
|
22081
22115
|
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["contentSource"], message: "content source is not bound to policy" });
|
|
22082
22116
|
}
|
|
22083
22117
|
if (value.policy.signature.suite !== "Ed25519" || value.policy.signature.signerDid !== value.policy.ownerDid) {
|
|
22084
22118
|
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "signature"], message: "policy must be signed by its owner" });
|
|
22085
22119
|
}
|
|
22086
22120
|
const kvCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "kv");
|
|
22087
|
-
const
|
|
22121
|
+
const expectedEncryptionResource = encryptionKeyPath === void 0 ? value.encryptionNetwork : `${value.encryptionNetwork}/${encryptionKeyPath}`;
|
|
22122
|
+
const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === expectedEncryptionResource && capability.action === "tinycloud.encryption/decrypt");
|
|
22088
22123
|
if (value.policy.capabilityCeiling.length !== 2 || kvCapabilities.length !== 1 || decryptCapabilities.length !== 1 || kvCapabilities[0]?.resource !== value.contentSource.kvResource || kvCapabilities[0]?.selector !== value.contentSource.selector) {
|
|
22089
|
-
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain exact decrypt
|
|
22124
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain the exact decrypt resource" });
|
|
22090
22125
|
}
|
|
22091
22126
|
}
|
|
22092
22127
|
var unsignedShareEnvelopeV3Schema = unsignedShareEnvelopeV3BaseSchema.superRefine(validateV3Invariants);
|
|
@@ -23377,6 +23412,23 @@ var unifiedEncryptionNetworkSchema2 = external_exports2.string().refine((value)
|
|
|
23377
23412
|
const network = separator < 0 ? "" : rest.slice(separator + 1);
|
|
23378
23413
|
return owner.startsWith("did:") && owner.length > 4 && network.length > 0 && !/[:/%?#\s]/.test(network);
|
|
23379
23414
|
}, { message: "expected canonical TinyCloud encryption network" });
|
|
23415
|
+
var unifiedEncryptionResourceSchema2 = external_exports2.string().refine((value) => {
|
|
23416
|
+
const prefix = "urn:tinycloud:encryption:";
|
|
23417
|
+
if (!value.startsWith(prefix)) return false;
|
|
23418
|
+
const match = /^(did:.+):([^/:?#\s]+)(?:\/(.+))?$/.exec(value.slice(prefix.length));
|
|
23419
|
+
if (match === null) return false;
|
|
23420
|
+
const path = match[3];
|
|
23421
|
+
return path === void 0 || path.split("/").every(
|
|
23422
|
+
(segment) => segment.length > 0 && segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment)
|
|
23423
|
+
);
|
|
23424
|
+
}, { message: "expected a canonical TinyCloud encryption resource" });
|
|
23425
|
+
var unifiedEncryptionKeyPathSchema2 = external_exports2.string().regex(/^share\/[^/]+$/).refine(
|
|
23426
|
+
(value) => {
|
|
23427
|
+
const segment = value.slice("share/".length);
|
|
23428
|
+
return segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment);
|
|
23429
|
+
},
|
|
23430
|
+
{ message: "expected an exact share key path" }
|
|
23431
|
+
);
|
|
23380
23432
|
var unifiedKvCapabilitySchema2 = external_exports2.object({
|
|
23381
23433
|
kind: external_exports2.literal("kv"),
|
|
23382
23434
|
resource: unifiedResourceSchema2,
|
|
@@ -23390,7 +23442,7 @@ var unifiedKvCapabilitySchema2 = external_exports2.object({
|
|
|
23390
23442
|
}).strict();
|
|
23391
23443
|
var unifiedEncryptionCapabilitySchema2 = external_exports2.object({
|
|
23392
23444
|
kind: external_exports2.literal("encryption"),
|
|
23393
|
-
resource:
|
|
23445
|
+
resource: unifiedEncryptionResourceSchema2,
|
|
23394
23446
|
action: external_exports2.literal("tinycloud.encryption/decrypt")
|
|
23395
23447
|
}).strict();
|
|
23396
23448
|
var unifiedCapabilitySchema2 = external_exports2.union([unifiedKvCapabilitySchema2, unifiedEncryptionCapabilitySchema2]);
|
|
@@ -23399,6 +23451,7 @@ var unifiedContentSourceSchema2 = external_exports2.object({
|
|
|
23399
23451
|
kvResource: unifiedResourceSchema2,
|
|
23400
23452
|
selector: external_exports2.union([external_exports2.literal("exact"), external_exports2.literal("prefix")]),
|
|
23401
23453
|
encryptionNetwork: unifiedEncryptionNetworkSchema2,
|
|
23454
|
+
encryptionKeyPath: unifiedEncryptionKeyPathSchema2.optional(),
|
|
23402
23455
|
encryptedSymmetricKeyDigestHex: external_exports2.string().regex(/^[0-9a-f]{64}$/),
|
|
23403
23456
|
keyVersion: external_exports2.number().int().positive(),
|
|
23404
23457
|
mode: external_exports2.union([external_exports2.literal("mutable"), external_exports2.literal("immutable")]),
|
|
@@ -23516,16 +23569,21 @@ function validateV3Invariants2(value, ctx) {
|
|
|
23516
23569
|
if (value.attestedEnforcerBinding.nodeAudience !== value.target.nodeAudience || value.attestedEnforcerBinding.signature.signerDid !== value.attestedEnforcerBinding.nodeAudience || Date.parse(value.attestedEnforcerBinding.expiresAt) < Date.parse(value.expiry)) {
|
|
23517
23570
|
ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["attestedEnforcerBinding"], message: "enforcer binding does not cover the target and share lifetime" });
|
|
23518
23571
|
}
|
|
23519
|
-
|
|
23572
|
+
const encryptionKeyPath = value.contentSource.encryptionKeyPath;
|
|
23573
|
+
if (encryptionKeyPath !== void 0 && encryptionKeyPath !== `share/${value.contentSource.shareId}`) {
|
|
23574
|
+
ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["contentSource", "encryptionKeyPath"], message: "share key path must name this share" });
|
|
23575
|
+
}
|
|
23576
|
+
if (value.policy.contentSource.shareId !== value.contentSource.shareId || value.policy.contentSource.kvResource !== value.contentSource.kvResource || value.policy.contentSource.selector !== value.contentSource.selector || value.policy.contentSource.encryptionNetwork !== value.encryptionNetwork || value.policy.contentSource.encryptionKeyPath !== encryptionKeyPath || value.policy.contentSource.encryptedSymmetricKeyDigestHex !== value.contentSource.encryptedSymmetricKeyDigestHex || value.policy.contentSource.keyVersion !== value.contentSource.keyVersion || value.policy.contentSource.mode !== value.contentSource.mode || value.policy.contentSource.initialCiphertextDigestHex !== value.contentSource.initialCiphertextDigestHex) {
|
|
23520
23577
|
ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["contentSource"], message: "content source is not bound to policy" });
|
|
23521
23578
|
}
|
|
23522
23579
|
if (value.policy.signature.suite !== "Ed25519" || value.policy.signature.signerDid !== value.policy.ownerDid) {
|
|
23523
23580
|
ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["policy", "signature"], message: "policy must be signed by its owner" });
|
|
23524
23581
|
}
|
|
23525
23582
|
const kvCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "kv");
|
|
23526
|
-
const
|
|
23583
|
+
const expectedEncryptionResource = encryptionKeyPath === void 0 ? value.encryptionNetwork : `${value.encryptionNetwork}/${encryptionKeyPath}`;
|
|
23584
|
+
const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === expectedEncryptionResource && capability.action === "tinycloud.encryption/decrypt");
|
|
23527
23585
|
if (value.policy.capabilityCeiling.length !== 2 || kvCapabilities.length !== 1 || decryptCapabilities.length !== 1 || kvCapabilities[0]?.resource !== value.contentSource.kvResource || kvCapabilities[0]?.selector !== value.contentSource.selector) {
|
|
23528
|
-
ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain exact decrypt
|
|
23586
|
+
ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain the exact decrypt resource" });
|
|
23529
23587
|
}
|
|
23530
23588
|
}
|
|
23531
23589
|
var unsignedShareEnvelopeV3Schema2 = unsignedShareEnvelopeV3BaseSchema2.superRefine(validateV3Invariants2);
|
|
@@ -23574,53 +23632,13 @@ function shortLinkId(link4) {
|
|
|
23574
23632
|
}
|
|
23575
23633
|
}
|
|
23576
23634
|
|
|
23577
|
-
// src/share/
|
|
23578
|
-
|
|
23579
|
-
|
|
23580
|
-
|
|
23581
|
-
|
|
23582
|
-
|
|
23583
|
-
|
|
23584
|
-
};
|
|
23585
|
-
var ShareHistoryRetryError = class extends Error {
|
|
23586
|
-
constructor(profile) {
|
|
23587
|
-
super("sender history changed during the operation");
|
|
23588
|
-
this.profile = profile;
|
|
23589
|
-
this.name = "ShareHistoryRetryError";
|
|
23590
|
-
}
|
|
23591
|
-
code = "SHARE_HISTORY_RETRY";
|
|
23592
|
-
};
|
|
23593
|
-
var ShareHistoryUnreadableError = class extends Error {
|
|
23594
|
-
constructor() {
|
|
23595
|
-
super("share history is unavailable");
|
|
23596
|
-
this.name = "ShareHistoryUnreadableError";
|
|
23597
|
-
}
|
|
23598
|
-
};
|
|
23599
|
-
var ShareHistoryReadError = class extends Error {
|
|
23600
|
-
constructor() {
|
|
23601
|
-
super("share history could not be read");
|
|
23602
|
-
this.name = "ShareHistoryReadError";
|
|
23603
|
-
}
|
|
23604
|
-
};
|
|
23605
|
-
var HISTORY_WRITE_ERROR_CODES = { EACCES: true, EPERM: true, EROFS: true, ENOSPC: true, EDQUOT: true, EIO: true };
|
|
23606
|
-
function shareHistoryReason(error) {
|
|
23607
|
-
if (error instanceof ShareHistoryUnreadableError) return "HISTORY_UNREADABLE";
|
|
23608
|
-
if (error instanceof ShareHistoryReadError) return "HISTORY_READ_FAILED";
|
|
23609
|
-
const code5 = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
23610
|
-
return typeof code5 === "string" && HISTORY_WRITE_ERROR_CODES[code5] === true ? "HISTORY_WRITE_FAILED" : "HISTORY_UNAVAILABLE";
|
|
23611
|
-
}
|
|
23612
|
-
|
|
23613
|
-
// src/share/adapters.ts
|
|
23614
|
-
init_storage2();
|
|
23615
|
-
init_profiles();
|
|
23616
|
-
init_constants();
|
|
23617
|
-
init_errors();
|
|
23618
|
-
init_session_expired();
|
|
23619
|
-
import { chmod as chmod4, link as link2, open as open8, readFile as readFile8, stat as stat4, unlink as unlink4 } from "fs/promises";
|
|
23620
|
-
import { dirname as dirname5, join as join12 } from "path";
|
|
23621
|
-
import { createHash } from "crypto";
|
|
23622
|
-
import { writeJsonAtomic as writeJsonAtomic2 } from "@tinycloud/operations/state";
|
|
23623
|
-
import { withoutBearer as withoutBearer2 } from "@tinycloud/operations/delegation-binding";
|
|
23635
|
+
// src/share/email-publish-v3.ts
|
|
23636
|
+
import {
|
|
23637
|
+
canonicalDigest,
|
|
23638
|
+
createEmailCredentialRequirement,
|
|
23639
|
+
credentialRequirementDigest,
|
|
23640
|
+
validateCredentialFlowDescriptor
|
|
23641
|
+
} from "@tinycloud/sdk-core";
|
|
23624
23642
|
|
|
23625
23643
|
// ../share-sdk/dist/index.js
|
|
23626
23644
|
init_sha2();
|
|
@@ -28840,6 +28858,23 @@ var unifiedEncryptionNetworkSchema3 = external_exports3.string().refine((value)
|
|
|
28840
28858
|
const network = separator < 0 ? "" : rest.slice(separator + 1);
|
|
28841
28859
|
return owner.startsWith("did:") && owner.length > 4 && network.length > 0 && !/[:/%?#\s]/.test(network);
|
|
28842
28860
|
}, { message: "expected canonical TinyCloud encryption network" });
|
|
28861
|
+
var unifiedEncryptionResourceSchema3 = external_exports3.string().refine((value) => {
|
|
28862
|
+
const prefix = "urn:tinycloud:encryption:";
|
|
28863
|
+
if (!value.startsWith(prefix)) return false;
|
|
28864
|
+
const match = /^(did:.+):([^/:?#\s]+)(?:\/(.+))?$/.exec(value.slice(prefix.length));
|
|
28865
|
+
if (match === null) return false;
|
|
28866
|
+
const path = match[3];
|
|
28867
|
+
return path === void 0 || path.split("/").every(
|
|
28868
|
+
(segment) => segment.length > 0 && segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment)
|
|
28869
|
+
);
|
|
28870
|
+
}, { message: "expected a canonical TinyCloud encryption resource" });
|
|
28871
|
+
var unifiedEncryptionKeyPathSchema3 = external_exports3.string().regex(/^share\/[^/]+$/).refine(
|
|
28872
|
+
(value) => {
|
|
28873
|
+
const segment = value.slice("share/".length);
|
|
28874
|
+
return segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment);
|
|
28875
|
+
},
|
|
28876
|
+
{ message: "expected an exact share key path" }
|
|
28877
|
+
);
|
|
28843
28878
|
var unifiedKvCapabilitySchema3 = external_exports3.object({
|
|
28844
28879
|
kind: external_exports3.literal("kv"),
|
|
28845
28880
|
resource: unifiedResourceSchema3,
|
|
@@ -28853,7 +28888,7 @@ var unifiedKvCapabilitySchema3 = external_exports3.object({
|
|
|
28853
28888
|
}).strict();
|
|
28854
28889
|
var unifiedEncryptionCapabilitySchema3 = external_exports3.object({
|
|
28855
28890
|
kind: external_exports3.literal("encryption"),
|
|
28856
|
-
resource:
|
|
28891
|
+
resource: unifiedEncryptionResourceSchema3,
|
|
28857
28892
|
action: external_exports3.literal("tinycloud.encryption/decrypt")
|
|
28858
28893
|
}).strict();
|
|
28859
28894
|
var unifiedCapabilitySchema3 = external_exports3.union([unifiedKvCapabilitySchema3, unifiedEncryptionCapabilitySchema3]);
|
|
@@ -28862,6 +28897,7 @@ var unifiedContentSourceSchema3 = external_exports3.object({
|
|
|
28862
28897
|
kvResource: unifiedResourceSchema3,
|
|
28863
28898
|
selector: external_exports3.union([external_exports3.literal("exact"), external_exports3.literal("prefix")]),
|
|
28864
28899
|
encryptionNetwork: unifiedEncryptionNetworkSchema3,
|
|
28900
|
+
encryptionKeyPath: unifiedEncryptionKeyPathSchema3.optional(),
|
|
28865
28901
|
encryptedSymmetricKeyDigestHex: external_exports3.string().regex(/^[0-9a-f]{64}$/),
|
|
28866
28902
|
keyVersion: external_exports3.number().int().positive(),
|
|
28867
28903
|
mode: external_exports3.union([external_exports3.literal("mutable"), external_exports3.literal("immutable")]),
|
|
@@ -28979,22 +29015,71 @@ function validateV3Invariants3(value, ctx) {
|
|
|
28979
29015
|
if (value.attestedEnforcerBinding.nodeAudience !== value.target.nodeAudience || value.attestedEnforcerBinding.signature.signerDid !== value.attestedEnforcerBinding.nodeAudience || Date.parse(value.attestedEnforcerBinding.expiresAt) < Date.parse(value.expiry)) {
|
|
28980
29016
|
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["attestedEnforcerBinding"], message: "enforcer binding does not cover the target and share lifetime" });
|
|
28981
29017
|
}
|
|
28982
|
-
|
|
29018
|
+
const encryptionKeyPath = value.contentSource.encryptionKeyPath;
|
|
29019
|
+
if (encryptionKeyPath !== void 0 && encryptionKeyPath !== `share/${value.contentSource.shareId}`) {
|
|
29020
|
+
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["contentSource", "encryptionKeyPath"], message: "share key path must name this share" });
|
|
29021
|
+
}
|
|
29022
|
+
if (value.policy.contentSource.shareId !== value.contentSource.shareId || value.policy.contentSource.kvResource !== value.contentSource.kvResource || value.policy.contentSource.selector !== value.contentSource.selector || value.policy.contentSource.encryptionNetwork !== value.encryptionNetwork || value.policy.contentSource.encryptionKeyPath !== encryptionKeyPath || value.policy.contentSource.encryptedSymmetricKeyDigestHex !== value.contentSource.encryptedSymmetricKeyDigestHex || value.policy.contentSource.keyVersion !== value.contentSource.keyVersion || value.policy.contentSource.mode !== value.contentSource.mode || value.policy.contentSource.initialCiphertextDigestHex !== value.contentSource.initialCiphertextDigestHex) {
|
|
28983
29023
|
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["contentSource"], message: "content source is not bound to policy" });
|
|
28984
29024
|
}
|
|
28985
29025
|
if (value.policy.signature.suite !== "Ed25519" || value.policy.signature.signerDid !== value.policy.ownerDid) {
|
|
28986
29026
|
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["policy", "signature"], message: "policy must be signed by its owner" });
|
|
28987
29027
|
}
|
|
28988
29028
|
const kvCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "kv");
|
|
28989
|
-
const
|
|
29029
|
+
const expectedEncryptionResource = encryptionKeyPath === void 0 ? value.encryptionNetwork : `${value.encryptionNetwork}/${encryptionKeyPath}`;
|
|
29030
|
+
const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === expectedEncryptionResource && capability.action === "tinycloud.encryption/decrypt");
|
|
28990
29031
|
if (value.policy.capabilityCeiling.length !== 2 || kvCapabilities.length !== 1 || decryptCapabilities.length !== 1 || kvCapabilities[0]?.resource !== value.contentSource.kvResource || kvCapabilities[0]?.selector !== value.contentSource.selector) {
|
|
28991
|
-
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain exact decrypt
|
|
29032
|
+
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain the exact decrypt resource" });
|
|
28992
29033
|
}
|
|
28993
29034
|
}
|
|
28994
29035
|
var unsignedShareEnvelopeV3Schema3 = unsignedShareEnvelopeV3BaseSchema3.superRefine(validateV3Invariants3);
|
|
28995
29036
|
var shareEnvelopeV3Schema3 = unsignedShareEnvelopeV3BaseSchema3.extend({ signature: signatureSchema3 }).strict().superRefine(validateV3Invariants3);
|
|
28996
29037
|
var ENVELOPE_AAD_LABEL4 = "tinycloud-share-envelope-v1";
|
|
29038
|
+
var SEALED_BLOB_VERSION2 = 1;
|
|
28997
29039
|
var AAD4 = utf8Bytes4(ENVELOPE_AAD_LABEL4);
|
|
29040
|
+
var KEY_LENGTH2 = 32;
|
|
29041
|
+
var NONCE_LENGTH2 = 12;
|
|
29042
|
+
var TAG_LENGTH2 = 16;
|
|
29043
|
+
var HEADER_LENGTH2 = 1;
|
|
29044
|
+
function assertKey3(key32) {
|
|
29045
|
+
if (key32.length !== KEY_LENGTH2) {
|
|
29046
|
+
throw new TypeError(`key must be ${KEY_LENGTH2} bytes, got ${key32.length}`);
|
|
29047
|
+
}
|
|
29048
|
+
}
|
|
29049
|
+
async function importAesKey2(key32, usage3) {
|
|
29050
|
+
assertKey3(key32);
|
|
29051
|
+
return globalThis.crypto.subtle.importKey(
|
|
29052
|
+
"raw",
|
|
29053
|
+
key32,
|
|
29054
|
+
"AES-GCM",
|
|
29055
|
+
false,
|
|
29056
|
+
[usage3]
|
|
29057
|
+
);
|
|
29058
|
+
}
|
|
29059
|
+
async function open8(blob, key32) {
|
|
29060
|
+
if (blob.length < HEADER_LENGTH2 + NONCE_LENGTH2 + TAG_LENGTH2) {
|
|
29061
|
+
throw new TypeError(`sealed blob too short: ${blob.length} bytes`);
|
|
29062
|
+
}
|
|
29063
|
+
if (blob[0] !== SEALED_BLOB_VERSION2) {
|
|
29064
|
+
throw new TypeError(`unknown sealed blob version: ${blob[0]}`);
|
|
29065
|
+
}
|
|
29066
|
+
const nonce = blob.subarray(HEADER_LENGTH2, HEADER_LENGTH2 + NONCE_LENGTH2);
|
|
29067
|
+
const ciphertext = blob.subarray(HEADER_LENGTH2 + NONCE_LENGTH2);
|
|
29068
|
+
return decryptEnvelope2(nonce, ciphertext, key32);
|
|
29069
|
+
}
|
|
29070
|
+
async function decryptEnvelope2(nonce, ciphertext, key32) {
|
|
29071
|
+
if (nonce.length !== NONCE_LENGTH2) {
|
|
29072
|
+
throw new TypeError(`nonce must be ${NONCE_LENGTH2} bytes, got ${nonce.length}`);
|
|
29073
|
+
}
|
|
29074
|
+
const key = await importAesKey2(key32, "decrypt");
|
|
29075
|
+
return new Uint8Array(
|
|
29076
|
+
await globalThis.crypto.subtle.decrypt(
|
|
29077
|
+
{ name: "AES-GCM", iv: nonce, additionalData: AAD4 },
|
|
29078
|
+
key,
|
|
29079
|
+
ciphertext
|
|
29080
|
+
)
|
|
29081
|
+
);
|
|
29082
|
+
}
|
|
28998
29083
|
function isPlainRecord2(value) {
|
|
28999
29084
|
const proto = Object.getPrototypeOf(value);
|
|
29000
29085
|
return proto === Object.prototype || proto === null;
|
|
@@ -29109,6 +29194,7 @@ function equal(left, right) {
|
|
|
29109
29194
|
return left.length === right.length && left.every((byte, index) => byte === right[index]);
|
|
29110
29195
|
}
|
|
29111
29196
|
var ED25519_VERIFY_OPTS2 = { zip215: false };
|
|
29197
|
+
var ENVELOPE_V2_SIGNATURE_DOMAIN = "xyz.tinycloud.share/envelope/v2\0";
|
|
29112
29198
|
var ENVELOPE_V3_SIGNATURE_DOMAIN = "xyz.tinycloud.share/envelope/v3\0";
|
|
29113
29199
|
var POLICY_V1_SIGNATURE_DOMAIN = "xyz.tinycloud.policy/policy/v1\0";
|
|
29114
29200
|
var POLICY_V2_SIGNATURE_DOMAIN = "xyz.tinycloud.policy/policy/v2\0";
|
|
@@ -29116,6 +29202,14 @@ var CONTENT_SOURCE_V1_DOMAIN = "xyz.tinycloud.policy/ContentSource/v1\0";
|
|
|
29116
29202
|
var POLICY_CAPABILITY_V1_DOMAIN = "xyz.tinycloud.policy/PolicyCapability/v1\0";
|
|
29117
29203
|
var NATIVE_PROJECTION_V1_DOMAIN = "xyz.tinycloud.policy/NativeProjection/v1\0";
|
|
29118
29204
|
var ATTESTED_ENFORCER_V2_DOMAIN = "xyz.tinycloud.policy/AttestedEnforcerBinding/v2\0";
|
|
29205
|
+
function signingBytesV2(unsigned) {
|
|
29206
|
+
const domain = utf8Bytes4(ENVELOPE_V2_SIGNATURE_DOMAIN);
|
|
29207
|
+
const body = utf8Bytes4(canonicalize2(unsigned));
|
|
29208
|
+
const bytes3 = new Uint8Array(domain.length + body.length);
|
|
29209
|
+
bytes3.set(domain);
|
|
29210
|
+
bytes3.set(body, domain.length);
|
|
29211
|
+
return bytes3;
|
|
29212
|
+
}
|
|
29119
29213
|
function signingBytesV3(unsigned) {
|
|
29120
29214
|
const domain = utf8Bytes4(ENVELOPE_V3_SIGNATURE_DOMAIN);
|
|
29121
29215
|
const body = utf8Bytes4(canonicalize2(unsigned));
|
|
@@ -29124,6 +29218,17 @@ function signingBytesV3(unsigned) {
|
|
|
29124
29218
|
bytes3.set(body, domain.length);
|
|
29125
29219
|
return bytes3;
|
|
29126
29220
|
}
|
|
29221
|
+
function verifyEnvelopeV2SignatureOnly(envelope) {
|
|
29222
|
+
const parsed = shareEnvelopeV2Schema3.parse(envelope);
|
|
29223
|
+
const { signature, ...unsigned } = parsed;
|
|
29224
|
+
return ed25519.verify(fromBase64Url4(signature.value), signingBytesV2(unsigned), ed25519PublicKeyFromDidKey2(signature.signerDid), ED25519_VERIFY_OPTS2);
|
|
29225
|
+
}
|
|
29226
|
+
async function verifyEnvelopeV2(envelope, options) {
|
|
29227
|
+
const parsed = shareEnvelopeV2Schema3.parse(envelope);
|
|
29228
|
+
if (parsed.signature.signerDid !== options.expectedSignerDid || !verifyEnvelopeV2SignatureOnly(parsed)) return false;
|
|
29229
|
+
if (parsed.authorizationTarget.kind === "policy") return await computeCid2(fromBase64Url4(parsed.authorizationTarget.policyBytes)) === parsed.authorizationTarget.policyCid;
|
|
29230
|
+
return true;
|
|
29231
|
+
}
|
|
29127
29232
|
function verifyEnvelopeV3SignatureOnly(envelope) {
|
|
29128
29233
|
const parsed = shareEnvelopeV3Schema3.parse(envelope);
|
|
29129
29234
|
const { signature, ...unsigned } = parsed;
|
|
@@ -29210,6 +29315,52 @@ function base32Lower(bytes3) {
|
|
|
29210
29315
|
if (bits > 0) output2 += alphabet[buffer << 5 - bits & 31];
|
|
29211
29316
|
return output2;
|
|
29212
29317
|
}
|
|
29318
|
+
var KEY_LENGTH22 = 32;
|
|
29319
|
+
function assertCanonicalCid(cidString) {
|
|
29320
|
+
const cid2 = CID4.parse(cidString);
|
|
29321
|
+
if (cid2.version !== 1 || cid2.code !== code4 || cid2.multihash.code !== 18 || // sha2-256 (0x12) only, at the link layer too
|
|
29322
|
+
cid2.toString() !== cidString) {
|
|
29323
|
+
throw new TypeError(`not a canonical CIDv1 raw sha2-256 base32 CID: ${cidString}`);
|
|
29324
|
+
}
|
|
29325
|
+
}
|
|
29326
|
+
function parseShareUrl(url, options = {}) {
|
|
29327
|
+
const parsed = new URL(url);
|
|
29328
|
+
if (parsed.protocol !== "https:") {
|
|
29329
|
+
throw new TypeError(`share URL must be https, got ${parsed.protocol}`);
|
|
29330
|
+
}
|
|
29331
|
+
if (parsed.username !== "" || parsed.password !== "") {
|
|
29332
|
+
throw new TypeError("share URL must not carry userinfo");
|
|
29333
|
+
}
|
|
29334
|
+
if (parsed.search !== "") {
|
|
29335
|
+
throw new TypeError("share URL must not have a query string");
|
|
29336
|
+
}
|
|
29337
|
+
if (options.expectedOrigin !== void 0) {
|
|
29338
|
+
if (!isCanonicalHttpsOrigin3(options.expectedOrigin)) {
|
|
29339
|
+
throw new TypeError(
|
|
29340
|
+
`expectedOrigin must be a canonical https origin, got ${options.expectedOrigin}`
|
|
29341
|
+
);
|
|
29342
|
+
}
|
|
29343
|
+
if (parsed.origin !== options.expectedOrigin) {
|
|
29344
|
+
throw new TypeError(
|
|
29345
|
+
`share URL origin ${parsed.origin} does not match expected ${options.expectedOrigin}`
|
|
29346
|
+
);
|
|
29347
|
+
}
|
|
29348
|
+
}
|
|
29349
|
+
const match = /^\/s\/([a-z2-7]+)$/.exec(parsed.pathname);
|
|
29350
|
+
if (!match || match[1] === void 0) {
|
|
29351
|
+
throw new TypeError(`not a share URL path: ${parsed.pathname}`);
|
|
29352
|
+
}
|
|
29353
|
+
const ciphertextCid = match[1];
|
|
29354
|
+
assertCanonicalCid(ciphertextCid);
|
|
29355
|
+
if (!parsed.hash.startsWith("#k=")) {
|
|
29356
|
+
throw new TypeError("share URL is missing the #k= key fragment");
|
|
29357
|
+
}
|
|
29358
|
+
const key32 = fromBase64Url4(parsed.hash.slice("#k=".length));
|
|
29359
|
+
if (key32.length !== KEY_LENGTH22) {
|
|
29360
|
+
throw new TypeError(`fragment key must be ${KEY_LENGTH22} bytes, got ${key32.length}`);
|
|
29361
|
+
}
|
|
29362
|
+
return { ciphertextCid, key32 };
|
|
29363
|
+
}
|
|
29213
29364
|
var ARTIFACT_ROOT2 = "artifacts";
|
|
29214
29365
|
function parseArtifactVersionPath2(path) {
|
|
29215
29366
|
const match = /^artifacts\/([a-z0-9][a-z0-9_-]{2,62})\/versions\/([1-9][0-9]{0,14})\/([^/]+)$/.exec(path);
|
|
@@ -29286,6 +29437,7 @@ function verifyArtifactShare(envelope, options) {
|
|
|
29286
29437
|
}
|
|
29287
29438
|
var SHARE_RESULT_VERSION = 1;
|
|
29288
29439
|
var DEFAULT_MAX_SEALED_BLOB_BYTES = 100 * 1024 * 1024 + 29;
|
|
29440
|
+
var DEFAULT_MAX_CONTENT_BLOB_BYTES = DEFAULT_MAX_SEALED_BLOB_BYTES;
|
|
29289
29441
|
var ShareReceiveError = class extends Error {
|
|
29290
29442
|
code;
|
|
29291
29443
|
details;
|
|
@@ -29300,7 +29452,7 @@ var ShareReceiveError = class extends Error {
|
|
|
29300
29452
|
}
|
|
29301
29453
|
};
|
|
29302
29454
|
function metadataFor(envelope, origin) {
|
|
29303
|
-
const kind = envelope.recipientMatcher.kind === "recipientDid" ? "recipientDid" : envelope.recipientMatcher.kind === "exactEmail" ? "email" : "emailDomain";
|
|
29455
|
+
const kind = envelope.recipientMatcher.kind === "recipientDid" ? "recipientDid" : envelope.recipientMatcher.kind === "exactEmail" ? "email" : envelope.recipientMatcher.kind === "emailDomain" ? "emailDomain" : "bearer";
|
|
29304
29456
|
return {
|
|
29305
29457
|
protocol: "tinycloud-share",
|
|
29306
29458
|
version: 1,
|
|
@@ -29315,7 +29467,7 @@ function metadataFor(envelope, origin) {
|
|
|
29315
29467
|
...envelope.display.filename === void 0 ? {} : { filename: envelope.display.filename },
|
|
29316
29468
|
...envelope.display.mode === void 0 ? {} : { mode: envelope.display.mode }
|
|
29317
29469
|
},
|
|
29318
|
-
...envelope.
|
|
29470
|
+
...envelope.version === 3 && envelope.artifactPin !== void 0 ? { artifact: { id: envelope.artifactPin.artifactId, version: envelope.artifactPin.version } } : {}
|
|
29319
29471
|
};
|
|
29320
29472
|
}
|
|
29321
29473
|
function receiveErrorFor(error) {
|
|
@@ -29413,14 +29565,165 @@ async function resolvePolicyShare(link4, options) {
|
|
|
29413
29565
|
options.onResolvedAddressedEnvelope?.(envelope, cid2);
|
|
29414
29566
|
return { envelope, origin: url.origin, cid: cid2 };
|
|
29415
29567
|
}
|
|
29568
|
+
async function resolveLegacyV2(link4, options) {
|
|
29569
|
+
let parsed;
|
|
29570
|
+
try {
|
|
29571
|
+
parsed = parseShareUrl(link4, { ...options.expectedOrigin === void 0 ? {} : { expectedOrigin: options.expectedOrigin } });
|
|
29572
|
+
} catch {
|
|
29573
|
+
throw new ShareReceiveError("invalid-link", "legacy share link format is invalid");
|
|
29574
|
+
}
|
|
29575
|
+
try {
|
|
29576
|
+
options.signal?.throwIfAborted();
|
|
29577
|
+
const origin = new URL(link4).origin;
|
|
29578
|
+
const limit = options.maxSealedBlobBytes ?? DEFAULT_MAX_SEALED_BLOB_BYTES;
|
|
29579
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope limit is invalid");
|
|
29580
|
+
let sealed;
|
|
29581
|
+
if (options.fetchBlob !== void 0) {
|
|
29582
|
+
try {
|
|
29583
|
+
sealed = await options.fetchBlob({ origin, cid: parsed.ciphertextCid, ...options.signal === void 0 ? {} : { signal: options.signal } });
|
|
29584
|
+
} catch {
|
|
29585
|
+
throw new ShareReceiveError("fetch-failed", "legacy registry unavailable");
|
|
29586
|
+
}
|
|
29587
|
+
} else {
|
|
29588
|
+
if (options.registryBaseUrl === void 0) throw new ShareReceiveError("fetch-failed", "legacy registry is required");
|
|
29589
|
+
const response = await (options.fetch ?? globalThis.fetch)(`${options.registryBaseUrl.replace(/\/+$/, "")}/ipfs/${parsed.ciphertextCid}?format=raw`, {
|
|
29590
|
+
headers: { accept: "application/vnd.ipld.raw" },
|
|
29591
|
+
redirect: "error",
|
|
29592
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
29593
|
+
});
|
|
29594
|
+
if (!response.ok) throw new ShareReceiveError("fetch-failed", "legacy registry unavailable");
|
|
29595
|
+
if (Number(response.headers.get("content-length")) > limit) throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope exceeds the size limit");
|
|
29596
|
+
const reader = response.body?.getReader();
|
|
29597
|
+
if (reader === void 0) throw new ShareReceiveError("fetch-failed", "legacy registry returned no body");
|
|
29598
|
+
const chunks = [];
|
|
29599
|
+
let length32 = 0;
|
|
29600
|
+
for (; ; ) {
|
|
29601
|
+
const part = await reader.read();
|
|
29602
|
+
if (part.done) break;
|
|
29603
|
+
length32 += part.value.length;
|
|
29604
|
+
if (length32 > limit) {
|
|
29605
|
+
await reader.cancel();
|
|
29606
|
+
throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope exceeds the size limit");
|
|
29607
|
+
}
|
|
29608
|
+
chunks.push(part.value);
|
|
29609
|
+
}
|
|
29610
|
+
sealed = new Uint8Array(length32);
|
|
29611
|
+
let offset = 0;
|
|
29612
|
+
for (const chunk of chunks) {
|
|
29613
|
+
sealed.set(chunk, offset);
|
|
29614
|
+
offset += chunk.length;
|
|
29615
|
+
}
|
|
29616
|
+
}
|
|
29617
|
+
options.signal?.throwIfAborted();
|
|
29618
|
+
if (!(sealed instanceof Uint8Array) || sealed.length > limit) throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope exceeds the size limit");
|
|
29619
|
+
if (await computeCid2(sealed) !== parsed.ciphertextCid) throw new ShareReceiveError("cid-mismatch", "legacy envelope CID does not match");
|
|
29620
|
+
let plaintext;
|
|
29621
|
+
try {
|
|
29622
|
+
plaintext = await open8(sealed, parsed.key32);
|
|
29623
|
+
} catch {
|
|
29624
|
+
throw new ShareReceiveError("decrypt-failed", "legacy envelope could not be opened");
|
|
29625
|
+
}
|
|
29626
|
+
let envelope;
|
|
29627
|
+
try {
|
|
29628
|
+
const encoded = new TextDecoder("utf-8", { fatal: true }).decode(plaintext);
|
|
29629
|
+
const value = JSON.parse(encoded);
|
|
29630
|
+
if (canonicalize2(value) !== encoded) throw new Error("non-canonical envelope");
|
|
29631
|
+
envelope = shareEnvelopeV2Schema3.parse(value);
|
|
29632
|
+
} catch {
|
|
29633
|
+
throw new ShareReceiveError("envelope-invalid", "legacy envelope is invalid");
|
|
29634
|
+
}
|
|
29635
|
+
return { envelope, origin, cid: parsed.ciphertextCid };
|
|
29636
|
+
} finally {
|
|
29637
|
+
parsed.key32.fill(0);
|
|
29638
|
+
}
|
|
29639
|
+
}
|
|
29640
|
+
function evidenceMatchesV2(envelope, evidence) {
|
|
29641
|
+
const actions = new Set(evidence.actions.map((action) => action === "read" ? "tinycloud.kv/get" : action === "list" ? "tinycloud.kv/list" : action === "edit" ? "tinycloud.kv/put" : action));
|
|
29642
|
+
const resourceCovers = evidence.resource.kind === "exact" ? envelope.resource.kind === "exact" && evidence.resource.path === envelope.resource.path : envelope.resource.path === evidence.resource.path || envelope.resource.path.startsWith(evidence.resource.path.endsWith("/") ? evidence.resource.path : `${evidence.resource.path}/`);
|
|
29643
|
+
return envelope.authorizationTarget.kind === "policy" && evidence.policyCid === envelope.authorizationTarget.policyCid && evidence.signerDid === envelope.signature.signerDid && evidence.registrationCid.length > 0 && evidence.shareId === envelope.shareId && canonicalize2(evidence.recipientMatcher) === canonicalize2(envelope.recipientMatcher) && canonicalize2(evidence.target) === canonicalize2(envelope.target) && resourceCovers && envelope.actions.every((action) => actions.has(action === "read" ? "tinycloud.kv/get" : action === "list" ? "tinycloud.kv/list" : "tinycloud.kv/put")) && canonicalize2(evidence.contentSource) === canonicalize2(envelope.contentSource) && evidence.contentSourceDigest === envelope.contentSourceDigest && evidence.delegationCid === envelope.delegationCid && evidence.authorityMaterialHandle === envelope.authorityMaterialHandle && evidence.authorityMaterialDigest === envelope.authorityMaterialDigest && evidence.expiresAt === envelope.expiry && Number.isFinite(Date.parse(evidence.expiresAt)) && Date.parse(evidence.expiresAt) === Date.parse(envelope.expiry);
|
|
29644
|
+
}
|
|
29645
|
+
async function verifyLegacyV2(envelope, options) {
|
|
29646
|
+
try {
|
|
29647
|
+
if (!await verifyEnvelopeV2(envelope, { expectedSignerDid: envelope.signature.signerDid })) throw new Error("signature");
|
|
29648
|
+
} catch {
|
|
29649
|
+
throw new ShareReceiveError("signature-invalid", "legacy envelope signature is invalid");
|
|
29650
|
+
}
|
|
29651
|
+
const expiry = Date.parse(envelope.expiry);
|
|
29652
|
+
if (!Number.isFinite(expiry)) throw new ShareReceiveError("envelope-invalid", "legacy share expiry is invalid");
|
|
29653
|
+
if (expiry <= (options.now?.() ?? Date.now())) throw new ShareReceiveError("expired", "legacy share has expired", { expiresAt: envelope.expiry });
|
|
29654
|
+
if (envelope.authorizationTarget.kind === "policy") {
|
|
29655
|
+
if (options.trustedPolicyAuthority === void 0) throw new ShareReceiveError("envelope-invalid", "a legacy policy authority is required");
|
|
29656
|
+
let evidence;
|
|
29657
|
+
try {
|
|
29658
|
+
evidence = await options.trustedPolicyAuthority.resolve({ policyCid: envelope.authorizationTarget.policyCid, envelope });
|
|
29659
|
+
} catch {
|
|
29660
|
+
}
|
|
29661
|
+
if (evidence === void 0 || !evidenceMatchesV2(envelope, evidence)) throw new ShareReceiveError("capability-invalid", "legacy policy evidence does not match");
|
|
29662
|
+
if (options.trustedSignerDid !== void 0 && options.trustedSignerDid !== evidence.signerDid) throw new ShareReceiveError("signature-invalid", "legacy signer is not trusted");
|
|
29663
|
+
} else if (options.trustedSignerDid === void 0 || options.trustedSignerDid !== envelope.signature.signerDid || envelope.authorizationTarget.kind === "recipientDid" && envelope.recipientMatcher.kind === "recipientDid" && envelope.authorizationTarget.did !== envelope.recipientMatcher.value) {
|
|
29664
|
+
throw new ShareReceiveError("capability-invalid", "legacy recipient authority does not match");
|
|
29665
|
+
}
|
|
29666
|
+
}
|
|
29416
29667
|
async function inspectShare(link4, options = {}) {
|
|
29668
|
+
if (isLegacyV2Link(link4)) {
|
|
29669
|
+
const legacy = await resolveLegacyV2(link4, options);
|
|
29670
|
+
await verifyLegacyV2(legacy.envelope, options);
|
|
29671
|
+
options.onResolvedAddressedEnvelope?.(legacy.envelope, legacy.cid);
|
|
29672
|
+
return { metadata: metadataFor(legacy.envelope, legacy.origin), link: { origin: legacy.origin, cid: legacy.cid, kind: "compact" } };
|
|
29673
|
+
}
|
|
29417
29674
|
const resolved = await resolvePolicyShare(link4, options);
|
|
29418
29675
|
return { metadata: metadataFor(resolved.envelope, resolved.origin), link: { origin: resolved.origin, cid: resolved.cid, kind: "policy" } };
|
|
29419
29676
|
}
|
|
29420
29677
|
async function receiveShare(link4, options = {}) {
|
|
29678
|
+
if (isLegacyV2Link(link4)) return receiveLegacyV2(link4, options);
|
|
29421
29679
|
const resolved = await resolvePolicyShare(link4, options);
|
|
29422
29680
|
return { state: "authorization-required", method: resolved.envelope.recipientMatcher.kind === "recipientDid" ? "openkey-device" : "email-claim" };
|
|
29423
29681
|
}
|
|
29682
|
+
function isLegacyV2Link(link4) {
|
|
29683
|
+
try {
|
|
29684
|
+
const url = new URL(link4);
|
|
29685
|
+
return /^\/s\/bafkrei[a-z2-7]{52}$/.test(url.pathname) && url.hash.startsWith("#k=");
|
|
29686
|
+
} catch {
|
|
29687
|
+
return false;
|
|
29688
|
+
}
|
|
29689
|
+
}
|
|
29690
|
+
async function receiveLegacyV2(link4, options) {
|
|
29691
|
+
const resolved = await resolveLegacyV2(link4, options);
|
|
29692
|
+
const { envelope } = resolved;
|
|
29693
|
+
await verifyLegacyV2(envelope, options);
|
|
29694
|
+
options.onResolvedAddressedEnvelope?.(envelope, resolved.cid);
|
|
29695
|
+
const method = envelope.recipientMatcher.kind === "recipientDid" ? "openkey-device" : "email-claim";
|
|
29696
|
+
if (options.authorization === void 0) return { state: "authorization-required", method };
|
|
29697
|
+
const result = options.authorizationResumeToken === void 0 ? await options.authorization.begin({ envelope, method }) : await options.authorization.resume({
|
|
29698
|
+
envelope,
|
|
29699
|
+
method,
|
|
29700
|
+
resumeToken: options.authorizationResumeToken,
|
|
29701
|
+
...options.authorizationProof === void 0 ? {} : { proof: options.authorizationProof }
|
|
29702
|
+
});
|
|
29703
|
+
if (result.state === "authorization-required") return result;
|
|
29704
|
+
if (result.state === "denied") throw new ShareReceiveError("authorization-denied", "legacy share authorization was denied");
|
|
29705
|
+
if (result.value === null || typeof result.value !== "object" || !(result.value.bytes instanceof Uint8Array) || result.value.proof === void 0 || options.authorization.verifyResult === void 0) {
|
|
29706
|
+
throw new ShareReceiveError("content-integrity-failed", "legacy content lacks a verifiable proof");
|
|
29707
|
+
}
|
|
29708
|
+
const authorized = result.value;
|
|
29709
|
+
try {
|
|
29710
|
+
if (!await options.authorization.verifyResult({ envelope, value: authorized, proof: authorized.proof })) throw new Error("proof");
|
|
29711
|
+
} catch {
|
|
29712
|
+
throw new ShareReceiveError("content-integrity-failed", "legacy content proof is invalid");
|
|
29713
|
+
}
|
|
29714
|
+
const bytes3 = authorized.bytes.slice();
|
|
29715
|
+
if (bytes3.length > (options.maxContentBlobBytes ?? DEFAULT_MAX_CONTENT_BLOB_BYTES)) throw new ShareReceiveError("max-bytes-exceeded", "legacy content exceeds the size limit");
|
|
29716
|
+
const bodyDigest = toBase64Url2(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes3)));
|
|
29717
|
+
if (authorized.bodyDigest !== bodyDigest || authorized.contentSourceDigest !== envelope.contentSourceDigest || authorized.binding.shareId !== envelope.shareId || authorized.binding.delegationCid !== envelope.delegationCid || authorized.binding.authorityMaterialHandle !== envelope.authorityMaterialHandle || authorized.binding.authorityMaterialDigest !== envelope.authorityMaterialDigest || authorized.binding.resource.kind !== envelope.resource.kind || authorized.binding.resource.path !== envelope.resource.path || authorized.binding.action !== void 0 && !envelope.actions.some((action) => action === authorized.binding.action || `tinycloud.kv/${action}` === authorized.binding.action) || envelope.metadata.byteLength !== void 0 && envelope.metadata.byteLength !== bytes3.length) {
|
|
29718
|
+
throw new ShareReceiveError("content-integrity-failed", "legacy content binding does not match");
|
|
29719
|
+
}
|
|
29720
|
+
let text;
|
|
29721
|
+
try {
|
|
29722
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(bytes3);
|
|
29723
|
+
} catch {
|
|
29724
|
+
}
|
|
29725
|
+
return { metadata: metadataFor(envelope, resolved.origin), link: { origin: resolved.origin, cid: resolved.cid, kind: "compact" }, bytes: bytes3, ...text === void 0 ? {} : { text } };
|
|
29726
|
+
}
|
|
29424
29727
|
var SHARE_CONTENT_LIMIT = 100 * 1024 * 1024;
|
|
29425
29728
|
var SHARE_PUBLISH_RESULT_VERSION = 1;
|
|
29426
29729
|
var DEFAULT_SHARE_LIFETIME_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -29908,7 +30211,10 @@ async function publishTargetShare(input) {
|
|
|
29908
30211
|
expiresAt,
|
|
29909
30212
|
expiryWasExplicit: input.expiryWasExplicit ?? input.expiresAt !== void 0,
|
|
29910
30213
|
origin: input.origin,
|
|
29911
|
-
...input.notify === void 0 ? {} : { notify: input.notify }
|
|
30214
|
+
...input.notify === void 0 ? {} : { notify: input.notify },
|
|
30215
|
+
...input.credentialDescriptor === void 0 ? {} : { credentialDescriptor: input.credentialDescriptor },
|
|
30216
|
+
...input.issuerDid === void 0 ? {} : { issuerDid: input.issuerDid },
|
|
30217
|
+
...input.issuerKid === void 0 ? {} : { issuerKid: input.issuerKid }
|
|
29912
30218
|
});
|
|
29913
30219
|
}
|
|
29914
30220
|
var textEncoder = new TextEncoder();
|
|
@@ -30642,7 +30948,53 @@ function parseNativeShareUrl(value) {
|
|
|
30642
30948
|
return token;
|
|
30643
30949
|
}
|
|
30644
30950
|
|
|
30951
|
+
// src/share/errors.ts
|
|
30952
|
+
var SharePublishAuthorityError = class extends Error {
|
|
30953
|
+
constructor(failure, options) {
|
|
30954
|
+
super(failure.kind, options);
|
|
30955
|
+
this.failure = failure;
|
|
30956
|
+
this.name = "SharePublishAuthorityError";
|
|
30957
|
+
}
|
|
30958
|
+
};
|
|
30959
|
+
var ShareHistoryRetryError = class extends Error {
|
|
30960
|
+
constructor(profile) {
|
|
30961
|
+
super("sender history changed during the operation");
|
|
30962
|
+
this.profile = profile;
|
|
30963
|
+
this.name = "ShareHistoryRetryError";
|
|
30964
|
+
}
|
|
30965
|
+
code = "SHARE_HISTORY_RETRY";
|
|
30966
|
+
};
|
|
30967
|
+
var ShareHistoryUnreadableError = class extends Error {
|
|
30968
|
+
constructor() {
|
|
30969
|
+
super("share history is unavailable");
|
|
30970
|
+
this.name = "ShareHistoryUnreadableError";
|
|
30971
|
+
}
|
|
30972
|
+
};
|
|
30973
|
+
var ShareHistoryReadError = class extends Error {
|
|
30974
|
+
constructor() {
|
|
30975
|
+
super("share history could not be read");
|
|
30976
|
+
this.name = "ShareHistoryReadError";
|
|
30977
|
+
}
|
|
30978
|
+
};
|
|
30979
|
+
var HISTORY_WRITE_ERROR_CODES = { EACCES: true, EPERM: true, EROFS: true, ENOSPC: true, EDQUOT: true, EIO: true };
|
|
30980
|
+
function shareHistoryReason(error) {
|
|
30981
|
+
if (error instanceof ShareHistoryUnreadableError) return "HISTORY_UNREADABLE";
|
|
30982
|
+
if (error instanceof ShareHistoryReadError) return "HISTORY_READ_FAILED";
|
|
30983
|
+
const code5 = typeof error === "object" && error !== null && "code" in error ? error.code : void 0;
|
|
30984
|
+
return typeof code5 === "string" && HISTORY_WRITE_ERROR_CODES[code5] === true ? "HISTORY_WRITE_FAILED" : "HISTORY_UNAVAILABLE";
|
|
30985
|
+
}
|
|
30986
|
+
|
|
30645
30987
|
// src/share/adapters.ts
|
|
30988
|
+
init_storage2();
|
|
30989
|
+
init_profiles();
|
|
30990
|
+
init_constants();
|
|
30991
|
+
init_errors();
|
|
30992
|
+
init_session_expired();
|
|
30993
|
+
import { chmod as chmod4, link as link2, open as open9, readFile as readFile8, stat as stat4, unlink as unlink4 } from "fs/promises";
|
|
30994
|
+
import { dirname as dirname5, join as join12 } from "path";
|
|
30995
|
+
import { createHash } from "crypto";
|
|
30996
|
+
import { writeJsonAtomic as writeJsonAtomic2 } from "@tinycloud/operations/state";
|
|
30997
|
+
import { withoutBearer as withoutBearer2 } from "@tinycloud/operations/delegation-binding";
|
|
30646
30998
|
import {
|
|
30647
30999
|
LocationRecordValidationError,
|
|
30648
31000
|
LocationRegistryHttpError,
|
|
@@ -30855,7 +31207,7 @@ function createEncryptedProfileHistory(profileName2, sessionSigner) {
|
|
|
30855
31207
|
await link2(found.file, target);
|
|
30856
31208
|
await chmod4(target, PRIVATE_HISTORY_MODE);
|
|
30857
31209
|
await unlink4(found.file);
|
|
30858
|
-
const directory = await
|
|
31210
|
+
const directory = await open9(dirname5(found.file), "r");
|
|
30859
31211
|
try {
|
|
30860
31212
|
await directory.sync();
|
|
30861
31213
|
} finally {
|
|
@@ -31228,8 +31580,8 @@ async function callbackFlow(did, options = {}, env = {}) {
|
|
|
31228
31580
|
}
|
|
31229
31581
|
if (openBrowser) {
|
|
31230
31582
|
try {
|
|
31231
|
-
const
|
|
31232
|
-
await
|
|
31583
|
+
const open16 = (await import("open")).default;
|
|
31584
|
+
await open16(authUrl);
|
|
31233
31585
|
} catch {
|
|
31234
31586
|
settle({ error: new Error("Failed to open browser") });
|
|
31235
31587
|
return;
|
|
@@ -32582,7 +32934,7 @@ init_errors();
|
|
|
32582
32934
|
import { randomBytes as randomBytes6, timingSafeEqual } from "crypto";
|
|
32583
32935
|
import { createServer as createServer2 } from "http";
|
|
32584
32936
|
import { createInterface as createInterface2 } from "readline";
|
|
32585
|
-
import
|
|
32937
|
+
import open10 from "open";
|
|
32586
32938
|
init_owner_did();
|
|
32587
32939
|
var MAX_BODY_BYTES = 1024 * 1024;
|
|
32588
32940
|
var RESULT_FIELDS = ["address", "permissions", "protocolVersion", "selectedActionKeys", "signature", "signedMessage"];
|
|
@@ -32787,7 +33139,7 @@ async function startPreparedOpenKeyHandoff(options) {
|
|
|
32787
33139
|
console.error(`Open this URL in a browser to authenticate: ${url}`);
|
|
32788
33140
|
if (!options.noPopup && !options.paste) {
|
|
32789
33141
|
try {
|
|
32790
|
-
await
|
|
33142
|
+
await open10(url);
|
|
32791
33143
|
} catch {
|
|
32792
33144
|
console.error("Could not open browser. Use the printed URL or paste the signed code.");
|
|
32793
33145
|
}
|
|
@@ -37473,7 +37825,7 @@ init_storage();
|
|
|
37473
37825
|
init_errors();
|
|
37474
37826
|
init_constants();
|
|
37475
37827
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
37476
|
-
import { lstat as lstat8, open as
|
|
37828
|
+
import { lstat as lstat8, open as open11, rename as rename3, rm as rm5 } from "fs/promises";
|
|
37477
37829
|
import { basename as basename2, dirname as dirname7, join as join15 } from "path";
|
|
37478
37830
|
async function validatePrivateOutput(path, label = "output") {
|
|
37479
37831
|
try {
|
|
@@ -37500,7 +37852,7 @@ async function writePrivateOutput(path, value, label = "output") {
|
|
|
37500
37852
|
const temp = join15(parentPath, `.${basename2(path)}.${randomUUID2()}.tmp`);
|
|
37501
37853
|
let created = false;
|
|
37502
37854
|
try {
|
|
37503
|
-
const handle = await
|
|
37855
|
+
const handle = await open11(temp, "wx", PRIVATE_FILE_MODE);
|
|
37504
37856
|
created = true;
|
|
37505
37857
|
try {
|
|
37506
37858
|
await handle.writeFile(value);
|
|
@@ -37515,7 +37867,7 @@ async function writePrivateOutput(path, value, label = "output") {
|
|
|
37515
37867
|
throw new CLIError("ERROR", `Could not write ${label.toLowerCase()} "${path}"${code5}.`, ExitCode.ERROR);
|
|
37516
37868
|
}
|
|
37517
37869
|
try {
|
|
37518
|
-
const parent = await
|
|
37870
|
+
const parent = await open11(parentPath, "r");
|
|
37519
37871
|
try {
|
|
37520
37872
|
await parent.sync();
|
|
37521
37873
|
} finally {
|
|
@@ -38499,7 +38851,7 @@ init_profile_replicas();
|
|
|
38499
38851
|
import { CLI_NAME as CLI_NAME31 } from "@tinycloud/operations/cli-name";
|
|
38500
38852
|
import { randomBytes as randomBytes7 } from "crypto";
|
|
38501
38853
|
import { constants as fsConstants5 } from "fs";
|
|
38502
|
-
import { mkdir as mkdir5, open as
|
|
38854
|
+
import { mkdir as mkdir5, open as open12, readdir as readdir6, rename as rename4, unlink as unlink5, lstat as lstat9 } from "fs/promises";
|
|
38503
38855
|
import { basename as basename3, dirname as dirname8, join as join16, resolve as resolve5 } from "path";
|
|
38504
38856
|
|
|
38505
38857
|
// src/lib/replication-report.ts
|
|
@@ -38966,7 +39318,7 @@ function describeStatus(status) {
|
|
|
38966
39318
|
async function writeFileAtomic(path, bytes) {
|
|
38967
39319
|
const target = resolve5(path);
|
|
38968
39320
|
const temp = join16(dirname8(target), `.${basename3(target)}.${randomBytes7(6).toString("hex")}.tmp`);
|
|
38969
|
-
const handle = await
|
|
39321
|
+
const handle = await open12(temp, fsConstants5.O_CREAT | fsConstants5.O_EXCL | fsConstants5.O_WRONLY, 384);
|
|
38970
39322
|
try {
|
|
38971
39323
|
await handle.writeFile(bytes);
|
|
38972
39324
|
await handle.sync();
|
|
@@ -39015,7 +39367,7 @@ async function withPinnedLocalDirectory(profile, parent, name, action) {
|
|
|
39015
39367
|
});
|
|
39016
39368
|
if (!before) return null;
|
|
39017
39369
|
if (before.isSymbolicLink() || !before.isDirectory()) throw localPathChanged(profile);
|
|
39018
|
-
const handle = await
|
|
39370
|
+
const handle = await open12(path, fsConstants5.O_RDONLY | fsConstants5.O_DIRECTORY | fsConstants5.O_NOFOLLOW).catch((error) => {
|
|
39019
39371
|
if (error.code === "ELOOP" || error.code === "ENOTDIR" || error.code === "ENOENT") throw localPathChanged(profile);
|
|
39020
39372
|
throw error;
|
|
39021
39373
|
});
|
|
@@ -39044,7 +39396,7 @@ async function readReportFile(profile, parent, name) {
|
|
|
39044
39396
|
});
|
|
39045
39397
|
if (!before) return null;
|
|
39046
39398
|
if (before.isSymbolicLink() || !before.isFile()) throw localPathChanged(profile);
|
|
39047
|
-
const handle = await
|
|
39399
|
+
const handle = await open12(path, fsConstants5.O_RDONLY | fsConstants5.O_NOFOLLOW).catch((error) => {
|
|
39048
39400
|
if (error.code === "ELOOP" || error.code === "ENOTDIR" || error.code === "ENOENT") throw localPathChanged(profile);
|
|
39049
39401
|
throw error;
|
|
39050
39402
|
});
|
|
@@ -39422,12 +39774,12 @@ init_space();
|
|
|
39422
39774
|
// src/lib/browser.ts
|
|
39423
39775
|
init_formatter();
|
|
39424
39776
|
init_theme();
|
|
39425
|
-
import
|
|
39777
|
+
import open13 from "open";
|
|
39426
39778
|
var LAUNCH_CHECK_MS = 1e3;
|
|
39427
39779
|
async function tryOpenBrowser(url) {
|
|
39428
39780
|
let launcher;
|
|
39429
39781
|
try {
|
|
39430
|
-
launcher = await
|
|
39782
|
+
launcher = await open13(url);
|
|
39431
39783
|
} catch {
|
|
39432
39784
|
return false;
|
|
39433
39785
|
}
|
|
@@ -41239,6 +41591,7 @@ function registerSecretsCommand(program, openKeyAcquisition) {
|
|
|
41239
41591
|
|
|
41240
41592
|
// src/commands/share.ts
|
|
41241
41593
|
import { Option } from "commander";
|
|
41594
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
41242
41595
|
import { CLI_NAME as CLI_NAME34 } from "@tinycloud/operations/cli-name";
|
|
41243
41596
|
import { ProfileLockTimeoutError as ProfileLockTimeoutError3 } from "@tinycloud/operations/state";
|
|
41244
41597
|
init_errors();
|
|
@@ -41334,7 +41687,7 @@ function receiveJson(result, path) {
|
|
|
41334
41687
|
|
|
41335
41688
|
// src/share/io.ts
|
|
41336
41689
|
import { constants } from "fs";
|
|
41337
|
-
import { lstat as lstat10, mkdir as mkdir6, mkdtemp, open as
|
|
41690
|
+
import { lstat as lstat10, mkdir as mkdir6, mkdtemp, open as open14, readFile as readFile15, realpath as realpath2, stat as stat6, link as link3, rename as rename5, rm as rm6, unlink as unlink6 } from "fs/promises";
|
|
41338
41691
|
import { randomBytes as randomBytes8 } from "crypto";
|
|
41339
41692
|
import { basename as basename4, join as join18, resolve as resolve6, sep as sep3 } from "path";
|
|
41340
41693
|
init_errors();
|
|
@@ -41410,7 +41763,7 @@ async function writeShareOutput(directory, filename, bytes, force) {
|
|
|
41410
41763
|
const outputDirectory = resolve6(directory);
|
|
41411
41764
|
await assertDirectory(outputDirectory);
|
|
41412
41765
|
const safeName = safeFilename(filename);
|
|
41413
|
-
const directoryHandle = await
|
|
41766
|
+
const directoryHandle = await open14(outputDirectory, constants.O_RDONLY | (constants.O_DIRECTORY ?? 0) | (constants.O_NOFOLLOW ?? 0));
|
|
41414
41767
|
const stableDirectory = await realpath2(outputDirectory);
|
|
41415
41768
|
const outputPath = join18(stableDirectory, safeName);
|
|
41416
41769
|
const directoryIdentity = await directoryHandle.stat();
|
|
@@ -41434,7 +41787,7 @@ async function writeShareOutput(directory, filename, bytes, force) {
|
|
|
41434
41787
|
if (error.code !== "ENOENT") throw error;
|
|
41435
41788
|
}
|
|
41436
41789
|
temporaryPath = stagingPath;
|
|
41437
|
-
handle = await
|
|
41790
|
+
handle = await open14(temporaryPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0), 384);
|
|
41438
41791
|
await handle.writeFile(bytes);
|
|
41439
41792
|
await handle.close();
|
|
41440
41793
|
handle = void 0;
|
|
@@ -41467,7 +41820,7 @@ init_profiles();
|
|
|
41467
41820
|
|
|
41468
41821
|
// src/share/html-folder.ts
|
|
41469
41822
|
import { constants as constants2, existsSync as existsSync2 } from "fs";
|
|
41470
|
-
import { lstat as lstat11, open as
|
|
41823
|
+
import { lstat as lstat11, open as open15, readdir as readdir7, realpath as realpath3, stat as stat7 } from "fs/promises";
|
|
41471
41824
|
import { basename as basename5, dirname as dirname9, isAbsolute, join as join19, relative as relativePath, resolve as resolve7, sep as sep4 } from "path";
|
|
41472
41825
|
|
|
41473
41826
|
// ../share-sdk/dist/html-bundle.js
|
|
@@ -41822,9 +42175,9 @@ function scanMarkup(html, options) {
|
|
|
41822
42175
|
if (comments > MAX_ARTIFACT_COMMENTS) fail("limit", "artifact page has too many comments");
|
|
41823
42176
|
};
|
|
41824
42177
|
while (index < length5) {
|
|
41825
|
-
const
|
|
41826
|
-
if (
|
|
41827
|
-
index =
|
|
42178
|
+
const open16 = html.indexOf("<", index);
|
|
42179
|
+
if (open16 === -1) return;
|
|
42180
|
+
index = open16 + 1;
|
|
41828
42181
|
const next = html[index] ?? "";
|
|
41829
42182
|
if (html.startsWith("!--", index)) {
|
|
41830
42183
|
countComment();
|
|
@@ -42425,7 +42778,7 @@ async function readRegularFile(root, path, relative, remaining, limit, hooks = {
|
|
|
42425
42778
|
await hooks.beforeOpen?.();
|
|
42426
42779
|
before = await lstat11(path);
|
|
42427
42780
|
await hooks.afterLstat?.();
|
|
42428
|
-
handle = await
|
|
42781
|
+
handle = await open15(path, constants2.O_RDONLY | (constants2.O_NOFOLLOW ?? 0) | (constants2.O_NONBLOCK ?? 0));
|
|
42429
42782
|
} catch (error) {
|
|
42430
42783
|
if (error.code === "ELOOP") return problem("symbolic link");
|
|
42431
42784
|
if (isRaceError(error)) return problem(CHANGED);
|
|
@@ -42484,7 +42837,7 @@ async function listFolderDirectory(root, absolute, relative, hooks = {}) {
|
|
|
42484
42837
|
if (before.isSymbolicLink()) return problem("symbolic link");
|
|
42485
42838
|
if (!before.isDirectory()) return problem(CHANGED);
|
|
42486
42839
|
await hooks.afterLstat?.();
|
|
42487
|
-
const handle = await
|
|
42840
|
+
const handle = await open15(absolute, constants2.O_RDONLY | (constants2.O_DIRECTORY ?? 0) | (constants2.O_NOFOLLOW ?? 0));
|
|
42488
42841
|
try {
|
|
42489
42842
|
await hooks.afterOpen?.();
|
|
42490
42843
|
const info = await handle.stat();
|
|
@@ -42908,7 +43261,7 @@ function assertAggregateInputLimit(inputs, maxBytes) {
|
|
|
42908
43261
|
}
|
|
42909
43262
|
function registerShareCommand(program) {
|
|
42910
43263
|
const share = program.command("share").description("Publish and consume TinyCloud Share links");
|
|
42911
|
-
share.command("publish <files...>").description("Publish one or more bounded files as a Share, or a folder with an index.html as one self-contained page (anyone links only)").option("--name <filename>", "Filename for stdin input, or the published .html name of a folder").option("--to <target>", "Share target", "anyone").option("--notify", "Email an exact-email recipient, or a domain mailbox on node 1.17.3+").option("--notify-to <address>", "Mailbox to invite for --to domain:<name> --notify (node 1.17.3+)").option("--expires <duration>", "Share lifetime").option("--max-bytes <bytes>", "Bound input bytes").option("--media-type <type>", "Media type for a single input").option("--action <actions...>", "Addressed permission: read, list, or edit").option("--prefix", "Publish multiple inputs beneath one addressed prefix").option("--binary", "Allow non-UTF-8 bearer content").option("--artifact <id>", 'Publish as the next write-once version of an artifact ("new" starts one); the share is pinned to that version').addOption(new Option("--ongoing").hideHelp()).option("--note <text>", "Note stored with the artifact version").option("--json", "Print versioned redacted JSON").option("--full-link", "Print the full self-contained link instead of a short link (addressed shares)").option("--viewer-origin <origin>", "Canonical HTTPS viewer origin", SHARE_ORIGIN2).action(async (files, options, command) => {
|
|
43264
|
+
share.command("publish <files...>").description("Publish one or more bounded files as a Share, or a folder with an index.html as one self-contained page (anyone links only)").option("--name <filename>", "Filename for stdin input, or the published .html name of a folder").option("--to <target>", "Share target", "anyone").option("--credential-descriptor <path>", "Email credential flow descriptor JSON").option("--issuer-did <did>", "Trusted email credential issuer DID").option("--issuer-kid <kid>", "Trusted email credential issuer key ID").option("--notify", "Email an exact-email recipient, or a domain mailbox on node 1.17.3+").option("--notify-to <address>", "Mailbox to invite for --to domain:<name> --notify (node 1.17.3+)").option("--expires <duration>", "Share lifetime").option("--max-bytes <bytes>", "Bound input bytes").option("--media-type <type>", "Media type for a single input").option("--action <actions...>", "Addressed permission: read, list, or edit").option("--prefix", "Publish multiple inputs beneath one addressed prefix").option("--binary", "Allow non-UTF-8 bearer content").option("--artifact <id>", 'Publish as the next write-once version of an artifact ("new" starts one); the share is pinned to that version').addOption(new Option("--ongoing").hideHelp()).option("--note <text>", "Note stored with the artifact version").option("--json", "Print versioned redacted JSON").option("--full-link", "Print the full self-contained link instead of a short link (addressed shares)").option("--viewer-origin <origin>", "Canonical HTTPS viewer origin", SHARE_ORIGIN2).action(async (files, options, command) => {
|
|
42912
43265
|
try {
|
|
42913
43266
|
const json = jsonOutput(options, command);
|
|
42914
43267
|
if (options.ongoing === true) throw new CLIError("UNSUPPORTED_TARGET", ONGOING_UNAVAILABLE, 2);
|
|
@@ -42916,6 +43269,15 @@ function registerShareCommand(program) {
|
|
|
42916
43269
|
if (files.length === 0 || files.includes("-") && files.length > 1) throw new CLIError("INVALID_ARGUMENT", "stdin must be the only publish input", 2);
|
|
42917
43270
|
const name = files.length === 1 ? options.name : void 0;
|
|
42918
43271
|
const target = parseShareTarget(options.to);
|
|
43272
|
+
const actions = requestedActions(options.action);
|
|
43273
|
+
if (options.notify === true && !actions.includes("read")) throw new CLIError("INVALID_ARGUMENT", "--notify requires the read action; nothing was shared", 2);
|
|
43274
|
+
if (target.kind === "email" && (!options.credentialDescriptor || !options.issuerDid || !options.issuerKid)) {
|
|
43275
|
+
throw new CLIError("INVALID_ARGUMENT", "email publication requires --credential-descriptor, --issuer-did, and --issuer-kid", 2);
|
|
43276
|
+
}
|
|
43277
|
+
if (target.kind !== "email" && (options.credentialDescriptor || options.issuerDid || options.issuerKid)) {
|
|
43278
|
+
throw new CLIError("INVALID_ARGUMENT", "credential options require an exact email target", 2);
|
|
43279
|
+
}
|
|
43280
|
+
const credentialDescriptor = target.kind === "email" ? JSON.parse(await readFile16(options.credentialDescriptor, "utf8")) : void 0;
|
|
42919
43281
|
const folder = files.length === 1 && target.kind === "bearer" && options.prefix !== true && await isDirectoryInput(files[0]);
|
|
42920
43282
|
let inputs;
|
|
42921
43283
|
if (folder) {
|
|
@@ -42932,13 +43294,11 @@ function registerShareCommand(program) {
|
|
|
42932
43294
|
inputs = await Promise.all(files.map((file) => readShareInput(file, name, maxBytes)));
|
|
42933
43295
|
}
|
|
42934
43296
|
assertAggregateInputLimit(inputs, maxBytes);
|
|
42935
|
-
const actions = requestedActions(options.action);
|
|
42936
43297
|
if (options.prefix && target.kind === "bearer") throw new CLIError("INVALID_ARGUMENT", "--prefix requires an addressed target", 2);
|
|
42937
43298
|
if (target.kind === "bearer" && actions.includes("edit")) throw new CLIError("INVALID_ARGUMENT", `--action edit is not available with --to ${options.to}: a link can only be read; address a recipient to allow edits`, 2);
|
|
42938
43299
|
if (inputs.length > 1 && target.kind === "bearer") throw new CLIError("UNSUPPORTED_LINK", "multiple files require an addressed target", 2);
|
|
42939
43300
|
const artifact = artifactRequest(options, actions, inputs.length);
|
|
42940
43301
|
if (inputs.length > 1 && !options.prefix) throw new CLIError("INVALID_ARGUMENT", "multiple files require --prefix", 2);
|
|
42941
|
-
if (options.notify === true && !actions.includes("read")) throw new CLIError("INVALID_ARGUMENT", "--notify requires the read action; nothing was shared", 2);
|
|
42942
43302
|
if (options.notifyTo !== void 0 && (options.notify !== true || target.kind !== "emailDomain")) {
|
|
42943
43303
|
throw new CLIError("INVALID_ARGUMENT", "--notify-to requires --to domain:<name> --notify", 2);
|
|
42944
43304
|
}
|
|
@@ -42968,6 +43328,7 @@ function registerShareCommand(program) {
|
|
|
42968
43328
|
origin: options.viewerOrigin,
|
|
42969
43329
|
...maxBytes === void 0 ? {} : { maxBytes },
|
|
42970
43330
|
notify: options.notify === true,
|
|
43331
|
+
...credentialDescriptor === void 0 ? {} : { credentialDescriptor, issuerDid: options.issuerDid, issuerKid: options.issuerKid },
|
|
42971
43332
|
// The artifact request rides on the adapter call rather than through `publishTargetShare`, whose code is part of
|
|
42972
43333
|
// the browser bundles (the web-sdk size ceiling counts every byte it carries).
|
|
42973
43334
|
targetAdapter: artifact === void 0 || shareServices.targetAdapter === void 0 ? shareServices.targetAdapter : { publish: (request) => shareServices.targetAdapter.publish({ ...request, artifact }) }
|
|
@@ -44103,7 +44464,7 @@ function registerUpgradeCommand(program) {
|
|
|
44103
44464
|
}
|
|
44104
44465
|
|
|
44105
44466
|
// src/commands/vault.ts
|
|
44106
|
-
import { readFile as
|
|
44467
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
44107
44468
|
init_profiles();
|
|
44108
44469
|
init_formatter();
|
|
44109
44470
|
init_errors();
|
|
@@ -44165,7 +44526,7 @@ function registerVaultCommand(program) {
|
|
|
44165
44526
|
throw new CLIError("USAGE_ERROR", "Provide only one of: value argument, --file, or --stdin", ExitCode.USAGE_ERROR);
|
|
44166
44527
|
}
|
|
44167
44528
|
if (options.file) {
|
|
44168
|
-
putValue = new Uint8Array(await
|
|
44529
|
+
putValue = new Uint8Array(await readFile17(options.file));
|
|
44169
44530
|
} else if (options.stdin) {
|
|
44170
44531
|
putValue = new Uint8Array(await readStdin5());
|
|
44171
44532
|
} else {
|
|
@@ -44280,7 +44641,7 @@ function registerVaultCommand(program) {
|
|
|
44280
44641
|
}
|
|
44281
44642
|
|
|
44282
44643
|
// src/commands/vars.ts
|
|
44283
|
-
import { readFile as
|
|
44644
|
+
import { readFile as readFile18 } from "fs/promises";
|
|
44284
44645
|
init_profiles();
|
|
44285
44646
|
init_formatter();
|
|
44286
44647
|
init_errors();
|
|
@@ -44385,7 +44746,7 @@ function registerVarsCommand(program) {
|
|
|
44385
44746
|
throw new CLIError("USAGE_ERROR", "Provide only one of: value argument, --file, or --stdin", ExitCode.USAGE_ERROR);
|
|
44386
44747
|
}
|
|
44387
44748
|
if (options.file) {
|
|
44388
|
-
varValue = await
|
|
44749
|
+
varValue = await readFile18(options.file, "utf-8");
|
|
44389
44750
|
} else if (options.stdin) {
|
|
44390
44751
|
varValue = (await readStdin6()).toString("utf-8");
|
|
44391
44752
|
} else {
|