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
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
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
42516
|
-
*
|
|
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
|
|
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
|
|
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}${
|
|
42733
|
-
console.error(`kanbo serve: ${describePersonRights(actors
|
|
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;
|