@defend-tech/opencode-optima 0.1.12 → 0.1.14
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 +136 -22
- package/dist/sanitize_cli.js +136 -22
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7929,6 +7929,8 @@ var CLICKUP_PM_MENTION_NAME = "Defend Tech Product Manager";
|
|
|
7929
7929
|
var CLICKUP_WEBHOOK_RUNTIME_VERSION = 1;
|
|
7930
7930
|
var CLICKUP_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
|
|
7931
7931
|
var CLICKUP_WEBHOOK_REQUEST_TIMEOUT_MS = 1e4;
|
|
7932
|
+
var CLICKUP_WEBHOOK_LOG_LEVELS = /* @__PURE__ */ new Set(["error", "info", "verbose"]);
|
|
7933
|
+
var CLICKUP_WEBHOOK_REDACTED = "[REDACTED]";
|
|
7932
7934
|
var DISCUSSION_BACKFILL_FETCH_LIMIT = 100;
|
|
7933
7935
|
var DEFAULT_PRESERVE_EXISTING_AGENTS = true;
|
|
7934
7936
|
var SAFE_WORKTREE_FAILURE = "FAIL: Optima needs a writable project directory. Open/add a project in OpenChamber first. Refusing to use '/'.";
|
|
@@ -8793,6 +8795,22 @@ function clickUpWebhookStatePath(worktree) {
|
|
|
8793
8795
|
function clickUpWebhookLogPath(worktree) {
|
|
8794
8796
|
return path2.join(optimaRuntimeDir(worktree), "clickup-webhook.log.jsonl");
|
|
8795
8797
|
}
|
|
8798
|
+
function normalizeClickUpWebhookLogLevel(value) {
|
|
8799
|
+
const level = String(value || "info").trim().toLowerCase();
|
|
8800
|
+
return CLICKUP_WEBHOOK_LOG_LEVELS.has(level) ? level : "info";
|
|
8801
|
+
}
|
|
8802
|
+
function clickUpWebhookAuditLogDir() {
|
|
8803
|
+
const dataHome = process.env.XDG_DATA_HOME && path2.isAbsolute(process.env.XDG_DATA_HOME) ? process.env.XDG_DATA_HOME : path2.join(os.homedir(), ".local", "share", "opencode");
|
|
8804
|
+
return path2.join(dataHome, "opencode-optima");
|
|
8805
|
+
}
|
|
8806
|
+
function clickUpWebhookAuditLogPath(at = /* @__PURE__ */ new Date(), logDir = clickUpWebhookAuditLogDir()) {
|
|
8807
|
+
return path2.join(logDir, `clickup-webhook-${at.toISOString().slice(0, 10)}.jsonl`);
|
|
8808
|
+
}
|
|
8809
|
+
function clickUpWebhookRequestFilePath(at = /* @__PURE__ */ new Date(), logDir = clickUpWebhookAuditLogDir()) {
|
|
8810
|
+
const stamp = at.toISOString().replace(/:/g, "-").replace(/\./g, "-");
|
|
8811
|
+
const shortId = crypto.randomBytes(4).toString("hex");
|
|
8812
|
+
return path2.join(logDir, "requests", `clickup-webhook-${stamp}-${shortId}.json`);
|
|
8813
|
+
}
|
|
8796
8814
|
function findOptimaPluginTupleOptions(pluginEntries = []) {
|
|
8797
8815
|
if (!Array.isArray(pluginEntries)) return null;
|
|
8798
8816
|
for (const entry of pluginEntries) {
|
|
@@ -8822,6 +8840,7 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
|
|
|
8822
8840
|
basePath: String(raw.base_path || raw.basePath || worktree || "").trim(),
|
|
8823
8841
|
teamId: String(raw.team_id || raw.teamId || "").trim(),
|
|
8824
8842
|
apiToken: String(raw.api_token || raw.apiToken || "").trim(),
|
|
8843
|
+
log: normalizeClickUpWebhookLogLevel(raw.log),
|
|
8825
8844
|
webhook: {
|
|
8826
8845
|
publicUrl: String(webhook.public_url || webhook.publicUrl || "").trim(),
|
|
8827
8846
|
bindHost: String(webhook.bind_host || webhook.bindHost || "127.0.0.1").trim(),
|
|
@@ -9188,10 +9207,26 @@ async function createOpenCodeSession(client, { title, directory } = {}) {
|
|
|
9188
9207
|
const result = await client.session.create({ query: { directory }, body: { title } });
|
|
9189
9208
|
return result?.data?.id || result?.id;
|
|
9190
9209
|
}
|
|
9210
|
+
async function callOpenCodePromptWithPathFallback(method, sessionId, payload) {
|
|
9211
|
+
try {
|
|
9212
|
+
return await method({ ...payload, path: { sessionID: sessionId } });
|
|
9213
|
+
} catch (error) {
|
|
9214
|
+
try {
|
|
9215
|
+
return await method({ ...payload, path: { id: sessionId } });
|
|
9216
|
+
} catch {
|
|
9217
|
+
throw error;
|
|
9218
|
+
}
|
|
9219
|
+
}
|
|
9220
|
+
}
|
|
9191
9221
|
async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory } = {}) {
|
|
9192
|
-
const payload = {
|
|
9193
|
-
if (typeof client?.session?.
|
|
9194
|
-
|
|
9222
|
+
const payload = { query: { directory }, body: { agent, parts: [{ type: "text", text }] } };
|
|
9223
|
+
if (typeof client?.session?.prompt === "function") {
|
|
9224
|
+
return callOpenCodePromptWithPathFallback(client.session.prompt.bind(client.session), sessionId, payload);
|
|
9225
|
+
}
|
|
9226
|
+
if (typeof client?.session?.promptAsync === "function") {
|
|
9227
|
+
return callOpenCodePromptWithPathFallback(client.session.promptAsync.bind(client.session), sessionId, payload);
|
|
9228
|
+
}
|
|
9229
|
+
throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
|
|
9195
9230
|
}
|
|
9196
9231
|
function formatClickUpWebhookPrompt({ eventType, taskId, payload }) {
|
|
9197
9232
|
const comment = clickUpCommentFromPayload(payload);
|
|
@@ -9212,6 +9247,74 @@ function appendClickUpWebhookLocalLog(worktree, entry) {
|
|
|
9212
9247
|
fs2.appendFileSync(logPath, `${JSON.stringify(safeEntry)}
|
|
9213
9248
|
`, "utf8");
|
|
9214
9249
|
}
|
|
9250
|
+
function redactClickUpWebhookAuditValue(value, secretValues = []) {
|
|
9251
|
+
if (typeof value === "string") {
|
|
9252
|
+
return secretValues.reduce((next, secret) => secret ? next.split(secret).join(CLICKUP_WEBHOOK_REDACTED) : next, value);
|
|
9253
|
+
}
|
|
9254
|
+
if (Array.isArray(value)) return value.map((item) => redactClickUpWebhookAuditValue(item, secretValues));
|
|
9255
|
+
if (!isPlainObject(value)) return value;
|
|
9256
|
+
return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => {
|
|
9257
|
+
const normalizedKey = normalizeLooseToken(key);
|
|
9258
|
+
const secretKey = normalizedKey.includes("signature") || normalizedKey.includes("authorization") || normalizedKey.includes("cookie") || normalizedKey.includes("token") || normalizedKey.includes("secret") || normalizedKey.includes("api-key") || normalizedKey.includes("apikey");
|
|
9259
|
+
return [key, secretKey ? CLICKUP_WEBHOOK_REDACTED : redactClickUpWebhookAuditValue(entryValue, secretValues)];
|
|
9260
|
+
}));
|
|
9261
|
+
}
|
|
9262
|
+
function buildClickUpWebhookAuditSummary({ method, url, config, handled, error, payload, requestFile, at = /* @__PURE__ */ new Date() } = {}) {
|
|
9263
|
+
const result = handled?.result || {};
|
|
9264
|
+
const summary = {
|
|
9265
|
+
at: at.toISOString(),
|
|
9266
|
+
method,
|
|
9267
|
+
path: url === null ? null : new URL(String(url || "/"), config?.webhook?.publicUrl || "http://localhost").pathname,
|
|
9268
|
+
status: handled?.status || 500,
|
|
9269
|
+
ok: Boolean(handled?.ok),
|
|
9270
|
+
reason: handled?.reason || result.reason || (error ? "handler_error" : void 0),
|
|
9271
|
+
action: result.action,
|
|
9272
|
+
event: payload ? clickUpEventType(payload) : void 0,
|
|
9273
|
+
task: result.taskId || clickUpTaskIdFromPayload(payload || {}),
|
|
9274
|
+
comment: clickUpCommentFromPayload(payload || {})?.id || payload?.comment_id || payload?.commentId,
|
|
9275
|
+
session: result.sessionId,
|
|
9276
|
+
base_path: config?.basePath || "",
|
|
9277
|
+
request_file: requestFile || void 0
|
|
9278
|
+
};
|
|
9279
|
+
return Object.fromEntries(Object.entries(summary).filter(([, value]) => value !== void 0 && value !== ""));
|
|
9280
|
+
}
|
|
9281
|
+
function writeClickUpWebhookAuditLog({ method, url, headers = {}, rawBody = "", config, state = {}, handled = null, error = null, payload = null, at = /* @__PURE__ */ new Date() } = {}) {
|
|
9282
|
+
try {
|
|
9283
|
+
const level = normalizeClickUpWebhookLogLevel(config?.log);
|
|
9284
|
+
const failed = Boolean(error || !handled?.ok || (handled?.status || 500) >= 400);
|
|
9285
|
+
if (level === "error" && !failed) return;
|
|
9286
|
+
const logDir = clickUpWebhookAuditLogDir();
|
|
9287
|
+
fs2.mkdirSync(logDir, { recursive: true });
|
|
9288
|
+
const secretValues = [resolveSecretReference(config?.apiToken), state?.secret, config?.webhook?.secret].filter(Boolean);
|
|
9289
|
+
let requestFile;
|
|
9290
|
+
if (level === "verbose") {
|
|
9291
|
+
const absoluteRequestFile = clickUpWebhookRequestFilePath(at, logDir);
|
|
9292
|
+
fs2.mkdirSync(path2.dirname(absoluteRequestFile), { recursive: true });
|
|
9293
|
+
requestFile = path2.relative(logDir, absoluteRequestFile).split(path2.sep).join("/");
|
|
9294
|
+
const parsedBody = payload || (() => {
|
|
9295
|
+
try {
|
|
9296
|
+
return JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || ""));
|
|
9297
|
+
} catch {
|
|
9298
|
+
return void 0;
|
|
9299
|
+
}
|
|
9300
|
+
})();
|
|
9301
|
+
const requestArtifact = redactClickUpWebhookAuditValue({
|
|
9302
|
+
at: at.toISOString(),
|
|
9303
|
+
method,
|
|
9304
|
+
url,
|
|
9305
|
+
path: url === null ? null : new URL(String(url || "/"), config?.webhook?.publicUrl || "http://localhost").pathname,
|
|
9306
|
+
headers,
|
|
9307
|
+
body: parsedBody === void 0 ? Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || "") : parsedBody
|
|
9308
|
+
}, secretValues);
|
|
9309
|
+
fs2.writeFileSync(absoluteRequestFile, `${JSON.stringify(requestArtifact, null, 2)}
|
|
9310
|
+
`, "utf8");
|
|
9311
|
+
}
|
|
9312
|
+
const summary = redactClickUpWebhookAuditValue(buildClickUpWebhookAuditSummary({ method, url, config, handled, error, payload, requestFile, at }), secretValues);
|
|
9313
|
+
fs2.appendFileSync(clickUpWebhookAuditLogPath(at, logDir), `${JSON.stringify(summary)}
|
|
9314
|
+
`, "utf8");
|
|
9315
|
+
} catch {
|
|
9316
|
+
}
|
|
9317
|
+
}
|
|
9215
9318
|
async function withClickUpTaskRouteLock(taskId, operation) {
|
|
9216
9319
|
const key = String(taskId || "");
|
|
9217
9320
|
if (!key) return operation();
|
|
@@ -9280,9 +9383,9 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
9280
9383
|
appendClickUpWebhookLocalLog(worktree, { type: "pending_session_metadata_failed", taskId, sessionId: pendingSessionId, message: error.message });
|
|
9281
9384
|
throw error;
|
|
9282
9385
|
}
|
|
9386
|
+
await sendSessionEvent(openCodeClient, { sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: config.basePath });
|
|
9283
9387
|
const nextMetadata = clearClickUpPendingSessionMetadata(setClickUpSessionMetadata(pendingMetadata, config.routing.metadataKey, pendingSessionId), config.routing.metadataKey);
|
|
9284
9388
|
await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
|
|
9285
|
-
await sendSessionEvent(openCodeClient, { sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: config.basePath });
|
|
9286
9389
|
const { [taskId]: _completedPending, ...remainingPending } = stateToPersist.pendingSessions || {};
|
|
9287
9390
|
stateToPersist = { ...stateToPersist, pendingSessions: remainingPending };
|
|
9288
9391
|
return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey });
|
|
@@ -9309,25 +9412,36 @@ function clickUpWebhookExpectedPath(config) {
|
|
|
9309
9412
|
return "/";
|
|
9310
9413
|
}
|
|
9311
9414
|
}
|
|
9312
|
-
async function handleClickUpWebhookRequest({ method = "POST", url = null, headers = {}, rawBody = "", config, state, worktree, clickupClient, openCodeClient, saveState } = {}) {
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
if (bodyBytes > maxBodyBytes) return { ok: false, status: 413, reason: "body_too_large" };
|
|
9321
|
-
const signature = headers["x-signature"] || headers["X-Signature"];
|
|
9322
|
-
if (!verifyClickUpSignature(rawBody, signature, state?.secret)) return { ok: false, status: 401, reason: "invalid_signature" };
|
|
9323
|
-
let payload;
|
|
9415
|
+
async function handleClickUpWebhookRequest({ method = "POST", url = null, headers = {}, rawBody = "", config, state, worktree, clickupClient, openCodeClient, saveState, now = () => /* @__PURE__ */ new Date() } = {}) {
|
|
9416
|
+
let payload = null;
|
|
9417
|
+
let handled = null;
|
|
9418
|
+
const finish = (result) => {
|
|
9419
|
+
handled = result;
|
|
9420
|
+
writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, payload, at: now() });
|
|
9421
|
+
return result;
|
|
9422
|
+
};
|
|
9324
9423
|
try {
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9424
|
+
if (method !== "POST") return finish({ ok: false, status: 405, reason: "method_not_allowed" });
|
|
9425
|
+
if (url !== null) {
|
|
9426
|
+
const requestPath = new URL(String(url), config?.webhook?.publicUrl || "http://localhost").pathname;
|
|
9427
|
+
if (requestPath !== clickUpWebhookExpectedPath(config)) return finish({ ok: false, status: 404, reason: "wrong_path" });
|
|
9428
|
+
}
|
|
9429
|
+
const maxBodyBytes = Number(config?.webhook?.maxBodyBytes || CLICKUP_WEBHOOK_MAX_BODY_BYTES);
|
|
9430
|
+
const bodyBytes = Buffer.isBuffer(rawBody) ? rawBody.length : Buffer.byteLength(String(rawBody || ""));
|
|
9431
|
+
if (bodyBytes > maxBodyBytes) return finish({ ok: false, status: 413, reason: "body_too_large" });
|
|
9432
|
+
const signature = headers["x-signature"] || headers["X-Signature"];
|
|
9433
|
+
if (!verifyClickUpSignature(rawBody, signature, state?.secret)) return finish({ ok: false, status: 401, reason: "invalid_signature" });
|
|
9434
|
+
try {
|
|
9435
|
+
payload = JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody));
|
|
9436
|
+
} catch {
|
|
9437
|
+
return finish({ ok: false, status: 400, reason: "invalid_json" });
|
|
9438
|
+
}
|
|
9439
|
+
const result = await routeClickUpWebhookEvent({ payload, config, state, worktree, clickupClient, openCodeClient, saveState });
|
|
9440
|
+
return finish({ ok: result.ok, status: result.ok ? 200 : 422, result });
|
|
9441
|
+
} catch (error) {
|
|
9442
|
+
writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, error, payload, at: now() });
|
|
9443
|
+
throw error;
|
|
9328
9444
|
}
|
|
9329
|
-
const result = await routeClickUpWebhookEvent({ payload, config, state, worktree, clickupClient, openCodeClient, saveState });
|
|
9330
|
-
return { ok: result.ok, status: result.ok ? 200 : 422, result };
|
|
9331
9445
|
}
|
|
9332
9446
|
function clickUpListenerKey(config) {
|
|
9333
9447
|
return `${config?.webhook?.bindHost || "127.0.0.1"}:${config?.webhook?.bindPort || 0}`;
|
|
@@ -11019,7 +11133,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
11019
11133
|
}
|
|
11020
11134
|
};
|
|
11021
11135
|
}
|
|
11022
|
-
OptimaPlugin.__internals = { buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentMentionsProductManager, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, readClickUpWebhookState, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
11136
|
+
OptimaPlugin.__internals = { buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, readClickUpWebhookState, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
11023
11137
|
export {
|
|
11024
11138
|
OptimaPlugin as default
|
|
11025
11139
|
};
|
package/dist/sanitize_cli.js
CHANGED
|
@@ -7936,6 +7936,8 @@ var CLICKUP_PM_MENTION_NAME = "Defend Tech Product Manager";
|
|
|
7936
7936
|
var CLICKUP_WEBHOOK_RUNTIME_VERSION = 1;
|
|
7937
7937
|
var CLICKUP_WEBHOOK_MAX_BODY_BYTES = 1024 * 1024;
|
|
7938
7938
|
var CLICKUP_WEBHOOK_REQUEST_TIMEOUT_MS = 1e4;
|
|
7939
|
+
var CLICKUP_WEBHOOK_LOG_LEVELS = /* @__PURE__ */ new Set(["error", "info", "verbose"]);
|
|
7940
|
+
var CLICKUP_WEBHOOK_REDACTED = "[REDACTED]";
|
|
7939
7941
|
var DISCUSSION_BACKFILL_FETCH_LIMIT = 100;
|
|
7940
7942
|
var DEFAULT_PRESERVE_EXISTING_AGENTS = true;
|
|
7941
7943
|
var SAFE_WORKTREE_FAILURE = "FAIL: Optima needs a writable project directory. Open/add a project in OpenChamber first. Refusing to use '/'.";
|
|
@@ -8800,6 +8802,22 @@ function clickUpWebhookStatePath(worktree) {
|
|
|
8800
8802
|
function clickUpWebhookLogPath(worktree) {
|
|
8801
8803
|
return path2.join(optimaRuntimeDir(worktree), "clickup-webhook.log.jsonl");
|
|
8802
8804
|
}
|
|
8805
|
+
function normalizeClickUpWebhookLogLevel(value) {
|
|
8806
|
+
const level = String(value || "info").trim().toLowerCase();
|
|
8807
|
+
return CLICKUP_WEBHOOK_LOG_LEVELS.has(level) ? level : "info";
|
|
8808
|
+
}
|
|
8809
|
+
function clickUpWebhookAuditLogDir() {
|
|
8810
|
+
const dataHome = process.env.XDG_DATA_HOME && path2.isAbsolute(process.env.XDG_DATA_HOME) ? process.env.XDG_DATA_HOME : path2.join(os.homedir(), ".local", "share", "opencode");
|
|
8811
|
+
return path2.join(dataHome, "opencode-optima");
|
|
8812
|
+
}
|
|
8813
|
+
function clickUpWebhookAuditLogPath(at = /* @__PURE__ */ new Date(), logDir = clickUpWebhookAuditLogDir()) {
|
|
8814
|
+
return path2.join(logDir, `clickup-webhook-${at.toISOString().slice(0, 10)}.jsonl`);
|
|
8815
|
+
}
|
|
8816
|
+
function clickUpWebhookRequestFilePath(at = /* @__PURE__ */ new Date(), logDir = clickUpWebhookAuditLogDir()) {
|
|
8817
|
+
const stamp = at.toISOString().replace(/:/g, "-").replace(/\./g, "-");
|
|
8818
|
+
const shortId = crypto.randomBytes(4).toString("hex");
|
|
8819
|
+
return path2.join(logDir, "requests", `clickup-webhook-${stamp}-${shortId}.json`);
|
|
8820
|
+
}
|
|
8803
8821
|
function findOptimaPluginTupleOptions(pluginEntries = []) {
|
|
8804
8822
|
if (!Array.isArray(pluginEntries)) return null;
|
|
8805
8823
|
for (const entry of pluginEntries) {
|
|
@@ -8829,6 +8847,7 @@ function normalizeClickUpWebhookConfig(rawClickUp = null, worktree = process.cwd
|
|
|
8829
8847
|
basePath: String(raw.base_path || raw.basePath || worktree || "").trim(),
|
|
8830
8848
|
teamId: String(raw.team_id || raw.teamId || "").trim(),
|
|
8831
8849
|
apiToken: String(raw.api_token || raw.apiToken || "").trim(),
|
|
8850
|
+
log: normalizeClickUpWebhookLogLevel(raw.log),
|
|
8832
8851
|
webhook: {
|
|
8833
8852
|
publicUrl: String(webhook.public_url || webhook.publicUrl || "").trim(),
|
|
8834
8853
|
bindHost: String(webhook.bind_host || webhook.bindHost || "127.0.0.1").trim(),
|
|
@@ -9195,10 +9214,26 @@ async function createOpenCodeSession(client, { title, directory } = {}) {
|
|
|
9195
9214
|
const result = await client.session.create({ query: { directory }, body: { title } });
|
|
9196
9215
|
return result?.data?.id || result?.id;
|
|
9197
9216
|
}
|
|
9217
|
+
async function callOpenCodePromptWithPathFallback(method, sessionId, payload) {
|
|
9218
|
+
try {
|
|
9219
|
+
return await method({ ...payload, path: { sessionID: sessionId } });
|
|
9220
|
+
} catch (error) {
|
|
9221
|
+
try {
|
|
9222
|
+
return await method({ ...payload, path: { id: sessionId } });
|
|
9223
|
+
} catch {
|
|
9224
|
+
throw error;
|
|
9225
|
+
}
|
|
9226
|
+
}
|
|
9227
|
+
}
|
|
9198
9228
|
async function sendOpenCodeSessionEvent(client, { sessionId, agent, text, directory } = {}) {
|
|
9199
|
-
const payload = {
|
|
9200
|
-
if (typeof client?.session?.
|
|
9201
|
-
|
|
9229
|
+
const payload = { query: { directory }, body: { agent, parts: [{ type: "text", text }] } };
|
|
9230
|
+
if (typeof client?.session?.prompt === "function") {
|
|
9231
|
+
return callOpenCodePromptWithPathFallback(client.session.prompt.bind(client.session), sessionId, payload);
|
|
9232
|
+
}
|
|
9233
|
+
if (typeof client?.session?.promptAsync === "function") {
|
|
9234
|
+
return callOpenCodePromptWithPathFallback(client.session.promptAsync.bind(client.session), sessionId, payload);
|
|
9235
|
+
}
|
|
9236
|
+
throw new Error("OpenCode client does not expose session.prompt or session.promptAsync.");
|
|
9202
9237
|
}
|
|
9203
9238
|
function formatClickUpWebhookPrompt({ eventType, taskId, payload }) {
|
|
9204
9239
|
const comment = clickUpCommentFromPayload(payload);
|
|
@@ -9219,6 +9254,74 @@ function appendClickUpWebhookLocalLog(worktree, entry) {
|
|
|
9219
9254
|
fs2.appendFileSync(logPath, `${JSON.stringify(safeEntry)}
|
|
9220
9255
|
`, "utf8");
|
|
9221
9256
|
}
|
|
9257
|
+
function redactClickUpWebhookAuditValue(value, secretValues = []) {
|
|
9258
|
+
if (typeof value === "string") {
|
|
9259
|
+
return secretValues.reduce((next, secret) => secret ? next.split(secret).join(CLICKUP_WEBHOOK_REDACTED) : next, value);
|
|
9260
|
+
}
|
|
9261
|
+
if (Array.isArray(value)) return value.map((item) => redactClickUpWebhookAuditValue(item, secretValues));
|
|
9262
|
+
if (!isPlainObject(value)) return value;
|
|
9263
|
+
return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => {
|
|
9264
|
+
const normalizedKey = normalizeLooseToken(key);
|
|
9265
|
+
const secretKey = normalizedKey.includes("signature") || normalizedKey.includes("authorization") || normalizedKey.includes("cookie") || normalizedKey.includes("token") || normalizedKey.includes("secret") || normalizedKey.includes("api-key") || normalizedKey.includes("apikey");
|
|
9266
|
+
return [key, secretKey ? CLICKUP_WEBHOOK_REDACTED : redactClickUpWebhookAuditValue(entryValue, secretValues)];
|
|
9267
|
+
}));
|
|
9268
|
+
}
|
|
9269
|
+
function buildClickUpWebhookAuditSummary({ method, url, config, handled, error, payload, requestFile, at = /* @__PURE__ */ new Date() } = {}) {
|
|
9270
|
+
const result = handled?.result || {};
|
|
9271
|
+
const summary = {
|
|
9272
|
+
at: at.toISOString(),
|
|
9273
|
+
method,
|
|
9274
|
+
path: url === null ? null : new URL(String(url || "/"), config?.webhook?.publicUrl || "http://localhost").pathname,
|
|
9275
|
+
status: handled?.status || 500,
|
|
9276
|
+
ok: Boolean(handled?.ok),
|
|
9277
|
+
reason: handled?.reason || result.reason || (error ? "handler_error" : void 0),
|
|
9278
|
+
action: result.action,
|
|
9279
|
+
event: payload ? clickUpEventType(payload) : void 0,
|
|
9280
|
+
task: result.taskId || clickUpTaskIdFromPayload(payload || {}),
|
|
9281
|
+
comment: clickUpCommentFromPayload(payload || {})?.id || payload?.comment_id || payload?.commentId,
|
|
9282
|
+
session: result.sessionId,
|
|
9283
|
+
base_path: config?.basePath || "",
|
|
9284
|
+
request_file: requestFile || void 0
|
|
9285
|
+
};
|
|
9286
|
+
return Object.fromEntries(Object.entries(summary).filter(([, value]) => value !== void 0 && value !== ""));
|
|
9287
|
+
}
|
|
9288
|
+
function writeClickUpWebhookAuditLog({ method, url, headers = {}, rawBody = "", config, state = {}, handled = null, error = null, payload = null, at = /* @__PURE__ */ new Date() } = {}) {
|
|
9289
|
+
try {
|
|
9290
|
+
const level = normalizeClickUpWebhookLogLevel(config?.log);
|
|
9291
|
+
const failed = Boolean(error || !handled?.ok || (handled?.status || 500) >= 400);
|
|
9292
|
+
if (level === "error" && !failed) return;
|
|
9293
|
+
const logDir = clickUpWebhookAuditLogDir();
|
|
9294
|
+
fs2.mkdirSync(logDir, { recursive: true });
|
|
9295
|
+
const secretValues = [resolveSecretReference(config?.apiToken), state?.secret, config?.webhook?.secret].filter(Boolean);
|
|
9296
|
+
let requestFile;
|
|
9297
|
+
if (level === "verbose") {
|
|
9298
|
+
const absoluteRequestFile = clickUpWebhookRequestFilePath(at, logDir);
|
|
9299
|
+
fs2.mkdirSync(path2.dirname(absoluteRequestFile), { recursive: true });
|
|
9300
|
+
requestFile = path2.relative(logDir, absoluteRequestFile).split(path2.sep).join("/");
|
|
9301
|
+
const parsedBody = payload || (() => {
|
|
9302
|
+
try {
|
|
9303
|
+
return JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || ""));
|
|
9304
|
+
} catch {
|
|
9305
|
+
return void 0;
|
|
9306
|
+
}
|
|
9307
|
+
})();
|
|
9308
|
+
const requestArtifact = redactClickUpWebhookAuditValue({
|
|
9309
|
+
at: at.toISOString(),
|
|
9310
|
+
method,
|
|
9311
|
+
url,
|
|
9312
|
+
path: url === null ? null : new URL(String(url || "/"), config?.webhook?.publicUrl || "http://localhost").pathname,
|
|
9313
|
+
headers,
|
|
9314
|
+
body: parsedBody === void 0 ? Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || "") : parsedBody
|
|
9315
|
+
}, secretValues);
|
|
9316
|
+
fs2.writeFileSync(absoluteRequestFile, `${JSON.stringify(requestArtifact, null, 2)}
|
|
9317
|
+
`, "utf8");
|
|
9318
|
+
}
|
|
9319
|
+
const summary = redactClickUpWebhookAuditValue(buildClickUpWebhookAuditSummary({ method, url, config, handled, error, payload, requestFile, at }), secretValues);
|
|
9320
|
+
fs2.appendFileSync(clickUpWebhookAuditLogPath(at, logDir), `${JSON.stringify(summary)}
|
|
9321
|
+
`, "utf8");
|
|
9322
|
+
} catch {
|
|
9323
|
+
}
|
|
9324
|
+
}
|
|
9222
9325
|
async function withClickUpTaskRouteLock(taskId, operation) {
|
|
9223
9326
|
const key = String(taskId || "");
|
|
9224
9327
|
if (!key) return operation();
|
|
@@ -9287,9 +9390,9 @@ async function routeClickUpWebhookEventUnlocked({ payload, config, state = {}, w
|
|
|
9287
9390
|
appendClickUpWebhookLocalLog(worktree, { type: "pending_session_metadata_failed", taskId, sessionId: pendingSessionId, message: error.message });
|
|
9288
9391
|
throw error;
|
|
9289
9392
|
}
|
|
9393
|
+
await sendSessionEvent(openCodeClient, { sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: config.basePath });
|
|
9290
9394
|
const nextMetadata = clearClickUpPendingSessionMetadata(setClickUpSessionMetadata(pendingMetadata, config.routing.metadataKey, pendingSessionId), config.routing.metadataKey);
|
|
9291
9395
|
await clickupClient.updateTaskMetadata({ taskId, fieldId: config.routing.metadataFieldId, value: nextMetadata });
|
|
9292
|
-
await sendSessionEvent(openCodeClient, { sessionId: pendingSessionId, agent: config.routing.targetAgent, text: prompt, directory: config.basePath });
|
|
9293
9396
|
const { [taskId]: _completedPending, ...remainingPending } = stateToPersist.pendingSessions || {};
|
|
9294
9397
|
stateToPersist = { ...stateToPersist, pendingSessions: remainingPending };
|
|
9295
9398
|
return finish({ ok: true, action: "created_session", taskId, sessionId: pendingSessionId, eventKey });
|
|
@@ -9316,25 +9419,36 @@ function clickUpWebhookExpectedPath(config) {
|
|
|
9316
9419
|
return "/";
|
|
9317
9420
|
}
|
|
9318
9421
|
}
|
|
9319
|
-
async function handleClickUpWebhookRequest({ method = "POST", url = null, headers = {}, rawBody = "", config, state, worktree, clickupClient, openCodeClient, saveState } = {}) {
|
|
9320
|
-
|
|
9321
|
-
|
|
9322
|
-
|
|
9323
|
-
|
|
9324
|
-
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
if (bodyBytes > maxBodyBytes) return { ok: false, status: 413, reason: "body_too_large" };
|
|
9328
|
-
const signature = headers["x-signature"] || headers["X-Signature"];
|
|
9329
|
-
if (!verifyClickUpSignature(rawBody, signature, state?.secret)) return { ok: false, status: 401, reason: "invalid_signature" };
|
|
9330
|
-
let payload;
|
|
9422
|
+
async function handleClickUpWebhookRequest({ method = "POST", url = null, headers = {}, rawBody = "", config, state, worktree, clickupClient, openCodeClient, saveState, now = () => /* @__PURE__ */ new Date() } = {}) {
|
|
9423
|
+
let payload = null;
|
|
9424
|
+
let handled = null;
|
|
9425
|
+
const finish = (result) => {
|
|
9426
|
+
handled = result;
|
|
9427
|
+
writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, payload, at: now() });
|
|
9428
|
+
return result;
|
|
9429
|
+
};
|
|
9331
9430
|
try {
|
|
9332
|
-
|
|
9333
|
-
|
|
9334
|
-
|
|
9431
|
+
if (method !== "POST") return finish({ ok: false, status: 405, reason: "method_not_allowed" });
|
|
9432
|
+
if (url !== null) {
|
|
9433
|
+
const requestPath = new URL(String(url), config?.webhook?.publicUrl || "http://localhost").pathname;
|
|
9434
|
+
if (requestPath !== clickUpWebhookExpectedPath(config)) return finish({ ok: false, status: 404, reason: "wrong_path" });
|
|
9435
|
+
}
|
|
9436
|
+
const maxBodyBytes = Number(config?.webhook?.maxBodyBytes || CLICKUP_WEBHOOK_MAX_BODY_BYTES);
|
|
9437
|
+
const bodyBytes = Buffer.isBuffer(rawBody) ? rawBody.length : Buffer.byteLength(String(rawBody || ""));
|
|
9438
|
+
if (bodyBytes > maxBodyBytes) return finish({ ok: false, status: 413, reason: "body_too_large" });
|
|
9439
|
+
const signature = headers["x-signature"] || headers["X-Signature"];
|
|
9440
|
+
if (!verifyClickUpSignature(rawBody, signature, state?.secret)) return finish({ ok: false, status: 401, reason: "invalid_signature" });
|
|
9441
|
+
try {
|
|
9442
|
+
payload = JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody));
|
|
9443
|
+
} catch {
|
|
9444
|
+
return finish({ ok: false, status: 400, reason: "invalid_json" });
|
|
9445
|
+
}
|
|
9446
|
+
const result = await routeClickUpWebhookEvent({ payload, config, state, worktree, clickupClient, openCodeClient, saveState });
|
|
9447
|
+
return finish({ ok: result.ok, status: result.ok ? 200 : 422, result });
|
|
9448
|
+
} catch (error) {
|
|
9449
|
+
writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, error, payload, at: now() });
|
|
9450
|
+
throw error;
|
|
9335
9451
|
}
|
|
9336
|
-
const result = await routeClickUpWebhookEvent({ payload, config, state, worktree, clickupClient, openCodeClient, saveState });
|
|
9337
|
-
return { ok: result.ok, status: result.ok ? 200 : 422, result };
|
|
9338
9452
|
}
|
|
9339
9453
|
function clickUpListenerKey(config) {
|
|
9340
9454
|
return `${config?.webhook?.bindHost || "127.0.0.1"}:${config?.webhook?.bindPort || 0}`;
|
|
@@ -11026,7 +11140,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
11026
11140
|
}
|
|
11027
11141
|
};
|
|
11028
11142
|
}
|
|
11029
|
-
OptimaPlugin.__internals = { buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentMentionsProductManager, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, readClickUpWebhookState, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
11143
|
+
OptimaPlugin.__internals = { buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, readClickUpWebhookState, resolveOptimaPluginOptions, resolveSecretReference, routeClickUpWebhookEvent, sendOpenCodeSessionEvent, startClickUpWebhookListener, validateClickUpWebhookState, verifyClickUpSignature, writeClickUpWebhookState, decideClickUpStatusAction, deriveClickUpBranchName, deriveClickUpPendingSubtaskBranch, deriveClickUpPrTarget, deriveClickUpWorktree, determineClickUpMergeAuthority, ensureOptimaGitignoreRules, explicitSafeInputWorktree, finalApprovalAssignees, formatValidationResult, isIgnoredClickUpTaskType, isOptimaPluginPackageWorktree, isSafeWritableDirectory, loadHumansRegistry, mergeClickUpAgentMetadata, mergeClickUpSessionMetadata, migrateLegacyOptimaLayout, normalizeAgentMetadataJson, normalizeClickUpStatus, normalizeClickUpTaskType, normalizePromptResponseParts, normalizeWorkflowTaskPath, parseClickUpSubtasksMarkdown, parseHumansRegistry, parseMarkdownArtifact, parseMarkdownSections, preEstimateClickUpWork, readMarkdownArtifact, resolveHumanRoles, resolveSafeWorktree, safeWorktreeOrFailure, stripRawLogSections, validateMainWorkspaceBranchSafety, workflowFinalMessageFromPromptResponse };
|
|
11030
11144
|
|
|
11031
11145
|
// src/sanitize_cli.js
|
|
11032
11146
|
var { migrateLegacyOptimaLayout: migrateLegacyOptimaLayout2 } = OptimaPlugin.__internals;
|