@pikku/core 0.12.136 → 0.12.138
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/CHANGELOG.md +41 -0
- package/dist/dev/hot-reload.js +5 -2
- package/dist/env.d.ts +18 -0
- package/dist/env.js +60 -1
- package/dist/function/abort-scope.d.ts +6 -5
- package/dist/function/abort-scope.js +6 -17
- package/dist/function/function-runner.js +2 -1
- package/dist/middleware/auth-apikey.js +1 -1
- package/dist/middleware/auth-bearer.js +1 -1
- package/dist/middleware/auth-cookie.js +1 -1
- package/dist/schema.js +3 -1
- package/dist/services/in-memory-queue-service.js +4 -2
- package/dist/services/in-memory-workflow-service.js +7 -6
- package/dist/services/index.d.ts +0 -1
- package/dist/services/index.js +0 -1
- package/dist/services/local-variables.d.ts +5 -0
- package/dist/services/local-variables.js +7 -2
- package/dist/services/meta-service.d.ts +11 -0
- package/dist/services/meta-service.js +10 -1
- package/dist/services/queue-webhook-service.js +1 -1
- package/dist/services/webhook-service.d.ts +2 -2
- package/dist/services/webhook-service.js +6 -4
- package/dist/types/core.types.d.ts +10 -1
- package/dist/utils/hmac.d.ts +25 -10
- package/dist/utils/hmac.js +202 -24
- package/dist/wirings/agent/agent-agui.js +65 -62
- package/dist/wirings/agent/agent-interrupt.d.ts +4 -1
- package/dist/wirings/agent/agent-interrupt.js +3 -3
- package/dist/wirings/agent/agent-memory.js +5 -6
- package/dist/wirings/agent/agent-model-config.js +2 -1
- package/dist/wirings/agent/agent-prepare.js +21 -13
- package/dist/wirings/agent/agent-stream.js +38 -34
- package/dist/wirings/agent/agent.types.d.ts +8 -1
- package/dist/wirings/channel/channel-handler.js +1 -1
- package/dist/wirings/channel/local/local-channel-handler.js +5 -1
- package/dist/wirings/channel/local/local-channel-runner.js +6 -4
- package/dist/wirings/channel/local/local-eventhub-service.d.ts +1 -0
- package/dist/wirings/channel/local/local-eventhub-service.js +4 -1
- package/dist/wirings/cli/channel/cli-channel-runner.js +7 -2
- package/dist/wirings/cli/channel/cli-raw-client-runner.js +8 -4
- package/dist/wirings/cli/cli-runner.js +1 -1
- package/dist/wirings/gateway/gateway-runner.js +3 -3
- package/dist/wirings/http/http-runner.js +6 -6
- package/dist/wirings/persona/persona-app-scopes.js +1 -0
- package/dist/wirings/scheduler/scheduler-runner.js +1 -1
- package/dist/wirings/scope/define-scope.d.ts +19 -0
- package/dist/wirings/scope/define-scope.js +19 -0
- package/dist/wirings/scope/index.d.ts +2 -2
- package/dist/wirings/scope/index.js +1 -1
- package/dist/wirings/scope/scope.types.d.ts +14 -0
- package/dist/wirings/scope/validate-scope-definitions.js +1 -0
- package/dist/wirings/trigger/trigger-runner.js +3 -1
- package/dist/wirings/trigger/webhook-source-runner.js +5 -2
- package/dist/wirings/virtual-user/virtual-user-scaffold.js +3 -2
- package/dist/wirings/workflow/scenario-coverage.d.ts +70 -0
- package/dist/wirings/workflow/scenario-coverage.js +159 -0
- package/dist/wirings/workflow/workflow-invocation-id.js +85 -8
- package/package.json +4 -1
- package/src/public-surface.json +10 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,44 @@
|
|
|
1
|
+
## 0.12.138
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 6f11bb5: A deployed Cloudflare Worker is always production. The adapter now pins `process.env.NODE_ENV` to `"production"` in the bundle, and core's `isProduction()` reads the literal `process.env.NODE_ENV` so that pin takes effect. Before, a Worker had no `NODE_ENV`, so `isProduction()` was false and every 5xx response returned the error message and stack trace to the client. `process.env.NODE_ENV` now wins over a variables service when it is set.
|
|
6
|
+
- 0c67e33: `@pikku/core` no longer imports `node:async_hooks`, so it is edge-clean end to end. The function abort scope that `beginChanges()` observes is now passed explicitly (agent tool `execute(input, { abortScope })` onto `wire.abortScope`, carried through nested `rpc` calls) instead of living in `AsyncLocalStorage`, which also means concurrent runs in one process or isolate cannot see each other's scope. `AgentToolDef.execute` gains an optional second argument; nothing else in the public API changes.
|
|
7
|
+
- 15c4968: The default runtime path no longer needs `node:*`, `Buffer` or an unguarded `process`, so it loads on edge runtimes (Web APIs only).
|
|
8
|
+
|
|
9
|
+
**Breaking:** `FileScenarioRunStore`, `scenarioArtifactContentType` and `scenarioRunSummary` leave the `@pikku/core/services` barrel because they use `node:fs`. Import them from `@pikku/core/services/file-scenario-run-store`. Pre-0.13 breaking changes still ship as a patch.
|
|
10
|
+
|
|
11
|
+
**Breaking:** the HMAC helpers in `@pikku/core/hmac` run on Web Crypto, which is async. `hmacSha256Hex`, `hmacDigest`, `verifyHmacSignature` and `verifyPublicKeySignature` now return a Promise; `timingSafeStringEqual` stays synchronous. `WebhookService.sign` and `WebhookService.verify` return a Promise, and so does `verifySlackSignature` in `@pikku/gateway-slack`: `await` them. A function-style `verify` on a webhook source may return a Promise as well. `verifyPublicKeySignature` accepts RSA and EC (P-256, P-384, P-521) PEM keys; Ed25519 and RSA-PSS keys verify as false.
|
|
12
|
+
|
|
13
|
+
`isProduction()` (and the `PIKKU_MODEL_ALIASES` and `PIKKU_ENV` reads) now ask the registered singleton services' `variables` service first, then `process.env` when `process` exists. With neither, `isProduction()` is true, so an edge deployment never exposes error details by accident. `LocalVariablesService` resolves its `process.env` default when constructed and falls back to an empty store without `process`.
|
|
14
|
+
|
|
15
|
+
Workflow invocation ids use a built-in SHA-1 and stay byte-identical. `@pikku/kysely` and the Cloudflare hibernation server use `globalThis.crypto.randomUUID()` and no `Buffer`; `@pikku/kysely` is marked `sideEffects: false`. `@pikku/ai-deepinfra` reads `DEEPINFRA_API_KEY` only when `process` exists and decodes base64 audio without `Buffer`.
|
|
16
|
+
|
|
17
|
+
Runtime tiers. A package declares where it can run in `package.json`: `"pikku": { "runtime": "edge" | "serverless" | "server" }`, with optional per-subpath overrides (`"exports": { "./dev": "server" }`). `server` is the default. `@pikku/core` and the default services are `edge` (core's Node-only subpaths are `server`), `@pikku/better-auth`, `@pikku/addon-admin` and `@pikku/cloudflare` are `serverless` (better-auth statically imports `node:crypto` scrypt), and `@pikku/addon-console` is `server` and must never land in a deployed unit.
|
|
18
|
+
|
|
19
|
+
`pikku deploy` now verifies each deployed unit before bundling it. The unit is bundled with esbuild (neutral platform, `workerd`/`worker`/`browser` conditions) under its tier's profile, ahead of the adapter's aliases and stubs so they cannot hide anything, and the build fails when the bundle holds a package declared above the unit's tier or a Node built-in the runtime does not provide. The error names the unit, package, import chain and the fix. A built-in the provider stubs is a warning. On an edge unit, a bundled `InMemory*` class also fails the build. The unit's tier is its `runtime`, else the project's own `pikku.runtime`, else the provider default (`serverless` on Cloudflare). Packages that declare nothing are judged by their imports only.
|
|
20
|
+
|
|
21
|
+
`ProviderAdapter.getRuntimeProfile(tier?)` returns the compat date, compat flags, the built-ins the runtime provides (keyed by compat date), the stubbed ones and the externals. The Cloudflare adapter derives `wrangler.toml`, the upload metadata and the bundle externals from it; `node:os` and `node:fs` are not provided before compat date 2025-09-15. The default is `nodejs_compat_v2` with compat date 2024-12-18, which is what `wrangler.toml` and Fabric's live workers already use, so Fabric deploys see no runtime change. Only the direct-API upload in `@pikku/deploy-cloudflare` (`createWorker`, used by its own `deploy()`) moves: it sent `nodejs_compat` with 2024-01-01 before. A newer date or flag is opt-in through the adapter option `new CloudflareProviderAdapter({ compatDate })` or `getCloudflareRuntimeProfile(tier, compatDate)`. From 2025-08-15 `node:http` and `node:https` exist, and from 2025-09-15 `node:os`, `node:fs` (stubbed by the build regardless) and `node:perf_hooks`; the verifier's allowed built-ins follow the effective date. A newer date changes the runtime of every worker deployed with it.
|
|
22
|
+
|
|
23
|
+
`declareScopes(['pikku:console'])` (from `@pikku/core/scope`) declares scopes by id without wiring anything. `wireAddon({ scopes })` only requires a scope of an addon's functions; the tree arrives with the addon's metadata, so a role granting `pikku:console` used to fail PKU124 unless the addon was wired, and a wired `@pikku/addon-console` lands in every unit carrying the `/rpc` catch-all. An app that grants the scope without the console now calls `declareScopes` and no longer needs the scaffold or the addon. When the addon is wired its tree and descriptions win and the bare id adds nothing.
|
|
24
|
+
|
|
25
|
+
More packages declare a tier after bundling each with the edge profile. `edge`: `@pikku/addon-graph`, `@pikku/pino`, `@pikku/jose`, `@pikku/ai-vercel`, `@pikku/ai-voice`. `serverless`: `@pikku/kysely-postgres` (postgres.js needs `node:buffer`, `stream`, `events`), `@pikku/backblaze` (`copyFile` reads a local file, which the build stubs), `@pikku/gateway-slack` (`@slack/web-api` imports `node:os` and `zlib`). `server`: `@pikku/schedule` (`cron` imports `child_process`), `@pikku/aws-services` (the AWS SDK's Node handler needs `node:http2`), `@pikku/browser` (puppeteer), and `@pikku/modelcontextprotocol` except its `./fetch` subpath, which is `edge`.
|
|
26
|
+
|
|
27
|
+
`@pikku/addon-graph` no longer needs `node:crypto` or `Buffer`: the Crypto function hashes and HMACs on Web Crypto (MD5 is built in, since Web Crypto has none) and encodes with `TextEncoder`/`atob`/`btoa`, with output unchanged.
|
|
28
|
+
|
|
29
|
+
- d75a23d: Await previously un-awaited promises (session setters, channel sends, schema compile, trigger and CLI renderer calls) and handle rejections in fire-and-forget timers and signal handlers, found by type-aware `no-floating-promises` / `no-misused-promises`.
|
|
30
|
+
- 9b12c8a: Harden webhook signature verification. An empty or missing secret now fails closed at every call site (`verifyHmacSignature`, webhook source verification, `WebhookService.verify`, `verifySlackSignature`), including a signature computed over an empty key. HMAC verification now decodes the signature to bytes and compares with `crypto.subtle.verify('HMAC', ...)` instead of a pure-JS string compare, and a malformed hex signature is rejected. `verifyPublicKeySignature` accepts Ed25519 SPKI keys again.
|
|
31
|
+
|
|
32
|
+
## 0.12.137
|
|
33
|
+
|
|
34
|
+
### Patch Changes
|
|
35
|
+
|
|
36
|
+
- 0c865d9: Changes and changesets replace milestones. The changes commands run on a local queue when the checkout has no fabric project; `pikku changes next` routes to a changes, upgrade or knowledge agent and merges finished changesets. A changeset that creates or alters a table, or has 6 or more changes, needs a plan at `knowledge/plans/<changeset>.plan.json` (anything else goes to an optional judge at `PIKKU_PLAN_JUDGE_URL`), and `changes done` holds it to that plan. `pikku knowledge gaps` replaces `knowledge next`/`reconcile`, and also reports code a merged plan built that is gone and notes that were deleted.
|
|
37
|
+
- c36b066: Scenario coverage moves into OSS. `@pikku/core/scenario/coverage` adds `readScenarioCoverage`, which reports the lines no scenario reaches, the mutations no scenario drives, and the pages scenarios open. It is exposed through `pikku scenario coverage`, `console:getScenarioCoverage`, and a "Not tested by any scenario" card on the Scenarios page. `LocalMetaService.getRpcMeta` now reads the `.internal.gen.json` file that codegen actually writes.
|
|
38
|
+
- 0963b73: Every scope tree now records where it came from: `origin` on `ScopeDefinitionMeta` is `{ kind: 'app' }` for the app's own `defineScope`, `{ kind: 'generated' }` for a tree the CLI wrote into the project (a `.gen.ts`, or the `app` root derived from personas), and `{ kind: 'addon', package, displayName }` for one an installed addon declared. An addon's build stamps its own package and display name on its trees, and the host keeps that name while recording the package it actually installed. `MetaService.getScopesMeta()` reads the result, and the console's all-meta payload carries it as `scopes`.
|
|
39
|
+
|
|
40
|
+
The console's Scopes page becomes **Permissions** and is grouped by that origin: the app's own permissions first, then what Pikku generated into the project, then one card per addon (Pikku's own before third-party, each alphabetical), each card folding to its header — the app's own starts open, every addon's starts folded, and any search or filter opens them all. Above the cards, two filters narrow the page to what one role is given (or to what no role is given yet) and to one source. A permission group no longer says "Not part of any role yet" above lines that are each given to a role.
|
|
41
|
+
|
|
1
42
|
## 0.12.136
|
|
2
43
|
|
|
3
44
|
### Patch Changes
|
package/dist/dev/hot-reload.js
CHANGED
|
@@ -105,14 +105,17 @@ export async function pikkuDevReloader(options) {
|
|
|
105
105
|
pendingChanges.add(filePath);
|
|
106
106
|
if (debounceTimer)
|
|
107
107
|
clearTimeout(debounceTimer);
|
|
108
|
-
|
|
108
|
+
const flush = async () => {
|
|
109
109
|
const files = [...pendingChanges];
|
|
110
110
|
pendingChanges.clear();
|
|
111
111
|
for (const file of files) {
|
|
112
112
|
postCodegenQueue.add(file);
|
|
113
113
|
await safeHandleFileChange(file);
|
|
114
114
|
}
|
|
115
|
-
}
|
|
115
|
+
};
|
|
116
|
+
// Fire-and-forget timer callback: safeHandleFileChange catches and logs
|
|
117
|
+
// every error, so nothing can reject here.
|
|
118
|
+
debounceTimer = setTimeout(() => void flush(), 50);
|
|
116
119
|
};
|
|
117
120
|
for (const srcDir of absSrcDirs) {
|
|
118
121
|
try {
|
package/dist/env.d.ts
CHANGED
|
@@ -1 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reads one variable without `process`, which edge runtimes do not have.
|
|
3
|
+
*
|
|
4
|
+
* Looks in the registered singleton services' `variables` service first, then
|
|
5
|
+
* in `process.env` when there is one. Never throws: services that were never
|
|
6
|
+
* registered (a unit test, an early boot path) simply fall through, which is
|
|
7
|
+
* why this reads the state directly instead of calling
|
|
8
|
+
* `getSingletonServices()`.
|
|
9
|
+
*
|
|
10
|
+
* The read is synchronous, so a `VariablesService` that answers with a Promise
|
|
11
|
+
* is ignored and the lookup falls through to `process.env`.
|
|
12
|
+
*/
|
|
13
|
+
export declare const readEnvVariable: (name: string) => string | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* `NODE_ENV` decides whether error details are exposed. With neither a
|
|
16
|
+
* variables service nor `process` to say, this is production: failing closed
|
|
17
|
+
* means an edge deployment never leaks error details by accident.
|
|
18
|
+
*/
|
|
1
19
|
export declare const isProduction: () => boolean;
|
package/dist/env.js
CHANGED
|
@@ -1 +1,60 @@
|
|
|
1
|
-
|
|
1
|
+
import { pikkuState } from './pikku-state.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reads one variable without `process`, which edge runtimes do not have.
|
|
4
|
+
*
|
|
5
|
+
* Looks in the registered singleton services' `variables` service first, then
|
|
6
|
+
* in `process.env` when there is one. Never throws: services that were never
|
|
7
|
+
* registered (a unit test, an early boot path) simply fall through, which is
|
|
8
|
+
* why this reads the state directly instead of calling
|
|
9
|
+
* `getSingletonServices()`.
|
|
10
|
+
*
|
|
11
|
+
* The read is synchronous, so a `VariablesService` that answers with a Promise
|
|
12
|
+
* is ignored and the lookup falls through to `process.env`.
|
|
13
|
+
*/
|
|
14
|
+
export const readEnvVariable = (name) => {
|
|
15
|
+
try {
|
|
16
|
+
const variables = pikkuState(null, 'package', 'singletonServices')?.variables;
|
|
17
|
+
if (variables) {
|
|
18
|
+
const value = variables.get(name);
|
|
19
|
+
if (value instanceof Promise) {
|
|
20
|
+
value.catch(() => { });
|
|
21
|
+
}
|
|
22
|
+
else if (typeof value === 'string' ||
|
|
23
|
+
typeof value === 'number' ||
|
|
24
|
+
typeof value === 'boolean') {
|
|
25
|
+
return String(value);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// A faulty variables service must not take error handling down with it.
|
|
31
|
+
}
|
|
32
|
+
return hasProcessEnv() ? globalThis.process.env[name] : undefined;
|
|
33
|
+
};
|
|
34
|
+
const hasProcessEnv = () => typeof globalThis.process?.env === 'object';
|
|
35
|
+
/**
|
|
36
|
+
* The literal `process.env.NODE_ENV`, kept as a literal on purpose: a deploy
|
|
37
|
+
* adapter pins it with a bundler `define` (a Cloudflare Worker is always
|
|
38
|
+
* production), and `define` only rewrites this exact expression, not
|
|
39
|
+
* `process.env[name]`. Without that define it reads the real value, and on a
|
|
40
|
+
* runtime with no `process` the ReferenceError means "unknown".
|
|
41
|
+
*/
|
|
42
|
+
const buildTimeNodeEnv = () => {
|
|
43
|
+
try {
|
|
44
|
+
return process.env.NODE_ENV;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* `NODE_ENV` decides whether error details are exposed. With neither a
|
|
52
|
+
* variables service nor `process` to say, this is production: failing closed
|
|
53
|
+
* means an edge deployment never leaks error details by accident.
|
|
54
|
+
*/
|
|
55
|
+
export const isProduction = () => {
|
|
56
|
+
const nodeEnv = buildTimeNodeEnv() ?? readEnvVariable('NODE_ENV');
|
|
57
|
+
if (nodeEnv !== undefined)
|
|
58
|
+
return nodeEnv === 'production';
|
|
59
|
+
return !hasProcessEnv();
|
|
60
|
+
};
|
|
@@ -14,10 +14,6 @@ export interface AbortScope {
|
|
|
14
14
|
/** Called by `beginChanges()` once a function commits to mutating. */
|
|
15
15
|
onBeginChanges?: () => void;
|
|
16
16
|
}
|
|
17
|
-
/** Run `fn` with an abort scope that `beginChanges()` will observe. */
|
|
18
|
-
export declare const runInAbortScope: <T>(scope: AbortScope, fn: () => T) => T;
|
|
19
|
-
/** The current scope, if the caller is running inside one. */
|
|
20
|
-
export declare const getAbortScope: () => AbortScope | undefined;
|
|
21
17
|
/**
|
|
22
18
|
* Declare that everything after this line changes something.
|
|
23
19
|
*
|
|
@@ -38,10 +34,15 @@ export declare const getAbortScope: () => AbortScope | undefined;
|
|
|
38
34
|
* ignoring the answer, and the failure mode of ignoring it is a mutation nobody
|
|
39
35
|
* asked for.
|
|
40
36
|
*
|
|
37
|
+
* The scope is passed explicitly — the function runner binds it from
|
|
38
|
+
* `wire.abortScope` — rather than looked up ambiently, so concurrent runs in
|
|
39
|
+
* one process (or one edge isolate) can never see each other's scope and core
|
|
40
|
+
* needs no `AsyncLocalStorage`.
|
|
41
|
+
*
|
|
41
42
|
* Entirely cooperative, and safe to call anywhere — outside a scope it is a
|
|
42
43
|
* no-op, so a function does not need to know how it was invoked. A function
|
|
43
44
|
* that never calls it keeps the conservative default: if it is not marked
|
|
44
45
|
* `readonly`, an interrupt assumes it changed something and says so. The tool
|
|
45
46
|
* that forgot to call this is exactly the one that cannot be assumed harmless.
|
|
46
47
|
*/
|
|
47
|
-
export declare const beginChanges: () => Promise<void>;
|
|
48
|
+
export declare const beginChanges: (scope?: AbortScope) => Promise<void>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
1
|
/**
|
|
3
2
|
* Raised by {@link beginChanges} when the caller has gone away before anything
|
|
4
3
|
* was changed. Distinct from a failure: nothing happened, so there is nothing
|
|
@@ -12,20 +11,6 @@ export class AbandonedError extends Error {
|
|
|
12
11
|
this.name = 'AbandonedError';
|
|
13
12
|
}
|
|
14
13
|
}
|
|
15
|
-
/**
|
|
16
|
-
* The ambient "is my caller still there?" signal.
|
|
17
|
-
*
|
|
18
|
-
* Ambient rather than a parameter because the question is the same one for
|
|
19
|
-
* every wiring — an agent run that was interrupted, an HTTP request whose
|
|
20
|
-
* client disconnected, a cancelled workflow — and threading it through every
|
|
21
|
-
* signature would mean changing every function that merely sits between the
|
|
22
|
-
* wiring and the mutation.
|
|
23
|
-
*/
|
|
24
|
-
const scopeStorage = new AsyncLocalStorage();
|
|
25
|
-
/** Run `fn` with an abort scope that `beginChanges()` will observe. */
|
|
26
|
-
export const runInAbortScope = (scope, fn) => scopeStorage.run(scope, fn);
|
|
27
|
-
/** The current scope, if the caller is running inside one. */
|
|
28
|
-
export const getAbortScope = () => scopeStorage.getStore();
|
|
29
14
|
/**
|
|
30
15
|
* Declare that everything after this line changes something.
|
|
31
16
|
*
|
|
@@ -46,14 +31,18 @@ export const getAbortScope = () => scopeStorage.getStore();
|
|
|
46
31
|
* ignoring the answer, and the failure mode of ignoring it is a mutation nobody
|
|
47
32
|
* asked for.
|
|
48
33
|
*
|
|
34
|
+
* The scope is passed explicitly — the function runner binds it from
|
|
35
|
+
* `wire.abortScope` — rather than looked up ambiently, so concurrent runs in
|
|
36
|
+
* one process (or one edge isolate) can never see each other's scope and core
|
|
37
|
+
* needs no `AsyncLocalStorage`.
|
|
38
|
+
*
|
|
49
39
|
* Entirely cooperative, and safe to call anywhere — outside a scope it is a
|
|
50
40
|
* no-op, so a function does not need to know how it was invoked. A function
|
|
51
41
|
* that never calls it keeps the conservative default: if it is not marked
|
|
52
42
|
* `readonly`, an interrupt assumes it changed something and says so. The tool
|
|
53
43
|
* that forgot to call this is exactly the one that cannot be assumed harmless.
|
|
54
44
|
*/
|
|
55
|
-
export const beginChanges = async () => {
|
|
56
|
-
const scope = scopeStorage.getStore();
|
|
45
|
+
export const beginChanges = async (scope) => {
|
|
57
46
|
if (!scope)
|
|
58
47
|
return;
|
|
59
48
|
if (scope.abandoned) {
|
|
@@ -235,7 +235,8 @@ export const runPikkuFunc = async (wireType, wireId, funcName, { singletonServic
|
|
|
235
235
|
// and offering it there would invite the contradiction of a `readonly`
|
|
236
236
|
// function announcing where its changes begin.
|
|
237
237
|
if (!funcMeta.readonly) {
|
|
238
|
-
|
|
238
|
+
const abortScope = resolvedWire.abortScope;
|
|
239
|
+
invocationWire.beginChanges = () => beginChanges(abortScope);
|
|
239
240
|
}
|
|
240
241
|
// knowledge: decisions/security/addon-scopes-are-resolved-where-the-function-runs.md
|
|
241
242
|
const addonScopes = resolveAddonScopes(packageName, addonInstance?.namespace);
|
|
@@ -19,7 +19,7 @@ export const authAPIKey = pikkuMiddlewareFactory(({ source }) => pikkuMiddleware
|
|
|
19
19
|
if (apiKey && jwtService) {
|
|
20
20
|
const userSession = await jwtService.decode(apiKey);
|
|
21
21
|
if (userSession) {
|
|
22
|
-
setSession?.(userSession);
|
|
22
|
+
await setSession?.(userSession);
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
return next();
|
|
@@ -47,7 +47,7 @@ export const authCookie = pikkuMiddlewareFactory(({ name, options, expiresIn })
|
|
|
47
47
|
// standard JWT timestamp, so an identity-less token is treated as no
|
|
48
48
|
// session rather than as an authenticated one.
|
|
49
49
|
if (userSession && hasSessionIdentity(userSession)) {
|
|
50
|
-
setSession?.(userSession);
|
|
50
|
+
await setSession?.(userSession);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
await next();
|
package/dist/schema.js
CHANGED
|
@@ -18,7 +18,9 @@ export const compileAllSchemas = (logger, schemaService) => {
|
|
|
18
18
|
for (const [pkgName, packageState] of getAllPackageStates()) {
|
|
19
19
|
const resolvedPkgName = pkgName === '__main__' ? null : pkgName;
|
|
20
20
|
for (const [name, schema] of packageState.misc.schemas) {
|
|
21
|
-
|
|
21
|
+
// compileSchema may be async; this function is synchronous (public
|
|
22
|
+
// signature), so a rejection is logged instead of left unhandled.
|
|
23
|
+
Promise.resolve(schemaService.compileSchema(schemaKey(name, resolvedPkgName), schema)).catch((e) => logger.error(`Failed to compile schema '${name}': ${e?.message ?? e}`));
|
|
22
24
|
}
|
|
23
25
|
}
|
|
24
26
|
validateAllSchemasLoaded(logger, schemaService);
|
|
@@ -24,7 +24,8 @@ export class InMemoryQueueService {
|
|
|
24
24
|
}
|
|
25
25
|
catch (e) {
|
|
26
26
|
if (attemptsMade < maxAttempts) {
|
|
27
|
-
|
|
27
|
+
// Fire-and-forget retry: runAttempt catches every error itself.
|
|
28
|
+
setTimeout(() => void runAttempt(), this.backoffDelay(options?.backoff, attemptsMade));
|
|
28
29
|
}
|
|
29
30
|
else {
|
|
30
31
|
console.error(`[InMemoryQueue] Job ${jobId} on ${queueName} failed after ${attemptsMade} attempt(s):`, e.message);
|
|
@@ -32,7 +33,8 @@ export class InMemoryQueueService {
|
|
|
32
33
|
}
|
|
33
34
|
};
|
|
34
35
|
const delay = options?.delay ?? 100 + Math.floor(Math.random() * 201);
|
|
35
|
-
|
|
36
|
+
// Fire-and-forget: runAttempt catches every error itself.
|
|
37
|
+
setTimeout(() => void runAttempt(), delay);
|
|
36
38
|
return jobId;
|
|
37
39
|
}
|
|
38
40
|
backoffDelay(backoff, attemptsMade) {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { randomUUID } from 'crypto';
|
|
2
1
|
import { PikkuWorkflowService } from '../wirings/workflow/pikku-workflow-service.js';
|
|
3
2
|
import { isExpectedError } from '../errors/error-handler.js';
|
|
4
3
|
import { isStepLeaseLive } from '../wirings/workflow/workflow-constants.js';
|
|
@@ -17,7 +16,7 @@ export class InMemoryWorkflowService extends PikkuWorkflowService {
|
|
|
17
16
|
branchKeys = new Map(); // keyed by stepId
|
|
18
17
|
workflowVersions = new Map(); // keyed by `${name}:${graphHash}`
|
|
19
18
|
async createRunImpl(workflowName, input, inline, graphHash, wire, options) {
|
|
20
|
-
const runId = randomUUID();
|
|
19
|
+
const runId = globalThis.crypto.randomUUID();
|
|
21
20
|
const now = new Date();
|
|
22
21
|
const run = {
|
|
23
22
|
id: runId,
|
|
@@ -41,7 +40,7 @@ export class InMemoryWorkflowService extends PikkuWorkflowService {
|
|
|
41
40
|
return runId;
|
|
42
41
|
}
|
|
43
42
|
async scheduleSleep(runId, stepId, duration) {
|
|
44
|
-
const
|
|
43
|
+
const resume = async () => {
|
|
45
44
|
this.sleepTimers.delete(timer);
|
|
46
45
|
try {
|
|
47
46
|
await this.executeWorkflowSleepCompleted(runId, stepId);
|
|
@@ -49,7 +48,9 @@ export class InMemoryWorkflowService extends PikkuWorkflowService {
|
|
|
49
48
|
catch (error) {
|
|
50
49
|
this.logger?.error(`Failed to resume workflow sleep for runId ${runId}: ${error?.message ?? error}`);
|
|
51
50
|
}
|
|
52
|
-
}
|
|
51
|
+
};
|
|
52
|
+
// Fire-and-forget timer callback: resume catches and logs every error.
|
|
53
|
+
const timer = setTimeout(() => void resume(), duration);
|
|
53
54
|
this.sleepTimers.add(timer);
|
|
54
55
|
return true;
|
|
55
56
|
}
|
|
@@ -71,7 +72,7 @@ export class InMemoryWorkflowService extends PikkuWorkflowService {
|
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
async insertStepStateImpl(runId, stepName, rpcName, data, stepOptions, fromStepName) {
|
|
74
|
-
const stepId = randomUUID();
|
|
75
|
+
const stepId = globalThis.crypto.randomUUID();
|
|
75
76
|
const now = new Date();
|
|
76
77
|
const step = {
|
|
77
78
|
stepId,
|
|
@@ -202,7 +203,7 @@ export class InMemoryWorkflowService extends PikkuWorkflowService {
|
|
|
202
203
|
throw new Error(`Step not found: ${failedStepId}`);
|
|
203
204
|
}
|
|
204
205
|
const failedStepData = this.stepData.get(failedStepId);
|
|
205
|
-
const newStepId = randomUUID();
|
|
206
|
+
const newStepId = globalThis.crypto.randomUUID();
|
|
206
207
|
const now = new Date();
|
|
207
208
|
const newStep = {
|
|
208
209
|
stepId: newStepId,
|
package/dist/services/index.d.ts
CHANGED
|
@@ -21,7 +21,6 @@ export { LeaseLostError, LeaseTakenError, holdLease, type Lease, type LeaseServi
|
|
|
21
21
|
export { InMemoryLeaseService } from './in-memory-lease-service.js';
|
|
22
22
|
export { InMemoryAgentRunStateService } from './in-memory-agent-run-state-service.js';
|
|
23
23
|
export { LocalGatewayService } from './local-gateway-service.js';
|
|
24
|
-
export { FileScenarioRunStore, scenarioArtifactContentType, scenarioRunSummary, type FileScenarioRunStoreOptions, } from './file-scenario-run-store.js';
|
|
25
24
|
export type { ContentService, SignContentKeyArgs, SignURLArgs, GetUploadURLArgs, UploadURLResult, BucketKeyArgs, WriteFileArgs, CopyFileArgs, } from './content-service.js';
|
|
26
25
|
export type { ScenarioPersona, ResolvedPersona, ScenarioPersonas, } from './personas-service.js';
|
|
27
26
|
export type { JWTService } from './jwt-service.js';
|
package/dist/services/index.js
CHANGED
|
@@ -21,7 +21,6 @@ export { LeaseLostError, LeaseTakenError, holdLease, } from './lease-service.js'
|
|
|
21
21
|
export { InMemoryLeaseService } from './in-memory-lease-service.js';
|
|
22
22
|
export { InMemoryAgentRunStateService } from './in-memory-agent-run-state-service.js';
|
|
23
23
|
export { LocalGatewayService } from './local-gateway-service.js';
|
|
24
|
-
export { FileScenarioRunStore, scenarioArtifactContentType, scenarioRunSummary, } from './file-scenario-run-store.js';
|
|
25
24
|
export { renderEmail, } from './email-template.js';
|
|
26
25
|
export { DEFAULT_WEBHOOK_RETRIES, PIKKU_OUTGOING_WEBHOOK_QUEUE_NAME, WebhookService, } from './webhook-service.js';
|
|
27
26
|
export { ACTOR_ROOT_SECRET_MIN_LENGTH, ACTOR_SECRET_INFO, ACTOR_SECRET_NAME, actorSecretSubject, deriveActorSecret, verifyActorSecret, } from './persona-actor-secret.js';
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { VariablesService } from './variables-service.js';
|
|
2
2
|
export declare class LocalVariablesService implements VariablesService {
|
|
3
3
|
private variables;
|
|
4
|
+
/**
|
|
5
|
+
* Defaults to `process.env`, resolved when the service is built rather than
|
|
6
|
+
* when this module loads, and guarded so a runtime without `process` gets an
|
|
7
|
+
* empty store instead of a ReferenceError.
|
|
8
|
+
*/
|
|
4
9
|
constructor(variables?: Record<string, string | undefined>);
|
|
5
10
|
getAll(): Record<string, string | undefined>;
|
|
6
11
|
getVariables<T extends Record<string, unknown> = Record<string, unknown>>(names: (keyof T & string)[]): Partial<T>;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
export class LocalVariablesService {
|
|
2
2
|
variables;
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Defaults to `process.env`, resolved when the service is built rather than
|
|
5
|
+
* when this module loads, and guarded so a runtime without `process` gets an
|
|
6
|
+
* empty store instead of a ReferenceError.
|
|
7
|
+
*/
|
|
8
|
+
constructor(variables) {
|
|
9
|
+
this.variables = variables ?? globalThis.process?.env ?? {};
|
|
5
10
|
}
|
|
6
11
|
getAll() {
|
|
7
12
|
return this.variables || {};
|
|
@@ -10,6 +10,7 @@ import type { FeaturesMeta } from '../wirings/workflow/scenario.types.js';
|
|
|
10
10
|
import type { ResolvedPersona } from './personas-service.js';
|
|
11
11
|
import type { SystemRoleDefinitionsMeta } from '../wirings/role/role.types.js';
|
|
12
12
|
import type { FeatureFlagDefinitionsMeta } from '../wirings/flag/flag.types.js';
|
|
13
|
+
import type { ScopeDefinitionsMeta } from '../wirings/scope/scope.types.js';
|
|
13
14
|
import type { AnalyticsEventsMeta } from '../analytics/analytics.types.js';
|
|
14
15
|
import type { TriggerMeta, TriggerSourceMeta } from '../wirings/trigger/trigger.types.js';
|
|
15
16
|
import type { SecretDefinitionsMeta } from '../wirings/secret/secret.types.js';
|
|
@@ -156,6 +157,14 @@ export interface MetaService {
|
|
|
156
157
|
* seed granted from rather than a second one.
|
|
157
158
|
*/
|
|
158
159
|
getSystemRolesMeta(): Promise<SystemRoleDefinitionsMeta>;
|
|
160
|
+
/**
|
|
161
|
+
* The scope trees declared with `defineScope`, keyed by root, each carrying
|
|
162
|
+
* where it came from.
|
|
163
|
+
*
|
|
164
|
+
* The scope store holds the grantable ids; this holds what the store does
|
|
165
|
+
* not — a root's display name and which addon, if any, declared it.
|
|
166
|
+
*/
|
|
167
|
+
getScopesMeta(): Promise<ScopeDefinitionsMeta>;
|
|
159
168
|
/**
|
|
160
169
|
* The flags declared with `defineFeatureFlags`, keyed by name.
|
|
161
170
|
*
|
|
@@ -199,6 +208,7 @@ export declare class LocalMetaService implements MetaService {
|
|
|
199
208
|
private workflowMetaCache;
|
|
200
209
|
private personasMetaCache;
|
|
201
210
|
private systemRolesMetaCache;
|
|
211
|
+
private scopesMetaCache;
|
|
202
212
|
private featureFlagsMetaCache;
|
|
203
213
|
private analyticsMetaCache;
|
|
204
214
|
private featuresMetaCache;
|
|
@@ -236,6 +246,7 @@ export declare class LocalMetaService implements MetaService {
|
|
|
236
246
|
getWorkflowMeta(): Promise<WorkflowsMeta>;
|
|
237
247
|
getPersonasMeta(): Promise<Record<string, ResolvedPersona>>;
|
|
238
248
|
getSystemRolesMeta(): Promise<SystemRoleDefinitionsMeta>;
|
|
249
|
+
getScopesMeta(): Promise<ScopeDefinitionsMeta>;
|
|
239
250
|
getFeatureFlagsMeta(): Promise<FeatureFlagDefinitionsMeta>;
|
|
240
251
|
getAnalyticsMeta(): Promise<AnalyticsEventsMeta>;
|
|
241
252
|
getFeaturesMeta(): Promise<FeaturesMeta>;
|
|
@@ -16,6 +16,7 @@ export class LocalMetaService {
|
|
|
16
16
|
workflowMetaCache = null;
|
|
17
17
|
personasMetaCache = null;
|
|
18
18
|
systemRolesMetaCache = null;
|
|
19
|
+
scopesMetaCache = null;
|
|
19
20
|
featureFlagsMetaCache = null;
|
|
20
21
|
analyticsMetaCache = null;
|
|
21
22
|
featuresMetaCache = null;
|
|
@@ -115,6 +116,7 @@ export class LocalMetaService {
|
|
|
115
116
|
this.workflowMetaCache = null;
|
|
116
117
|
this.personasMetaCache = null;
|
|
117
118
|
this.systemRolesMetaCache = null;
|
|
119
|
+
this.scopesMetaCache = null;
|
|
118
120
|
this.featureFlagsMetaCache = null;
|
|
119
121
|
this.analyticsMetaCache = null;
|
|
120
122
|
this.featuresMetaCache = null;
|
|
@@ -216,7 +218,7 @@ export class LocalMetaService {
|
|
|
216
218
|
if (this.rpcMetaCache)
|
|
217
219
|
return this.rpcMetaCache;
|
|
218
220
|
try {
|
|
219
|
-
const content = await this.readFile('rpc/pikku-rpc-wirings-meta.gen.json');
|
|
221
|
+
const content = await this.readFile('rpc/pikku-rpc-wirings-meta.internal.gen.json');
|
|
220
222
|
this.rpcMetaCache = content ? JSON.parse(content) : {};
|
|
221
223
|
return this.rpcMetaCache;
|
|
222
224
|
}
|
|
@@ -278,6 +280,13 @@ export class LocalMetaService {
|
|
|
278
280
|
this.systemRolesMetaCache = content ? JSON.parse(content) : {};
|
|
279
281
|
return this.systemRolesMetaCache;
|
|
280
282
|
}
|
|
283
|
+
async getScopesMeta() {
|
|
284
|
+
if (this.scopesMetaCache)
|
|
285
|
+
return this.scopesMetaCache;
|
|
286
|
+
const content = await this.readFile('scopes/pikku-scopes-meta.gen.json');
|
|
287
|
+
this.scopesMetaCache = content ? JSON.parse(content) : {};
|
|
288
|
+
return this.scopesMetaCache;
|
|
289
|
+
}
|
|
281
290
|
async getFeatureFlagsMeta() {
|
|
282
291
|
if (this.featureFlagsMetaCache)
|
|
283
292
|
return this.featureFlagsMetaCache;
|
|
@@ -39,7 +39,7 @@ export class QueueWebhookService extends WebhookService {
|
|
|
39
39
|
}
|
|
40
40
|
if (secret) {
|
|
41
41
|
const signatureHeader = webhookConfig?.signatureHeader ?? DEFAULT_WEBHOOK_SIGNATURE_HEADER;
|
|
42
|
-
headers[signatureHeader] = this.sign(secret, body);
|
|
42
|
+
headers[signatureHeader] = await this.sign(secret, body);
|
|
43
43
|
}
|
|
44
44
|
const jobData = {
|
|
45
45
|
url: input.url,
|
|
@@ -87,9 +87,9 @@ export declare const DEFAULT_WEBHOOK_RETRIES = 3;
|
|
|
87
87
|
export declare abstract class WebhookService {
|
|
88
88
|
abstract send<T extends SendWebhookInput>(input: Safe<T>): Promise<SendWebhookResult>;
|
|
89
89
|
/** Produces the header value, `sha256=<hex>`, not the bare digest. */
|
|
90
|
-
protected sign(secret: string, body: string): string
|
|
90
|
+
protected sign(secret: string, body: string): Promise<string>;
|
|
91
91
|
/** Public because receivers verify with it; they share the signing scheme. */
|
|
92
|
-
verify(secret: string, signature: string, body: string): boolean
|
|
92
|
+
verify(secret: string, signature: string, body: string): Promise<boolean>;
|
|
93
93
|
/**
|
|
94
94
|
* The three methods below are optional capability: the default queue-only
|
|
95
95
|
* service keeps no history, and a store-backed implementation (e.g.
|
|
@@ -5,12 +5,14 @@ export const DEFAULT_WEBHOOK_SIGNATURE_HEADER = 'X-Pikku-Signature';
|
|
|
5
5
|
export const DEFAULT_WEBHOOK_RETRIES = 3;
|
|
6
6
|
export class WebhookService {
|
|
7
7
|
/** Produces the header value, `sha256=<hex>`, not the bare digest. */
|
|
8
|
-
sign(secret, body) {
|
|
9
|
-
return `sha256=${hmacSha256Hex(secret, body)}`;
|
|
8
|
+
async sign(secret, body) {
|
|
9
|
+
return `sha256=${await hmacSha256Hex(secret, body)}`;
|
|
10
10
|
}
|
|
11
11
|
/** Public because receivers verify with it; they share the signing scheme. */
|
|
12
|
-
verify(secret, signature, body) {
|
|
13
|
-
|
|
12
|
+
async verify(secret, signature, body) {
|
|
13
|
+
if (!secret || !signature)
|
|
14
|
+
return false;
|
|
15
|
+
return timingSafeStringEqual(await this.sign(secret, body), signature);
|
|
14
16
|
}
|
|
15
17
|
/**
|
|
16
18
|
* The three methods below are optional capability: the default queue-only
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AbortScope } from '../function/abort-scope.js';
|
|
1
2
|
import type { IncomingWebhookService } from '../services/incoming-webhook-service.js';
|
|
2
3
|
import type { TriggerSourceStore } from '../services/trigger-source-store.js';
|
|
3
4
|
import type { LeaseService } from '../services/lease-service.js';
|
|
@@ -287,7 +288,15 @@ export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boo
|
|
|
287
288
|
beginChanges: () => Promise<void>;
|
|
288
289
|
}>;
|
|
289
290
|
/** Wire as constructed by runners, before the function runner adds `rpc` and the credential readers. */
|
|
290
|
-
export type PikkuRawWire = Omit<PikkuWire, 'rpc' | 'getCredential' | 'getCredentials'> & Partial<Pick<PikkuWire, 'getCredential' | 'getCredentials'
|
|
291
|
+
export type PikkuRawWire = Omit<PikkuWire, 'rpc' | 'getCredential' | 'getCredentials'> & Partial<Pick<PikkuWire, 'getCredential' | 'getCredentials'>> & {
|
|
292
|
+
/**
|
|
293
|
+
* Whether whatever asked for this work is still there. Set by wirings that
|
|
294
|
+
* can be abandoned (an interrupted agent run); the function runner binds
|
|
295
|
+
* `wire.beginChanges` to it. Carried through nested `rpc` calls by the
|
|
296
|
+
* wire spread, so a sub-call observes the same scope.
|
|
297
|
+
*/
|
|
298
|
+
abortScope?: AbortScope;
|
|
299
|
+
};
|
|
291
300
|
export type CoreServices<SingletonServices = CoreSingletonServices> = SingletonServices;
|
|
292
301
|
/** Strips `secrets` from a services type. */
|
|
293
302
|
export type SecretlessServices<Services> = Omit<Services, 'secrets'>;
|
package/dist/utils/hmac.d.ts
CHANGED
|
@@ -1,22 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Web Crypto only: no `node:crypto` and no `Buffer`, so this loads on edge
|
|
3
|
+
* runtimes. Web Crypto is async, so everything that touches a key returns a
|
|
4
|
+
* Promise; `timingSafeStringEqual` is plain JS and stays synchronous.
|
|
4
5
|
*/
|
|
5
|
-
export declare function hmacSha256Hex(secret: string, payload: string): string;
|
|
6
6
|
/**
|
|
7
|
-
* Constant-time
|
|
8
|
-
*
|
|
7
|
+
* Constant-time string comparison (pure JS; for opaque tokens, not HMACs, which
|
|
8
|
+
* `verifyHmacSignature` checks with Web Crypto). Returns false on a length mismatch
|
|
9
|
+
* (the lengths of the two values are not secret).
|
|
9
10
|
*/
|
|
10
11
|
export declare function timingSafeStringEqual(a: string, b: string): boolean;
|
|
11
12
|
export type WebhookPayload = string | Uint8Array;
|
|
12
13
|
export type HmacAlgorithm = 'sha1' | 'sha256' | 'sha512';
|
|
13
14
|
export type SecretEncoding = 'utf8' | 'hex' | 'base64';
|
|
15
|
+
/**
|
|
16
|
+
* HMAC-SHA256 of a payload, hex-encoded. Senders wrap this in their own scheme
|
|
17
|
+
* prefix (`sha256=`, `v0=`, …).
|
|
18
|
+
*/
|
|
19
|
+
export declare function hmacSha256Hex(secret: string, payload: string): Promise<string>;
|
|
14
20
|
/** The HMAC of `payload` under `secret`, for handshakes that answer with a digest. */
|
|
15
|
-
export declare function hmacDigest(secret: string, algorithm: HmacAlgorithm, payload: WebhookPayload, encoding: 'hex' | 'base64', secretEncoding?: SecretEncoding): string
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
export declare function hmacDigest(secret: string, algorithm: HmacAlgorithm, payload: WebhookPayload, encoding: 'hex' | 'base64', secretEncoding?: SecretEncoding): Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Whether `signature` is the HMAC of `payload` under `secret`. Fails closed on
|
|
24
|
+
* an empty secret (or one that decodes to an empty key) and on a missing or
|
|
25
|
+
* malformed signature. The comparison is `crypto.subtle.verify('HMAC', ...)`,
|
|
26
|
+
* so it runs in the platform's constant-time implementation.
|
|
27
|
+
*/
|
|
28
|
+
export declare function verifyHmacSignature(secret: string, signature: string | undefined, algorithm: HmacAlgorithm, payload: WebhookPayload, encoding: 'hex' | 'base64', secretEncoding?: SecretEncoding): Promise<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* Whether `signature` (base64) is `payload` signed by the private half of
|
|
31
|
+
* `publicKey` (an RSA, EC or Ed25519 PEM `SPKI` key). RSA-PSS keys are not
|
|
32
|
+
* supported and verify as false. Ed25519 ignores `algorithm` and `dsaEncoding`.
|
|
33
|
+
*/
|
|
19
34
|
export declare function verifyPublicKeySignature(publicKey: string, signature: string | undefined, payload: WebhookPayload, options?: {
|
|
20
35
|
algorithm?: string;
|
|
21
36
|
dsaEncoding?: 'der' | 'ieee-p1363';
|
|
22
|
-
}): boolean
|
|
37
|
+
}): Promise<boolean>;
|