@exposify/cli 0.0.3 → 0.0.5
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/index.d.ts +9 -0
- package/dist/index.js +31 -12
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ interface ScannedRoute {
|
|
|
23
23
|
exposed: boolean;
|
|
24
24
|
/** Exposed only when listed in `expose`, even a read (core's RouteInfo.optIn), e.g. a Pages Router method detected in code. */
|
|
25
25
|
optIn?: boolean;
|
|
26
|
+
/** Not a tool unless listed in `expose`, so setup leaves it unticked though it's a read: a page that's just a form, a route that finishes a sign-in. */
|
|
27
|
+
offByDefault?: boolean;
|
|
26
28
|
/** "page": an App Router page read as a tool (its HTML, like a browser), not a route handler. */
|
|
27
29
|
kind?: "page";
|
|
28
30
|
reason: string;
|
|
@@ -215,8 +217,13 @@ interface ApplyPlan {
|
|
|
215
217
|
after: string;
|
|
216
218
|
}
|
|
217
219
|
/** Either a plan, or why this one can't be applied safely (setup then prints the paste instructions). */
|
|
220
|
+
/** `also`: edits to other files that go with the plan, each with what to do by hand when it isn't written. */
|
|
218
221
|
type ApplyOutcome = {
|
|
219
222
|
plan: ApplyPlan;
|
|
223
|
+
also?: Array<{
|
|
224
|
+
plan: ApplyPlan;
|
|
225
|
+
manual: string;
|
|
226
|
+
}>;
|
|
220
227
|
} | {
|
|
221
228
|
blocked: string;
|
|
222
229
|
};
|
|
@@ -300,6 +307,8 @@ interface CliAdapter<R extends ScanResult = ScanResult> {
|
|
|
300
307
|
entryExample: string;
|
|
301
308
|
/** What `--entry` names, for the usage text, e.g. "The file that creates your Express app". */
|
|
302
309
|
entryHelp: string;
|
|
310
|
+
/** The adapter's own commands, for the usage text: ["generate", "Writes …"]. Its bin runs them, not runCli. */
|
|
311
|
+
commands?: Array<[name: string, help: string]>;
|
|
303
312
|
locate(cwd: string, entryFlag?: string): AppLocation | undefined;
|
|
304
313
|
scan(location: AppLocation, env: Record<string, string>): Promise<AdapterScanOutcome<R>>;
|
|
305
314
|
/**
|
package/dist/index.js
CHANGED
|
@@ -47,6 +47,7 @@ import { DESCRIPTIONS_FILE, parseDescriptions } from "@exposify/core";
|
|
|
47
47
|
// src/checklist.ts
|
|
48
48
|
var routeKey = (r) => `${r.method} ${r.path}`;
|
|
49
49
|
var isRead = (r) => r.method === "GET" || r.method === "HEAD";
|
|
50
|
+
var readByDefault = (r) => isRead(r) && !r.offByDefault;
|
|
50
51
|
function isLocked(r) {
|
|
51
52
|
return r.marker !== void 0;
|
|
52
53
|
}
|
|
@@ -55,10 +56,10 @@ function initialState(routes, opts) {
|
|
|
55
56
|
const listed = new Set(opts.listedActions ?? []);
|
|
56
57
|
const selected = /* @__PURE__ */ new Set();
|
|
57
58
|
for (const r of routes) {
|
|
58
|
-
const ticked = opts.hasConfig || isLocked(r) ? r.exposed :
|
|
59
|
+
const ticked = opts.hasConfig || isLocked(r) ? r.exposed : readByDefault(r);
|
|
59
60
|
if (ticked) selected.add(routeKey(r));
|
|
60
61
|
}
|
|
61
|
-
const readsOnly = new Set(routes.filter((r) => isLocked(r) ? r.exposed :
|
|
62
|
+
const readsOnly = new Set(routes.filter((r) => isLocked(r) ? r.exposed : readByDefault(r)).map(routeKey));
|
|
62
63
|
return {
|
|
63
64
|
routes,
|
|
64
65
|
selected,
|
|
@@ -130,7 +131,7 @@ function reduce(state, action) {
|
|
|
130
131
|
case "none":
|
|
131
132
|
return setTicks(state, () => false, true);
|
|
132
133
|
case "readsOnly":
|
|
133
|
-
return setTicks(state,
|
|
134
|
+
return setTicks(state, readByDefault, true);
|
|
134
135
|
case "filter":
|
|
135
136
|
return { ...state, filter: action.text, cursor: 0 };
|
|
136
137
|
case "enterNuclear": {
|
|
@@ -1345,14 +1346,18 @@ function openInBrowser(url) {
|
|
|
1345
1346
|
}
|
|
1346
1347
|
|
|
1347
1348
|
// src/run.ts
|
|
1348
|
-
var usage = (bin, entryHelp) => `Usage: ${bin} <command> [options]
|
|
1349
|
+
var usage = ({ bin, entryHelp, commands: commands2 = [] }) => `Usage: ${bin} <command> [options]
|
|
1349
1350
|
|
|
1350
1351
|
Commands:
|
|
1351
1352
|
setup Pick which endpoints agents can call, then get the aiReady() config to paste
|
|
1352
1353
|
inspect Show what agents see: every route, whether it's a tool, and why
|
|
1353
1354
|
describe Give tools real descriptions (AI-proposed and reviewed, or your own) in
|
|
1354
1355
|
${DESCRIPTIONS_FILE}, which aiReady() loads
|
|
1355
|
-
|
|
1356
|
+
${commands2.map(([name, help]) => name.length > 8 ? ` ${name}
|
|
1357
|
+
${help}
|
|
1358
|
+
` : ` ${name.padEnd(8)} ${help}
|
|
1359
|
+
`).join("")}${commands2.length ? ` (${bin} <command> --help for its options)
|
|
1360
|
+
` : ""}
|
|
1356
1361
|
Options:
|
|
1357
1362
|
--entry <path> ${entryHelp} (default: detected)
|
|
1358
1363
|
--env-vars <path> Load these variables into the scan (setup asks about .env otherwise)
|
|
@@ -1775,7 +1780,9 @@ ${bold("Step 2/5")} \xB7 Print "[agentready] MCP ready \u2026" when your server
|
|
|
1775
1780
|
}
|
|
1776
1781
|
const alreadyExposed = new Set(result.routes.filter((r) => r.exposed).map(routeKey));
|
|
1777
1782
|
const unchanged = result.aiReady && differences(result.routes, selected, alreadyExposed).length === 0 && (current?.startupLog ?? true) === startupLog && currentNuclear === nuclear && sameActions(current?.actions);
|
|
1778
|
-
|
|
1783
|
+
const step = adapter.configStep({ result, config, current, root, readLine: readLineAt });
|
|
1784
|
+
const pendingAlso = step.apply && "plan" in step.apply ? step.apply.also?.length ?? 0 : 0;
|
|
1785
|
+
if (unchanged && pendingAlso === 0) {
|
|
1779
1786
|
console.log(`
|
|
1780
1787
|
${green("\u2713 Already set up")}: your aiReady() config already matches. Nothing to change.`);
|
|
1781
1788
|
if (tty && !args.quick) {
|
|
@@ -1786,7 +1793,7 @@ ${green("\u2713 Already set up")}: your aiReady() config already matches. Nothin
|
|
|
1786
1793
|
await offerSnapshot(args, tty, toolRows(result.routes, result.actions), root, cwd);
|
|
1787
1794
|
return 0;
|
|
1788
1795
|
}
|
|
1789
|
-
const { code, hint, apply } =
|
|
1796
|
+
const { code, hint, apply } = step;
|
|
1790
1797
|
const shouldCopy = args.copy ?? tty;
|
|
1791
1798
|
const copied = shouldCopy ? copyToClipboard(code) : null;
|
|
1792
1799
|
const copyNote = copied?.certain ? green(" (copied to clipboard \u2713)") : copied ? dim(" (sent to your terminal's clipboard, if it supports that)") : "";
|
|
@@ -1795,6 +1802,7 @@ ${bold("Step 3/5")} \xB7 Your aiReady() config${copyNote}
|
|
|
1795
1802
|
`);
|
|
1796
1803
|
console.log(code.replace(/^/gm, " "));
|
|
1797
1804
|
let applied = false;
|
|
1805
|
+
let manual = apply && "plan" in apply ? (apply.also ?? []).map((a) => a.manual) : [];
|
|
1798
1806
|
if (args.apply !== false && (args.apply || tty && !args.quick)) {
|
|
1799
1807
|
if (!apply) {
|
|
1800
1808
|
if (args.apply) console.log(yellow("\n Can't apply automatically here: paste it as described below."));
|
|
@@ -1803,20 +1811,31 @@ ${bold("Step 3/5")} \xB7 Your aiReady() config${copyNote}
|
|
|
1803
1811
|
Can't apply automatically: ${apply.blocked}. Paste it as described below.`));
|
|
1804
1812
|
} else {
|
|
1805
1813
|
const { summary } = apply.plan;
|
|
1814
|
+
const also = apply.also ?? [];
|
|
1815
|
+
const all = [summary, ...also.map((a) => a.plan.summary.replace(/^./, (c) => c.toLowerCase()))];
|
|
1806
1816
|
const yes = args.apply === true || ["y", "yes"].includes((await ask(`
|
|
1807
|
-
Apply this: ${
|
|
1817
|
+
Apply this: ${all.join(", and ")}? ${dim("(y/N)")} `)).toLowerCase());
|
|
1808
1818
|
if (yes) {
|
|
1809
1819
|
const written = writePlan(apply.plan);
|
|
1810
1820
|
if (written.ok) {
|
|
1811
1821
|
applied = true;
|
|
1812
1822
|
console.log(green(` \u2713 Applied: ${summary}.`) + dim(apply.plan.before === null ? "" : " Review it with git diff."));
|
|
1823
|
+
manual = [];
|
|
1824
|
+
for (const a of also) {
|
|
1825
|
+
const done = writePlan(a.plan);
|
|
1826
|
+
if (done.ok) console.log(green(` \u2713 Applied: ${a.plan.summary}.`) + dim(" Review it with git diff."));
|
|
1827
|
+
else {
|
|
1828
|
+
console.log(yellow(` Couldn't apply: ${a.plan.summary} (${done.error}).`));
|
|
1829
|
+
manual.push(a.manual);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1813
1832
|
} else console.log(yellow(` Couldn't apply: ${written.error}. Paste it as described below.`));
|
|
1814
1833
|
}
|
|
1815
1834
|
}
|
|
1816
1835
|
}
|
|
1817
1836
|
if (!applied) console.log(`
|
|
1818
1837
|
${hint.lines[0]}`);
|
|
1819
|
-
for (const line of hint.lines.slice(1)) console.log(` ${line}`);
|
|
1838
|
+
for (const line of [...hint.lines.slice(1), ...manual]) console.log(` ${line}`);
|
|
1820
1839
|
if (hint.warning) console.log(yellow(` ! ${hint.warning}`));
|
|
1821
1840
|
if (droppedGlobs.length > 0) {
|
|
1822
1841
|
console.log(yellow(` ! Dropped exclude ${droppedGlobs.map((g) => `"${g}"`).join(", ")}: it would hide endpoints you ticked.`));
|
|
@@ -2190,11 +2209,11 @@ async function runCli(argv, forAdapter, cwd = process.cwd()) {
|
|
|
2190
2209
|
args = parseArgs(argv);
|
|
2191
2210
|
} catch (err) {
|
|
2192
2211
|
console.error(red(err.message));
|
|
2193
|
-
console.error(usage(adapter
|
|
2212
|
+
console.error(usage(adapter));
|
|
2194
2213
|
return 2;
|
|
2195
2214
|
}
|
|
2196
2215
|
if (args.help || !args.command) {
|
|
2197
|
-
console.log(usage(adapter
|
|
2216
|
+
console.log(usage(adapter));
|
|
2198
2217
|
return args.help ? 0 : 2;
|
|
2199
2218
|
}
|
|
2200
2219
|
if (args.ci) disableColor();
|
|
@@ -2215,7 +2234,7 @@ async function runCli(argv, forAdapter, cwd = process.cwd()) {
|
|
|
2215
2234
|
if (args.command === "setup") return setup(args, cwd);
|
|
2216
2235
|
if (args.command === "describe") return describeCommand(args, cwd);
|
|
2217
2236
|
console.error(red(`unknown command: ${args.command}`));
|
|
2218
|
-
console.error(usage(adapter
|
|
2237
|
+
console.error(usage(adapter));
|
|
2219
2238
|
return 2;
|
|
2220
2239
|
}
|
|
2221
2240
|
export {
|