@jiggai/recipes 0.4.71 → 0.4.73
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/README.md +0 -1
- package/dist/index.js +15592 -0
- package/docs/COMMANDS.md +0 -6
- package/docs/TEAM_WORKFLOW.md +0 -5
- package/index.ts +0 -14
- package/openclaw.plugin.json +2 -2
- package/package.json +16 -9
- package/src/lib/recipes.ts +2 -1
- package/src/lib/workflows/workflow-node-executor.ts +7 -38
- package/src/lib/workflows/workflow-worker.ts +8 -8
package/docs/COMMANDS.md
CHANGED
|
@@ -221,12 +221,6 @@ openclaw recipes handoff --team-id development-team --ticket 0007 --tester test
|
|
|
221
221
|
openclaw recipes complete --team-id development-team --ticket 0007
|
|
222
222
|
```
|
|
223
223
|
|
|
224
|
-
### Clean up stale assignment stubs for done work
|
|
225
|
-
|
|
226
|
-
```bash
|
|
227
|
-
openclaw recipes cleanup-closed-assignments --team-id development-team
|
|
228
|
-
openclaw recipes cleanup-closed-assignments --team-id development-team --ticket 0050 0064
|
|
229
|
-
```
|
|
230
224
|
|
|
231
225
|
---
|
|
232
226
|
|
package/docs/TEAM_WORKFLOW.md
CHANGED
|
@@ -157,11 +157,6 @@ openclaw recipes move-ticket --team-id development-team --ticket 0007 --to done
|
|
|
157
157
|
openclaw recipes assign --team-id development-team --ticket 0007 --owner devops
|
|
158
158
|
```
|
|
159
159
|
|
|
160
|
-
### Clean up stale assignment stubs for closed work
|
|
161
|
-
|
|
162
|
-
```bash
|
|
163
|
-
openclaw recipes cleanup-closed-assignments --team-id development-team
|
|
164
|
-
```
|
|
165
160
|
|
|
166
161
|
---
|
|
167
162
|
|
package/index.ts
CHANGED
|
@@ -27,7 +27,6 @@ import {
|
|
|
27
27
|
} from "./src/handlers/install";
|
|
28
28
|
import {
|
|
29
29
|
handleAssign,
|
|
30
|
-
handleCleanupClosedAssignments,
|
|
31
30
|
handleDispatch,
|
|
32
31
|
handleHandoff,
|
|
33
32
|
handleMoveTicket,
|
|
@@ -797,19 +796,6 @@ workflows
|
|
|
797
796
|
console.log(JSON.stringify({ ok: true, moved: { from: res.from, to: res.to } }, null, 2));
|
|
798
797
|
});
|
|
799
798
|
|
|
800
|
-
cmd
|
|
801
|
-
.command("cleanup-closed-assignments")
|
|
802
|
-
.description("Archive assignment stubs for tickets already in work/done (prevents done work resurfacing)")
|
|
803
|
-
.requiredOption("--team-id <teamId>", "Team id")
|
|
804
|
-
.option("--ticket <ticketNums...>", "Optional ticket numbers to target (e.g. 0050 0064)")
|
|
805
|
-
.action(async (options: { teamId?: string; ticket?: string[] }) => {
|
|
806
|
-
if (!options.teamId) throw new Error("--team-id is required");
|
|
807
|
-
const res = await handleCleanupClosedAssignments(api, {
|
|
808
|
-
teamId: options.teamId,
|
|
809
|
-
ticketNums: options.ticket,
|
|
810
|
-
});
|
|
811
|
-
console.log(JSON.stringify(res, null, 2));
|
|
812
|
-
});
|
|
813
799
|
|
|
814
800
|
cmd
|
|
815
801
|
.command("assign")
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "recipes",
|
|
3
3
|
"name": "Recipes",
|
|
4
4
|
"description": "Markdown recipes that scaffold agents and teams (workspace-local).",
|
|
5
|
-
"version": "0.4.
|
|
5
|
+
"version": "0.4.73",
|
|
6
6
|
"configSchema": {
|
|
7
7
|
"type": "object",
|
|
8
8
|
"additionalProperties": false,
|
|
@@ -72,5 +72,5 @@
|
|
|
72
72
|
"help": "Controls whether recipe-defined cron jobs are installed during scaffold. off=never, prompt=ask, on=auto-install."
|
|
73
73
|
}
|
|
74
74
|
},
|
|
75
|
-
"main": "index.
|
|
75
|
+
"main": "dist/index.js"
|
|
76
76
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jiggai/recipes",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.73",
|
|
4
4
|
"description": "ClawRecipes plugin for OpenClaw (markdown recipes -> scaffold agents/teams)",
|
|
5
|
-
"main": "index.
|
|
5
|
+
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|
|
7
7
|
"openclaw": {
|
|
8
8
|
"extensions": [
|
|
@@ -10,16 +10,20 @@
|
|
|
10
10
|
],
|
|
11
11
|
"compat": {
|
|
12
12
|
"pluginApi": ">=1.0.0",
|
|
13
|
-
"pluginApiRange": ">=2026.
|
|
13
|
+
"pluginApiRange": ">=2026.5"
|
|
14
14
|
},
|
|
15
15
|
"build": {
|
|
16
|
-
"openclawVersion": "2026.
|
|
17
|
-
}
|
|
16
|
+
"openclawVersion": "2026.5.7"
|
|
17
|
+
},
|
|
18
|
+
"runtimeExtensions": [
|
|
19
|
+
"./dist/index.js"
|
|
20
|
+
]
|
|
18
21
|
},
|
|
19
22
|
"publishConfig": {
|
|
20
23
|
"access": "public"
|
|
21
24
|
},
|
|
22
25
|
"files": [
|
|
26
|
+
"dist/",
|
|
23
27
|
"index.ts",
|
|
24
28
|
"src/",
|
|
25
29
|
"openclaw.plugin.json",
|
|
@@ -44,9 +48,11 @@
|
|
|
44
48
|
"jscpd": "jscpd src/ index.ts --min-lines 8 --min-tokens 50",
|
|
45
49
|
"prepare": "husky || true",
|
|
46
50
|
"check:plugin-version": "node scripts/check-openclaw-plugin-version.mjs",
|
|
47
|
-
"prepack": "npm run -s sync:plugin-version && npm run -s check:plugin-version",
|
|
48
|
-
"prepublishOnly": "npm run -s sync:plugin-version && npm run -s check:plugin-version",
|
|
49
|
-
"sync:plugin-version": "node scripts/sync-openclaw-plugin-version.mjs"
|
|
51
|
+
"prepack": "npm run -s build:plugin && npm run -s sync:plugin-version && npm run -s check:plugin-version",
|
|
52
|
+
"prepublishOnly": "npm run -s build:plugin && npm run -s sync:plugin-version && npm run -s check:plugin-version",
|
|
53
|
+
"sync:plugin-version": "node scripts/sync-openclaw-plugin-version.mjs",
|
|
54
|
+
"build:plugin": "esbuild index.ts --bundle --platform=node --format=cjs --target=node20 --outfile=dist/index.js --external:openclaw/plugin-sdk",
|
|
55
|
+
"verify:runtime-package": "node scripts/verify-runtime-package.mjs"
|
|
50
56
|
},
|
|
51
57
|
"keywords": [
|
|
52
58
|
"openclaw",
|
|
@@ -69,7 +75,8 @@
|
|
|
69
75
|
"jscpd": "^4.0.5",
|
|
70
76
|
"lint-staged": "^16.2.7",
|
|
71
77
|
"typescript-eslint": "^8.18.0",
|
|
72
|
-
"vitest": "^3.2.4"
|
|
78
|
+
"vitest": "^3.2.4",
|
|
79
|
+
"esbuild": "^0.27.3"
|
|
73
80
|
},
|
|
74
81
|
"lint-staged": {
|
|
75
82
|
"*.ts": [
|
package/src/lib/recipes.ts
CHANGED
|
@@ -18,7 +18,8 @@ export function workspacePath(api: OpenClawPluginApi, ...parts: string[]) {
|
|
|
18
18
|
* @returns Array of { source, path }
|
|
19
19
|
*/
|
|
20
20
|
export async function listRecipeFiles(api: OpenClawPluginApi, cfg: Required<RecipesConfig>) {
|
|
21
|
-
const
|
|
21
|
+
const pluginRoot = api.rootDir ?? path.resolve(__dirname, "..", "..");
|
|
22
|
+
const builtinDir = path.resolve(pluginRoot, "recipes", "default");
|
|
22
23
|
const workspaceDir = workspacePath(api, cfg.workspaceRecipesDir);
|
|
23
24
|
|
|
24
25
|
const out: Array<{ source: "builtin" | "workspace"; path: string }> = [];
|
|
@@ -305,14 +305,12 @@ export async function executeWorkflowNodes(opts: {
|
|
|
305
305
|
`- openclaw recipes workflows resume --team-id ${teamId} --run-id ${runId}`,
|
|
306
306
|
].join('\n');
|
|
307
307
|
|
|
308
|
-
// Deliver the approval prompt
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
//
|
|
313
|
-
//
|
|
314
|
-
// approve` CLI when this falls through.
|
|
315
|
-
let approvalDelivered = false;
|
|
308
|
+
// Deliver the approval prompt via the OpenClaw `message` tool. The tool
|
|
309
|
+
// requires the calling agent to have `group:messaging` (or `message`)
|
|
310
|
+
// in its tools.allow policy — see openclaw/openclaw#74780. If delivery
|
|
311
|
+
// fails (misconfigured policy, channel adapter down, etc.) we log and
|
|
312
|
+
// continue; approval.json is durable, so operators can still approve
|
|
313
|
+
// via the kitchen UI or `openclaw recipes workflows approve` CLI.
|
|
316
314
|
try {
|
|
317
315
|
await toolsInvoke<ToolTextResult>(api, {
|
|
318
316
|
tool: 'message',
|
|
@@ -324,39 +322,10 @@ export async function executeWorkflowNodes(opts: {
|
|
|
324
322
|
message: msg,
|
|
325
323
|
},
|
|
326
324
|
});
|
|
327
|
-
approvalDelivered = true;
|
|
328
325
|
} catch (err) {
|
|
329
326
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
330
327
|
console.warn(`[workflow] tools.invoke('message') failed for run ${runId} on node ${node.id}: ${errMsg}`);
|
|
331
|
-
|
|
332
|
-
try {
|
|
333
|
-
const cfg = await loadOpenClawConfig(api);
|
|
334
|
-
const tgToken = (cfg as { channels?: { telegram?: { botToken?: string } } })
|
|
335
|
-
.channels?.telegram?.botToken;
|
|
336
|
-
if (tgToken) {
|
|
337
|
-
const tgRes = await fetch(`https://api.telegram.org/bot${tgToken}/sendMessage`, {
|
|
338
|
-
method: 'POST',
|
|
339
|
-
headers: { 'content-type': 'application/json' },
|
|
340
|
-
body: JSON.stringify({ chat_id: target, text: msg }),
|
|
341
|
-
});
|
|
342
|
-
if (tgRes.ok) {
|
|
343
|
-
approvalDelivered = true;
|
|
344
|
-
console.log(`[workflow] approval delivered via direct telegram bot API for run ${runId}`);
|
|
345
|
-
} else {
|
|
346
|
-
const tgBody = await tgRes.text().catch(() => '');
|
|
347
|
-
console.error(`[workflow] telegram fallback failed (${tgRes.status}) for run ${runId}: ${tgBody}`);
|
|
348
|
-
}
|
|
349
|
-
} else {
|
|
350
|
-
console.error(`[workflow] telegram fallback skipped for run ${runId}: missing channels.telegram.botToken in openclaw config`);
|
|
351
|
-
}
|
|
352
|
-
} catch (fbErr) {
|
|
353
|
-
const fbMsg = fbErr instanceof Error ? fbErr.message : String(fbErr);
|
|
354
|
-
console.error(`[workflow] telegram fallback threw for run ${runId}: ${fbMsg}`);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
if (!approvalDelivered) {
|
|
358
|
-
console.warn(`[workflow] approval message not delivered for run ${runId}; approve via kitchen UI or CLI`);
|
|
359
|
-
}
|
|
328
|
+
console.warn(`[workflow] approval message not delivered for run ${runId}; approve via kitchen UI or CLI`);
|
|
360
329
|
}
|
|
361
330
|
|
|
362
331
|
const waitingTs = new Date().toISOString();
|
|
@@ -12,7 +12,7 @@ import type { WorkflowLane, WorkflowNode, RunLog } from './workflow-types';
|
|
|
12
12
|
import { dequeueNextTask, enqueueTask, hasPendingTaskFor, releaseTaskClaim, compactQueue } from './workflow-queue';
|
|
13
13
|
import { currentLockOwner, isLockHolderDead } from './lock-liveness';
|
|
14
14
|
import { loadPriorLlmInput, loadProposedPostTextFromPriorNode } from './workflow-node-output-readers';
|
|
15
|
-
import { readTextFile } from './workflow-runner-io';
|
|
15
|
+
import { readTextFile, readJsonFile } from './workflow-runner-io';
|
|
16
16
|
import { resolveApprovalBindingTarget } from './workflow-node-executor';
|
|
17
17
|
import { buildKitchenWorkflowReviewUrl } from './kitchen-review-url';
|
|
18
18
|
import {
|
|
@@ -45,7 +45,7 @@ async function buildMemoryContext(teamDir: string): Promise<string> {
|
|
|
45
45
|
// Read pinned items first (highest priority)
|
|
46
46
|
const pinnedPath = path.join(memoryDir, 'pinned.jsonl');
|
|
47
47
|
if (await fileExists(pinnedPath)) {
|
|
48
|
-
const pinnedContent = await
|
|
48
|
+
const pinnedContent = await readTextFile(pinnedPath);
|
|
49
49
|
const pinnedItems = pinnedContent.trim().split('\n').filter(Boolean);
|
|
50
50
|
|
|
51
51
|
if (pinnedItems.length > 0) {
|
|
@@ -75,7 +75,7 @@ async function buildMemoryContext(teamDir: string): Promise<string> {
|
|
|
75
75
|
if (currentTokens > maxTokens * 0.8) break; // Leave room for recent items
|
|
76
76
|
|
|
77
77
|
const filePath = path.join(memoryDir, filename);
|
|
78
|
-
const content = await
|
|
78
|
+
const content = await readTextFile(filePath);
|
|
79
79
|
const items = content.trim().split('\n').filter(Boolean);
|
|
80
80
|
|
|
81
81
|
if (items.length > 0) {
|
|
@@ -151,7 +151,7 @@ async function buildTemplateVars(
|
|
|
151
151
|
if (nid && nrOutPath) {
|
|
152
152
|
try {
|
|
153
153
|
const outAbs = path.resolve(teamDir, nrOutPath);
|
|
154
|
-
const outputContent = await
|
|
154
|
+
const outputContent = await readTextFile(outAbs);
|
|
155
155
|
vars[`${nid}.output`] = outputContent;
|
|
156
156
|
|
|
157
157
|
try {
|
|
@@ -314,7 +314,7 @@ async function checkWaitingHandoffs(api: OpenClawPluginApi, teamId: string, team
|
|
|
314
314
|
const runPath = path.join(runDir, 'run.json');
|
|
315
315
|
let run: RunLog;
|
|
316
316
|
try {
|
|
317
|
-
const raw = await
|
|
317
|
+
const raw = await readTextFile(runPath);
|
|
318
318
|
run = JSON.parse(raw) as RunLog;
|
|
319
319
|
} catch { continue; }
|
|
320
320
|
|
|
@@ -335,7 +335,7 @@ async function checkWaitingHandoffs(api: OpenClawPluginApi, teamId: string, team
|
|
|
335
335
|
nodeOutputRel: string;
|
|
336
336
|
};
|
|
337
337
|
try {
|
|
338
|
-
marker =
|
|
338
|
+
marker = await readJsonFile<typeof marker>(waitPath);
|
|
339
339
|
} catch { continue; }
|
|
340
340
|
|
|
341
341
|
// Check timeout
|
|
@@ -416,7 +416,7 @@ async function checkWaitingHandoffs(api: OpenClawPluginApi, teamId: string, team
|
|
|
416
416
|
const workflowsDir = path.join(teamDir, 'shared-context', 'workflows');
|
|
417
417
|
let workflow;
|
|
418
418
|
try {
|
|
419
|
-
const wfRaw = await
|
|
419
|
+
const wfRaw = await readTextFile(path.join(workflowsDir, run.workflow.file));
|
|
420
420
|
workflow = normalizeWorkflow(JSON.parse(wfRaw));
|
|
421
421
|
} catch { workflow = null; }
|
|
422
422
|
|
|
@@ -1505,7 +1505,7 @@ export async function runWorkflowWorkerTick(api: OpenClawPluginApi, opts: {
|
|
|
1505
1505
|
if (!wf.endsWith('.json')) continue;
|
|
1506
1506
|
try {
|
|
1507
1507
|
const wfPath = path.join(targetWorkflowsDir, wf);
|
|
1508
|
-
const wfRaw = await
|
|
1508
|
+
const wfRaw = await readTextFile(wfPath);
|
|
1509
1509
|
const wfParsed = JSON.parse(wfRaw);
|
|
1510
1510
|
if (String(wfParsed.id ?? '') === targetWorkflowId || String(wfParsed.name ?? '') === targetWorkflowId) {
|
|
1511
1511
|
targetWorkflowFile = wf;
|