@vertaaux/cli 0.4.0 → 0.5.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 +97 -0
- package/MIGRATION.md +239 -0
- package/README.md +34 -16
- package/dist/app/interactive-app.d.ts +101 -0
- package/dist/app/interactive-app.d.ts.map +1 -0
- package/dist/app/interactive-app.js +309 -0
- package/dist/app/layout/canvas.d.ts +23 -0
- package/dist/app/layout/canvas.d.ts.map +1 -0
- package/dist/app/layout/canvas.js +36 -0
- package/dist/app/layout/footer.d.ts +31 -0
- package/dist/app/layout/footer.d.ts.map +1 -0
- package/dist/app/layout/footer.js +41 -0
- package/dist/app/layout/header.d.ts +20 -0
- package/dist/app/layout/header.d.ts.map +1 -0
- package/dist/app/layout/header.js +27 -0
- package/dist/app/menu/categories.d.ts +20 -0
- package/dist/app/menu/categories.d.ts.map +1 -0
- package/dist/app/menu/categories.js +181 -0
- package/dist/app/menu/filter.d.ts +17 -0
- package/dist/app/menu/filter.d.ts.map +1 -0
- package/dist/app/menu/filter.js +33 -0
- package/dist/app/menu/menu-view.d.ts +35 -0
- package/dist/app/menu/menu-view.d.ts.map +1 -0
- package/dist/app/menu/menu-view.js +230 -0
- package/dist/app/menu/recent.d.ts +24 -0
- package/dist/app/menu/recent.d.ts.map +1 -0
- package/dist/app/menu/recent.js +49 -0
- package/dist/app/types.d.ts +43 -0
- package/dist/app/types.d.ts.map +1 -0
- package/dist/app/types.js +7 -0
- package/dist/app/views/command-runner.d.ts +36 -0
- package/dist/app/views/command-runner.d.ts.map +1 -0
- package/dist/app/views/command-runner.js +372 -0
- package/dist/app/views/help-overlay.d.ts +21 -0
- package/dist/app/views/help-overlay.d.ts.map +1 -0
- package/dist/app/views/help-overlay.js +45 -0
- package/dist/auth/ci-token.d.ts +8 -2
- package/dist/auth/ci-token.d.ts.map +1 -1
- package/dist/auth/ci-token.js +15 -30
- package/dist/auth/device-flow.d.ts +2 -1
- package/dist/auth/device-flow.d.ts.map +1 -1
- package/dist/auth/device-flow.js +13 -10
- package/dist/auth/token-store.d.ts.map +1 -1
- package/dist/auth/token-store.js +12 -2
- package/dist/baseline/diff.d.ts +2 -2
- package/dist/baseline/diff.d.ts.map +1 -1
- package/dist/baseline/diff.js +15 -34
- package/dist/commands/a11y.d.ts +9 -0
- package/dist/commands/a11y.d.ts.map +1 -0
- package/dist/commands/a11y.js +76 -0
- package/dist/commands/audit/artifacts.d.ts +27 -0
- package/dist/commands/audit/artifacts.d.ts.map +1 -0
- package/dist/commands/audit/artifacts.js +158 -0
- package/dist/commands/audit/ci-detection.d.ts +18 -0
- package/dist/commands/audit/ci-detection.d.ts.map +1 -0
- package/dist/commands/audit/ci-detection.js +71 -0
- package/dist/commands/audit/explain.d.ts +11 -0
- package/dist/commands/audit/explain.d.ts.map +1 -0
- package/dist/commands/audit/explain.js +45 -0
- package/dist/commands/audit/filters.d.ts +17 -0
- package/dist/commands/audit/filters.d.ts.map +1 -0
- package/dist/commands/audit/filters.js +40 -0
- package/dist/commands/audit/index.d.ts +18 -0
- package/dist/commands/audit/index.d.ts.map +1 -0
- package/dist/commands/audit/index.js +564 -0
- package/dist/commands/audit/output.d.ts +32 -0
- package/dist/commands/audit/output.d.ts.map +1 -0
- package/dist/commands/audit/output.js +130 -0
- package/dist/commands/audit/policy.d.ts +19 -0
- package/dist/commands/audit/policy.d.ts.map +1 -0
- package/dist/commands/audit/policy.js +102 -0
- package/dist/commands/audit/scoring.d.ts +23 -0
- package/dist/commands/audit/scoring.d.ts.map +1 -0
- package/dist/commands/audit/scoring.js +70 -0
- package/dist/commands/audit/types.d.ts +88 -0
- package/dist/commands/audit/types.d.ts.map +1 -0
- package/dist/commands/audit/types.js +8 -0
- package/dist/commands/audit.d.ts +2 -60
- package/dist/commands/audit.d.ts.map +1 -1
- package/dist/commands/audit.js +2 -1097
- package/dist/commands/baseline.d.ts +1 -0
- package/dist/commands/baseline.d.ts.map +1 -1
- package/dist/commands/baseline.js +205 -121
- package/dist/commands/comment.d.ts +22 -0
- package/dist/commands/comment.d.ts.map +1 -1
- package/dist/commands/comment.js +122 -58
- package/dist/commands/compare.d.ts +17 -0
- package/dist/commands/compare.d.ts.map +1 -1
- package/dist/commands/compare.js +287 -180
- package/dist/commands/diff.d.ts +5 -0
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +168 -141
- package/dist/commands/doc.d.ts +10 -0
- package/dist/commands/doc.d.ts.map +1 -1
- package/dist/commands/doc.js +134 -76
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +164 -17
- package/dist/commands/download.d.ts +10 -0
- package/dist/commands/download.d.ts.map +1 -1
- package/dist/commands/download.js +169 -112
- package/dist/commands/explain.d.ts +5 -0
- package/dist/commands/explain.d.ts.map +1 -1
- package/dist/commands/explain.js +241 -155
- package/dist/commands/fix-all.d.ts +25 -0
- package/dist/commands/fix-all.d.ts.map +1 -0
- package/dist/commands/fix-all.js +206 -0
- package/dist/commands/fix-plan.d.ts +9 -0
- package/dist/commands/fix-plan.d.ts.map +1 -1
- package/dist/commands/fix-plan.js +152 -89
- package/dist/commands/fix.d.ts +17 -0
- package/dist/commands/fix.d.ts.map +1 -0
- package/dist/commands/fix.js +111 -0
- package/dist/commands/init.d.ts +11 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +94 -42
- package/dist/commands/login.d.ts +18 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +263 -92
- package/dist/commands/patch-review.d.ts +11 -0
- package/dist/commands/patch-review.d.ts.map +1 -1
- package/dist/commands/patch-review.js +159 -97
- package/dist/commands/policy.d.ts +31 -0
- package/dist/commands/policy.d.ts.map +1 -1
- package/dist/commands/policy.js +269 -124
- package/dist/commands/release-notes.d.ts +10 -0
- package/dist/commands/release-notes.d.ts.map +1 -1
- package/dist/commands/release-notes.js +127 -73
- package/dist/commands/scan.d.ts +13 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +133 -0
- package/dist/commands/status.d.ts +9 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +81 -0
- package/dist/commands/suggest.d.ts +10 -0
- package/dist/commands/suggest.d.ts.map +1 -1
- package/dist/commands/suggest.js +153 -82
- package/dist/commands/triage.d.ts +35 -0
- package/dist/commands/triage.d.ts.map +1 -1
- package/dist/commands/triage.js +206 -81
- package/dist/commands/upload.d.ts +9 -0
- package/dist/commands/upload.d.ts.map +1 -1
- package/dist/commands/upload.js +140 -101
- package/dist/commands/verify.d.ts +13 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +118 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +125 -990
- package/dist/interactive/fix-wizard.d.ts +3 -0
- package/dist/interactive/fix-wizard.d.ts.map +1 -1
- package/dist/interactive/fix-wizard.js +130 -112
- package/dist/interactive/init-wizard.d.ts +3 -1
- package/dist/interactive/init-wizard.d.ts.map +1 -1
- package/dist/interactive/init-wizard.js +207 -138
- package/dist/interactive/prompts.d.ts +7 -3
- package/dist/interactive/prompts.d.ts.map +1 -1
- package/dist/interactive/prompts.js +44 -23
- package/dist/output/envelope.d.ts +2 -0
- package/dist/output/envelope.d.ts.map +1 -1
- package/dist/output/envelope.js +18 -2
- package/dist/output/factory.d.ts +2 -1
- package/dist/output/factory.d.ts.map +1 -1
- package/dist/output/html.d.ts +2 -1
- package/dist/output/html.d.ts.map +1 -1
- package/dist/output/html.js +3 -2
- package/dist/output/human.d.ts +2 -1
- package/dist/output/human.d.ts.map +1 -1
- package/dist/output/human.js +3 -2
- package/dist/output/json.d.ts +2 -1
- package/dist/output/json.d.ts.map +1 -1
- package/dist/output/junit.d.ts +2 -1
- package/dist/output/junit.d.ts.map +1 -1
- package/dist/output/sarif.d.ts +2 -1
- package/dist/output/sarif.d.ts.map +1 -1
- package/dist/types.d.ts +74 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/ui/banner.d.ts +34 -0
- package/dist/ui/banner.d.ts.map +1 -1
- package/dist/ui/banner.js +97 -5
- package/dist/ui/diagnostics.d.ts +9 -4
- package/dist/ui/diagnostics.d.ts.map +1 -1
- package/dist/ui/diagnostics.js +32 -82
- package/dist/ui/strings.d.ts +373 -0
- package/dist/ui/strings.d.ts.map +1 -0
- package/dist/ui/strings.js +499 -0
- package/dist/ui/table.d.ts +0 -2
- package/dist/ui/table.d.ts.map +1 -1
- package/dist/ui/table.js +3 -4
- package/dist/utils/api-client.d.ts +46 -0
- package/dist/utils/api-client.d.ts.map +1 -0
- package/dist/utils/api-client.js +170 -0
- package/dist/utils/client.d.ts +29 -18
- package/dist/utils/client.d.ts.map +1 -1
- package/dist/utils/client.js +102 -12
- package/dist/utils/formatters.d.ts +38 -0
- package/dist/utils/formatters.d.ts.map +1 -0
- package/dist/utils/formatters.js +277 -0
- package/dist/utils/url-classify.d.ts.map +1 -1
- package/dist/utils/url-classify.js +24 -3
- package/node_modules/@vertaaux/tui/dist/index.cjs +713 -20
- package/node_modules/@vertaaux/tui/dist/index.cjs.map +1 -1
- package/node_modules/@vertaaux/tui/dist/index.d.cts +361 -4
- package/node_modules/@vertaaux/tui/dist/index.d.ts +361 -4
- package/node_modules/@vertaaux/tui/dist/index.js +689 -21
- package/node_modules/@vertaaux/tui/dist/index.js.map +1 -1
- package/package.json +13 -5
- package/dist/commands/client.d.ts +0 -14
- package/dist/commands/client.d.ts.map +0 -1
- package/dist/commands/client.js +0 -362
- package/dist/commands/drift.d.ts +0 -15
- package/dist/commands/drift.d.ts.map +0 -1
- package/dist/commands/drift.js +0 -309
- package/dist/commands/protect.d.ts +0 -16
- package/dist/commands/protect.d.ts.map +0 -1
- package/dist/commands/protect.js +0 -323
- package/dist/commands/report.d.ts +0 -15
- package/dist/commands/report.d.ts.map +0 -1
- package/dist/commands/report.js +0 -214
- package/dist/policy/sync.d.ts +0 -67
- package/dist/policy/sync.d.ts.map +0 -1
- package/dist/policy/sync.js +0 -147
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommandRunnerView — Executes a CLI command within the InteractiveApp canvas.
|
|
3
|
+
*
|
|
4
|
+
* Lifecycle:
|
|
5
|
+
* 1. "collecting" — prompts for required args ON the alt screen using raw-mode
|
|
6
|
+
* keyboard input (same system as MenuView). No stdin mode transitions.
|
|
7
|
+
* 2. "running" — command runs INSIDE the app layout. A renderer override
|
|
8
|
+
* captures step states so this view can render them in the canvas.
|
|
9
|
+
* Header and footer stay visible throughout.
|
|
10
|
+
* 3. "done"/"error" — shows result summary in canvas, M returns to menu.
|
|
11
|
+
*
|
|
12
|
+
* Each command's handler is called directly (no program.parse()).
|
|
13
|
+
*/
|
|
14
|
+
import { dim, bold, colorize, brand, renderError, setRendererOverride, setKeyboardOverrideActive, renderStepList, getTerminalWidth, getTerminalHeight, } from "@vertaaux/tui";
|
|
15
|
+
import { setOutputBuffer } from "../../output/envelope.js";
|
|
16
|
+
// Command handler imports
|
|
17
|
+
import { handleAudit } from "../../commands/audit/index.js";
|
|
18
|
+
import { handleScan } from "../../commands/scan.js";
|
|
19
|
+
import { handleA11y } from "../../commands/a11y.js";
|
|
20
|
+
import { handleStatus } from "../../commands/status.js";
|
|
21
|
+
import { handleDiff } from "../../commands/diff.js";
|
|
22
|
+
import { handleCompare } from "../../commands/compare.js";
|
|
23
|
+
import { handleTriage } from "../../commands/triage.js";
|
|
24
|
+
import { handleExplain } from "../../commands/explain.js";
|
|
25
|
+
import { handleSuggest } from "../../commands/suggest.js";
|
|
26
|
+
import { handleFix } from "../../commands/fix.js";
|
|
27
|
+
import { handleFixAll } from "../../commands/fix-all.js";
|
|
28
|
+
import { handleFixPlan } from "../../commands/fix-plan.js";
|
|
29
|
+
import { handlePatchReview } from "../../commands/patch-review.js";
|
|
30
|
+
import { handleDoc } from "../../commands/doc.js";
|
|
31
|
+
import { handleComment } from "../../commands/comment.js";
|
|
32
|
+
import { handleReleaseNotes } from "../../commands/release-notes.js";
|
|
33
|
+
import { handleInit } from "../../commands/init.js";
|
|
34
|
+
import { handleBaseline } from "../../commands/baseline.js";
|
|
35
|
+
import { handleUpload } from "../../commands/upload.js";
|
|
36
|
+
import { handleDownload } from "../../commands/download.js";
|
|
37
|
+
import { handleVerify } from "../../commands/verify.js";
|
|
38
|
+
import { handleLogin, handleLogout, handleWhoami } from "../../commands/login.js";
|
|
39
|
+
import { handleDoctor } from "../../commands/doctor.js";
|
|
40
|
+
import { handlePolicy } from "../../commands/policy.js";
|
|
41
|
+
/**
|
|
42
|
+
* Get the argument definitions for a command.
|
|
43
|
+
*/
|
|
44
|
+
function getArgDefs(cmd) {
|
|
45
|
+
switch (cmd) {
|
|
46
|
+
case "audit":
|
|
47
|
+
case "scan":
|
|
48
|
+
case "a11y":
|
|
49
|
+
return [{ label: "Target URL", key: "url" }];
|
|
50
|
+
case "status":
|
|
51
|
+
return [{ label: "Job ID", key: "jobId" }];
|
|
52
|
+
case "fix":
|
|
53
|
+
return [{ label: "Job ID to fix", key: "jobId" }];
|
|
54
|
+
case "suggest":
|
|
55
|
+
return [{ label: "Describe the fix you need", key: "intent" }];
|
|
56
|
+
case "fix-all":
|
|
57
|
+
return [
|
|
58
|
+
{ label: "Job ID", key: "jobId" },
|
|
59
|
+
{ label: "File path or content", key: "fileContent" },
|
|
60
|
+
];
|
|
61
|
+
case "download":
|
|
62
|
+
return [{ label: "Job ID to download", key: "jobId" }];
|
|
63
|
+
default:
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A renderer that captures state instead of writing to the terminal.
|
|
69
|
+
* The CommandRunnerView reads from it in render().
|
|
70
|
+
*/
|
|
71
|
+
class CanvasRenderer {
|
|
72
|
+
stepStates = [];
|
|
73
|
+
result = null;
|
|
74
|
+
finished = false;
|
|
75
|
+
update(state) {
|
|
76
|
+
if (state.stepStates) {
|
|
77
|
+
this.stepStates = [...state.stepStates];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
finish(result) {
|
|
81
|
+
this.result = result;
|
|
82
|
+
this.finished = true;
|
|
83
|
+
}
|
|
84
|
+
dispose() { }
|
|
85
|
+
suspend() { }
|
|
86
|
+
resume() { }
|
|
87
|
+
}
|
|
88
|
+
export class CommandRunnerView {
|
|
89
|
+
status;
|
|
90
|
+
statusText = "";
|
|
91
|
+
commandValue;
|
|
92
|
+
app;
|
|
93
|
+
onReturn;
|
|
94
|
+
// Arg collection state
|
|
95
|
+
argDefs;
|
|
96
|
+
collectedArgs = {};
|
|
97
|
+
currentArgIndex = 0;
|
|
98
|
+
inputBuffer = "";
|
|
99
|
+
// Resolve function for the arg collection promise
|
|
100
|
+
resolveArgCollection = null;
|
|
101
|
+
// Canvas renderer for capturing step states
|
|
102
|
+
canvasRenderer = new CanvasRenderer();
|
|
103
|
+
// Captured command output (from writeOutput during execution)
|
|
104
|
+
outputBuffer = [];
|
|
105
|
+
// Scroll position for viewing results
|
|
106
|
+
scrollOffset = 0;
|
|
107
|
+
constructor(commandValue, app, onReturn) {
|
|
108
|
+
this.commandValue = commandValue;
|
|
109
|
+
this.app = app;
|
|
110
|
+
this.onReturn = onReturn;
|
|
111
|
+
this.argDefs = getArgDefs(commandValue);
|
|
112
|
+
this.status = this.argDefs.length > 0 ? "collecting" : "running";
|
|
113
|
+
}
|
|
114
|
+
render() {
|
|
115
|
+
const lines = [];
|
|
116
|
+
switch (this.status) {
|
|
117
|
+
case "collecting": {
|
|
118
|
+
lines.push(bold(this.commandValue));
|
|
119
|
+
lines.push("");
|
|
120
|
+
for (let i = 0; i < this.currentArgIndex; i++) {
|
|
121
|
+
const def = this.argDefs[i];
|
|
122
|
+
lines.push(dim(` ${def.label}: `) + this.collectedArgs[def.key]);
|
|
123
|
+
}
|
|
124
|
+
const currentDef = this.argDefs[this.currentArgIndex];
|
|
125
|
+
if (currentDef) {
|
|
126
|
+
lines.push(colorize(` ${currentDef.label}: `, brand.lime) +
|
|
127
|
+
this.inputBuffer +
|
|
128
|
+
colorize("█", brand.lime));
|
|
129
|
+
}
|
|
130
|
+
lines.push("");
|
|
131
|
+
lines.push(dim("Type your input, then press Enter to confirm"));
|
|
132
|
+
lines.push(dim("Press Escape to cancel"));
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
case "running": {
|
|
136
|
+
// Show step-list from the canvas renderer
|
|
137
|
+
if (this.canvasRenderer.stepStates.length > 0) {
|
|
138
|
+
const stepList = renderStepList(this.canvasRenderer.stepStates, this.app.getFrameIndex(), getTerminalWidth());
|
|
139
|
+
lines.push(stepList);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
lines.push(dim(`Running ${bold(this.commandValue)}...`));
|
|
143
|
+
}
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
case "done":
|
|
147
|
+
case "error": {
|
|
148
|
+
const content = this.status === "error" && this.outputBuffer.length === 0
|
|
149
|
+
? [renderError({ message: this.statusText || "Command failed", suggestion: "vertaa doctor" })]
|
|
150
|
+
: this.outputBuffer.length > 0
|
|
151
|
+
? [...this.outputBuffer]
|
|
152
|
+
: [colorize("Command completed.", brand.lime)];
|
|
153
|
+
// Scrollable view: show a window of lines based on scrollOffset
|
|
154
|
+
// Reserve 2 lines for footer hint
|
|
155
|
+
const availableHeight = Math.max(3, getTerminalHeight() - 12);
|
|
156
|
+
const totalLines = content.length;
|
|
157
|
+
const maxScroll = Math.max(0, totalLines - availableHeight);
|
|
158
|
+
this.scrollOffset = Math.min(this.scrollOffset, maxScroll);
|
|
159
|
+
const visible = content.slice(this.scrollOffset, this.scrollOffset + availableHeight);
|
|
160
|
+
lines.push(...visible);
|
|
161
|
+
// Scroll indicator
|
|
162
|
+
if (totalLines > availableHeight) {
|
|
163
|
+
const pct = maxScroll > 0 ? Math.round((this.scrollOffset / maxScroll) * 100) : 100;
|
|
164
|
+
lines.push("");
|
|
165
|
+
lines.push(dim(`↑↓ scroll (${pct}%) | M menu | Q quit`));
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
lines.push("");
|
|
169
|
+
lines.push(dim("M menu | Q quit"));
|
|
170
|
+
}
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return lines.join("\n");
|
|
175
|
+
}
|
|
176
|
+
handleKey(key, ctrl, _meta) {
|
|
177
|
+
if (this.status === "collecting") {
|
|
178
|
+
if (ctrl && key === "\x03")
|
|
179
|
+
return false;
|
|
180
|
+
if (key === "\x1b") {
|
|
181
|
+
this.onReturn();
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
if (key === "\r" || key === "\n") {
|
|
185
|
+
const currentDef = this.argDefs[this.currentArgIndex];
|
|
186
|
+
if (currentDef && this.inputBuffer.trim().length > 0) {
|
|
187
|
+
this.collectedArgs[currentDef.key] = this.inputBuffer.trim();
|
|
188
|
+
this.inputBuffer = "";
|
|
189
|
+
this.currentArgIndex++;
|
|
190
|
+
if (this.currentArgIndex >= this.argDefs.length) {
|
|
191
|
+
if (this.resolveArgCollection) {
|
|
192
|
+
this.resolveArgCollection();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
if (key === "\x7f" || key === "\b") {
|
|
199
|
+
this.inputBuffer = this.inputBuffer.slice(0, -1);
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
if (key.length === 1 && key >= " ") {
|
|
203
|
+
this.inputBuffer += key;
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
// Done/error state — handle navigation and scroll keys
|
|
209
|
+
if (this.status === "done" || this.status === "error") {
|
|
210
|
+
if (key === "m" || key === "M" || key === "\r") {
|
|
211
|
+
this.onReturn();
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
if (key === "\x1b") {
|
|
215
|
+
// Escape — but not arrow key sequences
|
|
216
|
+
this.onReturn();
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
if (key === "q" || key === "Q") {
|
|
220
|
+
process.exit(0);
|
|
221
|
+
}
|
|
222
|
+
// Arrow up / k
|
|
223
|
+
if (key === "\x1b[A" || key === "k") {
|
|
224
|
+
this.scrollOffset = Math.max(0, this.scrollOffset - 1);
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
// Arrow down / j
|
|
228
|
+
if (key === "\x1b[B" || key === "j") {
|
|
229
|
+
this.scrollOffset++;
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
// Page up
|
|
233
|
+
if (key === "\x1b[5~") {
|
|
234
|
+
this.scrollOffset = Math.max(0, this.scrollOffset - 10);
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
// Page down
|
|
238
|
+
if (key === "\x1b[6~") {
|
|
239
|
+
this.scrollOffset += 10;
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
// Home
|
|
243
|
+
if (key === "\x1b[H" || key === "g") {
|
|
244
|
+
this.scrollOffset = 0;
|
|
245
|
+
return true;
|
|
246
|
+
}
|
|
247
|
+
// End
|
|
248
|
+
if (key === "\x1b[F" || key === "G") {
|
|
249
|
+
this.scrollOffset = Infinity; // clamped in render()
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
async onMount() {
|
|
257
|
+
// If command needs args, wait for collection
|
|
258
|
+
if (this.argDefs.length > 0) {
|
|
259
|
+
await new Promise((resolve) => {
|
|
260
|
+
this.resolveArgCollection = resolve;
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
// Set overrides so command handlers use our canvas renderer,
|
|
264
|
+
// don't steal stdin, and buffer their output instead of writing to stderr.
|
|
265
|
+
this.status = "running";
|
|
266
|
+
setRendererOverride(this.canvasRenderer);
|
|
267
|
+
setKeyboardOverrideActive(true);
|
|
268
|
+
setOutputBuffer(this.outputBuffer);
|
|
269
|
+
try {
|
|
270
|
+
await this.executeCommand();
|
|
271
|
+
this.status = "done";
|
|
272
|
+
}
|
|
273
|
+
catch (err) {
|
|
274
|
+
this.status = "error";
|
|
275
|
+
this.statusText = err instanceof Error ? err.message : String(err);
|
|
276
|
+
}
|
|
277
|
+
// Restore all overrides
|
|
278
|
+
setOutputBuffer(null);
|
|
279
|
+
setRendererOverride(null);
|
|
280
|
+
setKeyboardOverrideActive(false);
|
|
281
|
+
}
|
|
282
|
+
// ── Private ──────────────────────────────────────────────────
|
|
283
|
+
async executeCommand() {
|
|
284
|
+
const cmd = this.commandValue;
|
|
285
|
+
const args = this.collectedArgs;
|
|
286
|
+
switch (cmd) {
|
|
287
|
+
case "audit":
|
|
288
|
+
await handleAudit(args.url, {}, {});
|
|
289
|
+
break;
|
|
290
|
+
case "scan":
|
|
291
|
+
await handleScan(args.url, {});
|
|
292
|
+
break;
|
|
293
|
+
case "a11y":
|
|
294
|
+
await handleA11y(args.url, {});
|
|
295
|
+
break;
|
|
296
|
+
case "status":
|
|
297
|
+
await handleStatus(args.jobId, {});
|
|
298
|
+
break;
|
|
299
|
+
case "fix":
|
|
300
|
+
await handleFix(args.jobId, {});
|
|
301
|
+
break;
|
|
302
|
+
case "download":
|
|
303
|
+
await handleDownload(args.jobId, {});
|
|
304
|
+
break;
|
|
305
|
+
case "suggest":
|
|
306
|
+
await handleSuggest({ intent: args.intent });
|
|
307
|
+
break;
|
|
308
|
+
case "fix-all":
|
|
309
|
+
await handleFixAll({
|
|
310
|
+
jobId: args.jobId,
|
|
311
|
+
fileContent: args.fileContent,
|
|
312
|
+
});
|
|
313
|
+
break;
|
|
314
|
+
case "diff":
|
|
315
|
+
await handleDiff({});
|
|
316
|
+
break;
|
|
317
|
+
case "compare":
|
|
318
|
+
await handleCompare({});
|
|
319
|
+
break;
|
|
320
|
+
case "triage":
|
|
321
|
+
await handleTriage({});
|
|
322
|
+
break;
|
|
323
|
+
case "explain":
|
|
324
|
+
await handleExplain({});
|
|
325
|
+
break;
|
|
326
|
+
case "fix-plan":
|
|
327
|
+
await handleFixPlan({});
|
|
328
|
+
break;
|
|
329
|
+
case "patch-review":
|
|
330
|
+
await handlePatchReview({});
|
|
331
|
+
break;
|
|
332
|
+
case "doc":
|
|
333
|
+
await handleDoc({});
|
|
334
|
+
break;
|
|
335
|
+
case "comment":
|
|
336
|
+
await handleComment({});
|
|
337
|
+
break;
|
|
338
|
+
case "release-notes":
|
|
339
|
+
await handleReleaseNotes({});
|
|
340
|
+
break;
|
|
341
|
+
case "init":
|
|
342
|
+
await handleInit({});
|
|
343
|
+
break;
|
|
344
|
+
case "baseline":
|
|
345
|
+
await handleBaseline(undefined, {});
|
|
346
|
+
break;
|
|
347
|
+
case "upload":
|
|
348
|
+
await handleUpload(undefined, {});
|
|
349
|
+
break;
|
|
350
|
+
case "verify":
|
|
351
|
+
await handleVerify({});
|
|
352
|
+
break;
|
|
353
|
+
case "login":
|
|
354
|
+
await handleLogin({});
|
|
355
|
+
break;
|
|
356
|
+
case "logout":
|
|
357
|
+
await handleLogout();
|
|
358
|
+
break;
|
|
359
|
+
case "whoami":
|
|
360
|
+
await handleWhoami({});
|
|
361
|
+
break;
|
|
362
|
+
case "doctor":
|
|
363
|
+
await handleDoctor({});
|
|
364
|
+
break;
|
|
365
|
+
case "policy":
|
|
366
|
+
await handlePolicy("show", {});
|
|
367
|
+
break;
|
|
368
|
+
default:
|
|
369
|
+
throw new Error(`Unknown command: ${cmd}`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HelpOverlayView — Keyboard shortcuts overlay implementing CommandView.
|
|
3
|
+
*
|
|
4
|
+
* Renders a box with context-specific keyboard shortcuts. Pressing H or Esc
|
|
5
|
+
* dismisses the overlay via the onDismiss callback.
|
|
6
|
+
*/
|
|
7
|
+
import type { CommandView } from "../types.js";
|
|
8
|
+
/**
|
|
9
|
+
* HelpOverlayView implements CommandView for the keyboard shortcuts overlay.
|
|
10
|
+
*
|
|
11
|
+
* @param onDismiss - Callback invoked when user dismisses the overlay (H or Esc)
|
|
12
|
+
*/
|
|
13
|
+
export declare class HelpOverlayView implements CommandView {
|
|
14
|
+
private readonly onDismiss;
|
|
15
|
+
constructor(onDismiss: () => void);
|
|
16
|
+
/** Render the help overlay as a box */
|
|
17
|
+
render(): string;
|
|
18
|
+
/** Handle H and Esc to dismiss the overlay */
|
|
19
|
+
handleKey(key: string, _ctrl: boolean, _meta: boolean): boolean;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=help-overlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help-overlay.d.ts","sourceRoot":"","sources":["../../../src/app/views/help-overlay.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAY/C;;;;GAIG;AACH,qBAAa,eAAgB,YAAW,WAAW;IACjD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAa;gBAE3B,SAAS,EAAE,MAAM,IAAI;IAIjC,uCAAuC;IACvC,MAAM,IAAI,MAAM;IAehB,8CAA8C;IAC9C,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO;CAOhE"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HelpOverlayView — Keyboard shortcuts overlay implementing CommandView.
|
|
3
|
+
*
|
|
4
|
+
* Renders a box with context-specific keyboard shortcuts. Pressing H or Esc
|
|
5
|
+
* dismisses the overlay via the onDismiss callback.
|
|
6
|
+
*/
|
|
7
|
+
import { box, bold, dim } from "@vertaaux/tui";
|
|
8
|
+
/** Default shortcuts shown in any context */
|
|
9
|
+
const DEFAULT_SHORTCUTS = [
|
|
10
|
+
["↑ / ↓", "Navigate menu items"],
|
|
11
|
+
["Enter", "Select / confirm"],
|
|
12
|
+
["/", "Start search (or type any character)"],
|
|
13
|
+
["Esc", "Clear search / go back"],
|
|
14
|
+
["H", "Toggle this help overlay"],
|
|
15
|
+
["Ctrl+C", "Exit vertaa"],
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* HelpOverlayView implements CommandView for the keyboard shortcuts overlay.
|
|
19
|
+
*
|
|
20
|
+
* @param onDismiss - Callback invoked when user dismisses the overlay (H or Esc)
|
|
21
|
+
*/
|
|
22
|
+
export class HelpOverlayView {
|
|
23
|
+
onDismiss;
|
|
24
|
+
constructor(onDismiss) {
|
|
25
|
+
this.onDismiss = onDismiss;
|
|
26
|
+
}
|
|
27
|
+
/** Render the help overlay as a box */
|
|
28
|
+
render() {
|
|
29
|
+
const rows = DEFAULT_SHORTCUTS.map(([key, description]) => ` ${bold(key.padEnd(12))} ${dim(description)}`);
|
|
30
|
+
const content = [
|
|
31
|
+
bold("Keyboard Shortcuts"),
|
|
32
|
+
dim("─".repeat(36)),
|
|
33
|
+
...rows,
|
|
34
|
+
].join("\n");
|
|
35
|
+
return box(content, { padding: 1 });
|
|
36
|
+
}
|
|
37
|
+
/** Handle H and Esc to dismiss the overlay */
|
|
38
|
+
handleKey(key, _ctrl, _meta) {
|
|
39
|
+
if (key === "h" || key === "H" || key === "\x1b") {
|
|
40
|
+
this.onDismiss();
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
package/dist/auth/ci-token.d.ts
CHANGED
|
@@ -38,16 +38,22 @@ interface TokenValidationResponse {
|
|
|
38
38
|
/**
|
|
39
39
|
* Validate a CI token against the API.
|
|
40
40
|
*
|
|
41
|
+
* Uses apiRequest() from client.ts with the token as explicit API key,
|
|
42
|
+
* so that auth header construction stays centralized in client.ts.
|
|
43
|
+
*
|
|
41
44
|
* @param token - Token to validate
|
|
42
|
-
* @param apiBase - API base URL (optional)
|
|
45
|
+
* @param apiBase - API base URL (optional, resolved via resolveApiBase)
|
|
43
46
|
* @returns true if token is valid
|
|
44
47
|
*/
|
|
45
48
|
export declare function validateCIToken(token: string, apiBase?: string): Promise<boolean>;
|
|
46
49
|
/**
|
|
47
50
|
* Get token info from the API.
|
|
48
51
|
*
|
|
52
|
+
* Uses apiRequest() from client.ts with the token as explicit API key,
|
|
53
|
+
* so that auth header construction stays centralized in client.ts.
|
|
54
|
+
*
|
|
49
55
|
* @param token - Token to check
|
|
50
|
-
* @param apiBase - API base URL (optional)
|
|
56
|
+
* @param apiBase - API base URL (optional, resolved via resolveApiBase)
|
|
51
57
|
* @returns Token info or null if invalid/error
|
|
52
58
|
*/
|
|
53
59
|
export declare function getTokenInfo(token: string, apiBase?: string): Promise<TokenValidationResponse | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ci-token.d.ts","sourceRoot":"","sources":["../../src/auth/ci-token.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"ci-token.d.ts","sourceRoot":"","sources":["../../src/auth/ci-token.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH;;;;;;GAMG;AACH,wBAAgB,UAAU,IAAI,MAAM,GAAG,IAAI,CAQ1C;AAED;;GAEG;AACH,UAAU,uBAAuB;IAC/B,6BAA6B;IAC7B,KAAK,EAAE,OAAO,CAAC;IACf,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iBAAiB;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wBAAwB;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,8BAA8B;IAC9B,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,gCAAgC;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAclB;AAED;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAYzC"}
|
package/dist/auth/ci-token.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* CI tokens are long-lived API keys used in CI/CD pipelines.
|
|
5
5
|
* They can be passed via environment variables or --token flag.
|
|
6
6
|
*/
|
|
7
|
+
import { apiRequest, resolveApiBase } from "../utils/client.js";
|
|
7
8
|
/**
|
|
8
9
|
* Environment variable names for CI tokens.
|
|
9
10
|
* Checked in order of preference.
|
|
@@ -25,30 +26,20 @@ export function getCIToken() {
|
|
|
25
26
|
}
|
|
26
27
|
return null;
|
|
27
28
|
}
|
|
28
|
-
/**
|
|
29
|
-
* Default API base URL.
|
|
30
|
-
*/
|
|
31
|
-
const DEFAULT_API_BASE = "https://vertaaux.ai/v1";
|
|
32
29
|
/**
|
|
33
30
|
* Validate a CI token against the API.
|
|
34
31
|
*
|
|
32
|
+
* Uses apiRequest() from client.ts with the token as explicit API key,
|
|
33
|
+
* so that auth header construction stays centralized in client.ts.
|
|
34
|
+
*
|
|
35
35
|
* @param token - Token to validate
|
|
36
|
-
* @param apiBase - API base URL (optional)
|
|
36
|
+
* @param apiBase - API base URL (optional, resolved via resolveApiBase)
|
|
37
37
|
* @returns true if token is valid
|
|
38
38
|
*/
|
|
39
|
-
export async function validateCIToken(token, apiBase
|
|
39
|
+
export async function validateCIToken(token, apiBase) {
|
|
40
40
|
try {
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
headers: {
|
|
44
|
-
"X-API-Key": token,
|
|
45
|
-
"Content-Type": "application/json",
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
if (!response.ok) {
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
const data = (await response.json());
|
|
41
|
+
const base = resolveApiBase(apiBase);
|
|
42
|
+
const data = await apiRequest(base, "/auth/validate", { method: "GET" }, token);
|
|
52
43
|
return data.valid === true;
|
|
53
44
|
}
|
|
54
45
|
catch {
|
|
@@ -59,23 +50,17 @@ export async function validateCIToken(token, apiBase = DEFAULT_API_BASE) {
|
|
|
59
50
|
/**
|
|
60
51
|
* Get token info from the API.
|
|
61
52
|
*
|
|
53
|
+
* Uses apiRequest() from client.ts with the token as explicit API key,
|
|
54
|
+
* so that auth header construction stays centralized in client.ts.
|
|
55
|
+
*
|
|
62
56
|
* @param token - Token to check
|
|
63
|
-
* @param apiBase - API base URL (optional)
|
|
57
|
+
* @param apiBase - API base URL (optional, resolved via resolveApiBase)
|
|
64
58
|
* @returns Token info or null if invalid/error
|
|
65
59
|
*/
|
|
66
|
-
export async function getTokenInfo(token, apiBase
|
|
60
|
+
export async function getTokenInfo(token, apiBase) {
|
|
67
61
|
try {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
headers: {
|
|
71
|
-
"X-API-Key": token,
|
|
72
|
-
"Content-Type": "application/json",
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
if (!response.ok) {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
return response.json();
|
|
62
|
+
const base = resolveApiBase(apiBase);
|
|
63
|
+
return await apiRequest(base, "/auth/validate", { method: "GET" }, token);
|
|
79
64
|
}
|
|
80
65
|
catch {
|
|
81
66
|
return null;
|
|
@@ -59,8 +59,9 @@ export interface DeviceFlowResult {
|
|
|
59
59
|
*
|
|
60
60
|
* @param clientId - OAuth client ID for the CLI
|
|
61
61
|
* @param authBase - Base URL for auth endpoints (default: https://vertaaux.ai)
|
|
62
|
+
* @param onMessage - Optional callback for displaying messages (defaults to writeOutput)
|
|
62
63
|
* @returns Device flow result with tokens
|
|
63
64
|
* @throws Error if authorization fails or times out
|
|
64
65
|
*/
|
|
65
|
-
export declare function startDeviceFlow(clientId: string, authBase?: string): Promise<DeviceFlowResult>;
|
|
66
|
+
export declare function startDeviceFlow(clientId: string, authBase?: string, onMessage?: (msg: string) => void): Promise<DeviceFlowResult>;
|
|
66
67
|
//# sourceMappingURL=device-flow.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device-flow.d.ts","sourceRoot":"","sources":["../../src/auth/device-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"device-flow.d.ts","sourceRoot":"","sources":["../../src/auth/device-flow.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,gBAAgB,EAAE,MAAM,CAAC;IACzB,oDAAoD;IACpD,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,8CAA8C;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,mBAAmB;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oBAAoB;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iCAAiC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;CACnB;AAmCD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,GAAE,MAA0B,EACpC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAChC,OAAO,CAAC,gBAAgB,CAAC,CA+B3B"}
|
package/dist/auth/device-flow.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* @see https://datatracker.ietf.org/doc/html/rfc8628
|
|
8
8
|
*/
|
|
9
9
|
import { createSpinner, succeedSpinner } from "../ui/spinner.js";
|
|
10
|
+
import { writeOutput } from "../output/envelope.js";
|
|
10
11
|
/**
|
|
11
12
|
* Format remaining time as MM:SS.
|
|
12
13
|
*/
|
|
@@ -33,24 +34,26 @@ const DEFAULT_TIMEOUT_SECONDS = 300;
|
|
|
33
34
|
*
|
|
34
35
|
* @param clientId - OAuth client ID for the CLI
|
|
35
36
|
* @param authBase - Base URL for auth endpoints (default: https://vertaaux.ai)
|
|
37
|
+
* @param onMessage - Optional callback for displaying messages (defaults to writeOutput)
|
|
36
38
|
* @returns Device flow result with tokens
|
|
37
39
|
* @throws Error if authorization fails or times out
|
|
38
40
|
*/
|
|
39
|
-
export async function startDeviceFlow(clientId, authBase = DEFAULT_AUTH_BASE) {
|
|
41
|
+
export async function startDeviceFlow(clientId, authBase = DEFAULT_AUTH_BASE, onMessage) {
|
|
42
|
+
const emit = onMessage ?? ((msg) => writeOutput(msg));
|
|
40
43
|
// Step 1: Request device code
|
|
41
44
|
const deviceCodeResponse = await requestDeviceCode(clientId, authBase);
|
|
42
45
|
// Step 2: Display instructions
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
emit("\n\n");
|
|
47
|
+
emit(" To authenticate, visit:\n");
|
|
48
|
+
emit(` ${deviceCodeResponse.verification_uri}\n`);
|
|
49
|
+
emit("\n");
|
|
50
|
+
emit(` Enter code: ${deviceCodeResponse.user_code}\n`);
|
|
51
|
+
emit("\n");
|
|
49
52
|
if (deviceCodeResponse.verification_uri_complete) {
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
emit(` Or open: ${deviceCodeResponse.verification_uri_complete}\n`);
|
|
54
|
+
emit("\n");
|
|
52
55
|
}
|
|
53
|
-
|
|
56
|
+
emit(" Press Ctrl+C to cancel.\n\n");
|
|
54
57
|
// Step 3: Poll for token with countdown
|
|
55
58
|
const tokens = await pollForToken(clientId, deviceCodeResponse.device_code, deviceCodeResponse.interval, deviceCodeResponse.expires_in, authBase);
|
|
56
59
|
return tokens;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,yBAAyB;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/auth/token-store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,yBAAyB;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtB,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAsB/D;AAED;;;;GAIG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAkB3D;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAMhD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAUxD"}
|
package/dist/auth/token-store.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* Tokens are stored in ~/.vertaaux/credentials.json with restrictive permissions.
|
|
5
5
|
* This is separate from project config - tokens are user-scoped, not project-scoped.
|
|
6
6
|
*/
|
|
7
|
-
import { readFile, writeFile, mkdir, unlink } from "fs/promises";
|
|
8
|
-
import { existsSync } from "fs";
|
|
7
|
+
import { readFile, writeFile, mkdir, unlink, chmod } from "fs/promises";
|
|
8
|
+
import { existsSync, lstatSync } from "fs";
|
|
9
9
|
import { homedir } from "os";
|
|
10
10
|
import { join, dirname } from "path";
|
|
11
11
|
/**
|
|
@@ -26,6 +26,10 @@ export function getCredentialsPath() {
|
|
|
26
26
|
export async function saveToken(token) {
|
|
27
27
|
const credPath = getCredentialsPath();
|
|
28
28
|
const dir = dirname(credPath);
|
|
29
|
+
// SECVAL-1: Refuse to write if credentials path is a symlink
|
|
30
|
+
if (existsSync(credPath) && lstatSync(credPath).isSymbolicLink()) {
|
|
31
|
+
throw new Error(`Security error: ${credPath} is a symlink. Refusing to write credentials.`);
|
|
32
|
+
}
|
|
29
33
|
// Create directory if needed
|
|
30
34
|
if (!existsSync(dir)) {
|
|
31
35
|
await mkdir(dir, { recursive: true, mode: 0o700 });
|
|
@@ -33,6 +37,8 @@ export async function saveToken(token) {
|
|
|
33
37
|
// Write token with restrictive permissions
|
|
34
38
|
const content = JSON.stringify(token, null, 2);
|
|
35
39
|
await writeFile(credPath, content + "\n", { encoding: "utf-8", mode: 0o600 });
|
|
40
|
+
// SECVAL-2: Ensure restrictive permissions after write
|
|
41
|
+
await chmod(credPath, 0o600);
|
|
36
42
|
}
|
|
37
43
|
/**
|
|
38
44
|
* Load token data from credentials file.
|
|
@@ -44,6 +50,10 @@ export async function loadToken() {
|
|
|
44
50
|
if (!existsSync(credPath)) {
|
|
45
51
|
return null;
|
|
46
52
|
}
|
|
53
|
+
// SECVAL-1: Refuse to read if credentials path is a symlink
|
|
54
|
+
if (lstatSync(credPath).isSymbolicLink()) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
47
57
|
try {
|
|
48
58
|
const content = await readFile(credPath, "utf-8");
|
|
49
59
|
return JSON.parse(content);
|