@pikku/core 0.12.13 → 0.12.15

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.
Files changed (150) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/errors/errors.d.ts +18 -0
  3. package/dist/errors/errors.js +33 -0
  4. package/dist/function/function-runner.d.ts +1 -1
  5. package/dist/function/function-runner.js +10 -5
  6. package/dist/function/functions.types.js +1 -0
  7. package/dist/function/index.d.ts +2 -1
  8. package/dist/function/index.js +1 -0
  9. package/dist/handle-error.d.ts +2 -2
  10. package/dist/handle-error.js +8 -8
  11. package/dist/index.d.ts +1 -2
  12. package/dist/index.js +1 -2
  13. package/dist/middleware/index.d.ts +2 -0
  14. package/dist/middleware/index.js +2 -0
  15. package/dist/middleware/remote-auth.js +5 -2
  16. package/dist/permissions.d.ts +13 -1
  17. package/dist/permissions.js +51 -0
  18. package/dist/pikku-state.d.ts +0 -1
  19. package/dist/pikku-state.js +2 -4
  20. package/dist/remote.d.ts +10 -0
  21. package/dist/remote.js +32 -0
  22. package/dist/schema.js +2 -0
  23. package/dist/services/credential-service.d.ts +11 -0
  24. package/dist/services/credential-wire-service.d.ts +14 -3
  25. package/dist/services/credential-wire-service.js +49 -6
  26. package/dist/services/deployment-service.d.ts +12 -1
  27. package/dist/services/in-memory-queue-service.d.ts +7 -0
  28. package/dist/services/in-memory-queue-service.js +28 -0
  29. package/dist/services/index.d.ts +4 -1
  30. package/dist/services/index.js +2 -1
  31. package/dist/services/local-credential-service.d.ts +2 -0
  32. package/dist/services/local-credential-service.js +20 -0
  33. package/dist/services/logger-console.d.ts +26 -40
  34. package/dist/services/logger-console.js +102 -46
  35. package/dist/services/logger.d.ts +5 -0
  36. package/dist/services/meta-service.d.ts +175 -0
  37. package/dist/services/meta-service.js +310 -0
  38. package/dist/services/pikku-user-id.d.ts +3 -0
  39. package/dist/services/pikku-user-id.js +16 -0
  40. package/dist/services/typed-credential-service.d.ts +2 -0
  41. package/dist/services/typed-credential-service.js +6 -0
  42. package/dist/services/workflow-service.d.ts +6 -1
  43. package/dist/testing/service-tests.js +0 -8
  44. package/dist/types/core.types.d.ts +14 -2
  45. package/dist/types/state.types.d.ts +9 -2
  46. package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +21 -0
  47. package/dist/wirings/ai-agent/ai-agent-prepare.js +90 -21
  48. package/dist/wirings/ai-agent/ai-agent-registry.js +2 -2
  49. package/dist/wirings/ai-agent/ai-agent-runner.js +24 -2
  50. package/dist/wirings/ai-agent/ai-agent-stream.d.ts +2 -1
  51. package/dist/wirings/ai-agent/ai-agent-stream.js +61 -3
  52. package/dist/wirings/ai-agent/ai-agent.types.d.ts +25 -4
  53. package/dist/wirings/ai-agent/index.d.ts +1 -1
  54. package/dist/wirings/ai-agent/index.js +1 -1
  55. package/dist/wirings/channel/channel-runner.js +3 -3
  56. package/dist/wirings/cli/cli-runner.js +4 -3
  57. package/dist/wirings/cli/command-parser.js +7 -3
  58. package/dist/wirings/http/http-runner.d.ts +1 -1
  59. package/dist/wirings/http/http-runner.js +23 -15
  60. package/dist/wirings/http/http.types.d.ts +2 -0
  61. package/dist/wirings/mcp/mcp-runner.js +5 -3
  62. package/dist/wirings/queue/queue-runner.d.ts +3 -1
  63. package/dist/wirings/queue/queue-runner.js +8 -3
  64. package/dist/wirings/queue/queue.types.d.ts +6 -0
  65. package/dist/wirings/rpc/rpc-runner.js +57 -90
  66. package/dist/wirings/scheduler/scheduler-runner.d.ts +3 -1
  67. package/dist/wirings/scheduler/scheduler-runner.js +9 -5
  68. package/dist/wirings/trigger/trigger-runner.js +4 -2
  69. package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +2 -0
  70. package/dist/wirings/workflow/dsl/workflow-runner.d.ts +1 -1
  71. package/dist/wirings/workflow/dsl/workflow-runner.js +6 -9
  72. package/dist/wirings/workflow/graph/graph-runner.d.ts +1 -1
  73. package/dist/wirings/workflow/graph/graph-runner.js +18 -4
  74. package/dist/wirings/workflow/index.d.ts +4 -2
  75. package/dist/wirings/workflow/index.js +4 -2
  76. package/dist/wirings/workflow/pikku-workflow-service.d.ts +16 -4
  77. package/dist/wirings/workflow/pikku-workflow-service.js +218 -24
  78. package/dist/wirings/workflow/workflow-queue-workers.d.ts +40 -0
  79. package/dist/wirings/workflow/workflow-queue-workers.js +41 -0
  80. package/dist/wirings/workflow/workflow.types.d.ts +19 -1
  81. package/package.json +4 -1
  82. package/src/errors/errors.ts +44 -0
  83. package/src/function/function-runner.ts +24 -13
  84. package/src/function/functions.types.ts +1 -0
  85. package/src/function/index.ts +10 -0
  86. package/src/handle-error.test.ts +2 -2
  87. package/src/handle-error.ts +8 -8
  88. package/src/index.ts +1 -2
  89. package/src/middleware/index.ts +2 -0
  90. package/src/middleware/remote-auth.test.ts +4 -4
  91. package/src/middleware/remote-auth.ts +7 -2
  92. package/src/permissions.ts +70 -0
  93. package/src/pikku-state.test.ts +0 -26
  94. package/src/pikku-state.ts +2 -5
  95. package/src/remote.ts +46 -0
  96. package/src/schema.ts +1 -0
  97. package/src/services/credential-service.ts +13 -0
  98. package/src/services/credential-wire-service.test.ts +174 -0
  99. package/src/services/credential-wire-service.ts +52 -8
  100. package/src/services/deployment-service.ts +17 -1
  101. package/src/services/in-memory-queue-service.ts +46 -0
  102. package/src/services/index.ts +21 -1
  103. package/src/services/local-credential-service.ts +22 -0
  104. package/src/services/logger-console.ts +127 -47
  105. package/src/services/logger.ts +6 -0
  106. package/src/services/meta-service.ts +523 -0
  107. package/src/services/pikku-user-id.test.ts +62 -0
  108. package/src/services/pikku-user-id.ts +17 -0
  109. package/src/services/typed-credential-service.ts +8 -0
  110. package/src/services/workflow-service.ts +8 -0
  111. package/src/testing/service-tests.ts +0 -10
  112. package/src/types/core.types.ts +16 -2
  113. package/src/types/state.types.ts +7 -2
  114. package/src/wirings/ai-agent/ai-agent-prepare.ts +122 -41
  115. package/src/wirings/ai-agent/ai-agent-registry.ts +3 -3
  116. package/src/wirings/ai-agent/ai-agent-runner.ts +22 -3
  117. package/src/wirings/ai-agent/ai-agent-stream.ts +115 -3
  118. package/src/wirings/ai-agent/ai-agent.types.ts +27 -4
  119. package/src/wirings/ai-agent/index.ts +1 -0
  120. package/src/wirings/channel/channel-handler.ts +0 -1
  121. package/src/wirings/channel/channel-runner.ts +4 -5
  122. package/src/wirings/cli/cli-runner.test.ts +8 -7
  123. package/src/wirings/cli/cli-runner.ts +5 -4
  124. package/src/wirings/cli/command-parser.ts +8 -3
  125. package/src/wirings/http/http-runner.ts +27 -18
  126. package/src/wirings/http/http.types.ts +2 -0
  127. package/src/wirings/mcp/mcp-runner.ts +7 -10
  128. package/src/wirings/queue/queue-runner.test.ts +5 -11
  129. package/src/wirings/queue/queue-runner.ts +12 -4
  130. package/src/wirings/queue/queue.types.ts +6 -0
  131. package/src/wirings/rpc/rpc-runner.ts +91 -118
  132. package/src/wirings/scheduler/scheduler-runner.test.ts +5 -11
  133. package/src/wirings/scheduler/scheduler-runner.ts +13 -5
  134. package/src/wirings/trigger/trigger-runner.test.ts +10 -11
  135. package/src/wirings/trigger/trigger-runner.ts +6 -4
  136. package/src/wirings/workflow/dsl/workflow-dsl.types.ts +2 -0
  137. package/src/wirings/workflow/dsl/workflow-runner.ts +11 -10
  138. package/src/wirings/workflow/graph/graph-runner.ts +19 -4
  139. package/src/wirings/workflow/index.ts +17 -6
  140. package/src/wirings/workflow/pikku-workflow-service.ts +286 -24
  141. package/src/wirings/workflow/workflow-queue-workers.ts +85 -0
  142. package/src/wirings/workflow/workflow.types.ts +18 -1
  143. package/tsconfig.tsbuildinfo +1 -1
  144. package/dist/wirings/ai-agent/agent-dynamic-workflow.d.ts +0 -6
  145. package/dist/wirings/ai-agent/agent-dynamic-workflow.js +0 -468
  146. package/dist/wirings/workflow/workflow-helpers.d.ts +0 -36
  147. package/dist/wirings/workflow/workflow-helpers.js +0 -38
  148. package/src/wirings/ai-agent/agent-dynamic-workflow.ts +0 -610
  149. package/src/wirings/workflow/workflow-helpers.test.ts +0 -129
  150. package/src/wirings/workflow/workflow-helpers.ts +0 -79
package/CHANGELOG.md CHANGED
@@ -1,5 +1,51 @@
1
1
  ## 0.12.4
2
2
 
3
+ ## 0.12.15
4
+
5
+ ### Patch Changes
6
+
7
+ - 9e8605f: Add Workers for Platforms dispatch namespace support and AI agent fixes.
8
+
9
+ - deploy-cloudflare: Thread dispatchNamespace through deploy pipeline, reads CF_DISPATCH_NAMESPACE env var
10
+ - core: Fix auth-gated tools visible to unauthenticated sessions (null session now hides permission-gated items)
11
+ - core: Recursive null stripping in AI agent tool call resume path
12
+ - ai-vercel: Handle anyOf/oneOf/array types when making optional fields nullable for strict providers
13
+
14
+ - 624097e: Add deploy pipeline with provider-agnostic architecture
15
+
16
+ - Add MetaService with explicit typed API, absorb WiringService reads
17
+ - Add deployment service, traceId propagation, scoped logger
18
+ - Rewrite analyzer: one function = one worker, gateways dispatch via RPC
19
+ - Add Cloudflare deploy provider with plan/apply commands
20
+ - Add per-unit filtered codegen for deploy pipeline
21
+ - Skip missing metadata in wiring registration for deploy units
22
+ - Fix schema coercion crash when schema has no properties
23
+ - Fix E2E codegen: double-pass resolves cross-package Zod type imports
24
+
25
+ - 7ab3243: Add server-fallback deployment target for functions that can't run serverless.
26
+
27
+ Functions can declare `deploy: 'serverless' | 'server' | 'auto'`. With `serverlessIncompatible` config, the analyzer auto-routes functions using incompatible services to a container.
28
+
29
+ Server functions are merged into a single tree-shaken unit with a PikkuUWSServer entry, Dockerfile, and CF Container proxy Worker.
30
+
31
+ Also adds sub-path exports to @pikku/cloudflare for tree-shaking (greet bundle 1.6MB → 444KB) and deploy verifiers for cloudflare, serverless, and azure providers.
32
+
33
+ ## 0.12.14
34
+
35
+ ### Patch Changes
36
+
37
+ - f85c234: Add unified credential system with per-user OAuth and AI agent pre-flight checks
38
+
39
+ - Unified CredentialService with lazy loading per user via pikkuUserId
40
+ - wire.getCredential() for typed single credential lookup
41
+ - MissingCredentialError with structured payload for client-side connect flows
42
+ - Console UI: Global/Users credential tabs, per-user OAuth connect/revoke
43
+ - AI agent pre-flight check: detects missing OAuth credentials from addon metadata, shows "Connect your accounts" prompt before chat
44
+ - CLI codegen: generates credentialsMeta per addon package for runtime lookup
45
+ - Vercel AI runner: catches MissingCredentialError as runtime fallback
46
+
47
+ - 88d3100: Fix CLI command resolution for addon functions by passing packageName to addFunction during registration.
48
+
3
49
  ## 0.12.13
4
50
 
5
51
  ### Patch Changes
@@ -6,6 +6,10 @@ export declare class InvalidMiddlewareWireError extends PikkuError {
6
6
  }
7
7
  export declare class PikkuMissingMetaError extends PikkuError {
8
8
  }
9
+ export declare class MissingServiceError extends PikkuError {
10
+ }
11
+ export declare class LocalEnvironmentOnlyError extends PikkuError {
12
+ }
9
13
  /**
10
14
  * The server cannot or will not process the request due to client error (e.g., malformed request syntax).
11
15
  * @group Error
@@ -42,6 +46,20 @@ export declare class PaymentRequiredError extends PikkuError {
42
46
  */
43
47
  export declare class ForbiddenError extends PikkuError {
44
48
  }
49
+ /**
50
+ * A required credential is missing. The payload contains metadata
51
+ * about the credential so the client can initiate a connect flow.
52
+ * @group Error
53
+ */
54
+ export declare class MissingCredentialError extends PikkuError {
55
+ payload: {
56
+ error: 'missing_credential';
57
+ credentialName: string;
58
+ credentialType: 'oauth2' | 'apikey';
59
+ connectUrl?: string;
60
+ };
61
+ constructor(credentialName: string, credentialType: 'oauth2' | 'apikey', connectUrl?: string);
62
+ }
45
63
  /**
46
64
  * The session is readonly and cannot access a non-readonly function.
47
65
  * @group Error
@@ -14,6 +14,18 @@ addError(PikkuMissingMetaError, {
14
14
  status: 500,
15
15
  message: 'Required metadata is missing',
16
16
  });
17
+ export class MissingServiceError extends PikkuError {
18
+ }
19
+ addError(MissingServiceError, {
20
+ status: 500,
21
+ message: 'A required service is not configured',
22
+ });
23
+ export class LocalEnvironmentOnlyError extends PikkuError {
24
+ }
25
+ addError(LocalEnvironmentOnlyError, {
26
+ status: 403,
27
+ message: 'This operation is only available in local development mode',
28
+ });
17
29
  /**
18
30
  * The server cannot or will not process the request due to client error (e.g., malformed request syntax).
19
31
  * @group Error
@@ -72,6 +84,27 @@ addError(ForbiddenError, {
72
84
  status: 403,
73
85
  message: 'The client does not have permission to access the requested resource.',
74
86
  });
87
+ /**
88
+ * A required credential is missing. The payload contains metadata
89
+ * about the credential so the client can initiate a connect flow.
90
+ * @group Error
91
+ */
92
+ export class MissingCredentialError extends PikkuError {
93
+ payload;
94
+ constructor(credentialName, credentialType, connectUrl) {
95
+ super(`Missing credential: ${credentialName}`);
96
+ this.payload = {
97
+ error: 'missing_credential',
98
+ credentialName,
99
+ credentialType,
100
+ connectUrl,
101
+ };
102
+ }
103
+ }
104
+ addError(MissingCredentialError, {
105
+ status: 403,
106
+ message: 'A required credential is not configured.',
107
+ });
75
108
  /**
76
109
  * The session is readonly and cannot access a non-readonly function.
77
110
  * @group Error
@@ -2,7 +2,7 @@ import type { CoreServices, CoreUserSession, CorePikkuMiddleware, PikkuWiringTyp
2
2
  import type { CorePikkuChannelMiddleware } from '../wirings/channel/channel.types.js';
3
3
  import type { CorePermissionGroup, CorePikkuFunctionConfig, CorePikkuPermission } from './functions.types.js';
4
4
  import type { SessionService } from '../services/user-session-service.js';
5
- import type { PikkuCredentialWireService } from '../services/credential-wire-service.js';
5
+ import { PikkuCredentialWireService } from '../services/credential-wire-service.js';
6
6
  export declare const addFunction: (funcName: string, funcConfig: CorePikkuFunctionConfig<any, any>, packageName?: string | null) => void;
7
7
  export declare const getFunctionNames: (packageName?: string | null) => string[];
8
8
  export declare const getAllFunctionNames: () => string[];
@@ -6,7 +6,7 @@ import { coerceTopLevelDataFromSchema, validateSchema } from '../schema.js';
6
6
  import { parseVersionedId } from '../version.js';
7
7
  import { createFunctionSessionWireProps } from '../services/user-session-service.js';
8
8
  import { ForbiddenError, ReadonlySessionError } from '../errors/errors.js';
9
- import { createWireServicesCredentialWireProps } from '../services/credential-wire-service.js';
9
+ import { PikkuCredentialWireService, createWireServicesCredentialWireProps, } from '../services/credential-wire-service.js';
10
10
  import { rpcService } from '../wirings/rpc/rpc-runner.js';
11
11
  import { closeWireServices } from '../utils.js';
12
12
  /**
@@ -114,6 +114,13 @@ export const runPikkuFunc = async (wireType, wireId, funcName, { singletonServic
114
114
  const resolvedWire = allChannelMiddleware.length > 0 && wire.channel
115
115
  ? wrapChannelWithMiddleware(wire, resolvedSingletonServices, allChannelMiddleware)
116
116
  : wire;
117
+ // Set up credential wire service early so middleware can use setCredential.
118
+ // Skip if already set up (e.g. addon functions reuse the parent wire).
119
+ if (!resolvedWire.getCredentials) {
120
+ const resolvedCredentialWireService = credentialWireService ??
121
+ new PikkuCredentialWireService(resolvedSingletonServices.credentialService, resolvedWire);
122
+ Object.assign(resolvedWire, createWireServicesCredentialWireProps(resolvedCredentialWireService));
123
+ }
117
124
  // Convert tags to PermissionMetadata and merge with inheritedPermissions
118
125
  let mergedInheritedPermissions;
119
126
  if (tags && tags.length > 0) {
@@ -189,11 +196,9 @@ export const runPikkuFunc = async (wireType, wireId, funcName, { singletonServic
189
196
  packageName,
190
197
  });
191
198
  }
192
- if (credentialWireService) {
193
- Object.assign(resolvedWire, createWireServicesCredentialWireProps(credentialWireService));
194
- }
195
- const wireServices = await resolvedCreateWireServices?.(resolvedSingletonServices, resolvedWire);
199
+ let wireServices;
196
200
  try {
201
+ wireServices = (await resolvedCreateWireServices?.(resolvedSingletonServices, resolvedWire));
197
202
  const services = wireServices && Object.keys(wireServices).length > 0
198
203
  ? { ...resolvedSingletonServices, ...wireServices }
199
204
  : resolvedSingletonServices;
@@ -73,5 +73,6 @@ export const pikkuAuth = (auth) => {
73
73
  return false;
74
74
  return fn(services, session);
75
75
  };
76
+ wrapper.__pikkuAuth = true;
76
77
  return wrapper;
77
78
  };
@@ -1,2 +1,3 @@
1
1
  export { addFunction, getAllFunctionNames } from './function-runner.js';
2
- export type { CorePikkuFunction, CorePikkuFunctionSessionless, } from './functions.types.js';
2
+ export { pikkuAuth, pikkuPermission, pikkuPermissionFactory, pikkuApprovalDescription, } from './functions.types.js';
3
+ export type { CorePikkuFunction, CorePikkuFunctionSessionless, CorePikkuFunctionConfig, CorePikkuAuth, CorePikkuAuthConfig, CorePikkuPermission, } from './functions.types.js';
@@ -1 +1,2 @@
1
1
  export { addFunction, getAllFunctionNames } from './function-runner.js';
2
+ export { pikkuAuth, pikkuPermission, pikkuPermissionFactory, pikkuApprovalDescription, } from './functions.types.js';
@@ -5,10 +5,10 @@ import type { PikkuHTTP } from './wirings/http/http.types.js';
5
5
  *
6
6
  * @param {any} e - The error that occurred
7
7
  * @param {PikkuHTTP | undefined} http - HTTP wire object
8
- * @param {string} trackerId - Unique ID for tracking this error
8
+ * @param {string} traceId - Unique ID for tracking this error
9
9
  * @param {Logger} logger - Logger service
10
10
  * @param {number[]} logWarningsForStatusCodes - HTTP status codes to log as warnings
11
11
  * @param {boolean} respondWith404 - Whether to respond with 404 for NotFoundError
12
12
  * @param {boolean} bubbleError - Whether to throw the error after handling
13
13
  */
14
- export declare const handleHTTPError: (e: any, http: PikkuHTTP | undefined, trackerId: string | undefined, logger: Logger, logWarningsForStatusCodes: number[], respondWith404: boolean, bubbleError: boolean, exposeErrors?: boolean) => void;
14
+ export declare const handleHTTPError: (e: any, http: PikkuHTTP | undefined, traceId: string | undefined, logger: Logger, logWarningsForStatusCodes: number[], respondWith404: boolean, bubbleError: boolean, exposeErrors?: boolean) => void;
@@ -6,13 +6,13 @@ import { NotFoundError } from './errors/errors.js';
6
6
  *
7
7
  * @param {any} e - The error that occurred
8
8
  * @param {PikkuHTTP | undefined} http - HTTP wire object
9
- * @param {string} trackerId - Unique ID for tracking this error
9
+ * @param {string} traceId - Unique ID for tracking this error
10
10
  * @param {Logger} logger - Logger service
11
11
  * @param {number[]} logWarningsForStatusCodes - HTTP status codes to log as warnings
12
12
  * @param {boolean} respondWith404 - Whether to respond with 404 for NotFoundError
13
13
  * @param {boolean} bubbleError - Whether to throw the error after handling
14
14
  */
15
- export const handleHTTPError = (e, http, trackerId, logger, logWarningsForStatusCodes, respondWith404, bubbleError, exposeErrors = false) => {
15
+ export const handleHTTPError = (e, http, traceId, logger, logWarningsForStatusCodes, respondWith404, bubbleError, exposeErrors = false) => {
16
16
  // Skip 404 handling if configured to do so
17
17
  if (e instanceof NotFoundError && !respondWith404) {
18
18
  return;
@@ -25,12 +25,12 @@ export const handleHTTPError = (e, http, trackerId, logger, logWarningsForStatus
25
25
  http?.response?.json({
26
26
  message: errorResponse.message,
27
27
  payload: e.payload,
28
- traceId: trackerId,
28
+ errorId: traceId,
29
29
  });
30
30
  // Log certain status codes as warnings
31
31
  if (logWarningsForStatusCodes.includes(errorResponse.status)) {
32
- if (trackerId) {
33
- logger.warn(`Warning id: ${trackerId}`);
32
+ if (traceId) {
33
+ logger.warn(`Warning id: ${traceId}`);
34
34
  }
35
35
  logger.warn(e instanceof Error ? e.message : e);
36
36
  }
@@ -39,9 +39,9 @@ export const handleHTTPError = (e, http, trackerId, logger, logWarningsForStatus
39
39
  // Handle unexpected errors
40
40
  logger.error(e instanceof Error ? e.message : e);
41
41
  http?.response?.status(500);
42
- if (trackerId) {
43
- logger.warn(`Error id: ${trackerId}`);
44
- const errorBody = { errorId: trackerId };
42
+ if (traceId) {
43
+ logger.warn(`Error id: ${traceId}`);
44
+ const errorBody = { errorId: traceId };
45
45
  if (exposeErrors && !isProduction() && e instanceof Error) {
46
46
  errorBody.message = e.message;
47
47
  errorBody.stack = e.stack;
package/dist/index.d.ts CHANGED
@@ -36,12 +36,11 @@ export type { AIStorageService } from './services/ai-storage-service.js';
36
36
  export type { HTTPMethod } from './wirings/http/http.types.js';
37
37
  export type { GraphNodeConfig } from './wirings/workflow/graph/workflow-graph.types.js';
38
38
  export { createGraph } from './wirings/workflow/graph/graph-node.js';
39
- export { workflow as wireWorkflow } from './wirings/workflow/workflow-helpers.js';
40
39
  export { wireAddon } from './wirings/rpc/wire-addon.js';
41
40
  export type { WireAddonConfig } from './wirings/rpc/wire-addon.js';
42
41
  export type { PikkuPackageState } from './types/state.types.js';
43
42
  export { runMiddleware, addMiddleware } from './middleware-runner.js';
44
- export { addPermission } from './permissions.js';
43
+ export { addPermission, checkAuthPermissions } from './permissions.js';
45
44
  export { isSerializable, stopSingletonServices } from './utils.js';
46
45
  export { getSingletonServices, getCreateWireServices } from './pikku-state.js';
47
46
  export { type ScheduledTaskInfo, type ScheduledTaskSummary, } from './services/scheduler-service.js';
package/dist/index.js CHANGED
@@ -12,10 +12,9 @@ export { runQueueJob } from './wirings/queue/queue-runner.js';
12
12
  export { runScheduledTask } from './wirings/scheduler/scheduler-runner.js';
13
13
  export { NotFoundError } from './errors/errors.js';
14
14
  export { createGraph } from './wirings/workflow/graph/graph-node.js';
15
- export { workflow as wireWorkflow } from './wirings/workflow/workflow-helpers.js';
16
15
  export { wireAddon } from './wirings/rpc/wire-addon.js';
17
16
  export { runMiddleware, addMiddleware } from './middleware-runner.js';
18
- export { addPermission } from './permissions.js';
17
+ export { addPermission, checkAuthPermissions } from './permissions.js';
19
18
  export { isSerializable, stopSingletonServices } from './utils.js';
20
19
  export { getSingletonServices, getCreateWireServices } from './pikku-state.js';
21
20
  export { SchedulerService } from './services/scheduler-service.js';
@@ -3,3 +3,5 @@ export { authCookie } from './auth-cookie.js';
3
3
  export { authBearer } from './auth-bearer.js';
4
4
  export { pikkuRemoteAuthMiddleware } from './remote-auth.js';
5
5
  export { cors } from './cors.js';
6
+ export { addMiddleware, runMiddleware } from '../middleware-runner.js';
7
+ export { addPermission } from '../permissions.js';
@@ -3,3 +3,5 @@ export { authCookie } from './auth-cookie.js';
3
3
  export { authBearer } from './auth-bearer.js';
4
4
  export { pikkuRemoteAuthMiddleware } from './remote-auth.js';
5
5
  export { cors } from './cors.js';
6
+ export { addMiddleware, runMiddleware } from '../middleware-runner.js';
7
+ export { addPermission } from '../permissions.js';
@@ -10,6 +10,9 @@ export const pikkuRemoteAuthMiddleware = pikkuMiddleware(async ({ secrets, jwt }
10
10
  secret = await secrets.getSecret('PIKKU_REMOTE_SECRET');
11
11
  }
12
12
  catch {
13
+ if (http.request.path().startsWith('/remote/rpc/')) {
14
+ throw new UnauthorizedError();
15
+ }
13
16
  return next();
14
17
  }
15
18
  if (!jwt) {
@@ -34,8 +37,8 @@ export const pikkuRemoteAuthMiddleware = pikkuMiddleware(async ({ secrets, jwt }
34
37
  if (payload?.aud !== 'pikku-remote') {
35
38
  throw new UnauthorizedError();
36
39
  }
37
- if (payload?.fn && http.request.path().startsWith('/rpc/')) {
38
- const fn = decodeURIComponent(http.request.path().slice('/rpc/'.length));
40
+ if (payload?.fn && http.request.path().startsWith('/remote/rpc/')) {
41
+ const fn = decodeURIComponent(http.request.path().slice('/remote/rpc/'.length));
39
42
  if (fn && payload.fn !== fn) {
40
43
  throw new UnauthorizedError();
41
44
  }
@@ -1,4 +1,4 @@
1
- import type { CoreServices, PikkuWiringTypes, PermissionMetadata, PikkuWire } from './types/core.types.js';
1
+ import type { CoreServices, CoreUserSession, PikkuWiringTypes, PermissionMetadata, PikkuWire } from './types/core.types.js';
2
2
  import type { CorePermissionGroup, CorePikkuPermission } from './function/functions.types.js';
3
3
  /**
4
4
  * Adds global permissions for a specific tag.
@@ -44,3 +44,15 @@ export declare const runPermissions: (wireType: PikkuWiringTypes, uid: string, {
44
44
  data: any;
45
45
  packageName?: string | null;
46
46
  }) => Promise<void>;
47
+ /**
48
+ * Checks whether a session passes the auth checks (pikkuAuth only) for a
49
+ * given function/agent. Skips pikkuPermission checks since those require
50
+ * request data which isn't available at filter time.
51
+ *
52
+ * @param funcPermissions - The PermissionMetadata[] from function or agent metadata
53
+ * @param session - The user's session
54
+ * @param services - Singleton services
55
+ * @param packageName - Optional package namespace
56
+ * @returns true if the session passes all auth checks (or no auth checks exist)
57
+ */
58
+ export declare const checkAuthPermissions: (funcPermissions: PermissionMetadata[] | undefined, session: CoreUserSession, services: CoreServices, packageName?: string | null) => Promise<boolean>;
@@ -234,3 +234,54 @@ export const runPermissions = async (wireType, uid, { wireInheritedPermissions,
234
234
  throw new ForbiddenError('Permission denied');
235
235
  }
236
236
  };
237
+ /**
238
+ * Checks whether a session passes the auth checks (pikkuAuth only) for a
239
+ * given function/agent. Skips pikkuPermission checks since those require
240
+ * request data which isn't available at filter time.
241
+ *
242
+ * @param funcPermissions - The PermissionMetadata[] from function or agent metadata
243
+ * @param session - The user's session
244
+ * @param services - Singleton services
245
+ * @param packageName - Optional package namespace
246
+ * @returns true if the session passes all auth checks (or no auth checks exist)
247
+ */
248
+ export const checkAuthPermissions = async (funcPermissions, session, services, packageName = null) => {
249
+ if (!funcPermissions?.length)
250
+ return true;
251
+ const allPermissions = combinePermissions('agent', `authcheck:${Math.random()}`, {
252
+ funcInheritedPermissions: funcPermissions,
253
+ packageName,
254
+ });
255
+ if (allPermissions.length === 0)
256
+ return true;
257
+ const wire = { session };
258
+ // Extract only pikkuAuth permissions (marked with __pikkuAuth)
259
+ const authPerms = [];
260
+ for (const permission of allPermissions) {
261
+ if (typeof permission === 'function') {
262
+ if (permission.__pikkuAuth) {
263
+ authPerms.push(permission);
264
+ }
265
+ }
266
+ else if (permission && typeof permission === 'object') {
267
+ for (const funcs of Object.values(permission)) {
268
+ const arr = Array.isArray(funcs) ? funcs : [funcs];
269
+ for (const fn of arr) {
270
+ if (typeof fn === 'function' && fn.__pikkuAuth) {
271
+ authPerms.push(fn);
272
+ }
273
+ }
274
+ }
275
+ }
276
+ }
277
+ // No auth permissions = allowed (only data-dependent permissions exist)
278
+ if (authPerms.length === 0)
279
+ return true;
280
+ // All auth permissions must pass
281
+ for (const perm of authPerms) {
282
+ const result = await perm(services, null, wire);
283
+ if (result)
284
+ return true;
285
+ }
286
+ return false;
287
+ };
@@ -23,7 +23,6 @@ export declare const pikkuState: <Type extends keyof PikkuPackageState, Content
23
23
  */
24
24
  export declare const initializePikkuState: (packageName: string) => void;
25
25
  export declare const resetPikkuState: () => void;
26
- export declare const getPikkuMetaDir: (packageName?: string | null) => string | null;
27
26
  export declare const getSingletonServices: () => CoreSingletonServices;
28
27
  export declare const getCreateWireServices: () => CreateWireServices | undefined;
29
28
  /**
@@ -126,7 +126,8 @@ const createEmptyPackageState = () => ({
126
126
  package: {
127
127
  factories: null,
128
128
  singletonServices: null,
129
- metaDir: null,
129
+ credentialsMeta: null,
130
+ requiredParentServices: null,
130
131
  },
131
132
  });
132
133
  /**
@@ -151,9 +152,6 @@ export const resetPikkuState = () => {
151
152
  if (!getAllPackageStates().has('__main__')) {
152
153
  resetPikkuState();
153
154
  }
154
- export const getPikkuMetaDir = (packageName) => {
155
- return pikkuState(packageName ?? null, 'package', 'metaDir');
156
- };
157
155
  export const getSingletonServices = () => {
158
156
  const services = pikkuState(null, 'package', 'singletonServices');
159
157
  if (!services)
@@ -0,0 +1,10 @@
1
+ import type { JWTService } from './services/jwt-service.js';
2
+ import type { SecretService } from './services/secret-service.js';
3
+ /**
4
+ * Build Authorization headers with JWT-signed session and traceId for
5
+ * pikkuRemoteAuthMiddleware on the receiving end.
6
+ *
7
+ * Used by all deployment services (Kysely, Redis, MongoDB, Lambda, CF, Azure)
8
+ * regardless of transport (HTTP, Lambda Invoke, service bindings).
9
+ */
10
+ export declare function buildRemoteHeaders(jwt: JWTService | undefined, secrets: SecretService | undefined, funcName: string, session?: unknown, traceId?: string): Promise<Record<string, string>>;
package/dist/remote.js ADDED
@@ -0,0 +1,32 @@
1
+ import { encryptJSON } from './crypto-utils.js';
2
+ /**
3
+ * Build Authorization headers with JWT-signed session and traceId for
4
+ * pikkuRemoteAuthMiddleware on the receiving end.
5
+ *
6
+ * Used by all deployment services (Kysely, Redis, MongoDB, Lambda, CF, Azure)
7
+ * regardless of transport (HTTP, Lambda Invoke, service bindings).
8
+ */
9
+ export async function buildRemoteHeaders(jwt, secrets, funcName, session, traceId) {
10
+ const headers = {
11
+ 'content-type': 'application/json',
12
+ ...(traceId && { 'x-request-id': traceId }),
13
+ };
14
+ let secret;
15
+ try {
16
+ secret = await secrets?.getSecret('PIKKU_REMOTE_SECRET');
17
+ }
18
+ catch { }
19
+ if (secret && jwt) {
20
+ const sessionEnc = session
21
+ ? await encryptJSON(secret, { session })
22
+ : undefined;
23
+ const token = await jwt.encode({ value: 5, unit: 'minute' }, {
24
+ aud: 'pikku-remote',
25
+ fn: funcName,
26
+ iat: Math.floor(Date.now() / 1000),
27
+ session: sessionEnc,
28
+ });
29
+ headers.authorization = `Bearer ${token}`;
30
+ }
31
+ return headers;
32
+ }
package/dist/schema.js CHANGED
@@ -67,6 +67,8 @@ const validateAllSchemasLoaded = (logger, schemaService) => {
67
67
  };
68
68
  export const coerceTopLevelDataFromSchema = (schemaName, data, packageName = null) => {
69
69
  const schema = pikkuState(packageName, 'misc', 'schemas').get(schemaName);
70
+ if (!schema?.properties)
71
+ return;
70
72
  for (const key in schema.properties) {
71
73
  const property = schema.properties[key];
72
74
  if (typeof property === 'boolean') {
@@ -37,4 +37,15 @@ export interface CredentialService {
37
37
  * @returns A record of credential name to value.
38
38
  */
39
39
  getAll(userId: string): Promise<Record<string, unknown>>;
40
+ /**
41
+ * Lists all user IDs that have a specific credential configured.
42
+ * @param name - The credential name.
43
+ * @returns Array of user IDs.
44
+ */
45
+ getUsersWithCredential(name: string): Promise<string[]>;
46
+ /**
47
+ * Lists all unique user IDs that have any credential configured.
48
+ * @returns Array of user IDs.
49
+ */
50
+ getAllUsers(): Promise<string[]>;
40
51
  }
@@ -1,13 +1,24 @@
1
+ import type { CredentialService } from './credential-service.js';
2
+ import type { PikkuWire } from '../types/core.types.js';
1
3
  export declare class PikkuCredentialWireService {
4
+ private credentialService?;
5
+ private wire?;
2
6
  private credentials;
7
+ private loaded;
8
+ private loadPromise;
9
+ constructor(credentialService?: CredentialService | undefined, wire?: PikkuWire | undefined);
3
10
  set(name: string, value: unknown): void;
4
- getAll(): Record<string, unknown>;
5
- getScoped(allowedNames: string[]): Record<string, unknown>;
11
+ get<T = unknown>(name: string): T | null | Promise<T | null>;
12
+ getAll(): Record<string, unknown> | Promise<Record<string, unknown>>;
13
+ getScoped(allowedNames: string[]): Record<string, unknown> | Promise<Record<string, unknown>>;
14
+ private lazyLoad;
15
+ private doLoad;
6
16
  }
7
17
  export declare function createMiddlewareCredentialWireProps(credentialWire: PikkuCredentialWireService): {
8
18
  setCredential: (name: string, value: unknown) => void;
9
19
  };
10
20
  export declare function createWireServicesCredentialWireProps(credentialWire: PikkuCredentialWireService, allowedNames?: string[]): {
11
21
  setCredential: (name: string, value: unknown) => void;
12
- getCredentials: () => Record<string, unknown>;
22
+ getCredential: <T = unknown>(name: string) => T | Promise<T | null> | null;
23
+ getCredentials: () => Record<string, unknown> | Promise<Record<string, unknown>>;
13
24
  };
@@ -1,19 +1,61 @@
1
+ import { defaultPikkuUserIdResolver } from './pikku-user-id.js';
1
2
  export class PikkuCredentialWireService {
3
+ credentialService;
4
+ wire;
2
5
  credentials = {};
6
+ loaded = false;
7
+ loadPromise;
8
+ constructor(credentialService, wire) {
9
+ this.credentialService = credentialService;
10
+ this.wire = wire;
11
+ }
3
12
  set(name, value) {
4
13
  this.credentials[name] = value;
5
14
  }
15
+ get(name) {
16
+ if (this.loaded)
17
+ return this.credentials[name] ?? null;
18
+ return this.lazyLoad().then(() => this.credentials[name] ?? null);
19
+ }
6
20
  getAll() {
7
- return this.credentials;
21
+ if (this.loaded)
22
+ return this.credentials;
23
+ return this.lazyLoad().then(() => this.credentials);
8
24
  }
9
25
  getScoped(allowedNames) {
10
- const scoped = {};
11
- for (const name of allowedNames) {
12
- if (name in this.credentials) {
13
- scoped[name] = this.credentials[name];
26
+ const buildScoped = () => {
27
+ const scoped = {};
28
+ for (const name of allowedNames) {
29
+ if (name in this.credentials) {
30
+ scoped[name] = this.credentials[name];
31
+ }
32
+ }
33
+ return scoped;
34
+ };
35
+ if (this.loaded)
36
+ return buildScoped();
37
+ return this.lazyLoad().then(buildScoped);
38
+ }
39
+ lazyLoad() {
40
+ if (this.loadPromise)
41
+ return this.loadPromise;
42
+ this.loadPromise = this.doLoad();
43
+ return this.loadPromise;
44
+ }
45
+ async doLoad() {
46
+ this.loaded = true;
47
+ if (!this.credentialService || !this.wire)
48
+ return;
49
+ const userId = defaultPikkuUserIdResolver(this.wire);
50
+ if (!userId)
51
+ return;
52
+ this.wire.pikkuUserId = userId;
53
+ const allCreds = await this.credentialService.getAll(userId);
54
+ for (const [name, value] of Object.entries(allCreds)) {
55
+ if (!(name in this.credentials)) {
56
+ this.credentials[name] = value;
14
57
  }
15
58
  }
16
- return scoped;
17
59
  }
18
60
  }
19
61
  export function createMiddlewareCredentialWireProps(credentialWire) {
@@ -24,6 +66,7 @@ export function createMiddlewareCredentialWireProps(credentialWire) {
24
66
  export function createWireServicesCredentialWireProps(credentialWire, allowedNames) {
25
67
  return {
26
68
  setCredential: (name, value) => credentialWire.set(name, value),
69
+ getCredential: (name) => credentialWire.get(name),
27
70
  getCredentials: () => allowedNames
28
71
  ? credentialWire.getScoped(allowedNames)
29
72
  : credentialWire.getAll(),
@@ -15,5 +15,16 @@ export interface DeploymentService {
15
15
  init(): Promise<void>;
16
16
  start(config: DeploymentConfig): Promise<void>;
17
17
  stop(): Promise<void>;
18
- findFunction(name: string): Promise<DeploymentInfo[]>;
18
+ /**
19
+ * Dispatch a remote RPC call to a function.
20
+ * The deployment service owns the full transport:
21
+ * - Resolving the target (endpoint, service binding, etc.)
22
+ * - Session propagation (JWT signing, headers)
23
+ * - The actual network call
24
+ *
25
+ * @param funcName - The function to invoke
26
+ * @param data - Input data for the function
27
+ * @param session - User session to propagate (optional)
28
+ */
29
+ invoke(funcName: string, data: unknown, session?: unknown, traceId?: string): Promise<unknown>;
19
30
  }
@@ -0,0 +1,7 @@
1
+ import type { QueueService, JobOptions } from '../wirings/queue/queue.types.js';
2
+ export declare class InMemoryQueueService implements QueueService {
3
+ readonly supportsResults = false;
4
+ private jobCounter;
5
+ add<T>(queueName: string, data: T, options?: JobOptions): Promise<string>;
6
+ getJob(): Promise<null>;
7
+ }