@ontrails/trails 1.0.0-beta.2 → 1.0.0-beta.22
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 +647 -0
- package/README.md +26 -0
- package/package.json +28 -7
- package/src/app.ts +86 -2
- package/src/clack.ts +22 -0
- package/src/cli.ts +330 -11
- package/src/completions.ts +240 -0
- package/src/lifecycle-source-io.ts +33 -0
- package/src/load-app-mirror.ts +202 -0
- package/src/local-state-io.ts +153 -0
- package/src/mcp-app.ts +30 -0
- package/src/mcp-options.ts +77 -0
- package/src/mcp.ts +8 -0
- package/src/project-writes.ts +377 -0
- package/src/release/bindings.ts +39 -0
- package/src/release/check.ts +818 -0
- package/src/release/config.ts +63 -0
- package/src/release/contract-facts.ts +425 -0
- package/src/release/index.ts +85 -0
- package/src/release/native-bun-publish.ts +651 -0
- package/src/release/native-bun-registry.ts +350 -0
- package/src/release/packed-artifacts-smoke.ts +236 -0
- package/src/release/smoke.ts +46 -0
- package/src/release/wayfinder-dogfood-smoke.ts +226 -0
- package/src/retired-topo-command.ts +36 -0
- package/src/run-adapter-check.ts +76 -0
- package/src/run-collision.ts +126 -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-release-check.ts +74 -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-version-sync.ts +183 -0
- package/src/scaffold-versions.generated.ts +12 -0
- package/src/trails/adapter-check.ts +244 -0
- package/src/trails/add-surface.ts +94 -40
- package/src/trails/add-trail.ts +79 -41
- package/src/trails/add-verify.ts +95 -25
- package/src/trails/compile.ts +67 -0
- package/src/trails/completions-complete.ts +165 -0
- package/src/trails/completions.ts +47 -0
- package/src/trails/create-adapter.ts +1084 -0
- package/src/trails/create-scaffold.ts +399 -104
- package/src/trails/create-versions.ts +62 -0
- package/src/trails/create.ts +185 -71
- package/src/trails/deprecate.ts +59 -0
- package/src/trails/dev-clean.ts +82 -0
- package/src/trails/dev-reset.ts +50 -0
- package/src/trails/dev-stats.ts +72 -0
- package/src/trails/dev-support.ts +340 -0
- package/src/trails/doctor.ts +56 -0
- package/src/trails/draft-promote.ts +949 -0
- package/src/trails/guide.ts +74 -68
- package/src/trails/load-app.ts +1143 -15
- package/src/trails/project.ts +17 -3
- package/src/trails/release-check.ts +104 -0
- package/src/trails/release-smoke.ts +48 -0
- package/src/trails/revise.ts +53 -0
- package/src/trails/root-dir.ts +21 -0
- package/src/trails/run-example.ts +491 -0
- package/src/trails/run-examples.ts +145 -0
- package/src/trails/run.ts +410 -0
- package/src/trails/scaffold-json.ts +58 -0
- package/src/trails/survey.ts +881 -226
- package/src/trails/topo-activation.ts +385 -0
- package/src/trails/topo-constants.ts +2 -0
- package/src/trails/topo-history.ts +47 -0
- package/src/trails/topo-output-schemas.ts +248 -0
- package/src/trails/topo-pin.ts +52 -0
- package/src/trails/topo-read-support.ts +313 -0
- package/src/trails/topo-reports.ts +807 -0
- package/src/trails/topo-store-support.ts +174 -0
- package/src/trails/topo-support.ts +220 -0
- package/src/trails/topo-unpin.ts +61 -0
- package/src/trails/topo.ts +106 -0
- package/src/trails/validate.ts +38 -0
- package/src/trails/version-lifecycle-support.ts +945 -0
- package/src/trails/warden-guide.ts +129 -0
- package/src/trails/warden.ts +165 -58
- package/src/versions.ts +31 -0
- 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 -6
- 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 -11
- 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 -62
- 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 -13
- 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 -88
- package/dist/src/trails/add-surface.js.map +0 -1
- package/dist/src/trails/add-trail.d.ts +0 -11
- package/dist/src/trails/add-trail.d.ts.map +0 -1
- package/dist/src/trails/add-trail.js +0 -85
- package/dist/src/trails/add-trail.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 -67
- 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 -288
- package/dist/src/trails/create-scaffold.js.map +0 -1
- package/dist/src/trails/create.d.ts +0 -22
- package/dist/src/trails/create.d.ts.map +0 -1
- package/dist/src/trails/create.js +0 -121
- package/dist/src/trails/create.js.map +0 -1
- package/dist/src/trails/guide.d.ts +0 -11
- package/dist/src/trails/guide.d.ts.map +0 -1
- package/dist/src/trails/guide.js +0 -80
- package/dist/src/trails/guide.js.map +0 -1
- package/dist/src/trails/load-app.d.ts +0 -4
- package/dist/src/trails/load-app.d.ts.map +0 -1
- package/dist/src/trails/load-app.js +0 -24
- 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 -43
- package/dist/src/trails/project.js.map +0 -1
- package/dist/src/trails/survey.d.ts +0 -33
- package/dist/src/trails/survey.d.ts.map +0 -1
- package/dist/src/trails/survey.js +0 -225
- package/dist/src/trails/survey.js.map +0 -1
- package/dist/src/trails/warden.d.ts +0 -19
- package/dist/src/trails/warden.d.ts.map +0 -1
- package/dist/src/trails/warden.js +0 -88
- package/dist/src/trails/warden.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/__tests__/create.test.ts +0 -349
- package/src/__tests__/guide.test.ts +0 -91
- package/src/__tests__/load-app.test.ts +0 -15
- package/src/__tests__/survey.test.ts +0 -161
- package/src/__tests__/warden.test.ts +0 -74
- package/tsconfig.json +0 -9
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Trails CLI
|
|
2
|
+
|
|
3
|
+
Command-line tools for working with Trails projects.
|
|
4
|
+
|
|
5
|
+
Use the CLI to scaffold a Trails app, add surfaces, inspect the current topo, run warden checks, manage draft state, and keep local Trails project state tidy.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bunx @ontrails/trails create
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Common workflows:
|
|
12
|
+
|
|
13
|
+
- `trails create` starts a new Trails project with generated trail, topo, surface, and verification files.
|
|
14
|
+
- `trails add surface` adds another surface entrypoint to an existing project.
|
|
15
|
+
- `trails topo` inspects topo state and manages pins/history.
|
|
16
|
+
- `trails compile` writes committed topo artifacts.
|
|
17
|
+
- `trails validate` checks committed topo artifacts for drift.
|
|
18
|
+
- `trails wayfind overview` and adjacent `trails wayfind ...` commands read
|
|
19
|
+
saved topo artifacts through Wayfinder for local graph navigation.
|
|
20
|
+
- `trails warden` runs Trails governance checks for contract and architecture drift.
|
|
21
|
+
- `trails guide` shows available trails and examples from a project.
|
|
22
|
+
|
|
23
|
+
Trails is contract-first: define trails once with typed input, Result output, examples, and meta; the framework derives CLI, MCP, HTTP, and future surfaces from the same contracts.
|
|
24
|
+
|
|
25
|
+
See the main Trails documentation for the full framework guide:
|
|
26
|
+
<https://github.com/outfitter-dev/trails>
|
package/package.json
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ontrails/trails",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.22",
|
|
4
4
|
"bin": {
|
|
5
5
|
"trails": "./bin/trails.ts"
|
|
6
6
|
},
|
|
7
|
+
"files": [
|
|
8
|
+
"bin/**/*.ts",
|
|
9
|
+
"src/**/*.ts",
|
|
10
|
+
"!src/**/__tests__/**",
|
|
11
|
+
"!src/**/*.test.ts",
|
|
12
|
+
"!src/**/*.test-d.ts",
|
|
13
|
+
"README.md",
|
|
14
|
+
"CHANGELOG.md"
|
|
15
|
+
],
|
|
7
16
|
"type": "module",
|
|
17
|
+
"exports": {
|
|
18
|
+
"./release": "./src/release/index.ts",
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
8
21
|
"scripts": {
|
|
9
22
|
"build": "tsc -b",
|
|
10
23
|
"test": "bun test",
|
|
@@ -14,15 +27,23 @@
|
|
|
14
27
|
},
|
|
15
28
|
"dependencies": {
|
|
16
29
|
"@clack/prompts": "^1.1.0",
|
|
17
|
-
"@ontrails/
|
|
18
|
-
"@ontrails/
|
|
19
|
-
"@ontrails/
|
|
20
|
-
"@ontrails/
|
|
21
|
-
"@ontrails/
|
|
30
|
+
"@ontrails/adapter-kit": "^1.0.0-beta.22",
|
|
31
|
+
"@ontrails/cli": "^1.0.0-beta.22",
|
|
32
|
+
"@ontrails/commander": "^1.0.0-beta.22",
|
|
33
|
+
"@ontrails/core": "^1.0.0-beta.22",
|
|
34
|
+
"@ontrails/http": "^1.0.0-beta.22",
|
|
35
|
+
"@ontrails/mcp": "^1.0.0-beta.22",
|
|
36
|
+
"@ontrails/observe": "^1.0.0-beta.22",
|
|
37
|
+
"@ontrails/permits": "^1.0.0-beta.22",
|
|
38
|
+
"@ontrails/topographer": "^1.0.0-beta.22",
|
|
39
|
+
"@ontrails/tracing": "^1.0.0-beta.22",
|
|
40
|
+
"@ontrails/warden": "^1.0.0-beta.22",
|
|
41
|
+
"@ontrails/wayfinder": "^1.0.0-beta.22",
|
|
22
42
|
"commander": "^14.0.3",
|
|
43
|
+
"typescript": "^5.9.3",
|
|
23
44
|
"zod": "^4.3.5"
|
|
24
45
|
},
|
|
25
46
|
"devDependencies": {
|
|
26
|
-
"@ontrails/testing": "^1.0.0-beta.
|
|
47
|
+
"@ontrails/testing": "^1.0.0-beta.22"
|
|
27
48
|
}
|
|
28
49
|
}
|
package/src/app.ts
CHANGED
|
@@ -1,22 +1,106 @@
|
|
|
1
1
|
import { topo } from '@ontrails/core';
|
|
2
|
+
import {
|
|
3
|
+
wayfindAdaptersTrail,
|
|
4
|
+
wayfindContractTrail,
|
|
5
|
+
wayfindDescribeTrail,
|
|
6
|
+
wayfindErrorsTrail,
|
|
7
|
+
wayfindExamplesTrail,
|
|
8
|
+
wayfindImpactTrail,
|
|
9
|
+
wayfindNearbyTrail,
|
|
10
|
+
wayfindOverviewTrail,
|
|
11
|
+
wayfindSearchTrail,
|
|
12
|
+
wayfindTrailsTrail,
|
|
13
|
+
} from '@ontrails/wayfinder';
|
|
2
14
|
|
|
3
15
|
import * as addSurface from './trails/add-surface.js';
|
|
4
16
|
import * as addTrail from './trails/add-trail.js';
|
|
5
17
|
import * as addVerify from './trails/add-verify.js';
|
|
18
|
+
import * as adapterCheck from './trails/adapter-check.js';
|
|
19
|
+
import * as compile from './trails/compile.js';
|
|
20
|
+
import * as completions from './trails/completions.js';
|
|
21
|
+
import * as completionsComplete from './trails/completions-complete.js';
|
|
6
22
|
import * as create from './trails/create.js';
|
|
23
|
+
import * as createAdapter from './trails/create-adapter.js';
|
|
7
24
|
import * as createScaffold from './trails/create-scaffold.js';
|
|
25
|
+
import * as createVersions from './trails/create-versions.js';
|
|
26
|
+
import * as deprecate from './trails/deprecate.js';
|
|
27
|
+
import * as devClean from './trails/dev-clean.js';
|
|
28
|
+
import * as devReset from './trails/dev-reset.js';
|
|
29
|
+
import * as devStats from './trails/dev-stats.js';
|
|
30
|
+
import * as doctor from './trails/doctor.js';
|
|
31
|
+
import * as draftPromote from './trails/draft-promote.js';
|
|
8
32
|
import * as guide from './trails/guide.js';
|
|
33
|
+
import * as releaseCheck from './trails/release-check.js';
|
|
34
|
+
import * as releaseSmoke from './trails/release-smoke.js';
|
|
35
|
+
import * as revise from './trails/revise.js';
|
|
36
|
+
import * as run from './trails/run.js';
|
|
37
|
+
import * as runExample from './trails/run-example.js';
|
|
38
|
+
import * as runExamples from './trails/run-examples.js';
|
|
9
39
|
import * as survey from './trails/survey.js';
|
|
40
|
+
import * as topoHistory from './trails/topo-history.js';
|
|
41
|
+
import * as topoPin from './trails/topo-pin.js';
|
|
42
|
+
import * as topoCommand from './trails/topo.js';
|
|
43
|
+
import * as topoUnpin from './trails/topo-unpin.js';
|
|
44
|
+
import * as validate from './trails/validate.js';
|
|
10
45
|
import * as warden from './trails/warden.js';
|
|
46
|
+
import * as wardenGuide from './trails/warden-guide.js';
|
|
11
47
|
|
|
12
|
-
export const
|
|
48
|
+
export const operatorApp = topo(
|
|
13
49
|
'trails',
|
|
50
|
+
run,
|
|
51
|
+
runExamples,
|
|
52
|
+
runExample,
|
|
14
53
|
survey,
|
|
54
|
+
topoCommand,
|
|
55
|
+
compile,
|
|
56
|
+
topoHistory,
|
|
57
|
+
topoPin,
|
|
58
|
+
topoUnpin,
|
|
59
|
+
validate,
|
|
60
|
+
revise,
|
|
61
|
+
deprecate,
|
|
62
|
+
doctor,
|
|
63
|
+
devStats,
|
|
64
|
+
devClean,
|
|
65
|
+
devReset,
|
|
15
66
|
guide,
|
|
67
|
+
releaseCheck,
|
|
68
|
+
releaseSmoke,
|
|
69
|
+
draftPromote,
|
|
70
|
+
adapterCheck,
|
|
16
71
|
warden,
|
|
72
|
+
wardenGuide,
|
|
17
73
|
create,
|
|
74
|
+
createAdapter,
|
|
18
75
|
createScaffold,
|
|
76
|
+
createVersions,
|
|
19
77
|
addSurface,
|
|
20
78
|
addVerify,
|
|
21
|
-
addTrail
|
|
79
|
+
addTrail,
|
|
80
|
+
completions,
|
|
81
|
+
completionsComplete
|
|
22
82
|
);
|
|
83
|
+
|
|
84
|
+
const operatorTrails = Object.fromEntries(
|
|
85
|
+
operatorApp.list().map((trailItem) => [trailItem.id, trailItem])
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
const cliWayfinderTrails = {
|
|
89
|
+
wayfindAdaptersTrail,
|
|
90
|
+
wayfindContractTrail,
|
|
91
|
+
wayfindDescribeTrail,
|
|
92
|
+
wayfindErrorsTrail,
|
|
93
|
+
wayfindExamplesTrail,
|
|
94
|
+
wayfindImpactTrail,
|
|
95
|
+
wayfindNearbyTrail,
|
|
96
|
+
wayfindOverviewTrail,
|
|
97
|
+
wayfindSearchTrail,
|
|
98
|
+
wayfindTrailsTrail,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
export const trailsCliIncludedTrails = [
|
|
102
|
+
...operatorApp.list().map((trailItem) => trailItem.id),
|
|
103
|
+
...Object.values(cliWayfinderTrails).map((trailItem) => trailItem.id),
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
export const app = topo('trails', operatorTrails, cliWayfinderTrails);
|
package/src/clack.ts
CHANGED
|
@@ -55,8 +55,30 @@ const fieldResolvers: Record<Field['type'], FieldResolver> = {
|
|
|
55
55
|
const raw = await clack.text({ message: field.label });
|
|
56
56
|
return clack.isCancel(raw) ? undefined : Number(raw);
|
|
57
57
|
},
|
|
58
|
+
'number[]': async (field) => {
|
|
59
|
+
const raw = await clack.text({
|
|
60
|
+
message: `${field.label} (comma-separated numbers)`,
|
|
61
|
+
});
|
|
62
|
+
if (clack.isCancel(raw)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
return String(raw)
|
|
66
|
+
.split(',')
|
|
67
|
+
.map((s) => Number(s.trim()));
|
|
68
|
+
},
|
|
58
69
|
string: async (field) =>
|
|
59
70
|
cancelable(await clack.text({ message: field.label })),
|
|
71
|
+
'string[]': async (field) => {
|
|
72
|
+
const raw = await clack.text({
|
|
73
|
+
message: `${field.label} (comma-separated)`,
|
|
74
|
+
});
|
|
75
|
+
if (clack.isCancel(raw)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
return String(raw)
|
|
79
|
+
.split(',')
|
|
80
|
+
.map((s) => s.trim());
|
|
81
|
+
},
|
|
60
82
|
};
|
|
61
83
|
|
|
62
84
|
/** Resolve a single field value with Clack. */
|
package/src/cli.ts
CHANGED
|
@@ -1,14 +1,333 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
3
4
|
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
defaultOnResult,
|
|
7
|
+
devPermitPreset,
|
|
8
|
+
outputModePreset,
|
|
9
|
+
permitPreset,
|
|
10
|
+
tokenPreset,
|
|
11
|
+
tracePreset,
|
|
12
|
+
watchPreset,
|
|
13
|
+
} from '@ontrails/cli';
|
|
14
|
+
import type {
|
|
15
|
+
ActionResultContext,
|
|
16
|
+
ResolveCliPermitFromToken,
|
|
17
|
+
} from '@ontrails/cli';
|
|
18
|
+
import { createProgram } from '@ontrails/commander';
|
|
19
|
+
import { resolvePermitFromBearerToken } from '@ontrails/permits';
|
|
20
|
+
import { deriveTopoGraph } from '@ontrails/topographer';
|
|
21
|
+
|
|
22
|
+
import { app, trailsCliIncludedTrails } from './app.js';
|
|
5
23
|
import { resolveInputWithClack } from './clack.js';
|
|
24
|
+
import { getRetiredTopoCommandDiagnostic } from './retired-topo-command.js';
|
|
25
|
+
import { attachCompletionsInstallCommand } from './run-completions-install.js';
|
|
26
|
+
import {
|
|
27
|
+
applyAdapterCheckExitCode,
|
|
28
|
+
tryAdapterCheckOutput,
|
|
29
|
+
} from './run-adapter-check.js';
|
|
30
|
+
import {
|
|
31
|
+
applyReleaseCheckExitCode,
|
|
32
|
+
tryReleaseCheckOutput,
|
|
33
|
+
} from './run-release-check.js';
|
|
34
|
+
import { tryRecoverFromRunCollision } from './run-collision.js';
|
|
35
|
+
import { tryExampleRunOutput } from './run-example.js';
|
|
36
|
+
import { tryExamplesRunOutput } from './run-examples.js';
|
|
37
|
+
import { tryQuietRunOutput } from './run-quiet.js';
|
|
38
|
+
import {
|
|
39
|
+
argvHasTraceFlag,
|
|
40
|
+
installTraceSink,
|
|
41
|
+
tryTraceJsonOutput,
|
|
42
|
+
writeTraceTreeToStderr,
|
|
43
|
+
} from './run-trace.js';
|
|
44
|
+
import type { TraceSession } from './run-trace.js';
|
|
45
|
+
import {
|
|
46
|
+
argvHasWatchFlag,
|
|
47
|
+
hashTopoGraphEntry,
|
|
48
|
+
readRunTrailId,
|
|
49
|
+
runWatchLoop,
|
|
50
|
+
} from './run-watch.js';
|
|
51
|
+
import { tryWardenOutput } from './run-warden.js';
|
|
52
|
+
import { tryLoadFreshAppLease } from './trails/load-app.js';
|
|
53
|
+
import { resolveRunModulePath } from './trails/run.js';
|
|
54
|
+
import { resolveTrailRootDir } from './trails/root-dir.js';
|
|
55
|
+
import { trailsPackageVersion } from './versions.js';
|
|
56
|
+
|
|
57
|
+
const buildOnResult =
|
|
58
|
+
(session: TraceSession | undefined) =>
|
|
59
|
+
async (ctx: ActionResultContext): Promise<void> => {
|
|
60
|
+
const recovered = await tryRecoverFromRunCollision(ctx, { graph: app });
|
|
61
|
+
const resolvedCtx: ActionResultContext =
|
|
62
|
+
recovered === undefined
|
|
63
|
+
? ctx
|
|
64
|
+
: {
|
|
65
|
+
...ctx,
|
|
66
|
+
input: recovered.isOk()
|
|
67
|
+
? (ctx.trail.input.safeParse(ctx.input).data ?? ctx.input)
|
|
68
|
+
: ctx.input,
|
|
69
|
+
result: recovered,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// `--trace --json` (without `--quiet`) emits a single Result-shaped
|
|
73
|
+
// envelope on stdout that includes the captured records under
|
|
74
|
+
// `tracing`. Hand that case off before the regular chain so the
|
|
75
|
+
// existing handlers do not also write to stdout.
|
|
76
|
+
applyAdapterCheckExitCode(resolvedCtx);
|
|
77
|
+
applyReleaseCheckExitCode(resolvedCtx);
|
|
78
|
+
if (session !== undefined && tryTraceJsonOutput(resolvedCtx, session)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (tryExampleRunOutput(resolvedCtx)) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (tryExamplesRunOutput(resolvedCtx)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (await tryQuietRunOutput(resolvedCtx)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (tryWardenOutput(resolvedCtx)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (tryAdapterCheckOutput(resolvedCtx)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (tryReleaseCheckOutput(resolvedCtx)) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
await defaultOnResult(resolvedCtx);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const traceEnabled = argvHasTraceFlag(process.argv);
|
|
104
|
+
const maybeInstallTraceSession = (): TraceSession | undefined =>
|
|
105
|
+
traceEnabled ? installTraceSink() : undefined;
|
|
106
|
+
|
|
107
|
+
const resolveCliPermitFromToken: ResolveCliPermitFromToken = (input) =>
|
|
108
|
+
resolvePermitFromBearerToken({
|
|
109
|
+
bearerToken: input.token,
|
|
110
|
+
configValues: input.configValues,
|
|
111
|
+
env: process.env as Record<string, string | undefined>,
|
|
112
|
+
graph: input.graph,
|
|
113
|
+
missingAuthResourceMessage:
|
|
114
|
+
'--token requires an auth adapter. Register authResource from @ontrails/permits in your topo.',
|
|
115
|
+
nullPermitMessage: 'Auth adapter did not produce a permit for --token',
|
|
116
|
+
requestId: input.requestId,
|
|
117
|
+
resources: input.resources,
|
|
118
|
+
surface: 'cli',
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
interface WatchRunTarget {
|
|
122
|
+
readonly app?: string | undefined;
|
|
123
|
+
readonly id: string;
|
|
124
|
+
readonly module?: string | undefined;
|
|
125
|
+
readonly rootDir?: string | undefined;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const readFlagValue = (
|
|
129
|
+
args: readonly string[],
|
|
130
|
+
flagName: string
|
|
131
|
+
): string | undefined => {
|
|
132
|
+
const longFlag = `--${flagName}`;
|
|
133
|
+
const prefixedFlag = `${longFlag}=`;
|
|
134
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
135
|
+
const arg = args[i];
|
|
136
|
+
if (arg === longFlag) {
|
|
137
|
+
return args[i + 1];
|
|
138
|
+
}
|
|
139
|
+
if (arg?.startsWith(prefixedFlag)) {
|
|
140
|
+
return arg.slice(prefixedFlag.length);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return undefined;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const resolveWatchRunTarget = (
|
|
147
|
+
argv: readonly string[]
|
|
148
|
+
): WatchRunTarget | null => {
|
|
149
|
+
const args = argv.slice(2);
|
|
150
|
+
const id = readRunTrailId(args);
|
|
151
|
+
if (id === undefined) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
app: readFlagValue(args, 'app'),
|
|
156
|
+
id,
|
|
157
|
+
module: readFlagValue(args, 'module'),
|
|
158
|
+
rootDir: readFlagValue(args, 'root-dir'),
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Resolve the directory whose source-file events wake the `--watch` loop.
|
|
164
|
+
* Reruns still depend on the resolved TopoGraph entry hash; this path is
|
|
165
|
+
* only the cheap filesystem event source.
|
|
166
|
+
*/
|
|
167
|
+
const toWatchSourcePath = (rootDir: string, modulePath: string): string => {
|
|
168
|
+
if (modulePath.startsWith('file:')) {
|
|
169
|
+
return fileURLToPath(modulePath);
|
|
170
|
+
}
|
|
171
|
+
return isAbsolute(modulePath) ? modulePath : resolve(rootDir, modulePath);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const resolveWatchDirectorySourcePath = async (
|
|
175
|
+
target: WatchRunTarget | null
|
|
176
|
+
): Promise<string> => {
|
|
177
|
+
if (target !== null) {
|
|
178
|
+
const rootDirResult = resolveTrailRootDir(target.rootDir, process.cwd());
|
|
179
|
+
if (rootDirResult.isErr()) {
|
|
180
|
+
throw rootDirResult.error;
|
|
181
|
+
}
|
|
182
|
+
const moduleResult = await resolveRunModulePath(
|
|
183
|
+
rootDirResult.value,
|
|
184
|
+
target.module,
|
|
185
|
+
target.id,
|
|
186
|
+
target.app
|
|
187
|
+
);
|
|
188
|
+
if (moduleResult.isOk()) {
|
|
189
|
+
return toWatchSourcePath(rootDirResult.value, moduleResult.value);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const cwd = process.cwd();
|
|
193
|
+
const srcDir = join(cwd, 'src');
|
|
194
|
+
if (existsSync(srcDir)) {
|
|
195
|
+
return join(srcDir, 'app.ts');
|
|
196
|
+
}
|
|
197
|
+
return join(cwd, 'app.ts');
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const readWatchTopoGraphEntryHash = async (
|
|
201
|
+
target: WatchRunTarget | null
|
|
202
|
+
): Promise<string | null> => {
|
|
203
|
+
if (target === null) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
const rootDirResult = resolveTrailRootDir(target.rootDir, process.cwd());
|
|
207
|
+
if (rootDirResult.isErr()) {
|
|
208
|
+
throw rootDirResult.error;
|
|
209
|
+
}
|
|
210
|
+
const rootDir = rootDirResult.value;
|
|
211
|
+
const moduleResult = await resolveRunModulePath(
|
|
212
|
+
rootDir,
|
|
213
|
+
target.module,
|
|
214
|
+
target.id,
|
|
215
|
+
target.app
|
|
216
|
+
);
|
|
217
|
+
if (moduleResult.isErr()) {
|
|
218
|
+
throw moduleResult.error;
|
|
219
|
+
}
|
|
220
|
+
const leaseResult = await tryLoadFreshAppLease(moduleResult.value, rootDir);
|
|
221
|
+
if (leaseResult.isErr()) {
|
|
222
|
+
throw leaseResult.error;
|
|
223
|
+
}
|
|
224
|
+
const lease = leaseResult.value;
|
|
225
|
+
try {
|
|
226
|
+
const topoGraph = deriveTopoGraph(lease.app);
|
|
227
|
+
const entry = topoGraph.entries.find(
|
|
228
|
+
(candidate) => candidate.kind === 'trail' && candidate.id === target.id
|
|
229
|
+
);
|
|
230
|
+
return entry === undefined ? null : hashTopoGraphEntry(entry);
|
|
231
|
+
} finally {
|
|
232
|
+
lease.release();
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const wardenValueFlags = new Set([
|
|
237
|
+
'--apps',
|
|
238
|
+
'--config-path',
|
|
239
|
+
'--depth',
|
|
240
|
+
'--drafts',
|
|
241
|
+
'--fail-on',
|
|
242
|
+
'--format',
|
|
243
|
+
'--lock',
|
|
244
|
+
'--root-dir',
|
|
245
|
+
]);
|
|
246
|
+
|
|
247
|
+
const normalizeWardenArgv = (argv: readonly string[]): string[] => {
|
|
248
|
+
if (argv[2] !== 'warden') {
|
|
249
|
+
return [...argv];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const normalized = [...argv];
|
|
253
|
+
let previousFlagConsumesValue = false;
|
|
254
|
+
for (let index = 3; index < normalized.length; index += 1) {
|
|
255
|
+
const arg = normalized[index];
|
|
256
|
+
if (arg === undefined) {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (previousFlagConsumesValue) {
|
|
261
|
+
previousFlagConsumesValue = false;
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (arg === '-a') {
|
|
266
|
+
normalized[index] = '--apps';
|
|
267
|
+
previousFlagConsumesValue = true;
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
previousFlagConsumesValue = wardenValueFlags.has(arg);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return normalized;
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Invoke `surface()` once with an optional fresh trace session.
|
|
279
|
+
*
|
|
280
|
+
* When `--trace` is set, a fresh {@link TraceSession} is installed for the
|
|
281
|
+
* duration of the call and finalized in the `finally` block. Under
|
|
282
|
+
* `--watch`, this produces a fresh sink (and a fresh stderr tree) per
|
|
283
|
+
* rerun rather than letting records accumulate in a single
|
|
284
|
+
* process-lifetime sink.
|
|
285
|
+
*/
|
|
286
|
+
const runSurfaceOnce = async (): Promise<void> => {
|
|
287
|
+
const retiredTopoCommand = getRetiredTopoCommandDiagnostic(process.argv);
|
|
288
|
+
if (retiredTopoCommand !== null) {
|
|
289
|
+
process.stderr.write(`${retiredTopoCommand.message}\n`);
|
|
290
|
+
process.exitCode = 1;
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const session = maybeInstallTraceSession();
|
|
295
|
+
try {
|
|
296
|
+
const program = createProgram(app, {
|
|
297
|
+
description: 'Agent-native, contract-first TypeScript framework',
|
|
298
|
+
include: trailsCliIncludedTrails,
|
|
299
|
+
name: 'trails',
|
|
300
|
+
onResult: buildOnResult(session),
|
|
301
|
+
presets: [
|
|
302
|
+
outputModePreset(),
|
|
303
|
+
tracePreset(),
|
|
304
|
+
permitPreset(),
|
|
305
|
+
tokenPreset(),
|
|
306
|
+
devPermitPreset(),
|
|
307
|
+
watchPreset(),
|
|
308
|
+
],
|
|
309
|
+
resolveInput: resolveInputWithClack,
|
|
310
|
+
resolvePermitFromToken: resolveCliPermitFromToken,
|
|
311
|
+
version: trailsPackageVersion,
|
|
312
|
+
});
|
|
313
|
+
attachCompletionsInstallCommand(program);
|
|
314
|
+
await program.parseAsync(normalizeWardenArgv(process.argv));
|
|
315
|
+
} finally {
|
|
316
|
+
if (session !== undefined) {
|
|
317
|
+
const records = session.finalize();
|
|
318
|
+
writeTraceTreeToStderr(records);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const watchTarget = argvHasWatchFlag(process.argv)
|
|
324
|
+
? resolveWatchRunTarget(process.argv)
|
|
325
|
+
: null;
|
|
6
326
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
});
|
|
327
|
+
await (argvHasWatchFlag(process.argv)
|
|
328
|
+
? runWatchLoop({
|
|
329
|
+
readTopoGraphEntryHash: () => readWatchTopoGraphEntryHash(watchTarget),
|
|
330
|
+
run: runSurfaceOnce,
|
|
331
|
+
sourcePath: await resolveWatchDirectorySourcePath(watchTarget),
|
|
332
|
+
})
|
|
333
|
+
: runSurfaceOnce());
|