@defend-tech/opencode-optima 0.1.65 → 0.1.67
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/index.js +95 -33
- package/dist/sanitize_cli.js +95 -33
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9978,17 +9978,11 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
|
|
|
9978
9978
|
body: JSON.stringify({ value })
|
|
9979
9979
|
});
|
|
9980
9980
|
},
|
|
9981
|
-
async postTaskComment(
|
|
9982
|
-
|
|
9983
|
-
method: "POST",
|
|
9984
|
-
body: JSON.stringify({ comment_text: comment })
|
|
9985
|
-
});
|
|
9981
|
+
async postTaskComment() {
|
|
9982
|
+
throw new Error("Optima runtime does not post ClickUp task comments; use local logs for process failures.");
|
|
9986
9983
|
},
|
|
9987
|
-
async addTaskTag(
|
|
9988
|
-
|
|
9989
|
-
method: "POST",
|
|
9990
|
-
body: JSON.stringify({})
|
|
9991
|
-
});
|
|
9984
|
+
async addTaskTag() {
|
|
9985
|
+
throw new Error("Optima runtime does not add ClickUp task tags; use local logs for process failures.");
|
|
9992
9986
|
},
|
|
9993
9987
|
async listAssignedTasks({ assigneeId, statuses = [], limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
|
|
9994
9988
|
const params = new URLSearchParams();
|
|
@@ -10548,15 +10542,54 @@ async function createOpenCodeSession(client, { title, directory, agent } = {}) {
|
|
|
10548
10542
|
throw error || new Error("OpenCode session create failed.");
|
|
10549
10543
|
}
|
|
10550
10544
|
}
|
|
10551
|
-
|
|
10552
|
-
if (
|
|
10545
|
+
function normalizeOpenCodeSessionCollection(value) {
|
|
10546
|
+
if (Array.isArray(value)) return value;
|
|
10547
|
+
if (!isPlainObject(value)) return [];
|
|
10548
|
+
for (const key of ["sessions", "items", "data", "result", "results"]) {
|
|
10549
|
+
const nested = value[key];
|
|
10550
|
+
if (Array.isArray(nested)) return nested;
|
|
10551
|
+
if (isPlainObject(nested)) {
|
|
10552
|
+
const normalized = normalizeOpenCodeSessionCollection(nested);
|
|
10553
|
+
if (normalized.length > 0) return normalized;
|
|
10554
|
+
}
|
|
10555
|
+
}
|
|
10556
|
+
return [];
|
|
10557
|
+
}
|
|
10558
|
+
async function listOpenCodeSessionsReadiness(client, { directory = "" } = {}) {
|
|
10559
|
+
if (typeof client?.session?.list !== "function") return null;
|
|
10560
|
+
const attempts = [
|
|
10561
|
+
{ query: directory ? { directory } : {} },
|
|
10562
|
+
directory ? { directory } : {}
|
|
10563
|
+
];
|
|
10564
|
+
for (const attempt of attempts) {
|
|
10565
|
+
try {
|
|
10566
|
+
const result = await client.session.list(attempt);
|
|
10567
|
+
return { ok: true, method: "session_list", sessions: normalizeOpenCodeSessionCollection(result?.data ?? result).length };
|
|
10568
|
+
} catch {
|
|
10569
|
+
}
|
|
10570
|
+
}
|
|
10571
|
+
return null;
|
|
10572
|
+
}
|
|
10573
|
+
async function listOpenCodeSessionsReadinessHttp({ baseUrl, directory = "", fetchImpl = globalThis.fetch } = {}) {
|
|
10574
|
+
const root = normalizeOpenCodeBaseUrl(baseUrl, "");
|
|
10575
|
+
if (!root || typeof fetchImpl !== "function") return null;
|
|
10576
|
+
const url = openChamberUrl(root, "/session", directory ? { directory } : {});
|
|
10577
|
+
const response = await fetchImpl(url, { method: "GET" });
|
|
10578
|
+
const contentType = response.headers?.get?.("content-type") || "";
|
|
10579
|
+
const raw = await response.text();
|
|
10580
|
+
if (!response.ok) throw new Error(`OpenCode session list readiness failed with status ${response.status}.`);
|
|
10581
|
+
if (responseLooksLikeHtml(contentType, raw)) throw new Error("OpenCode session list readiness returned an HTML app shell.");
|
|
10582
|
+
const data = raw.trim() ? JSON.parse(raw) : [];
|
|
10583
|
+
return { ok: true, method: "session_list_http", sessions: normalizeOpenCodeSessionCollection(data).length };
|
|
10584
|
+
}
|
|
10585
|
+
async function waitForOpenCodeReadiness(client, { worktree = process.cwd(), attempts = 10, delayMs = 500, opencodeBaseUrl = "", fetchImpl = globalThis.fetch } = {}) {
|
|
10553
10586
|
let lastError = "opencode_not_ready";
|
|
10554
10587
|
const maxAttempts = Math.max(1, Number(attempts) || 1);
|
|
10555
10588
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
10556
10589
|
try {
|
|
10557
|
-
const
|
|
10558
|
-
if (
|
|
10559
|
-
|
|
10590
|
+
const listed = await listOpenCodeSessionsReadiness(client, { directory: worktree }) || await listOpenCodeSessionsReadinessHttp({ baseUrl: opencodeBaseUrl, directory: worktree, fetchImpl });
|
|
10591
|
+
if (listed?.ok) return { ...listed, attempts: attempt };
|
|
10592
|
+
return { ok: true, skipped: true, reason: "session_list_probe_unavailable", attempts: attempt };
|
|
10560
10593
|
} catch (error) {
|
|
10561
10594
|
lastError = error.message || "opencode_not_ready";
|
|
10562
10595
|
}
|
|
@@ -10929,18 +10962,9 @@ async function postClickUpLaunchFailureComment({ clickupClient, worktree, taskId
|
|
|
10929
10962
|
return { ok: false, skipped: true, reason: "local_log_only" };
|
|
10930
10963
|
}
|
|
10931
10964
|
async function applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason, source, tagName = CLICKUP_BLOCKER_TAG_NAME } = {}) {
|
|
10932
|
-
|
|
10933
|
-
|
|
10934
|
-
|
|
10935
|
-
}
|
|
10936
|
-
try {
|
|
10937
|
-
await clickupClient.addTaskTag({ taskId, tagName });
|
|
10938
|
-
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_applied", taskId, reason, source, tagName });
|
|
10939
|
-
return { ok: true, tagName };
|
|
10940
|
-
} catch (error) {
|
|
10941
|
-
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_failed", taskId, reason, source, tagName, message: error.message });
|
|
10942
|
-
return { ok: false, error: error.message, tagName };
|
|
10943
|
-
}
|
|
10965
|
+
void clickupClient;
|
|
10966
|
+
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_skipped", taskId, reason, source, tagName, policy: "metadata_only_task_writes" });
|
|
10967
|
+
return { ok: false, skipped: true, reason: "metadata_only_task_writes", tagName };
|
|
10944
10968
|
}
|
|
10945
10969
|
function openCodeBlockingPromptVerification(result, sessionId) {
|
|
10946
10970
|
if (result?.__optimaPromptDelivery !== "prompt") return null;
|
|
@@ -11430,7 +11454,7 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
|
|
|
11430
11454
|
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...skipped });
|
|
11431
11455
|
return skipped;
|
|
11432
11456
|
}
|
|
11433
|
-
const readiness = await waitForReadiness(openCodeClient, { worktree, now });
|
|
11457
|
+
const readiness = await waitForReadiness(openCodeClient, { worktree, now, opencodeBaseUrl: config.opencode?.baseUrl });
|
|
11434
11458
|
appendClickUpWebhookLocalLog(worktree, { type: readiness.ok ? "startup_reconciliation_readiness_ready" : "startup_reconciliation_readiness_failed", ...readiness });
|
|
11435
11459
|
if (!readiness.ok) {
|
|
11436
11460
|
const failed = { ok: false, skipped: true, reason: "opencode_not_ready", readiness, assigned: 0, comments: 0, ignored: 0, errors: 1, undelivered: 1, tasks: [], validation: { undelivered: 1, emptyPromptSessions: [] } };
|
|
@@ -11569,17 +11593,49 @@ function clickUpWebhookExpectedPath(config) {
|
|
|
11569
11593
|
return "/";
|
|
11570
11594
|
}
|
|
11571
11595
|
}
|
|
11572
|
-
|
|
11596
|
+
function summarizeClickUpRouteForLog(result = {}, payload = {}) {
|
|
11597
|
+
return {
|
|
11598
|
+
taskId: result.taskId || clickUpTaskIdFromPayload(payload || {}) || null,
|
|
11599
|
+
event: clickUpEventType(payload || {}) || null,
|
|
11600
|
+
eventKey: result.eventKey || clickUpWebhookEventKey(payload || {}) || null,
|
|
11601
|
+
ok: result.ok === true,
|
|
11602
|
+
action: result.action || null,
|
|
11603
|
+
reason: result.reason || null,
|
|
11604
|
+
sessionId: result.sessionId || result.replacementSessionId || null,
|
|
11605
|
+
branch: result.branch || null,
|
|
11606
|
+
worktree: result.worktree || null
|
|
11607
|
+
};
|
|
11608
|
+
}
|
|
11609
|
+
function runClickUpWebhookRouteInBackground({ payload, config, state, worktree, clickupClient, openCodeClient, saveState, sessionExists, createSession, sendSessionEvent, ensureTaskWorktree, verifySessionEventDelivery, now } = {}) {
|
|
11610
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11611
|
+
appendClickUpWebhookLocalLog(worktree, { type: "webhook_route_started", taskId: clickUpTaskIdFromPayload(payload || {}) || null, event: clickUpEventType(payload || {}) || null, eventKey: clickUpWebhookEventKey(payload || {}) || null, async: true, startedAt });
|
|
11612
|
+
const route = Promise.resolve().then(() => routeClickUpWebhookEvent({ payload, config, state, worktree, clickupClient, openCodeClient, saveState, sessionExists, createSession, sendSessionEvent, ensureTaskWorktree, verifySessionEventDelivery, now })).then((result) => {
|
|
11613
|
+
appendClickUpWebhookLocalLog(worktree, { type: "webhook_route_finished", async: true, ...summarizeClickUpRouteForLog(result, payload) });
|
|
11614
|
+
return result;
|
|
11615
|
+
}).catch((error) => {
|
|
11616
|
+
appendClickUpWebhookLocalLog(worktree, { type: "webhook_route_failed", taskId: clickUpTaskIdFromPayload(payload || {}) || null, event: clickUpEventType(payload || {}) || null, eventKey: clickUpWebhookEventKey(payload || {}) || null, async: true, message: error.message });
|
|
11617
|
+
return { ok: false, action: "error", reason: "background_route_failed", message: error.message };
|
|
11618
|
+
});
|
|
11619
|
+
route.catch(() => {
|
|
11620
|
+
});
|
|
11621
|
+
return route;
|
|
11622
|
+
}
|
|
11623
|
+
async function handleClickUpWebhookRequest({ method = "POST", url = null, headers = {}, rawBody = "", config, state, worktree, clickupClient, openCodeClient, saveState, now = () => /* @__PURE__ */ new Date(), sessionExists, createSession, sendSessionEvent, ensureTaskWorktree, verifySessionEventDelivery, asyncRouting = false } = {}) {
|
|
11573
11624
|
let payload = null;
|
|
11574
11625
|
let handled = null;
|
|
11575
11626
|
let authenticatedWebhook = false;
|
|
11576
11627
|
let receivedAt = null;
|
|
11577
11628
|
let activeState = state;
|
|
11629
|
+
let latestPersistedState = state;
|
|
11630
|
+
const persistState = (nextState) => {
|
|
11631
|
+
latestPersistedState = nextState;
|
|
11632
|
+
if (saveState) saveState(nextState);
|
|
11633
|
+
};
|
|
11578
11634
|
const finish = (result) => {
|
|
11579
11635
|
handled = result;
|
|
11580
11636
|
receivedAt ??= now();
|
|
11581
|
-
const auditState = authenticatedWebhook ? { ...
|
|
11582
|
-
if (saveState && authenticatedWebhook)
|
|
11637
|
+
const auditState = authenticatedWebhook ? { ...latestPersistedState, lastWebhookAt: receivedAt.toISOString() } : latestPersistedState;
|
|
11638
|
+
if (saveState && authenticatedWebhook) persistState(auditState);
|
|
11583
11639
|
writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state: auditState, handled, payload, at: receivedAt });
|
|
11584
11640
|
return result;
|
|
11585
11641
|
};
|
|
@@ -11606,7 +11662,12 @@ async function handleClickUpWebhookRequest({ method = "POST", url = null, header
|
|
|
11606
11662
|
return finish({ ok: false, status: 400, reason: "invalid_json" });
|
|
11607
11663
|
}
|
|
11608
11664
|
const receivedState = { ...activeState, lastWebhookAt: receivedAt.toISOString() };
|
|
11609
|
-
|
|
11665
|
+
persistState(receivedState);
|
|
11666
|
+
if (asyncRouting) {
|
|
11667
|
+
runClickUpWebhookRouteInBackground({ payload, config, state: receivedState, worktree, clickupClient, openCodeClient, saveState: persistState, sessionExists, createSession, sendSessionEvent, ensureTaskWorktree, verifySessionEventDelivery, now });
|
|
11668
|
+
return finish({ ok: true, status: 200, reason: "accepted", result: { action: "accepted", taskId: clickUpTaskIdFromPayload(payload), eventKey: clickUpWebhookEventKey(payload) } });
|
|
11669
|
+
}
|
|
11670
|
+
const result = await routeClickUpWebhookEvent({ payload, config, state: receivedState, worktree, clickupClient, openCodeClient, saveState: persistState, sessionExists, createSession, sendSessionEvent, ensureTaskWorktree, verifySessionEventDelivery });
|
|
11610
11671
|
return finish({ ok: result.ok, status: result.ok ? 200 : 422, result });
|
|
11611
11672
|
} catch (error) {
|
|
11612
11673
|
writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, error, payload, at: now() });
|
|
@@ -11683,7 +11744,8 @@ function startClickUpWebhookListener({ config, state, worktree, clickupClient, o
|
|
|
11683
11744
|
worktree,
|
|
11684
11745
|
clickupClient,
|
|
11685
11746
|
openCodeClient,
|
|
11686
|
-
saveState: (nextState) => writeClickUpWebhookState(worktree, nextState, config)
|
|
11747
|
+
saveState: (nextState) => writeClickUpWebhookState(worktree, nextState, config),
|
|
11748
|
+
asyncRouting: true
|
|
11687
11749
|
});
|
|
11688
11750
|
res.writeHead(handled.status, { "Content-Type": "application/json" });
|
|
11689
11751
|
res.end(JSON.stringify({ ok: handled.ok, reason: handled.reason, result: handled.result?.action }));
|
package/dist/sanitize_cli.js
CHANGED
|
@@ -9985,17 +9985,11 @@ function createClickUpApiClient(config, fetchImpl = globalThis.fetch) {
|
|
|
9985
9985
|
body: JSON.stringify({ value })
|
|
9986
9986
|
});
|
|
9987
9987
|
},
|
|
9988
|
-
async postTaskComment(
|
|
9989
|
-
|
|
9990
|
-
method: "POST",
|
|
9991
|
-
body: JSON.stringify({ comment_text: comment })
|
|
9992
|
-
});
|
|
9988
|
+
async postTaskComment() {
|
|
9989
|
+
throw new Error("Optima runtime does not post ClickUp task comments; use local logs for process failures.");
|
|
9993
9990
|
},
|
|
9994
|
-
async addTaskTag(
|
|
9995
|
-
|
|
9996
|
-
method: "POST",
|
|
9997
|
-
body: JSON.stringify({})
|
|
9998
|
-
});
|
|
9991
|
+
async addTaskTag() {
|
|
9992
|
+
throw new Error("Optima runtime does not add ClickUp task tags; use local logs for process failures.");
|
|
9999
9993
|
},
|
|
10000
9994
|
async listAssignedTasks({ assigneeId, statuses = [], limit = CLICKUP_WEBHOOK_STARTUP_TASK_LIMIT } = {}) {
|
|
10001
9995
|
const params = new URLSearchParams();
|
|
@@ -10555,15 +10549,54 @@ async function createOpenCodeSession(client, { title, directory, agent } = {}) {
|
|
|
10555
10549
|
throw error || new Error("OpenCode session create failed.");
|
|
10556
10550
|
}
|
|
10557
10551
|
}
|
|
10558
|
-
|
|
10559
|
-
if (
|
|
10552
|
+
function normalizeOpenCodeSessionCollection(value) {
|
|
10553
|
+
if (Array.isArray(value)) return value;
|
|
10554
|
+
if (!isPlainObject(value)) return [];
|
|
10555
|
+
for (const key of ["sessions", "items", "data", "result", "results"]) {
|
|
10556
|
+
const nested = value[key];
|
|
10557
|
+
if (Array.isArray(nested)) return nested;
|
|
10558
|
+
if (isPlainObject(nested)) {
|
|
10559
|
+
const normalized = normalizeOpenCodeSessionCollection(nested);
|
|
10560
|
+
if (normalized.length > 0) return normalized;
|
|
10561
|
+
}
|
|
10562
|
+
}
|
|
10563
|
+
return [];
|
|
10564
|
+
}
|
|
10565
|
+
async function listOpenCodeSessionsReadiness(client, { directory = "" } = {}) {
|
|
10566
|
+
if (typeof client?.session?.list !== "function") return null;
|
|
10567
|
+
const attempts = [
|
|
10568
|
+
{ query: directory ? { directory } : {} },
|
|
10569
|
+
directory ? { directory } : {}
|
|
10570
|
+
];
|
|
10571
|
+
for (const attempt of attempts) {
|
|
10572
|
+
try {
|
|
10573
|
+
const result = await client.session.list(attempt);
|
|
10574
|
+
return { ok: true, method: "session_list", sessions: normalizeOpenCodeSessionCollection(result?.data ?? result).length };
|
|
10575
|
+
} catch {
|
|
10576
|
+
}
|
|
10577
|
+
}
|
|
10578
|
+
return null;
|
|
10579
|
+
}
|
|
10580
|
+
async function listOpenCodeSessionsReadinessHttp({ baseUrl, directory = "", fetchImpl = globalThis.fetch } = {}) {
|
|
10581
|
+
const root = normalizeOpenCodeBaseUrl(baseUrl, "");
|
|
10582
|
+
if (!root || typeof fetchImpl !== "function") return null;
|
|
10583
|
+
const url = openChamberUrl(root, "/session", directory ? { directory } : {});
|
|
10584
|
+
const response = await fetchImpl(url, { method: "GET" });
|
|
10585
|
+
const contentType = response.headers?.get?.("content-type") || "";
|
|
10586
|
+
const raw = await response.text();
|
|
10587
|
+
if (!response.ok) throw new Error(`OpenCode session list readiness failed with status ${response.status}.`);
|
|
10588
|
+
if (responseLooksLikeHtml(contentType, raw)) throw new Error("OpenCode session list readiness returned an HTML app shell.");
|
|
10589
|
+
const data = raw.trim() ? JSON.parse(raw) : [];
|
|
10590
|
+
return { ok: true, method: "session_list_http", sessions: normalizeOpenCodeSessionCollection(data).length };
|
|
10591
|
+
}
|
|
10592
|
+
async function waitForOpenCodeReadiness(client, { worktree = process.cwd(), attempts = 10, delayMs = 500, opencodeBaseUrl = "", fetchImpl = globalThis.fetch } = {}) {
|
|
10560
10593
|
let lastError = "opencode_not_ready";
|
|
10561
10594
|
const maxAttempts = Math.max(1, Number(attempts) || 1);
|
|
10562
10595
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
10563
10596
|
try {
|
|
10564
|
-
const
|
|
10565
|
-
if (
|
|
10566
|
-
|
|
10597
|
+
const listed = await listOpenCodeSessionsReadiness(client, { directory: worktree }) || await listOpenCodeSessionsReadinessHttp({ baseUrl: opencodeBaseUrl, directory: worktree, fetchImpl });
|
|
10598
|
+
if (listed?.ok) return { ...listed, attempts: attempt };
|
|
10599
|
+
return { ok: true, skipped: true, reason: "session_list_probe_unavailable", attempts: attempt };
|
|
10567
10600
|
} catch (error) {
|
|
10568
10601
|
lastError = error.message || "opencode_not_ready";
|
|
10569
10602
|
}
|
|
@@ -10936,18 +10969,9 @@ async function postClickUpLaunchFailureComment({ clickupClient, worktree, taskId
|
|
|
10936
10969
|
return { ok: false, skipped: true, reason: "local_log_only" };
|
|
10937
10970
|
}
|
|
10938
10971
|
async function applyClickUpBlockerTag({ clickupClient, worktree, taskId, reason, source, tagName = CLICKUP_BLOCKER_TAG_NAME } = {}) {
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
}
|
|
10943
|
-
try {
|
|
10944
|
-
await clickupClient.addTaskTag({ taskId, tagName });
|
|
10945
|
-
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_applied", taskId, reason, source, tagName });
|
|
10946
|
-
return { ok: true, tagName };
|
|
10947
|
-
} catch (error) {
|
|
10948
|
-
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_failed", taskId, reason, source, tagName, message: error.message });
|
|
10949
|
-
return { ok: false, error: error.message, tagName };
|
|
10950
|
-
}
|
|
10972
|
+
void clickupClient;
|
|
10973
|
+
appendClickUpWebhookLocalLog(worktree, { type: "blocker_tag_skipped", taskId, reason, source, tagName, policy: "metadata_only_task_writes" });
|
|
10974
|
+
return { ok: false, skipped: true, reason: "metadata_only_task_writes", tagName };
|
|
10951
10975
|
}
|
|
10952
10976
|
function openCodeBlockingPromptVerification(result, sessionId) {
|
|
10953
10977
|
if (result?.__optimaPromptDelivery !== "prompt") return null;
|
|
@@ -11437,7 +11461,7 @@ async function reconcileClickUpStartup({ config, state = {}, worktree = process.
|
|
|
11437
11461
|
clickUpWebhookLifecycleLog(worktree, { type: "startup_reconciliation_finished", ...skipped });
|
|
11438
11462
|
return skipped;
|
|
11439
11463
|
}
|
|
11440
|
-
const readiness = await waitForReadiness(openCodeClient, { worktree, now });
|
|
11464
|
+
const readiness = await waitForReadiness(openCodeClient, { worktree, now, opencodeBaseUrl: config.opencode?.baseUrl });
|
|
11441
11465
|
appendClickUpWebhookLocalLog(worktree, { type: readiness.ok ? "startup_reconciliation_readiness_ready" : "startup_reconciliation_readiness_failed", ...readiness });
|
|
11442
11466
|
if (!readiness.ok) {
|
|
11443
11467
|
const failed = { ok: false, skipped: true, reason: "opencode_not_ready", readiness, assigned: 0, comments: 0, ignored: 0, errors: 1, undelivered: 1, tasks: [], validation: { undelivered: 1, emptyPromptSessions: [] } };
|
|
@@ -11576,17 +11600,49 @@ function clickUpWebhookExpectedPath(config) {
|
|
|
11576
11600
|
return "/";
|
|
11577
11601
|
}
|
|
11578
11602
|
}
|
|
11579
|
-
|
|
11603
|
+
function summarizeClickUpRouteForLog(result = {}, payload = {}) {
|
|
11604
|
+
return {
|
|
11605
|
+
taskId: result.taskId || clickUpTaskIdFromPayload(payload || {}) || null,
|
|
11606
|
+
event: clickUpEventType(payload || {}) || null,
|
|
11607
|
+
eventKey: result.eventKey || clickUpWebhookEventKey(payload || {}) || null,
|
|
11608
|
+
ok: result.ok === true,
|
|
11609
|
+
action: result.action || null,
|
|
11610
|
+
reason: result.reason || null,
|
|
11611
|
+
sessionId: result.sessionId || result.replacementSessionId || null,
|
|
11612
|
+
branch: result.branch || null,
|
|
11613
|
+
worktree: result.worktree || null
|
|
11614
|
+
};
|
|
11615
|
+
}
|
|
11616
|
+
function runClickUpWebhookRouteInBackground({ payload, config, state, worktree, clickupClient, openCodeClient, saveState, sessionExists, createSession, sendSessionEvent, ensureTaskWorktree, verifySessionEventDelivery, now } = {}) {
|
|
11617
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11618
|
+
appendClickUpWebhookLocalLog(worktree, { type: "webhook_route_started", taskId: clickUpTaskIdFromPayload(payload || {}) || null, event: clickUpEventType(payload || {}) || null, eventKey: clickUpWebhookEventKey(payload || {}) || null, async: true, startedAt });
|
|
11619
|
+
const route = Promise.resolve().then(() => routeClickUpWebhookEvent({ payload, config, state, worktree, clickupClient, openCodeClient, saveState, sessionExists, createSession, sendSessionEvent, ensureTaskWorktree, verifySessionEventDelivery, now })).then((result) => {
|
|
11620
|
+
appendClickUpWebhookLocalLog(worktree, { type: "webhook_route_finished", async: true, ...summarizeClickUpRouteForLog(result, payload) });
|
|
11621
|
+
return result;
|
|
11622
|
+
}).catch((error) => {
|
|
11623
|
+
appendClickUpWebhookLocalLog(worktree, { type: "webhook_route_failed", taskId: clickUpTaskIdFromPayload(payload || {}) || null, event: clickUpEventType(payload || {}) || null, eventKey: clickUpWebhookEventKey(payload || {}) || null, async: true, message: error.message });
|
|
11624
|
+
return { ok: false, action: "error", reason: "background_route_failed", message: error.message };
|
|
11625
|
+
});
|
|
11626
|
+
route.catch(() => {
|
|
11627
|
+
});
|
|
11628
|
+
return route;
|
|
11629
|
+
}
|
|
11630
|
+
async function handleClickUpWebhookRequest({ method = "POST", url = null, headers = {}, rawBody = "", config, state, worktree, clickupClient, openCodeClient, saveState, now = () => /* @__PURE__ */ new Date(), sessionExists, createSession, sendSessionEvent, ensureTaskWorktree, verifySessionEventDelivery, asyncRouting = false } = {}) {
|
|
11580
11631
|
let payload = null;
|
|
11581
11632
|
let handled = null;
|
|
11582
11633
|
let authenticatedWebhook = false;
|
|
11583
11634
|
let receivedAt = null;
|
|
11584
11635
|
let activeState = state;
|
|
11636
|
+
let latestPersistedState = state;
|
|
11637
|
+
const persistState = (nextState) => {
|
|
11638
|
+
latestPersistedState = nextState;
|
|
11639
|
+
if (saveState) saveState(nextState);
|
|
11640
|
+
};
|
|
11585
11641
|
const finish = (result) => {
|
|
11586
11642
|
handled = result;
|
|
11587
11643
|
receivedAt ??= now();
|
|
11588
|
-
const auditState = authenticatedWebhook ? { ...
|
|
11589
|
-
if (saveState && authenticatedWebhook)
|
|
11644
|
+
const auditState = authenticatedWebhook ? { ...latestPersistedState, lastWebhookAt: receivedAt.toISOString() } : latestPersistedState;
|
|
11645
|
+
if (saveState && authenticatedWebhook) persistState(auditState);
|
|
11590
11646
|
writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state: auditState, handled, payload, at: receivedAt });
|
|
11591
11647
|
return result;
|
|
11592
11648
|
};
|
|
@@ -11613,7 +11669,12 @@ async function handleClickUpWebhookRequest({ method = "POST", url = null, header
|
|
|
11613
11669
|
return finish({ ok: false, status: 400, reason: "invalid_json" });
|
|
11614
11670
|
}
|
|
11615
11671
|
const receivedState = { ...activeState, lastWebhookAt: receivedAt.toISOString() };
|
|
11616
|
-
|
|
11672
|
+
persistState(receivedState);
|
|
11673
|
+
if (asyncRouting) {
|
|
11674
|
+
runClickUpWebhookRouteInBackground({ payload, config, state: receivedState, worktree, clickupClient, openCodeClient, saveState: persistState, sessionExists, createSession, sendSessionEvent, ensureTaskWorktree, verifySessionEventDelivery, now });
|
|
11675
|
+
return finish({ ok: true, status: 200, reason: "accepted", result: { action: "accepted", taskId: clickUpTaskIdFromPayload(payload), eventKey: clickUpWebhookEventKey(payload) } });
|
|
11676
|
+
}
|
|
11677
|
+
const result = await routeClickUpWebhookEvent({ payload, config, state: receivedState, worktree, clickupClient, openCodeClient, saveState: persistState, sessionExists, createSession, sendSessionEvent, ensureTaskWorktree, verifySessionEventDelivery });
|
|
11617
11678
|
return finish({ ok: result.ok, status: result.ok ? 200 : 422, result });
|
|
11618
11679
|
} catch (error) {
|
|
11619
11680
|
writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, error, payload, at: now() });
|
|
@@ -11690,7 +11751,8 @@ function startClickUpWebhookListener({ config, state, worktree, clickupClient, o
|
|
|
11690
11751
|
worktree,
|
|
11691
11752
|
clickupClient,
|
|
11692
11753
|
openCodeClient,
|
|
11693
|
-
saveState: (nextState) => writeClickUpWebhookState(worktree, nextState, config)
|
|
11754
|
+
saveState: (nextState) => writeClickUpWebhookState(worktree, nextState, config),
|
|
11755
|
+
asyncRouting: true
|
|
11694
11756
|
});
|
|
11695
11757
|
res.writeHead(handled.status, { "Content-Type": "application/json" });
|
|
11696
11758
|
res.end(JSON.stringify({ ok: handled.ok, reason: handled.reason, result: handled.result?.action }));
|