@pikku/core 0.12.80 → 0.12.82
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 +312 -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 -15
- package/dist/services/index.js +5 -5
- 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 +0 -2
- package/dist/types/state.types.d.ts +13 -0
- 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 +2 -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 +4 -0
- package/dist/wirings/rpc/addon-runner.js +19 -3
- package/dist/wirings/rpc/rpc-runner.js +2 -0
- package/dist/wirings/rpc/rpc-types.d.ts +4 -0
- package/dist/wirings/rpc/wire-addon.d.ts +13 -0
- package/dist/wirings/rpc/wire-addon.js +4 -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/index.d.ts +6 -6
- package/dist/wirings/workflow/index.js +2 -2
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +7 -7
- package/dist/wirings/workflow/pikku-scenario-service.js +39 -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/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 +1 -1
- 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 -1
- 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 +70 -94
- 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 +3 -43
- 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 +3 -6
- package/src/types/state.types.ts +16 -0
- 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 +2 -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 +34 -3
- package/src/wirings/rpc/addon-secrets.test.ts +261 -0
- package/src/wirings/rpc/rpc-runner.test.ts +2 -0
- package/src/wirings/rpc/rpc-runner.ts +2 -0
- package/src/wirings/rpc/rpc-types.ts +4 -0
- package/src/wirings/rpc/wire-addon.ts +17 -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/graph/graph-runner.test.ts +72 -0
- package/src/wirings/workflow/index.ts +2 -20
- package/src/wirings/workflow/pikku-scenario-service.ts +60 -15
- 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/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 +0 -9
- 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/services/user-flow-actors-service.js +0 -1
- 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
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PikkuAIScorer,
|
|
3
|
+
ScorerInput,
|
|
4
|
+
ScorerOutput,
|
|
5
|
+
} from './ai-scorer.types.js'
|
|
6
|
+
|
|
7
|
+
const assertSampleRate = (name: string, sampleRate: number | undefined) => {
|
|
8
|
+
if (sampleRate === undefined) return 1
|
|
9
|
+
if (sampleRate < 0 || sampleRate > 1) {
|
|
10
|
+
throw new Error(
|
|
11
|
+
`Scorer '${name}' has a sampleRate of ${sampleRate} — it is a fraction of live runs to grade, so it must be between 0 and 1`
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
return sampleRate
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A heuristic scorer: pure code over the finished run, no model call, so it
|
|
19
|
+
* grades on the fast lane.
|
|
20
|
+
*/
|
|
21
|
+
export const pikkuAIScorer = <Services = any>(config: {
|
|
22
|
+
name: string
|
|
23
|
+
description: string
|
|
24
|
+
/** 0..1 fraction of live runs to grade. Defaults to all of them. */
|
|
25
|
+
sampleRate?: number
|
|
26
|
+
/**
|
|
27
|
+
* Grades against a known-correct answer. Such a scorer is test-only — live
|
|
28
|
+
* traffic has no answer key, so the runtime never samples it.
|
|
29
|
+
*/
|
|
30
|
+
requiresReference?: boolean
|
|
31
|
+
score: (
|
|
32
|
+
input: ScorerInput,
|
|
33
|
+
services: Services
|
|
34
|
+
) => ScorerOutput | Promise<ScorerOutput>
|
|
35
|
+
}): PikkuAIScorer<Services> => ({
|
|
36
|
+
name: config.name,
|
|
37
|
+
description: config.description,
|
|
38
|
+
lane: 'fast',
|
|
39
|
+
sampleRate: assertSampleRate(config.name, config.sampleRate),
|
|
40
|
+
requiresReference: config.requiresReference ?? false,
|
|
41
|
+
score: config.score,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* An LLM-judge scorer: the runtime makes the model call and forces a structured
|
|
46
|
+
* `{ score, reason }`, so a judge is a rubric rather than a prompt to parse.
|
|
47
|
+
*
|
|
48
|
+
* The rubric field is `goal`, matching `pikkuAIAgent`'s prompt vocabulary — a
|
|
49
|
+
* judge is a degenerate agent, and should use the same word for the same thing.
|
|
50
|
+
* `prompt` is the escape hatch for non-standard framing.
|
|
51
|
+
*/
|
|
52
|
+
export const pikkuAIJudge = <Services = any>(config: {
|
|
53
|
+
name: string
|
|
54
|
+
description: string
|
|
55
|
+
/** 0..1 fraction of live runs to grade. Defaults to all of them. */
|
|
56
|
+
sampleRate?: number
|
|
57
|
+
/**
|
|
58
|
+
* Grades against a known-correct answer. Such a judge is test-only — live
|
|
59
|
+
* traffic has no answer key, so the runtime never samples it.
|
|
60
|
+
*/
|
|
61
|
+
requiresReference?: boolean
|
|
62
|
+
model: string
|
|
63
|
+
goal: string
|
|
64
|
+
prompt?: (input: ScorerInput) => string
|
|
65
|
+
}): PikkuAIScorer<Services> => ({
|
|
66
|
+
name: config.name,
|
|
67
|
+
description: config.description,
|
|
68
|
+
lane: 'slow',
|
|
69
|
+
sampleRate: assertSampleRate(config.name, config.sampleRate),
|
|
70
|
+
requiresReference: config.requiresReference ?? false,
|
|
71
|
+
judge: {
|
|
72
|
+
model: config.model,
|
|
73
|
+
goal: config.goal,
|
|
74
|
+
...(config.prompt ? { prompt: config.prompt } : {}),
|
|
75
|
+
},
|
|
76
|
+
})
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a scorer is shown about a finished run.
|
|
3
|
+
*
|
|
4
|
+
* One snapshot, taken after the output middleware has resolved, so a scorer and
|
|
5
|
+
* the persisted run record see the same thing — including the redactions.
|
|
6
|
+
*/
|
|
7
|
+
export interface ScorerInput {
|
|
8
|
+
runId: string
|
|
9
|
+
agentName: string
|
|
10
|
+
threadId?: string
|
|
11
|
+
resourceId?: string
|
|
12
|
+
/** The user prompt the run answered. */
|
|
13
|
+
input: string
|
|
14
|
+
/** The agent's final text answer. */
|
|
15
|
+
output: string
|
|
16
|
+
/**
|
|
17
|
+
* The known-correct answer. Supplied by a scenario for a reference-based
|
|
18
|
+
* judge; never set on live traffic, which has no answer key.
|
|
19
|
+
*/
|
|
20
|
+
reference?: string
|
|
21
|
+
toolCalls: {
|
|
22
|
+
name: string
|
|
23
|
+
args: unknown
|
|
24
|
+
result?: unknown
|
|
25
|
+
error?: string
|
|
26
|
+
}[]
|
|
27
|
+
usage: {
|
|
28
|
+
inputTokens: number
|
|
29
|
+
outputTokens: number
|
|
30
|
+
model?: string
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ScorerOutput {
|
|
35
|
+
/** 0..1, so grades are comparable across scorers. */
|
|
36
|
+
score: number
|
|
37
|
+
reason?: string
|
|
38
|
+
metadata?: Record<string, unknown>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The queue a scorer's jobs go to. Two lanes exist only so a flood of slow
|
|
43
|
+
* LLM-judge jobs cannot starve the cheap heuristic ones; within a lane it is
|
|
44
|
+
* plain FIFO.
|
|
45
|
+
*/
|
|
46
|
+
export type ScorerLane = 'fast' | 'slow'
|
|
47
|
+
|
|
48
|
+
export const SCORER_LANE_QUEUES: Record<ScorerLane, string> = {
|
|
49
|
+
fast: 'ai-score-fast',
|
|
50
|
+
slow: 'ai-score-slow',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A judge's model call. Present only on a scorer built with `pikkuAIJudge`;
|
|
55
|
+
* `score` is present only on one built with `pikkuAIScorer`. Exactly one of the
|
|
56
|
+
* two is set, which is what the two constructors exist to guarantee.
|
|
57
|
+
*/
|
|
58
|
+
export type ScorerJudgeConfig = {
|
|
59
|
+
model: string
|
|
60
|
+
goal: string
|
|
61
|
+
prompt?: (input: ScorerInput) => string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type PikkuAIScorer<Services = any> = {
|
|
65
|
+
name: string
|
|
66
|
+
description: string
|
|
67
|
+
lane: ScorerLane
|
|
68
|
+
/** 0..1 fraction of live runs to grade. */
|
|
69
|
+
sampleRate: number
|
|
70
|
+
/**
|
|
71
|
+
* Grades against a known-correct answer, so it is test-only: live traffic has
|
|
72
|
+
* no answer key and the runtime never samples it.
|
|
73
|
+
*/
|
|
74
|
+
requiresReference: boolean
|
|
75
|
+
score?: (
|
|
76
|
+
input: ScorerInput,
|
|
77
|
+
services: Services
|
|
78
|
+
) => ScorerOutput | Promise<ScorerOutput>
|
|
79
|
+
judge?: ScorerJudgeConfig
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export type ScorerMeta = Record<
|
|
83
|
+
string,
|
|
84
|
+
{
|
|
85
|
+
name: string
|
|
86
|
+
description: string
|
|
87
|
+
lane: ScorerLane
|
|
88
|
+
sampleRate: number
|
|
89
|
+
requiresReference: boolean
|
|
90
|
+
sourceFile?: string
|
|
91
|
+
exportedName?: string
|
|
92
|
+
}
|
|
93
|
+
>
|
|
94
|
+
|
|
95
|
+
/** A single scorer's job on a lane queue. */
|
|
96
|
+
export type ScoreJob = ScorerInput & {
|
|
97
|
+
scorerName: string
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export type AIRunScore = {
|
|
101
|
+
runId: string
|
|
102
|
+
scorerName: string
|
|
103
|
+
score: number
|
|
104
|
+
reason?: string
|
|
105
|
+
metadata?: Record<string, unknown>
|
|
106
|
+
createdAt: Date
|
|
107
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export { pikkuAIScorer, pikkuAIJudge } from './ai-scorer.js'
|
|
2
|
+
export {
|
|
3
|
+
addAIScorer,
|
|
4
|
+
getAIScorers,
|
|
5
|
+
getAIScorersMeta,
|
|
6
|
+
} from './ai-scorer-registry.js'
|
|
7
|
+
export { gradeRun } from './ai-scorer-grade.js'
|
|
8
|
+
export {
|
|
9
|
+
enableScoreSnapshots,
|
|
10
|
+
getScoreSnapshot,
|
|
11
|
+
} from './ai-scorer-snapshots.js'
|
|
12
|
+
export {
|
|
13
|
+
wireAIScorerQueueWorkers,
|
|
14
|
+
} from './ai-scorer-worker.js'
|
|
15
|
+
export type {
|
|
16
|
+
AIRunScore,
|
|
17
|
+
PikkuAIScorer,
|
|
18
|
+
ScoreJob,
|
|
19
|
+
ScorerInput,
|
|
20
|
+
ScorerJudgeConfig,
|
|
21
|
+
ScorerLane,
|
|
22
|
+
ScorerMeta,
|
|
23
|
+
ScorerOutput,
|
|
24
|
+
} from './ai-scorer.types.js'
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
export { wireChannel
|
|
1
|
+
export { wireChannel } from './channel-runner.js'
|
|
2
2
|
export {
|
|
3
3
|
addChannelMiddleware,
|
|
4
4
|
combineChannelMiddleware,
|
|
5
|
-
wrapChannelWithMiddleware,
|
|
6
5
|
} from './channel-middleware-runner.js'
|
|
7
6
|
export { logChannels } from './log-channels.js'
|
|
8
7
|
export { PikkuAbstractChannelHandler } from './pikku-abstract-channel-handler.js'
|
|
@@ -18,25 +17,14 @@ export type {
|
|
|
18
17
|
CorePikkuChannelMiddlewareFactory,
|
|
19
18
|
ChannelMessageMeta,
|
|
20
19
|
ChannelMeta,
|
|
21
|
-
ChannelRemote,
|
|
22
20
|
PikkuChannel,
|
|
23
21
|
PikkuChannelHandlerFactory,
|
|
24
22
|
} from './channel.types.js'
|
|
25
23
|
export { defineChannelRoutes } from './define-channel-routes.js'
|
|
26
24
|
export {
|
|
27
|
-
CHANNEL_RPC_PENDING,
|
|
28
|
-
CHANNEL_RPC_REQUEST,
|
|
29
|
-
CHANNEL_RPC_RESPONSE,
|
|
30
25
|
ChannelDeploymentService,
|
|
31
26
|
ChannelRPCError,
|
|
32
27
|
ChannelRPCRegistry,
|
|
33
|
-
createChannelRPCResponder,
|
|
34
|
-
createChannelRPCInputValidator,
|
|
35
|
-
createChannelRPCResultValidator,
|
|
36
|
-
isChannelRPCPending,
|
|
37
|
-
isChannelRPCRequest,
|
|
38
|
-
isChannelRPCResponse,
|
|
39
|
-
resolveCapability,
|
|
40
28
|
unsupportedChannelRemote,
|
|
41
29
|
} from './channel-rpc.js'
|
|
42
30
|
export type {
|
|
@@ -44,16 +32,9 @@ export type {
|
|
|
44
32
|
ApprovalRequester,
|
|
45
33
|
Capabilities,
|
|
46
34
|
Capability,
|
|
47
|
-
CapabilityDef,
|
|
48
35
|
CapabilityHandler,
|
|
49
36
|
ChannelRPCPending,
|
|
50
37
|
ChannelRPCRequest,
|
|
51
38
|
ChannelRPCResponse,
|
|
52
39
|
ChannelRPCValidator,
|
|
53
40
|
} from './channel-rpc.js'
|
|
54
|
-
export {
|
|
55
|
-
channelRemote,
|
|
56
|
-
getChannelHostRPC,
|
|
57
|
-
handleChannelRPCResponse,
|
|
58
|
-
releaseChannelHostRPC,
|
|
59
|
-
} from './channel-host-rpc.js'
|
|
@@ -3,6 +3,7 @@ import * as assert from 'node:assert/strict'
|
|
|
3
3
|
import { runLocalChannel } from './local-channel-runner.js'
|
|
4
4
|
import { pikkuState, resetPikkuState } from '../../../pikku-state.js'
|
|
5
5
|
import { wireChannel } from '../channel-runner.js'
|
|
6
|
+
import { addFunction } from '../../../function/function-runner.js'
|
|
6
7
|
import { addHTTPMiddleware } from '../../http/http-runner.js'
|
|
7
8
|
import type {
|
|
8
9
|
HTTPMethod,
|
|
@@ -270,3 +271,70 @@ test('runChannel should run HTTP middleware on websocket upgrade and establish s
|
|
|
270
271
|
await result.message(JSON.stringify({ action: 'ping' }))
|
|
271
272
|
assert.deepEqual(sent, { action: 'ping' })
|
|
272
273
|
})
|
|
274
|
+
|
|
275
|
+
test('a message handler that returns nothing does not attempt to send', async () => {
|
|
276
|
+
// The connect path already guards this; the message path did not, so a handler
|
|
277
|
+
// with nothing to say produced `send requires a non-empty message` on every
|
|
278
|
+
// message. Gateway websockets hit it every time — their generated message
|
|
279
|
+
// handler returns undefined by design — which is why a chat gateway could
|
|
280
|
+
// accept a connection and never deliver anything to its handler.
|
|
281
|
+
resetPikkuState()
|
|
282
|
+
pikkuState(null, 'package', 'singletonServices', mockSingletonServices as any)
|
|
283
|
+
pikkuState(null, 'package', 'factories', {
|
|
284
|
+
createWireServices: mockCreateWireServices,
|
|
285
|
+
} as any)
|
|
286
|
+
|
|
287
|
+
const sent: unknown[] = []
|
|
288
|
+
let handled = 0
|
|
289
|
+
|
|
290
|
+
pikkuState(null, 'channel', 'meta', {
|
|
291
|
+
quiet: {
|
|
292
|
+
name: 'quiet',
|
|
293
|
+
route: '/quiet-channel',
|
|
294
|
+
message: { pikkuFuncId: 'quietMessage' },
|
|
295
|
+
messageWirings: {},
|
|
296
|
+
},
|
|
297
|
+
} as any)
|
|
298
|
+
pikkuState(null, 'function', 'meta')['quietMessage'] = {
|
|
299
|
+
pikkuFuncId: 'quietMessage',
|
|
300
|
+
inputSchemaName: null,
|
|
301
|
+
outputSchemaName: null,
|
|
302
|
+
sessionless: true,
|
|
303
|
+
} as any
|
|
304
|
+
const quietMessage = {
|
|
305
|
+
auth: false,
|
|
306
|
+
func: async () => {
|
|
307
|
+
handled++
|
|
308
|
+
return undefined
|
|
309
|
+
},
|
|
310
|
+
}
|
|
311
|
+
wireChannel({
|
|
312
|
+
name: 'quiet',
|
|
313
|
+
route: '/quiet-channel',
|
|
314
|
+
auth: false,
|
|
315
|
+
onMessage: quietMessage as any,
|
|
316
|
+
} as any)
|
|
317
|
+
|
|
318
|
+
httpRouter.initialize()
|
|
319
|
+
|
|
320
|
+
const handler = await runLocalChannel({
|
|
321
|
+
channelId: 'quiet-channel-id',
|
|
322
|
+
request: new PikkuMockRequest('/quiet-channel', 'get'),
|
|
323
|
+
response: new PikkuMockResponse(),
|
|
324
|
+
route: '/quiet-channel',
|
|
325
|
+
})
|
|
326
|
+
assert.ok(handler)
|
|
327
|
+
handler.registerOnSend(async (message: unknown) => {
|
|
328
|
+
sent.push(message)
|
|
329
|
+
})
|
|
330
|
+
handler.open()
|
|
331
|
+
|
|
332
|
+
await handler.message(JSON.stringify({ text: 'hello' }))
|
|
333
|
+
|
|
334
|
+
assert.equal(handled, 1, 'the handler should have run')
|
|
335
|
+
assert.deepEqual(
|
|
336
|
+
sent,
|
|
337
|
+
[],
|
|
338
|
+
'nothing should have been sent for an empty result'
|
|
339
|
+
)
|
|
340
|
+
})
|
|
@@ -213,7 +213,14 @@ export const runLocalChannel = async ({
|
|
|
213
213
|
channelHandler.registerOnMessage(async (data) => {
|
|
214
214
|
try {
|
|
215
215
|
const result = await onMessage(data)
|
|
216
|
-
|
|
216
|
+
// Guarded exactly as the connect path above is. A handler with nothing
|
|
217
|
+
// to say is normal — a gateway websocket's generated message handler
|
|
218
|
+
// always returns undefined — and sending that raised `send requires a
|
|
219
|
+
// non-empty message`, which surfaced as a channel that accepted a
|
|
220
|
+
// connection and then silently delivered nothing.
|
|
221
|
+
if (result !== undefined) {
|
|
222
|
+
await channel.send(result)
|
|
223
|
+
}
|
|
217
224
|
} catch (e: any) {
|
|
218
225
|
singletonServices.logger.error(e)
|
|
219
226
|
const errorResponse = getErrorResponse(e)
|
|
@@ -103,6 +103,29 @@ describe('handleRawCLI', () => {
|
|
|
103
103
|
assert.match(result.error!, /deploy plan is red/)
|
|
104
104
|
})
|
|
105
105
|
|
|
106
|
+
test('masks the raw error message to the client in production', async () => {
|
|
107
|
+
const prior = process.env.NODE_ENV
|
|
108
|
+
process.env.NODE_ENV = 'production'
|
|
109
|
+
try {
|
|
110
|
+
wireTestCLI(() => {
|
|
111
|
+
throw new Error('connection to internal-db:5432 failed: bad password')
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
const result = await handleRawCLI({
|
|
115
|
+
programName: 'fabric',
|
|
116
|
+
args: ['deploy'],
|
|
117
|
+
singletonServices,
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
assert.equal(result.exitCode, 1)
|
|
121
|
+
assert.equal(result.error, 'Command failed')
|
|
122
|
+
assert.doesNotMatch(result.error!, /internal-db|password/)
|
|
123
|
+
} finally {
|
|
124
|
+
if (prior === undefined) delete process.env.NODE_ENV
|
|
125
|
+
else process.env.NODE_ENV = prior
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
|
|
106
129
|
test('exits 0 on help and 1 on an unknown command', async () => {
|
|
107
130
|
wireTestCLI(() => ({}))
|
|
108
131
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { pikkuState } from '../../../pikku-state.js'
|
|
2
|
+
import { isProduction } from '../../../env.js'
|
|
2
3
|
import type { CLIMeta } from '../cli.types.js'
|
|
3
4
|
import { parseCLIArguments, generateCommandHelp } from '../command-parser.js'
|
|
4
5
|
import { runCLICommand } from '../cli-runner.js'
|
|
@@ -132,6 +133,16 @@ export async function handleRawCLI({
|
|
|
132
133
|
// A command can throw anything; `.message` off a string is undefined, and
|
|
133
134
|
// the run would then exit 1 saying nothing at all.
|
|
134
135
|
const message = e instanceof Error ? e.message : String(e)
|
|
135
|
-
|
|
136
|
+
// The raw message can carry internals (a stack, a DB error, a path). This
|
|
137
|
+
// runs over a channel that may be remote, so return a generic error in
|
|
138
|
+
// production and keep the detail server-side. Dev keeps the message inline.
|
|
139
|
+
singletonServices.logger.error?.(
|
|
140
|
+
`CLI channel command failed: ${message}`,
|
|
141
|
+
e
|
|
142
|
+
)
|
|
143
|
+
const clientError = isProduction()
|
|
144
|
+
? 'Command failed'
|
|
145
|
+
: message || 'Command failed'
|
|
146
|
+
return { error: clientError, exitCode: 1, commandId }
|
|
136
147
|
}
|
|
137
148
|
}
|
|
@@ -3,13 +3,6 @@ export { handleRawCLI } from './cli-raw-channel-runner.js'
|
|
|
3
3
|
export type { RawCLIFrame, RawCLIResult } from './cli-raw-channel-runner.js'
|
|
4
4
|
export { executeRawCLIViaChannel } from './cli-raw-client-runner.js'
|
|
5
5
|
export type {
|
|
6
|
-
ClientCLIRenderServices,
|
|
7
6
|
CorePikkuCLIClientRender,
|
|
8
7
|
} from './cli-raw-client-runner.js'
|
|
9
|
-
export {
|
|
10
|
-
APPROVAL_FLAGS,
|
|
11
|
-
approverForMode,
|
|
12
|
-
createTerminalApprover,
|
|
13
|
-
takeApprovalFlags,
|
|
14
|
-
} from './cli-approval.js'
|
|
15
8
|
export type { ApprovalMode } from './cli-approval.js'
|
|
@@ -396,6 +396,74 @@ describe('CLI Runner', () => {
|
|
|
396
396
|
const programs = pikkuState(null, 'cli', 'programs')
|
|
397
397
|
assert.strictEqual(programs['nonexistent'], undefined)
|
|
398
398
|
})
|
|
399
|
+
|
|
400
|
+
test('enforces command-level permissions declared on a CLI command', async () => {
|
|
401
|
+
let functionRan = false
|
|
402
|
+
|
|
403
|
+
pikkuState(null, 'cli', 'meta', {
|
|
404
|
+
programs: {
|
|
405
|
+
'sec-cli': {
|
|
406
|
+
program: 'sec-cli',
|
|
407
|
+
commands: {
|
|
408
|
+
secret: {
|
|
409
|
+
command: 'secret',
|
|
410
|
+
pikkuFuncId: 'secretFunc',
|
|
411
|
+
positionals: [],
|
|
412
|
+
options: {},
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
options: {},
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
renderers: {},
|
|
419
|
+
})
|
|
420
|
+
pikkuState(null, 'function', 'meta', {
|
|
421
|
+
secretFunc: {
|
|
422
|
+
pikkuFuncId: 'secretFunc',
|
|
423
|
+
inputSchemaName: null,
|
|
424
|
+
outputSchemaName: null,
|
|
425
|
+
sessionless: true,
|
|
426
|
+
},
|
|
427
|
+
})
|
|
428
|
+
|
|
429
|
+
wireCLI({
|
|
430
|
+
program: 'sec-cli',
|
|
431
|
+
commands: {
|
|
432
|
+
// command.func is a function-config object, so unwrapFunc reads the
|
|
433
|
+
// inner func's auth/permissions and previously dropped the
|
|
434
|
+
// command-level ones — the branch this fix restores.
|
|
435
|
+
secret: {
|
|
436
|
+
func: {
|
|
437
|
+
func: async () => {
|
|
438
|
+
functionRan = true
|
|
439
|
+
return {}
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
// A permission that always denies.
|
|
443
|
+
permissions: { deny: async () => false },
|
|
444
|
+
} as any,
|
|
445
|
+
},
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
const result = await runCLICommand({
|
|
449
|
+
program: 'sec-cli',
|
|
450
|
+
commandPath: ['secret'],
|
|
451
|
+
data: {},
|
|
452
|
+
singletonServices,
|
|
453
|
+
createWireServices,
|
|
454
|
+
}).catch((error) => ({ success: false, error }))
|
|
455
|
+
|
|
456
|
+
assert.equal(
|
|
457
|
+
functionRan,
|
|
458
|
+
false,
|
|
459
|
+
'the function must not run when a declared permission denies'
|
|
460
|
+
)
|
|
461
|
+
assert.notEqual(
|
|
462
|
+
(result as any).success,
|
|
463
|
+
true,
|
|
464
|
+
'the command must not report success when permission is denied'
|
|
465
|
+
)
|
|
466
|
+
})
|
|
399
467
|
})
|
|
400
468
|
|
|
401
469
|
describe('pikkuCLIRender', () => {
|
|
@@ -169,7 +169,24 @@ function registerCLICommands(
|
|
|
169
169
|
}
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
|
|
172
|
+
// Merge the command-level auth/permissions into the registered config so
|
|
173
|
+
// they are actually enforced by the function runner. They were accepted by
|
|
174
|
+
// the types but dropped here, making a command's declared access control a
|
|
175
|
+
// silent no-op. Command-level wins over the handler's own, then falls back
|
|
176
|
+
// to it.
|
|
177
|
+
const unwrapped = unwrapFunc(command)
|
|
178
|
+
const commandAuth = typeof command === 'object' ? command.auth : undefined
|
|
179
|
+
const commandPermissions =
|
|
180
|
+
typeof command === 'object' ? command.permissions : undefined
|
|
181
|
+
addFunction(
|
|
182
|
+
funcName,
|
|
183
|
+
{
|
|
184
|
+
...unwrapped,
|
|
185
|
+
auth: commandAuth ?? unwrapped.auth,
|
|
186
|
+
permissions: commandPermissions ?? unwrapped.permissions,
|
|
187
|
+
},
|
|
188
|
+
currentMeta?.packageName
|
|
189
|
+
)
|
|
173
190
|
|
|
174
191
|
if (typeof command === 'object' && command.render) {
|
|
175
192
|
if (programs[program]) {
|
|
@@ -2,7 +2,6 @@ export { defineCredential } from './define-credential.js'
|
|
|
2
2
|
export { validateAndBuildCredentialDefinitionsMeta } from './validate-credential-definitions.js'
|
|
3
3
|
export type {
|
|
4
4
|
CoreCredential,
|
|
5
|
-
CredentialDefinitionMeta,
|
|
6
5
|
CredentialDefinitionsMeta,
|
|
7
6
|
CredentialDefinitions,
|
|
8
7
|
} from './credential.types.js'
|
|
@@ -6,13 +6,10 @@ export {
|
|
|
6
6
|
export type {
|
|
7
7
|
GatewayAdapter,
|
|
8
8
|
GatewayAdapterFactory,
|
|
9
|
-
GatewayAttachment,
|
|
10
9
|
GatewayInboundMessage,
|
|
11
10
|
GatewayOutboundMessage,
|
|
12
|
-
GatewayMeta,
|
|
13
11
|
GatewaysMeta,
|
|
14
12
|
GatewayTransportType,
|
|
15
13
|
CoreGateway,
|
|
16
|
-
PikkuGateway,
|
|
17
14
|
WebhookVerificationResult,
|
|
18
15
|
} from './gateway.types.js'
|
|
@@ -70,6 +70,11 @@ class TestResponse extends PikkuMockResponse {
|
|
|
70
70
|
return this
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
redirect(location: string, status: number = 302): this {
|
|
74
|
+
this.status(status)
|
|
75
|
+
return this.header('Location', location)
|
|
76
|
+
}
|
|
77
|
+
|
|
73
78
|
setMode(mode: 'stream') {
|
|
74
79
|
this.mode = mode
|
|
75
80
|
}
|
|
@@ -365,6 +370,67 @@ describe('http-runner helpers', () => {
|
|
|
365
370
|
assert.equal(response.statusCode, 204)
|
|
366
371
|
})
|
|
367
372
|
|
|
373
|
+
test('fetchData keeps a redirect status when the route returns undefined', async () => {
|
|
374
|
+
setRouteMeta('/redirect')
|
|
375
|
+
addFunction('pikku_func_name', {
|
|
376
|
+
func: async (_services: any, _data: any, { http }: any) => {
|
|
377
|
+
http?.response?.redirect('https://example.com/done', 302)
|
|
378
|
+
},
|
|
379
|
+
})
|
|
380
|
+
wireHTTP({
|
|
381
|
+
route: '/redirect',
|
|
382
|
+
method: 'get',
|
|
383
|
+
auth: false,
|
|
384
|
+
func: {
|
|
385
|
+
func: async (_services: any, _data: any, { http }: any) => {
|
|
386
|
+
http?.response?.redirect('https://example.com/done', 302)
|
|
387
|
+
},
|
|
388
|
+
},
|
|
389
|
+
})
|
|
390
|
+
httpRouter.initialize()
|
|
391
|
+
|
|
392
|
+
const request = new TestRequest('/redirect', 'get')
|
|
393
|
+
const response = new TestResponse()
|
|
394
|
+
|
|
395
|
+
await fetchData(request, response)
|
|
396
|
+
|
|
397
|
+
assert.equal(response.statusCode, 302)
|
|
398
|
+
assert.equal(
|
|
399
|
+
response.headersMap.get('Location'),
|
|
400
|
+
'https://example.com/done'
|
|
401
|
+
)
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
test('fetchData keeps a status the route set alongside a body', async () => {
|
|
405
|
+
setRouteMeta('/created')
|
|
406
|
+
addFunction('pikku_func_name', {
|
|
407
|
+
func: async (_services: any, _data: any, { http }: any) => {
|
|
408
|
+
http?.response?.status(201)
|
|
409
|
+
return { id: 'thing-1' }
|
|
410
|
+
},
|
|
411
|
+
})
|
|
412
|
+
wireHTTP({
|
|
413
|
+
route: '/created',
|
|
414
|
+
method: 'get',
|
|
415
|
+
auth: false,
|
|
416
|
+
func: {
|
|
417
|
+
func: async (_services: any, _data: any, { http }: any) => {
|
|
418
|
+
http?.response?.status(201)
|
|
419
|
+
return { id: 'thing-1' }
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
})
|
|
423
|
+
httpRouter.initialize()
|
|
424
|
+
|
|
425
|
+
const request = new TestRequest('/created', 'get')
|
|
426
|
+
const response = new TestResponse()
|
|
427
|
+
|
|
428
|
+
await fetchData(request, response)
|
|
429
|
+
|
|
430
|
+
assert.equal(response.statusCode, 201)
|
|
431
|
+
assert.deepEqual(response.jsonBody, { id: 'thing-1' })
|
|
432
|
+
})
|
|
433
|
+
|
|
368
434
|
test('fetchData writes binary responses when returnsJSON is false', async () => {
|
|
369
435
|
setRouteMeta('/binary')
|
|
370
436
|
wireHTTP({
|
|
@@ -283,6 +283,8 @@ const executeRoute = async (
|
|
|
283
283
|
hasSessionChanged: () => userSession.sessionChanged,
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
+
const statusBeforeRoute = http?.response?.statusCode
|
|
287
|
+
|
|
286
288
|
try {
|
|
287
289
|
result = await runPikkuFunc(
|
|
288
290
|
'http',
|
|
@@ -323,14 +325,20 @@ const executeRoute = async (
|
|
|
323
325
|
if (matchedRoute.route.sse) {
|
|
324
326
|
http?.response?.flushHeaders?.()
|
|
325
327
|
} else {
|
|
328
|
+
const statusSetByRoute = http?.response?.statusCode !== statusBeforeRoute
|
|
329
|
+
|
|
326
330
|
if (result instanceof Response) {
|
|
327
331
|
await applyWebResponse(http!.response!, result)
|
|
328
332
|
} else if (result === undefined || result === null) {
|
|
329
|
-
|
|
333
|
+
if (!statusSetByRoute) {
|
|
334
|
+
http?.response?.status(204)
|
|
335
|
+
}
|
|
330
336
|
} else if (route.returnsJSON === false) {
|
|
331
337
|
http?.response?.arrayBuffer(result)
|
|
332
338
|
} else {
|
|
333
|
-
|
|
339
|
+
if (!statusSetByRoute) {
|
|
340
|
+
http?.response?.status(200)
|
|
341
|
+
}
|
|
334
342
|
http?.response?.json(result)
|
|
335
343
|
}
|
|
336
344
|
}
|
|
@@ -2,7 +2,6 @@ export {
|
|
|
2
2
|
PikkuFetchHTTPRequest,
|
|
3
3
|
DEFAULT_MAX_BODY_SIZE,
|
|
4
4
|
} from './pikku-fetch-http-request.js'
|
|
5
|
-
export type { PikkuFetchHTTPRequestOptions } from './pikku-fetch-http-request.js'
|
|
6
5
|
export { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js'
|
|
7
6
|
export { logRoutes } from './log-http-routes.js'
|
|
8
7
|
|
|
@@ -19,6 +18,7 @@ export type {
|
|
|
19
18
|
HTTPRouteContract,
|
|
20
19
|
HTTPRouteMap,
|
|
21
20
|
HTTPWiringsMeta,
|
|
21
|
+
PikkuHTTP,
|
|
22
22
|
PikkuHTTPRequest,
|
|
23
23
|
PikkuHTTPResponse,
|
|
24
24
|
PikkuQuery,
|