@okclaw-build/cli 1.0.0-beta.41 → 1.0.0-beta.43

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 (36) hide show
  1. package/README.md +48 -0
  2. package/dist/commands/backup.d.ts +16 -0
  3. package/dist/commands/backup.js +72 -0
  4. package/dist/commands/backup.js.map +1 -0
  5. package/dist/commands/migration.d.ts +1 -0
  6. package/dist/commands/migration.js +205 -0
  7. package/dist/commands/migration.js.map +1 -0
  8. package/dist/commands/restore.d.ts +16 -0
  9. package/dist/commands/restore.js +67 -0
  10. package/dist/commands/restore.js.map +1 -0
  11. package/dist/commands/service.js +21 -31
  12. package/dist/commands/service.js.map +1 -1
  13. package/dist/index.js +10 -0
  14. package/dist/index.js.map +1 -1
  15. package/dist/installers/openclaw.d.ts +1 -14
  16. package/dist/installers/openclaw.js +12 -64
  17. package/dist/installers/openclaw.js.map +1 -1
  18. package/dist/migration/archive.d.ts +14 -0
  19. package/dist/migration/archive.js +84 -0
  20. package/dist/migration/archive.js.map +1 -0
  21. package/dist/migration/crypto.d.ts +16 -0
  22. package/dist/migration/crypto.js +56 -0
  23. package/dist/migration/crypto.js.map +1 -0
  24. package/dist/migration/manifest.d.ts +39 -0
  25. package/dist/migration/manifest.js +140 -0
  26. package/dist/migration/manifest.js.map +1 -0
  27. package/dist/openclaw-user-data.d.ts +63 -0
  28. package/dist/openclaw-user-data.js +246 -0
  29. package/dist/openclaw-user-data.js.map +1 -0
  30. package/dist/utils/constants.d.ts +1 -0
  31. package/dist/utils/constants.js +4 -0
  32. package/dist/utils/constants.js.map +1 -1
  33. package/dist/utils/openclaw-daemon.d.ts +43 -8
  34. package/dist/utils/openclaw-daemon.js +124 -18
  35. package/dist/utils/openclaw-daemon.js.map +1 -1
  36. package/package.json +1 -1
@@ -0,0 +1,246 @@
1
+ import { existsSync, cpSync, mkdirSync, rmSync, statSync, chmodSync, readdirSync, } from 'node:fs';
2
+ import { shell, shellCapture } from './utils/shell.js';
3
+ import { OKCLAW_BACKUP_DIR, OPENCLAW_WORKSPACE } from './utils/constants.js';
4
+ export const OPENCLAW_USER_DATA_FILES = [
5
+ 'MEMORY.md',
6
+ 'USER.md',
7
+ 'IDENTITY.md',
8
+ 'SOUL.md',
9
+ 'TOOLS.md',
10
+ 'AGENTS.md',
11
+ 'HEARTBEAT.md',
12
+ ];
13
+ export const LEGACY_OPENCLAW_WORKSPACE = '/root/openclaw/workspace';
14
+ const WORKSPACE_BACKUP_DIR = 'workspace';
15
+ const LEGACY_WORKSPACE_BACKUP_DIR = 'workspace-root-openclaw';
16
+ const SKILLS_DIR = 'skills';
17
+ const OPENCLAW_CONFIG_FILE = 'openclaw.json';
18
+ const realBackupFs = { existsSync, mkdirSync, cpSync, statSync };
19
+ const realArchiveFs = { ...realBackupFs, readdirSync: readdirSync };
20
+ const realRestoreFs = {
21
+ ...realBackupFs,
22
+ rmSync,
23
+ chmodSync,
24
+ readdirSync: readdirSync,
25
+ };
26
+ export function formatBackupTimestamp(now) {
27
+ return now.toISOString().replace(/\.\d{3}Z$/, 'Z').replace(/:/g, '');
28
+ }
29
+ function copyIfExists(fsOps, src, dest, recursive = false) {
30
+ if (!fsOps.existsSync(src))
31
+ return false;
32
+ fsOps.cpSync(src, dest, recursive ? { recursive: true } : undefined);
33
+ return true;
34
+ }
35
+ export function backupOpenclawUserData(now = new Date(), fsOps = realBackupFs) {
36
+ const backupDir = `${OKCLAW_BACKUP_DIR}/openclaw-user-data-${formatBackupTimestamp(now)}`;
37
+ const workspaces = Array.from(new Set([OPENCLAW_WORKSPACE, LEGACY_OPENCLAW_WORKSPACE]));
38
+ let copiedItems = 0;
39
+ let copiedWorkspaces = 0;
40
+ for (const workspace of workspaces) {
41
+ if (!fsOps.existsSync(workspace))
42
+ continue;
43
+ const workspaceBackupName = copiedWorkspaces === 0 ? WORKSPACE_BACKUP_DIR : LEGACY_WORKSPACE_BACKUP_DIR;
44
+ const workspaceBackupDir = `${backupDir}/${workspaceBackupName}`;
45
+ let workspaceDirCreated = false;
46
+ const ensureWorkspaceBackupDir = () => {
47
+ if (workspaceDirCreated)
48
+ return;
49
+ fsOps.mkdirSync(workspaceBackupDir, { recursive: true });
50
+ workspaceDirCreated = true;
51
+ };
52
+ for (const file of OPENCLAW_USER_DATA_FILES) {
53
+ const src = `${workspace}/${file}`;
54
+ if (!fsOps.existsSync(src))
55
+ continue;
56
+ ensureWorkspaceBackupDir();
57
+ if (copyIfExists(fsOps, src, `${workspaceBackupDir}/${file}`)) {
58
+ copiedItems += 1;
59
+ }
60
+ }
61
+ const skillsDir = `${workspace}/${SKILLS_DIR}`;
62
+ if (fsOps.existsSync(skillsDir) && fsOps.statSync(skillsDir).isDirectory()) {
63
+ ensureWorkspaceBackupDir();
64
+ if (copyIfExists(fsOps, skillsDir, `${workspaceBackupDir}/${SKILLS_DIR}`, true)) {
65
+ copiedItems += 1;
66
+ }
67
+ }
68
+ if (workspaceDirCreated) {
69
+ copiedWorkspaces += 1;
70
+ }
71
+ }
72
+ return copiedItems > 0 ? backupDir : null;
73
+ }
74
+ export async function createOpenclawUserDataArchive(options = {}) {
75
+ const now = options.now ?? new Date();
76
+ const fsOps = options.fsOps ?? realArchiveFs;
77
+ const shellOps = options.shellOps ?? { shell };
78
+ const backupDir = backupOpenclawUserData(now, fsOps);
79
+ if (!backupDir) {
80
+ return { backupDir: null, archivePath: null, includedFiles: [] };
81
+ }
82
+ const includedFiles = listRelativeFiles(fsOps, backupDir);
83
+ validateArchiveEntries(includedFiles);
84
+ const archivePath = options.archivePath ?? `/tmp/openclaw-user-data-${formatBackupTimestamp(now)}.tar.gz`;
85
+ try {
86
+ await shellOps.shell(`tar -czf ${shellSingleQuote(archivePath)} -C ${shellSingleQuote(backupDir)} .`);
87
+ }
88
+ catch (error) {
89
+ attachContext(error, { backupDir, archivePath });
90
+ throw error;
91
+ }
92
+ return { backupDir, archivePath, includedFiles };
93
+ }
94
+ export async function restoreOpenclawUserDataArchive(options) {
95
+ const now = options.now ?? new Date();
96
+ const fsOps = options.fsOps ?? realRestoreFs;
97
+ const shellOps = options.shellOps ?? { shell };
98
+ const stagingDir = `/tmp/openclaw-user-data-restore-${formatBackupTimestamp(now)}`;
99
+ try {
100
+ fsOps.rmSync(stagingDir, { recursive: true, force: true });
101
+ fsOps.mkdirSync(stagingDir, { recursive: true });
102
+ const tarEntries = await listArchiveEntries(shellOps, options.archivePath);
103
+ validateArchiveEntries(tarEntries.map((entry) => entry.path));
104
+ validateArchiveEntryTypes(tarEntries);
105
+ await shellOps.shell(`tar -xzf ${shellSingleQuote(options.archivePath)} -C ${shellSingleQuote(stagingDir)}`);
106
+ const entries = listRelativeFiles(fsOps, stagingDir);
107
+ validateArchiveEntries(entries);
108
+ fsOps.mkdirSync(OPENCLAW_WORKSPACE, { recursive: true });
109
+ fsOps.chmodSync(OPENCLAW_WORKSPACE, 0o755);
110
+ const restored = new Set();
111
+ restoreWorkspaceBackup(fsOps, `${stagingDir}/${WORKSPACE_BACKUP_DIR}`, restored);
112
+ restoreWorkspaceBackup(fsOps, `${stagingDir}/${LEGACY_WORKSPACE_BACKUP_DIR}`, restored);
113
+ const restoredFiles = Array.from(restored).sort();
114
+ for (const file of restoredFiles) {
115
+ const path = `${OPENCLAW_WORKSPACE}/${file}`;
116
+ if (file === SKILLS_DIR) {
117
+ applyRestoredPermissions(fsOps, path);
118
+ }
119
+ else {
120
+ fsOps.chmodSync(path, 0o644);
121
+ }
122
+ }
123
+ await shellOps.shell(`chown -R root:root ${shellSingleQuote(OPENCLAW_WORKSPACE)}`);
124
+ return { stagingDir, archivePath: options.archivePath, restoredFiles };
125
+ }
126
+ catch (error) {
127
+ attachContext(error, { stagingDir, archivePath: options.archivePath });
128
+ throw error;
129
+ }
130
+ }
131
+ async function listArchiveEntries(shellOps, archivePath) {
132
+ if (shellOps.capture) {
133
+ const stdout = await shellOps.capture(`tar -tvzf ${shellSingleQuote(archivePath)}`);
134
+ return parseTarVerboseEntries(stdout);
135
+ }
136
+ const result = await shellCapture(`tar -tvzf ${shellSingleQuote(archivePath)}`);
137
+ if (result.code !== 0) {
138
+ throw new Error(`Command failed (exit ${result.code}): tar -tvzf ${archivePath}`);
139
+ }
140
+ return parseTarVerboseEntries(result.stdout);
141
+ }
142
+ function parseTarVerboseEntries(stdout) {
143
+ return stdout.split(/\r?\n/)
144
+ .map((line) => line.trim())
145
+ .filter(Boolean)
146
+ .map((line) => {
147
+ const modeType = line[0];
148
+ const tokens = line.split(/\s+/);
149
+ const rawPath = tokens.slice(5).join(' ');
150
+ const path = rawPath.includes(' -> ') ? rawPath.slice(0, rawPath.indexOf(' -> ')) : rawPath;
151
+ return {
152
+ type: modeType === '-' ? 'file' : modeType === 'd' ? 'directory' : 'unsupported',
153
+ path,
154
+ raw: line,
155
+ };
156
+ });
157
+ }
158
+ function validateArchiveEntryTypes(entries) {
159
+ for (const entry of entries) {
160
+ if (entry.type === 'unsupported') {
161
+ throw new Error(`unsupported archive entry type: ${entry.raw}`);
162
+ }
163
+ }
164
+ }
165
+ function restoreWorkspaceBackup(fsOps, backupWorkspaceDir, restored) {
166
+ if (!fsOps.existsSync(backupWorkspaceDir))
167
+ return;
168
+ for (const file of OPENCLAW_USER_DATA_FILES) {
169
+ if (restored.has(file))
170
+ continue;
171
+ const src = `${backupWorkspaceDir}/${file}`;
172
+ if (!fsOps.existsSync(src))
173
+ continue;
174
+ fsOps.cpSync(src, `${OPENCLAW_WORKSPACE}/${file}`);
175
+ restored.add(file);
176
+ }
177
+ const skillsSrc = `${backupWorkspaceDir}/${SKILLS_DIR}`;
178
+ if (!restored.has(SKILLS_DIR) && fsOps.existsSync(skillsSrc) && fsOps.statSync(skillsSrc).isDirectory()) {
179
+ fsOps.rmSync(`${OPENCLAW_WORKSPACE}/${SKILLS_DIR}`, { recursive: true, force: true });
180
+ fsOps.cpSync(skillsSrc, `${OPENCLAW_WORKSPACE}/${SKILLS_DIR}`, { recursive: true });
181
+ restored.add(SKILLS_DIR);
182
+ }
183
+ }
184
+ function applyRestoredPermissions(fsOps, path) {
185
+ if (!fsOps.existsSync(path))
186
+ return;
187
+ if (fsOps.statSync(path).isDirectory()) {
188
+ fsOps.chmodSync(path, 0o755);
189
+ for (const entry of fsOps.readdirSync(path, { withFileTypes: true })) {
190
+ applyRestoredPermissions(fsOps, `${path}/${entry.name}`);
191
+ }
192
+ return;
193
+ }
194
+ fsOps.chmodSync(path, 0o644);
195
+ }
196
+ function listRelativeFiles(fsOps, root) {
197
+ if (!fsOps.existsSync(root))
198
+ return [];
199
+ const files = [];
200
+ const visit = (dir, prefix) => {
201
+ for (const entry of fsOps.readdirSync(dir, { withFileTypes: true })) {
202
+ const relative = prefix ? `${prefix}/${entry.name}` : entry.name;
203
+ const path = `${dir}/${entry.name}`;
204
+ if (entry.isDirectory()) {
205
+ visit(path, relative);
206
+ }
207
+ else if (entry.isFile()) {
208
+ files.push(relative);
209
+ }
210
+ }
211
+ };
212
+ visit(root, '');
213
+ return files.sort();
214
+ }
215
+ function validateArchiveEntries(entries) {
216
+ for (const entry of entries) {
217
+ const normalized = normalizeArchiveEntry(entry);
218
+ if (!normalized)
219
+ continue;
220
+ const parts = normalized.split('/');
221
+ if (entry.startsWith('/') ||
222
+ parts.includes('..') ||
223
+ parts.includes(OPENCLAW_CONFIG_FILE)) {
224
+ throw new Error(`unsafe archive entry: ${entry}`);
225
+ }
226
+ }
227
+ }
228
+ function normalizeArchiveEntry(entry) {
229
+ let normalized = entry.trim();
230
+ while (normalized.startsWith('./')) {
231
+ normalized = normalized.slice(2);
232
+ }
233
+ while (normalized.endsWith('/')) {
234
+ normalized = normalized.slice(0, -1);
235
+ }
236
+ return normalized === '.' ? '' : normalized;
237
+ }
238
+ function shellSingleQuote(value) {
239
+ return `'${value.replace(/'/g, `'\\''`)}'`;
240
+ }
241
+ function attachContext(error, context) {
242
+ if (error && typeof error === 'object') {
243
+ Object.assign(error, context);
244
+ }
245
+ }
246
+ //# sourceMappingURL=openclaw-user-data.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openclaw-user-data.js","sourceRoot":"","sources":["../src/openclaw-user-data.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@ export declare const OKCLAW_BACKUP_DIR = "/root/okclaw/backup";
4
4
  export declare const OKCLAW_TMP_DIR = "/root/okclaw/tmp";
5
5
  export declare const OPENCLAW_HOME = "/root/.openclaw";
6
6
  export declare const OPENCLAW_WORKSPACE = "/root/.openclaw/workspace";
7
+ export declare const OPENCLAW_GATEWAY_PORT = 18789;
7
8
  export declare const OPENVIKING_HOME = "/root/.openviking";
8
9
  export declare const OPENVIKING_CONF = "/root/.openviking/ov.conf";
9
10
  export declare const OPENVIKING_ENV = "/root/.openclaw/openviking.env";
@@ -5,6 +5,10 @@ export const OKCLAW_BACKUP_DIR = `${OKCLAW_HOME}/backup`;
5
5
  export const OKCLAW_TMP_DIR = `${OKCLAW_HOME}/tmp`;
6
6
  export const OPENCLAW_HOME = '/root/.openclaw';
7
7
  export const OPENCLAW_WORKSPACE = `${OPENCLAW_HOME}/workspace`;
8
+ // openclaw gateway 默认 WebSocket 端口。gateway 进程名在不同启动方式下不固定
9
+ // (`openclaw gateway run` → `openclaw`,systemd → `node`),所以进程识别一律
10
+ // 按这个监听端口来,不能用 `ps -C` / `pkill -f` 匹配进程名。
11
+ export const OPENCLAW_GATEWAY_PORT = 18789;
8
12
  export const OPENVIKING_HOME = '/root/.openviking';
9
13
  export const OPENVIKING_CONF = `${OPENVIKING_HOME}/ov.conf`;
10
14
  export const OPENVIKING_ENV = `${OPENCLAW_HOME}/openviking.env`;
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAC;AAC1C,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,WAAW,SAAS,CAAC;AACzD,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,WAAW,SAAS,CAAC;AACzD,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,WAAW,MAAM,CAAC;AAEnD,MAAM,CAAC,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAC/C,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,aAAa,YAAY,CAAC;AAC/D,MAAM,CAAC,MAAM,eAAe,GAAG,mBAAmB,CAAC;AACnD,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,eAAe,UAAU,CAAC;AAC5D,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,aAAa,iBAAiB,CAAC;AAChE,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AAEpC,yEAAyE;AACzE,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AACjC,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AACjC,MAAM,CAAC,MAAM,oBAAoB,GAAG,SAAS,CAAC;AAE9C,gBAAgB;AAChB,MAAM,CAAC,MAAM,YAAY,GAAG,gCAAgC,CAAC;AAC7D,MAAM,CAAC,MAAM,aAAa,GAAG,0CAA0C,CAAC;AACxE,MAAM,CAAC,MAAM,gBAAgB,GAAG,2BAA2B,CAAC;AAC5D,MAAM,CAAC,MAAM,WAAW,GAAG,oCAAoC,CAAC;AAEhE,wEAAwE;AACxE,MAAM,CAAC,MAAM,WAAW,GAAG,6BAA6B,CAAC;AACzD,MAAM,CAAC,MAAM,eAAe,GAAG,uBAAuB,CAAC;AACvD,MAAM,CAAC,MAAM,qBAAqB,GAAG,0BAA0B,CAAC;AAEhE,gDAAgD;AAChD,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,UAAU,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC;AAC1F,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,mBAAmB,EAAE,WAAW,EAAE,oBAAoB;IACtD,mBAAmB,EAAE,eAAe,EAAE,iBAAiB;IACvD,8BAA8B,EAAE,sBAAsB;IACtD,eAAe;CAChB,CAAC;AAEF,8BAA8B;AAC9B,MAAM,CAAC,MAAM,oBAAoB,GAA2B;IAC1D,QAAQ,EAAE,iBAAiB;IAC3B,MAAM,EAAE,iCAAiC;IACzC,iDAAiD;IACjD,mEAAmE;CACpE,CAAC;AAEF,yEAAyE;AACzE,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/utils/constants.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,MAAM,CAAC,MAAM,WAAW,GAAG,cAAc,CAAC;AAC1C,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,WAAW,SAAS,CAAC;AACzD,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,WAAW,SAAS,CAAC;AACzD,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,WAAW,MAAM,CAAC;AAEnD,MAAM,CAAC,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAC/C,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,aAAa,YAAY,CAAC;AAC/D,0DAA0D;AAC1D,kEAAkE;AAClE,2CAA2C;AAC3C,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,CAAC;AAC3C,MAAM,CAAC,MAAM,eAAe,GAAG,mBAAmB,CAAC;AACnD,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,eAAe,UAAU,CAAC;AAC5D,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,aAAa,iBAAiB,CAAC;AAChE,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,CAAC;AAEpC,yEAAyE;AACzE,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AACjC,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AACjC,MAAM,CAAC,MAAM,oBAAoB,GAAG,SAAS,CAAC;AAE9C,gBAAgB;AAChB,MAAM,CAAC,MAAM,YAAY,GAAG,gCAAgC,CAAC;AAC7D,MAAM,CAAC,MAAM,aAAa,GAAG,0CAA0C,CAAC;AACxE,MAAM,CAAC,MAAM,gBAAgB,GAAG,2BAA2B,CAAC;AAC5D,MAAM,CAAC,MAAM,WAAW,GAAG,oCAAoC,CAAC;AAEhE,wEAAwE;AACxE,MAAM,CAAC,MAAM,WAAW,GAAG,6BAA6B,CAAC;AACzD,MAAM,CAAC,MAAM,eAAe,GAAG,uBAAuB,CAAC;AACvD,MAAM,CAAC,MAAM,qBAAqB,GAAG,0BAA0B,CAAC;AAEhE,gDAAgD;AAChD,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,UAAU,EAAE,WAAW,EAAE,cAAc,CAAC,CAAC;AAC1F,MAAM,CAAC,MAAM,gCAAgC,GAAG;IAC9C,mBAAmB,EAAE,WAAW,EAAE,oBAAoB;IACtD,mBAAmB,EAAE,eAAe,EAAE,iBAAiB;IACvD,8BAA8B,EAAE,sBAAsB;IACtD,eAAe;CAChB,CAAC;AAEF,8BAA8B;AAC9B,MAAM,CAAC,MAAM,oBAAoB,GAA2B;IAC1D,QAAQ,EAAE,iBAAiB;IAC3B,MAAM,EAAE,iCAAiC;IACzC,iDAAiD;IACjD,mEAAmE;CACpE,CAAC;AAEF,yEAAyE;AACzE,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC"}
@@ -1,8 +1,47 @@
1
1
  /**
2
- * Best-effort stop of the openclaw gateway daemon so cli operations have
3
- * exclusive write access to openclaw.json + extensions/.
2
+ * 确保 gateway systemd 服务已安装且能常驻。
3
+ * - `loginctl enable-linger`:systemd user 服务默认随最后一个登录会话退出而停,
4
+ * 开 linger 后才能在部署 SSH 断开后 / 主机重启后继续运行。
5
+ * - `openclaw gateway install --force`:幂等(重装覆盖),顺带在 openclaw 升级后
6
+ * 刷新 unit 的 ExecStart。
7
+ */
8
+ export declare function installGatewayService(): Promise<void>;
9
+ /** 启动 gateway(先确保 systemd 服务已装)。 */
10
+ export declare function startGateway(): Promise<void>;
11
+ /** 重启 gateway(先确保 systemd 服务已装)。 */
12
+ export declare function restartGateway(): Promise<void>;
13
+ /**
14
+ * 停掉 gateway:先停掉所有已知的 systemd unit(新 + 旧 unit 名),再按监听
15
+ * 端口兜底杀掉非 systemd 托管的残留进程。顺序见 {@link STOP_GATEWAY_UNITS}。
16
+ */
17
+ export declare function stopGateway(): Promise<void>;
18
+ /** gateway 运行状态。健康的唯一标准是端口已监听。 */
19
+ export type GatewayState = 'listening' | 'starting' | 'down';
20
+ /**
21
+ * 探测 gateway 状态。**健康的唯一标准是 18789 端口已监听** —— 进程 active
22
+ * 不等于对外可服务。
23
+ *
24
+ * - `listening`:端口已监听,健康。
25
+ * - `starting` :systemd 服务 active,但端口在 bounded wait 内始终没监听 ——
26
+ * 配置异常 / 启动卡住 / 仍在初始化,**不算健康**。
27
+ * - `down` :systemd 服务非 active 且端口未监听。
4
28
  *
5
- * Why this is needed: the daemon has a config file watcher that writes its
29
+ * `openclaw gateway restart` 返回后服务立刻是 active,但 gateway 进程还要几秒
30
+ * 才 bind 端口。所以端口没起来、服务却 active 时给一段 bounded wait 覆盖这个
31
+ * 启动窗口;服务都不 active 则直接 `down`,不等。
32
+ */
33
+ export declare function probeGatewayState(): Promise<GatewayState>;
34
+ /**
35
+ * 清掉 systemd user 目录下的 gateway unit 残留。`openclaw gateway uninstall`
36
+ * 只删主 `.service` 文件,残留的 `.service.bak` 备份和 `.service.d/` drop-in
37
+ * 会在下次 install 时被 systemd 自动 merge 进来(注入过时的 env 等),必须一并清。
38
+ */
39
+ export declare function cleanupGatewayServiceFiles(): Promise<void>;
40
+ /**
41
+ * Best-effort stop of the openclaw gateway so cli operations have exclusive
42
+ * write access to openclaw.json + extensions/.
43
+ *
44
+ * Why this is needed: the gateway has a config file watcher that writes its
6
45
  * own metadata back into openclaw.json on every config change. Concurrent
7
46
  * writes during cli's cleanup / `openclaw plugins install` / `openclaw config
8
47
  * set` race with that watcher and produce:
@@ -10,12 +49,8 @@
10
49
  * - ConfigMutationConflictError: config changed since last load (hash check
11
50
  * in openclaw plugins install commit / openclaw config set atomic write)
12
51
  *
13
- * Tries every supervisor we know of, each tolerated if absent. Order matters:
14
- * ask the supervisor (openclaw cli / systemd) first so it doesn't auto-restart
15
- * on us, then pkill -9 as the last resort.
16
- *
17
52
  * Bootstrap orchestrator (commands/install.ts) is responsible for restarting
18
- * the daemon after the full component install pipeline finishes.
53
+ * the gateway after the full component install pipeline finishes.
19
54
  *
20
55
  * @param context A short label for logs, e.g. "dingtalk install" or
21
56
  * "openclaw deployment defaults".
@@ -1,10 +1,127 @@
1
- import { shell } from './shell.js';
1
+ import { shell, shellCapture } from './shell.js';
2
2
  import { info } from './logger.js';
3
+ import { OPENCLAW_GATEWAY_PORT } from './constants.js';
3
4
  /**
4
- * Best-effort stop of the openclaw gateway daemon so cli operations have
5
- * exclusive write access to openclaw.json + extensions/.
5
+ * openclaw gateway 生命周期管理。
6
6
  *
7
- * Why this is needed: the daemon has a config file watcher that writes its
7
+ * 关键决策:gateway 交给 openclaw 自带的 systemd 服务管理,cli 不再自己用
8
+ * nohup + ps + pkill 管进程。
9
+ *
10
+ * 为什么:openclaw 2026.5 的 gateway 有自己的单实例 lock、restart-handoff、
11
+ * 进程改名(`openclaw` / `node`)机制。cli 旧实现按进程名 `openclaw-gateway`
12
+ * 识别 + nohup 拉起 + 手动 kill,实测都顶不住:
13
+ * - 进程名匹配 0 个 → restart 存活检测恒失败,报 E_RESTART_FAILED
14
+ * (即使 gateway 其实已经起来了)
15
+ * - 改用 `openclaw gateway run --force` + nohup,会和 openclaw 自身的
16
+ * lock / handoff 互相赛跑 → 双进程、端口悬空、健康检查 abnormal closure
17
+ * 而 `openclaw gateway install/start/stop/restart`(systemd user 服务)实测
18
+ * 连续重启始终单进程、端口干净、状态准确。所以一律走 openclaw 的 service 命令。
19
+ */
20
+ // systemctl --user 需要 XDG_RUNTIME_DIR;非交互 SSH exec 下通常已由 pam_systemd
21
+ // 注入,这里再兜底一次,防个别主机 sshd PAM 配置不注入。
22
+ const SYSTEMD_ENV = 'export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"; ';
23
+ // gateway 没被 systemd 接管时(老主机残留 / 异常)按监听端口兜底杀进程。
24
+ // 覆盖 `openclaw` / `node` 两种进程名;且绝不会误杀正在执行本 CLI 的进程。
25
+ // 用 awk 解析 `pid=NNN` 而不是 `grep -oP` —— grep 的 -P(PCRE,含 \K)是 GNU
26
+ // 扩展,非 GNU 环境(BSD grep 等)会整条命令报错、兜底路径静默退化成 no-op。
27
+ // awk -F'pid=' 把每个 `pid=` 后的字段数值化($i+0 取前导数字),POSIX 通用。
28
+ const KILL_GATEWAY_BY_PORT = `ss -ltnHp 'sport = :${OPENCLAW_GATEWAY_PORT}' 2>/dev/null` +
29
+ ` | awk -F'pid=' '{for(i=2;i<=NF;i++){p=$i+0;if(p>0)print p}}'` +
30
+ ` | sort -u | xargs -r kill 2>/dev/null || true`;
31
+ /**
32
+ * 确保 gateway 的 systemd 服务已安装且能常驻。
33
+ * - `loginctl enable-linger`:systemd user 服务默认随最后一个登录会话退出而停,
34
+ * 开 linger 后才能在部署 SSH 断开后 / 主机重启后继续运行。
35
+ * - `openclaw gateway install --force`:幂等(重装覆盖),顺带在 openclaw 升级后
36
+ * 刷新 unit 的 ExecStart。
37
+ */
38
+ export async function installGatewayService() {
39
+ await shell(`loginctl enable-linger root 2>/dev/null || true`).catch(() => { });
40
+ await shell(`${SYSTEMD_ENV}openclaw gateway install --force --port ${OPENCLAW_GATEWAY_PORT}`);
41
+ }
42
+ /** 启动 gateway(先确保 systemd 服务已装)。 */
43
+ export async function startGateway() {
44
+ await installGatewayService();
45
+ await shell(`${SYSTEMD_ENV}openclaw gateway start`);
46
+ }
47
+ /** 重启 gateway(先确保 systemd 服务已装)。 */
48
+ export async function restartGateway() {
49
+ await installGatewayService();
50
+ await shell(`${SYSTEMD_ENV}openclaw gateway restart`);
51
+ }
52
+ // 停掉所有已知的 gateway systemd unit。新版 openclaw 的 unit 名是
53
+ // openclaw-gateway.service;老主机可能是 openclaw.service(早期 cli 用过的
54
+ // unit 名,--user / system 两种 scope 都覆盖)。
55
+ //
56
+ // **必须在按端口 kill 之前先停 unit**:先停 unit,systemd 视为"主动停止"、
57
+ // 不会触发 Restart=;反过来先 kill 进程,systemd 当成异常退出,会按
58
+ // Restart=always/on-failure 立刻把 gateway 拉回来 —— stopDaemonForExclusiveAccess
59
+ // 就拿不到真正的独占窗口,config watcher 写配置的竞争(ConfigMutationConflict)复现。
60
+ // 按端口 kill 只作为非 systemd 托管残留进程的兜底,不能替代停 supervisor。
61
+ const STOP_GATEWAY_UNITS = `${SYSTEMD_ENV}` +
62
+ `openclaw gateway stop 2>/dev/null || true; ` +
63
+ `systemctl --user stop openclaw-gateway.service 2>/dev/null || true; ` +
64
+ `systemctl --user stop openclaw.service 2>/dev/null || true; ` +
65
+ `systemctl stop openclaw.service 2>/dev/null || true`;
66
+ /**
67
+ * 停掉 gateway:先停掉所有已知的 systemd unit(新 + 旧 unit 名),再按监听
68
+ * 端口兜底杀掉非 systemd 托管的残留进程。顺序见 {@link STOP_GATEWAY_UNITS}。
69
+ */
70
+ export async function stopGateway() {
71
+ await shell(STOP_GATEWAY_UNITS).catch(() => { });
72
+ await shell(KILL_GATEWAY_BY_PORT).catch(() => { });
73
+ }
74
+ // `active 但端口未监听` 的最长等待:覆盖 restart 后进程已 active、端口还没
75
+ // bind 的启动窗口。等满仍未监听就判 starting(配置异常 / 卡住),不再傻等。
76
+ const STATUS_PORT_WAIT_ATTEMPTS = 20;
77
+ async function isPortListening() {
78
+ const r = await shellCapture(`ss -ltnH 'sport = :${OPENCLAW_GATEWAY_PORT}' 2>/dev/null || true`);
79
+ return r.stdout.trim().length > 0;
80
+ }
81
+ async function isServiceActive() {
82
+ const r = await shellCapture(`${SYSTEMD_ENV}systemctl --user is-active openclaw-gateway.service 2>/dev/null || true`);
83
+ return r.stdout.trim() === 'active';
84
+ }
85
+ /**
86
+ * 探测 gateway 状态。**健康的唯一标准是 18789 端口已监听** —— 进程 active
87
+ * 不等于对外可服务。
88
+ *
89
+ * - `listening`:端口已监听,健康。
90
+ * - `starting` :systemd 服务 active,但端口在 bounded wait 内始终没监听 ——
91
+ * 配置异常 / 启动卡住 / 仍在初始化,**不算健康**。
92
+ * - `down` :systemd 服务非 active 且端口未监听。
93
+ *
94
+ * `openclaw gateway restart` 返回后服务立刻是 active,但 gateway 进程还要几秒
95
+ * 才 bind 端口。所以端口没起来、服务却 active 时给一段 bounded wait 覆盖这个
96
+ * 启动窗口;服务都不 active 则直接 `down`,不等。
97
+ */
98
+ export async function probeGatewayState() {
99
+ if (await isPortListening())
100
+ return 'listening';
101
+ if (!(await isServiceActive()))
102
+ return 'down';
103
+ for (let i = 0; i < STATUS_PORT_WAIT_ATTEMPTS; i++) {
104
+ await new Promise((resolve) => setTimeout(resolve, 1000));
105
+ if (await isPortListening())
106
+ return 'listening';
107
+ }
108
+ return 'starting';
109
+ }
110
+ /**
111
+ * 清掉 systemd user 目录下的 gateway unit 残留。`openclaw gateway uninstall`
112
+ * 只删主 `.service` 文件,残留的 `.service.bak` 备份和 `.service.d/` drop-in
113
+ * 会在下次 install 时被 systemd 自动 merge 进来(注入过时的 env 等),必须一并清。
114
+ */
115
+ export async function cleanupGatewayServiceFiles() {
116
+ await shell(`${SYSTEMD_ENV}rm -rf /root/.config/systemd/user/openclaw-gateway.service.d` +
117
+ ` /root/.config/systemd/user/openclaw-gateway.service.bak;` +
118
+ ` systemctl --user daemon-reload 2>/dev/null || true`).catch(() => { });
119
+ }
120
+ /**
121
+ * Best-effort stop of the openclaw gateway so cli operations have exclusive
122
+ * write access to openclaw.json + extensions/.
123
+ *
124
+ * Why this is needed: the gateway has a config file watcher that writes its
8
125
  * own metadata back into openclaw.json on every config change. Concurrent
9
126
  * writes during cli's cleanup / `openclaw plugins install` / `openclaw config
10
127
  * set` race with that watcher and produce:
@@ -12,27 +129,16 @@ import { info } from './logger.js';
12
129
  * - ConfigMutationConflictError: config changed since last load (hash check
13
130
  * in openclaw plugins install commit / openclaw config set atomic write)
14
131
  *
15
- * Tries every supervisor we know of, each tolerated if absent. Order matters:
16
- * ask the supervisor (openclaw cli / systemd) first so it doesn't auto-restart
17
- * on us, then pkill -9 as the last resort.
18
- *
19
132
  * Bootstrap orchestrator (commands/install.ts) is responsible for restarting
20
- * the daemon after the full component install pipeline finishes.
133
+ * the gateway after the full component install pipeline finishes.
21
134
  *
22
135
  * @param context A short label for logs, e.g. "dingtalk install" or
23
136
  * "openclaw deployment defaults".
24
137
  */
25
138
  export async function stopDaemonForExclusiveAccess(context) {
26
139
  info(`Stopping openclaw gateway for exclusive access during ${context}...`);
27
- await shell(`openclaw gateway stop 2>/dev/null || true`).catch(() => { });
28
- await shell(`systemctl --user stop openclaw 2>/dev/null || true`).catch(() => { });
29
- await shell(`systemctl stop openclaw 2>/dev/null || true`).catch(() => { });
30
- // Match `pkill -f 'openclaw-gateway'` exactly the same way as
31
- // OpenclawInstaller.uninstall — narrow enough that we don't kill the cli
32
- // process running this code, broad enough to catch the gateway daemon.
33
- await shell(`pkill -9 -f 'openclaw-gateway' 2>/dev/null || true`).catch(() => { });
34
- // 1.5s is enough for systemd's default RestartSec (100ms) to fire if it
35
- // would, and short enough not to drag deploy time noticeably.
140
+ await stopGateway();
141
+ // `systemctl stop` 是显式停,Restart=always 不会把它拉回来;留 1.5s 兜底观察。
36
142
  await new Promise((resolve) => setTimeout(resolve, 1500));
37
143
  }
38
144
  //# sourceMappingURL=openclaw-daemon.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"openclaw-daemon.js","sourceRoot":"","sources":["../../src/utils/openclaw-daemon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,OAAe;IAChE,IAAI,CAAC,yDAAyD,OAAO,KAAK,CAAC,CAAC;IAC5E,MAAM,KAAK,CAAC,2CAA2C,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACzE,MAAM,KAAK,CAAC,oDAAoD,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAClF,MAAM,KAAK,CAAC,6CAA6C,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC3E,8DAA8D;IAC9D,yEAAyE;IACzE,uEAAuE;IACvE,MAAM,KAAK,CAAC,oDAAoD,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAClF,wEAAwE;IACxE,8DAA8D;IAC9D,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;AAC5D,CAAC"}
1
+ {"version":3,"file":"openclaw-daemon.js","sourceRoot":"","sources":["../../src/utils/openclaw-daemon.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okclaw-build/cli",
3
- "version": "1.0.0-beta.41",
3
+ "version": "1.0.0-beta.43",
4
4
  "description": "OKClaw deployment CLI - install and manage OpenClaw, OpenViking, and channel plugins",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",