copilot-hub 0.1.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.
Files changed (128) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +215 -0
  3. package/apps/agent-engine/.env.example +41 -0
  4. package/apps/agent-engine/LICENSE +21 -0
  5. package/apps/agent-engine/README.md +57 -0
  6. package/apps/agent-engine/bot-registry.example.json +28 -0
  7. package/apps/agent-engine/capabilities/example/index.js +3 -0
  8. package/apps/agent-engine/capabilities/example/manifest.json +14 -0
  9. package/apps/agent-engine/dist/agent-worker.js +241 -0
  10. package/apps/agent-engine/dist/config.js +225 -0
  11. package/apps/agent-engine/dist/index.js +352 -0
  12. package/apps/agent-engine/dist/test/project-fingerprint.test.js +40 -0
  13. package/apps/agent-engine/dist/test/thread-id.test.js +12 -0
  14. package/apps/agent-engine/package.json +28 -0
  15. package/apps/control-plane/.env.example +25 -0
  16. package/apps/control-plane/README.md +35 -0
  17. package/apps/control-plane/bot-registry.example.json +40 -0
  18. package/apps/control-plane/capabilities/example/index.js +3 -0
  19. package/apps/control-plane/capabilities/example/manifest.json +14 -0
  20. package/apps/control-plane/dist/agent-worker.js +243 -0
  21. package/apps/control-plane/dist/channels/channel-factory.js +21 -0
  22. package/apps/control-plane/dist/channels/hub-ops-commands.js +752 -0
  23. package/apps/control-plane/dist/channels/telegram-channel.js +743 -0
  24. package/apps/control-plane/dist/channels/whatsapp-channel.js +35 -0
  25. package/apps/control-plane/dist/config.js +230 -0
  26. package/apps/control-plane/dist/copilot-hub.js +138 -0
  27. package/apps/control-plane/dist/index.js +349 -0
  28. package/apps/control-plane/dist/kernel/admin-contract.js +51 -0
  29. package/apps/control-plane/dist/test/project-fingerprint.test.js +40 -0
  30. package/apps/control-plane/dist/test/thread-id.test.js +12 -0
  31. package/apps/control-plane/package.json +27 -0
  32. package/package.json +89 -0
  33. package/packages/contracts/README.md +10 -0
  34. package/packages/contracts/dist/control-plane.d.ts +24 -0
  35. package/packages/contracts/dist/control-plane.js +37 -0
  36. package/packages/contracts/dist/control-plane.js.map +1 -0
  37. package/packages/contracts/dist/index.d.ts +1 -0
  38. package/packages/contracts/dist/index.js +2 -0
  39. package/packages/contracts/dist/index.js.map +1 -0
  40. package/packages/contracts/package.json +27 -0
  41. package/packages/core/README.md +33 -0
  42. package/packages/core/dist/agent-supervisor.d.ts +39 -0
  43. package/packages/core/dist/agent-supervisor.js +552 -0
  44. package/packages/core/dist/agent-supervisor.js.map +1 -0
  45. package/packages/core/dist/bot-manager.d.ts +66 -0
  46. package/packages/core/dist/bot-manager.js +333 -0
  47. package/packages/core/dist/bot-manager.js.map +1 -0
  48. package/packages/core/dist/bot-registry.d.ts +60 -0
  49. package/packages/core/dist/bot-registry.js +381 -0
  50. package/packages/core/dist/bot-registry.js.map +1 -0
  51. package/packages/core/dist/bot-runtime.d.ts +135 -0
  52. package/packages/core/dist/bot-runtime.js +349 -0
  53. package/packages/core/dist/bot-runtime.js.map +1 -0
  54. package/packages/core/dist/bridge-service.d.ts +39 -0
  55. package/packages/core/dist/bridge-service.js +272 -0
  56. package/packages/core/dist/bridge-service.js.map +1 -0
  57. package/packages/core/dist/capability-manager.d.ts +18 -0
  58. package/packages/core/dist/capability-manager.js +335 -0
  59. package/packages/core/dist/capability-manager.js.map +1 -0
  60. package/packages/core/dist/capability-scaffold.d.ts +26 -0
  61. package/packages/core/dist/capability-scaffold.js +118 -0
  62. package/packages/core/dist/capability-scaffold.js.map +1 -0
  63. package/packages/core/dist/channel-factory.d.ts +6 -0
  64. package/packages/core/dist/channel-factory.js +22 -0
  65. package/packages/core/dist/channel-factory.js.map +1 -0
  66. package/packages/core/dist/codex-app-client.d.ts +56 -0
  67. package/packages/core/dist/codex-app-client.js +762 -0
  68. package/packages/core/dist/codex-app-client.js.map +1 -0
  69. package/packages/core/dist/codex-provider.d.ts +31 -0
  70. package/packages/core/dist/codex-provider.js +64 -0
  71. package/packages/core/dist/codex-provider.js.map +1 -0
  72. package/packages/core/dist/control-permission.d.ts +19 -0
  73. package/packages/core/dist/control-permission.js +106 -0
  74. package/packages/core/dist/control-permission.js.map +1 -0
  75. package/packages/core/dist/control-plane-actions.d.ts +1 -0
  76. package/packages/core/dist/control-plane-actions.js +2 -0
  77. package/packages/core/dist/control-plane-actions.js.map +1 -0
  78. package/packages/core/dist/example-capability.d.ts +17 -0
  79. package/packages/core/dist/example-capability.js +22 -0
  80. package/packages/core/dist/example-capability.js.map +1 -0
  81. package/packages/core/dist/extension-contract.d.ts +22 -0
  82. package/packages/core/dist/extension-contract.js +28 -0
  83. package/packages/core/dist/extension-contract.js.map +1 -0
  84. package/packages/core/dist/index.d.ts +26 -0
  85. package/packages/core/dist/index.js +27 -0
  86. package/packages/core/dist/index.js.map +1 -0
  87. package/packages/core/dist/instance-lock.d.ts +9 -0
  88. package/packages/core/dist/instance-lock.js +74 -0
  89. package/packages/core/dist/instance-lock.js.map +1 -0
  90. package/packages/core/dist/kernel-control-plane.d.ts +16 -0
  91. package/packages/core/dist/kernel-control-plane.js +500 -0
  92. package/packages/core/dist/kernel-control-plane.js.map +1 -0
  93. package/packages/core/dist/kernel-version.d.ts +1 -0
  94. package/packages/core/dist/kernel-version.js +2 -0
  95. package/packages/core/dist/kernel-version.js.map +1 -0
  96. package/packages/core/dist/project-fingerprint.d.ts +11 -0
  97. package/packages/core/dist/project-fingerprint.js +33 -0
  98. package/packages/core/dist/project-fingerprint.js.map +1 -0
  99. package/packages/core/dist/provider-factory.d.ts +7 -0
  100. package/packages/core/dist/provider-factory.js +21 -0
  101. package/packages/core/dist/provider-factory.js.map +1 -0
  102. package/packages/core/dist/secret-store.d.ts +18 -0
  103. package/packages/core/dist/secret-store.js +110 -0
  104. package/packages/core/dist/secret-store.js.map +1 -0
  105. package/packages/core/dist/state-store.d.ts +50 -0
  106. package/packages/core/dist/state-store.js +324 -0
  107. package/packages/core/dist/state-store.js.map +1 -0
  108. package/packages/core/dist/telegram-channel.d.ts +27 -0
  109. package/packages/core/dist/telegram-channel.js +951 -0
  110. package/packages/core/dist/telegram-channel.js.map +1 -0
  111. package/packages/core/dist/thread-id.d.ts +1 -0
  112. package/packages/core/dist/thread-id.js +12 -0
  113. package/packages/core/dist/thread-id.js.map +1 -0
  114. package/packages/core/dist/whatsapp-channel.d.ts +26 -0
  115. package/packages/core/dist/whatsapp-channel.js +36 -0
  116. package/packages/core/dist/whatsapp-channel.js.map +1 -0
  117. package/packages/core/dist/workspace-paths.d.ts +5 -0
  118. package/packages/core/dist/workspace-paths.js +77 -0
  119. package/packages/core/dist/workspace-paths.js.map +1 -0
  120. package/packages/core/dist/workspace-policy.d.ts +30 -0
  121. package/packages/core/dist/workspace-policy.js +104 -0
  122. package/packages/core/dist/workspace-policy.js.map +1 -0
  123. package/packages/core/package.json +126 -0
  124. package/scripts/cli.mjs +537 -0
  125. package/scripts/configure.mjs +254 -0
  126. package/scripts/ensure-shared-build.mjs +96 -0
  127. package/scripts/run-node-tests.mjs +52 -0
  128. package/scripts/supervisor.mjs +332 -0
@@ -0,0 +1,332 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import process from "node:process";
5
+ import { spawn } from "node:child_process";
6
+ import { fileURLToPath } from "node:url";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+ const repoRoot = path.resolve(__dirname, "..");
11
+
12
+ const runtimeDir = path.join(repoRoot, ".copilot-hub");
13
+ const pidsDir = path.join(runtimeDir, "pids");
14
+ const logsDir = path.join(repoRoot, "logs");
15
+
16
+ const SERVICES = [
17
+ {
18
+ id: "agent-engine",
19
+ workingDir: path.join(repoRoot, "apps", "agent-engine"),
20
+ entryScript: "dist/index.js",
21
+ logFile: path.join(logsDir, "agent-engine.log"),
22
+ },
23
+
24
+ {
25
+ id: "control-plane",
26
+ workingDir: path.join(repoRoot, "apps", "control-plane"),
27
+ entryScript: "dist/copilot-hub.js",
28
+ logFile: path.join(logsDir, "control-plane.log"),
29
+ },
30
+ ].map((service) => ({
31
+ ...service,
32
+ pidFile: path.join(pidsDir, `${service.id}.json`),
33
+ }));
34
+
35
+ const action = String(process.argv[2] ?? "up")
36
+ .trim()
37
+ .toLowerCase();
38
+
39
+ await main();
40
+
41
+ async function main() {
42
+ switch (action) {
43
+ case "up":
44
+ await startServices();
45
+ return;
46
+ case "down":
47
+ await stopServices();
48
+ return;
49
+ case "restart":
50
+ await stopServices();
51
+ await startServices();
52
+ return;
53
+ case "status":
54
+ showStatus();
55
+ return;
56
+ case "logs":
57
+ showLogs();
58
+ return;
59
+ default:
60
+ printUsage();
61
+ process.exit(1);
62
+ }
63
+ }
64
+
65
+ async function startServices() {
66
+ ensureRuntimeDirs();
67
+
68
+ const started = [];
69
+ for (const service of SERVICES) {
70
+ const ok = await startService(service);
71
+ if (!ok) {
72
+ for (let index = started.length - 1; index >= 0; index -= 1) {
73
+ await stopService(started[index]);
74
+ }
75
+ console.error("One or more services failed to start. Run 'npm run logs' for details.");
76
+ process.exit(1);
77
+ }
78
+ started.push(service);
79
+ }
80
+ }
81
+
82
+ async function stopServices() {
83
+ for (const service of SERVICES) {
84
+ await stopService(service);
85
+ }
86
+ }
87
+
88
+ function showStatus() {
89
+ ensureRuntimeDirs();
90
+
91
+ for (const service of SERVICES) {
92
+ const state = readState(service);
93
+ const pid = normalizePid(state?.pid);
94
+ const running = pid > 0 && isProcessRunning(pid);
95
+
96
+ if (state && !running) {
97
+ removeState(service);
98
+ }
99
+
100
+ console.log(`\n=== ${service.id} ===`);
101
+ console.log(`running: ${running ? "yes" : "no"}`);
102
+ console.log(`pid: ${running ? String(pid) : "-"}`);
103
+ console.log(`logFile: ${service.logFile}`);
104
+ }
105
+ }
106
+
107
+ function showLogs() {
108
+ ensureRuntimeDirs();
109
+
110
+ for (const service of SERVICES) {
111
+ console.log(`\n=== ${service.id} (${service.logFile}) ===`);
112
+ printTail(service.logFile, 120);
113
+ }
114
+ }
115
+
116
+ async function startService(service) {
117
+ const existing = readState(service);
118
+ const existingPid = normalizePid(existing?.pid);
119
+ if (existingPid > 0 && isProcessRunning(existingPid)) {
120
+ console.log(`[${service.id}] already running (pid ${existingPid})`);
121
+ return true;
122
+ }
123
+
124
+ if (existing) {
125
+ removeState(service);
126
+ }
127
+
128
+ fs.mkdirSync(path.dirname(service.logFile), { recursive: true });
129
+ const logFd = fs.openSync(service.logFile, "a");
130
+
131
+ let child;
132
+ try {
133
+ child = spawn(process.execPath, [service.entryScript], {
134
+ cwd: service.workingDir,
135
+ detached: true,
136
+ stdio: ["ignore", logFd, logFd],
137
+ windowsHide: true,
138
+ shell: false,
139
+ env: process.env,
140
+ });
141
+ } finally {
142
+ fs.closeSync(logFd);
143
+ }
144
+
145
+ const pid = normalizePid(child?.pid);
146
+ if (pid <= 0) {
147
+ console.error(`[${service.id}] failed to spawn.`);
148
+ return false;
149
+ }
150
+
151
+ child.unref();
152
+
153
+ writeState(service, {
154
+ pid,
155
+ startedAt: new Date().toISOString(),
156
+ command: `${process.execPath} ${service.entryScript}`,
157
+ });
158
+
159
+ await sleep(250);
160
+
161
+ if (!isProcessRunning(pid)) {
162
+ removeState(service);
163
+ console.error(`[${service.id}] exited immediately. Check logs: ${service.logFile}`);
164
+ return false;
165
+ }
166
+
167
+ console.log(`[${service.id}] started (pid ${pid})`);
168
+ return true;
169
+ }
170
+
171
+ async function stopService(service) {
172
+ const state = readState(service);
173
+ if (!state) {
174
+ console.log(`[${service.id}] not running (no pid file)`);
175
+ return;
176
+ }
177
+
178
+ const pid = normalizePid(state.pid);
179
+ if (pid <= 0) {
180
+ removeState(service);
181
+ console.log(`[${service.id}] removed invalid pid file`);
182
+ return;
183
+ }
184
+
185
+ if (!isProcessRunning(pid)) {
186
+ removeState(service);
187
+ console.log(`[${service.id}] not running (stale pid ${pid})`);
188
+ return;
189
+ }
190
+
191
+ const stopped = await terminateProcess(pid);
192
+ if (stopped) {
193
+ removeState(service);
194
+ console.log(`[${service.id}] stopped`);
195
+ return;
196
+ }
197
+
198
+ console.error(`[${service.id}] could not stop pid ${pid}`);
199
+ }
200
+
201
+ async function terminateProcess(pid) {
202
+ if (process.platform === "win32") {
203
+ await killTreeWindows(pid);
204
+ return waitForExit(pid, 5000);
205
+ }
206
+
207
+ sendSignal(pid, "SIGTERM");
208
+ if (await waitForExit(pid, 5000)) {
209
+ return true;
210
+ }
211
+
212
+ sendSignal(pid, "SIGKILL");
213
+ return waitForExit(pid, 2000);
214
+ }
215
+
216
+ function killTreeWindows(pid) {
217
+ return new Promise((resolve) => {
218
+ const child = spawn("taskkill", ["/PID", String(pid), "/T", "/F"], {
219
+ stdio: "ignore",
220
+ shell: false,
221
+ windowsHide: true,
222
+ });
223
+
224
+ child.once("error", () => resolve());
225
+ child.once("exit", () => resolve());
226
+ });
227
+ }
228
+
229
+ function sendSignal(pid, signal) {
230
+ try {
231
+ process.kill(-pid, signal);
232
+ return;
233
+ } catch {
234
+ // continue
235
+ }
236
+
237
+ try {
238
+ process.kill(pid, signal);
239
+ } catch {
240
+ // ignore
241
+ }
242
+ }
243
+
244
+ async function waitForExit(pid, timeoutMs) {
245
+ const deadline = Date.now() + timeoutMs;
246
+ while (Date.now() < deadline) {
247
+ if (!isProcessRunning(pid)) {
248
+ return true;
249
+ }
250
+ await sleep(150);
251
+ }
252
+
253
+ return !isProcessRunning(pid);
254
+ }
255
+
256
+ function readState(service) {
257
+ if (!fs.existsSync(service.pidFile)) {
258
+ return null;
259
+ }
260
+
261
+ try {
262
+ const raw = fs.readFileSync(service.pidFile, "utf8");
263
+ return JSON.parse(raw);
264
+ } catch {
265
+ return null;
266
+ }
267
+ }
268
+
269
+ function writeState(service, state) {
270
+ fs.mkdirSync(path.dirname(service.pidFile), { recursive: true });
271
+ fs.writeFileSync(service.pidFile, `${JSON.stringify(state, null, 2)}\n`, "utf8");
272
+ }
273
+
274
+ function removeState(service) {
275
+ if (fs.existsSync(service.pidFile)) {
276
+ fs.rmSync(service.pidFile, { force: true });
277
+ }
278
+ }
279
+
280
+ function normalizePid(value) {
281
+ const pid = Number.parseInt(String(value ?? ""), 10);
282
+ if (!Number.isFinite(pid) || pid <= 0) {
283
+ return 0;
284
+ }
285
+ return pid;
286
+ }
287
+
288
+ function isProcessRunning(pid) {
289
+ if (!Number.isInteger(pid) || pid <= 0) {
290
+ return false;
291
+ }
292
+
293
+ try {
294
+ process.kill(pid, 0);
295
+ return true;
296
+ } catch (error) {
297
+ if (error && typeof error === "object" && "code" in error && error.code === "EPERM") {
298
+ return true;
299
+ }
300
+ return false;
301
+ }
302
+ }
303
+
304
+ function ensureRuntimeDirs() {
305
+ fs.mkdirSync(runtimeDir, { recursive: true });
306
+ fs.mkdirSync(pidsDir, { recursive: true });
307
+ fs.mkdirSync(logsDir, { recursive: true });
308
+ }
309
+
310
+ function printTail(filePath, lines) {
311
+ if (!fs.existsSync(filePath)) {
312
+ console.log("(no log file yet)");
313
+ return;
314
+ }
315
+
316
+ const content = fs.readFileSync(filePath, "utf8");
317
+ const entries = content.split(/\r?\n/).filter(Boolean);
318
+ const start = Math.max(0, entries.length - lines);
319
+ for (let index = start; index < entries.length; index += 1) {
320
+ console.log(entries[index]);
321
+ }
322
+ }
323
+
324
+ function sleep(ms) {
325
+ return new Promise((resolve) => {
326
+ setTimeout(resolve, ms);
327
+ });
328
+ }
329
+
330
+ function printUsage() {
331
+ console.log("Usage: node scripts/supervisor.mjs <up|down|restart|status|logs>");
332
+ }