kanbo-cli 0.1.0 → 0.1.1

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.
Files changed (33) hide show
  1. package/README.md +1 -2
  2. package/dist/cli.cjs +96 -14
  3. package/dist/cli.cjs.map +1 -1
  4. package/dist/lib/{board-store-B16YarvX.d.mts → board-store-BfszzAJB.d.cts} +2 -2
  5. package/dist/lib/{board-store-BHHo_ZlR.d.cts → board-store-BvWn9xTB.d.mts} +2 -2
  6. package/dist/lib/index.cjs +2 -29
  7. package/dist/lib/index.cjs.map +1 -1
  8. package/dist/lib/index.d.cts +363 -363
  9. package/dist/lib/index.d.mts +363 -363
  10. package/dist/lib/index.mjs +1 -28
  11. package/dist/lib/index.mjs.map +1 -1
  12. package/dist/lib/{manifest-C0EcwwgO.mjs → manifest-DfOBqo-Q.mjs} +100 -17
  13. package/dist/lib/manifest-DfOBqo-Q.mjs.map +1 -0
  14. package/dist/lib/{manifest-CpZOcB_6.cjs → manifest-wdwFSquz.cjs} +104 -15
  15. package/dist/lib/manifest-wdwFSquz.cjs.map +1 -0
  16. package/dist/lib/mcp/index.cjs +1 -1
  17. package/dist/lib/mcp/index.d.cts +12 -12
  18. package/dist/lib/mcp/index.d.mts +12 -12
  19. package/dist/lib/mcp/index.mjs +1 -1
  20. package/dist/lib/postgres/index.d.cts +151 -151
  21. package/dist/lib/postgres/index.d.mts +151 -151
  22. package/dist/lib/{schema-fsHqNAip.d.cts → schema-D0MyXcej.d.cts} +41 -41
  23. package/dist/lib/{schema-fsHqNAip.d.mts → schema-D0MyXcej.d.mts} +41 -41
  24. package/dist/lib/sqlite/index.d.cts +12 -12
  25. package/dist/lib/sqlite/index.d.mts +12 -12
  26. package/dist/lib/sqlite/schema.d.cts +1 -1
  27. package/dist/lib/sqlite/schema.d.mts +1 -1
  28. package/dist/lib/{tool-names-9cmCn0gP.d.cts → tool-names-3q_GEZej.d.mts} +5 -5
  29. package/dist/lib/{tool-names-Dxg1UTUl.d.mts → tool-names-WfH4IURz.d.cts} +5 -5
  30. package/dist/page/board.js +1 -1
  31. package/package.json +4 -4
  32. package/dist/lib/manifest-C0EcwwgO.mjs.map +0 -1
  33. package/dist/lib/manifest-CpZOcB_6.cjs.map +0 -1
package/README.md CHANGED
@@ -34,8 +34,7 @@ This creates `.kanbo/board.db`, binds the project to it in `.kanbo/binding.json`
34
34
  ```bash
35
35
  kanbo card create --column to_do --title "Add rate limiting" --description "60 requests per minute per client"
36
36
  kanbo ready
37
- export KANBO_SERVE_TOKEN="$(openssl rand -hex 16)"
38
- kanbo serve # open http://127.0.0.1:4318 and paste the token into the Token field
37
+ kanbo serve # opens the board in your browser
39
38
  ```
40
39
 
41
40
  Now start your agent in the project. It reads the instruction block, runs `kanbo prime`, takes a card from `kanbo ready`, and works it. When it runs `kanbo card wait-approval`, you answer with `kanbo approve <card>` or `kanbo return <card> --comment "..."` — or with the buttons on the board page.
package/dist/cli.cjs CHANGED
@@ -3083,7 +3083,7 @@ var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin((
3083
3083
  * `package.version`. A build carries it as a literal; running from source
3084
3084
  * (tests, `tsx`) reads the package's own `package.json` instead.
3085
3085
  */
3086
- const KANBO_PACKAGE_VERSION = "0.1.0";
3086
+ const KANBO_PACKAGE_VERSION = "0.1.1";
3087
3087
  /**
3088
3088
  * Every exit code this tool promises, and what it means — the capabilities
3089
3089
  * manifest's `exitCodes` reads this rather than naming the codes again. `0` and
@@ -22777,6 +22777,33 @@ function readCardNumber(session, reference) {
22777
22777
  return reference.slice(0, separator).toUpperCase() === prefix ? Number(digits) : null;
22778
22778
  }
22779
22779
  //#endregion
22780
+ //#region src/domain/card-display-title.ts
22781
+ /**
22782
+ * What a card is called wherever a person or an agent reads it by name.
22783
+ *
22784
+ * A card written as a description alone is titled with its own key (every
22785
+ * writer does that). Named by its key, such a card says nothing — so its name
22786
+ * is the first line of its description instead. A browser bundle that does not
22787
+ * load this package keeps the same rule on its own, tested with the same
22788
+ * examples.
22789
+ */
22790
+ /** How long a description line may run before it is cut with an ellipsis. */
22791
+ const DESCRIPTION_LINE_LIMIT = 120;
22792
+ function cardDisplayTitle(card) {
22793
+ if (card.title !== card.id) return card.title;
22794
+ return descriptionFirstLine(card.description) ?? card.title;
22795
+ }
22796
+ function descriptionFirstLine(description) {
22797
+ for (const rawLine of (description ?? "").split("\n")) {
22798
+ const line = stripMarkdown(rawLine);
22799
+ if (line) return line.length > DESCRIPTION_LINE_LIMIT ? `${line.slice(0, 119).trimEnd()}…` : line;
22800
+ }
22801
+ return null;
22802
+ }
22803
+ function stripMarkdown(line) {
22804
+ return line.replace(/^\s*(?:>\s*)+/, "").replace(/^\s*#{1,6}\s+/, "").replace(/^\s*(?:[-*+]|\d+[.)])\s+(?:\[[ x]\]\s+)?/i, "").replace(/!\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/<[^>]+>/g, "").replace(/[*_~`]/g, "").replace(/\s+/g, " ").trim();
22805
+ }
22806
+ //#endregion
22780
22807
  //#region src/cli/view.ts
22781
22808
  /** Every field `kanbo card list|get --json` may ask for, in listing order. */
22782
22809
  const CARD_VIEW_FIELDS = [
@@ -22893,16 +22920,16 @@ function projectRun(run) {
22893
22920
  launchedByKind: run.launchedByKind
22894
22921
  };
22895
22922
  }
22896
- /** One card per line: the key, the column it is in, and what it is called. */
22923
+ /** One card per line: the key, the column it is in, and what it is called (`cardDisplayTitle`). */
22897
22924
  function describeCards(cards) {
22898
22925
  if (cards.length === 0) return "No cards";
22899
22926
  const keyWidth = Math.max(...cards.map((card) => card.id.length));
22900
22927
  const columnWidth = Math.max(...cards.map((card) => (card.column ?? "—").length));
22901
- return cards.map((card) => `${card.id.padEnd(keyWidth)} ${(card.column ?? "—").padEnd(columnWidth)} ${card.title}`).join("\n");
22928
+ return cards.map((card) => `${card.id.padEnd(keyWidth)} ${(card.column ?? "—").padEnd(columnWidth)} ${cardDisplayTitle(card)}`).join("\n");
22902
22929
  }
22903
22930
  /** The whole card, for a person who asked about exactly one. */
22904
22931
  function describeCard(card) {
22905
- const lines = [`${card.id} ${card.column ?? "—"} ${card.executionMode}`, card.title];
22932
+ const lines = [`${card.id} ${card.column ?? "—"} ${card.executionMode}`, cardDisplayTitle(card)];
22906
22933
  if (card.description) lines.push("", card.description);
22907
22934
  const facts = [
22908
22935
  card.statusLine ? `status line: ${card.statusLine}` : null,
@@ -42512,12 +42539,11 @@ async function startKanboServer(options) {
42512
42539
  }
42513
42540
  /**
42514
42541
  * Who may act for a person on this server, said once when it starts: only a
42515
- * request presenting the token does, and a server with no token, or started
42516
- * from an agent's shell, has nobody who may.
42542
+ * request presenting the token does, and a server started from an agent's
42543
+ * shell has nobody who may.
42517
42544
  */
42518
- function describePersonRights(actors, token) {
42545
+ function describePersonRights(actors) {
42519
42546
  if (!actors.person) return "no request acts for a person (started from an agent's shell)";
42520
- if (!token) return "no request acts for a person (no token; set KANBO_SERVE_TOKEN to allow it)";
42521
42547
  return `a request with the token acts for ${actors.person.id}`;
42522
42548
  }
42523
42549
  function stripBrackets(host) {
@@ -42699,12 +42725,58 @@ function sendJson(response, status, value) {
42699
42725
  response.end(body);
42700
42726
  }
42701
42727
  //#endregion
42728
+ //#region src/serve/access.ts
42729
+ /** The token given, or one made up for a loopback server; refused off loopback without one. */
42730
+ function resolveServeAccess(host, givenToken) {
42731
+ assertServeBinding(host, givenToken);
42732
+ if (givenToken) return {
42733
+ token: givenToken,
42734
+ generated: false
42735
+ };
42736
+ return {
42737
+ token: (0, node_crypto.randomBytes)(24).toString("base64url"),
42738
+ generated: true
42739
+ };
42740
+ }
42741
+ /**
42742
+ * Hand the person the board page: a link printed once, and opened in the
42743
+ * browser unless they said not to. A generated token rides in the link's
42744
+ * fragment; one they gave is theirs already and is left out.
42745
+ */
42746
+ function presentServePage(presentation, output) {
42747
+ const { url, access, open } = presentation;
42748
+ if (presentation.agentShell || !presentation.terminal) {
42749
+ if (access.generated) output.print("kanbo serve: a token was generated for this run and is not shown here; set KANBO_SERVE_TOKEN to choose your own");
42750
+ return;
42751
+ }
42752
+ const link = access.generated ? `${url}/#token=${access.token}` : `${url}/`;
42753
+ output.print(`kanbo serve: board page ${link}`);
42754
+ if (open) output.openInBrowser(link);
42755
+ }
42756
+ /** Open a link in the default browser, without waiting for it and without failing when there is none. */
42757
+ function openInBrowser(url) {
42758
+ const [command, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", [
42759
+ "/c",
42760
+ "start",
42761
+ "\"\"",
42762
+ url
42763
+ ]] : ["xdg-open", [url]];
42764
+ try {
42765
+ const child = (0, node_child_process.spawn)(command, args, {
42766
+ detached: true,
42767
+ stdio: "ignore",
42768
+ windowsVerbatimArguments: process.platform === "win32"
42769
+ });
42770
+ child.on("error", () => {});
42771
+ child.unref();
42772
+ } catch {}
42773
+ }
42774
+ //#endregion
42702
42775
  //#region src/cli/commands/serve.ts
42703
42776
  function registerServeCommand(program) {
42704
- program.command("serve").description("serve this board over HTTP, with a board page for the browser").option("--port <port>", "port to listen on", String(DEFAULT_SERVE_PORT)).option("--host <host>", "address to bind; anything but loopback needs a token", DEFAULT_SERVE_HOST).option("--db <path>", "board database file to open").option("--database-url <url>", "external Postgres board to serve instead of a board file").option("--workspace <nameOrId>", "workspace the server is about").option("--token <token>", "bearer token every request must carry (or set KANBO_SERVE_TOKEN)").option("--cors-origin <origin>", "let a browser call from this exact origin; repeat for more", collect, []).action(async (options) => {
42777
+ program.command("serve").description("serve this board over HTTP, with a board page for the browser").option("--port <port>", "port to listen on", String(DEFAULT_SERVE_PORT)).option("--host <host>", "address to bind; anything but loopback needs a token", DEFAULT_SERVE_HOST).option("--db <path>", "board database file to open").option("--database-url <url>", "external Postgres board to serve instead of a board file").option("--workspace <nameOrId>", "workspace the server is about").option("--token <token>", "bearer token every request must carry (or set KANBO_SERVE_TOKEN); made up for the run on loopback when absent").option("--cors-origin <origin>", "let a browser call from this exact origin; repeat for more", collect, []).option("--no-open", "print the board page link without opening it in the browser").action(async (options) => {
42705
42778
  const port = parsePort(options.port);
42706
- const token = options.token?.trim() || process.env.KANBO_SERVE_TOKEN?.trim() || null;
42707
- assertServeBinding(options.host, token);
42779
+ const access = resolveServeAccess(options.host, options.token?.trim() || process.env.KANBO_SERVE_TOKEN?.trim() || null);
42708
42780
  const session = await openBoardSession({
42709
42781
  db: options.db,
42710
42782
  databaseUrl: options.databaseUrl,
@@ -42722,15 +42794,25 @@ function registerServeCommand(program) {
42722
42794
  actors,
42723
42795
  host: options.host,
42724
42796
  port,
42725
- token,
42797
+ token: access.token,
42726
42798
  corsOrigins: options.corsOrigin
42727
42799
  });
42728
42800
  } catch (error) {
42729
42801
  await session.close();
42730
42802
  throw error;
42731
42803
  }
42732
- console.error(`kanbo serve: ${server.url} — workspace ${session.workspace.id}${token ? ", bearer token required" : ""}`);
42733
- console.error(`kanbo serve: ${describePersonRights(actors, token)}`);
42804
+ console.error(`kanbo serve: ${server.url} — workspace ${session.workspace.id}, bearer token required${access.generated ? " (generated for this run)" : ""}`);
42805
+ console.error(`kanbo serve: ${describePersonRights(actors)}`);
42806
+ presentServePage({
42807
+ url: server.url,
42808
+ access,
42809
+ agentShell,
42810
+ terminal: process.stdout.isTTY === true,
42811
+ open: options.open
42812
+ }, {
42813
+ print: (line) => console.log(line),
42814
+ openInBrowser
42815
+ });
42734
42816
  let stopping = false;
42735
42817
  const stop = async () => {
42736
42818
  if (stopping) return;