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.
- package/LICENSE +21 -0
- package/README.md +215 -0
- package/apps/agent-engine/.env.example +41 -0
- package/apps/agent-engine/LICENSE +21 -0
- package/apps/agent-engine/README.md +57 -0
- package/apps/agent-engine/bot-registry.example.json +28 -0
- package/apps/agent-engine/capabilities/example/index.js +3 -0
- package/apps/agent-engine/capabilities/example/manifest.json +14 -0
- package/apps/agent-engine/dist/agent-worker.js +241 -0
- package/apps/agent-engine/dist/config.js +225 -0
- package/apps/agent-engine/dist/index.js +352 -0
- package/apps/agent-engine/dist/test/project-fingerprint.test.js +40 -0
- package/apps/agent-engine/dist/test/thread-id.test.js +12 -0
- package/apps/agent-engine/package.json +28 -0
- package/apps/control-plane/.env.example +25 -0
- package/apps/control-plane/README.md +35 -0
- package/apps/control-plane/bot-registry.example.json +40 -0
- package/apps/control-plane/capabilities/example/index.js +3 -0
- package/apps/control-plane/capabilities/example/manifest.json +14 -0
- package/apps/control-plane/dist/agent-worker.js +243 -0
- package/apps/control-plane/dist/channels/channel-factory.js +21 -0
- package/apps/control-plane/dist/channels/hub-ops-commands.js +752 -0
- package/apps/control-plane/dist/channels/telegram-channel.js +743 -0
- package/apps/control-plane/dist/channels/whatsapp-channel.js +35 -0
- package/apps/control-plane/dist/config.js +230 -0
- package/apps/control-plane/dist/copilot-hub.js +138 -0
- package/apps/control-plane/dist/index.js +349 -0
- package/apps/control-plane/dist/kernel/admin-contract.js +51 -0
- package/apps/control-plane/dist/test/project-fingerprint.test.js +40 -0
- package/apps/control-plane/dist/test/thread-id.test.js +12 -0
- package/apps/control-plane/package.json +27 -0
- package/package.json +89 -0
- package/packages/contracts/README.md +10 -0
- package/packages/contracts/dist/control-plane.d.ts +24 -0
- package/packages/contracts/dist/control-plane.js +37 -0
- package/packages/contracts/dist/control-plane.js.map +1 -0
- package/packages/contracts/dist/index.d.ts +1 -0
- package/packages/contracts/dist/index.js +2 -0
- package/packages/contracts/dist/index.js.map +1 -0
- package/packages/contracts/package.json +27 -0
- package/packages/core/README.md +33 -0
- package/packages/core/dist/agent-supervisor.d.ts +39 -0
- package/packages/core/dist/agent-supervisor.js +552 -0
- package/packages/core/dist/agent-supervisor.js.map +1 -0
- package/packages/core/dist/bot-manager.d.ts +66 -0
- package/packages/core/dist/bot-manager.js +333 -0
- package/packages/core/dist/bot-manager.js.map +1 -0
- package/packages/core/dist/bot-registry.d.ts +60 -0
- package/packages/core/dist/bot-registry.js +381 -0
- package/packages/core/dist/bot-registry.js.map +1 -0
- package/packages/core/dist/bot-runtime.d.ts +135 -0
- package/packages/core/dist/bot-runtime.js +349 -0
- package/packages/core/dist/bot-runtime.js.map +1 -0
- package/packages/core/dist/bridge-service.d.ts +39 -0
- package/packages/core/dist/bridge-service.js +272 -0
- package/packages/core/dist/bridge-service.js.map +1 -0
- package/packages/core/dist/capability-manager.d.ts +18 -0
- package/packages/core/dist/capability-manager.js +335 -0
- package/packages/core/dist/capability-manager.js.map +1 -0
- package/packages/core/dist/capability-scaffold.d.ts +26 -0
- package/packages/core/dist/capability-scaffold.js +118 -0
- package/packages/core/dist/capability-scaffold.js.map +1 -0
- package/packages/core/dist/channel-factory.d.ts +6 -0
- package/packages/core/dist/channel-factory.js +22 -0
- package/packages/core/dist/channel-factory.js.map +1 -0
- package/packages/core/dist/codex-app-client.d.ts +56 -0
- package/packages/core/dist/codex-app-client.js +762 -0
- package/packages/core/dist/codex-app-client.js.map +1 -0
- package/packages/core/dist/codex-provider.d.ts +31 -0
- package/packages/core/dist/codex-provider.js +64 -0
- package/packages/core/dist/codex-provider.js.map +1 -0
- package/packages/core/dist/control-permission.d.ts +19 -0
- package/packages/core/dist/control-permission.js +106 -0
- package/packages/core/dist/control-permission.js.map +1 -0
- package/packages/core/dist/control-plane-actions.d.ts +1 -0
- package/packages/core/dist/control-plane-actions.js +2 -0
- package/packages/core/dist/control-plane-actions.js.map +1 -0
- package/packages/core/dist/example-capability.d.ts +17 -0
- package/packages/core/dist/example-capability.js +22 -0
- package/packages/core/dist/example-capability.js.map +1 -0
- package/packages/core/dist/extension-contract.d.ts +22 -0
- package/packages/core/dist/extension-contract.js +28 -0
- package/packages/core/dist/extension-contract.js.map +1 -0
- package/packages/core/dist/index.d.ts +26 -0
- package/packages/core/dist/index.js +27 -0
- package/packages/core/dist/index.js.map +1 -0
- package/packages/core/dist/instance-lock.d.ts +9 -0
- package/packages/core/dist/instance-lock.js +74 -0
- package/packages/core/dist/instance-lock.js.map +1 -0
- package/packages/core/dist/kernel-control-plane.d.ts +16 -0
- package/packages/core/dist/kernel-control-plane.js +500 -0
- package/packages/core/dist/kernel-control-plane.js.map +1 -0
- package/packages/core/dist/kernel-version.d.ts +1 -0
- package/packages/core/dist/kernel-version.js +2 -0
- package/packages/core/dist/kernel-version.js.map +1 -0
- package/packages/core/dist/project-fingerprint.d.ts +11 -0
- package/packages/core/dist/project-fingerprint.js +33 -0
- package/packages/core/dist/project-fingerprint.js.map +1 -0
- package/packages/core/dist/provider-factory.d.ts +7 -0
- package/packages/core/dist/provider-factory.js +21 -0
- package/packages/core/dist/provider-factory.js.map +1 -0
- package/packages/core/dist/secret-store.d.ts +18 -0
- package/packages/core/dist/secret-store.js +110 -0
- package/packages/core/dist/secret-store.js.map +1 -0
- package/packages/core/dist/state-store.d.ts +50 -0
- package/packages/core/dist/state-store.js +324 -0
- package/packages/core/dist/state-store.js.map +1 -0
- package/packages/core/dist/telegram-channel.d.ts +27 -0
- package/packages/core/dist/telegram-channel.js +951 -0
- package/packages/core/dist/telegram-channel.js.map +1 -0
- package/packages/core/dist/thread-id.d.ts +1 -0
- package/packages/core/dist/thread-id.js +12 -0
- package/packages/core/dist/thread-id.js.map +1 -0
- package/packages/core/dist/whatsapp-channel.d.ts +26 -0
- package/packages/core/dist/whatsapp-channel.js +36 -0
- package/packages/core/dist/whatsapp-channel.js.map +1 -0
- package/packages/core/dist/workspace-paths.d.ts +5 -0
- package/packages/core/dist/workspace-paths.js +77 -0
- package/packages/core/dist/workspace-paths.js.map +1 -0
- package/packages/core/dist/workspace-policy.d.ts +30 -0
- package/packages/core/dist/workspace-policy.js +104 -0
- package/packages/core/dist/workspace-policy.js.map +1 -0
- package/packages/core/package.json +126 -0
- package/scripts/cli.mjs +537 -0
- package/scripts/configure.mjs +254 -0
- package/scripts/ensure-shared-build.mjs +96 -0
- package/scripts/run-node-tests.mjs +52 -0
- package/scripts/supervisor.mjs +332 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import process, { stdin as input, stdout as output } from "node:process";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { createInterface } from "node:readline/promises";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
const repoRoot = path.resolve(__dirname, "..");
|
|
11
|
+
|
|
12
|
+
const engineEnvPath = path.join(repoRoot, "apps", "agent-engine", ".env");
|
|
13
|
+
const engineExamplePath = path.join(repoRoot, "apps", "agent-engine", ".env.example");
|
|
14
|
+
const controlPlaneEnvPath = path.join(repoRoot, "apps", "control-plane", ".env");
|
|
15
|
+
const controlPlaneExamplePath = path.join(repoRoot, "apps", "control-plane", ".env.example");
|
|
16
|
+
|
|
17
|
+
const args = new Set(process.argv.slice(2));
|
|
18
|
+
const requiredOnly = args.has("--required-only");
|
|
19
|
+
|
|
20
|
+
await main();
|
|
21
|
+
|
|
22
|
+
async function main() {
|
|
23
|
+
ensureEnvFile(engineEnvPath, engineExamplePath);
|
|
24
|
+
ensureEnvFile(controlPlaneEnvPath, controlPlaneExamplePath);
|
|
25
|
+
|
|
26
|
+
const engineLines = readLines(engineEnvPath);
|
|
27
|
+
const controlPlaneLines = readLines(controlPlaneEnvPath);
|
|
28
|
+
|
|
29
|
+
const rl = createInterface({ input, output });
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
if (requiredOnly) {
|
|
33
|
+
await configureRequiredTokens({ rl, controlPlaneLines });
|
|
34
|
+
} else {
|
|
35
|
+
await configureAll({ rl, engineLines, controlPlaneLines });
|
|
36
|
+
console.log("\nSaved:");
|
|
37
|
+
console.log(`- ${relativeFromRepo(engineEnvPath)}`);
|
|
38
|
+
console.log(`- ${relativeFromRepo(controlPlaneEnvPath)}`);
|
|
39
|
+
console.log("\nNext step:");
|
|
40
|
+
console.log("1) npm run start");
|
|
41
|
+
}
|
|
42
|
+
} finally {
|
|
43
|
+
rl.close();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
writeLines(engineEnvPath, engineLines);
|
|
47
|
+
writeLines(controlPlaneEnvPath, controlPlaneLines);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function configureRequiredTokens({ rl, controlPlaneLines }) {
|
|
51
|
+
const controlPlaneMap = parseEnvMap(controlPlaneLines);
|
|
52
|
+
|
|
53
|
+
const controlPlaneTokenEnvName = nonEmpty(
|
|
54
|
+
controlPlaneMap.HUB_TELEGRAM_TOKEN_ENV,
|
|
55
|
+
"HUB_TELEGRAM_TOKEN",
|
|
56
|
+
);
|
|
57
|
+
setEnvValue(controlPlaneLines, "HUB_TELEGRAM_TOKEN_ENV", controlPlaneTokenEnvName);
|
|
58
|
+
|
|
59
|
+
const postControlPlaneMap = parseEnvMap(controlPlaneLines);
|
|
60
|
+
|
|
61
|
+
if (String(postControlPlaneMap[controlPlaneTokenEnvName] ?? "").trim()) {
|
|
62
|
+
console.log("Required tokens already configured.");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!process.stdin.isTTY) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
"Missing required tokens and no interactive terminal. Run 'npm run configure'.",
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log("Missing required token. Please enter value:");
|
|
73
|
+
const value = await askRequired(
|
|
74
|
+
rl,
|
|
75
|
+
`Token value for ${controlPlaneTokenEnvName} (control-plane)`,
|
|
76
|
+
);
|
|
77
|
+
setEnvValue(controlPlaneLines, controlPlaneTokenEnvName, value);
|
|
78
|
+
console.log("Required token saved.");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function configureAll({ rl, engineLines, controlPlaneLines }) {
|
|
82
|
+
const engineMap = parseEnvMap(engineLines);
|
|
83
|
+
const controlPlaneMap = parseEnvMap(controlPlaneLines);
|
|
84
|
+
|
|
85
|
+
console.log("\nCopilot Hub token configuration\n");
|
|
86
|
+
|
|
87
|
+
const controlPlaneTokenEnvDefault = nonEmpty(
|
|
88
|
+
controlPlaneMap.HUB_TELEGRAM_TOKEN_ENV,
|
|
89
|
+
"HUB_TELEGRAM_TOKEN",
|
|
90
|
+
);
|
|
91
|
+
const controlPlaneTokenEnvName = await ask(
|
|
92
|
+
rl,
|
|
93
|
+
"control-plane token variable",
|
|
94
|
+
controlPlaneTokenEnvDefault,
|
|
95
|
+
);
|
|
96
|
+
setEnvValue(controlPlaneLines, "HUB_TELEGRAM_TOKEN_ENV", controlPlaneTokenEnvName);
|
|
97
|
+
const currentControlPlaneToken = parseEnvMap(controlPlaneLines)[controlPlaneTokenEnvName] ?? "";
|
|
98
|
+
const newControlPlaneToken = await ask(
|
|
99
|
+
rl,
|
|
100
|
+
`Token value for ${controlPlaneTokenEnvName} (control-plane, Enter to keep current)`,
|
|
101
|
+
"",
|
|
102
|
+
);
|
|
103
|
+
if (newControlPlaneToken) {
|
|
104
|
+
setEnvValue(controlPlaneLines, controlPlaneTokenEnvName, newControlPlaneToken);
|
|
105
|
+
} else if (!currentControlPlaneToken) {
|
|
106
|
+
console.log(`- No value set for ${controlPlaneTokenEnvName} yet.`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const configureAgentToken = await askYesNo(rl, "Configure TELEGRAM_TOKEN_AGENT_1 now?", true);
|
|
110
|
+
if (configureAgentToken) {
|
|
111
|
+
const currentAgentToken = engineMap.TELEGRAM_TOKEN_AGENT_1 ?? "";
|
|
112
|
+
const newAgentToken = await ask(
|
|
113
|
+
rl,
|
|
114
|
+
"Token value for TELEGRAM_TOKEN_AGENT_1 (agent-engine, Enter to keep current)",
|
|
115
|
+
"",
|
|
116
|
+
);
|
|
117
|
+
if (newAgentToken) {
|
|
118
|
+
setEnvValue(engineLines, "TELEGRAM_TOKEN_AGENT_1", newAgentToken);
|
|
119
|
+
} else if (!currentAgentToken) {
|
|
120
|
+
console.log("- No value set for TELEGRAM_TOKEN_AGENT_1 yet.");
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function ensureEnvFile(envPath, examplePath) {
|
|
126
|
+
fs.mkdirSync(path.dirname(envPath), { recursive: true });
|
|
127
|
+
if (fs.existsSync(envPath)) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (fs.existsSync(examplePath)) {
|
|
132
|
+
fs.copyFileSync(examplePath, envPath);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
fs.writeFileSync(envPath, "", "utf8");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function readLines(filePath) {
|
|
140
|
+
const content = fs.readFileSync(filePath, "utf8");
|
|
141
|
+
return content.split(/\r?\n/);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function writeLines(filePath, lines) {
|
|
145
|
+
const normalized = [...lines];
|
|
146
|
+
if (normalized.length === 0 || normalized[normalized.length - 1] !== "") {
|
|
147
|
+
normalized.push("");
|
|
148
|
+
}
|
|
149
|
+
fs.writeFileSync(filePath, normalized.join("\n"), "utf8");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function parseEnvMap(lines) {
|
|
153
|
+
const map = {};
|
|
154
|
+
for (const line of lines) {
|
|
155
|
+
const match = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)\s*$/);
|
|
156
|
+
if (!match) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const key = match[1];
|
|
161
|
+
const value = unquote(match[2] ?? "");
|
|
162
|
+
map[key] = value;
|
|
163
|
+
}
|
|
164
|
+
return map;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function setEnvValue(lines, key, value) {
|
|
168
|
+
const safeValue = sanitizeValue(value);
|
|
169
|
+
const pattern = new RegExp(`^\\s*${escapeRegex(key)}\\s*=`);
|
|
170
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
171
|
+
if (!pattern.test(lines[index])) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
lines[index] = `${key}=${safeValue}`;
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (lines.length > 0 && lines[lines.length - 1] !== "") {
|
|
180
|
+
lines.push("");
|
|
181
|
+
}
|
|
182
|
+
lines.push(`${key}=${safeValue}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function sanitizeValue(value) {
|
|
186
|
+
return String(value ?? "")
|
|
187
|
+
.replace(/[\r\n]/g, "")
|
|
188
|
+
.trim();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function unquote(value) {
|
|
192
|
+
const raw = String(value ?? "").trim();
|
|
193
|
+
if (!raw) {
|
|
194
|
+
return "";
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if ((raw.startsWith('"') && raw.endsWith('"')) || (raw.startsWith("'") && raw.endsWith("'"))) {
|
|
198
|
+
return raw.slice(1, -1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return raw;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function nonEmpty(value, fallback) {
|
|
205
|
+
const normalized = String(value ?? "").trim();
|
|
206
|
+
return normalized || fallback;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function escapeRegex(value) {
|
|
210
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function ask(rl, label, fallback) {
|
|
214
|
+
const value = await rl.question(`${label}${fallback ? ` [${fallback}]` : ""}: `);
|
|
215
|
+
const normalized = String(value ?? "").trim();
|
|
216
|
+
if (!normalized) {
|
|
217
|
+
return String(fallback ?? "").trim();
|
|
218
|
+
}
|
|
219
|
+
return normalized;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function askRequired(rl, label) {
|
|
223
|
+
while (true) {
|
|
224
|
+
const value = await rl.question(`${label}: `);
|
|
225
|
+
const normalized = String(value ?? "").trim();
|
|
226
|
+
if (normalized) {
|
|
227
|
+
return normalized;
|
|
228
|
+
}
|
|
229
|
+
console.log("Value is required.");
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async function askYesNo(rl, label, defaultYes) {
|
|
234
|
+
const suffix = defaultYes ? "[Y/n]" : "[y/N]";
|
|
235
|
+
const answer = await rl.question(`${label} ${suffix}: `);
|
|
236
|
+
const value = String(answer ?? "")
|
|
237
|
+
.trim()
|
|
238
|
+
.toLowerCase();
|
|
239
|
+
if (!value) {
|
|
240
|
+
return defaultYes;
|
|
241
|
+
}
|
|
242
|
+
if (value === "y" || value === "yes") {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
if (value === "n" || value === "no") {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
return defaultYes;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function relativeFromRepo(filePath) {
|
|
252
|
+
const relative = path.relative(repoRoot, filePath);
|
|
253
|
+
return relative || filePath;
|
|
254
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
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 { spawnSync } 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 requiredOutputs = [
|
|
13
|
+
path.join(repoRoot, "packages", "core", "dist", "index.js"),
|
|
14
|
+
path.join(repoRoot, "packages", "core", "dist", "workspace-policy.js"),
|
|
15
|
+
path.join(repoRoot, "packages", "contracts", "dist", "index.js"),
|
|
16
|
+
path.join(repoRoot, "packages", "contracts", "dist", "control-plane.js"),
|
|
17
|
+
path.join(repoRoot, "apps", "agent-engine", "dist", "index.js"),
|
|
18
|
+
path.join(repoRoot, "apps", "control-plane", "dist", "copilot-hub.js"),
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
await main();
|
|
22
|
+
|
|
23
|
+
async function main() {
|
|
24
|
+
if (requiredOutputs.every((entry) => fs.existsSync(entry))) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log("Shared package build artifacts are missing. Running 'npm run build'...");
|
|
29
|
+
const result = spawnNpm(["run", "build"], {
|
|
30
|
+
cwd: repoRoot,
|
|
31
|
+
stdio: "inherit",
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const code = Number.isInteger(result.status) ? result.status : 1;
|
|
35
|
+
if (code !== 0) {
|
|
36
|
+
if (result.error) {
|
|
37
|
+
throw new Error(formatNpmSpawnError(result.error));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
throw new Error(
|
|
41
|
+
[
|
|
42
|
+
"Failed to build shared packages.",
|
|
43
|
+
"Install project dependencies with 'npm install', then retry.",
|
|
44
|
+
].join("\n"),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const missingAfterBuild = requiredOutputs.filter((entry) => !fs.existsSync(entry));
|
|
49
|
+
if (missingAfterBuild.length > 0) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
[
|
|
52
|
+
"Shared package build completed but required artifacts are still missing:",
|
|
53
|
+
...missingAfterBuild.map((entry) => `- ${path.relative(repoRoot, entry)}`),
|
|
54
|
+
].join("\n"),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function formatNpmSpawnError(error) {
|
|
60
|
+
const code = String(error?.code ?? "")
|
|
61
|
+
.trim()
|
|
62
|
+
.toUpperCase();
|
|
63
|
+
if (code === "ENOENT") {
|
|
64
|
+
return "npm was not found. Install Node.js/npm, then retry.";
|
|
65
|
+
}
|
|
66
|
+
if (code === "EPERM") {
|
|
67
|
+
return "npm cannot be executed (EPERM). Check permissions.";
|
|
68
|
+
}
|
|
69
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
70
|
+
return `Failed to execute 'npm run build': ${firstLine(message)}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function firstLine(value) {
|
|
74
|
+
return (
|
|
75
|
+
String(value ?? "")
|
|
76
|
+
.split(/\r?\n/)
|
|
77
|
+
.map((line) => line.trim())
|
|
78
|
+
.find(Boolean) ?? ""
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function spawnNpm(args, options) {
|
|
83
|
+
if (process.platform === "win32") {
|
|
84
|
+
const comspec = process.env.ComSpec || "cmd.exe";
|
|
85
|
+
const commandLine = ["npm", ...args].join(" ");
|
|
86
|
+
return spawnSync(comspec, ["/d", "/s", "/c", commandLine], {
|
|
87
|
+
...options,
|
|
88
|
+
shell: false,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return spawnSync("npm", args, {
|
|
93
|
+
...options,
|
|
94
|
+
shell: false,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
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 { spawnSync } from "node:child_process";
|
|
6
|
+
|
|
7
|
+
const targetDirArg = String(process.argv[2] ?? "dist/test").trim() || "dist/test";
|
|
8
|
+
const targetDir = path.resolve(process.cwd(), targetDirArg);
|
|
9
|
+
|
|
10
|
+
if (!fs.existsSync(targetDir)) {
|
|
11
|
+
console.error(`Test directory not found: ${targetDir}`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const testFiles = listTestFiles(targetDir);
|
|
16
|
+
if (testFiles.length === 0) {
|
|
17
|
+
console.error(`No test files found under: ${targetDir}`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const result = spawnSync(process.execPath, ["--test", ...testFiles], {
|
|
22
|
+
stdio: "inherit",
|
|
23
|
+
shell: false,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const code = Number.isInteger(result.status) ? result.status : 1;
|
|
27
|
+
process.exit(code);
|
|
28
|
+
|
|
29
|
+
function listTestFiles(rootDir) {
|
|
30
|
+
const out = [];
|
|
31
|
+
walk(rootDir, out);
|
|
32
|
+
out.sort((a, b) => a.localeCompare(b));
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function walk(dirPath, out) {
|
|
37
|
+
const entries = fs.readdirSync(dirPath, { withFileTypes: true });
|
|
38
|
+
for (const entry of entries) {
|
|
39
|
+
const absolutePath = path.join(dirPath, entry.name);
|
|
40
|
+
if (entry.isDirectory()) {
|
|
41
|
+
walk(absolutePath, out);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (!entry.isFile()) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (!entry.name.endsWith(".test.js")) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
out.push(absolutePath);
|
|
51
|
+
}
|
|
52
|
+
}
|