@tinycloud/operations 0.4.0-beta.9 → 0.4.0
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 +6 -0
- package/dist/artifacts.cjs +19 -4
- package/dist/artifacts.cjs.map +1 -1
- package/dist/artifacts.d.cts +1 -1
- package/dist/artifacts.d.ts +1 -1
- package/dist/artifacts.js +19 -4
- package/dist/artifacts.js.map +1 -1
- package/dist/cli-runtime.cjs +162 -27
- 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 +162 -26
- package/dist/cli-runtime.js.map +1 -1
- package/dist/{contract-CjuEjE-M.d.cts → contract-BRwWJpPs.d.cts} +24 -2
- package/dist/{contract-CjuEjE-M.d.ts → contract-BRwWJpPs.d.ts} +24 -2
- package/dist/delegation-binding.cjs +79 -16
- package/dist/delegation-binding.cjs.map +1 -1
- package/dist/delegation-binding.d.cts +43 -13
- package/dist/delegation-binding.d.ts +43 -13
- package/dist/delegation-binding.js +78 -16
- package/dist/delegation-binding.js.map +1 -1
- package/dist/index.cjs +160 -28
- 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 +160 -27
- package/dist/index.js.map +1 -1
- package/dist/profile.cjs.map +1 -1
- package/dist/profile.d.cts +1 -1
- package/dist/profile.d.ts +1 -1
- package/dist/profile.js.map +1 -1
- package/dist/state.cjs +9 -3
- package/dist/state.cjs.map +1 -1
- package/dist/state.d.cts +7 -0
- package/dist/state.d.ts +7 -0
- package/dist/state.js +9 -3
- package/dist/state.js.map +1 -1
- package/generated/operations.json +2661 -4
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const OPERATION_ERROR_CODES: readonly ["INPUT_INVALID", "OPERATION_NOT_FOUND", "OPERATION_VERSION_UNSUPPORTED", "PROFILE_NOT_FOUND", "PROFILE_POSTURE_NOT_ALLOWED", "PROFILE_OWNER_OPT_IN_REQUIRED", "AUTH_REQUIRED", "PERMISSION_DENIED", "SESSION_NOT_FOUND", "PERMISSION_HINT_INVALID", "DELEGATION_ARTIFACT_INVALID", "DELEGATION_EXPIRED", "DELEGATION_AUDIENCE_MISMATCH", "DELEGATION_HOST_MISMATCH", "DELEGATION_REJECTED", "ENCRYPTION_NETWORK_UNRESOLVED", "NODE_UNREACHABLE", "NODE_ERROR", "KV_NOT_FOUND", "KV_PRECONDITION_FAILED", "KV_CONFLICT", "KV_RESPONSE_TOO_LARGE", "SQL_QUERY_INVALID", "SQL_EXECUTION_FAILED", "SQL_RESULT_LIMIT_EXCEEDED", "SQL_VALUE_UNSAFE", "SECRET_READ_FAILED", "SECRET_DECRYPT_FAILED", "OUTPUT_INVALID", "INTERNAL_ERROR"];
|
|
1
|
+
declare const OPERATION_ERROR_CODES: readonly ["INPUT_INVALID", "OPERATION_NOT_FOUND", "OPERATION_VERSION_UNSUPPORTED", "PROFILE_NOT_FOUND", "PROFILE_POSTURE_NOT_ALLOWED", "PROFILE_OWNER_OPT_IN_REQUIRED", "AUTH_REQUIRED", "PERMISSION_DENIED", "SESSION_NOT_FOUND", "PERMISSION_HINT_INVALID", "DELEGATION_ARTIFACT_INVALID", "DELEGATION_EXPIRED", "DELEGATION_AUDIENCE_MISMATCH", "DELEGATION_HOST_MISMATCH", "DELEGATION_REJECTED", "ENCRYPTION_NETWORK_UNRESOLVED", "NODE_UNREACHABLE", "NODE_ERROR", "STORAGE_QUOTA_EXCEEDED", "KV_NOT_FOUND", "KV_PRECONDITION_FAILED", "KV_CONFLICT", "KV_RESPONSE_TOO_LARGE", "SQL_QUERY_INVALID", "SQL_EXECUTION_FAILED", "SQL_RESULT_LIMIT_EXCEEDED", "SQL_VALUE_UNSAFE", "SECRET_READ_FAILED", "SECRET_DECRYPT_FAILED", "OUTPUT_INVALID", "INTERNAL_ERROR"];
|
|
2
2
|
type OperationErrorCode = (typeof OPERATION_ERROR_CODES)[number];
|
|
3
3
|
interface OperationError {
|
|
4
4
|
readonly code: OperationErrorCode;
|
|
@@ -25,6 +25,24 @@ interface OperationContextSummary {
|
|
|
25
25
|
readonly ownerDid?: string;
|
|
26
26
|
readonly space?: string;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Why a stored grant was not used. A fixed vocabulary: diagnostics never
|
|
30
|
+
* carry exception text, node responses, URLs or other stored transport data.
|
|
31
|
+
* `unbound`: the record has no valid request binding where one is required;
|
|
32
|
+
* `outside_request`: its signed authority exceeds its binding.
|
|
33
|
+
*/
|
|
34
|
+
declare const STORED_GRANT_SKIP_REASONS: readonly ["malformed", "unbound", "outside_request", "host_mismatch", "expired", "cid_mismatch", "authority_invalid", "proof_missing", "proof_invalid", "owner_mismatch", "audience_mismatch", "resources_mismatch", "unsupported", "activation_rejected", "invalid"];
|
|
35
|
+
type StoredGrantSkipReason = typeof STORED_GRANT_SKIP_REASONS[number];
|
|
36
|
+
/**
|
|
37
|
+
* A non-fatal diagnostic attached to a result. `STORED_GRANT_SKIPPED`: a
|
|
38
|
+
* stored grant failed validation and gave no authority; `grantCid` is present
|
|
39
|
+
* only when the stored CID is the CID of the stored authorization bytes.
|
|
40
|
+
*/
|
|
41
|
+
interface OperationWarning {
|
|
42
|
+
readonly code: "STORED_GRANT_SKIPPED";
|
|
43
|
+
readonly reason: StoredGrantSkipReason;
|
|
44
|
+
readonly grantCid?: string;
|
|
45
|
+
}
|
|
28
46
|
/**
|
|
29
47
|
* Capability details are finalized by the authority/artifacts increment. The
|
|
30
48
|
* kernel intentionally preserves them as canonical operation-owned data.
|
|
@@ -58,6 +76,7 @@ interface OperationOkResult<O> {
|
|
|
58
76
|
readonly operation: OperationRef;
|
|
59
77
|
readonly context: OperationContextSummary;
|
|
60
78
|
readonly output: O;
|
|
79
|
+
readonly warnings?: readonly OperationWarning[];
|
|
61
80
|
}
|
|
62
81
|
interface OperationAuthorityRequiredResult {
|
|
63
82
|
readonly status: "authority_required";
|
|
@@ -67,6 +86,7 @@ interface OperationAuthorityRequiredResult {
|
|
|
67
86
|
readonly request: PermissionRequestArtifact;
|
|
68
87
|
readonly approval: ApprovalAction;
|
|
69
88
|
readonly retry: RetryDescriptor;
|
|
89
|
+
readonly warnings?: readonly OperationWarning[];
|
|
70
90
|
}
|
|
71
91
|
interface OperationSetupRequiredResult {
|
|
72
92
|
readonly status: "setup_required";
|
|
@@ -74,12 +94,14 @@ interface OperationSetupRequiredResult {
|
|
|
74
94
|
readonly context: OperationContextSummary;
|
|
75
95
|
readonly setup: SetupAction;
|
|
76
96
|
readonly retry: RetryDescriptor;
|
|
97
|
+
readonly warnings?: readonly OperationWarning[];
|
|
77
98
|
}
|
|
78
99
|
interface OperationErrorResult {
|
|
79
100
|
readonly status: "error";
|
|
80
101
|
readonly operation: OperationRef;
|
|
81
102
|
readonly context: OperationContextSummary;
|
|
82
103
|
readonly error: OperationError;
|
|
104
|
+
readonly warnings?: readonly OperationWarning[];
|
|
83
105
|
}
|
|
84
106
|
type OperationResult<O> = OperationOkResult<O> | OperationAuthorityRequiredResult | OperationSetupRequiredResult | OperationErrorResult;
|
|
85
107
|
/**
|
|
@@ -100,4 +122,4 @@ interface InvocationTarget {
|
|
|
100
122
|
readonly privateKey?: string;
|
|
101
123
|
}
|
|
102
124
|
|
|
103
|
-
export type { InvocationTarget as I, OperationOperatorType as O, TinyCloudPosture as T, OperationResult as a, OperationContextSummary as b };
|
|
125
|
+
export type { InvocationTarget as I, OperationOperatorType as O, StoredGrantSkipReason as S, TinyCloudPosture as T, OperationResult as a, OperationContextSummary as b };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
declare const OPERATION_ERROR_CODES: readonly ["INPUT_INVALID", "OPERATION_NOT_FOUND", "OPERATION_VERSION_UNSUPPORTED", "PROFILE_NOT_FOUND", "PROFILE_POSTURE_NOT_ALLOWED", "PROFILE_OWNER_OPT_IN_REQUIRED", "AUTH_REQUIRED", "PERMISSION_DENIED", "SESSION_NOT_FOUND", "PERMISSION_HINT_INVALID", "DELEGATION_ARTIFACT_INVALID", "DELEGATION_EXPIRED", "DELEGATION_AUDIENCE_MISMATCH", "DELEGATION_HOST_MISMATCH", "DELEGATION_REJECTED", "ENCRYPTION_NETWORK_UNRESOLVED", "NODE_UNREACHABLE", "NODE_ERROR", "KV_NOT_FOUND", "KV_PRECONDITION_FAILED", "KV_CONFLICT", "KV_RESPONSE_TOO_LARGE", "SQL_QUERY_INVALID", "SQL_EXECUTION_FAILED", "SQL_RESULT_LIMIT_EXCEEDED", "SQL_VALUE_UNSAFE", "SECRET_READ_FAILED", "SECRET_DECRYPT_FAILED", "OUTPUT_INVALID", "INTERNAL_ERROR"];
|
|
1
|
+
declare const OPERATION_ERROR_CODES: readonly ["INPUT_INVALID", "OPERATION_NOT_FOUND", "OPERATION_VERSION_UNSUPPORTED", "PROFILE_NOT_FOUND", "PROFILE_POSTURE_NOT_ALLOWED", "PROFILE_OWNER_OPT_IN_REQUIRED", "AUTH_REQUIRED", "PERMISSION_DENIED", "SESSION_NOT_FOUND", "PERMISSION_HINT_INVALID", "DELEGATION_ARTIFACT_INVALID", "DELEGATION_EXPIRED", "DELEGATION_AUDIENCE_MISMATCH", "DELEGATION_HOST_MISMATCH", "DELEGATION_REJECTED", "ENCRYPTION_NETWORK_UNRESOLVED", "NODE_UNREACHABLE", "NODE_ERROR", "STORAGE_QUOTA_EXCEEDED", "KV_NOT_FOUND", "KV_PRECONDITION_FAILED", "KV_CONFLICT", "KV_RESPONSE_TOO_LARGE", "SQL_QUERY_INVALID", "SQL_EXECUTION_FAILED", "SQL_RESULT_LIMIT_EXCEEDED", "SQL_VALUE_UNSAFE", "SECRET_READ_FAILED", "SECRET_DECRYPT_FAILED", "OUTPUT_INVALID", "INTERNAL_ERROR"];
|
|
2
2
|
type OperationErrorCode = (typeof OPERATION_ERROR_CODES)[number];
|
|
3
3
|
interface OperationError {
|
|
4
4
|
readonly code: OperationErrorCode;
|
|
@@ -25,6 +25,24 @@ interface OperationContextSummary {
|
|
|
25
25
|
readonly ownerDid?: string;
|
|
26
26
|
readonly space?: string;
|
|
27
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Why a stored grant was not used. A fixed vocabulary: diagnostics never
|
|
30
|
+
* carry exception text, node responses, URLs or other stored transport data.
|
|
31
|
+
* `unbound`: the record has no valid request binding where one is required;
|
|
32
|
+
* `outside_request`: its signed authority exceeds its binding.
|
|
33
|
+
*/
|
|
34
|
+
declare const STORED_GRANT_SKIP_REASONS: readonly ["malformed", "unbound", "outside_request", "host_mismatch", "expired", "cid_mismatch", "authority_invalid", "proof_missing", "proof_invalid", "owner_mismatch", "audience_mismatch", "resources_mismatch", "unsupported", "activation_rejected", "invalid"];
|
|
35
|
+
type StoredGrantSkipReason = typeof STORED_GRANT_SKIP_REASONS[number];
|
|
36
|
+
/**
|
|
37
|
+
* A non-fatal diagnostic attached to a result. `STORED_GRANT_SKIPPED`: a
|
|
38
|
+
* stored grant failed validation and gave no authority; `grantCid` is present
|
|
39
|
+
* only when the stored CID is the CID of the stored authorization bytes.
|
|
40
|
+
*/
|
|
41
|
+
interface OperationWarning {
|
|
42
|
+
readonly code: "STORED_GRANT_SKIPPED";
|
|
43
|
+
readonly reason: StoredGrantSkipReason;
|
|
44
|
+
readonly grantCid?: string;
|
|
45
|
+
}
|
|
28
46
|
/**
|
|
29
47
|
* Capability details are finalized by the authority/artifacts increment. The
|
|
30
48
|
* kernel intentionally preserves them as canonical operation-owned data.
|
|
@@ -58,6 +76,7 @@ interface OperationOkResult<O> {
|
|
|
58
76
|
readonly operation: OperationRef;
|
|
59
77
|
readonly context: OperationContextSummary;
|
|
60
78
|
readonly output: O;
|
|
79
|
+
readonly warnings?: readonly OperationWarning[];
|
|
61
80
|
}
|
|
62
81
|
interface OperationAuthorityRequiredResult {
|
|
63
82
|
readonly status: "authority_required";
|
|
@@ -67,6 +86,7 @@ interface OperationAuthorityRequiredResult {
|
|
|
67
86
|
readonly request: PermissionRequestArtifact;
|
|
68
87
|
readonly approval: ApprovalAction;
|
|
69
88
|
readonly retry: RetryDescriptor;
|
|
89
|
+
readonly warnings?: readonly OperationWarning[];
|
|
70
90
|
}
|
|
71
91
|
interface OperationSetupRequiredResult {
|
|
72
92
|
readonly status: "setup_required";
|
|
@@ -74,12 +94,14 @@ interface OperationSetupRequiredResult {
|
|
|
74
94
|
readonly context: OperationContextSummary;
|
|
75
95
|
readonly setup: SetupAction;
|
|
76
96
|
readonly retry: RetryDescriptor;
|
|
97
|
+
readonly warnings?: readonly OperationWarning[];
|
|
77
98
|
}
|
|
78
99
|
interface OperationErrorResult {
|
|
79
100
|
readonly status: "error";
|
|
80
101
|
readonly operation: OperationRef;
|
|
81
102
|
readonly context: OperationContextSummary;
|
|
82
103
|
readonly error: OperationError;
|
|
104
|
+
readonly warnings?: readonly OperationWarning[];
|
|
83
105
|
}
|
|
84
106
|
type OperationResult<O> = OperationOkResult<O> | OperationAuthorityRequiredResult | OperationSetupRequiredResult | OperationErrorResult;
|
|
85
107
|
/**
|
|
@@ -100,4 +122,4 @@ interface InvocationTarget {
|
|
|
100
122
|
readonly privateKey?: string;
|
|
101
123
|
}
|
|
102
124
|
|
|
103
|
-
export type { InvocationTarget as I, OperationOperatorType as O, TinyCloudPosture as T, OperationResult as a, OperationContextSummary as b };
|
|
125
|
+
export type { InvocationTarget as I, OperationOperatorType as O, StoredGrantSkipReason as S, TinyCloudPosture as T, OperationResult as a, OperationContextSummary as b };
|
|
@@ -152,6 +152,7 @@ var delegation_binding_exports = {};
|
|
|
152
152
|
__export(delegation_binding_exports, {
|
|
153
153
|
BINDING_NOTES: () => BINDING_NOTES,
|
|
154
154
|
activateUnboundCompactImport: () => activateUnboundCompactImport,
|
|
155
|
+
bindCliGrant: () => bindCliGrant,
|
|
155
156
|
bindingMigrationPath: () => bindingMigrationPath,
|
|
156
157
|
bindingMigrationRecorded: () => bindingMigrationRecorded,
|
|
157
158
|
mergeDelegationsWithoutRequest: () => mergeDelegationsWithoutRequest,
|
|
@@ -277,8 +278,17 @@ function canonicalizeRecapCaveats(caveats) {
|
|
|
277
278
|
}
|
|
278
279
|
return `[${branches.join(",")}]`;
|
|
279
280
|
}
|
|
281
|
+
var EXPLICIT_ONLY_ACTIONS = {
|
|
282
|
+
"tinycloud.kv/sync": true,
|
|
283
|
+
"tinycloud.kv/retain": true
|
|
284
|
+
};
|
|
285
|
+
function isExplicitOnlyAction(action) {
|
|
286
|
+
return Object.prototype.hasOwnProperty.call(EXPLICIT_ONLY_ACTIONS, action);
|
|
287
|
+
}
|
|
280
288
|
function actionContains(grantedAction, requestedAction) {
|
|
281
|
-
if (grantedAction ===
|
|
289
|
+
if (grantedAction === requestedAction) return true;
|
|
290
|
+
if (isExplicitOnlyAction(requestedAction)) return false;
|
|
291
|
+
if (grantedAction === "*") return true;
|
|
282
292
|
if (!grantedAction.endsWith("/*")) return false;
|
|
283
293
|
const service = grantedAction.slice(0, -2);
|
|
284
294
|
return requestedAction === service || requestedAction.startsWith(`${service}/`);
|
|
@@ -580,7 +590,8 @@ async function acquireProfileLock(profile, options) {
|
|
|
580
590
|
const lockPath = profileLockPath(profile);
|
|
581
591
|
const turnDirectory = profileTurnLockPath(profile);
|
|
582
592
|
const directory = profilePath(profile);
|
|
583
|
-
|
|
593
|
+
if (options.requireProfile && !await exists(profileConfigPath(profile))) throw new ProfileDeletedError(profile);
|
|
594
|
+
if (!options.requireProfile) await (0, import_promises.mkdir)(directory, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
584
595
|
await (0, import_promises.mkdir)((0, import_node_path.dirname)(turnDirectory), { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
585
596
|
for (const path of [tinycloudHomePath(), profilesPath(), directory]) {
|
|
586
597
|
await (0, import_promises.chmod)(path, PRIVATE_DIR_MODE).catch((error) => {
|
|
@@ -589,11 +600,15 @@ async function acquireProfileLock(profile, options) {
|
|
|
589
600
|
}
|
|
590
601
|
const { turn, firstWaitedTurn } = await acquireTurn(profile, turnDirectory, deadline);
|
|
591
602
|
try {
|
|
603
|
+
if (options.requireProfile && !await exists(profileConfigPath(profile))) {
|
|
604
|
+
await (0, import_promises.rmdir)(directory).catch(() => void 0);
|
|
605
|
+
throw new ProfileDeletedError(profile);
|
|
606
|
+
}
|
|
592
607
|
const deletion = await readJson((0, import_node_path.join)(turnDirectory, "deleted.json")).catch(() => null);
|
|
593
608
|
const deletedWhileWaiting = typeof deletion?.slot === "number" && deletion.slot >= firstWaitedTurn;
|
|
594
609
|
while (true) {
|
|
595
610
|
const token = (0, import_node_crypto.randomUUID)();
|
|
596
|
-
if (await publishProfileLock(profile, lockPath, token)) {
|
|
611
|
+
if (await publishProfileLock(profile, lockPath, token, options.requireProfile === true)) {
|
|
597
612
|
return {
|
|
598
613
|
deletedWhileWaiting,
|
|
599
614
|
turnSlot: turn.slot,
|
|
@@ -844,12 +859,13 @@ async function collectTurns(profile, turn) {
|
|
|
844
859
|
}
|
|
845
860
|
}
|
|
846
861
|
}
|
|
847
|
-
async function publishProfileLock(profile, lockPath, token) {
|
|
862
|
+
async function publishProfileLock(profile, lockPath, token, requireProfile) {
|
|
848
863
|
try {
|
|
849
864
|
await (0, import_promises.mkdir)(lockPath, { mode: PRIVATE_DIR_MODE });
|
|
850
865
|
} catch (error) {
|
|
851
866
|
if (isErrno(error, "EEXIST")) return false;
|
|
852
867
|
if (!isErrno(error, "ENOENT")) throw error;
|
|
868
|
+
if (requireProfile) throw new ProfileDeletedError(profile);
|
|
853
869
|
await (0, import_promises.mkdir)(profilePath(profile), { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
854
870
|
return false;
|
|
855
871
|
}
|
|
@@ -1144,6 +1160,25 @@ var DelegationImportArtifactSchema = import_zod.z.object({
|
|
|
1144
1160
|
}).strict();
|
|
1145
1161
|
var DelegationRequestBindingSchema = PermissionRequestArtifactSchema.pick({ requestId: true, requested: true }).strict();
|
|
1146
1162
|
|
|
1163
|
+
// src/contract.ts
|
|
1164
|
+
var STORED_GRANT_SKIP_REASONS = [
|
|
1165
|
+
"malformed",
|
|
1166
|
+
"unbound",
|
|
1167
|
+
"outside_request",
|
|
1168
|
+
"host_mismatch",
|
|
1169
|
+
"expired",
|
|
1170
|
+
"cid_mismatch",
|
|
1171
|
+
"authority_invalid",
|
|
1172
|
+
"proof_missing",
|
|
1173
|
+
"proof_invalid",
|
|
1174
|
+
"owner_mismatch",
|
|
1175
|
+
"audience_mismatch",
|
|
1176
|
+
"resources_mismatch",
|
|
1177
|
+
"unsupported",
|
|
1178
|
+
"activation_rejected",
|
|
1179
|
+
"invalid"
|
|
1180
|
+
];
|
|
1181
|
+
|
|
1147
1182
|
// src/secrets.ts
|
|
1148
1183
|
var nodeSdk = __toESM(require("@tinycloud/node-sdk"), 1);
|
|
1149
1184
|
|
|
@@ -1224,11 +1259,13 @@ function invalidSecretInput() {
|
|
|
1224
1259
|
// src/delegation-binding.ts
|
|
1225
1260
|
var REQUEST_ID_PREFIX = {
|
|
1226
1261
|
migration: "migrated",
|
|
1227
|
-
"unbound-import": "unbound-import"
|
|
1262
|
+
"unbound-import": "unbound-import",
|
|
1263
|
+
"cli-grant": "cli-grant"
|
|
1228
1264
|
};
|
|
1229
1265
|
var BINDING_NOTES = {
|
|
1230
1266
|
migration: "Stored without a request binding before bindings were required. Bound at migration to exactly its own signed capabilities, so replay refuses any delegation in this record that exceeds them.",
|
|
1231
|
-
"unbound-import": "Imported by `tc auth import` without a stored request. Bound to exactly its own signed capabilities, so replay refuses any delegation in this record that exceeds them."
|
|
1267
|
+
"unbound-import": "Imported by `tc auth import` without a stored request. Bound to exactly its own signed capabilities, so replay refuses any delegation in this record that exceeds them.",
|
|
1268
|
+
"cli-grant": "Granted to this CLI by `tc auth request --grant` or a permission escalation, and checked against the request it answered. Bound to exactly its own signed capabilities, so replay refuses any delegation in this record that exceeds them."
|
|
1232
1269
|
};
|
|
1233
1270
|
var MIGRATION_LOCK_TIMEOUT_MS = 250;
|
|
1234
1271
|
function bindingMigrationPath(profile) {
|
|
@@ -1253,19 +1290,26 @@ function replayLimit(entry, migrated) {
|
|
|
1253
1290
|
if (binding.success) return binding.data.requested;
|
|
1254
1291
|
return !migrated && kind === "compact" && !("authorityRequest" in entry) ? "signed" : void 0;
|
|
1255
1292
|
}
|
|
1256
|
-
function
|
|
1293
|
+
function bindToRequest(record, requested, source, recordedAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
1257
1294
|
return {
|
|
1258
1295
|
...record,
|
|
1259
1296
|
authorityRequest: DelegationRequestBindingSchema.parse({
|
|
1260
1297
|
requestId: `${REQUEST_ID_PREFIX[source]}:${record.delegation.cid}`,
|
|
1261
|
-
requested: structuredClone(
|
|
1298
|
+
requested: structuredClone(requested)
|
|
1262
1299
|
}),
|
|
1263
1300
|
authorityRequestAudit: { source, recordedAt, note: BINDING_NOTES[source] }
|
|
1264
1301
|
};
|
|
1265
1302
|
}
|
|
1303
|
+
async function bindCliGrant(node, record, host) {
|
|
1304
|
+
const signed = await signedCapabilities(node, record.delegation, host);
|
|
1305
|
+
if (signed === void 0) {
|
|
1306
|
+
throw new Error("The grant's signed authority could not be validated for this session.");
|
|
1307
|
+
}
|
|
1308
|
+
return bindToRequest(record, signed, "cli-grant");
|
|
1309
|
+
}
|
|
1266
1310
|
async function activateUnboundCompactImport(node, delegation, host) {
|
|
1267
1311
|
const activated = await nodeSdk2.activateValidatedRuntimeDelegation(node, delegation, { host });
|
|
1268
|
-
return
|
|
1312
|
+
return bindToRequest(
|
|
1269
1313
|
// The record keeps the delegation as imported; replay validates it again.
|
|
1270
1314
|
{ delegation, permissions: [...activated.effectivePermissions] },
|
|
1271
1315
|
activated.effectivePermissions,
|
|
@@ -1334,23 +1378,41 @@ async function migratedRecord(node, entry, host, recordedAt) {
|
|
|
1334
1378
|
const signed = await signedCapabilities(node, delegation, host);
|
|
1335
1379
|
if (signed === void 0) return void 0;
|
|
1336
1380
|
try {
|
|
1337
|
-
return
|
|
1381
|
+
return bindToRequest({ ...entry, delegation: { ...entry.delegation, cid: delegation.cid } }, signed, "migration", recordedAt);
|
|
1338
1382
|
} catch {
|
|
1339
1383
|
return void 0;
|
|
1340
1384
|
}
|
|
1341
1385
|
}
|
|
1386
|
+
var NOT_REPLAYED = { status: "not-replayed" };
|
|
1387
|
+
function skipped(reason) {
|
|
1388
|
+
return { status: "skipped", reason };
|
|
1389
|
+
}
|
|
1342
1390
|
async function replayStoredDelegation(node, entry, options) {
|
|
1391
|
+
const kind = storedDelegationKind(entry);
|
|
1343
1392
|
const delegation = normalizeStoredDelegation(entry);
|
|
1344
|
-
if (
|
|
1393
|
+
if (kind === "refused" || delegation === void 0) return skipped("malformed");
|
|
1394
|
+
if (delegation.expiry.getTime() <= Date.now()) return NOT_REPLAYED;
|
|
1395
|
+
if (kind === "other") return skipped("proof_missing");
|
|
1345
1396
|
const limit = replayLimit(entry, options.migrated);
|
|
1346
|
-
if (limit === void 0) return
|
|
1347
|
-
|
|
1397
|
+
if (limit === void 0) return skipped("unbound");
|
|
1398
|
+
let outsideRequest = false;
|
|
1399
|
+
const authorize = limit === "signed" ? () => true : (effective) => {
|
|
1400
|
+
outsideRequest = !delegationWithinRequest(limit, effective, options.resolveSpace);
|
|
1401
|
+
return !outsideRequest;
|
|
1402
|
+
};
|
|
1348
1403
|
try {
|
|
1349
|
-
return
|
|
1350
|
-
|
|
1351
|
-
|
|
1404
|
+
return {
|
|
1405
|
+
status: "installed",
|
|
1406
|
+
delegation: await nodeSdk2.activateValidatedRuntimeDelegation(node, delegation, { host: options.host, authorize })
|
|
1407
|
+
};
|
|
1408
|
+
} catch (error) {
|
|
1409
|
+
return skipped(outsideRequest ? "outside_request" : rejectionReason(error));
|
|
1352
1410
|
}
|
|
1353
1411
|
}
|
|
1412
|
+
function rejectionReason(error) {
|
|
1413
|
+
const reason = isRecord(error) ? error.reason : void 0;
|
|
1414
|
+
return typeof reason === "string" && STORED_GRANT_SKIP_REASONS.includes(reason) ? reason : "invalid";
|
|
1415
|
+
}
|
|
1354
1416
|
async function bindingMigrationRecorded(profile) {
|
|
1355
1417
|
try {
|
|
1356
1418
|
return await readJson(bindingMigrationPath(profile)) !== null;
|
|
@@ -1399,6 +1461,7 @@ function isRecord(value) {
|
|
|
1399
1461
|
0 && (module.exports = {
|
|
1400
1462
|
BINDING_NOTES,
|
|
1401
1463
|
activateUnboundCompactImport,
|
|
1464
|
+
bindCliGrant,
|
|
1402
1465
|
bindingMigrationPath,
|
|
1403
1466
|
bindingMigrationRecorded,
|
|
1404
1467
|
mergeDelegationsWithoutRequest,
|