openclaw-linear 0.2.0 → 0.3.0
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.d.ts.map +1 -1
- package/dist/index.js +10 -14
- package/dist/index.js.map +1 -1
- package/dist/queue-tool.d.ts +4 -0
- package/dist/queue-tool.d.ts.map +1 -0
- package/dist/queue-tool.js +42 -0
- package/dist/queue-tool.js.map +1 -0
- package/dist/work-queue.d.ts +18 -24
- package/dist/work-queue.d.ts.map +1 -1
- package/dist/work-queue.js +124 -98
- package/dist/work-queue.js.map +1 -1
- package/package.json +1 -1
- package/skills/linear-queue/SKILL.md +15 -16
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAczE,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,MAAM,CAYzE;AAqFD,wBAAgB,QAAQ,CAAC,GAAG,EAAE,iBAAiB,GAAG,IAAI,CA8ErD;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAStE;AAED,QAAA,MAAM,MAAM;;;;;;CAYX,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createWebhookHandler } from "./webhook-handler.js";
|
|
2
2
|
import { createEventRouter } from "./event-router.js";
|
|
3
|
-
import {
|
|
3
|
+
import { InboxQueue } from "./work-queue.js";
|
|
4
|
+
import { createQueueTool } from "./queue-tool.js";
|
|
4
5
|
const CHANNEL_ID = "linear";
|
|
5
6
|
const DEFAULT_DEBOUNCE_MS = 30_000;
|
|
6
7
|
const EVENT_LABELS = {
|
|
@@ -30,7 +31,7 @@ function formatActionSummary(action) {
|
|
|
30
31
|
}
|
|
31
32
|
return action.issueLabel || action.detail;
|
|
32
33
|
}
|
|
33
|
-
async function dispatchConsolidatedActions(actions, api,
|
|
34
|
+
async function dispatchConsolidatedActions(actions, api, queue) {
|
|
34
35
|
if (actions.length === 0)
|
|
35
36
|
return;
|
|
36
37
|
const core = api.runtime;
|
|
@@ -47,13 +48,13 @@ async function dispatchConsolidatedActions(actions, api, queuePath) {
|
|
|
47
48
|
});
|
|
48
49
|
// Write to queue deterministically — no LLM involved
|
|
49
50
|
const rawBody = formatConsolidatedMessage(actions);
|
|
50
|
-
const added =
|
|
51
|
+
const added = await queue.enqueue(rawBody);
|
|
51
52
|
if (added === 0) {
|
|
52
53
|
api.logger.info("[linear] All notifications deduped — skipping agent dispatch");
|
|
53
54
|
return;
|
|
54
55
|
}
|
|
55
|
-
// Agent gets a minimal notification pointing to the
|
|
56
|
-
const body = `${added} new Linear notification(s) queued.
|
|
56
|
+
// Agent gets a minimal notification pointing to the linear_queue tool
|
|
57
|
+
const body = `${added} new Linear notification(s) queued. Use the linear_queue tool to process them.`;
|
|
57
58
|
const ctx = core.channel.reply.finalizeInboundContext({
|
|
58
59
|
Body: body,
|
|
59
60
|
BodyForAgent: body,
|
|
@@ -103,14 +104,9 @@ export function activate(api) {
|
|
|
103
104
|
const debounceMs = (typeof rawDebounceMs === "number" && rawDebounceMs > 0)
|
|
104
105
|
? rawDebounceMs
|
|
105
106
|
: DEFAULT_DEBOUNCE_MS;
|
|
106
|
-
const queuePath = api.resolvePath("queue/
|
|
107
|
-
|
|
108
|
-
api.
|
|
109
|
-
const recovered = handleRecovery(queuePath);
|
|
110
|
-
if (recovered > 0) {
|
|
111
|
-
api.logger.info(`[linear] Recovered ${recovered} stale queue item(s)`);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
107
|
+
const queuePath = api.resolvePath("queue/inbox.jsonl");
|
|
108
|
+
const queue = new InboxQueue(queuePath);
|
|
109
|
+
api.registerTool(createQueueTool(queue));
|
|
114
110
|
const route = createEventRouter({
|
|
115
111
|
agentMapping,
|
|
116
112
|
logger: api.logger,
|
|
@@ -122,7 +118,7 @@ export function activate(api) {
|
|
|
122
118
|
buildKey: (action) => action.agentId,
|
|
123
119
|
shouldDebounce: () => true,
|
|
124
120
|
onFlush: async (actions) => {
|
|
125
|
-
await dispatchConsolidatedActions(actions, api,
|
|
121
|
+
await dispatchConsolidatedActions(actions, api, queue);
|
|
126
122
|
},
|
|
127
123
|
onError: (err) => {
|
|
128
124
|
api.logger.error(`[linear] Debounce flush failed: ${err instanceof Error ? err.message : String(err)}`);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAqB,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAqB,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,UAAU,GAAG,QAAQ,CAAC;AAC5B,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEnC,MAAM,YAAY,GAA2B;IAC3C,gBAAgB,EAAE,UAAU;IAC5B,kBAAkB,EAAE,YAAY;IAChC,kBAAkB,EAAE,YAAY;IAChC,iBAAiB,EAAE,WAAW;CAC/B,CAAC;AAEF,MAAM,UAAU,yBAAyB,CAAC,OAAuB;IAC/D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACjC,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC;QAC/C,MAAM,OAAO,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC;QACvC,OAAO,GAAG,CAAC,GAAG,CAAC,MAAM,KAAK,KAAK,OAAO,EAAE,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,OAAO,YAAY,OAAO,CAAC,MAAM,iCAAiC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,iDAAiD,CAAC;AACtI,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAoB;IAC/C,IAAI,MAAM,CAAC,KAAK,KAAK,iBAAiB,EAAE,CAAC;QACvC,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAClD,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,gBAAgB;YAClE,OAAO,GAAG,MAAM,CAAC,UAAU,MAAM,KAAK,GAAG,CAAC;QAC5C,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC;AAC5C,CAAC;AAED,KAAK,UAAU,2BAA2B,CACxC,OAAuB,EACvB,GAAsB,EACtB,KAAiB;IAEjB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO;IAEjC,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC;IACzB,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC;IAEvB,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAEzB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC;QACnD,GAAG;QACH,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,SAAS;QACpB,IAAI,EAAE;YACJ,IAAI,EAAE,QAAiB;YACvB,EAAE,EAAE,KAAK,CAAC,YAAY;SACvB;KACF,CAAC,CAAC;IAEH,qDAAqD;IACrD,MAAM,OAAO,GAAG,yBAAyB,CAAC,OAAO,CAAC,CAAC;IACnD,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAE3C,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;QAChF,OAAO;IACT,CAAC;IAED,sEAAsE;IACtE,MAAM,IAAI,GAAG,GAAG,KAAK,gFAAgF,CAAC;IAEtG,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,sBAAsB,CAAC;QACpD,IAAI,EAAE,IAAI;QACV,YAAY,EAAE,IAAI;QAClB,OAAO,EAAE,IAAI;QACb,WAAW,EAAE,IAAI;QACjB,IAAI,EAAE,GAAG,UAAU,IAAI,KAAK,CAAC,YAAY,EAAE;QAC3C,EAAE,EAAE,GAAG,UAAU,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,EAAE;QACrD,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,SAAS;QACvC,QAAQ,EAAE,QAAQ;QAClB,iBAAiB,EAAE,kBAAkB,OAAO,CAAC,MAAM,UAAU;QAC7D,QAAQ,EAAE,KAAK,CAAC,YAAY;QAC5B,QAAQ,EAAE,UAAU;QACpB,OAAO,EAAE,UAAU;QACnB,kBAAkB,EAAE,UAAU;QAC9B,aAAa,EAAE,GAAG,UAAU,IAAI,KAAK,CAAC,YAAY,EAAE;KACrD,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,wCAAwC,CAAC;QAChE,GAAG;QACH,GAAG;QACH,iBAAiB,EAAE;YACjB,OAAO,EAAE,KAAK,IAAI,EAAE;gBAClB,4EAA4E;YAC9E,CAAC;YACD,OAAO,EAAE,CAAC,GAAY,EAAE,EAAE;gBACxB,GAAG,CAAC,MAAM,CAAC,KAAK,CACd,yBAAyB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAC5E,CAAC;YACJ,CAAC;SACF;KACF,CAAC,CAAC;AACL,CAAC;AAED,IAAI,eAAyE,CAAC;AAC9E,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAU,CAAC;AAE9C,MAAM,UAAU,QAAQ,CAAC,GAAsB;IAC7C,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAE3C,MAAM,aAAa,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC,eAAe,CAAC,CAAC;IAC1D,IAAI,OAAO,aAAa,KAAK,QAAQ,IAAI,CAAC,aAAa,EAAE,CAAC;QACxD,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,4DAA4D,CAAC,CAAC;QAC/E,OAAO;IACT,CAAC;IAED,MAAM,YAAY,GACf,GAAG,CAAC,YAAY,EAAE,CAAC,cAAc,CAA4B,IAAI,EAAE,CAAC;IACvE,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3C,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IAED,MAAM,WAAW,GACd,GAAG,CAAC,YAAY,EAAE,CAAC,aAAa,CAAc,IAAI,EAAE,CAAC;IACxD,MAAM,OAAO,GACV,GAAG,CAAC,YAAY,EAAE,CAAC,SAAS,CAAc,IAAI,EAAE,CAAC;IACpD,MAAM,aAAa,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC,YAAY,CAAuB,CAAC;IAC7E,MAAM,UAAU,GACd,CAAC,OAAO,aAAa,KAAK,QAAQ,IAAI,aAAa,GAAG,CAAC,CAAC;QACtD,CAAC,CAAC,aAAa;QACf,CAAC,CAAC,mBAAmB,CAAC;IAE1B,MAAM,SAAS,GAAG,GAAG,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;IAExC,GAAG,CAAC,YAAY,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC;IAEzC,MAAM,KAAK,GAAG,iBAAiB,CAAC;QAC9B,YAAY;QACZ,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS;QACzD,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;KAC9C,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAe;QAClF,UAAU;QACV,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO;QACpC,cAAc,EAAE,GAAG,EAAE,CAAC,IAAI;QAC1B,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACzB,MAAM,2BAA2B,CAAC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;YACf,GAAG,CAAC,MAAM,CAAC,KAAK,CACd,mCAAmC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACtF,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;IACH,eAAe,GAAG,SAAS,CAAC;IAE5B,MAAM,OAAO,GAAG,oBAAoB,CAAC;QACnC,aAAa;QACb,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YACjB,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;YAC7B,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,kBAAkB,MAAM,CAAC,IAAI,UAAU,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,KAAK,KAAK,MAAM,CAAC,MAAM,EAAE,CAChG,CAAC;gBAEF,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC3B,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;oBACxC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBAC5B,CAAC;YACH,CAAC;QACH,CAAC;KACF,CAAC,CAAC;IAEH,GAAG,CAAC,iBAAiB,CAAC;QACpB,IAAI,EAAE,eAAe;QACrB,OAAO;KACR,CAAC,CAAC;IAEH,GAAG,CAAC,MAAM,CAAC,IAAI,CACb,iEAAiE,UAAU,KAAK,CACjF,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,GAAsB;IACrD,IAAI,eAAe,EAAE,CAAC;QACpB,KAAK,MAAM,GAAG,IAAI,mBAAmB,EAAE,CAAC;YACtC,MAAM,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC;QACD,mBAAmB,CAAC,KAAK,EAAE,CAAC;QAC5B,eAAe,GAAG,SAAS,CAAC;IAC9B,CAAC;IACD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,MAAM,GAAG;IACb,EAAE,EAAE,QAAQ;IACZ,IAAI,EAAE,QAAQ;IACd,WAAW,EAAE,oDAAoD;IACjE,QAAQ;IACR,UAAU;CAOX,CAAC;AAEF,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-tool.d.ts","sourceRoot":"","sources":["../src/queue-tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAiBlD,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,GAAG,YAAY,CA6B/D"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox";
|
|
2
|
+
import { jsonResult } from "openclaw/plugin-sdk";
|
|
3
|
+
const QueueAction = Type.Unsafe({
|
|
4
|
+
type: "string",
|
|
5
|
+
enum: ["peek", "pop", "drain"],
|
|
6
|
+
description: "peek: view all pending items without removing them. " +
|
|
7
|
+
"pop: remove and return the highest-priority item. " +
|
|
8
|
+
"drain: remove and return all items.",
|
|
9
|
+
});
|
|
10
|
+
const QueueToolParams = Type.Object({
|
|
11
|
+
action: QueueAction,
|
|
12
|
+
});
|
|
13
|
+
export function createQueueTool(queue) {
|
|
14
|
+
return {
|
|
15
|
+
name: "linear_queue",
|
|
16
|
+
label: "Linear Queue",
|
|
17
|
+
description: "Manage the Linear notification inbox queue. " +
|
|
18
|
+
"Use 'peek' to see pending items, 'pop' to take the next item, or 'drain' to take all items.",
|
|
19
|
+
parameters: QueueToolParams,
|
|
20
|
+
async execute(_toolCallId, params) {
|
|
21
|
+
switch (params.action) {
|
|
22
|
+
case "peek": {
|
|
23
|
+
const items = await queue.peek();
|
|
24
|
+
return jsonResult({ count: items.length, items });
|
|
25
|
+
}
|
|
26
|
+
case "pop": {
|
|
27
|
+
const item = await queue.pop();
|
|
28
|
+
return jsonResult(item ? { item } : { item: null, message: "Queue is empty" });
|
|
29
|
+
}
|
|
30
|
+
case "drain": {
|
|
31
|
+
const items = await queue.drain();
|
|
32
|
+
return jsonResult({ count: items.length, items });
|
|
33
|
+
}
|
|
34
|
+
default:
|
|
35
|
+
return jsonResult({
|
|
36
|
+
error: `Unknown action: ${params.action}`,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=queue-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-tool.js","sourceRoot":"","sources":["../src/queue-tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAe,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGjD,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAA2B;IACxD,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC;IAC9B,WAAW,EACT,sDAAsD;QACtD,oDAAoD;QACpD,qCAAqC;CACxC,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,CAAC;IAClC,MAAM,EAAE,WAAW;CACpB,CAAC,CAAC;AAIH,MAAM,UAAU,eAAe,CAAC,KAAiB;IAC/C,OAAO;QACL,IAAI,EAAE,cAAc;QACpB,KAAK,EAAE,cAAc;QACrB,WAAW,EACT,8CAA8C;YAC9C,6FAA6F;QAC/F,UAAU,EAAE,eAAe;QAC3B,KAAK,CAAC,OAAO,CAAC,WAAmB,EAAE,MAAuB;YACxD,QAAQ,MAAM,CAAC,MAAM,EAAE,CAAC;gBACtB,KAAK,MAAM,CAAC,CAAC,CAAC;oBACZ,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;oBACjC,OAAO,UAAU,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;gBACpD,CAAC;gBACD,KAAK,KAAK,CAAC,CAAC,CAAC;oBACX,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC;oBAC/B,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,CAAC,CAAC;gBACjF,CAAC;gBACD,KAAK,OAAO,CAAC,CAAC,CAAC;oBACb,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;oBAClC,OAAO,UAAU,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;gBACpD,CAAC;gBACD;oBACE,OAAO,UAAU,CAAC;wBAChB,KAAK,EAAE,mBAAoB,MAA6B,CAAC,MAAM,EAAE;qBAClE,CAAC,CAAC;YACP,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/work-queue.d.ts
CHANGED
|
@@ -3,36 +3,30 @@ export interface QueueItem {
|
|
|
3
3
|
issueId: string;
|
|
4
4
|
event: string;
|
|
5
5
|
summary: string;
|
|
6
|
-
status: "pending" | "in_progress" | "done";
|
|
7
6
|
priority: number;
|
|
8
7
|
addedAt: string;
|
|
9
|
-
startedAt: string | null;
|
|
10
|
-
completedAt: string | null;
|
|
11
|
-
}
|
|
12
|
-
export interface WorkQueue {
|
|
13
|
-
items: QueueItem[];
|
|
14
8
|
}
|
|
9
|
+
export declare const EVENT_PRIORITY: Record<string, number>;
|
|
15
10
|
export declare function parseNotificationMessage(message: string): Array<{
|
|
16
11
|
id: string;
|
|
17
12
|
event: string;
|
|
18
13
|
summary: string;
|
|
19
14
|
}>;
|
|
20
|
-
export declare
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
export declare function cleanupDoneItems(queue: WorkQueue, maxAgeMs?: number): number;
|
|
15
|
+
export declare class Mutex {
|
|
16
|
+
private _lock;
|
|
17
|
+
acquire(): Promise<() => void>;
|
|
18
|
+
}
|
|
19
|
+
export declare class InboxQueue {
|
|
20
|
+
private readonly path;
|
|
21
|
+
private readonly mutex;
|
|
22
|
+
constructor(path: string);
|
|
23
|
+
/** Parse a notification message, dedup, and append new items. Returns count added. */
|
|
24
|
+
enqueue(message: string): Promise<number>;
|
|
25
|
+
/** Return all items sorted by priority (lowest number first). Non-destructive. */
|
|
26
|
+
peek(): Promise<QueueItem[]>;
|
|
27
|
+
/** Remove and return the highest-priority item, or null if empty. */
|
|
28
|
+
pop(): Promise<QueueItem | null>;
|
|
29
|
+
/** Remove and return all items sorted by priority. */
|
|
30
|
+
drain(): Promise<QueueItem[]>;
|
|
31
|
+
}
|
|
38
32
|
//# sourceMappingURL=work-queue.d.ts.map
|
package/dist/work-queue.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work-queue.d.ts","sourceRoot":"","sources":["../src/work-queue.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"work-queue.d.ts","sourceRoot":"","sources":["../src/work-queue.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAKjD,CAAC;AAyFF,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,GACd,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAcvD;AAID,qBAAa,KAAK;IAChB,OAAO,CAAC,KAAK,CAAoC;IAE3C,OAAO,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC;CAUrC;AAyDD,qBAAa,UAAU;IAGT,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAER,IAAI,EAAE,MAAM;IAEzC,sFAAsF;IAChF,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuC/C,kFAAkF;IAC5E,IAAI,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAUlC,qEAAqE;IAC/D,GAAG,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAetC,sDAAsD;IAChD,KAAK,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;CAapC"}
|
package/dist/work-queue.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { readFileSync, existsSync, mkdirSync, openSync, writeSync, fsyncSync, closeSync, unlinkSync, renameSync, } from "node:fs";
|
|
1
|
+
import { readFileSync, existsSync, mkdirSync, openSync, writeSync, fsyncSync, closeSync, unlinkSync, renameSync, appendFileSync, } from "node:fs";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
|
-
const EVENT_PRIORITY = {
|
|
3
|
+
export const EVENT_PRIORITY = {
|
|
4
4
|
"issue.assigned": 1,
|
|
5
5
|
"issue.reassigned": 2,
|
|
6
6
|
"comment.mention": 3,
|
|
@@ -86,23 +86,50 @@ export function parseNotificationMessage(message) {
|
|
|
86
86
|
}
|
|
87
87
|
return results;
|
|
88
88
|
}
|
|
89
|
-
// ---
|
|
90
|
-
export
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
// --- Mutex ---
|
|
90
|
+
export class Mutex {
|
|
91
|
+
_lock = Promise.resolve();
|
|
92
|
+
async acquire() {
|
|
93
|
+
let release;
|
|
94
|
+
const next = new Promise((r) => {
|
|
95
|
+
release = r;
|
|
96
|
+
});
|
|
97
|
+
const prev = this._lock;
|
|
98
|
+
this._lock = next;
|
|
99
|
+
await prev;
|
|
100
|
+
return release;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// --- InboxQueue ---
|
|
104
|
+
function readJsonl(path) {
|
|
105
|
+
if (!existsSync(path))
|
|
106
|
+
return [];
|
|
93
107
|
try {
|
|
94
|
-
|
|
108
|
+
const content = readFileSync(path, "utf-8");
|
|
109
|
+
const items = [];
|
|
110
|
+
for (const line of content.split("\n")) {
|
|
111
|
+
const trimmed = line.trim();
|
|
112
|
+
if (!trimmed)
|
|
113
|
+
continue;
|
|
114
|
+
try {
|
|
115
|
+
items.push(JSON.parse(trimmed));
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// skip malformed lines
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return items;
|
|
95
122
|
}
|
|
96
123
|
catch {
|
|
97
|
-
return
|
|
124
|
+
return [];
|
|
98
125
|
}
|
|
99
126
|
}
|
|
100
|
-
|
|
101
|
-
const dir = dirname(
|
|
127
|
+
function writeJsonl(path, items) {
|
|
128
|
+
const dir = dirname(path);
|
|
102
129
|
if (!existsSync(dir))
|
|
103
130
|
mkdirSync(dir, { recursive: true });
|
|
104
|
-
const tmpPath = `${
|
|
105
|
-
const content = JSON.stringify(
|
|
131
|
+
const tmpPath = `${path}.tmp.${process.pid}.${Date.now()}`;
|
|
132
|
+
const content = items.map((item) => JSON.stringify(item)).join("\n") + (items.length ? "\n" : "");
|
|
106
133
|
try {
|
|
107
134
|
const fd = openSync(tmpPath, "w");
|
|
108
135
|
try {
|
|
@@ -112,7 +139,7 @@ export function writeQueue(queuePath, queue) {
|
|
|
112
139
|
finally {
|
|
113
140
|
closeSync(fd);
|
|
114
141
|
}
|
|
115
|
-
renameSync(tmpPath,
|
|
142
|
+
renameSync(tmpPath, path);
|
|
116
143
|
}
|
|
117
144
|
catch (err) {
|
|
118
145
|
try {
|
|
@@ -124,95 +151,94 @@ export function writeQueue(queuePath, queue) {
|
|
|
124
151
|
throw err;
|
|
125
152
|
}
|
|
126
153
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
export function handleIntake(message, queuePath) {
|
|
134
|
-
const parsed = parseNotificationMessage(message);
|
|
135
|
-
if (parsed.length === 0)
|
|
136
|
-
return 0;
|
|
137
|
-
const queue = readQueue(queuePath);
|
|
138
|
-
// Only dedup against non-done items so re-assignments after completion work
|
|
139
|
-
const existingKeys = new Set(queue.items
|
|
140
|
-
.filter((item) => item.status !== "done")
|
|
141
|
-
.map((item) => `${item.issueId ?? item.id}:${item.event}`));
|
|
142
|
-
let added = 0;
|
|
143
|
-
const now = new Date().toISOString();
|
|
144
|
-
for (const entry of parsed) {
|
|
145
|
-
const dedupKey = `${entry.id}:${entry.event}`;
|
|
146
|
-
if (existingKeys.has(dedupKey))
|
|
147
|
-
continue;
|
|
148
|
-
queue.items.push({
|
|
149
|
-
id: entry.id,
|
|
150
|
-
issueId: entry.id,
|
|
151
|
-
event: entry.event,
|
|
152
|
-
summary: entry.summary,
|
|
153
|
-
status: "pending",
|
|
154
|
-
priority: EVENT_PRIORITY[entry.event] ?? 5,
|
|
155
|
-
addedAt: now,
|
|
156
|
-
startedAt: null,
|
|
157
|
-
completedAt: null,
|
|
158
|
-
});
|
|
159
|
-
existingKeys.add(dedupKey);
|
|
160
|
-
added++;
|
|
161
|
-
}
|
|
162
|
-
if (added > 0) {
|
|
163
|
-
cleanupDoneItems(queue);
|
|
164
|
-
queue.items.sort((a, b) => a.priority - b.priority);
|
|
165
|
-
writeQueue(queuePath, queue);
|
|
166
|
-
}
|
|
167
|
-
return added;
|
|
154
|
+
function appendJsonl(path, items) {
|
|
155
|
+
const dir = dirname(path);
|
|
156
|
+
if (!existsSync(dir))
|
|
157
|
+
mkdirSync(dir, { recursive: true });
|
|
158
|
+
const content = items.map((item) => JSON.stringify(item)).join("\n") + "\n";
|
|
159
|
+
appendFileSync(path, content, "utf-8");
|
|
168
160
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
161
|
+
export class InboxQueue {
|
|
162
|
+
path;
|
|
163
|
+
mutex = new Mutex();
|
|
164
|
+
constructor(path) {
|
|
165
|
+
this.path = path;
|
|
166
|
+
}
|
|
167
|
+
/** Parse a notification message, dedup, and append new items. Returns count added. */
|
|
168
|
+
async enqueue(message) {
|
|
169
|
+
const parsed = parseNotificationMessage(message);
|
|
170
|
+
if (parsed.length === 0)
|
|
171
|
+
return 0;
|
|
172
|
+
const release = await this.mutex.acquire();
|
|
173
|
+
try {
|
|
174
|
+
const existing = readJsonl(this.path);
|
|
175
|
+
const existingKeys = new Set(existing.map((item) => `${item.issueId}:${item.event}`));
|
|
176
|
+
const newItems = [];
|
|
177
|
+
const now = new Date().toISOString();
|
|
178
|
+
for (const entry of parsed) {
|
|
179
|
+
const dedupKey = `${entry.id}:${entry.event}`;
|
|
180
|
+
if (existingKeys.has(dedupKey))
|
|
181
|
+
continue;
|
|
182
|
+
newItems.push({
|
|
183
|
+
id: entry.id,
|
|
184
|
+
issueId: entry.id,
|
|
185
|
+
event: entry.event,
|
|
186
|
+
summary: entry.summary,
|
|
187
|
+
priority: EVENT_PRIORITY[entry.event] ?? 5,
|
|
188
|
+
addedAt: now,
|
|
189
|
+
});
|
|
190
|
+
existingKeys.add(dedupKey);
|
|
191
|
+
}
|
|
192
|
+
if (newItems.length > 0) {
|
|
193
|
+
appendJsonl(this.path, newItems);
|
|
194
|
+
}
|
|
195
|
+
return newItems.length;
|
|
196
|
+
}
|
|
197
|
+
finally {
|
|
198
|
+
release();
|
|
199
|
+
}
|
|
180
200
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
201
|
+
/** Return all items sorted by priority (lowest number first). Non-destructive. */
|
|
202
|
+
async peek() {
|
|
203
|
+
const release = await this.mutex.acquire();
|
|
204
|
+
try {
|
|
205
|
+
const items = readJsonl(this.path);
|
|
206
|
+
return items.sort((a, b) => a.priority - b.priority || a.addedAt.localeCompare(b.addedAt));
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
release();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/** Remove and return the highest-priority item, or null if empty. */
|
|
213
|
+
async pop() {
|
|
214
|
+
const release = await this.mutex.acquire();
|
|
215
|
+
try {
|
|
216
|
+
const items = readJsonl(this.path);
|
|
217
|
+
if (items.length === 0)
|
|
218
|
+
return null;
|
|
219
|
+
items.sort((a, b) => a.priority - b.priority || a.addedAt.localeCompare(b.addedAt));
|
|
220
|
+
const [popped, ...rest] = items;
|
|
221
|
+
writeJsonl(this.path, rest);
|
|
222
|
+
return popped;
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
release();
|
|
192
226
|
}
|
|
193
227
|
}
|
|
194
|
-
|
|
195
|
-
|
|
228
|
+
/** Remove and return all items sorted by priority. */
|
|
229
|
+
async drain() {
|
|
230
|
+
const release = await this.mutex.acquire();
|
|
231
|
+
try {
|
|
232
|
+
const items = readJsonl(this.path);
|
|
233
|
+
if (items.length === 0)
|
|
234
|
+
return [];
|
|
235
|
+
items.sort((a, b) => a.priority - b.priority || a.addedAt.localeCompare(b.addedAt));
|
|
236
|
+
writeJsonl(this.path, []);
|
|
237
|
+
return items;
|
|
238
|
+
}
|
|
239
|
+
finally {
|
|
240
|
+
release();
|
|
241
|
+
}
|
|
196
242
|
}
|
|
197
|
-
return recovered;
|
|
198
|
-
}
|
|
199
|
-
// --- Cleanup ---
|
|
200
|
-
const DONE_MAX_AGE_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
201
|
-
/**
|
|
202
|
-
* Remove done items older than maxAgeMs from the queue.
|
|
203
|
-
* Mutates the queue in place. Returns number of items purged.
|
|
204
|
-
*/
|
|
205
|
-
export function cleanupDoneItems(queue, maxAgeMs = DONE_MAX_AGE_MS) {
|
|
206
|
-
const cutoff = Date.now() - maxAgeMs;
|
|
207
|
-
const before = queue.items.length;
|
|
208
|
-
queue.items = queue.items.filter((item) => {
|
|
209
|
-
if (item.status !== "done")
|
|
210
|
-
return true;
|
|
211
|
-
const completedAt = item.completedAt
|
|
212
|
-
? new Date(item.completedAt).getTime()
|
|
213
|
-
: 0;
|
|
214
|
-
return completedAt > cutoff;
|
|
215
|
-
});
|
|
216
|
-
return before - queue.items.length;
|
|
217
243
|
}
|
|
218
244
|
//# sourceMappingURL=work-queue.js.map
|
package/dist/work-queue.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work-queue.js","sourceRoot":"","sources":["../src/work-queue.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,SAAS,EACT,UAAU,EACV,UAAU,
|
|
1
|
+
{"version":3,"file":"work-queue.js","sourceRoot":"","sources":["../src/work-queue.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -6,7 +6,15 @@ metadata: { "openclaw": { "always": true } }
|
|
|
6
6
|
|
|
7
7
|
# Linear Work Queue
|
|
8
8
|
|
|
9
|
-
You have a
|
|
9
|
+
You have a `linear_queue` tool for managing Linear notifications that need your attention. The tool is the source of truth — don't parse raw notification messages yourself.
|
|
10
|
+
|
|
11
|
+
## Tool actions
|
|
12
|
+
|
|
13
|
+
| Action | Effect |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `peek` | View all pending items sorted by priority. Non-destructive. |
|
|
16
|
+
| `pop` | Remove and return the highest-priority item. |
|
|
17
|
+
| `drain` | Remove and return all items sorted by priority. |
|
|
10
18
|
|
|
11
19
|
## Queue item structure
|
|
12
20
|
|
|
@@ -16,11 +24,8 @@ You have a work queue at `queue/work-queue.json` containing Linear notifications
|
|
|
16
24
|
"issueId": "TEAM-123",
|
|
17
25
|
"event": "issue.assigned",
|
|
18
26
|
"summary": "Issue title or comment text",
|
|
19
|
-
"status": "pending",
|
|
20
27
|
"priority": 1,
|
|
21
|
-
"addedAt": "ISO timestamp"
|
|
22
|
-
"startedAt": null,
|
|
23
|
-
"completedAt": null
|
|
28
|
+
"addedAt": "ISO timestamp"
|
|
24
29
|
}
|
|
25
30
|
```
|
|
26
31
|
|
|
@@ -35,14 +40,8 @@ You have a work queue at `queue/work-queue.json` containing Linear notifications
|
|
|
35
40
|
|
|
36
41
|
## Processing workflow
|
|
37
42
|
|
|
38
|
-
1. **
|
|
39
|
-
2. **Skip** if there are no
|
|
40
|
-
3. **
|
|
41
|
-
4. **
|
|
42
|
-
5. **
|
|
43
|
-
6. **Mark `done`**: set `status` to `"done"` and `completedAt` to the current ISO timestamp. Write the queue back to disk.
|
|
44
|
-
7. **Repeat** from step 1 until no `pending` items remain.
|
|
45
|
-
|
|
46
|
-
## File update rules
|
|
47
|
-
|
|
48
|
-
Always do atomic read-modify-write cycles: read the full file, modify the target item in memory, write the entire file back. Never partially update the file.
|
|
43
|
+
1. **Peek** the queue with `linear_queue { action: "peek" }` to see all pending items.
|
|
44
|
+
2. **Skip** if there are no items.
|
|
45
|
+
3. **Pop** the next item with `linear_queue { action: "pop" }`.
|
|
46
|
+
4. **Act**: use the Linear tools (list-issues, update-issue, add-comment) to handle the item per the event type table above.
|
|
47
|
+
5. **Repeat** from step 3 until pop returns null.
|