openclaw-telegram-manager 1.0.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/LICENSE +21 -0
- package/README.md +110 -0
- package/dist/commands/archive.d.ts +4 -0
- package/dist/commands/archive.d.ts.map +1 -0
- package/dist/commands/archive.js +71 -0
- package/dist/commands/archive.js.map +1 -0
- package/dist/commands/doctor-all.d.ts +3 -0
- package/dist/commands/doctor-all.d.ts.map +1 -0
- package/dist/commands/doctor-all.js +193 -0
- package/dist/commands/doctor-all.js.map +1 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +74 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/help.d.ts +4 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +8 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/init.d.ts +17 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +304 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/list.d.ts +3 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +22 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/rename.d.ts +3 -0
- package/dist/commands/rename.d.ts.map +1 -0
- package/dist/commands/rename.js +115 -0
- package/dist/commands/rename.js.map +1 -0
- package/dist/commands/snooze.d.ts +3 -0
- package/dist/commands/snooze.d.ts.map +1 -0
- package/dist/commands/snooze.js +52 -0
- package/dist/commands/snooze.js.map +1 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +48 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +3 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +38 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/upgrade.d.ts +3 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +52 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/audit.d.ts +12 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/audit.js +35 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/auth.d.ts +26 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +73 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/capsule.d.ts +27 -0
- package/dist/lib/capsule.d.ts.map +1 -0
- package/dist/lib/capsule.js +130 -0
- package/dist/lib/capsule.js.map +1 -0
- package/dist/lib/config-restart.d.ts +23 -0
- package/dist/lib/config-restart.d.ts.map +1 -0
- package/dist/lib/config-restart.js +129 -0
- package/dist/lib/config-restart.js.map +1 -0
- package/dist/lib/doctor-checks.d.ts +50 -0
- package/dist/lib/doctor-checks.d.ts.map +1 -0
- package/dist/lib/doctor-checks.js +421 -0
- package/dist/lib/doctor-checks.js.map +1 -0
- package/dist/lib/include-generator.d.ts +35 -0
- package/dist/lib/include-generator.d.ts.map +1 -0
- package/dist/lib/include-generator.js +140 -0
- package/dist/lib/include-generator.js.map +1 -0
- package/dist/lib/registry.d.ts +27 -0
- package/dist/lib/registry.d.ts.map +1 -0
- package/dist/lib/registry.js +154 -0
- package/dist/lib/registry.js.map +1 -0
- package/dist/lib/security.d.ts +57 -0
- package/dist/lib/security.d.ts.map +1 -0
- package/dist/lib/security.js +133 -0
- package/dist/lib/security.js.map +1 -0
- package/dist/lib/telegram.d.ts +55 -0
- package/dist/lib/telegram.d.ts.map +1 -0
- package/dist/lib/telegram.js +254 -0
- package/dist/lib/telegram.js.map +1 -0
- package/dist/lib/types.d.ts +120 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +85 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/setup.d.ts +3 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +333 -0
- package/dist/setup.js.map +1 -0
- package/dist/tool.d.ts +15 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +201 -0
- package/dist/tool.js.map +1 -0
- package/openclaw.plugin.json +9 -0
- package/package.json +48 -0
- package/skills/topic/SKILL.md +35 -0
- package/src/commands/archive.ts +89 -0
- package/src/commands/doctor-all.ts +243 -0
- package/src/commands/doctor.ts +100 -0
- package/src/commands/help.ts +11 -0
- package/src/commands/init.ts +376 -0
- package/src/commands/list.ts +28 -0
- package/src/commands/rename.ts +140 -0
- package/src/commands/snooze.ts +69 -0
- package/src/commands/status.ts +59 -0
- package/src/commands/sync.ts +46 -0
- package/src/commands/upgrade.ts +64 -0
- package/src/index.ts +54 -0
- package/src/lib/audit.ts +44 -0
- package/src/lib/auth.ts +96 -0
- package/src/lib/capsule.ts +206 -0
- package/src/lib/config-restart.ts +167 -0
- package/src/lib/doctor-checks.ts +639 -0
- package/src/lib/include-generator.ts +174 -0
- package/src/lib/registry.ts +197 -0
- package/src/lib/security.ts +174 -0
- package/src/lib/telegram.ts +311 -0
- package/src/lib/types.ts +172 -0
- package/src/setup.ts +402 -0
- package/src/templates/base/COMMANDS.md +3 -0
- package/src/templates/base/CRON.md +3 -0
- package/src/templates/base/LINKS.md +3 -0
- package/src/templates/base/NOTES.md +3 -0
- package/src/templates/base/README.md +3 -0
- package/src/templates/base/STATUS.md +13 -0
- package/src/templates/base/TODO.md +11 -0
- package/src/templates/overlays/coding/ARCHITECTURE.md +3 -0
- package/src/templates/overlays/coding/DEPLOY.md +3 -0
- package/src/templates/overlays/marketing/CAMPAIGNS.md +3 -0
- package/src/templates/overlays/marketing/METRICS.md +3 -0
- package/src/templates/overlays/research/FINDINGS.md +3 -0
- package/src/templates/overlays/research/SOURCES.md +3 -0
- package/src/tool.ts +282 -0
package/src/setup.ts
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import * as crypto from 'node:crypto';
|
|
6
|
+
import { execSync } from 'node:child_process';
|
|
7
|
+
import * as readline from 'node:readline';
|
|
8
|
+
|
|
9
|
+
// ── Constants ──────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
const MIN_OPENCLAW_VERSION = '2026.1.0';
|
|
12
|
+
const INCLUDE_FILENAME = 'telegram-manager.generated.groups.json5';
|
|
13
|
+
const REGISTRY_FILENAME = 'topics.json';
|
|
14
|
+
|
|
15
|
+
// ── Main ──────────────────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
async function main(): Promise<void> {
|
|
18
|
+
console.log('openclaw-telegram-manager setup');
|
|
19
|
+
console.log('================================\n');
|
|
20
|
+
|
|
21
|
+
// Step 1: Check OpenClaw version
|
|
22
|
+
const version = checkOpenClawVersion();
|
|
23
|
+
console.log(`[1/11] OpenClaw version: ${version}`);
|
|
24
|
+
|
|
25
|
+
// Step 2: Locate config directory
|
|
26
|
+
const configDir = locateConfigDir();
|
|
27
|
+
console.log(`[2/11] Config directory: ${configDir}`);
|
|
28
|
+
|
|
29
|
+
// Step 3: Check directory permissions
|
|
30
|
+
checkDirPermissions(configDir);
|
|
31
|
+
console.log('[3/11] Directory permissions checked');
|
|
32
|
+
|
|
33
|
+
// Step 4: Install plugin
|
|
34
|
+
installPlugin();
|
|
35
|
+
console.log('[4/11] Plugin installation checked');
|
|
36
|
+
|
|
37
|
+
// Step 5: Patch openclaw.json with $include reference
|
|
38
|
+
patchConfig(configDir);
|
|
39
|
+
console.log('[5/11] Config patched with $include reference');
|
|
40
|
+
|
|
41
|
+
// Step 6: Create workspace directory structure
|
|
42
|
+
const workspaceDir = path.join(configDir, 'workspace');
|
|
43
|
+
const projectsDir = path.join(workspaceDir, 'projects');
|
|
44
|
+
ensureDir(projectsDir);
|
|
45
|
+
console.log(`[6/11] Workspace directory: ${projectsDir}`);
|
|
46
|
+
|
|
47
|
+
// Step 7: Initialize empty registry
|
|
48
|
+
initRegistry(projectsDir);
|
|
49
|
+
console.log('[7/11] Registry initialized');
|
|
50
|
+
|
|
51
|
+
// Step 8: Create empty generated include
|
|
52
|
+
createEmptyInclude(configDir);
|
|
53
|
+
console.log('[8/11] Empty include file created');
|
|
54
|
+
|
|
55
|
+
// Step 9: Optional cron setup
|
|
56
|
+
const isInteractive = process.stdin.isTTY === true;
|
|
57
|
+
const setupCron = isInteractive ? await promptYesNo('Set up daily doctor cron job? [Y/n] ') : true;
|
|
58
|
+
if (setupCron) {
|
|
59
|
+
const groupId = isInteractive
|
|
60
|
+
? await promptInput('Enter your Telegram group ID (e.g., -1003731538650): ')
|
|
61
|
+
: '';
|
|
62
|
+
setupDoctorCron(configDir, groupId);
|
|
63
|
+
console.log('[9/11] Doctor cron job configured');
|
|
64
|
+
} else {
|
|
65
|
+
console.log('[9/11] Skipped cron setup');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Step 10: Trigger gateway restart
|
|
69
|
+
triggerRestart(configDir);
|
|
70
|
+
console.log('[10/11] Gateway restart triggered');
|
|
71
|
+
|
|
72
|
+
// Step 11: Print summary
|
|
73
|
+
printSummary(configDir, projectsDir);
|
|
74
|
+
console.log('[11/11] Setup complete!\n');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── Step implementations ──────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
function checkOpenClawVersion(): string {
|
|
80
|
+
let version: string;
|
|
81
|
+
try {
|
|
82
|
+
version = execSync('openclaw --version', { encoding: 'utf-8' }).trim();
|
|
83
|
+
} catch {
|
|
84
|
+
console.error(
|
|
85
|
+
'Error: OpenClaw not found. Please install OpenClaw (>=2026.1.0) first.',
|
|
86
|
+
);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Extract version number (e.g., "openclaw 2026.2.0" -> "2026.2.0")
|
|
91
|
+
const match = version.match(/(\d+\.\d+\.\d+)/);
|
|
92
|
+
if (!match) {
|
|
93
|
+
console.warn(`Warning: Could not parse OpenClaw version from "${version}". Proceeding anyway.`);
|
|
94
|
+
return version;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const versionStr = match[1]!;
|
|
98
|
+
if (compareVersions(versionStr, MIN_OPENCLAW_VERSION) < 0) {
|
|
99
|
+
console.error(
|
|
100
|
+
`Error: OpenClaw ${versionStr} found, but openclaw-telegram-manager requires >=${MIN_OPENCLAW_VERSION}. Please upgrade.`,
|
|
101
|
+
);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return versionStr;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function locateConfigDir(): string {
|
|
109
|
+
// Check environment variable
|
|
110
|
+
const envDir = process.env['OPENCLAW_CONFIG_DIR'];
|
|
111
|
+
if (envDir && fs.existsSync(envDir)) {
|
|
112
|
+
return path.resolve(envDir);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check default location
|
|
116
|
+
const homeDir = process.env['HOME'] ?? process.env['USERPROFILE'] ?? '';
|
|
117
|
+
const defaultDir = path.join(homeDir, '.openclaw');
|
|
118
|
+
if (fs.existsSync(defaultDir)) {
|
|
119
|
+
return defaultDir;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Walk up from cwd looking for openclaw.json
|
|
123
|
+
let dir = process.cwd();
|
|
124
|
+
while (dir !== path.dirname(dir)) {
|
|
125
|
+
if (fs.existsSync(path.join(dir, 'openclaw.json'))) {
|
|
126
|
+
return dir;
|
|
127
|
+
}
|
|
128
|
+
dir = path.dirname(dir);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
console.error(
|
|
132
|
+
'Error: Could not find OpenClaw config directory. Set $OPENCLAW_CONFIG_DIR or ensure ~/.openclaw/ exists.',
|
|
133
|
+
);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function checkDirPermissions(dir: string): void {
|
|
138
|
+
try {
|
|
139
|
+
const stat = fs.statSync(dir);
|
|
140
|
+
const mode = stat.mode;
|
|
141
|
+
const permissions = (mode & 0o777).toString(8);
|
|
142
|
+
|
|
143
|
+
// Check for world-writable or group-writable
|
|
144
|
+
if (mode & 0o002) {
|
|
145
|
+
console.warn(
|
|
146
|
+
`Warning: ${dir} is world-writable (${permissions}). Consider restricting to owner-only (chmod 700).`,
|
|
147
|
+
);
|
|
148
|
+
} else if (mode & 0o020) {
|
|
149
|
+
console.warn(
|
|
150
|
+
`Warning: ${dir} is group-writable (${permissions}). Consider restricting to owner-only (chmod 700).`,
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
console.warn(`Warning: Could not check permissions for ${dir}.`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function installPlugin(): void {
|
|
159
|
+
try {
|
|
160
|
+
// Check if already installed
|
|
161
|
+
const result = execSync('openclaw plugins list', { encoding: 'utf-8' });
|
|
162
|
+
if (result.includes('openclaw-telegram-manager')) {
|
|
163
|
+
console.log(' Plugin already installed, skipping.');
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
} catch {
|
|
167
|
+
// plugins list might not be available, try installing anyway
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
execSync('openclaw plugins install openclaw-telegram-manager', {
|
|
172
|
+
encoding: 'utf-8',
|
|
173
|
+
stdio: 'inherit',
|
|
174
|
+
});
|
|
175
|
+
} catch {
|
|
176
|
+
console.warn(
|
|
177
|
+
' Warning: Could not install plugin via `openclaw plugins install`. You may need to install manually.',
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function patchConfig(configDir: string): void {
|
|
183
|
+
const configPath = path.join(configDir, 'openclaw.json');
|
|
184
|
+
|
|
185
|
+
if (!fs.existsSync(configPath)) {
|
|
186
|
+
console.warn(` Warning: ${configPath} not found. Skipping config patch.`);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let content: string;
|
|
191
|
+
try {
|
|
192
|
+
content = fs.readFileSync(configPath, 'utf-8');
|
|
193
|
+
} catch (err) {
|
|
194
|
+
console.warn(` Warning: Could not read ${configPath}. Skipping config patch.`);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Check if $include reference already exists
|
|
199
|
+
if (content.includes(INCLUDE_FILENAME)) {
|
|
200
|
+
console.log(' $include reference already present, skipping.');
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Parse as JSON (OpenClaw config may be JSON or JSON5)
|
|
205
|
+
let config: Record<string, unknown>;
|
|
206
|
+
try {
|
|
207
|
+
config = JSON.parse(content) as Record<string, unknown>;
|
|
208
|
+
} catch {
|
|
209
|
+
// Try a more lenient approach: just check for the string and warn
|
|
210
|
+
console.warn(
|
|
211
|
+
' Warning: Could not parse openclaw.json as JSON. Please manually add the $include reference.',
|
|
212
|
+
);
|
|
213
|
+
console.warn(` Add to channels.telegram.groups: { "$include": "./${INCLUDE_FILENAME}" }`);
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Ensure path exists: channels.telegram.groups
|
|
218
|
+
if (!config['channels']) config['channels'] = {};
|
|
219
|
+
const channels = config['channels'] as Record<string, unknown>;
|
|
220
|
+
|
|
221
|
+
if (!channels['telegram']) channels['telegram'] = {};
|
|
222
|
+
const telegram = channels['telegram'] as Record<string, unknown>;
|
|
223
|
+
|
|
224
|
+
// Set groups to $include
|
|
225
|
+
telegram['groups'] = { $include: `./${INCLUDE_FILENAME}` };
|
|
226
|
+
|
|
227
|
+
// Backup and write
|
|
228
|
+
const bakPath = configPath + '.bak';
|
|
229
|
+
fs.copyFileSync(configPath, bakPath);
|
|
230
|
+
|
|
231
|
+
const newContent = JSON.stringify(config, null, 2) + '\n';
|
|
232
|
+
fs.writeFileSync(configPath, newContent, { mode: 0o600 });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function ensureDir(dir: string): void {
|
|
236
|
+
if (!fs.existsSync(dir)) {
|
|
237
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function initRegistry(projectsDir: string): void {
|
|
242
|
+
const registryPath = path.join(projectsDir, REGISTRY_FILENAME);
|
|
243
|
+
|
|
244
|
+
if (fs.existsSync(registryPath)) {
|
|
245
|
+
console.log(' Registry already exists, skipping initialization.');
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const callbackSecret = crypto.randomBytes(32).toString('hex');
|
|
250
|
+
const registry = {
|
|
251
|
+
version: 1,
|
|
252
|
+
topicManagerAdmins: [],
|
|
253
|
+
callbackSecret,
|
|
254
|
+
lastDoctorAllRunAt: null,
|
|
255
|
+
maxTopics: 100,
|
|
256
|
+
topics: {},
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
fs.writeFileSync(registryPath, JSON.stringify(registry, null, 2) + '\n', {
|
|
260
|
+
mode: 0o600,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function createEmptyInclude(configDir: string): void {
|
|
265
|
+
const includePath = path.join(configDir, INCLUDE_FILENAME);
|
|
266
|
+
|
|
267
|
+
if (fs.existsSync(includePath)) {
|
|
268
|
+
console.log(' Include file already exists, skipping.');
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const content = [
|
|
273
|
+
'// This file is generated by telegram-manager. Do not hand-edit.',
|
|
274
|
+
'{}',
|
|
275
|
+
'',
|
|
276
|
+
].join('\n');
|
|
277
|
+
|
|
278
|
+
fs.writeFileSync(includePath, content, { mode: 0o600 });
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function setupDoctorCron(configDir: string, groupId: string): void {
|
|
282
|
+
const cronDir = path.join(configDir, 'cron');
|
|
283
|
+
ensureDir(cronDir);
|
|
284
|
+
|
|
285
|
+
const cronJobPath = path.join(cronDir, 'topic-doctor-daily.json');
|
|
286
|
+
|
|
287
|
+
if (fs.existsSync(cronJobPath)) {
|
|
288
|
+
console.log(' Cron job already exists, skipping.');
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const target = groupId
|
|
293
|
+
? `${groupId}:topic:1`
|
|
294
|
+
: '-100XXXXXXXXXX:topic:1';
|
|
295
|
+
|
|
296
|
+
const cronJob = {
|
|
297
|
+
name: 'topic-doctor-daily',
|
|
298
|
+
schedule: { kind: 'cron', expr: '0 9 * * *', tz: 'UTC' },
|
|
299
|
+
sessionTarget: 'isolated',
|
|
300
|
+
payload: {
|
|
301
|
+
kind: 'agentTurn',
|
|
302
|
+
message:
|
|
303
|
+
'Run topic doctor health checks on all registered topics. Check the registry at projects/topics.json and evaluate each eligible topic. Post per-topic reports with inline keyboards. If any topic\'s thread returns an API error (deleted/migrated), log it and continue to the next topic.',
|
|
304
|
+
timeoutSeconds: 300,
|
|
305
|
+
},
|
|
306
|
+
delivery: {
|
|
307
|
+
mode: 'announce',
|
|
308
|
+
channel: 'telegram',
|
|
309
|
+
to: target,
|
|
310
|
+
bestEffort: true,
|
|
311
|
+
},
|
|
312
|
+
enabled: true,
|
|
313
|
+
deleteAfterRun: false,
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
fs.writeFileSync(cronJobPath, JSON.stringify(cronJob, null, 2) + '\n', {
|
|
317
|
+
mode: 0o600,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
if (!groupId) {
|
|
321
|
+
console.warn(
|
|
322
|
+
' Warning: No group ID provided. Edit the cron job at:',
|
|
323
|
+
);
|
|
324
|
+
console.warn(` ${cronJobPath}`);
|
|
325
|
+
console.warn(' Replace -100XXXXXXXXXX with your actual group ID.');
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function triggerRestart(configDir: string): void {
|
|
330
|
+
try {
|
|
331
|
+
execSync('openclaw gateway restart', {
|
|
332
|
+
encoding: 'utf-8',
|
|
333
|
+
timeout: 10_000,
|
|
334
|
+
});
|
|
335
|
+
} catch {
|
|
336
|
+
console.warn(
|
|
337
|
+
' Warning: Could not restart gateway. Run `openclaw gateway restart` manually.',
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function printSummary(configDir: string, projectsDir: string): void {
|
|
343
|
+
console.log('\n================================');
|
|
344
|
+
console.log('Setup complete!\n');
|
|
345
|
+
console.log('What was done:');
|
|
346
|
+
console.log(` - Config directory: ${configDir}`);
|
|
347
|
+
console.log(` - Projects directory: ${projectsDir}`);
|
|
348
|
+
console.log(` - Registry: ${path.join(projectsDir, REGISTRY_FILENAME)}`);
|
|
349
|
+
console.log(` - Include: ${path.join(configDir, INCLUDE_FILENAME)}`);
|
|
350
|
+
console.log('\nNext steps:');
|
|
351
|
+
console.log(' 1. Go to any Telegram forum topic');
|
|
352
|
+
console.log(' 2. Type /topic init');
|
|
353
|
+
console.log(' 3. The topic will be registered and a capsule created');
|
|
354
|
+
console.log('\nFor help: /topic help');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
358
|
+
|
|
359
|
+
function compareVersions(a: string, b: string): number {
|
|
360
|
+
const aParts = a.split('.').map(Number);
|
|
361
|
+
const bParts = b.split('.').map(Number);
|
|
362
|
+
for (let i = 0; i < Math.max(aParts.length, bParts.length); i++) {
|
|
363
|
+
const aVal = aParts[i] ?? 0;
|
|
364
|
+
const bVal = bParts[i] ?? 0;
|
|
365
|
+
if (aVal !== bVal) return aVal - bVal;
|
|
366
|
+
}
|
|
367
|
+
return 0;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function promptYesNo(question: string): Promise<boolean> {
|
|
371
|
+
return new Promise((resolve) => {
|
|
372
|
+
const rl = readline.createInterface({
|
|
373
|
+
input: process.stdin,
|
|
374
|
+
output: process.stdout,
|
|
375
|
+
});
|
|
376
|
+
rl.question(question, (answer) => {
|
|
377
|
+
rl.close();
|
|
378
|
+
const normalized = answer.trim().toLowerCase();
|
|
379
|
+
resolve(normalized === '' || normalized === 'y' || normalized === 'yes');
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function promptInput(question: string): Promise<string> {
|
|
385
|
+
return new Promise((resolve) => {
|
|
386
|
+
const rl = readline.createInterface({
|
|
387
|
+
input: process.stdin,
|
|
388
|
+
output: process.stdout,
|
|
389
|
+
});
|
|
390
|
+
rl.question(question, (answer) => {
|
|
391
|
+
rl.close();
|
|
392
|
+
resolve(answer.trim());
|
|
393
|
+
});
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// ── Entry point ───────────────────────────────────────────────────────
|
|
398
|
+
|
|
399
|
+
main().catch((err) => {
|
|
400
|
+
console.error('Setup failed:', err instanceof Error ? err.message : String(err));
|
|
401
|
+
process.exit(1);
|
|
402
|
+
});
|