@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/index.js
CHANGED
|
@@ -13800,6 +13800,7 @@ init_profiles();
|
|
|
13800
13800
|
|
|
13801
13801
|
// src/commands/share.ts
|
|
13802
13802
|
import { Option } from "commander";
|
|
13803
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
13803
13804
|
import { CLI_NAME as CLI_NAME12 } from "@tinycloud/operations/cli-name";
|
|
13804
13805
|
|
|
13805
13806
|
// ../share-sdk/dist/artifact.js
|
|
@@ -18986,6 +18987,23 @@ var unifiedEncryptionNetworkSchema = external_exports.string().refine((value) =>
|
|
|
18986
18987
|
const network = separator < 0 ? "" : rest.slice(separator + 1);
|
|
18987
18988
|
return owner.startsWith("did:") && owner.length > 4 && network.length > 0 && !/[:/%?#\s]/.test(network);
|
|
18988
18989
|
}, { message: "expected canonical TinyCloud encryption network" });
|
|
18990
|
+
var unifiedEncryptionResourceSchema = external_exports.string().refine((value) => {
|
|
18991
|
+
const prefix = "urn:tinycloud:encryption:";
|
|
18992
|
+
if (!value.startsWith(prefix)) return false;
|
|
18993
|
+
const match = /^(did:.+):([^/:?#\s]+)(?:\/(.+))?$/.exec(value.slice(prefix.length));
|
|
18994
|
+
if (match === null) return false;
|
|
18995
|
+
const path = match[3];
|
|
18996
|
+
return path === void 0 || path.split("/").every(
|
|
18997
|
+
(segment) => segment.length > 0 && segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment)
|
|
18998
|
+
);
|
|
18999
|
+
}, { message: "expected a canonical TinyCloud encryption resource" });
|
|
19000
|
+
var unifiedEncryptionKeyPathSchema = external_exports.string().regex(/^share\/[^/]+$/).refine(
|
|
19001
|
+
(value) => {
|
|
19002
|
+
const segment = value.slice("share/".length);
|
|
19003
|
+
return segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment);
|
|
19004
|
+
},
|
|
19005
|
+
{ message: "expected an exact share key path" }
|
|
19006
|
+
);
|
|
18989
19007
|
var unifiedKvCapabilitySchema = external_exports.object({
|
|
18990
19008
|
kind: external_exports.literal("kv"),
|
|
18991
19009
|
resource: unifiedResourceSchema,
|
|
@@ -18999,7 +19017,7 @@ var unifiedKvCapabilitySchema = external_exports.object({
|
|
|
18999
19017
|
}).strict();
|
|
19000
19018
|
var unifiedEncryptionCapabilitySchema = external_exports.object({
|
|
19001
19019
|
kind: external_exports.literal("encryption"),
|
|
19002
|
-
resource:
|
|
19020
|
+
resource: unifiedEncryptionResourceSchema,
|
|
19003
19021
|
action: external_exports.literal("tinycloud.encryption/decrypt")
|
|
19004
19022
|
}).strict();
|
|
19005
19023
|
var unifiedCapabilitySchema = external_exports.union([unifiedKvCapabilitySchema, unifiedEncryptionCapabilitySchema]);
|
|
@@ -19008,6 +19026,7 @@ var unifiedContentSourceSchema = external_exports.object({
|
|
|
19008
19026
|
kvResource: unifiedResourceSchema,
|
|
19009
19027
|
selector: external_exports.union([external_exports.literal("exact"), external_exports.literal("prefix")]),
|
|
19010
19028
|
encryptionNetwork: unifiedEncryptionNetworkSchema,
|
|
19029
|
+
encryptionKeyPath: unifiedEncryptionKeyPathSchema.optional(),
|
|
19011
19030
|
encryptedSymmetricKeyDigestHex: external_exports.string().regex(/^[0-9a-f]{64}$/),
|
|
19012
19031
|
keyVersion: external_exports.number().int().positive(),
|
|
19013
19032
|
mode: external_exports.union([external_exports.literal("mutable"), external_exports.literal("immutable")]),
|
|
@@ -19125,16 +19144,21 @@ function validateV3Invariants(value, ctx) {
|
|
|
19125
19144
|
if (value.attestedEnforcerBinding.nodeAudience !== value.target.nodeAudience || value.attestedEnforcerBinding.signature.signerDid !== value.attestedEnforcerBinding.nodeAudience || Date.parse(value.attestedEnforcerBinding.expiresAt) < Date.parse(value.expiry)) {
|
|
19126
19145
|
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["attestedEnforcerBinding"], message: "enforcer binding does not cover the target and share lifetime" });
|
|
19127
19146
|
}
|
|
19128
|
-
|
|
19147
|
+
const encryptionKeyPath = value.contentSource.encryptionKeyPath;
|
|
19148
|
+
if (encryptionKeyPath !== void 0 && encryptionKeyPath !== `share/${value.contentSource.shareId}`) {
|
|
19149
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["contentSource", "encryptionKeyPath"], message: "share key path must name this share" });
|
|
19150
|
+
}
|
|
19151
|
+
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) {
|
|
19129
19152
|
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["contentSource"], message: "content source is not bound to policy" });
|
|
19130
19153
|
}
|
|
19131
19154
|
if (value.policy.signature.suite !== "Ed25519" || value.policy.signature.signerDid !== value.policy.ownerDid) {
|
|
19132
19155
|
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "signature"], message: "policy must be signed by its owner" });
|
|
19133
19156
|
}
|
|
19134
19157
|
const kvCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "kv");
|
|
19135
|
-
const
|
|
19158
|
+
const expectedEncryptionResource = encryptionKeyPath === void 0 ? value.encryptionNetwork : `${value.encryptionNetwork}/${encryptionKeyPath}`;
|
|
19159
|
+
const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === expectedEncryptionResource && capability.action === "tinycloud.encryption/decrypt");
|
|
19136
19160
|
if (value.policy.capabilityCeiling.length !== 2 || kvCapabilities.length !== 1 || decryptCapabilities.length !== 1 || kvCapabilities[0]?.resource !== value.contentSource.kvResource || kvCapabilities[0]?.selector !== value.contentSource.selector) {
|
|
19137
|
-
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain exact decrypt
|
|
19161
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain the exact decrypt resource" });
|
|
19138
19162
|
}
|
|
19139
19163
|
}
|
|
19140
19164
|
var unsignedShareEnvelopeV3Schema = unsignedShareEnvelopeV3BaseSchema.superRefine(validateV3Invariants);
|
|
@@ -24637,6 +24661,23 @@ var unifiedEncryptionNetworkSchema2 = external_exports3.string().refine((value)
|
|
|
24637
24661
|
const network = separator < 0 ? "" : rest.slice(separator + 1);
|
|
24638
24662
|
return owner.startsWith("did:") && owner.length > 4 && network.length > 0 && !/[:/%?#\s]/.test(network);
|
|
24639
24663
|
}, { message: "expected canonical TinyCloud encryption network" });
|
|
24664
|
+
var unifiedEncryptionResourceSchema2 = external_exports3.string().refine((value) => {
|
|
24665
|
+
const prefix = "urn:tinycloud:encryption:";
|
|
24666
|
+
if (!value.startsWith(prefix)) return false;
|
|
24667
|
+
const match = /^(did:.+):([^/:?#\s]+)(?:\/(.+))?$/.exec(value.slice(prefix.length));
|
|
24668
|
+
if (match === null) return false;
|
|
24669
|
+
const path = match[3];
|
|
24670
|
+
return path === void 0 || path.split("/").every(
|
|
24671
|
+
(segment) => segment.length > 0 && segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment)
|
|
24672
|
+
);
|
|
24673
|
+
}, { message: "expected a canonical TinyCloud encryption resource" });
|
|
24674
|
+
var unifiedEncryptionKeyPathSchema2 = external_exports3.string().regex(/^share\/[^/]+$/).refine(
|
|
24675
|
+
(value) => {
|
|
24676
|
+
const segment = value.slice("share/".length);
|
|
24677
|
+
return segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment);
|
|
24678
|
+
},
|
|
24679
|
+
{ message: "expected an exact share key path" }
|
|
24680
|
+
);
|
|
24640
24681
|
var unifiedKvCapabilitySchema2 = external_exports3.object({
|
|
24641
24682
|
kind: external_exports3.literal("kv"),
|
|
24642
24683
|
resource: unifiedResourceSchema2,
|
|
@@ -24650,7 +24691,7 @@ var unifiedKvCapabilitySchema2 = external_exports3.object({
|
|
|
24650
24691
|
}).strict();
|
|
24651
24692
|
var unifiedEncryptionCapabilitySchema2 = external_exports3.object({
|
|
24652
24693
|
kind: external_exports3.literal("encryption"),
|
|
24653
|
-
resource:
|
|
24694
|
+
resource: unifiedEncryptionResourceSchema2,
|
|
24654
24695
|
action: external_exports3.literal("tinycloud.encryption/decrypt")
|
|
24655
24696
|
}).strict();
|
|
24656
24697
|
var unifiedCapabilitySchema2 = external_exports3.union([unifiedKvCapabilitySchema2, unifiedEncryptionCapabilitySchema2]);
|
|
@@ -24659,6 +24700,7 @@ var unifiedContentSourceSchema2 = external_exports3.object({
|
|
|
24659
24700
|
kvResource: unifiedResourceSchema2,
|
|
24660
24701
|
selector: external_exports3.union([external_exports3.literal("exact"), external_exports3.literal("prefix")]),
|
|
24661
24702
|
encryptionNetwork: unifiedEncryptionNetworkSchema2,
|
|
24703
|
+
encryptionKeyPath: unifiedEncryptionKeyPathSchema2.optional(),
|
|
24662
24704
|
encryptedSymmetricKeyDigestHex: external_exports3.string().regex(/^[0-9a-f]{64}$/),
|
|
24663
24705
|
keyVersion: external_exports3.number().int().positive(),
|
|
24664
24706
|
mode: external_exports3.union([external_exports3.literal("mutable"), external_exports3.literal("immutable")]),
|
|
@@ -24776,16 +24818,21 @@ function validateV3Invariants2(value, ctx) {
|
|
|
24776
24818
|
if (value.attestedEnforcerBinding.nodeAudience !== value.target.nodeAudience || value.attestedEnforcerBinding.signature.signerDid !== value.attestedEnforcerBinding.nodeAudience || Date.parse(value.attestedEnforcerBinding.expiresAt) < Date.parse(value.expiry)) {
|
|
24777
24819
|
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["attestedEnforcerBinding"], message: "enforcer binding does not cover the target and share lifetime" });
|
|
24778
24820
|
}
|
|
24779
|
-
|
|
24821
|
+
const encryptionKeyPath = value.contentSource.encryptionKeyPath;
|
|
24822
|
+
if (encryptionKeyPath !== void 0 && encryptionKeyPath !== `share/${value.contentSource.shareId}`) {
|
|
24823
|
+
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["contentSource", "encryptionKeyPath"], message: "share key path must name this share" });
|
|
24824
|
+
}
|
|
24825
|
+
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) {
|
|
24780
24826
|
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["contentSource"], message: "content source is not bound to policy" });
|
|
24781
24827
|
}
|
|
24782
24828
|
if (value.policy.signature.suite !== "Ed25519" || value.policy.signature.signerDid !== value.policy.ownerDid) {
|
|
24783
24829
|
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["policy", "signature"], message: "policy must be signed by its owner" });
|
|
24784
24830
|
}
|
|
24785
24831
|
const kvCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "kv");
|
|
24786
|
-
const
|
|
24832
|
+
const expectedEncryptionResource = encryptionKeyPath === void 0 ? value.encryptionNetwork : `${value.encryptionNetwork}/${encryptionKeyPath}`;
|
|
24833
|
+
const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === expectedEncryptionResource && capability.action === "tinycloud.encryption/decrypt");
|
|
24787
24834
|
if (value.policy.capabilityCeiling.length !== 2 || kvCapabilities.length !== 1 || decryptCapabilities.length !== 1 || kvCapabilities[0]?.resource !== value.contentSource.kvResource || kvCapabilities[0]?.selector !== value.contentSource.selector) {
|
|
24788
|
-
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain exact decrypt
|
|
24835
|
+
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain the exact decrypt resource" });
|
|
24789
24836
|
}
|
|
24790
24837
|
}
|
|
24791
24838
|
var unsignedShareEnvelopeV3Schema2 = unsignedShareEnvelopeV3BaseSchema2.superRefine(validateV3Invariants2);
|
|
@@ -24795,6 +24842,7 @@ var SEALED_BLOB_VERSION2 = 1;
|
|
|
24795
24842
|
var AAD3 = utf8Bytes3(ENVELOPE_AAD_LABEL3);
|
|
24796
24843
|
var KEY_LENGTH2 = 32;
|
|
24797
24844
|
var NONCE_LENGTH2 = 12;
|
|
24845
|
+
var TAG_LENGTH2 = 16;
|
|
24798
24846
|
var HEADER_LENGTH2 = 1;
|
|
24799
24847
|
function assertKey3(key32) {
|
|
24800
24848
|
if (key32.length !== KEY_LENGTH2) {
|
|
@@ -24822,6 +24870,17 @@ async function seal2(plaintextBytes, key32) {
|
|
|
24822
24870
|
blob.set(ciphertext, HEADER_LENGTH2 + nonce.length);
|
|
24823
24871
|
return { blob, cid: await computeCid2(blob) };
|
|
24824
24872
|
}
|
|
24873
|
+
async function open7(blob, key32) {
|
|
24874
|
+
if (blob.length < HEADER_LENGTH2 + NONCE_LENGTH2 + TAG_LENGTH2) {
|
|
24875
|
+
throw new TypeError(`sealed blob too short: ${blob.length} bytes`);
|
|
24876
|
+
}
|
|
24877
|
+
if (blob[0] !== SEALED_BLOB_VERSION2) {
|
|
24878
|
+
throw new TypeError(`unknown sealed blob version: ${blob[0]}`);
|
|
24879
|
+
}
|
|
24880
|
+
const nonce = blob.subarray(HEADER_LENGTH2, HEADER_LENGTH2 + NONCE_LENGTH2);
|
|
24881
|
+
const ciphertext = blob.subarray(HEADER_LENGTH2 + NONCE_LENGTH2);
|
|
24882
|
+
return decryptEnvelope2(nonce, ciphertext, key32);
|
|
24883
|
+
}
|
|
24825
24884
|
async function encryptEnvelope2(plaintextBytes, key32) {
|
|
24826
24885
|
const key = await importAesKey2(key32, "encrypt");
|
|
24827
24886
|
const nonce = globalThis.crypto.getRandomValues(new Uint8Array(NONCE_LENGTH2));
|
|
@@ -24834,6 +24893,19 @@ async function encryptEnvelope2(plaintextBytes, key32) {
|
|
|
24834
24893
|
);
|
|
24835
24894
|
return { nonce, ciphertext };
|
|
24836
24895
|
}
|
|
24896
|
+
async function decryptEnvelope2(nonce, ciphertext, key32) {
|
|
24897
|
+
if (nonce.length !== NONCE_LENGTH2) {
|
|
24898
|
+
throw new TypeError(`nonce must be ${NONCE_LENGTH2} bytes, got ${nonce.length}`);
|
|
24899
|
+
}
|
|
24900
|
+
const key = await importAesKey2(key32, "decrypt");
|
|
24901
|
+
return new Uint8Array(
|
|
24902
|
+
await globalThis.crypto.subtle.decrypt(
|
|
24903
|
+
{ name: "AES-GCM", iv: nonce, additionalData: AAD3 },
|
|
24904
|
+
key,
|
|
24905
|
+
ciphertext
|
|
24906
|
+
)
|
|
24907
|
+
);
|
|
24908
|
+
}
|
|
24837
24909
|
function isPlainRecord3(value) {
|
|
24838
24910
|
const proto = Object.getPrototypeOf(value);
|
|
24839
24911
|
return proto === Object.prototype || proto === null;
|
|
@@ -24948,6 +25020,7 @@ function equal(left, right) {
|
|
|
24948
25020
|
return left.length === right.length && left.every((byte, index) => byte === right[index]);
|
|
24949
25021
|
}
|
|
24950
25022
|
var ED25519_VERIFY_OPTS2 = { zip215: false };
|
|
25023
|
+
var ENVELOPE_V2_SIGNATURE_DOMAIN = "xyz.tinycloud.share/envelope/v2\0";
|
|
24951
25024
|
var ENVELOPE_V3_SIGNATURE_DOMAIN = "xyz.tinycloud.share/envelope/v3\0";
|
|
24952
25025
|
var POLICY_V1_SIGNATURE_DOMAIN = "xyz.tinycloud.policy/policy/v1\0";
|
|
24953
25026
|
var POLICY_V2_SIGNATURE_DOMAIN = "xyz.tinycloud.policy/policy/v2\0";
|
|
@@ -24955,6 +25028,14 @@ var CONTENT_SOURCE_V1_DOMAIN = "xyz.tinycloud.policy/ContentSource/v1\0";
|
|
|
24955
25028
|
var POLICY_CAPABILITY_V1_DOMAIN = "xyz.tinycloud.policy/PolicyCapability/v1\0";
|
|
24956
25029
|
var NATIVE_PROJECTION_V1_DOMAIN = "xyz.tinycloud.policy/NativeProjection/v1\0";
|
|
24957
25030
|
var ATTESTED_ENFORCER_V2_DOMAIN = "xyz.tinycloud.policy/AttestedEnforcerBinding/v2\0";
|
|
25031
|
+
function signingBytesV2(unsigned) {
|
|
25032
|
+
const domain = utf8Bytes3(ENVELOPE_V2_SIGNATURE_DOMAIN);
|
|
25033
|
+
const body = utf8Bytes3(canonicalize3(unsigned));
|
|
25034
|
+
const bytes3 = new Uint8Array(domain.length + body.length);
|
|
25035
|
+
bytes3.set(domain);
|
|
25036
|
+
bytes3.set(body, domain.length);
|
|
25037
|
+
return bytes3;
|
|
25038
|
+
}
|
|
24958
25039
|
function signingBytesV3(unsigned) {
|
|
24959
25040
|
const domain = utf8Bytes3(ENVELOPE_V3_SIGNATURE_DOMAIN);
|
|
24960
25041
|
const body = utf8Bytes3(canonicalize3(unsigned));
|
|
@@ -24963,6 +25044,17 @@ function signingBytesV3(unsigned) {
|
|
|
24963
25044
|
bytes3.set(body, domain.length);
|
|
24964
25045
|
return bytes3;
|
|
24965
25046
|
}
|
|
25047
|
+
function verifyEnvelopeV2SignatureOnly(envelope) {
|
|
25048
|
+
const parsed = shareEnvelopeV2Schema2.parse(envelope);
|
|
25049
|
+
const { signature, ...unsigned } = parsed;
|
|
25050
|
+
return ed25519.verify(fromBase64Url3(signature.value), signingBytesV2(unsigned), ed25519PublicKeyFromDidKey2(signature.signerDid), ED25519_VERIFY_OPTS2);
|
|
25051
|
+
}
|
|
25052
|
+
async function verifyEnvelopeV2(envelope, options) {
|
|
25053
|
+
const parsed = shareEnvelopeV2Schema2.parse(envelope);
|
|
25054
|
+
if (parsed.signature.signerDid !== options.expectedSignerDid || !verifyEnvelopeV2SignatureOnly(parsed)) return false;
|
|
25055
|
+
if (parsed.authorizationTarget.kind === "policy") return await computeCid2(fromBase64Url3(parsed.authorizationTarget.policyBytes)) === parsed.authorizationTarget.policyCid;
|
|
25056
|
+
return true;
|
|
25057
|
+
}
|
|
24966
25058
|
function verifyEnvelopeV3SignatureOnly(envelope) {
|
|
24967
25059
|
const parsed = shareEnvelopeV3Schema2.parse(envelope);
|
|
24968
25060
|
const { signature, ...unsigned } = parsed;
|
|
@@ -25049,6 +25141,52 @@ function base32Lower(bytes3) {
|
|
|
25049
25141
|
if (bits > 0) output += alphabet[buffer << 5 - bits & 31];
|
|
25050
25142
|
return output;
|
|
25051
25143
|
}
|
|
25144
|
+
var KEY_LENGTH22 = 32;
|
|
25145
|
+
function assertCanonicalCid(cidString) {
|
|
25146
|
+
const cid2 = CID3.parse(cidString);
|
|
25147
|
+
if (cid2.version !== 1 || cid2.code !== code3 || cid2.multihash.code !== 18 || // sha2-256 (0x12) only, at the link layer too
|
|
25148
|
+
cid2.toString() !== cidString) {
|
|
25149
|
+
throw new TypeError(`not a canonical CIDv1 raw sha2-256 base32 CID: ${cidString}`);
|
|
25150
|
+
}
|
|
25151
|
+
}
|
|
25152
|
+
function parseShareUrl(url, options = {}) {
|
|
25153
|
+
const parsed = new URL(url);
|
|
25154
|
+
if (parsed.protocol !== "https:") {
|
|
25155
|
+
throw new TypeError(`share URL must be https, got ${parsed.protocol}`);
|
|
25156
|
+
}
|
|
25157
|
+
if (parsed.username !== "" || parsed.password !== "") {
|
|
25158
|
+
throw new TypeError("share URL must not carry userinfo");
|
|
25159
|
+
}
|
|
25160
|
+
if (parsed.search !== "") {
|
|
25161
|
+
throw new TypeError("share URL must not have a query string");
|
|
25162
|
+
}
|
|
25163
|
+
if (options.expectedOrigin !== void 0) {
|
|
25164
|
+
if (!isCanonicalHttpsOrigin2(options.expectedOrigin)) {
|
|
25165
|
+
throw new TypeError(
|
|
25166
|
+
`expectedOrigin must be a canonical https origin, got ${options.expectedOrigin}`
|
|
25167
|
+
);
|
|
25168
|
+
}
|
|
25169
|
+
if (parsed.origin !== options.expectedOrigin) {
|
|
25170
|
+
throw new TypeError(
|
|
25171
|
+
`share URL origin ${parsed.origin} does not match expected ${options.expectedOrigin}`
|
|
25172
|
+
);
|
|
25173
|
+
}
|
|
25174
|
+
}
|
|
25175
|
+
const match = /^\/s\/([a-z2-7]+)$/.exec(parsed.pathname);
|
|
25176
|
+
if (!match || match[1] === void 0) {
|
|
25177
|
+
throw new TypeError(`not a share URL path: ${parsed.pathname}`);
|
|
25178
|
+
}
|
|
25179
|
+
const ciphertextCid = match[1];
|
|
25180
|
+
assertCanonicalCid(ciphertextCid);
|
|
25181
|
+
if (!parsed.hash.startsWith("#k=")) {
|
|
25182
|
+
throw new TypeError("share URL is missing the #k= key fragment");
|
|
25183
|
+
}
|
|
25184
|
+
const key32 = fromBase64Url3(parsed.hash.slice("#k=".length));
|
|
25185
|
+
if (key32.length !== KEY_LENGTH22) {
|
|
25186
|
+
throw new TypeError(`fragment key must be ${KEY_LENGTH22} bytes, got ${key32.length}`);
|
|
25187
|
+
}
|
|
25188
|
+
return { ciphertextCid, key32 };
|
|
25189
|
+
}
|
|
25052
25190
|
var ARTIFACT_ROOT2 = "artifacts";
|
|
25053
25191
|
function parseArtifactVersionPath2(path) {
|
|
25054
25192
|
const match = /^artifacts\/([a-z0-9][a-z0-9_-]{2,62})\/versions\/([1-9][0-9]{0,14})\/([^/]+)$/.exec(path);
|
|
@@ -25125,6 +25263,7 @@ function verifyArtifactShare(envelope, options) {
|
|
|
25125
25263
|
}
|
|
25126
25264
|
var SHARE_RESULT_VERSION = 1;
|
|
25127
25265
|
var DEFAULT_MAX_SEALED_BLOB_BYTES = 100 * 1024 * 1024 + 29;
|
|
25266
|
+
var DEFAULT_MAX_CONTENT_BLOB_BYTES = DEFAULT_MAX_SEALED_BLOB_BYTES;
|
|
25128
25267
|
var ShareReceiveError = class extends Error {
|
|
25129
25268
|
code;
|
|
25130
25269
|
details;
|
|
@@ -25139,7 +25278,7 @@ var ShareReceiveError = class extends Error {
|
|
|
25139
25278
|
}
|
|
25140
25279
|
};
|
|
25141
25280
|
function metadataFor(envelope, origin) {
|
|
25142
|
-
const kind = envelope.recipientMatcher.kind === "recipientDid" ? "recipientDid" : envelope.recipientMatcher.kind === "exactEmail" ? "email" : "emailDomain";
|
|
25281
|
+
const kind = envelope.recipientMatcher.kind === "recipientDid" ? "recipientDid" : envelope.recipientMatcher.kind === "exactEmail" ? "email" : envelope.recipientMatcher.kind === "emailDomain" ? "emailDomain" : "bearer";
|
|
25143
25282
|
return {
|
|
25144
25283
|
protocol: "tinycloud-share",
|
|
25145
25284
|
version: 1,
|
|
@@ -25154,7 +25293,7 @@ function metadataFor(envelope, origin) {
|
|
|
25154
25293
|
...envelope.display.filename === void 0 ? {} : { filename: envelope.display.filename },
|
|
25155
25294
|
...envelope.display.mode === void 0 ? {} : { mode: envelope.display.mode }
|
|
25156
25295
|
},
|
|
25157
|
-
...envelope.
|
|
25296
|
+
...envelope.version === 3 && envelope.artifactPin !== void 0 ? { artifact: { id: envelope.artifactPin.artifactId, version: envelope.artifactPin.version } } : {}
|
|
25158
25297
|
};
|
|
25159
25298
|
}
|
|
25160
25299
|
function receiveErrorFor(error) {
|
|
@@ -25252,14 +25391,165 @@ async function resolvePolicyShare(link4, options) {
|
|
|
25252
25391
|
options.onResolvedAddressedEnvelope?.(envelope, cid2);
|
|
25253
25392
|
return { envelope, origin: url.origin, cid: cid2 };
|
|
25254
25393
|
}
|
|
25394
|
+
async function resolveLegacyV2(link4, options) {
|
|
25395
|
+
let parsed;
|
|
25396
|
+
try {
|
|
25397
|
+
parsed = parseShareUrl(link4, { ...options.expectedOrigin === void 0 ? {} : { expectedOrigin: options.expectedOrigin } });
|
|
25398
|
+
} catch {
|
|
25399
|
+
throw new ShareReceiveError("invalid-link", "legacy share link format is invalid");
|
|
25400
|
+
}
|
|
25401
|
+
try {
|
|
25402
|
+
options.signal?.throwIfAborted();
|
|
25403
|
+
const origin = new URL(link4).origin;
|
|
25404
|
+
const limit = options.maxSealedBlobBytes ?? DEFAULT_MAX_SEALED_BLOB_BYTES;
|
|
25405
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope limit is invalid");
|
|
25406
|
+
let sealed;
|
|
25407
|
+
if (options.fetchBlob !== void 0) {
|
|
25408
|
+
try {
|
|
25409
|
+
sealed = await options.fetchBlob({ origin, cid: parsed.ciphertextCid, ...options.signal === void 0 ? {} : { signal: options.signal } });
|
|
25410
|
+
} catch {
|
|
25411
|
+
throw new ShareReceiveError("fetch-failed", "legacy registry unavailable");
|
|
25412
|
+
}
|
|
25413
|
+
} else {
|
|
25414
|
+
if (options.registryBaseUrl === void 0) throw new ShareReceiveError("fetch-failed", "legacy registry is required");
|
|
25415
|
+
const response = await (options.fetch ?? globalThis.fetch)(`${options.registryBaseUrl.replace(/\/+$/, "")}/ipfs/${parsed.ciphertextCid}?format=raw`, {
|
|
25416
|
+
headers: { accept: "application/vnd.ipld.raw" },
|
|
25417
|
+
redirect: "error",
|
|
25418
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
25419
|
+
});
|
|
25420
|
+
if (!response.ok) throw new ShareReceiveError("fetch-failed", "legacy registry unavailable");
|
|
25421
|
+
if (Number(response.headers.get("content-length")) > limit) throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope exceeds the size limit");
|
|
25422
|
+
const reader = response.body?.getReader();
|
|
25423
|
+
if (reader === void 0) throw new ShareReceiveError("fetch-failed", "legacy registry returned no body");
|
|
25424
|
+
const chunks = [];
|
|
25425
|
+
let length32 = 0;
|
|
25426
|
+
for (; ; ) {
|
|
25427
|
+
const part = await reader.read();
|
|
25428
|
+
if (part.done) break;
|
|
25429
|
+
length32 += part.value.length;
|
|
25430
|
+
if (length32 > limit) {
|
|
25431
|
+
await reader.cancel();
|
|
25432
|
+
throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope exceeds the size limit");
|
|
25433
|
+
}
|
|
25434
|
+
chunks.push(part.value);
|
|
25435
|
+
}
|
|
25436
|
+
sealed = new Uint8Array(length32);
|
|
25437
|
+
let offset = 0;
|
|
25438
|
+
for (const chunk of chunks) {
|
|
25439
|
+
sealed.set(chunk, offset);
|
|
25440
|
+
offset += chunk.length;
|
|
25441
|
+
}
|
|
25442
|
+
}
|
|
25443
|
+
options.signal?.throwIfAborted();
|
|
25444
|
+
if (!(sealed instanceof Uint8Array) || sealed.length > limit) throw new ShareReceiveError("max-bytes-exceeded", "sealed envelope exceeds the size limit");
|
|
25445
|
+
if (await computeCid2(sealed) !== parsed.ciphertextCid) throw new ShareReceiveError("cid-mismatch", "legacy envelope CID does not match");
|
|
25446
|
+
let plaintext;
|
|
25447
|
+
try {
|
|
25448
|
+
plaintext = await open7(sealed, parsed.key32);
|
|
25449
|
+
} catch {
|
|
25450
|
+
throw new ShareReceiveError("decrypt-failed", "legacy envelope could not be opened");
|
|
25451
|
+
}
|
|
25452
|
+
let envelope;
|
|
25453
|
+
try {
|
|
25454
|
+
const encoded = new TextDecoder("utf-8", { fatal: true }).decode(plaintext);
|
|
25455
|
+
const value = JSON.parse(encoded);
|
|
25456
|
+
if (canonicalize3(value) !== encoded) throw new Error("non-canonical envelope");
|
|
25457
|
+
envelope = shareEnvelopeV2Schema2.parse(value);
|
|
25458
|
+
} catch {
|
|
25459
|
+
throw new ShareReceiveError("envelope-invalid", "legacy envelope is invalid");
|
|
25460
|
+
}
|
|
25461
|
+
return { envelope, origin, cid: parsed.ciphertextCid };
|
|
25462
|
+
} finally {
|
|
25463
|
+
parsed.key32.fill(0);
|
|
25464
|
+
}
|
|
25465
|
+
}
|
|
25466
|
+
function evidenceMatchesV2(envelope, evidence) {
|
|
25467
|
+
const actions = new Set(evidence.actions.map((action) => action === "read" ? "tinycloud.kv/get" : action === "list" ? "tinycloud.kv/list" : action === "edit" ? "tinycloud.kv/put" : action));
|
|
25468
|
+
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}/`);
|
|
25469
|
+
return envelope.authorizationTarget.kind === "policy" && evidence.policyCid === envelope.authorizationTarget.policyCid && evidence.signerDid === envelope.signature.signerDid && evidence.registrationCid.length > 0 && evidence.shareId === envelope.shareId && canonicalize3(evidence.recipientMatcher) === canonicalize3(envelope.recipientMatcher) && canonicalize3(evidence.target) === canonicalize3(envelope.target) && resourceCovers && envelope.actions.every((action) => actions.has(action === "read" ? "tinycloud.kv/get" : action === "list" ? "tinycloud.kv/list" : "tinycloud.kv/put")) && canonicalize3(evidence.contentSource) === canonicalize3(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);
|
|
25470
|
+
}
|
|
25471
|
+
async function verifyLegacyV2(envelope, options) {
|
|
25472
|
+
try {
|
|
25473
|
+
if (!await verifyEnvelopeV2(envelope, { expectedSignerDid: envelope.signature.signerDid })) throw new Error("signature");
|
|
25474
|
+
} catch {
|
|
25475
|
+
throw new ShareReceiveError("signature-invalid", "legacy envelope signature is invalid");
|
|
25476
|
+
}
|
|
25477
|
+
const expiry = Date.parse(envelope.expiry);
|
|
25478
|
+
if (!Number.isFinite(expiry)) throw new ShareReceiveError("envelope-invalid", "legacy share expiry is invalid");
|
|
25479
|
+
if (expiry <= (options.now?.() ?? Date.now())) throw new ShareReceiveError("expired", "legacy share has expired", { expiresAt: envelope.expiry });
|
|
25480
|
+
if (envelope.authorizationTarget.kind === "policy") {
|
|
25481
|
+
if (options.trustedPolicyAuthority === void 0) throw new ShareReceiveError("envelope-invalid", "a legacy policy authority is required");
|
|
25482
|
+
let evidence;
|
|
25483
|
+
try {
|
|
25484
|
+
evidence = await options.trustedPolicyAuthority.resolve({ policyCid: envelope.authorizationTarget.policyCid, envelope });
|
|
25485
|
+
} catch {
|
|
25486
|
+
}
|
|
25487
|
+
if (evidence === void 0 || !evidenceMatchesV2(envelope, evidence)) throw new ShareReceiveError("capability-invalid", "legacy policy evidence does not match");
|
|
25488
|
+
if (options.trustedSignerDid !== void 0 && options.trustedSignerDid !== evidence.signerDid) throw new ShareReceiveError("signature-invalid", "legacy signer is not trusted");
|
|
25489
|
+
} 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) {
|
|
25490
|
+
throw new ShareReceiveError("capability-invalid", "legacy recipient authority does not match");
|
|
25491
|
+
}
|
|
25492
|
+
}
|
|
25255
25493
|
async function inspectShare(link4, options = {}) {
|
|
25494
|
+
if (isLegacyV2Link(link4)) {
|
|
25495
|
+
const legacy = await resolveLegacyV2(link4, options);
|
|
25496
|
+
await verifyLegacyV2(legacy.envelope, options);
|
|
25497
|
+
options.onResolvedAddressedEnvelope?.(legacy.envelope, legacy.cid);
|
|
25498
|
+
return { metadata: metadataFor(legacy.envelope, legacy.origin), link: { origin: legacy.origin, cid: legacy.cid, kind: "compact" } };
|
|
25499
|
+
}
|
|
25256
25500
|
const resolved = await resolvePolicyShare(link4, options);
|
|
25257
25501
|
return { metadata: metadataFor(resolved.envelope, resolved.origin), link: { origin: resolved.origin, cid: resolved.cid, kind: "policy" } };
|
|
25258
25502
|
}
|
|
25259
25503
|
async function receiveShare(link4, options = {}) {
|
|
25504
|
+
if (isLegacyV2Link(link4)) return receiveLegacyV2(link4, options);
|
|
25260
25505
|
const resolved = await resolvePolicyShare(link4, options);
|
|
25261
25506
|
return { state: "authorization-required", method: resolved.envelope.recipientMatcher.kind === "recipientDid" ? "openkey-device" : "email-claim" };
|
|
25262
25507
|
}
|
|
25508
|
+
function isLegacyV2Link(link4) {
|
|
25509
|
+
try {
|
|
25510
|
+
const url = new URL(link4);
|
|
25511
|
+
return /^\/s\/bafkrei[a-z2-7]{52}$/.test(url.pathname) && url.hash.startsWith("#k=");
|
|
25512
|
+
} catch {
|
|
25513
|
+
return false;
|
|
25514
|
+
}
|
|
25515
|
+
}
|
|
25516
|
+
async function receiveLegacyV2(link4, options) {
|
|
25517
|
+
const resolved = await resolveLegacyV2(link4, options);
|
|
25518
|
+
const { envelope } = resolved;
|
|
25519
|
+
await verifyLegacyV2(envelope, options);
|
|
25520
|
+
options.onResolvedAddressedEnvelope?.(envelope, resolved.cid);
|
|
25521
|
+
const method = envelope.recipientMatcher.kind === "recipientDid" ? "openkey-device" : "email-claim";
|
|
25522
|
+
if (options.authorization === void 0) return { state: "authorization-required", method };
|
|
25523
|
+
const result = options.authorizationResumeToken === void 0 ? await options.authorization.begin({ envelope, method }) : await options.authorization.resume({
|
|
25524
|
+
envelope,
|
|
25525
|
+
method,
|
|
25526
|
+
resumeToken: options.authorizationResumeToken,
|
|
25527
|
+
...options.authorizationProof === void 0 ? {} : { proof: options.authorizationProof }
|
|
25528
|
+
});
|
|
25529
|
+
if (result.state === "authorization-required") return result;
|
|
25530
|
+
if (result.state === "denied") throw new ShareReceiveError("authorization-denied", "legacy share authorization was denied");
|
|
25531
|
+
if (result.value === null || typeof result.value !== "object" || !(result.value.bytes instanceof Uint8Array) || result.value.proof === void 0 || options.authorization.verifyResult === void 0) {
|
|
25532
|
+
throw new ShareReceiveError("content-integrity-failed", "legacy content lacks a verifiable proof");
|
|
25533
|
+
}
|
|
25534
|
+
const authorized = result.value;
|
|
25535
|
+
try {
|
|
25536
|
+
if (!await options.authorization.verifyResult({ envelope, value: authorized, proof: authorized.proof })) throw new Error("proof");
|
|
25537
|
+
} catch {
|
|
25538
|
+
throw new ShareReceiveError("content-integrity-failed", "legacy content proof is invalid");
|
|
25539
|
+
}
|
|
25540
|
+
const bytes3 = authorized.bytes.slice();
|
|
25541
|
+
if (bytes3.length > (options.maxContentBlobBytes ?? DEFAULT_MAX_CONTENT_BLOB_BYTES)) throw new ShareReceiveError("max-bytes-exceeded", "legacy content exceeds the size limit");
|
|
25542
|
+
const bodyDigest = toBase64Url2(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes3)));
|
|
25543
|
+
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) {
|
|
25544
|
+
throw new ShareReceiveError("content-integrity-failed", "legacy content binding does not match");
|
|
25545
|
+
}
|
|
25546
|
+
let text;
|
|
25547
|
+
try {
|
|
25548
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(bytes3);
|
|
25549
|
+
} catch {
|
|
25550
|
+
}
|
|
25551
|
+
return { metadata: metadataFor(envelope, resolved.origin), link: { origin: resolved.origin, cid: resolved.cid, kind: "compact" }, bytes: bytes3, ...text === void 0 ? {} : { text } };
|
|
25552
|
+
}
|
|
25263
25553
|
var SHARE_CONTENT_LIMIT = 100 * 1024 * 1024;
|
|
25264
25554
|
var SHARE_PUBLISH_RESULT_VERSION = 1;
|
|
25265
25555
|
var DEFAULT_SHARE_LIFETIME_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
@@ -25747,7 +26037,10 @@ async function publishTargetShare(input) {
|
|
|
25747
26037
|
expiresAt,
|
|
25748
26038
|
expiryWasExplicit: input.expiryWasExplicit ?? input.expiresAt !== void 0,
|
|
25749
26039
|
origin: input.origin,
|
|
25750
|
-
...input.notify === void 0 ? {} : { notify: input.notify }
|
|
26040
|
+
...input.notify === void 0 ? {} : { notify: input.notify },
|
|
26041
|
+
...input.credentialDescriptor === void 0 ? {} : { credentialDescriptor: input.credentialDescriptor },
|
|
26042
|
+
...input.issuerDid === void 0 ? {} : { issuerDid: input.issuerDid },
|
|
26043
|
+
...input.issuerKid === void 0 ? {} : { issuerKid: input.issuerKid }
|
|
25751
26044
|
});
|
|
25752
26045
|
}
|
|
25753
26046
|
var POLICY_V1_DOMAIN = "xyz.tinycloud.policy/policy/v1\0";
|
|
@@ -25833,7 +26126,7 @@ async function createPolicy(options, capabilities) {
|
|
|
25833
26126
|
function addressedPlan(options) {
|
|
25834
26127
|
const capabilities = sortCanonical([
|
|
25835
26128
|
{ kind: "kv", resource: options.contentSource.kvResource, selector: options.resource.kind, actions: [...options.policyActions] },
|
|
25836
|
-
{ kind: "encryption", resource: options.contentSource.encryptionNetwork
|
|
26129
|
+
{ kind: "encryption", resource: options.contentSource.encryptionKeyPath === void 0 ? options.contentSource.encryptionNetwork : `${options.contentSource.encryptionNetwork}/${options.contentSource.encryptionKeyPath}`, action: "tinycloud.encryption/decrypt" }
|
|
25837
26130
|
]);
|
|
25838
26131
|
return {
|
|
25839
26132
|
expiry: rfc3339Seconds(options.expiresAt),
|
|
@@ -26158,6 +26451,7 @@ async function addressedShareBodyUpperBound(input) {
|
|
|
26158
26451
|
kvResource: `${input.spaceId}/kv/${path}`,
|
|
26159
26452
|
selector: input.resource.kind,
|
|
26160
26453
|
encryptionNetwork: input.encryptionNetwork,
|
|
26454
|
+
...input.encryptionKeyPath === void 0 ? {} : { encryptionKeyPath: input.encryptionKeyPath },
|
|
26161
26455
|
encryptedSymmetricKeyDigestHex: "f".repeat(64),
|
|
26162
26456
|
keyVersion: Number.MAX_SAFE_INTEGER,
|
|
26163
26457
|
mode: "immutable",
|
|
@@ -28202,6 +28496,23 @@ var unifiedEncryptionNetworkSchema3 = external_exports2.string().refine((value)
|
|
|
28202
28496
|
const network = separator < 0 ? "" : rest.slice(separator + 1);
|
|
28203
28497
|
return owner.startsWith("did:") && owner.length > 4 && network.length > 0 && !/[:/%?#\s]/.test(network);
|
|
28204
28498
|
}, { message: "expected canonical TinyCloud encryption network" });
|
|
28499
|
+
var unifiedEncryptionResourceSchema3 = external_exports2.string().refine((value) => {
|
|
28500
|
+
const prefix = "urn:tinycloud:encryption:";
|
|
28501
|
+
if (!value.startsWith(prefix)) return false;
|
|
28502
|
+
const match = /^(did:.+):([^/:?#\s]+)(?:\/(.+))?$/.exec(value.slice(prefix.length));
|
|
28503
|
+
if (match === null) return false;
|
|
28504
|
+
const path = match[3];
|
|
28505
|
+
return path === void 0 || path.split("/").every(
|
|
28506
|
+
(segment) => segment.length > 0 && segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment)
|
|
28507
|
+
);
|
|
28508
|
+
}, { message: "expected a canonical TinyCloud encryption resource" });
|
|
28509
|
+
var unifiedEncryptionKeyPathSchema3 = external_exports2.string().regex(/^share\/[^/]+$/).refine(
|
|
28510
|
+
(value) => {
|
|
28511
|
+
const segment = value.slice("share/".length);
|
|
28512
|
+
return segment !== "." && segment !== ".." && !/[\\:%?#\s]/.test(segment);
|
|
28513
|
+
},
|
|
28514
|
+
{ message: "expected an exact share key path" }
|
|
28515
|
+
);
|
|
28205
28516
|
var unifiedKvCapabilitySchema3 = external_exports2.object({
|
|
28206
28517
|
kind: external_exports2.literal("kv"),
|
|
28207
28518
|
resource: unifiedResourceSchema3,
|
|
@@ -28215,7 +28526,7 @@ var unifiedKvCapabilitySchema3 = external_exports2.object({
|
|
|
28215
28526
|
}).strict();
|
|
28216
28527
|
var unifiedEncryptionCapabilitySchema3 = external_exports2.object({
|
|
28217
28528
|
kind: external_exports2.literal("encryption"),
|
|
28218
|
-
resource:
|
|
28529
|
+
resource: unifiedEncryptionResourceSchema3,
|
|
28219
28530
|
action: external_exports2.literal("tinycloud.encryption/decrypt")
|
|
28220
28531
|
}).strict();
|
|
28221
28532
|
var unifiedCapabilitySchema3 = external_exports2.union([unifiedKvCapabilitySchema3, unifiedEncryptionCapabilitySchema3]);
|
|
@@ -28224,6 +28535,7 @@ var unifiedContentSourceSchema3 = external_exports2.object({
|
|
|
28224
28535
|
kvResource: unifiedResourceSchema3,
|
|
28225
28536
|
selector: external_exports2.union([external_exports2.literal("exact"), external_exports2.literal("prefix")]),
|
|
28226
28537
|
encryptionNetwork: unifiedEncryptionNetworkSchema3,
|
|
28538
|
+
encryptionKeyPath: unifiedEncryptionKeyPathSchema3.optional(),
|
|
28227
28539
|
encryptedSymmetricKeyDigestHex: external_exports2.string().regex(/^[0-9a-f]{64}$/),
|
|
28228
28540
|
keyVersion: external_exports2.number().int().positive(),
|
|
28229
28541
|
mode: external_exports2.union([external_exports2.literal("mutable"), external_exports2.literal("immutable")]),
|
|
@@ -28341,16 +28653,21 @@ function validateV3Invariants3(value, ctx) {
|
|
|
28341
28653
|
if (value.attestedEnforcerBinding.nodeAudience !== value.target.nodeAudience || value.attestedEnforcerBinding.signature.signerDid !== value.attestedEnforcerBinding.nodeAudience || Date.parse(value.attestedEnforcerBinding.expiresAt) < Date.parse(value.expiry)) {
|
|
28342
28654
|
ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["attestedEnforcerBinding"], message: "enforcer binding does not cover the target and share lifetime" });
|
|
28343
28655
|
}
|
|
28344
|
-
|
|
28656
|
+
const encryptionKeyPath = value.contentSource.encryptionKeyPath;
|
|
28657
|
+
if (encryptionKeyPath !== void 0 && encryptionKeyPath !== `share/${value.contentSource.shareId}`) {
|
|
28658
|
+
ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["contentSource", "encryptionKeyPath"], message: "share key path must name this share" });
|
|
28659
|
+
}
|
|
28660
|
+
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) {
|
|
28345
28661
|
ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["contentSource"], message: "content source is not bound to policy" });
|
|
28346
28662
|
}
|
|
28347
28663
|
if (value.policy.signature.suite !== "Ed25519" || value.policy.signature.signerDid !== value.policy.ownerDid) {
|
|
28348
28664
|
ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["policy", "signature"], message: "policy must be signed by its owner" });
|
|
28349
28665
|
}
|
|
28350
28666
|
const kvCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "kv");
|
|
28351
|
-
const
|
|
28667
|
+
const expectedEncryptionResource = encryptionKeyPath === void 0 ? value.encryptionNetwork : `${value.encryptionNetwork}/${encryptionKeyPath}`;
|
|
28668
|
+
const decryptCapabilities = value.policy.capabilityCeiling.filter((capability) => capability.kind === "encryption" && capability.resource === expectedEncryptionResource && capability.action === "tinycloud.encryption/decrypt");
|
|
28352
28669
|
if (value.policy.capabilityCeiling.length !== 2 || kvCapabilities.length !== 1 || decryptCapabilities.length !== 1 || kvCapabilities[0]?.resource !== value.contentSource.kvResource || kvCapabilities[0]?.selector !== value.contentSource.selector) {
|
|
28353
|
-
ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain exact decrypt
|
|
28670
|
+
ctx.addIssue({ code: external_exports2.ZodIssueCode.custom, path: ["policy", "capabilityCeiling"], message: "policy ceiling must contain the exact decrypt resource" });
|
|
28354
28671
|
}
|
|
28355
28672
|
}
|
|
28356
28673
|
var unsignedShareEnvelopeV3Schema3 = unsignedShareEnvelopeV3BaseSchema3.superRefine(validateV3Invariants3);
|
|
@@ -28557,7 +28874,7 @@ function receiveJson(result, path) {
|
|
|
28557
28874
|
|
|
28558
28875
|
// src/share/io.ts
|
|
28559
28876
|
import { constants } from "fs";
|
|
28560
|
-
import { lstat as lstat6, mkdir as mkdir4, mkdtemp, open as
|
|
28877
|
+
import { lstat as lstat6, mkdir as mkdir4, mkdtemp, open as open8, readFile as readFile6, realpath as realpath2, stat as stat4, link as link2, rename as rename3, rm as rm5, unlink as unlink4 } from "fs/promises";
|
|
28561
28878
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
28562
28879
|
import { basename as basename2, join as join11, resolve as resolve4, sep as sep3 } from "path";
|
|
28563
28880
|
init_errors();
|
|
@@ -28633,7 +28950,7 @@ async function writeShareOutput(directory, filename, bytes, force) {
|
|
|
28633
28950
|
const outputDirectory = resolve4(directory);
|
|
28634
28951
|
await assertDirectory(outputDirectory);
|
|
28635
28952
|
const safeName = safeFilename(filename);
|
|
28636
|
-
const directoryHandle = await
|
|
28953
|
+
const directoryHandle = await open8(outputDirectory, constants.O_RDONLY | (constants.O_DIRECTORY ?? 0) | (constants.O_NOFOLLOW ?? 0));
|
|
28637
28954
|
const stableDirectory = await realpath2(outputDirectory);
|
|
28638
28955
|
const outputPath = join11(stableDirectory, safeName);
|
|
28639
28956
|
const directoryIdentity = await directoryHandle.stat();
|
|
@@ -28657,7 +28974,7 @@ async function writeShareOutput(directory, filename, bytes, force) {
|
|
|
28657
28974
|
if (error.code !== "ENOENT") throw error;
|
|
28658
28975
|
}
|
|
28659
28976
|
temporaryPath = stagingPath;
|
|
28660
|
-
handle = await
|
|
28977
|
+
handle = await open8(temporaryPath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0), 384);
|
|
28661
28978
|
await handle.writeFile(bytes);
|
|
28662
28979
|
await handle.close();
|
|
28663
28980
|
handle = void 0;
|
|
@@ -28807,6 +29124,38 @@ function createShareLinks(input) {
|
|
|
28807
29124
|
};
|
|
28808
29125
|
}
|
|
28809
29126
|
|
|
29127
|
+
// src/share/email-publish-v3.ts
|
|
29128
|
+
import {
|
|
29129
|
+
canonicalDigest,
|
|
29130
|
+
createEmailCredentialRequirement,
|
|
29131
|
+
credentialRequirementDigest,
|
|
29132
|
+
validateCredentialFlowDescriptor
|
|
29133
|
+
} from "@tinycloud/sdk-core";
|
|
29134
|
+
async function publishEmailShareV3(input) {
|
|
29135
|
+
const { contentSource } = input;
|
|
29136
|
+
if (input.resource.kind !== "exact" || contentSource.encryptionKeyPath !== `share/${input.shareId}` || !/^urn:tinycloud:encryption:did:pkh:eip155:\d+:0x[0-9a-fA-F]{40}:default$/.test(contentSource.encryptionNetwork)) {
|
|
29137
|
+
throw new TypeError("email share binding is invalid");
|
|
29138
|
+
}
|
|
29139
|
+
const descriptor = validateCredentialFlowDescriptor(input.credentialDescriptor);
|
|
29140
|
+
if (descriptor.issuer.did !== input.issuerDid || descriptor.issuer.kid !== input.issuerKid || !descriptor.claims.some((claim) => claim.name === "email" && claim.matching === "normalized_exact")) {
|
|
29141
|
+
throw new TypeError("email credential issuer or claim is invalid");
|
|
29142
|
+
}
|
|
29143
|
+
const profile = { id: descriptor.profile, version: 1 };
|
|
29144
|
+
const credentialType = { id: descriptor.format.vct, version: 1 };
|
|
29145
|
+
const requirement = createEmailCredentialRequirement({ email: input.target.address, profile, credentialType });
|
|
29146
|
+
const credentialRequirement = {
|
|
29147
|
+
type: "TinyCloudPolicyCredentialRequirement",
|
|
29148
|
+
version: 1,
|
|
29149
|
+
requirementDigest: await credentialRequirementDigest(requirement),
|
|
29150
|
+
descriptorDigest: await canonicalDigest(descriptor),
|
|
29151
|
+
issuerDid: input.issuerDid,
|
|
29152
|
+
issuerKid: input.issuerKid,
|
|
29153
|
+
profile,
|
|
29154
|
+
credentialType
|
|
29155
|
+
};
|
|
29156
|
+
return publishAddressedShare({ ...input, credentialRequirement });
|
|
29157
|
+
}
|
|
29158
|
+
|
|
28810
29159
|
// src/share/errors.ts
|
|
28811
29160
|
var SharePublishAuthorityError = class extends Error {
|
|
28812
29161
|
constructor(failure, options) {
|
|
@@ -28868,7 +29217,7 @@ init_profiles();
|
|
|
28868
29217
|
init_constants();
|
|
28869
29218
|
init_errors();
|
|
28870
29219
|
init_session_expired();
|
|
28871
|
-
import { chmod as chmod4, link as link3, open as
|
|
29220
|
+
import { chmod as chmod4, link as link3, open as open9, readFile as readFile7, stat as stat5, unlink as unlink5 } from "fs/promises";
|
|
28872
29221
|
import { dirname as dirname5, join as join12 } from "path";
|
|
28873
29222
|
import { createHash } from "crypto";
|
|
28874
29223
|
import { writeJsonAtomic as writeJsonAtomic2 } from "@tinycloud/operations/state";
|
|
@@ -29225,7 +29574,7 @@ function createEncryptedProfileHistory(profileName, sessionSigner) {
|
|
|
29225
29574
|
await link3(found.file, target);
|
|
29226
29575
|
await chmod4(target, PRIVATE_HISTORY_MODE);
|
|
29227
29576
|
await unlink5(found.file);
|
|
29228
|
-
const directory = await
|
|
29577
|
+
const directory = await open9(dirname5(found.file), "r");
|
|
29229
29578
|
try {
|
|
29230
29579
|
await directory.sync();
|
|
29231
29580
|
} finally {
|
|
@@ -29428,8 +29777,15 @@ function createShareAuthorityAdapters(input = {}) {
|
|
|
29428
29777
|
profileName: activeProfileName2
|
|
29429
29778
|
});
|
|
29430
29779
|
}
|
|
29431
|
-
|
|
29432
|
-
|
|
29780
|
+
if (targetInput.target.kind === "email" && (targetInput.credentialDescriptor === void 0 || targetInput.issuerDid === void 0 || targetInput.issuerKid === void 0)) {
|
|
29781
|
+
throw new TypeError("email credential descriptor and issuer are required");
|
|
29782
|
+
}
|
|
29783
|
+
const addressedEncryptionNetwork = targetInput.target.kind === "bearer" ? void 0 : targetInput.target.kind === "email" ? node.getDefaultEncryptionNetworkId() : node.getEncryptionNetworkIdForSpace(ownerSpaceId);
|
|
29784
|
+
const emailKeyPath = targetInput.target.kind === "email" ? `share/${crypto.randomUUID().replaceAll("-", "")}` : void 0;
|
|
29785
|
+
if (addressedEncryptionNetwork !== void 0 && node.isSessionOnly && !sessionHoldsEncryptionDecrypt(
|
|
29786
|
+
node,
|
|
29787
|
+
emailKeyPath === void 0 ? addressedEncryptionNetwork : `${addressedEncryptionNetwork}/${emailKeyPath}`
|
|
29788
|
+
)) {
|
|
29433
29789
|
throw new SharePublishAuthorityError({ kind: "decrypt-not-held" });
|
|
29434
29790
|
}
|
|
29435
29791
|
const config = await publicConfig();
|
|
@@ -29438,7 +29794,7 @@ function createShareAuthorityAdapters(input = {}) {
|
|
|
29438
29794
|
if (targetInput.notify === true && targetInput.target.kind === "emailDomain") {
|
|
29439
29795
|
await assertDomainDeliveryForOrigin(activeNode.origin, "publish");
|
|
29440
29796
|
}
|
|
29441
|
-
const shareId = crypto.randomUUID().replaceAll("-", "");
|
|
29797
|
+
const shareId = emailKeyPath?.slice("share/".length) ?? crypto.randomUUID().replaceAll("-", "");
|
|
29442
29798
|
const files = targetInput.files === void 0 || targetInput.files.length === 0 ? [{ bytes: targetInput.source, filename: targetInput.filename, mediaType: targetInput.mediaType }] : targetInput.files;
|
|
29443
29799
|
const resourceKind = targetInput.resourceKind ?? "exact";
|
|
29444
29800
|
if (targetInput.target.kind === "bearer") {
|
|
@@ -29560,6 +29916,7 @@ function createShareAuthorityAdapters(input = {}) {
|
|
|
29560
29916
|
resource: { kind: resourceKind },
|
|
29561
29917
|
storage: artifact === void 0 ? { path: resourcePath } : { artifactId: artifact.id, storageName },
|
|
29562
29918
|
encryptionNetwork: addressedEncryptionNetwork,
|
|
29919
|
+
...emailKeyPath === void 0 ? {} : { encryptionKeyPath: emailKeyPath },
|
|
29563
29920
|
actions,
|
|
29564
29921
|
policyActions,
|
|
29565
29922
|
...deliveryEmail === void 0 ? {} : { deliveryEmail },
|
|
@@ -29575,8 +29932,14 @@ function createShareAuthorityAdapters(input = {}) {
|
|
|
29575
29932
|
throw new SharePublishAuthorityError({ kind: rejected ? "registry-rejected" : "registry-unavailable" });
|
|
29576
29933
|
}
|
|
29577
29934
|
const encryptionNetwork = addressedEncryptionNetwork;
|
|
29578
|
-
const encrypted = await node.encryption.encryptToNetwork(encryptionNetwork, file.bytes, {
|
|
29935
|
+
const encrypted = await node.encryption.encryptToNetwork(encryptionNetwork, file.bytes, {
|
|
29936
|
+
...emailKeyPath === void 0 ? {} : { keyPath: emailKeyPath },
|
|
29937
|
+
metadata: { contentType: mediaType }
|
|
29938
|
+
});
|
|
29579
29939
|
if (!encrypted.ok) throw new Error("addressed source encryption was rejected");
|
|
29940
|
+
if (emailKeyPath !== void 0 && (encrypted.data.networkId !== encryptionNetwork || encrypted.data.keyPath !== emailKeyPath)) {
|
|
29941
|
+
throw new Error("email share encryption was rejected");
|
|
29942
|
+
}
|
|
29580
29943
|
const storedBytes = new TextEncoder().encode(canonicalize4(encrypted.data));
|
|
29581
29944
|
let artifactVersion;
|
|
29582
29945
|
if (artifact !== void 0) {
|
|
@@ -29609,6 +29972,7 @@ function createShareAuthorityAdapters(input = {}) {
|
|
|
29609
29972
|
kvResource: `${ownerSpaceId}/kv/${resourcePath}`,
|
|
29610
29973
|
selector: resourceKind,
|
|
29611
29974
|
encryptionNetwork: encrypted.data.networkId,
|
|
29975
|
+
...emailKeyPath === void 0 ? {} : { encryptionKeyPath: emailKeyPath },
|
|
29612
29976
|
encryptedSymmetricKeyDigestHex: encrypted.data.encryptedSymmetricKeyHash,
|
|
29613
29977
|
keyVersion: encrypted.data.keyVersion,
|
|
29614
29978
|
mode: "immutable",
|
|
@@ -29650,7 +30014,13 @@ function createShareAuthorityAdapters(input = {}) {
|
|
|
29650
30014
|
};
|
|
29651
30015
|
let published;
|
|
29652
30016
|
try {
|
|
29653
|
-
published = await
|
|
30017
|
+
published = prepared.target.kind === "email" ? await publishEmailShareV3({
|
|
30018
|
+
...addressedOptions,
|
|
30019
|
+
target: prepared.target,
|
|
30020
|
+
credentialDescriptor: targetInput.credentialDescriptor,
|
|
30021
|
+
issuerDid: targetInput.issuerDid,
|
|
30022
|
+
issuerKid: targetInput.issuerKid
|
|
30023
|
+
}) : await publishAddressedShare(addressedOptions);
|
|
29654
30024
|
} catch (error) {
|
|
29655
30025
|
if (!(error instanceof AddressedShareOverLimitError)) throw error;
|
|
29656
30026
|
const remaining = await removeOverLimitShare({
|
|
@@ -29843,7 +30213,7 @@ init_profiles();
|
|
|
29843
30213
|
|
|
29844
30214
|
// src/share/html-folder.ts
|
|
29845
30215
|
import { constants as constants2, existsSync } from "fs";
|
|
29846
|
-
import { lstat as lstat7, open as
|
|
30216
|
+
import { lstat as lstat7, open as open10, readdir as readdir6, realpath as realpath3, stat as stat6 } from "fs/promises";
|
|
29847
30217
|
import { basename as basename3, dirname as dirname6, isAbsolute, join as join13, relative as relativePath, resolve as resolve5, sep as sep4 } from "path";
|
|
29848
30218
|
|
|
29849
30219
|
// ../share-sdk/dist/html-bundle.js
|
|
@@ -30198,9 +30568,9 @@ function scanMarkup(html, options) {
|
|
|
30198
30568
|
if (comments > MAX_ARTIFACT_COMMENTS) fail("limit", "artifact page has too many comments");
|
|
30199
30569
|
};
|
|
30200
30570
|
while (index < length5) {
|
|
30201
|
-
const
|
|
30202
|
-
if (
|
|
30203
|
-
index =
|
|
30571
|
+
const open11 = html.indexOf("<", index);
|
|
30572
|
+
if (open11 === -1) return;
|
|
30573
|
+
index = open11 + 1;
|
|
30204
30574
|
const next = html[index] ?? "";
|
|
30205
30575
|
if (html.startsWith("!--", index)) {
|
|
30206
30576
|
countComment();
|
|
@@ -30801,7 +31171,7 @@ async function readRegularFile(root, path, relative, remaining, limit, hooks = {
|
|
|
30801
31171
|
await hooks.beforeOpen?.();
|
|
30802
31172
|
before = await lstat7(path);
|
|
30803
31173
|
await hooks.afterLstat?.();
|
|
30804
|
-
handle = await
|
|
31174
|
+
handle = await open10(path, constants2.O_RDONLY | (constants2.O_NOFOLLOW ?? 0) | (constants2.O_NONBLOCK ?? 0));
|
|
30805
31175
|
} catch (error) {
|
|
30806
31176
|
if (error.code === "ELOOP") return problem("symbolic link");
|
|
30807
31177
|
if (isRaceError(error)) return problem(CHANGED);
|
|
@@ -30860,7 +31230,7 @@ async function listFolderDirectory(root, absolute, relative, hooks = {}) {
|
|
|
30860
31230
|
if (before.isSymbolicLink()) return problem("symbolic link");
|
|
30861
31231
|
if (!before.isDirectory()) return problem(CHANGED);
|
|
30862
31232
|
await hooks.afterLstat?.();
|
|
30863
|
-
const handle = await
|
|
31233
|
+
const handle = await open10(absolute, constants2.O_RDONLY | (constants2.O_DIRECTORY ?? 0) | (constants2.O_NOFOLLOW ?? 0));
|
|
30864
31234
|
try {
|
|
30865
31235
|
await hooks.afterOpen?.();
|
|
30866
31236
|
const info = await handle.stat();
|
|
@@ -31287,7 +31657,7 @@ function assertAggregateInputLimit(inputs, maxBytes) {
|
|
|
31287
31657
|
}
|
|
31288
31658
|
function registerShareCommand(program2) {
|
|
31289
31659
|
const share = program2.command("share").description("Publish and consume TinyCloud Share links");
|
|
31290
|
-
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_ORIGIN).action(async (files, options, command) => {
|
|
31660
|
+
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_ORIGIN).action(async (files, options, command) => {
|
|
31291
31661
|
try {
|
|
31292
31662
|
const json = jsonOutput(options, command);
|
|
31293
31663
|
if (options.ongoing === true) throw new CLIError("UNSUPPORTED_TARGET", ONGOING_UNAVAILABLE, 2);
|
|
@@ -31295,6 +31665,15 @@ function registerShareCommand(program2) {
|
|
|
31295
31665
|
if (files.length === 0 || files.includes("-") && files.length > 1) throw new CLIError("INVALID_ARGUMENT", "stdin must be the only publish input", 2);
|
|
31296
31666
|
const name = files.length === 1 ? options.name : void 0;
|
|
31297
31667
|
const target = parseShareTarget(options.to);
|
|
31668
|
+
const actions = requestedActions(options.action);
|
|
31669
|
+
if (options.notify === true && !actions.includes("read")) throw new CLIError("INVALID_ARGUMENT", "--notify requires the read action; nothing was shared", 2);
|
|
31670
|
+
if (target.kind === "email" && (!options.credentialDescriptor || !options.issuerDid || !options.issuerKid)) {
|
|
31671
|
+
throw new CLIError("INVALID_ARGUMENT", "email publication requires --credential-descriptor, --issuer-did, and --issuer-kid", 2);
|
|
31672
|
+
}
|
|
31673
|
+
if (target.kind !== "email" && (options.credentialDescriptor || options.issuerDid || options.issuerKid)) {
|
|
31674
|
+
throw new CLIError("INVALID_ARGUMENT", "credential options require an exact email target", 2);
|
|
31675
|
+
}
|
|
31676
|
+
const credentialDescriptor = target.kind === "email" ? JSON.parse(await readFile8(options.credentialDescriptor, "utf8")) : void 0;
|
|
31298
31677
|
const folder = files.length === 1 && target.kind === "bearer" && options.prefix !== true && await isDirectoryInput(files[0]);
|
|
31299
31678
|
let inputs;
|
|
31300
31679
|
if (folder) {
|
|
@@ -31311,13 +31690,11 @@ function registerShareCommand(program2) {
|
|
|
31311
31690
|
inputs = await Promise.all(files.map((file) => readShareInput(file, name, maxBytes)));
|
|
31312
31691
|
}
|
|
31313
31692
|
assertAggregateInputLimit(inputs, maxBytes);
|
|
31314
|
-
const actions = requestedActions(options.action);
|
|
31315
31693
|
if (options.prefix && target.kind === "bearer") throw new CLIError("INVALID_ARGUMENT", "--prefix requires an addressed target", 2);
|
|
31316
31694
|
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);
|
|
31317
31695
|
if (inputs.length > 1 && target.kind === "bearer") throw new CLIError("UNSUPPORTED_LINK", "multiple files require an addressed target", 2);
|
|
31318
31696
|
const artifact = artifactRequest(options, actions, inputs.length);
|
|
31319
31697
|
if (inputs.length > 1 && !options.prefix) throw new CLIError("INVALID_ARGUMENT", "multiple files require --prefix", 2);
|
|
31320
|
-
if (options.notify === true && !actions.includes("read")) throw new CLIError("INVALID_ARGUMENT", "--notify requires the read action; nothing was shared", 2);
|
|
31321
31698
|
if (options.notifyTo !== void 0 && (options.notify !== true || target.kind !== "emailDomain")) {
|
|
31322
31699
|
throw new CLIError("INVALID_ARGUMENT", "--notify-to requires --to domain:<name> --notify", 2);
|
|
31323
31700
|
}
|
|
@@ -31347,6 +31724,7 @@ function registerShareCommand(program2) {
|
|
|
31347
31724
|
origin: options.viewerOrigin,
|
|
31348
31725
|
...maxBytes === void 0 ? {} : { maxBytes },
|
|
31349
31726
|
notify: options.notify === true,
|
|
31727
|
+
...credentialDescriptor === void 0 ? {} : { credentialDescriptor, issuerDid: options.issuerDid, issuerKid: options.issuerKid },
|
|
31350
31728
|
// The artifact request rides on the adapter call rather than through `publishTargetShare`, whose code is part of
|
|
31351
31729
|
// the browser bundles (the web-sdk size ceiling counts every byte it carries).
|
|
31352
31730
|
targetAdapter: artifact === void 0 || shareServices.targetAdapter === void 0 ? shareServices.targetAdapter : { publish: (request) => shareServices.targetAdapter.publish({ ...request, artifact }) }
|