@exposify/cli 0.0.3 → 0.0.4

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 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 : isRead(r);
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 : isRead(r)).map(routeKey));
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, isRead, true);
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)
@@ -1795,6 +1800,7 @@ ${bold("Step 3/5")} \xB7 Your aiReady() config${copyNote}
1795
1800
  `);
1796
1801
  console.log(code.replace(/^/gm, " "));
1797
1802
  let applied = false;
1803
+ let manual = apply && "plan" in apply ? (apply.also ?? []).map((a) => a.manual) : [];
1798
1804
  if (args.apply !== false && (args.apply || tty && !args.quick)) {
1799
1805
  if (!apply) {
1800
1806
  if (args.apply) console.log(yellow("\n Can't apply automatically here: paste it as described below."));
@@ -1803,20 +1809,31 @@ ${bold("Step 3/5")} \xB7 Your aiReady() config${copyNote}
1803
1809
  Can't apply automatically: ${apply.blocked}. Paste it as described below.`));
1804
1810
  } else {
1805
1811
  const { summary } = apply.plan;
1812
+ const also = apply.also ?? [];
1813
+ const all = [summary, ...also.map((a) => a.plan.summary.replace(/^./, (c) => c.toLowerCase()))];
1806
1814
  const yes = args.apply === true || ["y", "yes"].includes((await ask(`
1807
- Apply this: ${summary}? ${dim("(y/N)")} `)).toLowerCase());
1815
+ Apply this: ${all.join(", and ")}? ${dim("(y/N)")} `)).toLowerCase());
1808
1816
  if (yes) {
1809
1817
  const written = writePlan(apply.plan);
1810
1818
  if (written.ok) {
1811
1819
  applied = true;
1812
1820
  console.log(green(` \u2713 Applied: ${summary}.`) + dim(apply.plan.before === null ? "" : " Review it with git diff."));
1821
+ manual = [];
1822
+ for (const a of also) {
1823
+ const done = writePlan(a.plan);
1824
+ if (done.ok) console.log(green(` \u2713 Applied: ${a.plan.summary}.`) + dim(" Review it with git diff."));
1825
+ else {
1826
+ console.log(yellow(` Couldn't apply: ${a.plan.summary} (${done.error}).`));
1827
+ manual.push(a.manual);
1828
+ }
1829
+ }
1813
1830
  } else console.log(yellow(` Couldn't apply: ${written.error}. Paste it as described below.`));
1814
1831
  }
1815
1832
  }
1816
1833
  }
1817
1834
  if (!applied) console.log(`
1818
1835
  ${hint.lines[0]}`);
1819
- for (const line of hint.lines.slice(1)) console.log(` ${line}`);
1836
+ for (const line of [...hint.lines.slice(1), ...manual]) console.log(` ${line}`);
1820
1837
  if (hint.warning) console.log(yellow(` ! ${hint.warning}`));
1821
1838
  if (droppedGlobs.length > 0) {
1822
1839
  console.log(yellow(` ! Dropped exclude ${droppedGlobs.map((g) => `"${g}"`).join(", ")}: it would hide endpoints you ticked.`));
@@ -2190,11 +2207,11 @@ async function runCli(argv, forAdapter, cwd = process.cwd()) {
2190
2207
  args = parseArgs(argv);
2191
2208
  } catch (err) {
2192
2209
  console.error(red(err.message));
2193
- console.error(usage(adapter.bin, adapter.entryHelp));
2210
+ console.error(usage(adapter));
2194
2211
  return 2;
2195
2212
  }
2196
2213
  if (args.help || !args.command) {
2197
- console.log(usage(adapter.bin, adapter.entryHelp));
2214
+ console.log(usage(adapter));
2198
2215
  return args.help ? 0 : 2;
2199
2216
  }
2200
2217
  if (args.ci) disableColor();
@@ -2215,7 +2232,7 @@ async function runCli(argv, forAdapter, cwd = process.cwd()) {
2215
2232
  if (args.command === "setup") return setup(args, cwd);
2216
2233
  if (args.command === "describe") return describeCommand(args, cwd);
2217
2234
  console.error(red(`unknown command: ${args.command}`));
2218
- console.error(usage(adapter.bin, adapter.entryHelp));
2235
+ console.error(usage(adapter));
2219
2236
  return 2;
2220
2237
  }
2221
2238
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exposify/cli",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",