@tinycloud/operations 0.5.0-beta.0 → 0.5.0-beta.2
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/coverage.json +9 -1
- package/dist/artifacts.d.cts +1 -1
- package/dist/artifacts.d.ts +1 -1
- package/dist/cli-runtime.cjs +788 -180
- package/dist/cli-runtime.cjs.map +1 -1
- package/dist/cli-runtime.d.cts +1 -1
- package/dist/cli-runtime.d.ts +1 -1
- package/dist/cli-runtime.js +786 -179
- package/dist/cli-runtime.js.map +1 -1
- package/dist/{contract-DfyrluLB.d.cts → contract-zRklonbR.d.cts} +6 -3
- package/dist/{contract-DfyrluLB.d.ts → contract-zRklonbR.d.ts} +6 -3
- package/dist/delegation-binding.cjs +7 -0
- package/dist/delegation-binding.cjs.map +1 -1
- package/dist/delegation-binding.d.cts +15 -3
- package/dist/delegation-binding.d.ts +15 -3
- package/dist/delegation-binding.js +6 -0
- package/dist/delegation-binding.js.map +1 -1
- package/dist/index.cjs +816 -181
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +812 -178
- package/dist/index.js.map +1 -1
- package/dist/profile.cjs +5 -0
- package/dist/profile.cjs.map +1 -1
- package/dist/profile.d.cts +14 -2
- package/dist/profile.d.ts +14 -2
- package/dist/profile.js +4 -0
- package/dist/profile.js.map +1 -1
- package/dist/recovery.cjs +67 -0
- package/dist/recovery.cjs.map +1 -0
- package/dist/recovery.d.cts +38 -0
- package/dist/recovery.d.ts +38 -0
- package/dist/recovery.js +39 -0
- package/dist/recovery.js.map +1 -0
- package/dist/secret-request.cjs +271 -0
- package/dist/secret-request.cjs.map +1 -0
- package/dist/secret-request.d.cts +119 -0
- package/dist/secret-request.d.ts +119 -0
- package/dist/secret-request.js +222 -0
- package/dist/secret-request.js.map +1 -0
- package/generated/operations.json +1838 -89
- package/package.json +15 -5
package/dist/index.js
CHANGED
|
@@ -298,6 +298,7 @@ var init_errors2 = __esm({
|
|
|
298
298
|
"PROFILE_NOT_FOUND",
|
|
299
299
|
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
300
300
|
"PROFILE_OWNER_OPT_IN_REQUIRED",
|
|
301
|
+
"PROFILE_MIGRATION_REQUIRED",
|
|
301
302
|
"AUTH_REQUIRED",
|
|
302
303
|
"PERMISSION_DENIED",
|
|
303
304
|
"SESSION_NOT_FOUND",
|
|
@@ -5243,6 +5244,45 @@ var init_artifacts = __esm({
|
|
|
5243
5244
|
}
|
|
5244
5245
|
});
|
|
5245
5246
|
|
|
5247
|
+
// src/recovery.ts
|
|
5248
|
+
function signInRecovery(profile, posture) {
|
|
5249
|
+
if (!SAFE_PROFILE_TOKEN.test(profile)) return { action: "sign_in" };
|
|
5250
|
+
const template = posture === "local-owner-key" ? SIGN_IN_TEMPLATES.local : posture === "delegate-session" ? SIGN_IN_TEMPLATES.scoped : SIGN_IN_TEMPLATES.openkey;
|
|
5251
|
+
return { action: "sign_in", command: template(profile) };
|
|
5252
|
+
}
|
|
5253
|
+
function migrationRecovery(profile) {
|
|
5254
|
+
return SAFE_PROFILE_TOKEN.test(profile) ? { action: "complete_migration", command: migrationCommand(profile), operationId: MIGRATION_OPERATION_ID } : { action: "complete_migration", operationId: MIGRATION_OPERATION_ID };
|
|
5255
|
+
}
|
|
5256
|
+
function isSafeRecovery(value, action) {
|
|
5257
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
5258
|
+
const record = value;
|
|
5259
|
+
if (record.action !== action) return false;
|
|
5260
|
+
const keys = Object.keys(record);
|
|
5261
|
+
const allowed = action === "sign_in" ? ["action", "command"] : ["action", "command", "operationId"];
|
|
5262
|
+
if (!keys.every((key) => allowed.includes(key))) return false;
|
|
5263
|
+
if (action === "complete_migration" && record.operationId !== MIGRATION_OPERATION_ID) return false;
|
|
5264
|
+
if (!("command" in record)) return true;
|
|
5265
|
+
const command = record.command;
|
|
5266
|
+
if (typeof command !== "string") return false;
|
|
5267
|
+
const profile = /^tc --profile (\S+) /.exec(command)?.[1];
|
|
5268
|
+
if (profile === void 0 || !SAFE_PROFILE_TOKEN.test(profile)) return false;
|
|
5269
|
+
return action === "sign_in" ? Object.values(SIGN_IN_TEMPLATES).some((template) => template(profile) === command) : migrationCommand(profile) === command;
|
|
5270
|
+
}
|
|
5271
|
+
var SAFE_PROFILE_TOKEN, MIGRATION_OPERATION_ID, SIGN_IN_TEMPLATES, migrationCommand;
|
|
5272
|
+
var init_recovery = __esm({
|
|
5273
|
+
"src/recovery.ts"() {
|
|
5274
|
+
"use strict";
|
|
5275
|
+
SAFE_PROFILE_TOKEN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
|
|
5276
|
+
MIGRATION_OPERATION_ID = "tinycloud.auth.capabilities";
|
|
5277
|
+
SIGN_IN_TEMPLATES = {
|
|
5278
|
+
local: (profile) => `tc --profile ${profile} auth login --method local`,
|
|
5279
|
+
openkey: (profile) => `tc --profile ${profile} auth login --method openkey`,
|
|
5280
|
+
scoped: (profile) => `tc --profile ${profile} auth login --method openkey --paste --manifest <manifest.json>`
|
|
5281
|
+
};
|
|
5282
|
+
migrationCommand = (profile) => `tc --profile ${profile} auth caps`;
|
|
5283
|
+
}
|
|
5284
|
+
});
|
|
5285
|
+
|
|
5246
5286
|
// src/secrets.ts
|
|
5247
5287
|
import * as nodeSdk from "@tinycloud/node-sdk";
|
|
5248
5288
|
function normalizeSecretsGetInput(input) {
|
|
@@ -5330,15 +5370,6 @@ function canonicalizeSpaceUri(space) {
|
|
|
5330
5370
|
}
|
|
5331
5371
|
return space;
|
|
5332
5372
|
}
|
|
5333
|
-
function buildSecretSetupUrl(reference) {
|
|
5334
|
-
const parameters = [
|
|
5335
|
-
["secret", reference.name],
|
|
5336
|
-
...reference.scope === void 0 ? [] : [["scope", reference.scope]],
|
|
5337
|
-
["space", reference.space]
|
|
5338
|
-
];
|
|
5339
|
-
const query = parameters.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
|
|
5340
|
-
return `${SECRET_MANAGER_BASE_URL}/app?${query}`;
|
|
5341
|
-
}
|
|
5342
5373
|
function canonicalizeSecretScope(scope) {
|
|
5343
5374
|
if (scope === void 0 || scope === "") return void 0;
|
|
5344
5375
|
const trimmed = scope.trim();
|
|
@@ -5366,13 +5397,12 @@ function invalidSecretInput() {
|
|
|
5366
5397
|
operationError("INPUT_INVALID", "The secret reference is invalid.")
|
|
5367
5398
|
);
|
|
5368
5399
|
}
|
|
5369
|
-
var DEFAULT_SECRETS_SPACE,
|
|
5400
|
+
var DEFAULT_SECRETS_SPACE, SECRET_NAME_RE2, RESERVED_SECRET_SCOPES;
|
|
5370
5401
|
var init_secrets = __esm({
|
|
5371
5402
|
"src/secrets.ts"() {
|
|
5372
5403
|
"use strict";
|
|
5373
5404
|
init_errors2();
|
|
5374
5405
|
DEFAULT_SECRETS_SPACE = "secrets";
|
|
5375
|
-
SECRET_MANAGER_BASE_URL = "https://secrets.tinycloud.xyz";
|
|
5376
5406
|
SECRET_NAME_RE2 = /^[A-Z][A-Z0-9_]*$/;
|
|
5377
5407
|
RESERVED_SECRET_SCOPES = /* @__PURE__ */ new Set(["default", "global"]);
|
|
5378
5408
|
}
|
|
@@ -5529,7 +5559,10 @@ async function importRequestBoundDelegation(context, input) {
|
|
|
5529
5559
|
delegation,
|
|
5530
5560
|
{ host }
|
|
5531
5561
|
);
|
|
5532
|
-
} catch {
|
|
5562
|
+
} catch (error) {
|
|
5563
|
+
if (typeof error === "object" && error !== null && "reason" in error && error.reason === "expired") {
|
|
5564
|
+
return expiredDelegation();
|
|
5565
|
+
}
|
|
5533
5566
|
return operationFailure(
|
|
5534
5567
|
"DELEGATION_REJECTED",
|
|
5535
5568
|
"The delegation was rejected by the TinyCloud runtime."
|
|
@@ -5547,55 +5580,67 @@ async function importRequestBoundDelegation(context, input) {
|
|
|
5547
5580
|
);
|
|
5548
5581
|
}
|
|
5549
5582
|
try {
|
|
5550
|
-
return await
|
|
5551
|
-
context.summary.profile
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
return {
|
|
5558
|
-
|
|
5559
|
-
|
|
5583
|
+
return await withProfileLock(context.summary.profile, async () => {
|
|
5584
|
+
const previousRecords = await readAdditionalDelegations(context.summary.profile);
|
|
5585
|
+
const result = await updateProfileStore(
|
|
5586
|
+
context.summary.profile,
|
|
5587
|
+
"additional-delegations",
|
|
5588
|
+
async (records) => {
|
|
5589
|
+
const state = await importState(context, input.requestId);
|
|
5590
|
+
if (!state.ok) return { records, result: state.result };
|
|
5591
|
+
if (state.sessionDid !== sessionDid || normalizeHost(state.host) !== normalizeHost(host)) {
|
|
5592
|
+
return {
|
|
5593
|
+
records,
|
|
5594
|
+
result: audienceMismatch(state.sessionDid, sessionDid)
|
|
5595
|
+
};
|
|
5596
|
+
}
|
|
5597
|
+
if (state.request.profile !== context.summary.profile || !samePrincipal(state.request.sessionDid, sessionDid) || normalizeHost(state.request.host) !== normalizeHost(host) || JSON.stringify(canonicalizeOperationCapabilities(state.request.requested, resolveSpace2)) !== JSON.stringify(canonicalizeOperationCapabilities(request.requested, resolveSpace2))) {
|
|
5598
|
+
return {
|
|
5599
|
+
records,
|
|
5600
|
+
result: operationFailure(
|
|
5601
|
+
"DELEGATION_ARTIFACT_INVALID",
|
|
5602
|
+
"The delegation does not reference a current stored request."
|
|
5603
|
+
)
|
|
5604
|
+
};
|
|
5605
|
+
}
|
|
5606
|
+
const record = {
|
|
5607
|
+
delegation: activated.delegation,
|
|
5608
|
+
permissions: activated.effectivePermissions,
|
|
5609
|
+
authorityRequest: {
|
|
5610
|
+
requestId: request.requestId,
|
|
5611
|
+
requested: request.requested
|
|
5612
|
+
}
|
|
5560
5613
|
};
|
|
5561
|
-
|
|
5562
|
-
|
|
5614
|
+
const position = records.findIndex((entry) => delegationCid(entry) === activated.cid);
|
|
5615
|
+
const alreadyPresent = position !== -1;
|
|
5616
|
+
const others = records.filter((entry) => delegationCid(entry) !== activated.cid);
|
|
5563
5617
|
return {
|
|
5564
|
-
records,
|
|
5565
|
-
result:
|
|
5566
|
-
"
|
|
5567
|
-
|
|
5568
|
-
|
|
5618
|
+
records: alreadyPresent ? [...others.slice(0, position), record, ...others.slice(position)] : [...records, record],
|
|
5619
|
+
result: {
|
|
5620
|
+
status: "ok",
|
|
5621
|
+
output: {
|
|
5622
|
+
cid: activated.cid,
|
|
5623
|
+
effectivePermissions,
|
|
5624
|
+
expiry: activated.expiry.toISOString(),
|
|
5625
|
+
audience: activated.audience,
|
|
5626
|
+
host: activated.host,
|
|
5627
|
+
activated: true,
|
|
5628
|
+
alreadyPresent
|
|
5629
|
+
}
|
|
5630
|
+
}
|
|
5569
5631
|
};
|
|
5570
5632
|
}
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
const position = records.findIndex((entry) => delegationCid(entry) === activated.cid);
|
|
5580
|
-
const alreadyPresent = position !== -1;
|
|
5581
|
-
const others = records.filter((entry) => delegationCid(entry) !== activated.cid);
|
|
5582
|
-
return {
|
|
5583
|
-
records: alreadyPresent ? [...others.slice(0, position), record, ...others.slice(position)] : [...records, record],
|
|
5584
|
-
result: {
|
|
5585
|
-
status: "ok",
|
|
5586
|
-
output: {
|
|
5587
|
-
cid: activated.cid,
|
|
5588
|
-
effectivePermissions,
|
|
5589
|
-
expiry: activated.expiry.toISOString(),
|
|
5590
|
-
audience: activated.audience,
|
|
5591
|
-
host: activated.host,
|
|
5592
|
-
activated: true,
|
|
5593
|
-
alreadyPresent
|
|
5594
|
-
}
|
|
5595
|
-
}
|
|
5596
|
-
};
|
|
5633
|
+
);
|
|
5634
|
+
if (activated.expiry.getTime() <= Date.now()) {
|
|
5635
|
+
await updateProfileStore(
|
|
5636
|
+
context.summary.profile,
|
|
5637
|
+
"additional-delegations",
|
|
5638
|
+
() => ({ records: previousRecords, result: void 0 })
|
|
5639
|
+
);
|
|
5640
|
+
return expiredDelegation();
|
|
5597
5641
|
}
|
|
5598
|
-
|
|
5642
|
+
return result;
|
|
5643
|
+
});
|
|
5599
5644
|
} catch {
|
|
5600
5645
|
return internalImportFailure();
|
|
5601
5646
|
}
|
|
@@ -5842,8 +5887,33 @@ var init_auth = __esm({
|
|
|
5842
5887
|
}
|
|
5843
5888
|
});
|
|
5844
5889
|
|
|
5845
|
-
// src/
|
|
5890
|
+
// src/kv-failure.ts
|
|
5846
5891
|
import { authorizationVerdictOf, validatedCapabilityOf } from "@tinycloud/sdk-services";
|
|
5892
|
+
function kvRefusal(error) {
|
|
5893
|
+
const verdict = authorizationVerdictOf(error);
|
|
5894
|
+
if (verdict === "unauthenticated") {
|
|
5895
|
+
return validatedCapabilityOf(error) === void 0 ? "auth_required" : "permission_denied";
|
|
5896
|
+
}
|
|
5897
|
+
return verdict === "forbidden" ? "permission_denied" : void 0;
|
|
5898
|
+
}
|
|
5899
|
+
function authorizationFailure(error, action) {
|
|
5900
|
+
switch (kvRefusal(error)) {
|
|
5901
|
+
case "auth_required":
|
|
5902
|
+
return { status: "error", error: operationError("AUTH_REQUIRED", `Authentication is required to ${action}.`) };
|
|
5903
|
+
case "permission_denied":
|
|
5904
|
+
return { status: "error", error: operationError("PERMISSION_DENIED", `Permission is denied to ${action}.`) };
|
|
5905
|
+
case void 0:
|
|
5906
|
+
return void 0;
|
|
5907
|
+
}
|
|
5908
|
+
}
|
|
5909
|
+
var init_kv_failure = __esm({
|
|
5910
|
+
"src/kv-failure.ts"() {
|
|
5911
|
+
"use strict";
|
|
5912
|
+
init_errors2();
|
|
5913
|
+
}
|
|
5914
|
+
});
|
|
5915
|
+
|
|
5916
|
+
// src/operations/exploration.ts
|
|
5847
5917
|
import { z as z4 } from "zod";
|
|
5848
5918
|
async function planAccountRegistryRead(context, path) {
|
|
5849
5919
|
return [{
|
|
@@ -6211,19 +6281,6 @@ function caughtKvFailure(error, action) {
|
|
|
6211
6281
|
if (error instanceof OperationInvocationError) return { status: "error", error: error.operationError };
|
|
6212
6282
|
return authorizationFailure(error, action) ?? nodeFailure(action);
|
|
6213
6283
|
}
|
|
6214
|
-
function authorizationFailure(error, action) {
|
|
6215
|
-
const verdict = authorizationVerdictOf(error);
|
|
6216
|
-
if (verdict === "unauthenticated") {
|
|
6217
|
-
if (validatedCapabilityOf(error) !== void 0) {
|
|
6218
|
-
return { status: "error", error: operationError("PERMISSION_DENIED", `Permission is denied to ${action}.`) };
|
|
6219
|
-
}
|
|
6220
|
-
return { status: "error", error: operationError("AUTH_REQUIRED", `Authentication is required to ${action}.`) };
|
|
6221
|
-
}
|
|
6222
|
-
if (verdict === "forbidden") {
|
|
6223
|
-
return { status: "error", error: operationError("PERMISSION_DENIED", `Permission is denied to ${action}.`) };
|
|
6224
|
-
}
|
|
6225
|
-
return void 0;
|
|
6226
|
-
}
|
|
6227
6284
|
function accountRegistryOutput(output) {
|
|
6228
6285
|
if (Buffer.byteLength(JSON.stringify(output), "utf8") > MAX_ACCOUNT_REGISTRY_BYTES) {
|
|
6229
6286
|
return {
|
|
@@ -6276,6 +6333,7 @@ var MAX_KV_VALUE_BYTES, DEFAULT_KV_LIST_LIMIT, MAX_ACCOUNT_REGISTRY_RECORDS, MAX
|
|
|
6276
6333
|
var init_exploration = __esm({
|
|
6277
6334
|
"src/operations/exploration.ts"() {
|
|
6278
6335
|
"use strict";
|
|
6336
|
+
init_kv_failure();
|
|
6279
6337
|
init_errors2();
|
|
6280
6338
|
init_secrets();
|
|
6281
6339
|
MAX_KV_VALUE_BYTES = 1024 * 1024;
|
|
@@ -6576,6 +6634,191 @@ var init_exploration = __esm({
|
|
|
6576
6634
|
}
|
|
6577
6635
|
});
|
|
6578
6636
|
|
|
6637
|
+
// src/secret-request.ts
|
|
6638
|
+
import * as nodeSdk3 from "@tinycloud/node-sdk";
|
|
6639
|
+
function canonicalSecretRequestOwner(did) {
|
|
6640
|
+
const match = typeof did === "string" ? OWNER_RE.exec(did) : null;
|
|
6641
|
+
if (match === null || !Number.isSafeInteger(Number(match[1]))) throw invalidOwner();
|
|
6642
|
+
const address = match[2];
|
|
6643
|
+
let checksummed;
|
|
6644
|
+
try {
|
|
6645
|
+
checksummed = nodeSdk3.canonicalizeAddress(address);
|
|
6646
|
+
} catch {
|
|
6647
|
+
throw invalidOwner();
|
|
6648
|
+
}
|
|
6649
|
+
if (/[A-F]/.test(address.slice(2)) && address !== checksummed) throw invalidOwner();
|
|
6650
|
+
return `did:pkh:eip155:${match[1]}:${checksummed}`;
|
|
6651
|
+
}
|
|
6652
|
+
function buildSecretRequestUrl(input) {
|
|
6653
|
+
const owner = canonicalSecretRequestOwner(input.owner);
|
|
6654
|
+
const names = validSecretNames(input.names);
|
|
6655
|
+
if (input.scope !== void 0) assertSecretRequestScope(input.scope);
|
|
6656
|
+
const requester = input.requester;
|
|
6657
|
+
if (requester !== void 0 && (typeof requester !== "string" || requester.length === 0 || requester.length > MAX_REQUESTER_LENGTH || CONTROL_OR_FORMAT.test(requester))) {
|
|
6658
|
+
throw new SecretRequestInputError("INVALID_REQUESTER", "The requester label must be 1-64 characters with no control or format characters.");
|
|
6659
|
+
}
|
|
6660
|
+
if (input.returnTo !== void 0 && !isAllowedReturnTo(input.returnTo)) {
|
|
6661
|
+
throw new SecretRequestInputError("INVALID_RETURN_TO", "The return address is not an allowed Secret Manager callback.");
|
|
6662
|
+
}
|
|
6663
|
+
if (input.state !== void 0 && !STATE_RE.test(input.state)) {
|
|
6664
|
+
throw new SecretRequestInputError("INVALID_STATE", "The state must be 1-128 characters of A-Z, a-z, 0-9, '.', '_', '~' or '-'.");
|
|
6665
|
+
}
|
|
6666
|
+
const parameters = [
|
|
6667
|
+
["owner", owner],
|
|
6668
|
+
...names.map((name) => ["name", name]),
|
|
6669
|
+
...input.scope === void 0 ? [] : [["scope", input.scope]],
|
|
6670
|
+
...input.requester === void 0 ? [] : [["requester", input.requester]],
|
|
6671
|
+
...input.returnTo === void 0 ? [] : [["return_to", input.returnTo]],
|
|
6672
|
+
...input.state === void 0 ? [] : [["state", input.state]],
|
|
6673
|
+
["v", "1"]
|
|
6674
|
+
];
|
|
6675
|
+
const url = `${SECRET_MANAGER_ORIGIN}/request?${parameters.map(([key, value]) => `${key}=${encodeURIComponent(value)}`).join("&")}`;
|
|
6676
|
+
if (url.length > SECRET_REQUEST_MAX_URL_LENGTH) {
|
|
6677
|
+
throw new SecretRequestInputError("REQUEST_LINK_TOO_LONG", `The request link is longer than ${SECRET_REQUEST_MAX_URL_LENGTH} characters.`);
|
|
6678
|
+
}
|
|
6679
|
+
return { url, owner, names, ...input.scope === void 0 ? {} : { scope: input.scope } };
|
|
6680
|
+
}
|
|
6681
|
+
function secretSetupFor(link2) {
|
|
6682
|
+
return {
|
|
6683
|
+
url: link2.url,
|
|
6684
|
+
names: [...link2.names],
|
|
6685
|
+
...link2.scope === void 0 ? {} : { scope: link2.scope },
|
|
6686
|
+
owner: link2.owner,
|
|
6687
|
+
action: "add_secret"
|
|
6688
|
+
};
|
|
6689
|
+
}
|
|
6690
|
+
function resolveSecretsOwner(profile, requestedOwner) {
|
|
6691
|
+
const resolved = profile.local ? ownerOrUndefined(profile.principalDid) : ownerOrUndefined(profile.ownerDid) ?? (profile.posture === "delegate-session" ? ownerOfSpace(profile.space) : void 0) ?? ownerOrUndefined(profile.principalDid);
|
|
6692
|
+
const requested = requestedOwner === void 0 ? void 0 : ownerOrUndefined(requestedOwner);
|
|
6693
|
+
if (resolved !== void 0) {
|
|
6694
|
+
if (requestedOwner !== void 0 && requested !== resolved) {
|
|
6695
|
+
return {
|
|
6696
|
+
ok: false,
|
|
6697
|
+
code: "OWNER_MISMATCH",
|
|
6698
|
+
message: "The requested owner is not the owner of this identity's secrets."
|
|
6699
|
+
};
|
|
6700
|
+
}
|
|
6701
|
+
return { ok: true, owner: resolved, local: profile.local };
|
|
6702
|
+
}
|
|
6703
|
+
if (requested !== void 0 && !profile.local) return { ok: true, owner: requested, local: false };
|
|
6704
|
+
return {
|
|
6705
|
+
ok: false,
|
|
6706
|
+
code: "OWNER_DID_UNKNOWN",
|
|
6707
|
+
message: "The owner of this identity's secrets is unknown. Pass the owner's did:pkh with --owner."
|
|
6708
|
+
};
|
|
6709
|
+
}
|
|
6710
|
+
function missingSecretRecovery(input) {
|
|
6711
|
+
if (input.local) {
|
|
6712
|
+
return { kind: "put_locally", hint: `Store it with: ${putCommands(input.names, input.scope)}` };
|
|
6713
|
+
}
|
|
6714
|
+
if (input.owner !== void 0) {
|
|
6715
|
+
try {
|
|
6716
|
+
return {
|
|
6717
|
+
kind: "add_secret",
|
|
6718
|
+
setup: secretSetupFor(buildSecretRequestUrl({
|
|
6719
|
+
owner: input.owner,
|
|
6720
|
+
names: input.names,
|
|
6721
|
+
...input.scope === void 0 ? {} : { scope: input.scope }
|
|
6722
|
+
}))
|
|
6723
|
+
};
|
|
6724
|
+
} catch {
|
|
6725
|
+
}
|
|
6726
|
+
}
|
|
6727
|
+
return {
|
|
6728
|
+
kind: "not_requestable",
|
|
6729
|
+
hint: `No Secret Manager request link can express this secret. The owner can store it with: ${putCommands(input.names, input.scope)}`
|
|
6730
|
+
};
|
|
6731
|
+
}
|
|
6732
|
+
function isValidSecretRequestScope(scope) {
|
|
6733
|
+
return scope.length <= MAX_SCOPE_LENGTH && SCOPE_RE.test(scope) && !RESERVED_SCOPES.includes(scope);
|
|
6734
|
+
}
|
|
6735
|
+
function isValidSecretName(name) {
|
|
6736
|
+
return name.length <= MAX_NAME_LENGTH && NAME_RE.test(name);
|
|
6737
|
+
}
|
|
6738
|
+
function validSecretNames(names) {
|
|
6739
|
+
for (const name of names) assertSecretName(name);
|
|
6740
|
+
const unique = dedupe(names);
|
|
6741
|
+
if (unique.length === 0) {
|
|
6742
|
+
throw new SecretRequestInputError("INVALID_SECRET_NAME", "At least one secret name is required.");
|
|
6743
|
+
}
|
|
6744
|
+
if (unique.length > SECRET_REQUEST_MAX_NAMES) {
|
|
6745
|
+
throw new SecretRequestInputError("TOO_MANY_SECRET_NAMES", `At most ${SECRET_REQUEST_MAX_NAMES} secret names can be requested at once.`);
|
|
6746
|
+
}
|
|
6747
|
+
return unique;
|
|
6748
|
+
}
|
|
6749
|
+
function assertSecretName(name) {
|
|
6750
|
+
if (typeof name !== "string" || !isValidSecretName(name)) {
|
|
6751
|
+
throw new SecretRequestInputError("INVALID_SECRET_NAME", "Secret names are 1-64 characters of A-Z, 0-9 and '_', starting with a letter.");
|
|
6752
|
+
}
|
|
6753
|
+
}
|
|
6754
|
+
function assertSecretRequestScope(scope) {
|
|
6755
|
+
if (typeof scope !== "string" || !isValidSecretRequestScope(scope)) {
|
|
6756
|
+
throw new SecretRequestInputError("INVALID_SECRET_SCOPE", "The scope must be a canonical scope of at most 64 characters, not default, global or scoped.");
|
|
6757
|
+
}
|
|
6758
|
+
}
|
|
6759
|
+
function isAllowedReturnTo(returnTo) {
|
|
6760
|
+
if (typeof returnTo !== "string" || returnTo.length > MAX_RETURN_TO_LENGTH) return false;
|
|
6761
|
+
let url;
|
|
6762
|
+
try {
|
|
6763
|
+
url = new URL(returnTo);
|
|
6764
|
+
} catch {
|
|
6765
|
+
return false;
|
|
6766
|
+
}
|
|
6767
|
+
if (url.username !== "" || url.password !== "") return false;
|
|
6768
|
+
return RETURN_TO_ORIGINS.includes(url.origin) || RETURN_TO_PREVIEW.test(url.origin);
|
|
6769
|
+
}
|
|
6770
|
+
function ownerOrUndefined(did) {
|
|
6771
|
+
if (did === void 0) return void 0;
|
|
6772
|
+
try {
|
|
6773
|
+
return canonicalSecretRequestOwner(did);
|
|
6774
|
+
} catch {
|
|
6775
|
+
return void 0;
|
|
6776
|
+
}
|
|
6777
|
+
}
|
|
6778
|
+
function ownerOfSpace(space) {
|
|
6779
|
+
const match = space === void 0 ? null : PKH_SPACE_RE.exec(space);
|
|
6780
|
+
if (match === null) return void 0;
|
|
6781
|
+
return ownerOrUndefined(`did:pkh:eip155:${match[1]}:${match[2].toLowerCase()}`);
|
|
6782
|
+
}
|
|
6783
|
+
function putCommands(names, scope) {
|
|
6784
|
+
return dedupe(names).map((name) => `tc secrets put ${name}${scope === void 0 ? "" : ` --scope ${scope}`} --stdin`).join("; ");
|
|
6785
|
+
}
|
|
6786
|
+
function dedupe(names) {
|
|
6787
|
+
return [...new Set(names)];
|
|
6788
|
+
}
|
|
6789
|
+
function invalidOwner() {
|
|
6790
|
+
return new SecretRequestInputError("INVALID_OWNER", "The owner must be a did:pkh:eip155 account with a valid EIP-55 address.");
|
|
6791
|
+
}
|
|
6792
|
+
var SECRET_MANAGER_ORIGIN, SECRET_REQUEST_MAX_NAMES, SECRET_REQUEST_MAX_URL_LENGTH, OWNER_RE, NAME_RE, MAX_NAME_LENGTH, SCOPE_RE, MAX_SCOPE_LENGTH, RESERVED_SCOPES, MAX_REQUESTER_LENGTH, CONTROL_OR_FORMAT, STATE_RE, MAX_RETURN_TO_LENGTH, RETURN_TO_ORIGINS, RETURN_TO_PREVIEW, PKH_SPACE_RE, SecretRequestInputError;
|
|
6793
|
+
var init_secret_request = __esm({
|
|
6794
|
+
"src/secret-request.ts"() {
|
|
6795
|
+
"use strict";
|
|
6796
|
+
SECRET_MANAGER_ORIGIN = "https://secrets.tinycloud.xyz";
|
|
6797
|
+
SECRET_REQUEST_MAX_NAMES = 10;
|
|
6798
|
+
SECRET_REQUEST_MAX_URL_LENGTH = 8192;
|
|
6799
|
+
OWNER_RE = /^did:pkh:eip155:([1-9][0-9]*):(0x[0-9a-fA-F]{40})$/;
|
|
6800
|
+
NAME_RE = /^[A-Z][A-Z0-9_]*$/;
|
|
6801
|
+
MAX_NAME_LENGTH = 64;
|
|
6802
|
+
SCOPE_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
6803
|
+
MAX_SCOPE_LENGTH = 64;
|
|
6804
|
+
RESERVED_SCOPES = ["default", "global", "scoped"];
|
|
6805
|
+
MAX_REQUESTER_LENGTH = 64;
|
|
6806
|
+
CONTROL_OR_FORMAT = /[\p{Cc}\p{Cf}]/u;
|
|
6807
|
+
STATE_RE = /^[A-Za-z0-9._~-]{1,128}$/;
|
|
6808
|
+
MAX_RETURN_TO_LENGTH = 2048;
|
|
6809
|
+
RETURN_TO_ORIGINS = ["https://feed.tinycloud.xyz"];
|
|
6810
|
+
RETURN_TO_PREVIEW = /^https:\/\/[a-z0-9-]+\.tinyfeed\.pages\.dev$/;
|
|
6811
|
+
PKH_SPACE_RE = /^tinycloud:pkh:eip155:([1-9][0-9]*):(0x[0-9a-fA-F]{40}):[^:]+$/;
|
|
6812
|
+
SecretRequestInputError = class extends Error {
|
|
6813
|
+
constructor(code, message) {
|
|
6814
|
+
super(message);
|
|
6815
|
+
this.name = "SecretRequestInputError";
|
|
6816
|
+
this.code = code;
|
|
6817
|
+
}
|
|
6818
|
+
};
|
|
6819
|
+
}
|
|
6820
|
+
});
|
|
6821
|
+
|
|
6579
6822
|
// src/operations/secrets-get.ts
|
|
6580
6823
|
import { z as z5 } from "zod";
|
|
6581
6824
|
async function planSecretGet(context, input) {
|
|
@@ -6643,27 +6886,14 @@ async function executeSecretGet(context, input) {
|
|
|
6643
6886
|
)
|
|
6644
6887
|
};
|
|
6645
6888
|
}
|
|
6646
|
-
return classifySecretRead(reference, result);
|
|
6889
|
+
return classifySecretRead(reference, result, context.runtime?.localOwner === true);
|
|
6647
6890
|
}
|
|
6648
|
-
function classifySecretRead(reference, result) {
|
|
6891
|
+
function classifySecretRead(reference, result, localOwner) {
|
|
6649
6892
|
switch (result.status) {
|
|
6650
6893
|
case "ok":
|
|
6651
6894
|
return { status: "ok", output: { value: result.value } };
|
|
6652
6895
|
case "not_found":
|
|
6653
|
-
return
|
|
6654
|
-
status: "setup_required",
|
|
6655
|
-
setup: {
|
|
6656
|
-
kind: "secret_manager",
|
|
6657
|
-
secret: {
|
|
6658
|
-
name: reference.name,
|
|
6659
|
-
...reference.scope === void 0 ? {} : { scope: reference.scope },
|
|
6660
|
-
space: reference.space
|
|
6661
|
-
},
|
|
6662
|
-
url: buildSecretSetupUrl(reference),
|
|
6663
|
-
message: "Enter this secret in Secret Manager, then retry the operation."
|
|
6664
|
-
},
|
|
6665
|
-
requiresCallerInput: true
|
|
6666
|
-
};
|
|
6896
|
+
return missingSecretOutcome(reference, localOwner);
|
|
6667
6897
|
case "permission_required":
|
|
6668
6898
|
return {
|
|
6669
6899
|
status: "authority_required",
|
|
@@ -6708,6 +6938,47 @@ function classifySecretRead(reference, result) {
|
|
|
6708
6938
|
error: operationError("SECRET_READ_FAILED", "The secret read failed.")
|
|
6709
6939
|
};
|
|
6710
6940
|
}
|
|
6941
|
+
function missingSecretOutcome(reference, localOwner) {
|
|
6942
|
+
const owner = ownerOfSpace(reference.space);
|
|
6943
|
+
const recovery = missingSecretRecovery({
|
|
6944
|
+
owner,
|
|
6945
|
+
local: localOwner,
|
|
6946
|
+
names: [reference.name],
|
|
6947
|
+
...reference.scope === void 0 ? {} : { scope: reference.scope }
|
|
6948
|
+
});
|
|
6949
|
+
if (recovery.kind === "add_secret") {
|
|
6950
|
+
const { setup } = recovery;
|
|
6951
|
+
return {
|
|
6952
|
+
status: "setup_required",
|
|
6953
|
+
setup: {
|
|
6954
|
+
kind: "secret_manager",
|
|
6955
|
+
action: setup.action,
|
|
6956
|
+
url: setup.url,
|
|
6957
|
+
names: setup.names,
|
|
6958
|
+
...setup.scope === void 0 ? {} : { scope: setup.scope },
|
|
6959
|
+
owner: setup.owner,
|
|
6960
|
+
secret: {
|
|
6961
|
+
name: reference.name,
|
|
6962
|
+
...reference.scope === void 0 ? {} : { scope: reference.scope },
|
|
6963
|
+
space: reference.space
|
|
6964
|
+
},
|
|
6965
|
+
message: "Ask the owner to add this secret in Secret Manager, then retry the operation."
|
|
6966
|
+
},
|
|
6967
|
+
requiresCallerInput: true
|
|
6968
|
+
};
|
|
6969
|
+
}
|
|
6970
|
+
return {
|
|
6971
|
+
status: "error",
|
|
6972
|
+
error: operationError("KV_NOT_FOUND", recovery.hint, {
|
|
6973
|
+
details: {
|
|
6974
|
+
names: [reference.name],
|
|
6975
|
+
...reference.scope === void 0 ? {} : { scope: reference.scope },
|
|
6976
|
+
...owner === void 0 ? {} : { owner },
|
|
6977
|
+
recoveryKind: recovery.kind
|
|
6978
|
+
}
|
|
6979
|
+
})
|
|
6980
|
+
};
|
|
6981
|
+
}
|
|
6711
6982
|
function resolveEncryptionNetworkId(node, space) {
|
|
6712
6983
|
const candidate = node;
|
|
6713
6984
|
let scoped;
|
|
@@ -6734,6 +7005,7 @@ var init_secrets_get = __esm({
|
|
|
6734
7005
|
"src/operations/secrets-get.ts"() {
|
|
6735
7006
|
"use strict";
|
|
6736
7007
|
init_errors2();
|
|
7008
|
+
init_secret_request();
|
|
6737
7009
|
init_secrets();
|
|
6738
7010
|
SecretsGetInputSchema = z5.object({
|
|
6739
7011
|
name: z5.string(),
|
|
@@ -6785,9 +7057,197 @@ var init_secrets_get = __esm({
|
|
|
6785
7057
|
}
|
|
6786
7058
|
});
|
|
6787
7059
|
|
|
7060
|
+
// src/operations/secrets-status.ts
|
|
7061
|
+
import { z as z6 } from "zod";
|
|
7062
|
+
import * as nodeSdk4 from "@tinycloud/node-sdk";
|
|
7063
|
+
async function executeSecretsStatus(context, input) {
|
|
7064
|
+
const runtime = context.runtime;
|
|
7065
|
+
const node = runtime?.node;
|
|
7066
|
+
if (runtime === void 0 || typeof node?.kvForSpace !== "function") {
|
|
7067
|
+
return {
|
|
7068
|
+
status: "error",
|
|
7069
|
+
error: operationError("NODE_ERROR", "The TinyCloud node could not check the secrets.", { retryable: true })
|
|
7070
|
+
};
|
|
7071
|
+
}
|
|
7072
|
+
const local = runtime.localOwner === true;
|
|
7073
|
+
const resolution = resolveSecretsOwner({
|
|
7074
|
+
local,
|
|
7075
|
+
posture: context.summary.posture,
|
|
7076
|
+
...context.summary.ownerDid === void 0 ? {} : { ownerDid: context.summary.ownerDid },
|
|
7077
|
+
...context.summary.principalDid === void 0 ? {} : { principalDid: context.summary.principalDid },
|
|
7078
|
+
...context.summary.space === void 0 ? {} : { space: context.summary.space }
|
|
7079
|
+
}, input.owner);
|
|
7080
|
+
if (!resolution.ok) {
|
|
7081
|
+
return {
|
|
7082
|
+
status: "error",
|
|
7083
|
+
error: operationError("INPUT_INVALID", resolution.message, { details: { reason: resolution.code } })
|
|
7084
|
+
};
|
|
7085
|
+
}
|
|
7086
|
+
const owner = resolution.owner;
|
|
7087
|
+
const skipped2 = runtime.skippedGrantReasons ?? [];
|
|
7088
|
+
const pkh = nodeSdk4.parsePkhDid(owner);
|
|
7089
|
+
const kv = node.kvForSpace(nodeSdk4.makePkhSpaceId(pkh.address, pkh.chainId, "secrets"));
|
|
7090
|
+
const outcomes = await Promise.all(input.names.map(async (name) => {
|
|
7091
|
+
const path = input.scope === void 0 ? `vault/secrets/${name}` : `vault/secrets/scoped/${input.scope}/${name}`;
|
|
7092
|
+
try {
|
|
7093
|
+
const result = await kv.get(path, { raw: true, prefix: "", timeout: PROBE_TIMEOUT_MS });
|
|
7094
|
+
if (result.ok) return "present";
|
|
7095
|
+
const error = result.error;
|
|
7096
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "KV_NOT_FOUND") {
|
|
7097
|
+
return "pending";
|
|
7098
|
+
}
|
|
7099
|
+
const refusal = kvRefusal(error);
|
|
7100
|
+
if (refusal === "auth_required") return "auth_required";
|
|
7101
|
+
if (refusal === "permission_denied") return "denied";
|
|
7102
|
+
return "unreachable";
|
|
7103
|
+
} catch {
|
|
7104
|
+
return "unreachable";
|
|
7105
|
+
}
|
|
7106
|
+
}));
|
|
7107
|
+
const namesWith = (outcome) => input.names.filter((_, index) => outcomes[index] === outcome);
|
|
7108
|
+
const present = namesWith("present");
|
|
7109
|
+
const pending = namesWith("pending");
|
|
7110
|
+
const denied = namesWith("denied");
|
|
7111
|
+
if (present.length + pending.length < input.names.length) {
|
|
7112
|
+
if (outcomes.includes("auth_required") || skipped2.includes("activation_unauthenticated")) {
|
|
7113
|
+
return {
|
|
7114
|
+
status: "error",
|
|
7115
|
+
error: operationError(
|
|
7116
|
+
"AUTH_REQUIRED",
|
|
7117
|
+
"The node did not accept this profile's stored session. Sign in again, then retry.",
|
|
7118
|
+
{ retryable: false, details: { recovery: signInRecovery(context.summary.profile, context.summary.posture) } }
|
|
7119
|
+
)
|
|
7120
|
+
};
|
|
7121
|
+
}
|
|
7122
|
+
if (outcomes.includes("unreachable") || skipped2.includes("activation_unavailable")) {
|
|
7123
|
+
return {
|
|
7124
|
+
status: "error",
|
|
7125
|
+
error: operationError("NODE_UNREACHABLE", "The TinyCloud node could not be reached.", { retryable: true })
|
|
7126
|
+
};
|
|
7127
|
+
}
|
|
7128
|
+
}
|
|
7129
|
+
const recovery = pending.length === 0 ? void 0 : missingSecretRecovery({
|
|
7130
|
+
owner,
|
|
7131
|
+
local,
|
|
7132
|
+
names: pending,
|
|
7133
|
+
...input.scope === void 0 ? {} : { scope: input.scope }
|
|
7134
|
+
});
|
|
7135
|
+
return {
|
|
7136
|
+
status: "ok",
|
|
7137
|
+
output: {
|
|
7138
|
+
status: denied.length > 0 ? "consent_required" : pending.length > 0 ? "pending" : "ready",
|
|
7139
|
+
owner,
|
|
7140
|
+
names: [...input.names],
|
|
7141
|
+
...input.scope === void 0 ? {} : { scope: input.scope },
|
|
7142
|
+
present,
|
|
7143
|
+
pending,
|
|
7144
|
+
denied,
|
|
7145
|
+
...recovery === void 0 ? {} : recovery.kind === "add_secret" ? { setup: recovery.setup } : { hint: recovery.hint }
|
|
7146
|
+
}
|
|
7147
|
+
};
|
|
7148
|
+
}
|
|
7149
|
+
var PROBE_TIMEOUT_MS, SECRETS_STATUS_DEADLINE_MS, SecretsStatusInputSchema, SecretSetupSchema, SecretsStatusOutputSchema, SECRETS_STATUS_POSTURES, SECRETS_STATUS_EXPOSURE, SECRETS_STATUS_SENSITIVITY, secretsStatusOperationDefinition, secretsStatusOperationDefinitions;
|
|
7150
|
+
var init_secrets_status = __esm({
|
|
7151
|
+
"src/operations/secrets-status.ts"() {
|
|
7152
|
+
"use strict";
|
|
7153
|
+
init_errors2();
|
|
7154
|
+
init_kv_failure();
|
|
7155
|
+
init_recovery();
|
|
7156
|
+
init_secret_request();
|
|
7157
|
+
init_secrets();
|
|
7158
|
+
PROBE_TIMEOUT_MS = 15e3;
|
|
7159
|
+
SECRETS_STATUS_DEADLINE_MS = 18e3;
|
|
7160
|
+
SecretsStatusInputSchema = z6.object({
|
|
7161
|
+
names: z6.array(z6.string()),
|
|
7162
|
+
scope: z6.string().optional(),
|
|
7163
|
+
owner: z6.string().optional()
|
|
7164
|
+
}).strict().superRefine((input, context) => {
|
|
7165
|
+
try {
|
|
7166
|
+
validSecretNames(input.names);
|
|
7167
|
+
} catch {
|
|
7168
|
+
context.addIssue({ code: z6.ZodIssueCode.custom, path: ["names"], message: "Invalid secret names." });
|
|
7169
|
+
}
|
|
7170
|
+
try {
|
|
7171
|
+
const scope = canonicalizeSecretScope(input.scope);
|
|
7172
|
+
if (scope !== void 0 && !isValidSecretRequestScope(scope)) throw new Error("invalid scope");
|
|
7173
|
+
} catch {
|
|
7174
|
+
context.addIssue({ code: z6.ZodIssueCode.custom, path: ["scope"], message: "Invalid secret scope." });
|
|
7175
|
+
}
|
|
7176
|
+
if (input.owner !== void 0) {
|
|
7177
|
+
try {
|
|
7178
|
+
canonicalSecretRequestOwner(input.owner);
|
|
7179
|
+
} catch {
|
|
7180
|
+
context.addIssue({ code: z6.ZodIssueCode.custom, path: ["owner"], message: "Invalid owner." });
|
|
7181
|
+
}
|
|
7182
|
+
}
|
|
7183
|
+
}).transform((input) => {
|
|
7184
|
+
const scope = canonicalizeSecretScope(input.scope);
|
|
7185
|
+
return {
|
|
7186
|
+
names: validSecretNames(input.names),
|
|
7187
|
+
...scope === void 0 ? {} : { scope },
|
|
7188
|
+
...input.owner === void 0 ? {} : { owner: canonicalSecretRequestOwner(input.owner) }
|
|
7189
|
+
};
|
|
7190
|
+
});
|
|
7191
|
+
SecretSetupSchema = z6.object({
|
|
7192
|
+
url: z6.string().url(),
|
|
7193
|
+
names: z6.array(z6.string()).min(1),
|
|
7194
|
+
scope: z6.string().optional(),
|
|
7195
|
+
owner: z6.string(),
|
|
7196
|
+
action: z6.literal("add_secret")
|
|
7197
|
+
}).strict();
|
|
7198
|
+
SecretsStatusOutputSchema = z6.object({
|
|
7199
|
+
status: z6.enum(["ready", "pending", "consent_required"]),
|
|
7200
|
+
owner: z6.string(),
|
|
7201
|
+
names: z6.array(z6.string()).min(1),
|
|
7202
|
+
scope: z6.string().optional(),
|
|
7203
|
+
present: z6.array(z6.string()),
|
|
7204
|
+
pending: z6.array(z6.string()),
|
|
7205
|
+
denied: z6.array(z6.string()),
|
|
7206
|
+
setup: SecretSetupSchema.optional(),
|
|
7207
|
+
hint: z6.string().optional()
|
|
7208
|
+
}).strict();
|
|
7209
|
+
SECRETS_STATUS_POSTURES = [
|
|
7210
|
+
"owner-openkey",
|
|
7211
|
+
"delegate-session",
|
|
7212
|
+
"local-owner-key"
|
|
7213
|
+
];
|
|
7214
|
+
SECRETS_STATUS_EXPOSURE = {
|
|
7215
|
+
cli: { status: "required" },
|
|
7216
|
+
mcp: { status: "required" },
|
|
7217
|
+
skill: { status: "required" },
|
|
7218
|
+
docs: { status: "required" }
|
|
7219
|
+
};
|
|
7220
|
+
SECRETS_STATUS_SENSITIVITY = {
|
|
7221
|
+
input: [],
|
|
7222
|
+
output: []
|
|
7223
|
+
};
|
|
7224
|
+
secretsStatusOperationDefinition = {
|
|
7225
|
+
id: "tinycloud.secrets.status",
|
|
7226
|
+
version: 1,
|
|
7227
|
+
title: "Check whether TinyCloud secrets exist",
|
|
7228
|
+
description: "Report, without reading any value, which named secrets exist in the owner's secrets space: ready, pending (with a Secret Manager request link), or consent_required for names this identity may not read. Each name needs tinycloud.kv/get; this operation never requests it. It never signs in: it uses only the profile's stored session, and writes no profile state.",
|
|
7229
|
+
input: SecretsStatusInputSchema,
|
|
7230
|
+
output: SecretsStatusOutputSchema,
|
|
7231
|
+
effects: ["read"],
|
|
7232
|
+
runtime: "restored",
|
|
7233
|
+
postures: SECRETS_STATUS_POSTURES,
|
|
7234
|
+
exposure: SECRETS_STATUS_EXPOSURE,
|
|
7235
|
+
sensitivity: SECRETS_STATUS_SENSITIVITY,
|
|
7236
|
+
deadlineMs: SECRETS_STATUS_DEADLINE_MS,
|
|
7237
|
+
// Never evaluated against the session: a missing grant is reported as
|
|
7238
|
+
// `consent_required`, never saved as an authority request.
|
|
7239
|
+
authority: async () => [],
|
|
7240
|
+
execute: executeSecretsStatus
|
|
7241
|
+
};
|
|
7242
|
+
secretsStatusOperationDefinitions = [
|
|
7243
|
+
secretsStatusOperationDefinition
|
|
7244
|
+
];
|
|
7245
|
+
}
|
|
7246
|
+
});
|
|
7247
|
+
|
|
6788
7248
|
// src/operations/sql.ts
|
|
6789
7249
|
import nodeSqlParser from "node-sql-parser";
|
|
6790
|
-
import { z as
|
|
7250
|
+
import { z as z7 } from "zod";
|
|
6791
7251
|
async function planSqlRead(context, input) {
|
|
6792
7252
|
const space = resolveSqlSpace(context, input.space);
|
|
6793
7253
|
return [{
|
|
@@ -7105,7 +7565,7 @@ var init_sql = __esm({
|
|
|
7105
7565
|
"use strict";
|
|
7106
7566
|
init_errors2();
|
|
7107
7567
|
init_secrets();
|
|
7108
|
-
|
|
7568
|
+
init_kv_failure();
|
|
7109
7569
|
DEFAULT_MAX_ROWS = 100;
|
|
7110
7570
|
MAX_MAX_ROWS = 1e3;
|
|
7111
7571
|
DEFAULT_MAX_BYTES = 1024 * 1024;
|
|
@@ -7121,29 +7581,29 @@ WHERE type IN ('table', 'view', 'index', 'trigger')
|
|
|
7121
7581
|
AND name NOT LIKE 'sqlite_%'
|
|
7122
7582
|
ORDER BY type, name`;
|
|
7123
7583
|
sqlParser = new nodeSqlParser.Parser();
|
|
7124
|
-
SpaceSchema2 =
|
|
7584
|
+
SpaceSchema2 = z7.string().min(1).refine(
|
|
7125
7585
|
(space) => (/^[A-Za-z0-9_-]+$/.test(space) || space.startsWith("tinycloud:")) && !space.includes("*"),
|
|
7126
7586
|
"Invalid TinyCloud space."
|
|
7127
7587
|
);
|
|
7128
|
-
DatabaseSchema =
|
|
7588
|
+
DatabaseSchema = z7.string().min(1).max(256).refine(
|
|
7129
7589
|
(database) => database !== "/" && !database.endsWith("/") && !database.includes("*") && !/[\u0000-\u001f\u007f]/.test(database),
|
|
7130
7590
|
"Invalid SQLite database name."
|
|
7131
7591
|
);
|
|
7132
|
-
SafeNumberSchema =
|
|
7592
|
+
SafeNumberSchema = z7.number().finite().refine(
|
|
7133
7593
|
(value) => !Number.isInteger(value) || Number.isSafeInteger(value),
|
|
7134
7594
|
"SQLite integer parameters must be JavaScript safe integers."
|
|
7135
7595
|
);
|
|
7136
|
-
Base64Schema =
|
|
7596
|
+
Base64Schema = z7.string().max(Math.ceil(MAX_MAX_BYTES / 3) * 4).refine(
|
|
7137
7597
|
isCanonicalBase642,
|
|
7138
7598
|
"Invalid base64 BLOB value."
|
|
7139
7599
|
);
|
|
7140
|
-
SqlInputValueSchema =
|
|
7141
|
-
|
|
7600
|
+
SqlInputValueSchema = z7.union([
|
|
7601
|
+
z7.null(),
|
|
7142
7602
|
SafeNumberSchema,
|
|
7143
|
-
|
|
7144
|
-
|
|
7603
|
+
z7.string().max(MAX_MAX_BYTES),
|
|
7604
|
+
z7.object({ type: z7.literal("blob"), base64: Base64Schema }).strict()
|
|
7145
7605
|
]);
|
|
7146
|
-
SqlParamsSchema =
|
|
7606
|
+
SqlParamsSchema = z7.array(SqlInputValueSchema).max(MAX_PARAMETERS).superRefine(
|
|
7147
7607
|
(params, context) => {
|
|
7148
7608
|
const totalBytes = params.reduce(
|
|
7149
7609
|
(total, value) => total + sqlInputValueBytes(value),
|
|
@@ -7151,13 +7611,13 @@ ORDER BY type, name`;
|
|
|
7151
7611
|
);
|
|
7152
7612
|
if (totalBytes > MAX_MAX_BYTES) {
|
|
7153
7613
|
context.addIssue({
|
|
7154
|
-
code:
|
|
7614
|
+
code: z7.ZodIssueCode.custom,
|
|
7155
7615
|
message: "SQLite query parameters exceed the 4 MiB input limit."
|
|
7156
7616
|
});
|
|
7157
7617
|
}
|
|
7158
7618
|
}
|
|
7159
7619
|
);
|
|
7160
|
-
RequiredSqlParamsSchema =
|
|
7620
|
+
RequiredSqlParamsSchema = z7.array(SqlInputValueSchema).min(1).max(MAX_PARAMETERS).superRefine(
|
|
7161
7621
|
(params, context) => {
|
|
7162
7622
|
const totalBytes = params.reduce(
|
|
7163
7623
|
(total, value) => total + sqlInputValueBytes(value),
|
|
@@ -7165,88 +7625,88 @@ ORDER BY type, name`;
|
|
|
7165
7625
|
);
|
|
7166
7626
|
if (totalBytes > MAX_MAX_BYTES) {
|
|
7167
7627
|
context.addIssue({
|
|
7168
|
-
code:
|
|
7628
|
+
code: z7.ZodIssueCode.custom,
|
|
7169
7629
|
message: "SQLite query parameters exceed the 4 MiB input limit."
|
|
7170
7630
|
});
|
|
7171
7631
|
}
|
|
7172
7632
|
}
|
|
7173
7633
|
);
|
|
7174
|
-
SqlOutputValueSchema =
|
|
7175
|
-
|
|
7634
|
+
SqlOutputValueSchema = z7.union([
|
|
7635
|
+
z7.null(),
|
|
7176
7636
|
SafeNumberSchema,
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
type:
|
|
7180
|
-
base64:
|
|
7181
|
-
byteLength:
|
|
7637
|
+
z7.string(),
|
|
7638
|
+
z7.object({
|
|
7639
|
+
type: z7.literal("blob"),
|
|
7640
|
+
base64: z7.string(),
|
|
7641
|
+
byteLength: z7.number().int().nonnegative()
|
|
7182
7642
|
}).strict()
|
|
7183
7643
|
]);
|
|
7184
|
-
SqlTargetInputSchema =
|
|
7644
|
+
SqlTargetInputSchema = z7.object({
|
|
7185
7645
|
space: SpaceSchema2,
|
|
7186
7646
|
database: DatabaseSchema
|
|
7187
7647
|
}).strict();
|
|
7188
|
-
SqlQueryInputSchema =
|
|
7648
|
+
SqlQueryInputSchema = z7.object({
|
|
7189
7649
|
space: SpaceSchema2,
|
|
7190
7650
|
database: DatabaseSchema,
|
|
7191
|
-
sql:
|
|
7651
|
+
sql: z7.string().min(1).max(MAX_SQL_LENGTH).superRefine((sql, context) => {
|
|
7192
7652
|
if (!isSingleReadQuery(sql)) {
|
|
7193
7653
|
context.addIssue({
|
|
7194
|
-
code:
|
|
7654
|
+
code: z7.ZodIssueCode.custom,
|
|
7195
7655
|
message: "SQL must parse as exactly one SQLite SELECT statement."
|
|
7196
7656
|
});
|
|
7197
7657
|
}
|
|
7198
7658
|
}),
|
|
7199
7659
|
params: SqlParamsSchema.optional(),
|
|
7200
|
-
maxRows:
|
|
7201
|
-
maxBytes:
|
|
7660
|
+
maxRows: z7.number().int().positive().max(MAX_MAX_ROWS).optional(),
|
|
7661
|
+
maxBytes: z7.number().int().positive().max(MAX_MAX_BYTES).optional()
|
|
7202
7662
|
}).strict();
|
|
7203
|
-
SqlExecuteInputSchema =
|
|
7663
|
+
SqlExecuteInputSchema = z7.object({
|
|
7204
7664
|
space: SpaceSchema2,
|
|
7205
7665
|
database: DatabaseSchema,
|
|
7206
|
-
sql:
|
|
7666
|
+
sql: z7.string().min(1).max(MAX_SQL_LENGTH),
|
|
7207
7667
|
params: RequiredSqlParamsSchema,
|
|
7208
|
-
acknowledgeDatabaseWideAuthority:
|
|
7668
|
+
acknowledgeDatabaseWideAuthority: z7.literal(true).describe(SQL_WRITE_AUTHORITY_NOTICE)
|
|
7209
7669
|
}).strict().superRefine((input, context) => {
|
|
7210
7670
|
if (parseSingleDmlStatement(input.sql, input.params.length) === void 0) {
|
|
7211
7671
|
context.addIssue({
|
|
7212
|
-
code:
|
|
7672
|
+
code: z7.ZodIssueCode.custom,
|
|
7213
7673
|
path: ["sql"],
|
|
7214
7674
|
message: "SQL must parse as exactly one parameterized INSERT, UPDATE, or DELETE with one value per positional placeholder."
|
|
7215
7675
|
});
|
|
7216
7676
|
}
|
|
7217
7677
|
});
|
|
7218
|
-
SqlLimitsOutputSchema =
|
|
7219
|
-
maxRows:
|
|
7220
|
-
maxBytes:
|
|
7221
|
-
enforcement:
|
|
7678
|
+
SqlLimitsOutputSchema = z7.object({
|
|
7679
|
+
maxRows: z7.number().int().positive(),
|
|
7680
|
+
maxBytes: z7.number().int().positive(),
|
|
7681
|
+
enforcement: z7.literal("node-requested-client-verified")
|
|
7222
7682
|
}).strict();
|
|
7223
|
-
SqlSchemaInspectOutputSchema =
|
|
7224
|
-
space:
|
|
7225
|
-
database:
|
|
7226
|
-
objects:
|
|
7227
|
-
type:
|
|
7228
|
-
name:
|
|
7229
|
-
tableName:
|
|
7230
|
-
sql:
|
|
7683
|
+
SqlSchemaInspectOutputSchema = z7.object({
|
|
7684
|
+
space: z7.string().min(1),
|
|
7685
|
+
database: z7.string().min(1),
|
|
7686
|
+
objects: z7.array(z7.object({
|
|
7687
|
+
type: z7.enum(["table", "view", "index", "trigger"]),
|
|
7688
|
+
name: z7.string().min(1),
|
|
7689
|
+
tableName: z7.string().min(1),
|
|
7690
|
+
sql: z7.string().optional()
|
|
7231
7691
|
}).strict()),
|
|
7232
|
-
count:
|
|
7692
|
+
count: z7.number().int().nonnegative(),
|
|
7233
7693
|
limits: SqlLimitsOutputSchema
|
|
7234
7694
|
}).strict();
|
|
7235
|
-
SqlQueryOutputSchema =
|
|
7236
|
-
space:
|
|
7237
|
-
database:
|
|
7238
|
-
columns:
|
|
7239
|
-
rows:
|
|
7240
|
-
rowCount:
|
|
7695
|
+
SqlQueryOutputSchema = z7.object({
|
|
7696
|
+
space: z7.string().min(1),
|
|
7697
|
+
database: z7.string().min(1),
|
|
7698
|
+
columns: z7.array(z7.string()),
|
|
7699
|
+
rows: z7.array(z7.array(SqlOutputValueSchema)),
|
|
7700
|
+
rowCount: z7.number().int().nonnegative(),
|
|
7241
7701
|
limits: SqlLimitsOutputSchema
|
|
7242
7702
|
}).strict();
|
|
7243
|
-
SqlExecuteOutputSchema =
|
|
7244
|
-
space:
|
|
7245
|
-
database:
|
|
7246
|
-
statementType:
|
|
7247
|
-
changes:
|
|
7248
|
-
lastInsertRowId:
|
|
7249
|
-
authorityNotice:
|
|
7703
|
+
SqlExecuteOutputSchema = z7.object({
|
|
7704
|
+
space: z7.string().min(1),
|
|
7705
|
+
database: z7.string().min(1),
|
|
7706
|
+
statementType: z7.enum(["insert", "update", "delete"]),
|
|
7707
|
+
changes: z7.number().int().nonnegative(),
|
|
7708
|
+
lastInsertRowId: z7.number().int().nullable(),
|
|
7709
|
+
authorityNotice: z7.literal(SQL_WRITE_AUTHORITY_NOTICE)
|
|
7250
7710
|
}).strict();
|
|
7251
7711
|
AUTHENTICATED_POSTURES2 = [
|
|
7252
7712
|
"owner-openkey",
|
|
@@ -7346,6 +7806,8 @@ var init_contract = __esm({
|
|
|
7346
7806
|
"resources_mismatch",
|
|
7347
7807
|
"unsupported",
|
|
7348
7808
|
"activation_rejected",
|
|
7809
|
+
"activation_unavailable",
|
|
7810
|
+
"activation_unauthenticated",
|
|
7349
7811
|
"invalid"
|
|
7350
7812
|
];
|
|
7351
7813
|
DELEGATION_CID_PATTERN = "^bafkr4i[a-z2-7]{52}$";
|
|
@@ -7354,7 +7816,7 @@ var init_contract = __esm({
|
|
|
7354
7816
|
|
|
7355
7817
|
// src/delegation-binding.ts
|
|
7356
7818
|
import { join as join2 } from "path";
|
|
7357
|
-
import * as
|
|
7819
|
+
import * as nodeSdk5 from "@tinycloud/node-sdk";
|
|
7358
7820
|
function bindingMigrationPath(profile) {
|
|
7359
7821
|
return join2(profilePath(profile), "delegation-binding-migration.json");
|
|
7360
7822
|
}
|
|
@@ -7450,6 +7912,9 @@ function skipped(reason) {
|
|
|
7450
7912
|
}
|
|
7451
7913
|
async function replayProfileDelegations(profile, node, options) {
|
|
7452
7914
|
await prepareStoredDelegationReplay(profile, node, { host: options.host, migrate: options.migrate });
|
|
7915
|
+
return replayStoredDelegationRecords(profile, node, options);
|
|
7916
|
+
}
|
|
7917
|
+
async function replayStoredDelegationRecords(profile, node, options) {
|
|
7453
7918
|
const installed = [];
|
|
7454
7919
|
const warnings = [];
|
|
7455
7920
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -7490,7 +7955,7 @@ function storedDelegationForAnotherKey(entry, node) {
|
|
|
7490
7955
|
const audience = verifiedCompactAudience(node, delegation);
|
|
7491
7956
|
if (audience === void 0) return false;
|
|
7492
7957
|
try {
|
|
7493
|
-
return
|
|
7958
|
+
return nodeSdk5.principalDidEquals(delegation.delegateDID, audience) && !nodeSdk5.principalDidEquals(audience, node.sessionDid);
|
|
7494
7959
|
} catch {
|
|
7495
7960
|
return false;
|
|
7496
7961
|
}
|
|
@@ -7524,7 +7989,7 @@ async function assess(node, entry, options, mode) {
|
|
|
7524
7989
|
let outsideRequest = false;
|
|
7525
7990
|
let effectivePermissions;
|
|
7526
7991
|
try {
|
|
7527
|
-
const installed = await
|
|
7992
|
+
const installed = await nodeSdk5.activateValidatedRuntimeDelegation(node, toActivate, {
|
|
7528
7993
|
host: options.host,
|
|
7529
7994
|
authorize: (effective) => {
|
|
7530
7995
|
verifyCompactProof(toActivate);
|
|
@@ -7556,7 +8021,7 @@ function verifyCompactProof(delegation) {
|
|
|
7556
8021
|
try {
|
|
7557
8022
|
parseSignedCompactUcanAttenuation(withoutBearer(authorization), delegation.cid);
|
|
7558
8023
|
} catch {
|
|
7559
|
-
throw new
|
|
8024
|
+
throw new nodeSdk5.RuntimeDelegationRejectedError("proof_invalid", "The compact delegation proof is invalid.");
|
|
7560
8025
|
}
|
|
7561
8026
|
}
|
|
7562
8027
|
function rejectionReason(error) {
|
|
@@ -7573,7 +8038,7 @@ async function bindingMigrationRecorded(profile) {
|
|
|
7573
8038
|
async function signedCapabilities(node, delegation, host) {
|
|
7574
8039
|
let signed;
|
|
7575
8040
|
try {
|
|
7576
|
-
await
|
|
8041
|
+
await nodeSdk5.activateValidatedRuntimeDelegation(node, delegation, {
|
|
7577
8042
|
// The transport host is not signed authority; check it at replay instead.
|
|
7578
8043
|
host: delegation.host ?? host,
|
|
7579
8044
|
authorize: (effective) => {
|
|
@@ -7636,7 +8101,7 @@ var init_delegation_binding = __esm({
|
|
|
7636
8101
|
});
|
|
7637
8102
|
|
|
7638
8103
|
// src/operations/status.ts
|
|
7639
|
-
import { z as
|
|
8104
|
+
import { z as z8 } from "zod";
|
|
7640
8105
|
function createStatusDefinition(params) {
|
|
7641
8106
|
return {
|
|
7642
8107
|
id: params.id,
|
|
@@ -7761,27 +8226,27 @@ var init_status = __esm({
|
|
|
7761
8226
|
init_delegation_binding();
|
|
7762
8227
|
init_errors2();
|
|
7763
8228
|
init_state();
|
|
7764
|
-
StatusInputSchema =
|
|
7765
|
-
StatusOutputSchema =
|
|
7766
|
-
profile:
|
|
7767
|
-
host:
|
|
7768
|
-
posture:
|
|
8229
|
+
StatusInputSchema = z8.object({}).strict();
|
|
8230
|
+
StatusOutputSchema = z8.object({
|
|
8231
|
+
profile: z8.string().min(1),
|
|
8232
|
+
host: z8.string().min(1),
|
|
8233
|
+
posture: z8.enum([
|
|
7769
8234
|
"owner-openkey",
|
|
7770
8235
|
"delegate-session",
|
|
7771
8236
|
"local-owner-key",
|
|
7772
8237
|
"unauthenticated"
|
|
7773
8238
|
]),
|
|
7774
|
-
operatorType:
|
|
7775
|
-
principalDid:
|
|
7776
|
-
sessionDid:
|
|
7777
|
-
ownerDid:
|
|
7778
|
-
space:
|
|
7779
|
-
session:
|
|
7780
|
-
present:
|
|
7781
|
-
expired:
|
|
7782
|
-
expiresAt:
|
|
8239
|
+
operatorType: z8.enum(["human", "agent"]).optional(),
|
|
8240
|
+
principalDid: z8.string().min(1).optional(),
|
|
8241
|
+
sessionDid: z8.string().min(1).optional(),
|
|
8242
|
+
ownerDid: z8.string().min(1).optional(),
|
|
8243
|
+
space: z8.string().min(1).optional(),
|
|
8244
|
+
session: z8.object({
|
|
8245
|
+
present: z8.boolean(),
|
|
8246
|
+
expired: z8.boolean().nullable(),
|
|
8247
|
+
expiresAt: z8.string().datetime({ offset: true }).nullable()
|
|
7783
8248
|
}).strict(),
|
|
7784
|
-
liveAdditionalDelegationCount:
|
|
8249
|
+
liveAdditionalDelegationCount: z8.number().int().nonnegative().describe(
|
|
7785
8250
|
"Stored, unexpired compact-UCAN or signed-login delegations with a valid request binding. Each still requires local proof validation and node activation before it grants authority."
|
|
7786
8251
|
)
|
|
7787
8252
|
}).strict();
|
|
@@ -7825,7 +8290,8 @@ function registeredOperationDefinitions() {
|
|
|
7825
8290
|
...authOperationDefinitions,
|
|
7826
8291
|
...explorationOperationDefinitions,
|
|
7827
8292
|
...sqlOperationDefinitions,
|
|
7828
|
-
...secretsGetOperationDefinitions
|
|
8293
|
+
...secretsGetOperationDefinitions,
|
|
8294
|
+
...secretsStatusOperationDefinitions
|
|
7829
8295
|
]);
|
|
7830
8296
|
return operationDefinitions;
|
|
7831
8297
|
}
|
|
@@ -7857,6 +8323,7 @@ var init_registry = __esm({
|
|
|
7857
8323
|
init_auth();
|
|
7858
8324
|
init_exploration();
|
|
7859
8325
|
init_secrets_get();
|
|
8326
|
+
init_secrets_status();
|
|
7860
8327
|
init_sql();
|
|
7861
8328
|
init_status();
|
|
7862
8329
|
}
|
|
@@ -7909,6 +8376,9 @@ function resolvePosture(profile) {
|
|
|
7909
8376
|
if (profile.authMethod === "local") return "local-owner-key";
|
|
7910
8377
|
return "owner-openkey";
|
|
7911
8378
|
}
|
|
8379
|
+
function isLocalOwnerProfile(profile) {
|
|
8380
|
+
return resolvePosture(profile) === "local-owner-key" || profile.authMethod === "local" || typeof profile.privateKey === "string";
|
|
8381
|
+
}
|
|
7912
8382
|
function profileNotFound2(profile) {
|
|
7913
8383
|
return {
|
|
7914
8384
|
ok: false,
|
|
@@ -7980,7 +8450,7 @@ var runtime_exports = {};
|
|
|
7980
8450
|
__export(runtime_exports, {
|
|
7981
8451
|
createInvocationRuntime: () => createInvocationRuntime
|
|
7982
8452
|
});
|
|
7983
|
-
import * as
|
|
8453
|
+
import * as nodeSdk6 from "@tinycloud/node-sdk";
|
|
7984
8454
|
async function createInvocationRuntime(target, requirement = "authenticated") {
|
|
7985
8455
|
const resolved = await resolveInvocationProfile(target);
|
|
7986
8456
|
if (!resolved.ok) {
|
|
@@ -7994,11 +8464,14 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
|
|
|
7994
8464
|
if (requirement === "inspection") {
|
|
7995
8465
|
return { ok: true, context: { summary } };
|
|
7996
8466
|
}
|
|
8467
|
+
if (requirement === "restored") {
|
|
8468
|
+
return createRestoredRuntime(target, resolved);
|
|
8469
|
+
}
|
|
7997
8470
|
const profileName = summary.profile;
|
|
7998
8471
|
try {
|
|
7999
8472
|
const {
|
|
8000
8473
|
TinyCloudNode: TinyCloudNodeConstructor
|
|
8001
|
-
} =
|
|
8474
|
+
} = nodeSdk6;
|
|
8002
8475
|
const explicitPrivateKeyOverride = typeof target.privateKey === "string";
|
|
8003
8476
|
const [session, key] = explicitPrivateKeyOverride ? [null, null] : await Promise.all([
|
|
8004
8477
|
readSession(profileName),
|
|
@@ -8076,7 +8549,9 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
|
|
|
8076
8549
|
const runtime = {
|
|
8077
8550
|
node,
|
|
8078
8551
|
granted: canonicalizeCapabilities(livePermissions),
|
|
8079
|
-
...warnings.length === 0 ? {} : { warnings }
|
|
8552
|
+
...warnings.length === 0 ? {} : { warnings },
|
|
8553
|
+
localOwner: explicitPrivateKeyOverride || isLocalOwnerProfile(profile),
|
|
8554
|
+
skippedGrantReasons: warnings.map((warning) => warning.reason)
|
|
8080
8555
|
};
|
|
8081
8556
|
return {
|
|
8082
8557
|
ok: true,
|
|
@@ -8100,11 +8575,112 @@ async function createInvocationRuntime(target, requirement = "authenticated") {
|
|
|
8100
8575
|
));
|
|
8101
8576
|
}
|
|
8102
8577
|
}
|
|
8578
|
+
async function createRestoredRuntime(target, resolved) {
|
|
8579
|
+
const summary = resolved.context;
|
|
8580
|
+
const profileName = summary.profile;
|
|
8581
|
+
const profile = resolved.profile;
|
|
8582
|
+
if (typeof target.privateKey === "string") {
|
|
8583
|
+
return failed(summary, operationError(
|
|
8584
|
+
"AUTH_REQUIRED",
|
|
8585
|
+
"This operation never signs in; an explicit private key has no stored session.",
|
|
8586
|
+
{ details: { reason: "explicit_key" } }
|
|
8587
|
+
));
|
|
8588
|
+
}
|
|
8589
|
+
try {
|
|
8590
|
+
const actualPosture = resolvePosture(profile);
|
|
8591
|
+
const selectedPosture = profile.authMethod === "local" ? "local-owner-key" : summary.posture;
|
|
8592
|
+
if (actualPosture !== summary.posture || selectedPosture !== summary.posture) {
|
|
8593
|
+
return failed(
|
|
8594
|
+
{ ...summary, posture: actualPosture !== summary.posture ? actualPosture : selectedPosture },
|
|
8595
|
+
operationError(
|
|
8596
|
+
"PROFILE_POSTURE_NOT_ALLOWED",
|
|
8597
|
+
"The selected profile changed posture during runtime resolution."
|
|
8598
|
+
)
|
|
8599
|
+
);
|
|
8600
|
+
}
|
|
8601
|
+
const [session, key] = await Promise.all([
|
|
8602
|
+
readSession(profileName),
|
|
8603
|
+
readJson(`${profilePath(profileName)}/key.json`)
|
|
8604
|
+
]);
|
|
8605
|
+
const activeSession = session === null ? void 0 : normalizeSession(session, key);
|
|
8606
|
+
if (activeSession === void 0) {
|
|
8607
|
+
return failed(summary, operationError(
|
|
8608
|
+
"SESSION_NOT_FOUND",
|
|
8609
|
+
"The selected profile does not have an active session. This operation never signs in.",
|
|
8610
|
+
{ details: { recovery: signInRecovery(profileName, summary.posture) } }
|
|
8611
|
+
));
|
|
8612
|
+
}
|
|
8613
|
+
if (!await bindingMigrationRecorded(profileName)) {
|
|
8614
|
+
const records = await readAdditionalDelegations(profileName);
|
|
8615
|
+
if (records.some(
|
|
8616
|
+
(entry) => storedDelegationKind(entry) === "compact" && !("authorityRequest" in entry)
|
|
8617
|
+
)) {
|
|
8618
|
+
return failed(summary, operationError(
|
|
8619
|
+
"PROFILE_MIGRATION_REQUIRED",
|
|
8620
|
+
`Profile "${profileName}" has a one-time stored-grant upgrade to finish. Run one authenticated command on it (tc --profile <profile> auth caps), then retry.`,
|
|
8621
|
+
{ retryable: false, details: { recovery: migrationRecovery(profileName) } }
|
|
8622
|
+
));
|
|
8623
|
+
}
|
|
8624
|
+
}
|
|
8625
|
+
const { TinyCloudNode: TinyCloudNodeConstructor } = nodeSdk6;
|
|
8626
|
+
const node = new TinyCloudNodeConstructor({
|
|
8627
|
+
host: summary.host,
|
|
8628
|
+
...typeof profile.privateKey === "string" ? { privateKey: profile.privateKey } : {}
|
|
8629
|
+
});
|
|
8630
|
+
try {
|
|
8631
|
+
await node.restoreSession(activeSession);
|
|
8632
|
+
} catch (error) {
|
|
8633
|
+
if (isRecord7(error) && error.code === "AUTH_EXPIRED") {
|
|
8634
|
+
return failed(summary, operationError(
|
|
8635
|
+
"AUTH_REQUIRED",
|
|
8636
|
+
"The stored session has expired or is no longer valid. Sign in again.",
|
|
8637
|
+
{ retryable: false, details: { recovery: signInRecovery(profileName, summary.posture) } }
|
|
8638
|
+
));
|
|
8639
|
+
}
|
|
8640
|
+
throw error;
|
|
8641
|
+
}
|
|
8642
|
+
const activeSessionDid = normalizeDid(node.sessionDid);
|
|
8643
|
+
const livePrincipalDid = normalizeDid(node.did);
|
|
8644
|
+
const livePermissions = [...node.getVerifiedSessionCapabilities()];
|
|
8645
|
+
const replay = await replayStoredDelegationRecords(
|
|
8646
|
+
profileName,
|
|
8647
|
+
node,
|
|
8648
|
+
{ host: summary.host, resolveSpace: operationSpaceResolver(node, summary.space) }
|
|
8649
|
+
);
|
|
8650
|
+
for (const installed of replay.installed) {
|
|
8651
|
+
livePermissions.push(...installed.effectivePermissions);
|
|
8652
|
+
}
|
|
8653
|
+
const runtime = {
|
|
8654
|
+
node,
|
|
8655
|
+
granted: canonicalizeCapabilities(livePermissions),
|
|
8656
|
+
...replay.warnings.length === 0 ? {} : { warnings: replay.warnings },
|
|
8657
|
+
localOwner: isLocalOwnerProfile(profile),
|
|
8658
|
+
skippedGrantReasons: replay.warnings.map((warning) => warning.reason)
|
|
8659
|
+
};
|
|
8660
|
+
return {
|
|
8661
|
+
ok: true,
|
|
8662
|
+
context: {
|
|
8663
|
+
summary: {
|
|
8664
|
+
...summary,
|
|
8665
|
+
...livePrincipalDid === void 0 ? {} : { principalDid: livePrincipalDid },
|
|
8666
|
+
...activeSessionDid === void 0 ? {} : { sessionDid: activeSessionDid }
|
|
8667
|
+
},
|
|
8668
|
+
runtime
|
|
8669
|
+
}
|
|
8670
|
+
};
|
|
8671
|
+
} catch {
|
|
8672
|
+
return failed(summary, operationError(
|
|
8673
|
+
"NODE_ERROR",
|
|
8674
|
+
"The selected profile runtime could not be initialized.",
|
|
8675
|
+
{ retryable: true }
|
|
8676
|
+
));
|
|
8677
|
+
}
|
|
8678
|
+
}
|
|
8103
8679
|
function spaceForAuthenticatedPrincipal(principal2) {
|
|
8104
8680
|
if (principal2 === void 0) return void 0;
|
|
8105
8681
|
try {
|
|
8106
|
-
const pkh =
|
|
8107
|
-
return pkh === null ? void 0 :
|
|
8682
|
+
const pkh = nodeSdk6.parsePkhDid(principal2);
|
|
8683
|
+
return pkh === null ? void 0 : nodeSdk6.makePkhSpaceId(pkh.address, pkh.chainId, "secrets");
|
|
8108
8684
|
} catch {
|
|
8109
8685
|
return void 0;
|
|
8110
8686
|
}
|
|
@@ -8154,6 +8730,7 @@ var init_runtime = __esm({
|
|
|
8154
8730
|
init_authority();
|
|
8155
8731
|
init_errors2();
|
|
8156
8732
|
init_profile();
|
|
8733
|
+
init_recovery();
|
|
8157
8734
|
init_secrets();
|
|
8158
8735
|
init_state();
|
|
8159
8736
|
init_delegation_binding();
|
|
@@ -8168,11 +8745,14 @@ init_artifacts();
|
|
|
8168
8745
|
import { createHash } from "crypto";
|
|
8169
8746
|
import { isAbsolute as isAbsolute2 } from "path";
|
|
8170
8747
|
import {
|
|
8171
|
-
makePkhSpaceId as
|
|
8172
|
-
parsePkhDid as
|
|
8748
|
+
makePkhSpaceId as makePkhSpaceId4,
|
|
8749
|
+
parsePkhDid as parsePkhDid4,
|
|
8173
8750
|
parseSpaceUri as parseSpaceUri2
|
|
8174
8751
|
} from "@tinycloud/node-sdk";
|
|
8175
8752
|
|
|
8753
|
+
// src/redaction.ts
|
|
8754
|
+
init_recovery();
|
|
8755
|
+
|
|
8176
8756
|
// src/safe-values.ts
|
|
8177
8757
|
function safeOriginHost(value) {
|
|
8178
8758
|
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
@@ -8249,7 +8829,25 @@ function redactOperationError(_definition, error) {
|
|
|
8249
8829
|
}
|
|
8250
8830
|
var SAFE_ERROR_DETAIL_POLICIES = {
|
|
8251
8831
|
INPUT_INVALID: {
|
|
8252
|
-
issues: (value) => Array.isArray(value) ? safeIssueCodes(value) : void 0
|
|
8832
|
+
issues: (value) => Array.isArray(value) ? safeIssueCodes(value) : void 0,
|
|
8833
|
+
reason: (value) => value === "OWNER_MISMATCH" || value === "OWNER_DID_UNKNOWN" ? value : void 0
|
|
8834
|
+
},
|
|
8835
|
+
AUTH_REQUIRED: {
|
|
8836
|
+
recovery: (value) => isSafeRecovery(value, "sign_in") ? { ...value } : void 0,
|
|
8837
|
+
reason: (value) => value === "explicit_key" ? value : void 0
|
|
8838
|
+
},
|
|
8839
|
+
SESSION_NOT_FOUND: {
|
|
8840
|
+
recovery: (value) => isSafeRecovery(value, "sign_in") ? { ...value } : void 0
|
|
8841
|
+
},
|
|
8842
|
+
PROFILE_MIGRATION_REQUIRED: {
|
|
8843
|
+
recovery: (value) => isSafeRecovery(value, "complete_migration") ? { ...value } : void 0
|
|
8844
|
+
},
|
|
8845
|
+
// `secrets.get` not found for an identity no request link serves.
|
|
8846
|
+
KV_NOT_FOUND: {
|
|
8847
|
+
recoveryKind: (value) => value === "put_locally" || value === "not_requestable" ? value : void 0,
|
|
8848
|
+
names: (value) => Array.isArray(value) && value.length >= 1 && value.length <= 10 && value.every((name) => typeof name === "string" && name.length <= 256 && /^[A-Z][A-Z0-9_]*$/.test(name)) ? [...value] : void 0,
|
|
8849
|
+
scope: (value) => typeof value === "string" && value.length <= 256 && /^[a-z0-9]+(-[a-z0-9]+)*$/.test(value) ? value : void 0,
|
|
8850
|
+
owner: (value) => typeof value === "string" && /^did:pkh:eip155:[1-9][0-9]*:0x[0-9a-fA-F]{40}$/.test(value) ? value : void 0
|
|
8253
8851
|
},
|
|
8254
8852
|
OPERATION_VERSION_UNSUPPORTED: {
|
|
8255
8853
|
supportedVersions: (value) => Array.isArray(value) && value.every((item) => Number.isInteger(item)) ? value : void 0
|
|
@@ -8331,7 +8929,7 @@ async function invokeOperation(operationId, operationVersion, invocationTarget,
|
|
|
8331
8929
|
operationError("INPUT_INVALID", "The invocation target is invalid.")
|
|
8332
8930
|
);
|
|
8333
8931
|
}
|
|
8334
|
-
|
|
8932
|
+
const run = withTinyCloudStateRoot(target.stateRoot, async () => {
|
|
8335
8933
|
const prepared = await prepareInvocation(
|
|
8336
8934
|
operationId,
|
|
8337
8935
|
operationVersion,
|
|
@@ -8341,6 +8939,33 @@ async function invokeOperation(operationId, operationVersion, invocationTarget,
|
|
|
8341
8939
|
if (isPreparedError(prepared)) return prepared.result;
|
|
8342
8940
|
return withRuntimeWarnings(await executePreparedInvocation(prepared), prepared);
|
|
8343
8941
|
});
|
|
8942
|
+
const { lookupOperation: lookupOperation2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
|
|
8943
|
+
const lookup = lookupOperation2(operationId, operationVersion);
|
|
8944
|
+
const deadlineMs = lookup.status === "found" ? lookup.definition.deadlineMs : void 0;
|
|
8945
|
+
if (deadlineMs === void 0) return run;
|
|
8946
|
+
return withResponseDeadline(run, deadlineMs, () => errorResult(
|
|
8947
|
+
{ operationId, operationVersion },
|
|
8948
|
+
unresolvedContextSummary(target),
|
|
8949
|
+
operationError(
|
|
8950
|
+
"NODE_UNREACHABLE",
|
|
8951
|
+
`The operation did not finish within ${deadlineMs / 1e3} s.`,
|
|
8952
|
+
{ retryable: true }
|
|
8953
|
+
)
|
|
8954
|
+
));
|
|
8955
|
+
}
|
|
8956
|
+
async function withResponseDeadline(run, deadlineMs, expired) {
|
|
8957
|
+
run.catch(() => {
|
|
8958
|
+
});
|
|
8959
|
+
let timer;
|
|
8960
|
+
const deadline = new Promise((resolve2) => {
|
|
8961
|
+
timer = setTimeout(() => resolve2(expired()), deadlineMs);
|
|
8962
|
+
timer.unref();
|
|
8963
|
+
});
|
|
8964
|
+
try {
|
|
8965
|
+
return await Promise.race([run, deadline]);
|
|
8966
|
+
} finally {
|
|
8967
|
+
clearTimeout(timer);
|
|
8968
|
+
}
|
|
8344
8969
|
}
|
|
8345
8970
|
function withRuntimeWarnings(result, prepared) {
|
|
8346
8971
|
const warnings = prepared.runtimeResolution.context.runtime?.warnings;
|
|
@@ -8415,7 +9040,7 @@ async function prepareInvocation(operationId, operationVersion, invocationTarget
|
|
|
8415
9040
|
)
|
|
8416
9041
|
) };
|
|
8417
9042
|
}
|
|
8418
|
-
if (target.privateKey === void 0 && definition.runtime
|
|
9043
|
+
if (target.privateKey === void 0 && requiresRuntime(definition.runtime) && isOwnerPosture(inspectionResolution.context.summary.posture) && target.allowOwnerProfile !== true) {
|
|
8419
9044
|
return { status: "error", result: errorResult(
|
|
8420
9045
|
operation,
|
|
8421
9046
|
inspectionResolution.context.summary,
|
|
@@ -8425,15 +9050,15 @@ async function prepareInvocation(operationId, operationVersion, invocationTarget
|
|
|
8425
9050
|
)
|
|
8426
9051
|
) };
|
|
8427
9052
|
}
|
|
8428
|
-
const runtimeResolution = definition.runtime === "inspection" ? inspectionResolution : await resolveRuntime(target,
|
|
9053
|
+
const runtimeResolution = definition.runtime === "inspection" ? inspectionResolution : await resolveRuntime(target, definition.runtime);
|
|
8429
9054
|
if (!runtimeResolution.ok) {
|
|
8430
9055
|
return { status: "error", result: errorResult(
|
|
8431
9056
|
operation,
|
|
8432
9057
|
runtimeResolution.context,
|
|
8433
|
-
runtimeResolution.error
|
|
9058
|
+
redactOperationError(definition, runtimeResolution.error)
|
|
8434
9059
|
) };
|
|
8435
9060
|
}
|
|
8436
|
-
if (definition.runtime
|
|
9061
|
+
if (requiresRuntime(definition.runtime)) {
|
|
8437
9062
|
const actualPosture = runtimeResolution.context.summary.posture;
|
|
8438
9063
|
if (!definition.postures.includes(actualPosture)) {
|
|
8439
9064
|
return { status: "error", result: errorResult(
|
|
@@ -8820,6 +9445,15 @@ function exactCapabilities2(value) {
|
|
|
8820
9445
|
function isOwnerPosture(posture) {
|
|
8821
9446
|
return posture === "owner-openkey" || posture === "local-owner-key";
|
|
8822
9447
|
}
|
|
9448
|
+
function requiresRuntime(requirement) {
|
|
9449
|
+
switch (requirement) {
|
|
9450
|
+
case "authenticated":
|
|
9451
|
+
case "restored":
|
|
9452
|
+
return true;
|
|
9453
|
+
case "inspection":
|
|
9454
|
+
return false;
|
|
9455
|
+
}
|
|
9456
|
+
}
|
|
8823
9457
|
function zodIssueDetails(error) {
|
|
8824
9458
|
return {
|
|
8825
9459
|
// The canonical error channel carries only schema-owned issue codes.
|