copilot-hub 0.1.19 → 0.1.21
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/README.md +3 -2
- package/apps/agent-engine/dist/config.js +58 -0
- package/apps/agent-engine/dist/index.js +90 -16
- package/apps/control-plane/dist/channels/codex-quota-cache.js +16 -0
- package/apps/control-plane/dist/channels/hub-model-utils.js +244 -24
- package/apps/control-plane/dist/channels/hub-ops-commands.js +631 -279
- package/apps/control-plane/dist/channels/telegram-channel.js +5 -7
- package/apps/control-plane/dist/config.js +58 -0
- package/apps/control-plane/dist/index.js +16 -0
- package/apps/control-plane/dist/test/hub-model-utils.test.js +110 -13
- package/package.json +3 -2
- package/packages/core/dist/agent-supervisor.d.ts +5 -0
- package/packages/core/dist/agent-supervisor.js +11 -0
- package/packages/core/dist/agent-supervisor.js.map +1 -1
- package/packages/core/dist/bot-manager.js +17 -1
- package/packages/core/dist/bot-manager.js.map +1 -1
- package/packages/core/dist/bot-runtime.d.ts +4 -0
- package/packages/core/dist/bot-runtime.js +5 -1
- package/packages/core/dist/bot-runtime.js.map +1 -1
- package/packages/core/dist/codex-app-client.d.ts +13 -2
- package/packages/core/dist/codex-app-client.js +51 -13
- package/packages/core/dist/codex-app-client.js.map +1 -1
- package/packages/core/dist/codex-app-utils.d.ts +6 -0
- package/packages/core/dist/codex-app-utils.js +49 -0
- package/packages/core/dist/codex-app-utils.js.map +1 -1
- package/packages/core/dist/codex-provider.d.ts +3 -1
- package/packages/core/dist/codex-provider.js +3 -1
- package/packages/core/dist/codex-provider.js.map +1 -1
- package/packages/core/dist/kernel-control-plane.d.ts +1 -0
- package/packages/core/dist/kernel-control-plane.js +132 -13
- package/packages/core/dist/kernel-control-plane.js.map +1 -1
- package/packages/core/dist/provider-factory.d.ts +2 -0
- package/packages/core/dist/provider-factory.js +3 -0
- package/packages/core/dist/provider-factory.js.map +1 -1
- package/packages/core/dist/provider-options.js +24 -17
- package/packages/core/dist/provider-options.js.map +1 -1
- package/packages/core/dist/state-store.d.ts +1 -0
- package/packages/core/dist/state-store.js +28 -2
- package/packages/core/dist/state-store.js.map +1 -1
- package/packages/core/dist/telegram-channel.d.ts +1 -0
- package/packages/core/dist/telegram-channel.js +3 -0
- package/packages/core/dist/telegram-channel.js.map +1 -1
- package/scripts/dist/cli.mjs +132 -203
- package/scripts/dist/codex-runtime.mjs +352 -0
- package/scripts/dist/codex-version.mjs +91 -0
- package/scripts/dist/configure.mjs +26 -49
- package/scripts/dist/daemon.mjs +58 -0
- package/scripts/src/cli.mts +166 -233
- package/scripts/src/codex-runtime.mts +499 -0
- package/scripts/src/codex-version.mts +114 -0
- package/scripts/src/configure.mts +30 -65
- package/scripts/src/daemon.mts +69 -0
- package/scripts/test/codex-version.test.mjs +21 -0
|
@@ -14,6 +14,7 @@ const engineExamplePath = path.join(repoRoot, "apps", "agent-engine", ".env.exam
|
|
|
14
14
|
const controlPlaneEnvPath = path.join(repoRoot, "apps", "control-plane", ".env");
|
|
15
15
|
const controlPlaneExamplePath = path.join(repoRoot, "apps", "control-plane", ".env.example");
|
|
16
16
|
const TELEGRAM_TOKEN_PATTERN = /^\d{5,}:[A-Za-z0-9_-]{20,}$/;
|
|
17
|
+
const DEFAULT_CONTROL_PLANE_TOKEN_ENV = "HUB_TELEGRAM_TOKEN";
|
|
17
18
|
|
|
18
19
|
const args = new Set(process.argv.slice(2));
|
|
19
20
|
const requiredOnly = args.has("--required-only");
|
|
@@ -33,9 +34,8 @@ async function main() {
|
|
|
33
34
|
if (requiredOnly) {
|
|
34
35
|
await configureRequiredTokens({ rl, controlPlaneLines });
|
|
35
36
|
} else {
|
|
36
|
-
await configureAll({ rl,
|
|
37
|
+
await configureAll({ rl, controlPlaneLines });
|
|
37
38
|
console.log("\nSaved:");
|
|
38
|
-
console.log(`- ${relativeFromRepo(engineEnvPath)}`);
|
|
39
39
|
console.log(`- ${relativeFromRepo(controlPlaneEnvPath)}`);
|
|
40
40
|
console.log("\nNext step:");
|
|
41
41
|
console.log("1) npm run start");
|
|
@@ -53,7 +53,7 @@ async function configureRequiredTokens({ rl, controlPlaneLines }) {
|
|
|
53
53
|
|
|
54
54
|
const controlPlaneTokenEnvName = nonEmpty(
|
|
55
55
|
controlPlaneMap.HUB_TELEGRAM_TOKEN_ENV,
|
|
56
|
-
|
|
56
|
+
DEFAULT_CONTROL_PLANE_TOKEN_ENV,
|
|
57
57
|
);
|
|
58
58
|
setEnvValue(controlPlaneLines, "HUB_TELEGRAM_TOKEN_ENV", controlPlaneTokenEnvName);
|
|
59
59
|
|
|
@@ -72,55 +72,33 @@ async function configureRequiredTokens({ rl, controlPlaneLines }) {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
console.log("Missing or invalid hub token. Please enter a valid Telegram bot token.");
|
|
75
|
-
const value = await askRequiredTelegramToken(
|
|
76
|
-
rl,
|
|
77
|
-
`Token value for ${controlPlaneTokenEnvName} (control-plane)`,
|
|
78
|
-
);
|
|
75
|
+
const value = await askRequiredTelegramToken(rl, "Control-plane Telegram token");
|
|
79
76
|
setEnvValue(controlPlaneLines, controlPlaneTokenEnvName, value);
|
|
80
77
|
console.log("Required token saved.");
|
|
81
78
|
}
|
|
82
79
|
|
|
83
|
-
async function configureAll({ rl,
|
|
84
|
-
const engineMap = parseEnvMap(engineLines);
|
|
80
|
+
async function configureAll({ rl, controlPlaneLines }) {
|
|
85
81
|
const controlPlaneMap = parseEnvMap(controlPlaneLines);
|
|
86
82
|
|
|
87
|
-
console.log("\nCopilot Hub
|
|
83
|
+
console.log("\nCopilot Hub control-plane configuration\n");
|
|
88
84
|
|
|
89
85
|
const controlPlaneTokenEnvDefault = nonEmpty(
|
|
90
86
|
controlPlaneMap.HUB_TELEGRAM_TOKEN_ENV,
|
|
91
|
-
|
|
92
|
-
);
|
|
93
|
-
const controlPlaneTokenEnvName = await ask(
|
|
94
|
-
rl,
|
|
95
|
-
"control-plane token variable",
|
|
96
|
-
controlPlaneTokenEnvDefault,
|
|
97
|
-
);
|
|
98
|
-
setEnvValue(controlPlaneLines, "HUB_TELEGRAM_TOKEN_ENV", controlPlaneTokenEnvName);
|
|
99
|
-
const currentControlPlaneToken = parseEnvMap(controlPlaneLines)[controlPlaneTokenEnvName] ?? "";
|
|
100
|
-
const newControlPlaneToken = await ask(
|
|
101
|
-
rl,
|
|
102
|
-
`Token value for ${controlPlaneTokenEnvName} (control-plane, Enter to keep current)`,
|
|
103
|
-
"",
|
|
87
|
+
DEFAULT_CONTROL_PLANE_TOKEN_ENV,
|
|
104
88
|
);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
89
|
+
setEnvValue(controlPlaneLines, "HUB_TELEGRAM_TOKEN_ENV", controlPlaneTokenEnvDefault);
|
|
90
|
+
const currentControlPlaneToken = String(
|
|
91
|
+
parseEnvMap(controlPlaneLines)[controlPlaneTokenEnvDefault] ?? "",
|
|
92
|
+
).trim();
|
|
110
93
|
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
);
|
|
119
|
-
if (newAgentToken) {
|
|
120
|
-
setEnvValue(engineLines, "TELEGRAM_TOKEN_AGENT_1", newAgentToken);
|
|
121
|
-
} else if (!currentAgentToken) {
|
|
122
|
-
console.log("- No value set for TELEGRAM_TOKEN_AGENT_1 yet.");
|
|
123
|
-
}
|
|
94
|
+
const newControlPlaneToken = currentControlPlaneToken
|
|
95
|
+
? await askTelegramToken(rl, "Control-plane Telegram token (press Enter to keep current)", true)
|
|
96
|
+
: await askRequiredTelegramToken(rl, "Control-plane Telegram token");
|
|
97
|
+
|
|
98
|
+
if (newControlPlaneToken) {
|
|
99
|
+
setEnvValue(controlPlaneLines, controlPlaneTokenEnvDefault, newControlPlaneToken);
|
|
100
|
+
} else {
|
|
101
|
+
console.log("- Control-plane token left unchanged.");
|
|
124
102
|
}
|
|
125
103
|
}
|
|
126
104
|
|
|
@@ -212,15 +190,6 @@ function escapeRegex(value) {
|
|
|
212
190
|
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
213
191
|
}
|
|
214
192
|
|
|
215
|
-
async function ask(rl, label, fallback) {
|
|
216
|
-
const value = await rl.question(`${label}${fallback ? ` [${fallback}]` : ""}: `);
|
|
217
|
-
const normalized = String(value ?? "").trim();
|
|
218
|
-
if (!normalized) {
|
|
219
|
-
return String(fallback ?? "").trim();
|
|
220
|
-
}
|
|
221
|
-
return normalized;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
193
|
async function askRequired(rl, label) {
|
|
225
194
|
while (true) {
|
|
226
195
|
const value = await rl.question(`${label}: `);
|
|
@@ -242,22 +211,18 @@ async function askRequiredTelegramToken(rl, label) {
|
|
|
242
211
|
}
|
|
243
212
|
}
|
|
244
213
|
|
|
245
|
-
async function
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}
|
|
257
|
-
if (value === "n" || value === "no") {
|
|
258
|
-
return false;
|
|
214
|
+
async function askTelegramToken(rl, label, allowEmpty) {
|
|
215
|
+
while (true) {
|
|
216
|
+
const value = await rl.question(`${label}: `);
|
|
217
|
+
const normalized = String(value ?? "").trim();
|
|
218
|
+
if (!normalized && allowEmpty) {
|
|
219
|
+
return "";
|
|
220
|
+
}
|
|
221
|
+
if (isUsableTelegramToken(normalized)) {
|
|
222
|
+
return normalized;
|
|
223
|
+
}
|
|
224
|
+
console.log("Token format looks invalid. Expected format like: 123456789:AA...");
|
|
259
225
|
}
|
|
260
|
-
return defaultYes;
|
|
261
226
|
}
|
|
262
227
|
|
|
263
228
|
function relativeFromRepo(filePath) {
|
package/scripts/src/daemon.mts
CHANGED
|
@@ -5,6 +5,13 @@ import process from "node:process";
|
|
|
5
5
|
import { spawn, spawnSync } from "node:child_process";
|
|
6
6
|
import { createInterface } from "node:readline/promises";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { codexInstallPackageSpec } from "./codex-version.mjs";
|
|
9
|
+
import {
|
|
10
|
+
buildCodexCompatibilityError,
|
|
11
|
+
probeCodexVersion,
|
|
12
|
+
resolveCodexBinForStart,
|
|
13
|
+
resolveCompatibleInstalledCodexBin,
|
|
14
|
+
} from "./codex-runtime.mjs";
|
|
8
15
|
|
|
9
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
17
|
const __dirname = path.dirname(__filename);
|
|
@@ -22,6 +29,9 @@ const agentEngineLogPath = path.join(logsDir, "agent-engine.log");
|
|
|
22
29
|
const daemonScriptPath = path.join(repoRoot, "scripts", "dist", "daemon.mjs");
|
|
23
30
|
const supervisorScriptPath = path.join(repoRoot, "scripts", "dist", "supervisor.mjs");
|
|
24
31
|
const nodeBin = process.execPath;
|
|
32
|
+
const agentEngineEnvPath = path.join(repoRoot, "apps", "agent-engine", ".env");
|
|
33
|
+
const controlPlaneEnvPath = path.join(repoRoot, "apps", "control-plane", ".env");
|
|
34
|
+
const codexInstallCommand = `npm install -g ${codexInstallPackageSpec}`;
|
|
25
35
|
|
|
26
36
|
const BASE_CHECK_MS = 5000;
|
|
27
37
|
const MAX_BACKOFF_MS = 60000;
|
|
@@ -120,6 +130,26 @@ async function runDaemonLoop() {
|
|
|
120
130
|
const state = { stopping: false, shuttingDown: false };
|
|
121
131
|
setupSignalHandlers(state);
|
|
122
132
|
|
|
133
|
+
try {
|
|
134
|
+
ensureCompatibleCodexForDaemon();
|
|
135
|
+
clearLastStartupError();
|
|
136
|
+
} catch (error) {
|
|
137
|
+
writeLastStartupError({
|
|
138
|
+
detectedAt: new Date().toISOString(),
|
|
139
|
+
reason: getErrorMessage(error),
|
|
140
|
+
action: buildCodexCompatibilityAction(error),
|
|
141
|
+
});
|
|
142
|
+
console.error(`[daemon] fatal startup error: ${getErrorMessage(error)}`);
|
|
143
|
+
console.error(`[daemon] action required: ${buildCodexCompatibilityAction(error)}`);
|
|
144
|
+
state.stopping = true;
|
|
145
|
+
await shutdownDaemon(state, {
|
|
146
|
+
reason: "fatal-codex-compatibility",
|
|
147
|
+
exitCode: 1,
|
|
148
|
+
pauseBeforeExit: true,
|
|
149
|
+
});
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
123
153
|
console.log(`[daemon] running (pid ${process.pid})`);
|
|
124
154
|
|
|
125
155
|
let failureCount = 0;
|
|
@@ -614,6 +644,45 @@ function printLastStartupError() {
|
|
|
614
644
|
}
|
|
615
645
|
}
|
|
616
646
|
|
|
647
|
+
function ensureCompatibleCodexForDaemon(): void {
|
|
648
|
+
const resolved = resolveCodexBinForStart({
|
|
649
|
+
repoRoot,
|
|
650
|
+
agentEngineEnvPath,
|
|
651
|
+
controlPlaneEnvPath,
|
|
652
|
+
});
|
|
653
|
+
const currentProbe = probeCodexVersion({
|
|
654
|
+
codexBin: resolved.bin,
|
|
655
|
+
repoRoot,
|
|
656
|
+
});
|
|
657
|
+
if (currentProbe.ok && currentProbe.compatible) {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
if (!resolved.userConfigured) {
|
|
662
|
+
const compatibleInstalled = resolveCompatibleInstalledCodexBin({ repoRoot });
|
|
663
|
+
if (compatibleInstalled) {
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
throw new Error(
|
|
669
|
+
buildCodexCompatibilityError({
|
|
670
|
+
resolved,
|
|
671
|
+
probe: currentProbe,
|
|
672
|
+
includeInstallHint: !resolved.userConfigured,
|
|
673
|
+
installCommand: codexInstallCommand,
|
|
674
|
+
}),
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
function buildCodexCompatibilityAction(error: unknown): string {
|
|
679
|
+
const message = getErrorMessage(error);
|
|
680
|
+
if (message.includes("Install a compatible version with")) {
|
|
681
|
+
return `Install a compatible version with '${codexInstallCommand}', then restart the service.`;
|
|
682
|
+
}
|
|
683
|
+
return "Update that binary or point CODEX_BIN to a compatible executable, then restart the service.";
|
|
684
|
+
}
|
|
685
|
+
|
|
617
686
|
function printUsage() {
|
|
618
687
|
console.log("Usage: node scripts/dist/daemon.mjs <start|run|stop|status|help>");
|
|
619
688
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { compareSemver, isCodexVersionCompatible } from "../dist/codex-version.mjs";
|
|
4
|
+
|
|
5
|
+
test("isCodexVersionCompatible accepts only validated stable 0.113.x releases", () => {
|
|
6
|
+
assert.equal(isCodexVersionCompatible("0.113.0"), true);
|
|
7
|
+
assert.equal(isCodexVersionCompatible("0.113.9"), true);
|
|
8
|
+
assert.equal(isCodexVersionCompatible("0.112.9"), false);
|
|
9
|
+
assert.equal(isCodexVersionCompatible("0.114.0"), false);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
test("isCodexVersionCompatible rejects prerelease builds outside the validated lane", () => {
|
|
13
|
+
assert.equal(isCodexVersionCompatible("0.113.1-alpha.1"), false);
|
|
14
|
+
assert.equal(isCodexVersionCompatible("0.114.0-alpha.1"), false);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test("compareSemver keeps prerelease ordering stable", () => {
|
|
18
|
+
assert.equal(compareSemver("0.113.0", "0.113.0"), 0);
|
|
19
|
+
assert.equal(compareSemver("0.113.1", "0.113.0"), 1);
|
|
20
|
+
assert.equal(compareSemver("0.113.0-alpha.1", "0.113.0-alpha.2"), -1);
|
|
21
|
+
});
|