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.
- package/bin/agent-relay-broker-linux-x64 +0 -0
- package/package.json +9 -9
- package/packages/acp-bridge/package.json +2 -2
- package/packages/config/package.json +1 -1
- package/packages/hooks/package.json +4 -4
- package/packages/memory/package.json +2 -2
- package/packages/openclaw/README.md +78 -0
- package/packages/openclaw/bin/relay-openclaw.mjs +2 -0
- package/packages/openclaw/bridge/bridge.mjs +305 -0
- package/packages/openclaw/dist/__tests__/gateway-threads.test.d.ts +2 -0
- package/packages/openclaw/dist/__tests__/gateway-threads.test.d.ts.map +1 -0
- package/packages/openclaw/dist/__tests__/gateway-threads.test.js +320 -0
- package/packages/openclaw/dist/__tests__/gateway-threads.test.js.map +1 -0
- package/packages/openclaw/dist/__tests__/naming.test.d.ts +2 -0
- package/packages/openclaw/dist/__tests__/naming.test.d.ts.map +1 -0
- package/packages/openclaw/dist/__tests__/naming.test.js +21 -0
- package/packages/openclaw/dist/__tests__/naming.test.js.map +1 -0
- package/packages/openclaw/dist/__tests__/spawn-manager.test.d.ts +2 -0
- package/packages/openclaw/dist/__tests__/spawn-manager.test.d.ts.map +1 -0
- package/packages/openclaw/dist/__tests__/spawn-manager.test.js +126 -0
- package/packages/openclaw/dist/__tests__/spawn-manager.test.js.map +1 -0
- package/packages/openclaw/dist/auth/converter.d.ts +28 -0
- package/packages/openclaw/dist/auth/converter.d.ts.map +1 -0
- package/packages/openclaw/dist/auth/converter.js +64 -0
- package/packages/openclaw/dist/auth/converter.js.map +1 -0
- package/packages/openclaw/dist/cli.d.ts +2 -0
- package/packages/openclaw/dist/cli.d.ts.map +1 -0
- package/packages/openclaw/dist/cli.js +230 -0
- package/packages/openclaw/dist/cli.js.map +1 -0
- package/packages/openclaw/dist/config.d.ts +27 -0
- package/packages/openclaw/dist/config.d.ts.map +1 -0
- package/packages/openclaw/dist/config.js +97 -0
- package/packages/openclaw/dist/config.js.map +1 -0
- package/packages/openclaw/dist/control.d.ts +22 -0
- package/packages/openclaw/dist/control.d.ts.map +1 -0
- package/packages/openclaw/dist/control.js +58 -0
- package/packages/openclaw/dist/control.js.map +1 -0
- package/packages/openclaw/dist/gateway.d.ts +71 -0
- package/packages/openclaw/dist/gateway.d.ts.map +1 -0
- package/packages/openclaw/dist/gateway.js +785 -0
- package/packages/openclaw/dist/gateway.js.map +1 -0
- package/packages/openclaw/dist/identity/contract.d.ts +11 -0
- package/packages/openclaw/dist/identity/contract.d.ts.map +1 -0
- package/packages/openclaw/dist/identity/contract.js +40 -0
- package/packages/openclaw/dist/identity/contract.js.map +1 -0
- package/packages/openclaw/dist/identity/files.d.ts +33 -0
- package/packages/openclaw/dist/identity/files.d.ts.map +1 -0
- package/packages/openclaw/dist/identity/files.js +145 -0
- package/packages/openclaw/dist/identity/files.js.map +1 -0
- package/packages/openclaw/dist/identity/model.d.ts +11 -0
- package/packages/openclaw/dist/identity/model.d.ts.map +1 -0
- package/packages/openclaw/dist/identity/model.js +28 -0
- package/packages/openclaw/dist/identity/model.js.map +1 -0
- package/packages/openclaw/dist/identity/naming.d.ts +5 -0
- package/packages/openclaw/dist/identity/naming.d.ts.map +1 -0
- package/packages/openclaw/dist/identity/naming.js +7 -0
- package/packages/openclaw/dist/identity/naming.js.map +1 -0
- package/packages/openclaw/dist/index.d.ts +20 -0
- package/packages/openclaw/dist/index.d.ts.map +1 -0
- package/packages/openclaw/dist/index.js +27 -0
- package/packages/openclaw/dist/index.js.map +1 -0
- package/packages/openclaw/dist/inject.d.ts +14 -0
- package/packages/openclaw/dist/inject.d.ts.map +1 -0
- package/packages/openclaw/dist/inject.js +66 -0
- package/packages/openclaw/dist/inject.js.map +1 -0
- package/packages/openclaw/dist/mcp/server.d.ts +8 -0
- package/packages/openclaw/dist/mcp/server.d.ts.map +1 -0
- package/packages/openclaw/dist/mcp/server.js +105 -0
- package/packages/openclaw/dist/mcp/server.js.map +1 -0
- package/packages/openclaw/dist/mcp/tools.d.ts +17 -0
- package/packages/openclaw/dist/mcp/tools.d.ts.map +1 -0
- package/packages/openclaw/dist/mcp/tools.js +145 -0
- package/packages/openclaw/dist/mcp/tools.js.map +1 -0
- package/packages/openclaw/dist/runtime/openclaw-config.d.ts +20 -0
- package/packages/openclaw/dist/runtime/openclaw-config.d.ts.map +1 -0
- package/packages/openclaw/dist/runtime/openclaw-config.js +50 -0
- package/packages/openclaw/dist/runtime/openclaw-config.js.map +1 -0
- package/packages/openclaw/dist/runtime/patch.d.ts +24 -0
- package/packages/openclaw/dist/runtime/patch.d.ts.map +1 -0
- package/packages/openclaw/dist/runtime/patch.js +92 -0
- package/packages/openclaw/dist/runtime/patch.js.map +1 -0
- package/packages/openclaw/dist/runtime/setup.d.ts +26 -0
- package/packages/openclaw/dist/runtime/setup.d.ts.map +1 -0
- package/packages/openclaw/dist/runtime/setup.js +58 -0
- package/packages/openclaw/dist/runtime/setup.js.map +1 -0
- package/packages/openclaw/dist/setup.d.ts +29 -0
- package/packages/openclaw/dist/setup.d.ts.map +1 -0
- package/packages/openclaw/dist/setup.js +300 -0
- package/packages/openclaw/dist/setup.js.map +1 -0
- package/packages/openclaw/dist/spawn/docker.d.ts +58 -0
- package/packages/openclaw/dist/spawn/docker.d.ts.map +1 -0
- package/packages/openclaw/dist/spawn/docker.js +222 -0
- package/packages/openclaw/dist/spawn/docker.js.map +1 -0
- package/packages/openclaw/dist/spawn/manager.d.ts +45 -0
- package/packages/openclaw/dist/spawn/manager.d.ts.map +1 -0
- package/packages/openclaw/dist/spawn/manager.js +140 -0
- package/packages/openclaw/dist/spawn/manager.js.map +1 -0
- package/packages/openclaw/dist/spawn/process.d.ts +16 -0
- package/packages/openclaw/dist/spawn/process.d.ts.map +1 -0
- package/packages/openclaw/dist/spawn/process.js +241 -0
- package/packages/openclaw/dist/spawn/process.js.map +1 -0
- package/packages/openclaw/dist/spawn/types.d.ts +42 -0
- package/packages/openclaw/dist/spawn/types.d.ts.map +1 -0
- package/packages/openclaw/dist/spawn/types.js +2 -0
- package/packages/openclaw/dist/spawn/types.js.map +1 -0
- package/packages/openclaw/dist/types.d.ts +37 -0
- package/packages/openclaw/dist/types.d.ts.map +1 -0
- package/packages/openclaw/dist/types.js +2 -0
- package/packages/openclaw/dist/types.js.map +1 -0
- package/packages/openclaw/package.json +63 -0
- package/packages/openclaw/skill/SKILL.md +194 -0
- package/packages/openclaw/src/__tests__/gateway-threads.test.ts +384 -0
- package/packages/openclaw/src/__tests__/naming.test.ts +24 -0
- package/packages/openclaw/src/__tests__/spawn-manager.test.ts +152 -0
- package/packages/openclaw/src/auth/converter.ts +90 -0
- package/packages/openclaw/src/cli.ts +269 -0
- package/packages/openclaw/src/config.ts +124 -0
- package/packages/openclaw/src/control.ts +100 -0
- package/packages/openclaw/src/gateway.ts +941 -0
- package/packages/openclaw/src/identity/contract.ts +44 -0
- package/packages/openclaw/src/identity/files.ts +198 -0
- package/packages/openclaw/src/identity/model.ts +27 -0
- package/packages/openclaw/src/identity/naming.ts +6 -0
- package/packages/openclaw/src/index.ts +59 -0
- package/packages/openclaw/src/inject.ts +77 -0
- package/packages/openclaw/src/mcp/server.ts +121 -0
- package/packages/openclaw/src/mcp/tools.ts +174 -0
- package/packages/openclaw/src/runtime/openclaw-config.ts +64 -0
- package/packages/openclaw/src/runtime/patch.ts +103 -0
- package/packages/openclaw/src/runtime/setup.ts +89 -0
- package/packages/openclaw/src/setup.ts +336 -0
- package/packages/openclaw/src/spawn/docker.ts +261 -0
- package/packages/openclaw/src/spawn/manager.ts +181 -0
- package/packages/openclaw/src/spawn/process.ts +272 -0
- package/packages/openclaw/src/spawn/types.ts +43 -0
- package/packages/openclaw/src/types.ts +38 -0
- package/packages/openclaw/templates/SOUL.md.template +34 -0
- package/packages/openclaw/tsconfig.json +12 -0
- package/packages/policy/package.json +2 -2
- package/packages/sdk/package.json +2 -2
- package/packages/sdk-py/pyproject.toml +1 -1
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/package.json +2 -2
- package/bin/agent-relay-broker-darwin-arm64 +0 -0
- package/bin/agent-relay-broker-darwin-x64 +0 -0
- 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
|
+
}
|