@foldrun/core 9.0.0 → 9.2.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/dist/src/a2a-client.d.ts +165 -0
- package/dist/src/a2a-client.js +440 -0
- package/dist/src/a2a-publish.d.ts +37 -0
- package/dist/src/a2a-publish.js +116 -0
- package/dist/src/a2a-remotes.d.ts +73 -0
- package/dist/src/a2a-remotes.js +192 -0
- package/dist/src/a2a-tools.d.ts +148 -0
- package/dist/src/a2a-tools.js +833 -0
- package/dist/src/api-tools.js +15 -10
- package/dist/src/approvals.d.ts +18 -10
- package/dist/src/approvals.js +51 -1
- package/dist/src/browser-pod.js +4 -0
- package/dist/src/catalog.d.ts +14 -0
- package/dist/src/catalog.js +28 -0
- package/dist/src/completions.js +8 -8
- package/dist/src/cron.d.ts +9 -0
- package/dist/src/cron.js +20 -0
- package/dist/src/frontmatter-fields.js +3 -1
- package/dist/src/frontmatter-schema.d.ts +5 -1
- package/dist/src/frontmatter-schema.js +44 -1
- package/dist/src/limits.js +22 -4
- package/dist/src/notify.d.ts +6 -3
- package/dist/src/notify.js +25 -12
- package/dist/src/package.d.ts +5 -1
- package/dist/src/package.js +5 -3
- package/dist/src/run-container.d.ts +10 -1
- package/dist/src/run-container.js +17 -0
- package/dist/src/runner.d.ts +10 -0
- package/dist/src/runner.js +163 -16
- package/dist/src/scan.js +123 -5
- package/dist/src/secrets.d.ts +122 -10
- package/dist/src/secrets.js +291 -27
- package/dist/src/starter.js +5 -1
- package/dist/src/store.d.ts +34 -0
- package/dist/src/store.js +28 -7
- package/dist/src/subagents.d.ts +16 -0
- package/dist/src/subagents.js +23 -2
- package/dist/src/triggers.js +2 -2
- package/dist/src/webhook.d.ts +19 -3
- package/dist/src/webhook.js +14 -5
- package/package.json +4 -1
- package/src/a2a-client.ts +517 -0
- package/src/a2a-publish.ts +120 -0
- package/src/a2a-remotes.ts +225 -0
- package/src/a2a-tools.ts +919 -0
- package/src/api-tools.ts +20 -8
- package/src/approvals.ts +55 -1
- package/src/browser-pod.ts +3 -0
- package/src/catalog.ts +27 -0
- package/src/completions.ts +8 -8
- package/src/cron.ts +19 -0
- package/src/frontmatter-fields.ts +3 -1
- package/src/frontmatter-schema.ts +42 -2
- package/src/limits.ts +22 -4
- package/src/notify.ts +23 -12
- package/src/package.ts +5 -3
- package/src/run-container.ts +22 -0
- package/src/runner.ts +157 -13
- package/src/scan.ts +146 -6
- package/src/secrets.ts +319 -33
- package/src/starter.ts +5 -1
- package/src/store.ts +47 -2
- package/src/subagents.ts +29 -2
- package/src/triggers.ts +2 -2
- package/src/webhook.ts +22 -5
package/dist/src/secrets.d.ts
CHANGED
|
@@ -31,12 +31,27 @@ interface StoredSecret {
|
|
|
31
31
|
* these sites alone. Metadata, not a value, so it is stored in the clear
|
|
32
32
|
* beside the ciphertext and survives the value being set again. */
|
|
33
33
|
sites?: string[];
|
|
34
|
+
/** An ACCOUNT secret limited to these workspaces (GitHub's "selected
|
|
35
|
+
* repositories" for an organisation secret). Absent: every workspace may
|
|
36
|
+
* use it, as before. Every other workspace is answered as if it were not
|
|
37
|
+
* set (getSecret). Metadata like `sites`: in the clear, and in the seal
|
|
38
|
+
* of a bound record. Never on a workspace's own record. */
|
|
39
|
+
workspaces?: string[];
|
|
34
40
|
/** 2: sealed with the record's place as AAD — account, scope, name, kind,
|
|
35
|
-
* key and
|
|
41
|
+
* key, sites and, when limited, workspaces (vaultAad). Absent: the older,
|
|
42
|
+
* unbound form. */
|
|
36
43
|
v?: 2;
|
|
37
44
|
}
|
|
38
45
|
type SecretsFile = Record<string, StoredSecret>;
|
|
39
46
|
declare const writeBound: () => boolean;
|
|
47
|
+
/**
|
|
48
|
+
* An exclusive lock on `file` for a read-change-write, across processes and
|
|
49
|
+
* pods: `<file>.lock` opened O_EXCL (NFS 4.1 and EFS honour it), taken over
|
|
50
|
+
* when older than LOCK_STALE_MS (a holder that died), waited for up to
|
|
51
|
+
* LOCK_WAIT_MS, then `busy` is thrown. Returns the release. The vault's,
|
|
52
|
+
* and any other small state file several runs write (a2a-tools' handles).
|
|
53
|
+
*/
|
|
54
|
+
export declare function lockFileExclusive(file: string, busy?: string): () => void;
|
|
40
55
|
/**
|
|
41
56
|
* Change one vault file safely: `change` gets what is stored now and returns
|
|
42
57
|
* the new contents, or null to leave the file untouched. Returns what is
|
|
@@ -44,7 +59,30 @@ declare const writeBound: () => boolean;
|
|
|
44
59
|
*/
|
|
45
60
|
export declare function mutateVaultFile(file: string, change: (all: SecretsFile) => SecretsFile | null): SecretsFile;
|
|
46
61
|
export declare const SECRET_NAME: RegExp;
|
|
47
|
-
|
|
62
|
+
/**
|
|
63
|
+
* How a `signing_secret:` is shown in an answer, a log line or a delivery
|
|
64
|
+
* record: the name it holds, and only a name (30 Oct 2026). It is meant to
|
|
65
|
+
* name a vault entry, but nothing stopped the value itself being written
|
|
66
|
+
* there, and notify's test send echoed it back to the dashboard as "signed
|
|
67
|
+
* with <it>". Anything that is not a secret name is described, never
|
|
68
|
+
* repeated (foldrun check flags it as secret/signing-secret-value).
|
|
69
|
+
*/
|
|
70
|
+
export declare function signingSecretShown(name: string): string;
|
|
71
|
+
/**
|
|
72
|
+
* Which workspaces an ACCOUNT secret may be used in, given with the value so
|
|
73
|
+
* both are sealed in one locked write (30 Oct 2026): a new secret created
|
|
74
|
+
* and limited in two calls was open to every workspace between them, and
|
|
75
|
+
* stayed open if the second failed. `workspaces` undefined keeps what the
|
|
76
|
+
* record has; null opens it to every workspace; a list limits it.
|
|
77
|
+
* `exists` is asked about each name under the vault lock, so a workspace
|
|
78
|
+
* deleted meanwhile is not granted (the delete takes its name off every
|
|
79
|
+
* list under the same lock — retargetSecretWorkspaces).
|
|
80
|
+
*/
|
|
81
|
+
export interface SecretLimit {
|
|
82
|
+
workspaces?: string[] | null;
|
|
83
|
+
exists?: (workspace: string) => boolean;
|
|
84
|
+
}
|
|
85
|
+
export declare function setSecret(tenant: string, name: string, value: string, workspace?: string, kind?: SecretKind, limit?: SecretLimit): void;
|
|
48
86
|
/**
|
|
49
87
|
* Bind a secret to sites, or unbind it (an empty list). The secret must
|
|
50
88
|
* exist in that scope. Sites are `api.example.com` or `*.example.com` — no
|
|
@@ -71,11 +109,61 @@ export declare function decryptValue(tenant: string, rec: {
|
|
|
71
109
|
data: string;
|
|
72
110
|
k?: "tenant";
|
|
73
111
|
} | undefined): string | null;
|
|
74
|
-
/** Nearest wins: a workspace secret shadows an account one of the same name.
|
|
112
|
+
/** Nearest wins: a workspace secret shadows an account one of the same name.
|
|
113
|
+
* An account secret limited to some workspaces (setSecretWorkspaces) is
|
|
114
|
+
* absent for every other — the answer a secret that was never set gets, on
|
|
115
|
+
* every path that resolves one for a workspace: runs, tool tests, notify,
|
|
116
|
+
* the egress proxy, MCP servers. Without a workspace it is the account's
|
|
117
|
+
* own use (its model key, its mail sender), which no list limits. */
|
|
75
118
|
export declare function getSecret(tenant: string, name: string, workspace?: string): {
|
|
76
119
|
value: string;
|
|
77
120
|
scope: SecretScope;
|
|
78
121
|
} | null;
|
|
122
|
+
/** May the ACCOUNT's secret `name` be used for this workspace? True when it
|
|
123
|
+
* is not limited (or not set); for callers that read the account's copy on
|
|
124
|
+
* a workspace's behalf without getSecret's workspace-first lookup — the
|
|
125
|
+
* account's mail sender for a workspace's notifications (notify.ts). */
|
|
126
|
+
export declare function accountSecretUsableIn(tenant: string, name: string, workspace: string): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Limit an account secret to some workspaces, or lift the limit (null).
|
|
129
|
+
* An empty list means no workspace may use it. Returns the list as stored,
|
|
130
|
+
* sorted, or null.
|
|
131
|
+
*
|
|
132
|
+
* `exists` is asked about each name under the vault lock (the API passes
|
|
133
|
+
* "is there such a workspace"), so it cannot grant a name a delete has just
|
|
134
|
+
* taken off every list. `ifWorkspaces`, when given, is the list the caller
|
|
135
|
+
* saw (null: every workspace): a list changed since is refused (409), so
|
|
136
|
+
* two admins editing it at once do not silently overwrite each other.
|
|
137
|
+
*
|
|
138
|
+
* The record is sealed (v2) whatever it was, whatever
|
|
139
|
+
* FOLDRUN_VAULT_WRITE_BOUND says: a list in the clear on an unbound record
|
|
140
|
+
* could be stripped on disk to open the secret to every workspace; sealed,
|
|
141
|
+
* a stripped list makes it unreadable. Every reader since 29 Oct 2026 reads v2.
|
|
142
|
+
*/
|
|
143
|
+
export declare function setSecretWorkspaces(tenant: string, name: string, workspaces: string[] | null, opts?: {
|
|
144
|
+
exists?: (workspace: string) => boolean;
|
|
145
|
+
ifWorkspaces?: string[] | null;
|
|
146
|
+
}): string[] | null;
|
|
147
|
+
/**
|
|
148
|
+
* A workspace is being renamed (to: its new name) or deleted (to: null):
|
|
149
|
+
* every account secret limited to it follows. Without this a rename quietly
|
|
150
|
+
* cut the workspace off its secrets, and a delete left the name on the list
|
|
151
|
+
* for the next workspace given it.
|
|
152
|
+
*
|
|
153
|
+
* `failed` names the records that hold the name but will not open with this
|
|
154
|
+
* install's keys: they are left exactly as they are, so the old name is
|
|
155
|
+
* STILL on their list — a key restored later would bring the grant back.
|
|
156
|
+
* The caller must not report the rename or delete as clean while any are
|
|
157
|
+
* left (the API refuses it, naming them: set them again, or delete them).
|
|
158
|
+
*
|
|
159
|
+
* Names, not ids: a grant naming a workspace's immutable id would survive a
|
|
160
|
+
* rename untouched and could never pass to a new workspace given an old
|
|
161
|
+
* name — the stronger design, not built yet (30 Oct 2026).
|
|
162
|
+
*/
|
|
163
|
+
export declare function retargetSecretWorkspaces(tenant: string, from: string, to: string | null): {
|
|
164
|
+
changed: string[];
|
|
165
|
+
failed: string[];
|
|
166
|
+
};
|
|
79
167
|
export declare function deleteSecret(tenant: string, name: string, workspace?: string): void;
|
|
80
168
|
export interface SecretEntry {
|
|
81
169
|
name: string;
|
|
@@ -89,6 +177,8 @@ export interface SecretEntry {
|
|
|
89
177
|
/** The sites it is bound to — the proxy fills it for these and no other,
|
|
90
178
|
* and it never enters a sandbox. Absent when unbound. */
|
|
91
179
|
sites?: string[];
|
|
180
|
+
/** An account secret limited to these workspaces; absent: every one. */
|
|
181
|
+
workspaces?: string[];
|
|
92
182
|
}
|
|
93
183
|
export declare function listSecrets(tenant: string, workspace?: string): SecretEntry[];
|
|
94
184
|
export declare function resolveSecrets(tenant: string, names: string[], workspace?: string): {
|
|
@@ -154,7 +244,7 @@ export interface OAuth2Config {
|
|
|
154
244
|
export declare const OAUTH_REFRESH_TRIES = 3;
|
|
155
245
|
/** Wait before try n (1-based retry): 1s, 3s — FOLDRUN_OAUTH_RETRY_MS scales it for tests. */
|
|
156
246
|
export declare function oauthRetryDelayMs(n: number): number;
|
|
157
|
-
export declare function setOAuth2Secret(tenant: string, name: string, config: OAuth2Config, workspace?: string): void;
|
|
247
|
+
export declare function setOAuth2Secret(tenant: string, name: string, config: OAuth2Config, workspace?: string, limit?: SecretLimit): void;
|
|
158
248
|
export declare function isOAuth2Value(value: string): boolean;
|
|
159
249
|
/**
|
|
160
250
|
* The cache key for a live token: SHA-256 over the kind, the account and the
|
|
@@ -193,6 +283,28 @@ ctx?: {
|
|
|
193
283
|
* the stored value with the new refresh token. */
|
|
194
284
|
onRotate?: (name: string, usedValue: string, next: string) => unknown;
|
|
195
285
|
}): Promise<Record<string, string>>;
|
|
286
|
+
/**
|
|
287
|
+
* materializeSecrets for a tool that holds secrets for a whole step and
|
|
288
|
+
* makes them live per call (the API tools, the a2a tools), on a host with
|
|
289
|
+
* the vault.
|
|
290
|
+
*
|
|
291
|
+
* Two things the step's copy of a recipe cannot do alone. A provider that
|
|
292
|
+
* rotates refresh tokens has spent the refresh token the step started with —
|
|
293
|
+
* the runner materialised it at step start, and any refresh since spent its
|
|
294
|
+
* successor — so an oauth2 recipe is read again from the vault as it is now
|
|
295
|
+
* (and `env` updated in place, for the next call). And it is made live in
|
|
296
|
+
* the account's scope, as the runner and the egress proxy do: the cached
|
|
297
|
+
* access token is keyed by account, and a rotation is written back to the
|
|
298
|
+
* vault by the compare-and-set above. Refreshing the starting recipe outside
|
|
299
|
+
* that scope missed the cache and sent a spent refresh token: invalid_grant.
|
|
300
|
+
*
|
|
301
|
+
* No `scope` (inside a sandbox, where there is no vault and the values
|
|
302
|
+
* crossed already made live): materializeSecrets as it was.
|
|
303
|
+
*/
|
|
304
|
+
export declare function materializeCurrent(env: Record<string, string>, scope?: {
|
|
305
|
+
tenant: string;
|
|
306
|
+
workspace?: string;
|
|
307
|
+
} | null): Promise<Record<string, string>>;
|
|
196
308
|
export interface ServiceAccountConfig {
|
|
197
309
|
token_url: string;
|
|
198
310
|
issuer: string;
|
|
@@ -205,8 +317,8 @@ export interface ServiceAccountConfig {
|
|
|
205
317
|
* a Workspace admin grants it. Absent, the account acts as itself. */
|
|
206
318
|
subject?: string;
|
|
207
319
|
}
|
|
208
|
-
export declare function setServiceAccountSecret(tenant: string, name: string, config: ServiceAccountConfig, workspace?: string): void;
|
|
209
|
-
export declare function setFileSecret(tenant: string, name: string, content: string, workspace?: string): void;
|
|
320
|
+
export declare function setServiceAccountSecret(tenant: string, name: string, config: ServiceAccountConfig, workspace?: string, limit?: SecretLimit): void;
|
|
321
|
+
export declare function setFileSecret(tenant: string, name: string, content: string, workspace?: string, limit?: SecretLimit): void;
|
|
210
322
|
export declare function isFileValue(value: string): boolean;
|
|
211
323
|
export declare function fileContent(value: string): string;
|
|
212
324
|
export interface SshConfig {
|
|
@@ -217,7 +329,7 @@ export interface SshConfig {
|
|
|
217
329
|
private_key?: string;
|
|
218
330
|
password?: string;
|
|
219
331
|
}
|
|
220
|
-
export declare function setSshSecret(tenant: string, name: string, config: SshConfig, workspace?: string): void;
|
|
332
|
+
export declare function setSshSecret(tenant: string, name: string, config: SshConfig, workspace?: string, limit?: SecretLimit): void;
|
|
221
333
|
export declare function isSshValue(value: string): boolean;
|
|
222
334
|
export declare function sshConfigOf(value: string): SshConfig;
|
|
223
335
|
/** A client certificate for mutual TLS: presented by the egress proxy, on its
|
|
@@ -232,14 +344,14 @@ export interface ClientCertConfig {
|
|
|
232
344
|
passphrase?: string;
|
|
233
345
|
export?: boolean;
|
|
234
346
|
}
|
|
235
|
-
export declare function setClientCertSecret(tenant: string, name: string, config: ClientCertConfig, workspace?: string): void;
|
|
347
|
+
export declare function setClientCertSecret(tenant: string, name: string, config: ClientCertConfig, workspace?: string, limit?: SecretLimit): void;
|
|
236
348
|
/** A keyless cloud identity: no credential at all, only the audience the
|
|
237
349
|
* platform's signed tokens are for. A step that declares it gets a token
|
|
238
350
|
* file the sandbox driver keeps fresh through the egress proxy. */
|
|
239
351
|
export interface IdentityConfig {
|
|
240
352
|
audience: string;
|
|
241
353
|
}
|
|
242
|
-
export declare function setIdentitySecret(tenant: string, name: string, config: IdentityConfig, workspace?: string): void;
|
|
354
|
+
export declare function setIdentitySecret(tenant: string, name: string, config: IdentityConfig, workspace?: string, limit?: SecretLimit): void;
|
|
243
355
|
export declare function isIdentityValue(value: string): boolean;
|
|
244
356
|
export declare function identityOf(value: string): IdentityConfig;
|
|
245
357
|
export declare function isClientCertValue(value: string): boolean;
|
|
@@ -260,7 +372,7 @@ export interface ApiConfig {
|
|
|
260
372
|
/** `Authorization: Basic …` from a username and password (RFC 7617 §2):
|
|
261
373
|
* UTF-8, no colon in the username, no control characters in either. */
|
|
262
374
|
export declare function basicAuthorization(username: string, password: string): string;
|
|
263
|
-
export declare function setApiSecret(tenant: string, name: string, config: ApiConfig, workspace?: string): void;
|
|
375
|
+
export declare function setApiSecret(tenant: string, name: string, config: ApiConfig, workspace?: string, limit?: SecretLimit): void;
|
|
264
376
|
export declare function isApiValue(value: string): boolean;
|
|
265
377
|
export declare function apiConfigOf(value: string): ApiConfig;
|
|
266
378
|
/**
|