kanban-lite 1.2.2 → 1.2.3

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/extension.js CHANGED
@@ -22970,7 +22970,7 @@ var require_thread_stream = __commonJS({
22970
22970
  var { version } = require_package();
22971
22971
  var { EventEmitter: EventEmitter3 } = require("events");
22972
22972
  var { Worker } = require("worker_threads");
22973
- var { join: join23 } = require("path");
22973
+ var { join: join24 } = require("path");
22974
22974
  var { pathToFileURL } = require("url");
22975
22975
  var { wait } = require_wait();
22976
22976
  var {
@@ -23006,7 +23006,7 @@ var require_thread_stream = __commonJS({
23006
23006
  function createWorker(stream, opts) {
23007
23007
  const { filename, workerData } = opts;
23008
23008
  const bundlerOverrides = "__bundlerPathsOverrides" in globalThis ? globalThis.__bundlerPathsOverrides : {};
23009
- const toExecute = bundlerOverrides["thread-stream-worker"] || join23(__dirname, "lib", "worker.js");
23009
+ const toExecute = bundlerOverrides["thread-stream-worker"] || join24(__dirname, "lib", "worker.js");
23010
23010
  const worker = new Worker(toExecute, {
23011
23011
  ...opts.workerOpts,
23012
23012
  trackUnmanagedFds: false,
@@ -23397,7 +23397,7 @@ var require_transport = __commonJS({
23397
23397
  var { createRequire: createRequire2 } = require("module");
23398
23398
  var { existsSync: existsSync4 } = require("node:fs");
23399
23399
  var getCallers = require_caller();
23400
- var { join: join23, isAbsolute: isAbsolute2, sep: sep2 } = require("node:path");
23400
+ var { join: join24, isAbsolute: isAbsolute2, sep: sep2 } = require("node:path");
23401
23401
  var { fileURLToPath } = require("node:url");
23402
23402
  var sleep = require_atomic_sleep();
23403
23403
  var onExit = require_on_exit_leak_free();
@@ -23550,7 +23550,7 @@ var require_transport = __commonJS({
23550
23550
  throw new Error("only one of target or targets can be specified");
23551
23551
  }
23552
23552
  if (targets) {
23553
- target = bundlerOverrides["pino-worker"] || join23(__dirname, "worker.js");
23553
+ target = bundlerOverrides["pino-worker"] || join24(__dirname, "worker.js");
23554
23554
  options.targets = targets.filter((dest) => dest.target).map((dest) => {
23555
23555
  return {
23556
23556
  ...dest,
@@ -23568,7 +23568,7 @@ var require_transport = __commonJS({
23568
23568
  });
23569
23569
  });
23570
23570
  } else if (pipeline) {
23571
- target = bundlerOverrides["pino-worker"] || join23(__dirname, "worker.js");
23571
+ target = bundlerOverrides["pino-worker"] || join24(__dirname, "worker.js");
23572
23572
  options.pipelines = [pipeline.map((dest) => {
23573
23573
  return {
23574
23574
  ...dest,
@@ -23591,7 +23591,7 @@ var require_transport = __commonJS({
23591
23591
  return origin;
23592
23592
  }
23593
23593
  if (origin === "pino/file") {
23594
- return join23(__dirname, "..", "file.js");
23594
+ return join24(__dirname, "..", "file.js");
23595
23595
  }
23596
23596
  let fixTarget2;
23597
23597
  for (const filePath of callers) {
@@ -24581,7 +24581,7 @@ var require_safe_stable_stringify = __commonJS({
24581
24581
  return circularValue;
24582
24582
  }
24583
24583
  let res = "";
24584
- let join23 = ",";
24584
+ let join24 = ",";
24585
24585
  const originalIndentation = indentation;
24586
24586
  if (Array.isArray(value)) {
24587
24587
  if (value.length === 0) {
@@ -24595,7 +24595,7 @@ var require_safe_stable_stringify = __commonJS({
24595
24595
  indentation += spacer;
24596
24596
  res += `
24597
24597
  ${indentation}`;
24598
- join23 = `,
24598
+ join24 = `,
24599
24599
  ${indentation}`;
24600
24600
  }
24601
24601
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -24603,13 +24603,13 @@ ${indentation}`;
24603
24603
  for (; i < maximumValuesToStringify - 1; i++) {
24604
24604
  const tmp2 = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
24605
24605
  res += tmp2 !== void 0 ? tmp2 : "null";
24606
- res += join23;
24606
+ res += join24;
24607
24607
  }
24608
24608
  const tmp = stringifyFnReplacer(String(i), value, stack, replacer, spacer, indentation);
24609
24609
  res += tmp !== void 0 ? tmp : "null";
24610
24610
  if (value.length - 1 > maximumBreadth) {
24611
24611
  const removedKeys = value.length - maximumBreadth - 1;
24612
- res += `${join23}"... ${getItemCount(removedKeys)} not stringified"`;
24612
+ res += `${join24}"... ${getItemCount(removedKeys)} not stringified"`;
24613
24613
  }
24614
24614
  if (spacer !== "") {
24615
24615
  res += `
@@ -24630,7 +24630,7 @@ ${originalIndentation}`;
24630
24630
  let separator = "";
24631
24631
  if (spacer !== "") {
24632
24632
  indentation += spacer;
24633
- join23 = `,
24633
+ join24 = `,
24634
24634
  ${indentation}`;
24635
24635
  whitespace = " ";
24636
24636
  }
@@ -24644,13 +24644,13 @@ ${indentation}`;
24644
24644
  const tmp = stringifyFnReplacer(key2, value, stack, replacer, spacer, indentation);
24645
24645
  if (tmp !== void 0) {
24646
24646
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
24647
- separator = join23;
24647
+ separator = join24;
24648
24648
  }
24649
24649
  }
24650
24650
  if (keyLength > maximumBreadth) {
24651
24651
  const removedKeys = keyLength - maximumBreadth;
24652
24652
  res += `${separator}"...":${whitespace}"${getItemCount(removedKeys)} not stringified"`;
24653
- separator = join23;
24653
+ separator = join24;
24654
24654
  }
24655
24655
  if (spacer !== "" && separator.length > 1) {
24656
24656
  res = `
@@ -24690,7 +24690,7 @@ ${originalIndentation}`;
24690
24690
  }
24691
24691
  const originalIndentation = indentation;
24692
24692
  let res = "";
24693
- let join23 = ",";
24693
+ let join24 = ",";
24694
24694
  if (Array.isArray(value)) {
24695
24695
  if (value.length === 0) {
24696
24696
  return "[]";
@@ -24703,7 +24703,7 @@ ${originalIndentation}`;
24703
24703
  indentation += spacer;
24704
24704
  res += `
24705
24705
  ${indentation}`;
24706
- join23 = `,
24706
+ join24 = `,
24707
24707
  ${indentation}`;
24708
24708
  }
24709
24709
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
@@ -24711,13 +24711,13 @@ ${indentation}`;
24711
24711
  for (; i < maximumValuesToStringify - 1; i++) {
24712
24712
  const tmp2 = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
24713
24713
  res += tmp2 !== void 0 ? tmp2 : "null";
24714
- res += join23;
24714
+ res += join24;
24715
24715
  }
24716
24716
  const tmp = stringifyArrayReplacer(String(i), value[i], stack, replacer, spacer, indentation);
24717
24717
  res += tmp !== void 0 ? tmp : "null";
24718
24718
  if (value.length - 1 > maximumBreadth) {
24719
24719
  const removedKeys = value.length - maximumBreadth - 1;
24720
- res += `${join23}"... ${getItemCount(removedKeys)} not stringified"`;
24720
+ res += `${join24}"... ${getItemCount(removedKeys)} not stringified"`;
24721
24721
  }
24722
24722
  if (spacer !== "") {
24723
24723
  res += `
@@ -24730,7 +24730,7 @@ ${originalIndentation}`;
24730
24730
  let whitespace = "";
24731
24731
  if (spacer !== "") {
24732
24732
  indentation += spacer;
24733
- join23 = `,
24733
+ join24 = `,
24734
24734
  ${indentation}`;
24735
24735
  whitespace = " ";
24736
24736
  }
@@ -24739,7 +24739,7 @@ ${indentation}`;
24739
24739
  const tmp = stringifyArrayReplacer(key2, value[key2], stack, replacer, spacer, indentation);
24740
24740
  if (tmp !== void 0) {
24741
24741
  res += `${separator}${strEscape(key2)}:${whitespace}${tmp}`;
24742
- separator = join23;
24742
+ separator = join24;
24743
24743
  }
24744
24744
  }
24745
24745
  if (spacer !== "" && separator.length > 1) {
@@ -24796,20 +24796,20 @@ ${originalIndentation}`;
24796
24796
  indentation += spacer;
24797
24797
  let res2 = `
24798
24798
  ${indentation}`;
24799
- const join24 = `,
24799
+ const join25 = `,
24800
24800
  ${indentation}`;
24801
24801
  const maximumValuesToStringify = Math.min(value.length, maximumBreadth);
24802
24802
  let i = 0;
24803
24803
  for (; i < maximumValuesToStringify - 1; i++) {
24804
24804
  const tmp2 = stringifyIndent(String(i), value[i], stack, spacer, indentation);
24805
24805
  res2 += tmp2 !== void 0 ? tmp2 : "null";
24806
- res2 += join24;
24806
+ res2 += join25;
24807
24807
  }
24808
24808
  const tmp = stringifyIndent(String(i), value[i], stack, spacer, indentation);
24809
24809
  res2 += tmp !== void 0 ? tmp : "null";
24810
24810
  if (value.length - 1 > maximumBreadth) {
24811
24811
  const removedKeys = value.length - maximumBreadth - 1;
24812
- res2 += `${join24}"... ${getItemCount(removedKeys)} not stringified"`;
24812
+ res2 += `${join25}"... ${getItemCount(removedKeys)} not stringified"`;
24813
24813
  }
24814
24814
  res2 += `
24815
24815
  ${originalIndentation}`;
@@ -24825,16 +24825,16 @@ ${originalIndentation}`;
24825
24825
  return '"[Object]"';
24826
24826
  }
24827
24827
  indentation += spacer;
24828
- const join23 = `,
24828
+ const join24 = `,
24829
24829
  ${indentation}`;
24830
24830
  let res = "";
24831
24831
  let separator = "";
24832
24832
  let maximumPropertiesToStringify = Math.min(keyLength, maximumBreadth);
24833
24833
  if (isTypedArrayWithEntries(value)) {
24834
- res += stringifyTypedArray(value, join23, maximumBreadth);
24834
+ res += stringifyTypedArray(value, join24, maximumBreadth);
24835
24835
  keys = keys.slice(value.length);
24836
24836
  maximumPropertiesToStringify -= value.length;
24837
- separator = join23;
24837
+ separator = join24;
24838
24838
  }
24839
24839
  if (deterministic) {
24840
24840
  keys = sort(keys, comparator);
@@ -24845,13 +24845,13 @@ ${indentation}`;
24845
24845
  const tmp = stringifyIndent(key2, value[key2], stack, spacer, indentation);
24846
24846
  if (tmp !== void 0) {
24847
24847
  res += `${separator}${strEscape(key2)}: ${tmp}`;
24848
- separator = join23;
24848
+ separator = join24;
24849
24849
  }
24850
24850
  }
24851
24851
  if (keyLength > maximumBreadth) {
24852
24852
  const removedKeys = keyLength - maximumBreadth;
24853
24853
  res += `${separator}"...": "${getItemCount(removedKeys)} not stringified"`;
24854
- separator = join23;
24854
+ separator = join24;
24855
24855
  }
24856
24856
  if (separator !== "") {
24857
24857
  res = `
@@ -59636,7 +59636,7 @@ var require_send = __commonJS({
59636
59636
  var { parseTokenList } = require_parseTokenList();
59637
59637
  var { createHttpError } = require_createHttpError();
59638
59638
  var extname5 = path24.extname;
59639
- var join23 = path24.join;
59639
+ var join24 = path24.join;
59640
59640
  var normalize2 = path24.normalize;
59641
59641
  var resolve10 = path24.resolve;
59642
59642
  var sep2 = path24.sep;
@@ -59723,7 +59723,7 @@ var require_send = __commonJS({
59723
59723
  return { statusCode: 403 };
59724
59724
  }
59725
59725
  parts = path25.split(sep2);
59726
- path25 = normalize2(join23(root, path25));
59726
+ path25 = normalize2(join24(root, path25));
59727
59727
  } else {
59728
59728
  if (UP_PATH_REGEXP.test(path25)) {
59729
59729
  debug('malicious path "%s"', path25);
@@ -60007,7 +60007,7 @@ var require_send = __commonJS({
60007
60007
  let err;
60008
60008
  for (let i = 0; i < options.index.length; i++) {
60009
60009
  const index = options.index[i];
60010
- const p = join23(path25, index);
60010
+ const p = join24(path25, index);
60011
60011
  const { error, stat: stat4 } = await tryStat(p);
60012
60012
  if (error) {
60013
60013
  err = error;
@@ -65329,11 +65329,83 @@ function migrateConfigV1ToV2(raw) {
65329
65329
  }
65330
65330
  return v2;
65331
65331
  }
65332
+ function loadDotEnv(dir) {
65333
+ const envPath = path.join(dir, ".env");
65334
+ let content;
65335
+ try {
65336
+ content = fs.readFileSync(envPath, "utf-8");
65337
+ } catch {
65338
+ return;
65339
+ }
65340
+ for (const line of content.split("\n")) {
65341
+ const trimmed = line.trim();
65342
+ if (!trimmed || trimmed.startsWith("#"))
65343
+ continue;
65344
+ const eqIdx = trimmed.indexOf("=");
65345
+ if (eqIdx < 1)
65346
+ continue;
65347
+ const key = trimmed.slice(0, eqIdx).trim();
65348
+ let val = trimmed.slice(eqIdx + 1).trim();
65349
+ if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
65350
+ val = val.slice(1, -1);
65351
+ }
65352
+ if (process.env[key] === void 0) {
65353
+ process.env[key] = val;
65354
+ }
65355
+ }
65356
+ }
65357
+ function resolveConfigEnvVars(node, configFileName, nodePath = "") {
65358
+ if (typeof node === "string") {
65359
+ return node.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
65360
+ const envValue = process.env[varName];
65361
+ if (envValue === void 0) {
65362
+ throw new Error(
65363
+ `missing ${varName} in ${configFileName}: ${nodePath} "${node}"`
65364
+ );
65365
+ }
65366
+ return envValue;
65367
+ });
65368
+ }
65369
+ if (Array.isArray(node)) {
65370
+ for (let i = 0; i < node.length; i++) {
65371
+ node[i] = resolveConfigEnvVars(node[i], configFileName, `${nodePath}[${i}]`);
65372
+ }
65373
+ return node;
65374
+ }
65375
+ if (node !== null && typeof node === "object") {
65376
+ const obj = node;
65377
+ for (const key of Object.keys(obj)) {
65378
+ const jsonKey = /[^a-zA-Z0-9_]/.test(key) ? `"${key}"` : key;
65379
+ const childPath = nodePath ? `${nodePath}.${jsonKey}` : `.${jsonKey}`;
65380
+ obj[key] = resolveConfigEnvVars(obj[key], configFileName, childPath);
65381
+ }
65382
+ return obj;
65383
+ }
65384
+ return node;
65385
+ }
65332
65386
  function readConfig(workspaceRoot) {
65333
65387
  const filePath = configPath(workspaceRoot);
65334
65388
  const defaults = { ...DEFAULT_CONFIG, boards: { default: { ...DEFAULT_BOARD_CONFIG, columns: [...DEFAULT_COLUMNS] } } };
65389
+ let raw;
65390
+ try {
65391
+ raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
65392
+ } catch {
65393
+ return defaults;
65394
+ }
65395
+ loadDotEnv(workspaceRoot);
65396
+ try {
65397
+ resolveConfigEnvVars(raw, CONFIG_FILENAME);
65398
+ } catch (err) {
65399
+ const msg = err instanceof Error ? err.message : String(err);
65400
+ process.stderr.write(`
65401
+ Configuration error: ${msg}
65402
+
65403
+ Set the missing environment variable before starting the server.
65404
+
65405
+ `);
65406
+ process.exit(1);
65407
+ }
65335
65408
  try {
65336
- const raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
65337
65409
  const isV1 = raw.version === 1 || !raw.version && !(typeof raw.boards === "object" && raw.boards !== null && !Array.isArray(raw.boards));
65338
65410
  if (isV1) {
65339
65411
  const v2 = migrateConfigV1ToV2(raw);
@@ -68937,7 +69009,7 @@ function resolveAuthIdentityPlugin(ref) {
68937
69009
  if (ref.provider === "rbac")
68938
69010
  return RBAC_IDENTITY_PLUGIN;
68939
69011
  const packageName = AUTH_PROVIDER_ALIASES.get(ref.provider) ?? ref.provider;
68940
- return loadExternalAuthIdentityPlugin(packageName, ref.provider);
69012
+ return loadExternalAuthIdentityPlugin(packageName, ref.provider, ref.options);
68941
69013
  }
68942
69014
  function resolveAuthPolicyPlugin(ref) {
68943
69015
  if (ref.provider === "noop")
@@ -69163,8 +69235,13 @@ function selectAuthPolicyPlugin(mod, providerId) {
69163
69235
  return direct;
69164
69236
  return null;
69165
69237
  }
69166
- function loadExternalAuthIdentityPlugin(packageName, providerId) {
69238
+ function loadExternalAuthIdentityPlugin(packageName, providerId, options) {
69167
69239
  const mod = loadExternalModule(packageName);
69240
+ if (options !== void 0 && typeof mod.createAuthIdentityPlugin === "function") {
69241
+ const created = mod.createAuthIdentityPlugin(options);
69242
+ if (isValidAuthIdentityPlugin(created, providerId))
69243
+ return created;
69244
+ }
69168
69245
  const plugin = selectAuthIdentityPlugin(mod, providerId);
69169
69246
  if (!plugin) {
69170
69247
  throw new Error(
@@ -72167,29 +72244,10 @@ async function updateCard(ctx, { cardId, updates, boardId }) {
72167
72244
  return card;
72168
72245
  }
72169
72246
  async function triggerAction(ctx, { cardId, action, boardId }) {
72170
- const config = readConfig(ctx.workspaceRoot);
72171
- const { actionWebhookUrl } = config;
72172
- if (!actionWebhookUrl) {
72173
- throw new Error("No action webhook URL configured. Set actionWebhookUrl in .kanban.json");
72174
- }
72175
72247
  const card = await getCard(ctx, { cardId, boardId });
72176
72248
  if (!card)
72177
72249
  throw new Error(`Card not found: ${cardId}`);
72178
72250
  const resolvedBoardId = card.boardId || ctx._resolveBoardId(boardId);
72179
- const payload = {
72180
- action,
72181
- board: resolvedBoardId,
72182
- list: card.status,
72183
- card: sanitizeCard(card)
72184
- };
72185
- const response = await fetch(actionWebhookUrl, {
72186
- method: "POST",
72187
- headers: { "Content-Type": "application/json" },
72188
- body: JSON.stringify(payload)
72189
- });
72190
- if (!response.ok) {
72191
- throw new Error(`Action webhook responded with ${response.status}: ${response.statusText}`);
72192
- }
72193
72251
  await appendActivityLog(ctx, {
72194
72252
  cardId,
72195
72253
  boardId: resolvedBoardId,
@@ -72200,6 +72258,12 @@ async function triggerAction(ctx, { cardId, action, boardId }) {
72200
72258
  }
72201
72259
  }).catch(() => {
72202
72260
  });
72261
+ return {
72262
+ action,
72263
+ board: resolvedBoardId,
72264
+ list: card.status,
72265
+ card: sanitizeCard(card)
72266
+ };
72203
72267
  }
72204
72268
  async function submitForm(ctx, input) {
72205
72269
  const card = await getCard(ctx, { cardId: input.cardId, boardId: input.boardId });
@@ -74082,21 +74146,17 @@ var KanbanSDK = class _KanbanSDK {
74082
74146
  return result;
74083
74147
  }
74084
74148
  /**
74085
- * Triggers a named action for a card by POSTing to the global `actionWebhookUrl`
74086
- * configured in `.kanban.json`.
74149
+ * Triggers a named action for a card.
74087
74150
  *
74088
- * The payload sent to the webhook is:
74089
- * ```json
74090
- * { "action": "retry", "board": "default", "list": "in-progress", "card": { ...sanitizedCard } }
74091
- * ```
74151
+ * Validates the card, appends an activity log entry, and emits the
74152
+ * `card.action.triggered` after-event so registered webhooks receive
74153
+ * the action payload automatically.
74092
74154
  *
74093
74155
  * @param cardId - The ID of the card to trigger the action for.
74094
74156
  * @param action - The action name string (e.g. `'retry'`, `'sendEmail'`).
74095
74157
  * @param boardId - Optional board ID. Defaults to the workspace's default board.
74096
- * @returns A promise resolving when the webhook responds with 2xx.
74097
- * @throws {Error} If no `actionWebhookUrl` is configured in `.kanban.json`.
74158
+ * @returns A promise that resolves when the action has been processed.
74098
74159
  * @throws {Error} If the card is not found.
74099
- * @throws {Error} If the webhook responds with a non-2xx status.
74100
74160
  *
74101
74161
  * @example
74102
74162
  * ```ts
@@ -74106,7 +74166,8 @@ var KanbanSDK = class _KanbanSDK {
74106
74166
  */
74107
74167
  async triggerAction(cardId, action, boardId) {
74108
74168
  const mergedInput = await this._runBeforeEvent("card.action.trigger", { cardId, action, boardId }, void 0, boardId);
74109
- return triggerAction(this, mergedInput);
74169
+ const payload = await triggerAction(this, mergedInput);
74170
+ this._runAfterEvent("card.action.triggered", payload, void 0, payload.board);
74110
74171
  }
74111
74172
  /**
74112
74173
  * Moves a card to a different status column and/or position within that column.
@@ -78318,6 +78379,13 @@ function setupWatcher(ctx, server) {
78318
78379
  ctx.wss.close();
78319
78380
  });
78320
78381
  }
78382
+ const configFilePath = path16.join(ctx.workspaceRoot, ".kanban.json");
78383
+ const configWatcher = esm_default.watch(configFilePath, {
78384
+ ignoreInitial: true,
78385
+ awaitWriteFinish: { stabilityThreshold: 100 }
78386
+ });
78387
+ configWatcher.on("change", () => handleFileChange(ctx, debounceRef));
78388
+ server.on("close", () => configWatcher.close());
78321
78389
  }
78322
78390
 
78323
78391
  // src/standalone/httpUtils.ts
@@ -78494,20 +78562,24 @@ async function handleCardFileRoute(request) {
78494
78562
  var http = __toESM(require("http"));
78495
78563
  var fs12 = __toESM(require("fs"));
78496
78564
  var path18 = __toESM(require("path"));
78497
- var indexHtml = `<!DOCTYPE html>
78565
+ function getIndexHtml(basePath = "") {
78566
+ return `<!DOCTYPE html>
78498
78567
  <html lang="en">
78499
78568
  <head>
78500
78569
  <meta charset="UTF-8">
78501
78570
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
78502
- <link rel="icon" type="image/svg+xml" href="/favicon.svg">
78503
- <link href="/style.css" rel="stylesheet">
78571
+ <link rel="icon" type="image/svg+xml" href="${basePath}/favicon.svg">
78572
+ <link href="${basePath}/style.css" rel="stylesheet">
78504
78573
  <title>Kanban Board</title>
78574
+ <script>window.__KB_BASE__ = ${JSON.stringify(basePath)}</script>
78505
78575
  </head>
78506
78576
  <body>
78507
78577
  <div id="root"></div>
78508
- <script type="module" src="/index.js"></script>
78578
+ <script type="module" src="${basePath}/index.js"></script>
78509
78579
  </body>
78510
78580
  </html>`;
78581
+ }
78582
+ var indexHtml = getIndexHtml();
78511
78583
  function resolveStandaloneWebviewDir(webviewDir) {
78512
78584
  if (webviewDir)
78513
78585
  return webviewDir;
@@ -78521,12 +78593,12 @@ function resolveStandaloneWebviewDir(webviewDir) {
78521
78593
  }
78522
78594
  return candidates[0];
78523
78595
  }
78524
- function createStandaloneRuntime(kanbanDir, webviewDir, httpServer) {
78596
+ function createStandaloneRuntime(kanbanDir, webviewDir, httpServer, basePath) {
78525
78597
  const absoluteKanbanDir = path18.resolve(kanbanDir);
78526
78598
  const workspaceRoot = path18.dirname(absoluteKanbanDir);
78527
78599
  const resolvedWebviewDir = resolveStandaloneWebviewDir(webviewDir);
78528
78600
  const server = httpServer ?? http.createServer();
78529
- const wss = new import_websocket_server.default({ server, path: "/ws" });
78601
+ const wss = new import_websocket_server.default({ server, path: (basePath || "") + "/ws" });
78530
78602
  const ctx = {};
78531
78603
  const sdk = new KanbanSDK(absoluteKanbanDir, {
78532
78604
  onEvent: (event, data) => {
@@ -80800,10 +80872,13 @@ function startServer(kanbanDir, port, webviewDir, resolvedConfigPath) {
80800
80872
  const swaggerUiStaticDir = resolveSwaggerUiStaticDir();
80801
80873
  const swaggerUiLogoPath = swaggerUiStaticDir ? path20.join(swaggerUiStaticDir, "logo.svg") : void 0;
80802
80874
  const swaggerUiLogo = swaggerUiLogoPath && fs15.existsSync(swaggerUiLogoPath) ? { type: "image/svg+xml", content: fs15.readFileSync(swaggerUiLogoPath) } : null;
80803
- const runtime = createStandaloneRuntime(kanbanDir, webviewDir, fastify.server);
80875
+ const workspaceRoot = path20.dirname(path20.resolve(kanbanDir));
80876
+ const config = readConfig(workspaceRoot);
80877
+ const rawBase = config.basePath ?? "";
80878
+ const basePath = rawBase ? (rawBase.startsWith("/") ? rawBase : "/" + rawBase).replace(/\/+$/, "") : "";
80879
+ const runtime = createStandaloneRuntime(kanbanDir, webviewDir, fastify.server, basePath);
80804
80880
  const { ctx, resolvedWebviewDir } = runtime;
80805
- const config = readConfig(ctx.workspaceRoot);
80806
- let resolvedIndexHtml = indexHtml;
80881
+ let resolvedIndexHtml = getIndexHtml(basePath);
80807
80882
  let customHead = config.customHeadHtml || "";
80808
80883
  if (config.customHeadHtmlFile) {
80809
80884
  try {
@@ -80813,14 +80888,14 @@ function startServer(kanbanDir, port, webviewDir, resolvedConfigPath) {
80813
80888
  }
80814
80889
  }
80815
80890
  if (customHead) {
80816
- resolvedIndexHtml = indexHtml.replace("</head>", `${customHead}
80891
+ resolvedIndexHtml = resolvedIndexHtml.replace("</head>", `${customHead}
80817
80892
  </head>`);
80818
80893
  }
80819
80894
  const standaloneHttpPlugins = ctx.sdk.capabilities?.standaloneHttpPlugins ?? [];
80820
80895
  const standaloneOpenApiSpec = buildStandaloneOpenApiSpec(standaloneHttpPlugins);
80821
80896
  fastify.register(import_swagger.default, { openapi: standaloneOpenApiSpec });
80822
80897
  fastify.register(import_swagger_ui.default, {
80823
- routePrefix: "/api/docs",
80898
+ routePrefix: `${basePath}/api/docs`,
80824
80899
  uiConfig: { docExpansion: "list", deepLinking: false },
80825
80900
  logo: swaggerUiLogo,
80826
80901
  ...swaggerUiStaticDir ? { baseDir: swaggerUiStaticDir } : {}
@@ -80852,6 +80927,14 @@ function startServer(kanbanDir, port, webviewDir, resolvedConfigPath) {
80852
80927
  if (request.body instanceof Buffer && request.body.length > 0) {
80853
80928
  req._rawBody = request.body;
80854
80929
  }
80930
+ if (basePath) {
80931
+ const rawUrl = req.url ?? "/";
80932
+ if (rawUrl === basePath) {
80933
+ req.url = "/";
80934
+ } else if (rawUrl.startsWith(basePath + "/") || rawUrl.startsWith(basePath + "?")) {
80935
+ req.url = rawUrl.slice(basePath.length);
80936
+ }
80937
+ }
80855
80938
  const requestContext = createRequestContext(ctx, req, reply.raw, resolvedWebviewDir, resolvedIndexHtml);
80856
80939
  await dispatchRequest(requestContext, middlewareHandlers);
80857
80940
  if (!reply.sent && !reply.raw.writableEnded) {
@@ -80867,7 +80950,7 @@ function startServer(kanbanDir, port, webviewDir, resolvedConfigPath) {
80867
80950
  console.error("Failed to start server:", err);
80868
80951
  process.exit(1);
80869
80952
  }
80870
- console.log(`Kanban board running at http://localhost:${port}`);
80953
+ console.log(`Kanban board running at http://localhost:${port}${basePath}`);
80871
80954
  console.log(`API available at http://localhost:${port}/api`);
80872
80955
  console.log(`Kanban config: ${effectiveConfigPath}`);
80873
80956
  console.log(`Kanban directory: ${ctx.absoluteKanbanDir}`);