@pikku/core 0.12.80 → 0.12.83
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 +345 -0
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/function/function-runner.js +2 -5
- package/dist/function/index.d.ts +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +3 -3
- package/dist/pikku-state.js +4 -0
- package/dist/services/ai-agent-runner-service.d.ts +7 -0
- package/dist/services/ai-run-state-service.d.ts +10 -0
- package/dist/services/in-memory-ai-run-state-service.d.ts +5 -1
- package/dist/services/in-memory-ai-run-state-service.js +9 -0
- package/dist/services/index.d.ts +15 -16
- package/dist/services/index.js +5 -5
- package/dist/services/meta-service.d.ts +2 -1
- package/dist/services/scoped-credential-service.d.ts +21 -0
- package/dist/services/scoped-credential-service.js +53 -0
- package/dist/testing/service-tests/ai-storage-service-tests.js +76 -0
- package/dist/types/core.types.d.ts +2 -3
- package/dist/types/state.types.d.ts +19 -1
- package/dist/wirings/actor-flow/index.d.ts +1 -1
- package/dist/wirings/ai-agent/ai-agent-finalize.d.ts +58 -0
- package/dist/wirings/ai-agent/ai-agent-finalize.js +138 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.js +1 -0
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +2 -8
- package/dist/wirings/ai-agent/ai-agent-memory.js +34 -17
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +7 -0
- package/dist/wirings/ai-agent/ai-agent-model-config.js +44 -1
- package/dist/wirings/ai-agent/ai-agent-prepare.js +4 -0
- package/dist/wirings/ai-agent/ai-agent-runner.js +61 -40
- package/dist/wirings/ai-agent/ai-agent-stream.js +89 -36
- package/dist/wirings/ai-agent/ai-agent-turn.d.ts +1 -0
- package/dist/wirings/ai-agent/ai-agent-turn.js +1 -0
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +46 -1
- package/dist/wirings/ai-agent/index.d.ts +8 -7
- package/dist/wirings/ai-agent/index.js +5 -4
- package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +26 -0
- package/dist/wirings/ai-scorer/ai-scorer-grade.js +33 -0
- package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +17 -0
- package/dist/wirings/ai-scorer/ai-scorer-judge.js +92 -0
- package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +15 -0
- package/dist/wirings/ai-scorer/ai-scorer-live.js +38 -0
- package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +18 -0
- package/dist/wirings/ai-scorer/ai-scorer-registry.js +46 -0
- package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +8 -0
- package/dist/wirings/ai-scorer/ai-scorer-sampling.js +31 -0
- package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +10 -0
- package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +40 -0
- package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +15 -0
- package/dist/wirings/ai-scorer/ai-scorer-worker.js +58 -0
- package/dist/wirings/ai-scorer/ai-scorer.d.ts +39 -0
- package/dist/wirings/ai-scorer/ai-scorer.js +40 -0
- package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +90 -0
- package/dist/wirings/ai-scorer/ai-scorer.types.js +4 -0
- package/dist/wirings/ai-scorer/index.d.ts +6 -0
- package/dist/wirings/ai-scorer/index.js +5 -0
- package/dist/wirings/channel/index.d.ts +5 -6
- package/dist/wirings/channel/index.js +3 -4
- package/dist/wirings/channel/local/local-channel-runner.js +8 -1
- package/dist/wirings/cli/channel/cli-raw-channel-runner.js +9 -1
- package/dist/wirings/cli/channel/index.d.ts +1 -2
- package/dist/wirings/cli/channel/index.js +0 -1
- package/dist/wirings/cli/cli-runner.js +13 -1
- package/dist/wirings/credential/index.d.ts +1 -1
- package/dist/wirings/gateway/index.d.ts +1 -1
- package/dist/wirings/http/http-runner.js +8 -2
- package/dist/wirings/http/index.d.ts +1 -2
- package/dist/wirings/mcp/index.d.ts +1 -1
- package/dist/wirings/mcp/mcp-runner.d.ts +15 -0
- package/dist/wirings/mcp/mcp-runner.js +18 -5
- package/dist/wirings/persona/index.d.ts +3 -4
- package/dist/wirings/persona/index.js +2 -3
- package/dist/wirings/queue/index.d.ts +1 -3
- package/dist/wirings/queue/index.js +1 -3
- package/dist/wirings/rpc/addon-runner.d.ts +8 -0
- package/dist/wirings/rpc/addon-runner.js +31 -3
- package/dist/wirings/rpc/rpc-runner.js +4 -0
- package/dist/wirings/rpc/rpc-types.d.ts +8 -0
- package/dist/wirings/rpc/wire-addon.d.ts +25 -0
- package/dist/wirings/rpc/wire-addon.js +8 -0
- package/dist/wirings/scheduler/index.d.ts +1 -1
- package/dist/wirings/trigger/index.d.ts +1 -1
- package/dist/wirings/virtual-user/index.d.ts +5 -6
- package/dist/wirings/virtual-user/index.js +2 -4
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +85 -15
- package/dist/wirings/workflow/feature.d.ts +2 -1
- package/dist/wirings/workflow/index.d.ts +5 -16
- package/dist/wirings/workflow/index.js +1 -9
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -7
- package/dist/wirings/workflow/pikku-scenario-service.js +48 -13
- package/dist/wirings/workflow/pikku-workflow-service.js +17 -3
- package/dist/wirings/workflow/scenario-step.types.d.ts +8 -0
- package/dist/wirings/workflow/scenario.types.d.ts +37 -0
- package/dist/wirings/workflow/workflow-approval-audit.d.ts +16 -0
- package/dist/wirings/workflow/workflow-approval-audit.js +40 -0
- package/dist/wirings/workflow/workflow-approval-policy.d.ts +20 -0
- package/dist/wirings/workflow/workflow-approval-policy.js +48 -0
- package/dist/wirings/workflow/workflow-approval.d.ts +29 -1
- package/dist/wirings/workflow/workflow-approval.js +65 -2
- package/dist/wirings/workflow/workflow-run-ownership.d.ts +2 -1
- package/dist/wirings/workflow/workflow-run-ownership.js +2 -1
- package/dist/wirings/workflow/workflow.types.d.ts +2 -37
- package/knowledge/decisions/internals/addon-pikku-meta-ships-at-the-package-root-or-under-dist.md +32 -0
- package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +39 -0
- package/knowledge/decisions/internals/index.md +30 -3
- package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +115 -0
- package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +37 -0
- package/knowledge/decisions/security/a-workflow-run-is-read-and-approved-by-its-owner.md +30 -14
- package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +59 -0
- package/knowledge/decisions/security/index.md +3 -1
- package/knowledge/questions/index.md +1 -1
- package/package.json +3 -2
- package/scripts/generate-api-report.mts +143 -18
- package/src/api-report.test.ts +2 -2
- package/src/errors/index.ts +1 -1
- package/src/function/function-runner.test.ts +52 -0
- package/src/function/function-runner.ts +5 -9
- package/src/function/index.ts +0 -2
- package/src/index.ts +0 -35
- package/src/pikku-state.ts +5 -0
- package/src/public-surface.json +81 -118
- package/src/services/ai-agent-runner-service.ts +12 -1
- package/src/services/ai-run-state-service.ts +11 -0
- package/src/services/in-memory-ai-run-state-service.ts +13 -0
- package/src/services/index.ts +7 -58
- package/src/services/meta-service.ts +2 -4
- package/src/services/scoped-credential-service.test.ts +86 -0
- package/src/services/scoped-credential-service.ts +63 -0
- package/src/testing/service-tests/ai-storage-service-tests.ts +93 -0
- package/src/types/core.types.ts +4 -7
- package/src/types/state.types.ts +21 -1
- package/src/wirings/actor-flow/index.ts +0 -3
- package/src/wirings/ai-agent/ai-agent-finalize.test.ts +186 -0
- package/src/wirings/ai-agent/ai-agent-finalize.ts +197 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.ts +1 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +54 -38
- package/src/wirings/ai-agent/ai-agent-model-config.test.ts +72 -3
- package/src/wirings/ai-agent/ai-agent-model-config.ts +49 -1
- package/src/wirings/ai-agent/ai-agent-prepare.ts +4 -0
- package/src/wirings/ai-agent/ai-agent-runner.ts +71 -40
- package/src/wirings/ai-agent/ai-agent-stream-output-hooks.test.ts +353 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +116 -54
- package/src/wirings/ai-agent/ai-agent-turn.test.ts +67 -0
- package/src/wirings/ai-agent/ai-agent-turn.ts +1 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +64 -4
- package/src/wirings/ai-agent/index.ts +2 -16
- package/src/wirings/ai-scorer/ai-scorer-grade.test.ts +106 -0
- package/src/wirings/ai-scorer/ai-scorer-grade.ts +55 -0
- package/src/wirings/ai-scorer/ai-scorer-judge.test.ts +143 -0
- package/src/wirings/ai-scorer/ai-scorer-judge.ts +120 -0
- package/src/wirings/ai-scorer/ai-scorer-live.test.ts +174 -0
- package/src/wirings/ai-scorer/ai-scorer-live.ts +56 -0
- package/src/wirings/ai-scorer/ai-scorer-registry.ts +63 -0
- package/src/wirings/ai-scorer/ai-scorer-sampling.test.ts +34 -0
- package/src/wirings/ai-scorer/ai-scorer-sampling.ts +36 -0
- package/src/wirings/ai-scorer/ai-scorer-snapshots.test.ts +49 -0
- package/src/wirings/ai-scorer/ai-scorer-snapshots.ts +46 -0
- package/src/wirings/ai-scorer/ai-scorer-worker.test.ts +122 -0
- package/src/wirings/ai-scorer/ai-scorer-worker.ts +69 -0
- package/src/wirings/ai-scorer/ai-scorer.ts +76 -0
- package/src/wirings/ai-scorer/ai-scorer.types.ts +107 -0
- package/src/wirings/ai-scorer/index.ts +24 -0
- package/src/wirings/channel/index.ts +1 -20
- package/src/wirings/channel/local/local-channel-runner.test.ts +68 -0
- package/src/wirings/channel/local/local-channel-runner.ts +8 -1
- package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +23 -0
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +12 -1
- package/src/wirings/cli/channel/index.ts +0 -7
- package/src/wirings/cli/cli-runner.test.ts +68 -0
- package/src/wirings/cli/cli-runner.ts +18 -1
- package/src/wirings/credential/index.ts +0 -1
- package/src/wirings/gateway/index.ts +0 -3
- package/src/wirings/http/http-runner.test.ts +66 -0
- package/src/wirings/http/http-runner.ts +10 -2
- package/src/wirings/http/index.ts +1 -1
- package/src/wirings/mcp/index.ts +0 -1
- package/src/wirings/mcp/mcp-runner.test.ts +181 -0
- package/src/wirings/mcp/mcp-runner.ts +35 -5
- package/src/wirings/persona/index.ts +0 -8
- package/src/wirings/queue/index.ts +0 -14
- package/src/wirings/rpc/addon-runner.ts +62 -3
- package/src/wirings/rpc/addon-secrets.test.ts +391 -0
- package/src/wirings/rpc/rpc-runner.test.ts +2 -0
- package/src/wirings/rpc/rpc-runner.ts +4 -0
- package/src/wirings/rpc/rpc-types.ts +8 -0
- package/src/wirings/rpc/wire-addon.ts +33 -0
- package/src/wirings/scheduler/index.ts +0 -1
- package/src/wirings/trigger/index.ts +0 -1
- package/src/wirings/virtual-user/index.ts +0 -16
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +96 -16
- package/src/wirings/workflow/feature.ts +2 -5
- package/src/wirings/workflow/graph/graph-runner.test.ts +72 -0
- package/src/wirings/workflow/index.ts +2 -68
- package/src/wirings/workflow/pikku-scenario-service.ts +81 -16
- package/src/wirings/workflow/pikku-workflow-service.test.ts +13 -12
- package/src/wirings/workflow/pikku-workflow-service.ts +28 -4
- package/src/wirings/workflow/scenario-expectations.test.ts +75 -0
- package/src/wirings/workflow/scenario-hooks.test.ts +3 -2
- package/src/wirings/workflow/scenario-step.types.ts +8 -0
- package/src/wirings/workflow/scenario.types.ts +63 -0
- package/src/wirings/workflow/workflow-approval-audit.ts +47 -0
- package/src/wirings/workflow/workflow-approval-policy.test.ts +524 -0
- package/src/wirings/workflow/workflow-approval-policy.ts +68 -0
- package/src/wirings/workflow/workflow-approval.ts +113 -9
- package/src/wirings/workflow/workflow-run-authority.test.ts +12 -15
- package/src/wirings/workflow/workflow-run-ownership.ts +2 -1
- package/src/wirings/workflow/workflow.types.ts +1 -63
- package/src/wirings-stay-decoupled.test.ts +6 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/internal.d.ts +0 -3
- package/dist/internal.js +0 -2
- package/dist/middleware/timeout.d.ts +0 -9
- package/dist/middleware/timeout.js +0 -15
- package/dist/pikku-response.d.ts +0 -6
- package/dist/pikku-response.js +0 -6
- package/dist/services/gopass-secrets.d.ts +0 -15
- package/dist/services/gopass-secrets.js +0 -76
- package/dist/services/http-scenario-actors.d.ts +0 -75
- package/dist/services/http-scenario-actors.js +0 -195
- package/dist/services/http-user-flow-actors.d.ts +0 -67
- package/dist/services/http-user-flow-actors.js +0 -193
- package/dist/services/scenario-actors-service.d.ts +0 -127
- package/dist/services/scenario-actors-service.js +0 -40
- package/dist/services/user-flow-actors-service.d.ts +0 -39
- package/dist/wirings/credential/wire-credential.d.ts +0 -48
- package/dist/wirings/credential/wire-credential.js +0 -47
- package/dist/wirings/oauth2/oauth2-client.d.ts +0 -47
- package/dist/wirings/oauth2/oauth2-client.js +0 -263
- package/dist/wirings/oauth2/oauth2-routes.d.ts +0 -35
- package/dist/wirings/oauth2/oauth2-routes.js +0 -146
- package/dist/wirings/scope/wire-scope.d.ts +0 -33
- package/dist/wirings/scope/wire-scope.js +0 -32
- package/dist/wirings/workflow/dsl/index.d.ts +0 -5
- package/dist/wirings/workflow/dsl/index.js +0 -4
- package/dist/wirings/workflow/graph/index.d.ts +0 -5
- package/dist/wirings/workflow/graph/index.js +0 -4
- /package/dist/{services/user-flow-actors-service.js → wirings/workflow/scenario.types.js} +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
type: decision
|
|
3
|
-
title: A workflow run is read and
|
|
4
|
-
description: A run started through a session records that user and only that user may read it
|
|
3
|
+
title: A workflow run is read by its owner, and answered by whoever its gate declares
|
|
4
|
+
description: A run started through a session records that user and only that user may read it; who may answer an approval gate is the gate's own declaration, and a run with no recorded owner has no ownership to enforce
|
|
5
5
|
tags: workflow
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
# A workflow run is read and
|
|
8
|
+
# A workflow run is read by its owner, and answered by whoever its gate declares
|
|
9
9
|
|
|
10
10
|
`WorkflowRunWire.pikkuUserId` has always been recorded on every run started
|
|
11
11
|
through a session — `RPCService.startWorkflow` copies it off the wire — and
|
|
@@ -14,9 +14,9 @@ routes (which stream `output` and `error`) and `approveStep`, which took no
|
|
|
14
14
|
session at all and rejected only an already-resolved gate.
|
|
15
15
|
|
|
16
16
|
`assertWorkflowRunOwner`
|
|
17
|
-
(`packages/core/src/wirings/workflow/workflow-run-ownership.ts`) is the
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
(`packages/core/src/wirings/workflow/workflow-run-ownership.ts`) is the check the
|
|
18
|
+
**read** paths share: the generated status routes assert it against the run they
|
|
19
|
+
were already reading.
|
|
20
20
|
|
|
21
21
|
**A run with no recorded owner is not gated.** Triggers, schedulers and routes
|
|
22
22
|
wired without auth start runs with no `pikkuUserId`; there is nobody to compare a
|
|
@@ -24,11 +24,27 @@ caller against, and inventing one would reject the framework's own callers rathe
|
|
|
24
24
|
than secure anything. Gate those at the entrypoint with `auth` or `permissions`.
|
|
25
25
|
|
|
26
26
|
This is ownership, not an approver model. It answers "is this your run", not "are
|
|
27
|
-
you entitled to approve this particular gate"
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
you entitled to approve this particular gate".
|
|
28
|
+
|
|
29
|
+
## Approving is a separate question, and the gate answers it
|
|
30
|
+
|
|
31
|
+
`approveStep` originally shared `assertWorkflowRunOwner`, which made "only the
|
|
32
|
+
initiator may answer" the one available rule. That is right for "confirm your own
|
|
33
|
+
action" and exactly wrong for four-eyes sign-off, where the initiator is the one
|
|
34
|
+
person who must not sign. Which applies is a property of the decision, so it is
|
|
35
|
+
declared on the gate — `approvers` (`'any' | 'owner' | 'not-initiator'`) and
|
|
36
|
+
`approverScope` on `WorkflowApprovalOptions`.
|
|
37
|
+
|
|
38
|
+
It is enforced wherever the policy is known. Reaching the gate publishes the
|
|
39
|
+
policy into the run state, so a decision submitted after that is judged by the
|
|
40
|
+
approve entrypoint and refused with a 403. A decision can legitimately be
|
|
41
|
+
recorded before the run has reached the gate, and that one has no policy to be
|
|
42
|
+
judged against yet — it is judged on replay instead, alongside payload
|
|
43
|
+
validation, and discarded if it fails.
|
|
44
|
+
|
|
45
|
+
The default is therefore `any`: a gate is a pause for a decision, not an
|
|
46
|
+
authorization boundary, and a route that needs one has `auth`/`permissions`.
|
|
47
|
+
|
|
48
|
+
**What this rules out:** treating a run id as a capability, adding a new run read
|
|
49
|
+
path that does not take a session, and re-deriving who may approve from who
|
|
50
|
+
started the run.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
title: An approval answer outlives the run it answered
|
|
4
|
+
description: Run state holds a decision only while the gate is open, so the settled answer carries decidedBy/decidedAt into the step result and every attempt is written to the audit sink, which has no foreign key to the run
|
|
5
|
+
tags: workflow, audit
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# An approval answer outlives the run it answered
|
|
9
|
+
|
|
10
|
+
An approval gate is asked for so it can be answered for afterwards. "Who
|
|
11
|
+
released the funds" is the entire point of four-eyes sign-off, and until this
|
|
12
|
+
change none of it was kept.
|
|
13
|
+
|
|
14
|
+
Run state is where a decision *waits*, not where it is kept. The record under
|
|
15
|
+
`workflowRuns.state.__approval_<hex>` is overwritten by the next write to that
|
|
16
|
+
key, and cleared outright — `decidedBy: undefined` included — whenever a
|
|
17
|
+
decision is refused on replay. A trail that erases exactly the events worth
|
|
18
|
+
keeping is not a trail.
|
|
19
|
+
|
|
20
|
+
So the answer is recorded twice, in two places with different lifetimes.
|
|
21
|
+
|
|
22
|
+
## The settled decision keeps its provenance
|
|
23
|
+
|
|
24
|
+
`ApprovalOutcome<T>` carries `decidedBy` and `decidedAt` alongside `data`, so
|
|
25
|
+
the answer reaches `workflowStep.result` and, through it,
|
|
26
|
+
`workflowStepHistory` — append-only, and already the per-step event log. Both
|
|
27
|
+
are spread in only when present, so a gate answered without a session keeps the
|
|
28
|
+
shape it had before there was anything to record.
|
|
29
|
+
|
|
30
|
+
## The audit sink is what survives deletion
|
|
31
|
+
|
|
32
|
+
`deleteRun` cascades: `workflowStep` is `onDelete('cascade')` from
|
|
33
|
+
`workflowRuns`, and `workflowStepHistory` from `workflowStep`. Deleting a run
|
|
34
|
+
therefore deletes the sign-off with it — and a *refused* attempt never reaches a
|
|
35
|
+
step at all, so it was never in that trail to begin with.
|
|
36
|
+
|
|
37
|
+
Every answer — accepted, refused at submission, or cleared on replay — is
|
|
38
|
+
written to the `AuditService` as `workflow.approval.decided`, with
|
|
39
|
+
`outcome: 'success' | 'denied'`, the decider under `userIdentity.pikkuUserId`,
|
|
40
|
+
and the run, reason, scopes and refusal in `metadata`. The `audit` table holds
|
|
41
|
+
no foreign key to any workflow table, which is precisely why it is the right
|
|
42
|
+
home.
|
|
43
|
+
|
|
44
|
+
`auditApprovalDecision`
|
|
45
|
+
(`packages/core/src/wirings/workflow/workflow-approval-audit.ts`) is a module of
|
|
46
|
+
its own rather than a method on `PikkuWorkflowService`: the trail is a separate
|
|
47
|
+
concern from running the workflow, and the service is already at the 2000-line
|
|
48
|
+
limit `source-files-stay-composable.test.ts` enforces.
|
|
49
|
+
|
|
50
|
+
**A sink that fails is logged, never thrown.** The trail must not be the reason a
|
|
51
|
+
decision is lost. A project with no `audit` service wired records nothing and is
|
|
52
|
+
otherwise unaffected — the sink is opt-in, and `auditSchema` is deliberately not
|
|
53
|
+
in `pikkuSchemas`.
|
|
54
|
+
|
|
55
|
+
**What this rules out:** a dedicated `workflowApprovals` table. It would have to
|
|
56
|
+
be implemented in all seven backends and would still be deleted with the run
|
|
57
|
+
unless it deliberately broke the cascade — at which point it is the audit table
|
|
58
|
+
with extra steps. If approvals later need to be *queried* as a first-class
|
|
59
|
+
entity rather than read back from the trail, that is when to revisit.
|
|
@@ -10,11 +10,12 @@ A rule about who may do what, and which way it fails when it is unsure.
|
|
|
10
10
|
|
|
11
11
|
<!-- pikku:knowledge-index -->
|
|
12
12
|
- [A dropped audit write is always logged](a-dropped-audit-write-is-always-logged.md) — The no-op audit service falls back to the singleton logger when the wire carries none, so an unconfigured audit call is never silent
|
|
13
|
+
- [A function never receives the secret service](a-function-never-receives-the-secret-service.md) — Every function-, permission- and auth-facing services type is bounded by SecretlessServices, so reaching for `secrets` in a function body is a type error rather than a lint
|
|
13
14
|
- [A graph run starts at an entry node the graph declared](a-graph-run-starts-at-an-entry-node-the-graph-declared.md) — startNode may only name a node in meta.entryNodeIds, and no generated route offers it, because otherwise a caller picks which half of the graph to skip
|
|
14
15
|
- [A permission gets a wire it cannot reply on](a-permission-gets-a-wire-it-cannot-reply-on.md) — The permission wire is typed with Out = never so a permission cannot send on the channel; that narrowing is not a subtype, so the call site asserts
|
|
15
16
|
- [A step runs the function the workflow dispatched it with](a-step-runs-the-function-the-workflow-dispatched-it-with.md) — StepState records the step's function name so the worker can reject a queue message naming a different one, because the step executes under the run owner's identity
|
|
16
17
|
- [A virtual user is never offered a scenario, platform or addon step](a-virtual-user-is-never-offered-a-step-that-would-forge-its-own-oracle.md) — Being able to invoke "the webhook arrives" lets the user manufacture the outcome it exists to discover, which invalidates every finding downstream
|
|
17
|
-
- [A workflow run is read and
|
|
18
|
+
- [A workflow run is read by its owner, and answered by whoever its gate declares](a-workflow-run-is-read-and-approved-by-its-owner.md) — A run started through a session records that user and only that user may read it; who may answer an approval gate is the gate's own declaration, and a run with no recorded owner has no ownership to enforce
|
|
18
19
|
- [An actor's missing approval decision defaults to denied](actor-flow-missing-approval-decisions-default-to-denied.md) — Every pending tool call gets an explicit decision; an id the persona LLM omitted is denied, so a dropped field can never read as consent
|
|
19
20
|
- [Actor sign-in is proven by Set-Cookie, not a non-empty jar](actor-sign-in-is-proven-by-set-cookie-not-a-non-empty-jar.md) — HttpScenarioActor tracks its own signedIn flag and requires the sign-in response itself to set a cookie, because a populated jar proves nothing
|
|
20
21
|
- [Actor sign-in only works for actor-flagged users](actor-sign-in-only-works-for-actor-flagged-users.md) — The scenario actor secret mints sessions for user rows flagged actor and nothing else, so holding it never impersonates a real user
|
|
@@ -30,6 +31,7 @@ A rule about who may do what, and which way it fails when it is unsure.
|
|
|
30
31
|
- [An agent thread key is always prefixed with the trusted principal](ai-agent-thread-ownership-composes-the-session-principal.md) — Ownership keys are composed as principal:resourceId, so a client id can sub-divide its own boundary but never widen it
|
|
31
32
|
- [Agent tool permission filtering reads the live function config, not the metadata](ai-agent-tool-filtering-reads-the-live-function-config.md) — The pikkuAuth brand survives only on live permission objects, so a metadata-driven check would silently admit every gated tool
|
|
32
33
|
- [An agent approval is claimed before the tool runs](an-agent-approval-is-claimed-before-the-tool-runs.md) — resolveApproval is a compare-and-swap returning whether this caller won, because the read that precedes it is not a claim and ten concurrent approvals would otherwise mean ten refunds
|
|
34
|
+
- [An approval answer outlives the run it answered](an-approval-answer-outlives-the-run-it-answered.md) — Run state holds a decision only while the gate is open, so the settled answer carries decidedBy/decidedAt into the step result and every attempt is written to the audit sink, which has no foreign key to the run
|
|
33
35
|
- [An empty owners constraint matches nothing](an-empty-owners-constraint-matches-nothing.md) — owners is an authorization boundary, so every storage backend must treat [] as no rows rather than no filter
|
|
34
36
|
- [An exposed function with no gate is reported at codegen, not at boot](an-exposed-ungated-function-is-a-codegen-warning.md) — The check runs in the inspector where function meta and every wireAddon declaration are both in hand, because neither source alone can tell a gated function from an ungated one
|
|
35
37
|
- [An upload is counted as it arrives, not buffered and then measured](an-upload-is-counted-as-it-arrives-not-buffered-then-measured.md) — Reading the whole body before checking its size hands an unauthenticated caller a way to spend the server's memory
|
|
@@ -12,5 +12,5 @@ gets recorded.
|
|
|
12
12
|
|
|
13
13
|
<!-- pikku:knowledge-index -->
|
|
14
14
|
- [A channel's middleware list accepts bare factories that nothing ever resolves](channel-middleware-accepts-bare-factories-that-nothing-resolves.md) — CoreChannel.channelMiddleware admits CorePikkuChannelMiddlewareFactory, but no runner calls one, so a bare factory would stall the chain
|
|
15
|
-
- [An unauthorized channel reply is sent outside the channel's declared Out type](unauthorized-channel-replies-escape-the-declared-out-type.md) — processMessageHandlers sends a bare string on auth failure
|
|
15
|
+
- [An unauthorized channel reply is sent outside the channel's declared Out type](unauthorized-channel-replies-escape-the-declared-out-type.md) — processMessageHandlers sends a bare string on auth failure, which no generated client type admits — nobody has decided what the typed shape should be
|
|
16
16
|
<!-- /pikku:knowledge-index -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/core",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.83",
|
|
4
4
|
"description": "The Pikku runtime — functions, wirings, services, middleware and types",
|
|
5
5
|
"author": "yasser.fadl@gmail.com",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"./dist/errors/errors.js",
|
|
23
23
|
"./dist/wirings/rpc/rpc-runner.js",
|
|
24
24
|
"./dist/wirings/rpc/remote-addon-auth.js",
|
|
25
|
+
"./dist/wirings/workflow/workflow-approval-policy.js",
|
|
25
26
|
"./dist/wirings/workflow/workflow-errors.js",
|
|
26
27
|
"./dist/wirings/workflow/workflow-run-ownership.js",
|
|
27
28
|
"./dist/wirings/workflow/pikku-scenario-service.js"
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
"./node-host-resolver": "./dist/utils/node-host-resolver.js",
|
|
50
51
|
"./mcp": "./dist/wirings/mcp/index.js",
|
|
51
52
|
"./ai-agent": "./dist/wirings/ai-agent/index.js",
|
|
53
|
+
"./ai-scorer": "./dist/wirings/ai-scorer/index.js",
|
|
52
54
|
"./gateway": "./dist/wirings/gateway/index.js",
|
|
53
55
|
"./cli": "./dist/wirings/cli/index.js",
|
|
54
56
|
"./cli/command-parser": "./dist/wirings/cli/command-parser.js",
|
|
@@ -72,7 +74,6 @@
|
|
|
72
74
|
"./crypto-utils": "./dist/crypto-utils.js",
|
|
73
75
|
"./hmac": "./dist/utils/hmac.js",
|
|
74
76
|
"./ecosystem": "./dist/ecosystem.js",
|
|
75
|
-
"./internal": "./dist/ecosystem.js",
|
|
76
77
|
"./schema": "./dist/schema.js",
|
|
77
78
|
"./testing": "./dist/testing/index.js",
|
|
78
79
|
"./dev": "./dist/dev/hot-reload.js"
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
* disagree — so a member-level change is a reviewable diff rather than a
|
|
17
17
|
* surprise in someone else's CI.
|
|
18
18
|
*
|
|
19
|
+
* A member per line, and no member bodies. Both are load-bearing rather than
|
|
20
|
+
* cosmetic: the report exists to be diffed, and a class emitted as one long
|
|
21
|
+
* line of its own source is neither reviewable nor mergeable.
|
|
22
|
+
*
|
|
19
23
|
* Run: yarn api-report
|
|
20
24
|
*/
|
|
21
25
|
import { readFileSync, writeFileSync } from 'node:fs'
|
|
@@ -50,7 +54,132 @@ const program = ts.createProgram(
|
|
|
50
54
|
)
|
|
51
55
|
const checker = program.getTypeChecker()
|
|
52
56
|
|
|
53
|
-
/**
|
|
57
|
+
/** Comments are noise in a report the diff is read for. */
|
|
58
|
+
const stripComments = (text: string): string =>
|
|
59
|
+
text.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/[^\n]*/g, '')
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Punctuation that makes a line and its successor one thing.
|
|
63
|
+
*
|
|
64
|
+
* Read from both ends rather than one, because the closers are ambiguous from
|
|
65
|
+
* behind: `string[]`, `Promise<T>` and `= {}` all end in a closing bracket and
|
|
66
|
+
* none of them is a continuation. What the *next* line starts with is not.
|
|
67
|
+
*/
|
|
68
|
+
const CONTINUES = /(=>|[,;{([<|&=])$/
|
|
69
|
+
const CLOSES = /^(=>|extends\b|[}\])>,;|&])/
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* A member's own text on one line — long, but never long enough to matter.
|
|
73
|
+
*
|
|
74
|
+
* Inside an object type the author's newline *was* the separator, so collapsing
|
|
75
|
+
* without restoring it produces `{ a: string b: number }`, which is not the
|
|
76
|
+
* type it describes and does not parse in the ```ts fence it is printed into.
|
|
77
|
+
*/
|
|
78
|
+
const oneLine = (text: string): string => {
|
|
79
|
+
const lines = stripComments(text)
|
|
80
|
+
.split('\n')
|
|
81
|
+
.map((l) => l.trim())
|
|
82
|
+
.filter(Boolean)
|
|
83
|
+
let depth = 0
|
|
84
|
+
const joined = lines
|
|
85
|
+
.map((line, index) => {
|
|
86
|
+
const inObjectType = depth > 0
|
|
87
|
+
for (const character of line) {
|
|
88
|
+
if (character === '{') depth++
|
|
89
|
+
else if (character === '}') depth--
|
|
90
|
+
}
|
|
91
|
+
const next = lines[index + 1]
|
|
92
|
+
return inObjectType &&
|
|
93
|
+
next !== undefined &&
|
|
94
|
+
!CONTINUES.test(line) &&
|
|
95
|
+
!CLOSES.test(next)
|
|
96
|
+
? `${line};`
|
|
97
|
+
: line
|
|
98
|
+
})
|
|
99
|
+
.join(' ')
|
|
100
|
+
return joined.replace(/\(\s+/g, '(').replace(/\s+\)/g, ')')
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* A class member as a consumer sees it: the signature, and nothing after it.
|
|
105
|
+
*
|
|
106
|
+
* The body is where every character of this report used to come from — a class
|
|
107
|
+
* arrived as its own source, so the report promised implementation details and
|
|
108
|
+
* a one-word change to a method rewrote the whole declaration. Truncating at
|
|
109
|
+
* the body leaves exactly the part a compatibility promise covers.
|
|
110
|
+
*/
|
|
111
|
+
const memberSignature = (member: ts.ClassElement | ts.TypeElement): string => {
|
|
112
|
+
const source = member.getSourceFile().text
|
|
113
|
+
const start = member.getStart()
|
|
114
|
+
|
|
115
|
+
const body = (member as { body?: ts.Node }).body
|
|
116
|
+
if (body) {
|
|
117
|
+
const head = oneLine(source.slice(start, body.getStart())).replace(
|
|
118
|
+
/\s*$/,
|
|
119
|
+
''
|
|
120
|
+
)
|
|
121
|
+
// An inferred return type is still a return type a consumer depends on, and
|
|
122
|
+
// truncating at the body is what threw the author's away.
|
|
123
|
+
if (ts.isMethodDeclaration(member) || ts.isGetAccessorDeclaration(member)) {
|
|
124
|
+
const signature = member.type
|
|
125
|
+
? undefined
|
|
126
|
+
: checker.getSignatureFromDeclaration(member)
|
|
127
|
+
if (signature) {
|
|
128
|
+
const returns = checker.getReturnTypeOfSignature(signature)
|
|
129
|
+
return `${head}: ${checker.typeToString(returns, undefined, ts.TypeFormatFlags.NoTruncation)}`
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return head
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// A property's initializer is a body by another name. Its annotation stands in
|
|
136
|
+
// for it, and the checker supplies one when the author left it inferred.
|
|
137
|
+
if (ts.isPropertyDeclaration(member) && member.initializer) {
|
|
138
|
+
if (member.type) return oneLine(source.slice(start, member.type.end))
|
|
139
|
+
const type = checker.getTypeAtLocation(member)
|
|
140
|
+
return `${oneLine(source.slice(start, member.name.end))}: ${checker.typeToString(type, undefined, ts.TypeFormatFlags.NoTruncation)}`
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return oneLine(member.getText())
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Members a consumer can reach, so a private field is not a promise. */
|
|
147
|
+
const isPublic = (member: ts.ClassElement | ts.TypeElement): boolean =>
|
|
148
|
+
!(ts.getModifiers(member as ts.HasModifiers) ?? []).some(
|
|
149
|
+
(modifier) => modifier.kind === ts.SyntaxKind.PrivateKeyword
|
|
150
|
+
) && !(member.name && ts.isPrivateIdentifier(member.name))
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* A declaration with members, one member per line.
|
|
154
|
+
*
|
|
155
|
+
* The line break is the point. Two branches that touch different methods of the
|
|
156
|
+
* same class are a textual conflict when the class is one line and an ordinary
|
|
157
|
+
* merge when it is not, which is a tax the whole repo was paying: every rebase
|
|
158
|
+
* over this file stopped to re-resolve a forty-thousand-character line by hand.
|
|
159
|
+
*/
|
|
160
|
+
const membered = (
|
|
161
|
+
declaration:
|
|
162
|
+
| ts.ClassDeclaration
|
|
163
|
+
| ts.InterfaceDeclaration
|
|
164
|
+
| ts.EnumDeclaration,
|
|
165
|
+
members: ts.NodeArray<ts.ClassElement | ts.TypeElement | ts.EnumMember>
|
|
166
|
+
): string => {
|
|
167
|
+
if (members.length === 0) return oneLine(declaration.getText())
|
|
168
|
+
const source = declaration.getSourceFile().text
|
|
169
|
+
// `members.pos` is the offset just past the `{`, so the header comes with its
|
|
170
|
+
// own brace and needs no reassembling.
|
|
171
|
+
const header = oneLine(source.slice(declaration.getStart(), members.pos))
|
|
172
|
+
const lines = ts.isEnumDeclaration(declaration)
|
|
173
|
+
? (members as readonly ts.EnumMember[]).map(
|
|
174
|
+
(member) => `${oneLine(member.getText())},`
|
|
175
|
+
)
|
|
176
|
+
: (members as readonly (ts.ClassElement | ts.TypeElement)[])
|
|
177
|
+
.filter(isPublic)
|
|
178
|
+
.map(memberSignature)
|
|
179
|
+
return [header, ...lines.map((line) => ` ${line}`), '}'].join('\n')
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** How a symbol is declared, in as many lines as its members need. */
|
|
54
183
|
const signature = (exported: ts.Symbol): string => {
|
|
55
184
|
// A re-export is an alias; the declaration that matters is the target's, so
|
|
56
185
|
// an interface re-exported through a barrel still reports its members.
|
|
@@ -62,20 +191,21 @@ const signature = (exported: ts.Symbol): string => {
|
|
|
62
191
|
if (!declaration) return exported.getName()
|
|
63
192
|
|
|
64
193
|
if (
|
|
65
|
-
ts.isInterfaceDeclaration(declaration) ||
|
|
66
194
|
ts.isClassDeclaration(declaration) ||
|
|
67
|
-
ts.
|
|
195
|
+
ts.isInterfaceDeclaration(declaration) ||
|
|
68
196
|
ts.isEnumDeclaration(declaration)
|
|
69
197
|
) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
198
|
+
return membered(declaration, declaration.members)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (ts.isTypeAliasDeclaration(declaration)) {
|
|
202
|
+
// No members to walk, so the author's own line breaks are the ones that
|
|
203
|
+
// keep a long union reviewable.
|
|
204
|
+
return stripComments(declaration.getText())
|
|
75
205
|
.split('\n')
|
|
76
|
-
.map((l) => l.
|
|
77
|
-
.filter(
|
|
78
|
-
.join('
|
|
206
|
+
.map((l) => l.trimEnd())
|
|
207
|
+
.filter((l) => l.trim())
|
|
208
|
+
.join('\n')
|
|
79
209
|
}
|
|
80
210
|
|
|
81
211
|
const type = checker.getTypeOfSymbolAtLocation(symbol, declaration)
|
|
@@ -92,17 +222,12 @@ const ECOSYSTEM_SUBPATHS = new Set(['./ecosystem', './internal'])
|
|
|
92
222
|
|
|
93
223
|
type Exported = { name: string; members: number; signature: string }
|
|
94
224
|
|
|
95
|
-
/**
|
|
225
|
+
/** The members the report lists, which is what the tally is counting. */
|
|
96
226
|
const memberCount = (declaration: ts.Declaration): number => {
|
|
97
227
|
if (ts.isInterfaceDeclaration(declaration)) return declaration.members.length
|
|
98
228
|
if (ts.isEnumDeclaration(declaration)) return declaration.members.length
|
|
99
229
|
if (!ts.isClassDeclaration(declaration)) return 0
|
|
100
|
-
return declaration.members.filter(
|
|
101
|
-
(member) =>
|
|
102
|
-
!(ts.getModifiers(member) ?? []).some(
|
|
103
|
-
(modifier) => modifier.kind === ts.SyntaxKind.PrivateKeyword
|
|
104
|
-
) && !(member.name && ts.isPrivateIdentifier(member.name))
|
|
105
|
-
).length
|
|
230
|
+
return declaration.members.filter(isPublic).length
|
|
106
231
|
}
|
|
107
232
|
|
|
108
233
|
const modules = new Map<string, Exported[]>()
|
package/src/api-report.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, test } from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
3
|
import { execFileSync } from 'node:child_process'
|
|
4
|
-
import { readFileSync } from 'node:fs'
|
|
4
|
+
import { readFileSync, writeFileSync } from 'node:fs'
|
|
5
5
|
import { fileURLToPath } from 'node:url'
|
|
6
6
|
import { join, dirname } from 'node:path'
|
|
7
7
|
|
|
@@ -39,7 +39,7 @@ describe('the API report matches the code', () => {
|
|
|
39
39
|
const firstDiff = regeneratedLines.findIndex(
|
|
40
40
|
(line, i) => line !== committedLines[i]
|
|
41
41
|
)
|
|
42
|
-
|
|
42
|
+
writeFileSync(reportPath, committed)
|
|
43
43
|
|
|
44
44
|
assert.fail(
|
|
45
45
|
'the public API changed but api-report.md was not regenerated.\n' +
|
package/src/errors/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './errors.js'
|
|
2
|
-
export { PikkuError, addError
|
|
2
|
+
export { PikkuError, addError } from './error-handler.js'
|
|
@@ -14,6 +14,7 @@ import type { CorePermissionGroup } from './functions.types.js'
|
|
|
14
14
|
import { PikkuSessionService } from '../services/user-session-service.js'
|
|
15
15
|
import { MissingScopeError, ReadonlySessionError } from '../errors/errors.js'
|
|
16
16
|
import { createInvocationAudit } from '../services/audit-service.js'
|
|
17
|
+
import { SecretAccessDeniedError } from '../services/secretless.js'
|
|
17
18
|
|
|
18
19
|
beforeEach(() => {
|
|
19
20
|
resetPikkuState()
|
|
@@ -1563,3 +1564,54 @@ describe('runPikkuFunc - scopes', () => {
|
|
|
1563
1564
|
})
|
|
1564
1565
|
})
|
|
1565
1566
|
})
|
|
1567
|
+
|
|
1568
|
+
describe('runPikkuFunc - secrets', () => {
|
|
1569
|
+
const singletonServicesWithSecrets = {
|
|
1570
|
+
...mockSingletonServices,
|
|
1571
|
+
secrets: { getSecret: async () => 'super-secret' },
|
|
1572
|
+
} as any
|
|
1573
|
+
|
|
1574
|
+
const addSecretReadingFunction = (
|
|
1575
|
+
funcName: string,
|
|
1576
|
+
seen: { error?: unknown }
|
|
1577
|
+
) => {
|
|
1578
|
+
addTestFunction(funcName, {
|
|
1579
|
+
func: async (services: any) => {
|
|
1580
|
+
try {
|
|
1581
|
+
return services.secrets
|
|
1582
|
+
} catch (error) {
|
|
1583
|
+
seen.error = error
|
|
1584
|
+
return 'denied'
|
|
1585
|
+
}
|
|
1586
|
+
},
|
|
1587
|
+
})
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
const run = (funcName: string) =>
|
|
1591
|
+
runPikkuFunc('rpc', Math.random().toString(), funcName, {
|
|
1592
|
+
singletonServices: singletonServicesWithSecrets,
|
|
1593
|
+
getAllServices: () => singletonServicesWithSecrets,
|
|
1594
|
+
data: () => ({}),
|
|
1595
|
+
auth: false,
|
|
1596
|
+
wire: {},
|
|
1597
|
+
})
|
|
1598
|
+
|
|
1599
|
+
test('a function never receives the secrets service', async () => {
|
|
1600
|
+
const seen: { error?: unknown } = {}
|
|
1601
|
+
addSecretReadingFunction('readsSecrets', seen)
|
|
1602
|
+
|
|
1603
|
+
assert.equal(await run('readsSecrets'), 'denied')
|
|
1604
|
+
assert.ok(seen.error instanceof SecretAccessDeniedError)
|
|
1605
|
+
})
|
|
1606
|
+
|
|
1607
|
+
test('no metadata flag can restore the secrets service', async () => {
|
|
1608
|
+
const seen: { error?: unknown } = {}
|
|
1609
|
+
addSecretReadingFunction('claimsAnExemption', seen)
|
|
1610
|
+
Object.assign(pikkuState(null, 'function', 'meta')['claimsAnExemption']!, {
|
|
1611
|
+
secretBroker: true,
|
|
1612
|
+
})
|
|
1613
|
+
|
|
1614
|
+
assert.equal(await run('claimsAnExemption'), 'denied')
|
|
1615
|
+
assert.ok(seen.error instanceof SecretAccessDeniedError)
|
|
1616
|
+
})
|
|
1617
|
+
})
|
|
@@ -177,8 +177,6 @@ export const runPikkuFunc = async <In = any, Out = any>(
|
|
|
177
177
|
|
|
178
178
|
const resolvedFunctionId = funcMeta.pikkuFuncId ?? funcName
|
|
179
179
|
|
|
180
|
-
const keepsSecrets = funcMeta.secretBroker === true
|
|
181
|
-
|
|
182
180
|
const resolvedSingletonServices = packageName
|
|
183
181
|
? await getOrCreatePackageSingletonServices(
|
|
184
182
|
packageName,
|
|
@@ -363,12 +361,10 @@ export const runPikkuFunc = async <In = any, Out = any>(
|
|
|
363
361
|
|
|
364
362
|
await runPermissions({
|
|
365
363
|
funcPermissions: funcConfig.permissions,
|
|
366
|
-
services:
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
'a permission'
|
|
371
|
-
) as CoreSingletonServices),
|
|
364
|
+
services: withoutSecrets(
|
|
365
|
+
resolvedSingletonServices,
|
|
366
|
+
'a permission'
|
|
367
|
+
) as CoreSingletonServices,
|
|
372
368
|
// knowledge: decisions/security/a-permission-gets-a-wire-it-cannot-reply-on.md
|
|
373
369
|
wire: invocationWire as PermissionWire,
|
|
374
370
|
data: actualData,
|
|
@@ -420,7 +416,7 @@ export const runPikkuFunc = async <In = any, Out = any>(
|
|
|
420
416
|
enumerable: true,
|
|
421
417
|
})
|
|
422
418
|
return await funcConfig.func(
|
|
423
|
-
|
|
419
|
+
withoutSecrets(services, 'a pikku function'),
|
|
424
420
|
actualData,
|
|
425
421
|
invocationWire
|
|
426
422
|
)
|
package/src/function/index.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -17,12 +17,10 @@ export type {
|
|
|
17
17
|
CreateConfig,
|
|
18
18
|
ServerLifecycle,
|
|
19
19
|
FunctionMeta,
|
|
20
|
-
FunctionRuntimeMeta,
|
|
21
20
|
FunctionServicesMeta,
|
|
22
21
|
FunctionWiresMeta,
|
|
23
22
|
FunctionsMeta,
|
|
24
23
|
FunctionsRuntimeMeta,
|
|
25
|
-
JSONPrimitive,
|
|
26
24
|
JSONValue,
|
|
27
25
|
MakeRequired,
|
|
28
26
|
MiddlewareMetadata,
|
|
@@ -43,7 +41,6 @@ export type {
|
|
|
43
41
|
SecuritySeverity,
|
|
44
42
|
SecurityUpdateLevel,
|
|
45
43
|
SerializedError,
|
|
46
|
-
WireServices,
|
|
47
44
|
} from './types/core.types.js'
|
|
48
45
|
export {
|
|
49
46
|
pikkuAIMiddleware,
|
|
@@ -57,7 +54,6 @@ export type {
|
|
|
57
54
|
CorePikkuAuthConfig,
|
|
58
55
|
CorePikkuFunction,
|
|
59
56
|
CorePikkuFunctionConfig,
|
|
60
|
-
CorePikkuFunctionHook,
|
|
61
57
|
CorePikkuPermission,
|
|
62
58
|
CorePikkuPermissionConfig,
|
|
63
59
|
CorePikkuPermissionFactory,
|
|
@@ -75,8 +71,6 @@ export type {
|
|
|
75
71
|
ListInput,
|
|
76
72
|
ListOutput,
|
|
77
73
|
Filter,
|
|
78
|
-
LeafFilter,
|
|
79
|
-
LeafValue,
|
|
80
74
|
} from './function/list.types.js'
|
|
81
75
|
export { pikkuCLIRender } from './wirings/cli/cli-runner.js'
|
|
82
76
|
export { PikkuRequest } from './pikku-request.js'
|
|
@@ -92,9 +86,6 @@ export {
|
|
|
92
86
|
} from './version.js'
|
|
93
87
|
export {
|
|
94
88
|
AbandonedError,
|
|
95
|
-
beginChanges,
|
|
96
|
-
getAbortScope,
|
|
97
|
-
runInAbortScope,
|
|
98
89
|
type AbortScope,
|
|
99
90
|
} from './function/abort-scope.js'
|
|
100
91
|
export { fetch } from './wirings/http/http-runner.js'
|
|
@@ -152,7 +143,6 @@ export type { QueueService } from './wirings/queue/queue.types.js'
|
|
|
152
143
|
export type { JWTService } from './services/jwt-service.js'
|
|
153
144
|
export type {
|
|
154
145
|
EmailService,
|
|
155
|
-
EmailTemplateReference,
|
|
156
146
|
SendEmailInput,
|
|
157
147
|
SendEmailResult,
|
|
158
148
|
SendHTMLEmailInput,
|
|
@@ -188,8 +178,6 @@ export type { SessionService } from './services/user-session-service.js'
|
|
|
188
178
|
export {
|
|
189
179
|
NoopAuditService,
|
|
190
180
|
createInvocationAudit,
|
|
191
|
-
resolveAuditConfig,
|
|
192
|
-
resolveAuditUserIdentityFromWire,
|
|
193
181
|
} from './services/audit-service.js'
|
|
194
182
|
export type {
|
|
195
183
|
AuditConfig,
|
|
@@ -198,31 +186,14 @@ export type {
|
|
|
198
186
|
AuditEventBatch,
|
|
199
187
|
AuditFacets,
|
|
200
188
|
AuditLog,
|
|
201
|
-
AuditLogWriteInput,
|
|
202
|
-
AuditOutcome,
|
|
203
189
|
AuditQuery,
|
|
204
190
|
AuditQueryResult,
|
|
205
191
|
AuditService,
|
|
206
|
-
AuditSource,
|
|
207
192
|
AuditUserIdentity,
|
|
208
193
|
ResolvedAuditConfig,
|
|
209
194
|
} from './services/audit-service.js'
|
|
210
195
|
export type {
|
|
211
196
|
AIAgentRunnerService,
|
|
212
|
-
AIEmbedManyParams,
|
|
213
|
-
AIEmbedManyResult,
|
|
214
|
-
AIEmbedParams,
|
|
215
|
-
AIEmbedResult,
|
|
216
|
-
AIGenerateImageParams,
|
|
217
|
-
AIGenerateImagePrompt,
|
|
218
|
-
AIGenerateImageResult,
|
|
219
|
-
AIGenerateSpeechParams,
|
|
220
|
-
AIGenerateSpeechResult,
|
|
221
|
-
AIProviderOptions,
|
|
222
|
-
AIRerankParams,
|
|
223
|
-
AIRerankResult,
|
|
224
|
-
AITranscriptionParams,
|
|
225
|
-
AITranscriptionResult,
|
|
226
197
|
} from './services/ai-agent-runner-service.js'
|
|
227
198
|
export type { AIEmbeddingService } from './services/ai-embedding-service.js'
|
|
228
199
|
export type { AIRunStateService } from './services/ai-run-state-service.js'
|
|
@@ -230,8 +201,6 @@ export type { AIStorageService } from './services/ai-storage-service.js'
|
|
|
230
201
|
export type {
|
|
231
202
|
EmailsMeta,
|
|
232
203
|
EmailTemplateMeta,
|
|
233
|
-
EmailTemplateLocaleMeta,
|
|
234
|
-
EmailTemplateAssets,
|
|
235
204
|
MetaService,
|
|
236
205
|
} from './services/meta-service.js'
|
|
237
206
|
export type { HTTPMethod } from './wirings/http/http.types.js'
|
|
@@ -266,7 +235,6 @@ export type {
|
|
|
266
235
|
Secret,
|
|
267
236
|
Classification,
|
|
268
237
|
AnonymizeStrategy,
|
|
269
|
-
ColumnClassification,
|
|
270
238
|
ClassificationManifest,
|
|
271
239
|
ColumnForm,
|
|
272
240
|
WrappedValue,
|
|
@@ -276,9 +244,6 @@ export type {
|
|
|
276
244
|
|
|
277
245
|
export {
|
|
278
246
|
hashToken,
|
|
279
|
-
unsafeAsWrapped,
|
|
280
|
-
unsafeAsSealed,
|
|
281
|
-
unsafeAsHashed,
|
|
282
247
|
} from './column-form.js'
|
|
283
248
|
|
|
284
249
|
export type { SecretValue, Safe } from './secret-value.js'
|
package/src/pikku-state.ts
CHANGED
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
MCPPromptMeta,
|
|
11
11
|
} from './wirings/mcp/mcp.types.js'
|
|
12
12
|
import type { AIAgentMeta } from './wirings/ai-agent/ai-agent.types.js'
|
|
13
|
+
import type { ScorerMeta } from './wirings/ai-scorer/ai-scorer.types.js'
|
|
13
14
|
import type { GatewaysMeta } from './wirings/gateway/gateway.types.js'
|
|
14
15
|
import type { ScheduledTasksMeta } from './wirings/scheduler/scheduler.types.js'
|
|
15
16
|
import type { TriggerMeta } from './wirings/trigger/trigger.types.js'
|
|
@@ -118,6 +119,9 @@ const createEmptyPackageState = (): PikkuPackageState => ({
|
|
|
118
119
|
agent: {
|
|
119
120
|
agents: new Map(),
|
|
120
121
|
agentsMeta: {} as AIAgentMeta,
|
|
122
|
+
scorers: new Map(),
|
|
123
|
+
scorersMeta: {} as ScorerMeta,
|
|
124
|
+
modelAliases: {},
|
|
121
125
|
},
|
|
122
126
|
gateway: {
|
|
123
127
|
gateways: new Map(),
|
|
@@ -151,6 +155,7 @@ const createEmptyPackageState = (): PikkuPackageState => ({
|
|
|
151
155
|
authFactory: null,
|
|
152
156
|
credentialsMeta: null,
|
|
153
157
|
requiredParentServices: null,
|
|
158
|
+
declaredSecrets: null,
|
|
154
159
|
},
|
|
155
160
|
})
|
|
156
161
|
|