@pikku/core 0.12.9 → 0.12.11
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 +13 -0
- package/dist/function/function-runner.d.ts +3 -1
- package/dist/function/function-runner.js +5 -1
- package/dist/services/credential-service.d.ts +40 -0
- package/dist/services/credential-service.js +1 -0
- package/dist/services/credential-wire-service.d.ts +13 -0
- package/dist/services/credential-wire-service.js +31 -0
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.js +3 -0
- package/dist/services/local-credential-service.d.ts +10 -0
- package/dist/services/local-credential-service.js +33 -0
- package/dist/services/typed-credential-service.d.ts +27 -0
- package/dist/services/typed-credential-service.js +40 -0
- package/dist/testing/service-tests.d.ts +8 -0
- package/dist/testing/service-tests.js +106 -0
- package/dist/types/core.types.d.ts +7 -0
- package/dist/wirings/ai-agent/agent-dynamic-workflow.js +173 -53
- package/dist/wirings/cli/channel/cli-raw-channel-runner.d.ts +15 -0
- package/dist/wirings/cli/channel/cli-raw-channel-runner.js +58 -0
- package/dist/wirings/cli/channel/index.d.ts +1 -0
- package/dist/wirings/cli/channel/index.js +1 -0
- package/dist/wirings/credential/credential.types.d.ts +24 -0
- package/dist/wirings/credential/credential.types.js +1 -0
- package/dist/wirings/credential/index.d.ts +3 -0
- package/dist/wirings/credential/index.js +2 -0
- package/dist/wirings/credential/validate-credential-definitions.d.ts +6 -0
- package/dist/wirings/credential/validate-credential-definitions.js +38 -0
- package/dist/wirings/credential/wire-credential.d.ts +48 -0
- package/dist/wirings/credential/wire-credential.js +47 -0
- package/dist/wirings/http/http-runner.js +4 -0
- package/dist/wirings/oauth2/index.d.ts +2 -1
- package/dist/wirings/oauth2/index.js +1 -1
- package/dist/wirings/oauth2/oauth2-client.js +4 -8
- package/dist/wirings/oauth2/oauth2-routes.d.ts +35 -0
- package/dist/wirings/oauth2/oauth2-routes.js +146 -0
- package/dist/wirings/oauth2/oauth2.types.d.ts +0 -26
- package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -1
- package/dist/wirings/workflow/graph/graph-runner.js +15 -0
- package/dist/wirings/workflow/pikku-workflow-service.d.ts +2 -2
- package/dist/wirings/workflow/pikku-workflow-service.js +40 -8
- package/package.json +2 -1
- package/src/function/function-runner.ts +11 -0
- package/src/services/credential-service.ts +44 -0
- package/src/services/credential-wire-service.ts +44 -0
- package/src/services/index.ts +12 -0
- package/src/services/local-credential-service.ts +41 -0
- package/src/services/typed-credential-service.ts +75 -0
- package/src/testing/service-tests.ts +139 -0
- package/src/types/core.types.ts +7 -0
- package/src/wirings/ai-agent/agent-dynamic-workflow.ts +378 -237
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +89 -0
- package/src/wirings/cli/channel/index.ts +1 -0
- package/src/wirings/credential/credential.types.ts +28 -0
- package/src/wirings/credential/index.ts +8 -0
- package/src/wirings/credential/validate-credential-definitions.ts +64 -0
- package/src/wirings/credential/wire-credential.ts +49 -0
- package/src/wirings/http/http-runner.ts +7 -0
- package/src/wirings/oauth2/index.ts +2 -1
- package/src/wirings/oauth2/oauth2-client.ts +4 -8
- package/src/wirings/oauth2/oauth2-routes.ts +234 -0
- package/src/wirings/oauth2/oauth2.types.ts +0 -27
- package/src/wirings/workflow/graph/graph-runner.ts +33 -1
- package/src/wirings/workflow/pikku-workflow-service.ts +55 -9
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/wirings/oauth2/wire-oauth2-credential.d.ts +0 -20
- package/dist/wirings/oauth2/wire-oauth2-credential.js +0 -21
- package/src/wirings/oauth2/wire-oauth2-credential.ts +0 -23
|
@@ -9,6 +9,7 @@ import type { DeploymentService } from '../services/deployment-service.js'
|
|
|
9
9
|
import type { AIStorageService } from '../services/ai-storage-service.js'
|
|
10
10
|
import type { AIRunStateService } from '../services/ai-run-state-service.js'
|
|
11
11
|
import type { SecretService } from '../services/secret-service.js'
|
|
12
|
+
import type { CredentialService } from '../services/credential-service.js'
|
|
12
13
|
import type { AgentRunService } from '../wirings/ai-agent/ai-agent.types.js'
|
|
13
14
|
|
|
14
15
|
export interface ServiceTestConfig {
|
|
@@ -28,6 +29,11 @@ export interface ServiceTestConfig {
|
|
|
28
29
|
keyVersion?: number
|
|
29
30
|
previousKey?: string
|
|
30
31
|
}) => Promise<SecretService & { rotateKEK?(): Promise<number> }>
|
|
32
|
+
credentialService?: (config: {
|
|
33
|
+
key: string
|
|
34
|
+
keyVersion?: number
|
|
35
|
+
previousKey?: string
|
|
36
|
+
}) => Promise<CredentialService & { rotateKEK?(): Promise<number> }>
|
|
31
37
|
}
|
|
32
38
|
}
|
|
33
39
|
|
|
@@ -840,6 +846,139 @@ export function defineServiceTests(config: ServiceTestConfig): void {
|
|
|
840
846
|
})
|
|
841
847
|
}
|
|
842
848
|
|
|
849
|
+
if (services.credentialService) {
|
|
850
|
+
const factory = services.credentialService
|
|
851
|
+
const kek = 'test-key-encryption-key-32chars!'
|
|
852
|
+
|
|
853
|
+
describe(`CredentialService [${name}]`, () => {
|
|
854
|
+
test('set and get (global)', async () => {
|
|
855
|
+
const service = await factory({ key: kek })
|
|
856
|
+
await service.set('stripe', { apiKey: 'sk-123' })
|
|
857
|
+
const result = await service.get<{ apiKey: string }>('stripe')
|
|
858
|
+
assert.deepEqual(result, { apiKey: 'sk-123' })
|
|
859
|
+
})
|
|
860
|
+
|
|
861
|
+
test('get returns null for missing', async () => {
|
|
862
|
+
const service = await factory({ key: kek })
|
|
863
|
+
const result = await service.get('nonexistent')
|
|
864
|
+
assert.equal(result, null)
|
|
865
|
+
})
|
|
866
|
+
|
|
867
|
+
test('has returns true/false', async () => {
|
|
868
|
+
const service = await factory({ key: kek })
|
|
869
|
+
await service.set('exists', { key: 'val' })
|
|
870
|
+
assert.strictEqual(await service.has('exists'), true)
|
|
871
|
+
assert.strictEqual(await service.has('nope'), false)
|
|
872
|
+
})
|
|
873
|
+
|
|
874
|
+
test('set upserts existing credential', async () => {
|
|
875
|
+
const service = await factory({ key: kek })
|
|
876
|
+
await service.set('upsert', { v: 1 })
|
|
877
|
+
await service.set('upsert', { v: 2 })
|
|
878
|
+
const result = await service.get<{ v: number }>('upsert')
|
|
879
|
+
assert.deepEqual(result, { v: 2 })
|
|
880
|
+
})
|
|
881
|
+
|
|
882
|
+
test('delete removes credential', async () => {
|
|
883
|
+
const service = await factory({ key: kek })
|
|
884
|
+
await service.set('to-delete', { bye: true })
|
|
885
|
+
assert.strictEqual(await service.has('to-delete'), true)
|
|
886
|
+
await service.delete('to-delete')
|
|
887
|
+
assert.strictEqual(await service.has('to-delete'), false)
|
|
888
|
+
})
|
|
889
|
+
|
|
890
|
+
test('per-user isolation', async () => {
|
|
891
|
+
const service = await factory({ key: kek })
|
|
892
|
+
await service.set('token', { access: 'user1' }, 'user-1')
|
|
893
|
+
await service.set('token', { access: 'user2' }, 'user-2')
|
|
894
|
+
|
|
895
|
+
const u1 = await service.get<{ access: string }>('token', 'user-1')
|
|
896
|
+
const u2 = await service.get<{ access: string }>('token', 'user-2')
|
|
897
|
+
assert.deepEqual(u1, { access: 'user1' })
|
|
898
|
+
assert.deepEqual(u2, { access: 'user2' })
|
|
899
|
+
})
|
|
900
|
+
|
|
901
|
+
test('per-user delete does not affect other users', async () => {
|
|
902
|
+
const service = await factory({ key: kek })
|
|
903
|
+
await service.set('cred', { val: 'a' }, 'user-a')
|
|
904
|
+
await service.set('cred', { val: 'b' }, 'user-b')
|
|
905
|
+
|
|
906
|
+
await service.delete('cred', 'user-a')
|
|
907
|
+
assert.strictEqual(await service.has('cred', 'user-a'), false)
|
|
908
|
+
assert.strictEqual(await service.has('cred', 'user-b'), true)
|
|
909
|
+
})
|
|
910
|
+
|
|
911
|
+
test('global and per-user are separate', async () => {
|
|
912
|
+
const service = await factory({ key: kek })
|
|
913
|
+
await service.set('shared', { scope: 'global' })
|
|
914
|
+
await service.set('shared', { scope: 'user' }, 'user-1')
|
|
915
|
+
|
|
916
|
+
const global = await service.get<{ scope: string }>('shared')
|
|
917
|
+
const perUser = await service.get<{ scope: string }>('shared', 'user-1')
|
|
918
|
+
assert.deepEqual(global, { scope: 'global' })
|
|
919
|
+
assert.deepEqual(perUser, { scope: 'user' })
|
|
920
|
+
})
|
|
921
|
+
|
|
922
|
+
test('getAll returns all credentials for a user', async () => {
|
|
923
|
+
const service = await factory({ key: kek })
|
|
924
|
+
await service.set('cred-a', { a: 1 }, 'user-x')
|
|
925
|
+
await service.set('cred-b', { b: 2 }, 'user-x')
|
|
926
|
+
await service.set('cred-c', { c: 3 }, 'user-y')
|
|
927
|
+
|
|
928
|
+
const all = await service.getAll('user-x')
|
|
929
|
+
assert.deepEqual(all['cred-a'], { a: 1 })
|
|
930
|
+
assert.deepEqual(all['cred-b'], { b: 2 })
|
|
931
|
+
assert.strictEqual(all['cred-c'], undefined)
|
|
932
|
+
})
|
|
933
|
+
|
|
934
|
+
test('getAll returns empty for unknown user', async () => {
|
|
935
|
+
const service = await factory({ key: kek })
|
|
936
|
+
const all = await service.getAll('nobody')
|
|
937
|
+
assert.deepEqual(all, {})
|
|
938
|
+
})
|
|
939
|
+
|
|
940
|
+
test('rotateKEK re-wraps all credentials', async () => {
|
|
941
|
+
const newKEK = 'new-key-encryption-key-rotated!'
|
|
942
|
+
const oldService = await factory({ key: kek })
|
|
943
|
+
await oldService.set('rotate-cred', { important: 'data' }, 'user-r')
|
|
944
|
+
|
|
945
|
+
const rotatedService = await factory({
|
|
946
|
+
key: newKEK,
|
|
947
|
+
keyVersion: 2,
|
|
948
|
+
previousKey: kek,
|
|
949
|
+
})
|
|
950
|
+
|
|
951
|
+
const before = await rotatedService.get<{ important: string }>(
|
|
952
|
+
'rotate-cred',
|
|
953
|
+
'user-r'
|
|
954
|
+
)
|
|
955
|
+
assert.deepEqual(before, { important: 'data' })
|
|
956
|
+
|
|
957
|
+
assert.ok(rotatedService.rotateKEK)
|
|
958
|
+
const count = await rotatedService.rotateKEK!()
|
|
959
|
+
assert.ok(count > 0)
|
|
960
|
+
|
|
961
|
+
const newOnlyService = await factory({
|
|
962
|
+
key: newKEK,
|
|
963
|
+
keyVersion: 2,
|
|
964
|
+
})
|
|
965
|
+
const after = await newOnlyService.get<{ important: string }>(
|
|
966
|
+
'rotate-cred',
|
|
967
|
+
'user-r'
|
|
968
|
+
)
|
|
969
|
+
assert.deepEqual(after, { important: 'data' })
|
|
970
|
+
})
|
|
971
|
+
|
|
972
|
+
test('rotateKEK throws without previousKey', async () => {
|
|
973
|
+
const service = await factory({ key: kek })
|
|
974
|
+
assert.ok(service.rotateKEK)
|
|
975
|
+
await assert.rejects(() => service.rotateKEK!(), {
|
|
976
|
+
message: 'No previousKey configured — nothing to rotate from',
|
|
977
|
+
})
|
|
978
|
+
})
|
|
979
|
+
})
|
|
980
|
+
}
|
|
981
|
+
|
|
843
982
|
if (services.agentRunService) {
|
|
844
983
|
const factory = services.agentRunService
|
|
845
984
|
describe(`AgentRunService [${name}]`, () => {
|
package/src/types/core.types.ts
CHANGED
|
@@ -33,6 +33,7 @@ import type { AIRunStateService } from '../services/ai-run-state-service.js'
|
|
|
33
33
|
import type { AgentRunService } from '../wirings/ai-agent/ai-agent.types.js'
|
|
34
34
|
import type { PikkuAIMiddlewareHooks } from '../wirings/ai-agent/ai-agent.types.js'
|
|
35
35
|
import type { WorkflowRunService } from '../wirings/workflow/workflow.types.js'
|
|
36
|
+
import type { CredentialService } from '../services/credential-service.js'
|
|
36
37
|
|
|
37
38
|
export type PikkuWiringTypes =
|
|
38
39
|
| 'http'
|
|
@@ -227,6 +228,8 @@ export interface CoreSingletonServices<Config extends CoreConfig = CoreConfig> {
|
|
|
227
228
|
agentRunService?: AgentRunService
|
|
228
229
|
/** Workflow run service (listing workflow runs) */
|
|
229
230
|
workflowRunService?: WorkflowRunService
|
|
231
|
+
/** Credential service for dynamic/managed credentials (OAuth tokens, per-user API keys) */
|
|
232
|
+
credentialService?: CredentialService
|
|
230
233
|
}
|
|
231
234
|
|
|
232
235
|
/**
|
|
@@ -270,6 +273,10 @@ export type PikkuWire<
|
|
|
270
273
|
getSession: () => Promise<UserSession> | UserSession | undefined
|
|
271
274
|
/** Whether the session was modified during this run */
|
|
272
275
|
hasSessionChanged: () => boolean
|
|
276
|
+
/** Set a credential value (available in middleware) */
|
|
277
|
+
setCredential: (name: string, value: unknown) => void
|
|
278
|
+
/** Get all resolved credentials (only available in pikkuWireServices) */
|
|
279
|
+
getCredentials: () => Record<string, unknown>
|
|
273
280
|
}>
|
|
274
281
|
|
|
275
282
|
/**
|