@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
|
@@ -4,6 +4,8 @@ import type {
|
|
|
4
4
|
CoreSingletonServices,
|
|
5
5
|
} from '../../types/core.types.js'
|
|
6
6
|
import type { SecretService } from '../../services/secret-service.js'
|
|
7
|
+
import { ScopedSecretService } from '../../services/scoped-secret-service.js'
|
|
8
|
+
import { ScopedCredentialService } from '../../services/scoped-credential-service.js'
|
|
7
9
|
import type { VariablesService } from '../../services/variables-service.js'
|
|
8
10
|
|
|
9
11
|
export type AddonInstance = {
|
|
@@ -11,8 +13,32 @@ export type AddonInstance = {
|
|
|
11
13
|
secretOverrides?: Record<string, string>
|
|
12
14
|
variableOverrides?: Record<string, string>
|
|
13
15
|
credentialOverrides?: Record<string, string>
|
|
16
|
+
/** Set by the consuming app: secrets it lends this instance, as the addon names them. */
|
|
17
|
+
secretGrants?: string[]
|
|
18
|
+
/** Set by the consuming app: credentials it lends this instance, as the addon names them. */
|
|
19
|
+
credentialGrants?: string[]
|
|
20
|
+
/** Set by the consuming app to opt this instance out of secret scoping. */
|
|
21
|
+
globalSecrets?: string
|
|
22
|
+
/** Set by the consuming app to opt this instance out of credential scoping. */
|
|
23
|
+
globalCredentials?: string
|
|
14
24
|
}
|
|
15
25
|
|
|
26
|
+
/**
|
|
27
|
+
* What the addon declared, plus what the host lent it. Scoping runs outside the
|
|
28
|
+
* aliaser, so every name here is the one the addon reads — which is why an
|
|
29
|
+
* override's *key* grants, and its value does not.
|
|
30
|
+
*/
|
|
31
|
+
const allowedNames = (
|
|
32
|
+
declared: string[] | null | undefined,
|
|
33
|
+
grants: string[] | undefined,
|
|
34
|
+
overrides: Record<string, string> | undefined
|
|
35
|
+
): Set<string> =>
|
|
36
|
+
new Set([
|
|
37
|
+
...(declared ?? []),
|
|
38
|
+
...(grants ?? []),
|
|
39
|
+
...Object.keys(overrides ?? {}),
|
|
40
|
+
])
|
|
41
|
+
|
|
16
42
|
const aliasSecretService = (
|
|
17
43
|
secrets: SecretService,
|
|
18
44
|
overrides: Record<string, string>
|
|
@@ -111,9 +137,6 @@ export const getOrCreatePackageSingletonServices = async (
|
|
|
111
137
|
}
|
|
112
138
|
|
|
113
139
|
const factories = pikkuState(packageName, 'package', 'factories')
|
|
114
|
-
if (!factories || !factories.createSingletonServices) {
|
|
115
|
-
return parentServices
|
|
116
|
-
}
|
|
117
140
|
|
|
118
141
|
let existingServices = parentServices
|
|
119
142
|
if (addonInstance?.secretOverrides && parentServices.secrets) {
|
|
@@ -134,6 +157,38 @@ export const getOrCreatePackageSingletonServices = async (
|
|
|
134
157
|
),
|
|
135
158
|
}
|
|
136
159
|
}
|
|
160
|
+
if (!addonInstance?.globalSecrets && existingServices.secrets) {
|
|
161
|
+
existingServices = {
|
|
162
|
+
...existingServices,
|
|
163
|
+
secrets: new ScopedSecretService(
|
|
164
|
+
existingServices.secrets,
|
|
165
|
+
allowedNames(
|
|
166
|
+
pikkuState(packageName, 'package', 'declaredSecrets'),
|
|
167
|
+
addonInstance?.secretGrants,
|
|
168
|
+
addonInstance?.secretOverrides
|
|
169
|
+
)
|
|
170
|
+
),
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (!addonInstance?.globalCredentials && existingServices.credentialService) {
|
|
174
|
+
existingServices = {
|
|
175
|
+
...existingServices,
|
|
176
|
+
credentialService: new ScopedCredentialService(
|
|
177
|
+
existingServices.credentialService,
|
|
178
|
+
allowedNames(
|
|
179
|
+
Object.keys(
|
|
180
|
+
pikkuState(packageName, 'package', 'credentialsMeta') ?? {}
|
|
181
|
+
),
|
|
182
|
+
addonInstance?.credentialGrants,
|
|
183
|
+
addonInstance?.credentialOverrides
|
|
184
|
+
)
|
|
185
|
+
),
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!factories || !factories.createSingletonServices) {
|
|
190
|
+
return existingServices
|
|
191
|
+
}
|
|
137
192
|
|
|
138
193
|
let config: CoreConfig = existingServices.config
|
|
139
194
|
if (factories.createConfig) {
|
|
@@ -173,5 +228,9 @@ export const addonInstanceForNamespace = (
|
|
|
173
228
|
secretOverrides: cfg.secretOverrides,
|
|
174
229
|
variableOverrides: cfg.variableOverrides,
|
|
175
230
|
credentialOverrides: cfg.credentialOverrides,
|
|
231
|
+
secretGrants: cfg.secretGrants,
|
|
232
|
+
credentialGrants: cfg.credentialGrants,
|
|
233
|
+
globalSecrets: cfg.globalSecrets,
|
|
234
|
+
globalCredentials: cfg.globalCredentials,
|
|
176
235
|
}
|
|
177
236
|
}
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { beforeEach, describe, test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { pikkuState, resetPikkuState } from '../../pikku-state.js'
|
|
5
|
+
import { getOrCreatePackageSingletonServices } from './addon-runner.js'
|
|
6
|
+
import { wireAddon } from './wire-addon.js'
|
|
7
|
+
import type { SecretService } from '../../services/secret-service.js'
|
|
8
|
+
import type { CredentialService } from '../../services/credential-service.js'
|
|
9
|
+
import type { CoreSingletonServices } from '../../types/core.types.js'
|
|
10
|
+
|
|
11
|
+
const ADDON_PACKAGE = '@addon/example'
|
|
12
|
+
|
|
13
|
+
const createSecretService = (): SecretService =>
|
|
14
|
+
({
|
|
15
|
+
getSecret: async (key: string) => `value-of-${key}` as never,
|
|
16
|
+
hasSecret: async () => true,
|
|
17
|
+
setSecret: async () => {},
|
|
18
|
+
deleteSecret: async () => {},
|
|
19
|
+
getSecrets: async (keys: string[]) =>
|
|
20
|
+
Object.fromEntries(keys.map((k) => [k, `value-of-${k}`])) as never,
|
|
21
|
+
}) as SecretService
|
|
22
|
+
|
|
23
|
+
const createCredentialService = (): CredentialService =>
|
|
24
|
+
({
|
|
25
|
+
get: async (name: string) => `credential-of-${name}`,
|
|
26
|
+
set: async () => {},
|
|
27
|
+
delete: async () => {},
|
|
28
|
+
has: async () => true,
|
|
29
|
+
getAll: async () => ({ slack: 'a', stripe: 'b' }),
|
|
30
|
+
getUsersWithCredential: async () => ['user-1'],
|
|
31
|
+
getAllUsers: async () => ['user-1'],
|
|
32
|
+
}) as unknown as CredentialService
|
|
33
|
+
|
|
34
|
+
const createParentServices = () =>
|
|
35
|
+
({
|
|
36
|
+
config: {},
|
|
37
|
+
logger: {
|
|
38
|
+
debug: () => {},
|
|
39
|
+
info: () => {},
|
|
40
|
+
warn: () => {},
|
|
41
|
+
error: () => {},
|
|
42
|
+
},
|
|
43
|
+
secrets: createSecretService(),
|
|
44
|
+
credentialService: createCredentialService(),
|
|
45
|
+
}) as unknown as CoreSingletonServices
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Registers an addon package whose singleton factory hands back whatever
|
|
49
|
+
* secrets service the host decided it should see.
|
|
50
|
+
*/
|
|
51
|
+
const registerAddon = (
|
|
52
|
+
declaredSecrets: string[] | null,
|
|
53
|
+
declaredCredentials?: string[]
|
|
54
|
+
) => {
|
|
55
|
+
pikkuState(ADDON_PACKAGE, 'package', 'factories', {
|
|
56
|
+
createSingletonServices: (async (_config: unknown, parent: any) => ({
|
|
57
|
+
...parent,
|
|
58
|
+
secretsSeenByAddon: parent.secrets,
|
|
59
|
+
credentialsSeenByAddon: parent.credentialService,
|
|
60
|
+
})) as never,
|
|
61
|
+
})
|
|
62
|
+
if (declaredSecrets) {
|
|
63
|
+
pikkuState(ADDON_PACKAGE, 'package', 'declaredSecrets', declaredSecrets)
|
|
64
|
+
}
|
|
65
|
+
if (declaredCredentials) {
|
|
66
|
+
pikkuState(
|
|
67
|
+
ADDON_PACKAGE,
|
|
68
|
+
'package',
|
|
69
|
+
'credentialsMeta',
|
|
70
|
+
Object.fromEntries(
|
|
71
|
+
declaredCredentials.map((name) => [
|
|
72
|
+
name,
|
|
73
|
+
{ name, displayName: name, type: 'singleton' },
|
|
74
|
+
])
|
|
75
|
+
)
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const servicesHandedToAddon = async (namespace: string) =>
|
|
81
|
+
(await getOrCreatePackageSingletonServices(
|
|
82
|
+
ADDON_PACKAGE,
|
|
83
|
+
createParentServices(),
|
|
84
|
+
pikkuState(null, 'addons', 'packages').get(namespace) as never
|
|
85
|
+
)) as any
|
|
86
|
+
|
|
87
|
+
const secretsHandedToAddon = async (namespace: string) =>
|
|
88
|
+
(await servicesHandedToAddon(namespace)).secretsSeenByAddon as SecretService
|
|
89
|
+
|
|
90
|
+
const credentialsHandedToAddon = async (namespace: string) =>
|
|
91
|
+
(await servicesHandedToAddon(namespace))
|
|
92
|
+
.credentialsSeenByAddon as CredentialService
|
|
93
|
+
|
|
94
|
+
describe('addon secrets are scoped to what the addon declared', () => {
|
|
95
|
+
beforeEach(() => {
|
|
96
|
+
resetPikkuState()
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
test('a declared secret is readable', async () => {
|
|
100
|
+
registerAddon(['ADDON_KEY'])
|
|
101
|
+
wireAddon({
|
|
102
|
+
name: 'example',
|
|
103
|
+
package: ADDON_PACKAGE,
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
const secrets = await secretsHandedToAddon('example')
|
|
107
|
+
assert.equal(await secrets.getSecret('ADDON_KEY'), 'value-of-ADDON_KEY')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('an undeclared secret is denied', async () => {
|
|
111
|
+
registerAddon(['ADDON_KEY'])
|
|
112
|
+
wireAddon({
|
|
113
|
+
name: 'example',
|
|
114
|
+
package: ADDON_PACKAGE,
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
const secrets = await secretsHandedToAddon('example')
|
|
118
|
+
await assert.rejects(
|
|
119
|
+
() => secrets.getSecret('SOMEONE_ELSES_KEY'),
|
|
120
|
+
/denied/i
|
|
121
|
+
)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test('a scoped addon cannot write secrets', async () => {
|
|
125
|
+
registerAddon(['ADDON_KEY'])
|
|
126
|
+
wireAddon({
|
|
127
|
+
name: 'example',
|
|
128
|
+
package: ADDON_PACKAGE,
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
const secrets = await secretsHandedToAddon('example')
|
|
132
|
+
await assert.rejects(() => secrets.setSecret('ADDON_KEY', 'x'))
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
test('an addon that declares nothing is scoped to nothing', async () => {
|
|
136
|
+
registerAddon([])
|
|
137
|
+
wireAddon({
|
|
138
|
+
name: 'example',
|
|
139
|
+
package: ADDON_PACKAGE,
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
const secrets = await secretsHandedToAddon('example')
|
|
143
|
+
await assert.rejects(() => secrets.getSecret('ADDON_KEY'), /denied/i)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
test('globalSecrets, set by the host, hands over the whole service', async () => {
|
|
147
|
+
registerAddon(['ADDON_KEY'])
|
|
148
|
+
wireAddon({
|
|
149
|
+
name: 'example',
|
|
150
|
+
package: ADDON_PACKAGE,
|
|
151
|
+
globalSecrets: 'administers secrets named by the operator at runtime',
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
const secrets = await secretsHandedToAddon('example')
|
|
155
|
+
assert.equal(
|
|
156
|
+
await secrets.getSecret('SOMEONE_ELSES_KEY'),
|
|
157
|
+
'value-of-SOMEONE_ELSES_KEY'
|
|
158
|
+
)
|
|
159
|
+
await assert.doesNotReject(() => secrets.setSecret('ADDON_KEY', 'x'))
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
test('an addon with no singleton factory is still scoped', async () => {
|
|
163
|
+
pikkuState(ADDON_PACKAGE, 'package', 'declaredSecrets', ['ADDON_KEY'])
|
|
164
|
+
wireAddon({
|
|
165
|
+
name: 'example',
|
|
166
|
+
package: ADDON_PACKAGE,
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
const services = await getOrCreatePackageSingletonServices(
|
|
170
|
+
ADDON_PACKAGE,
|
|
171
|
+
createParentServices(),
|
|
172
|
+
pikkuState(null, 'addons', 'packages').get('example') as never
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
assert.equal(
|
|
176
|
+
await services.secrets.getSecret('ADDON_KEY'),
|
|
177
|
+
'value-of-ADDON_KEY'
|
|
178
|
+
)
|
|
179
|
+
await assert.rejects(
|
|
180
|
+
() => services.secrets.getSecret('SOMEONE_ELSES_KEY'),
|
|
181
|
+
/denied/i
|
|
182
|
+
)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
test('scoping applies to the key the addon asks for, not the aliased one', async () => {
|
|
186
|
+
registerAddon(['ADDON_KEY'])
|
|
187
|
+
wireAddon({
|
|
188
|
+
name: 'example',
|
|
189
|
+
package: ADDON_PACKAGE,
|
|
190
|
+
secretOverrides: { ADDON_KEY: 'PROD_ADDON_KEY' },
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
const secrets = await secretsHandedToAddon('example')
|
|
194
|
+
assert.equal(
|
|
195
|
+
await secrets.getSecret('ADDON_KEY'),
|
|
196
|
+
'value-of-PROD_ADDON_KEY'
|
|
197
|
+
)
|
|
198
|
+
await assert.rejects(() => secrets.getSecret('PROD_ADDON_KEY'), /denied/i)
|
|
199
|
+
})
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
describe('addon credentials are scoped to what the addon declared', () => {
|
|
203
|
+
beforeEach(() => {
|
|
204
|
+
resetPikkuState()
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
test('a declared credential is reachable', async () => {
|
|
208
|
+
registerAddon(['ADDON_KEY'], ['slack'])
|
|
209
|
+
wireAddon({ name: 'example', package: ADDON_PACKAGE })
|
|
210
|
+
|
|
211
|
+
const credentials = await credentialsHandedToAddon('example')
|
|
212
|
+
assert.equal(await credentials.get('slack'), 'credential-of-slack')
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
test('an undeclared credential is denied', async () => {
|
|
216
|
+
registerAddon(['ADDON_KEY'], ['slack'])
|
|
217
|
+
wireAddon({ name: 'example', package: ADDON_PACKAGE })
|
|
218
|
+
|
|
219
|
+
const credentials = await credentialsHandedToAddon('example')
|
|
220
|
+
await assert.rejects(() => credentials.get('stripe'), /denied/i)
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
test('a scoped addon cannot enumerate the app users', async () => {
|
|
224
|
+
registerAddon(['ADDON_KEY'], ['slack'])
|
|
225
|
+
wireAddon({ name: 'example', package: ADDON_PACKAGE })
|
|
226
|
+
|
|
227
|
+
const credentials = await credentialsHandedToAddon('example')
|
|
228
|
+
await assert.rejects(() => credentials.getAllUsers(), /denied/i)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
test('an addon with no singleton factory is still scoped', async () => {
|
|
232
|
+
pikkuState(ADDON_PACKAGE, 'package', 'credentialsMeta', {
|
|
233
|
+
slack: { name: 'slack', displayName: 'slack', type: 'singleton' },
|
|
234
|
+
} as never)
|
|
235
|
+
wireAddon({ name: 'example', package: ADDON_PACKAGE })
|
|
236
|
+
|
|
237
|
+
const services = await getOrCreatePackageSingletonServices(
|
|
238
|
+
ADDON_PACKAGE,
|
|
239
|
+
createParentServices(),
|
|
240
|
+
pikkuState(null, 'addons', 'packages').get('example') as never
|
|
241
|
+
)
|
|
242
|
+
const credentials = services.credentialService as CredentialService
|
|
243
|
+
|
|
244
|
+
assert.equal(await credentials.get('slack'), 'credential-of-slack')
|
|
245
|
+
await assert.rejects(() => credentials.get('stripe'), /denied/i)
|
|
246
|
+
await assert.rejects(() => credentials.getAllUsers(), /denied/i)
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
test('globalCredentials, set by the host, hands over the whole service', async () => {
|
|
250
|
+
registerAddon(['ADDON_KEY'], ['slack'])
|
|
251
|
+
wireAddon({
|
|
252
|
+
name: 'example',
|
|
253
|
+
package: ADDON_PACKAGE,
|
|
254
|
+
globalCredentials: 'links credentials an operator names at runtime',
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
const credentials = await credentialsHandedToAddon('example')
|
|
258
|
+
assert.equal(await credentials.get('stripe'), 'credential-of-stripe')
|
|
259
|
+
assert.deepEqual(await credentials.getAllUsers(), ['user-1'])
|
|
260
|
+
})
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* An addon whose secret names come off its input rather than its own source
|
|
265
|
+
* declares nothing, so the host lends it names it could not have derived.
|
|
266
|
+
*
|
|
267
|
+
* knowledge: decisions/security/decision-secretless-functions.md
|
|
268
|
+
*/
|
|
269
|
+
describe('the host grants an addon secrets it could not declare', () => {
|
|
270
|
+
beforeEach(() => {
|
|
271
|
+
resetPikkuState()
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
test('a granted secret is readable', async () => {
|
|
275
|
+
registerAddon([])
|
|
276
|
+
wireAddon({
|
|
277
|
+
name: 'example',
|
|
278
|
+
package: ADDON_PACKAGE,
|
|
279
|
+
secretGrants: ['STRIPE_KEY'],
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
const secrets = await secretsHandedToAddon('example')
|
|
283
|
+
assert.equal(await secrets.getSecret('STRIPE_KEY'), 'value-of-STRIPE_KEY')
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
test('a secret the host did not grant is still denied', async () => {
|
|
287
|
+
registerAddon([])
|
|
288
|
+
wireAddon({
|
|
289
|
+
name: 'example',
|
|
290
|
+
package: ADDON_PACKAGE,
|
|
291
|
+
secretGrants: ['STRIPE_KEY'],
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
const secrets = await secretsHandedToAddon('example')
|
|
295
|
+
await assert.rejects(() => secrets.getSecret('DATABASE_URL'), /denied/i)
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
test('a grant adds to what the addon declared rather than replacing it', async () => {
|
|
299
|
+
registerAddon(['ADDON_KEY'])
|
|
300
|
+
wireAddon({
|
|
301
|
+
name: 'example',
|
|
302
|
+
package: ADDON_PACKAGE,
|
|
303
|
+
secretGrants: ['STRIPE_KEY'],
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
const secrets = await secretsHandedToAddon('example')
|
|
307
|
+
assert.equal(await secrets.getSecret('ADDON_KEY'), 'value-of-ADDON_KEY')
|
|
308
|
+
assert.equal(await secrets.getSecret('STRIPE_KEY'), 'value-of-STRIPE_KEY')
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
test('an overridden secret is granted by the override itself', async () => {
|
|
312
|
+
registerAddon([])
|
|
313
|
+
wireAddon({
|
|
314
|
+
name: 'example',
|
|
315
|
+
package: ADDON_PACKAGE,
|
|
316
|
+
secretOverrides: { MAILGUN_KEY: 'PROD_EMAIL_KEY' },
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
const secrets = await secretsHandedToAddon('example')
|
|
320
|
+
assert.equal(
|
|
321
|
+
await secrets.getSecret('MAILGUN_KEY'),
|
|
322
|
+
'value-of-PROD_EMAIL_KEY'
|
|
323
|
+
)
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
test('a grant is checked before the override renames it', async () => {
|
|
327
|
+
registerAddon([])
|
|
328
|
+
wireAddon({
|
|
329
|
+
name: 'example',
|
|
330
|
+
package: ADDON_PACKAGE,
|
|
331
|
+
secretGrants: ['MAILGUN_KEY'],
|
|
332
|
+
secretOverrides: { MAILGUN_KEY: 'PROD_EMAIL_KEY' },
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
const secrets = await secretsHandedToAddon('example')
|
|
336
|
+
assert.equal(
|
|
337
|
+
await secrets.getSecret('MAILGUN_KEY'),
|
|
338
|
+
'value-of-PROD_EMAIL_KEY'
|
|
339
|
+
)
|
|
340
|
+
await assert.rejects(() => secrets.getSecret('PROD_EMAIL_KEY'), /denied/i)
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
test('a granted credential is readable', async () => {
|
|
344
|
+
registerAddon([], [])
|
|
345
|
+
wireAddon({
|
|
346
|
+
name: 'example',
|
|
347
|
+
package: ADDON_PACKAGE,
|
|
348
|
+
credentialGrants: ['stripe'],
|
|
349
|
+
})
|
|
350
|
+
|
|
351
|
+
const credentials = await credentialsHandedToAddon('example')
|
|
352
|
+
assert.equal(await credentials.get('stripe'), 'credential-of-stripe')
|
|
353
|
+
await assert.rejects(() => credentials.get('slack'), /denied/i)
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
test('a granted credential does not widen the rest of the service', async () => {
|
|
357
|
+
registerAddon([], [])
|
|
358
|
+
wireAddon({
|
|
359
|
+
name: 'example',
|
|
360
|
+
package: ADDON_PACKAGE,
|
|
361
|
+
credentialGrants: ['stripe'],
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
const credentials = await credentialsHandedToAddon('example')
|
|
365
|
+
await assert.rejects(() => credentials.getAllUsers(), /denied/i)
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
test('an addon without a singleton factory is granted too', async () => {
|
|
369
|
+
pikkuState(ADDON_PACKAGE, 'package', 'declaredSecrets', [])
|
|
370
|
+
wireAddon({
|
|
371
|
+
name: 'example',
|
|
372
|
+
package: ADDON_PACKAGE,
|
|
373
|
+
secretGrants: ['STRIPE_KEY'],
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
const services = await getOrCreatePackageSingletonServices(
|
|
377
|
+
ADDON_PACKAGE,
|
|
378
|
+
createParentServices(),
|
|
379
|
+
pikkuState(null, 'addons', 'packages').get('example') as never
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
assert.equal(
|
|
383
|
+
await services.secrets.getSecret('STRIPE_KEY'),
|
|
384
|
+
'value-of-STRIPE_KEY'
|
|
385
|
+
)
|
|
386
|
+
await assert.rejects(
|
|
387
|
+
() => services.secrets.getSecret('DATABASE_URL'),
|
|
388
|
+
/denied/i
|
|
389
|
+
)
|
|
390
|
+
})
|
|
391
|
+
})
|
|
@@ -708,6 +708,7 @@ describe('multi-instance addons', () => {
|
|
|
708
708
|
return svc
|
|
709
709
|
},
|
|
710
710
|
} as never)
|
|
711
|
+
pikkuState('@addon/slack', 'package', 'declaredSecrets', ['slackToken'])
|
|
711
712
|
registerFunction(
|
|
712
713
|
'send',
|
|
713
714
|
async (services: any) => ({ token: services.token }),
|
|
@@ -753,6 +754,7 @@ describe('multi-instance addons', () => {
|
|
|
753
754
|
}
|
|
754
755
|
},
|
|
755
756
|
} as never)
|
|
757
|
+
pikkuState('@addon/slack', 'package', 'declaredSecrets', ['slackToken'])
|
|
756
758
|
|
|
757
759
|
registerFunction(
|
|
758
760
|
'leaf',
|
|
@@ -284,6 +284,10 @@ export class ContextAwareRPCService {
|
|
|
284
284
|
secretOverrides: resolved.addonConfig?.secretOverrides,
|
|
285
285
|
variableOverrides: resolved.addonConfig?.variableOverrides,
|
|
286
286
|
credentialOverrides: resolved.addonConfig?.credentialOverrides,
|
|
287
|
+
secretGrants: resolved.addonConfig?.secretGrants,
|
|
288
|
+
credentialGrants: resolved.addonConfig?.credentialGrants,
|
|
289
|
+
globalSecrets: resolved.addonConfig?.globalSecrets,
|
|
290
|
+
globalCredentials: resolved.addonConfig?.globalCredentials,
|
|
287
291
|
},
|
|
288
292
|
}
|
|
289
293
|
}
|
|
@@ -67,6 +67,14 @@ export interface ResolvedFunction {
|
|
|
67
67
|
secretOverrides?: Record<string, string>
|
|
68
68
|
variableOverrides?: Record<string, string>
|
|
69
69
|
credentialOverrides?: Record<string, string>
|
|
70
|
+
/** Set by the consuming app: secrets it lends this instance, as the addon names them */
|
|
71
|
+
secretGrants?: string[]
|
|
72
|
+
/** Set by the consuming app: credentials it lends this instance, as the addon names them */
|
|
73
|
+
credentialGrants?: string[]
|
|
74
|
+
/** Set by the consuming app: hand this instance the unscoped `SecretService` */
|
|
75
|
+
globalSecrets?: string
|
|
76
|
+
/** Set by the consuming app: hand this instance the unscoped `CredentialService` */
|
|
77
|
+
globalCredentials?: string
|
|
70
78
|
/** Set by `wireRemoteAddon`: dispatch this namespace's RPCs over HTTP */
|
|
71
79
|
remote?: boolean
|
|
72
80
|
serverUrl?: string | ((services: any) => string | Promise<string>)
|
|
@@ -14,6 +14,31 @@ export type WireAddonConfig = {
|
|
|
14
14
|
secretOverrides?: Record<string, string>
|
|
15
15
|
variableOverrides?: Record<string, string>
|
|
16
16
|
credentialOverrides?: Record<string, string>
|
|
17
|
+
/**
|
|
18
|
+
* Secrets this instance may read on top of the ones it declared, named as the
|
|
19
|
+
* addon reads them — the scope check runs before `secretOverrides` renames
|
|
20
|
+
* them, so an overridden secret is named here by its addon-side key. Listing
|
|
21
|
+
* one in `secretOverrides` grants it too.
|
|
22
|
+
*
|
|
23
|
+
* For an addon whose secret names come off its input rather than its own
|
|
24
|
+
* source, this is how the host lends names the addon could not declare.
|
|
25
|
+
*/
|
|
26
|
+
secretGrants?: string[]
|
|
27
|
+
/** Credentials this instance may read on top of the ones it declared. */
|
|
28
|
+
credentialGrants?: string[]
|
|
29
|
+
/**
|
|
30
|
+
* Hands this instance the whole `SecretService` instead of one scoped to the
|
|
31
|
+
* secrets it declared. The value is the reason, recorded in the deploy
|
|
32
|
+
* manifest — an addon that names secrets at runtime cannot be scoped, and
|
|
33
|
+
* only the consuming app, never the addon, can grant it.
|
|
34
|
+
*/
|
|
35
|
+
globalSecrets?: string
|
|
36
|
+
/**
|
|
37
|
+
* Hands this instance the whole `CredentialService` instead of one narrowed
|
|
38
|
+
* to the credentials it declared. The value is the reason, recorded in the
|
|
39
|
+
* deploy manifest, and only the consuming app can grant it.
|
|
40
|
+
*/
|
|
41
|
+
globalCredentials?: string
|
|
17
42
|
}
|
|
18
43
|
|
|
19
44
|
export const wireAddon = (config: WireAddonConfig): void => {
|
|
@@ -32,6 +57,14 @@ export const wireAddon = (config: WireAddonConfig): void => {
|
|
|
32
57
|
...(config.credentialOverrides
|
|
33
58
|
? { credentialOverrides: config.credentialOverrides }
|
|
34
59
|
: {}),
|
|
60
|
+
...(config.secretGrants ? { secretGrants: config.secretGrants } : {}),
|
|
61
|
+
...(config.credentialGrants
|
|
62
|
+
? { credentialGrants: config.credentialGrants }
|
|
63
|
+
: {}),
|
|
64
|
+
...(config.globalSecrets ? { globalSecrets: config.globalSecrets } : {}),
|
|
65
|
+
...(config.globalCredentials
|
|
66
|
+
? { globalCredentials: config.globalCredentials }
|
|
67
|
+
: {}),
|
|
35
68
|
})
|
|
36
69
|
}
|
|
37
70
|
|
|
@@ -18,25 +18,16 @@
|
|
|
18
18
|
*/
|
|
19
19
|
export type {
|
|
20
20
|
ApiCatalogueEntry,
|
|
21
|
-
IntentRecord,
|
|
22
21
|
IntentSource,
|
|
23
|
-
IntentStatus,
|
|
24
|
-
StepRecord,
|
|
25
|
-
VirtualUserAction,
|
|
26
|
-
VirtualUserBudget,
|
|
27
22
|
VirtualUserDisposition,
|
|
28
23
|
VirtualUserFinding,
|
|
29
|
-
VirtualUserFindingKind,
|
|
30
24
|
VirtualUserRunResult,
|
|
31
|
-
VirtualUserTally,
|
|
32
25
|
VirtualUserTarget,
|
|
33
26
|
} from './virtual-user.types.js'
|
|
34
27
|
export { PRODUCTION_DISPOSITION } from './virtual-user.types.js'
|
|
35
28
|
export {
|
|
36
29
|
runVirtualUser,
|
|
37
|
-
rememberIds,
|
|
38
30
|
type RunVirtualUserParams,
|
|
39
|
-
type VirtualUserCallContext,
|
|
40
31
|
} from './run-virtual-user.js'
|
|
41
32
|
export {
|
|
42
33
|
personaScopes,
|
|
@@ -57,26 +48,19 @@ export {
|
|
|
57
48
|
} from './virtual-user-dispositions.js'
|
|
58
49
|
export {
|
|
59
50
|
catalogueClassification,
|
|
60
|
-
catalogueIndex,
|
|
61
51
|
catalogueLookup,
|
|
62
|
-
describeEntry,
|
|
63
52
|
isReadOnly,
|
|
64
53
|
reachableCatalogue,
|
|
65
|
-
renderCatalogue,
|
|
66
54
|
unreachableCatalogue,
|
|
67
55
|
} from './virtual-user-catalogue.js'
|
|
68
56
|
export {
|
|
69
|
-
reachableAgents,
|
|
70
57
|
type AgentReachability,
|
|
71
58
|
type ReachableAgent,
|
|
72
59
|
} from './virtual-user-agents.js'
|
|
73
60
|
export {
|
|
74
61
|
IntentStack,
|
|
75
62
|
intentsForPersona,
|
|
76
|
-
type IntentMove,
|
|
77
|
-
type ScheduledTick,
|
|
78
63
|
} from './virtual-user-intents.js'
|
|
79
|
-
export { createRng, type VirtualUserRng } from './virtual-user-rng.js'
|
|
80
64
|
export {
|
|
81
65
|
deriveCatalogue,
|
|
82
66
|
deriveIntents,
|