@joshualelon/clawdbot-skill-flow 0.4.0 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@joshualelon/clawdbot-skill-flow",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Multi-step workflow orchestration plugin for Clawdbot",
6
6
  "keywords": [
package/src/config.ts CHANGED
@@ -2,6 +2,11 @@ import { z } from "zod";
2
2
 
3
3
  export const SkillFlowConfigSchema = z
4
4
  .object({
5
+ flowsDir: z
6
+ .string()
7
+ .optional()
8
+ .describe("Custom flows directory (default: ~/.clawdbot/flows)"),
9
+
5
10
  sessionTimeoutMinutes: z
6
11
  .number()
7
12
  .int()
@@ -35,11 +40,25 @@ export const SkillFlowConfigSchema = z
35
40
 
36
41
  export type SkillFlowConfig = z.infer<typeof SkillFlowConfigSchema>;
37
42
 
43
+ // Store parsed config for access by other modules
44
+ let pluginConfig: SkillFlowConfig | null = null;
45
+
38
46
  /**
39
47
  * Parse and validate plugin config with defaults
40
48
  */
41
49
  export function parseSkillFlowConfig(
42
50
  raw: unknown
43
51
  ): SkillFlowConfig {
44
- return SkillFlowConfigSchema.parse(raw ?? {});
52
+ pluginConfig = SkillFlowConfigSchema.parse(raw ?? {});
53
+ return pluginConfig;
54
+ }
55
+
56
+ /**
57
+ * Get the current plugin config (must call parseSkillFlowConfig first)
58
+ */
59
+ export function getPluginConfig(): SkillFlowConfig {
60
+ if (!pluginConfig) {
61
+ throw new Error("Plugin config not initialized. Call parseSkillFlowConfig first.");
62
+ }
63
+ return pluginConfig;
45
64
  }
@@ -10,6 +10,20 @@ import {
10
10
  resolvePathSafely,
11
11
  validatePathWithinBase,
12
12
  } from "../security/path-validation.js";
13
+ import { getPluginConfig } from "../config.js";
14
+
15
+ /**
16
+ * Get the flows directory path (same logic as flow-store.ts)
17
+ */
18
+ function getFlowsDir(api: ClawdbotPluginApi): string {
19
+ const config = getPluginConfig();
20
+ if (config.flowsDir) {
21
+ const expandedPath = config.flowsDir.replace(/^~/, process.env.HOME || "~");
22
+ return path.resolve(expandedPath);
23
+ }
24
+ const stateDir = api.runtime.state.resolveStateDir();
25
+ return path.join(stateDir, "flows");
26
+ }
13
27
 
14
28
  /**
15
29
  * Load hooks from a file path
@@ -23,7 +37,7 @@ export async function loadHooks(
23
37
  ): Promise<FlowHooks | null> {
24
38
  try {
25
39
  // Validate path is within flows directory
26
- const flowsDir = path.join(api.runtime.state.resolveStateDir(), "flows");
40
+ const flowsDir = getFlowsDir(api);
27
41
  validatePathWithinBase(hooksPath, flowsDir, "hooks file");
28
42
 
29
43
  // Check if file exists
@@ -55,7 +69,7 @@ export async function loadStorageBackend(
55
69
  ): Promise<StorageBackend | null> {
56
70
  try {
57
71
  // Validate path is within flows directory
58
- const flowsDir = path.join(api.runtime.state.resolveStateDir(), "flows");
72
+ const flowsDir = getFlowsDir(api);
59
73
  validatePathWithinBase(backendPath, flowsDir, "storage backend");
60
74
 
61
75
  // Check if file exists
@@ -87,11 +101,8 @@ export function resolveFlowPath(
87
101
  flowName: string,
88
102
  relativePath: string
89
103
  ): string {
90
- const flowDir = path.join(
91
- api.runtime.state.resolveStateDir(),
92
- "flows",
93
- flowName
94
- );
104
+ const flowsDir = getFlowsDir(api);
105
+ const flowDir = path.join(flowsDir, flowName);
95
106
 
96
107
  // Validate that relativePath doesn't escape flowDir
97
108
  return resolvePathSafely(flowDir, relativePath, "flow path");
@@ -9,6 +9,7 @@ import { promisify } from "node:util";
9
9
  import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
10
10
  import type { FlowMetadata } from "../types.js";
11
11
  import { FlowMetadataSchema } from "../validation.js";
12
+ import { getPluginConfig } from "../config.js";
12
13
 
13
14
  const lock = promisify(lockfile.lock);
14
15
  const unlock = promisify(lockfile.unlock);
@@ -17,6 +18,12 @@ const unlock = promisify(lockfile.unlock);
17
18
  * Get the flows directory path
18
19
  */
19
20
  function getFlowsDir(api: ClawdbotPluginApi): string {
21
+ const config = getPluginConfig();
22
+ if (config.flowsDir) {
23
+ // Expand ~ to home directory
24
+ const expandedPath = config.flowsDir.replace(/^~/, process.env.HOME || "~");
25
+ return path.resolve(expandedPath);
26
+ }
20
27
  const stateDir = api.runtime.state.resolveStateDir();
21
28
  return path.join(stateDir, "flows");
22
29
  }
@@ -7,17 +7,31 @@ import path from "node:path";
7
7
  import type { ClawdbotPluginApi } from "clawdbot/plugin-sdk";
8
8
  import type { FlowSession, FlowMetadata } from "../types.js";
9
9
  import type { SkillFlowConfig } from "../config.js";
10
+ import { getPluginConfig } from "../config.js";
10
11
  import {
11
12
  loadStorageBackend,
12
13
  resolveFlowPath,
13
14
  } from "../engine/hooks-loader.js";
14
15
 
16
+ /**
17
+ * Get the flows directory path (same logic as flow-store.ts)
18
+ */
19
+ function getFlowsDir(api: ClawdbotPluginApi): string {
20
+ const config = getPluginConfig();
21
+ if (config.flowsDir) {
22
+ const expandedPath = config.flowsDir.replace(/^~/, process.env.HOME || "~");
23
+ return path.resolve(expandedPath);
24
+ }
25
+ const stateDir = api.runtime.state.resolveStateDir();
26
+ return path.join(stateDir, "flows");
27
+ }
28
+
15
29
  /**
16
30
  * Get history file path for a flow
17
31
  */
18
32
  function getHistoryPath(api: ClawdbotPluginApi, flowName: string): string {
19
- const stateDir = api.runtime.state.resolveStateDir();
20
- return path.join(stateDir, "flows", flowName, "history.jsonl");
33
+ const flowsDir = getFlowsDir(api);
34
+ return path.join(flowsDir, flowName, "history.jsonl");
21
35
  }
22
36
 
23
37
  /**
package/src/types.ts CHANGED
@@ -43,6 +43,8 @@ export interface FlowMetadata {
43
43
  backend?: string; // Path to custom storage backend
44
44
  builtin?: boolean; // Also write to JSONL (default: true)
45
45
  };
46
+ // Allow extra fields (e.g., job config)
47
+ [key: string]: unknown;
46
48
  }
47
49
 
48
50
  export interface FlowSession {
package/src/validation.ts CHANGED
@@ -58,6 +58,7 @@ const StorageSchema = z
58
58
  .optional();
59
59
 
60
60
  // Complete flow metadata schema
61
+ // Use .passthrough() to allow extra fields (e.g., job config)
61
62
  export const FlowMetadataSchema = z.object({
62
63
  name: z.string(),
63
64
  description: z.string(),
@@ -67,7 +68,7 @@ export const FlowMetadataSchema = z.object({
67
68
  triggers: TriggerSchema,
68
69
  hooks: z.string().optional(),
69
70
  storage: StorageSchema,
70
- });
71
+ }).passthrough();
71
72
 
72
73
  /**
73
74
  * Normalize button input to Button object