agent-relay 3.1.0 → 3.1.1

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.
Files changed (148) hide show
  1. package/bin/agent-relay-broker-linux-x64 +0 -0
  2. package/package.json +9 -9
  3. package/packages/acp-bridge/package.json +2 -2
  4. package/packages/config/package.json +1 -1
  5. package/packages/hooks/package.json +4 -4
  6. package/packages/memory/package.json +2 -2
  7. package/packages/openclaw/README.md +78 -0
  8. package/packages/openclaw/bin/relay-openclaw.mjs +2 -0
  9. package/packages/openclaw/bridge/bridge.mjs +305 -0
  10. package/packages/openclaw/dist/__tests__/gateway-threads.test.d.ts +2 -0
  11. package/packages/openclaw/dist/__tests__/gateway-threads.test.d.ts.map +1 -0
  12. package/packages/openclaw/dist/__tests__/gateway-threads.test.js +320 -0
  13. package/packages/openclaw/dist/__tests__/gateway-threads.test.js.map +1 -0
  14. package/packages/openclaw/dist/__tests__/naming.test.d.ts +2 -0
  15. package/packages/openclaw/dist/__tests__/naming.test.d.ts.map +1 -0
  16. package/packages/openclaw/dist/__tests__/naming.test.js +21 -0
  17. package/packages/openclaw/dist/__tests__/naming.test.js.map +1 -0
  18. package/packages/openclaw/dist/__tests__/spawn-manager.test.d.ts +2 -0
  19. package/packages/openclaw/dist/__tests__/spawn-manager.test.d.ts.map +1 -0
  20. package/packages/openclaw/dist/__tests__/spawn-manager.test.js +126 -0
  21. package/packages/openclaw/dist/__tests__/spawn-manager.test.js.map +1 -0
  22. package/packages/openclaw/dist/auth/converter.d.ts +28 -0
  23. package/packages/openclaw/dist/auth/converter.d.ts.map +1 -0
  24. package/packages/openclaw/dist/auth/converter.js +64 -0
  25. package/packages/openclaw/dist/auth/converter.js.map +1 -0
  26. package/packages/openclaw/dist/cli.d.ts +2 -0
  27. package/packages/openclaw/dist/cli.d.ts.map +1 -0
  28. package/packages/openclaw/dist/cli.js +230 -0
  29. package/packages/openclaw/dist/cli.js.map +1 -0
  30. package/packages/openclaw/dist/config.d.ts +27 -0
  31. package/packages/openclaw/dist/config.d.ts.map +1 -0
  32. package/packages/openclaw/dist/config.js +97 -0
  33. package/packages/openclaw/dist/config.js.map +1 -0
  34. package/packages/openclaw/dist/control.d.ts +22 -0
  35. package/packages/openclaw/dist/control.d.ts.map +1 -0
  36. package/packages/openclaw/dist/control.js +58 -0
  37. package/packages/openclaw/dist/control.js.map +1 -0
  38. package/packages/openclaw/dist/gateway.d.ts +71 -0
  39. package/packages/openclaw/dist/gateway.d.ts.map +1 -0
  40. package/packages/openclaw/dist/gateway.js +785 -0
  41. package/packages/openclaw/dist/gateway.js.map +1 -0
  42. package/packages/openclaw/dist/identity/contract.d.ts +11 -0
  43. package/packages/openclaw/dist/identity/contract.d.ts.map +1 -0
  44. package/packages/openclaw/dist/identity/contract.js +40 -0
  45. package/packages/openclaw/dist/identity/contract.js.map +1 -0
  46. package/packages/openclaw/dist/identity/files.d.ts +33 -0
  47. package/packages/openclaw/dist/identity/files.d.ts.map +1 -0
  48. package/packages/openclaw/dist/identity/files.js +145 -0
  49. package/packages/openclaw/dist/identity/files.js.map +1 -0
  50. package/packages/openclaw/dist/identity/model.d.ts +11 -0
  51. package/packages/openclaw/dist/identity/model.d.ts.map +1 -0
  52. package/packages/openclaw/dist/identity/model.js +28 -0
  53. package/packages/openclaw/dist/identity/model.js.map +1 -0
  54. package/packages/openclaw/dist/identity/naming.d.ts +5 -0
  55. package/packages/openclaw/dist/identity/naming.d.ts.map +1 -0
  56. package/packages/openclaw/dist/identity/naming.js +7 -0
  57. package/packages/openclaw/dist/identity/naming.js.map +1 -0
  58. package/packages/openclaw/dist/index.d.ts +20 -0
  59. package/packages/openclaw/dist/index.d.ts.map +1 -0
  60. package/packages/openclaw/dist/index.js +27 -0
  61. package/packages/openclaw/dist/index.js.map +1 -0
  62. package/packages/openclaw/dist/inject.d.ts +14 -0
  63. package/packages/openclaw/dist/inject.d.ts.map +1 -0
  64. package/packages/openclaw/dist/inject.js +66 -0
  65. package/packages/openclaw/dist/inject.js.map +1 -0
  66. package/packages/openclaw/dist/mcp/server.d.ts +8 -0
  67. package/packages/openclaw/dist/mcp/server.d.ts.map +1 -0
  68. package/packages/openclaw/dist/mcp/server.js +105 -0
  69. package/packages/openclaw/dist/mcp/server.js.map +1 -0
  70. package/packages/openclaw/dist/mcp/tools.d.ts +17 -0
  71. package/packages/openclaw/dist/mcp/tools.d.ts.map +1 -0
  72. package/packages/openclaw/dist/mcp/tools.js +145 -0
  73. package/packages/openclaw/dist/mcp/tools.js.map +1 -0
  74. package/packages/openclaw/dist/runtime/openclaw-config.d.ts +20 -0
  75. package/packages/openclaw/dist/runtime/openclaw-config.d.ts.map +1 -0
  76. package/packages/openclaw/dist/runtime/openclaw-config.js +50 -0
  77. package/packages/openclaw/dist/runtime/openclaw-config.js.map +1 -0
  78. package/packages/openclaw/dist/runtime/patch.d.ts +24 -0
  79. package/packages/openclaw/dist/runtime/patch.d.ts.map +1 -0
  80. package/packages/openclaw/dist/runtime/patch.js +92 -0
  81. package/packages/openclaw/dist/runtime/patch.js.map +1 -0
  82. package/packages/openclaw/dist/runtime/setup.d.ts +26 -0
  83. package/packages/openclaw/dist/runtime/setup.d.ts.map +1 -0
  84. package/packages/openclaw/dist/runtime/setup.js +58 -0
  85. package/packages/openclaw/dist/runtime/setup.js.map +1 -0
  86. package/packages/openclaw/dist/setup.d.ts +29 -0
  87. package/packages/openclaw/dist/setup.d.ts.map +1 -0
  88. package/packages/openclaw/dist/setup.js +300 -0
  89. package/packages/openclaw/dist/setup.js.map +1 -0
  90. package/packages/openclaw/dist/spawn/docker.d.ts +58 -0
  91. package/packages/openclaw/dist/spawn/docker.d.ts.map +1 -0
  92. package/packages/openclaw/dist/spawn/docker.js +222 -0
  93. package/packages/openclaw/dist/spawn/docker.js.map +1 -0
  94. package/packages/openclaw/dist/spawn/manager.d.ts +45 -0
  95. package/packages/openclaw/dist/spawn/manager.d.ts.map +1 -0
  96. package/packages/openclaw/dist/spawn/manager.js +140 -0
  97. package/packages/openclaw/dist/spawn/manager.js.map +1 -0
  98. package/packages/openclaw/dist/spawn/process.d.ts +16 -0
  99. package/packages/openclaw/dist/spawn/process.d.ts.map +1 -0
  100. package/packages/openclaw/dist/spawn/process.js +241 -0
  101. package/packages/openclaw/dist/spawn/process.js.map +1 -0
  102. package/packages/openclaw/dist/spawn/types.d.ts +42 -0
  103. package/packages/openclaw/dist/spawn/types.d.ts.map +1 -0
  104. package/packages/openclaw/dist/spawn/types.js +2 -0
  105. package/packages/openclaw/dist/spawn/types.js.map +1 -0
  106. package/packages/openclaw/dist/types.d.ts +37 -0
  107. package/packages/openclaw/dist/types.d.ts.map +1 -0
  108. package/packages/openclaw/dist/types.js +2 -0
  109. package/packages/openclaw/dist/types.js.map +1 -0
  110. package/packages/openclaw/package.json +63 -0
  111. package/packages/openclaw/skill/SKILL.md +194 -0
  112. package/packages/openclaw/src/__tests__/gateway-threads.test.ts +384 -0
  113. package/packages/openclaw/src/__tests__/naming.test.ts +24 -0
  114. package/packages/openclaw/src/__tests__/spawn-manager.test.ts +152 -0
  115. package/packages/openclaw/src/auth/converter.ts +90 -0
  116. package/packages/openclaw/src/cli.ts +269 -0
  117. package/packages/openclaw/src/config.ts +124 -0
  118. package/packages/openclaw/src/control.ts +100 -0
  119. package/packages/openclaw/src/gateway.ts +941 -0
  120. package/packages/openclaw/src/identity/contract.ts +44 -0
  121. package/packages/openclaw/src/identity/files.ts +198 -0
  122. package/packages/openclaw/src/identity/model.ts +27 -0
  123. package/packages/openclaw/src/identity/naming.ts +6 -0
  124. package/packages/openclaw/src/index.ts +59 -0
  125. package/packages/openclaw/src/inject.ts +77 -0
  126. package/packages/openclaw/src/mcp/server.ts +121 -0
  127. package/packages/openclaw/src/mcp/tools.ts +174 -0
  128. package/packages/openclaw/src/runtime/openclaw-config.ts +64 -0
  129. package/packages/openclaw/src/runtime/patch.ts +103 -0
  130. package/packages/openclaw/src/runtime/setup.ts +89 -0
  131. package/packages/openclaw/src/setup.ts +336 -0
  132. package/packages/openclaw/src/spawn/docker.ts +261 -0
  133. package/packages/openclaw/src/spawn/manager.ts +181 -0
  134. package/packages/openclaw/src/spawn/process.ts +272 -0
  135. package/packages/openclaw/src/spawn/types.ts +43 -0
  136. package/packages/openclaw/src/types.ts +38 -0
  137. package/packages/openclaw/templates/SOUL.md.template +34 -0
  138. package/packages/openclaw/tsconfig.json +12 -0
  139. package/packages/policy/package.json +2 -2
  140. package/packages/sdk/package.json +2 -2
  141. package/packages/sdk-py/pyproject.toml +1 -1
  142. package/packages/telemetry/package.json +1 -1
  143. package/packages/trajectory/package.json +2 -2
  144. package/packages/user-directory/package.json +2 -2
  145. package/packages/utils/package.json +2 -2
  146. package/bin/agent-relay-broker-darwin-arm64 +0 -0
  147. package/bin/agent-relay-broker-darwin-x64 +0 -0
  148. package/bin/agent-relay-broker-linux-arm64 +0 -0
@@ -0,0 +1,174 @@
1
+ import { InboundGateway } from '../gateway.js';
2
+
3
+ /** Control API base URL — the gateway's spawn control server. */
4
+ const CONTROL_URL = `http://127.0.0.1:${
5
+ Number(process.env.RELAYCAST_CONTROL_PORT) || InboundGateway.DEFAULT_CONTROL_PORT
6
+ }`;
7
+
8
+ export interface McpToolDefinition {
9
+ name: string;
10
+ description: string;
11
+ inputSchema: Record<string, unknown>;
12
+ }
13
+
14
+ export function getToolDefinitions(): McpToolDefinition[] {
15
+ return [
16
+ {
17
+ name: 'spawn_openclaw',
18
+ description:
19
+ 'Spawn a new independent OpenClaw instance. The spawned instance gets its own gateway, ' +
20
+ 'relay broker, and Relaycast messaging. It runs as an independent peer, not a child.',
21
+ inputSchema: {
22
+ type: 'object',
23
+ properties: {
24
+ name: {
25
+ type: 'string',
26
+ description: 'Name for the new OpenClaw instance (e.g. "researcher", "coder").',
27
+ },
28
+ role: {
29
+ type: 'string',
30
+ description: 'Role description for the agent (e.g. "code review specialist").',
31
+ },
32
+ model: {
33
+ type: 'string',
34
+ description: 'Model reference (e.g. "openai-codex/gpt-5.3-codex"). Defaults to parent model.',
35
+ },
36
+ channels: {
37
+ type: 'array',
38
+ items: { type: 'string' },
39
+ description: 'Relaycast channels to join (default: ["#general"]).',
40
+ },
41
+ system_prompt: {
42
+ type: 'string',
43
+ description: 'System prompt / task description for the spawned agent.',
44
+ },
45
+ },
46
+ required: ['name'],
47
+ },
48
+ },
49
+ {
50
+ name: 'list_openclaws',
51
+ description: 'List all currently running OpenClaw instances spawned by this agent.',
52
+ inputSchema: {
53
+ type: 'object',
54
+ properties: {},
55
+ },
56
+ },
57
+ {
58
+ name: 'release_openclaw',
59
+ description: 'Stop and release a spawned OpenClaw instance by name or ID.',
60
+ inputSchema: {
61
+ type: 'object',
62
+ properties: {
63
+ name: {
64
+ type: 'string',
65
+ description: 'Name of the OpenClaw to release (as provided during spawn).',
66
+ },
67
+ id: {
68
+ type: 'string',
69
+ description: 'ID of the OpenClaw to release (from list_openclaws).',
70
+ },
71
+ },
72
+ },
73
+ },
74
+ ];
75
+ }
76
+
77
+ export async function handleToolCall(
78
+ toolName: string,
79
+ args: Record<string, unknown>,
80
+ ): Promise<{ content: Array<{ type: 'text'; text: string }> }> {
81
+ switch (toolName) {
82
+ case 'spawn_openclaw': {
83
+ const name = args.name as string;
84
+ if (!name) {
85
+ return text('Error: "name" is required.');
86
+ }
87
+
88
+ try {
89
+ const res = await fetch(`${CONTROL_URL}/spawn`, {
90
+ method: 'POST',
91
+ headers: { 'Content-Type': 'application/json' },
92
+ body: JSON.stringify(args),
93
+ });
94
+ const body = await res.json() as Record<string, unknown>;
95
+ if (!body.ok) {
96
+ return text(`Failed to spawn "${name}": ${body.error ?? 'unknown error'}`);
97
+ }
98
+ return text(
99
+ `Spawned OpenClaw "${name}"\n` +
100
+ ` Agent name: ${body.agentName}\n` +
101
+ ` ID: ${body.id}\n` +
102
+ ` Gateway port: ${body.gatewayPort}\n` +
103
+ ` Total active: ${body.active}`,
104
+ );
105
+ } catch (err) {
106
+ return text(
107
+ `Failed to spawn "${name}": ${err instanceof Error ? err.message : String(err)}\n` +
108
+ 'Is the gateway running? Start it with: relay-openclaw gateway',
109
+ );
110
+ }
111
+ }
112
+
113
+ case 'list_openclaws': {
114
+ try {
115
+ const res = await fetch(`${CONTROL_URL}/list`);
116
+ const body = await res.json() as Record<string, unknown>;
117
+ const claws = body.claws as Array<Record<string, unknown>>;
118
+ if (!claws || claws.length === 0) {
119
+ return text('No spawned OpenClaws currently running.');
120
+ }
121
+ const lines = claws.map(
122
+ (h) => `- ${h.name} → ${h.agentName} (id: ${h.id}, port: ${h.gatewayPort})`,
123
+ );
124
+ return text(`Active OpenClaws (${claws.length}):\n${lines.join('\n')}`);
125
+ } catch (err) {
126
+ return text(
127
+ `Failed to list claws: ${err instanceof Error ? err.message : String(err)}\n` +
128
+ 'Is the gateway running? Start it with: relay-openclaw gateway',
129
+ );
130
+ }
131
+ }
132
+
133
+ case 'release_openclaw': {
134
+ const name = args.name as string | undefined;
135
+ const id = args.id as string | undefined;
136
+
137
+ if (!name && !id) {
138
+ return text('Error: provide either "name" or "id" to release.');
139
+ }
140
+
141
+ try {
142
+ const res = await fetch(`${CONTROL_URL}/release`, {
143
+ method: 'POST',
144
+ headers: { 'Content-Type': 'application/json' },
145
+ body: JSON.stringify({ name, id }),
146
+ });
147
+ const body = await res.json() as Record<string, unknown>;
148
+ if (body.ok) {
149
+ return text(`Released OpenClaw "${name ?? id}". Active: ${body.active}`);
150
+ }
151
+ return text(`OpenClaw "${name ?? id}" not found among active spawns.`);
152
+ } catch (err) {
153
+ return text(
154
+ `Failed to release: ${err instanceof Error ? err.message : String(err)}\n` +
155
+ 'Is the gateway running? Start it with: relay-openclaw gateway',
156
+ );
157
+ }
158
+ }
159
+
160
+ default:
161
+ return text(`Unknown tool: ${toolName}`);
162
+ }
163
+ }
164
+
165
+ function text(message: string) {
166
+ return { content: [{ type: 'text' as const, text: message }] };
167
+ }
168
+
169
+ /**
170
+ * Cleanup: no-op since spawns are managed by the gateway process.
171
+ */
172
+ export async function cleanup(): Promise<void> {
173
+ // Spawns live in the gateway — nothing to clean up here.
174
+ }
@@ -0,0 +1,64 @@
1
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+
4
+ export interface OpenClawConfigOptions {
5
+ /** Fully-qualified model ref, e.g. "openai-codex/gpt-5.3-codex". */
6
+ modelRef: string;
7
+ /** Path to ~/.openclaw/ (default: $HOME/.openclaw). */
8
+ openclawHome?: string;
9
+ /** Default workspace path (default: ~/.openclaw/workspace). */
10
+ workspacePath?: string;
11
+ /** MCP servers to include. Keys are server names, values are MCP server configs. */
12
+ mcpServers?: Record<string, { command: string; args: string[]; env?: Record<string, string> }>;
13
+ }
14
+
15
+ /**
16
+ * Write (or update) ~/.openclaw/openclaw.json with model, workspace, skipBootstrap,
17
+ * and MCP server configuration.
18
+ */
19
+ export async function writeOpenClawConfig(options: OpenClawConfigOptions): Promise<void> {
20
+ const home = options.openclawHome ?? join(process.env.HOME ?? '/home/node', '.openclaw');
21
+ await mkdir(home, { recursive: true });
22
+
23
+ const configPath = join(home, 'openclaw.json');
24
+ let config: Record<string, unknown> = {};
25
+
26
+ try {
27
+ const raw = await readFile(configPath, 'utf8');
28
+ config = JSON.parse(raw);
29
+ } catch {
30
+ // File doesn't exist or isn't valid JSON — start fresh
31
+ config = {};
32
+ }
33
+ if (!config || typeof config !== 'object') config = {};
34
+
35
+ // agents.defaults
36
+ if (!config.agents || typeof config.agents !== 'object') config.agents = {};
37
+ const agents = config.agents as Record<string, unknown>;
38
+ if (!agents.defaults || typeof agents.defaults !== 'object') agents.defaults = {};
39
+ const defaults = agents.defaults as Record<string, unknown>;
40
+
41
+ if (!defaults.workspace || typeof defaults.workspace !== 'string') {
42
+ defaults.workspace = options.workspacePath ?? '~/.openclaw/workspace';
43
+ }
44
+
45
+ // Model shape: { primary: "provider/model" }
46
+ if (typeof defaults.model === 'string') {
47
+ defaults.model = { primary: defaults.model };
48
+ } else if (!defaults.model || typeof defaults.model !== 'object') {
49
+ defaults.model = {};
50
+ }
51
+ (defaults.model as Record<string, string>).primary = options.modelRef;
52
+
53
+ defaults.skipBootstrap = true;
54
+
55
+ // MCP servers
56
+ if (options.mcpServers) {
57
+ if (!config.mcpServers || typeof config.mcpServers !== 'object') {
58
+ config.mcpServers = {};
59
+ }
60
+ Object.assign(config.mcpServers as Record<string, unknown>, options.mcpServers);
61
+ }
62
+
63
+ await writeFile(configPath, JSON.stringify(config, null, 2) + '\n', 'utf8');
64
+ }
@@ -0,0 +1,103 @@
1
+ import { readdir, readFile, writeFile, rm } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { existsSync } from 'node:fs';
4
+
5
+ /**
6
+ * Patch OpenClaw's compiled dist JS files to replace hardcoded identity constants.
7
+ *
8
+ * FRAGILE: This is a best-effort operation. OpenClaw bakes Claude defaults into
9
+ * its compiled output. These patterns may change between OpenClaw versions.
10
+ * Prefer runtime identity enforcement (SOUL.md, runtime-identity.json, identity
11
+ * preamble in bridge) over relying on this patch.
12
+ *
13
+ * Known hardcoded constants (as of OpenClaw ~0.x):
14
+ * - DEFAULT_MODEL = "claude-opus-4-6"
15
+ * - KILOCODE_DEFAULT_MODEL_ID = "anthropic/claude-opus-4.6"
16
+ * - KILOCODE_DEFAULT_MODEL_NAME = "Claude Opus 4.6"
17
+ * - "Claude Code" branding
18
+ *
19
+ * @param distDir - Path to OpenClaw's dist directory (e.g. /usr/lib/node_modules/openclaw/dist)
20
+ * @param modelRef - Full model reference (e.g. "openai-codex/gpt-5.3-codex")
21
+ * @returns Number of files patched, or 0 if dist not found or patching skipped
22
+ */
23
+ export async function patchOpenClawDist(distDir: string, modelRef: string): Promise<number> {
24
+ if (!existsSync(distDir)) {
25
+ process.stderr.write(`[patch] OpenClaw dist not found at ${distDir}, skipping\n`);
26
+ return 0;
27
+ }
28
+
29
+ // Extract bare model ID (e.g. "gpt-5.3-codex" from "openai-codex/gpt-5.3-codex")
30
+ const modelId = modelRef.includes('/') ? modelRef.split('/').pop()! : modelRef;
31
+
32
+ // Order matters: replace fully-qualified patterns before bare model IDs,
33
+ // otherwise the bare pattern destroys the substring the qualified pattern needs.
34
+ const replacements: [RegExp, string][] = [
35
+ [/anthropic\/claude-opus-4\.6/g, modelRef],
36
+ [/Claude Opus 4\.6/g, modelRef],
37
+ [/claude-opus-4\.6/g, modelId],
38
+ [/claude-opus-4-6/g, modelId],
39
+ [/Claude Code/g, 'OpenClaw Agent'],
40
+ ];
41
+
42
+ let patchedCount = 0;
43
+
44
+ try {
45
+ async function walkAndPatch(dir: string): Promise<void> {
46
+ const entries = await readdir(dir, { withFileTypes: true });
47
+
48
+ for (const entry of entries) {
49
+ const fullPath = join(dir, entry.name);
50
+ if (entry.isDirectory()) {
51
+ await walkAndPatch(fullPath);
52
+ } else if (entry.isFile() && entry.name.endsWith('.js')) {
53
+ try {
54
+ let content = await readFile(fullPath, 'utf8');
55
+ let modified = false;
56
+
57
+ for (const [pattern, replacement] of replacements) {
58
+ const newContent = content.replace(pattern, replacement);
59
+ if (newContent !== content) {
60
+ content = newContent;
61
+ modified = true;
62
+ }
63
+ }
64
+
65
+ if (modified) {
66
+ await writeFile(fullPath, content, 'utf8');
67
+ patchedCount++;
68
+ }
69
+ } catch (err) {
70
+ // Individual file patch failure is non-fatal
71
+ process.stderr.write(
72
+ `[patch] Warning: could not patch ${fullPath}: ${err instanceof Error ? err.message : String(err)}\n`,
73
+ );
74
+ }
75
+ }
76
+ }
77
+ }
78
+
79
+ await walkAndPatch(distDir);
80
+ } catch (err) {
81
+ // Entire patching failure is non-fatal — runtime identity enforcement is the primary defense
82
+ process.stderr.write(
83
+ `[patch] Warning: dist patching failed (non-fatal): ${err instanceof Error ? err.message : String(err)}\n`,
84
+ );
85
+ }
86
+
87
+ if (patchedCount > 0) {
88
+ process.stderr.write(`[patch] Patched ${patchedCount} file(s) in ${distDir}\n`);
89
+ }
90
+
91
+ return patchedCount;
92
+ }
93
+
94
+ /**
95
+ * Clear JIT cache (/tmp/jiti/) which may contain unpatched constants.
96
+ */
97
+ export async function clearJitCache(): Promise<void> {
98
+ try {
99
+ await rm('/tmp/jiti', { recursive: true, force: true });
100
+ } catch {
101
+ // Ignore — cache may not exist
102
+ }
103
+ }
@@ -0,0 +1,89 @@
1
+ import { join } from 'node:path';
2
+ import { unlink } from 'node:fs/promises';
3
+ import { existsSync } from 'node:fs';
4
+
5
+ import { normalizeModelRef } from '../identity/model.js';
6
+ import { convertCodexAuth } from '../auth/converter.js';
7
+ import { writeOpenClawConfig } from './openclaw-config.js';
8
+ import { patchOpenClawDist, clearJitCache } from './patch.js';
9
+ import {
10
+ generateSoulMd,
11
+ generateIdentityMd,
12
+ ensureWorkspace,
13
+ } from '../identity/files.js';
14
+
15
+ export interface RuntimeSetupOptions {
16
+ /** Raw model string (e.g. "gpt-5.3-codex"). Defaults to env OPENCLAW_MODEL. */
17
+ model?: string;
18
+ /** Agent name. Defaults to env OPENCLAW_NAME or AGENT_NAME. */
19
+ name?: string;
20
+ /** Workspace ID. Defaults to env OPENCLAW_WORKSPACE_ID. */
21
+ workspaceId?: string;
22
+ /** Agent role. Defaults to env OPENCLAW_ROLE. */
23
+ role?: string;
24
+ /** OpenClaw dist directory for patching. Defaults to /usr/lib/node_modules/openclaw/dist. */
25
+ openclawDistDir?: string;
26
+ /** Home directory. Defaults to $HOME. */
27
+ homeDir?: string;
28
+ }
29
+
30
+ /**
31
+ * Full runtime setup: auth conversion, config writing, identity files, dist patching, JIT cache clear.
32
+ *
33
+ * This replaces the inline node -e block + shell logic in start-claw.sh.
34
+ * Call this before starting the OpenClaw gateway.
35
+ */
36
+ export async function runtimeSetup(options: RuntimeSetupOptions = {}): Promise<{
37
+ modelRef: string;
38
+ agentName: string;
39
+ workspaceId: string;
40
+ }> {
41
+ const home = options.homeDir ?? process.env.HOME ?? '/home/node';
42
+ const model = options.model ?? process.env.OPENCLAW_MODEL ?? 'openai-codex/gpt-5.3-codex';
43
+ const name = options.name ?? process.env.OPENCLAW_NAME ?? process.env.AGENT_NAME ?? 'agent';
44
+ const workspaceId = options.workspaceId ?? process.env.OPENCLAW_WORKSPACE_ID ?? 'unknown';
45
+ const role = options.role ?? process.env.OPENCLAW_ROLE ?? 'general';
46
+ // Resolve OpenClaw dist dir: try explicit, then known install locations
47
+ const distDirCandidates = options.openclawDistDir
48
+ ? [options.openclawDistDir]
49
+ : [
50
+ '/usr/lib/node_modules/openclaw/dist', // Global npm (ClawRunner sandbox)
51
+ '/app/dist', // Vanilla Docker image
52
+ '/usr/local/lib/node_modules/openclaw/dist', // Global npm (macOS/other)
53
+ ];
54
+ const distDir = distDirCandidates.find((d) => existsSync(d)) ?? distDirCandidates[0];
55
+
56
+ // 1. Convert codex auth
57
+ const { preferredProvider } = await convertCodexAuth();
58
+ const modelRef = normalizeModelRef(model, preferredProvider);
59
+
60
+ // 2. Write openclaw.json config
61
+ await writeOpenClawConfig({
62
+ modelRef,
63
+ openclawHome: join(home, '.openclaw'),
64
+ });
65
+
66
+ // 3. Write identity files in workspace
67
+ const wsDir = join(home, '.openclaw', 'workspace');
68
+ await ensureWorkspace({
69
+ workspacePath: wsDir,
70
+ workspaceId,
71
+ clawName: name,
72
+ role,
73
+ modelRef,
74
+ });
75
+
76
+ // Remove BOOTSTRAP.md if present (agent is pre-configured)
77
+ const bootstrapPath = join(wsDir, 'BOOTSTRAP.md');
78
+ if (existsSync(bootstrapPath)) {
79
+ await unlink(bootstrapPath);
80
+ }
81
+
82
+ // 4. Patch OpenClaw dist
83
+ await patchOpenClawDist(distDir, modelRef);
84
+
85
+ // 5. Clear JIT cache
86
+ await clearJitCache();
87
+
88
+ return { modelRef, agentName: name, workspaceId };
89
+ }