@defend-tech/opencode-optima 0.1.11 → 0.1.13
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 +125 -20
- package/dist/sanitize_cli.js +125 -20
- 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(),
|
|
@@ -9035,7 +9054,13 @@ async function ensureClickUpWebhookSubscription({ validation, worktree, clickupC
|
|
|
9035
9054
|
});
|
|
9036
9055
|
const createdState = normalizeClickUpWebhookApiResponse(created, config);
|
|
9037
9056
|
const stateForValidation = { ...createdState, recentEventKeys: existing.recentEventKeys || [] };
|
|
9038
|
-
|
|
9057
|
+
let createdValidation = await validateClickUpWebhookState(stateForValidation, config, clickupClient?.listWebhooks ? clickupClient : null);
|
|
9058
|
+
if (!createdValidation.valid && createdValidation.reason === "remote_state_missing") {
|
|
9059
|
+
const localCreatedValidation = await validateClickUpWebhookState(stateForValidation, config, null);
|
|
9060
|
+
if (localCreatedValidation.valid) {
|
|
9061
|
+
createdValidation = { ...localCreatedValidation, mode: "created_pending_remote_validation", limitation: "ClickUp webhook created from API response; remote list validation had not propagated yet." };
|
|
9062
|
+
}
|
|
9063
|
+
}
|
|
9039
9064
|
if (!createdValidation.valid) {
|
|
9040
9065
|
if (stateForValidation.webhookId && clickupClient?.deleteWebhook) {
|
|
9041
9066
|
try {
|
|
@@ -9046,7 +9071,8 @@ async function ensureClickUpWebhookSubscription({ validation, worktree, clickupC
|
|
|
9046
9071
|
return { active: false, valid: false, reason: "created_state_invalid", validation: createdValidation, state: stateForValidation };
|
|
9047
9072
|
}
|
|
9048
9073
|
const next = writeClickUpWebhookState(worktree, createdValidation.state || stateForValidation, config);
|
|
9049
|
-
|
|
9074
|
+
const mode = createdValidation.mode === "created_pending_remote_validation" ? "created_pending_remote_validation" : clickupClient?.listWebhooks ? "created_remote_validated" : "created";
|
|
9075
|
+
return { active: true, valid: true, mode, limitation: createdValidation.limitation, state: next };
|
|
9050
9076
|
}
|
|
9051
9077
|
function verifyClickUpSignature(rawBody, signatureHeader, secret) {
|
|
9052
9078
|
const signature = String(signatureHeader || "").replace(/^sha256=/i, "").trim();
|
|
@@ -9205,6 +9231,74 @@ function appendClickUpWebhookLocalLog(worktree, entry) {
|
|
|
9205
9231
|
fs2.appendFileSync(logPath, `${JSON.stringify(safeEntry)}
|
|
9206
9232
|
`, "utf8");
|
|
9207
9233
|
}
|
|
9234
|
+
function redactClickUpWebhookAuditValue(value, secretValues = []) {
|
|
9235
|
+
if (typeof value === "string") {
|
|
9236
|
+
return secretValues.reduce((next, secret) => secret ? next.split(secret).join(CLICKUP_WEBHOOK_REDACTED) : next, value);
|
|
9237
|
+
}
|
|
9238
|
+
if (Array.isArray(value)) return value.map((item) => redactClickUpWebhookAuditValue(item, secretValues));
|
|
9239
|
+
if (!isPlainObject(value)) return value;
|
|
9240
|
+
return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => {
|
|
9241
|
+
const normalizedKey = normalizeLooseToken(key);
|
|
9242
|
+
const secretKey = normalizedKey.includes("signature") || normalizedKey.includes("authorization") || normalizedKey.includes("cookie") || normalizedKey.includes("token") || normalizedKey.includes("secret") || normalizedKey.includes("api-key") || normalizedKey.includes("apikey");
|
|
9243
|
+
return [key, secretKey ? CLICKUP_WEBHOOK_REDACTED : redactClickUpWebhookAuditValue(entryValue, secretValues)];
|
|
9244
|
+
}));
|
|
9245
|
+
}
|
|
9246
|
+
function buildClickUpWebhookAuditSummary({ method, url, config, handled, error, payload, requestFile, at = /* @__PURE__ */ new Date() } = {}) {
|
|
9247
|
+
const result = handled?.result || {};
|
|
9248
|
+
const summary = {
|
|
9249
|
+
at: at.toISOString(),
|
|
9250
|
+
method,
|
|
9251
|
+
path: url === null ? null : new URL(String(url || "/"), config?.webhook?.publicUrl || "http://localhost").pathname,
|
|
9252
|
+
status: handled?.status || 500,
|
|
9253
|
+
ok: Boolean(handled?.ok),
|
|
9254
|
+
reason: handled?.reason || result.reason || (error ? "handler_error" : void 0),
|
|
9255
|
+
action: result.action,
|
|
9256
|
+
event: payload ? clickUpEventType(payload) : void 0,
|
|
9257
|
+
task: result.taskId || clickUpTaskIdFromPayload(payload || {}),
|
|
9258
|
+
comment: clickUpCommentFromPayload(payload || {})?.id || payload?.comment_id || payload?.commentId,
|
|
9259
|
+
session: result.sessionId,
|
|
9260
|
+
base_path: config?.basePath || "",
|
|
9261
|
+
request_file: requestFile || void 0
|
|
9262
|
+
};
|
|
9263
|
+
return Object.fromEntries(Object.entries(summary).filter(([, value]) => value !== void 0 && value !== ""));
|
|
9264
|
+
}
|
|
9265
|
+
function writeClickUpWebhookAuditLog({ method, url, headers = {}, rawBody = "", config, state = {}, handled = null, error = null, payload = null, at = /* @__PURE__ */ new Date() } = {}) {
|
|
9266
|
+
try {
|
|
9267
|
+
const level = normalizeClickUpWebhookLogLevel(config?.log);
|
|
9268
|
+
const failed = Boolean(error || !handled?.ok || (handled?.status || 500) >= 400);
|
|
9269
|
+
if (level === "error" && !failed) return;
|
|
9270
|
+
const logDir = clickUpWebhookAuditLogDir();
|
|
9271
|
+
fs2.mkdirSync(logDir, { recursive: true });
|
|
9272
|
+
const secretValues = [resolveSecretReference(config?.apiToken), state?.secret, config?.webhook?.secret].filter(Boolean);
|
|
9273
|
+
let requestFile;
|
|
9274
|
+
if (level === "verbose") {
|
|
9275
|
+
const absoluteRequestFile = clickUpWebhookRequestFilePath(at, logDir);
|
|
9276
|
+
fs2.mkdirSync(path2.dirname(absoluteRequestFile), { recursive: true });
|
|
9277
|
+
requestFile = path2.relative(logDir, absoluteRequestFile).split(path2.sep).join("/");
|
|
9278
|
+
const parsedBody = payload || (() => {
|
|
9279
|
+
try {
|
|
9280
|
+
return JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || ""));
|
|
9281
|
+
} catch {
|
|
9282
|
+
return void 0;
|
|
9283
|
+
}
|
|
9284
|
+
})();
|
|
9285
|
+
const requestArtifact = redactClickUpWebhookAuditValue({
|
|
9286
|
+
at: at.toISOString(),
|
|
9287
|
+
method,
|
|
9288
|
+
url,
|
|
9289
|
+
path: url === null ? null : new URL(String(url || "/"), config?.webhook?.publicUrl || "http://localhost").pathname,
|
|
9290
|
+
headers,
|
|
9291
|
+
body: parsedBody === void 0 ? Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || "") : parsedBody
|
|
9292
|
+
}, secretValues);
|
|
9293
|
+
fs2.writeFileSync(absoluteRequestFile, `${JSON.stringify(requestArtifact, null, 2)}
|
|
9294
|
+
`, "utf8");
|
|
9295
|
+
}
|
|
9296
|
+
const summary = redactClickUpWebhookAuditValue(buildClickUpWebhookAuditSummary({ method, url, config, handled, error, payload, requestFile, at }), secretValues);
|
|
9297
|
+
fs2.appendFileSync(clickUpWebhookAuditLogPath(at, logDir), `${JSON.stringify(summary)}
|
|
9298
|
+
`, "utf8");
|
|
9299
|
+
} catch {
|
|
9300
|
+
}
|
|
9301
|
+
}
|
|
9208
9302
|
async function withClickUpTaskRouteLock(taskId, operation) {
|
|
9209
9303
|
const key = String(taskId || "");
|
|
9210
9304
|
if (!key) return operation();
|
|
@@ -9302,25 +9396,36 @@ function clickUpWebhookExpectedPath(config) {
|
|
|
9302
9396
|
return "/";
|
|
9303
9397
|
}
|
|
9304
9398
|
}
|
|
9305
|
-
async function handleClickUpWebhookRequest({ method = "POST", url = null, headers = {}, rawBody = "", config, state, worktree, clickupClient, openCodeClient, saveState } = {}) {
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
|
|
9309
|
-
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
if (bodyBytes > maxBodyBytes) return { ok: false, status: 413, reason: "body_too_large" };
|
|
9314
|
-
const signature = headers["x-signature"] || headers["X-Signature"];
|
|
9315
|
-
if (!verifyClickUpSignature(rawBody, signature, state?.secret)) return { ok: false, status: 401, reason: "invalid_signature" };
|
|
9316
|
-
let payload;
|
|
9399
|
+
async function handleClickUpWebhookRequest({ method = "POST", url = null, headers = {}, rawBody = "", config, state, worktree, clickupClient, openCodeClient, saveState, now = () => /* @__PURE__ */ new Date() } = {}) {
|
|
9400
|
+
let payload = null;
|
|
9401
|
+
let handled = null;
|
|
9402
|
+
const finish = (result) => {
|
|
9403
|
+
handled = result;
|
|
9404
|
+
writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, payload, at: now() });
|
|
9405
|
+
return result;
|
|
9406
|
+
};
|
|
9317
9407
|
try {
|
|
9318
|
-
|
|
9319
|
-
|
|
9320
|
-
|
|
9408
|
+
if (method !== "POST") return finish({ ok: false, status: 405, reason: "method_not_allowed" });
|
|
9409
|
+
if (url !== null) {
|
|
9410
|
+
const requestPath = new URL(String(url), config?.webhook?.publicUrl || "http://localhost").pathname;
|
|
9411
|
+
if (requestPath !== clickUpWebhookExpectedPath(config)) return finish({ ok: false, status: 404, reason: "wrong_path" });
|
|
9412
|
+
}
|
|
9413
|
+
const maxBodyBytes = Number(config?.webhook?.maxBodyBytes || CLICKUP_WEBHOOK_MAX_BODY_BYTES);
|
|
9414
|
+
const bodyBytes = Buffer.isBuffer(rawBody) ? rawBody.length : Buffer.byteLength(String(rawBody || ""));
|
|
9415
|
+
if (bodyBytes > maxBodyBytes) return finish({ ok: false, status: 413, reason: "body_too_large" });
|
|
9416
|
+
const signature = headers["x-signature"] || headers["X-Signature"];
|
|
9417
|
+
if (!verifyClickUpSignature(rawBody, signature, state?.secret)) return finish({ ok: false, status: 401, reason: "invalid_signature" });
|
|
9418
|
+
try {
|
|
9419
|
+
payload = JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody));
|
|
9420
|
+
} catch {
|
|
9421
|
+
return finish({ ok: false, status: 400, reason: "invalid_json" });
|
|
9422
|
+
}
|
|
9423
|
+
const result = await routeClickUpWebhookEvent({ payload, config, state, worktree, clickupClient, openCodeClient, saveState });
|
|
9424
|
+
return finish({ ok: result.ok, status: result.ok ? 200 : 422, result });
|
|
9425
|
+
} catch (error) {
|
|
9426
|
+
writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, error, payload, at: now() });
|
|
9427
|
+
throw error;
|
|
9321
9428
|
}
|
|
9322
|
-
const result = await routeClickUpWebhookEvent({ payload, config, state, worktree, clickupClient, openCodeClient, saveState });
|
|
9323
|
-
return { ok: result.ok, status: result.ok ? 200 : 422, result };
|
|
9324
9429
|
}
|
|
9325
9430
|
function clickUpListenerKey(config) {
|
|
9326
9431
|
return `${config?.webhook?.bindHost || "127.0.0.1"}:${config?.webhook?.bindPort || 0}`;
|
|
@@ -11012,7 +11117,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
11012
11117
|
}
|
|
11013
11118
|
};
|
|
11014
11119
|
}
|
|
11015
|
-
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 };
|
|
11120
|
+
OptimaPlugin.__internals = { buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, 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 };
|
|
11016
11121
|
export {
|
|
11017
11122
|
OptimaPlugin as default
|
|
11018
11123
|
};
|
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(),
|
|
@@ -9042,7 +9061,13 @@ async function ensureClickUpWebhookSubscription({ validation, worktree, clickupC
|
|
|
9042
9061
|
});
|
|
9043
9062
|
const createdState = normalizeClickUpWebhookApiResponse(created, config);
|
|
9044
9063
|
const stateForValidation = { ...createdState, recentEventKeys: existing.recentEventKeys || [] };
|
|
9045
|
-
|
|
9064
|
+
let createdValidation = await validateClickUpWebhookState(stateForValidation, config, clickupClient?.listWebhooks ? clickupClient : null);
|
|
9065
|
+
if (!createdValidation.valid && createdValidation.reason === "remote_state_missing") {
|
|
9066
|
+
const localCreatedValidation = await validateClickUpWebhookState(stateForValidation, config, null);
|
|
9067
|
+
if (localCreatedValidation.valid) {
|
|
9068
|
+
createdValidation = { ...localCreatedValidation, mode: "created_pending_remote_validation", limitation: "ClickUp webhook created from API response; remote list validation had not propagated yet." };
|
|
9069
|
+
}
|
|
9070
|
+
}
|
|
9046
9071
|
if (!createdValidation.valid) {
|
|
9047
9072
|
if (stateForValidation.webhookId && clickupClient?.deleteWebhook) {
|
|
9048
9073
|
try {
|
|
@@ -9053,7 +9078,8 @@ async function ensureClickUpWebhookSubscription({ validation, worktree, clickupC
|
|
|
9053
9078
|
return { active: false, valid: false, reason: "created_state_invalid", validation: createdValidation, state: stateForValidation };
|
|
9054
9079
|
}
|
|
9055
9080
|
const next = writeClickUpWebhookState(worktree, createdValidation.state || stateForValidation, config);
|
|
9056
|
-
|
|
9081
|
+
const mode = createdValidation.mode === "created_pending_remote_validation" ? "created_pending_remote_validation" : clickupClient?.listWebhooks ? "created_remote_validated" : "created";
|
|
9082
|
+
return { active: true, valid: true, mode, limitation: createdValidation.limitation, state: next };
|
|
9057
9083
|
}
|
|
9058
9084
|
function verifyClickUpSignature(rawBody, signatureHeader, secret) {
|
|
9059
9085
|
const signature = String(signatureHeader || "").replace(/^sha256=/i, "").trim();
|
|
@@ -9212,6 +9238,74 @@ function appendClickUpWebhookLocalLog(worktree, entry) {
|
|
|
9212
9238
|
fs2.appendFileSync(logPath, `${JSON.stringify(safeEntry)}
|
|
9213
9239
|
`, "utf8");
|
|
9214
9240
|
}
|
|
9241
|
+
function redactClickUpWebhookAuditValue(value, secretValues = []) {
|
|
9242
|
+
if (typeof value === "string") {
|
|
9243
|
+
return secretValues.reduce((next, secret) => secret ? next.split(secret).join(CLICKUP_WEBHOOK_REDACTED) : next, value);
|
|
9244
|
+
}
|
|
9245
|
+
if (Array.isArray(value)) return value.map((item) => redactClickUpWebhookAuditValue(item, secretValues));
|
|
9246
|
+
if (!isPlainObject(value)) return value;
|
|
9247
|
+
return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => {
|
|
9248
|
+
const normalizedKey = normalizeLooseToken(key);
|
|
9249
|
+
const secretKey = normalizedKey.includes("signature") || normalizedKey.includes("authorization") || normalizedKey.includes("cookie") || normalizedKey.includes("token") || normalizedKey.includes("secret") || normalizedKey.includes("api-key") || normalizedKey.includes("apikey");
|
|
9250
|
+
return [key, secretKey ? CLICKUP_WEBHOOK_REDACTED : redactClickUpWebhookAuditValue(entryValue, secretValues)];
|
|
9251
|
+
}));
|
|
9252
|
+
}
|
|
9253
|
+
function buildClickUpWebhookAuditSummary({ method, url, config, handled, error, payload, requestFile, at = /* @__PURE__ */ new Date() } = {}) {
|
|
9254
|
+
const result = handled?.result || {};
|
|
9255
|
+
const summary = {
|
|
9256
|
+
at: at.toISOString(),
|
|
9257
|
+
method,
|
|
9258
|
+
path: url === null ? null : new URL(String(url || "/"), config?.webhook?.publicUrl || "http://localhost").pathname,
|
|
9259
|
+
status: handled?.status || 500,
|
|
9260
|
+
ok: Boolean(handled?.ok),
|
|
9261
|
+
reason: handled?.reason || result.reason || (error ? "handler_error" : void 0),
|
|
9262
|
+
action: result.action,
|
|
9263
|
+
event: payload ? clickUpEventType(payload) : void 0,
|
|
9264
|
+
task: result.taskId || clickUpTaskIdFromPayload(payload || {}),
|
|
9265
|
+
comment: clickUpCommentFromPayload(payload || {})?.id || payload?.comment_id || payload?.commentId,
|
|
9266
|
+
session: result.sessionId,
|
|
9267
|
+
base_path: config?.basePath || "",
|
|
9268
|
+
request_file: requestFile || void 0
|
|
9269
|
+
};
|
|
9270
|
+
return Object.fromEntries(Object.entries(summary).filter(([, value]) => value !== void 0 && value !== ""));
|
|
9271
|
+
}
|
|
9272
|
+
function writeClickUpWebhookAuditLog({ method, url, headers = {}, rawBody = "", config, state = {}, handled = null, error = null, payload = null, at = /* @__PURE__ */ new Date() } = {}) {
|
|
9273
|
+
try {
|
|
9274
|
+
const level = normalizeClickUpWebhookLogLevel(config?.log);
|
|
9275
|
+
const failed = Boolean(error || !handled?.ok || (handled?.status || 500) >= 400);
|
|
9276
|
+
if (level === "error" && !failed) return;
|
|
9277
|
+
const logDir = clickUpWebhookAuditLogDir();
|
|
9278
|
+
fs2.mkdirSync(logDir, { recursive: true });
|
|
9279
|
+
const secretValues = [resolveSecretReference(config?.apiToken), state?.secret, config?.webhook?.secret].filter(Boolean);
|
|
9280
|
+
let requestFile;
|
|
9281
|
+
if (level === "verbose") {
|
|
9282
|
+
const absoluteRequestFile = clickUpWebhookRequestFilePath(at, logDir);
|
|
9283
|
+
fs2.mkdirSync(path2.dirname(absoluteRequestFile), { recursive: true });
|
|
9284
|
+
requestFile = path2.relative(logDir, absoluteRequestFile).split(path2.sep).join("/");
|
|
9285
|
+
const parsedBody = payload || (() => {
|
|
9286
|
+
try {
|
|
9287
|
+
return JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || ""));
|
|
9288
|
+
} catch {
|
|
9289
|
+
return void 0;
|
|
9290
|
+
}
|
|
9291
|
+
})();
|
|
9292
|
+
const requestArtifact = redactClickUpWebhookAuditValue({
|
|
9293
|
+
at: at.toISOString(),
|
|
9294
|
+
method,
|
|
9295
|
+
url,
|
|
9296
|
+
path: url === null ? null : new URL(String(url || "/"), config?.webhook?.publicUrl || "http://localhost").pathname,
|
|
9297
|
+
headers,
|
|
9298
|
+
body: parsedBody === void 0 ? Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody || "") : parsedBody
|
|
9299
|
+
}, secretValues);
|
|
9300
|
+
fs2.writeFileSync(absoluteRequestFile, `${JSON.stringify(requestArtifact, null, 2)}
|
|
9301
|
+
`, "utf8");
|
|
9302
|
+
}
|
|
9303
|
+
const summary = redactClickUpWebhookAuditValue(buildClickUpWebhookAuditSummary({ method, url, config, handled, error, payload, requestFile, at }), secretValues);
|
|
9304
|
+
fs2.appendFileSync(clickUpWebhookAuditLogPath(at, logDir), `${JSON.stringify(summary)}
|
|
9305
|
+
`, "utf8");
|
|
9306
|
+
} catch {
|
|
9307
|
+
}
|
|
9308
|
+
}
|
|
9215
9309
|
async function withClickUpTaskRouteLock(taskId, operation) {
|
|
9216
9310
|
const key = String(taskId || "");
|
|
9217
9311
|
if (!key) return operation();
|
|
@@ -9309,25 +9403,36 @@ function clickUpWebhookExpectedPath(config) {
|
|
|
9309
9403
|
return "/";
|
|
9310
9404
|
}
|
|
9311
9405
|
}
|
|
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;
|
|
9406
|
+
async function handleClickUpWebhookRequest({ method = "POST", url = null, headers = {}, rawBody = "", config, state, worktree, clickupClient, openCodeClient, saveState, now = () => /* @__PURE__ */ new Date() } = {}) {
|
|
9407
|
+
let payload = null;
|
|
9408
|
+
let handled = null;
|
|
9409
|
+
const finish = (result) => {
|
|
9410
|
+
handled = result;
|
|
9411
|
+
writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, payload, at: now() });
|
|
9412
|
+
return result;
|
|
9413
|
+
};
|
|
9324
9414
|
try {
|
|
9325
|
-
|
|
9326
|
-
|
|
9327
|
-
|
|
9415
|
+
if (method !== "POST") return finish({ ok: false, status: 405, reason: "method_not_allowed" });
|
|
9416
|
+
if (url !== null) {
|
|
9417
|
+
const requestPath = new URL(String(url), config?.webhook?.publicUrl || "http://localhost").pathname;
|
|
9418
|
+
if (requestPath !== clickUpWebhookExpectedPath(config)) return finish({ ok: false, status: 404, reason: "wrong_path" });
|
|
9419
|
+
}
|
|
9420
|
+
const maxBodyBytes = Number(config?.webhook?.maxBodyBytes || CLICKUP_WEBHOOK_MAX_BODY_BYTES);
|
|
9421
|
+
const bodyBytes = Buffer.isBuffer(rawBody) ? rawBody.length : Buffer.byteLength(String(rawBody || ""));
|
|
9422
|
+
if (bodyBytes > maxBodyBytes) return finish({ ok: false, status: 413, reason: "body_too_large" });
|
|
9423
|
+
const signature = headers["x-signature"] || headers["X-Signature"];
|
|
9424
|
+
if (!verifyClickUpSignature(rawBody, signature, state?.secret)) return finish({ ok: false, status: 401, reason: "invalid_signature" });
|
|
9425
|
+
try {
|
|
9426
|
+
payload = JSON.parse(Buffer.isBuffer(rawBody) ? rawBody.toString("utf8") : String(rawBody));
|
|
9427
|
+
} catch {
|
|
9428
|
+
return finish({ ok: false, status: 400, reason: "invalid_json" });
|
|
9429
|
+
}
|
|
9430
|
+
const result = await routeClickUpWebhookEvent({ payload, config, state, worktree, clickupClient, openCodeClient, saveState });
|
|
9431
|
+
return finish({ ok: result.ok, status: result.ok ? 200 : 422, result });
|
|
9432
|
+
} catch (error) {
|
|
9433
|
+
writeClickUpWebhookAuditLog({ method, url, headers, rawBody, config, state, handled, error, payload, at: now() });
|
|
9434
|
+
throw error;
|
|
9328
9435
|
}
|
|
9329
|
-
const result = await routeClickUpWebhookEvent({ payload, config, state, worktree, clickupClient, openCodeClient, saveState });
|
|
9330
|
-
return { ok: result.ok, status: result.ok ? 200 : 422, result };
|
|
9331
9436
|
}
|
|
9332
9437
|
function clickUpListenerKey(config) {
|
|
9333
9438
|
return `${config?.webhook?.bindHost || "127.0.0.1"}:${config?.webhook?.bindPort || 0}`;
|
|
@@ -11019,7 +11124,7 @@ Follow-up: use optima_prompt_workflow with session_id '${sessionId}' to check in
|
|
|
11019
11124
|
}
|
|
11020
11125
|
};
|
|
11021
11126
|
}
|
|
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 };
|
|
11127
|
+
OptimaPlugin.__internals = { buildClickUpApplyPayloadResult, buildClickUpCreateSubtasksPayload, buildClickUpStartTaskPayload, buildClickUpSummaryPayload, buildClickUpTransitionPayload, buildOptimaAgents, clickUpCommentMentionsProductManager, clickUpWebhookAuditLogDir, clickUpWebhookAuditLogPath, createClickUpApiClient, ensureClickUpWebhookSubscription, handleClickUpWebhookRequest, isClickUpWebhookStateActive, normalizeClickUpWebhookConfig, normalizeClickUpWebhookLogLevel, 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 };
|
|
11023
11128
|
|
|
11024
11129
|
// src/sanitize_cli.js
|
|
11025
11130
|
var { migrateLegacyOptimaLayout: migrateLegacyOptimaLayout2 } = OptimaPlugin.__internals;
|