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.
- package/README.md +1 -2
- package/dist/cli.cjs +96 -14
- package/dist/cli.cjs.map +1 -1
- package/dist/lib/{board-store-B16YarvX.d.mts → board-store-BfszzAJB.d.cts} +2 -2
- package/dist/lib/{board-store-BHHo_ZlR.d.cts → board-store-BvWn9xTB.d.mts} +2 -2
- package/dist/lib/index.cjs +2 -29
- package/dist/lib/index.cjs.map +1 -1
- package/dist/lib/index.d.cts +363 -363
- package/dist/lib/index.d.mts +363 -363
- package/dist/lib/index.mjs +1 -28
- package/dist/lib/index.mjs.map +1 -1
- package/dist/lib/{manifest-C0EcwwgO.mjs → manifest-DfOBqo-Q.mjs} +100 -17
- package/dist/lib/manifest-DfOBqo-Q.mjs.map +1 -0
- package/dist/lib/{manifest-CpZOcB_6.cjs → manifest-wdwFSquz.cjs} +104 -15
- package/dist/lib/manifest-wdwFSquz.cjs.map +1 -0
- package/dist/lib/mcp/index.cjs +1 -1
- package/dist/lib/mcp/index.d.cts +12 -12
- package/dist/lib/mcp/index.d.mts +12 -12
- package/dist/lib/mcp/index.mjs +1 -1
- package/dist/lib/postgres/index.d.cts +151 -151
- package/dist/lib/postgres/index.d.mts +151 -151
- package/dist/lib/{schema-fsHqNAip.d.cts → schema-D0MyXcej.d.cts} +41 -41
- package/dist/lib/{schema-fsHqNAip.d.mts → schema-D0MyXcej.d.mts} +41 -41
- package/dist/lib/sqlite/index.d.cts +12 -12
- package/dist/lib/sqlite/index.d.mts +12 -12
- package/dist/lib/sqlite/schema.d.cts +1 -1
- package/dist/lib/sqlite/schema.d.mts +1 -1
- package/dist/lib/{tool-names-9cmCn0gP.d.cts → tool-names-3q_GEZej.d.mts} +5 -5
- package/dist/lib/{tool-names-Dxg1UTUl.d.mts → tool-names-WfH4IURz.d.cts} +5 -5
- package/dist/page/board.js +1 -1
- package/package.json +4 -4
- package/dist/lib/manifest-C0EcwwgO.mjs.map +0 -1
- 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
|
|
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
|
|
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.
|
|
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
|
|
7343
|
-
*
|
|
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
|
|
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
|
|
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}${
|
|
7560
|
-
console.error(`kanbo serve: ${describePersonRights(actors
|
|
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-
|
|
8361
|
+
//# sourceMappingURL=manifest-wdwFSquz.cjs.map
|