@absolutejs/secrets 0.7.0 → 0.7.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/README.md +53 -41
- package/dist/agent.d.ts +2 -0
- package/dist/agent.js +264 -0
- package/dist/agent.js.map +12 -0
- package/dist/broker.d.ts +340 -0
- package/dist/broker.js +825 -0
- package/dist/broker.js.map +11 -0
- package/dist/index.d.ts +2 -342
- package/dist/index.js +86 -60
- package/dist/index.js.map +7 -7
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.js +2 -4
- package/dist/manifest.js.map +3 -3
- package/dist/operations.d.ts +1 -1
- package/dist/postgres.d.ts +1 -1
- package/package.json +13 -3
package/README.md
CHANGED
|
@@ -28,35 +28,42 @@ operation. Results and audit events contain digests and identifiers, never the
|
|
|
28
28
|
credential.
|
|
29
29
|
|
|
30
30
|
```ts
|
|
31
|
+
import {
|
|
32
|
+
createCredentialOperationBroker,
|
|
33
|
+
createMemoryCredentialGrantStore,
|
|
34
|
+
} from "@absolutejs/secrets/agent";
|
|
35
|
+
|
|
31
36
|
const grants = createMemoryCredentialGrantStore();
|
|
32
37
|
await grants.put({
|
|
33
|
-
agentId:
|
|
34
|
-
allowedOrigins: [
|
|
38
|
+
agentId: "research-agent",
|
|
39
|
+
allowedOrigins: ["https://api.example.com"],
|
|
35
40
|
createdAt: Date.now(),
|
|
36
41
|
expiresAt: Date.now() + 60_000,
|
|
37
|
-
grantId:
|
|
42
|
+
grantId: "grant_123",
|
|
38
43
|
maximumUses: 1,
|
|
39
|
-
provider:
|
|
40
|
-
scopes: [
|
|
41
|
-
secretName:
|
|
44
|
+
provider: "example",
|
|
45
|
+
scopes: ["create-report"],
|
|
46
|
+
secretName: "EXAMPLE_API_KEY",
|
|
42
47
|
used: 0,
|
|
43
|
-
userId:
|
|
48
|
+
userId: "user_123",
|
|
44
49
|
});
|
|
45
50
|
|
|
46
51
|
const operations = createCredentialOperationBroker({
|
|
47
52
|
agency,
|
|
48
|
-
providers: [
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
providers: [
|
|
54
|
+
{
|
|
55
|
+
provider: "example",
|
|
56
|
+
operations: {
|
|
57
|
+
"create-report": ({ credential, destination, input, signal }) =>
|
|
58
|
+
fetch(new URL("/reports", destination), {
|
|
59
|
+
body: JSON.stringify(input),
|
|
60
|
+
headers: { authorization: `Bearer ${credential}` },
|
|
61
|
+
method: "POST",
|
|
62
|
+
signal,
|
|
63
|
+
}).then((response) => response.json()),
|
|
64
|
+
},
|
|
58
65
|
},
|
|
59
|
-
|
|
66
|
+
],
|
|
60
67
|
secrets: broker,
|
|
61
68
|
store: grants,
|
|
62
69
|
});
|
|
@@ -68,50 +75,55 @@ Requestable denials can be resumed after approval with
|
|
|
68
75
|
`operations.resume(actionId)`.
|
|
69
76
|
|
|
70
77
|
```ts
|
|
71
|
-
import {
|
|
78
|
+
import {
|
|
79
|
+
createSecretBroker,
|
|
80
|
+
envAdapter,
|
|
81
|
+
inMemoryAdapter,
|
|
82
|
+
compositeAdapter,
|
|
83
|
+
} from "@absolutejs/secrets/broker";
|
|
72
84
|
|
|
73
85
|
const broker = createSecretBroker({
|
|
74
86
|
adapter: compositeAdapter([
|
|
75
|
-
inMemoryAdapter({ initial: { TEST_KEY:
|
|
76
|
-
envAdapter({ prefix:
|
|
87
|
+
inMemoryAdapter({ initial: { TEST_KEY: "sk_test_local_value" } }),
|
|
88
|
+
envAdapter({ prefix: "ABS_SECRET_" }),
|
|
77
89
|
]),
|
|
78
90
|
audit: (event) => observabilitySink.write(event),
|
|
79
91
|
cacheTtlMs: 60_000,
|
|
80
92
|
});
|
|
81
93
|
|
|
82
94
|
// In bridgeFetch.authorization():
|
|
83
|
-
const { value, fingerprint } = (await broker.resolve(
|
|
84
|
-
logger.info(
|
|
85
|
-
return {
|
|
95
|
+
const { value, fingerprint } = (await broker.resolve("STRIPE_KEY"))!;
|
|
96
|
+
logger.info("charging", { tenant, fingerprint }); // safe — no plaintext
|
|
97
|
+
return { Authorization: `Bearer ${value}` };
|
|
86
98
|
|
|
87
99
|
// In a log sink, before text leaves the host:
|
|
88
|
-
const sanitized = broker.redact(line);
|
|
100
|
+
const sanitized = broker.redact(line); // [REDACTED:STRIPE_KEY] replaces plaintext
|
|
89
101
|
sinkToCustomerVisibleLog(sanitized);
|
|
90
102
|
|
|
91
103
|
// Rotate:
|
|
92
|
-
const next = await broker.rotate(
|
|
93
|
-
notifyDependents(next.fingerprint);
|
|
104
|
+
const next = await broker.rotate("STRIPE_KEY");
|
|
105
|
+
notifyDependents(next.fingerprint); // tell consumers a new key is in cache
|
|
94
106
|
```
|
|
95
107
|
|
|
96
108
|
## v0.0.1 surface
|
|
97
109
|
|
|
98
|
-
| API
|
|
99
|
-
|
|
100
|
-
| `createSecretBroker(options)` | Factory. Returns a `SecretBroker`.
|
|
101
|
-
| `broker.resolve(name)`
|
|
102
|
-
| `broker.fingerprint(value)`
|
|
103
|
-
| `broker.redact(text)`
|
|
104
|
-
| `broker.rotate(name)`
|
|
105
|
-
| `broker.invalidate(name?)`
|
|
106
|
-
| `broker.dispose()`
|
|
110
|
+
| API | Purpose |
|
|
111
|
+
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
|
|
112
|
+
| `createSecretBroker(options)` | Factory. Returns a `SecretBroker`. |
|
|
113
|
+
| `broker.resolve(name)` | Returns `{ value, fingerprint } | null`. Uses cache within `cacheTtlMs`. |
|
|
114
|
+
| `broker.fingerprint(value)` | Pure helper — sha256 prefix of any string. No adapter call. |
|
|
115
|
+
| `broker.redact(text)` | Rewrite arbitrary text, replacing every cached value (longer-first) with `[REDACTED:NAME]`. Skips values shorter than `redactionMinLength`. |
|
|
116
|
+
| `broker.rotate(name)` | Calls `adapter.rotate?`, caches the result, returns it. Throws if the adapter doesn't support rotation. |
|
|
117
|
+
| `broker.invalidate(name?)` | Clear one entry or the whole cache. |
|
|
118
|
+
| `broker.dispose()` | Tear down — clears cache; subsequent resolves return `null`. |
|
|
107
119
|
|
|
108
120
|
### Bundled adapters
|
|
109
121
|
|
|
110
|
-
| Adapter
|
|
111
|
-
|
|
112
|
-
| `inMemoryAdapter({ initial?, rotate? })` | Tests, dev, and starter templates. Supports every operation. Default rotate = random base36.
|
|
113
|
-
| `envAdapter({ prefix?, env? })`
|
|
114
|
-
| `compositeAdapter([...])`
|
|
122
|
+
| Adapter | Use |
|
|
123
|
+
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
124
|
+
| `inMemoryAdapter({ initial?, rotate? })` | Tests, dev, and starter templates. Supports every operation. Default rotate = random base36. |
|
|
125
|
+
| `envAdapter({ prefix?, env? })` | Reads `process.env` (or any injected `env` map). Prefix-scoped to avoid leaking unrelated env vars via `list`. Read-only. |
|
|
126
|
+
| `compositeAdapter([...])` | Fan-out / fallback. `fetch` falls through; writes go to the first writeable adapter. |
|
|
115
127
|
|
|
116
128
|
AWS Secrets Manager / HashiCorp Vault / Doppler / Infisical / GCP Secret
|
|
117
129
|
Manager / Azure Key Vault adapters ship later as siblings — they're the
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { createCredentialOperationBroker, createMemoryCredentialGrantStore, type CredentialGrant, type CredentialGrantStore, type CredentialOperationBroker, type CredentialOperationBrokerOptions, type CredentialOperationContext, type CredentialOperationEvent, type CredentialOperationInput, type CredentialOperationResult, type CredentialProvider, type PendingCredentialOperation, } from "./operations";
|
|
2
|
+
export { createPostgresCredentialGrantStore, credentialGrantsPostgresSchemaSql, type SecretsSqlClient, type SecretsSqlResult, } from "./postgres";
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __require = import.meta.require;
|
|
17
|
+
|
|
18
|
+
// node_modules/@absolutejs/agency/dist/index.js
|
|
19
|
+
var normalize = (value) => {
|
|
20
|
+
if (Array.isArray(value))
|
|
21
|
+
return value.map(normalize);
|
|
22
|
+
if (value !== null && typeof value === "object") {
|
|
23
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalize(entry)]));
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
};
|
|
27
|
+
var canonicalJson = (value) => JSON.stringify(normalize(value));
|
|
28
|
+
var digest = async (value) => {
|
|
29
|
+
const encoded = new TextEncoder().encode(canonicalJson(value));
|
|
30
|
+
const result = await crypto.subtle.digest("SHA-256", encoded);
|
|
31
|
+
return [...new Uint8Array(result)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/operations.ts
|
|
35
|
+
var normalizeOrigin = (destination) => new URL(destination).origin;
|
|
36
|
+
var validateGrant = (grant, input, now) => {
|
|
37
|
+
if (grant.agentId !== input.actor.agentId || grant.userId !== input.actor.userId)
|
|
38
|
+
throw new Error("Credential grant actor mismatch");
|
|
39
|
+
if (grant.expiresAt <= now)
|
|
40
|
+
throw new Error("Credential grant has expired");
|
|
41
|
+
if (!grant.scopes.includes(input.operation))
|
|
42
|
+
throw new Error("Credential operation is outside grant scope");
|
|
43
|
+
const origin = normalizeOrigin(input.destination);
|
|
44
|
+
if (!grant.allowedOrigins.map(normalizeOrigin).includes(origin))
|
|
45
|
+
throw new Error("Credential destination is outside grant scope");
|
|
46
|
+
if (grant.used >= grant.maximumUses)
|
|
47
|
+
throw new Error("Credential grant has been exhausted");
|
|
48
|
+
return origin;
|
|
49
|
+
};
|
|
50
|
+
var createMemoryCredentialGrantStore = () => {
|
|
51
|
+
const grants = new Map;
|
|
52
|
+
const pending = new Map;
|
|
53
|
+
let lock = Promise.resolve();
|
|
54
|
+
const exclusive = async (run) => {
|
|
55
|
+
const previous = lock;
|
|
56
|
+
let release = () => {};
|
|
57
|
+
lock = new Promise((resolve) => {
|
|
58
|
+
release = resolve;
|
|
59
|
+
});
|
|
60
|
+
await previous;
|
|
61
|
+
try {
|
|
62
|
+
return await run();
|
|
63
|
+
} finally {
|
|
64
|
+
release();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
consume: (grantId, now) => exclusive(() => {
|
|
69
|
+
const grant = grants.get(grantId);
|
|
70
|
+
if (grant === undefined || grant.expiresAt <= now || grant.used >= grant.maximumUses)
|
|
71
|
+
return;
|
|
72
|
+
const consumed = { ...grant, used: grant.used + 1 };
|
|
73
|
+
grants.set(grantId, consumed);
|
|
74
|
+
return { ...consumed };
|
|
75
|
+
}),
|
|
76
|
+
get: async (grantId) => {
|
|
77
|
+
const grant = grants.get(grantId);
|
|
78
|
+
return grant === undefined ? undefined : { ...grant };
|
|
79
|
+
},
|
|
80
|
+
getPending: async (actionId) => pending.get(actionId),
|
|
81
|
+
put: async (grant) => {
|
|
82
|
+
if (grant.maximumUses < 1)
|
|
83
|
+
throw new Error("maximumUses must be positive");
|
|
84
|
+
if (grant.used < 0 || grant.used > grant.maximumUses)
|
|
85
|
+
throw new Error("Invalid credential grant usage");
|
|
86
|
+
grants.set(grant.grantId, { ...grant });
|
|
87
|
+
},
|
|
88
|
+
putPending: async (operation) => {
|
|
89
|
+
pending.set(operation.actionId, operation);
|
|
90
|
+
},
|
|
91
|
+
revoke: async (grantId) => grants.delete(grantId)
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
var createCredentialOperationBroker = ({
|
|
95
|
+
agency,
|
|
96
|
+
emit,
|
|
97
|
+
now = Date.now,
|
|
98
|
+
providers,
|
|
99
|
+
secrets,
|
|
100
|
+
store
|
|
101
|
+
}) => {
|
|
102
|
+
const providerMap = new Map(providers.map((provider) => [provider.provider, provider]));
|
|
103
|
+
const perform = async (input, actionId, signal) => {
|
|
104
|
+
const preview = await store.get(input.grantId);
|
|
105
|
+
if (preview === undefined)
|
|
106
|
+
throw new Error("Unknown credential grant");
|
|
107
|
+
const origin = validateGrant(preview, input, now());
|
|
108
|
+
const provider = providerMap.get(preview.provider);
|
|
109
|
+
const operation = provider?.operations[input.operation];
|
|
110
|
+
if (provider === undefined || operation === undefined)
|
|
111
|
+
throw new Error("Credential provider operation is not registered");
|
|
112
|
+
await emit?.({
|
|
113
|
+
actionId,
|
|
114
|
+
destinationOrigin: origin,
|
|
115
|
+
grantId: preview.grantId,
|
|
116
|
+
operation: input.operation,
|
|
117
|
+
provider: preview.provider,
|
|
118
|
+
type: "credential.operation.requested"
|
|
119
|
+
});
|
|
120
|
+
const consumed = await store.consume(input.grantId, now());
|
|
121
|
+
if (consumed === undefined)
|
|
122
|
+
throw new Error("Credential grant has been exhausted");
|
|
123
|
+
validateGrant({ ...consumed, used: consumed.used - 1 }, input, now());
|
|
124
|
+
const secret = await secrets.resolve(consumed.secretName);
|
|
125
|
+
if (secret === null)
|
|
126
|
+
throw new Error("Credential is not configured");
|
|
127
|
+
try {
|
|
128
|
+
const result = await operation({
|
|
129
|
+
credential: secret.value,
|
|
130
|
+
destination: new URL(input.destination),
|
|
131
|
+
input: input.input,
|
|
132
|
+
signal
|
|
133
|
+
});
|
|
134
|
+
const resultDigest = await digest(result);
|
|
135
|
+
await emit?.({
|
|
136
|
+
actionId,
|
|
137
|
+
destinationOrigin: origin,
|
|
138
|
+
grantId: consumed.grantId,
|
|
139
|
+
operation: input.operation,
|
|
140
|
+
provider: consumed.provider,
|
|
141
|
+
resultDigest,
|
|
142
|
+
status: "succeeded",
|
|
143
|
+
type: "credential.operation.completed"
|
|
144
|
+
});
|
|
145
|
+
return { actionId, kind: "completed", result, resultDigest };
|
|
146
|
+
} catch (error) {
|
|
147
|
+
await emit?.({
|
|
148
|
+
actionId,
|
|
149
|
+
destinationOrigin: origin,
|
|
150
|
+
grantId: consumed.grantId,
|
|
151
|
+
operation: input.operation,
|
|
152
|
+
provider: consumed.provider,
|
|
153
|
+
status: "failed",
|
|
154
|
+
type: "credential.operation.completed"
|
|
155
|
+
});
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const request = async (input, signal) => {
|
|
160
|
+
const grant = await store.get(input.grantId);
|
|
161
|
+
if (grant === undefined)
|
|
162
|
+
throw new Error("Unknown credential grant");
|
|
163
|
+
const origin = validateGrant(grant, input, now());
|
|
164
|
+
if (agency === undefined)
|
|
165
|
+
return perform(input, undefined, signal);
|
|
166
|
+
const { action, decision } = await agency.request({
|
|
167
|
+
action: `credential.${grant.provider}.${input.operation}`,
|
|
168
|
+
actor: input.actor,
|
|
169
|
+
context: { destinationOrigin: origin, grantId: grant.grantId },
|
|
170
|
+
effects: ["external-network"],
|
|
171
|
+
expiresAt: grant.expiresAt,
|
|
172
|
+
idempotencyKey: input.idempotencyKey,
|
|
173
|
+
input: { destination: input.destination, input: input.input },
|
|
174
|
+
resource: { id: grant.grantId, type: "credential-grant" }
|
|
175
|
+
});
|
|
176
|
+
if (decision.kind !== "allow") {
|
|
177
|
+
await store.putPending({ actionId: action.actionId, input });
|
|
178
|
+
return { actionId: action.actionId, decision, kind: "pending" };
|
|
179
|
+
}
|
|
180
|
+
const lease = await agency.issueLease(action.actionId);
|
|
181
|
+
const execution = await agency.execute({
|
|
182
|
+
executor: `credential-provider:${grant.provider}`,
|
|
183
|
+
leaseId: lease.leaseId,
|
|
184
|
+
run: () => perform(input, action.actionId, signal)
|
|
185
|
+
});
|
|
186
|
+
return execution.result;
|
|
187
|
+
};
|
|
188
|
+
const resume = async (actionId, signal) => {
|
|
189
|
+
if (agency === undefined)
|
|
190
|
+
throw new Error("Agency is not configured");
|
|
191
|
+
const pending = await store.getPending(actionId);
|
|
192
|
+
if (pending === undefined)
|
|
193
|
+
throw new Error("Unknown pending credential operation");
|
|
194
|
+
const lease = await agency.issueLease(actionId);
|
|
195
|
+
const execution = await agency.execute({
|
|
196
|
+
executor: "credential-provider",
|
|
197
|
+
leaseId: lease.leaseId,
|
|
198
|
+
run: () => perform(pending.input, actionId, signal)
|
|
199
|
+
});
|
|
200
|
+
return execution.result;
|
|
201
|
+
};
|
|
202
|
+
return { request, resume, revoke: store.revoke };
|
|
203
|
+
};
|
|
204
|
+
// src/postgres.ts
|
|
205
|
+
var namespaceOf = (namespace) => {
|
|
206
|
+
if (!/^[a-z_][a-z0-9_]*$/.test(namespace))
|
|
207
|
+
throw new Error("Secrets PostgreSQL namespace must be a simple identifier");
|
|
208
|
+
return namespace;
|
|
209
|
+
};
|
|
210
|
+
var credentialGrantsPostgresSchemaSql = (namespace = "secrets") => {
|
|
211
|
+
const ns = namespaceOf(namespace);
|
|
212
|
+
return `CREATE SCHEMA IF NOT EXISTS ${ns};
|
|
213
|
+
CREATE TABLE IF NOT EXISTS ${ns}.credential_grants (
|
|
214
|
+
grant_id text PRIMARY KEY, agent_id text NOT NULL, user_id text NOT NULL,
|
|
215
|
+
provider text NOT NULL, expires_at bigint NOT NULL, maximum_uses integer NOT NULL CHECK (maximum_uses > 0),
|
|
216
|
+
used integer NOT NULL DEFAULT 0 CHECK (used >= 0 AND used <= maximum_uses), data jsonb NOT NULL
|
|
217
|
+
);
|
|
218
|
+
CREATE INDEX IF NOT EXISTS credential_grants_agent_idx ON ${ns}.credential_grants (agent_id, expires_at);
|
|
219
|
+
CREATE TABLE IF NOT EXISTS ${ns}.pending_operations (
|
|
220
|
+
action_id text PRIMARY KEY, grant_id text NOT NULL REFERENCES ${ns}.credential_grants(grant_id) ON DELETE CASCADE,
|
|
221
|
+
created_at timestamptz NOT NULL DEFAULT now(), data jsonb NOT NULL
|
|
222
|
+
);`;
|
|
223
|
+
};
|
|
224
|
+
var createPostgresCredentialGrantStore = ({
|
|
225
|
+
client,
|
|
226
|
+
namespace = "secrets"
|
|
227
|
+
}) => {
|
|
228
|
+
const ns = namespaceOf(namespace);
|
|
229
|
+
return {
|
|
230
|
+
consume: async (grantId, now) => {
|
|
231
|
+
const result = await client.query(`UPDATE ${ns}.credential_grants SET used = used + 1, data = jsonb_set(data, '{used}', to_jsonb(used + 1), true) WHERE grant_id = $1 AND expires_at > $2 AND used < maximum_uses RETURNING data`, [grantId, now]);
|
|
232
|
+
return result.rows[0]?.data;
|
|
233
|
+
},
|
|
234
|
+
get: async (grantId) => (await client.query(`SELECT data FROM ${ns}.credential_grants WHERE grant_id = $1`, [grantId])).rows[0]?.data,
|
|
235
|
+
getPending: async (actionId) => (await client.query(`SELECT data FROM ${ns}.pending_operations WHERE action_id = $1`, [actionId])).rows[0]?.data,
|
|
236
|
+
put: async (grant) => {
|
|
237
|
+
if (grant.maximumUses < 1 || grant.used < 0 || grant.used > grant.maximumUses)
|
|
238
|
+
throw new Error("Invalid credential grant usage");
|
|
239
|
+
await client.query(`INSERT INTO ${ns}.credential_grants (grant_id, agent_id, user_id, provider, expires_at, maximum_uses, used, data) VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb) ON CONFLICT (grant_id) DO UPDATE SET expires_at=EXCLUDED.expires_at, maximum_uses=EXCLUDED.maximum_uses, used=EXCLUDED.used, data=EXCLUDED.data`, [
|
|
240
|
+
grant.grantId,
|
|
241
|
+
grant.agentId,
|
|
242
|
+
grant.userId,
|
|
243
|
+
grant.provider,
|
|
244
|
+
grant.expiresAt,
|
|
245
|
+
grant.maximumUses,
|
|
246
|
+
grant.used,
|
|
247
|
+
JSON.stringify(grant)
|
|
248
|
+
]);
|
|
249
|
+
},
|
|
250
|
+
putPending: async (pending) => {
|
|
251
|
+
await client.query(`INSERT INTO ${ns}.pending_operations (action_id, grant_id, data) VALUES ($1,$2,$3::jsonb) ON CONFLICT (action_id) DO UPDATE SET data=EXCLUDED.data`, [pending.actionId, pending.input.grantId, JSON.stringify(pending)]);
|
|
252
|
+
},
|
|
253
|
+
revoke: async (grantId) => (await client.query(`DELETE FROM ${ns}.credential_grants WHERE grant_id = $1`, [grantId])).rowCount === 1
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
export {
|
|
257
|
+
credentialGrantsPostgresSchemaSql,
|
|
258
|
+
createPostgresCredentialGrantStore,
|
|
259
|
+
createMemoryCredentialGrantStore,
|
|
260
|
+
createCredentialOperationBroker
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
//# debugId=2D416FD568D31C1164756E2164756E21
|
|
264
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../node_modules/@absolutejs/agency/dist/index.js", "../src/operations.ts", "../src/postgres.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"// @bun\nvar __defProp = Object.defineProperty;\nvar __returnValue = (v) => v;\nfunction __exportSetter(name, newValue) {\n this[name] = __returnValue.bind(null, newValue);\n}\nvar __export = (target, all) => {\n for (var name in all)\n __defProp(target, name, {\n get: all[name],\n enumerable: true,\n configurable: true,\n set: __exportSetter.bind(all, name)\n });\n};\n\n// src/canonical.ts\nvar normalize = (value) => {\n if (Array.isArray(value))\n return value.map(normalize);\n if (value !== null && typeof value === \"object\") {\n return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined).sort(([left], [right]) => left.localeCompare(right)).map(([key, entry]) => [key, normalize(entry)]));\n }\n return value;\n};\nvar canonicalJson = (value) => JSON.stringify(normalize(value));\nvar digest = async (value) => {\n const encoded = new TextEncoder().encode(canonicalJson(value));\n const result = await crypto.subtle.digest(\"SHA-256\", encoded);\n return [...new Uint8Array(result)].map((byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n};\nvar actionBinding = (action) => digest({\n action: action.action,\n actionId: action.actionId,\n actor: action.actor,\n effects: action.effects,\n expiresAt: action.expiresAt,\n inputDigest: action.inputDigest,\n resource: action.resource,\n spend: action.spend\n});\n// src/engine.ts\nvar DEFAULT_LEASE_TTL_MS = 60000;\nvar required = (value, message) => {\n if (value === undefined)\n throw new Error(message);\n return value;\n};\nvar createAgency = ({\n control,\n delegations,\n defaultLeaseTtlMs = DEFAULT_LEASE_TTL_MS,\n emit,\n now = Date.now,\n policy,\n store\n}) => {\n const decide = async (action, approval) => {\n const decision = await policy.evaluate({ action, approval, now: now() });\n await emit?.({\n actionId: action.actionId,\n decision,\n type: \"action.decided\"\n });\n return decision;\n };\n const request = async (input) => {\n await control?.assertActive(input.actor.agentId);\n const delegation = await delegations?.assertAllows(input);\n const createdAt = now();\n const delegatedExpiry = Math.min(input.expiresAt ?? Number.POSITIVE_INFINITY, delegation?.expiresAt ?? Number.POSITIVE_INFINITY);\n const action = {\n ...input,\n ...Number.isFinite(delegatedExpiry) ? { expiresAt: delegatedExpiry } : {},\n actionId: `act_${crypto.randomUUID()}`,\n createdAt,\n inputDigest: await digest(input.input ?? null)\n };\n await store.saveAction(action);\n await emit?.({ action, type: \"action.requested\" });\n return { action, decision: await decide(action) };\n };\n const approve = async ({\n actionId,\n approvedBy,\n approvedUntil,\n conditions,\n state\n }) => {\n const action = required(await store.getAction(actionId), \"Unknown action\");\n const approvedAt = now();\n if (approvedUntil <= approvedAt)\n throw new Error(\"Approval must expire in the future\");\n const approval = {\n actionId,\n approvalId: `apr_${crypto.randomUUID()}`,\n approvedAt,\n approvedBy,\n approvedUntil,\n bindingDigest: await actionBinding(action),\n conditions,\n state\n };\n await store.saveApproval(approval);\n await emit?.({ actionId, approval, type: \"action.approved\" });\n return approval;\n };\n const issueLease = async (actionId) => {\n const action = required(await store.getAction(actionId), \"Unknown action\");\n await control?.assertActive(action.actor.agentId);\n const delegation = await delegations?.assertAllows(action);\n const approval = await store.getApproval(actionId);\n const currentTime = now();\n if (action.expiresAt !== undefined && action.expiresAt <= currentTime) {\n throw new Error(\"Action request has expired\");\n }\n if (approval !== undefined) {\n if (approval.approvedUntil <= currentTime)\n throw new Error(\"Approval has expired\");\n if (approval.bindingDigest !== await actionBinding(action)) {\n throw new Error(\"Approval is not bound to the current action\");\n }\n }\n const decision = await decide(action, approval);\n if (decision.kind !== \"allow\")\n throw new Error(`Action denied: ${decision.reason}`);\n const issuedAt = now();\n const lease = {\n actionId,\n bindingDigest: await actionBinding(action),\n expiresAt: Math.min(issuedAt + defaultLeaseTtlMs, action.expiresAt ?? Number.POSITIVE_INFINITY, delegation?.expiresAt ?? Number.POSITIVE_INFINITY, approval?.approvedUntil ?? Number.POSITIVE_INFINITY, decision.expiresAt ?? Number.POSITIVE_INFINITY),\n issuedAt,\n leaseId: `lease_${crypto.randomUUID()}`,\n maximumUses: 1\n };\n await store.saveLease(lease);\n await emit?.({ actionId, lease, type: \"action.lease-issued\" });\n return lease;\n };\n const execute = async ({\n costs,\n executor,\n leaseId,\n run\n }) => {\n const lease = required(await store.getLease(leaseId), \"Unknown execution lease\");\n const action = required(await store.getAction(lease.actionId), \"Unknown action\");\n await control?.assertActive(action.actor.agentId);\n await delegations?.assertAllows(action);\n const startedAt = now();\n if (lease.expiresAt <= startedAt)\n throw new Error(\"Execution lease has expired\");\n if (lease.bindingDigest !== await actionBinding(action)) {\n throw new Error(\"Execution lease is not bound to the current action\");\n }\n if (!await store.consumeLease(leaseId, startedAt)) {\n throw new Error(\"Execution lease has already been consumed\");\n }\n let result;\n let receipt;\n try {\n result = await run();\n receipt = {\n actionId: action.actionId,\n completedAt: now(),\n costs,\n executor,\n leaseId,\n receiptId: `rcpt_${crypto.randomUUID()}`,\n resultDigest: await digest(result),\n startedAt,\n status: \"succeeded\"\n };\n } catch (error) {\n receipt = {\n actionId: action.actionId,\n completedAt: now(),\n costs,\n error: error instanceof Error ? error.message : \"Action failed\",\n executor,\n leaseId,\n receiptId: `rcpt_${crypto.randomUUID()}`,\n startedAt,\n status: \"failed\"\n };\n await store.saveReceipt(receipt);\n await emit?.({\n actionId: action.actionId,\n receipt,\n type: \"action.completed\"\n });\n throw error;\n }\n await store.saveReceipt(receipt);\n await emit?.({\n actionId: action.actionId,\n receipt,\n type: \"action.completed\"\n });\n return { receipt, result };\n };\n const inspect = async (actorId) => ({\n actions: await store.listActions(actorId),\n approvals: await store.listApprovals(actorId),\n leases: await store.listLeases(actorId),\n receipts: await store.listReceipts(actorId)\n });\n return { approve, execute, inspect, issueLease, request };\n};\n// src/memory.ts\nvar clone = (value) => structuredClone(value);\nvar createMemoryAgencyStore = () => {\n const actions = new Map;\n const approvals = new Map;\n const leases = new Map;\n const receipts = new Map;\n const actorForAction = (actionId) => actions.get(actionId)?.actor.agentId;\n return {\n consumeLease: async (leaseId, consumedAt) => {\n const lease = leases.get(leaseId);\n if (lease === undefined || lease.consumedAt !== undefined)\n return false;\n leases.set(leaseId, { ...lease, consumedAt });\n return true;\n },\n getAction: async (actionId) => {\n const action = actions.get(actionId);\n return action === undefined ? undefined : clone(action);\n },\n getApproval: async (actionId) => {\n const approval = approvals.get(actionId);\n return approval === undefined ? undefined : clone(approval);\n },\n getLease: async (leaseId) => {\n const lease = leases.get(leaseId);\n return lease === undefined ? undefined : clone(lease);\n },\n listActions: async (actorId) => [...actions.values()].filter((action) => actorId === undefined || action.actor.agentId === actorId).map(clone),\n listApprovals: async (actorId) => [...approvals.values()].filter((approval) => actorId === undefined || actorForAction(approval.actionId) === actorId).map(clone),\n listLeases: async (actorId) => [...leases.values()].filter((lease) => actorId === undefined || actorForAction(lease.actionId) === actorId).map(clone),\n listReceipts: async (actorId) => [...receipts.values()].filter((receipt) => actorId === undefined || actorForAction(receipt.actionId) === actorId).map(clone),\n saveAction: async (action) => {\n actions.set(action.actionId, clone(action));\n },\n saveApproval: async (approval) => {\n approvals.set(approval.actionId, clone(approval));\n },\n saveLease: async (lease) => {\n leases.set(lease.leaseId, clone(lease));\n },\n saveReceipt: async (receipt) => {\n receipts.set(receipt.receiptId, clone(receipt));\n }\n };\n};\n// src/delegation.ts\nvar clone2 = (value) => structuredClone(value);\nvar createMemoryAgentDelegationStore = () => {\n const grants = new Map;\n return {\n get: async (id) => {\n const grant = grants.get(id);\n return grant ? clone2(grant) : undefined;\n },\n listForAgent: async (agentId) => [...grants.values()].filter((grant) => grant.subjectAgentId === agentId || grant.issuerAgentId === agentId).map(clone2),\n revokeTree: async (rootId, revokedAt) => {\n const pending = [rootId];\n let count = 0;\n while (pending.length > 0) {\n const id = pending.shift();\n const grant = grants.get(id);\n if (!grant)\n continue;\n if (!grant.revokedAt) {\n grant.revokedAt = revokedAt;\n count += 1;\n }\n for (const child of grants.values())\n if (child.parentDelegationId === id)\n pending.push(child.delegationId);\n }\n return count;\n },\n save: async (grant) => {\n if (grants.has(grant.delegationId))\n throw new Error(\"Delegation already exists\");\n grants.set(grant.delegationId, clone2(grant));\n }\n };\n};\nvar subset = (child, parent) => child.every((value) => parent.includes(value));\nvar validMoney = (money) => money === undefined || Number.isSafeInteger(money.amountMinor) && money.amountMinor >= 0 && /^[A-Z]{3}$/u.test(money.currency);\nvar validate = (grant) => {\n if (!grant.audience || !grant.issuerAgentId || !grant.subjectAgentId || !grant.userId || grant.scopes.length === 0 || grant.effects.length === 0 || grant.resourceTypes.length === 0)\n throw new Error(\"Delegation is missing required boundaries\");\n if (!validMoney(grant.spendLimit))\n throw new Error(\"Invalid delegation spend limit\");\n};\nvar createAgentDelegationAuthority = (options) => {\n const now = options.now ?? Date.now;\n const issue = async (input) => {\n validate(input);\n const issuedAt = now();\n if (input.parentDelegationId)\n throw new Error(\"Use delegate() to issue a child delegation\");\n if (input.audience !== options.audience)\n throw new Error(\"Delegation audience mismatch\");\n if (input.expiresAt <= issuedAt)\n throw new Error(\"Delegation must expire in the future\");\n const grant = {\n ...clone2(input),\n delegationId: `dlg_${crypto.randomUUID()}`,\n depth: 0,\n issuedAt\n };\n await options.store.save(grant);\n return clone2(grant);\n };\n const delegate = async (parentDelegationId, input) => {\n validate(input);\n const parent = await options.store.get(parentDelegationId);\n if (!parent)\n throw new Error(\"Unknown parent delegation\");\n const current = now();\n if (parent.revokedAt || parent.expiresAt <= current)\n throw new Error(\"Parent delegation is inactive\");\n if (parent.depth + 1 > (options.maxDepth ?? 8))\n throw new Error(\"Delegation depth exceeded\");\n if (input.issuerAgentId !== parent.subjectAgentId || input.userId !== parent.userId || input.audience !== parent.audience)\n throw new Error(\"Child delegation changed identity or audience\");\n if (input.expiresAt > parent.expiresAt || !subset(input.scopes, parent.scopes) || !subset(input.effects, parent.effects) || !subset(input.resourceTypes, parent.resourceTypes) || parent.resourceIds && (!input.resourceIds || !subset(input.resourceIds, parent.resourceIds)))\n throw new Error(\"Child delegation escalates parent authority\");\n if (parent.spendLimit) {\n if (!input.spendLimit || input.spendLimit.currency !== parent.spendLimit.currency || input.spendLimit.amountMinor > parent.spendLimit.amountMinor)\n throw new Error(\"Child delegation escalates spend\");\n }\n const grant = {\n ...clone2(input),\n delegationId: `dlg_${crypto.randomUUID()}`,\n depth: parent.depth + 1,\n issuedAt: current,\n parentDelegationId\n };\n await options.store.save(grant);\n return clone2(grant);\n };\n const getActiveChain = async (delegationId) => {\n const chain = [];\n const seen = new Set;\n let id = delegationId;\n while (id) {\n if (seen.has(id))\n throw new Error(\"Delegation chain contains a cycle\");\n seen.add(id);\n const grant = await options.store.get(id);\n if (!grant)\n throw new Error(\"Unknown delegation\");\n if (grant.revokedAt || grant.expiresAt <= now())\n throw new Error(\"Delegation is inactive\");\n chain.push(grant);\n id = grant.parentDelegationId;\n }\n return chain;\n };\n const assertAllows = async (action) => {\n if (!action.actor.delegationId)\n return {};\n const chain = await getActiveChain(action.actor.delegationId);\n const grant = chain[0];\n if (grant.audience !== options.audience || grant.subjectAgentId !== action.actor.agentId || grant.userId !== action.actor.userId || !subset(action.actor.scopes, grant.scopes))\n throw new Error(\"Delegation actor or audience mismatch\");\n if (!grant.scopes.includes(action.action))\n throw new Error(\"Action is outside delegated scope\");\n if (!action.effects.every((effect) => grant.effects.includes(effect)))\n throw new Error(\"Action effect is outside delegation\");\n if (!grant.resourceTypes.includes(action.resource.type))\n throw new Error(\"Resource type is outside delegation\");\n if (grant.resourceIds && !grant.resourceIds.includes(action.resource.id))\n throw new Error(\"Resource is outside delegation\");\n if (action.spend) {\n if (!validMoney(action.spend) || !grant.spendLimit || action.spend.currency !== grant.spendLimit.currency || action.spend.amountMinor > grant.spendLimit.amountMinor)\n throw new Error(\"Spend is outside delegation\");\n }\n return { expiresAt: Math.min(...chain.map(({ expiresAt }) => expiresAt)) };\n };\n return {\n assertAllows,\n delegate,\n issue,\n listForAgent: options.store.listForAgent,\n revoke: (delegationId) => options.store.revokeTree(delegationId, now())\n };\n};\n// src/policies.ts\nvar allowAllPolicy = () => ({\n evaluate: ({ now }) => ({\n decisionId: `decision_${crypto.randomUUID()}`,\n evaluatedAt: now,\n kind: \"allow\"\n })\n});\nvar denyAllPolicy = (reason = \"default_deny\") => ({\n evaluate: ({ now }) => ({\n decisionId: `decision_${crypto.randomUUID()}`,\n evaluatedAt: now,\n kind: \"deny\",\n reason,\n requestable: false\n })\n});\n// src/control.ts\nvar createMemoryAgentControlStore = () => {\n const switches = new Map;\n return {\n clearKillSwitch: async (agentId) => {\n switches.delete(agentId);\n },\n getKillSwitch: async (agentId) => switches.get(agentId),\n setKillSwitch: async (killSwitch) => {\n switches.set(killSwitch.agentId, structuredClone(killSwitch));\n }\n };\n};\nvar createAgentControlPlane = ({\n now = Date.now,\n sources,\n store\n}) => {\n const status = async (agentId) => store.getKillSwitch(agentId);\n const assertActive = async (agentId) => {\n const killSwitch = await status(agentId);\n if (killSwitch !== undefined)\n throw new Error(`Agent is disabled: ${killSwitch.reason}`);\n };\n const inventory = async (agentId) => {\n const settled = await Promise.allSettled(sources.map(async (source) => ({\n items: await source.inventory(agentId),\n source: source.name\n })));\n return {\n agentId,\n items: settled.flatMap((result) => result.status === \"fulfilled\" ? result.value.items : []),\n killSwitch: await status(agentId),\n sourceErrors: settled.flatMap((result, index) => result.status === \"rejected\" ? [\n {\n error: result.reason instanceof Error ? result.reason.message : \"Control source failed\",\n source: sources[index]?.name ?? \"unknown\"\n }\n ] : [])\n };\n };\n const revoke = async ({\n activatedBy,\n agentId,\n reason\n }) => {\n const killSwitch = {\n activatedAt: now(),\n activatedBy,\n agentId,\n reason\n };\n await store.setKillSwitch(killSwitch);\n const settled = await Promise.allSettled(sources.map(async (source) => ({\n revoked: await source.revoke(agentId, reason),\n source: source.name\n })));\n return {\n killSwitch,\n results: settled.map((result, index) => result.status === \"fulfilled\" ? { ...result.value, status: \"fulfilled\" } : {\n error: result.reason instanceof Error ? result.reason.message : \"Control source failed\",\n source: sources[index]?.name ?? \"unknown\",\n status: \"rejected\"\n })\n };\n };\n return {\n assertActive,\n inventory,\n restore: store.clearKillSwitch,\n revoke,\n status\n };\n};\n// src/handoff.ts\nvar encode = (value) => btoa(String.fromCharCode(...value instanceof Uint8Array ? value : new Uint8Array(value))).replaceAll(\"+\", \"-\").replaceAll(\"/\", \"_\").replace(/=+$/, \"\");\nvar decode = (value) => {\n const padded = value.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\");\n const raw = atob(padded + \"=\".repeat((4 - padded.length % 4) % 4));\n return Uint8Array.from(raw, (character) => character.charCodeAt(0));\n};\nvar payloadFor = (claims, keyId) => new TextEncoder().encode(canonicalJson({ claims, keyId }));\nvar keyBytes = (key) => typeof key === \"string\" ? new TextEncoder().encode(key) : key;\nvar constantTimeEqual = (left, right) => {\n if (left.length !== right.length)\n return false;\n let difference = 0;\n for (let index = 0;index < left.length; index += 1)\n difference |= (left[index] ?? 0) ^ (right[index] ?? 0);\n return difference === 0;\n};\nvar sign = async (claims, keyId, key) => {\n const rawKey = Uint8Array.from(keyBytes(key));\n const cryptoKey = await crypto.subtle.importKey(\"raw\", rawKey.buffer, { hash: \"SHA-256\", name: \"HMAC\" }, false, [\"sign\"]);\n return encode(await crypto.subtle.sign(\"HMAC\", cryptoKey, payloadFor(claims, keyId)));\n};\nvar signAgentHandoffWith = async ({\n claims,\n signer\n}) => {\n const versioned = {\n ...claims,\n version: \"absolute.agent-handoff/1\"\n };\n const signature = await signer.sign(payloadFor(versioned, signer.keyId));\n return {\n algorithm: signer.algorithm,\n claims: versioned,\n keyId: signer.keyId,\n signature: encode(signature)\n };\n};\nvar verifyAgentHandoffWith = async ({\n expectedAudience,\n handoff,\n now = Date.now,\n replayStore,\n verifier\n}) => {\n if (handoff.claims.version !== \"absolute.agent-handoff/1\")\n throw new Error(\"Unsupported handoff version\");\n if (handoff.claims.audience !== expectedAudience)\n throw new Error(\"Agent handoff audience mismatch\");\n if (handoff.claims.expiresAt <= now())\n throw new Error(\"Agent handoff has expired\");\n if (!await verifier.verify({\n algorithm: handoff.algorithm,\n keyId: handoff.keyId,\n payload: payloadFor(handoff.claims, handoff.keyId),\n signature: decode(handoff.signature)\n })) {\n throw new Error(\"Invalid agent handoff signature\");\n }\n if (!await replayStore.consume(handoff.claims.nonce, handoff.claims.expiresAt)) {\n throw new Error(\"Agent handoff has already been consumed\");\n }\n return structuredClone(handoff.claims);\n};\nvar signAgentHandoff = async ({\n claims,\n key,\n keyId\n}) => {\n const versioned = {\n ...claims,\n version: \"absolute.agent-handoff/1\"\n };\n return {\n algorithm: \"HS256\",\n claims: versioned,\n keyId,\n signature: await sign(versioned, keyId, key)\n };\n};\nvar verifyAgentHandoff = async ({\n expectedAudience,\n handoff,\n key,\n now = Date.now,\n replayStore\n}) => {\n if (handoff.algorithm !== \"HS256\")\n throw new Error(\"Unsupported handoff algorithm\");\n if (handoff.claims.version !== \"absolute.agent-handoff/1\")\n throw new Error(\"Unsupported handoff version\");\n if (handoff.claims.audience !== expectedAudience)\n throw new Error(\"Agent handoff audience mismatch\");\n if (handoff.claims.expiresAt <= now())\n throw new Error(\"Agent handoff has expired\");\n const expected = await sign(handoff.claims, handoff.keyId, key);\n const actualBytes = new TextEncoder().encode(handoff.signature);\n const expectedBytes = new TextEncoder().encode(expected);\n if (!constantTimeEqual(actualBytes, expectedBytes))\n throw new Error(\"Invalid agent handoff signature\");\n if (!await replayStore.consume(handoff.claims.nonce, handoff.claims.expiresAt))\n throw new Error(\"Agent handoff has already been consumed\");\n return structuredClone(handoff.claims);\n};\nvar createMemoryHandoffReplayStore = (now = Date.now) => {\n const nonces = new Map;\n return {\n consume: async (nonce, expiresAt) => {\n for (const [storedNonce, expiry] of nonces)\n if (expiry <= now())\n nonces.delete(storedNonce);\n if (nonces.has(nonce))\n return false;\n nonces.set(nonce, expiresAt);\n return true;\n }\n };\n};\nvar attenuateAgentHandoff = (parent, child) => {\n if (child.userId !== parent.userId)\n throw new Error(\"Agent handoff cannot change users\");\n if (child.expiresAt > parent.expiresAt)\n throw new Error(\"Agent handoff cannot extend expiry\");\n if (child.scopes.some((scope) => !parent.scopes.includes(scope)))\n throw new Error(\"Agent handoff cannot escalate scopes\");\n if (parent.spendLimit !== undefined) {\n if (child.spendLimit === undefined || child.spendLimit.currency !== parent.spendLimit.currency || child.spendLimit.amountMinor > parent.spendLimit.amountMinor)\n throw new Error(\"Agent handoff cannot escalate spend\");\n }\n return child;\n};\n// src/simulation.ts\nvar simulateAction = async ({\n approval,\n input,\n now = Date.now,\n policy\n}) => {\n const evaluatedAt = now();\n const action = {\n ...input,\n actionId: `sim_${await digest(input)}`,\n createdAt: evaluatedAt,\n inputDigest: await digest(input.input ?? null)\n };\n const decision = await policy.evaluate({\n action,\n approval,\n now: evaluatedAt\n });\n return {\n action,\n bindingDigest: await actionBinding(action),\n decision,\n dryRun: true,\n wouldExecute: decision.kind === \"allow\"\n };\n};\n// src/postgres.ts\nvar namespaceOf = (namespace) => {\n if (!/^[a-z_][a-z0-9_]*$/.test(namespace))\n throw new Error(\"Agency PostgreSQL namespace must be a simple identifier\");\n return namespace;\n};\nvar agencyPostgresSchemaSql = (namespace = \"agency\") => {\n const ns = namespaceOf(namespace);\n return `CREATE SCHEMA IF NOT EXISTS ${ns};\nCREATE TABLE IF NOT EXISTS ${ns}.actions (\n action_id text PRIMARY KEY,\n actor_id text NOT NULL,\n created_at bigint NOT NULL,\n data jsonb NOT NULL\n);\nCREATE INDEX IF NOT EXISTS actions_actor_created_idx ON ${ns}.actions (actor_id, created_at DESC);\nCREATE TABLE IF NOT EXISTS ${ns}.approvals (\n action_id text PRIMARY KEY REFERENCES ${ns}.actions(action_id) ON DELETE CASCADE,\n actor_id text NOT NULL,\n approved_at bigint NOT NULL,\n data jsonb NOT NULL\n);\nCREATE TABLE IF NOT EXISTS ${ns}.leases (\n lease_id text PRIMARY KEY,\n action_id text NOT NULL REFERENCES ${ns}.actions(action_id) ON DELETE CASCADE,\n actor_id text NOT NULL,\n issued_at bigint NOT NULL,\n consumed_at bigint,\n data jsonb NOT NULL\n);\nCREATE TABLE IF NOT EXISTS ${ns}.receipts (\n receipt_id text PRIMARY KEY,\n action_id text NOT NULL REFERENCES ${ns}.actions(action_id) ON DELETE CASCADE,\n actor_id text NOT NULL,\n completed_at bigint NOT NULL,\n data jsonb NOT NULL\n);\nCREATE TABLE IF NOT EXISTS ${ns}.kill_switches (\n agent_id text PRIMARY KEY,\n activated_at bigint NOT NULL,\n data jsonb NOT NULL\n);\nCREATE TABLE IF NOT EXISTS ${ns}.handoff_nonces (\n nonce_hash text PRIMARY KEY,\n expires_at bigint NOT NULL\n);\nCREATE INDEX IF NOT EXISTS handoff_nonces_expiry_idx ON ${ns}.handoff_nonces (expires_at);\nCREATE TABLE IF NOT EXISTS ${ns}.delegations (\n delegation_id text PRIMARY KEY,\n parent_delegation_id text REFERENCES ${ns}.delegations(delegation_id),\n issuer_agent_id text NOT NULL,\n subject_agent_id text NOT NULL,\n expires_at bigint NOT NULL,\n revoked_at bigint,\n data jsonb NOT NULL\n);\nCREATE INDEX IF NOT EXISTS delegations_subject_idx ON ${ns}.delegations (subject_agent_id, expires_at DESC);\nCREATE INDEX IF NOT EXISTS delegations_issuer_idx ON ${ns}.delegations (issuer_agent_id, expires_at DESC);\nCREATE INDEX IF NOT EXISTS delegations_parent_idx ON ${ns}.delegations (parent_delegation_id);`;\n};\nvar createPostgresAgencyStore = ({\n client,\n namespace = \"agency\"\n}) => {\n const ns = namespaceOf(namespace);\n const one = async (sql, parameters) => (await client.query(sql, parameters)).rows[0]?.data;\n const many = async (sql, parameters) => (await client.query(sql, parameters)).rows.map(({ data }) => data);\n const actorFor = async (actionId) => {\n const row = await client.query(`SELECT actor_id FROM ${ns}.actions WHERE action_id = $1`, [actionId]);\n if (row.rows[0] === undefined)\n throw new Error(\"Unknown action\");\n return row.rows[0].actor_id;\n };\n return {\n consumeLease: async (leaseId, consumedAt) => (await client.query(`UPDATE ${ns}.leases SET consumed_at = $2, data = jsonb_set(data, '{consumedAt}', to_jsonb($2::bigint), true) WHERE lease_id = $1 AND consumed_at IS NULL`, [leaseId, consumedAt])).rowCount === 1,\n getAction: (id) => one(`SELECT data FROM ${ns}.actions WHERE action_id = $1`, [id]),\n getApproval: (id) => one(`SELECT data FROM ${ns}.approvals WHERE action_id = $1`, [id]),\n getLease: (id) => one(`SELECT data FROM ${ns}.leases WHERE lease_id = $1`, [id]),\n listActions: (actorId) => many(`SELECT data FROM ${ns}.actions WHERE ($1::text IS NULL OR actor_id = $1) ORDER BY created_at DESC`, [actorId ?? null]),\n listApprovals: (actorId) => many(`SELECT data FROM ${ns}.approvals WHERE ($1::text IS NULL OR actor_id = $1) ORDER BY approved_at DESC`, [actorId ?? null]),\n listLeases: (actorId) => many(`SELECT data FROM ${ns}.leases WHERE ($1::text IS NULL OR actor_id = $1) ORDER BY issued_at DESC`, [actorId ?? null]),\n listReceipts: (actorId) => many(`SELECT data FROM ${ns}.receipts WHERE ($1::text IS NULL OR actor_id = $1) ORDER BY completed_at DESC`, [actorId ?? null]),\n saveAction: async (action) => {\n await client.query(`INSERT INTO ${ns}.actions (action_id, actor_id, created_at, data) VALUES ($1, $2, $3, $4::jsonb) ON CONFLICT (action_id) DO UPDATE SET data = EXCLUDED.data`, [\n action.actionId,\n action.actor.agentId,\n action.createdAt,\n JSON.stringify(action)\n ]);\n },\n saveApproval: async (approval) => {\n await client.query(`INSERT INTO ${ns}.approvals (action_id, actor_id, approved_at, data) VALUES ($1, $2, $3, $4::jsonb) ON CONFLICT (action_id) DO UPDATE SET approved_at = EXCLUDED.approved_at, data = EXCLUDED.data`, [\n approval.actionId,\n await actorFor(approval.actionId),\n approval.approvedAt,\n JSON.stringify(approval)\n ]);\n },\n saveLease: async (lease) => {\n await client.query(`INSERT INTO ${ns}.leases (lease_id, action_id, actor_id, issued_at, consumed_at, data) VALUES ($1, $2, $3, $4, $5, $6::jsonb) ON CONFLICT (lease_id) DO NOTHING`, [\n lease.leaseId,\n lease.actionId,\n await actorFor(lease.actionId),\n lease.issuedAt,\n lease.consumedAt ?? null,\n JSON.stringify(lease)\n ]);\n },\n saveReceipt: async (receipt) => {\n await client.query(`INSERT INTO ${ns}.receipts (receipt_id, action_id, actor_id, completed_at, data) VALUES ($1, $2, $3, $4, $5::jsonb) ON CONFLICT (receipt_id) DO NOTHING`, [\n receipt.receiptId,\n receipt.actionId,\n await actorFor(receipt.actionId),\n receipt.completedAt,\n JSON.stringify(receipt)\n ]);\n }\n };\n};\nvar createPostgresAgentControlStore = ({\n client,\n namespace = \"agency\"\n}) => {\n const ns = namespaceOf(namespace);\n return {\n clearKillSwitch: async (agentId) => {\n await client.query(`DELETE FROM ${ns}.kill_switches WHERE agent_id = $1`, [agentId]);\n },\n getKillSwitch: async (agentId) => (await client.query(`SELECT data FROM ${ns}.kill_switches WHERE agent_id = $1`, [agentId])).rows[0]?.data,\n setKillSwitch: async (killSwitch) => {\n await client.query(`INSERT INTO ${ns}.kill_switches (agent_id, activated_at, data) VALUES ($1, $2, $3::jsonb) ON CONFLICT (agent_id) DO UPDATE SET activated_at = EXCLUDED.activated_at, data = EXCLUDED.data`, [\n killSwitch.agentId,\n killSwitch.activatedAt,\n JSON.stringify(killSwitch)\n ]);\n }\n };\n};\nvar createPostgresHandoffReplayStore = ({\n client,\n namespace = \"agency\",\n now = Date.now\n}) => {\n const ns = namespaceOf(namespace);\n return {\n consume: async (nonce, expiresAt) => {\n if (expiresAt <= now())\n return false;\n const nonceHash = await digest(nonce);\n const result = await client.query(`INSERT INTO ${ns}.handoff_nonces (nonce_hash, expires_at) VALUES ($1, $2) ON CONFLICT (nonce_hash) DO NOTHING`, [nonceHash, expiresAt]);\n return result.rowCount === 1;\n }\n };\n};\nvar createPostgresAgentDelegationStore = ({\n client,\n namespace = \"agency\"\n}) => {\n const ns = namespaceOf(namespace);\n return {\n get: async (id) => (await client.query(`SELECT data FROM ${ns}.delegations WHERE delegation_id=$1`, [id])).rows[0]?.data,\n listForAgent: async (agentId) => (await client.query(`SELECT data FROM ${ns}.delegations WHERE issuer_agent_id=$1 OR subject_agent_id=$1 ORDER BY expires_at DESC`, [agentId])).rows.map(({ data }) => data),\n revokeTree: async (id, revokedAt) => (await client.query(`WITH RECURSIVE tree AS (SELECT delegation_id FROM ${ns}.delegations WHERE delegation_id=$1 UNION ALL SELECT child.delegation_id FROM ${ns}.delegations child JOIN tree parent ON child.parent_delegation_id=parent.delegation_id) UPDATE ${ns}.delegations SET revoked_at=$2, data=jsonb_set(data,'{revokedAt}',to_jsonb($2::bigint),true) WHERE delegation_id IN (SELECT delegation_id FROM tree) AND revoked_at IS NULL`, [id, revokedAt])).rowCount,\n save: async (grant) => {\n const result = await client.query(`INSERT INTO ${ns}.delegations (delegation_id,parent_delegation_id,issuer_agent_id,subject_agent_id,expires_at,revoked_at,data) VALUES ($1,$2,$3,$4,$5,$6,$7::jsonb) ON CONFLICT (delegation_id) DO NOTHING`, [\n grant.delegationId,\n grant.parentDelegationId ?? null,\n grant.issuerAgentId,\n grant.subjectAgentId,\n grant.expiresAt,\n grant.revokedAt ?? null,\n JSON.stringify(grant)\n ]);\n if (result.rowCount !== 1)\n throw new Error(\"Delegation already exists\");\n }\n };\n};\n// src/telemetry.ts\nvar agencyEventToTelemetry = (event, timestamp = Date.now()) => {\n const actionId = \"action\" in event ? event.action.actionId : event.actionId;\n const attributes = {\n \"agent.action.id\": actionId,\n \"agent.event.type\": event.type\n };\n if (event.type === \"action.requested\") {\n attributes[\"agent.id\"] = event.action.actor.agentId;\n attributes[\"agent.action.name\"] = event.action.action;\n attributes[\"agent.action.effects\"] = event.action.effects.join(\",\");\n attributes[\"agent.user.id\"] = event.action.actor.userId;\n } else if (event.type === \"action.decided\") {\n attributes[\"agent.decision.kind\"] = event.decision.kind;\n attributes[\"agent.decision.requestable\"] = event.decision.kind === \"deny\" && event.decision.requestable;\n } else if (event.type === \"action.lease-issued\") {\n attributes[\"agent.lease.id\"] = event.lease.leaseId;\n attributes[\"agent.lease.expires_at\"] = event.lease.expiresAt;\n } else if (event.type === \"action.completed\") {\n attributes[\"agent.receipt.id\"] = event.receipt.receiptId;\n attributes[\"agent.execution.status\"] = event.receipt.status;\n } else {\n attributes[\"agent.approval.id\"] = event.approval.approvalId;\n attributes[\"agent.approval.by\"] = event.approval.approvedBy;\n }\n return { attributes, name: `agent.${event.type}`, timestamp };\n};\nvar createAgencyTelemetryEmitter = (emit, now = Date.now) => (event) => emit(agencyEventToTelemetry(event, now()));\nexport {\n verifyAgentHandoffWith,\n verifyAgentHandoff,\n simulateAction,\n signAgentHandoffWith,\n signAgentHandoff,\n digest,\n denyAllPolicy,\n createPostgresHandoffReplayStore,\n createPostgresAgentDelegationStore,\n createPostgresAgentControlStore,\n createPostgresAgencyStore,\n createMemoryHandoffReplayStore,\n createMemoryAgentDelegationStore,\n createMemoryAgentControlStore,\n createMemoryAgencyStore,\n createAgentDelegationAuthority,\n createAgentControlPlane,\n createAgencyTelemetryEmitter,\n createAgency,\n canonicalJson,\n attenuateAgentHandoff,\n allowAllPolicy,\n agencyPostgresSchemaSql,\n agencyEventToTelemetry,\n actionBinding\n};\n\n//# debugId=7A5482058A946C0964756E2164756E21\n//# sourceMappingURL=index.js.map\n",
|
|
6
|
+
"import {\n type ActionDecision,\n type Agency,\n type AgentActor,\n digest,\n} from \"@absolutejs/agency\";\nimport type { SecretBroker } from \"./broker\";\n\nexport type CredentialGrant = {\n agentId: string;\n allowedOrigins: ReadonlyArray<string>;\n createdAt: number;\n expiresAt: number;\n grantId: string;\n maximumUses: number;\n provider: string;\n scopes: ReadonlyArray<string>;\n secretName: string;\n used: number;\n userId: string;\n};\n\nexport type CredentialOperationInput = {\n actor: AgentActor;\n destination: string;\n grantId: string;\n idempotencyKey?: string;\n input?: unknown;\n operation: string;\n};\n\nexport type CredentialOperationContext = {\n credential: string;\n destination: URL;\n input: unknown;\n signal?: AbortSignal;\n};\n\nexport type CredentialProvider = {\n operations: Record<\n string,\n (context: CredentialOperationContext) => Promise<unknown> | unknown\n >;\n provider: string;\n};\n\nexport type PendingCredentialOperation = {\n actionId: string;\n input: CredentialOperationInput;\n};\n\nexport type CredentialGrantStore = {\n consume: (\n grantId: string,\n now: number,\n ) => Promise<CredentialGrant | undefined>;\n get: (grantId: string) => Promise<CredentialGrant | undefined>;\n getPending: (\n actionId: string,\n ) => Promise<PendingCredentialOperation | undefined>;\n put: (grant: CredentialGrant) => Promise<void>;\n putPending: (pending: PendingCredentialOperation) => Promise<void>;\n revoke: (grantId: string) => Promise<boolean>;\n};\n\nexport type CredentialOperationEvent =\n | {\n actionId?: string;\n destinationOrigin: string;\n grantId: string;\n operation: string;\n provider: string;\n type: \"credential.operation.requested\";\n }\n | {\n actionId?: string;\n destinationOrigin: string;\n grantId: string;\n operation: string;\n provider: string;\n resultDigest?: string;\n status: \"failed\" | \"succeeded\";\n type: \"credential.operation.completed\";\n };\n\nexport type CredentialOperationResult =\n | {\n actionId: string;\n decision: ActionDecision;\n kind: \"pending\";\n }\n | {\n actionId?: string;\n kind: \"completed\";\n result: unknown;\n resultDigest: string;\n };\n\nexport type CredentialOperationBrokerOptions = {\n agency?: Agency;\n emit?: (event: CredentialOperationEvent) => Promise<void> | void;\n now?: () => number;\n providers: ReadonlyArray<CredentialProvider>;\n secrets: SecretBroker;\n store: CredentialGrantStore;\n};\n\nconst normalizeOrigin = (destination: string) => new URL(destination).origin;\n\nconst validateGrant = (\n grant: CredentialGrant,\n input: CredentialOperationInput,\n now: number,\n) => {\n if (\n grant.agentId !== input.actor.agentId ||\n grant.userId !== input.actor.userId\n )\n throw new Error(\"Credential grant actor mismatch\");\n if (grant.expiresAt <= now) throw new Error(\"Credential grant has expired\");\n if (!grant.scopes.includes(input.operation))\n throw new Error(\"Credential operation is outside grant scope\");\n const origin = normalizeOrigin(input.destination);\n if (!grant.allowedOrigins.map(normalizeOrigin).includes(origin))\n throw new Error(\"Credential destination is outside grant scope\");\n if (grant.used >= grant.maximumUses)\n throw new Error(\"Credential grant has been exhausted\");\n\n return origin;\n};\n\nexport const createMemoryCredentialGrantStore = (): CredentialGrantStore => {\n const grants = new Map<string, CredentialGrant>();\n const pending = new Map<string, PendingCredentialOperation>();\n let lock = Promise.resolve();\n\n const exclusive = async <Result>(run: () => Result | Promise<Result>) => {\n const previous = lock;\n let release = () => {};\n lock = new Promise<void>((resolve) => {\n release = resolve;\n });\n await previous;\n try {\n return await run();\n } finally {\n release();\n }\n };\n\n return {\n consume: (grantId, now) =>\n exclusive(() => {\n const grant = grants.get(grantId);\n if (\n grant === undefined ||\n grant.expiresAt <= now ||\n grant.used >= grant.maximumUses\n )\n return undefined;\n const consumed = { ...grant, used: grant.used + 1 };\n grants.set(grantId, consumed);\n return { ...consumed };\n }),\n get: async (grantId) => {\n const grant = grants.get(grantId);\n return grant === undefined ? undefined : { ...grant };\n },\n getPending: async (actionId) => pending.get(actionId),\n put: async (grant) => {\n if (grant.maximumUses < 1)\n throw new Error(\"maximumUses must be positive\");\n if (grant.used < 0 || grant.used > grant.maximumUses)\n throw new Error(\"Invalid credential grant usage\");\n grants.set(grant.grantId, { ...grant });\n },\n putPending: async (operation) => {\n pending.set(operation.actionId, operation);\n },\n revoke: async (grantId) => grants.delete(grantId),\n };\n};\n\nexport const createCredentialOperationBroker = ({\n agency,\n emit,\n now = Date.now,\n providers,\n secrets,\n store,\n}: CredentialOperationBrokerOptions) => {\n const providerMap = new Map(\n providers.map((provider) => [provider.provider, provider]),\n );\n\n const perform = async (\n input: CredentialOperationInput,\n actionId?: string,\n signal?: AbortSignal,\n ): Promise<CredentialOperationResult> => {\n const preview = await store.get(input.grantId);\n if (preview === undefined) throw new Error(\"Unknown credential grant\");\n const origin = validateGrant(preview, input, now());\n const provider = providerMap.get(preview.provider);\n const operation = provider?.operations[input.operation];\n if (provider === undefined || operation === undefined)\n throw new Error(\"Credential provider operation is not registered\");\n await emit?.({\n actionId,\n destinationOrigin: origin,\n grantId: preview.grantId,\n operation: input.operation,\n provider: preview.provider,\n type: \"credential.operation.requested\",\n });\n const consumed = await store.consume(input.grantId, now());\n if (consumed === undefined)\n throw new Error(\"Credential grant has been exhausted\");\n validateGrant({ ...consumed, used: consumed.used - 1 }, input, now());\n const secret = await secrets.resolve(consumed.secretName);\n if (secret === null) throw new Error(\"Credential is not configured\");\n try {\n const result = await operation({\n credential: secret.value,\n destination: new URL(input.destination),\n input: input.input,\n signal,\n });\n const resultDigest = await digest(result);\n await emit?.({\n actionId,\n destinationOrigin: origin,\n grantId: consumed.grantId,\n operation: input.operation,\n provider: consumed.provider,\n resultDigest,\n status: \"succeeded\",\n type: \"credential.operation.completed\",\n });\n return { actionId, kind: \"completed\", result, resultDigest };\n } catch (error) {\n await emit?.({\n actionId,\n destinationOrigin: origin,\n grantId: consumed.grantId,\n operation: input.operation,\n provider: consumed.provider,\n status: \"failed\",\n type: \"credential.operation.completed\",\n });\n throw error;\n }\n };\n\n const request = async (\n input: CredentialOperationInput,\n signal?: AbortSignal,\n ): Promise<CredentialOperationResult> => {\n const grant = await store.get(input.grantId);\n if (grant === undefined) throw new Error(\"Unknown credential grant\");\n const origin = validateGrant(grant, input, now());\n if (agency === undefined) return perform(input, undefined, signal);\n const { action, decision } = await agency.request({\n action: `credential.${grant.provider}.${input.operation}`,\n actor: input.actor,\n context: { destinationOrigin: origin, grantId: grant.grantId },\n effects: [\"external-network\"],\n expiresAt: grant.expiresAt,\n idempotencyKey: input.idempotencyKey,\n input: { destination: input.destination, input: input.input },\n resource: { id: grant.grantId, type: \"credential-grant\" },\n });\n if (decision.kind !== \"allow\") {\n await store.putPending({ actionId: action.actionId, input });\n return { actionId: action.actionId, decision, kind: \"pending\" };\n }\n const lease = await agency.issueLease(action.actionId);\n const execution = await agency.execute({\n executor: `credential-provider:${grant.provider}`,\n leaseId: lease.leaseId,\n run: () => perform(input, action.actionId, signal),\n });\n return execution.result;\n };\n\n const resume = async (actionId: string, signal?: AbortSignal) => {\n if (agency === undefined) throw new Error(\"Agency is not configured\");\n const pending = await store.getPending(actionId);\n if (pending === undefined)\n throw new Error(\"Unknown pending credential operation\");\n const lease = await agency.issueLease(actionId);\n const execution = await agency.execute({\n executor: \"credential-provider\",\n leaseId: lease.leaseId,\n run: () => perform(pending.input, actionId, signal),\n });\n return execution.result;\n };\n\n return { request, resume, revoke: store.revoke };\n};\n\nexport type CredentialOperationBroker = ReturnType<\n typeof createCredentialOperationBroker\n>;\n",
|
|
7
|
+
"import type {\n CredentialGrant,\n CredentialGrantStore,\n PendingCredentialOperation,\n} from \"./operations\";\n\nexport type SecretsSqlResult<Row> = {\n rowCount: number;\n rows: ReadonlyArray<Row>;\n};\nexport type SecretsSqlClient = {\n query: <Row = Record<string, unknown>>(\n sql: string,\n parameters?: ReadonlyArray<unknown>,\n ) => Promise<SecretsSqlResult<Row>>;\n};\n\nconst namespaceOf = (namespace: string) => {\n if (!/^[a-z_][a-z0-9_]*$/.test(namespace))\n throw new Error(\"Secrets PostgreSQL namespace must be a simple identifier\");\n return namespace;\n};\n\nexport const credentialGrantsPostgresSchemaSql = (namespace = \"secrets\") => {\n const ns = namespaceOf(namespace);\n return `CREATE SCHEMA IF NOT EXISTS ${ns};\nCREATE TABLE IF NOT EXISTS ${ns}.credential_grants (\n grant_id text PRIMARY KEY, agent_id text NOT NULL, user_id text NOT NULL,\n provider text NOT NULL, expires_at bigint NOT NULL, maximum_uses integer NOT NULL CHECK (maximum_uses > 0),\n used integer NOT NULL DEFAULT 0 CHECK (used >= 0 AND used <= maximum_uses), data jsonb NOT NULL\n);\nCREATE INDEX IF NOT EXISTS credential_grants_agent_idx ON ${ns}.credential_grants (agent_id, expires_at);\nCREATE TABLE IF NOT EXISTS ${ns}.pending_operations (\n action_id text PRIMARY KEY, grant_id text NOT NULL REFERENCES ${ns}.credential_grants(grant_id) ON DELETE CASCADE,\n created_at timestamptz NOT NULL DEFAULT now(), data jsonb NOT NULL\n);`;\n};\n\ntype DataRow<Value> = { data: Value };\n\nexport const createPostgresCredentialGrantStore = ({\n client,\n namespace = \"secrets\",\n}: {\n client: SecretsSqlClient;\n namespace?: string;\n}): CredentialGrantStore => {\n const ns = namespaceOf(namespace);\n return {\n consume: async (grantId, now) => {\n const result = await client.query<DataRow<CredentialGrant>>(\n `UPDATE ${ns}.credential_grants SET used = used + 1, data = jsonb_set(data, '{used}', to_jsonb(used + 1), true) WHERE grant_id = $1 AND expires_at > $2 AND used < maximum_uses RETURNING data`,\n [grantId, now],\n );\n return result.rows[0]?.data;\n },\n get: async (grantId) =>\n (\n await client.query<DataRow<CredentialGrant>>(\n `SELECT data FROM ${ns}.credential_grants WHERE grant_id = $1`,\n [grantId],\n )\n ).rows[0]?.data,\n getPending: async (actionId) =>\n (\n await client.query<DataRow<PendingCredentialOperation>>(\n `SELECT data FROM ${ns}.pending_operations WHERE action_id = $1`,\n [actionId],\n )\n ).rows[0]?.data,\n put: async (grant) => {\n if (\n grant.maximumUses < 1 ||\n grant.used < 0 ||\n grant.used > grant.maximumUses\n )\n throw new Error(\"Invalid credential grant usage\");\n await client.query(\n `INSERT INTO ${ns}.credential_grants (grant_id, agent_id, user_id, provider, expires_at, maximum_uses, used, data) VALUES ($1,$2,$3,$4,$5,$6,$7,$8::jsonb) ON CONFLICT (grant_id) DO UPDATE SET expires_at=EXCLUDED.expires_at, maximum_uses=EXCLUDED.maximum_uses, used=EXCLUDED.used, data=EXCLUDED.data`,\n [\n grant.grantId,\n grant.agentId,\n grant.userId,\n grant.provider,\n grant.expiresAt,\n grant.maximumUses,\n grant.used,\n JSON.stringify(grant),\n ],\n );\n },\n putPending: async (pending) => {\n await client.query(\n `INSERT INTO ${ns}.pending_operations (action_id, grant_id, data) VALUES ($1,$2,$3::jsonb) ON CONFLICT (action_id) DO UPDATE SET data=EXCLUDED.data`,\n [pending.actionId, pending.input.grantId, JSON.stringify(pending)],\n );\n },\n revoke: async (grantId) =>\n (\n await client.query(\n `DELETE FROM ${ns}.credential_grants WHERE grant_id = $1`,\n [grantId],\n )\n ).rowCount === 1,\n };\n};\n"
|
|
8
|
+
],
|
|
9
|
+
"mappings": "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",
|
|
10
|
+
"debugId": "2D416FD568D31C1164756E2164756E21",
|
|
11
|
+
"names": []
|
|
12
|
+
}
|