@chrysb/alphaclaw 0.8.7-beta.0 → 0.8.7-beta.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/alphaclaw.js CHANGED
@@ -19,6 +19,11 @@ const {
19
19
  const {
20
20
  applyPendingOpenclawUpdate,
21
21
  } = require("../lib/server/pending-openclaw-update");
22
+ const {
23
+ getManagedOpenclawRuntimeDir,
24
+ prependManagedOpenclawBinToPath,
25
+ syncManagedOpenclawRuntimeWithBundled,
26
+ } = require("../lib/server/openclaw-runtime");
22
27
 
23
28
  const kUsageTrackerPluginPath = path.resolve(
24
29
  __dirname,
@@ -206,21 +211,34 @@ if (fs.existsSync(pendingUpdateMarker)) {
206
211
  }
207
212
 
208
213
  const pendingOpenclawUpdateMarker = path.join(rootDir, ".openclaw-update-pending");
214
+ const managedOpenclawRuntimeDir = getManagedOpenclawRuntimeDir({ rootDir });
209
215
  if (fs.existsSync(pendingOpenclawUpdateMarker)) {
210
- const alphaPkgRoot = path.resolve(__dirname, "..");
211
- const nmIndex = alphaPkgRoot.lastIndexOf(
212
- `${path.sep}node_modules${path.sep}`,
213
- );
214
- const installDir =
215
- nmIndex >= 0 ? alphaPkgRoot.slice(0, nmIndex) : alphaPkgRoot;
216
216
  applyPendingOpenclawUpdate({
217
217
  execSyncImpl: execSync,
218
218
  fsModule: fs,
219
- installDir,
219
+ installDir: managedOpenclawRuntimeDir,
220
220
  logger: console,
221
221
  markerPath: pendingOpenclawUpdateMarker,
222
222
  });
223
223
  }
224
+ try {
225
+ syncManagedOpenclawRuntimeWithBundled({
226
+ execSyncImpl: execSync,
227
+ fsModule: fs,
228
+ logger: console,
229
+ runtimeDir: managedOpenclawRuntimeDir,
230
+ });
231
+ } catch (error) {
232
+ console.log(
233
+ `[alphaclaw] Could not sync managed OpenClaw runtime from bundled install: ${error.message}`,
234
+ );
235
+ }
236
+ prependManagedOpenclawBinToPath({
237
+ env: process.env,
238
+ fsModule: fs,
239
+ logger: console,
240
+ runtimeDir: managedOpenclawRuntimeDir,
241
+ });
224
242
 
225
243
  // ---------------------------------------------------------------------------
226
244
  // 3. Symlink ~/.openclaw -> <root>/.openclaw
@@ -0,0 +1,260 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const { kRootDir } = require("./constants");
5
+ const {
6
+ compareVersionParts,
7
+ normalizeOpenclawVersion,
8
+ } = require("./helpers");
9
+
10
+ const getManagedOpenclawRuntimeDir = ({ rootDir = kRootDir } = {}) =>
11
+ path.join(rootDir, ".openclaw-runtime");
12
+
13
+ const getManagedOpenclawBinDir = ({ runtimeDir } = {}) =>
14
+ path.join(
15
+ runtimeDir || getManagedOpenclawRuntimeDir(),
16
+ "node_modules",
17
+ ".bin",
18
+ );
19
+
20
+ const getManagedOpenclawBinPath = ({ runtimeDir } = {}) =>
21
+ path.join(getManagedOpenclawBinDir({ runtimeDir }), "openclaw");
22
+
23
+ const getManagedOpenclawPackageJsonPath = ({ runtimeDir } = {}) =>
24
+ path.join(
25
+ runtimeDir || getManagedOpenclawRuntimeDir(),
26
+ "node_modules",
27
+ "openclaw",
28
+ "package.json",
29
+ );
30
+
31
+ const ensureManagedOpenclawRuntimeProject = ({
32
+ fsModule = fs,
33
+ runtimeDir,
34
+ } = {}) => {
35
+ const resolvedRuntimeDir = runtimeDir || getManagedOpenclawRuntimeDir();
36
+ const packageJsonPath = path.join(resolvedRuntimeDir, "package.json");
37
+ fsModule.mkdirSync(resolvedRuntimeDir, { recursive: true });
38
+ if (!fsModule.existsSync(packageJsonPath)) {
39
+ fsModule.writeFileSync(
40
+ packageJsonPath,
41
+ JSON.stringify(
42
+ {
43
+ name: "alphaclaw-openclaw-runtime",
44
+ private: true,
45
+ },
46
+ null,
47
+ 2,
48
+ ),
49
+ );
50
+ }
51
+ return {
52
+ runtimeDir: resolvedRuntimeDir,
53
+ packageJsonPath,
54
+ };
55
+ };
56
+
57
+ const readManagedOpenclawRuntimeVersion = ({
58
+ fsModule = fs,
59
+ runtimeDir,
60
+ } = {}) => {
61
+ try {
62
+ const pkg = JSON.parse(
63
+ fsModule.readFileSync(
64
+ getManagedOpenclawPackageJsonPath({ runtimeDir }),
65
+ "utf8",
66
+ ),
67
+ );
68
+ return normalizeOpenclawVersion(pkg?.version || "");
69
+ } catch {
70
+ return null;
71
+ }
72
+ };
73
+
74
+ const readBundledOpenclawVersion = ({
75
+ fsModule = fs,
76
+ resolveImpl = require.resolve,
77
+ } = {}) => {
78
+ try {
79
+ const pkgPath = resolveImpl("openclaw/package.json");
80
+ const pkg = JSON.parse(fsModule.readFileSync(pkgPath, "utf8"));
81
+ return normalizeOpenclawVersion(pkg?.version || "");
82
+ } catch {
83
+ return null;
84
+ }
85
+ };
86
+
87
+ const shellQuote = (value) =>
88
+ `'${String(value || "").replace(/'/g, `'\"'\"'`)}'`;
89
+
90
+ const applyManagedOpenclawPatch = ({
91
+ execSyncImpl,
92
+ fsModule = fs,
93
+ logger = console,
94
+ runtimeDir,
95
+ version,
96
+ alphaclawRoot = path.resolve(__dirname, "..", ".."),
97
+ } = {}) => {
98
+ const normalizedVersion = normalizeOpenclawVersion(version);
99
+ if (!normalizedVersion) return false;
100
+ const patchesDir = path.join(alphaclawRoot, "patches");
101
+ const patchFileName = `openclaw+${normalizedVersion}.patch`;
102
+ const patchFilePath = path.join(patchesDir, patchFileName);
103
+ if (!fsModule.existsSync(patchFilePath)) {
104
+ return false;
105
+ }
106
+
107
+ const runtimePatchDirName = ".alphaclaw-patches";
108
+ const runtimePatchDirPath = path.join(runtimeDir, runtimePatchDirName);
109
+ try {
110
+ if (fsModule.existsSync(runtimePatchDirPath)) {
111
+ fsModule.rmSync(runtimePatchDirPath, { recursive: true, force: true });
112
+ }
113
+ } catch {}
114
+ fsModule.symlinkSync(patchesDir, runtimePatchDirPath);
115
+
116
+ const patchPackageMain = require.resolve("patch-package/dist/index.js", {
117
+ paths: [alphaclawRoot],
118
+ });
119
+ logger.log(
120
+ `[alphaclaw] Applying bundled OpenClaw patch for ${normalizedVersion}...`,
121
+ );
122
+ execSyncImpl(
123
+ `${shellQuote(process.execPath)} ${shellQuote(patchPackageMain)} --patch-dir ${shellQuote(runtimePatchDirName)}`,
124
+ {
125
+ cwd: runtimeDir,
126
+ stdio: "inherit",
127
+ timeout: 120000,
128
+ },
129
+ );
130
+ return true;
131
+ };
132
+
133
+ const installManagedOpenclawRuntime = ({
134
+ execSyncImpl,
135
+ fsModule = fs,
136
+ logger = console,
137
+ runtimeDir,
138
+ spec,
139
+ alphaclawRoot,
140
+ } = {}) => {
141
+ const normalizedSpec = String(spec || "").trim() || "openclaw@latest";
142
+ ensureManagedOpenclawRuntimeProject({
143
+ fsModule,
144
+ runtimeDir,
145
+ });
146
+ execSyncImpl(
147
+ `npm install ${shellQuote(normalizedSpec)} --omit=dev --no-save --save=false --package-lock=false --prefer-online`,
148
+ {
149
+ cwd: runtimeDir,
150
+ stdio: "inherit",
151
+ timeout: 180000,
152
+ },
153
+ );
154
+ const installedVersion = readManagedOpenclawRuntimeVersion({
155
+ fsModule,
156
+ runtimeDir,
157
+ });
158
+ applyManagedOpenclawPatch({
159
+ execSyncImpl,
160
+ fsModule,
161
+ logger,
162
+ runtimeDir,
163
+ version: installedVersion,
164
+ alphaclawRoot,
165
+ });
166
+ return {
167
+ spec: normalizedSpec,
168
+ version: installedVersion,
169
+ };
170
+ };
171
+
172
+ const syncManagedOpenclawRuntimeWithBundled = ({
173
+ execSyncImpl,
174
+ fsModule = fs,
175
+ logger = console,
176
+ runtimeDir,
177
+ resolveImpl,
178
+ alphaclawRoot,
179
+ } = {}) => {
180
+ const bundledVersion = readBundledOpenclawVersion({
181
+ fsModule,
182
+ resolveImpl,
183
+ });
184
+ if (!bundledVersion) {
185
+ return {
186
+ checked: false,
187
+ synced: false,
188
+ bundledVersion: null,
189
+ runtimeVersion: readManagedOpenclawRuntimeVersion({ fsModule, runtimeDir }),
190
+ };
191
+ }
192
+
193
+ const runtimeVersion = readManagedOpenclawRuntimeVersion({
194
+ fsModule,
195
+ runtimeDir,
196
+ });
197
+ if (runtimeVersion && compareVersionParts(runtimeVersion, bundledVersion) >= 0) {
198
+ return {
199
+ checked: true,
200
+ synced: false,
201
+ bundledVersion,
202
+ runtimeVersion,
203
+ };
204
+ }
205
+
206
+ logger.log(
207
+ runtimeVersion
208
+ ? `[alphaclaw] Managed OpenClaw runtime ${runtimeVersion} is older than bundled ${bundledVersion}; syncing runtime...`
209
+ : `[alphaclaw] Managed OpenClaw runtime missing; seeding bundled OpenClaw ${bundledVersion}...`,
210
+ );
211
+ const installResult = installManagedOpenclawRuntime({
212
+ execSyncImpl,
213
+ fsModule,
214
+ logger,
215
+ runtimeDir,
216
+ spec: `openclaw@${bundledVersion}`,
217
+ alphaclawRoot,
218
+ });
219
+ return {
220
+ checked: true,
221
+ synced: true,
222
+ bundledVersion,
223
+ runtimeVersion: installResult.version || bundledVersion,
224
+ };
225
+ };
226
+
227
+ const prependManagedOpenclawBinToPath = ({
228
+ env = process.env,
229
+ fsModule = fs,
230
+ logger = console,
231
+ runtimeDir,
232
+ } = {}) => {
233
+ const resolvedRuntimeDir = runtimeDir || getManagedOpenclawRuntimeDir();
234
+ const binDir = getManagedOpenclawBinDir({ runtimeDir: resolvedRuntimeDir });
235
+ const binPath = getManagedOpenclawBinPath({ runtimeDir: resolvedRuntimeDir });
236
+ if (!fsModule.existsSync(binPath)) {
237
+ return false;
238
+ }
239
+ const currentEntries = String(env.PATH || "")
240
+ .split(path.delimiter)
241
+ .filter(Boolean);
242
+ const nextEntries = [binDir, ...currentEntries.filter((entry) => entry !== binDir)];
243
+ env.PATH = nextEntries.join(path.delimiter);
244
+ logger.log(`[alphaclaw] Using managed OpenClaw runtime from ${resolvedRuntimeDir}`);
245
+ return true;
246
+ };
247
+
248
+ module.exports = {
249
+ applyManagedOpenclawPatch,
250
+ ensureManagedOpenclawRuntimeProject,
251
+ getManagedOpenclawBinDir,
252
+ getManagedOpenclawBinPath,
253
+ getManagedOpenclawPackageJsonPath,
254
+ getManagedOpenclawRuntimeDir,
255
+ installManagedOpenclawRuntime,
256
+ prependManagedOpenclawBinToPath,
257
+ readBundledOpenclawVersion,
258
+ readManagedOpenclawRuntimeVersion,
259
+ syncManagedOpenclawRuntimeWithBundled,
260
+ };
@@ -1,3 +1,7 @@
1
+ const {
2
+ installManagedOpenclawRuntime,
3
+ } = require("./openclaw-runtime");
4
+
1
5
  const buildPendingOpenclawInstallSpec = (marker = {}) => {
2
6
  const explicitSpec = String(marker?.spec || "").trim();
3
7
  if (explicitSpec) {
@@ -7,9 +11,6 @@ const buildPendingOpenclawInstallSpec = (marker = {}) => {
7
11
  return `openclaw@${targetVersion}`;
8
12
  };
9
13
 
10
- const shellQuote = (value) =>
11
- `'${String(value || "").replace(/'/g, `'\"'\"'`)}'`;
12
-
13
14
  const applyPendingOpenclawUpdate = ({
14
15
  execSyncImpl,
15
16
  fsModule,
@@ -36,14 +37,13 @@ const applyPendingOpenclawUpdate = ({
36
37
  logger.log(`[alphaclaw] Pending OpenClaw update detected, installing ${spec}...`);
37
38
 
38
39
  try {
39
- execSyncImpl(
40
- `npm install ${shellQuote(spec)} --omit=dev --no-save --save=false --package-lock=false --prefer-online`,
41
- {
42
- cwd: installDir,
43
- stdio: "inherit",
44
- timeout: 180000,
45
- },
46
- );
40
+ installManagedOpenclawRuntime({
41
+ execSyncImpl,
42
+ fsModule,
43
+ logger,
44
+ runtimeDir: installDir,
45
+ spec,
46
+ });
47
47
  fsModule.unlinkSync(markerPath);
48
48
  logger.log("[alphaclaw] OpenClaw update applied successfully");
49
49
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.8.7-beta.0",
3
+ "version": "0.8.7-beta.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },