kanban-lite 1.2.3 → 1.2.6
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/cli.js +89 -5
- package/dist/extension.js +88 -4
- package/dist/mcp-server.js +23 -0
- package/dist/sdk/index.cjs +23 -0
- package/dist/sdk/index.mjs +23 -0
- package/dist/sdk/sdk/KanbanSDK.d.ts +22 -1
- package/dist/sdk/sdk/plugins/index.d.ts +7 -0
- package/dist/sdk/sdk/types.d.ts +11 -26
- package/dist/standalone.js +88 -4
- package/package.json +1 -1
- package/src/cli/index.test.ts +157 -0
- package/src/cli/index.ts +1 -1
- package/src/mcp-server/index.test.ts +76 -0
- package/src/sdk/KanbanSDK.d.ts +21 -1
- package/src/sdk/KanbanSDK.ts +30 -1
- package/src/sdk/__tests__/KanbanSDK.test.ts +79 -24
- package/src/sdk/plugins/index.d.ts +7 -0
- package/src/sdk/plugins/index.ts +7 -0
- package/src/sdk/types.d.ts +9 -25
- package/src/sdk/types.ts +10 -24
- package/src/shared/config.ts +6 -0
- package/src/standalone/__tests__/server.integration.test.ts +81 -2
- package/src/standalone/internal/websocket.ts +13 -3
- package/src/standalone/server.ts +46 -2
- package/tmp/screenshots-workspace/.kanban/.active-card.json +5 -0
- package/tmp/screenshots-workspace/.kanban/boards/default/deleted/1-dddd.md +17 -0
- package/tmp/screenshots-workspace/.kanban/boards/default/deleted/attachments/1.log +1 -0
- package/tmp/screenshots-workspace/.kanban.json +59 -0
package/dist/cli.js
CHANGED
|
@@ -199,6 +199,10 @@ function loadDotEnv(dir) {
|
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
function resolveConfigEnvVars(node, configFileName, nodePath = "") {
|
|
202
|
+
const isFormDefaultDataPath = /^\.forms\.(?:[^.]+|"[^"]+")\.data(?:$|[.\[])/.test(nodePath);
|
|
203
|
+
if (isFormDefaultDataPath) {
|
|
204
|
+
return node;
|
|
205
|
+
}
|
|
202
206
|
if (typeof node === "string") {
|
|
203
207
|
return node.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
|
|
204
208
|
const envValue = process.env[varName];
|
|
@@ -26234,6 +26238,25 @@ var init_KanbanSDK = __esm({
|
|
|
26234
26238
|
get workspaceRoot() {
|
|
26235
26239
|
return path14.dirname(this.kanbanDir);
|
|
26236
26240
|
}
|
|
26241
|
+
/**
|
|
26242
|
+
* Returns a cloned read-only snapshot of the current workspace config.
|
|
26243
|
+
*
|
|
26244
|
+
* The returned snapshot is created from a fresh config read and deep-cloned
|
|
26245
|
+
* before being returned, so callers receive an isolated view of the current
|
|
26246
|
+
* `.kanban.json` state rather than a live mutable runtime object. Mutating the
|
|
26247
|
+
* returned snapshot does not update persisted config or affect this SDK instance.
|
|
26248
|
+
*
|
|
26249
|
+
* @returns A cloned read-only snapshot of the current {@link KanbanConfig}.
|
|
26250
|
+
*
|
|
26251
|
+
* @example
|
|
26252
|
+
* ```ts
|
|
26253
|
+
* const config = sdk.getConfigSnapshot()
|
|
26254
|
+
* console.log(config.defaultBoard)
|
|
26255
|
+
* ```
|
|
26256
|
+
*/
|
|
26257
|
+
getConfigSnapshot() {
|
|
26258
|
+
return structuredClone(readConfig(this.workspaceRoot));
|
|
26259
|
+
}
|
|
26237
26260
|
// --- Board resolution helpers ---
|
|
26238
26261
|
/** @internal */
|
|
26239
26262
|
_resolveBoardId(boardId) {
|
|
@@ -114318,9 +114341,14 @@ var init_messageHandlers = __esm({
|
|
|
114318
114341
|
});
|
|
114319
114342
|
|
|
114320
114343
|
// src/standalone/internal/websocket.ts
|
|
114321
|
-
function attachWebSocketHandlers(ctx) {
|
|
114322
|
-
ctx.wss.on("connection", (ws, req) => {
|
|
114323
|
-
|
|
114344
|
+
function attachWebSocketHandlers(ctx, resolveAuthContext) {
|
|
114345
|
+
ctx.wss.on("connection", async (ws, req) => {
|
|
114346
|
+
let authContext;
|
|
114347
|
+
try {
|
|
114348
|
+
authContext = resolveAuthContext ? await resolveAuthContext(req) : extractAuthContext(req);
|
|
114349
|
+
} catch {
|
|
114350
|
+
authContext = extractAuthContext(req);
|
|
114351
|
+
}
|
|
114324
114352
|
setClientEditingCard(ctx, ws, null);
|
|
114325
114353
|
ws.on("message", (data) => {
|
|
114326
114354
|
let message;
|
|
@@ -114395,6 +114423,7 @@ function isPageRequest(method, pathname) {
|
|
|
114395
114423
|
function collectStandaloneHttpHandlers(requestType, ctx) {
|
|
114396
114424
|
const plugins = ctx.sdk.capabilities?.standaloneHttpPlugins ?? [];
|
|
114397
114425
|
const registrationOptions = {
|
|
114426
|
+
sdk: ctx.sdk,
|
|
114398
114427
|
workspaceRoot: ctx.workspaceRoot,
|
|
114399
114428
|
kanbanDir: ctx.absoluteKanbanDir,
|
|
114400
114429
|
capabilities: ctx.sdk.capabilities?.providers ?? {
|
|
@@ -114522,7 +114551,62 @@ function startServer(kanbanDir, port, webviewDir, resolvedConfigPath) {
|
|
|
114522
114551
|
}
|
|
114523
114552
|
reply.hijack();
|
|
114524
114553
|
});
|
|
114525
|
-
|
|
114554
|
+
const resolveWsAuthContext = async (req) => {
|
|
114555
|
+
const silentRes = /* @__PURE__ */ (() => {
|
|
114556
|
+
const r = {
|
|
114557
|
+
writableEnded: false,
|
|
114558
|
+
writeHead() {
|
|
114559
|
+
return r;
|
|
114560
|
+
},
|
|
114561
|
+
setHeader() {
|
|
114562
|
+
return r;
|
|
114563
|
+
},
|
|
114564
|
+
removeHeader() {
|
|
114565
|
+
},
|
|
114566
|
+
getHeader() {
|
|
114567
|
+
return void 0;
|
|
114568
|
+
},
|
|
114569
|
+
getHeaders() {
|
|
114570
|
+
return {};
|
|
114571
|
+
},
|
|
114572
|
+
end(..._args) {
|
|
114573
|
+
r.writableEnded = true;
|
|
114574
|
+
return r;
|
|
114575
|
+
},
|
|
114576
|
+
write() {
|
|
114577
|
+
return false;
|
|
114578
|
+
}
|
|
114579
|
+
};
|
|
114580
|
+
return r;
|
|
114581
|
+
})();
|
|
114582
|
+
const reqWithBody = req;
|
|
114583
|
+
const wsUrl = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
|
|
114584
|
+
const requestContext = {
|
|
114585
|
+
ctx,
|
|
114586
|
+
sdk: ctx.sdk,
|
|
114587
|
+
workspaceRoot: ctx.workspaceRoot,
|
|
114588
|
+
kanbanDir: ctx.absoluteKanbanDir,
|
|
114589
|
+
req: reqWithBody,
|
|
114590
|
+
res: silentRes,
|
|
114591
|
+
url: wsUrl,
|
|
114592
|
+
pathname: wsUrl.pathname,
|
|
114593
|
+
method: "GET",
|
|
114594
|
+
resolvedWebviewDir,
|
|
114595
|
+
indexHtml: resolvedIndexHtml,
|
|
114596
|
+
route: createRouteMatcher("GET", wsUrl.pathname, matchRoute),
|
|
114597
|
+
isApiRequest: false,
|
|
114598
|
+
isPageRequest: false,
|
|
114599
|
+
getAuthContext: () => getRequestAuthContext(req),
|
|
114600
|
+
setAuthContext: (auth) => setRequestAuthContext(req, auth),
|
|
114601
|
+
mergeAuthContext: (auth) => mergeRequestAuthContext(req, auth)
|
|
114602
|
+
};
|
|
114603
|
+
for (const handler of middlewareHandlers) {
|
|
114604
|
+
if (await handler(requestContext))
|
|
114605
|
+
break;
|
|
114606
|
+
}
|
|
114607
|
+
return extractAuthContext(req);
|
|
114608
|
+
};
|
|
114609
|
+
attachWebSocketHandlers(ctx, resolveWsAuthContext);
|
|
114526
114610
|
setupStandaloneLifecycle(ctx, fastify.server);
|
|
114527
114611
|
const effectiveConfigPath = resolvedConfigPath ?? configPath(path21.dirname(ctx.absoluteKanbanDir));
|
|
114528
114612
|
fastify.listen({ port, host: "0.0.0.0" }, (err) => {
|
|
@@ -116952,7 +117036,7 @@ async function cmdAuth(sdk, positional, flags, cliPlugins, workspaceRoot) {
|
|
|
116952
117036
|
if (sub !== "status") {
|
|
116953
117037
|
const authPlugin = findCliPlugin(cliPlugins, "auth");
|
|
116954
117038
|
if (authPlugin) {
|
|
116955
|
-
await authPlugin
|
|
117039
|
+
await runCliPlugin(authPlugin, positional, flags, workspaceRoot, sdk);
|
|
116956
117040
|
return;
|
|
116957
117041
|
}
|
|
116958
117042
|
console.error(red(`Unknown auth sub-command: ${sub}`));
|
package/dist/extension.js
CHANGED
|
@@ -65355,6 +65355,10 @@ function loadDotEnv(dir) {
|
|
|
65355
65355
|
}
|
|
65356
65356
|
}
|
|
65357
65357
|
function resolveConfigEnvVars(node, configFileName, nodePath = "") {
|
|
65358
|
+
const isFormDefaultDataPath = /^\.forms\.(?:[^.]+|"[^"]+")\.data(?:$|[.\[])/.test(nodePath);
|
|
65359
|
+
if (isFormDefaultDataPath) {
|
|
65360
|
+
return node;
|
|
65361
|
+
}
|
|
65358
65362
|
if (typeof node === "string") {
|
|
65359
65363
|
return node.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
|
|
65360
65364
|
const envValue = process.env[varName];
|
|
@@ -73680,6 +73684,25 @@ var KanbanSDK = class _KanbanSDK {
|
|
|
73680
73684
|
get workspaceRoot() {
|
|
73681
73685
|
return path14.dirname(this.kanbanDir);
|
|
73682
73686
|
}
|
|
73687
|
+
/**
|
|
73688
|
+
* Returns a cloned read-only snapshot of the current workspace config.
|
|
73689
|
+
*
|
|
73690
|
+
* The returned snapshot is created from a fresh config read and deep-cloned
|
|
73691
|
+
* before being returned, so callers receive an isolated view of the current
|
|
73692
|
+
* `.kanban.json` state rather than a live mutable runtime object. Mutating the
|
|
73693
|
+
* returned snapshot does not update persisted config or affect this SDK instance.
|
|
73694
|
+
*
|
|
73695
|
+
* @returns A cloned read-only snapshot of the current {@link KanbanConfig}.
|
|
73696
|
+
*
|
|
73697
|
+
* @example
|
|
73698
|
+
* ```ts
|
|
73699
|
+
* const config = sdk.getConfigSnapshot()
|
|
73700
|
+
* console.log(config.defaultBoard)
|
|
73701
|
+
* ```
|
|
73702
|
+
*/
|
|
73703
|
+
getConfigSnapshot() {
|
|
73704
|
+
return structuredClone(readConfig(this.workspaceRoot));
|
|
73705
|
+
}
|
|
73683
73706
|
// --- Board resolution helpers ---
|
|
73684
73707
|
/** @internal */
|
|
73685
73708
|
_resolveBoardId(boardId) {
|
|
@@ -80646,9 +80669,14 @@ async function handleMessage(ctx, ws, message, authContext) {
|
|
|
80646
80669
|
}
|
|
80647
80670
|
|
|
80648
80671
|
// src/standalone/internal/websocket.ts
|
|
80649
|
-
function attachWebSocketHandlers(ctx) {
|
|
80650
|
-
ctx.wss.on("connection", (ws, req) => {
|
|
80651
|
-
|
|
80672
|
+
function attachWebSocketHandlers(ctx, resolveAuthContext) {
|
|
80673
|
+
ctx.wss.on("connection", async (ws, req) => {
|
|
80674
|
+
let authContext;
|
|
80675
|
+
try {
|
|
80676
|
+
authContext = resolveAuthContext ? await resolveAuthContext(req) : extractAuthContext(req);
|
|
80677
|
+
} catch {
|
|
80678
|
+
authContext = extractAuthContext(req);
|
|
80679
|
+
}
|
|
80652
80680
|
setClientEditingCard(ctx, ws, null);
|
|
80653
80681
|
ws.on("message", (data) => {
|
|
80654
80682
|
let message;
|
|
@@ -80815,6 +80843,7 @@ function isPageRequest(method, pathname) {
|
|
|
80815
80843
|
function collectStandaloneHttpHandlers(requestType, ctx) {
|
|
80816
80844
|
const plugins = ctx.sdk.capabilities?.standaloneHttpPlugins ?? [];
|
|
80817
80845
|
const registrationOptions = {
|
|
80846
|
+
sdk: ctx.sdk,
|
|
80818
80847
|
workspaceRoot: ctx.workspaceRoot,
|
|
80819
80848
|
kanbanDir: ctx.absoluteKanbanDir,
|
|
80820
80849
|
capabilities: ctx.sdk.capabilities?.providers ?? {
|
|
@@ -80942,7 +80971,62 @@ function startServer(kanbanDir, port, webviewDir, resolvedConfigPath) {
|
|
|
80942
80971
|
}
|
|
80943
80972
|
reply.hijack();
|
|
80944
80973
|
});
|
|
80945
|
-
|
|
80974
|
+
const resolveWsAuthContext = async (req) => {
|
|
80975
|
+
const silentRes = /* @__PURE__ */ (() => {
|
|
80976
|
+
const r = {
|
|
80977
|
+
writableEnded: false,
|
|
80978
|
+
writeHead() {
|
|
80979
|
+
return r;
|
|
80980
|
+
},
|
|
80981
|
+
setHeader() {
|
|
80982
|
+
return r;
|
|
80983
|
+
},
|
|
80984
|
+
removeHeader() {
|
|
80985
|
+
},
|
|
80986
|
+
getHeader() {
|
|
80987
|
+
return void 0;
|
|
80988
|
+
},
|
|
80989
|
+
getHeaders() {
|
|
80990
|
+
return {};
|
|
80991
|
+
},
|
|
80992
|
+
end(..._args) {
|
|
80993
|
+
r.writableEnded = true;
|
|
80994
|
+
return r;
|
|
80995
|
+
},
|
|
80996
|
+
write() {
|
|
80997
|
+
return false;
|
|
80998
|
+
}
|
|
80999
|
+
};
|
|
81000
|
+
return r;
|
|
81001
|
+
})();
|
|
81002
|
+
const reqWithBody = req;
|
|
81003
|
+
const wsUrl = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
|
|
81004
|
+
const requestContext = {
|
|
81005
|
+
ctx,
|
|
81006
|
+
sdk: ctx.sdk,
|
|
81007
|
+
workspaceRoot: ctx.workspaceRoot,
|
|
81008
|
+
kanbanDir: ctx.absoluteKanbanDir,
|
|
81009
|
+
req: reqWithBody,
|
|
81010
|
+
res: silentRes,
|
|
81011
|
+
url: wsUrl,
|
|
81012
|
+
pathname: wsUrl.pathname,
|
|
81013
|
+
method: "GET",
|
|
81014
|
+
resolvedWebviewDir,
|
|
81015
|
+
indexHtml: resolvedIndexHtml,
|
|
81016
|
+
route: createRouteMatcher("GET", wsUrl.pathname, matchRoute),
|
|
81017
|
+
isApiRequest: false,
|
|
81018
|
+
isPageRequest: false,
|
|
81019
|
+
getAuthContext: () => getRequestAuthContext(req),
|
|
81020
|
+
setAuthContext: (auth) => setRequestAuthContext(req, auth),
|
|
81021
|
+
mergeAuthContext: (auth) => mergeRequestAuthContext(req, auth)
|
|
81022
|
+
};
|
|
81023
|
+
for (const handler of middlewareHandlers) {
|
|
81024
|
+
if (await handler(requestContext))
|
|
81025
|
+
break;
|
|
81026
|
+
}
|
|
81027
|
+
return extractAuthContext(req);
|
|
81028
|
+
};
|
|
81029
|
+
attachWebSocketHandlers(ctx, resolveWsAuthContext);
|
|
80946
81030
|
setupStandaloneLifecycle(ctx, fastify.server);
|
|
80947
81031
|
const effectiveConfigPath = resolvedConfigPath ?? configPath(path20.dirname(ctx.absoluteKanbanDir));
|
|
80948
81032
|
fastify.listen({ port, host: "0.0.0.0" }, (err) => {
|
package/dist/mcp-server.js
CHANGED
|
@@ -17690,6 +17690,10 @@ function loadDotEnv(dir) {
|
|
|
17690
17690
|
}
|
|
17691
17691
|
}
|
|
17692
17692
|
function resolveConfigEnvVars(node, configFileName, nodePath = "") {
|
|
17693
|
+
const isFormDefaultDataPath = /^\.forms\.(?:[^.]+|"[^"]+")\.data(?:$|[.\[])/.test(nodePath);
|
|
17694
|
+
if (isFormDefaultDataPath) {
|
|
17695
|
+
return node;
|
|
17696
|
+
}
|
|
17693
17697
|
if (typeof node === "string") {
|
|
17694
17698
|
return node.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
|
|
17695
17699
|
const envValue = process.env[varName];
|
|
@@ -26078,6 +26082,25 @@ var KanbanSDK = class _KanbanSDK {
|
|
|
26078
26082
|
get workspaceRoot() {
|
|
26079
26083
|
return path14.dirname(this.kanbanDir);
|
|
26080
26084
|
}
|
|
26085
|
+
/**
|
|
26086
|
+
* Returns a cloned read-only snapshot of the current workspace config.
|
|
26087
|
+
*
|
|
26088
|
+
* The returned snapshot is created from a fresh config read and deep-cloned
|
|
26089
|
+
* before being returned, so callers receive an isolated view of the current
|
|
26090
|
+
* `.kanban.json` state rather than a live mutable runtime object. Mutating the
|
|
26091
|
+
* returned snapshot does not update persisted config or affect this SDK instance.
|
|
26092
|
+
*
|
|
26093
|
+
* @returns A cloned read-only snapshot of the current {@link KanbanConfig}.
|
|
26094
|
+
*
|
|
26095
|
+
* @example
|
|
26096
|
+
* ```ts
|
|
26097
|
+
* const config = sdk.getConfigSnapshot()
|
|
26098
|
+
* console.log(config.defaultBoard)
|
|
26099
|
+
* ```
|
|
26100
|
+
*/
|
|
26101
|
+
getConfigSnapshot() {
|
|
26102
|
+
return structuredClone(readConfig(this.workspaceRoot));
|
|
26103
|
+
}
|
|
26081
26104
|
// --- Board resolution helpers ---
|
|
26082
26105
|
/** @internal */
|
|
26083
26106
|
_resolveBoardId(boardId) {
|
package/dist/sdk/index.cjs
CHANGED
|
@@ -17720,6 +17720,10 @@ function loadDotEnv(dir) {
|
|
|
17720
17720
|
}
|
|
17721
17721
|
}
|
|
17722
17722
|
function resolveConfigEnvVars(node, configFileName, nodePath = "") {
|
|
17723
|
+
const isFormDefaultDataPath = /^\.forms\.(?:[^.]+|"[^"]+")\.data(?:$|[.\[])/.test(nodePath);
|
|
17724
|
+
if (isFormDefaultDataPath) {
|
|
17725
|
+
return node;
|
|
17726
|
+
}
|
|
17723
17727
|
if (typeof node === "string") {
|
|
17724
17728
|
return node.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
|
|
17725
17729
|
const envValue = process.env[varName];
|
|
@@ -26045,6 +26049,25 @@ var KanbanSDK = class _KanbanSDK {
|
|
|
26045
26049
|
get workspaceRoot() {
|
|
26046
26050
|
return path14.dirname(this.kanbanDir);
|
|
26047
26051
|
}
|
|
26052
|
+
/**
|
|
26053
|
+
* Returns a cloned read-only snapshot of the current workspace config.
|
|
26054
|
+
*
|
|
26055
|
+
* The returned snapshot is created from a fresh config read and deep-cloned
|
|
26056
|
+
* before being returned, so callers receive an isolated view of the current
|
|
26057
|
+
* `.kanban.json` state rather than a live mutable runtime object. Mutating the
|
|
26058
|
+
* returned snapshot does not update persisted config or affect this SDK instance.
|
|
26059
|
+
*
|
|
26060
|
+
* @returns A cloned read-only snapshot of the current {@link KanbanConfig}.
|
|
26061
|
+
*
|
|
26062
|
+
* @example
|
|
26063
|
+
* ```ts
|
|
26064
|
+
* const config = sdk.getConfigSnapshot()
|
|
26065
|
+
* console.log(config.defaultBoard)
|
|
26066
|
+
* ```
|
|
26067
|
+
*/
|
|
26068
|
+
getConfigSnapshot() {
|
|
26069
|
+
return structuredClone(readConfig(this.workspaceRoot));
|
|
26070
|
+
}
|
|
26048
26071
|
// --- Board resolution helpers ---
|
|
26049
26072
|
/** @internal */
|
|
26050
26073
|
_resolveBoardId(boardId) {
|
package/dist/sdk/index.mjs
CHANGED
|
@@ -17679,6 +17679,10 @@ function loadDotEnv(dir) {
|
|
|
17679
17679
|
}
|
|
17680
17680
|
}
|
|
17681
17681
|
function resolveConfigEnvVars(node, configFileName, nodePath = "") {
|
|
17682
|
+
const isFormDefaultDataPath = /^\.forms\.(?:[^.]+|"[^"]+")\.data(?:$|[.\[])/.test(nodePath);
|
|
17683
|
+
if (isFormDefaultDataPath) {
|
|
17684
|
+
return node;
|
|
17685
|
+
}
|
|
17682
17686
|
if (typeof node === "string") {
|
|
17683
17687
|
return node.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
|
|
17684
17688
|
const envValue = process.env[varName];
|
|
@@ -26004,6 +26008,25 @@ var KanbanSDK = class _KanbanSDK {
|
|
|
26004
26008
|
get workspaceRoot() {
|
|
26005
26009
|
return path14.dirname(this.kanbanDir);
|
|
26006
26010
|
}
|
|
26011
|
+
/**
|
|
26012
|
+
* Returns a cloned read-only snapshot of the current workspace config.
|
|
26013
|
+
*
|
|
26014
|
+
* The returned snapshot is created from a fresh config read and deep-cloned
|
|
26015
|
+
* before being returned, so callers receive an isolated view of the current
|
|
26016
|
+
* `.kanban.json` state rather than a live mutable runtime object. Mutating the
|
|
26017
|
+
* returned snapshot does not update persisted config or affect this SDK instance.
|
|
26018
|
+
*
|
|
26019
|
+
* @returns A cloned read-only snapshot of the current {@link KanbanConfig}.
|
|
26020
|
+
*
|
|
26021
|
+
* @example
|
|
26022
|
+
* ```ts
|
|
26023
|
+
* const config = sdk.getConfigSnapshot()
|
|
26024
|
+
* console.log(config.defaultBoard)
|
|
26025
|
+
* ```
|
|
26026
|
+
*/
|
|
26027
|
+
getConfigSnapshot() {
|
|
26028
|
+
return structuredClone(readConfig(this.workspaceRoot));
|
|
26029
|
+
}
|
|
26007
26030
|
// --- Board resolution helpers ---
|
|
26008
26031
|
/** @internal */
|
|
26009
26032
|
_resolveBoardId(boardId) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Comment, Card, KanbanColumn, BoardInfo, LabelDefinition, CardSortOption, LogEntry } from '../shared/types';
|
|
2
2
|
import type { CardDisplaySettings, Priority } from '../shared/types';
|
|
3
|
-
import type { BoardConfig, ResolvedCapabilities, Webhook } from '../shared/config';
|
|
3
|
+
import type { BoardConfig, KanbanConfig, ResolvedCapabilities, Webhook } from '../shared/config';
|
|
4
4
|
import type { CreateCardInput, SDKEvent, SDKEventType, SDKOptions, SubmitFormInput, SubmitFormResult, AuthContext, AuthDecision, SDKBeforeEventType, SDKAfterEventType, CardStateStatus, CardUnreadSummary } from './types';
|
|
5
5
|
import type { EventBusAnyListener, EventBusWaitOptions } from './eventBus';
|
|
6
6
|
import { EventBus } from './eventBus';
|
|
@@ -46,6 +46,9 @@ export interface AuthStatus {
|
|
|
46
46
|
*/
|
|
47
47
|
policyEnabled: boolean;
|
|
48
48
|
}
|
|
49
|
+
type ReadonlySnapshot<T> = T extends (...args: never[]) => unknown ? T : T extends readonly (infer U)[] ? readonly ReadonlySnapshot<U>[] : T extends object ? {
|
|
50
|
+
readonly [K in keyof T]: ReadonlySnapshot<T[K]>;
|
|
51
|
+
} : T;
|
|
49
52
|
/**
|
|
50
53
|
* Active webhook provider metadata for diagnostics and host surfaces.
|
|
51
54
|
*
|
|
@@ -535,6 +538,23 @@ export declare class KanbanSDK {
|
|
|
535
538
|
* ```
|
|
536
539
|
*/
|
|
537
540
|
get workspaceRoot(): string;
|
|
541
|
+
/**
|
|
542
|
+
* Returns a cloned read-only snapshot of the current workspace config.
|
|
543
|
+
*
|
|
544
|
+
* The returned snapshot is created from a fresh config read and deep-cloned
|
|
545
|
+
* before being returned, so callers receive an isolated view of the current
|
|
546
|
+
* `.kanban.json` state rather than a live mutable runtime object. Mutating the
|
|
547
|
+
* returned snapshot does not update persisted config or affect this SDK instance.
|
|
548
|
+
*
|
|
549
|
+
* @returns A cloned read-only snapshot of the current {@link KanbanConfig}.
|
|
550
|
+
*
|
|
551
|
+
* @example
|
|
552
|
+
* ```ts
|
|
553
|
+
* const config = sdk.getConfigSnapshot()
|
|
554
|
+
* console.log(config.defaultBoard)
|
|
555
|
+
* ```
|
|
556
|
+
*/
|
|
557
|
+
getConfigSnapshot(): ReadonlySnapshot<KanbanConfig>;
|
|
538
558
|
/** @internal */
|
|
539
559
|
_resolveBoardId(boardId?: string): string;
|
|
540
560
|
/** @internal */
|
|
@@ -1771,3 +1791,4 @@ export declare class KanbanSDK {
|
|
|
1771
1791
|
*/
|
|
1772
1792
|
updateWebhook(id: string, updates: Partial<Pick<Webhook, 'url' | 'events' | 'secret' | 'active'>>): Promise<Webhook | null>;
|
|
1773
1793
|
}
|
|
1794
|
+
export {};
|
|
@@ -337,6 +337,13 @@ export type StandaloneHttpHandler = (request: StandaloneHttpRequestContext) => P
|
|
|
337
337
|
* resolved the active workspace capability selections.
|
|
338
338
|
*/
|
|
339
339
|
export interface StandaloneHttpPluginRegistrationOptions {
|
|
340
|
+
/**
|
|
341
|
+
* Active SDK instance backing the standalone runtime, when provided by the host.
|
|
342
|
+
*
|
|
343
|
+
* Plugin registration code may use the full public {@link KanbanSDK} surface,
|
|
344
|
+
* including `getConfigSnapshot()`, when this seam is available.
|
|
345
|
+
*/
|
|
346
|
+
readonly sdk?: KanbanSDK;
|
|
340
347
|
/** Absolute workspace root containing `.kanban.json`. */
|
|
341
348
|
readonly workspaceRoot: string;
|
|
342
349
|
/** Absolute workspace `.kanban` directory. */
|
package/dist/sdk/sdk/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Card, CardFormAttachment, CardFormDataMap, Priority, ResolvedFormDescriptor } from '../shared/types';
|
|
2
|
-
import type { CapabilitySelections
|
|
2
|
+
import type { CapabilitySelections } from '../shared/config';
|
|
3
|
+
import type { KanbanSDK } from './KanbanSDK';
|
|
3
4
|
import type { StorageEngine, StorageEngineType } from './plugins/types';
|
|
4
5
|
import type { CardStateCursor } from './plugins';
|
|
5
6
|
export type { StorageEngine, StorageEngineType } from './plugins/types';
|
|
@@ -489,29 +490,11 @@ export declare class CardStateError extends Error {
|
|
|
489
490
|
constructor(code: CardStateErrorCode, message: string);
|
|
490
491
|
}
|
|
491
492
|
/**
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
* importing `KanbanSDK` directly so they remain decoupled from core internals.
|
|
493
|
+
* @deprecated Use {@link KanbanSDK}. CLI plugin hosts now advertise the full
|
|
494
|
+
* public SDK surface, including `getConfigSnapshot()`, instead of a narrowed
|
|
495
|
+
* webhook-only facade.
|
|
496
496
|
*/
|
|
497
|
-
export
|
|
498
|
-
/**
|
|
499
|
-
* Returns the SDK extension bag contributed by the plugin with the given id,
|
|
500
|
-
* when the host is backed by a full `KanbanSDK` instance.
|
|
501
|
-
*
|
|
502
|
-
* CLI plugins should prefer this extension path when available and fall back
|
|
503
|
-
* to compatibility methods only when running against older or mocked SDK facades.
|
|
504
|
-
*/
|
|
505
|
-
getExtension?<T extends Record<string, unknown> = Record<string, unknown>>(id: string): T | undefined;
|
|
506
|
-
listWebhooks(): Webhook[];
|
|
507
|
-
createWebhook(input: {
|
|
508
|
-
url: string;
|
|
509
|
-
events: string[];
|
|
510
|
-
secret?: string;
|
|
511
|
-
}): Promise<Webhook>;
|
|
512
|
-
updateWebhook(id: string, updates: Partial<Pick<Webhook, 'url' | 'events' | 'secret' | 'active'>>): Promise<Webhook | null>;
|
|
513
|
-
deleteWebhook(id: string): Promise<boolean>;
|
|
514
|
-
}
|
|
497
|
+
export type CliPluginSdk = KanbanSDK;
|
|
515
498
|
/**
|
|
516
499
|
* Runtime context supplied to a {@link KanbanCliPlugin} when it is invoked by
|
|
517
500
|
* the `kl` CLI.
|
|
@@ -524,10 +507,12 @@ export interface CliPluginContext {
|
|
|
524
507
|
*
|
|
525
508
|
* Present when the plugin is invoked through the core `kl` CLI.
|
|
526
509
|
* Absent in isolated unit tests or standalone invocations.
|
|
527
|
-
* Plugins
|
|
528
|
-
*
|
|
510
|
+
* Plugins may use the full public {@link KanbanSDK} contract here, including
|
|
511
|
+
* extension lookup and `getConfigSnapshot()`, instead of relying on older
|
|
512
|
+
* helper-only SDK facades. Plugins should prefer this over constructing their
|
|
513
|
+
* own SDK so that SDK-level auth policy is honoured.
|
|
529
514
|
*/
|
|
530
|
-
sdk?:
|
|
515
|
+
sdk?: KanbanSDK;
|
|
531
516
|
/**
|
|
532
517
|
* Core-owned CLI auth helper.
|
|
533
518
|
*
|
package/dist/standalone.js
CHANGED
|
@@ -49920,6 +49920,10 @@ function loadDotEnv(dir2) {
|
|
|
49920
49920
|
}
|
|
49921
49921
|
}
|
|
49922
49922
|
function resolveConfigEnvVars(node, configFileName, nodePath = "") {
|
|
49923
|
+
const isFormDefaultDataPath = /^\.forms\.(?:[^.]+|"[^"]+")\.data(?:$|[.\[])/.test(nodePath);
|
|
49924
|
+
if (isFormDefaultDataPath) {
|
|
49925
|
+
return node;
|
|
49926
|
+
}
|
|
49923
49927
|
if (typeof node === "string") {
|
|
49924
49928
|
return node.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
|
|
49925
49929
|
const envValue = process.env[varName];
|
|
@@ -61678,6 +61682,25 @@ var KanbanSDK = class _KanbanSDK {
|
|
|
61678
61682
|
get workspaceRoot() {
|
|
61679
61683
|
return path17.dirname(this.kanbanDir);
|
|
61680
61684
|
}
|
|
61685
|
+
/**
|
|
61686
|
+
* Returns a cloned read-only snapshot of the current workspace config.
|
|
61687
|
+
*
|
|
61688
|
+
* The returned snapshot is created from a fresh config read and deep-cloned
|
|
61689
|
+
* before being returned, so callers receive an isolated view of the current
|
|
61690
|
+
* `.kanban.json` state rather than a live mutable runtime object. Mutating the
|
|
61691
|
+
* returned snapshot does not update persisted config or affect this SDK instance.
|
|
61692
|
+
*
|
|
61693
|
+
* @returns A cloned read-only snapshot of the current {@link KanbanConfig}.
|
|
61694
|
+
*
|
|
61695
|
+
* @example
|
|
61696
|
+
* ```ts
|
|
61697
|
+
* const config = sdk.getConfigSnapshot()
|
|
61698
|
+
* console.log(config.defaultBoard)
|
|
61699
|
+
* ```
|
|
61700
|
+
*/
|
|
61701
|
+
getConfigSnapshot() {
|
|
61702
|
+
return structuredClone(readConfig(this.workspaceRoot));
|
|
61703
|
+
}
|
|
61681
61704
|
// --- Board resolution helpers ---
|
|
61682
61705
|
/** @internal */
|
|
61683
61706
|
_resolveBoardId(boardId) {
|
|
@@ -65218,9 +65241,14 @@ async function handleMessage(ctx, ws, message, authContext) {
|
|
|
65218
65241
|
}
|
|
65219
65242
|
|
|
65220
65243
|
// src/standalone/internal/websocket.ts
|
|
65221
|
-
function attachWebSocketHandlers(ctx) {
|
|
65222
|
-
ctx.wss.on("connection", (ws, req) => {
|
|
65223
|
-
|
|
65244
|
+
function attachWebSocketHandlers(ctx, resolveAuthContext) {
|
|
65245
|
+
ctx.wss.on("connection", async (ws, req) => {
|
|
65246
|
+
let authContext;
|
|
65247
|
+
try {
|
|
65248
|
+
authContext = resolveAuthContext ? await resolveAuthContext(req) : extractAuthContext(req);
|
|
65249
|
+
} catch {
|
|
65250
|
+
authContext = extractAuthContext(req);
|
|
65251
|
+
}
|
|
65224
65252
|
setClientEditingCard(ctx, ws, null);
|
|
65225
65253
|
ws.on("message", (data) => {
|
|
65226
65254
|
let message;
|
|
@@ -65387,6 +65415,7 @@ function isPageRequest(method, pathname) {
|
|
|
65387
65415
|
function collectStandaloneHttpHandlers(requestType, ctx) {
|
|
65388
65416
|
const plugins = ctx.sdk.capabilities?.standaloneHttpPlugins ?? [];
|
|
65389
65417
|
const registrationOptions = {
|
|
65418
|
+
sdk: ctx.sdk,
|
|
65390
65419
|
workspaceRoot: ctx.workspaceRoot,
|
|
65391
65420
|
kanbanDir: ctx.absoluteKanbanDir,
|
|
65392
65421
|
capabilities: ctx.sdk.capabilities?.providers ?? {
|
|
@@ -65514,7 +65543,62 @@ function startServer(kanbanDir, port2, webviewDir, resolvedConfigPath) {
|
|
|
65514
65543
|
}
|
|
65515
65544
|
reply.hijack();
|
|
65516
65545
|
});
|
|
65517
|
-
|
|
65546
|
+
const resolveWsAuthContext = async (req) => {
|
|
65547
|
+
const silentRes = /* @__PURE__ */ (() => {
|
|
65548
|
+
const r = {
|
|
65549
|
+
writableEnded: false,
|
|
65550
|
+
writeHead() {
|
|
65551
|
+
return r;
|
|
65552
|
+
},
|
|
65553
|
+
setHeader() {
|
|
65554
|
+
return r;
|
|
65555
|
+
},
|
|
65556
|
+
removeHeader() {
|
|
65557
|
+
},
|
|
65558
|
+
getHeader() {
|
|
65559
|
+
return void 0;
|
|
65560
|
+
},
|
|
65561
|
+
getHeaders() {
|
|
65562
|
+
return {};
|
|
65563
|
+
},
|
|
65564
|
+
end(..._args) {
|
|
65565
|
+
r.writableEnded = true;
|
|
65566
|
+
return r;
|
|
65567
|
+
},
|
|
65568
|
+
write() {
|
|
65569
|
+
return false;
|
|
65570
|
+
}
|
|
65571
|
+
};
|
|
65572
|
+
return r;
|
|
65573
|
+
})();
|
|
65574
|
+
const reqWithBody = req;
|
|
65575
|
+
const wsUrl = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
|
|
65576
|
+
const requestContext = {
|
|
65577
|
+
ctx,
|
|
65578
|
+
sdk: ctx.sdk,
|
|
65579
|
+
workspaceRoot: ctx.workspaceRoot,
|
|
65580
|
+
kanbanDir: ctx.absoluteKanbanDir,
|
|
65581
|
+
req: reqWithBody,
|
|
65582
|
+
res: silentRes,
|
|
65583
|
+
url: wsUrl,
|
|
65584
|
+
pathname: wsUrl.pathname,
|
|
65585
|
+
method: "GET",
|
|
65586
|
+
resolvedWebviewDir,
|
|
65587
|
+
indexHtml: resolvedIndexHtml,
|
|
65588
|
+
route: createRouteMatcher("GET", wsUrl.pathname, matchRoute),
|
|
65589
|
+
isApiRequest: false,
|
|
65590
|
+
isPageRequest: false,
|
|
65591
|
+
getAuthContext: () => getRequestAuthContext(req),
|
|
65592
|
+
setAuthContext: (auth) => setRequestAuthContext(req, auth),
|
|
65593
|
+
mergeAuthContext: (auth) => mergeRequestAuthContext(req, auth)
|
|
65594
|
+
};
|
|
65595
|
+
for (const handler of middlewareHandlers) {
|
|
65596
|
+
if (await handler(requestContext))
|
|
65597
|
+
break;
|
|
65598
|
+
}
|
|
65599
|
+
return extractAuthContext(req);
|
|
65600
|
+
};
|
|
65601
|
+
attachWebSocketHandlers(ctx, resolveWsAuthContext);
|
|
65518
65602
|
setupStandaloneLifecycle(ctx, fastify.server);
|
|
65519
65603
|
const effectiveConfigPath = resolvedConfigPath ?? configPath(path20.dirname(ctx.absoluteKanbanDir));
|
|
65520
65604
|
fastify.listen({ port: port2, host: "0.0.0.0" }, (err) => {
|