@ontrails/trails 1.0.0-beta.15 → 1.0.0-beta.17
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 +215 -2
- package/README.md +27 -0
- package/package.json +21 -8
- package/src/app.ts +15 -5
- package/src/cli.ts +303 -10
- package/src/completions.ts +240 -0
- package/src/load-app-mirror.ts +160 -0
- package/src/local-state-io.ts +153 -0
- package/src/project-writes.ts +320 -0
- package/src/run-collision.ts +125 -0
- package/src/run-completions-install.ts +179 -0
- package/src/run-example.ts +149 -0
- package/src/run-examples.ts +148 -0
- package/src/run-quiet.ts +75 -0
- package/src/run-trace.ts +273 -0
- package/src/run-warden.ts +39 -0
- package/src/run-watch.ts +432 -0
- package/src/scaffold-versions.generated.ts +12 -0
- package/src/trails/add-surface.ts +45 -23
- package/src/trails/add-trail.ts +27 -17
- package/src/trails/add-verify.ts +57 -17
- package/src/trails/completions-complete.ts +165 -0
- package/src/trails/completions.ts +47 -0
- package/src/trails/create-scaffold.ts +86 -33
- package/src/trails/create.ts +11 -3
- package/src/trails/dev-clean.ts +6 -1
- package/src/trails/dev-reset.ts +6 -1
- package/src/trails/dev-stats.ts +6 -1
- package/src/trails/dev-support.ts +29 -17
- package/src/trails/draft-promote.ts +289 -80
- package/src/trails/guide.ts +54 -34
- package/src/trails/load-app.ts +251 -56
- package/src/trails/root-dir.ts +21 -0
- package/src/trails/run-example.ts +482 -0
- package/src/trails/run-examples.ts +141 -0
- package/src/trails/run.ts +403 -0
- package/src/trails/survey.ts +534 -200
- package/src/trails/topo-activation.ts +385 -0
- package/src/trails/topo-compile.ts +55 -0
- package/src/trails/topo-history.ts +6 -1
- package/src/trails/topo-output-schemas.ts +219 -0
- package/src/trails/topo-pin.ts +19 -6
- package/src/trails/topo-read-support.ts +171 -228
- package/src/trails/topo-reports.ts +587 -25
- package/src/trails/topo-store-support.ts +43 -19
- package/src/trails/topo-support.ts +18 -28
- package/src/trails/topo-unpin.ts +6 -1
- package/src/trails/topo-verify.ts +18 -5
- package/src/trails/topo.ts +60 -23
- package/src/trails/warden-guide.ts +121 -0
- package/src/trails/warden.ts +137 -56
- package/src/versions.ts +3 -18
- package/.turbo/turbo-build.log +0 -1
- package/.turbo/turbo-lint.log +0 -3
- package/.turbo/turbo-typecheck.log +0 -1
- package/__tests__/examples.test.ts +0 -45
- package/dist/bin/trails.d.ts +0 -3
- package/dist/bin/trails.d.ts.map +0 -1
- package/dist/bin/trails.js +0 -4
- package/dist/bin/trails.js.map +0 -1
- package/dist/src/app.d.ts +0 -2
- package/dist/src/app.d.ts.map +0 -1
- package/dist/src/app.js +0 -22
- package/dist/src/app.js.map +0 -1
- package/dist/src/clack.d.ts +0 -9
- package/dist/src/clack.d.ts.map +0 -1
- package/dist/src/clack.js +0 -84
- package/dist/src/clack.js.map +0 -1
- package/dist/src/cli.d.ts +0 -2
- package/dist/src/cli.d.ts.map +0 -1
- package/dist/src/cli.js +0 -14
- package/dist/src/cli.js.map +0 -1
- package/dist/src/trails/add-surface.d.ts +0 -13
- package/dist/src/trails/add-surface.d.ts.map +0 -1
- package/dist/src/trails/add-surface.js +0 -110
- package/dist/src/trails/add-surface.js.map +0 -1
- package/dist/src/trails/add-trail.d.ts +0 -12
- package/dist/src/trails/add-trail.d.ts.map +0 -1
- package/dist/src/trails/add-trail.js +0 -104
- package/dist/src/trails/add-trail.js.map +0 -1
- package/dist/src/trails/add-trailhead.d.ts +0 -13
- package/dist/src/trails/add-trailhead.d.ts.map +0 -1
- package/dist/src/trails/add-trailhead.js +0 -88
- package/dist/src/trails/add-trailhead.js.map +0 -1
- package/dist/src/trails/add-verify.d.ts +0 -10
- package/dist/src/trails/add-verify.d.ts.map +0 -1
- package/dist/src/trails/add-verify.js +0 -68
- package/dist/src/trails/add-verify.js.map +0 -1
- package/dist/src/trails/create-scaffold.d.ts +0 -15
- package/dist/src/trails/create-scaffold.d.ts.map +0 -1
- package/dist/src/trails/create-scaffold.js +0 -295
- package/dist/src/trails/create-scaffold.js.map +0 -1
- package/dist/src/trails/create.d.ts +0 -18
- package/dist/src/trails/create.d.ts.map +0 -1
- package/dist/src/trails/create.js +0 -126
- package/dist/src/trails/create.js.map +0 -1
- package/dist/src/trails/dev-clean.d.ts +0 -9
- package/dist/src/trails/dev-clean.d.ts.map +0 -1
- package/dist/src/trails/dev-clean.js +0 -66
- package/dist/src/trails/dev-clean.js.map +0 -1
- package/dist/src/trails/dev-reset.d.ts +0 -6
- package/dist/src/trails/dev-reset.d.ts.map +0 -1
- package/dist/src/trails/dev-reset.js +0 -39
- package/dist/src/trails/dev-reset.js.map +0 -1
- package/dist/src/trails/dev-stats.d.ts +0 -7
- package/dist/src/trails/dev-stats.d.ts.map +0 -1
- package/dist/src/trails/dev-stats.js +0 -61
- package/dist/src/trails/dev-stats.js.map +0 -1
- package/dist/src/trails/dev-support.d.ts +0 -64
- package/dist/src/trails/dev-support.d.ts.map +0 -1
- package/dist/src/trails/dev-support.js +0 -181
- package/dist/src/trails/dev-support.js.map +0 -1
- package/dist/src/trails/draft-promote.d.ts +0 -18
- package/dist/src/trails/draft-promote.d.ts.map +0 -1
- package/dist/src/trails/draft-promote.js +0 -400
- package/dist/src/trails/draft-promote.js.map +0 -1
- package/dist/src/trails/guide.d.ts +0 -21
- package/dist/src/trails/guide.d.ts.map +0 -1
- package/dist/src/trails/guide.js +0 -61
- package/dist/src/trails/guide.js.map +0 -1
- package/dist/src/trails/load-app.d.ts +0 -12
- package/dist/src/trails/load-app.d.ts.map +0 -1
- package/dist/src/trails/load-app.js +0 -415
- package/dist/src/trails/load-app.js.map +0 -1
- package/dist/src/trails/project.d.ts +0 -8
- package/dist/src/trails/project.d.ts.map +0 -1
- package/dist/src/trails/project.js +0 -54
- package/dist/src/trails/project.js.map +0 -1
- package/dist/src/trails/survey.d.ts +0 -18
- package/dist/src/trails/survey.d.ts.map +0 -1
- package/dist/src/trails/survey.js +0 -234
- package/dist/src/trails/survey.js.map +0 -1
- package/dist/src/trails/topo-constants.d.ts +0 -3
- package/dist/src/trails/topo-constants.d.ts.map +0 -1
- package/dist/src/trails/topo-constants.js +0 -3
- package/dist/src/trails/topo-constants.js.map +0 -1
- package/dist/src/trails/topo-export.d.ts +0 -19
- package/dist/src/trails/topo-export.d.ts.map +0 -1
- package/dist/src/trails/topo-export.js +0 -31
- package/dist/src/trails/topo-export.js.map +0 -1
- package/dist/src/trails/topo-history.d.ts +0 -20
- package/dist/src/trails/topo-history.d.ts.map +0 -1
- package/dist/src/trails/topo-history.js +0 -32
- package/dist/src/trails/topo-history.js.map +0 -1
- package/dist/src/trails/topo-pin.d.ts +0 -17
- package/dist/src/trails/topo-pin.d.ts.map +0 -1
- package/dist/src/trails/topo-pin.js +0 -31
- package/dist/src/trails/topo-pin.js.map +0 -1
- package/dist/src/trails/topo-read-support.d.ts +0 -58
- package/dist/src/trails/topo-read-support.d.ts.map +0 -1
- package/dist/src/trails/topo-read-support.js +0 -167
- package/dist/src/trails/topo-read-support.js.map +0 -1
- package/dist/src/trails/topo-reports.d.ts +0 -54
- package/dist/src/trails/topo-reports.d.ts.map +0 -1
- package/dist/src/trails/topo-reports.js +0 -128
- package/dist/src/trails/topo-reports.js.map +0 -1
- package/dist/src/trails/topo-show.d.ts +0 -23
- package/dist/src/trails/topo-show.d.ts.map +0 -1
- package/dist/src/trails/topo-show.js +0 -49
- package/dist/src/trails/topo-show.js.map +0 -1
- package/dist/src/trails/topo-store-support.d.ts +0 -13
- package/dist/src/trails/topo-store-support.d.ts.map +0 -1
- package/dist/src/trails/topo-store-support.js +0 -55
- package/dist/src/trails/topo-store-support.js.map +0 -1
- package/dist/src/trails/topo-support.d.ts +0 -76
- package/dist/src/trails/topo-support.d.ts.map +0 -1
- package/dist/src/trails/topo-support.js +0 -132
- package/dist/src/trails/topo-support.js.map +0 -1
- package/dist/src/trails/topo-unpin.d.ts +0 -20
- package/dist/src/trails/topo-unpin.d.ts.map +0 -1
- package/dist/src/trails/topo-unpin.js +0 -44
- package/dist/src/trails/topo-unpin.js.map +0 -1
- package/dist/src/trails/topo-verify.d.ts +0 -5
- package/dist/src/trails/topo-verify.d.ts.map +0 -1
- package/dist/src/trails/topo-verify.js +0 -24
- package/dist/src/trails/topo-verify.js.map +0 -1
- package/dist/src/trails/topo.d.ts +0 -5
- package/dist/src/trails/topo.d.ts.map +0 -1
- package/dist/src/trails/topo.js +0 -63
- package/dist/src/trails/topo.js.map +0 -1
- package/dist/src/trails/warden.d.ts +0 -20
- package/dist/src/trails/warden.d.ts.map +0 -1
- package/dist/src/trails/warden.js +0 -98
- package/dist/src/trails/warden.js.map +0 -1
- package/dist/src/versions.d.ts +0 -12
- package/dist/src/versions.d.ts.map +0 -1
- package/dist/src/versions.js +0 -23
- package/dist/src/versions.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/__tests__/add-trail.test.ts +0 -97
- package/src/__tests__/create.test.ts +0 -415
- package/src/__tests__/draft-promote.test.ts +0 -144
- package/src/__tests__/guide.test.ts +0 -96
- package/src/__tests__/load-app.test.ts +0 -419
- package/src/__tests__/survey.test.ts +0 -377
- package/src/__tests__/topo-dev.test.ts +0 -426
- package/src/__tests__/warden.test.ts +0 -74
- package/src/trails/topo-export.ts +0 -35
- package/src/trails/topo-show.ts +0 -54
- package/tsconfig.json +0 -9
- package/tsconfig.tests.json +0 -10
|
@@ -1,26 +1,138 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
DETOUR_MAX_ATTEMPTS_CAP,
|
|
3
|
+
deriveCliPath,
|
|
4
|
+
filterSurfaceTrails,
|
|
5
|
+
zodToJsonSchema,
|
|
6
|
+
} from '@ontrails/core';
|
|
7
|
+
import type { AnyTrail, Signal, Topo } from '@ontrails/core';
|
|
8
|
+
import { deriveHttpMethod } from '@ontrails/http';
|
|
9
|
+
import type { HttpMethod } from '@ontrails/http';
|
|
10
|
+
import { deriveToolName } from '@ontrails/mcp';
|
|
11
|
+
import { deriveTopoGraph } from '@ontrails/topographer';
|
|
12
|
+
import type {
|
|
13
|
+
JsonSchema,
|
|
14
|
+
TopoGraph,
|
|
15
|
+
TopoGraphActivationEdge,
|
|
16
|
+
TopoGraphEntry,
|
|
17
|
+
TopoGraphFieldOverride,
|
|
18
|
+
TopoGraphLayerReference,
|
|
19
|
+
} from '@ontrails/topographer';
|
|
20
|
+
import { z } from 'zod';
|
|
2
21
|
|
|
22
|
+
import type {
|
|
23
|
+
ActivationChainReport,
|
|
24
|
+
ActivationEdgeReport,
|
|
25
|
+
ActivationGraphReport,
|
|
26
|
+
ActivationOverviewReport,
|
|
27
|
+
ActivationSourceReport,
|
|
28
|
+
SignalActivationRelations,
|
|
29
|
+
} from './topo-activation.js';
|
|
30
|
+
import {
|
|
31
|
+
deriveActivationGraph,
|
|
32
|
+
deriveDeclaredTrailActivation,
|
|
33
|
+
deriveSignalActivationRelations,
|
|
34
|
+
} from './topo-activation.js';
|
|
3
35
|
import { REPORT_CONTRACT_VERSION, REPORT_VERSION } from './topo-constants.js';
|
|
4
36
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
37
|
+
export type {
|
|
38
|
+
ActivationChainReport,
|
|
39
|
+
ActivationEdgeReport,
|
|
40
|
+
ActivationGraphReport,
|
|
41
|
+
ActivationOverviewReport,
|
|
42
|
+
ActivationSourceReport,
|
|
43
|
+
SignalActivationRelations,
|
|
44
|
+
TrailActivationReport,
|
|
45
|
+
} from './topo-activation.js';
|
|
46
|
+
|
|
47
|
+
export const briefReportSchema = z.object({
|
|
48
|
+
contractVersion: z.string(),
|
|
49
|
+
features: z.object({
|
|
50
|
+
detours: z.boolean(),
|
|
51
|
+
examples: z.boolean(),
|
|
52
|
+
outputSchemas: z.boolean(),
|
|
53
|
+
resources: z.boolean(),
|
|
54
|
+
signals: z.boolean(),
|
|
55
|
+
}),
|
|
56
|
+
name: z.string(),
|
|
57
|
+
resources: z.number(),
|
|
58
|
+
signals: z.number(),
|
|
59
|
+
trails: z.number(),
|
|
60
|
+
version: z.string(),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
type BriefReportShape = z.infer<typeof briefReportSchema>;
|
|
64
|
+
|
|
65
|
+
export type BriefReport = Readonly<
|
|
66
|
+
Omit<BriefReportShape, 'features'> & {
|
|
67
|
+
readonly features: Readonly<BriefReportShape['features']>;
|
|
68
|
+
}
|
|
69
|
+
>;
|
|
70
|
+
|
|
71
|
+
export type SurfaceLayerKey = 'cli' | 'http' | 'mcp';
|
|
72
|
+
|
|
73
|
+
export type SurfaceLayerNames = Readonly<
|
|
74
|
+
Record<SurfaceLayerKey, readonly string[]>
|
|
75
|
+
>;
|
|
76
|
+
|
|
77
|
+
export type ShippedSurfaceKey = 'cli' | 'mcp' | 'http';
|
|
78
|
+
|
|
79
|
+
export type SurfaceProjectionSource = 'authored' | 'default-derived';
|
|
80
|
+
|
|
81
|
+
export type ShippedSurfaceProjection =
|
|
82
|
+
| {
|
|
83
|
+
readonly commandPath: readonly string[];
|
|
84
|
+
readonly derivedName: string;
|
|
85
|
+
readonly method: null;
|
|
86
|
+
readonly source: SurfaceProjectionSource;
|
|
87
|
+
readonly surface: 'cli';
|
|
88
|
+
readonly trailId: string;
|
|
89
|
+
}
|
|
90
|
+
| {
|
|
91
|
+
readonly derivedName: string;
|
|
92
|
+
readonly method: null;
|
|
93
|
+
readonly source: SurfaceProjectionSource;
|
|
94
|
+
readonly surface: 'mcp';
|
|
95
|
+
readonly toolName: string;
|
|
96
|
+
readonly trailId: string;
|
|
97
|
+
}
|
|
98
|
+
| {
|
|
99
|
+
readonly derivedName: string;
|
|
100
|
+
readonly method: HttpMethod;
|
|
101
|
+
readonly path: string;
|
|
102
|
+
readonly source: SurfaceProjectionSource;
|
|
103
|
+
readonly surface: 'http';
|
|
104
|
+
readonly trailId: string;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export interface ShippedSurfaceInventoryReport {
|
|
108
|
+
readonly count: number;
|
|
109
|
+
readonly excludedSurfaces: readonly {
|
|
110
|
+
readonly reason: string;
|
|
111
|
+
readonly status: 'planned';
|
|
112
|
+
readonly surface: 'websocket';
|
|
113
|
+
}[];
|
|
114
|
+
readonly projections: readonly ShippedSurfaceProjection[];
|
|
115
|
+
readonly shippedSurfaces: readonly ShippedSurfaceKey[];
|
|
116
|
+
readonly trails: readonly {
|
|
117
|
+
readonly explicitSurfaces: readonly string[];
|
|
118
|
+
readonly projections: readonly ShippedSurfaceProjection[];
|
|
119
|
+
readonly trailId: string;
|
|
120
|
+
}[];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
type TopoGraphContourEntry = TopoGraphEntry & { readonly kind: 'contour' };
|
|
124
|
+
|
|
125
|
+
export interface TrailDetailOptions {
|
|
126
|
+
readonly surfaceLayerNames?: Partial<SurfaceLayerNames> | undefined;
|
|
127
|
+
readonly topoGraph?: TopoGraph | undefined;
|
|
19
128
|
}
|
|
20
129
|
|
|
21
130
|
export interface SurveyListReport {
|
|
131
|
+
readonly activation: ActivationOverviewReport;
|
|
22
132
|
readonly count: number;
|
|
23
133
|
readonly entries: readonly {
|
|
134
|
+
readonly activatedBy: readonly string[];
|
|
135
|
+
readonly activates: readonly string[];
|
|
24
136
|
readonly examples: number;
|
|
25
137
|
readonly id: string;
|
|
26
138
|
readonly kind: string;
|
|
@@ -35,21 +147,88 @@ export interface SurveyListReport {
|
|
|
35
147
|
readonly lifetime: 'singleton';
|
|
36
148
|
readonly usedBy: readonly string[];
|
|
37
149
|
}[];
|
|
150
|
+
readonly signalCount: number;
|
|
151
|
+
readonly signals: readonly {
|
|
152
|
+
readonly consumers: readonly string[];
|
|
153
|
+
readonly description: string | null;
|
|
154
|
+
readonly examples: number;
|
|
155
|
+
readonly from: readonly string[];
|
|
156
|
+
readonly id: string;
|
|
157
|
+
readonly kind: 'signal';
|
|
158
|
+
readonly payloadSchema: boolean;
|
|
159
|
+
readonly producers: readonly string[];
|
|
160
|
+
}[];
|
|
38
161
|
}
|
|
39
162
|
|
|
40
163
|
export interface TrailDetailReport {
|
|
164
|
+
readonly activatedBy: readonly string[];
|
|
165
|
+
readonly activates: readonly string[];
|
|
166
|
+
readonly activationChains: readonly ActivationChainReport[];
|
|
167
|
+
readonly activationContext: {
|
|
168
|
+
readonly edgeCount: number;
|
|
169
|
+
readonly sourceCount: number;
|
|
170
|
+
readonly sourceKeys: readonly string[];
|
|
171
|
+
readonly trailIds: readonly string[];
|
|
172
|
+
};
|
|
173
|
+
readonly activationEdges: readonly ActivationEdgeReport[];
|
|
174
|
+
readonly activationSources: readonly ActivationSourceReport[];
|
|
175
|
+
readonly cli: {
|
|
176
|
+
readonly path: readonly string[];
|
|
177
|
+
} | null;
|
|
178
|
+
/**
|
|
179
|
+
* Composed layer names visible at the survey boundary.
|
|
180
|
+
*
|
|
181
|
+
* Reports the names of typed layers that wrap this trail at execution time,
|
|
182
|
+
* in the framework's composition order: `topo → surface → trail`
|
|
183
|
+
* (outermost-first). Surface-scope layers are keyed by surface because
|
|
184
|
+
* each surface owns its own attachment set.
|
|
185
|
+
*/
|
|
186
|
+
readonly composedLayers: {
|
|
187
|
+
readonly topo: readonly string[];
|
|
188
|
+
readonly surface: SurfaceLayerNames;
|
|
189
|
+
readonly trail: readonly string[];
|
|
190
|
+
};
|
|
191
|
+
readonly contourDetails: readonly TopoGraphContourEntry[];
|
|
192
|
+
readonly contours: readonly string[];
|
|
41
193
|
readonly description: string | null;
|
|
42
194
|
readonly detours:
|
|
43
195
|
| readonly { readonly on: string; readonly maxAttempts: number }[]
|
|
44
196
|
| null;
|
|
45
197
|
readonly examples: readonly unknown[];
|
|
198
|
+
readonly fieldOverrides: readonly TopoGraphFieldOverride[];
|
|
46
199
|
readonly crosses: readonly string[];
|
|
200
|
+
readonly fires: readonly string[];
|
|
201
|
+
readonly governance: Readonly<Record<string, unknown>> | null;
|
|
47
202
|
readonly id: string;
|
|
203
|
+
readonly input: JsonSchema | null;
|
|
48
204
|
readonly intent: 'read' | 'write' | 'destroy';
|
|
49
|
-
readonly kind:
|
|
205
|
+
readonly kind: 'trail';
|
|
206
|
+
readonly layers: readonly TopoGraphLayerReference[];
|
|
207
|
+
readonly on: readonly string[];
|
|
208
|
+
readonly output: JsonSchema | null;
|
|
50
209
|
readonly pattern: string | null;
|
|
51
210
|
readonly safety: string;
|
|
52
211
|
readonly resources: readonly string[];
|
|
212
|
+
readonly surfaceProjections: readonly ShippedSurfaceProjection[];
|
|
213
|
+
readonly surfaces: readonly string[];
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export interface SignalDetailReport {
|
|
217
|
+
readonly consumers: readonly string[];
|
|
218
|
+
readonly description: string | null;
|
|
219
|
+
readonly examples: readonly unknown[];
|
|
220
|
+
readonly from: readonly string[];
|
|
221
|
+
readonly id: string;
|
|
222
|
+
readonly kind: 'signal';
|
|
223
|
+
/**
|
|
224
|
+
* The signal's payload schema (JSON Schema object), or `null` when the
|
|
225
|
+
* surface-map entry is missing for this signal. `null` is meaningful:
|
|
226
|
+
* it matches the list view's `payloadSchema: false` flag and lets
|
|
227
|
+
* consumers distinguish "schema not found" from "schema accepts any
|
|
228
|
+
* value" (the latter would be an empty object `{}`).
|
|
229
|
+
*/
|
|
230
|
+
readonly payload: Readonly<Record<string, unknown>> | null;
|
|
231
|
+
readonly producers: readonly string[];
|
|
53
232
|
}
|
|
54
233
|
|
|
55
234
|
const trailHas = (raw: Record<string, unknown>, key: string): boolean => {
|
|
@@ -170,9 +349,40 @@ const formatResourceList = (app: Topo): SurveyListReport['resources'] => {
|
|
|
170
349
|
.toSorted((a, b) => a.id.localeCompare(b.id));
|
|
171
350
|
};
|
|
172
351
|
|
|
352
|
+
const formatSignalList = (
|
|
353
|
+
app: Topo,
|
|
354
|
+
relations: ReadonlyMap<string, SignalActivationRelations>
|
|
355
|
+
): SurveyListReport['signals'] =>
|
|
356
|
+
app
|
|
357
|
+
.listSignals()
|
|
358
|
+
.map((signalDef) => {
|
|
359
|
+
const related = relations.get(signalDef.id);
|
|
360
|
+
const consumers = related?.consumers ?? [];
|
|
361
|
+
const producers = related?.producers ?? [];
|
|
362
|
+
return {
|
|
363
|
+
consumers,
|
|
364
|
+
description: signalDef.description ?? null,
|
|
365
|
+
examples: signalDef.examples?.length ?? 0,
|
|
366
|
+
from: signalDef.from?.toSorted() ?? [],
|
|
367
|
+
id: signalDef.id,
|
|
368
|
+
kind: signalDef.kind,
|
|
369
|
+
// Mirror the store path (`mapSignalRow` in `topo-store-read.ts`) which
|
|
370
|
+
// derives this from the surface-map entry. SignalSpec<T> requires
|
|
371
|
+
// `payload` so this is `true` in practice today; the explicit check
|
|
372
|
+
// keeps the in-memory and store reports self-consistent if a future
|
|
373
|
+
// SignalSpec variant ever omits `payload`.
|
|
374
|
+
payloadSchema: signalDef.payload !== undefined,
|
|
375
|
+
producers,
|
|
376
|
+
};
|
|
377
|
+
})
|
|
378
|
+
.toSorted((a, b) => a.id.localeCompare(b.id));
|
|
379
|
+
|
|
173
380
|
export const deriveSurveyList = (app: Topo): SurveyListReport => {
|
|
174
381
|
const items = app.list();
|
|
382
|
+
const activation = deriveActivationGraph(app);
|
|
175
383
|
const entries = items.map((item) => {
|
|
384
|
+
const trailActivation =
|
|
385
|
+
activation.trails.get(item.id) ?? deriveDeclaredTrailActivation(item);
|
|
176
386
|
const safety = safetyLabel(
|
|
177
387
|
item as unknown as { intent?: 'read' | 'write' | 'destroy' }
|
|
178
388
|
);
|
|
@@ -183,6 +393,8 @@ export const deriveSurveyList = (app: Topo): SurveyListReport => {
|
|
|
183
393
|
: 0;
|
|
184
394
|
|
|
185
395
|
return {
|
|
396
|
+
activatedBy: trailActivation.activatedBy,
|
|
397
|
+
activates: trailActivation.activates,
|
|
186
398
|
examples,
|
|
187
399
|
id: item.id,
|
|
188
400
|
kind: item.kind,
|
|
@@ -191,38 +403,388 @@ export const deriveSurveyList = (app: Topo): SurveyListReport => {
|
|
|
191
403
|
});
|
|
192
404
|
|
|
193
405
|
const resources = formatResourceList(app);
|
|
406
|
+
const signals = formatSignalList(app, activation.signals);
|
|
194
407
|
|
|
195
408
|
return {
|
|
409
|
+
activation: activation.overview,
|
|
196
410
|
count: items.length,
|
|
197
411
|
entries,
|
|
198
412
|
resourceCount: resources.length,
|
|
199
413
|
resources,
|
|
414
|
+
signalCount: signals.length,
|
|
415
|
+
signals,
|
|
416
|
+
};
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
export const deriveSignalDetail = (
|
|
420
|
+
app: Topo,
|
|
421
|
+
signalId: string,
|
|
422
|
+
activationGraph?: ActivationGraphReport | undefined
|
|
423
|
+
): SignalDetailReport | undefined => {
|
|
424
|
+
const signalDef = app.signals.get(signalId) as Signal<unknown> | undefined;
|
|
425
|
+
if (signalDef === undefined) {
|
|
426
|
+
return undefined;
|
|
427
|
+
}
|
|
428
|
+
const related =
|
|
429
|
+
activationGraph?.signals.get(signalId) ??
|
|
430
|
+
deriveSignalActivationRelations(app, signalId);
|
|
431
|
+
|
|
432
|
+
return {
|
|
433
|
+
consumers: [...related.consumers],
|
|
434
|
+
description: signalDef.description ?? null,
|
|
435
|
+
examples: [...(signalDef.examples ?? [])],
|
|
436
|
+
from: signalDef.from?.toSorted() ?? [],
|
|
437
|
+
id: signalDef.id,
|
|
438
|
+
kind: 'signal',
|
|
439
|
+
payload: zodToJsonSchema(signalDef.payload),
|
|
440
|
+
producers: [...related.producers],
|
|
441
|
+
};
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
const emptySurfaceLayerNames = (): SurfaceLayerNames => ({
|
|
445
|
+
cli: [],
|
|
446
|
+
http: [],
|
|
447
|
+
mcp: [],
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
const normalizeSurfaceLayerNames = (
|
|
451
|
+
names?: Partial<SurfaceLayerNames> | undefined
|
|
452
|
+
): SurfaceLayerNames => {
|
|
453
|
+
const base = emptySurfaceLayerNames();
|
|
454
|
+
if (names === undefined) {
|
|
455
|
+
return base;
|
|
456
|
+
}
|
|
457
|
+
return {
|
|
458
|
+
cli: names.cli ?? base.cli,
|
|
459
|
+
http: names.http ?? base.http,
|
|
460
|
+
mcp: names.mcp ?? base.mcp,
|
|
200
461
|
};
|
|
201
462
|
};
|
|
202
463
|
|
|
464
|
+
const emptyActivationContext = (): TrailDetailReport['activationContext'] => ({
|
|
465
|
+
edgeCount: 0,
|
|
466
|
+
sourceCount: 0,
|
|
467
|
+
sourceKeys: [],
|
|
468
|
+
trailIds: [],
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
const activationContextFromTopoGraph = (
|
|
472
|
+
topoGraph: TopoGraph | undefined,
|
|
473
|
+
trailId: string,
|
|
474
|
+
fallbackEdges: readonly TopoGraphActivationEdge[]
|
|
475
|
+
): TrailDetailReport['activationContext'] => {
|
|
476
|
+
const edges =
|
|
477
|
+
topoGraph?.activationGraph.edges.filter(
|
|
478
|
+
(edge) => edge.trailId === trailId
|
|
479
|
+
) ?? fallbackEdges;
|
|
480
|
+
if (edges.length === 0) {
|
|
481
|
+
return emptyActivationContext();
|
|
482
|
+
}
|
|
483
|
+
return {
|
|
484
|
+
edgeCount: edges.length,
|
|
485
|
+
sourceCount: new Set(edges.map((edge) => edge.sourceKey)).size,
|
|
486
|
+
sourceKeys: [...new Set(edges.map((edge) => edge.sourceKey))].toSorted(),
|
|
487
|
+
trailIds: [...new Set(edges.map((edge) => edge.trailId))].toSorted(),
|
|
488
|
+
};
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
const findTopoEntry = (
|
|
492
|
+
topoGraph: TopoGraph | undefined,
|
|
493
|
+
id: string,
|
|
494
|
+
kind: TopoGraphEntry['kind']
|
|
495
|
+
): TopoGraphEntry | undefined =>
|
|
496
|
+
topoGraph?.entries.find((entry) => entry.id === id && entry.kind === kind);
|
|
497
|
+
|
|
498
|
+
const trailActivationEdgesFromTopoGraph = (
|
|
499
|
+
topoGraph: TopoGraph | undefined,
|
|
500
|
+
trailId: string,
|
|
501
|
+
fallback: readonly TopoGraphActivationEdge[]
|
|
502
|
+
): readonly TopoGraphActivationEdge[] =>
|
|
503
|
+
topoGraph?.activationGraph.edges.filter((edge) => edge.trailId === trailId) ??
|
|
504
|
+
fallback;
|
|
505
|
+
|
|
506
|
+
const SHIPPED_SURFACES = ['cli', 'mcp', 'http'] as const;
|
|
507
|
+
|
|
508
|
+
const PLANNED_SURFACE_EXCLUSIONS = [
|
|
509
|
+
{
|
|
510
|
+
reason: 'WebSocket is planned, but no public package or API ships yet.',
|
|
511
|
+
status: 'planned' as const,
|
|
512
|
+
surface: 'websocket' as const,
|
|
513
|
+
},
|
|
514
|
+
] as const;
|
|
515
|
+
|
|
516
|
+
const surfaceOrder = (surface: ShippedSurfaceKey): number =>
|
|
517
|
+
SHIPPED_SURFACES.indexOf(surface);
|
|
518
|
+
|
|
519
|
+
const sortSurfaceProjections = (
|
|
520
|
+
projections: readonly ShippedSurfaceProjection[]
|
|
521
|
+
): readonly ShippedSurfaceProjection[] =>
|
|
522
|
+
projections.toSorted(
|
|
523
|
+
(a, b) =>
|
|
524
|
+
a.trailId.localeCompare(b.trailId) ||
|
|
525
|
+
surfaceOrder(a.surface) - surfaceOrder(b.surface)
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
const explicitSurfacesForEntry = (
|
|
529
|
+
entry: TopoGraphEntry | undefined
|
|
530
|
+
): readonly string[] => entry?.surfaces ?? [];
|
|
531
|
+
|
|
532
|
+
const projectionSource = (
|
|
533
|
+
entry: TopoGraphEntry | undefined,
|
|
534
|
+
surface: ShippedSurfaceKey
|
|
535
|
+
): SurfaceProjectionSource =>
|
|
536
|
+
explicitSurfacesForEntry(entry).includes(surface)
|
|
537
|
+
? 'authored'
|
|
538
|
+
: 'default-derived';
|
|
539
|
+
|
|
540
|
+
const deriveHttpPath = (trailId: string): string =>
|
|
541
|
+
`/${trailId.replaceAll('.', '/')}`;
|
|
542
|
+
|
|
543
|
+
const isSurfaceEligibleTrail = (app: Topo, trail: AnyTrail): boolean =>
|
|
544
|
+
filterSurfaceTrails([trail]).length > 0 &&
|
|
545
|
+
app.trails.get(trail.id) !== undefined;
|
|
546
|
+
|
|
547
|
+
export const deriveShippedSurfaceProjectionsForTrail = (
|
|
548
|
+
app: Topo,
|
|
549
|
+
trail: AnyTrail,
|
|
550
|
+
topoGraph?: TopoGraph | undefined
|
|
551
|
+
): readonly ShippedSurfaceProjection[] => {
|
|
552
|
+
if (!isSurfaceEligibleTrail(app, trail)) {
|
|
553
|
+
return [];
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const entry = findTopoEntry(
|
|
557
|
+
topoGraph ?? deriveTopoGraph(app),
|
|
558
|
+
trail.id,
|
|
559
|
+
'trail'
|
|
560
|
+
);
|
|
561
|
+
const commandPath = deriveCliPath(trail.id);
|
|
562
|
+
const httpMethod = deriveHttpMethod(trail.intent);
|
|
563
|
+
const httpPath = deriveHttpPath(trail.id);
|
|
564
|
+
const mcpToolName = deriveToolName(app.name, trail.id);
|
|
565
|
+
|
|
566
|
+
return sortSurfaceProjections([
|
|
567
|
+
{
|
|
568
|
+
commandPath,
|
|
569
|
+
derivedName: commandPath.join(' '),
|
|
570
|
+
method: null,
|
|
571
|
+
source: projectionSource(entry, 'cli'),
|
|
572
|
+
surface: 'cli',
|
|
573
|
+
trailId: trail.id,
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
derivedName: mcpToolName,
|
|
577
|
+
method: null,
|
|
578
|
+
source: projectionSource(entry, 'mcp'),
|
|
579
|
+
surface: 'mcp',
|
|
580
|
+
toolName: mcpToolName,
|
|
581
|
+
trailId: trail.id,
|
|
582
|
+
},
|
|
583
|
+
{
|
|
584
|
+
derivedName: httpPath,
|
|
585
|
+
method: httpMethod,
|
|
586
|
+
path: httpPath,
|
|
587
|
+
source: projectionSource(entry, 'http'),
|
|
588
|
+
surface: 'http',
|
|
589
|
+
trailId: trail.id,
|
|
590
|
+
},
|
|
591
|
+
]);
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
const deriveFallbackSurfaceProjections = (
|
|
595
|
+
entry: TopoGraphEntry | undefined
|
|
596
|
+
): readonly ShippedSurfaceProjection[] => {
|
|
597
|
+
if (entry?.cli === undefined) {
|
|
598
|
+
return [];
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
return [
|
|
602
|
+
{
|
|
603
|
+
commandPath: entry.cli.path,
|
|
604
|
+
derivedName: entry.cli.path.join(' '),
|
|
605
|
+
method: null,
|
|
606
|
+
source: projectionSource(entry, 'cli'),
|
|
607
|
+
surface: 'cli',
|
|
608
|
+
trailId: entry.id,
|
|
609
|
+
},
|
|
610
|
+
];
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
export const deriveShippedSurfaceProjectionInventory = (
|
|
614
|
+
app: Topo
|
|
615
|
+
): ShippedSurfaceInventoryReport => {
|
|
616
|
+
const topoGraph = deriveTopoGraph(app);
|
|
617
|
+
const trails = filterSurfaceTrails(app.list()).map((trail) => {
|
|
618
|
+
const entry = findTopoEntry(topoGraph, trail.id, 'trail');
|
|
619
|
+
const projections = deriveShippedSurfaceProjectionsForTrail(
|
|
620
|
+
app,
|
|
621
|
+
trail,
|
|
622
|
+
topoGraph
|
|
623
|
+
);
|
|
624
|
+
|
|
625
|
+
return {
|
|
626
|
+
explicitSurfaces: explicitSurfacesForEntry(entry),
|
|
627
|
+
projections,
|
|
628
|
+
trailId: trail.id,
|
|
629
|
+
};
|
|
630
|
+
});
|
|
631
|
+
const projections = sortSurfaceProjections(
|
|
632
|
+
trails.flatMap((trail) => trail.projections)
|
|
633
|
+
);
|
|
634
|
+
|
|
635
|
+
return {
|
|
636
|
+
count: trails.length,
|
|
637
|
+
excludedSurfaces: PLANNED_SURFACE_EXCLUSIONS,
|
|
638
|
+
projections,
|
|
639
|
+
shippedSurfaces: SHIPPED_SURFACES,
|
|
640
|
+
trails: trails.toSorted((a, b) => a.trailId.localeCompare(b.trailId)),
|
|
641
|
+
};
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
const deriveResolvedSurfaceProjections = (
|
|
645
|
+
app: Topo | undefined,
|
|
646
|
+
trailId: string,
|
|
647
|
+
topoEntry: TopoGraphEntry | undefined,
|
|
648
|
+
topoGraph: TopoGraph | undefined
|
|
649
|
+
): readonly ShippedSurfaceProjection[] => {
|
|
650
|
+
if (app === undefined) {
|
|
651
|
+
return deriveFallbackSurfaceProjections(topoEntry);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const trail = app.trails.get(trailId);
|
|
655
|
+
return trail === undefined
|
|
656
|
+
? deriveFallbackSurfaceProjections(topoEntry)
|
|
657
|
+
: deriveShippedSurfaceProjectionsForTrail(app, trail, topoGraph);
|
|
658
|
+
};
|
|
659
|
+
|
|
660
|
+
const deriveResolvedTrailGraphDetail = (
|
|
661
|
+
app: Topo | undefined,
|
|
662
|
+
trailId: string,
|
|
663
|
+
fallbackActivationEdges: readonly TopoGraphActivationEdge[],
|
|
664
|
+
topoGraphOverride?: TopoGraph | undefined
|
|
665
|
+
): Pick<
|
|
666
|
+
TrailDetailReport,
|
|
667
|
+
| 'activationContext'
|
|
668
|
+
| 'activationEdges'
|
|
669
|
+
| 'cli'
|
|
670
|
+
| 'contourDetails'
|
|
671
|
+
| 'contours'
|
|
672
|
+
| 'fieldOverrides'
|
|
673
|
+
| 'governance'
|
|
674
|
+
| 'input'
|
|
675
|
+
| 'layers'
|
|
676
|
+
| 'output'
|
|
677
|
+
| 'surfaceProjections'
|
|
678
|
+
| 'surfaces'
|
|
679
|
+
> => {
|
|
680
|
+
const topoGraph =
|
|
681
|
+
topoGraphOverride ?? (app === undefined ? undefined : deriveTopoGraph(app));
|
|
682
|
+
const topoEntry = findTopoEntry(topoGraph, trailId, 'trail');
|
|
683
|
+
const contours = topoEntry?.contours ?? [];
|
|
684
|
+
const contourDetails = contours
|
|
685
|
+
.map((contourId) => findTopoEntry(topoGraph, contourId, 'contour'))
|
|
686
|
+
.filter(
|
|
687
|
+
(entry): entry is TopoGraphContourEntry =>
|
|
688
|
+
entry !== undefined && entry.kind === 'contour'
|
|
689
|
+
);
|
|
690
|
+
|
|
691
|
+
return {
|
|
692
|
+
activationContext: activationContextFromTopoGraph(
|
|
693
|
+
topoGraph,
|
|
694
|
+
trailId,
|
|
695
|
+
fallbackActivationEdges
|
|
696
|
+
),
|
|
697
|
+
activationEdges: trailActivationEdgesFromTopoGraph(
|
|
698
|
+
topoGraph,
|
|
699
|
+
trailId,
|
|
700
|
+
fallbackActivationEdges
|
|
701
|
+
),
|
|
702
|
+
cli: topoEntry?.cli ?? null,
|
|
703
|
+
contourDetails,
|
|
704
|
+
contours,
|
|
705
|
+
fieldOverrides: topoEntry?.fieldOverrides ?? [],
|
|
706
|
+
governance: topoEntry?.governance ?? null,
|
|
707
|
+
input: topoEntry?.input ?? null,
|
|
708
|
+
layers: topoEntry?.layers ?? [],
|
|
709
|
+
output: topoEntry?.output ?? null,
|
|
710
|
+
surfaceProjections: deriveResolvedSurfaceProjections(
|
|
711
|
+
app,
|
|
712
|
+
trailId,
|
|
713
|
+
topoEntry,
|
|
714
|
+
topoGraph
|
|
715
|
+
),
|
|
716
|
+
surfaces: topoEntry?.surfaces ?? [],
|
|
717
|
+
};
|
|
718
|
+
};
|
|
719
|
+
|
|
720
|
+
const formatTrailDetours = (item: AnyTrail): TrailDetailReport['detours'] =>
|
|
721
|
+
item.detours.length > 0
|
|
722
|
+
? item.detours.map((d) => ({
|
|
723
|
+
maxAttempts: Math.max(
|
|
724
|
+
1,
|
|
725
|
+
Math.min(d.maxAttempts ?? 1, DETOUR_MAX_ATTEMPTS_CAP)
|
|
726
|
+
),
|
|
727
|
+
on: d.on.name,
|
|
728
|
+
}))
|
|
729
|
+
: null;
|
|
730
|
+
|
|
203
731
|
export const deriveTrailDetail = (
|
|
204
|
-
item:
|
|
732
|
+
item: AnyTrail,
|
|
733
|
+
app?: Topo | undefined,
|
|
734
|
+
activationGraph?: ActivationGraphReport | undefined,
|
|
735
|
+
options: TrailDetailOptions = {}
|
|
205
736
|
): TrailDetailReport => {
|
|
737
|
+
const activation =
|
|
738
|
+
app === undefined
|
|
739
|
+
? deriveDeclaredTrailActivation(item)
|
|
740
|
+
: ((activationGraph ?? deriveActivationGraph(app)).trails.get(item.id) ??
|
|
741
|
+
deriveDeclaredTrailActivation(item));
|
|
206
742
|
const safety = safetyLabel(
|
|
207
743
|
item as unknown as { intent?: 'read' | 'write' | 'destroy' }
|
|
208
744
|
);
|
|
209
745
|
|
|
746
|
+
const trailLayerNames = item.layers.map((layer) => layer.name);
|
|
747
|
+
const topoLayerNames = (app?.layers ?? []).map((layer) => layer.name);
|
|
748
|
+
const graphDetail = deriveResolvedTrailGraphDetail(
|
|
749
|
+
app,
|
|
750
|
+
item.id,
|
|
751
|
+
activation.edges,
|
|
752
|
+
options.topoGraph
|
|
753
|
+
);
|
|
754
|
+
|
|
210
755
|
return {
|
|
756
|
+
activatedBy: activation.activatedBy,
|
|
757
|
+
activates: activation.activates,
|
|
758
|
+
activationChains: activation.chains,
|
|
759
|
+
activationContext: graphDetail.activationContext,
|
|
760
|
+
activationEdges: graphDetail.activationEdges,
|
|
761
|
+
activationSources: activation.sources,
|
|
762
|
+
cli: graphDetail.cli,
|
|
763
|
+
composedLayers: {
|
|
764
|
+
surface: normalizeSurfaceLayerNames(options.surfaceLayerNames),
|
|
765
|
+
topo: topoLayerNames,
|
|
766
|
+
trail: trailLayerNames,
|
|
767
|
+
},
|
|
768
|
+
contourDetails: graphDetail.contourDetails,
|
|
769
|
+
contours: graphDetail.contours,
|
|
211
770
|
crosses: item.crosses.toSorted(),
|
|
212
771
|
description: item.description ?? null,
|
|
213
|
-
detours:
|
|
214
|
-
item.detours.length > 0
|
|
215
|
-
? item.detours.map((d) => ({
|
|
216
|
-
maxAttempts: d.maxAttempts ?? 1,
|
|
217
|
-
on: d.on.name,
|
|
218
|
-
}))
|
|
219
|
-
: null,
|
|
772
|
+
detours: formatTrailDetours(item),
|
|
220
773
|
examples: item.examples ?? [],
|
|
774
|
+
fieldOverrides: graphDetail.fieldOverrides,
|
|
775
|
+
fires: activation.fires,
|
|
776
|
+
governance: graphDetail.governance,
|
|
221
777
|
id: item.id,
|
|
778
|
+
input: graphDetail.input,
|
|
222
779
|
intent: item.intent,
|
|
223
|
-
kind:
|
|
780
|
+
kind: 'trail',
|
|
781
|
+
layers: graphDetail.layers,
|
|
782
|
+
on: activation.on,
|
|
783
|
+
output: graphDetail.output,
|
|
224
784
|
pattern: item.pattern ?? null,
|
|
225
785
|
resources: item.resources.map((resource) => resource.id).toSorted(),
|
|
226
786
|
safety,
|
|
787
|
+
surfaceProjections: graphDetail.surfaceProjections,
|
|
788
|
+
surfaces: graphDetail.surfaces,
|
|
227
789
|
};
|
|
228
790
|
};
|