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.
@@ -17664,11 +17664,83 @@ function migrateConfigV1ToV2(raw) {
17664
17664
  }
17665
17665
  return v2;
17666
17666
  }
17667
+ function loadDotEnv(dir) {
17668
+ const envPath = path.join(dir, ".env");
17669
+ let content;
17670
+ try {
17671
+ content = fs.readFileSync(envPath, "utf-8");
17672
+ } catch {
17673
+ return;
17674
+ }
17675
+ for (const line of content.split("\n")) {
17676
+ const trimmed = line.trim();
17677
+ if (!trimmed || trimmed.startsWith("#"))
17678
+ continue;
17679
+ const eqIdx = trimmed.indexOf("=");
17680
+ if (eqIdx < 1)
17681
+ continue;
17682
+ const key = trimmed.slice(0, eqIdx).trim();
17683
+ let val = trimmed.slice(eqIdx + 1).trim();
17684
+ if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
17685
+ val = val.slice(1, -1);
17686
+ }
17687
+ if (process.env[key] === void 0) {
17688
+ process.env[key] = val;
17689
+ }
17690
+ }
17691
+ }
17692
+ function resolveConfigEnvVars(node, configFileName, nodePath = "") {
17693
+ if (typeof node === "string") {
17694
+ return node.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
17695
+ const envValue = process.env[varName];
17696
+ if (envValue === void 0) {
17697
+ throw new Error(
17698
+ `missing ${varName} in ${configFileName}: ${nodePath} "${node}"`
17699
+ );
17700
+ }
17701
+ return envValue;
17702
+ });
17703
+ }
17704
+ if (Array.isArray(node)) {
17705
+ for (let i = 0; i < node.length; i++) {
17706
+ node[i] = resolveConfigEnvVars(node[i], configFileName, `${nodePath}[${i}]`);
17707
+ }
17708
+ return node;
17709
+ }
17710
+ if (node !== null && typeof node === "object") {
17711
+ const obj = node;
17712
+ for (const key of Object.keys(obj)) {
17713
+ const jsonKey = /[^a-zA-Z0-9_]/.test(key) ? `"${key}"` : key;
17714
+ const childPath = nodePath ? `${nodePath}.${jsonKey}` : `.${jsonKey}`;
17715
+ obj[key] = resolveConfigEnvVars(obj[key], configFileName, childPath);
17716
+ }
17717
+ return obj;
17718
+ }
17719
+ return node;
17720
+ }
17667
17721
  function readConfig(workspaceRoot) {
17668
17722
  const filePath = configPath(workspaceRoot);
17669
17723
  const defaults = { ...DEFAULT_CONFIG, boards: { default: { ...DEFAULT_BOARD_CONFIG, columns: [...DEFAULT_COLUMNS] } } };
17724
+ let raw;
17725
+ try {
17726
+ raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
17727
+ } catch {
17728
+ return defaults;
17729
+ }
17730
+ loadDotEnv(workspaceRoot);
17731
+ try {
17732
+ resolveConfigEnvVars(raw, CONFIG_FILENAME);
17733
+ } catch (err) {
17734
+ const msg = err instanceof Error ? err.message : String(err);
17735
+ process.stderr.write(`
17736
+ Configuration error: ${msg}
17737
+
17738
+ Set the missing environment variable before starting the server.
17739
+
17740
+ `);
17741
+ process.exit(1);
17742
+ }
17670
17743
  try {
17671
- const raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
17672
17744
  const isV1 = raw.version === 1 || !raw.version && !(typeof raw.boards === "object" && raw.boards !== null && !Array.isArray(raw.boards));
17673
17745
  if (isV1) {
17674
17746
  const v2 = migrateConfigV1ToV2(raw);
@@ -21268,7 +21340,7 @@ function resolveAuthIdentityPlugin(ref) {
21268
21340
  if (ref.provider === "rbac")
21269
21341
  return RBAC_IDENTITY_PLUGIN;
21270
21342
  const packageName = AUTH_PROVIDER_ALIASES.get(ref.provider) ?? ref.provider;
21271
- return loadExternalAuthIdentityPlugin(packageName, ref.provider);
21343
+ return loadExternalAuthIdentityPlugin(packageName, ref.provider, ref.options);
21272
21344
  }
21273
21345
  function resolveAuthPolicyPlugin(ref) {
21274
21346
  if (ref.provider === "noop")
@@ -21494,8 +21566,13 @@ function selectAuthPolicyPlugin(mod, providerId) {
21494
21566
  return direct;
21495
21567
  return null;
21496
21568
  }
21497
- function loadExternalAuthIdentityPlugin(packageName, providerId) {
21569
+ function loadExternalAuthIdentityPlugin(packageName, providerId, options) {
21498
21570
  const mod = loadExternalModule(packageName);
21571
+ if (options !== void 0 && typeof mod.createAuthIdentityPlugin === "function") {
21572
+ const created = mod.createAuthIdentityPlugin(options);
21573
+ if (isValidAuthIdentityPlugin(created, providerId))
21574
+ return created;
21575
+ }
21499
21576
  const plugin = selectAuthIdentityPlugin(mod, providerId);
21500
21577
  if (!plugin) {
21501
21578
  throw new Error(
@@ -24565,29 +24642,10 @@ async function updateCard(ctx, { cardId, updates, boardId }) {
24565
24642
  return card;
24566
24643
  }
24567
24644
  async function triggerAction(ctx, { cardId, action, boardId }) {
24568
- const config = readConfig(ctx.workspaceRoot);
24569
- const { actionWebhookUrl } = config;
24570
- if (!actionWebhookUrl) {
24571
- throw new Error("No action webhook URL configured. Set actionWebhookUrl in .kanban.json");
24572
- }
24573
24645
  const card = await getCard(ctx, { cardId, boardId });
24574
24646
  if (!card)
24575
24647
  throw new Error(`Card not found: ${cardId}`);
24576
24648
  const resolvedBoardId = card.boardId || ctx._resolveBoardId(boardId);
24577
- const payload = {
24578
- action,
24579
- board: resolvedBoardId,
24580
- list: card.status,
24581
- card: sanitizeCard(card)
24582
- };
24583
- const response = await fetch(actionWebhookUrl, {
24584
- method: "POST",
24585
- headers: { "Content-Type": "application/json" },
24586
- body: JSON.stringify(payload)
24587
- });
24588
- if (!response.ok) {
24589
- throw new Error(`Action webhook responded with ${response.status}: ${response.statusText}`);
24590
- }
24591
24649
  await appendActivityLog(ctx, {
24592
24650
  cardId,
24593
24651
  boardId: resolvedBoardId,
@@ -24598,6 +24656,12 @@ async function triggerAction(ctx, { cardId, action, boardId }) {
24598
24656
  }
24599
24657
  }).catch(() => {
24600
24658
  });
24659
+ return {
24660
+ action,
24661
+ board: resolvedBoardId,
24662
+ list: card.status,
24663
+ card: sanitizeCard(card)
24664
+ };
24601
24665
  }
24602
24666
  async function submitForm(ctx, input) {
24603
24667
  const card = await getCard(ctx, { cardId: input.cardId, boardId: input.boardId });
@@ -26480,21 +26544,17 @@ var KanbanSDK = class _KanbanSDK {
26480
26544
  return result;
26481
26545
  }
26482
26546
  /**
26483
- * Triggers a named action for a card by POSTing to the global `actionWebhookUrl`
26484
- * configured in `.kanban.json`.
26547
+ * Triggers a named action for a card.
26485
26548
  *
26486
- * The payload sent to the webhook is:
26487
- * ```json
26488
- * { "action": "retry", "board": "default", "list": "in-progress", "card": { ...sanitizedCard } }
26489
- * ```
26549
+ * Validates the card, appends an activity log entry, and emits the
26550
+ * `card.action.triggered` after-event so registered webhooks receive
26551
+ * the action payload automatically.
26490
26552
  *
26491
26553
  * @param cardId - The ID of the card to trigger the action for.
26492
26554
  * @param action - The action name string (e.g. `'retry'`, `'sendEmail'`).
26493
26555
  * @param boardId - Optional board ID. Defaults to the workspace's default board.
26494
- * @returns A promise resolving when the webhook responds with 2xx.
26495
- * @throws {Error} If no `actionWebhookUrl` is configured in `.kanban.json`.
26556
+ * @returns A promise that resolves when the action has been processed.
26496
26557
  * @throws {Error} If the card is not found.
26497
- * @throws {Error} If the webhook responds with a non-2xx status.
26498
26558
  *
26499
26559
  * @example
26500
26560
  * ```ts
@@ -26504,7 +26564,8 @@ var KanbanSDK = class _KanbanSDK {
26504
26564
  */
26505
26565
  async triggerAction(cardId, action, boardId) {
26506
26566
  const mergedInput = await this._runBeforeEvent("card.action.trigger", { cardId, action, boardId }, void 0, boardId);
26507
- return triggerAction(this, mergedInput);
26567
+ const payload = await triggerAction(this, mergedInput);
26568
+ this._runAfterEvent("card.action.triggered", payload, void 0, payload.board);
26508
26569
  }
26509
26570
  /**
26510
26571
  * Moves a card to a different status column and/or position within that column.
@@ -28159,7 +28220,7 @@ async function main() {
28159
28220
  );
28160
28221
  server.tool(
28161
28222
  "trigger_action",
28162
- "Trigger a named action on a card. The action name must match one of the card's configured actions. Calls the configured action webhook URL with the action name and card details.",
28223
+ "Trigger a named action on a card. The action name must match one of the card's configured actions. Emits a card.action.triggered event delivered to registered webhooks.",
28163
28224
  {
28164
28225
  card_id: import_zod.z.string().describe("Card ID (partial match supported)"),
28165
28226
  action: import_zod.z.string().describe("Action name to trigger"),
@@ -17694,11 +17694,83 @@ function migrateConfigV1ToV2(raw) {
17694
17694
  }
17695
17695
  return v2;
17696
17696
  }
17697
+ function loadDotEnv(dir) {
17698
+ const envPath = path.join(dir, ".env");
17699
+ let content;
17700
+ try {
17701
+ content = fs.readFileSync(envPath, "utf-8");
17702
+ } catch {
17703
+ return;
17704
+ }
17705
+ for (const line of content.split("\n")) {
17706
+ const trimmed = line.trim();
17707
+ if (!trimmed || trimmed.startsWith("#"))
17708
+ continue;
17709
+ const eqIdx = trimmed.indexOf("=");
17710
+ if (eqIdx < 1)
17711
+ continue;
17712
+ const key = trimmed.slice(0, eqIdx).trim();
17713
+ let val = trimmed.slice(eqIdx + 1).trim();
17714
+ if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
17715
+ val = val.slice(1, -1);
17716
+ }
17717
+ if (process.env[key] === void 0) {
17718
+ process.env[key] = val;
17719
+ }
17720
+ }
17721
+ }
17722
+ function resolveConfigEnvVars(node, configFileName, nodePath = "") {
17723
+ if (typeof node === "string") {
17724
+ return node.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
17725
+ const envValue = process.env[varName];
17726
+ if (envValue === void 0) {
17727
+ throw new Error(
17728
+ `missing ${varName} in ${configFileName}: ${nodePath} "${node}"`
17729
+ );
17730
+ }
17731
+ return envValue;
17732
+ });
17733
+ }
17734
+ if (Array.isArray(node)) {
17735
+ for (let i = 0; i < node.length; i++) {
17736
+ node[i] = resolveConfigEnvVars(node[i], configFileName, `${nodePath}[${i}]`);
17737
+ }
17738
+ return node;
17739
+ }
17740
+ if (node !== null && typeof node === "object") {
17741
+ const obj = node;
17742
+ for (const key of Object.keys(obj)) {
17743
+ const jsonKey = /[^a-zA-Z0-9_]/.test(key) ? `"${key}"` : key;
17744
+ const childPath = nodePath ? `${nodePath}.${jsonKey}` : `.${jsonKey}`;
17745
+ obj[key] = resolveConfigEnvVars(obj[key], configFileName, childPath);
17746
+ }
17747
+ return obj;
17748
+ }
17749
+ return node;
17750
+ }
17697
17751
  function readConfig(workspaceRoot) {
17698
17752
  const filePath = configPath(workspaceRoot);
17699
17753
  const defaults = { ...DEFAULT_CONFIG, boards: { default: { ...DEFAULT_BOARD_CONFIG, columns: [...DEFAULT_COLUMNS] } } };
17754
+ let raw;
17755
+ try {
17756
+ raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
17757
+ } catch {
17758
+ return defaults;
17759
+ }
17760
+ loadDotEnv(workspaceRoot);
17761
+ try {
17762
+ resolveConfigEnvVars(raw, CONFIG_FILENAME);
17763
+ } catch (err) {
17764
+ const msg = err instanceof Error ? err.message : String(err);
17765
+ process.stderr.write(`
17766
+ Configuration error: ${msg}
17767
+
17768
+ Set the missing environment variable before starting the server.
17769
+
17770
+ `);
17771
+ process.exit(1);
17772
+ }
17700
17773
  try {
17701
- const raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
17702
17774
  const isV1 = raw.version === 1 || !raw.version && !(typeof raw.boards === "object" && raw.boards !== null && !Array.isArray(raw.boards));
17703
17775
  if (isV1) {
17704
17776
  const v2 = migrateConfigV1ToV2(raw);
@@ -21302,7 +21374,7 @@ function resolveAuthIdentityPlugin(ref) {
21302
21374
  if (ref.provider === "rbac")
21303
21375
  return RBAC_IDENTITY_PLUGIN;
21304
21376
  const packageName = AUTH_PROVIDER_ALIASES.get(ref.provider) ?? ref.provider;
21305
- return loadExternalAuthIdentityPlugin(packageName, ref.provider);
21377
+ return loadExternalAuthIdentityPlugin(packageName, ref.provider, ref.options);
21306
21378
  }
21307
21379
  function resolveAuthPolicyPlugin(ref) {
21308
21380
  if (ref.provider === "noop")
@@ -21528,8 +21600,13 @@ function selectAuthPolicyPlugin(mod, providerId) {
21528
21600
  return direct;
21529
21601
  return null;
21530
21602
  }
21531
- function loadExternalAuthIdentityPlugin(packageName, providerId) {
21603
+ function loadExternalAuthIdentityPlugin(packageName, providerId, options) {
21532
21604
  const mod = loadExternalModule(packageName);
21605
+ if (options !== void 0 && typeof mod.createAuthIdentityPlugin === "function") {
21606
+ const created = mod.createAuthIdentityPlugin(options);
21607
+ if (isValidAuthIdentityPlugin(created, providerId))
21608
+ return created;
21609
+ }
21533
21610
  const plugin = selectAuthIdentityPlugin(mod, providerId);
21534
21611
  if (!plugin) {
21535
21612
  throw new Error(
@@ -24532,29 +24609,10 @@ async function updateCard(ctx, { cardId, updates, boardId }) {
24532
24609
  return card;
24533
24610
  }
24534
24611
  async function triggerAction(ctx, { cardId, action, boardId }) {
24535
- const config = readConfig(ctx.workspaceRoot);
24536
- const { actionWebhookUrl } = config;
24537
- if (!actionWebhookUrl) {
24538
- throw new Error("No action webhook URL configured. Set actionWebhookUrl in .kanban.json");
24539
- }
24540
24612
  const card = await getCard(ctx, { cardId, boardId });
24541
24613
  if (!card)
24542
24614
  throw new Error(`Card not found: ${cardId}`);
24543
24615
  const resolvedBoardId = card.boardId || ctx._resolveBoardId(boardId);
24544
- const payload = {
24545
- action,
24546
- board: resolvedBoardId,
24547
- list: card.status,
24548
- card: sanitizeCard(card)
24549
- };
24550
- const response = await fetch(actionWebhookUrl, {
24551
- method: "POST",
24552
- headers: { "Content-Type": "application/json" },
24553
- body: JSON.stringify(payload)
24554
- });
24555
- if (!response.ok) {
24556
- throw new Error(`Action webhook responded with ${response.status}: ${response.statusText}`);
24557
- }
24558
24616
  await appendActivityLog(ctx, {
24559
24617
  cardId,
24560
24618
  boardId: resolvedBoardId,
@@ -24565,6 +24623,12 @@ async function triggerAction(ctx, { cardId, action, boardId }) {
24565
24623
  }
24566
24624
  }).catch(() => {
24567
24625
  });
24626
+ return {
24627
+ action,
24628
+ board: resolvedBoardId,
24629
+ list: card.status,
24630
+ card: sanitizeCard(card)
24631
+ };
24568
24632
  }
24569
24633
  async function submitForm(ctx, input) {
24570
24634
  const card = await getCard(ctx, { cardId: input.cardId, boardId: input.boardId });
@@ -26447,21 +26511,17 @@ var KanbanSDK = class _KanbanSDK {
26447
26511
  return result;
26448
26512
  }
26449
26513
  /**
26450
- * Triggers a named action for a card by POSTing to the global `actionWebhookUrl`
26451
- * configured in `.kanban.json`.
26514
+ * Triggers a named action for a card.
26452
26515
  *
26453
- * The payload sent to the webhook is:
26454
- * ```json
26455
- * { "action": "retry", "board": "default", "list": "in-progress", "card": { ...sanitizedCard } }
26456
- * ```
26516
+ * Validates the card, appends an activity log entry, and emits the
26517
+ * `card.action.triggered` after-event so registered webhooks receive
26518
+ * the action payload automatically.
26457
26519
  *
26458
26520
  * @param cardId - The ID of the card to trigger the action for.
26459
26521
  * @param action - The action name string (e.g. `'retry'`, `'sendEmail'`).
26460
26522
  * @param boardId - Optional board ID. Defaults to the workspace's default board.
26461
- * @returns A promise resolving when the webhook responds with 2xx.
26462
- * @throws {Error} If no `actionWebhookUrl` is configured in `.kanban.json`.
26523
+ * @returns A promise that resolves when the action has been processed.
26463
26524
  * @throws {Error} If the card is not found.
26464
- * @throws {Error} If the webhook responds with a non-2xx status.
26465
26525
  *
26466
26526
  * @example
26467
26527
  * ```ts
@@ -26471,7 +26531,8 @@ var KanbanSDK = class _KanbanSDK {
26471
26531
  */
26472
26532
  async triggerAction(cardId, action, boardId) {
26473
26533
  const mergedInput = await this._runBeforeEvent("card.action.trigger", { cardId, action, boardId }, void 0, boardId);
26474
- return triggerAction(this, mergedInput);
26534
+ const payload = await triggerAction(this, mergedInput);
26535
+ this._runAfterEvent("card.action.triggered", payload, void 0, payload.board);
26475
26536
  }
26476
26537
  /**
26477
26538
  * Moves a card to a different status column and/or position within that column.
@@ -27519,6 +27580,7 @@ var AFTER_ENTRIES = [
27519
27580
  { event: "board.updated", resource: "board", label: "Board updated" },
27520
27581
  { event: "board.deleted", resource: "board", label: "Board deleted" },
27521
27582
  { event: "board.action", resource: "board", label: "Board action triggered" },
27583
+ { event: "card.action.triggered", resource: "card", label: "Card action triggered" },
27522
27584
  { event: "board.log.added", resource: "board", label: "Board log added" },
27523
27585
  { event: "board.log.cleared", resource: "board", label: "Board log cleared" },
27524
27586
  // card logs
@@ -17653,11 +17653,83 @@ function migrateConfigV1ToV2(raw) {
17653
17653
  }
17654
17654
  return v2;
17655
17655
  }
17656
+ function loadDotEnv(dir) {
17657
+ const envPath = path.join(dir, ".env");
17658
+ let content;
17659
+ try {
17660
+ content = fs.readFileSync(envPath, "utf-8");
17661
+ } catch {
17662
+ return;
17663
+ }
17664
+ for (const line of content.split("\n")) {
17665
+ const trimmed = line.trim();
17666
+ if (!trimmed || trimmed.startsWith("#"))
17667
+ continue;
17668
+ const eqIdx = trimmed.indexOf("=");
17669
+ if (eqIdx < 1)
17670
+ continue;
17671
+ const key = trimmed.slice(0, eqIdx).trim();
17672
+ let val = trimmed.slice(eqIdx + 1).trim();
17673
+ if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
17674
+ val = val.slice(1, -1);
17675
+ }
17676
+ if (process.env[key] === void 0) {
17677
+ process.env[key] = val;
17678
+ }
17679
+ }
17680
+ }
17681
+ function resolveConfigEnvVars(node, configFileName, nodePath = "") {
17682
+ if (typeof node === "string") {
17683
+ return node.replace(/\$\{([^}]+)\}/g, (_match, varName) => {
17684
+ const envValue = process.env[varName];
17685
+ if (envValue === void 0) {
17686
+ throw new Error(
17687
+ `missing ${varName} in ${configFileName}: ${nodePath} "${node}"`
17688
+ );
17689
+ }
17690
+ return envValue;
17691
+ });
17692
+ }
17693
+ if (Array.isArray(node)) {
17694
+ for (let i = 0; i < node.length; i++) {
17695
+ node[i] = resolveConfigEnvVars(node[i], configFileName, `${nodePath}[${i}]`);
17696
+ }
17697
+ return node;
17698
+ }
17699
+ if (node !== null && typeof node === "object") {
17700
+ const obj = node;
17701
+ for (const key of Object.keys(obj)) {
17702
+ const jsonKey = /[^a-zA-Z0-9_]/.test(key) ? `"${key}"` : key;
17703
+ const childPath = nodePath ? `${nodePath}.${jsonKey}` : `.${jsonKey}`;
17704
+ obj[key] = resolveConfigEnvVars(obj[key], configFileName, childPath);
17705
+ }
17706
+ return obj;
17707
+ }
17708
+ return node;
17709
+ }
17656
17710
  function readConfig(workspaceRoot) {
17657
17711
  const filePath = configPath(workspaceRoot);
17658
17712
  const defaults = { ...DEFAULT_CONFIG, boards: { default: { ...DEFAULT_BOARD_CONFIG, columns: [...DEFAULT_COLUMNS] } } };
17713
+ let raw;
17714
+ try {
17715
+ raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
17716
+ } catch {
17717
+ return defaults;
17718
+ }
17719
+ loadDotEnv(workspaceRoot);
17720
+ try {
17721
+ resolveConfigEnvVars(raw, CONFIG_FILENAME);
17722
+ } catch (err) {
17723
+ const msg = err instanceof Error ? err.message : String(err);
17724
+ process.stderr.write(`
17725
+ Configuration error: ${msg}
17726
+
17727
+ Set the missing environment variable before starting the server.
17728
+
17729
+ `);
17730
+ process.exit(1);
17731
+ }
17659
17732
  try {
17660
- const raw = JSON.parse(fs.readFileSync(filePath, "utf-8"));
17661
17733
  const isV1 = raw.version === 1 || !raw.version && !(typeof raw.boards === "object" && raw.boards !== null && !Array.isArray(raw.boards));
17662
17734
  if (isV1) {
17663
17735
  const v2 = migrateConfigV1ToV2(raw);
@@ -21261,7 +21333,7 @@ function resolveAuthIdentityPlugin(ref) {
21261
21333
  if (ref.provider === "rbac")
21262
21334
  return RBAC_IDENTITY_PLUGIN;
21263
21335
  const packageName = AUTH_PROVIDER_ALIASES.get(ref.provider) ?? ref.provider;
21264
- return loadExternalAuthIdentityPlugin(packageName, ref.provider);
21336
+ return loadExternalAuthIdentityPlugin(packageName, ref.provider, ref.options);
21265
21337
  }
21266
21338
  function resolveAuthPolicyPlugin(ref) {
21267
21339
  if (ref.provider === "noop")
@@ -21487,8 +21559,13 @@ function selectAuthPolicyPlugin(mod, providerId) {
21487
21559
  return direct;
21488
21560
  return null;
21489
21561
  }
21490
- function loadExternalAuthIdentityPlugin(packageName, providerId) {
21562
+ function loadExternalAuthIdentityPlugin(packageName, providerId, options) {
21491
21563
  const mod = loadExternalModule(packageName);
21564
+ if (options !== void 0 && typeof mod.createAuthIdentityPlugin === "function") {
21565
+ const created = mod.createAuthIdentityPlugin(options);
21566
+ if (isValidAuthIdentityPlugin(created, providerId))
21567
+ return created;
21568
+ }
21492
21569
  const plugin = selectAuthIdentityPlugin(mod, providerId);
21493
21570
  if (!plugin) {
21494
21571
  throw new Error(
@@ -24491,29 +24568,10 @@ async function updateCard(ctx, { cardId, updates, boardId }) {
24491
24568
  return card;
24492
24569
  }
24493
24570
  async function triggerAction(ctx, { cardId, action, boardId }) {
24494
- const config = readConfig(ctx.workspaceRoot);
24495
- const { actionWebhookUrl } = config;
24496
- if (!actionWebhookUrl) {
24497
- throw new Error("No action webhook URL configured. Set actionWebhookUrl in .kanban.json");
24498
- }
24499
24571
  const card = await getCard(ctx, { cardId, boardId });
24500
24572
  if (!card)
24501
24573
  throw new Error(`Card not found: ${cardId}`);
24502
24574
  const resolvedBoardId = card.boardId || ctx._resolveBoardId(boardId);
24503
- const payload = {
24504
- action,
24505
- board: resolvedBoardId,
24506
- list: card.status,
24507
- card: sanitizeCard(card)
24508
- };
24509
- const response = await fetch(actionWebhookUrl, {
24510
- method: "POST",
24511
- headers: { "Content-Type": "application/json" },
24512
- body: JSON.stringify(payload)
24513
- });
24514
- if (!response.ok) {
24515
- throw new Error(`Action webhook responded with ${response.status}: ${response.statusText}`);
24516
- }
24517
24575
  await appendActivityLog(ctx, {
24518
24576
  cardId,
24519
24577
  boardId: resolvedBoardId,
@@ -24524,6 +24582,12 @@ async function triggerAction(ctx, { cardId, action, boardId }) {
24524
24582
  }
24525
24583
  }).catch(() => {
24526
24584
  });
24585
+ return {
24586
+ action,
24587
+ board: resolvedBoardId,
24588
+ list: card.status,
24589
+ card: sanitizeCard(card)
24590
+ };
24527
24591
  }
24528
24592
  async function submitForm(ctx, input) {
24529
24593
  const card = await getCard(ctx, { cardId: input.cardId, boardId: input.boardId });
@@ -26406,21 +26470,17 @@ var KanbanSDK = class _KanbanSDK {
26406
26470
  return result;
26407
26471
  }
26408
26472
  /**
26409
- * Triggers a named action for a card by POSTing to the global `actionWebhookUrl`
26410
- * configured in `.kanban.json`.
26473
+ * Triggers a named action for a card.
26411
26474
  *
26412
- * The payload sent to the webhook is:
26413
- * ```json
26414
- * { "action": "retry", "board": "default", "list": "in-progress", "card": { ...sanitizedCard } }
26415
- * ```
26475
+ * Validates the card, appends an activity log entry, and emits the
26476
+ * `card.action.triggered` after-event so registered webhooks receive
26477
+ * the action payload automatically.
26416
26478
  *
26417
26479
  * @param cardId - The ID of the card to trigger the action for.
26418
26480
  * @param action - The action name string (e.g. `'retry'`, `'sendEmail'`).
26419
26481
  * @param boardId - Optional board ID. Defaults to the workspace's default board.
26420
- * @returns A promise resolving when the webhook responds with 2xx.
26421
- * @throws {Error} If no `actionWebhookUrl` is configured in `.kanban.json`.
26482
+ * @returns A promise that resolves when the action has been processed.
26422
26483
  * @throws {Error} If the card is not found.
26423
- * @throws {Error} If the webhook responds with a non-2xx status.
26424
26484
  *
26425
26485
  * @example
26426
26486
  * ```ts
@@ -26430,7 +26490,8 @@ var KanbanSDK = class _KanbanSDK {
26430
26490
  */
26431
26491
  async triggerAction(cardId, action, boardId) {
26432
26492
  const mergedInput = await this._runBeforeEvent("card.action.trigger", { cardId, action, boardId }, void 0, boardId);
26433
- return triggerAction(this, mergedInput);
26493
+ const payload = await triggerAction(this, mergedInput);
26494
+ this._runAfterEvent("card.action.triggered", payload, void 0, payload.board);
26434
26495
  }
26435
26496
  /**
26436
26497
  * Moves a card to a different status column and/or position within that column.
@@ -27478,6 +27539,7 @@ var AFTER_ENTRIES = [
27478
27539
  { event: "board.updated", resource: "board", label: "Board updated" },
27479
27540
  { event: "board.deleted", resource: "board", label: "Board deleted" },
27480
27541
  { event: "board.action", resource: "board", label: "Board action triggered" },
27542
+ { event: "card.action.triggered", resource: "card", label: "Card action triggered" },
27481
27543
  { event: "board.log.added", resource: "board", label: "Board log added" },
27482
27544
  { event: "board.log.cleared", resource: "board", label: "Board log cleared" },
27483
27545
  // card logs
@@ -969,21 +969,17 @@ export declare class KanbanSDK {
969
969
  */
970
970
  submitForm(input: SubmitFormInput): Promise<SubmitFormResult>;
971
971
  /**
972
- * Triggers a named action for a card by POSTing to the global `actionWebhookUrl`
973
- * configured in `.kanban.json`.
972
+ * Triggers a named action for a card.
974
973
  *
975
- * The payload sent to the webhook is:
976
- * ```json
977
- * { "action": "retry", "board": "default", "list": "in-progress", "card": { ...sanitizedCard } }
978
- * ```
974
+ * Validates the card, appends an activity log entry, and emits the
975
+ * `card.action.triggered` after-event so registered webhooks receive
976
+ * the action payload automatically.
979
977
  *
980
978
  * @param cardId - The ID of the card to trigger the action for.
981
979
  * @param action - The action name string (e.g. `'retry'`, `'sendEmail'`).
982
980
  * @param boardId - Optional board ID. Defaults to the workspace's default board.
983
- * @returns A promise resolving when the webhook responds with 2xx.
984
- * @throws {Error} If no `actionWebhookUrl` is configured in `.kanban.json`.
981
+ * @returns A promise that resolves when the action has been processed.
985
982
  * @throws {Error} If the card is not found.
986
- * @throws {Error} If the webhook responds with a non-2xx status.
987
983
  *
988
984
  * @example
989
985
  * ```ts
@@ -52,13 +52,22 @@ export declare function updateCard(ctx: SDKContext, { cardId, updates, boardId }
52
52
  boardId?: string;
53
53
  }): Promise<Card>;
54
54
  /**
55
- * Triggers a named action for a card by POSTing to the global `actionWebhookUrl`.
55
+ * Triggers a named action for a card.
56
+ *
57
+ * Validates the card exists, appends an activity log entry, and returns the
58
+ * action payload. Webhook delivery is handled by the webhook plugin via the
59
+ * `card.action.triggered` after-event emitted by {@link KanbanSDK.triggerAction}.
56
60
  */
57
61
  export declare function triggerAction(ctx: SDKContext, { cardId, action, boardId }: {
58
62
  cardId: string;
59
63
  action: string;
60
64
  boardId?: string;
61
- }): Promise<void>;
65
+ }): Promise<{
66
+ action: string;
67
+ board: string;
68
+ list: string;
69
+ card: Omit<Card, 'filePath'>;
70
+ }>;
62
71
  /**
63
72
  * Validates and persists a card form submission, then emits `form.submit`.
64
73
  */
@@ -58,7 +58,7 @@ export type SDKBeforeEventType = 'card.create' | 'card.update' | 'card.move' | '
58
58
  *
59
59
  * @see AfterEventPayload for the payload envelope passed to after-event listeners.
60
60
  */
61
- export type SDKAfterEventType = 'task.created' | 'task.updated' | 'task.moved' | 'task.deleted' | 'comment.created' | 'comment.updated' | 'comment.deleted' | 'column.created' | 'column.updated' | 'column.deleted' | 'attachment.added' | 'attachment.removed' | 'settings.updated' | 'board.created' | 'board.updated' | 'board.deleted' | 'board.action' | 'board.log.added' | 'board.log.cleared' | 'log.added' | 'log.cleared' | 'storage.migrated' | 'form.submitted' | 'auth.allowed' | 'auth.denied';
61
+ export type SDKAfterEventType = 'task.created' | 'task.updated' | 'task.moved' | 'task.deleted' | 'comment.created' | 'comment.updated' | 'comment.deleted' | 'column.created' | 'column.updated' | 'column.deleted' | 'attachment.added' | 'attachment.removed' | 'settings.updated' | 'board.created' | 'board.updated' | 'board.deleted' | 'board.action' | 'card.action.triggered' | 'board.log.added' | 'board.log.cleared' | 'log.added' | 'log.cleared' | 'storage.migrated' | 'form.submitted' | 'auth.allowed' | 'auth.denied';
62
62
  /**
63
63
  * Union of all SDK event types (before-events and after-events).
64
64
  *