@delegance/claude-autopilot 5.5.2 → 7.2.0
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 +1776 -6
- package/README.md +65 -1
- package/bin/_launcher.js +38 -23
- package/dist/src/adapters/council/openai.js +12 -6
- package/dist/src/adapters/deploy/_http.d.ts +43 -0
- package/dist/src/adapters/deploy/_http.js +99 -0
- package/dist/src/adapters/deploy/fly.d.ts +206 -0
- package/dist/src/adapters/deploy/fly.js +696 -0
- package/dist/src/adapters/deploy/index.d.ts +2 -0
- package/dist/src/adapters/deploy/index.js +33 -0
- package/dist/src/adapters/deploy/render.d.ts +181 -0
- package/dist/src/adapters/deploy/render.js +550 -0
- package/dist/src/adapters/deploy/types.d.ts +67 -3
- package/dist/src/adapters/deploy/vercel.d.ts +17 -1
- package/dist/src/adapters/deploy/vercel.js +29 -49
- package/dist/src/adapters/pricing.d.ts +36 -0
- package/dist/src/adapters/pricing.js +40 -0
- package/dist/src/adapters/review-engine/codex.js +10 -7
- package/dist/src/cli/autopilot.d.ts +75 -0
- package/dist/src/cli/autopilot.js +750 -0
- package/dist/src/cli/brainstorm.d.ts +23 -0
- package/dist/src/cli/brainstorm.js +131 -0
- package/dist/src/cli/costs.d.ts +15 -1
- package/dist/src/cli/costs.js +99 -10
- package/dist/src/cli/dashboard/index.d.ts +5 -0
- package/dist/src/cli/dashboard/index.js +49 -0
- package/dist/src/cli/dashboard/login.d.ts +22 -0
- package/dist/src/cli/dashboard/login.js +260 -0
- package/dist/src/cli/dashboard/logout.d.ts +12 -0
- package/dist/src/cli/dashboard/logout.js +45 -0
- package/dist/src/cli/dashboard/status.d.ts +30 -0
- package/dist/src/cli/dashboard/status.js +65 -0
- package/dist/src/cli/dashboard/upload.d.ts +16 -0
- package/dist/src/cli/dashboard/upload.js +48 -0
- package/dist/src/cli/deploy.d.ts +3 -3
- package/dist/src/cli/deploy.js +34 -9
- package/dist/src/cli/engine-flag-deprecation.d.ts +14 -0
- package/dist/src/cli/engine-flag-deprecation.js +20 -0
- package/dist/src/cli/fix.d.ts +18 -0
- package/dist/src/cli/fix.js +105 -11
- package/dist/src/cli/help-text.d.ts +52 -0
- package/dist/src/cli/help-text.js +416 -0
- package/dist/src/cli/implement.d.ts +91 -0
- package/dist/src/cli/implement.js +196 -0
- package/dist/src/cli/index.d.ts +2 -1
- package/dist/src/cli/index.js +774 -245
- package/dist/src/cli/json-envelope.d.ts +187 -0
- package/dist/src/cli/json-envelope.js +270 -0
- package/dist/src/cli/json-mode.d.ts +33 -0
- package/dist/src/cli/json-mode.js +201 -0
- package/dist/src/cli/migrate.d.ts +111 -0
- package/dist/src/cli/migrate.js +305 -0
- package/dist/src/cli/plan.d.ts +81 -0
- package/dist/src/cli/plan.js +149 -0
- package/dist/src/cli/pr.d.ts +106 -0
- package/dist/src/cli/pr.js +191 -19
- package/dist/src/cli/preflight.js +26 -0
- package/dist/src/cli/review.d.ts +27 -0
- package/dist/src/cli/review.js +126 -0
- package/dist/src/cli/runs-watch-renderer.d.ts +45 -0
- package/dist/src/cli/runs-watch-renderer.js +275 -0
- package/dist/src/cli/runs-watch.d.ts +41 -0
- package/dist/src/cli/runs-watch.js +395 -0
- package/dist/src/cli/runs.d.ts +122 -0
- package/dist/src/cli/runs.js +902 -0
- package/dist/src/cli/scaffold.d.ts +39 -0
- package/dist/src/cli/scaffold.js +287 -0
- package/dist/src/cli/scan.d.ts +93 -0
- package/dist/src/cli/scan.js +166 -40
- package/dist/src/cli/setup.d.ts +30 -0
- package/dist/src/cli/setup.js +137 -0
- package/dist/src/cli/spec.d.ts +66 -0
- package/dist/src/cli/spec.js +132 -0
- package/dist/src/cli/validate.d.ts +29 -0
- package/dist/src/cli/validate.js +131 -0
- package/dist/src/core/config/schema.d.ts +9 -0
- package/dist/src/core/config/schema.js +7 -0
- package/dist/src/core/config/types.d.ts +11 -0
- package/dist/src/core/council/runner.d.ts +10 -1
- package/dist/src/core/council/runner.js +25 -3
- package/dist/src/core/council/types.d.ts +7 -0
- package/dist/src/core/errors.d.ts +1 -1
- package/dist/src/core/errors.js +11 -0
- package/dist/src/core/logging/redaction.d.ts +13 -0
- package/dist/src/core/logging/redaction.js +20 -0
- package/dist/src/core/migrate/schema-validator.js +15 -1
- package/dist/src/core/phases/static-rules.d.ts +5 -1
- package/dist/src/core/phases/static-rules.js +2 -5
- package/dist/src/core/run-state/budget.d.ts +88 -0
- package/dist/src/core/run-state/budget.js +141 -0
- package/dist/src/core/run-state/cli-internal.d.ts +21 -0
- package/dist/src/core/run-state/cli-internal.js +174 -0
- package/dist/src/core/run-state/events.d.ts +59 -0
- package/dist/src/core/run-state/events.js +512 -0
- package/dist/src/core/run-state/lock.d.ts +61 -0
- package/dist/src/core/run-state/lock.js +206 -0
- package/dist/src/core/run-state/phase-context.d.ts +60 -0
- package/dist/src/core/run-state/phase-context.js +108 -0
- package/dist/src/core/run-state/phase-registry.d.ts +137 -0
- package/dist/src/core/run-state/phase-registry.js +162 -0
- package/dist/src/core/run-state/phase-runner.d.ts +80 -0
- package/dist/src/core/run-state/phase-runner.js +447 -0
- package/dist/src/core/run-state/provider-readback.d.ts +130 -0
- package/dist/src/core/run-state/provider-readback.js +426 -0
- package/dist/src/core/run-state/replay-decision.d.ts +69 -0
- package/dist/src/core/run-state/replay-decision.js +144 -0
- package/dist/src/core/run-state/resolve-engine.d.ts +45 -0
- package/dist/src/core/run-state/resolve-engine.js +74 -0
- package/dist/src/core/run-state/resume-preflight.d.ts +66 -0
- package/dist/src/core/run-state/resume-preflight.js +116 -0
- package/dist/src/core/run-state/run-phase-with-lifecycle.d.ts +69 -0
- package/dist/src/core/run-state/run-phase-with-lifecycle.js +193 -0
- package/dist/src/core/run-state/runs.d.ts +57 -0
- package/dist/src/core/run-state/runs.js +288 -0
- package/dist/src/core/run-state/snapshot.d.ts +14 -0
- package/dist/src/core/run-state/snapshot.js +114 -0
- package/dist/src/core/run-state/state.d.ts +40 -0
- package/dist/src/core/run-state/state.js +164 -0
- package/dist/src/core/run-state/types.d.ts +284 -0
- package/dist/src/core/run-state/types.js +19 -0
- package/dist/src/core/run-state/ulid.d.ts +11 -0
- package/dist/src/core/run-state/ulid.js +95 -0
- package/dist/src/core/schema-alignment/extractor/index.d.ts +1 -1
- package/dist/src/core/schema-alignment/extractor/index.js +2 -2
- package/dist/src/core/schema-alignment/extractor/prisma.d.ts +13 -1
- package/dist/src/core/schema-alignment/extractor/prisma.js +65 -10
- package/dist/src/core/schema-alignment/git-history.d.ts +19 -0
- package/dist/src/core/schema-alignment/git-history.js +53 -0
- package/dist/src/core/static-rules/rules/brand-tokens.js +2 -2
- package/dist/src/core/static-rules/rules/schema-alignment.js +14 -4
- package/dist/src/dashboard/auto-upload.d.ts +26 -0
- package/dist/src/dashboard/auto-upload.js +107 -0
- package/dist/src/dashboard/config.d.ts +22 -0
- package/dist/src/dashboard/config.js +109 -0
- package/dist/src/dashboard/upload/canonical.d.ts +3 -0
- package/dist/src/dashboard/upload/canonical.js +16 -0
- package/dist/src/dashboard/upload/chain.d.ts +9 -0
- package/dist/src/dashboard/upload/chain.js +27 -0
- package/dist/src/dashboard/upload/snapshot.d.ts +23 -0
- package/dist/src/dashboard/upload/snapshot.js +66 -0
- package/dist/src/dashboard/upload/uploader.d.ts +54 -0
- package/dist/src/dashboard/upload/uploader.js +330 -0
- package/package.json +19 -3
- package/scripts/autoregress.ts +1 -1
- package/scripts/test-runner.mjs +4 -0
- package/skills/claude-autopilot.md +1 -1
- package/skills/make-interfaces-feel-better/SKILL.md +104 -0
- package/skills/simplify-ui/SKILL.md +103 -0
- package/skills/ui/SKILL.md +117 -0
- package/skills/ui-ux-pro-max/SKILL.md +90 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// src/core/run-state/resolve-engine.ts
|
|
2
|
+
//
|
|
3
|
+
// v7.0 — engine-off path retired. The function is preserved for source
|
|
4
|
+
// compatibility with callers that pass `cliEngine` / `envValue` /
|
|
5
|
+
// `configEnabled`, but it now returns `enabled: true` unconditionally.
|
|
6
|
+
//
|
|
7
|
+
// What changed in v7.0 vs v6.x:
|
|
8
|
+
// - `ENGINE_DEFAULT_V6_0` and `ENGINE_DEFAULT_V6_1` exports REMOVED.
|
|
9
|
+
// Direct importers must replace with literal `true` (see
|
|
10
|
+
// docs/v7/breaking-changes.md).
|
|
11
|
+
// - The deprecation warning helpers (`emitEngineOffDeprecationWarning`
|
|
12
|
+
// / `shouldWarnEngineOffDeprecation` / `ENGINE_OFF_DEPRECATION_MESSAGE`)
|
|
13
|
+
// are RETAINED as no-op stubs so call sites don't have to change in
|
|
14
|
+
// the same PR — they always return false / never fire.
|
|
15
|
+
// - `parseEngineEnvValue()` is RETAINED for back-compat with any
|
|
16
|
+
// out-of-tree callers; `resolveEngineEnabled()` ignores the env
|
|
17
|
+
// value entirely (the engine-off env path is gone).
|
|
18
|
+
//
|
|
19
|
+
// Why keep the stub function shape: the CLI dispatcher passes
|
|
20
|
+
// `cliEngine` / `envEngine` / config to `runPhaseWithLifecycle`, which
|
|
21
|
+
// in turn calls `resolveEngineEnabled()`. Those parameters become
|
|
22
|
+
// effective no-ops in v7.0 — the values are observed (so a future PR
|
|
23
|
+
// can re-enable the path or surface a deprecation event) but never
|
|
24
|
+
// override the always-on result.
|
|
25
|
+
/** Parse a stringly-typed env value into a tri-state boolean.
|
|
26
|
+
* Retained for back-compat with any out-of-tree callers; the v7
|
|
27
|
+
* resolver does not consult env values. */
|
|
28
|
+
export function parseEngineEnvValue(raw) {
|
|
29
|
+
if (raw === undefined)
|
|
30
|
+
return undefined;
|
|
31
|
+
const normalized = raw.trim().toLowerCase();
|
|
32
|
+
if (normalized === '')
|
|
33
|
+
return undefined;
|
|
34
|
+
switch (normalized) {
|
|
35
|
+
case 'on':
|
|
36
|
+
case 'true':
|
|
37
|
+
case '1':
|
|
38
|
+
case 'yes':
|
|
39
|
+
return true;
|
|
40
|
+
case 'off':
|
|
41
|
+
case 'false':
|
|
42
|
+
case '0':
|
|
43
|
+
case 'no':
|
|
44
|
+
return false;
|
|
45
|
+
default:
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** v7.0+ — engine is always on. Pure function; ignores all inputs.
|
|
50
|
+
* Source compatible with v6.x call sites. */
|
|
51
|
+
export function resolveEngineEnabled(_opts = {}) {
|
|
52
|
+
return {
|
|
53
|
+
enabled: true,
|
|
54
|
+
source: 'default',
|
|
55
|
+
reason: 'v7.0+ — engine always on (engine-off path removed)',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// v6.1 deprecation helpers — retained as no-op stubs for source compat.
|
|
60
|
+
// v7.0 removed the engine-off path entirely; no warning ever fires.
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
/** v6.1-era stable deprecation banner. v7.0+ never emits this string —
|
|
63
|
+
* the path is gone. Kept exported so out-of-tree consumers that imported
|
|
64
|
+
* it still type-check. */
|
|
65
|
+
export const ENGINE_OFF_DEPRECATION_MESSAGE = '[deprecation] --no-engine / engine.enabled: false were removed in v7.0. Migration: drop the flag/env/config.';
|
|
66
|
+
/** v7.0+ no-op. Always returns false. */
|
|
67
|
+
export function shouldWarnEngineOffDeprecation(_resolved) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
/** v7.0+ no-op. Always returns false. */
|
|
71
|
+
export function emitEngineOffDeprecationWarning(_resolved, _warn = () => { }) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=resolve-engine.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { ExternalRef } from './types.ts';
|
|
2
|
+
import type { ReadbackResult } from './provider-readback.ts';
|
|
3
|
+
/** What the orchestrator should do for this phase on resume. */
|
|
4
|
+
export type ResumeDecision =
|
|
5
|
+
/** No prior success → run the phase normally (no preflight needed). */
|
|
6
|
+
{
|
|
7
|
+
kind: 'proceed-fresh';
|
|
8
|
+
}
|
|
9
|
+
/** All postEffect refs `merged`/`live` → emit phase.success with
|
|
10
|
+
* `replayed: true` and skip the phase body. */
|
|
11
|
+
| {
|
|
12
|
+
kind: 'skip-already-applied';
|
|
13
|
+
readback: ReadbackResult[];
|
|
14
|
+
reason: string;
|
|
15
|
+
}
|
|
16
|
+
/** PreEffect ref `open` + postEffect incomplete → re-run the phase body;
|
|
17
|
+
* its own ledger handles partial-state dedup. */
|
|
18
|
+
| {
|
|
19
|
+
kind: 'retry';
|
|
20
|
+
readback: ReadbackResult[];
|
|
21
|
+
reason: string;
|
|
22
|
+
}
|
|
23
|
+
/** Ambiguous state → emit replay.override + throw needs_human. */
|
|
24
|
+
| {
|
|
25
|
+
kind: 'needs-human';
|
|
26
|
+
readback: ReadbackResult[];
|
|
27
|
+
reason: string;
|
|
28
|
+
refsConsulted: ExternalRef[];
|
|
29
|
+
};
|
|
30
|
+
export interface ResumePreflightInput {
|
|
31
|
+
/** The phase's contract — read out of `PHASE_REGISTRY[name]`. Empty
|
|
32
|
+
* arrays are valid (e.g. `pr` declares post-effect = []). */
|
|
33
|
+
preEffectRefKinds: readonly string[];
|
|
34
|
+
postEffectRefKinds: readonly string[];
|
|
35
|
+
/** Did the prior orchestrator attempt record `phase.success` for this
|
|
36
|
+
* phaseIdx? When false, we return `proceed-fresh` immediately. */
|
|
37
|
+
priorPhaseSuccess: boolean;
|
|
38
|
+
/** Refs persisted by the prior attempt — both pre-effect and post-effect
|
|
39
|
+
* kinds, in event order. The preflight filters them by kind to map
|
|
40
|
+
* to the contract. */
|
|
41
|
+
priorRefs: readonly ExternalRef[];
|
|
42
|
+
/** Test seam — replace `verifyRefs` so unit tests don't need the real
|
|
43
|
+
* github / supabase readbacks. Production callers omit this. */
|
|
44
|
+
verifyRefsImpl?: (refs: readonly ExternalRef[]) => Promise<ReadbackResult[]>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Make the resume decision for one side-effecting phase.
|
|
48
|
+
*
|
|
49
|
+
* The contract:
|
|
50
|
+
* - When `priorPhaseSuccess === false` → `proceed-fresh` (no preflight).
|
|
51
|
+
* - When `priorRefs` is empty AND we have a prior phase.success →
|
|
52
|
+
* `needs-human` (the phase claimed success but didn't persist any
|
|
53
|
+
* ref — corrupted state we shouldn't auto-retry).
|
|
54
|
+
* - When all refs whose kind is in `postEffectRefKinds` read back as
|
|
55
|
+
* `merged` / `live` AND postEffect is non-empty → `skip-already-applied`.
|
|
56
|
+
* - When at least one preEffect ref reads back as `open` AND not all
|
|
57
|
+
* post-effect refs merged → `retry`.
|
|
58
|
+
* - Otherwise → `needs-human`.
|
|
59
|
+
*
|
|
60
|
+
* `pr`'s contract has `postEffectRefKinds: []`. For that case the
|
|
61
|
+
* pre-effect ref (`github-pr`) doubles as the reconciliation ref — we
|
|
62
|
+
* inspect IT against `merged`/`open` semantics. Any other state →
|
|
63
|
+
* needs-human.
|
|
64
|
+
*/
|
|
65
|
+
export declare function resumePreflight(input: ResumePreflightInput): Promise<ResumeDecision>;
|
|
66
|
+
//# sourceMappingURL=resume-preflight.d.ts.map
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// src/core/run-state/resume-preflight.ts
|
|
2
|
+
//
|
|
3
|
+
// v6.2.1 — orchestrator resume preflight for side-effecting phases.
|
|
4
|
+
//
|
|
5
|
+
// Background — the v6.2.1 side-effect idempotency contract requires every
|
|
6
|
+
// `hasSideEffects: true` phase to declare two ref kind sets:
|
|
7
|
+
//
|
|
8
|
+
// - `preEffectRefKinds` — recorded BEFORE the side effect runs.
|
|
9
|
+
// - `postEffectRefKinds` — recorded AFTER the side effect completes.
|
|
10
|
+
//
|
|
11
|
+
// On resume of a runId the orchestrator must consult the persisted refs +
|
|
12
|
+
// the platform of record (via `verifyRefs`) before re-invoking the phase
|
|
13
|
+
// body. This module owns that decision.
|
|
14
|
+
//
|
|
15
|
+
// The decision matrix (per spec "Resume preflight pseudocode"):
|
|
16
|
+
//
|
|
17
|
+
// - All postEffect refs read back as `merged` / `live`
|
|
18
|
+
// ⇒ `skip-already-applied` — orchestrator emits `phase.success
|
|
19
|
+
// { replayed: true, reason: 'side-effect-already-applied' }` and
|
|
20
|
+
// advances. NO retry.
|
|
21
|
+
// - PreEffect ref read back as `open` AND postEffect refs incomplete
|
|
22
|
+
// ⇒ `retry` — orchestrator runs the phase body normally; the phase's
|
|
23
|
+
// own ledger / preflight handles dedup against the partial state.
|
|
24
|
+
// - Otherwise (no prior success, ambiguous, or readback unknown)
|
|
25
|
+
// ⇒ `needs-human` — orchestrator emits `replay.override` event and
|
|
26
|
+
// throws `GuardrailError('needs_human')`.
|
|
27
|
+
//
|
|
28
|
+
// Fresh runs (no prior phase.success for this phaseIdx) get `proceed-fresh`
|
|
29
|
+
// — the orchestrator just runs the phase normally.
|
|
30
|
+
import { verifyRefs as defaultVerifyRefs } from "./provider-readback.js";
|
|
31
|
+
/**
|
|
32
|
+
* Make the resume decision for one side-effecting phase.
|
|
33
|
+
*
|
|
34
|
+
* The contract:
|
|
35
|
+
* - When `priorPhaseSuccess === false` → `proceed-fresh` (no preflight).
|
|
36
|
+
* - When `priorRefs` is empty AND we have a prior phase.success →
|
|
37
|
+
* `needs-human` (the phase claimed success but didn't persist any
|
|
38
|
+
* ref — corrupted state we shouldn't auto-retry).
|
|
39
|
+
* - When all refs whose kind is in `postEffectRefKinds` read back as
|
|
40
|
+
* `merged` / `live` AND postEffect is non-empty → `skip-already-applied`.
|
|
41
|
+
* - When at least one preEffect ref reads back as `open` AND not all
|
|
42
|
+
* post-effect refs merged → `retry`.
|
|
43
|
+
* - Otherwise → `needs-human`.
|
|
44
|
+
*
|
|
45
|
+
* `pr`'s contract has `postEffectRefKinds: []`. For that case the
|
|
46
|
+
* pre-effect ref (`github-pr`) doubles as the reconciliation ref — we
|
|
47
|
+
* inspect IT against `merged`/`open` semantics. Any other state →
|
|
48
|
+
* needs-human.
|
|
49
|
+
*/
|
|
50
|
+
export async function resumePreflight(input) {
|
|
51
|
+
if (!input.priorPhaseSuccess && input.priorRefs.length === 0) {
|
|
52
|
+
return { kind: 'proceed-fresh' };
|
|
53
|
+
}
|
|
54
|
+
const verify = input.verifyRefsImpl ?? defaultVerifyRefs;
|
|
55
|
+
const readback = input.priorRefs.length > 0 ? await verify(input.priorRefs) : [];
|
|
56
|
+
// postEffect path — if the contract declares post-effect kinds, the
|
|
57
|
+
// pre-effect breadcrumb's readback state is authoritative for the
|
|
58
|
+
// batch's "did all the planned work land?" question. Post-effect refs
|
|
59
|
+
// alone aren't authoritative because the resume doesn't know the total
|
|
60
|
+
// planned set just from the persisted ref count (a partial crash
|
|
61
|
+
// persists 3 of 5 migration-version refs and 0 readback for the missing
|
|
62
|
+
// 2 — counting the persisted set against itself always says "complete").
|
|
63
|
+
// The batch readback is the source of truth: it queries the dispatcher's
|
|
64
|
+
// ledger for the planned set vs the applied set.
|
|
65
|
+
if (input.postEffectRefKinds.length > 0) {
|
|
66
|
+
const preRefs = readback.filter(r => input.preEffectRefKinds.includes(r.refKind));
|
|
67
|
+
const postRefs = readback.filter(r => input.postEffectRefKinds.includes(r.refKind));
|
|
68
|
+
// Skip-already-applied: pre-effect breadcrumb confirms `merged` AND
|
|
69
|
+
// every persisted post-effect ref is live/merged. The pre-effect's
|
|
70
|
+
// `merged` state comes from the batch readback comparing planned to
|
|
71
|
+
// applied — so we trust it as the cross-set check.
|
|
72
|
+
const preMerged = preRefs.length > 0 && preRefs.every(r => r.currentState === 'merged' || r.currentState === 'live');
|
|
73
|
+
const postAllLive = postRefs.every(r => r.currentState === 'merged' || r.currentState === 'live');
|
|
74
|
+
if (preMerged && postAllLive) {
|
|
75
|
+
return {
|
|
76
|
+
kind: 'skip-already-applied',
|
|
77
|
+
readback,
|
|
78
|
+
reason: `pre-effect ref reports merged + ${postRefs.length} post-effect ref(s) live`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
// Retry: pre-effect breadcrumb is `open` (some planned items still
|
|
82
|
+
// pending). The phase body's own ledger guard handles dedup of the
|
|
83
|
+
// already-applied items.
|
|
84
|
+
const preOpen = preRefs.some(r => r.currentState === 'open');
|
|
85
|
+
if (preOpen) {
|
|
86
|
+
return {
|
|
87
|
+
kind: 'retry',
|
|
88
|
+
readback,
|
|
89
|
+
reason: 'pre-effect breadcrumb open + post-effect refs incomplete',
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
// Empty postEffectRefKinds — pre-effect ref doubles as reconciliation.
|
|
95
|
+
// Used by `pr`: the github-pr ref IS the post-effect ref.
|
|
96
|
+
const preRefs = readback.filter(r => input.preEffectRefKinds.includes(r.refKind));
|
|
97
|
+
if (preRefs.length > 0 && preRefs.every(r => r.currentState === 'merged' || r.currentState === 'live' || r.currentState === 'open')) {
|
|
98
|
+
// For pr: open/merged both mean "the PR exists, don't recreate it."
|
|
99
|
+
return {
|
|
100
|
+
kind: 'skip-already-applied',
|
|
101
|
+
readback,
|
|
102
|
+
reason: `pre-effect ref doubles as reconciliation; all ${preRefs.length} report live/merged/open`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Anything else — refs missing, closed without merge, unknown — punt.
|
|
107
|
+
return {
|
|
108
|
+
kind: 'needs-human',
|
|
109
|
+
readback,
|
|
110
|
+
refsConsulted: [...input.priorRefs],
|
|
111
|
+
reason: readback.length === 0
|
|
112
|
+
? 'prior phase.success exists but no externalRefs persisted'
|
|
113
|
+
: 'readback could not confirm skip-already-applied or retry-safe',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=resume-preflight.js.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type RunPhase } from './phase-runner.ts';
|
|
2
|
+
import type { GuardrailConfig } from '../config/types.ts';
|
|
3
|
+
/** Caller-supplied inputs to drive a single-phase engine run.
|
|
4
|
+
*
|
|
5
|
+
* The helper is intentionally narrow: every wrapped verb passes the same
|
|
6
|
+
* shape today. Side-effecting verbs (`pr`, `migrate`, `implement`) will
|
|
7
|
+
* use the same shape too — they emit externalRefs from inside `phase.run`
|
|
8
|
+
* via `ctx.emitExternalRef()`, which the underlying `runPhase()` records.
|
|
9
|
+
* No new helper field is needed for them in v6.0.7+. */
|
|
10
|
+
export interface RunPhaseWithLifecycleOpts<I, O> {
|
|
11
|
+
/** Project working directory. Passed straight through to `createRun`
|
|
12
|
+
* so `.guardrail-cache/runs/<ulid>/` lands in the right place. */
|
|
13
|
+
cwd: string;
|
|
14
|
+
/** The pre-built phase definition (name, idempotent, hasSideEffects, run).
|
|
15
|
+
* Same `RunPhase<I, O>` the caller would pass to `runPhase()` directly. */
|
|
16
|
+
phase: RunPhase<I, O>;
|
|
17
|
+
/** Phase input — same shape the caller would pass to `runPhase()`. */
|
|
18
|
+
input: I;
|
|
19
|
+
/** Loaded `guardrail.config.yaml` (or the default `{ configVersion: 1 }`).
|
|
20
|
+
* Used only for the `engine.enabled` precedence layer. The helper does
|
|
21
|
+
* NOT re-load config — that's the caller's responsibility (and lets
|
|
22
|
+
* callers pass synthetic configs in tests). */
|
|
23
|
+
config: GuardrailConfig;
|
|
24
|
+
/** CLI flag override — `true` from `--engine`, `false` from `--no-engine`,
|
|
25
|
+
* `undefined` if neither was passed. */
|
|
26
|
+
cliEngine: boolean | undefined;
|
|
27
|
+
/** Raw value of `process.env.CLAUDE_AUTOPILOT_ENGINE`. `undefined` if
|
|
28
|
+
* unset. The helper passes this through to `resolveEngineEnabled` —
|
|
29
|
+
* invalid values fall through with a `run.warning` recorded automatically. */
|
|
30
|
+
envEngine: string | undefined;
|
|
31
|
+
/** v6.x escape hatch — invoked when the engine was disabled. Retained
|
|
32
|
+
* in the v7.0 type for source compatibility with existing callers,
|
|
33
|
+
* but the helper NEVER calls it in v7.0+ (engine-off path was
|
|
34
|
+
* removed). Optional in v7.0; can be omitted from new call sites. */
|
|
35
|
+
runEngineOff?: () => Promise<O>;
|
|
36
|
+
}
|
|
37
|
+
/** What the helper hands back. `runId` and `runDir` are null on the
|
|
38
|
+
* engine-off path so callers can branch on whether engine artifacts exist
|
|
39
|
+
* (e.g. for a future `--json` envelope that surfaces the runId). */
|
|
40
|
+
export interface RunPhaseWithLifecycleResult<O> {
|
|
41
|
+
output: O;
|
|
42
|
+
/** ULID of the created run, or null when engine-off. */
|
|
43
|
+
runId: string | null;
|
|
44
|
+
/** Absolute path to the run dir, or null when engine-off. */
|
|
45
|
+
runDir: string | null;
|
|
46
|
+
}
|
|
47
|
+
/** Drive a single-phase engine run with full lifecycle instrumentation,
|
|
48
|
+
* OR fall through to the legacy `runEngineOff` callback when the engine
|
|
49
|
+
* is disabled by config / CLI / env precedence.
|
|
50
|
+
*
|
|
51
|
+
* Engine-on lifecycle (in order):
|
|
52
|
+
* createRun → (optional run.warning for invalid env) → runPhase →
|
|
53
|
+
* run.complete (success or failed) → refresh state.json → release lock.
|
|
54
|
+
*
|
|
55
|
+
* On phase failure the helper:
|
|
56
|
+
* 1. Emits `run.complete` with `status: 'failed'`.
|
|
57
|
+
* 2. Refreshes state.json from the replayed events.
|
|
58
|
+
* 3. Prints the legacy `[<phase>] engine: phase failed — <msg>` banner
|
|
59
|
+
* to stderr (byte-for-byte identical to the inline pattern that
|
|
60
|
+
* lived in 8 of 8 wrapped verbs pre-v6.0.6).
|
|
61
|
+
* 4. Releases the lock and re-throws so the caller can return its
|
|
62
|
+
* legacy non-zero exit code.
|
|
63
|
+
*
|
|
64
|
+
* The lock release in `finally` is best-effort. `release()` is idempotent
|
|
65
|
+
* (the runs lock module accepts double-release without throwing), so the
|
|
66
|
+
* catch block does not need to release the lock itself — `finally` covers
|
|
67
|
+
* both the success and failure exit paths. */
|
|
68
|
+
export declare function runPhaseWithLifecycle<I, O>(opts: RunPhaseWithLifecycleOpts<I, O>): Promise<RunPhaseWithLifecycleResult<O>>;
|
|
69
|
+
//# sourceMappingURL=run-phase-with-lifecycle.d.ts.map
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// src/core/run-state/run-phase-with-lifecycle.ts
|
|
2
|
+
//
|
|
3
|
+
// v6.0.6 — extract the lifecycle wrapper that's been duplicated across
|
|
4
|
+
// every wrapped CLI verb (`scan`, `costs`, `fix`, `brainstorm`, `spec`,
|
|
5
|
+
// `plan`, `review`, `validate`). The pattern is mechanical:
|
|
6
|
+
//
|
|
7
|
+
// 1. If engine-off → run the legacy phase body via `runEngineOff()` and
|
|
8
|
+
// return its result.
|
|
9
|
+
// 2. If engine-on → createRun → optional run.warning for invalid env →
|
|
10
|
+
// runPhase → emit run.complete (success or failed) → refresh state.json
|
|
11
|
+
// → release lock (best effort, in finally).
|
|
12
|
+
//
|
|
13
|
+
// This helper sits ON TOP of `runPhase()` from `phase-runner.ts` — it does
|
|
14
|
+
// not replace it. Callers continue to define their own `RunPhase<I, O>` with
|
|
15
|
+
// per-phase `idempotent` / `hasSideEffects` / `run` and pass it in.
|
|
16
|
+
//
|
|
17
|
+
// Why now: with 8 of 10 phases wrapped (the v6.0.5 milestone), the pattern
|
|
18
|
+
// is fully evidenced. The remaining 3 phases (`implement`, `migrate`, `pr`)
|
|
19
|
+
// are side-effecting and need externalRefs — those will inform a v6.0.7+
|
|
20
|
+
// extension to this helper but won't change its core shape. Doing the
|
|
21
|
+
// extraction now means those 3 wraps build against the helper instead of
|
|
22
|
+
// re-introducing the boilerplate.
|
|
23
|
+
//
|
|
24
|
+
// What this helper does NOT do:
|
|
25
|
+
// - Print success banners — rendering stays in the caller.
|
|
26
|
+
// - Decide engine-off behavior — that's `runEngineOff`, supplied by the
|
|
27
|
+
// caller (typically a thin closure over the phase body).
|
|
28
|
+
// - Plumb externalRefs / readback — the underlying `runPhase()` already
|
|
29
|
+
// handles those. This helper just owns the run-level lifecycle events.
|
|
30
|
+
//
|
|
31
|
+
// Future extension (v6.0.7+): `implement` / `migrate` / `pr` need
|
|
32
|
+
// externalRef ledger entries (`git-remote-push`, `migration-version`,
|
|
33
|
+
// `github-pr`). The helper's `phase.run` already receives `ctx` so
|
|
34
|
+
// `ctx.emitExternalRef()` works without changes here. If a future PR needs
|
|
35
|
+
// to fan-in run-wide externalRefs from multiple phases (multi-phase
|
|
36
|
+
// pipelines, e.g. autopilot orchestrator), the signature can grow a
|
|
37
|
+
// `phases: RunPhase[]` overload — but the single-phase shape stays identical.
|
|
38
|
+
import { createRun } from "./runs.js";
|
|
39
|
+
import { runPhase } from "./phase-runner.js";
|
|
40
|
+
import { appendEvent, replayState } from "./events.js";
|
|
41
|
+
import { writeStateSnapshot } from "./state.js";
|
|
42
|
+
import { resolveEngineEnabled, } from "./resolve-engine.js";
|
|
43
|
+
// Inline ANSI codes — same shape every wrapped verb uses. Kept here so the
|
|
44
|
+
// helper doesn't depend on a verb-local `fmt`. The error message format
|
|
45
|
+
// (`[<phase>] engine: phase failed — <msg>` + dim inspect hint) is
|
|
46
|
+
// byte-for-byte identical to what every wrapped phase printed pre-extract.
|
|
47
|
+
const ANSI_RESET = '\x1b[0m';
|
|
48
|
+
const ANSI_DIM = '\x1b[2m';
|
|
49
|
+
const ANSI_RED = '\x1b[31m';
|
|
50
|
+
/** Drive a single-phase engine run with full lifecycle instrumentation,
|
|
51
|
+
* OR fall through to the legacy `runEngineOff` callback when the engine
|
|
52
|
+
* is disabled by config / CLI / env precedence.
|
|
53
|
+
*
|
|
54
|
+
* Engine-on lifecycle (in order):
|
|
55
|
+
* createRun → (optional run.warning for invalid env) → runPhase →
|
|
56
|
+
* run.complete (success or failed) → refresh state.json → release lock.
|
|
57
|
+
*
|
|
58
|
+
* On phase failure the helper:
|
|
59
|
+
* 1. Emits `run.complete` with `status: 'failed'`.
|
|
60
|
+
* 2. Refreshes state.json from the replayed events.
|
|
61
|
+
* 3. Prints the legacy `[<phase>] engine: phase failed — <msg>` banner
|
|
62
|
+
* to stderr (byte-for-byte identical to the inline pattern that
|
|
63
|
+
* lived in 8 of 8 wrapped verbs pre-v6.0.6).
|
|
64
|
+
* 4. Releases the lock and re-throws so the caller can return its
|
|
65
|
+
* legacy non-zero exit code.
|
|
66
|
+
*
|
|
67
|
+
* The lock release in `finally` is best-effort. `release()` is idempotent
|
|
68
|
+
* (the runs lock module accepts double-release without throwing), so the
|
|
69
|
+
* catch block does not need to release the lock itself — `finally` covers
|
|
70
|
+
* both the success and failure exit paths. */
|
|
71
|
+
export async function runPhaseWithLifecycle(opts) {
|
|
72
|
+
const { cwd, phase, input, config, cliEngine, envEngine } = opts;
|
|
73
|
+
// v7.0 — engine is always on. resolveEngineEnabled() returns
|
|
74
|
+
// { enabled: true, source: 'default' } unconditionally. We still
|
|
75
|
+
// pass the v6-era inputs through so any future re-introduction of
|
|
76
|
+
// observability (a run.warning when a user passes the removed flags
|
|
77
|
+
// via env vars in CI) is a one-line change.
|
|
78
|
+
const engineResolved = resolveEngineEnabled({
|
|
79
|
+
...(cliEngine !== undefined ? { cliEngine } : {}),
|
|
80
|
+
...(envEngine !== undefined ? { envValue: envEngine } : {}),
|
|
81
|
+
...(typeof config.engine?.enabled === 'boolean'
|
|
82
|
+
? { configEnabled: config.engine.enabled }
|
|
83
|
+
: {}),
|
|
84
|
+
});
|
|
85
|
+
// Engine on — full lifecycle. Mirrors the pre-v6.0.6 inline shape that
|
|
86
|
+
// every wrapped verb duplicated.
|
|
87
|
+
const created = await createRun({
|
|
88
|
+
cwd,
|
|
89
|
+
phases: [phase.name],
|
|
90
|
+
config: {
|
|
91
|
+
engine: { enabled: true, source: engineResolved.source },
|
|
92
|
+
...(engineResolved.invalidEnvValue !== undefined
|
|
93
|
+
? { invalidEnvValue: engineResolved.invalidEnvValue }
|
|
94
|
+
: {}),
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
if (engineResolved.invalidEnvValue !== undefined) {
|
|
98
|
+
// Surface the invalid env value as a typed warning so observers
|
|
99
|
+
// (`runs show <id> --events`) can attribute the fallthrough.
|
|
100
|
+
appendEvent(created.runDir, {
|
|
101
|
+
event: 'run.warning',
|
|
102
|
+
message: `invalid CLAUDE_AUTOPILOT_ENGINE=${JSON.stringify(engineResolved.invalidEnvValue)} ignored`,
|
|
103
|
+
details: { resolution: engineResolved },
|
|
104
|
+
}, { writerId: created.lock.writerId, runId: created.runId });
|
|
105
|
+
}
|
|
106
|
+
// v7.0 — emit `engine_off_removed` warning when CLAUDE_AUTOPILOT_ENGINE
|
|
107
|
+
// is set to an off-style value. The env value is otherwise ignored
|
|
108
|
+
// (engine remains on). Softer than --no-engine (which exits 1) because
|
|
109
|
+
// env vars in CI are sticky and silently breaking every v6.x → v7
|
|
110
|
+
// upgrade in CI on day one would burn user trust. See spec test #1(c).
|
|
111
|
+
if (envEngine !== undefined) {
|
|
112
|
+
const normalized = envEngine.trim().toLowerCase();
|
|
113
|
+
if (normalized === 'off' || normalized === 'false' || normalized === '0' || normalized === 'no') {
|
|
114
|
+
appendEvent(created.runDir, {
|
|
115
|
+
event: 'run.warning',
|
|
116
|
+
message: 'engine_off_removed',
|
|
117
|
+
details: {
|
|
118
|
+
code: 'engine_off_removed',
|
|
119
|
+
envValue: envEngine,
|
|
120
|
+
note: 'CLAUDE_AUTOPILOT_ENGINE=off has no effect in v7.0+; engine remains on.',
|
|
121
|
+
},
|
|
122
|
+
}, { writerId: created.lock.writerId, runId: created.runId });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const runStartedAt = Date.now();
|
|
126
|
+
try {
|
|
127
|
+
const output = await runPhase(phase, input, {
|
|
128
|
+
runDir: created.runDir,
|
|
129
|
+
runId: created.runId,
|
|
130
|
+
writerId: created.lock.writerId,
|
|
131
|
+
phaseIdx: 0,
|
|
132
|
+
});
|
|
133
|
+
// Final lifecycle event — run.complete. The runner doesn't emit this
|
|
134
|
+
// on its own; it's the caller's responsibility (multi-phase pipelines
|
|
135
|
+
// emit it after the LAST phase, single-phase wrappers like this emit
|
|
136
|
+
// after the only phase). Total cost falls back to 0 when the phase
|
|
137
|
+
// doesn't expose a `costUSD` field on its output (read-only verbs
|
|
138
|
+
// don't track cost; scan does).
|
|
139
|
+
const totalCostUSD = extractCostUSD(output);
|
|
140
|
+
appendEvent(created.runDir, {
|
|
141
|
+
event: 'run.complete',
|
|
142
|
+
status: 'success',
|
|
143
|
+
totalCostUSD,
|
|
144
|
+
durationMs: Date.now() - runStartedAt,
|
|
145
|
+
}, { writerId: created.lock.writerId, runId: created.runId });
|
|
146
|
+
// Refresh state.json from the replayed events. The events.ndjson is
|
|
147
|
+
// the source of truth; state.json is a derived snapshot that we MUST
|
|
148
|
+
// rewrite after run.complete so `runs show` / `runs list` reflect the
|
|
149
|
+
// terminal status without needing to replay on every read.
|
|
150
|
+
writeStateSnapshot(created.runDir, replayState(created.runDir));
|
|
151
|
+
return { output, runId: created.runId, runDir: created.runDir };
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
// Engine-on failure — write run.complete with failed status, refresh
|
|
155
|
+
// state.json, print the legacy banner to stderr, then re-throw so the
|
|
156
|
+
// caller can return its legacy non-zero exit code. (Lock release
|
|
157
|
+
// happens in `finally` regardless of success / failure path.)
|
|
158
|
+
appendEvent(created.runDir, {
|
|
159
|
+
event: 'run.complete',
|
|
160
|
+
status: 'failed',
|
|
161
|
+
totalCostUSD: 0,
|
|
162
|
+
durationMs: Date.now() - runStartedAt,
|
|
163
|
+
}, { writerId: created.lock.writerId, runId: created.runId });
|
|
164
|
+
writeStateSnapshot(created.runDir, replayState(created.runDir));
|
|
165
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
166
|
+
process.stderr.write(`${ANSI_RED}[${phase.name}] engine: phase failed — ${message}${ANSI_RESET}\n`);
|
|
167
|
+
process.stderr.write(`${ANSI_DIM} inspect: claude-autopilot runs show ${created.runId} --events${ANSI_RESET}\n`);
|
|
168
|
+
throw err;
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
// Best-effort lock release. The lock module's `release()` is
|
|
172
|
+
// idempotent; if the catch path already released (it doesn't, but a
|
|
173
|
+
// future change might), this is a no-op. Wrapping the await in
|
|
174
|
+
// `.catch(() => {})` ensures a release error never masks the original
|
|
175
|
+
// throw — the spec calls this out explicitly.
|
|
176
|
+
await created.lock.release().catch(() => { });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/** Extract `costUSD` from a phase output if present, else 0. JSON-style
|
|
180
|
+
* duck-typing: we accept any output that exposes a numeric `costUSD`
|
|
181
|
+
* field. Today only `scan` exposes one; the other 7 wrapped verbs
|
|
182
|
+
* return outputs without a cost field, which means `extractCostUSD`
|
|
183
|
+
* returns 0 — byte-for-byte matching the inline `totalCostUSD: 0` they
|
|
184
|
+
* used pre-v6.0.6. */
|
|
185
|
+
function extractCostUSD(output) {
|
|
186
|
+
if (output !== null && typeof output === 'object' && 'costUSD' in output) {
|
|
187
|
+
const v = output.costUSD;
|
|
188
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
189
|
+
return v;
|
|
190
|
+
}
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=run-phase-with-lifecycle.js.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type RunLockHandle } from './lock.ts';
|
|
2
|
+
import { type RunIndex, type RunIndexEntry, type RunState } from './types.ts';
|
|
3
|
+
export declare function runsRoot(cwd: string): string;
|
|
4
|
+
export declare function indexPath(cwd: string): string;
|
|
5
|
+
export declare function runDirFor(cwd: string, runId: string): string;
|
|
6
|
+
export interface CreateRunOptions {
|
|
7
|
+
cwd: string;
|
|
8
|
+
/** Phase names in the order they will execute. */
|
|
9
|
+
phases: string[];
|
|
10
|
+
/** Snapshot of the relevant guardrail.config.yaml fields. Free-form. */
|
|
11
|
+
config?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
export interface CreateRunResult {
|
|
14
|
+
runId: string;
|
|
15
|
+
runDir: string;
|
|
16
|
+
state: RunState;
|
|
17
|
+
/** Lock handle. Caller MUST `release()` on shutdown. */
|
|
18
|
+
lock: RunLockHandle;
|
|
19
|
+
}
|
|
20
|
+
/** Create a fresh run directory, acquire its advisory lock, write the
|
|
21
|
+
* initial state.json, and emit the `run.start` event.
|
|
22
|
+
*
|
|
23
|
+
* Throws GuardrailError(lock_held) if a stale lock exists for the freshly-
|
|
24
|
+
* generated runId — extremely unlikely (ULIDs are unique) but possible if
|
|
25
|
+
* two parallel invocations on the same OS clock collide on a leftover dir
|
|
26
|
+
* on disk. Caller can simply retry. */
|
|
27
|
+
export declare function createRun(opts: CreateRunOptions): Promise<CreateRunResult>;
|
|
28
|
+
/** Rebuild index.json from each run dir's state.json (or replayed state if
|
|
29
|
+
* the snapshot is missing / corrupt). Newest-first ordering by ULID. */
|
|
30
|
+
export declare function rebuildIndex(cwd: string): RunIndex;
|
|
31
|
+
export interface ListRunsOptions {
|
|
32
|
+
/** Force a rebuild from disk even if index.json is fresh. */
|
|
33
|
+
rebuild?: boolean;
|
|
34
|
+
}
|
|
35
|
+
/** List all runs, newest-first. Lazily rebuilds index.json if missing. */
|
|
36
|
+
export declare function listRuns(cwd: string, opts?: ListRunsOptions): RunIndexEntry[];
|
|
37
|
+
export interface GcRunsOptions {
|
|
38
|
+
/** Delete completed runs older than this many days. Required. */
|
|
39
|
+
olderThanDays: number;
|
|
40
|
+
/** Don't actually delete; just return what would be removed. */
|
|
41
|
+
dryRun?: boolean;
|
|
42
|
+
/** Override "now" for tests. Default Date.now(). */
|
|
43
|
+
now?: number;
|
|
44
|
+
}
|
|
45
|
+
export interface GcRunsResult {
|
|
46
|
+
/** runIds that were (or would be) deleted. */
|
|
47
|
+
deleted: string[];
|
|
48
|
+
/** runIds skipped because they're still active or too young. */
|
|
49
|
+
kept: string[];
|
|
50
|
+
/** runIds skipped for safety reasons (symlink, suspicious path). */
|
|
51
|
+
skippedUnsafe: string[];
|
|
52
|
+
}
|
|
53
|
+
/** Delete completed runs older than N days. Honors the spec's symlink
|
|
54
|
+
* safety: uses lstat so we never traverse a symlink out of the runs/
|
|
55
|
+
* tree. */
|
|
56
|
+
export declare function gcRuns(cwd: string, opts: GcRunsOptions): GcRunsResult;
|
|
57
|
+
//# sourceMappingURL=runs.d.ts.map
|