@pikku/inspector 0.12.42 → 0.12.44

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 (126) hide show
  1. package/CHANGELOG.md +266 -0
  2. package/dist/add/add-addon-bans.js +1 -0
  3. package/dist/add/add-ai-agent.d.ts +3 -1
  4. package/dist/add/add-ai-agent.js +82 -0
  5. package/dist/add/add-credential.js +5 -1
  6. package/dist/add/add-functions.js +10 -9
  7. package/dist/add/add-gateway.js +3 -0
  8. package/dist/add/add-http-route.js +2 -2
  9. package/dist/add/add-keyed-wiring.js +2 -0
  10. package/dist/add/add-mcp-prompt.js +0 -1
  11. package/dist/add/add-mcp-resource.js +0 -1
  12. package/dist/add/add-permission.d.ts +1 -1
  13. package/dist/add/add-permission.js +3 -177
  14. package/dist/add/add-queue-worker.js +3 -0
  15. package/dist/add/add-schedule.js +3 -0
  16. package/dist/add/add-scope.d.ts +2 -0
  17. package/dist/add/add-scope.js +146 -0
  18. package/dist/add/add-trigger.js +3 -0
  19. package/dist/add/add-wire-remote-addon.d.ts +10 -0
  20. package/dist/add/add-wire-remote-addon.js +66 -0
  21. package/dist/add/add-workflow-graph.js +32 -3
  22. package/dist/add/add-workflow.js +9 -4
  23. package/dist/error-codes.d.ts +3 -0
  24. package/dist/error-codes.js +4 -0
  25. package/dist/inspector.js +11 -5
  26. package/dist/types.d.ts +19 -13
  27. package/dist/utils/custom-types-generator.js +2 -1
  28. package/dist/utils/ensure-function-metadata.d.ts +15 -0
  29. package/dist/utils/ensure-function-metadata.js +24 -0
  30. package/dist/utils/filter-inspector-state.js +9 -0
  31. package/dist/utils/get-property-value.d.ts +7 -2
  32. package/dist/utils/get-property-value.js +48 -1
  33. package/dist/utils/load-addon-functions-meta.js +81 -6
  34. package/dist/utils/permissions.d.ts +1 -21
  35. package/dist/utils/permissions.js +17 -108
  36. package/dist/utils/post-process.d.ts +30 -1
  37. package/dist/utils/post-process.js +150 -72
  38. package/dist/utils/schema-generator.js +19 -4
  39. package/dist/utils/serialize-inspector-state.d.ts +13 -8
  40. package/dist/utils/serialize-inspector-state.js +12 -6
  41. package/dist/utils/serialize-permissions-groups-meta.d.ts +0 -2
  42. package/dist/utils/serialize-permissions-groups-meta.js +0 -26
  43. package/dist/utils/workflow/derive-workflow-plan.js +9 -3
  44. package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +373 -91
  45. package/dist/utils/workflow/dsl/extract-dsl-workflow.js +274 -100
  46. package/dist/utils/workflow/dsl/patterns.d.ts +4 -0
  47. package/dist/utils/workflow/dsl/patterns.js +11 -0
  48. package/dist/utils/workflow/graph/convert-dsl-to-graph.js +76 -5
  49. package/dist/utils/workflow/graph/finalize-workflows.d.ts +7 -0
  50. package/dist/utils/workflow/graph/finalize-workflows.js +11 -2
  51. package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +2 -0
  52. package/dist/utils/workflow/graph/serialize-workflow-graph.js +4 -0
  53. package/dist/utils/workflow/graph/workflow-graph.types.d.ts +8 -4
  54. package/dist/visit.js +4 -0
  55. package/package.json +3 -3
  56. package/run-tests.sh +1 -1
  57. package/src/add/add-addon-bans.ts +1 -0
  58. package/src/add/add-ai-agent.test.ts +87 -0
  59. package/src/add/add-ai-agent.ts +122 -0
  60. package/src/add/add-credential.ts +8 -0
  61. package/src/add/add-functions.ts +10 -12
  62. package/src/add/add-gateway.ts +11 -0
  63. package/src/add/add-http-route.ts +2 -8
  64. package/src/add/add-keyed-wiring.ts +2 -0
  65. package/src/add/add-mcp-prompt.ts +0 -1
  66. package/src/add/add-mcp-resource.ts +0 -1
  67. package/src/add/add-permission.ts +4 -242
  68. package/src/add/add-queue-worker.ts +11 -0
  69. package/src/add/add-schedule.ts +11 -0
  70. package/src/add/add-scope.test.ts +346 -0
  71. package/src/add/add-scope.ts +225 -0
  72. package/src/add/add-trigger.ts +11 -0
  73. package/src/add/add-wire-remote-addon.ts +77 -0
  74. package/src/add/add-workflow-edge-cases.test.ts +201 -0
  75. package/src/add/add-workflow-fanout.test.ts +60 -0
  76. package/src/add/add-workflow-graph-input.test.ts +94 -0
  77. package/src/add/add-workflow-graph-notes.test.ts +74 -0
  78. package/src/add/add-workflow-graph.ts +35 -3
  79. package/src/add/add-workflow-on-error.test.ts +118 -0
  80. package/src/add/add-workflow-return-switch.test.ts +121 -0
  81. package/src/add/add-workflow-silent-drops.test.ts +163 -0
  82. package/src/add/add-workflow-sleep-expression.test.ts +97 -0
  83. package/src/add/add-workflow.ts +9 -4
  84. package/src/add/config-value-metadata.test.ts +169 -0
  85. package/src/add/inline-wiring-function.test.ts +104 -0
  86. package/src/add/validate-workflow-graph-addons.test.ts +102 -0
  87. package/src/error-codes.ts +5 -0
  88. package/src/inspector.ts +14 -4
  89. package/src/types.ts +16 -17
  90. package/src/utils/custom-types-generator.test.ts +26 -1
  91. package/src/utils/custom-types-generator.ts +2 -1
  92. package/src/utils/ensure-function-metadata.ts +42 -0
  93. package/src/utils/filter-inspector-state.test.ts +0 -2
  94. package/src/utils/filter-inspector-state.ts +9 -0
  95. package/src/utils/get-property-value.test.ts +141 -0
  96. package/src/utils/get-property-value.ts +62 -1
  97. package/src/utils/load-addon-functions-meta.test.ts +157 -0
  98. package/src/utils/load-addon-functions-meta.ts +94 -6
  99. package/src/utils/load-addon-scopes.test.ts +138 -0
  100. package/src/utils/permissions.test.ts +7 -232
  101. package/src/utils/permissions.ts +20 -160
  102. package/src/utils/post-process.test.ts +269 -4
  103. package/src/utils/post-process.ts +216 -95
  104. package/src/utils/schema-generator.test.ts +164 -0
  105. package/src/utils/schema-generator.ts +26 -3
  106. package/src/utils/serialize-inspector-state.ts +22 -25
  107. package/src/utils/serialize-permissions-groups-meta.ts +0 -28
  108. package/src/utils/workflow/derive-workflow-plan.test.ts +41 -3
  109. package/src/utils/workflow/derive-workflow-plan.ts +11 -4
  110. package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +480 -119
  111. package/src/utils/workflow/dsl/deserialize-fanout-body.test.ts +71 -0
  112. package/src/utils/workflow/dsl/deserialize-fanout-fidelity.test.ts +99 -0
  113. package/src/utils/workflow/dsl/deserialize-graph-next.test.ts +161 -0
  114. package/src/utils/workflow/dsl/deserialize-roundtrip-bindings.test.ts +121 -0
  115. package/src/utils/workflow/dsl/deserialize-roundtrip-values.test.ts +116 -0
  116. package/src/utils/workflow/dsl/deserialize-roundtrip.test.ts +145 -0
  117. package/src/utils/workflow/dsl/extract-dsl-workflow.ts +311 -105
  118. package/src/utils/workflow/dsl/patterns.ts +18 -0
  119. package/src/utils/workflow/graph/convert-dsl-to-graph.ts +82 -10
  120. package/src/utils/workflow/graph/fanout-node-identity.test.ts +105 -0
  121. package/src/utils/workflow/graph/finalize-workflows.test.ts +50 -0
  122. package/src/utils/workflow/graph/finalize-workflows.ts +13 -2
  123. package/src/utils/workflow/graph/serialize-workflow-graph.ts +6 -0
  124. package/src/utils/workflow/graph/workflow-graph.types.ts +11 -2
  125. package/src/visit.ts +4 -0
  126. package/tsconfig.tsbuildinfo +1 -1
@@ -1,2 +1,9 @@
1
+ import type { SerializedWorkflowGraph } from './workflow-graph.types.js';
1
2
  import type { InspectorState } from '../../../types.js';
2
3
  export declare function finalizeWorkflows(state: InspectorState): void;
4
+ /**
5
+ * Hash of graph topology only. `notes` (node- and graph-level) is deliberately
6
+ * excluded so editing documentation never changes the hash / marks the workflow
7
+ * as a new version.
8
+ */
9
+ export declare function computeGraphHash(graph: SerializedWorkflowGraph): string;
@@ -80,11 +80,20 @@ function computeStepHashes(graph, functionsMeta) {
80
80
  node.stepHash = hashString(`${node.nodeId}:${meta?.contractHash ?? ''}`, 12);
81
81
  }
82
82
  }
83
- function computeGraphHash(graph) {
83
+ /**
84
+ * Hash of graph topology only. `notes` (node- and graph-level) is deliberately
85
+ * excluded so editing documentation never changes the hash / marks the workflow
86
+ * as a new version.
87
+ */
88
+ export function computeGraphHash(graph) {
89
+ const nodesForHash = Object.fromEntries(Object.entries(graph.nodes).map(([nodeId, node]) => {
90
+ const { notes: _notes, ...rest } = node;
91
+ return [nodeId, rest];
92
+ }));
84
93
  return hashString(canonicalJSON({
85
94
  source: graph.source,
86
95
  context: graph.context,
87
- nodes: graph.nodes,
96
+ nodes: nodesForHash,
88
97
  entryNodeIds: graph.entryNodeIds,
89
98
  }), 12);
90
99
  }
@@ -14,10 +14,12 @@ export declare function serializeWorkflowGraph(definition: {
14
14
  input?: (ref: any) => Record<string, unknown>;
15
15
  next?: string | string[] | Record<string, string | string[]>;
16
16
  onError?: string | string[];
17
+ notes?: string;
17
18
  }>;
18
19
  }, options?: {
19
20
  description?: string;
20
21
  tags?: string[];
22
+ notes?: string[];
21
23
  }): SerializedWorkflowGraph;
22
24
  /**
23
25
  * Deserialize a workflow graph from JSON to runtime format
@@ -108,6 +108,9 @@ export function serializeWorkflowGraph(definition, options) {
108
108
  next: serializeNext(node.next),
109
109
  onError: node.onError,
110
110
  };
111
+ if (node.notes !== undefined) {
112
+ funcNode.notes = node.notes;
113
+ }
111
114
  nodes[nodeId] = funcNode;
112
115
  // Entry nodes have no incoming edges
113
116
  if (!hasIncomingEdge.has(nodeId)) {
@@ -120,6 +123,7 @@ export function serializeWorkflowGraph(definition, options) {
120
123
  source: 'graph',
121
124
  description: options?.description,
122
125
  tags: options?.tags,
126
+ notes: options?.notes,
123
127
  nodes,
124
128
  entryNodeIds,
125
129
  };
@@ -51,22 +51,22 @@ export type SerializedNext = string | string[] | {
51
51
  conditions: BranchCondition[];
52
52
  /** Default target if no conditions match */
53
53
  default?: string | string[];
54
- };
54
+ } | Record<string, string | string[]>;
55
55
  /**
56
56
  * Node execution options
57
57
  */
58
58
  export interface NodeOptions {
59
59
  /** Number of retry attempts on failure */
60
60
  retries?: number;
61
- /** Delay between retries (e.g., '1s', '5s') */
62
- retryDelay?: string;
61
+ /** Delay between retries (e.g., '1s', '5s', or a number of milliseconds) */
62
+ retryDelay?: string | number;
63
63
  /** Timeout for node execution (e.g., '30s', '5m') */
64
64
  timeout?: string;
65
65
  }
66
66
  /**
67
67
  * Flow node types for control flow (no RPC call)
68
68
  */
69
- export type FlowType = 'sleep' | 'branch' | 'parallel' | 'fanout' | 'inline' | 'switch' | 'filter' | 'arrayPredicate' | 'return' | 'cancel' | 'set';
69
+ export type FlowType = 'sleep' | 'suspend' | 'approval' | 'branch' | 'parallel' | 'fanout' | 'inline' | 'switch' | 'filter' | 'arrayPredicate' | 'return' | 'cancel' | 'set';
70
70
  import type { ContextVariable, WorkflowContext, WorkflowPlannedStep } from '@pikku/core/workflow';
71
71
  export type { ContextVariable, WorkflowContext };
72
72
  /**
@@ -83,6 +83,8 @@ interface BaseNode {
83
83
  onError?: string | string[];
84
84
  /** Execution options */
85
85
  options?: NodeOptions;
86
+ /** Free-text node documentation. Non-semantic — excluded from graphHash. */
87
+ notes?: string;
86
88
  }
87
89
  /**
88
90
  * Function node - calls an RPC
@@ -154,6 +156,8 @@ export interface SerializedWorkflowGraph {
154
156
  context?: WorkflowContext;
155
157
  /** Serialized nodes */
156
158
  nodes: Record<string, SerializedGraphNode>;
159
+ /** Graph-level free-text notes (e.g. imported sticky notes). Non-semantic — excluded from graphHash. */
160
+ notes?: string[];
157
161
  /** Entry node(s) - first nodes to execute */
158
162
  entryNodeIds: string[];
159
163
  /** Hash of graph topology (nodes, edges, input mappings) */
package/dist/visit.js CHANGED
@@ -15,12 +15,14 @@ import { addChannel } from './add/add-channel.js';
15
15
  import { addGateway } from './add/add-gateway.js';
16
16
  import { addRPCInvocations } from './add/add-rpc-invocations.js';
17
17
  import { addWireAddon } from './add/add-wire-addon.js';
18
+ import { addWireRemoteAddon } from './add/add-wire-remote-addon.js';
18
19
  import { addMiddleware } from './add/add-middleware.js';
19
20
  import { addPermission } from './add/add-permission.js';
20
21
  import { addCLI, addCLIRenderers } from './add/add-cli.js';
21
22
  import { addAuth } from './add/add-auth.js';
22
23
  import { addSecret } from './add/add-secret.js';
23
24
  import { addCredential } from './add/add-credential.js';
25
+ import { addScope } from './add/add-scope.js';
24
26
  import { addVariable } from './add/add-variable.js';
25
27
  import { addWorkflowGraph } from './add/add-workflow-graph.js';
26
28
  import { addAIAgent } from './add/add-ai-agent.js';
@@ -36,6 +38,7 @@ export const visitSetup = (logger, checker, node, state, options) => {
36
38
  addFileWithFactory(node, checker, state.serverLifecycleFactories, 'ServerLifecycle');
37
39
  addRPCInvocations(node, state, logger);
38
40
  addWireAddon(node, state, logger);
41
+ addWireRemoteAddon(node, state, logger);
39
42
  addMiddleware(logger, node, checker, state, options);
40
43
  addPermission(logger, node, checker, state, options);
41
44
  addApprovalDescription(logger, node, checker, state, options);
@@ -64,6 +67,7 @@ export const visitRoutes = (logger, checker, node, state, options) => {
64
67
  addAuth(logger, node, checker, state, nextOptions);
65
68
  addSecret(logger, node, checker, state, nextOptions);
66
69
  addCredential(logger, node, checker, state, nextOptions);
70
+ addScope(logger, node, checker, state, nextOptions);
67
71
  addVariable(logger, node, checker, state, nextOptions);
68
72
  addHTTPRoute(logger, node, checker, state, nextOptions);
69
73
  addHTTPRoutes(logger, node, checker, state, nextOptions);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/inspector",
3
- "version": "0.12.42",
3
+ "version": "0.12.44",
4
4
  "author": "yasser.fadl@gmail.com",
5
5
  "license": "BUSL-1.1",
6
6
  "type": "module",
@@ -44,10 +44,10 @@
44
44
  "zod-to-ts": "^2.1.0"
45
45
  },
46
46
  "peerDependencies": {
47
- "@pikku/core": "^0.12.61"
47
+ "@pikku/core": "^0.12.65"
48
48
  },
49
49
  "devDependencies": {
50
- "@pikku/core": "^0.12.61",
50
+ "@pikku/core": "^0.12.65",
51
51
  "@types/node": "^24.11.0"
52
52
  },
53
53
  "engines": {
package/run-tests.sh CHANGED
@@ -38,7 +38,7 @@ if [ ${#files[@]} -eq 0 ]; then
38
38
  fi
39
39
 
40
40
  # Construct the node command
41
- node_cmd=(node --import tsx --test)
41
+ node_cmd=(node --import tsx --expose-gc --test)
42
42
 
43
43
  # Append options based on flags
44
44
  if [ "$watch_mode" = true ]; then
@@ -9,6 +9,7 @@ import { ErrorCode } from '../error-codes.js'
9
9
  */
10
10
  const BANNED_WIRINGS = new Set([
11
11
  'wireAddon',
12
+ 'wireRemoteAddon',
12
13
  'wireChannel',
13
14
  'wireCLI',
14
15
  'wireGateway',
@@ -0,0 +1,87 @@
1
+ import { describe, test } from 'node:test'
2
+ import assert from 'node:assert'
3
+ import * as ts from 'typescript'
4
+
5
+ import { resolveWorkflowReferences } from './add-ai-agent.js'
6
+ import type { InspectorLogger } from '../types.js'
7
+
8
+ function createSourceFile(code: string): ts.SourceFile {
9
+ return ts.createSourceFile('test.ts', code, ts.ScriptTarget.ESNext, true)
10
+ }
11
+
12
+ function getObjectLiteral(
13
+ sourceFile: ts.SourceFile
14
+ ): ts.ObjectLiteralExpression {
15
+ let result: ts.ObjectLiteralExpression | undefined
16
+ ts.forEachChild(sourceFile, function visit(node) {
17
+ if (ts.isObjectLiteralExpression(node) && !result) {
18
+ result = node
19
+ }
20
+ ts.forEachChild(node, visit)
21
+ })
22
+ if (!result) throw new Error('No object literal found')
23
+ return result
24
+ }
25
+
26
+ function createLogger(): {
27
+ logger: InspectorLogger
28
+ criticals: Array<{ code: unknown; message: string }>
29
+ } {
30
+ const criticals: Array<{ code: unknown; message: string }> = []
31
+ const logger = {
32
+ info: () => {},
33
+ error: () => {},
34
+ warn: () => {},
35
+ debug: () => {},
36
+ diagnostic: () => {},
37
+ critical: (code: unknown, message: string) =>
38
+ criticals.push({ code, message }),
39
+ hasCriticalErrors: () => criticals.length > 0,
40
+ } as unknown as InspectorLogger
41
+ return { logger, criticals }
42
+ }
43
+
44
+ describe('resolveWorkflowReferences', () => {
45
+ test('returns null when no workflows property exists', () => {
46
+ const obj = getObjectLiteral(createSourceFile('const x = { name: "a" }'))
47
+ const { logger } = createLogger()
48
+ assert.strictEqual(
49
+ resolveWorkflowReferences(obj, {} as any, 'a', logger),
50
+ null
51
+ )
52
+ })
53
+
54
+ test('returns null for an empty workflows array', () => {
55
+ const obj = getObjectLiteral(
56
+ createSourceFile('const x = { workflows: [] }')
57
+ )
58
+ const { logger } = createLogger()
59
+ assert.strictEqual(
60
+ resolveWorkflowReferences(obj, {} as any, 'a', logger),
61
+ null
62
+ )
63
+ })
64
+
65
+ test('resolves ref() and workflow() string-literal references', () => {
66
+ const obj = getObjectLiteral(
67
+ createSourceFile(
68
+ `const x = { workflows: [ref('buildReport'), workflow('country_capitals')] }`
69
+ )
70
+ )
71
+ const { logger, criticals } = createLogger()
72
+ const result = resolveWorkflowReferences(obj, {} as any, 'planner', logger)
73
+ assert.deepStrictEqual(result, ['buildReport', 'country_capitals'])
74
+ assert.strictEqual(criticals.length, 0)
75
+ })
76
+
77
+ test('flags a raw string literal as a critical error', () => {
78
+ const obj = getObjectLiteral(
79
+ createSourceFile(`const x = { workflows: ['buildReport'] }`)
80
+ )
81
+ const { logger, criticals } = createLogger()
82
+ const result = resolveWorkflowReferences(obj, {} as any, 'planner', logger)
83
+ assert.strictEqual(result, null)
84
+ assert.strictEqual(criticals.length, 1)
85
+ assert.match(criticals[0].message, /string literal/)
86
+ })
87
+ })
@@ -142,6 +142,114 @@ function resolveAgentReferences(
142
142
  return resolved.length > 0 ? resolved : null
143
143
  }
144
144
 
145
+ export function resolveWorkflowReferences(
146
+ obj: ts.ObjectLiteralExpression,
147
+ checker: ts.TypeChecker,
148
+ agentName: string,
149
+ logger: InspectorLogger
150
+ ): string[] | null {
151
+ const property = obj.properties.find(
152
+ (p) =>
153
+ ts.isPropertyAssignment(p) &&
154
+ ts.isIdentifier(p.name) &&
155
+ p.name.text === 'workflows'
156
+ )
157
+
158
+ if (!property || !ts.isPropertyAssignment(property)) {
159
+ return null
160
+ }
161
+
162
+ const initializer = property.initializer
163
+ if (!ts.isArrayLiteralExpression(initializer)) {
164
+ return null
165
+ }
166
+
167
+ const resolved: string[] = []
168
+
169
+ for (const element of initializer.elements) {
170
+ if (ts.isStringLiteral(element)) {
171
+ logger.critical(
172
+ ErrorCode.INVALID_VALUE,
173
+ `AI agent '${agentName}' workflows array contains a string literal '${element.text}'. ` +
174
+ `Use a workflow reference instead (e.g., ref('<workflowName>') or import the pikkuWorkflowGraph variable).`
175
+ )
176
+ continue
177
+ }
178
+
179
+ if (ts.isCallExpression(element) && ts.isIdentifier(element.expression)) {
180
+ const calleeName = element.expression.text
181
+ if (calleeName === 'ref' || calleeName === 'workflow') {
182
+ const [firstArg] = element.arguments
183
+ if (firstArg && ts.isStringLiteral(firstArg)) {
184
+ resolved.push(firstArg.text)
185
+ continue
186
+ }
187
+ }
188
+ }
189
+
190
+ if (ts.isIdentifier(element)) {
191
+ const name = resolveIdentifierToWorkflowName(element, checker)
192
+ if (name) {
193
+ resolved.push(name)
194
+ continue
195
+ }
196
+
197
+ logger.critical(
198
+ ErrorCode.INVALID_VALUE,
199
+ `AI agent '${agentName}' workflows array contains identifier '${element.text}' ` +
200
+ `that could not be resolved to a pikkuWorkflowGraph.`
201
+ )
202
+ }
203
+ }
204
+
205
+ return resolved.length > 0 ? resolved : null
206
+ }
207
+
208
+ function resolveIdentifierToWorkflowName(
209
+ identifier: ts.Identifier,
210
+ checker: ts.TypeChecker
211
+ ): string | null {
212
+ const symbol = checker.getSymbolAtLocation(identifier)
213
+ if (!symbol) return null
214
+
215
+ let resolved = symbol
216
+ if (resolved.flags & ts.SymbolFlags.Alias) {
217
+ resolved = checker.getAliasedSymbol(resolved) ?? resolved
218
+ }
219
+
220
+ const decl = resolved.valueDeclaration ?? resolved.declarations?.[0]
221
+ if (!decl) return null
222
+
223
+ if (ts.isVariableDeclaration(decl) && decl.initializer) {
224
+ if (
225
+ ts.isCallExpression(decl.initializer) &&
226
+ ts.isIdentifier(decl.initializer.expression) &&
227
+ (decl.initializer.expression.text === 'pikkuWorkflowGraph' ||
228
+ decl.initializer.expression.text === 'pikkuWorkflow' ||
229
+ decl.initializer.expression.text === 'pikkuWorkflowComplex')
230
+ ) {
231
+ const firstArg = decl.initializer.arguments[0]
232
+ if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
233
+ for (const prop of firstArg.properties) {
234
+ if (
235
+ ts.isPropertyAssignment(prop) &&
236
+ ts.isIdentifier(prop.name) &&
237
+ prop.name.text === 'name' &&
238
+ ts.isStringLiteral(prop.initializer)
239
+ ) {
240
+ return prop.initializer.text
241
+ }
242
+ }
243
+ }
244
+ if (ts.isIdentifier(decl.name)) {
245
+ return decl.name.text
246
+ }
247
+ }
248
+ }
249
+
250
+ return null
251
+ }
252
+
145
253
  function resolveIdentifierToRpcName(
146
254
  identifier: ts.Identifier,
147
255
  checker: ts.TypeChecker
@@ -293,6 +401,19 @@ export const addAIAgent: AddWiring = (
293
401
  logger
294
402
  )
295
403
 
404
+ const workflowsValue = resolveWorkflowReferences(
405
+ obj,
406
+ checker,
407
+ nameValue || '',
408
+ logger
409
+ )
410
+
411
+ if (workflowsValue) {
412
+ for (const workflowName of workflowsValue) {
413
+ state.workflows.invokedWorkflows.add(workflowName)
414
+ }
415
+ }
416
+
296
417
  if (!nameValue) {
297
418
  logger.critical(
298
419
  ErrorCode.MISSING_NAME,
@@ -460,6 +581,7 @@ export const addAIAgent: AddWiring = (
460
581
  }),
461
582
  ...(toolsValue !== null && { tools: toolsValue }),
462
583
  ...(agentsValue !== null && { agents: agentsValue }),
584
+ ...(workflowsValue !== null && { workflows: workflowsValue }),
463
585
  tags,
464
586
  inputSchema,
465
587
  outputSchema,
@@ -2,6 +2,7 @@ import * as ts from 'typescript'
2
2
  import {
3
3
  getPropertyValue,
4
4
  getArrayPropertyValue,
5
+ getRecordPropertyValue,
5
6
  assertStringLiteralProperty,
6
7
  } from '../utils/get-property-value.js'
7
8
  import type { AddWiring } from '../types.js'
@@ -44,6 +45,7 @@ export const addCredential: AddWiring = (
44
45
  | string
45
46
  | null
46
47
  const typeValue = getPropertyValue(obj, 'type') as string | null
48
+ const docsUrlValue = getPropertyValue(obj, 'docsUrl') as string | null
47
49
 
48
50
  if (!nameValue) {
49
51
  logger.critical(
@@ -133,6 +135,10 @@ export const addCredential: AddWiring = (
133
135
  const tokenUrl = getPropertyValue(oauth2Obj, 'tokenUrl') as string | null
134
136
  const scopes = getArrayPropertyValue(oauth2Obj, 'scopes')
135
137
  const pkce = getPropertyValue(oauth2Obj, 'pkce') as boolean | null
138
+ const additionalParams = getRecordPropertyValue(
139
+ oauth2Obj,
140
+ 'additionalParams'
141
+ )
136
142
 
137
143
  if (appCredentialSecretId && authorizationUrl && tokenUrl && scopes) {
138
144
  oauth2 = {
@@ -142,6 +148,7 @@ export const addCredential: AddWiring = (
142
148
  tokenUrl,
143
149
  scopes,
144
150
  pkce: pkce || undefined,
151
+ additionalParams: additionalParams || undefined,
145
152
  }
146
153
  }
147
154
  }
@@ -173,6 +180,7 @@ export const addCredential: AddWiring = (
173
180
  displayName: displayNameValue,
174
181
  description: descriptionValue || undefined,
175
182
  type: typeValue as 'singleton' | 'wire',
183
+ docsUrl: docsUrlValue || undefined,
176
184
  schema: schemaLookupName,
177
185
  oauth2,
178
186
  sourceFile,
@@ -12,6 +12,7 @@ import type { FunctionServicesMeta } from '@pikku/core'
12
12
  import { formatVersionedId, parseVersionedId } from '@pikku/core'
13
13
  import {
14
14
  getPropertyValue,
15
+ getArrayPropertyValue,
15
16
  getCommonWireMetaData,
16
17
  } from '../utils/get-property-value.js'
17
18
  import { canonicalJSON, hashString } from '../utils/hash.js'
@@ -426,6 +427,7 @@ export const addFunctions: AddWiring = (
426
427
  let workflowQueued: boolean | undefined
427
428
  let workflowRetries: number | undefined
428
429
  let workflowTimeout: string | undefined
430
+ let scopes: string[] | undefined
429
431
  let version: number | undefined
430
432
  let objectNode: ts.ObjectLiteralExpression | undefined
431
433
  let nodeDisplayName: string | null = null
@@ -530,6 +532,7 @@ export const addFunctions: AddWiring = (
530
532
  workflowTimeout = getPropertyValue(firstArg, 'workflowTimeout') as
531
533
  | string
532
534
  | undefined
535
+ scopes = getArrayPropertyValue(firstArg, 'scopes') ?? undefined
533
536
 
534
537
  // Extract approvalDescription identifier reference
535
538
  for (const prop of firstArg.properties) {
@@ -1029,17 +1032,6 @@ export const addFunctions: AddWiring = (
1029
1032
  if (newMiddleware.length > 0) {
1030
1033
  middleware = [...(middleware || []), ...newMiddleware]
1031
1034
  }
1032
- const existingPermissionTags = new Set(
1033
- (permissions || [])
1034
- .filter((p) => p.type === 'tag')
1035
- .map((p) => (p as any).tag)
1036
- )
1037
- const newPermissions = tagEntries.filter(
1038
- (e) => !existingPermissionTags.has(e.tag)
1039
- )
1040
- if (newPermissions.length > 0) {
1041
- permissions = [...(permissions || []), ...newPermissions]
1042
- }
1043
1035
  }
1044
1036
 
1045
1037
  const implementationHash = computeImplementationHash({
@@ -1094,6 +1086,7 @@ export const addFunctions: AddWiring = (
1094
1086
  workflowQueued: workflowQueued === true ? true : undefined,
1095
1087
  workflowRetries: workflowRetries ?? undefined,
1096
1088
  workflowTimeout: workflowTimeout ?? undefined,
1089
+ scopes: scopes ?? undefined,
1097
1090
  implementationHash,
1098
1091
  version,
1099
1092
  title,
@@ -1147,7 +1140,6 @@ export const addFunctions: AddWiring = (
1147
1140
  inputSchema: inputNames[0] ?? null,
1148
1141
  outputSchema: outputNames[0] ?? null,
1149
1142
  middleware,
1150
- permissions,
1151
1143
  }
1152
1144
  state.serviceAggregation.usedFunctions.add(pikkuFuncId)
1153
1145
  extractWireNames(middleware).forEach((n) =>
@@ -1192,6 +1184,12 @@ export const addFunctions: AddWiring = (
1192
1184
 
1193
1185
  if (remote) {
1194
1186
  state.rpc.invokedFunctions.add(pikkuFuncId)
1187
+ // The consumer-facing surface a wireRemoteAddon imports (mirrors exposedMeta)
1188
+ state.rpc.remoteMeta[name] = pikkuFuncId
1189
+ state.rpc.remoteFiles.set(name, {
1190
+ path: node.getSourceFile().fileName,
1191
+ exportedName,
1192
+ })
1195
1193
  }
1196
1194
 
1197
1195
  if (expose) {
@@ -9,6 +9,7 @@ import {
9
9
  makeContextBasedId,
10
10
  } from '../utils/extract-function-name.js'
11
11
  import { getPropertyAssignmentInitializer } from '../utils/type-utils.js'
12
+ import { ensureInlineWiringFunction } from '../utils/ensure-function-metadata.js'
12
13
  import { resolveMiddleware } from '../utils/middleware.js'
13
14
  import { extractWireNames } from '../utils/post-process.js'
14
15
  import { resolveAddonName } from '../utils/resolve-addon-package.js'
@@ -83,6 +84,16 @@ export const addGateway: AddWiring = (
83
84
  return
84
85
  }
85
86
 
87
+ // Register metadata for a func inlined into the wiring (see helper).
88
+ ensureInlineWiringFunction(
89
+ state,
90
+ pikkuFuncId,
91
+ nameValue,
92
+ funcInitializer,
93
+ checker,
94
+ extracted.isHelper
95
+ )
96
+
86
97
  const middleware = resolveMiddleware(state, obj, tags, checker)
87
98
 
88
99
  state.serviceAggregation.usedFunctions.add(pikkuFuncId)
@@ -19,7 +19,7 @@ import type {
19
19
  InspectorState,
20
20
  } from '../types.js'
21
21
  import { resolveHTTPMiddlewareFromObject } from '../utils/middleware.js'
22
- import { resolveHTTPPermissionsFromObject } from '../utils/permissions.js'
22
+ import { resolvePermissions } from '../utils/permissions.js'
23
23
  import { extractWireNames } from '../utils/post-process.js'
24
24
  import { ensureFunctionMetadata } from '../utils/ensure-function-metadata.js'
25
25
  import { resolveFunctionMeta } from '../utils/resolve-function-meta.js'
@@ -381,13 +381,7 @@ export function registerHTTPRoute({
381
381
  )
382
382
 
383
383
  // Resolve permissions
384
- const permissions = resolveHTTPPermissionsFromObject(
385
- state,
386
- fullRoute,
387
- obj,
388
- tags,
389
- checker
390
- )
384
+ const permissions = resolvePermissions(state, obj, tags, checker)
391
385
 
392
386
  state.serviceAggregation.usedFunctions.add(funcName)
393
387
  if (refAddonTarget) {
@@ -56,6 +56,7 @@ export const createAddKeyedWiring = (config: KeyedWiringConfig): AddWiring => {
56
56
  const rotationPeriodValue = getPropertyValue(obj, 'rotationPeriod') as
57
57
  | string
58
58
  | null
59
+ const docsUrlValue = getPropertyValue(obj, 'docsUrl') as string | null
59
60
  const idValue = getPropertyValue(obj, config.idField) as string | null
60
61
 
61
62
  let schemaVariableName: string | null = null
@@ -165,6 +166,7 @@ export const createAddKeyedWiring = (config: KeyedWiringConfig): AddWiring => {
165
166
  displayName: displayNameValue,
166
167
  description: descriptionValue || undefined,
167
168
  rotationPeriod: rotationPeriodValue || undefined,
169
+ docsUrl: docsUrlValue || undefined,
168
170
  [config.idField]: idValue,
169
171
  schema: schemaLookupName,
170
172
  sourceFile,
@@ -152,7 +152,6 @@ export const addMCPPrompt: AddWiring = (
152
152
  outputSchema,
153
153
  arguments: [], // Will be populated by CLI during serialization
154
154
  middleware,
155
- permissions,
156
155
  }
157
156
  }
158
157
  }
@@ -170,7 +170,6 @@ export const addMCPResource: AddWiring = (
170
170
  inputSchema,
171
171
  outputSchema,
172
172
  middleware,
173
- permissions,
174
173
  }
175
174
  }
176
175
  }