kanban-lite 1.2.2 → 1.2.4
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 +257 -90
- package/dist/extension.js +245 -78
- package/dist/mcp-server.js +117 -33
- package/dist/sdk/index.cjs +117 -32
- package/dist/sdk/index.mjs +117 -32
- package/dist/sdk/sdk/KanbanSDK.d.ts +27 -10
- package/dist/sdk/sdk/modules/cards.d.ts +11 -2
- package/dist/sdk/sdk/plugins/index.d.ts +7 -0
- package/dist/sdk/sdk/types.d.ts +12 -27
- package/dist/sdk/shared/config.d.ts +17 -1
- package/dist/standalone-webview/index.js +38 -38
- package/dist/standalone-webview/index.js.map +1 -1
- package/dist/standalone.js +307 -125
- 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/mcp-server/index.ts +1 -1
- package/src/sdk/KanbanSDK.d.ts +26 -10
- package/src/sdk/KanbanSDK.ts +37 -11
- package/src/sdk/__tests__/KanbanSDK.test.ts +79 -24
- package/src/sdk/integrationCatalog.ts +1 -0
- package/src/sdk/modules/cards.ts +13 -24
- package/src/sdk/plugins/index.d.ts +7 -0
- package/src/sdk/plugins/index.ts +17 -2
- package/src/sdk/types.d.ts +10 -26
- package/src/sdk/types.ts +11 -24
- package/src/sdk/webhooks.ts +19 -2
- package/src/shared/config.ts +130 -2
- package/src/standalone/__tests__/server.integration.test.ts +81 -2
- package/src/standalone/internal/runtime.ts +11 -6
- package/src/standalone/internal/websocket.ts +13 -3
- package/src/standalone/server.ts +67 -9
- package/src/standalone/watcherSetup.ts +9 -0
- package/src/webview/standalone-shim.ts +2 -1
- 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/src/standalone/server.ts
CHANGED
|
@@ -9,11 +9,11 @@ import type { StandaloneHttpHandler, StandaloneHttpPlugin } from '../sdk'
|
|
|
9
9
|
import { createRouteMatcher, type StandaloneRequestContext, type StandaloneRouteHandler } from './internal/common'
|
|
10
10
|
import { KANBAN_OPENAPI_SPEC } from './internal/openapi-spec'
|
|
11
11
|
import { handleCardFileRoute, setupStandaloneLifecycle } from './internal/lifecycle'
|
|
12
|
-
import { createStandaloneRuntime,
|
|
12
|
+
import { createStandaloneRuntime, getIndexHtml } from './internal/runtime'
|
|
13
13
|
import { handleBoardRoutes } from './internal/routes/boards'
|
|
14
14
|
import { handleSystemRoutes } from './internal/routes/system'
|
|
15
15
|
import { handleTaskRoutes } from './internal/routes/tasks'
|
|
16
|
-
import { getRequestAuthContext, mergeRequestAuthContext, setRequestAuthContext } from './authUtils'
|
|
16
|
+
import { extractAuthContext, getRequestAuthContext, mergeRequestAuthContext, setRequestAuthContext } from './authUtils'
|
|
17
17
|
import { attachWebSocketHandlers } from './internal/websocket'
|
|
18
18
|
import { matchRoute, type IncomingMessageWithRawBody } from './httpUtils'
|
|
19
19
|
|
|
@@ -176,6 +176,7 @@ function collectStandaloneHttpHandlers(
|
|
|
176
176
|
): StandaloneHttpHandler[] {
|
|
177
177
|
const plugins = ctx.sdk.capabilities?.standaloneHttpPlugins ?? []
|
|
178
178
|
const registrationOptions = {
|
|
179
|
+
sdk: ctx.sdk,
|
|
179
180
|
workspaceRoot: ctx.workspaceRoot,
|
|
180
181
|
kanbanDir: ctx.absoluteKanbanDir,
|
|
181
182
|
capabilities: ctx.sdk.capabilities?.providers ?? {
|
|
@@ -249,11 +250,15 @@ export function startServer(kanbanDir: string, port: number, webviewDir?: string
|
|
|
249
250
|
? { type: 'image/svg+xml', content: fs.readFileSync(swaggerUiLogoPath) }
|
|
250
251
|
: null
|
|
251
252
|
|
|
252
|
-
const
|
|
253
|
+
const workspaceRoot = path.dirname(path.resolve(kanbanDir))
|
|
254
|
+
const config = readConfig(workspaceRoot)
|
|
255
|
+
const rawBase = config.basePath ?? ''
|
|
256
|
+
const basePath = rawBase ? (rawBase.startsWith('/') ? rawBase : '/' + rawBase).replace(/\/+$/, '') : ''
|
|
257
|
+
|
|
258
|
+
const runtime = createStandaloneRuntime(kanbanDir, webviewDir, fastify.server, basePath)
|
|
253
259
|
const { ctx, resolvedWebviewDir } = runtime
|
|
254
260
|
|
|
255
|
-
|
|
256
|
-
let resolvedIndexHtml = indexHtml
|
|
261
|
+
let resolvedIndexHtml = getIndexHtml(basePath)
|
|
257
262
|
let customHead = config.customHeadHtml || ''
|
|
258
263
|
if (config.customHeadHtmlFile) {
|
|
259
264
|
try {
|
|
@@ -262,7 +267,7 @@ export function startServer(kanbanDir: string, port: number, webviewDir?: string
|
|
|
262
267
|
} catch { /* file not found, fall back to customHeadHtml */ }
|
|
263
268
|
}
|
|
264
269
|
if (customHead) {
|
|
265
|
-
resolvedIndexHtml =
|
|
270
|
+
resolvedIndexHtml = resolvedIndexHtml.replace('</head>', `${customHead}\n</head>`)
|
|
266
271
|
}
|
|
267
272
|
const standaloneHttpPlugins = ctx.sdk.capabilities?.standaloneHttpPlugins ?? []
|
|
268
273
|
const standaloneOpenApiSpec = buildStandaloneOpenApiSpec(standaloneHttpPlugins)
|
|
@@ -270,7 +275,7 @@ export function startServer(kanbanDir: string, port: number, webviewDir?: string
|
|
|
270
275
|
// OpenAPI spec and interactive docs (served before the catch-all so Fastify prefers these routes)
|
|
271
276
|
fastify.register(swagger, { openapi: standaloneOpenApiSpec as any })
|
|
272
277
|
fastify.register(swaggerUi, {
|
|
273
|
-
routePrefix:
|
|
278
|
+
routePrefix: `${basePath}/api/docs`,
|
|
274
279
|
uiConfig: { docExpansion: 'list', deepLinking: false },
|
|
275
280
|
logo: swaggerUiLogo,
|
|
276
281
|
...(swaggerUiStaticDir ? { baseDir: swaggerUiStaticDir } : {}),
|
|
@@ -313,6 +318,16 @@ export function startServer(kanbanDir: string, port: number, webviewDir?: string
|
|
|
313
318
|
req._rawBody = request.body
|
|
314
319
|
}
|
|
315
320
|
|
|
321
|
+
// Strip base path prefix so internal route handlers see root-relative paths
|
|
322
|
+
if (basePath) {
|
|
323
|
+
const rawUrl = req.url ?? '/'
|
|
324
|
+
if (rawUrl === basePath) {
|
|
325
|
+
req.url = '/'
|
|
326
|
+
} else if (rawUrl.startsWith(basePath + '/') || rawUrl.startsWith(basePath + '?')) {
|
|
327
|
+
req.url = rawUrl.slice(basePath.length)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
316
331
|
const requestContext = createRequestContext(ctx, req, reply.raw, resolvedWebviewDir, resolvedIndexHtml)
|
|
317
332
|
|
|
318
333
|
await dispatchRequest(requestContext, middlewareHandlers)
|
|
@@ -324,7 +339,50 @@ export function startServer(kanbanDir: string, port: number, webviewDir?: string
|
|
|
324
339
|
reply.hijack()
|
|
325
340
|
})
|
|
326
341
|
|
|
327
|
-
|
|
342
|
+
// Resolve auth context for WebSocket upgrade requests by running the middleware
|
|
343
|
+
// pipeline so session cookies set by auth plugins (e.g. kl-auth-plugin) are honoured.
|
|
344
|
+
const resolveWsAuthContext = async (req: http.IncomingMessage) => {
|
|
345
|
+
const silentRes = (() => {
|
|
346
|
+
const r: Record<string, unknown> = {
|
|
347
|
+
writableEnded: false,
|
|
348
|
+
writeHead() { return r },
|
|
349
|
+
setHeader() { return r },
|
|
350
|
+
removeHeader() { /* no-op */ },
|
|
351
|
+
getHeader() { return undefined },
|
|
352
|
+
getHeaders() { return {} },
|
|
353
|
+
end(..._args: unknown[]) { (r as { writableEnded: boolean }).writableEnded = true; return r },
|
|
354
|
+
write() { return false },
|
|
355
|
+
}
|
|
356
|
+
return r as unknown as import('http').ServerResponse
|
|
357
|
+
})()
|
|
358
|
+
const reqWithBody = req as IncomingMessageWithRawBody
|
|
359
|
+
const wsUrl = new URL(req.url || '/', `http://${req.headers.host || 'localhost'}`)
|
|
360
|
+
const requestContext: StandaloneRequestContext = {
|
|
361
|
+
ctx,
|
|
362
|
+
sdk: ctx.sdk,
|
|
363
|
+
workspaceRoot: ctx.workspaceRoot,
|
|
364
|
+
kanbanDir: ctx.absoluteKanbanDir,
|
|
365
|
+
req: reqWithBody,
|
|
366
|
+
res: silentRes,
|
|
367
|
+
url: wsUrl,
|
|
368
|
+
pathname: wsUrl.pathname,
|
|
369
|
+
method: 'GET',
|
|
370
|
+
resolvedWebviewDir,
|
|
371
|
+
indexHtml: resolvedIndexHtml,
|
|
372
|
+
route: createRouteMatcher('GET', wsUrl.pathname, matchRoute),
|
|
373
|
+
isApiRequest: false,
|
|
374
|
+
isPageRequest: false,
|
|
375
|
+
getAuthContext: () => getRequestAuthContext(req),
|
|
376
|
+
setAuthContext: (auth) => setRequestAuthContext(req, auth),
|
|
377
|
+
mergeAuthContext: (auth) => mergeRequestAuthContext(req, auth),
|
|
378
|
+
}
|
|
379
|
+
for (const handler of middlewareHandlers) {
|
|
380
|
+
if (await handler(requestContext)) break
|
|
381
|
+
}
|
|
382
|
+
return extractAuthContext(req)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
attachWebSocketHandlers(ctx, resolveWsAuthContext)
|
|
328
386
|
setupStandaloneLifecycle(ctx, fastify.server)
|
|
329
387
|
|
|
330
388
|
const effectiveConfigPath = resolvedConfigPath ?? configPath(path.dirname(ctx.absoluteKanbanDir))
|
|
@@ -334,7 +392,7 @@ export function startServer(kanbanDir: string, port: number, webviewDir?: string
|
|
|
334
392
|
console.error('Failed to start server:', err)
|
|
335
393
|
process.exit(1)
|
|
336
394
|
}
|
|
337
|
-
console.log(`Kanban board running at http://localhost:${port}`)
|
|
395
|
+
console.log(`Kanban board running at http://localhost:${port}${basePath}`)
|
|
338
396
|
console.log(`API available at http://localhost:${port}/api`)
|
|
339
397
|
console.log(`Kanban config: ${effectiveConfigPath}`)
|
|
340
398
|
console.log(`Kanban directory: ${ctx.absoluteKanbanDir}`)
|
|
@@ -110,4 +110,13 @@ export function setupWatcher(ctx: StandaloneContext, server: http.Server): void
|
|
|
110
110
|
ctx.wss.close()
|
|
111
111
|
})
|
|
112
112
|
}
|
|
113
|
+
|
|
114
|
+
// Watch .kanban.json for config changes and re-broadcast init on change
|
|
115
|
+
const configFilePath = path.join(ctx.workspaceRoot, '.kanban.json')
|
|
116
|
+
const configWatcher = chokidar.watch(configFilePath, {
|
|
117
|
+
ignoreInitial: true,
|
|
118
|
+
awaitWriteFinish: { stabilityThreshold: 100 }
|
|
119
|
+
})
|
|
120
|
+
configWatcher.on('change', () => handleFileChange(ctx, debounceRef))
|
|
121
|
+
server.on('close', () => configWatcher.close())
|
|
113
122
|
}
|
|
@@ -6,7 +6,8 @@ import type { ConnectionStatusMessage, ExtensionMessage, WebviewMessage } from '
|
|
|
6
6
|
|
|
7
7
|
if (!('acquireVsCodeApi' in window)) {
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const kbBase = (window as unknown as { __KB_BASE__?: string }).__KB_BASE__ ?? ''
|
|
10
|
+
const WS_URL = `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${window.location.host}${kbBase}/ws`
|
|
10
11
|
const RECONNECT_DELAYS_MS = [250, 500, 1000, 2000, 4000] as const
|
|
11
12
|
const MAX_RETRIES = RECONNECT_DELAYS_MS.length
|
|
12
13
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
version: 1
|
|
3
|
+
id: "1"
|
|
4
|
+
status: "deleted"
|
|
5
|
+
priority: "medium"
|
|
6
|
+
assignee: null
|
|
7
|
+
dueDate: null
|
|
8
|
+
created: "2026-03-26T00:54:50.866Z"
|
|
9
|
+
modified: "2026-03-26T00:55:04.504Z"
|
|
10
|
+
completedAt: null
|
|
11
|
+
labels: []
|
|
12
|
+
attachments:
|
|
13
|
+
- "1.log"
|
|
14
|
+
order: "a0"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# dddd
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2026-03-26T00:55:04.503Z [system] Status changed: `in-progress` → `deleted` {"previousStatus":"in-progress","status":"deleted","activity":{"type":"card.status.changed","qualifiesForUnread":true}}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 2,
|
|
3
|
+
"boards": {
|
|
4
|
+
"default": {
|
|
5
|
+
"name": "Default",
|
|
6
|
+
"columns": [
|
|
7
|
+
{
|
|
8
|
+
"id": "backlog",
|
|
9
|
+
"name": "Backlog",
|
|
10
|
+
"color": "#6b7280"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"id": "todo",
|
|
14
|
+
"name": "To Do",
|
|
15
|
+
"color": "#3b82f6"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"id": "in-progress",
|
|
19
|
+
"name": "In Progress",
|
|
20
|
+
"color": "#f59e0b"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "review",
|
|
24
|
+
"name": "Review",
|
|
25
|
+
"color": "#8b5cf6"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "done",
|
|
29
|
+
"name": "Done",
|
|
30
|
+
"color": "#22c55e"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"nextCardId": 1,
|
|
34
|
+
"defaultStatus": "backlog",
|
|
35
|
+
"defaultPriority": "medium"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"defaultBoard": "default",
|
|
39
|
+
"kanbanDirectory": ".kanban",
|
|
40
|
+
"aiAgent": "claude",
|
|
41
|
+
"defaultPriority": "medium",
|
|
42
|
+
"defaultStatus": "backlog",
|
|
43
|
+
"nextCardId": 2,
|
|
44
|
+
"showPriorityBadges": true,
|
|
45
|
+
"showAssignee": true,
|
|
46
|
+
"showDueDate": true,
|
|
47
|
+
"showLabels": true,
|
|
48
|
+
"showBuildWithAI": true,
|
|
49
|
+
"showFileName": false,
|
|
50
|
+
"compactMode": false,
|
|
51
|
+
"markdownEditorMode": false,
|
|
52
|
+
"showDeletedColumn": false,
|
|
53
|
+
"boardZoom": 100,
|
|
54
|
+
"cardZoom": 100,
|
|
55
|
+
"boardBackgroundMode": "fancy",
|
|
56
|
+
"boardBackgroundPreset": "aurora",
|
|
57
|
+
"port": 2954,
|
|
58
|
+
"labels": {}
|
|
59
|
+
}
|