uidex 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/dist/cli/cli.cjs +996 -73
- package/dist/cli/cli.cjs.map +1 -1
- package/dist/cloud/index.cjs +375 -72
- package/dist/cloud/index.cjs.map +1 -1
- package/dist/cloud/index.d.cts +82 -0
- package/dist/cloud/index.d.ts +82 -0
- package/dist/cloud/index.js +376 -71
- package/dist/cloud/index.js.map +1 -1
- package/dist/headless/index.cjs +620 -469
- package/dist/headless/index.cjs.map +1 -1
- package/dist/headless/index.d.cts +77 -75
- package/dist/headless/index.d.ts +77 -75
- package/dist/headless/index.js +624 -469
- package/dist/headless/index.js.map +1 -1
- package/dist/index.cjs +4255 -2884
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +275 -234
- package/dist/index.d.ts +275 -234
- package/dist/index.js +4277 -2890
- package/dist/index.js.map +1 -1
- package/dist/playwright/index.cjs +4 -4
- package/dist/playwright/index.cjs.map +1 -1
- package/dist/playwright/index.js +3 -3
- package/dist/playwright/index.js.map +1 -1
- package/dist/playwright/reporter.cjs +3 -3
- package/dist/playwright/reporter.cjs.map +1 -1
- package/dist/playwright/reporter.js +3 -3
- package/dist/playwright/reporter.js.map +1 -1
- package/dist/react/index.cjs +4298 -2908
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +206 -200
- package/dist/react/index.d.ts +206 -200
- package/dist/react/index.js +4334 -2924
- package/dist/react/index.js.map +1 -1
- package/dist/scan/index.cjs +91 -40
- package/dist/scan/index.cjs.map +1 -1
- package/dist/scan/index.d.cts +26 -0
- package/dist/scan/index.d.ts +26 -0
- package/dist/scan/index.js +90 -39
- package/dist/scan/index.js.map +1 -1
- package/package.json +23 -22
- package/templates/claude/api.md +110 -0
package/dist/cli/cli.cjs
CHANGED
|
@@ -23,18 +23,18 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
23
23
|
mod
|
|
24
24
|
));
|
|
25
25
|
|
|
26
|
-
// src/scan/cli.ts
|
|
26
|
+
// src/scanner/scan/cli.ts
|
|
27
27
|
var fs7 = __toESM(require("fs"), 1);
|
|
28
28
|
var path9 = __toESM(require("path"), 1);
|
|
29
29
|
|
|
30
|
-
// src/scan/ai/index.ts
|
|
30
|
+
// src/scanner/scan/ai/index.ts
|
|
31
31
|
var p = __toESM(require("@clack/prompts"), 1);
|
|
32
32
|
|
|
33
|
-
// src/scan/ai/providers/claude.ts
|
|
33
|
+
// src/scanner/scan/ai/providers/claude.ts
|
|
34
34
|
var fs2 = __toESM(require("fs"), 1);
|
|
35
35
|
var path2 = __toESM(require("path"), 1);
|
|
36
36
|
|
|
37
|
-
// src/scan/ai/templates.ts
|
|
37
|
+
// src/scanner/scan/ai/templates.ts
|
|
38
38
|
var fs = __toESM(require("fs"), 1);
|
|
39
39
|
var path = __toESM(require("path"), 1);
|
|
40
40
|
function templatePath(rel) {
|
|
@@ -61,15 +61,16 @@ function readTemplate(rel) {
|
|
|
61
61
|
return fs.readFileSync(templatePath(rel), "utf8");
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
// src/scan/ai/providers/claude.ts
|
|
64
|
+
// src/scanner/scan/ai/providers/claude.ts
|
|
65
65
|
var CLAUDE_FILES = [
|
|
66
66
|
{ dest: ".claude/rules/uidex.md", template: "claude/rules.md" },
|
|
67
|
-
{ dest: ".claude/commands/uidex/audit.md", template: "claude/audit.md" }
|
|
67
|
+
{ dest: ".claude/commands/uidex/audit.md", template: "claude/audit.md" },
|
|
68
|
+
{ dest: ".claude/commands/uidex/api.md", template: "claude/api.md" }
|
|
68
69
|
];
|
|
69
70
|
var claudeProvider = {
|
|
70
71
|
id: "claude",
|
|
71
72
|
label: "Claude Code",
|
|
72
|
-
description: "Adds .claude/rules/uidex.md and
|
|
73
|
+
description: "Adds .claude/rules/uidex.md, /uidex:audit, and /uidex:api slash commands.",
|
|
73
74
|
async install({ cwd, force }) {
|
|
74
75
|
const changes = [];
|
|
75
76
|
for (const file of CLAUDE_FILES) {
|
|
@@ -117,16 +118,16 @@ function cleanupEmpty(dir) {
|
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
120
|
|
|
120
|
-
// src/scan/ai/providers/index.ts
|
|
121
|
+
// src/scanner/scan/ai/providers/index.ts
|
|
121
122
|
var PROVIDERS = [claudeProvider];
|
|
122
123
|
function getProvider(id) {
|
|
123
124
|
return PROVIDERS.find((p2) => p2.id === id);
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
// src/scan/ai/index.ts
|
|
127
|
+
// src/scanner/scan/ai/index.ts
|
|
127
128
|
async function runAiCommand(opts) {
|
|
128
|
-
const { cwd, argv } = opts;
|
|
129
|
-
const sub =
|
|
129
|
+
const { cwd, argv: argv2 } = opts;
|
|
130
|
+
const sub = argv2[0];
|
|
130
131
|
if (!sub || sub === "--help" || sub === "-h" || sub === "help") {
|
|
131
132
|
return out(0, helpText());
|
|
132
133
|
}
|
|
@@ -141,18 +142,18 @@ async function runAiCommand(opts) {
|
|
|
141
142
|
|
|
142
143
|
${helpText()}`);
|
|
143
144
|
}
|
|
144
|
-
const flags = parseFlags(
|
|
145
|
+
const flags = parseFlags(argv2.slice(1));
|
|
145
146
|
const provider = await selectProvider(opts, flags.provider);
|
|
146
147
|
if (!provider) return out(0, "Cancelled.\n");
|
|
147
148
|
if (sub === "install") {
|
|
148
|
-
const
|
|
149
|
+
const result3 = await provider.install({
|
|
149
150
|
cwd,
|
|
150
151
|
force: flags.force === true
|
|
151
152
|
});
|
|
152
|
-
return out(0, formatChanges(provider, "Installed",
|
|
153
|
+
return out(0, formatChanges(provider, "Installed", result3));
|
|
153
154
|
}
|
|
154
|
-
const
|
|
155
|
-
return out(0, formatChanges(provider, "Uninstalled",
|
|
155
|
+
const result2 = await provider.uninstall({ cwd });
|
|
156
|
+
return out(0, formatChanges(provider, "Uninstalled", result2));
|
|
156
157
|
}
|
|
157
158
|
async function selectProvider(opts, explicit) {
|
|
158
159
|
if (explicit) {
|
|
@@ -195,9 +196,9 @@ function parseFlags(args) {
|
|
|
195
196
|
}
|
|
196
197
|
return flags;
|
|
197
198
|
}
|
|
198
|
-
function formatChanges(provider, verb,
|
|
199
|
+
function formatChanges(provider, verb, result2) {
|
|
199
200
|
const lines = [`${verb} ${provider.label}:`];
|
|
200
|
-
for (const c of
|
|
201
|
+
for (const c of result2.changes) lines.push(` ${describe(c)}`);
|
|
201
202
|
return lines.join("\n") + "\n";
|
|
202
203
|
}
|
|
203
204
|
function describe(c) {
|
|
@@ -234,11 +235,11 @@ function err(exitCode, stderr) {
|
|
|
234
235
|
return { exitCode, stdout: "", stderr };
|
|
235
236
|
}
|
|
236
237
|
|
|
237
|
-
// src/scan/discover.ts
|
|
238
|
+
// src/scanner/scan/discover.ts
|
|
238
239
|
var fs3 = __toESM(require("fs"), 1);
|
|
239
240
|
var path3 = __toESM(require("path"), 1);
|
|
240
241
|
|
|
241
|
-
// src/scan/config.ts
|
|
242
|
+
// src/scanner/scan/config.ts
|
|
242
243
|
var DEFAULT_TYPE_MODE = "strict";
|
|
243
244
|
var WELL_KNOWN_FILES = {
|
|
244
245
|
page: "uidex.page.ts",
|
|
@@ -402,7 +403,7 @@ var DEFAULT_CONVENTIONS = {
|
|
|
402
403
|
regions: "landmarks"
|
|
403
404
|
};
|
|
404
405
|
|
|
405
|
-
// src/scan/discover.ts
|
|
406
|
+
// src/scanner/scan/discover.ts
|
|
406
407
|
var CONFIG_FILENAME = ".uidex.json";
|
|
407
408
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
408
409
|
"node_modules",
|
|
@@ -461,14 +462,14 @@ function discover(options = {}) {
|
|
|
461
462
|
return results.sort((a, b) => a.configPath.localeCompare(b.configPath));
|
|
462
463
|
}
|
|
463
464
|
|
|
464
|
-
// src/scan/pipeline.ts
|
|
465
|
+
// src/scanner/scan/pipeline.ts
|
|
465
466
|
var fs5 = __toESM(require("fs"), 1);
|
|
466
467
|
var path7 = __toESM(require("path"), 1);
|
|
467
468
|
|
|
468
|
-
// src/scan/audit.ts
|
|
469
|
+
// src/scanner/scan/audit.ts
|
|
469
470
|
var path4 = __toESM(require("path"), 1);
|
|
470
471
|
|
|
471
|
-
// src/entities/types.ts
|
|
472
|
+
// src/shared/entities/types.ts
|
|
472
473
|
var ENTITY_KINDS = [
|
|
473
474
|
"route",
|
|
474
475
|
"page",
|
|
@@ -501,7 +502,7 @@ function assertEntityKind(kind) {
|
|
|
501
502
|
if (!KIND_SET.has(kind)) throw new UnknownEntityKindError(kind);
|
|
502
503
|
}
|
|
503
504
|
|
|
504
|
-
// src/entities/registry.ts
|
|
505
|
+
// src/shared/entities/registry.ts
|
|
505
506
|
function emptyStore() {
|
|
506
507
|
return {
|
|
507
508
|
route: /* @__PURE__ */ new Map(),
|
|
@@ -565,11 +566,11 @@ function createRegistry() {
|
|
|
565
566
|
};
|
|
566
567
|
const query = (predicate) => {
|
|
567
568
|
const flows = getFlows();
|
|
568
|
-
const
|
|
569
|
+
const result2 = [];
|
|
569
570
|
for (const entity of allEntities()) {
|
|
570
|
-
if (predicate(entity))
|
|
571
|
+
if (predicate(entity)) result2.push(freezeEntity(entity, flows));
|
|
571
572
|
}
|
|
572
|
-
return
|
|
573
|
+
return result2;
|
|
573
574
|
};
|
|
574
575
|
const byScope = (scope) => query(
|
|
575
576
|
(entity) => "scopes" in entity && Array.isArray(entity.scopes) && entity.scopes.includes(scope)
|
|
@@ -583,10 +584,33 @@ function createRegistry() {
|
|
|
583
584
|
return ids.has(entity.id);
|
|
584
585
|
});
|
|
585
586
|
};
|
|
586
|
-
|
|
587
|
+
const reports = /* @__PURE__ */ new Map();
|
|
588
|
+
const reportsCbs = /* @__PURE__ */ new Set();
|
|
589
|
+
const setReports = (kind, id, records) => {
|
|
590
|
+
reports.set(`${kind}:${id}`, records);
|
|
591
|
+
for (const cb of reportsCbs) cb();
|
|
592
|
+
};
|
|
593
|
+
const getReports = (kind, id) => reports.get(`${kind}:${id}`) ?? [];
|
|
594
|
+
const listReportKeys = () => Array.from(reports.keys());
|
|
595
|
+
const onReportsChange = (cb) => {
|
|
596
|
+
reportsCbs.add(cb);
|
|
597
|
+
return () => reportsCbs.delete(cb);
|
|
598
|
+
};
|
|
599
|
+
return {
|
|
600
|
+
add,
|
|
601
|
+
get,
|
|
602
|
+
list,
|
|
603
|
+
query,
|
|
604
|
+
byScope,
|
|
605
|
+
touchedBy,
|
|
606
|
+
setReports,
|
|
607
|
+
getReports,
|
|
608
|
+
listReportKeys,
|
|
609
|
+
onReportsChange
|
|
610
|
+
};
|
|
587
611
|
}
|
|
588
612
|
|
|
589
|
-
// src/scan/audit.ts
|
|
613
|
+
// src/scanner/scan/audit.ts
|
|
590
614
|
var MARKER_FILENAMES = ["UIDEX_PAGE.md", "UIDEX_FEATURE.md"];
|
|
591
615
|
function audit(opts) {
|
|
592
616
|
const diagnostics = [];
|
|
@@ -740,6 +764,15 @@ function audit(opts) {
|
|
|
740
764
|
const primitives = registry.list("primitive");
|
|
741
765
|
const byName = /* @__PURE__ */ new Map();
|
|
742
766
|
for (const p2 of primitives) byName.set(p2.id, p2);
|
|
767
|
+
const declaredFeatures = /* @__PURE__ */ new Map();
|
|
768
|
+
for (const ef of extracted) {
|
|
769
|
+
if (!ef.metadata) continue;
|
|
770
|
+
for (const m of ef.metadata) {
|
|
771
|
+
if (m.features && m.features.length > 0) {
|
|
772
|
+
declaredFeatures.set(ef.file.displayPath, new Set(m.features));
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
743
776
|
for (const f of files) {
|
|
744
777
|
const importRe = /import\s+(?:[^'"]+)\s+from\s+['"]([^'"]+)['"]/g;
|
|
745
778
|
let m;
|
|
@@ -754,14 +787,19 @@ function audit(opts) {
|
|
|
754
787
|
if (!scope) continue;
|
|
755
788
|
const [kind, id] = scope.split(":");
|
|
756
789
|
const importerSegments = f.displayPath.split("/");
|
|
757
|
-
if (
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
});
|
|
790
|
+
if (importerSegments.includes(id) && importerSegments.includes(kind + "s")) {
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
if (kind === "feature" && importerSegments.includes(id)) continue;
|
|
794
|
+
if (kind === "feature" && declaredFeatures.get(f.displayPath)?.has(id)) {
|
|
795
|
+
continue;
|
|
764
796
|
}
|
|
797
|
+
diagnostics.push({
|
|
798
|
+
code: "scope-leak",
|
|
799
|
+
severity: "warning",
|
|
800
|
+
message: `Primitive "${primitive.id}" is scoped to ${scope} but is imported from ${f.displayPath}`,
|
|
801
|
+
file: f.displayPath
|
|
802
|
+
});
|
|
765
803
|
}
|
|
766
804
|
}
|
|
767
805
|
}
|
|
@@ -935,7 +973,7 @@ function stableReplacer(_key, value) {
|
|
|
935
973
|
return value;
|
|
936
974
|
}
|
|
937
975
|
|
|
938
|
-
// src/scan/emit.ts
|
|
976
|
+
// src/scanner/scan/emit.ts
|
|
939
977
|
function sortById(arr) {
|
|
940
978
|
return [...arr].sort((a, b) => a.id.localeCompare(b.id));
|
|
941
979
|
}
|
|
@@ -1059,6 +1097,7 @@ function emit(opts) {
|
|
|
1059
1097
|
lines.push(" export interface Feature {");
|
|
1060
1098
|
lines.push(" feature: FeatureId | false");
|
|
1061
1099
|
lines.push(" name?: string");
|
|
1100
|
+
lines.push(" features?: readonly FeatureId[]");
|
|
1062
1101
|
lines.push(" acceptance?: readonly string[]");
|
|
1063
1102
|
lines.push(" description?: string");
|
|
1064
1103
|
lines.push(" }");
|
|
@@ -1115,7 +1154,7 @@ function emit(opts) {
|
|
|
1115
1154
|
return lines.join("\n");
|
|
1116
1155
|
}
|
|
1117
1156
|
|
|
1118
|
-
// src/scan/extract-uidex-export.ts
|
|
1157
|
+
// src/scanner/scan/extract-uidex-export.ts
|
|
1119
1158
|
var KIND_DISCRIMINATORS = [
|
|
1120
1159
|
"page",
|
|
1121
1160
|
"feature",
|
|
@@ -1133,7 +1172,13 @@ var ALLOWED_FIELDS = {
|
|
|
1133
1172
|
"acceptance",
|
|
1134
1173
|
"description"
|
|
1135
1174
|
]),
|
|
1136
|
-
feature: /* @__PURE__ */ new Set([
|
|
1175
|
+
feature: /* @__PURE__ */ new Set([
|
|
1176
|
+
"feature",
|
|
1177
|
+
"name",
|
|
1178
|
+
"features",
|
|
1179
|
+
"acceptance",
|
|
1180
|
+
"description"
|
|
1181
|
+
]),
|
|
1137
1182
|
primitive: /* @__PURE__ */ new Set(["primitive", "name", "description"]),
|
|
1138
1183
|
widget: /* @__PURE__ */ new Set(["widget", "name", "acceptance", "description"]),
|
|
1139
1184
|
flow: /* @__PURE__ */ new Set(["flow", "notFlow", "name", "description"])
|
|
@@ -1869,7 +1914,7 @@ function buildMetadata(value, file, headerPos, diagnostics) {
|
|
|
1869
1914
|
line: pos.line
|
|
1870
1915
|
});
|
|
1871
1916
|
}
|
|
1872
|
-
const features = kind === "page" ? readStringArrayField(byKey, "features") : void 0;
|
|
1917
|
+
const features = kind === "page" || kind === "feature" ? readStringArrayField(byKey, "features") : void 0;
|
|
1873
1918
|
const widgets = kind === "page" ? readStringArrayField(byKey, "widgets") : void 0;
|
|
1874
1919
|
const notFlow = kind === "flow" && discriminator === "notFlow" ? true : void 0;
|
|
1875
1920
|
const metadata = {
|
|
@@ -1964,7 +2009,7 @@ function posAt(content, offset) {
|
|
|
1964
2009
|
return { offset, line, column: offset - lineStart + 1 };
|
|
1965
2010
|
}
|
|
1966
2011
|
|
|
1967
|
-
// src/scan/jsx-ancestry.ts
|
|
2012
|
+
// src/scanner/scan/jsx-ancestry.ts
|
|
1968
2013
|
var DATA_ATTR_RE = /\bdata-uidex(?:-(region|widget|primitive))?\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
|
|
1969
2014
|
function parseDataAttrs(tagSource) {
|
|
1970
2015
|
if (!tagSource.includes("data-uidex")) return [];
|
|
@@ -2153,7 +2198,7 @@ function findTagEnd(content, start) {
|
|
|
2153
2198
|
return -1;
|
|
2154
2199
|
}
|
|
2155
2200
|
|
|
2156
|
-
// src/scan/extract.ts
|
|
2201
|
+
// src/scanner/scan/extract.ts
|
|
2157
2202
|
var JSDOC_BLOCK = /\/\*\*([\s\S]*?)\*\//g;
|
|
2158
2203
|
function lineAt(content, index) {
|
|
2159
2204
|
let line = 1;
|
|
@@ -2256,7 +2301,7 @@ function extractOne(file) {
|
|
|
2256
2301
|
return annotations;
|
|
2257
2302
|
}
|
|
2258
2303
|
|
|
2259
|
-
// src/scan/git.ts
|
|
2304
|
+
// src/scanner/scan/git.ts
|
|
2260
2305
|
var import_node_child_process = require("child_process");
|
|
2261
2306
|
function runGit(args, cwd) {
|
|
2262
2307
|
try {
|
|
@@ -2288,10 +2333,10 @@ function parseGitHubRef(ref) {
|
|
|
2288
2333
|
return m ? m[1] : null;
|
|
2289
2334
|
}
|
|
2290
2335
|
|
|
2291
|
-
// src/scan/resolve.ts
|
|
2336
|
+
// src/scanner/scan/resolve.ts
|
|
2292
2337
|
var path6 = __toESM(require("path"), 1);
|
|
2293
2338
|
|
|
2294
|
-
// src/scan/routes.ts
|
|
2339
|
+
// src/scanner/scan/routes.ts
|
|
2295
2340
|
var PAGE_BASENAME = /^page\.(tsx|ts|jsx|js|mjs|cjs)$/;
|
|
2296
2341
|
var PAGES_ROUTER_BASENAME = /\.(tsx|ts|jsx|js|mjs|cjs)$/;
|
|
2297
2342
|
var ROUTE_BASENAME = /^route\.(tsx|ts|jsx|js|mjs|cjs)$/;
|
|
@@ -2357,7 +2402,7 @@ function pathToId(routePath) {
|
|
|
2357
2402
|
return routePath.replace(/^\/+/, "").replace(/\[\.{3}([^\]]+)\]/g, "$1").replace(/\[([^\]]+)\]/g, "$1").replace(/\//g, "-").replace(/[^a-zA-Z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
2358
2403
|
}
|
|
2359
2404
|
|
|
2360
|
-
// src/scan/walk.ts
|
|
2405
|
+
// src/scanner/scan/walk.ts
|
|
2361
2406
|
var fs4 = __toESM(require("fs"), 1);
|
|
2362
2407
|
var path5 = __toESM(require("path"), 1);
|
|
2363
2408
|
var DEFAULT_INCLUDES = ["**/*.{ts,tsx,js,jsx,mjs,cjs}"];
|
|
@@ -2482,7 +2527,7 @@ function* walkDir(root, dir) {
|
|
|
2482
2527
|
}
|
|
2483
2528
|
}
|
|
2484
2529
|
|
|
2485
|
-
// src/scan/resolve.ts
|
|
2530
|
+
// src/scanner/scan/resolve.ts
|
|
2486
2531
|
var DOM_ATTR_KINDS = /* @__PURE__ */ new Set([
|
|
2487
2532
|
"element",
|
|
2488
2533
|
"region",
|
|
@@ -2967,7 +3012,7 @@ function dedupe(arr) {
|
|
|
2967
3012
|
return Array.from(new Set(arr));
|
|
2968
3013
|
}
|
|
2969
3014
|
|
|
2970
|
-
// src/scan/pipeline.ts
|
|
3015
|
+
// src/scanner/scan/pipeline.ts
|
|
2971
3016
|
function runScan(opts = {}) {
|
|
2972
3017
|
const cwd = opts.cwd ?? process.cwd();
|
|
2973
3018
|
const configs = opts.configs ?? discover({ cwd });
|
|
@@ -3034,12 +3079,12 @@ function runOne(dc, opts) {
|
|
|
3034
3079
|
outputPath
|
|
3035
3080
|
};
|
|
3036
3081
|
}
|
|
3037
|
-
function writeScanResult(
|
|
3038
|
-
fs5.mkdirSync(path7.dirname(
|
|
3039
|
-
fs5.writeFileSync(
|
|
3082
|
+
function writeScanResult(result2) {
|
|
3083
|
+
fs5.mkdirSync(path7.dirname(result2.outputPath), { recursive: true });
|
|
3084
|
+
fs5.writeFileSync(result2.outputPath, result2.generated, "utf8");
|
|
3040
3085
|
}
|
|
3041
3086
|
|
|
3042
|
-
// src/scan/scaffold.ts
|
|
3087
|
+
// src/scanner/scan/scaffold.ts
|
|
3043
3088
|
var fs6 = __toESM(require("fs"), 1);
|
|
3044
3089
|
var path8 = __toESM(require("path"), 1);
|
|
3045
3090
|
function scaffoldWidgetSpec(opts) {
|
|
@@ -3102,8 +3147,8 @@ function renderSpec(args) {
|
|
|
3102
3147
|
return lines.join("\n");
|
|
3103
3148
|
}
|
|
3104
3149
|
|
|
3105
|
-
// src/
|
|
3106
|
-
function
|
|
3150
|
+
// src/scanner/cli/parse-args.ts
|
|
3151
|
+
function parseArgs(args) {
|
|
3107
3152
|
const positional = [];
|
|
3108
3153
|
const flags = {};
|
|
3109
3154
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -3127,13 +3172,15 @@ function parseFlags2(args) {
|
|
|
3127
3172
|
}
|
|
3128
3173
|
return { positional, flags };
|
|
3129
3174
|
}
|
|
3175
|
+
|
|
3176
|
+
// src/scanner/scan/cli.ts
|
|
3130
3177
|
async function run(opts) {
|
|
3131
3178
|
const cwd = opts.cwd ?? process.cwd();
|
|
3132
|
-
const { positional, flags } =
|
|
3133
|
-
const
|
|
3179
|
+
const { positional, flags } = parseArgs(opts.argv);
|
|
3180
|
+
const command2 = positional[0] ?? "help";
|
|
3134
3181
|
const writer = createWriter();
|
|
3135
3182
|
try {
|
|
3136
|
-
switch (
|
|
3183
|
+
switch (command2) {
|
|
3137
3184
|
case "help":
|
|
3138
3185
|
case "--help":
|
|
3139
3186
|
case "-h":
|
|
@@ -3146,16 +3193,16 @@ async function run(opts) {
|
|
|
3146
3193
|
case "scaffold":
|
|
3147
3194
|
return runScaffold(cwd, positional.slice(1), flags, writer);
|
|
3148
3195
|
case "ai": {
|
|
3149
|
-
const
|
|
3196
|
+
const result2 = await runAiCommand({
|
|
3150
3197
|
cwd,
|
|
3151
3198
|
argv: opts.argv.slice(1)
|
|
3152
3199
|
});
|
|
3153
|
-
if (
|
|
3154
|
-
if (
|
|
3155
|
-
return writer.result(
|
|
3200
|
+
if (result2.stdout) writer.out(result2.stdout.replace(/\n$/, ""));
|
|
3201
|
+
if (result2.stderr) writer.err(result2.stderr.replace(/\n$/, ""));
|
|
3202
|
+
return writer.result(result2.exitCode);
|
|
3156
3203
|
}
|
|
3157
3204
|
default:
|
|
3158
|
-
writer.err(`Unknown command: ${
|
|
3205
|
+
writer.err(`Unknown command: ${command2}`);
|
|
3159
3206
|
writer.err(helpText2());
|
|
3160
3207
|
return writer.result(1);
|
|
3161
3208
|
}
|
|
@@ -3173,6 +3220,10 @@ function helpText2() {
|
|
|
3173
3220
|
" scan [flags] Run the scanner pipeline",
|
|
3174
3221
|
" scaffold widget <id> Emit a Playwright spec from a widget's acceptance",
|
|
3175
3222
|
" ai <install|uninstall|providers> Manage AI assistant integrations",
|
|
3223
|
+
" api <METHOD> <PATH> Call the uidex API",
|
|
3224
|
+
" api --list Show available API routes",
|
|
3225
|
+
" api login Authenticate via browser",
|
|
3226
|
+
" api login --token <tok> Store an auth token directly",
|
|
3176
3227
|
"",
|
|
3177
3228
|
"Flags:",
|
|
3178
3229
|
" --check Verify the on-disk gen file matches a fresh scan; exit non-zero on drift (read-only)",
|
|
@@ -3275,17 +3326,17 @@ function runScaffold(cwd, args, flags, w) {
|
|
|
3275
3326
|
const widget = r.registry.get("widget", id);
|
|
3276
3327
|
if (!widget) continue;
|
|
3277
3328
|
const outDir = path9.resolve(r.configDir, "e2e");
|
|
3278
|
-
const
|
|
3329
|
+
const result2 = scaffoldWidgetSpec({
|
|
3279
3330
|
registry: r.registry,
|
|
3280
3331
|
widgetId: id,
|
|
3281
3332
|
outDir,
|
|
3282
3333
|
force: Boolean(flags.force)
|
|
3283
3334
|
});
|
|
3284
|
-
if (
|
|
3285
|
-
w.err(
|
|
3335
|
+
if (result2.skipped) {
|
|
3336
|
+
w.err(result2.reason ?? "skipped");
|
|
3286
3337
|
return w.result(1);
|
|
3287
3338
|
}
|
|
3288
|
-
w.out(`Wrote ${
|
|
3339
|
+
w.out(`Wrote ${result2.outputPath}`);
|
|
3289
3340
|
return w.result(0);
|
|
3290
3341
|
}
|
|
3291
3342
|
w.err(`Widget "${id}" not found in registry`);
|
|
@@ -3307,12 +3358,884 @@ function createWriter() {
|
|
|
3307
3358
|
};
|
|
3308
3359
|
}
|
|
3309
3360
|
|
|
3310
|
-
// src/cli/
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3361
|
+
// src/scanner/cli/auth.ts
|
|
3362
|
+
function createMemoryTokenStorage(initial) {
|
|
3363
|
+
let token = initial ?? null;
|
|
3364
|
+
return {
|
|
3365
|
+
get: () => token,
|
|
3366
|
+
set: (t) => {
|
|
3367
|
+
token = t;
|
|
3368
|
+
},
|
|
3369
|
+
clear: () => {
|
|
3370
|
+
token = null;
|
|
3371
|
+
}
|
|
3372
|
+
};
|
|
3373
|
+
}
|
|
3374
|
+
function createFileTokenStorage(options) {
|
|
3375
|
+
const nodeFs = require("fs");
|
|
3376
|
+
const nodePath = require("path");
|
|
3377
|
+
const file = options.path;
|
|
3378
|
+
function read() {
|
|
3379
|
+
if (!nodeFs.existsSync(file)) return null;
|
|
3380
|
+
try {
|
|
3381
|
+
const raw = nodeFs.readFileSync(file, "utf8");
|
|
3382
|
+
const parsed = JSON.parse(raw);
|
|
3383
|
+
if (parsed && typeof parsed === "object") {
|
|
3384
|
+
return parsed;
|
|
3385
|
+
}
|
|
3386
|
+
} catch {
|
|
3387
|
+
return null;
|
|
3388
|
+
}
|
|
3389
|
+
return null;
|
|
3390
|
+
}
|
|
3391
|
+
return {
|
|
3392
|
+
get: () => read()?.token ?? null,
|
|
3393
|
+
set: (token) => {
|
|
3394
|
+
nodeFs.mkdirSync(nodePath.dirname(file), { recursive: true });
|
|
3395
|
+
nodeFs.writeFileSync(file, JSON.stringify({ token }, null, 2));
|
|
3396
|
+
},
|
|
3397
|
+
clear: () => {
|
|
3398
|
+
if (nodeFs.existsSync(file)) nodeFs.unlinkSync(file);
|
|
3399
|
+
}
|
|
3400
|
+
};
|
|
3401
|
+
}
|
|
3402
|
+
function defaultTokenPath() {
|
|
3403
|
+
const os = require("os");
|
|
3404
|
+
const path10 = require("path");
|
|
3405
|
+
return path10.join(os.homedir(), ".uidex", "cloud-token.json");
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3408
|
+
// src/scanner/cli/http.ts
|
|
3409
|
+
function createHttpClient(options) {
|
|
3410
|
+
const baseUrl = options.baseUrl.replace(/\/$/, "");
|
|
3411
|
+
async function request(path10, opts = {}) {
|
|
3412
|
+
let url = `${baseUrl}${path10.startsWith("/") ? path10 : `/${path10}`}`;
|
|
3413
|
+
if (opts.query) {
|
|
3414
|
+
url += (url.includes("?") ? "&" : "?") + opts.query;
|
|
3415
|
+
}
|
|
3416
|
+
const headers = {
|
|
3417
|
+
Accept: "application/json",
|
|
3418
|
+
...opts.headers
|
|
3419
|
+
};
|
|
3420
|
+
const token = options.token();
|
|
3421
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
3422
|
+
if (opts.body !== void 0) {
|
|
3423
|
+
headers["Content-Type"] ??= "application/json";
|
|
3424
|
+
}
|
|
3425
|
+
const res = await fetch(url, {
|
|
3426
|
+
method: opts.method ?? (opts.body ? "POST" : "GET"),
|
|
3427
|
+
headers,
|
|
3428
|
+
body: opts.body
|
|
3429
|
+
});
|
|
3430
|
+
const raw = await res.text();
|
|
3431
|
+
let body = raw;
|
|
3432
|
+
try {
|
|
3433
|
+
body = JSON.parse(raw);
|
|
3434
|
+
} catch {
|
|
3435
|
+
}
|
|
3436
|
+
return { status: res.status, body, raw };
|
|
3437
|
+
}
|
|
3438
|
+
return { request };
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3441
|
+
// src/scanner/cli/json-highlight.ts
|
|
3442
|
+
var RESET = "\x1B[0m";
|
|
3443
|
+
var BOLD = "\x1B[1m";
|
|
3444
|
+
var DIM = "\x1B[2m";
|
|
3445
|
+
var RED = "\x1B[31m";
|
|
3446
|
+
var CYAN = "\x1B[36m";
|
|
3447
|
+
var GREEN = "\x1B[32m";
|
|
3448
|
+
var YELLOW = "\x1B[33m";
|
|
3449
|
+
var MAGENTA = "\x1B[35m";
|
|
3450
|
+
function highlightJson(value, color) {
|
|
3451
|
+
const json = JSON.stringify(value, null, 2);
|
|
3452
|
+
if (!color) return json;
|
|
3453
|
+
return json.replace(
|
|
3454
|
+
/("(?:\\.|[^"\\])*")\s*:|("(?:\\.|[^"\\])*")|(\b(?:true|false|null)\b)|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g,
|
|
3455
|
+
(match, key, str, literal, num) => {
|
|
3456
|
+
if (key) return `${CYAN}${key}${RESET}:`;
|
|
3457
|
+
if (str) return `${GREEN}${str}${RESET}`;
|
|
3458
|
+
if (literal) return `${MAGENTA}${literal}${RESET}`;
|
|
3459
|
+
if (num) return `${YELLOW}${num}${RESET}`;
|
|
3460
|
+
return match;
|
|
3461
|
+
}
|
|
3462
|
+
);
|
|
3463
|
+
}
|
|
3464
|
+
function formatStatus(status, color) {
|
|
3465
|
+
if (!color) return `HTTP ${status}`;
|
|
3466
|
+
const code = status >= 400 ? RED : status >= 300 ? YELLOW : GREEN;
|
|
3467
|
+
return `${code}HTTP ${status}${RESET}`;
|
|
3468
|
+
}
|
|
3469
|
+
function boldText(text, color) {
|
|
3470
|
+
if (!color) return text;
|
|
3471
|
+
return `${BOLD}${text}${RESET}`;
|
|
3472
|
+
}
|
|
3473
|
+
function dimText(text, color) {
|
|
3474
|
+
if (!color) return text;
|
|
3475
|
+
return `${DIM}${text}${RESET}`;
|
|
3476
|
+
}
|
|
3477
|
+
|
|
3478
|
+
// src/scanner/cli/api-routes.gen.ts
|
|
3479
|
+
var API_ROUTES = [
|
|
3480
|
+
{
|
|
3481
|
+
"method": "POST",
|
|
3482
|
+
"path": "/api/ingest",
|
|
3483
|
+
"operationId": "submitReport",
|
|
3484
|
+
"summary": "Submit feedback from the SDK.",
|
|
3485
|
+
"tag": "Ingest",
|
|
3486
|
+
"params": []
|
|
3487
|
+
},
|
|
3488
|
+
{
|
|
3489
|
+
"method": "GET",
|
|
3490
|
+
"path": "/api/ingest/config",
|
|
3491
|
+
"operationId": "getIngestConfig",
|
|
3492
|
+
"summary": "Get integration configuration for the project.",
|
|
3493
|
+
"tag": "Ingest",
|
|
3494
|
+
"params": []
|
|
3495
|
+
},
|
|
3496
|
+
{
|
|
3497
|
+
"method": "GET",
|
|
3498
|
+
"path": "/api/ingest/reports",
|
|
3499
|
+
"operationId": "listIngestReports",
|
|
3500
|
+
"summary": "List feedback for the project (SDK view).",
|
|
3501
|
+
"tag": "Ingest",
|
|
3502
|
+
"params": []
|
|
3503
|
+
},
|
|
3504
|
+
{
|
|
3505
|
+
"method": "GET",
|
|
3506
|
+
"path": "/api/ingest/pins",
|
|
3507
|
+
"operationId": "listPins",
|
|
3508
|
+
"summary": "List pins by route and/or entity.",
|
|
3509
|
+
"tag": "Ingest",
|
|
3510
|
+
"params": []
|
|
3511
|
+
},
|
|
3512
|
+
{
|
|
3513
|
+
"method": "POST",
|
|
3514
|
+
"path": "/api/ingest/pins/archive",
|
|
3515
|
+
"operationId": "archivePin",
|
|
3516
|
+
"summary": "Archive a pin.",
|
|
3517
|
+
"tag": "Ingest",
|
|
3518
|
+
"params": []
|
|
3519
|
+
},
|
|
3520
|
+
{
|
|
3521
|
+
"method": "POST",
|
|
3522
|
+
"path": "/api/cli-auth/authorize",
|
|
3523
|
+
"operationId": "authorizeCli",
|
|
3524
|
+
"summary": "Authorize a CLI session by delivering a token.",
|
|
3525
|
+
"tag": "CLI Auth",
|
|
3526
|
+
"params": []
|
|
3527
|
+
},
|
|
3528
|
+
{
|
|
3529
|
+
"method": "GET",
|
|
3530
|
+
"path": "/api/organizations",
|
|
3531
|
+
"operationId": "listOrganizations",
|
|
3532
|
+
"summary": "List organizations for the current user.",
|
|
3533
|
+
"tag": "Organizations",
|
|
3534
|
+
"params": []
|
|
3535
|
+
},
|
|
3536
|
+
{
|
|
3537
|
+
"method": "POST",
|
|
3538
|
+
"path": "/api/organizations",
|
|
3539
|
+
"operationId": "createOrganization",
|
|
3540
|
+
"summary": "Create an organization.",
|
|
3541
|
+
"tag": "Organizations",
|
|
3542
|
+
"params": []
|
|
3543
|
+
},
|
|
3544
|
+
{
|
|
3545
|
+
"method": "POST",
|
|
3546
|
+
"path": "/api/organizations/switch",
|
|
3547
|
+
"operationId": "switchOrganization",
|
|
3548
|
+
"summary": "Switch the active organization.",
|
|
3549
|
+
"tag": "Organizations",
|
|
3550
|
+
"params": []
|
|
3551
|
+
},
|
|
3552
|
+
{
|
|
3553
|
+
"method": "GET",
|
|
3554
|
+
"path": "/api/organizations/{orgId}",
|
|
3555
|
+
"operationId": "getOrganization",
|
|
3556
|
+
"summary": "Get organization details.",
|
|
3557
|
+
"tag": "Organizations",
|
|
3558
|
+
"params": [
|
|
3559
|
+
"orgId"
|
|
3560
|
+
]
|
|
3561
|
+
},
|
|
3562
|
+
{
|
|
3563
|
+
"method": "PATCH",
|
|
3564
|
+
"path": "/api/organizations/{orgId}",
|
|
3565
|
+
"operationId": "updateOrganization",
|
|
3566
|
+
"summary": "Update an organization.",
|
|
3567
|
+
"tag": "Organizations",
|
|
3568
|
+
"params": [
|
|
3569
|
+
"orgId"
|
|
3570
|
+
]
|
|
3571
|
+
},
|
|
3572
|
+
{
|
|
3573
|
+
"method": "DELETE",
|
|
3574
|
+
"path": "/api/organizations/{orgId}",
|
|
3575
|
+
"operationId": "deleteOrganization",
|
|
3576
|
+
"summary": "Delete an organization.",
|
|
3577
|
+
"tag": "Organizations",
|
|
3578
|
+
"params": [
|
|
3579
|
+
"orgId"
|
|
3580
|
+
]
|
|
3581
|
+
},
|
|
3582
|
+
{
|
|
3583
|
+
"method": "GET",
|
|
3584
|
+
"path": "/api/organizations/{orgId}/members",
|
|
3585
|
+
"operationId": "listMembers",
|
|
3586
|
+
"summary": "List organization members.",
|
|
3587
|
+
"tag": "Members",
|
|
3588
|
+
"params": [
|
|
3589
|
+
"orgId"
|
|
3590
|
+
]
|
|
3591
|
+
},
|
|
3592
|
+
{
|
|
3593
|
+
"method": "DELETE",
|
|
3594
|
+
"path": "/api/organizations/{orgId}/members",
|
|
3595
|
+
"operationId": "removeMember",
|
|
3596
|
+
"summary": "Remove a member from the organization.",
|
|
3597
|
+
"tag": "Members",
|
|
3598
|
+
"params": [
|
|
3599
|
+
"orgId"
|
|
3600
|
+
]
|
|
3601
|
+
},
|
|
3602
|
+
{
|
|
3603
|
+
"method": "PATCH",
|
|
3604
|
+
"path": "/api/organizations/{orgId}/members/{memberId}",
|
|
3605
|
+
"operationId": "updateMember",
|
|
3606
|
+
"summary": "Update a member's role.",
|
|
3607
|
+
"tag": "Members",
|
|
3608
|
+
"params": [
|
|
3609
|
+
"orgId",
|
|
3610
|
+
"memberId"
|
|
3611
|
+
]
|
|
3612
|
+
},
|
|
3613
|
+
{
|
|
3614
|
+
"method": "GET",
|
|
3615
|
+
"path": "/api/organizations/{orgId}/invitations",
|
|
3616
|
+
"operationId": "listInvitations",
|
|
3617
|
+
"summary": "List organization invitations.",
|
|
3618
|
+
"tag": "Invitations",
|
|
3619
|
+
"params": [
|
|
3620
|
+
"orgId"
|
|
3621
|
+
]
|
|
3622
|
+
},
|
|
3623
|
+
{
|
|
3624
|
+
"method": "POST",
|
|
3625
|
+
"path": "/api/organizations/{orgId}/invitations",
|
|
3626
|
+
"operationId": "createInvitation",
|
|
3627
|
+
"summary": "Create an invitation link.",
|
|
3628
|
+
"tag": "Invitations",
|
|
3629
|
+
"params": [
|
|
3630
|
+
"orgId"
|
|
3631
|
+
]
|
|
3632
|
+
},
|
|
3633
|
+
{
|
|
3634
|
+
"method": "DELETE",
|
|
3635
|
+
"path": "/api/organizations/{orgId}/invitations/{inviteId}",
|
|
3636
|
+
"operationId": "deleteInvitation",
|
|
3637
|
+
"summary": "Revoke an invitation.",
|
|
3638
|
+
"tag": "Invitations",
|
|
3639
|
+
"params": [
|
|
3640
|
+
"orgId",
|
|
3641
|
+
"inviteId"
|
|
3642
|
+
]
|
|
3643
|
+
},
|
|
3644
|
+
{
|
|
3645
|
+
"method": "GET",
|
|
3646
|
+
"path": "/api/organizations/{orgId}/projects",
|
|
3647
|
+
"operationId": "listProjects",
|
|
3648
|
+
"summary": "List projects in an organization.",
|
|
3649
|
+
"tag": "Projects",
|
|
3650
|
+
"params": [
|
|
3651
|
+
"orgId"
|
|
3652
|
+
]
|
|
3653
|
+
},
|
|
3654
|
+
{
|
|
3655
|
+
"method": "POST",
|
|
3656
|
+
"path": "/api/organizations/{orgId}/projects",
|
|
3657
|
+
"operationId": "createProject",
|
|
3658
|
+
"summary": "Create a project.",
|
|
3659
|
+
"tag": "Projects",
|
|
3660
|
+
"params": [
|
|
3661
|
+
"orgId"
|
|
3662
|
+
]
|
|
3663
|
+
},
|
|
3664
|
+
{
|
|
3665
|
+
"method": "GET",
|
|
3666
|
+
"path": "/api/organizations/{orgId}/projects/{projectId}",
|
|
3667
|
+
"operationId": "getProject",
|
|
3668
|
+
"summary": "Get project details.",
|
|
3669
|
+
"tag": "Projects",
|
|
3670
|
+
"params": [
|
|
3671
|
+
"orgId",
|
|
3672
|
+
"projectId"
|
|
3673
|
+
]
|
|
3674
|
+
},
|
|
3675
|
+
{
|
|
3676
|
+
"method": "PATCH",
|
|
3677
|
+
"path": "/api/organizations/{orgId}/projects/{projectId}",
|
|
3678
|
+
"operationId": "updateProject",
|
|
3679
|
+
"summary": "Update a project.",
|
|
3680
|
+
"tag": "Projects",
|
|
3681
|
+
"params": [
|
|
3682
|
+
"orgId",
|
|
3683
|
+
"projectId"
|
|
3684
|
+
]
|
|
3685
|
+
},
|
|
3686
|
+
{
|
|
3687
|
+
"method": "DELETE",
|
|
3688
|
+
"path": "/api/organizations/{orgId}/projects/{projectId}",
|
|
3689
|
+
"operationId": "deleteProject",
|
|
3690
|
+
"summary": "Delete a project.",
|
|
3691
|
+
"tag": "Projects",
|
|
3692
|
+
"params": [
|
|
3693
|
+
"orgId",
|
|
3694
|
+
"projectId"
|
|
3695
|
+
]
|
|
3696
|
+
},
|
|
3697
|
+
{
|
|
3698
|
+
"method": "GET",
|
|
3699
|
+
"path": "/api/projects/resolve",
|
|
3700
|
+
"operationId": "resolveProject",
|
|
3701
|
+
"summary": "Resolve an API key to its project and organization.",
|
|
3702
|
+
"tag": "Projects",
|
|
3703
|
+
"params": []
|
|
3704
|
+
},
|
|
3705
|
+
{
|
|
3706
|
+
"method": "GET",
|
|
3707
|
+
"path": "/api/organizations/{orgId}/projects/{projectId}/api-keys",
|
|
3708
|
+
"operationId": "listApiKeys",
|
|
3709
|
+
"summary": "List API keys for a project.",
|
|
3710
|
+
"tag": "API Keys",
|
|
3711
|
+
"params": [
|
|
3712
|
+
"orgId",
|
|
3713
|
+
"projectId"
|
|
3714
|
+
]
|
|
3715
|
+
},
|
|
3716
|
+
{
|
|
3717
|
+
"method": "POST",
|
|
3718
|
+
"path": "/api/organizations/{orgId}/projects/{projectId}/api-keys",
|
|
3719
|
+
"operationId": "createApiKey",
|
|
3720
|
+
"summary": "Create an API key.",
|
|
3721
|
+
"tag": "API Keys",
|
|
3722
|
+
"params": [
|
|
3723
|
+
"orgId",
|
|
3724
|
+
"projectId"
|
|
3725
|
+
]
|
|
3726
|
+
},
|
|
3727
|
+
{
|
|
3728
|
+
"method": "PATCH",
|
|
3729
|
+
"path": "/api/organizations/{orgId}/projects/{projectId}/api-keys/{keyId}",
|
|
3730
|
+
"operationId": "revokeApiKey",
|
|
3731
|
+
"summary": "Revoke an API key.",
|
|
3732
|
+
"tag": "API Keys",
|
|
3733
|
+
"params": [
|
|
3734
|
+
"orgId",
|
|
3735
|
+
"projectId",
|
|
3736
|
+
"keyId"
|
|
3737
|
+
]
|
|
3738
|
+
},
|
|
3739
|
+
{
|
|
3740
|
+
"method": "DELETE",
|
|
3741
|
+
"path": "/api/organizations/{orgId}/projects/{projectId}/api-keys/{keyId}",
|
|
3742
|
+
"operationId": "deleteApiKey",
|
|
3743
|
+
"summary": "Permanently delete an API key.",
|
|
3744
|
+
"tag": "API Keys",
|
|
3745
|
+
"params": [
|
|
3746
|
+
"orgId",
|
|
3747
|
+
"projectId",
|
|
3748
|
+
"keyId"
|
|
3749
|
+
]
|
|
3750
|
+
},
|
|
3751
|
+
{
|
|
3752
|
+
"method": "GET",
|
|
3753
|
+
"path": "/api/organizations/{orgId}/projects/{projectId}/reports",
|
|
3754
|
+
"operationId": "listProjectReports",
|
|
3755
|
+
"summary": "List feedback for a project.",
|
|
3756
|
+
"tag": "Reports",
|
|
3757
|
+
"params": [
|
|
3758
|
+
"orgId",
|
|
3759
|
+
"projectId"
|
|
3760
|
+
]
|
|
3761
|
+
},
|
|
3762
|
+
{
|
|
3763
|
+
"method": "GET",
|
|
3764
|
+
"path": "/api/organizations/{orgId}/projects/{projectId}/feedback/{reportId}",
|
|
3765
|
+
"operationId": "getReport",
|
|
3766
|
+
"summary": "Get feedback details.",
|
|
3767
|
+
"tag": "Reports",
|
|
3768
|
+
"params": [
|
|
3769
|
+
"orgId",
|
|
3770
|
+
"projectId",
|
|
3771
|
+
"reportId"
|
|
3772
|
+
]
|
|
3773
|
+
},
|
|
3774
|
+
{
|
|
3775
|
+
"method": "PATCH",
|
|
3776
|
+
"path": "/api/organizations/{orgId}/projects/{projectId}/feedback/{reportId}",
|
|
3777
|
+
"operationId": "updateReport",
|
|
3778
|
+
"summary": "Update feedback status, priority, or assignment.",
|
|
3779
|
+
"tag": "Reports",
|
|
3780
|
+
"params": [
|
|
3781
|
+
"orgId",
|
|
3782
|
+
"projectId",
|
|
3783
|
+
"reportId"
|
|
3784
|
+
]
|
|
3785
|
+
},
|
|
3786
|
+
{
|
|
3787
|
+
"method": "DELETE",
|
|
3788
|
+
"path": "/api/organizations/{orgId}/projects/{projectId}/feedback/{reportId}",
|
|
3789
|
+
"operationId": "deleteReport",
|
|
3790
|
+
"summary": "Delete a feedback record.",
|
|
3791
|
+
"tag": "Reports",
|
|
3792
|
+
"params": [
|
|
3793
|
+
"orgId",
|
|
3794
|
+
"projectId",
|
|
3795
|
+
"reportId"
|
|
3796
|
+
]
|
|
3797
|
+
},
|
|
3798
|
+
{
|
|
3799
|
+
"method": "POST",
|
|
3800
|
+
"path": "/api/organizations/{orgId}/projects/{projectId}/reports/archive",
|
|
3801
|
+
"operationId": "bulkArchiveReport",
|
|
3802
|
+
"summary": "Archive multiple feedback records.",
|
|
3803
|
+
"tag": "Reports",
|
|
3804
|
+
"params": [
|
|
3805
|
+
"orgId",
|
|
3806
|
+
"projectId"
|
|
3807
|
+
]
|
|
3808
|
+
},
|
|
3809
|
+
{
|
|
3810
|
+
"method": "GET",
|
|
3811
|
+
"path": "/api/organizations/{orgId}/integrations",
|
|
3812
|
+
"operationId": "listIntegrations",
|
|
3813
|
+
"summary": "List integrations for an organization.",
|
|
3814
|
+
"tag": "Integrations",
|
|
3815
|
+
"params": [
|
|
3816
|
+
"orgId"
|
|
3817
|
+
]
|
|
3818
|
+
},
|
|
3819
|
+
{
|
|
3820
|
+
"method": "POST",
|
|
3821
|
+
"path": "/api/organizations/{orgId}/integrations",
|
|
3822
|
+
"operationId": "createIntegration",
|
|
3823
|
+
"summary": "Create an integration.",
|
|
3824
|
+
"tag": "Integrations",
|
|
3825
|
+
"params": [
|
|
3826
|
+
"orgId"
|
|
3827
|
+
]
|
|
3828
|
+
},
|
|
3829
|
+
{
|
|
3830
|
+
"method": "GET",
|
|
3831
|
+
"path": "/api/organizations/{orgId}/integrations/{integrationId}",
|
|
3832
|
+
"operationId": "getIntegration",
|
|
3833
|
+
"summary": "Get integration details.",
|
|
3834
|
+
"tag": "Integrations",
|
|
3835
|
+
"params": [
|
|
3836
|
+
"orgId",
|
|
3837
|
+
"integrationId"
|
|
3838
|
+
]
|
|
3839
|
+
},
|
|
3840
|
+
{
|
|
3841
|
+
"method": "DELETE",
|
|
3842
|
+
"path": "/api/organizations/{orgId}/integrations/{integrationId}",
|
|
3843
|
+
"operationId": "deleteIntegration",
|
|
3844
|
+
"summary": "Delete an integration.",
|
|
3845
|
+
"tag": "Integrations",
|
|
3846
|
+
"params": [
|
|
3847
|
+
"orgId",
|
|
3848
|
+
"integrationId"
|
|
3849
|
+
]
|
|
3850
|
+
},
|
|
3851
|
+
{
|
|
3852
|
+
"method": "GET",
|
|
3853
|
+
"path": "/api/organizations/{orgId}/integrations/{integrationId}/targets",
|
|
3854
|
+
"operationId": "listIntegrationTargets",
|
|
3855
|
+
"summary": "List available targets (e.g. Jira projects/boards).",
|
|
3856
|
+
"tag": "Integrations",
|
|
3857
|
+
"params": [
|
|
3858
|
+
"orgId",
|
|
3859
|
+
"integrationId"
|
|
3860
|
+
]
|
|
3861
|
+
},
|
|
3862
|
+
{
|
|
3863
|
+
"method": "POST",
|
|
3864
|
+
"path": "/api/organizations/{orgId}/integrations/{integrationId}/test",
|
|
3865
|
+
"operationId": "testIntegration",
|
|
3866
|
+
"summary": "Test integration connectivity.",
|
|
3867
|
+
"tag": "Integrations",
|
|
3868
|
+
"params": [
|
|
3869
|
+
"orgId",
|
|
3870
|
+
"integrationId"
|
|
3871
|
+
]
|
|
3872
|
+
},
|
|
3873
|
+
{
|
|
3874
|
+
"method": "GET",
|
|
3875
|
+
"path": "/api/organizations/{orgId}/external-issues",
|
|
3876
|
+
"operationId": "listExternalIssues",
|
|
3877
|
+
"summary": "List external issues, optionally filtered by feedback.",
|
|
3878
|
+
"tag": "External Issues",
|
|
3879
|
+
"params": [
|
|
3880
|
+
"orgId"
|
|
3881
|
+
]
|
|
3882
|
+
},
|
|
3883
|
+
{
|
|
3884
|
+
"method": "GET",
|
|
3885
|
+
"path": "/api/organizations/{orgId}/issue-drafts",
|
|
3886
|
+
"operationId": "listIssueDrafts",
|
|
3887
|
+
"summary": "List issue drafts for a project.",
|
|
3888
|
+
"tag": "Issue Drafts",
|
|
3889
|
+
"params": [
|
|
3890
|
+
"orgId"
|
|
3891
|
+
]
|
|
3892
|
+
},
|
|
3893
|
+
{
|
|
3894
|
+
"method": "POST",
|
|
3895
|
+
"path": "/api/organizations/{orgId}/issue-drafts",
|
|
3896
|
+
"operationId": "createIssueDraft",
|
|
3897
|
+
"summary": "Manually create an issue draft.",
|
|
3898
|
+
"tag": "Issue Drafts",
|
|
3899
|
+
"params": [
|
|
3900
|
+
"orgId"
|
|
3901
|
+
]
|
|
3902
|
+
},
|
|
3903
|
+
{
|
|
3904
|
+
"method": "PATCH",
|
|
3905
|
+
"path": "/api/organizations/{orgId}/issue-drafts/{draftId}",
|
|
3906
|
+
"operationId": "updateIssueDraft",
|
|
3907
|
+
"summary": "Update an issue draft.",
|
|
3908
|
+
"tag": "Issue Drafts",
|
|
3909
|
+
"params": [
|
|
3910
|
+
"orgId",
|
|
3911
|
+
"draftId"
|
|
3912
|
+
]
|
|
3913
|
+
},
|
|
3914
|
+
{
|
|
3915
|
+
"method": "DELETE",
|
|
3916
|
+
"path": "/api/organizations/{orgId}/issue-drafts/{draftId}",
|
|
3917
|
+
"operationId": "deleteIssueDraft",
|
|
3918
|
+
"summary": "Delete an issue draft.",
|
|
3919
|
+
"tag": "Issue Drafts",
|
|
3920
|
+
"params": [
|
|
3921
|
+
"orgId",
|
|
3922
|
+
"draftId"
|
|
3923
|
+
]
|
|
3924
|
+
},
|
|
3925
|
+
{
|
|
3926
|
+
"method": "POST",
|
|
3927
|
+
"path": "/api/organizations/{orgId}/issue-drafts/{draftId}/submit",
|
|
3928
|
+
"operationId": "submitIssueDraft",
|
|
3929
|
+
"summary": "Submit a draft to the configured integration.",
|
|
3930
|
+
"tag": "Issue Drafts",
|
|
3931
|
+
"params": [
|
|
3932
|
+
"orgId",
|
|
3933
|
+
"draftId"
|
|
3934
|
+
]
|
|
3935
|
+
},
|
|
3936
|
+
{
|
|
3937
|
+
"method": "POST",
|
|
3938
|
+
"path": "/api/organizations/{orgId}/projects/{projectId}/triage",
|
|
3939
|
+
"operationId": "runTriage",
|
|
3940
|
+
"summary": "Run LLM-powered triage on untriaged feedback.",
|
|
3941
|
+
"tag": "Triage",
|
|
3942
|
+
"params": [
|
|
3943
|
+
"orgId",
|
|
3944
|
+
"projectId"
|
|
3945
|
+
]
|
|
3946
|
+
},
|
|
3947
|
+
{
|
|
3948
|
+
"method": "GET",
|
|
3949
|
+
"path": "/api/user/tokens",
|
|
3950
|
+
"operationId": "listUserTokens",
|
|
3951
|
+
"summary": "List personal access tokens.",
|
|
3952
|
+
"tag": "User Tokens",
|
|
3953
|
+
"params": []
|
|
3954
|
+
},
|
|
3955
|
+
{
|
|
3956
|
+
"method": "POST",
|
|
3957
|
+
"path": "/api/user/tokens",
|
|
3958
|
+
"operationId": "createUserToken",
|
|
3959
|
+
"summary": "Create a personal access token.",
|
|
3960
|
+
"tag": "User Tokens",
|
|
3961
|
+
"params": []
|
|
3962
|
+
},
|
|
3963
|
+
{
|
|
3964
|
+
"method": "DELETE",
|
|
3965
|
+
"path": "/api/user/tokens/{tokenId}",
|
|
3966
|
+
"operationId": "deleteUserToken",
|
|
3967
|
+
"summary": "Revoke and delete a personal access token.",
|
|
3968
|
+
"tag": "User Tokens",
|
|
3969
|
+
"params": [
|
|
3970
|
+
"tokenId"
|
|
3971
|
+
]
|
|
3972
|
+
}
|
|
3973
|
+
];
|
|
3974
|
+
|
|
3975
|
+
// src/scanner/cli/api.ts
|
|
3976
|
+
var METHODS = /* @__PURE__ */ new Set([
|
|
3977
|
+
"GET",
|
|
3978
|
+
"POST",
|
|
3979
|
+
"PUT",
|
|
3980
|
+
"PATCH",
|
|
3981
|
+
"DELETE",
|
|
3982
|
+
"HEAD",
|
|
3983
|
+
"OPTIONS"
|
|
3984
|
+
]);
|
|
3985
|
+
var HELP = `uidex api \u2014 call the uidex API
|
|
3986
|
+
|
|
3987
|
+
Usage:
|
|
3988
|
+
uidex api <METHOD> <PATH> [flags] Make an API request
|
|
3989
|
+
uidex api --list [--tag <tag>] List available routes
|
|
3990
|
+
uidex api login Authenticate via browser
|
|
3991
|
+
uidex api login --token <tok> Store an auth token directly
|
|
3992
|
+
uidex api status Check auth state
|
|
3993
|
+
|
|
3994
|
+
Flags:
|
|
3995
|
+
--body <json> Request body (JSON string)
|
|
3996
|
+
--query <params> Query string (key=val&key2=val2)
|
|
3997
|
+
--base-url <url> Override the API base URL
|
|
3998
|
+
--token <tok> Use a specific token (instead of stored)
|
|
3999
|
+
--raw Disable JSON pretty-printing
|
|
4000
|
+
--no-color Disable colored output
|
|
4001
|
+
--list List available API routes
|
|
4002
|
+
--tag <tag> Filter --list by tag
|
|
4003
|
+
--json Emit --list as JSON
|
|
4004
|
+
`;
|
|
4005
|
+
async function runApiCommand(opts) {
|
|
4006
|
+
const { positional, flags } = parseArgs(opts.argv);
|
|
4007
|
+
const stdout = [];
|
|
4008
|
+
const stderr = [];
|
|
4009
|
+
const color = opts.color ?? (flags["no-color"] ? false : process.stdout.isTTY ?? false);
|
|
4010
|
+
const ctx = { flags, opts, color, stdout, stderr };
|
|
4011
|
+
const sub = positional[0];
|
|
4012
|
+
if (!sub && !flags.list) {
|
|
4013
|
+
stdout.push(HELP);
|
|
4014
|
+
return result(0, stdout, stderr);
|
|
4015
|
+
}
|
|
4016
|
+
if (flags.list) return listRoutes(ctx);
|
|
4017
|
+
if (sub === "help" || sub === "--help" || sub === "-h") {
|
|
4018
|
+
stdout.push(HELP);
|
|
4019
|
+
return result(0, stdout, stderr);
|
|
4020
|
+
}
|
|
4021
|
+
if (sub === "login") return runLogin(ctx);
|
|
4022
|
+
if (sub === "status") return runStatus(ctx);
|
|
4023
|
+
const method = sub?.toUpperCase();
|
|
4024
|
+
const path10 = positional[1];
|
|
4025
|
+
if (!method || !METHODS.has(method)) {
|
|
4026
|
+
stderr.push(`Unknown command or method: ${sub}`);
|
|
4027
|
+
stderr.push(HELP);
|
|
4028
|
+
return result(1, stdout, stderr);
|
|
4029
|
+
}
|
|
4030
|
+
if (!path10) {
|
|
4031
|
+
stderr.push("Missing path. Usage: uidex api GET /api/organizations");
|
|
4032
|
+
return result(1, stdout, stderr);
|
|
4033
|
+
}
|
|
4034
|
+
return runRequest(ctx, method, path10);
|
|
4035
|
+
}
|
|
4036
|
+
function listRoutes(ctx) {
|
|
4037
|
+
const { flags, color, stdout, stderr } = ctx;
|
|
4038
|
+
let routes = API_ROUTES;
|
|
4039
|
+
const tagFilter = flags.tag;
|
|
4040
|
+
if (typeof tagFilter === "string") {
|
|
4041
|
+
routes = routes.filter(
|
|
4042
|
+
(r) => r.tag.toLowerCase() === tagFilter.toLowerCase()
|
|
4043
|
+
);
|
|
4044
|
+
}
|
|
4045
|
+
if (flags.json) {
|
|
4046
|
+
stdout.push(JSON.stringify(routes, null, 2));
|
|
4047
|
+
return result(0, stdout, stderr);
|
|
4048
|
+
}
|
|
4049
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4050
|
+
for (const r of routes) {
|
|
4051
|
+
const tag = r.tag || "Other";
|
|
4052
|
+
let list = grouped.get(tag);
|
|
4053
|
+
if (!list) {
|
|
4054
|
+
list = [];
|
|
4055
|
+
grouped.set(tag, list);
|
|
4056
|
+
}
|
|
4057
|
+
list.push(r);
|
|
4058
|
+
}
|
|
4059
|
+
const methodPad = 7;
|
|
4060
|
+
for (const [tag, tagRoutes] of grouped) {
|
|
4061
|
+
stdout.push("");
|
|
4062
|
+
stdout.push(boldText(tag, color));
|
|
4063
|
+
for (const r of tagRoutes) {
|
|
4064
|
+
const m = r.method.padEnd(methodPad);
|
|
4065
|
+
const desc = r.summary ? dimText(` ${r.operationId} \u2014 ${r.summary}`, color) : dimText(` ${r.operationId}`, color);
|
|
4066
|
+
stdout.push(` ${m} ${r.path}${desc}`);
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
stdout.push("");
|
|
4070
|
+
return result(0, stdout, stderr);
|
|
4071
|
+
}
|
|
4072
|
+
async function runLogin(ctx) {
|
|
4073
|
+
const { flags, opts, color, stdout, stderr } = ctx;
|
|
4074
|
+
const token = flags.token;
|
|
4075
|
+
if (typeof token === "string" && token.length > 0) {
|
|
4076
|
+
const storage = resolveTokenStorage(opts);
|
|
4077
|
+
storage.set(token);
|
|
4078
|
+
stdout.push("Token stored.");
|
|
4079
|
+
return result(0, stdout, stderr);
|
|
4080
|
+
}
|
|
4081
|
+
return runBrowserLogin(ctx);
|
|
4082
|
+
}
|
|
4083
|
+
async function runBrowserLogin(ctx) {
|
|
4084
|
+
const { flags, opts, color, stdout, stderr } = ctx;
|
|
4085
|
+
const http = await import("http");
|
|
4086
|
+
const crypto = await import("crypto");
|
|
4087
|
+
const { exec } = await import("child_process");
|
|
4088
|
+
const baseUrl = resolveBaseUrl(flags, opts);
|
|
4089
|
+
const code = crypto.randomBytes(3).toString("hex").toUpperCase();
|
|
4090
|
+
return new Promise((resolve7) => {
|
|
4091
|
+
let settled = false;
|
|
4092
|
+
const settle = (exitCode) => {
|
|
4093
|
+
if (settled) return;
|
|
4094
|
+
settled = true;
|
|
4095
|
+
server.close();
|
|
4096
|
+
clearTimeout(timeout);
|
|
4097
|
+
resolve7(result(exitCode, stdout, stderr));
|
|
4098
|
+
};
|
|
4099
|
+
const timeout = setTimeout(() => {
|
|
4100
|
+
stderr.push("Timed out waiting for browser authorization.");
|
|
4101
|
+
settle(1);
|
|
4102
|
+
}, 12e4);
|
|
4103
|
+
const server = http.createServer((req, res) => {
|
|
4104
|
+
if (!req.url?.startsWith("/callback")) {
|
|
4105
|
+
res.writeHead(404);
|
|
4106
|
+
res.end();
|
|
4107
|
+
return;
|
|
4108
|
+
}
|
|
4109
|
+
const url = new URL(req.url, `http://127.0.0.1`);
|
|
4110
|
+
const receivedToken = url.searchParams.get("token");
|
|
4111
|
+
const receivedCode = url.searchParams.get("code");
|
|
4112
|
+
if (receivedCode !== code) {
|
|
4113
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
4114
|
+
res.end(JSON.stringify({ error: "Code mismatch" }));
|
|
4115
|
+
stderr.push("Received callback with mismatched code. Aborting.");
|
|
4116
|
+
settle(1);
|
|
4117
|
+
return;
|
|
4118
|
+
}
|
|
4119
|
+
if (!receivedToken) {
|
|
4120
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
4121
|
+
res.end(JSON.stringify({ error: "Missing token" }));
|
|
4122
|
+
stderr.push("Received callback without token. Aborting.");
|
|
4123
|
+
settle(1);
|
|
4124
|
+
return;
|
|
4125
|
+
}
|
|
4126
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
4127
|
+
res.end(JSON.stringify({ ok: true }));
|
|
4128
|
+
const storage = resolveTokenStorage(opts);
|
|
4129
|
+
storage.set(receivedToken);
|
|
4130
|
+
stdout.push(dimText("Authenticated successfully.", color));
|
|
4131
|
+
settle(0);
|
|
4132
|
+
});
|
|
4133
|
+
server.listen(0, "127.0.0.1", () => {
|
|
4134
|
+
const addr = server.address();
|
|
4135
|
+
if (!addr || typeof addr === "string") {
|
|
4136
|
+
stderr.push("Failed to start local server.");
|
|
4137
|
+
settle(1);
|
|
4138
|
+
return;
|
|
4139
|
+
}
|
|
4140
|
+
const port = addr.port;
|
|
4141
|
+
const authUrl = `${baseUrl}/cli-auth?code=${encodeURIComponent(code)}&port=${port}`;
|
|
4142
|
+
stdout.push(dimText("Opening browser to authorize...", color));
|
|
4143
|
+
stdout.push("");
|
|
4144
|
+
stdout.push(` Confirmation code: ${boldText(code, color)}`);
|
|
4145
|
+
stdout.push("");
|
|
4146
|
+
stdout.push(
|
|
4147
|
+
dimText("If the browser doesn't open, visit this URL manually:", color)
|
|
4148
|
+
);
|
|
4149
|
+
stdout.push(` ${authUrl}`);
|
|
4150
|
+
stdout.push("");
|
|
4151
|
+
process.stdout.write(result(0, stdout, stderr).stdout);
|
|
4152
|
+
stdout.length = 0;
|
|
4153
|
+
openBrowser(authUrl);
|
|
4154
|
+
});
|
|
4155
|
+
server.on("error", (err2) => {
|
|
4156
|
+
stderr.push(`Failed to start local server: ${err2.message}`);
|
|
4157
|
+
settle(1);
|
|
4158
|
+
});
|
|
4159
|
+
});
|
|
4160
|
+
}
|
|
4161
|
+
function openBrowser(url) {
|
|
4162
|
+
const { exec } = require("child_process");
|
|
4163
|
+
const platform = process.platform;
|
|
4164
|
+
const cmd = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
|
|
4165
|
+
exec(`${cmd} ${JSON.stringify(url)}`);
|
|
4166
|
+
}
|
|
4167
|
+
function runStatus(ctx) {
|
|
4168
|
+
const { flags, opts, stdout, stderr } = ctx;
|
|
4169
|
+
const storage = resolveTokenStorage(opts);
|
|
4170
|
+
const baseUrl = resolveBaseUrl(flags, opts);
|
|
4171
|
+
const token = storage.get();
|
|
4172
|
+
stdout.push(`baseUrl: ${baseUrl}`);
|
|
4173
|
+
stdout.push(`auth: ${token ? "authenticated" : "not authenticated"}`);
|
|
4174
|
+
return result(token ? 0 : 1, stdout, stderr);
|
|
4175
|
+
}
|
|
4176
|
+
async function runRequest(ctx, method, path10) {
|
|
4177
|
+
const { flags, opts, color, stdout, stderr } = ctx;
|
|
4178
|
+
const tokenFromFlag = flags.token;
|
|
4179
|
+
const token = typeof tokenFromFlag === "string" ? tokenFromFlag : resolveTokenStorage(opts).get();
|
|
4180
|
+
if (!token) {
|
|
4181
|
+
stderr.push("Not authenticated. Run: uidex api login --token <value>");
|
|
4182
|
+
return result(1, stdout, stderr);
|
|
4183
|
+
}
|
|
4184
|
+
const baseUrl = resolveBaseUrl(flags, opts);
|
|
4185
|
+
const http = opts.http ?? createHttpClient({ baseUrl, token: () => token });
|
|
4186
|
+
const bodyStr = typeof flags.body === "string" ? flags.body : void 0;
|
|
4187
|
+
if (bodyStr !== void 0) {
|
|
4188
|
+
try {
|
|
4189
|
+
JSON.parse(bodyStr);
|
|
4190
|
+
} catch {
|
|
4191
|
+
stderr.push("Invalid JSON in --body");
|
|
4192
|
+
return result(1, stdout, stderr);
|
|
4193
|
+
}
|
|
4194
|
+
}
|
|
4195
|
+
const res = await http.request(path10, {
|
|
4196
|
+
method,
|
|
4197
|
+
body: bodyStr,
|
|
4198
|
+
query: typeof flags.query === "string" ? flags.query : void 0
|
|
4199
|
+
});
|
|
4200
|
+
const isSuccess = res.status >= 200 && res.status < 300;
|
|
4201
|
+
if (!isSuccess) {
|
|
4202
|
+
stderr.push(formatStatus(res.status, color));
|
|
4203
|
+
}
|
|
4204
|
+
if (res.body !== void 0 && res.body !== null && res.body !== "") {
|
|
4205
|
+
if (flags.raw) {
|
|
4206
|
+
stdout.push(res.raw);
|
|
4207
|
+
} else {
|
|
4208
|
+
stdout.push(highlightJson(res.body, color));
|
|
4209
|
+
}
|
|
4210
|
+
}
|
|
4211
|
+
return result(isSuccess ? 0 : 1, stdout, stderr);
|
|
4212
|
+
}
|
|
4213
|
+
function resolveTokenStorage(opts) {
|
|
4214
|
+
if (opts.tokenStorage) return opts.tokenStorage;
|
|
4215
|
+
const envToken = opts.env?.UIDEX_TOKEN;
|
|
4216
|
+
if (envToken) return createMemoryTokenStorage(envToken);
|
|
4217
|
+
return createFileTokenStorage({ path: defaultTokenPath() });
|
|
4218
|
+
}
|
|
4219
|
+
function resolveBaseUrl(flags, opts) {
|
|
4220
|
+
return (typeof flags["base-url"] === "string" ? flags["base-url"] : void 0) ?? opts.baseUrl ?? opts.env?.UIDEX_API_URL ?? "https://app.uidex.dev";
|
|
4221
|
+
}
|
|
4222
|
+
function result(exitCode, stdout, stderr) {
|
|
4223
|
+
return {
|
|
4224
|
+
exitCode,
|
|
4225
|
+
stdout: stdout.join("\n") + (stdout.length ? "\n" : ""),
|
|
4226
|
+
stderr: stderr.join("\n") + (stderr.length ? "\n" : "")
|
|
4227
|
+
};
|
|
4228
|
+
}
|
|
4229
|
+
|
|
4230
|
+
// src/scanner/cli/cli.ts
|
|
4231
|
+
var argv = process.argv.slice(2);
|
|
4232
|
+
var command = argv[0];
|
|
4233
|
+
var resultP = command === "api" ? runApiCommand({ argv: argv.slice(1) }) : run({ argv });
|
|
4234
|
+
resultP.then(
|
|
4235
|
+
(result2) => {
|
|
4236
|
+
if (result2.stdout) process.stdout.write(result2.stdout);
|
|
4237
|
+
if (result2.stderr) process.stderr.write(result2.stderr);
|
|
4238
|
+
process.exit(result2.exitCode);
|
|
3316
4239
|
},
|
|
3317
4240
|
(error) => {
|
|
3318
4241
|
process.stderr.write(
|