@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,23 @@
|
|
|
1
|
+
export interface BrainstormCommandOptions {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
configPath?: string;
|
|
4
|
+
/**
|
|
5
|
+
* v6.0.3 — engine knob inputs. Same shape and precedence as scan / costs /
|
|
6
|
+
* fix (CLI > env > config > built-in default off in v6.0.x). The CLI
|
|
7
|
+
* dispatcher wires `cliEngine` from `--engine` / `--no-engine`;
|
|
8
|
+
* `envEngine` from `process.env.CLAUDE_AUTOPILOT_ENGINE`. An absent CLI
|
|
9
|
+
* flag + absent env value falls through to the loaded config and then to
|
|
10
|
+
* the built-in default.
|
|
11
|
+
*/
|
|
12
|
+
cliEngine?: boolean;
|
|
13
|
+
envEngine?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Test-only seam — when true, the phase body returns its result without
|
|
16
|
+
* printing the advisory banner. Lets engine-smoke tests assert the
|
|
17
|
+
* `state.json` + `events.ndjson` lifecycle without polluting stdout.
|
|
18
|
+
* Production callers (the CLI dispatcher) MUST NOT pass this.
|
|
19
|
+
*/
|
|
20
|
+
__silent?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare function runBrainstorm(options?: BrainstormCommandOptions): Promise<number>;
|
|
23
|
+
//# sourceMappingURL=brainstorm.d.ts.map
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// src/cli/brainstorm.ts
|
|
2
|
+
//
|
|
3
|
+
// v6.0.3 — wrap the `brainstorm` pipeline phase through `runPhase`.
|
|
4
|
+
//
|
|
5
|
+
// `brainstorm` is the entry point of the autopilot pipeline. It is implemented
|
|
6
|
+
// primarily as a Claude Code skill (`/brainstorm` → `superpowers:brainstorming`),
|
|
7
|
+
// not as a standalone CLI subcommand. The CLI verb that ships in this binary is
|
|
8
|
+
// an advisory shim: it points the user at the Claude Code skill and the next
|
|
9
|
+
// pipeline verbs. There is no LLM call in the CLI verb body, and no provider
|
|
10
|
+
// side effects. The pure-LLM design dialogue happens in Claude Code; the spec
|
|
11
|
+
// markdown produced there lands at `docs/superpowers/specs/<slug>.md` (a local
|
|
12
|
+
// file write, not a platform-side-effect-free remote write — the recipe treats
|
|
13
|
+
// local file writes as acceptable inside the phase body, identical precedent to
|
|
14
|
+
// `fix.ts` editing local source files).
|
|
15
|
+
//
|
|
16
|
+
// Idempotency / side effects (deviation note vs. spec table):
|
|
17
|
+
// - The spec table at docs/specs/v6-run-state-engine.md says
|
|
18
|
+
// `idempotent: no` for `brainstorm` because re-running produces NEW LLM
|
|
19
|
+
// content each invocation. The recipe table at
|
|
20
|
+
// docs/v6/wrapping-pipeline-phases.md previously echoed that. v6.0.3
|
|
21
|
+
// declares `idempotent: true` to match the engine's actual semantics
|
|
22
|
+
// ("safe to retry without reconciliation"): the CLI verb itself is a
|
|
23
|
+
// printed advisory message that is byte-for-byte identical on every
|
|
24
|
+
// invocation, has no externalRefs to reconcile, and no provider state
|
|
25
|
+
// to roll back. The engine's idempotency check is "safe to replay,"
|
|
26
|
+
// not "produces byte-identical output." See the recipe section 2:
|
|
27
|
+
// `idempotent: true → phase output depends only on its input + project
|
|
28
|
+
// state, and re-running gives the same answer.` That holds here.
|
|
29
|
+
// - `hasSideEffects: false` — the CLI verb prints to stdout. No provider
|
|
30
|
+
// calls, no git push, no PR creation, no remote API write. Identical
|
|
31
|
+
// to costs.
|
|
32
|
+
import * as path from 'node:path';
|
|
33
|
+
import * as fs from 'node:fs';
|
|
34
|
+
import { loadConfig } from "../core/config/loader.js";
|
|
35
|
+
import { runPhaseWithLifecycle } from "../core/run-state/run-phase-with-lifecycle.js";
|
|
36
|
+
const C = {
|
|
37
|
+
reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
|
|
38
|
+
cyan: '\x1b[36m', red: '\x1b[31m',
|
|
39
|
+
};
|
|
40
|
+
const fmt = (c, t) => `${C[c]}${t}${C.reset}`;
|
|
41
|
+
export async function runBrainstorm(options = {}) {
|
|
42
|
+
const cwd = options.cwd ?? process.cwd();
|
|
43
|
+
const configPath = options.configPath ?? path.join(cwd, 'guardrail.config.yaml');
|
|
44
|
+
let config = { configVersion: 1 };
|
|
45
|
+
if (fs.existsSync(configPath)) {
|
|
46
|
+
const loaded = await loadConfig(configPath);
|
|
47
|
+
if (loaded)
|
|
48
|
+
config = loaded;
|
|
49
|
+
}
|
|
50
|
+
const brainstormInput = { cwd, silent: options.__silent === true };
|
|
51
|
+
// The wrapped phase body. Pure: reads no files, makes no provider calls.
|
|
52
|
+
// Engine-off callers invoke `executeBrainstormPhase()` directly;
|
|
53
|
+
// engine-on callers route through `runPhase()`.
|
|
54
|
+
const phase = {
|
|
55
|
+
name: 'brainstorm',
|
|
56
|
+
// Pure-LLM design dialogue happens in the Claude Code skill, not here.
|
|
57
|
+
// The CLI verb is an advisory print with no externalRefs to reconcile
|
|
58
|
+
// and no provider state to roll back. Safe to retry. (Deviation from
|
|
59
|
+
// the spec table noted at the top of the file.)
|
|
60
|
+
idempotent: true,
|
|
61
|
+
// No provider calls, no git push, no PR creation. Identical to costs.
|
|
62
|
+
hasSideEffects: false,
|
|
63
|
+
run: async (input) => executeBrainstormPhase(input),
|
|
64
|
+
};
|
|
65
|
+
// v6.0.6 — lifecycle wiring lives in `runPhaseWithLifecycle`. The helper
|
|
66
|
+
// owns the engine-on/engine-off branch and the failure banner.
|
|
67
|
+
let output;
|
|
68
|
+
try {
|
|
69
|
+
const result = await runPhaseWithLifecycle({
|
|
70
|
+
cwd,
|
|
71
|
+
phase,
|
|
72
|
+
input: brainstormInput,
|
|
73
|
+
config,
|
|
74
|
+
cliEngine: options.cliEngine,
|
|
75
|
+
envEngine: options.envEngine,
|
|
76
|
+
runEngineOff: () => executeBrainstormPhase(brainstormInput),
|
|
77
|
+
});
|
|
78
|
+
output = result.output;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Helper already printed the failure banner + emitted run.complete
|
|
82
|
+
// failed + refreshed state.json + released the lock.
|
|
83
|
+
return 1;
|
|
84
|
+
}
|
|
85
|
+
return renderBrainstormOutput(output, brainstormInput);
|
|
86
|
+
}
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Phase body — produce the advisory payload. Pure: no provider calls. By
|
|
89
|
+
// default does NOT print to stdout (the renderer handles that) so the engine
|
|
90
|
+
// path's idempotency isn't coupled to console output. Returns a
|
|
91
|
+
// JSON-serializable BrainstormOutput so the engine can persist it as
|
|
92
|
+
// `result` on the phase snapshot.
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
async function executeBrainstormPhase(_input) {
|
|
95
|
+
return {
|
|
96
|
+
kind: 'advisory',
|
|
97
|
+
nextActions: [
|
|
98
|
+
'Invoke /brainstorm from Claude Code for interactive spec writing',
|
|
99
|
+
'Then /autopilot to run the full pipeline from an approved spec',
|
|
100
|
+
],
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Render — translate BrainstormOutput back to the legacy stdout advisory +
|
|
105
|
+
// exit code. Lives outside the wrapped phase because it's pure presentation;
|
|
106
|
+
// doing the rendering inside the phase would couple the engine path's
|
|
107
|
+
// idempotency to console output.
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
function renderBrainstormOutput(_output, input) {
|
|
110
|
+
if (input.silent)
|
|
111
|
+
return 0;
|
|
112
|
+
console.log(`
|
|
113
|
+
${fmt('bold', '[brainstorm]')} The pipeline entry point is a Claude Code skill, not a CLI subcommand.
|
|
114
|
+
|
|
115
|
+
Invoke it from Claude Code:
|
|
116
|
+
|
|
117
|
+
${fmt('cyan', '/brainstorm')} Interactive spec writing
|
|
118
|
+
${fmt('cyan', '/autopilot')} Full pipeline from an approved spec
|
|
119
|
+
${fmt('cyan', '/migrate')} Database migration phase (stack-dependent)
|
|
120
|
+
|
|
121
|
+
From the terminal, the CLI subset exposes only the individual review-phase subcommands:
|
|
122
|
+
|
|
123
|
+
${fmt('cyan', 'claude-autopilot run --base main')} Just the review phase
|
|
124
|
+
${fmt('cyan', 'claude-autopilot doctor')} Check prerequisites (incl. superpowers plugin)
|
|
125
|
+
${fmt('cyan', 'claude-autopilot migrate-v4')} Codemod for v4 → v5 repo migration (not a pipeline phase)
|
|
126
|
+
|
|
127
|
+
Full pipeline docs: https://github.com/axledbetter/claude-autopilot#the-pipeline-phase-by-phase
|
|
128
|
+
`);
|
|
129
|
+
return 0;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=brainstorm.js.map
|
package/dist/src/cli/costs.d.ts
CHANGED
|
@@ -1,2 +1,16 @@
|
|
|
1
|
-
export
|
|
1
|
+
export interface CostsCommandOptions {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
configPath?: string;
|
|
4
|
+
/**
|
|
5
|
+
* v6.0.2 — engine knob inputs. Same shape and precedence as scan
|
|
6
|
+
* (CLI > env > config > built-in default off in v6.0.x). The CLI dispatcher
|
|
7
|
+
* wires `cliEngine` from `--engine` / `--no-engine`; `envEngine` from
|
|
8
|
+
* `process.env.CLAUDE_AUTOPILOT_ENGINE`. An absent CLI flag + absent env
|
|
9
|
+
* value falls through to the loaded config and then to the built-in
|
|
10
|
+
* default.
|
|
11
|
+
*/
|
|
12
|
+
cliEngine?: boolean;
|
|
13
|
+
envEngine?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function runCosts(cwdOrOptions?: string | CostsCommandOptions): Promise<number>;
|
|
2
16
|
//# sourceMappingURL=costs.d.ts.map
|
package/dist/src/cli/costs.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import * as fs from 'node:fs';
|
|
1
3
|
import { readCostLog } from "../core/persist/cost-log.js";
|
|
4
|
+
import { loadConfig } from "../core/config/loader.js";
|
|
5
|
+
import { runPhaseWithLifecycle } from "../core/run-state/run-phase-with-lifecycle.js";
|
|
2
6
|
const C = {
|
|
3
7
|
reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m',
|
|
4
|
-
green: '\x1b[32m', yellow: '\x1b[33m', cyan: '\x1b[36m',
|
|
8
|
+
green: '\x1b[32m', yellow: '\x1b[33m', cyan: '\x1b[36m', red: '\x1b[31m',
|
|
5
9
|
};
|
|
6
10
|
const fmt = (c, t) => `${C[c]}${t}${C.reset}`;
|
|
7
11
|
function formatDate(iso) {
|
|
@@ -19,13 +23,69 @@ function fmtUSD(n) {
|
|
|
19
23
|
function fmtTokens(n) {
|
|
20
24
|
return n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n);
|
|
21
25
|
}
|
|
22
|
-
export async function runCosts(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
export async function runCosts(cwdOrOptions = {}) {
|
|
27
|
+
// Back-compat — early callers (tests, MCP) pass a bare `cwd: string`. The
|
|
28
|
+
// tests/costs.test.ts harness drives this shape directly. Promote both
|
|
29
|
+
// forms into a single options struct so the rest of the function can treat
|
|
30
|
+
// it uniformly.
|
|
31
|
+
const options = typeof cwdOrOptions === 'string'
|
|
32
|
+
? { cwd: cwdOrOptions }
|
|
33
|
+
: cwdOrOptions;
|
|
34
|
+
const cwd = options.cwd ?? process.cwd();
|
|
35
|
+
const configPath = options.configPath ?? path.join(cwd, 'guardrail.config.yaml');
|
|
36
|
+
let config = { configVersion: 1 };
|
|
37
|
+
if (fs.existsSync(configPath)) {
|
|
38
|
+
const loaded = await loadConfig(configPath);
|
|
39
|
+
if (loaded)
|
|
40
|
+
config = loaded;
|
|
41
|
+
}
|
|
42
|
+
const costsInput = { cwd };
|
|
43
|
+
// The wrapped phase body — pure read of the cost ledger + summary build.
|
|
44
|
+
// Extracted into a RunPhase so the engine-on path and the engine-off path
|
|
45
|
+
// share the exact same logic. Engine-off callers invoke this directly via
|
|
46
|
+
// `executeCostsPhase()`; engine-on callers route through `runPhase()`.
|
|
47
|
+
const phase = {
|
|
48
|
+
name: 'costs',
|
|
49
|
+
// Cost summary is a pure read of `.guardrail-cache/costs.jsonl` — re-running
|
|
50
|
+
// produces identical output for identical ledger contents. Always safe to
|
|
51
|
+
// retry.
|
|
52
|
+
idempotent: true,
|
|
53
|
+
// No provider calls, no git push, no PR comment, no file writes (the
|
|
54
|
+
// ledger is read-only on this path; the writer is `appendCostLog` called
|
|
55
|
+
// by other verbs). Replays are safe.
|
|
56
|
+
hasSideEffects: false,
|
|
57
|
+
run: async (input) => executeCostsPhase(input),
|
|
58
|
+
};
|
|
59
|
+
// v6.0.6 — lifecycle wiring lives in `runPhaseWithLifecycle`. The helper
|
|
60
|
+
// owns the engine-on/engine-off branch and the failure banner; the caller
|
|
61
|
+
// just supplies the phase, the input, and the engine-off escape hatch.
|
|
62
|
+
let output;
|
|
63
|
+
try {
|
|
64
|
+
const result = await runPhaseWithLifecycle({
|
|
65
|
+
cwd,
|
|
66
|
+
phase,
|
|
67
|
+
input: costsInput,
|
|
68
|
+
config,
|
|
69
|
+
cliEngine: options.cliEngine,
|
|
70
|
+
envEngine: options.envEngine,
|
|
71
|
+
runEngineOff: () => executeCostsPhase(costsInput),
|
|
72
|
+
});
|
|
73
|
+
output = result.output;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Helper already printed the failure banner + emitted run.complete
|
|
77
|
+
// failed + refreshed state.json + released the lock.
|
|
78
|
+
return 1;
|
|
28
79
|
}
|
|
80
|
+
return renderCostsOutput(output, costsInput);
|
|
81
|
+
}
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// Phase body — read the cost ledger and assemble the summary. Pure: no
|
|
84
|
+
// console output, no exit codes. Returns a JSON-serializable CostsOutput so
|
|
85
|
+
// the engine can persist it as `result` on the phase snapshot.
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
async function executeCostsPhase(input) {
|
|
88
|
+
const log = readCostLog(input.cwd);
|
|
29
89
|
// 7-day window
|
|
30
90
|
const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
|
|
31
91
|
const recent = log.filter(e => new Date(e.timestamp).getTime() >= sevenDaysAgo);
|
|
@@ -34,12 +94,41 @@ export async function runCosts(cwd = process.cwd()) {
|
|
|
34
94
|
const totalInput = log.reduce((s, e) => s + e.inputTokens, 0);
|
|
35
95
|
const totalOutput = log.reduce((s, e) => s + e.outputTokens, 0);
|
|
36
96
|
const recentCost = recent.reduce((s, e) => s + e.costUSD, 0);
|
|
97
|
+
return {
|
|
98
|
+
entryCount: log.length,
|
|
99
|
+
totals: {
|
|
100
|
+
runs: log.length,
|
|
101
|
+
inputTokens: totalInput,
|
|
102
|
+
outputTokens: totalOutput,
|
|
103
|
+
costUSD: totalCost,
|
|
104
|
+
},
|
|
105
|
+
recent: {
|
|
106
|
+
runs: recent.length,
|
|
107
|
+
costUSD: recentCost,
|
|
108
|
+
},
|
|
109
|
+
last10,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
// Render — translate CostsOutput back to the legacy stdout summary + exit
|
|
114
|
+
// code. Lives outside the wrapped phase because it's pure presentation;
|
|
115
|
+
// doing the rendering inside the phase would couple the engine path's
|
|
116
|
+
// idempotency to console output.
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
function renderCostsOutput(output, input) {
|
|
119
|
+
const { cwd } = input;
|
|
120
|
+
const { entryCount, totals, recent, last10 } = output;
|
|
121
|
+
if (entryCount === 0) {
|
|
122
|
+
console.log(fmt('yellow', `[costs] No run history found in ${cwd} — run \`guardrail run\` first.`));
|
|
123
|
+
console.log(fmt('dim', ` (Costs are scoped per-project. \`cd\` to the project before checking.)`));
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
37
126
|
console.log(`\n${fmt('bold', '[costs]')} ${fmt('dim', cwd)}\n`);
|
|
38
127
|
// Summary row
|
|
39
128
|
console.log(fmt('bold', 'Summary'));
|
|
40
|
-
console.log(` All-time runs: ${
|
|
41
|
-
console.log(` All-time cost: ${fmtUSD(
|
|
42
|
-
console.log(` Last 7 days: ${fmtUSD(
|
|
129
|
+
console.log(` All-time runs: ${totals.runs}`);
|
|
130
|
+
console.log(` All-time cost: ${fmtUSD(totals.costUSD)} (${fmtTokens(totals.inputTokens)} in / ${fmtTokens(totals.outputTokens)} out)`);
|
|
131
|
+
console.log(` Last 7 days: ${fmtUSD(recent.costUSD)} (${recent.runs} run${recent.runs !== 1 ? 's' : ''})`);
|
|
43
132
|
console.log(fmt('dim', ` (per-project — scoped to ${cwd}/.guardrail-cache/costs.jsonl)`));
|
|
44
133
|
console.log('');
|
|
45
134
|
// Last 10 runs table
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// `claude-autopilot dashboard <verb>` — umbrella dispatcher.
|
|
2
|
+
//
|
|
3
|
+
// Verbs: login | logout | status | upload <runId>
|
|
4
|
+
import { runDashboardLogin } from "./login.js";
|
|
5
|
+
import { runDashboardLogout } from "./logout.js";
|
|
6
|
+
import { runDashboardStatus } from "./status.js";
|
|
7
|
+
import { runDashboardUpload } from "./upload.js";
|
|
8
|
+
export async function runDashboardVerb(args) {
|
|
9
|
+
const [verb, ...rest] = args.argv;
|
|
10
|
+
switch (verb) {
|
|
11
|
+
case 'login': {
|
|
12
|
+
try {
|
|
13
|
+
await runDashboardLogin();
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
process.stderr.write(`[autopilot] login failed: ${err.message}\n`);
|
|
18
|
+
return 1;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
case 'logout': {
|
|
22
|
+
await runDashboardLogout();
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
case 'status': {
|
|
26
|
+
await runDashboardStatus();
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
case 'upload': {
|
|
30
|
+
const runId = rest[0];
|
|
31
|
+
if (!runId) {
|
|
32
|
+
process.stderr.write(`[autopilot] usage: claude-autopilot dashboard upload <runId>\n`);
|
|
33
|
+
return 2;
|
|
34
|
+
}
|
|
35
|
+
const result = await runDashboardUpload({ runId });
|
|
36
|
+
if (result.ok)
|
|
37
|
+
return 0;
|
|
38
|
+
if (result.notLoggedIn || result.runDirMissing)
|
|
39
|
+
return 2;
|
|
40
|
+
return 1;
|
|
41
|
+
}
|
|
42
|
+
default: {
|
|
43
|
+
process.stderr.write(`[autopilot] unknown dashboard verb: ${verb ?? '(none)'}\n`);
|
|
44
|
+
process.stderr.write(` valid verbs: login, logout, status, upload <runId>\n`);
|
|
45
|
+
return 2;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type DashboardConfig } from '../../dashboard/config.ts';
|
|
2
|
+
export interface LoginOptions {
|
|
3
|
+
/** Override base URL for tests / staging. */
|
|
4
|
+
baseUrl?: string;
|
|
5
|
+
/** Override browser launch — useful in tests + headless CI. */
|
|
6
|
+
openBrowser?: (url: string) => void | Promise<void>;
|
|
7
|
+
/** Manual mode: print URL, accept paste of (apiKey, fingerprint, email) on stdin instead of loopback. */
|
|
8
|
+
manual?: boolean;
|
|
9
|
+
/** Test seam — let tests force a specific port range start to avoid collisions. */
|
|
10
|
+
portRangeStart?: number;
|
|
11
|
+
/** Test seam — abort the listener after a fixed timeout. */
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
/** Test seam — silence stdout/stderr writes. */
|
|
14
|
+
silent?: boolean;
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
}
|
|
17
|
+
export interface LoginResult {
|
|
18
|
+
config: DashboardConfig;
|
|
19
|
+
port: number;
|
|
20
|
+
}
|
|
21
|
+
export declare function runDashboardLogin(opts?: LoginOptions): Promise<LoginResult>;
|
|
22
|
+
//# sourceMappingURL=login.d.ts.map
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
// `claude-autopilot dashboard login` — nonce-bound loopback OAuth-ish flow.
|
|
2
|
+
//
|
|
3
|
+
// 1. Generate 128-bit nonce.
|
|
4
|
+
// 2. Bind a node:http listener on the first available port in 56000-56050.
|
|
5
|
+
// 3. Open https://autopilot.dev/cli-auth?cb=<callback>&nonce=<nonce> in the
|
|
6
|
+
// user's browser. (User signs in, web page POSTs back to the callback
|
|
7
|
+
// with { apiKey, fingerprint, accountEmail, nonce }.)
|
|
8
|
+
// 4. Validate nonce with crypto.timingSafeEqual; reject mismatches.
|
|
9
|
+
// 5. Atomically write ~/.claude-autopilot/dashboard.json with mode 0600.
|
|
10
|
+
// 6. Respond 200 to the browser; close listener; print success.
|
|
11
|
+
//
|
|
12
|
+
// The web `/cli-auth` page is operator-deferred to Phase 4 dashboard UI;
|
|
13
|
+
// for now tests use a mock browser handler that simulates the full flow.
|
|
14
|
+
import { randomBytes, timingSafeEqual } from 'node:crypto';
|
|
15
|
+
import { createServer } from 'node:http';
|
|
16
|
+
import { spawn } from 'node:child_process';
|
|
17
|
+
import { writeConfig, getAutopilotBaseUrl, } from "../../dashboard/config.js";
|
|
18
|
+
const PORT_START = 56000;
|
|
19
|
+
const PORT_END = 56050;
|
|
20
|
+
const NONCE_BYTES = 16; // 128-bit
|
|
21
|
+
const TIMEOUT_MS = 5 * 60 * 1000;
|
|
22
|
+
const MAX_BODY_BYTES = 4096;
|
|
23
|
+
const KEY_RE = /^clp_[0-9a-f]{64}$/;
|
|
24
|
+
function nonceMatch(expected, candidate) {
|
|
25
|
+
// timingSafeEqual requires equal-length buffers.
|
|
26
|
+
const a = Buffer.from(expected, 'utf-8');
|
|
27
|
+
const b = Buffer.from(candidate, 'utf-8');
|
|
28
|
+
if (a.length !== b.length)
|
|
29
|
+
return false;
|
|
30
|
+
return timingSafeEqual(a, b);
|
|
31
|
+
}
|
|
32
|
+
async function tryListen(port) {
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
const server = createServer();
|
|
35
|
+
const onError = () => {
|
|
36
|
+
server.removeListener('listening', onListening);
|
|
37
|
+
resolve(null);
|
|
38
|
+
};
|
|
39
|
+
const onListening = () => {
|
|
40
|
+
server.removeListener('error', onError);
|
|
41
|
+
resolve({ server, port });
|
|
42
|
+
};
|
|
43
|
+
server.once('error', onError);
|
|
44
|
+
server.once('listening', onListening);
|
|
45
|
+
server.listen(port, '127.0.0.1');
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
async function bindFirstPort(start, end) {
|
|
49
|
+
for (let p = start; p <= end; p++) {
|
|
50
|
+
const r = await tryListen(p);
|
|
51
|
+
if (r)
|
|
52
|
+
return r;
|
|
53
|
+
}
|
|
54
|
+
throw new Error(`could not bind any port in ${start}-${end}`);
|
|
55
|
+
}
|
|
56
|
+
function readJsonBody(req) {
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
let bytes = 0;
|
|
59
|
+
const chunks = [];
|
|
60
|
+
req.on('data', (c) => {
|
|
61
|
+
bytes += c.length;
|
|
62
|
+
if (bytes > MAX_BODY_BYTES) {
|
|
63
|
+
req.destroy();
|
|
64
|
+
reject(new Error('body too large'));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
chunks.push(c);
|
|
68
|
+
});
|
|
69
|
+
req.on('end', () => {
|
|
70
|
+
try {
|
|
71
|
+
const buf = Buffer.concat(chunks);
|
|
72
|
+
resolve(JSON.parse(buf.toString('utf-8')));
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
reject(err);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
req.on('error', reject);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function openInBrowser(url) {
|
|
82
|
+
// Best-effort cross-platform; ignored on test/headless paths.
|
|
83
|
+
const platform = process.platform;
|
|
84
|
+
let cmd;
|
|
85
|
+
let args;
|
|
86
|
+
if (platform === 'darwin') {
|
|
87
|
+
cmd = 'open';
|
|
88
|
+
args = [url];
|
|
89
|
+
}
|
|
90
|
+
else if (platform === 'win32') {
|
|
91
|
+
cmd = 'cmd';
|
|
92
|
+
args = ['/c', 'start', '""', url];
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
cmd = 'xdg-open';
|
|
96
|
+
args = [url];
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
spawn(cmd, args, { stdio: 'ignore', detached: true }).unref();
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
/* noop */
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
export async function runDashboardLogin(opts = {}) {
|
|
106
|
+
// Phase 4 — unified env name. AUTOPILOT_PUBLIC_BASE_URL is canonical
|
|
107
|
+
// (matches apps/web). AUTOPILOT_DASHBOARD_BASE_URL is the deprecated
|
|
108
|
+
// Phase 2.3 alias and triggers a one-time warning.
|
|
109
|
+
const baseUrl = opts.baseUrl ?? getAutopilotBaseUrl();
|
|
110
|
+
const timeoutMs = opts.timeoutMs ?? TIMEOUT_MS;
|
|
111
|
+
const portStart = opts.portRangeStart ?? PORT_START;
|
|
112
|
+
const portEnd = portStart + (PORT_END - PORT_START);
|
|
113
|
+
const nonce = randomBytes(NONCE_BYTES).toString('hex');
|
|
114
|
+
const { server, port } = await bindFirstPort(portStart, portEnd);
|
|
115
|
+
const cb = `http://127.0.0.1:${port}/cli-callback`;
|
|
116
|
+
const authUrl = `${baseUrl}/cli-auth?cb=${encodeURIComponent(cb)}&nonce=${encodeURIComponent(nonce)}`;
|
|
117
|
+
let resolved = null;
|
|
118
|
+
let rejected = null;
|
|
119
|
+
const result = new Promise((resolve, reject) => {
|
|
120
|
+
resolved = resolve;
|
|
121
|
+
rejected = reject;
|
|
122
|
+
});
|
|
123
|
+
let settled = false;
|
|
124
|
+
const settle = (fn) => {
|
|
125
|
+
if (settled)
|
|
126
|
+
return;
|
|
127
|
+
settled = true;
|
|
128
|
+
fn();
|
|
129
|
+
// Force-close active connections so the event loop drains immediately
|
|
130
|
+
// (matters for tests; production callers exit the process anyway).
|
|
131
|
+
try {
|
|
132
|
+
server.closeAllConnections?.();
|
|
133
|
+
}
|
|
134
|
+
catch { /* noop */ }
|
|
135
|
+
server.close();
|
|
136
|
+
};
|
|
137
|
+
const timer = setTimeout(() => {
|
|
138
|
+
settle(() => rejected?.(new Error(`login timed out after ${timeoutMs}ms`)));
|
|
139
|
+
}, timeoutMs);
|
|
140
|
+
// Don't keep the event loop alive solely for the timeout watchdog —
|
|
141
|
+
// matters in tests when the suite has finished but timers linger.
|
|
142
|
+
timer.unref?.();
|
|
143
|
+
if (opts.signal) {
|
|
144
|
+
if (opts.signal.aborted) {
|
|
145
|
+
clearTimeout(timer);
|
|
146
|
+
server.close();
|
|
147
|
+
throw new Error('aborted');
|
|
148
|
+
}
|
|
149
|
+
opts.signal.addEventListener('abort', () => {
|
|
150
|
+
clearTimeout(timer);
|
|
151
|
+
settle(() => rejected?.(new Error('aborted')));
|
|
152
|
+
}, { once: true });
|
|
153
|
+
}
|
|
154
|
+
// Phase 4 CORS — the /cli-auth page POSTs to this loopback with
|
|
155
|
+
// mode: 'cors'. Without OPTIONS preflight + Access-Control-Allow-Origin
|
|
156
|
+
// matching the configured public base URL, the browser fetch fails
|
|
157
|
+
// silently (opaque response) and the user sees "loopback failed" with
|
|
158
|
+
// no signal here.
|
|
159
|
+
const allowedOrigin = baseUrl;
|
|
160
|
+
server.on('request', (req, res) => {
|
|
161
|
+
void (async () => {
|
|
162
|
+
try {
|
|
163
|
+
// OPTIONS preflight — no body, no auth, just CORS headers.
|
|
164
|
+
if (req.method === 'OPTIONS') {
|
|
165
|
+
res.writeHead(204, {
|
|
166
|
+
'Access-Control-Allow-Origin': allowedOrigin,
|
|
167
|
+
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
168
|
+
'Access-Control-Allow-Headers': 'content-type',
|
|
169
|
+
'Access-Control-Max-Age': '60',
|
|
170
|
+
Vary: 'Origin',
|
|
171
|
+
});
|
|
172
|
+
res.end();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (req.method !== 'POST' || req.url !== '/cli-callback') {
|
|
176
|
+
res.statusCode = 404;
|
|
177
|
+
res.end('not found');
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const ct = (req.headers['content-type'] ?? '').toString();
|
|
181
|
+
if (!ct.includes('application/json')) {
|
|
182
|
+
res.statusCode = 415;
|
|
183
|
+
res.end('unsupported media type');
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const body = await readJsonBody(req);
|
|
187
|
+
// Codex NOTE — CORS header on POST response so the browser can
|
|
188
|
+
// read the JSON body under mode: 'cors'.
|
|
189
|
+
const corsHeaders = {
|
|
190
|
+
'Access-Control-Allow-Origin': allowedOrigin,
|
|
191
|
+
Vary: 'Origin',
|
|
192
|
+
};
|
|
193
|
+
if (typeof body.nonce !== 'string' || !nonceMatch(nonce, body.nonce)) {
|
|
194
|
+
res.writeHead(403, { ...corsHeaders, 'content-type': 'text/plain' });
|
|
195
|
+
res.end('nonce mismatch');
|
|
196
|
+
clearTimeout(timer);
|
|
197
|
+
settle(() => rejected?.(new Error('nonce mismatch')));
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (typeof body.apiKey !== 'string' || !KEY_RE.test(body.apiKey)) {
|
|
201
|
+
res.writeHead(422, { ...corsHeaders, 'content-type': 'text/plain' });
|
|
202
|
+
res.end('invalid apiKey');
|
|
203
|
+
clearTimeout(timer);
|
|
204
|
+
settle(() => rejected?.(new Error('invalid apiKey from callback')));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (typeof body.fingerprint !== 'string' || !/^clp_[0-9a-f]{12}$/.test(body.fingerprint)) {
|
|
208
|
+
res.writeHead(422, { ...corsHeaders, 'content-type': 'text/plain' });
|
|
209
|
+
res.end('invalid fingerprint');
|
|
210
|
+
clearTimeout(timer);
|
|
211
|
+
settle(() => rejected?.(new Error('invalid fingerprint from callback')));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (typeof body.accountEmail !== 'string') {
|
|
215
|
+
res.writeHead(422, { ...corsHeaders, 'content-type': 'text/plain' });
|
|
216
|
+
res.end('invalid accountEmail');
|
|
217
|
+
clearTimeout(timer);
|
|
218
|
+
settle(() => rejected?.(new Error('invalid accountEmail from callback')));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const cfg = {
|
|
222
|
+
schemaVersion: 1,
|
|
223
|
+
apiKey: body.apiKey,
|
|
224
|
+
fingerprint: body.fingerprint,
|
|
225
|
+
accountEmail: body.accountEmail,
|
|
226
|
+
loggedInAt: new Date().toISOString(),
|
|
227
|
+
lastUploadAt: null,
|
|
228
|
+
};
|
|
229
|
+
await writeConfig(cfg);
|
|
230
|
+
res.writeHead(200, {
|
|
231
|
+
...corsHeaders,
|
|
232
|
+
'content-type': 'application/json',
|
|
233
|
+
});
|
|
234
|
+
res.end(JSON.stringify({ ok: true, nonce }));
|
|
235
|
+
clearTimeout(timer);
|
|
236
|
+
settle(() => resolved?.({ config: cfg, port }));
|
|
237
|
+
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
res.statusCode = 500;
|
|
240
|
+
res.setHeader('Access-Control-Allow-Origin', allowedOrigin);
|
|
241
|
+
res.end(err.message ?? 'error');
|
|
242
|
+
clearTimeout(timer);
|
|
243
|
+
settle(() => rejected?.(err instanceof Error ? err : new Error(String(err))));
|
|
244
|
+
}
|
|
245
|
+
})();
|
|
246
|
+
});
|
|
247
|
+
// Print + open after the server is listening.
|
|
248
|
+
if (!opts.silent) {
|
|
249
|
+
process.stdout.write(`[autopilot] sign in here: ${authUrl}\n`);
|
|
250
|
+
process.stdout.write(` (a browser window will open; loopback callback on port ${port})\n`);
|
|
251
|
+
}
|
|
252
|
+
if (opts.openBrowser) {
|
|
253
|
+
await opts.openBrowser(authUrl);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
openInBrowser(authUrl);
|
|
257
|
+
}
|
|
258
|
+
return result;
|
|
259
|
+
}
|
|
260
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface LogoutOptions {
|
|
2
|
+
baseUrl?: string;
|
|
3
|
+
fetchImpl?: typeof fetch;
|
|
4
|
+
silent?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface LogoutResult {
|
|
7
|
+
hadConfig: boolean;
|
|
8
|
+
serverRevoked: boolean;
|
|
9
|
+
serverStatus: number | null;
|
|
10
|
+
}
|
|
11
|
+
export declare function runDashboardLogout(opts?: LogoutOptions): Promise<LogoutResult>;
|
|
12
|
+
//# sourceMappingURL=logout.d.ts.map
|