@pikku/inspector 0.12.0 → 0.12.1

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 (109) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/add/add-ai-agent.d.ts +1 -1
  3. package/dist/add/add-ai-agent.js +1 -1
  4. package/dist/add/add-channel.js +12 -0
  5. package/dist/add/add-cli.d.ts +1 -1
  6. package/dist/add/add-cli.js +6 -1
  7. package/dist/add/add-file-extends-core-type.d.ts +1 -1
  8. package/dist/add/add-file-with-config.d.ts +1 -1
  9. package/dist/add/add-file-with-factory.d.ts +1 -1
  10. package/dist/add/add-file-with-factory.js +2 -2
  11. package/dist/add/add-functions.d.ts +1 -1
  12. package/dist/add/add-functions.js +6 -7
  13. package/dist/add/add-http-route.d.ts +3 -2
  14. package/dist/add/add-http-route.js +5 -1
  15. package/dist/add/add-http-routes.d.ts +1 -1
  16. package/dist/add/add-http-routes.js +1 -0
  17. package/dist/add/add-keyed-wiring.d.ts +1 -1
  18. package/dist/add/add-mcp-prompt.d.ts +1 -1
  19. package/dist/add/add-mcp-resource.d.ts +1 -1
  20. package/dist/add/add-queue-worker.d.ts +1 -1
  21. package/dist/add/add-rpc-invocations.d.ts +3 -3
  22. package/dist/add/add-rpc-invocations.js +10 -10
  23. package/dist/add/add-schedule.d.ts +1 -1
  24. package/dist/add/add-secret.d.ts +1 -1
  25. package/dist/add/add-trigger.d.ts +1 -1
  26. package/dist/add/add-trigger.js +2 -2
  27. package/dist/add/add-wire-addon.d.ts +7 -0
  28. package/dist/add/add-wire-addon.js +70 -0
  29. package/dist/add/add-workflow.d.ts +1 -1
  30. package/dist/error-codes.d.ts +1 -0
  31. package/dist/error-codes.js +1 -0
  32. package/dist/index.d.ts +1 -1
  33. package/dist/inspector.d.ts +1 -1
  34. package/dist/inspector.js +7 -4
  35. package/dist/types.d.ts +26 -20
  36. package/dist/utils/compute-required-schemas.js +1 -2
  37. package/dist/utils/contract-hashes.d.ts +20 -3
  38. package/dist/utils/contract-hashes.js +77 -10
  39. package/dist/utils/custom-types-generator.d.ts +1 -1
  40. package/dist/utils/detect-schema-vendor.d.ts +1 -1
  41. package/dist/utils/does-type-extend-core-type.d.ts +1 -1
  42. package/dist/utils/ensure-function-metadata.d.ts +1 -1
  43. package/dist/utils/extract-services.d.ts +1 -1
  44. package/dist/utils/filter-inspector-state.d.ts +1 -1
  45. package/dist/utils/filter-utils.d.ts +2 -2
  46. package/dist/utils/get-files-and-methods.d.ts +1 -1
  47. package/dist/utils/middleware.d.ts +2 -2
  48. package/dist/utils/permissions.d.ts +2 -2
  49. package/dist/utils/post-process.d.ts +4 -3
  50. package/dist/utils/post-process.js +24 -8
  51. package/dist/utils/resolve-addon-package.d.ts +16 -0
  52. package/dist/utils/{resolve-external-package.js → resolve-addon-package.js} +8 -8
  53. package/dist/utils/schema-generator.d.ts +2 -2
  54. package/dist/utils/serialize-inspector-state.d.ts +13 -3
  55. package/dist/utils/serialize-inspector-state.js +6 -2
  56. package/dist/utils/validate-auth-sessionless.d.ts +3 -0
  57. package/dist/utils/validate-auth-sessionless.js +14 -0
  58. package/dist/utils/workflow/dsl/extract-dsl-workflow.d.ts +1 -1
  59. package/dist/visit.d.ts +1 -1
  60. package/dist/visit.js +2 -0
  61. package/package.json +2 -2
  62. package/src/add/add-ai-agent.ts +2 -2
  63. package/src/add/add-channel.ts +21 -0
  64. package/src/add/add-cli.ts +19 -4
  65. package/src/add/add-file-extends-core-type.ts +1 -1
  66. package/src/add/add-file-with-config.ts +1 -1
  67. package/src/add/add-file-with-factory.ts +3 -3
  68. package/src/add/add-functions.ts +21 -19
  69. package/src/add/add-http-route.ts +19 -2
  70. package/src/add/add-http-routes.ts +2 -1
  71. package/src/add/add-keyed-wiring.ts +1 -1
  72. package/src/add/add-mcp-prompt.ts +1 -1
  73. package/src/add/add-mcp-resource.ts +1 -1
  74. package/src/add/add-queue-worker.ts +1 -1
  75. package/src/add/add-rpc-invocations.ts +11 -11
  76. package/src/add/add-schedule.ts +1 -1
  77. package/src/add/add-secret.ts +1 -1
  78. package/src/add/add-trigger.ts +4 -4
  79. package/src/add/add-wire-addon.ts +80 -0
  80. package/src/add/add-workflow.ts +2 -2
  81. package/src/error-codes.ts +1 -0
  82. package/src/index.ts +1 -0
  83. package/src/inspector.ts +13 -5
  84. package/src/types.ts +33 -20
  85. package/src/utils/compute-required-schemas.ts +1 -2
  86. package/src/utils/contract-hashes.test.ts +30 -5
  87. package/src/utils/contract-hashes.ts +110 -14
  88. package/src/utils/custom-types-generator.ts +1 -1
  89. package/src/utils/detect-schema-vendor.ts +1 -1
  90. package/src/utils/does-type-extend-core-type.ts +1 -1
  91. package/src/utils/ensure-function-metadata.ts +1 -1
  92. package/src/utils/extract-services.ts +1 -1
  93. package/src/utils/filter-inspector-state.test.ts +3 -5
  94. package/src/utils/filter-inspector-state.ts +7 -2
  95. package/src/utils/filter-utils.test.ts +1 -1
  96. package/src/utils/filter-utils.ts +2 -2
  97. package/src/utils/get-files-and-methods.ts +1 -1
  98. package/src/utils/middleware.ts +2 -2
  99. package/src/utils/permissions.ts +2 -2
  100. package/src/utils/post-process.ts +34 -12
  101. package/src/utils/{resolve-external-package.ts → resolve-addon-package.ts} +17 -10
  102. package/src/utils/resolve-versions.test.ts +1 -1
  103. package/src/utils/schema-generator.ts +4 -4
  104. package/src/utils/serialize-inspector-state.ts +23 -5
  105. package/src/utils/validate-auth-sessionless.ts +29 -0
  106. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +2 -2
  107. package/src/visit.ts +7 -1
  108. package/tsconfig.tsbuildinfo +1 -1
  109. package/dist/utils/resolve-external-package.d.ts +0 -12
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  ## 0.12.0
2
2
 
3
+ ## 0.12.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 62a8725: Rename 'external' to 'addon' throughout the codebase. All types, functions, config keys, and CLI options previously named `external` or `External` are now named `addon` or `Addon` (e.g. `ExternalPackageConfig` → `AddonConfig`, `externalPackages` → `addons`, `function-external` → `function-addon`).
8
+ - 8eed717: Add `readonly` flag to function config and runtime enforcement. Functions can be marked `readonly: true` in their config. At runtime, if a session has `readonly: true`, only functions marked as readonly can be called — otherwise a `ReadonlySessionError` (403) is thrown.
9
+ - 62a8725: `pikku versions check` now prints rich, human-readable output for all contract version errors instead of raw error codes. Each error type (PKU861–PKU865) shows the function name, separate input/output schema hashes with a `prev → current` arrow, and clear next-step instructions.
10
+
11
+ The version manifest now stores separate `inputHash` and `outputHash` per version entry (backward-compatible — old string-hash manifests still load and validate correctly). `VersionValidateError` gains optional detail fields (`functionKey`, `version`, `previousInputHash`, `currentInputHash`, `previousOutputHash`, `currentOutputHash`, `nextVersion`, `latestVersion`, `expectedNextVersion`) for use by tooling.
12
+
13
+ - 62a8725: Replace config-based addon declarations with the new `wireAddon()` code-based API. Addons are now declared directly in wiring files using `wireAddon({ name, package, rpcEndpoint?, auth?, tags? })` instead of the `addons` field in `pikku.config.json`. The inspector reads these declarations from the TypeScript AST at build time.
14
+ - 62a8725: Add `secretOverrides` and `variableOverrides` support to `wireAddon()`. These optional maps allow an app to remap an addon's secret/variable keys to its own names (e.g. `secretOverrides: { SENDGRID_API_KEY: 'MY_EMAIL_API_KEY' }`). The inspector validates that all override keys exist in the app's own secrets/variables definitions.
15
+ - Updated dependencies [62a8725]
16
+ - Updated dependencies [a3bdb0d]
17
+ - Updated dependencies [e0349ff]
18
+ - Updated dependencies [62a8725]
19
+ - Updated dependencies [e04531f]
20
+ - Updated dependencies [62a8725]
21
+ - Updated dependencies [a83efb8]
22
+ - Updated dependencies [8eed717]
23
+ - Updated dependencies [62a8725]
24
+ - Updated dependencies [62a8725]
25
+ - Updated dependencies [62a8725]
26
+ - @pikku/core@0.12.1
27
+
3
28
  ### New Features
4
29
 
5
30
  - AI agent metadata extraction
@@ -1,2 +1,2 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  export declare const addAIAgent: AddWiring;
@@ -33,7 +33,7 @@ function resolveToolReferences(obj, checker, agentName, logger) {
33
33
  continue;
34
34
  }
35
35
  }
36
- if (calleeName === 'external') {
36
+ if (calleeName === 'addon') {
37
37
  const [firstArg] = element.arguments;
38
38
  if (firstArg && ts.isStringLiteral(firstArg)) {
39
39
  resolved.push(firstArg.text);
@@ -7,6 +7,7 @@ import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
7
7
  import { resolveMiddleware, resolveChannelMiddleware, } from '../utils/middleware.js';
8
8
  import { extractWireNames } from '../utils/post-process.js';
9
9
  import { resolveIdentifier } from '../utils/resolve-identifier.js';
10
+ import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js';
10
11
  /**
11
12
  * Safely get the "initializer" expression of a property-like AST node:
12
13
  * - for `foo: expr`, returns `expr`
@@ -416,6 +417,17 @@ export const addChannel = (logger, node, checker, state, options) => {
416
417
  }
417
418
  }
418
419
  extractWireNames(middleware).forEach((name) => state.serviceAggregation.usedMiddleware.add(name));
420
+ // --- validate auth/sessionless ---
421
+ const handlersToValidate = [
422
+ connectFuncId,
423
+ disconnectFuncId,
424
+ message?.pikkuFuncId,
425
+ ].filter(Boolean);
426
+ for (const funcId of handlersToValidate) {
427
+ if (!validateAuthSessionless(logger, obj, state, funcId, `Channel '${name}'`)) {
428
+ return;
429
+ }
430
+ }
419
431
  state.channels.files.add(node.getSourceFile().fileName);
420
432
  state.channels.meta[name] = {
421
433
  name,
@@ -1,4 +1,4 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  /**
3
3
  * Adds CLI command metadata to the inspector state
4
4
  */
@@ -4,6 +4,7 @@ import { resolveMiddleware } from '../utils/middleware.js';
4
4
  import { extractWireNames } from '../utils/post-process.js';
5
5
  import { getPropertyValue } from '../utils/get-property-value.js';
6
6
  import { resolveIdentifier } from '../utils/resolve-identifier.js';
7
+ import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js';
7
8
  // Track if we've warned about missing Config type to avoid duplicate warnings
8
9
  const configTypeWarningShown = new Set();
9
10
  /**
@@ -270,10 +271,14 @@ function processCommand(logger, inspectorState, options, name, node, sourceFile,
270
271
  break;
271
272
  }
272
273
  }
274
+ // --- validate auth/sessionless ---
275
+ if (meta.pikkuFuncId &&
276
+ !validateAuthSessionless(logger, node, inspectorState, meta.pikkuFuncId, `CLI command '${name}'`)) {
277
+ return null;
278
+ }
273
279
  // --- track used functions/middleware for service aggregation ---
274
280
  inspectorState.serviceAggregation.usedFunctions.add(meta.pikkuFuncId);
275
281
  extractWireNames(meta.middleware).forEach((name) => inspectorState.serviceAggregation.usedMiddleware.add(name));
276
- // Note: subcommands are tracked recursively when they're processed
277
282
  return meta;
278
283
  }
279
284
  /**
@@ -1,3 +1,3 @@
1
1
  import * as ts from 'typescript';
2
- import { PathToNameAndType, InspectorState } from '../types.js';
2
+ import type { PathToNameAndType, InspectorState } from '../types.js';
3
3
  export declare const addFileExtendsCoreType: (node: ts.Node, checker: ts.TypeChecker, methods: PathToNameAndType, expectedTypeName: string, state?: InspectorState) => void;
@@ -1,3 +1,3 @@
1
1
  import * as ts from 'typescript';
2
- import { PathToNameAndType } from '../types.js';
2
+ import type { PathToNameAndType } from '../types.js';
3
3
  export declare const addFileWithConfig: (node: ts.Node, checker: ts.TypeChecker, configs: PathToNameAndType) => void;
@@ -1,3 +1,3 @@
1
1
  import * as ts from 'typescript';
2
- import { PathToNameAndType, InspectorState } from '../types.js';
2
+ import type { PathToNameAndType, InspectorState } from '../types.js';
3
3
  export declare const addFileWithFactory: (node: ts.Node, checker: ts.TypeChecker, methods: PathToNameAndType | undefined, expectedTypeName: string, state?: InspectorState) => void;
@@ -3,9 +3,9 @@ import { extractServicesFromFunction } from '../utils/extract-services.js';
3
3
  // Mapping of wrapper function names to their corresponding types
4
4
  const wrapperFunctionMap = {
5
5
  pikkuConfig: 'CreateConfig',
6
- pikkuExternalConfig: 'CreateConfig',
6
+ pikkuAddonConfig: 'CreateConfig',
7
7
  pikkuServices: 'CreateSingletonServices',
8
- pikkuExternalServices: 'CreateSingletonServices',
8
+ pikkuAddonServices: 'CreateSingletonServices',
9
9
  pikkuWireServices: 'CreateWireServices',
10
10
  };
11
11
  export const addFileWithFactory = (node, checker, methods = new Map(), expectedTypeName, state) => {
@@ -1,4 +1,4 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  /**
3
3
  * Inspect pikkuFunc calls, extract input/output and first-arg destructuring,
4
4
  * then push into state.functions.meta.
@@ -154,9 +154,7 @@ const getNamesAndTypes = (checker, typesMap, direction, funcName, type) => {
154
154
  const aliasType = rawNames.join(' | ');
155
155
  const aliasName = funcIdToTypeName(funcName) + direction;
156
156
  // record the alias in your TypesMap
157
- const references = rawTypes
158
- .map((t) => resolveTypeImports(t, typesMap, true, checker))
159
- .flat();
157
+ const references = rawTypes.flatMap((t) => resolveTypeImports(t, typesMap, true, checker));
160
158
  typesMap.addCustomType(aliasName, aliasType, references);
161
159
  return {
162
160
  names: [aliasName],
@@ -164,8 +162,7 @@ const getNamesAndTypes = (checker, typesMap, direction, funcName, type) => {
164
162
  };
165
163
  }
166
164
  // 4) Single, valid name → inline it
167
- const uniqueNames = rawNames
168
- .map((name, i) => {
165
+ const uniqueNames = rawNames.flatMap((name, i) => {
169
166
  const t = rawTypes[i];
170
167
  if (!t) {
171
168
  throw new Error(`Expected type for name "${name}" in ${funcName}`);
@@ -175,8 +172,7 @@ const getNamesAndTypes = (checker, typesMap, direction, funcName, type) => {
175
172
  }
176
173
  // non-primitive: import/alias it inline
177
174
  return resolveTypeImports(t, typesMap, false, checker);
178
- })
179
- .flat();
175
+ });
180
176
  return {
181
177
  names: uniqueNames,
182
178
  types: rawTypes,
@@ -248,6 +244,7 @@ export const addFunctions = (logger, node, checker, state, options) => {
248
244
  let expose;
249
245
  let remote;
250
246
  let mcp;
247
+ let readonly_;
251
248
  let requiresApproval;
252
249
  let version;
253
250
  let objectNode;
@@ -313,6 +310,7 @@ export const addFunctions = (logger, node, checker, state, options) => {
313
310
  expose = getPropertyValue(firstArg, 'expose');
314
311
  remote = getPropertyValue(firstArg, 'remote');
315
312
  mcp = getPropertyValue(firstArg, 'mcp');
313
+ readonly_ = getPropertyValue(firstArg, 'readonly');
316
314
  requiresApproval = getPropertyValue(firstArg, 'requiresApproval');
317
315
  const versionRaw = getPropertyValue(firstArg, 'version');
318
316
  if (versionRaw !== null && versionRaw !== undefined) {
@@ -566,6 +564,7 @@ export const addFunctions = (logger, node, checker, state, options) => {
566
564
  expose: expose || undefined,
567
565
  remote: remote || undefined,
568
566
  mcp: mcpEnabled || undefined,
567
+ readonly: readonly_ || undefined,
569
568
  requiresApproval: requiresApproval || undefined,
570
569
  version,
571
570
  title,
@@ -1,5 +1,5 @@
1
1
  import * as ts from 'typescript';
2
- import { AddWiring, InspectorState } from '../types.js';
2
+ import type { AddWiring, InspectorState } from '../types.js';
3
3
  import type { InspectorLogger } from '../types.js';
4
4
  /**
5
5
  * Parameters for registering an HTTP route
@@ -12,12 +12,13 @@ export interface RegisterHTTPRouteParams {
12
12
  sourceFile: ts.SourceFile;
13
13
  basePath?: string;
14
14
  inheritedTags?: string[];
15
+ inheritedAuth?: boolean;
15
16
  }
16
17
  /**
17
18
  * Shared function to register an HTTP route in the inspector state.
18
19
  * Used by both wireHTTP and wireHTTPRoutes.
19
20
  */
20
- export declare function registerHTTPRoute({ obj, state, checker, logger, sourceFile, basePath, inheritedTags, }: RegisterHTTPRouteParams): void;
21
+ export declare function registerHTTPRoute({ obj, state, checker, logger, sourceFile, basePath, inheritedTags, inheritedAuth, }: RegisterHTTPRouteParams): void;
21
22
  /**
22
23
  * Process wireHTTP calls
23
24
  */
@@ -8,6 +8,7 @@ import { resolveHTTPPermissionsFromObject } from '../utils/permissions.js';
8
8
  import { extractWireNames } from '../utils/post-process.js';
9
9
  import { ensureFunctionMetadata } from '../utils/ensure-function-metadata.js';
10
10
  import { ErrorCode } from '../error-codes.js';
11
+ import { validateAuthSessionless } from '../utils/validate-auth-sessionless.js';
11
12
  import { detectSchemaVendorOrError } from '../utils/detect-schema-vendor.js';
12
13
  /**
13
14
  * Extract header schema reference from headers property
@@ -76,7 +77,7 @@ const computeInputTypes = (metaTypes, methodType, inputType, queryValues, params
76
77
  * Shared function to register an HTTP route in the inspector state.
77
78
  * Used by both wireHTTP and wireHTTPRoutes.
78
79
  */
79
- export function registerHTTPRoute({ obj, state, checker, logger, sourceFile, basePath = '', inheritedTags = [], }) {
80
+ export function registerHTTPRoute({ obj, state, checker, logger, sourceFile, basePath = '', inheritedTags = [], inheritedAuth, }) {
80
81
  // Extract route path
81
82
  const routePath = getPropertyValue(obj, 'route');
82
83
  if (!routePath)
@@ -118,6 +119,9 @@ export function registerHTTPRoute({ obj, state, checker, logger, sourceFile, bas
118
119
  logger.critical(ErrorCode.FUNCTION_METADATA_NOT_FOUND, `No function metadata found for '${funcName}'.`);
119
120
  return;
120
121
  }
122
+ if (!validateAuthSessionless(logger, obj, state, funcName, `Route '${fullRoute}'`, inheritedAuth)) {
123
+ return;
124
+ }
121
125
  const input = fnMeta.inputs?.[0] || null;
122
126
  // Validate that route params and query params exist in function input type
123
127
  if (params.length > 0 || query.length > 0) {
@@ -1,4 +1,4 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  /**
3
3
  * Process wireHTTPRoutes calls
4
4
  */
@@ -155,5 +155,6 @@ function processRoute(obj, groupConfig, state, checker, logger, sourceFile) {
155
155
  sourceFile,
156
156
  basePath: groupConfig.basePath,
157
157
  inheritedTags: groupConfig.tags,
158
+ inheritedAuth: groupConfig.auth,
158
159
  });
159
160
  }
@@ -1,4 +1,4 @@
1
- import { AddWiring, InspectorState } from '../types.js';
1
+ import type { AddWiring, InspectorState } from '../types.js';
2
2
  export interface KeyedWiringConfig {
3
3
  functionName: string;
4
4
  idField: string;
@@ -1,2 +1,2 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  export declare const addMCPPrompt: AddWiring;
@@ -1,2 +1,2 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  export declare const addMCPResource: AddWiring;
@@ -1,2 +1,2 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  export declare const addQueueWorker: AddWiring;
@@ -1,9 +1,9 @@
1
1
  import * as ts from 'typescript';
2
- import { InspectorState, InspectorLogger } from '../types.js';
2
+ import type { InspectorState, InspectorLogger } from '../types.js';
3
3
  /**
4
4
  * Scan for rpc.invoke() calls to track which functions are actually being invoked
5
- * Also detects external package usage via:
5
+ * Also detects addon usage via:
6
6
  * - Namespaced calls: rpc.invoke('namespace:function')
7
- * - External helper: external('namespace:function')
7
+ * - Addon helper: addon('namespace:function')
8
8
  */
9
9
  export declare function addRPCInvocations(node: ts.Node, state: InspectorState, logger: InspectorLogger): void;
@@ -11,25 +11,25 @@ function extractNamespace(functionRef) {
11
11
  }
12
12
  /**
13
13
  * Scan for rpc.invoke() calls to track which functions are actually being invoked
14
- * Also detects external package usage via:
14
+ * Also detects addon usage via:
15
15
  * - Namespaced calls: rpc.invoke('namespace:function')
16
- * - External helper: external('namespace:function')
16
+ * - Addon helper: addon('namespace:function')
17
17
  */
18
18
  export function addRPCInvocations(node, state, logger) {
19
- // Look for call expressions: external('ext:hello') or rpc.invoke('...')
19
+ // Look for call expressions: addon('ext:hello') or rpc.invoke('...')
20
20
  if (ts.isCallExpression(node)) {
21
21
  const { expression, arguments: args } = node;
22
- // Check for external('namespace:function') calls
23
- if (ts.isIdentifier(expression) && expression.text === 'external') {
22
+ // Check for addon('namespace:function') calls
23
+ if (ts.isIdentifier(expression) && expression.text === 'addon') {
24
24
  const [firstArg] = args;
25
25
  if (firstArg && ts.isStringLiteral(firstArg)) {
26
26
  const functionRef = firstArg.text;
27
- logger.debug(`• Found external() call: ${functionRef}`);
27
+ logger.debug(`• Found addon() call: ${functionRef}`);
28
28
  state.rpc.invokedFunctions.add(functionRef);
29
29
  const namespace = extractNamespace(functionRef);
30
30
  if (namespace) {
31
- logger.debug(` → External package detected: ${namespace}`);
32
- state.rpc.usedExternalPackages.add(namespace);
31
+ logger.debug(` → Addon detected: ${namespace}`);
32
+ state.rpc.usedAddons.add(namespace);
33
33
  }
34
34
  }
35
35
  }
@@ -60,8 +60,8 @@ export function addRPCInvocations(node, state, logger) {
60
60
  state.rpc.invokedFunctions.add(functionRef);
61
61
  const namespace = extractNamespace(functionRef);
62
62
  if (namespace) {
63
- logger.debug(` → External package detected: ${namespace}`);
64
- state.rpc.usedExternalPackages.add(namespace);
63
+ logger.debug(` → Addon detected: ${namespace}`);
64
+ state.rpc.usedAddons.add(namespace);
65
65
  }
66
66
  }
67
67
  // Handle template literals like `function-${name}`
@@ -1,2 +1,2 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  export declare const addSchedule: AddWiring;
@@ -1,3 +1,3 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  export declare const addSecret: AddWiring;
3
3
  export declare const addOAuth2Credential: AddWiring;
@@ -1,2 +1,2 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  export declare const addTrigger: AddWiring;
@@ -4,7 +4,7 @@ import { extractFunctionName, makeContextBasedId, } from '../utils/extract-funct
4
4
  import { getPropertyAssignmentInitializer } from '../utils/type-utils.js';
5
5
  import { resolveMiddleware } from '../utils/middleware.js';
6
6
  import { extractWireNames } from '../utils/post-process.js';
7
- import { resolveExternalPackageName } from '../utils/resolve-external-package.js';
7
+ import { resolveAddonName } from '../utils/resolve-addon-package.js';
8
8
  import { ErrorCode } from '../error-codes.js';
9
9
  export const addTrigger = (logger, node, checker, state, options) => {
10
10
  if (!ts.isCallExpression(node)) {
@@ -76,7 +76,7 @@ const addWireTriggerSource = (logger, node, checker, state, options, firstArg) =
76
76
  return;
77
77
  }
78
78
  if (ts.isIdentifier(funcInitializer)) {
79
- const packageName = resolveExternalPackageName(funcInitializer, checker, options.externalPackages);
79
+ const packageName = resolveAddonName(funcInitializer, checker, state.rpc.wireAddonDeclarations);
80
80
  state.triggers.sourceMeta[nameValue] = {
81
81
  name: nameValue,
82
82
  pikkuFuncId: funcInitializer.text,
@@ -0,0 +1,7 @@
1
+ import * as ts from 'typescript';
2
+ import type { InspectorState, InspectorLogger } from '../types.js';
3
+ /**
4
+ * Detect wireAddon({ name: '...', package: '...' }) call expressions and
5
+ * populate state.rpc.wireAddonDeclarations and state.rpc.usedAddons.
6
+ */
7
+ export declare function addWireAddon(node: ts.Node, state: InspectorState, logger: InspectorLogger): void;
@@ -0,0 +1,70 @@
1
+ import * as ts from 'typescript';
2
+ function parseStringRecord(obj) {
3
+ const result = {};
4
+ for (const prop of obj.properties) {
5
+ if (!ts.isPropertyAssignment(prop))
6
+ continue;
7
+ const keyNode = prop.name;
8
+ const key = ts.isIdentifier(keyNode)
9
+ ? keyNode.text
10
+ : ts.isStringLiteral(keyNode)
11
+ ? keyNode.text
12
+ : undefined;
13
+ if (key && ts.isStringLiteral(prop.initializer)) {
14
+ result[key] = prop.initializer.text;
15
+ }
16
+ }
17
+ return result;
18
+ }
19
+ /**
20
+ * Detect wireAddon({ name: '...', package: '...' }) call expressions and
21
+ * populate state.rpc.wireAddonDeclarations and state.rpc.usedAddons.
22
+ */
23
+ export function addWireAddon(node, state, logger) {
24
+ if (!ts.isCallExpression(node))
25
+ return;
26
+ const { expression, arguments: args } = node;
27
+ if (!ts.isIdentifier(expression) || expression.text !== 'wireAddon')
28
+ return;
29
+ const [firstArg] = args;
30
+ if (!firstArg || !ts.isObjectLiteralExpression(firstArg))
31
+ return;
32
+ let name;
33
+ let pkg;
34
+ let rpcEndpoint;
35
+ let secretOverrides;
36
+ let variableOverrides;
37
+ for (const prop of firstArg.properties) {
38
+ if (!ts.isPropertyAssignment(prop) || !ts.isIdentifier(prop.name))
39
+ continue;
40
+ const key = prop.name.text;
41
+ if (key === 'name' && ts.isStringLiteral(prop.initializer)) {
42
+ name = prop.initializer.text;
43
+ }
44
+ else if (key === 'package' && ts.isStringLiteral(prop.initializer)) {
45
+ pkg = prop.initializer.text;
46
+ }
47
+ else if (key === 'rpcEndpoint' && ts.isStringLiteral(prop.initializer)) {
48
+ rpcEndpoint = prop.initializer.text;
49
+ }
50
+ else if (key === 'secretOverrides' &&
51
+ ts.isObjectLiteralExpression(prop.initializer)) {
52
+ secretOverrides = parseStringRecord(prop.initializer);
53
+ }
54
+ else if (key === 'variableOverrides' &&
55
+ ts.isObjectLiteralExpression(prop.initializer)) {
56
+ variableOverrides = parseStringRecord(prop.initializer);
57
+ }
58
+ }
59
+ if (!name || !pkg)
60
+ return;
61
+ logger.debug(`• Found wireAddon: ${name} → ${pkg}`);
62
+ state.rpc.wireAddonDeclarations.set(name, {
63
+ package: pkg,
64
+ rpcEndpoint,
65
+ secretOverrides,
66
+ variableOverrides,
67
+ });
68
+ state.rpc.usedAddons.add(name);
69
+ state.rpc.wireAddonFiles.add(node.getSourceFile().fileName);
70
+ }
@@ -1,4 +1,4 @@
1
- import { AddWiring } from '../types.js';
1
+ import type { AddWiring } from '../types.js';
2
2
  /**
3
3
  * Inspector for pikkuWorkflow() and pikkuSimpleWorkflow() calls
4
4
  * Detects workflow registration and extracts metadata
@@ -31,6 +31,7 @@ export declare enum ErrorCode {
31
31
  HANDLER_NOT_RESOLVED = "PKU568",
32
32
  ROUTE_PARAM_MISMATCH = "PKU571",
33
33
  ROUTE_QUERY_MISMATCH = "PKU572",
34
+ AUTH_DISABLED_REQUIRES_SESSIONLESS = "PKU573",
34
35
  MIDDLEWARE_HANDLER_INVALID = "PKU685",
35
36
  MIDDLEWARE_TAG_INVALID = "PKU715",
36
37
  MIDDLEWARE_EMPTY_ARRAY = "PKU736",
@@ -36,6 +36,7 @@ export var ErrorCode;
36
36
  // HTTP Route errors
37
37
  ErrorCode["ROUTE_PARAM_MISMATCH"] = "PKU571";
38
38
  ErrorCode["ROUTE_QUERY_MISMATCH"] = "PKU572";
39
+ ErrorCode["AUTH_DISABLED_REQUIRES_SESSIONLESS"] = "PKU573";
39
40
  // Middleware/Permission errors
40
41
  ErrorCode["MIDDLEWARE_HANDLER_INVALID"] = "PKU685";
41
42
  ErrorCode["MIDDLEWARE_TAG_INVALID"] = "PKU715";
package/dist/index.d.ts CHANGED
@@ -8,7 +8,7 @@ export type { SerializableInspectorState } from './utils/serialize-inspector-sta
8
8
  export { filterInspectorState } from './utils/filter-inspector-state.js';
9
9
  export { generateCustomTypes, sanitizeTypeName, } from './utils/custom-types-generator.js';
10
10
  export { createEmptyManifest, serializeManifest, } from './utils/contract-hashes.js';
11
- export type { ContractEntry, VersionValidateError, VersionManifest, VersionManifestEntry, } from './utils/contract-hashes.js';
11
+ export type { ContractEntry, VersionHashEntry, VersionValidateError, VersionManifest, VersionManifestEntry, } from './utils/contract-hashes.js';
12
12
  export { serializeMCPJson } from './utils/serialize-mcp-json.js';
13
13
  export type { OpenAPISpecInfo } from './utils/serialize-openapi-json.js';
14
14
  export { deserializeDslWorkflow, deserializeGraphWorkflow, deserializeAllDslWorkflows, } from './utils/workflow/dsl/index.js';
@@ -1,4 +1,4 @@
1
- import { InspectorState, InspectorLogger, InspectorOptions } from './types.js';
1
+ import type { InspectorState, InspectorLogger, InspectorOptions } from './types.js';
2
2
  /**
3
3
  * Creates an initial/empty inspector state with all required properties initialized
4
4
  * @param rootDir - The root directory for the project
package/dist/inspector.js CHANGED
@@ -4,9 +4,9 @@ import { visitSetup, visitRoutes } from './visit.js';
4
4
  import { TypesMap } from './types-map.js';
5
5
  import { getFilesAndMethods } from './utils/get-files-and-methods.js';
6
6
  import { findCommonAncestor } from './utils/find-root-dir.js';
7
- import { aggregateRequiredServices, validateSecretOverrides, validateAgentModels, validateAgentOverrides, computeResolvedIOTypes, computeMiddlewareGroupsMeta, computePermissionsGroupsMeta, computeRequiredSchemas, computeDiagnostics, } from './utils/post-process.js';
7
+ import { aggregateRequiredServices, validateAgentModels, validateAgentOverrides, validateSecretOverrides, validateVariableOverrides, computeResolvedIOTypes, computeMiddlewareGroupsMeta, computePermissionsGroupsMeta, computeRequiredSchemas, computeDiagnostics, } from './utils/post-process.js';
8
8
  import { generateOpenAPISpec } from './utils/serialize-openapi-json.js';
9
- import { pikkuState } from '@pikku/core';
9
+ import { pikkuState } from '@pikku/core/internal';
10
10
  import { resolveLatestVersions } from './utils/resolve-versions.js';
11
11
  import { finalizeWorkflows } from './utils/workflow/graph/finalize-workflows.js';
12
12
  import { finalizeWorkflowHelperTypes, finalizeWorkflowWires, } from './utils/workflow/graph/finalize-workflow-wires.js';
@@ -83,7 +83,9 @@ export function getInitialInspectorState(rootDir) {
83
83
  exposedMeta: {},
84
84
  exposedFiles: new Map(),
85
85
  invokedFunctions: new Set(),
86
- usedExternalPackages: new Set(),
86
+ usedAddons: new Set(),
87
+ wireAddonDeclarations: new Map(),
88
+ wireAddonFiles: new Set(),
87
89
  },
88
90
  mcpEndpoints: {
89
91
  resourcesMeta: {},
@@ -238,9 +240,10 @@ export const inspect = async (logger, routeFiles, options = {}) => {
238
240
  if (options.openAPI) {
239
241
  state.openAPISpec = await generateOpenAPISpec(logger, state.functions.meta, state.http.meta, state.schemas, options.openAPI.additionalInfo, pikkuState(null, 'misc', 'errors'));
240
242
  }
241
- validateSecretOverrides(logger, state, options.externalPackages);
242
243
  validateAgentModels(logger, state, options.modelConfig);
243
244
  validateAgentOverrides(logger, state, options.modelConfig);
245
+ validateSecretOverrides(logger, state);
246
+ validateVariableOverrides(logger, state);
244
247
  }
245
248
  return state;
246
249
  };
package/dist/types.d.ts CHANGED
@@ -1,20 +1,20 @@
1
- import * as ts from 'typescript';
2
- import { ChannelsMeta } from '@pikku/core/channel';
3
- import { HTTPWiringsMeta } from '@pikku/core/http';
4
- import { ScheduledTasksMeta } from '@pikku/core/scheduler';
5
- import { TriggerMeta, TriggerSourceMeta } from '@pikku/core/trigger';
6
- import { QueueWorkersMeta } from '@pikku/core/queue';
7
- import { WorkflowsMeta } from '@pikku/core/workflow';
8
- import { MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from '@pikku/core/mcp';
9
- import { AIAgentMeta } from '@pikku/core/ai-agent';
10
- import { CLIMeta } from '@pikku/core/cli';
11
- import { NodesMeta } from '@pikku/core/node';
12
- import { SecretDefinitions } from '@pikku/core/secret';
13
- import { VariableDefinitions } from '@pikku/core/variable';
14
- import { TypesMap } from './types-map.js';
15
- import { FunctionsMeta, FunctionServicesMeta, FunctionWiresMeta, JSONValue } from '@pikku/core';
1
+ import type * as ts from 'typescript';
2
+ import type { ChannelsMeta } from '@pikku/core/channel';
3
+ import type { HTTPWiringsMeta } from '@pikku/core/http';
4
+ import type { ScheduledTasksMeta } from '@pikku/core/scheduler';
5
+ import type { TriggerMeta, TriggerSourceMeta } from '@pikku/core/trigger';
6
+ import type { QueueWorkersMeta } from '@pikku/core/queue';
7
+ import type { WorkflowsMeta } from '@pikku/core/workflow';
8
+ import type { MCPResourceMeta, MCPToolMeta, MCPPromptMeta } from '@pikku/core/mcp';
9
+ import type { AIAgentMeta } from '@pikku/core/ai-agent';
10
+ import type { CLIMeta } from '@pikku/core/cli';
11
+ import type { NodesMeta } from '@pikku/core/node';
12
+ import type { SecretDefinitions } from '@pikku/core/secret';
13
+ import type { VariableDefinitions } from '@pikku/core/variable';
14
+ import type { TypesMap } from './types-map.js';
15
+ import type { FunctionsMeta, FunctionServicesMeta, FunctionWiresMeta, JSONValue } from '@pikku/core';
16
16
  import type { OpenAPISpecInfo } from './utils/serialize-openapi-json.js';
17
- import { ErrorCode } from './error-codes.js';
17
+ import type { ErrorCode } from './error-codes.js';
18
18
  import type { VersionManifest, VersionValidateError } from './utils/contract-hashes.js';
19
19
  import type { SerializedWorkflowGraphs } from './utils/workflow/graph/workflow-graph.types.js';
20
20
  export type PathToNameAndType = Map<string, {
@@ -138,7 +138,7 @@ export type InspectorFilters = {
138
138
  httpRoutes?: string[];
139
139
  httpMethods?: string[];
140
140
  };
141
- export type ExternalPackageConfig = {
141
+ export type AddonConfig = {
142
142
  package: string;
143
143
  rpcEndpoint?: string;
144
144
  secretOverrides?: Record<string, string>;
@@ -164,14 +164,13 @@ export type InspectorModelConfig = {
164
164
  export type InspectorOptions = Partial<{
165
165
  setupOnly: boolean;
166
166
  rootDir: string;
167
- isExternalPackage: boolean;
167
+ isAddon: boolean;
168
168
  types: Partial<{
169
169
  configFileType: string;
170
170
  userSessionType: string;
171
171
  singletonServicesFactoryType: string;
172
172
  wireServicesFactoryType: string;
173
173
  }>;
174
- externalPackages: Record<string, ExternalPackageConfig>;
175
174
  schemaConfig: {
176
175
  tsconfig: string;
177
176
  schemasFromTypes?: string[];
@@ -301,7 +300,14 @@ export interface InspectorState {
301
300
  exportedName: string;
302
301
  }>;
303
302
  invokedFunctions: Set<string>;
304
- usedExternalPackages: Set<string>;
303
+ usedAddons: Set<string>;
304
+ wireAddonDeclarations: Map<string, {
305
+ package: string;
306
+ rpcEndpoint?: string;
307
+ secretOverrides?: Record<string, string>;
308
+ variableOverrides?: Record<string, string>;
309
+ }>;
310
+ wireAddonFiles: Set<string>;
305
311
  };
306
312
  mcpEndpoints: {
307
313
  resourcesMeta: MCPResourceMeta;
@@ -12,7 +12,7 @@ const PRIMITIVE_TYPES = new Set([
12
12
  export function computeRequiredSchemas(functionsMeta, typesMap, additionalTypes, schemaLookup) {
13
13
  return new Set([
14
14
  ...Object.values(functionsMeta)
15
- .map(({ inputs, outputs }) => {
15
+ .flatMap(({ inputs, outputs }) => {
16
16
  const types = [];
17
17
  if (inputs?.[0]) {
18
18
  try {
@@ -32,7 +32,6 @@ export function computeRequiredSchemas(functionsMeta, typesMap, additionalTypes,
32
32
  }
33
33
  return types;
34
34
  })
35
- .flat()
36
35
  .filter((s) => !!s && !PRIMITIVE_TYPES.has(s)),
37
36
  ...typesMap.customTypes.keys(),
38
37
  ...(additionalTypes || []),