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/cli.js +168 -85
- package/dist/extension.js +157 -74
- package/dist/mcp-server.js +94 -33
- package/dist/sdk/index.cjs +94 -32
- package/dist/sdk/index.mjs +94 -32
- package/dist/sdk/sdk/KanbanSDK.d.ts +5 -9
- package/dist/sdk/sdk/modules/cards.d.ts +11 -2
- package/dist/sdk/sdk/types.d.ts +1 -1
- package/dist/sdk/shared/config.d.ts +17 -1
- package/dist/standalone-webview/index.js +15 -15
- package/dist/standalone-webview/index.js.map +1 -1
- package/dist/standalone.js +219 -121
- package/package.json +1 -1
- package/src/mcp-server/index.ts +1 -1
- package/src/sdk/KanbanSDK.d.ts +5 -9
- package/src/sdk/KanbanSDK.ts +7 -10
- package/src/sdk/integrationCatalog.ts +1 -0
- package/src/sdk/modules/cards.ts +13 -24
- package/src/sdk/plugins/index.ts +10 -2
- package/src/sdk/types.d.ts +1 -1
- package/src/sdk/types.ts +1 -0
- package/src/sdk/webhooks.ts +19 -2
- package/src/shared/config.ts +124 -2
- package/src/standalone/internal/runtime.ts +11 -6
- package/src/standalone/server.ts +21 -7
- package/src/standalone/watcherSetup.ts +9 -0
- package/src/webview/standalone-shim.ts +2 -1
package/dist/mcp-server.js
CHANGED
|
@@ -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
|
|
26484
|
-
* configured in `.kanban.json`.
|
|
26547
|
+
* Triggers a named action for a card.
|
|
26485
26548
|
*
|
|
26486
|
-
*
|
|
26487
|
-
*
|
|
26488
|
-
*
|
|
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
|
|
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
|
-
|
|
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.
|
|
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"),
|
package/dist/sdk/index.cjs
CHANGED
|
@@ -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
|
|
26451
|
-
* configured in `.kanban.json`.
|
|
26514
|
+
* Triggers a named action for a card.
|
|
26452
26515
|
*
|
|
26453
|
-
*
|
|
26454
|
-
*
|
|
26455
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
package/dist/sdk/index.mjs
CHANGED
|
@@ -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
|
|
26410
|
-
* configured in `.kanban.json`.
|
|
26473
|
+
* Triggers a named action for a card.
|
|
26411
26474
|
*
|
|
26412
|
-
*
|
|
26413
|
-
*
|
|
26414
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
|
973
|
-
* configured in `.kanban.json`.
|
|
972
|
+
* Triggers a named action for a card.
|
|
974
973
|
*
|
|
975
|
-
*
|
|
976
|
-
*
|
|
977
|
-
*
|
|
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
|
|
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
|
|
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<
|
|
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
|
*/
|
package/dist/sdk/sdk/types.d.ts
CHANGED
|
@@ -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
|
*
|