@cfio/cohort-sync 0.34.1 → 0.34.2
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 +172 -2
- package/dist/openclaw.plugin.json +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14119,7 +14119,7 @@ function dumpEvent(event) {
|
|
|
14119
14119
|
function positiveNumber(value) {
|
|
14120
14120
|
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
14121
14121
|
}
|
|
14122
|
-
var PLUGIN_VERSION = true ? "0.34.
|
|
14122
|
+
var PLUGIN_VERSION = true ? "0.34.2" : "unknown";
|
|
14123
14123
|
function resolveGatewayToken(api) {
|
|
14124
14124
|
const token2 = api.config?.gateway?.auth?.token;
|
|
14125
14125
|
return typeof token2 === "string" ? token2 : null;
|
|
@@ -15212,9 +15212,87 @@ function getStorageIdFromUploadResponse(body) {
|
|
|
15212
15212
|
function formatMetric(metric) {
|
|
15213
15213
|
return `${metric.current}/${metric.target} ${metric.unit}`;
|
|
15214
15214
|
}
|
|
15215
|
+
function isRecord2(value) {
|
|
15216
|
+
return typeof value === "object" && value !== null;
|
|
15217
|
+
}
|
|
15215
15218
|
function redactSecrets(text) {
|
|
15216
15219
|
return text.replace(/ch_(?:live|test)_[A-Za-z0-9_-]+/g, "[redacted]");
|
|
15217
15220
|
}
|
|
15221
|
+
function isTaskRelationSummary(value) {
|
|
15222
|
+
if (!isRecord2(value) || !isRecord2(value.task)) return false;
|
|
15223
|
+
return typeof value.id === "string" && (value.type === "blocks" || value.type === "related" || value.type === "duplicate_of") && (value.direction === "incoming" || value.direction === "outgoing") && (value.task.taskNumber === void 0 || typeof value.task.taskNumber === "number") && (value.task.title === void 0 || typeof value.task.title === "string") && typeof value.task.status === "string" && (typeof value.task.assignedTo === "string" || value.task.assignedTo === null);
|
|
15224
|
+
}
|
|
15225
|
+
function relationTaskLabel(relation) {
|
|
15226
|
+
const id = relation.task.taskNumber !== void 0 ? `#${relation.task.taskNumber}` : relation.task.title ?? "untitled task";
|
|
15227
|
+
return `${id} (${relation.task.status}, ${relation.task.assignedTo ?? "unassigned"})`;
|
|
15228
|
+
}
|
|
15229
|
+
function formatTaskRelationsSummary(relations) {
|
|
15230
|
+
if (!Array.isArray(relations)) return [];
|
|
15231
|
+
const rows = relations.filter(isTaskRelationSummary);
|
|
15232
|
+
if (rows.length === 0) return [];
|
|
15233
|
+
const groups = [
|
|
15234
|
+
{ label: "Blocked by", rows: rows.filter((r) => r.type === "blocks" && r.direction === "incoming") },
|
|
15235
|
+
{ label: "Blocks", rows: rows.filter((r) => r.type === "blocks" && r.direction === "outgoing") },
|
|
15236
|
+
{ label: "Related", rows: rows.filter((r) => r.type === "related") },
|
|
15237
|
+
{ label: "Duplicate of", rows: rows.filter((r) => r.type === "duplicate_of" && r.direction === "outgoing") },
|
|
15238
|
+
{ label: "Duplicated by", rows: rows.filter((r) => r.type === "duplicate_of" && r.direction === "incoming") }
|
|
15239
|
+
];
|
|
15240
|
+
return groups.filter((group) => group.rows.length > 0).map((group) => `${group.label}: ${group.rows.map(relationTaskLabel).join(", ")}`);
|
|
15241
|
+
}
|
|
15242
|
+
function blockerLine(blocker) {
|
|
15243
|
+
const id = blocker.taskNumber !== void 0 ? `#${blocker.taskNumber}` : "Unnumbered task";
|
|
15244
|
+
return `${id} ${blocker.title} (${blocker.status}, ${blocker.assignedTo ?? "unassigned"})`;
|
|
15245
|
+
}
|
|
15246
|
+
function getTaskBlockedErrorData(error) {
|
|
15247
|
+
if (!isRecord2(error) || !isRecord2(error.data)) return null;
|
|
15248
|
+
const data = error.data;
|
|
15249
|
+
if (data.code !== "CONFLICT" || data.subcode !== "TASK_BLOCKED" || typeof data.message !== "string") {
|
|
15250
|
+
return null;
|
|
15251
|
+
}
|
|
15252
|
+
const blockers = Array.isArray(data.blockers) ? data.blockers.flatMap((blocker) => {
|
|
15253
|
+
if (!isRecord2(blocker) || typeof blocker.title !== "string" || typeof blocker.status !== "string") {
|
|
15254
|
+
return [];
|
|
15255
|
+
}
|
|
15256
|
+
const assignedTo = typeof blocker.assignedTo === "string" ? blocker.assignedTo : null;
|
|
15257
|
+
return [{
|
|
15258
|
+
...typeof blocker.taskNumber === "number" ? { taskNumber: blocker.taskNumber } : {},
|
|
15259
|
+
title: blocker.title,
|
|
15260
|
+
status: blocker.status,
|
|
15261
|
+
assignedTo
|
|
15262
|
+
}];
|
|
15263
|
+
}) : void 0;
|
|
15264
|
+
return {
|
|
15265
|
+
code: "CONFLICT",
|
|
15266
|
+
subcode: "TASK_BLOCKED",
|
|
15267
|
+
message: data.message,
|
|
15268
|
+
...blockers ? { blockers } : {}
|
|
15269
|
+
};
|
|
15270
|
+
}
|
|
15271
|
+
function formatTaskBlockedError(error) {
|
|
15272
|
+
const data = getTaskBlockedErrorData(error);
|
|
15273
|
+
if (!data) return null;
|
|
15274
|
+
const lines = [data.message];
|
|
15275
|
+
if (data.blockers && data.blockers.length > 0) {
|
|
15276
|
+
lines.push("", "Open blockers:");
|
|
15277
|
+
for (const blocker of data.blockers) {
|
|
15278
|
+
lines.push(`- ${blockerLine(blocker)}`);
|
|
15279
|
+
}
|
|
15280
|
+
}
|
|
15281
|
+
return lines.join("\n");
|
|
15282
|
+
}
|
|
15283
|
+
function relationMatchesVerb(relation, relationVerb, otherTaskNumber) {
|
|
15284
|
+
if (relation.task.taskNumber !== otherTaskNumber) return false;
|
|
15285
|
+
switch (relationVerb) {
|
|
15286
|
+
case "blocked_by":
|
|
15287
|
+
return relation.type === "blocks" && relation.direction === "incoming";
|
|
15288
|
+
case "blocks":
|
|
15289
|
+
return relation.type === "blocks" && relation.direction === "outgoing";
|
|
15290
|
+
case "related":
|
|
15291
|
+
return relation.type === "related";
|
|
15292
|
+
case "duplicate_of":
|
|
15293
|
+
return relation.type === "duplicate_of" && relation.direction === "outgoing";
|
|
15294
|
+
}
|
|
15295
|
+
}
|
|
15218
15296
|
async function safeHttpError(response) {
|
|
15219
15297
|
try {
|
|
15220
15298
|
const body = await response.text();
|
|
@@ -15842,10 +15920,17 @@ ${body}`,
|
|
|
15842
15920
|
`**Assigned to:** ${task.assignedTo ?? "unassigned"}`,
|
|
15843
15921
|
`**Created:** ${task.createdAt}`
|
|
15844
15922
|
];
|
|
15923
|
+
if (task.blocked === true) {
|
|
15924
|
+
lines.push("**Blocked:** yes");
|
|
15925
|
+
}
|
|
15845
15926
|
const contextBlock = renderTaskContext(task.context);
|
|
15846
15927
|
if (contextBlock) {
|
|
15847
15928
|
lines.push("", contextBlock);
|
|
15848
15929
|
}
|
|
15930
|
+
const relationsSummary = formatTaskRelationsSummary(task.relations);
|
|
15931
|
+
if (relationsSummary.length > 0) {
|
|
15932
|
+
lines.push("", "## Relations", ...relationsSummary);
|
|
15933
|
+
}
|
|
15849
15934
|
lines.push("", "## Description", task.description || "(no description)");
|
|
15850
15935
|
if (params.include_comments !== false) {
|
|
15851
15936
|
const limit = params.comment_limit ?? 10;
|
|
@@ -15961,7 +16046,92 @@ ${renderGoal(goal)}`, goal);
|
|
|
15961
16046
|
});
|
|
15962
16047
|
return textResult(`Task #${params.task_number} transitioned to "${params.status}".`);
|
|
15963
16048
|
} catch (err) {
|
|
15964
|
-
|
|
16049
|
+
const blockedMessage = formatTaskBlockedError(err);
|
|
16050
|
+
if (blockedMessage) {
|
|
16051
|
+
return textResult(blockedMessage, getTaskBlockedErrorData(err));
|
|
16052
|
+
}
|
|
16053
|
+
const appMessage = getConvexAppErrorMessage(err);
|
|
16054
|
+
return textResult(`Failed to transition task #${params.task_number}: ${appMessage ?? (err instanceof Error ? err.message : String(err))}`);
|
|
16055
|
+
}
|
|
16056
|
+
}
|
|
16057
|
+
};
|
|
16058
|
+
});
|
|
16059
|
+
api.registerTool(() => {
|
|
16060
|
+
return {
|
|
16061
|
+
name: "cohort_relate",
|
|
16062
|
+
label: "cohort_relate",
|
|
16063
|
+
description: "Create or remove Cohort task relations by task number. This is THE way to decompose work into ordered blockers: create blocker tasks, then relate them with blocked_by or blocks so Cohort can sequence handoffs.",
|
|
16064
|
+
parameters: Type.Object({
|
|
16065
|
+
task_number: Type.Number({ description: "Primary task number (e.g. 214)." }),
|
|
16066
|
+
relation: Type.Union([
|
|
16067
|
+
Type.Literal("blocked_by"),
|
|
16068
|
+
Type.Literal("blocks"),
|
|
16069
|
+
Type.Literal("related"),
|
|
16070
|
+
Type.Literal("duplicate_of")
|
|
16071
|
+
], { description: "Relation from task_number to other_task_number." }),
|
|
16072
|
+
other_task_number: Type.Number({ description: "Other task number to relate to the primary task." }),
|
|
16073
|
+
remove: Type.Optional(Type.Boolean({ description: "Set true to remove the matching relation instead of creating it." }))
|
|
16074
|
+
}),
|
|
16075
|
+
async execute(_toolCallId, params) {
|
|
16076
|
+
const rt = getToolRuntime();
|
|
16077
|
+
if (!rt.isReady) {
|
|
16078
|
+
return textResult("cohort_relate is not ready yet \u2014 the plugin is still starting up.");
|
|
16079
|
+
}
|
|
16080
|
+
if (params.task_number === params.other_task_number) {
|
|
16081
|
+
return textResult("Cannot relate a task to itself.");
|
|
16082
|
+
}
|
|
16083
|
+
try {
|
|
16084
|
+
if (!params.remove) {
|
|
16085
|
+
const response = await fetch(`${rt.apiUrl}/api/v1/tasks/${params.task_number}/relations`, {
|
|
16086
|
+
method: "POST",
|
|
16087
|
+
headers: {
|
|
16088
|
+
"Authorization": `Bearer ${rt.apiKey}`,
|
|
16089
|
+
"Content-Type": "application/json"
|
|
16090
|
+
},
|
|
16091
|
+
body: JSON.stringify({
|
|
16092
|
+
type: params.relation,
|
|
16093
|
+
taskNumber: params.other_task_number
|
|
16094
|
+
}),
|
|
16095
|
+
signal: AbortSignal.timeout(1e4)
|
|
16096
|
+
});
|
|
16097
|
+
if (!response.ok) {
|
|
16098
|
+
const message = await safeHttpError(response);
|
|
16099
|
+
return textResult(`Failed to relate task #${params.task_number}: ${response.status}${message}`);
|
|
16100
|
+
}
|
|
16101
|
+
const relation2 = await response.json();
|
|
16102
|
+
return textResult(`Related task #${params.task_number} ${params.relation} #${params.other_task_number}.`, relation2);
|
|
16103
|
+
}
|
|
16104
|
+
const taskResponse = await fetch(`${rt.apiUrl}/api/v1/tasks/${params.task_number}`, {
|
|
16105
|
+
headers: { "Authorization": `Bearer ${rt.apiKey}` },
|
|
16106
|
+
signal: AbortSignal.timeout(1e4)
|
|
16107
|
+
});
|
|
16108
|
+
if (!taskResponse.ok) {
|
|
16109
|
+
const message = await safeHttpError(taskResponse);
|
|
16110
|
+
return textResult(`Failed to inspect task #${params.task_number}: ${taskResponse.status}${message}`);
|
|
16111
|
+
}
|
|
16112
|
+
const task = await taskResponse.json();
|
|
16113
|
+
const relations = Array.isArray(task.relations) ? task.relations.filter(isTaskRelationSummary) : [];
|
|
16114
|
+
const relation = relations.find(
|
|
16115
|
+
(candidate) => relationMatchesVerb(candidate, params.relation, params.other_task_number)
|
|
16116
|
+
);
|
|
16117
|
+
if (!relation) {
|
|
16118
|
+
return textResult(`No ${params.relation} relation found between task #${params.task_number} and #${params.other_task_number}.`);
|
|
16119
|
+
}
|
|
16120
|
+
const deleteResponse = await fetch(
|
|
16121
|
+
`${rt.apiUrl}/api/v1/tasks/${params.task_number}/relations/${encodeURIComponent(relation.id)}`,
|
|
16122
|
+
{
|
|
16123
|
+
method: "DELETE",
|
|
16124
|
+
headers: { "Authorization": `Bearer ${rt.apiKey}` },
|
|
16125
|
+
signal: AbortSignal.timeout(1e4)
|
|
16126
|
+
}
|
|
16127
|
+
);
|
|
16128
|
+
if (!deleteResponse.ok) {
|
|
16129
|
+
const message = await safeHttpError(deleteResponse);
|
|
16130
|
+
return textResult(`Failed to remove relation from task #${params.task_number}: ${deleteResponse.status}${message}`);
|
|
16131
|
+
}
|
|
16132
|
+
return textResult(`Removed ${params.relation} relation between task #${params.task_number} and #${params.other_task_number}.`);
|
|
16133
|
+
} catch (err) {
|
|
16134
|
+
return textResult(`Failed to relate task #${params.task_number}: ${err instanceof Error ? redactSecrets(err.message) : "Unknown error"}`);
|
|
15965
16135
|
}
|
|
15966
16136
|
}
|
|
15967
16137
|
};
|
package/dist/package.json
CHANGED
package/package.json
CHANGED