@pikku/inspector 0.11.1 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -1
- package/dist/add/add-forge-credential.d.ts +8 -0
- package/dist/add/add-forge-credential.js +77 -0
- package/dist/add/add-forge-node.d.ts +7 -0
- package/dist/add/add-forge-node.js +77 -0
- package/dist/add/add-functions.js +102 -9
- package/dist/add/add-http-route.js +24 -1
- package/dist/add/add-rpc-invocations.d.ts +3 -0
- package/dist/add/add-rpc-invocations.js +51 -25
- package/dist/add/add-workflow-graph.d.ts +6 -0
- package/dist/add/add-workflow-graph.js +659 -0
- package/dist/add/add-workflow.js +118 -22
- package/dist/error-codes.d.ts +3 -1
- package/dist/error-codes.js +3 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/inspector.js +19 -3
- package/dist/types.d.ts +26 -0
- package/dist/utils/extract-function-name.js +7 -7
- package/dist/utils/get-property-value.d.ts +2 -1
- package/dist/utils/get-property-value.js +6 -2
- package/dist/utils/serialize-inspector-state.d.ts +24 -1
- package/dist/utils/serialize-inspector-state.js +24 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.d.ts +24 -0
- package/dist/utils/workflow/dsl/deserialize-dsl-workflow.js +898 -0
- package/dist/{workflow/extract-simple-workflow.d.ts → utils/workflow/dsl/extract-dsl-workflow.d.ts} +4 -2
- package/dist/{workflow/extract-simple-workflow.js → utils/workflow/dsl/extract-dsl-workflow.js} +549 -68
- package/dist/utils/workflow/dsl/index.d.ts +7 -0
- package/dist/utils/workflow/dsl/index.js +7 -0
- package/dist/{workflow → utils/workflow/dsl}/patterns.d.ts +21 -0
- package/dist/{workflow → utils/workflow/dsl}/patterns.js +90 -10
- package/dist/{workflow → utils/workflow/dsl}/validation.d.ts +2 -0
- package/dist/{workflow → utils/workflow/dsl}/validation.js +25 -7
- package/dist/utils/workflow/graph/convert-dsl-to-graph.d.ts +13 -0
- package/dist/utils/workflow/graph/convert-dsl-to-graph.js +316 -0
- package/dist/utils/workflow/graph/index.d.ts +6 -0
- package/dist/utils/workflow/graph/index.js +6 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +43 -0
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +152 -0
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +229 -0
- package/dist/utils/workflow/graph/workflow-graph.types.js +38 -0
- package/dist/visit.js +6 -0
- package/package.json +14 -2
- package/src/add/add-forge-credential.ts +119 -0
- package/src/add/add-forge-node.ts +132 -0
- package/src/add/add-functions.ts +129 -15
- package/src/add/add-http-route.ts +25 -1
- package/src/add/add-rpc-invocations.ts +61 -31
- package/src/add/add-workflow-graph.ts +864 -0
- package/src/add/add-workflow.ts +112 -26
- package/src/error-codes.ts +3 -1
- package/src/index.ts +10 -0
- package/src/inspector.ts +20 -4
- package/src/types.ts +25 -1
- package/src/utils/extract-function-name.ts +7 -7
- package/src/utils/get-property-value.ts +9 -2
- package/src/utils/serialize-inspector-state.ts +39 -1
- package/src/utils/workflow/dsl/deserialize-dsl-workflow.ts +1180 -0
- package/src/{workflow/extract-simple-workflow.ts → utils/workflow/dsl/extract-dsl-workflow.ts} +654 -81
- package/src/utils/workflow/dsl/index.ts +11 -0
- package/src/{workflow → utils/workflow/dsl}/patterns.ts +108 -11
- package/src/{workflow → utils/workflow/dsl}/validation.ts +34 -7
- package/src/utils/workflow/graph/convert-dsl-to-graph.ts +415 -0
- package/src/utils/workflow/graph/index.ts +6 -0
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +223 -0
- package/src/utils/workflow/graph/workflow-graph.types.ts +280 -0
- package/src/visit.ts +6 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert a RefValue (from runtime) to DataRef (serialized)
|
|
3
|
+
*/
|
|
4
|
+
function convertRef(ref) {
|
|
5
|
+
return {
|
|
6
|
+
$ref: ref.nodeId,
|
|
7
|
+
path: ref.path,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Check if a value is a runtime RefValue
|
|
12
|
+
*/
|
|
13
|
+
function isRefValue(value) {
|
|
14
|
+
return (typeof value === 'object' &&
|
|
15
|
+
value !== null &&
|
|
16
|
+
'__isRef' in value &&
|
|
17
|
+
value.__isRef === true);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Convert input mapping from runtime format to serialized format
|
|
21
|
+
*/
|
|
22
|
+
function serializeInputMapping(input) {
|
|
23
|
+
const result = {};
|
|
24
|
+
for (const [key, value] of Object.entries(input)) {
|
|
25
|
+
if (isRefValue(value)) {
|
|
26
|
+
result[key] = convertRef(value);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
result[key] = value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Convert next config from runtime format to serialized format
|
|
36
|
+
* Runtime uses Record<string, string | string[]> for branching with graph.branch()
|
|
37
|
+
* Serialized uses { conditions: [...], default: ... } for UI-friendly branching
|
|
38
|
+
*/
|
|
39
|
+
function serializeNext(next) {
|
|
40
|
+
if (!next)
|
|
41
|
+
return undefined;
|
|
42
|
+
if (typeof next === 'string')
|
|
43
|
+
return next;
|
|
44
|
+
if (Array.isArray(next))
|
|
45
|
+
return next;
|
|
46
|
+
// Record format - convert to conditions format
|
|
47
|
+
// For now, treat keys as branch identifiers (from graph.branch())
|
|
48
|
+
// UI can display these as condition labels
|
|
49
|
+
const conditions = Object.entries(next).map(([key, target]) => ({
|
|
50
|
+
expression: key, // The branch key becomes the expression
|
|
51
|
+
target,
|
|
52
|
+
}));
|
|
53
|
+
return { conditions };
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Serialize a workflow graph definition (from runtime) to JSON format
|
|
57
|
+
*
|
|
58
|
+
* @param definition - The runtime definition (with callbacks evaluated)
|
|
59
|
+
* @param rpcNameLookup - Function to get RPC name from a node's func
|
|
60
|
+
*/
|
|
61
|
+
export function serializeWorkflowGraph(definition, options) {
|
|
62
|
+
const nodes = {};
|
|
63
|
+
const entryNodeIds = [];
|
|
64
|
+
// Create a ref function that captures refs
|
|
65
|
+
const createRef = (nodeId, path) => ({
|
|
66
|
+
__isRef: true,
|
|
67
|
+
nodeId,
|
|
68
|
+
path,
|
|
69
|
+
});
|
|
70
|
+
// Track which nodes have incoming edges
|
|
71
|
+
const hasIncomingEdge = new Set();
|
|
72
|
+
// First pass: identify nodes with incoming edges
|
|
73
|
+
for (const [_nodeId, node] of Object.entries(definition.graph)) {
|
|
74
|
+
const next = node.next;
|
|
75
|
+
if (!next)
|
|
76
|
+
continue;
|
|
77
|
+
if (typeof next === 'string') {
|
|
78
|
+
hasIncomingEdge.add(next);
|
|
79
|
+
}
|
|
80
|
+
else if (Array.isArray(next)) {
|
|
81
|
+
next.forEach((n) => hasIncomingEdge.add(n));
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
for (const targets of Object.values(next)) {
|
|
85
|
+
if (typeof targets === 'string') {
|
|
86
|
+
hasIncomingEdge.add(targets);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
targets.forEach((n) => hasIncomingEdge.add(n));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Second pass: serialize nodes
|
|
95
|
+
for (const [nodeId, node] of Object.entries(definition.graph)) {
|
|
96
|
+
// Evaluate input callback to get the mapping
|
|
97
|
+
let input = {};
|
|
98
|
+
if (node.input) {
|
|
99
|
+
const rawInput = node.input(createRef);
|
|
100
|
+
input = serializeInputMapping(rawInput);
|
|
101
|
+
}
|
|
102
|
+
// Get RPC name from func
|
|
103
|
+
const rpcName = node.func?.name || 'unknown';
|
|
104
|
+
const funcNode = {
|
|
105
|
+
nodeId,
|
|
106
|
+
rpcName,
|
|
107
|
+
input,
|
|
108
|
+
next: serializeNext(node.next),
|
|
109
|
+
onError: node.onError,
|
|
110
|
+
};
|
|
111
|
+
nodes[nodeId] = funcNode;
|
|
112
|
+
// Entry nodes have no incoming edges
|
|
113
|
+
if (!hasIncomingEdge.has(nodeId)) {
|
|
114
|
+
entryNodeIds.push(nodeId);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
name: definition.name,
|
|
119
|
+
pikkuFuncName: definition.name, // For graph workflows, pikkuFuncName is the workflow name
|
|
120
|
+
source: 'graph',
|
|
121
|
+
description: options?.description,
|
|
122
|
+
tags: options?.tags,
|
|
123
|
+
wires: definition.wires,
|
|
124
|
+
nodes,
|
|
125
|
+
entryNodeIds,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Deserialize a workflow graph from JSON to runtime format
|
|
130
|
+
* This re-hydrates the JSON so it can be executed
|
|
131
|
+
*/
|
|
132
|
+
export function deserializeWorkflowGraph(serialized) {
|
|
133
|
+
const graph = {};
|
|
134
|
+
for (const [nodeId, node] of Object.entries(serialized.nodes)) {
|
|
135
|
+
// Only include FunctionNode properties (nodes with rpcName)
|
|
136
|
+
if ('rpcName' in node) {
|
|
137
|
+
const funcNode = node;
|
|
138
|
+
graph[nodeId] = {
|
|
139
|
+
rpcName: funcNode.rpcName,
|
|
140
|
+
input: funcNode.input ?? {},
|
|
141
|
+
next: funcNode.next,
|
|
142
|
+
onError: funcNode.onError,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
name: serialized.name,
|
|
148
|
+
wires: serialized.wires,
|
|
149
|
+
graph,
|
|
150
|
+
entryNodeIds: serialized.entryNodeIds,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serialized types for workflow graphs
|
|
3
|
+
* These are extracted by the inspector and stored as JSON
|
|
4
|
+
* Can be created from code (wireWorkflow) or UI
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Reference to data from another node or trigger
|
|
8
|
+
*/
|
|
9
|
+
export interface DataRef {
|
|
10
|
+
/** Source: 'trigger' for trigger input, or node ID for node output */
|
|
11
|
+
$ref: string;
|
|
12
|
+
/** Optional path into the data (dot notation: 'body.orderId') */
|
|
13
|
+
path?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Check if value is a DataRef
|
|
17
|
+
*/
|
|
18
|
+
export declare const isDataRef: (value: unknown) => value is DataRef;
|
|
19
|
+
/**
|
|
20
|
+
* Reference to a context/state variable
|
|
21
|
+
*/
|
|
22
|
+
export interface StateRef {
|
|
23
|
+
/** Context variable name */
|
|
24
|
+
$state: string;
|
|
25
|
+
/** Optional path into the value (dot notation for nested objects) */
|
|
26
|
+
path?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if value is a StateRef
|
|
30
|
+
*/
|
|
31
|
+
export declare const isStateRef: (value: unknown) => value is StateRef;
|
|
32
|
+
/**
|
|
33
|
+
* Helper functions for building input mappings
|
|
34
|
+
*/
|
|
35
|
+
export declare const ref: (nodeId: string, path?: string) => DataRef;
|
|
36
|
+
export declare const state: (name: string, path?: string) => StateRef;
|
|
37
|
+
/**
|
|
38
|
+
* Condition for branching
|
|
39
|
+
*/
|
|
40
|
+
export interface BranchCondition {
|
|
41
|
+
/** Expression to evaluate (uses node output references) */
|
|
42
|
+
expression: string;
|
|
43
|
+
/** Target node(s) if condition is true */
|
|
44
|
+
target: string | string[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Next node configuration
|
|
48
|
+
*/
|
|
49
|
+
export type SerializedNext = string | string[] | {
|
|
50
|
+
/** Conditions evaluated in order, first match wins */
|
|
51
|
+
conditions: BranchCondition[];
|
|
52
|
+
/** Default target if no conditions match */
|
|
53
|
+
default?: string | string[];
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Node execution options
|
|
57
|
+
*/
|
|
58
|
+
export interface NodeOptions {
|
|
59
|
+
/** Number of retry attempts on failure */
|
|
60
|
+
retries?: number;
|
|
61
|
+
/** Delay between retries (e.g., '1s', '5s') */
|
|
62
|
+
retryDelay?: string;
|
|
63
|
+
/** Timeout for node execution (e.g., '30s', '5m') */
|
|
64
|
+
timeout?: string;
|
|
65
|
+
/** If true, execute via queue (async). Default: false (inline) */
|
|
66
|
+
async?: boolean;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Flow node types for control flow (no RPC call)
|
|
70
|
+
*/
|
|
71
|
+
export type FlowType = 'sleep' | 'branch' | 'parallel' | 'fanout' | 'inline' | 'switch' | 'filter' | 'arrayPredicate' | 'return' | 'cancel' | 'set';
|
|
72
|
+
import type { ContextVariable, WorkflowContext } from '@pikku/core/workflow';
|
|
73
|
+
export type { ContextVariable, WorkflowContext };
|
|
74
|
+
/**
|
|
75
|
+
* Base node properties shared by all node types
|
|
76
|
+
*/
|
|
77
|
+
interface BaseNode {
|
|
78
|
+
/** Node ID */
|
|
79
|
+
nodeId: string;
|
|
80
|
+
/** Step name/description */
|
|
81
|
+
stepName?: string;
|
|
82
|
+
/** Next node(s) - simple, parallel, or conditional */
|
|
83
|
+
next?: SerializedNext;
|
|
84
|
+
/** Error routing - node(s) to execute on error */
|
|
85
|
+
onError?: string | string[];
|
|
86
|
+
/** Execution options */
|
|
87
|
+
options?: NodeOptions;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Function node - calls an RPC
|
|
91
|
+
*/
|
|
92
|
+
export interface FunctionNode extends BaseNode {
|
|
93
|
+
/** RPC function name */
|
|
94
|
+
rpcName: string;
|
|
95
|
+
/** Input mapping - values can be literals or DataRefs */
|
|
96
|
+
input?: Record<string, unknown | DataRef>;
|
|
97
|
+
/** Output variable name for storing result */
|
|
98
|
+
outputVar?: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Flow node - control flow only, no RPC call
|
|
102
|
+
*/
|
|
103
|
+
export interface FlowNode extends BaseNode {
|
|
104
|
+
/** Flow type */
|
|
105
|
+
flow: FlowType;
|
|
106
|
+
/** Flow-specific properties */
|
|
107
|
+
[key: string]: unknown;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Serialized graph node - either a function node or flow node
|
|
111
|
+
*/
|
|
112
|
+
export type SerializedGraphNode = FunctionNode | FlowNode;
|
|
113
|
+
/**
|
|
114
|
+
* Type guard for function nodes
|
|
115
|
+
*/
|
|
116
|
+
export declare const isFunctionNode: (node: SerializedGraphNode) => node is FunctionNode;
|
|
117
|
+
/**
|
|
118
|
+
* Type guard for flow nodes
|
|
119
|
+
*/
|
|
120
|
+
export declare const isFlowNode: (node: SerializedGraphNode) => node is FlowNode;
|
|
121
|
+
/**
|
|
122
|
+
* HTTP wire configuration with startNode
|
|
123
|
+
*/
|
|
124
|
+
export interface HttpWire {
|
|
125
|
+
route: string;
|
|
126
|
+
method: 'get' | 'post' | 'put' | 'patch' | 'delete';
|
|
127
|
+
startNode: string;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Channel wire configuration
|
|
131
|
+
*/
|
|
132
|
+
export interface ChannelWire {
|
|
133
|
+
name: string;
|
|
134
|
+
onConnect?: string;
|
|
135
|
+
onDisconnect?: string;
|
|
136
|
+
onMessage?: string;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Queue wire configuration
|
|
140
|
+
*/
|
|
141
|
+
export interface QueueWire {
|
|
142
|
+
name: string;
|
|
143
|
+
startNode: string;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* CLI wire configuration
|
|
147
|
+
*/
|
|
148
|
+
export interface CliWire {
|
|
149
|
+
command: string;
|
|
150
|
+
startNode: string;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* MCP wire configurations
|
|
154
|
+
*/
|
|
155
|
+
export interface McpWires {
|
|
156
|
+
tool?: Array<{
|
|
157
|
+
name: string;
|
|
158
|
+
startNode: string;
|
|
159
|
+
}>;
|
|
160
|
+
prompt?: Array<{
|
|
161
|
+
name: string;
|
|
162
|
+
startNode: string;
|
|
163
|
+
}>;
|
|
164
|
+
resource?: Array<{
|
|
165
|
+
uri: string;
|
|
166
|
+
startNode: string;
|
|
167
|
+
}>;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Schedule wire configuration
|
|
171
|
+
*/
|
|
172
|
+
export interface ScheduleWire {
|
|
173
|
+
cron?: string;
|
|
174
|
+
interval?: string;
|
|
175
|
+
startNode: string;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Trigger wire configuration
|
|
179
|
+
*/
|
|
180
|
+
export interface TriggerWire {
|
|
181
|
+
name: string;
|
|
182
|
+
startNode: string;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* All wire configurations for workflows
|
|
186
|
+
*/
|
|
187
|
+
export interface WorkflowWiresConfig {
|
|
188
|
+
http?: HttpWire[];
|
|
189
|
+
channel?: ChannelWire[];
|
|
190
|
+
queue?: QueueWire[];
|
|
191
|
+
cli?: CliWire[];
|
|
192
|
+
mcp?: McpWires;
|
|
193
|
+
schedule?: ScheduleWire[];
|
|
194
|
+
trigger?: TriggerWire[];
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Workflow source type
|
|
198
|
+
* - 'dsl': Pure DSL workflow (pikkuWorkflowFunc) - can be round-tripped to code
|
|
199
|
+
* - 'complex': Complex workflow (pikkuWorkflowComplexFunc) - contains inline steps, not serializable
|
|
200
|
+
* - 'graph': Graph-based workflow (pikkuWorkflowGraph)
|
|
201
|
+
*/
|
|
202
|
+
export type WorkflowSourceType = 'dsl' | 'complex' | 'graph';
|
|
203
|
+
/**
|
|
204
|
+
* Serialized workflow graph - the canonical JSON format
|
|
205
|
+
*/
|
|
206
|
+
export interface SerializedWorkflowGraph {
|
|
207
|
+
/** Workflow name */
|
|
208
|
+
name: string;
|
|
209
|
+
/** Pikku function name (for runtime registration) */
|
|
210
|
+
pikkuFuncName: string;
|
|
211
|
+
/** Source type: 'dsl' for pikkuWorkflowFunc, 'graph' for pikkuWorkflowGraph */
|
|
212
|
+
source: WorkflowSourceType;
|
|
213
|
+
/** Optional description */
|
|
214
|
+
description?: string;
|
|
215
|
+
/** Tags for organization */
|
|
216
|
+
tags?: string[];
|
|
217
|
+
/** Workflow context/state variables (from Zod schema) */
|
|
218
|
+
context?: WorkflowContext;
|
|
219
|
+
/** Wires - how the workflow is triggered */
|
|
220
|
+
wires: WorkflowWiresConfig;
|
|
221
|
+
/** Serialized nodes */
|
|
222
|
+
nodes: Record<string, SerializedGraphNode>;
|
|
223
|
+
/** Entry node(s) - first nodes to execute */
|
|
224
|
+
entryNodeIds: string[];
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* All workflow graphs (serialized)
|
|
228
|
+
*/
|
|
229
|
+
export type SerializedWorkflowGraphs = Record<string, SerializedWorkflowGraph>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serialized types for workflow graphs
|
|
3
|
+
* These are extracted by the inspector and stored as JSON
|
|
4
|
+
* Can be created from code (wireWorkflow) or UI
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Check if value is a DataRef
|
|
8
|
+
*/
|
|
9
|
+
export const isDataRef = (value) => typeof value === 'object' &&
|
|
10
|
+
value !== null &&
|
|
11
|
+
'$ref' in value &&
|
|
12
|
+
typeof value.$ref === 'string';
|
|
13
|
+
/**
|
|
14
|
+
* Check if value is a StateRef
|
|
15
|
+
*/
|
|
16
|
+
export const isStateRef = (value) => typeof value === 'object' &&
|
|
17
|
+
value !== null &&
|
|
18
|
+
'$state' in value &&
|
|
19
|
+
typeof value.$state === 'string';
|
|
20
|
+
/**
|
|
21
|
+
* Helper functions for building input mappings
|
|
22
|
+
*/
|
|
23
|
+
export const ref = (nodeId, path) => ({
|
|
24
|
+
$ref: nodeId,
|
|
25
|
+
path,
|
|
26
|
+
});
|
|
27
|
+
export const state = (name, path) => ({
|
|
28
|
+
$state: name,
|
|
29
|
+
path,
|
|
30
|
+
});
|
|
31
|
+
/**
|
|
32
|
+
* Type guard for function nodes
|
|
33
|
+
*/
|
|
34
|
+
export const isFunctionNode = (node) => 'rpcName' in node;
|
|
35
|
+
/**
|
|
36
|
+
* Type guard for flow nodes
|
|
37
|
+
*/
|
|
38
|
+
export const isFlowNode = (node) => 'flow' in node;
|
package/dist/visit.js
CHANGED
|
@@ -14,6 +14,9 @@ import { addRPCInvocations } from './add/add-rpc-invocations.js';
|
|
|
14
14
|
import { addMiddleware } from './add/add-middleware.js';
|
|
15
15
|
import { addPermission } from './add/add-permission.js';
|
|
16
16
|
import { addCLI, addCLIRenderers } from './add/add-cli.js';
|
|
17
|
+
import { addForgeNode } from './add/add-forge-node.js';
|
|
18
|
+
import { addForgeCredential } from './add/add-forge-credential.js';
|
|
19
|
+
import { addWorkflowGraph } from './add/add-workflow-graph.js';
|
|
17
20
|
export const visitSetup = (logger, checker, node, state, options) => {
|
|
18
21
|
addFileExtendsCoreType(node, checker, state.singletonServicesTypeImportMap, 'CoreSingletonServices', state);
|
|
19
22
|
addFileExtendsCoreType(node, checker, state.wireServicesTypeImportMap, 'CoreServices', state);
|
|
@@ -39,5 +42,8 @@ export const visitRoutes = (logger, checker, node, state, options) => {
|
|
|
39
42
|
addMCPResource(logger, node, checker, state, options);
|
|
40
43
|
addMCPTool(logger, node, checker, state, options);
|
|
41
44
|
addMCPPrompt(logger, node, checker, state, options);
|
|
45
|
+
addForgeNode(logger, node, checker, state, options);
|
|
46
|
+
addForgeCredential(logger, node, checker, state, options);
|
|
47
|
+
addWorkflowGraph(logger, node, checker, state, options);
|
|
42
48
|
ts.forEachChild(node, (child) => visitRoutes(logger, checker, child, state, options));
|
|
43
49
|
};
|
package/package.json
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/inspector",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "BUSL-1.1",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"module": "dist/index.js",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./workflow-graph": {
|
|
16
|
+
"types": "./dist/utils/workflow/graph/index.d.ts",
|
|
17
|
+
"import": "./dist/utils/workflow/graph/index.js",
|
|
18
|
+
"default": "./dist/utils/workflow/graph/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
9
21
|
"scripts": {
|
|
10
22
|
"tsc": "tsc",
|
|
11
23
|
"build": "tsc -b",
|
|
@@ -16,7 +28,7 @@
|
|
|
16
28
|
"test:coverage": "bash run-tests.sh --coverage"
|
|
17
29
|
},
|
|
18
30
|
"dependencies": {
|
|
19
|
-
"@pikku/core": "^0.11.
|
|
31
|
+
"@pikku/core": "^0.11.2",
|
|
20
32
|
"path-to-regexp": "^8.3.0",
|
|
21
33
|
"typescript": "^5.9"
|
|
22
34
|
},
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as ts from 'typescript'
|
|
2
|
+
import { getPropertyValue } from '../utils/get-property-value.js'
|
|
3
|
+
import { AddWiring } from '../types.js'
|
|
4
|
+
import { ErrorCode } from '../error-codes.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Inspector for wireForgeCredential calls.
|
|
8
|
+
* Extracts metadata for Forge package credential declarations.
|
|
9
|
+
* Note: wireForgeCredential is metadata-only - no runtime behavior.
|
|
10
|
+
* Schema is stored as the variable name reference; actual Zod→JSON Schema conversion happens at CLI build time.
|
|
11
|
+
*/
|
|
12
|
+
export const addForgeCredential: AddWiring = (
|
|
13
|
+
logger,
|
|
14
|
+
node,
|
|
15
|
+
_checker,
|
|
16
|
+
state,
|
|
17
|
+
_options
|
|
18
|
+
) => {
|
|
19
|
+
if (!ts.isCallExpression(node)) {
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const args = node.arguments
|
|
24
|
+
const firstArg = args[0]
|
|
25
|
+
const expression = node.expression
|
|
26
|
+
|
|
27
|
+
// Check if the call is to wireForgeCredential
|
|
28
|
+
if (
|
|
29
|
+
!ts.isIdentifier(expression) ||
|
|
30
|
+
expression.text !== 'wireForgeCredential'
|
|
31
|
+
) {
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!firstArg) {
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (ts.isObjectLiteralExpression(firstArg)) {
|
|
40
|
+
const obj = firstArg
|
|
41
|
+
|
|
42
|
+
const nameValue = getPropertyValue(obj, 'name') as string | null
|
|
43
|
+
const displayNameValue = getPropertyValue(obj, 'displayName') as
|
|
44
|
+
| string
|
|
45
|
+
| null
|
|
46
|
+
const descriptionValue = getPropertyValue(obj, 'description') as
|
|
47
|
+
| string
|
|
48
|
+
| null
|
|
49
|
+
const secretIdValue = getPropertyValue(obj, 'secretId') as string | null
|
|
50
|
+
|
|
51
|
+
// Get schema variable name for later runtime import
|
|
52
|
+
let schemaVariableName: string | null = null
|
|
53
|
+
for (const prop of obj.properties) {
|
|
54
|
+
if (
|
|
55
|
+
ts.isPropertyAssignment(prop) &&
|
|
56
|
+
ts.isIdentifier(prop.name) &&
|
|
57
|
+
prop.name.text === 'schema'
|
|
58
|
+
) {
|
|
59
|
+
if (ts.isIdentifier(prop.initializer)) {
|
|
60
|
+
schemaVariableName = prop.initializer.text
|
|
61
|
+
}
|
|
62
|
+
break
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Validate required fields
|
|
67
|
+
if (!nameValue) {
|
|
68
|
+
logger.critical(
|
|
69
|
+
ErrorCode.MISSING_NAME,
|
|
70
|
+
"Forge credential is missing the required 'name' property."
|
|
71
|
+
)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!displayNameValue) {
|
|
76
|
+
logger.critical(
|
|
77
|
+
ErrorCode.MISSING_NAME,
|
|
78
|
+
`Forge credential '${nameValue}' is missing the required 'displayName' property.`
|
|
79
|
+
)
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!secretIdValue) {
|
|
84
|
+
logger.critical(
|
|
85
|
+
ErrorCode.MISSING_NAME,
|
|
86
|
+
`Forge credential '${nameValue}' is missing the required 'secretId' property.`
|
|
87
|
+
)
|
|
88
|
+
return
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!schemaVariableName) {
|
|
92
|
+
logger.critical(
|
|
93
|
+
ErrorCode.MISSING_NAME,
|
|
94
|
+
`Forge credential '${nameValue}' is missing the required 'schema' property or schema is not a variable reference.`
|
|
95
|
+
)
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const sourceFile = node.getSourceFile().fileName
|
|
100
|
+
|
|
101
|
+
state.forgeCredentials.files.add(sourceFile)
|
|
102
|
+
|
|
103
|
+
// Register the zod schema in the central zodLookup for deferred conversion
|
|
104
|
+
const schemaLookupName = `ForgeCredential_${nameValue}`
|
|
105
|
+
state.zodLookup.set(schemaLookupName, {
|
|
106
|
+
variableName: schemaVariableName,
|
|
107
|
+
sourceFile,
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
// Store metadata - schema conversion happens later in schema-generator
|
|
111
|
+
state.forgeCredentials.meta[nameValue] = {
|
|
112
|
+
name: nameValue,
|
|
113
|
+
displayName: displayNameValue,
|
|
114
|
+
description: descriptionValue || undefined,
|
|
115
|
+
secretId: secretIdValue,
|
|
116
|
+
schema: schemaLookupName,
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|