@jiggai/recipes 0.4.51 → 0.4.52

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.
@@ -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.51",
5
+ "version": "0.4.52",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jiggai/recipes",
3
- "version": "0.4.51",
3
+ "version": "0.4.52",
4
4
  "description": "ClawRecipes plugin for OpenClaw (markdown recipes -> scaffold agents/teams)",
5
5
  "main": "index.ts",
6
6
  "type": "commonjs",
@@ -0,0 +1,49 @@
1
+ import type { OpenClawPluginApi } from 'openclaw/plugin-sdk';
2
+
3
+ function asRecord(v: unknown): Record<string, unknown> | null {
4
+ return v && typeof v === 'object' && !Array.isArray(v) ? (v as Record<string, unknown>) : null;
5
+ }
6
+
7
+ function asString(v: unknown): string {
8
+ return typeof v === 'string' ? v : (v == null ? '' : String(v));
9
+ }
10
+
11
+ function asPort(v: unknown): number | null {
12
+ if (typeof v === 'number' && Number.isFinite(v) && v > 0) return v;
13
+ if (typeof v === 'string' && v.trim()) {
14
+ const parsed = Number(v.trim());
15
+ if (Number.isFinite(parsed) && parsed > 0) return parsed;
16
+ }
17
+ return null;
18
+ }
19
+
20
+ function trimTrailingSlash(url: string): string {
21
+ return url.replace(/\/+$/, '');
22
+ }
23
+
24
+ function buildBaseUrl(host: string, port: number | null): string {
25
+ const trimmedHost = host.trim();
26
+ if (!trimmedHost) return 'http://localhost:7777';
27
+ if (/^https?:\/\//i.test(trimmedHost)) return trimTrailingSlash(trimmedHost);
28
+ const safeHost = trimmedHost.includes(':') && !trimmedHost.startsWith('[') ? `[${trimmedHost}]` : trimmedHost;
29
+ return trimTrailingSlash(`http://${safeHost}${port ? `:${port}` : ''}`);
30
+ }
31
+
32
+ export function getKitchenBaseUrl(api: OpenClawPluginApi): string {
33
+ const config = asRecord((api as unknown as { config?: unknown }).config) ?? {};
34
+ const envVars = asRecord(asRecord(config.env)?.vars);
35
+ const envBaseUrl = asString(envVars?.CK_BASE_URL).trim();
36
+ if (envBaseUrl) return trimTrailingSlash(envBaseUrl);
37
+
38
+ const kitchenConfig = asRecord(asRecord(asRecord(config.plugins)?.entries)?.kitchen)?.config;
39
+ const host = asString(kitchenConfig?.host).trim();
40
+ const port = asPort(kitchenConfig?.port);
41
+ if (host) return buildBaseUrl(host, port);
42
+
43
+ return 'http://localhost:7777';
44
+ }
45
+
46
+ export function buildKitchenWorkflowReviewUrl(api: OpenClawPluginApi, teamId: string, workflowId: string): string {
47
+ const baseUrl = getKitchenBaseUrl(api);
48
+ return `${baseUrl}/teams/${encodeURIComponent(teamId)}/workflows/${encodeURIComponent(workflowId)}`;
49
+ }
@@ -13,6 +13,7 @@ import { dequeueNextTask, enqueueTask, hasPendingTaskFor, releaseTaskClaim, comp
13
13
  import { loadPriorLlmInput, loadProposedPostTextFromPriorNode } from './workflow-node-output-readers';
14
14
  import { readTextFile } from './workflow-runner-io';
15
15
  import { resolveApprovalBindingTarget } from './workflow-node-executor';
16
+ import { buildKitchenWorkflowReviewUrl } from './kitchen-review-url';
16
17
  import {
17
18
  asRecord, asString, isRecord,
18
19
  normalizeWorkflow,
@@ -1032,6 +1033,7 @@ export async function runWorkflowWorkerTick(api: OpenClawPluginApi, opts: {
1032
1033
  }
1033
1034
  proposed = sanitizeDraftOnlyText(proposed);
1034
1035
 
1036
+ const kitchenReviewUrl = buildKitchenWorkflowReviewUrl(api, teamId, String(workflow.id ?? ''));
1035
1037
  const msg = [
1036
1038
  `Approval requested: ${workflow.name ?? workflow.id ?? workflowFile}`,
1037
1039
  `Ticket: ${path.relative(teamDir, curTicketPath)}`,
@@ -1040,7 +1042,7 @@ export async function runWorkflowWorkerTick(api: OpenClawPluginApi, opts: {
1040
1042
  `\nReply with:`,
1041
1043
  `- approve ${code}`,
1042
1044
  `- decline ${code} <what to change>`,
1043
- `\n(You can also review in Kitchen: ${process.env['CK_BASE_URL'] || 'http://localhost:7777'}/teams/${teamId}/workflows/${workflow.id ?? ''})`,
1045
+ `\n(You can also review in Kitchen: ${kitchenReviewUrl})`,
1044
1046
  ].join('\n');
1045
1047
 
1046
1048
  await toolsInvoke<ToolTextResult>(api, {