@pikku/inspector 0.12.59 → 0.12.61
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 +373 -0
- package/OPTIMIZATION-PLAN.md +19 -19
- package/dist/add/add-agent-scorer.d.ts +2 -0
- package/dist/add/{add-ai-scorer.js → add-agent-scorer.js} +4 -4
- package/dist/add/{add-ai-agent.d.ts → add-agent.d.ts} +1 -1
- package/dist/add/{add-ai-agent.js → add-agent.js} +11 -11
- package/dist/add/add-auth.js +1 -1
- package/dist/add/add-cli.js +5 -0
- package/dist/add/add-functions.js +10 -1
- package/dist/add/add-http-route.js +18 -18
- package/dist/add/add-keyed-wiring.d.ts +2 -0
- package/dist/add/add-keyed-wiring.js +3 -1
- package/dist/add/add-middleware.js +5 -5
- package/dist/add/add-personas.js +3 -0
- package/dist/add/add-secret.js +1 -0
- package/dist/add/add-variable.js +1 -0
- package/dist/add/add-wire-remote-addon.js +2 -1
- package/dist/add/add-workflow-graph.js +63 -6
- package/dist/error-codes.d.ts +3 -1
- package/dist/error-codes.js +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/inspector.js +13 -3
- package/dist/types.d.ts +10 -5
- package/dist/utils/annotate-http-route-auth.js +2 -1
- package/dist/utils/check-secret-sinks.js +2 -1
- package/dist/utils/compute-required-schemas.d.ts +1 -1
- package/dist/utils/contract-hashes.d.ts +2 -1
- package/dist/utils/contract-hashes.js +1 -1
- package/dist/utils/extract-function-name.js +1 -1
- package/dist/utils/extract-services.d.ts +1 -1
- package/dist/utils/extract-surface-usage.d.ts +29 -0
- package/dist/utils/extract-surface-usage.js +97 -0
- package/dist/utils/filter-inspector-state.js +53 -16
- package/dist/utils/filter-utils.d.ts +1 -1
- package/dist/utils/get-property-value.js +42 -3
- package/dist/utils/load-addon-functions-meta.js +31 -0
- package/dist/utils/middleware.d.ts +2 -2
- package/dist/utils/middleware.js +5 -5
- package/dist/utils/permissions.d.ts +1 -1
- package/dist/utils/post-process.d.ts +28 -8
- package/dist/utils/post-process.js +83 -18
- package/dist/utils/resolve-deploy-target.d.ts +1 -1
- package/dist/utils/resolve-function-meta.d.ts +1 -1
- package/dist/utils/resolve-function-types.d.ts +1 -1
- package/dist/utils/resolve-versions.js +1 -1
- package/dist/utils/schema-generator.d.ts +1 -1
- package/dist/utils/serialize-inspector-state.d.ts +4 -3
- package/dist/utils/serialize-inspector-state.js +6 -4
- package/dist/utils/serialize-openapi-json.d.ts +1 -1
- package/dist/utils/validate-auth-sessionless.js +2 -1
- package/dist/utils/workflow/graph/finalize-workflows.js +1 -1
- package/dist/utils/workflow/graph/serialize-workflow-graph.d.ts +6 -1
- package/dist/utils/workflow/graph/serialize-workflow-graph.js +26 -2
- package/dist/utils/workflow/graph/workflow-graph.types.d.ts +9 -0
- package/dist/visit.js +4 -4
- package/package.json +10 -10
- package/run-tests.sh +0 -0
- package/src/add/{add-ai-scorer.test.ts → add-agent-scorer.test.ts} +11 -11
- package/src/add/{add-ai-scorer.ts → add-agent-scorer.ts} +4 -4
- package/src/add/{add-ai-agent.test.ts → add-agent.test.ts} +1 -1
- package/src/add/{add-ai-agent.ts → add-agent.ts} +13 -21
- package/src/add/add-auth.ts +2 -2
- package/src/add/add-cli.ts +6 -0
- package/src/add/add-credential.ts +3 -6
- package/src/add/add-file-with-factory.ts +2 -6
- package/src/add/add-functions.ts +23 -25
- package/src/add/add-http-route-ref.test.ts +149 -0
- package/src/add/add-http-route.ts +25 -28
- package/src/add/add-keyed-wiring.ts +8 -7
- package/src/add/add-middleware.ts +7 -5
- package/src/add/add-permission.ts +1 -1
- package/src/add/add-personas.test.ts +24 -0
- package/src/add/add-personas.ts +3 -1
- package/src/add/add-scenario-step.test.ts +35 -0
- package/src/add/add-scope.test.ts +4 -4
- package/src/add/add-scope.ts +4 -8
- package/src/add/add-secret.ts +1 -0
- package/src/add/add-system-role.test.ts +8 -8
- package/src/add/add-system-role.ts +2 -4
- package/src/add/add-variable.ts +1 -0
- package/src/add/add-wire-remote-addon.ts +4 -1
- package/src/add/add-workflow-graph-foreach.test.ts +174 -0
- package/src/add/add-workflow-graph.ts +78 -6
- package/src/add/add-workflow-sleep-expression.test.ts +3 -4
- package/src/add/addon-contracts.test.ts +1 -1
- package/src/add/config-value-metadata.test.ts +94 -0
- package/src/add/secret-sink-check.test.ts +4 -1
- package/src/error-codes.ts +3 -1
- package/src/index.ts +3 -4
- package/src/inspector.ts +20 -3
- package/src/types.ts +10 -7
- package/src/utils/annotate-http-route-auth.test.ts +5 -7
- package/src/utils/annotate-http-route-auth.ts +2 -1
- package/src/utils/check-secret-sinks.ts +4 -1
- package/src/utils/compute-required-schemas.ts +1 -1
- package/src/utils/contract-hashes.test.ts +1 -1
- package/src/utils/contract-hashes.ts +3 -2
- package/src/utils/extract-function-name.ts +1 -1
- package/src/utils/extract-services.ts +4 -2
- package/src/utils/extract-surface-usage.test.ts +147 -0
- package/src/utils/extract-surface-usage.ts +121 -0
- package/src/utils/filter-inspector-state.test.ts +138 -12
- package/src/utils/filter-inspector-state.ts +82 -21
- package/src/utils/filter-utils.ts +1 -1
- package/src/utils/get-property-value.ts +43 -5
- package/src/utils/load-addon-functions-meta.ts +40 -0
- package/src/utils/middleware.test.ts +23 -23
- package/src/utils/middleware.ts +6 -6
- package/src/utils/permissions.ts +1 -1
- package/src/utils/post-process.test.ts +68 -11
- package/src/utils/post-process.ts +91 -28
- package/src/utils/resolve-deploy-target.ts +1 -1
- package/src/utils/resolve-function-meta.ts +1 -1
- package/src/utils/resolve-function-types.ts +1 -1
- package/src/utils/resolve-versions.ts +1 -1
- package/src/utils/schema-generator.ts +2 -1
- package/src/utils/serialize-inspector-state.ts +10 -7
- package/src/utils/serialize-mcp-json.ts +1 -1
- package/src/utils/serialize-openapi-json.ts +1 -1
- package/src/utils/validate-auth-sessionless.ts +2 -1
- package/src/utils/validate-tags-resolve-to-middleware.test.ts +4 -1
- package/src/utils/workflow/dsl/extract-approval-policy.test.ts +4 -1
- package/src/utils/workflow/graph/convert-dsl-to-graph-skip.test.ts +1 -1
- package/src/utils/workflow/graph/finalize-workflows.ts +6 -2
- package/src/utils/workflow/graph/serialize-workflow-graph.ts +34 -3
- package/src/utils/workflow/graph/workflow-graph.types.ts +10 -0
- package/src/visit.ts +4 -4
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/add/add-ai-scorer.d.ts +0 -2
- package/dist/utils/secret-brokers.d.ts +0 -2
- package/dist/utils/secret-brokers.js +0 -9
|
@@ -7,11 +7,12 @@ import type {
|
|
|
7
7
|
} from '../types.js'
|
|
8
8
|
import type {
|
|
9
9
|
FunctionServicesMeta,
|
|
10
|
-
MiddlewareMetadata,
|
|
11
10
|
PermissionMetadata,
|
|
12
|
-
} from '@pikku/core'
|
|
11
|
+
} from '@pikku/core/function'
|
|
12
|
+
import type { MiddlewareMetadata } from '@pikku/core/middleware'
|
|
13
13
|
import type ts from 'typescript'
|
|
14
14
|
import { existsSync, readFileSync } from 'node:fs'
|
|
15
|
+
import { createRequire } from 'node:module'
|
|
15
16
|
import { join } from 'node:path'
|
|
16
17
|
import { extractTypeKeys } from './type-utils.js'
|
|
17
18
|
import { ErrorCode } from '../error-codes.js'
|
|
@@ -308,12 +309,12 @@ export function aggregateRequiredServices(
|
|
|
308
309
|
}
|
|
309
310
|
}
|
|
310
311
|
|
|
311
|
-
// AI agents need
|
|
312
|
+
// AI agents need agentStorage + agentRunState + agentRunService + agentRunner
|
|
312
313
|
if (Object.keys(state.agents.agentsMeta).length > 0) {
|
|
313
|
-
requiredServices.add('
|
|
314
|
-
requiredServices.add('
|
|
314
|
+
requiredServices.add('agentStorage')
|
|
315
|
+
requiredServices.add('agentRunState')
|
|
315
316
|
requiredServices.add('agentRunService')
|
|
316
|
-
requiredServices.add('
|
|
317
|
+
requiredServices.add('agentRunner')
|
|
317
318
|
}
|
|
318
319
|
|
|
319
320
|
// Channels need eventHub for pub/sub
|
|
@@ -480,10 +481,56 @@ export function validateVariableOverrides(
|
|
|
480
481
|
}
|
|
481
482
|
|
|
482
483
|
/**
|
|
484
|
+
* Can `pkg` be resolved from `rootDir`, by any means the loader would use?
|
|
485
|
+
*
|
|
486
|
+
* Resolution is attempted against `<pkg>/package.json` first and the bare
|
|
487
|
+
* specifier second, because neither alone is conclusive. A package with a
|
|
488
|
+
* restrictive `exports` map refuses the first with
|
|
489
|
+
* `ERR_PACKAGE_PATH_NOT_EXPORTED` — which is not a failure at all: reaching the
|
|
490
|
+
* exports map means the package was found. Only a module-not-found from both
|
|
491
|
+
* attempts means it is genuinely absent.
|
|
492
|
+
*/
|
|
493
|
+
const isPackageResolvable = (pkg: string, rootDir: string): boolean => {
|
|
494
|
+
const NOT_FOUND = new Set(['MODULE_NOT_FOUND', 'ERR_MODULE_NOT_FOUND'])
|
|
495
|
+
let req: ReturnType<typeof createRequire>
|
|
496
|
+
try {
|
|
497
|
+
req = createRequire(join(rootDir, 'package.json'))
|
|
498
|
+
} catch {
|
|
499
|
+
return true // cannot ask; do not accuse
|
|
500
|
+
}
|
|
501
|
+
for (const specifier of [`${pkg}/package.json`, pkg]) {
|
|
502
|
+
try {
|
|
503
|
+
req.resolve(specifier)
|
|
504
|
+
return true
|
|
505
|
+
} catch (e: any) {
|
|
506
|
+
if (!NOT_FOUND.has(e?.code)) return true
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return false
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Every wired addon must be installed, in the bucket matching how it is used.
|
|
514
|
+
*
|
|
483
515
|
* A `wireRemoteAddon` package ships types only — its handlers run on the host —
|
|
484
516
|
* so it MUST be a devDependency, not a production dependency (a prod dep would
|
|
485
|
-
* drag in the runtime deps remote consumption exists to avoid).
|
|
486
|
-
*
|
|
517
|
+
* drag in the runtime deps remote consumption exists to avoid).
|
|
518
|
+
*
|
|
519
|
+
* A plain `wireAddon` only has to be *resolvable*. The failure it allows is
|
|
520
|
+
* quiet in a way the remote one is not — an absent package makes the metadata
|
|
521
|
+
* load fail, which is caught and downgraded to a warning, so every
|
|
522
|
+
* `ref('<namespace>:…')` resolves against nothing and the surface is dead at
|
|
523
|
+
* runtime. The generated console is the common case: `scaffold.console` emits
|
|
524
|
+
* `wireAddon({ package: '@pikku/addon-console' })` and refs into it, and a
|
|
525
|
+
* project that never installed the package gets a console that 404s with
|
|
526
|
+
* nothing in the build output saying why.
|
|
527
|
+
*
|
|
528
|
+
* Deliberately a resolution check and not a `package.json` one. An addon is
|
|
529
|
+
* discovered because it is *wired*, not because it is a declared dependency —
|
|
530
|
+
* addons arrive by workspace link, by a local `addons/` directory recorded in
|
|
531
|
+
* `pikku-addons.json`, and by plain install, and only the last of those shows
|
|
532
|
+
* up in `dependencies`. Asking the resolver is the only question that matches
|
|
533
|
+
* what the loader will actually do.
|
|
487
534
|
*/
|
|
488
535
|
export function validateRemoteAddonDependencies(
|
|
489
536
|
logger: InspectorLogger,
|
|
@@ -492,10 +539,19 @@ export function validateRemoteAddonDependencies(
|
|
|
492
539
|
const { wireAddonDeclarations } = state.rpc
|
|
493
540
|
if (!wireAddonDeclarations || wireAddonDeclarations.size === 0) return
|
|
494
541
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
)
|
|
498
|
-
|
|
542
|
+
// Local addons first, and independently of the manifest below: their check
|
|
543
|
+
// asks the resolver, not `package.json`, so a project without a manifest (as
|
|
544
|
+
// in some tests) must not skip it.
|
|
545
|
+
for (const [namespace, decl] of wireAddonDeclarations.entries()) {
|
|
546
|
+
if (decl.remote) continue
|
|
547
|
+
if (isPackageResolvable(decl.package, state.rootDir)) continue
|
|
548
|
+
logger.critical(
|
|
549
|
+
ErrorCode.ADDON_NOT_INSTALLED,
|
|
550
|
+
`Addon '${namespace}' ('${decl.package}') is wired with wireAddon but cannot be resolved — every ref('${namespace}:…') will resolve to nothing and the surface will be dead at runtime. Install it, or remove the wireAddon call.`
|
|
551
|
+
)
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (!Array.from(wireAddonDeclarations.values()).some((d) => d.remote)) return
|
|
499
555
|
|
|
500
556
|
const pkgJsonPath = join(state.rootDir, 'package.json')
|
|
501
557
|
if (!existsSync(pkgJsonPath)) return // no manifest to check (e.g. some tests)
|
|
@@ -724,10 +780,7 @@ export function computeRequiredSchemas(
|
|
|
724
780
|
* the functions that are actually invoked, so this blocks CI through
|
|
725
781
|
* `--fail-on-error` without failing a dev server over a function nobody calls.
|
|
726
782
|
*/
|
|
727
|
-
export function
|
|
728
|
-
logger: InspectorLogger,
|
|
729
|
-
state: InspectorState
|
|
730
|
-
): void {
|
|
783
|
+
export function unresolvedSchemaReferences(state: InspectorState): string[] {
|
|
731
784
|
const { functions, schemas } = state
|
|
732
785
|
const available = new Set(Object.keys(schemas ?? {}))
|
|
733
786
|
const unresolved: string[] = []
|
|
@@ -752,6 +805,15 @@ export function validateSchemaReferences(
|
|
|
752
805
|
}
|
|
753
806
|
}
|
|
754
807
|
|
|
808
|
+
return unresolved
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
export function validateSchemaReferences(
|
|
812
|
+
logger: InspectorLogger,
|
|
813
|
+
state: InspectorState
|
|
814
|
+
): void {
|
|
815
|
+
const unresolved = unresolvedSchemaReferences(state)
|
|
816
|
+
|
|
755
817
|
if (unresolved.length === 0) return
|
|
756
818
|
|
|
757
819
|
logger.diagnostic({
|
|
@@ -842,10 +904,11 @@ export function validateScenarioServices(
|
|
|
842
904
|
*
|
|
843
905
|
* - the step target must be a static string literal (PKU678) — otherwise it
|
|
844
906
|
* can't be bundled, typed or drawn;
|
|
845
|
-
* - a step
|
|
846
|
-
*
|
|
847
|
-
*
|
|
848
|
-
*
|
|
907
|
+
* - a step that runs as a persona must be given one (PKU677) — declared by
|
|
908
|
+
* `actor: true`, or implied by a `browser` binding, since the browser context
|
|
909
|
+
* signs in as that persona. It is checked statically even though a browser
|
|
910
|
+
* binding only runs on a browser run: a step that can never be driven by a
|
|
911
|
+
* human is a latent failure, not a passing default run.
|
|
849
912
|
* - a scenario written as a step ladder must assert something (PKU680).
|
|
850
913
|
*
|
|
851
914
|
* It also marks the step's function as used, so `scenario run` boots the
|
|
@@ -872,14 +935,14 @@ export function validateScenarioSteps(
|
|
|
872
935
|
}
|
|
873
936
|
state.serviceAggregation.usedFunctions.add(step.stepFunc)
|
|
874
937
|
const stepMeta = state.functions.meta[step.stepFunc]
|
|
875
|
-
if (
|
|
876
|
-
stepMeta
|
|
877
|
-
|
|
878
|
-
|
|
938
|
+
if (stepMeta?.scenarioStepRequiresActor && !step.actor) {
|
|
939
|
+
const why = stepMeta.scenarioStepSurfaces?.includes('browser')
|
|
940
|
+
? 'declares a browser binding'
|
|
941
|
+
: 'is driven by a persona'
|
|
879
942
|
logger.critical(
|
|
880
|
-
ErrorCode.
|
|
881
|
-
`Scenario '${workflowName}' calls step '${step.stepFunc}', which
|
|
882
|
-
`Pass { actor: actors.<name> } so
|
|
943
|
+
ErrorCode.SCENARIO_STEP_NEEDS_ACTOR,
|
|
944
|
+
`Scenario '${workflowName}' calls step '${step.stepFunc}', which ${why}, without an actor. ` +
|
|
945
|
+
`Pass { actor: actors.<name> } so it runs as that persona.`
|
|
883
946
|
)
|
|
884
947
|
}
|
|
885
948
|
continue
|
|
@@ -1230,7 +1293,7 @@ export function validateScopeReferences(
|
|
|
1230
1293
|
*
|
|
1231
1294
|
* The description check is separate and opt-in (`--strict-meta`), because the
|
|
1232
1295
|
* description is what the model is told the tool does: without one, the tool is
|
|
1233
|
-
* offered to it under its own name (`
|
|
1296
|
+
* offered to it under its own name (`agent-prepare.ts`), which is a silent
|
|
1234
1297
|
* quality loss rather than a failure. A title deliberately does not satisfy it —
|
|
1235
1298
|
* a title labels a tool in a UI, it does not tell a model when to reach for it.
|
|
1236
1299
|
*/
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { parseVersionedId, formatVersionedId } from '@pikku/core'
|
|
1
|
+
import { parseVersionedId, formatVersionedId } from '@pikku/core/utils'
|
|
2
2
|
import type { CLICommandMeta } from '@pikku/core/cli'
|
|
3
3
|
import type { InspectorState, InspectorLogger } from '../types.js'
|
|
4
4
|
import { ErrorCode } from '../error-codes.js'
|
|
@@ -12,7 +12,8 @@ import { createGenerator, RootlessError } from 'ts-json-schema-generator'
|
|
|
12
12
|
import { register } from 'tsx/esm/api'
|
|
13
13
|
import * as z from 'zod'
|
|
14
14
|
import { zodToTs, createAuxiliaryTypeStore } from 'zod-to-ts'
|
|
15
|
-
import type { FunctionsMeta
|
|
15
|
+
import type { FunctionsMeta } from '@pikku/core/services'
|
|
16
|
+
import type { JSONValue } from '@pikku/core/utils'
|
|
16
17
|
import type { HTTPWiringsMeta } from '@pikku/core/http'
|
|
17
18
|
import type { TypesMap } from '../types-map.js'
|
|
18
19
|
import { ErrorCode } from '../error-codes.js'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SecretUsage } from './extract-secret-usage.js'
|
|
2
|
-
import type { JSONValue } from '@pikku/core'
|
|
2
|
+
import type { JSONValue } from '@pikku/core/utils'
|
|
3
3
|
import type {
|
|
4
4
|
InspectorDiagnostic,
|
|
5
5
|
InspectorFeature,
|
|
@@ -212,6 +212,7 @@ export interface SerializableInspectorState {
|
|
|
212
212
|
files: string[]
|
|
213
213
|
usage: Record<string, SecretUsage>
|
|
214
214
|
}
|
|
215
|
+
surfaceUsage: InspectorState['surfaceUsage']
|
|
215
216
|
credentials: {
|
|
216
217
|
definitions: InspectorState['credentials']['definitions']
|
|
217
218
|
files: string[]
|
|
@@ -263,8 +264,8 @@ export interface SerializableInspectorState {
|
|
|
263
264
|
]
|
|
264
265
|
>
|
|
265
266
|
}
|
|
266
|
-
|
|
267
|
-
definitions: InspectorState['
|
|
267
|
+
agentMiddleware: {
|
|
268
|
+
definitions: InspectorState['agentMiddleware']['definitions']
|
|
268
269
|
}
|
|
269
270
|
permissions: {
|
|
270
271
|
definitions: InspectorState['permissions']['definitions']
|
|
@@ -440,6 +441,7 @@ export function serializeInspectorState(
|
|
|
440
441
|
files: Array.from(state.secrets.files),
|
|
441
442
|
usage: Object.fromEntries(state.secrets.usage),
|
|
442
443
|
},
|
|
444
|
+
surfaceUsage: state.surfaceUsage,
|
|
443
445
|
credentials: {
|
|
444
446
|
definitions: state.credentials.definitions,
|
|
445
447
|
files: Array.from(state.credentials.files),
|
|
@@ -473,8 +475,8 @@ export function serializeInspectorState(
|
|
|
473
475
|
state.channelMiddleware.tagMiddleware.entries()
|
|
474
476
|
),
|
|
475
477
|
},
|
|
476
|
-
|
|
477
|
-
definitions: state.
|
|
478
|
+
agentMiddleware: {
|
|
479
|
+
definitions: state.agentMiddleware.definitions,
|
|
478
480
|
},
|
|
479
481
|
permissions: {
|
|
480
482
|
definitions: state.permissions.definitions,
|
|
@@ -648,6 +650,7 @@ export function deserializeInspectorState(
|
|
|
648
650
|
files: new Set(data.secrets?.files || []),
|
|
649
651
|
usage: new Map(Object.entries(data.secrets?.usage || {})),
|
|
650
652
|
},
|
|
653
|
+
surfaceUsage: data.surfaceUsage || {},
|
|
651
654
|
credentials: {
|
|
652
655
|
definitions: data.credentials?.definitions || [],
|
|
653
656
|
files: new Set(data.credentials?.files || []),
|
|
@@ -679,8 +682,8 @@ export function deserializeInspectorState(
|
|
|
679
682
|
instances: data.channelMiddleware?.instances || {},
|
|
680
683
|
tagMiddleware: new Map(data.channelMiddleware?.tagMiddleware || []),
|
|
681
684
|
},
|
|
682
|
-
|
|
683
|
-
definitions: data.
|
|
685
|
+
agentMiddleware: {
|
|
686
|
+
definitions: data.agentMiddleware?.definitions || {},
|
|
684
687
|
},
|
|
685
688
|
permissions: {
|
|
686
689
|
definitions: data.permissions.definitions,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FunctionsMeta } from '@pikku/core'
|
|
1
|
+
import type { FunctionsMeta } from '@pikku/core/services'
|
|
2
2
|
import type { HTTPWiringsMeta } from '@pikku/core/http'
|
|
3
3
|
import type { InspectorLogger } from '../types.js'
|
|
4
4
|
import _convertSchema from '@openapi-contrib/json-schema-to-openapi-schema'
|
|
@@ -2,6 +2,7 @@ import type * as ts from 'typescript'
|
|
|
2
2
|
import { getPropertyValue } from './get-property-value.js'
|
|
3
3
|
import { ErrorCode } from '../error-codes.js'
|
|
4
4
|
import type { InspectorLogger, InspectorState } from '../types.js'
|
|
5
|
+
import { resolveFunctionMeta } from './resolve-function-meta.js'
|
|
5
6
|
|
|
6
7
|
export function validateAuthSessionless(
|
|
7
8
|
logger: InspectorLogger,
|
|
@@ -11,7 +12,7 @@ export function validateAuthSessionless(
|
|
|
11
12
|
wireDescription: string,
|
|
12
13
|
inheritedAuth?: boolean
|
|
13
14
|
): boolean {
|
|
14
|
-
const fnMeta = state
|
|
15
|
+
const fnMeta = resolveFunctionMeta(state, funcName)
|
|
15
16
|
if (!fnMeta) return true
|
|
16
17
|
|
|
17
18
|
const routeAuth = getPropertyValue(obj, 'auth')
|
|
@@ -59,7 +59,10 @@ describe('validateTagsResolveToMiddleware', () => {
|
|
|
59
59
|
})
|
|
60
60
|
|
|
61
61
|
test('stays quiet when http middleware is registered for the tag', () => {
|
|
62
|
-
assert.deepEqual(
|
|
62
|
+
assert.deepEqual(
|
|
63
|
+
run(stateWith({ console: { tags: ['admin'] } }, ['admin'])),
|
|
64
|
+
[]
|
|
65
|
+
)
|
|
63
66
|
})
|
|
64
67
|
|
|
65
68
|
test('stays quiet when only channel middleware is registered for the tag', () => {
|
|
@@ -26,7 +26,10 @@ const approvalStepFor = (options: string): ApprovalStepMeta => {
|
|
|
26
26
|
|
|
27
27
|
let node: ts.Node | undefined
|
|
28
28
|
const visit = (n: ts.Node) => {
|
|
29
|
-
if (
|
|
29
|
+
if (
|
|
30
|
+
ts.isCallExpression(n) &&
|
|
31
|
+
n.expression.getText() === 'pikkuWorkflowFunc'
|
|
32
|
+
) {
|
|
30
33
|
node = n
|
|
31
34
|
}
|
|
32
35
|
ts.forEachChild(n, visit)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { test } from 'node:test'
|
|
2
2
|
import assert from 'node:assert'
|
|
3
3
|
import { convertDslToGraph } from './convert-dsl-to-graph.js'
|
|
4
|
-
import type { WorkflowsMeta } from '@pikku/core'
|
|
4
|
+
import type { WorkflowsMeta } from '@pikku/core/workflow'
|
|
5
5
|
|
|
6
6
|
const scenarioMeta = (
|
|
7
7
|
overrides: Partial<WorkflowsMeta[string]> = {}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import type { FunctionsMeta } from '@pikku/core'
|
|
2
|
-
import {
|
|
1
|
+
import type { FunctionsMeta } from '@pikku/core/services'
|
|
2
|
+
import {
|
|
3
|
+
isVersionedId,
|
|
4
|
+
formatVersionedId,
|
|
5
|
+
parseVersionedId,
|
|
6
|
+
} from '@pikku/core/utils'
|
|
3
7
|
import type { SerializedWorkflowGraph } from './workflow-graph.types.js'
|
|
4
8
|
import { canonicalJSON, hashString } from '../../hash.js'
|
|
5
9
|
import { convertDslToGraph } from './convert-dsl-to-graph.js'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isRef } from '@pikku/core/workflow'
|
|
1
|
+
import { isRef, template } from '@pikku/core/workflow'
|
|
2
2
|
import type {
|
|
3
3
|
SerializedWorkflowGraph,
|
|
4
4
|
SerializedGraphNode,
|
|
@@ -36,6 +36,26 @@ function serializeInputMapping(
|
|
|
36
36
|
return result
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Convert a `forEach` source — a bare node id, a ref, or a `(ref) => ref(...)`
|
|
41
|
+
* callback — to the serialized DataRef form
|
|
42
|
+
*/
|
|
43
|
+
function serializeForEach(
|
|
44
|
+
forEach: unknown,
|
|
45
|
+
createRef: (nodeId: string, path?: string) => { nodeId: string; path?: string }
|
|
46
|
+
): DataRef | undefined {
|
|
47
|
+
if (forEach === undefined) return undefined
|
|
48
|
+
if (typeof forEach === 'string') return { $ref: forEach }
|
|
49
|
+
if (typeof forEach === 'function') {
|
|
50
|
+
return serializeForEach(
|
|
51
|
+
(forEach as (ref: unknown) => unknown)(createRef),
|
|
52
|
+
createRef
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
if (isRef(forEach)) return convertRef(forEach)
|
|
56
|
+
return undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
39
59
|
/**
|
|
40
60
|
* Convert next config from runtime format to serialized format
|
|
41
61
|
* Runtime uses Record<string, string | string[]> for branching with graph.branch()
|
|
@@ -73,7 +93,9 @@ export function serializeWorkflowGraph(
|
|
|
73
93
|
string,
|
|
74
94
|
{
|
|
75
95
|
func: { name?: string }
|
|
76
|
-
input?: (ref: any) => Record<string, unknown>
|
|
96
|
+
input?: (ref: any, template?: any, $item?: any) => Record<string, unknown>
|
|
97
|
+
forEach?: string | { nodeId: string; path?: string } | ((ref: any) => unknown)
|
|
98
|
+
mode?: 'parallel' | 'sequential'
|
|
77
99
|
next?: string | string[] | Record<string, string | string[]>
|
|
78
100
|
onError?: string | string[]
|
|
79
101
|
notes?: string
|
|
@@ -96,6 +118,8 @@ export function serializeWorkflowGraph(
|
|
|
96
118
|
path,
|
|
97
119
|
})
|
|
98
120
|
|
|
121
|
+
const createItemRef = (path?: string) => createRef('$item', path)
|
|
122
|
+
|
|
99
123
|
// Track which nodes have incoming edges
|
|
100
124
|
const hasIncomingEdge = new Set<string>()
|
|
101
125
|
|
|
@@ -124,7 +148,7 @@ export function serializeWorkflowGraph(
|
|
|
124
148
|
// Evaluate input callback to get the mapping
|
|
125
149
|
let input: Record<string, unknown | DataRef> = {}
|
|
126
150
|
if (node.input) {
|
|
127
|
-
const rawInput = node.input(createRef)
|
|
151
|
+
const rawInput = node.input(createRef, template, createItemRef)
|
|
128
152
|
input = serializeInputMapping(rawInput)
|
|
129
153
|
}
|
|
130
154
|
|
|
@@ -138,6 +162,13 @@ export function serializeWorkflowGraph(
|
|
|
138
162
|
next: serializeNext(node.next),
|
|
139
163
|
onError: node.onError,
|
|
140
164
|
}
|
|
165
|
+
const forEach = serializeForEach(node.forEach, createRef)
|
|
166
|
+
if (forEach) {
|
|
167
|
+
funcNode.forEach = forEach
|
|
168
|
+
if (node.mode !== undefined) {
|
|
169
|
+
funcNode.mode = node.mode
|
|
170
|
+
}
|
|
171
|
+
}
|
|
141
172
|
if (node.notes !== undefined) {
|
|
142
173
|
funcNode.notes = node.notes
|
|
143
174
|
}
|
|
@@ -141,11 +141,21 @@ interface BaseNode {
|
|
|
141
141
|
/**
|
|
142
142
|
* Function node - calls an RPC
|
|
143
143
|
*/
|
|
144
|
+
/** How the per-item instances of a `forEach` node run */
|
|
145
|
+
export type ForEachMode = 'parallel' | 'sequential'
|
|
146
|
+
|
|
144
147
|
export interface FunctionNode extends BaseNode {
|
|
145
148
|
/** RPC function name */
|
|
146
149
|
rpcName: string
|
|
147
150
|
/** Input mapping - values can be literals or DataRefs */
|
|
148
151
|
input?: Record<string, unknown | DataRef>
|
|
152
|
+
/**
|
|
153
|
+
* Run this node once per element of the referenced array. The node's own
|
|
154
|
+
* result becomes the ordered array of per-item results.
|
|
155
|
+
*/
|
|
156
|
+
forEach?: DataRef
|
|
157
|
+
/** How the per-item instances run. Defaults to 'parallel'. */
|
|
158
|
+
mode?: ForEachMode
|
|
149
159
|
/** Output variable name for storing result */
|
|
150
160
|
outputVar?: string
|
|
151
161
|
/** Hash of nodeId + RPC input/output schemas for version detection */
|
package/src/visit.ts
CHANGED
|
@@ -33,8 +33,8 @@ import { addPersonas } from './add/add-personas.js'
|
|
|
33
33
|
import { addVariable } from './add/add-variable.js'
|
|
34
34
|
import { addWorkflowGraph } from './add/add-workflow-graph.js'
|
|
35
35
|
import { addFeature } from './add/add-feature.js'
|
|
36
|
-
import {
|
|
37
|
-
import {
|
|
36
|
+
import { addAgent } from './add/add-agent.js'
|
|
37
|
+
import { addAgentScorer } from './add/add-agent-scorer.js'
|
|
38
38
|
import { addApprovalDescription } from './add/add-approval-description.js'
|
|
39
39
|
|
|
40
40
|
export const visitSetup = (
|
|
@@ -172,8 +172,8 @@ export const visitRoutes = (
|
|
|
172
172
|
addMCPPrompt(logger, node, checker, state, nextOptions)
|
|
173
173
|
addWorkflowGraph(logger, node, checker, state, nextOptions)
|
|
174
174
|
addFeature(logger, node, checker, state, nextOptions)
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
addAgent(logger, node, checker, state, nextOptions)
|
|
176
|
+
addAgentScorer(logger, node, checker, state, nextOptions)
|
|
177
177
|
|
|
178
178
|
ts.forEachChild(node, (child) =>
|
|
179
179
|
visitRoutes(logger, checker, child, state, nextOptions)
|