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
@@ -37,6 +37,7 @@ postgres = __toESM(postgres, 1);
37
37
  let node_url = require("node:url");
38
38
  let _modelcontextprotocol_sdk_server_stdio_js = require("@modelcontextprotocol/sdk/server/stdio.js");
39
39
  let _modelcontextprotocol_sdk_server_mcp_js = require("@modelcontextprotocol/sdk/server/mcp.js");
40
+ let node_child_process = require("node:child_process");
40
41
  let node_http = require("node:http");
41
42
  /**
42
43
  * Every exit code this tool promises, and what it means — the capabilities
@@ -3676,6 +3677,33 @@ function readCardNumber(session, reference) {
3676
3677
  return reference.slice(0, separator).toUpperCase() === prefix ? Number(digits) : null;
3677
3678
  }
3678
3679
  //#endregion
3680
+ //#region src/domain/card-display-title.ts
3681
+ /**
3682
+ * What a card is called wherever a person or an agent reads it by name.
3683
+ *
3684
+ * A card written as a description alone is titled with its own key (every
3685
+ * writer does that). Named by its key, such a card says nothing — so its name
3686
+ * is the first line of its description instead. A browser bundle that does not
3687
+ * load this package keeps the same rule on its own, tested with the same
3688
+ * examples.
3689
+ */
3690
+ /** How long a description line may run before it is cut with an ellipsis. */
3691
+ const DESCRIPTION_LINE_LIMIT = 120;
3692
+ function cardDisplayTitle(card) {
3693
+ if (card.title !== card.id) return card.title;
3694
+ return descriptionFirstLine(card.description) ?? card.title;
3695
+ }
3696
+ function descriptionFirstLine(description) {
3697
+ for (const rawLine of (description ?? "").split("\n")) {
3698
+ const line = stripMarkdown(rawLine);
3699
+ if (line) return line.length > DESCRIPTION_LINE_LIMIT ? `${line.slice(0, 119).trimEnd()}…` : line;
3700
+ }
3701
+ return null;
3702
+ }
3703
+ function stripMarkdown(line) {
3704
+ 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();
3705
+ }
3706
+ //#endregion
3679
3707
  //#region src/cli/view.ts
3680
3708
  /** Every field `kanbo card list|get --json` may ask for, in listing order. */
3681
3709
  const CARD_VIEW_FIELDS = [
@@ -3792,16 +3820,16 @@ function projectRun(run) {
3792
3820
  launchedByKind: run.launchedByKind
3793
3821
  };
3794
3822
  }
3795
- /** One card per line: the key, the column it is in, and what it is called. */
3823
+ /** One card per line: the key, the column it is in, and what it is called (`cardDisplayTitle`). */
3796
3824
  function describeCards(cards) {
3797
3825
  if (cards.length === 0) return "No cards";
3798
3826
  const keyWidth = Math.max(...cards.map((card) => card.id.length));
3799
3827
  const columnWidth = Math.max(...cards.map((card) => (card.column ?? "—").length));
3800
- return cards.map((card) => `${card.id.padEnd(keyWidth)} ${(card.column ?? "—").padEnd(columnWidth)} ${card.title}`).join("\n");
3828
+ return cards.map((card) => `${card.id.padEnd(keyWidth)} ${(card.column ?? "—").padEnd(columnWidth)} ${cardDisplayTitle(card)}`).join("\n");
3801
3829
  }
3802
3830
  /** The whole card, for a person who asked about exactly one. */
3803
3831
  function describeCard(card) {
3804
- const lines = [`${card.id} ${card.column ?? "—"} ${card.executionMode}`, card.title];
3832
+ const lines = [`${card.id} ${card.column ?? "—"} ${card.executionMode}`, cardDisplayTitle(card)];
3805
3833
  if (card.description) lines.push("", card.description);
3806
3834
  const facts = [
3807
3835
  card.statusLine ? `status line: ${card.statusLine}` : null,
@@ -5135,7 +5163,7 @@ async function createDbTransport(input) {
5135
5163
  * `package.version`. A build carries it as a literal; running from source
5136
5164
  * (tests, `tsx`) reads the package's own `package.json` instead.
5137
5165
  */
5138
- const KANBO_PACKAGE_VERSION = "0.1.0";
5166
+ const KANBO_PACKAGE_VERSION = "0.1.1";
5139
5167
  //#endregion
5140
5168
  //#region src/mcp/resources.ts
5141
5169
  /** The URI a client reads the manifest as JSON from. */
@@ -7339,12 +7367,11 @@ async function startKanboServer(options) {
7339
7367
  }
7340
7368
  /**
7341
7369
  * Who may act for a person on this server, said once when it starts: only a
7342
- * request presenting the token does, and a server with no token, or started
7343
- * from an agent's shell, has nobody who may.
7370
+ * request presenting the token does, and a server started from an agent's
7371
+ * shell has nobody who may.
7344
7372
  */
7345
- function describePersonRights(actors, token) {
7373
+ function describePersonRights(actors) {
7346
7374
  if (!actors.person) return "no request acts for a person (started from an agent's shell)";
7347
- if (!token) return "no request acts for a person (no token; set KANBO_SERVE_TOKEN to allow it)";
7348
7375
  return `a request with the token acts for ${actors.person.id}`;
7349
7376
  }
7350
7377
  function stripBrackets(host) {
@@ -7526,12 +7553,58 @@ function sendJson(response, status, value) {
7526
7553
  response.end(body);
7527
7554
  }
7528
7555
  //#endregion
7556
+ //#region src/serve/access.ts
7557
+ /** The token given, or one made up for a loopback server; refused off loopback without one. */
7558
+ function resolveServeAccess(host, givenToken) {
7559
+ assertServeBinding(host, givenToken);
7560
+ if (givenToken) return {
7561
+ token: givenToken,
7562
+ generated: false
7563
+ };
7564
+ return {
7565
+ token: (0, node_crypto.randomBytes)(24).toString("base64url"),
7566
+ generated: true
7567
+ };
7568
+ }
7569
+ /**
7570
+ * Hand the person the board page: a link printed once, and opened in the
7571
+ * browser unless they said not to. A generated token rides in the link's
7572
+ * fragment; one they gave is theirs already and is left out.
7573
+ */
7574
+ function presentServePage(presentation, output) {
7575
+ const { url, access, open } = presentation;
7576
+ if (presentation.agentShell || !presentation.terminal) {
7577
+ 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");
7578
+ return;
7579
+ }
7580
+ const link = access.generated ? `${url}/#token=${access.token}` : `${url}/`;
7581
+ output.print(`kanbo serve: board page ${link}`);
7582
+ if (open) output.openInBrowser(link);
7583
+ }
7584
+ /** Open a link in the default browser, without waiting for it and without failing when there is none. */
7585
+ function openInBrowser(url) {
7586
+ const [command, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["cmd", [
7587
+ "/c",
7588
+ "start",
7589
+ "\"\"",
7590
+ url
7591
+ ]] : ["xdg-open", [url]];
7592
+ try {
7593
+ const child = (0, node_child_process.spawn)(command, args, {
7594
+ detached: true,
7595
+ stdio: "ignore",
7596
+ windowsVerbatimArguments: process.platform === "win32"
7597
+ });
7598
+ child.on("error", () => {});
7599
+ child.unref();
7600
+ } catch {}
7601
+ }
7602
+ //#endregion
7529
7603
  //#region src/cli/commands/serve.ts
7530
7604
  function registerServeCommand(program) {
7531
- 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) => {
7605
+ 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) => {
7532
7606
  const port = parsePort(options.port);
7533
- const token = options.token?.trim() || process.env.KANBO_SERVE_TOKEN?.trim() || null;
7534
- assertServeBinding(options.host, token);
7607
+ const access = resolveServeAccess(options.host, options.token?.trim() || process.env.KANBO_SERVE_TOKEN?.trim() || null);
7535
7608
  const session = await openBoardSession({
7536
7609
  db: options.db,
7537
7610
  databaseUrl: options.databaseUrl,
@@ -7549,15 +7622,25 @@ function registerServeCommand(program) {
7549
7622
  actors,
7550
7623
  host: options.host,
7551
7624
  port,
7552
- token,
7625
+ token: access.token,
7553
7626
  corsOrigins: options.corsOrigin
7554
7627
  });
7555
7628
  } catch (error) {
7556
7629
  await session.close();
7557
7630
  throw error;
7558
7631
  }
7559
- console.error(`kanbo serve: ${server.url} — workspace ${session.workspace.id}${token ? ", bearer token required" : ""}`);
7560
- console.error(`kanbo serve: ${describePersonRights(actors, token)}`);
7632
+ console.error(`kanbo serve: ${server.url} — workspace ${session.workspace.id}, bearer token required${access.generated ? " (generated for this run)" : ""}`);
7633
+ console.error(`kanbo serve: ${describePersonRights(actors)}`);
7634
+ presentServePage({
7635
+ url: server.url,
7636
+ access,
7637
+ agentShell,
7638
+ terminal: process.stdout.isTTY === true,
7639
+ open: options.open
7640
+ }, {
7641
+ print: (line) => console.log(line),
7642
+ openInBrowser
7643
+ });
7561
7644
  let stopping = false;
7562
7645
  const stop = async () => {
7563
7646
  if (stopping) return;
@@ -8010,6 +8093,12 @@ Object.defineProperty(exports, "buildKanboTools", {
8010
8093
  return buildKanboTools;
8011
8094
  }
8012
8095
  });
8096
+ Object.defineProperty(exports, "cardDisplayTitle", {
8097
+ enumerable: true,
8098
+ get: function() {
8099
+ return cardDisplayTitle;
8100
+ }
8101
+ });
8013
8102
  Object.defineProperty(exports, "commentAuthorReference", {
8014
8103
  enumerable: true,
8015
8104
  get: function() {
@@ -8269,4 +8358,4 @@ Object.defineProperty(exports, "writeBinding", {
8269
8358
  }
8270
8359
  });
8271
8360
 
8272
- //# sourceMappingURL=manifest-CpZOcB_6.cjs.map
8361
+ //# sourceMappingURL=manifest-wdwFSquz.cjs.map