@camstack/kernel 0.1.6 → 0.1.9

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.
@@ -5,8 +5,12 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __commonJS = (cb, mod) => function __require() {
9
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
8
+ var __esm = (fn, res) => function __init() {
9
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
10
14
  };
11
15
  var __copyProps = (to, from, except, desc) => {
12
16
  if (from && typeof from === "object" || typeof from === "function") {
@@ -25,395 +29,111 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
29
  mod
26
30
  ));
27
31
 
28
- // src/worker/worker-process-manager.js
29
- var require_worker_process_manager = __commonJS({
30
- "src/worker/worker-process-manager.js"(exports2) {
31
- "use strict";
32
- Object.defineProperty(exports2, "__esModule", { value: true });
33
- exports2.WorkerProcessManager = void 0;
34
- var node_child_process_1 = require("child_process");
35
- var WorkerProcessManager2 = class {
36
- sendToMain;
37
- processes = /* @__PURE__ */ new Map();
38
- constructor(sendToMain) {
39
- this.sendToMain = sendToMain;
40
- }
41
- async spawn(config) {
42
- const child = (0, node_child_process_1.spawn)(config.command, [...config.args ?? []], {
43
- cwd: config.cwd,
44
- env: config.env ? { ...process.env, ...config.env } : void 0,
45
- stdio: ["pipe", "pipe", "pipe"]
46
- });
47
- const managed = new ManagedProcess(child, config, this.sendToMain);
48
- this.processes.set(child.pid, managed);
49
- this.sendToMain({
50
- type: "SUB_PROCESS_SPAWNED",
51
- pid: child.pid,
52
- name: config.name,
53
- command: config.command
54
- });
55
- child.on("exit", (code) => {
56
- this.sendToMain({
57
- type: "SUB_PROCESS_EXITED",
58
- pid: child.pid,
59
- code
60
- });
61
- if (config.autoRestart && managed.restartCount < (config.maxRestarts ?? 3)) {
62
- managed.restartCount++;
63
- setTimeout(() => {
64
- this.spawn(config).catch(() => {
65
- });
66
- }, 2e3);
67
- }
68
- });
69
- return managed;
70
- }
71
- listProcesses() {
72
- return [...this.processes.values()].map((p) => p.toInfo());
73
- }
74
- getWorkerStats() {
75
- const mem = process.memoryUsage();
76
- const cpu = process.cpuUsage();
77
- return {
78
- pid: process.pid,
79
- cpuPercent: (cpu.user + cpu.system) / 1e6,
80
- memoryRss: mem.rss,
81
- heapUsed: mem.heapUsed,
82
- uptimeSeconds: Math.round(process.uptime()),
83
- restartCount: 0,
84
- state: "running"
85
- };
86
- }
87
- async killAll() {
88
- for (const [, proc] of this.processes) {
89
- proc.kill("SIGTERM");
90
- }
91
- this.processes.clear();
92
- }
93
- };
94
- exports2.WorkerProcessManager = WorkerProcessManager2;
95
- var ManagedProcess = class {
96
- child;
97
- config;
98
- sendToMain;
99
- pid;
100
- name;
101
- restartCount = 0;
102
- startedAt = Date.now();
103
- exitHandlers = [];
104
- errorHandlers = [];
105
- constructor(child, config, sendToMain) {
106
- this.child = child;
107
- this.config = config;
108
- this.sendToMain = sendToMain;
109
- this.pid = child.pid;
110
- this.name = config.name;
111
- child.on("exit", (code) => {
112
- for (const handler of this.exitHandlers)
113
- handler(code);
114
- });
115
- child.on("error", (err) => {
116
- for (const handler of this.errorHandlers)
117
- handler(err);
118
- });
119
- }
120
- getStats() {
121
- return {
122
- pid: this.pid,
123
- cpuPercent: 0,
124
- memoryRss: 0,
125
- uptimeSeconds: Math.round((Date.now() - this.startedAt) / 1e3),
126
- restartCount: this.restartCount,
127
- state: this.child.exitCode !== null ? "stopped" : "running"
128
- };
129
- }
130
- write(data) {
131
- this.child.stdin?.write(data);
132
- }
133
- get stdout() {
134
- return this.child.stdout;
135
- }
136
- get stderr() {
137
- return this.child.stderr;
138
- }
139
- kill(signal = "SIGTERM") {
140
- this.child.kill(signal);
141
- }
142
- wait() {
143
- if (this.child.exitCode !== null) {
144
- return Promise.resolve({ code: this.child.exitCode, signal: null });
145
- }
146
- return new Promise((resolve2) => {
147
- this.child.on("exit", (code, signal) => {
148
- resolve2({ code, signal });
149
- });
150
- });
151
- }
152
- onExit(handler) {
153
- this.exitHandlers.push(handler);
154
- }
155
- onError(handler) {
156
- this.errorHandlers.push(handler);
157
- }
158
- toInfo() {
159
- return {
160
- pid: this.pid,
161
- name: this.name,
162
- command: this.config.command,
163
- state: this.child.exitCode !== null ? "stopped" : "running",
164
- cpuPercent: 0,
165
- memoryRss: 0,
166
- uptimeSeconds: Math.round((Date.now() - this.startedAt) / 1e3)
167
- };
168
- }
169
- };
170
- }
171
- });
172
-
173
- // src/fs-utils.js
174
- var require_fs_utils = __commonJS({
175
- "src/fs-utils.js"(exports2) {
176
- "use strict";
177
- var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
178
- if (k2 === void 0) k2 = k;
179
- var desc = Object.getOwnPropertyDescriptor(m, k);
180
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
181
- desc = { enumerable: true, get: function() {
182
- return m[k];
183
- } };
184
- }
185
- Object.defineProperty(o, k2, desc);
186
- }) : (function(o, m, k, k2) {
187
- if (k2 === void 0) k2 = k;
188
- o[k2] = m[k];
189
- }));
190
- var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
191
- Object.defineProperty(o, "default", { enumerable: true, value: v });
192
- }) : function(o, v) {
193
- o["default"] = v;
194
- });
195
- var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
196
- var ownKeys = function(o) {
197
- ownKeys = Object.getOwnPropertyNames || function(o2) {
198
- var ar = [];
199
- for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
200
- return ar;
201
- };
202
- return ownKeys(o);
203
- };
204
- return function(mod) {
205
- if (mod && mod.__esModule) return mod;
206
- var result = {};
207
- if (mod != null) {
208
- for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
209
- }
210
- __setModuleDefault(result, mod);
211
- return result;
212
- };
213
- })();
214
- Object.defineProperty(exports2, "__esModule", { value: true });
215
- exports2.ensureDir = ensureDir;
216
- exports2.copyDirRecursive = copyDirRecursive;
217
- exports2.stripCamstackDeps = stripCamstackDeps;
218
- exports2.copyExtraFileDirs = copyExtraFileDirs;
219
- exports2.symlinkAddonsToNodeModules = symlinkAddonsToNodeModules;
220
- exports2.isSourceNewer = isSourceNewer;
221
- exports2.ensureLibraryBuilt = ensureLibraryBuilt;
222
- exports2.installPackageFromNpmSync = installPackageFromNpmSync;
223
- var node_child_process_1 = require("child_process");
224
- var fs2 = __importStar(require("fs"));
225
- var path2 = __importStar(require("path"));
226
- function ensureDir(dirPath) {
227
- fs2.mkdirSync(dirPath, { recursive: true });
228
- }
229
- function copyDirRecursive(src, dest) {
230
- ensureDir(dest);
231
- const entries = fs2.readdirSync(src, { withFileTypes: true });
232
- for (const entry of entries) {
233
- const srcPath = path2.join(src, entry.name);
234
- const destPath = path2.join(dest, entry.name);
235
- if (entry.isDirectory()) {
236
- copyDirRecursive(srcPath, destPath);
237
- } else {
238
- fs2.copyFileSync(srcPath, destPath);
239
- }
240
- }
241
- }
242
- function stripCamstackDeps(pkg) {
243
- const result = { ...pkg };
244
- for (const depType of ["dependencies", "peerDependencies", "devDependencies"]) {
245
- const deps = result[depType];
246
- if (deps) {
247
- const filtered = {};
248
- for (const [name, version] of Object.entries(deps)) {
249
- if (!name.startsWith("@camstack/")) {
250
- filtered[name] = version;
251
- }
252
- }
253
- result[depType] = Object.keys(filtered).length > 0 ? filtered : void 0;
254
- }
255
- }
256
- delete result.devDependencies;
257
- return result;
258
- }
259
- function copyExtraFileDirs(pkgJson, sourceDir, destDir) {
260
- const files = pkgJson.files;
261
- if (!files)
262
- return;
263
- for (const fileEntry of files) {
264
- if (fileEntry === "dist" || fileEntry.includes("*"))
265
- continue;
266
- const srcPath = path2.join(sourceDir, fileEntry);
267
- if (!fs2.existsSync(srcPath))
268
- continue;
269
- const destPath = path2.join(destDir, fileEntry);
270
- const stat = fs2.statSync(srcPath);
271
- if (stat.isDirectory()) {
272
- copyDirRecursive(srcPath, destPath);
273
- } else if (stat.isFile()) {
274
- ensureDir(path2.dirname(destPath));
275
- fs2.copyFileSync(srcPath, destPath);
276
- }
277
- }
278
- }
279
- function symlinkAddonsToNodeModules(addonsDir, nodeModulesDir) {
280
- const camstackDir = path2.join(nodeModulesDir, "@camstack");
281
- ensureDir(camstackDir);
282
- const packagesToLink = ["core"];
283
- for (const pkg of packagesToLink) {
284
- const addonDir = path2.join(addonsDir, "@camstack", pkg);
285
- const linkPath = path2.join(camstackDir, pkg);
286
- if (!fs2.existsSync(addonDir))
287
- continue;
288
- try {
289
- const stat = fs2.lstatSync(linkPath);
290
- if (stat.isSymbolicLink() || stat.isDirectory()) {
291
- fs2.rmSync(linkPath, { recursive: true, force: true });
292
- }
293
- } catch {
294
- }
295
- fs2.symlinkSync(addonDir, linkPath, "dir");
296
- console.log(`[symlink] node_modules/@camstack/${pkg} -> ${addonDir}`);
297
- }
32
+ // src/fs-utils.ts
33
+ function ensureDir(dirPath) {
34
+ fs.mkdirSync(dirPath, { recursive: true });
35
+ }
36
+ function copyDirRecursive(src, dest) {
37
+ ensureDir(dest);
38
+ const entries = fs.readdirSync(src, { withFileTypes: true });
39
+ for (const entry of entries) {
40
+ const srcPath = path.join(src, entry.name);
41
+ const destPath = path.join(dest, entry.name);
42
+ if (entry.isDirectory()) {
43
+ copyDirRecursive(srcPath, destPath);
44
+ } else {
45
+ fs.copyFileSync(srcPath, destPath);
298
46
  }
299
- function isSourceNewer(packageDir) {
300
- const srcDir = path2.join(packageDir, "src");
301
- const distDir = path2.join(packageDir, "dist");
302
- if (!fs2.existsSync(srcDir) || !fs2.existsSync(distDir))
303
- return true;
304
- try {
305
- const distMtime = fs2.statSync(distDir).mtimeMs;
306
- const entries = fs2.readdirSync(srcDir, { withFileTypes: true, recursive: true });
307
- for (const entry of entries) {
308
- if (!entry.isFile())
309
- continue;
310
- const filePath = path2.join(entry.parentPath ?? entry.path, entry.name);
311
- if (fs2.statSync(filePath).mtimeMs > distMtime)
312
- return true;
47
+ }
48
+ }
49
+ function stripCamstackDeps(pkg) {
50
+ const result = { ...pkg };
51
+ for (const depType of ["dependencies", "peerDependencies", "devDependencies"]) {
52
+ const deps = result[depType];
53
+ if (deps) {
54
+ const filtered = {};
55
+ for (const [name, version] of Object.entries(deps)) {
56
+ if (!name.startsWith("@camstack/")) {
57
+ filtered[name] = version;
313
58
  }
314
- return false;
315
- } catch {
316
- return true;
317
59
  }
60
+ result[depType] = Object.keys(filtered).length > 0 ? filtered : void 0;
318
61
  }
319
- function ensureLibraryBuilt(packageName, packagesDir) {
320
- const dirName = packageName.replace("@camstack/", "");
321
- const sourceDir = path2.join(packagesDir, dirName);
322
- if (!fs2.existsSync(sourceDir))
323
- return;
324
- const distDir = path2.join(sourceDir, "dist");
325
- const hasIndex = fs2.existsSync(path2.join(distDir, "index.js")) || fs2.existsSync(path2.join(distDir, "index.mjs"));
326
- if (hasIndex)
327
- return;
328
- console.warn(`[ensureLibraryBuilt] ${packageName} has no dist/ \u2014 run 'npm run build' first`);
62
+ }
63
+ delete result.devDependencies;
64
+ return result;
65
+ }
66
+ function copyExtraFileDirs(pkgJson, sourceDir, destDir) {
67
+ const files = pkgJson.files;
68
+ if (!files) return;
69
+ for (const fileEntry of files) {
70
+ if (fileEntry === "dist" || fileEntry.includes("*")) continue;
71
+ const srcPath = path.join(sourceDir, fileEntry);
72
+ if (!fs.existsSync(srcPath)) continue;
73
+ const destPath = path.join(destDir, fileEntry);
74
+ const stat = fs.statSync(srcPath);
75
+ if (stat.isDirectory()) {
76
+ copyDirRecursive(srcPath, destPath);
77
+ } else if (stat.isFile()) {
78
+ ensureDir(path.dirname(destPath));
79
+ fs.copyFileSync(srcPath, destPath);
329
80
  }
330
- function installPackageFromNpmSync(packageName, targetDir) {
331
- const os2 = require("os");
332
- const tmpDir = fs2.mkdtempSync(path2.join(os2.tmpdir(), "camstack-install-"));
333
- try {
334
- const stdout = (0, node_child_process_1.execFileSync)("npm", ["pack", packageName, "--pack-destination", tmpDir], {
335
- timeout: 12e4,
336
- encoding: "utf-8"
337
- });
338
- const tgzFilename = stdout.trim().split("\n").pop()?.trim();
339
- if (!tgzFilename)
340
- throw new Error("npm pack produced no output");
341
- const tgzPath = path2.join(tmpDir, tgzFilename);
342
- const extractDir = path2.join(tmpDir, "extracted");
343
- ensureDir(extractDir);
344
- (0, node_child_process_1.execFileSync)("tar", ["-xzf", tgzPath, "-C", extractDir], { timeout: 3e4 });
345
- const packageSubDir = path2.join(extractDir, "package");
346
- const srcPkgJsonDir = fs2.existsSync(path2.join(packageSubDir, "package.json")) ? packageSubDir : extractDir;
347
- fs2.rmSync(targetDir, { recursive: true, force: true });
348
- ensureDir(targetDir);
349
- fs2.copyFileSync(path2.join(srcPkgJsonDir, "package.json"), path2.join(targetDir, "package.json"));
350
- const distSrc = path2.join(srcPkgJsonDir, "dist");
351
- if (fs2.existsSync(distSrc)) {
352
- copyDirRecursive(distSrc, path2.join(targetDir, "dist"));
353
- }
354
- try {
355
- const npmPkg = JSON.parse(fs2.readFileSync(path2.join(srcPkgJsonDir, "package.json"), "utf-8"));
356
- copyExtraFileDirs(npmPkg, srcPkgJsonDir, targetDir);
357
- } catch {
358
- }
359
- } finally {
360
- fs2.rmSync(tmpDir, { recursive: true, force: true });
361
- }
81
+ }
82
+ }
83
+ function isSourceNewer(packageDir) {
84
+ const srcDir = path.join(packageDir, "src");
85
+ const distDir = path.join(packageDir, "dist");
86
+ if (!fs.existsSync(srcDir) || !fs.existsSync(distDir)) return true;
87
+ try {
88
+ const distMtime = fs.statSync(distDir).mtimeMs;
89
+ const entries = fs.readdirSync(srcDir, { withFileTypes: true, recursive: true });
90
+ for (const entry of entries) {
91
+ if (!entry.isFile()) continue;
92
+ const filePath = path.join(entry.parentPath ?? entry.path, entry.name);
93
+ if (fs.statSync(filePath).mtimeMs > distMtime) return true;
362
94
  }
95
+ return false;
96
+ } catch {
97
+ return true;
98
+ }
99
+ }
100
+ function ensureLibraryBuilt(packageName, packagesDir) {
101
+ const dirName = packageName.replace("@camstack/", "");
102
+ const sourceDir = path.join(packagesDir, dirName);
103
+ if (!fs.existsSync(sourceDir)) return;
104
+ const distDir = path.join(sourceDir, "dist");
105
+ const hasIndex = fs.existsSync(path.join(distDir, "index.js")) || fs.existsSync(path.join(distDir, "index.mjs"));
106
+ if (hasIndex) return;
107
+ console.warn(`[ensureLibraryBuilt] ${packageName} has no dist/ \u2014 run 'npm run build' first`);
108
+ }
109
+ var import_node_child_process2, fs, os, path;
110
+ var init_fs_utils = __esm({
111
+ "src/fs-utils.ts"() {
112
+ "use strict";
113
+ import_node_child_process2 = require("child_process");
114
+ fs = __toESM(require("fs"));
115
+ os = __toESM(require("os"));
116
+ path = __toESM(require("path"));
363
117
  }
364
118
  });
365
119
 
366
- // src/addon-installer.js
367
- var require_addon_installer = __commonJS({
368
- "src/addon-installer.js"(exports2) {
120
+ // src/addon-installer.ts
121
+ var addon_installer_exports = {};
122
+ __export(addon_installer_exports, {
123
+ AddonInstaller: () => AddonInstaller
124
+ });
125
+ var import_node_child_process3, import_node_util, fs2, path2, os2, execFileAsync, AddonInstaller;
126
+ var init_addon_installer = __esm({
127
+ "src/addon-installer.ts"() {
369
128
  "use strict";
370
- var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
371
- if (k2 === void 0) k2 = k;
372
- var desc = Object.getOwnPropertyDescriptor(m, k);
373
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
374
- desc = { enumerable: true, get: function() {
375
- return m[k];
376
- } };
377
- }
378
- Object.defineProperty(o, k2, desc);
379
- }) : (function(o, m, k, k2) {
380
- if (k2 === void 0) k2 = k;
381
- o[k2] = m[k];
382
- }));
383
- var __setModuleDefault = exports2 && exports2.__setModuleDefault || (Object.create ? (function(o, v) {
384
- Object.defineProperty(o, "default", { enumerable: true, value: v });
385
- }) : function(o, v) {
386
- o["default"] = v;
387
- });
388
- var __importStar = exports2 && exports2.__importStar || /* @__PURE__ */ (function() {
389
- var ownKeys = function(o) {
390
- ownKeys = Object.getOwnPropertyNames || function(o2) {
391
- var ar = [];
392
- for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
393
- return ar;
394
- };
395
- return ownKeys(o);
396
- };
397
- return function(mod) {
398
- if (mod && mod.__esModule) return mod;
399
- var result = {};
400
- if (mod != null) {
401
- for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
402
- }
403
- __setModuleDefault(result, mod);
404
- return result;
405
- };
406
- })();
407
- Object.defineProperty(exports2, "__esModule", { value: true });
408
- exports2.AddonInstaller = void 0;
409
- var node_child_process_1 = require("child_process");
410
- var node_util_1 = require("util");
411
- var fs2 = __importStar(require("fs"));
412
- var path2 = __importStar(require("path"));
413
- var os2 = __importStar(require("os"));
414
- var fs_utils_js_1 = require_fs_utils();
415
- var execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
416
- var AddonInstaller = class _AddonInstaller {
129
+ import_node_child_process3 = require("child_process");
130
+ import_node_util = require("util");
131
+ fs2 = __toESM(require("fs"));
132
+ path2 = __toESM(require("path"));
133
+ os2 = __toESM(require("os"));
134
+ init_fs_utils();
135
+ execFileAsync = (0, import_node_util.promisify)(import_node_child_process3.execFile);
136
+ AddonInstaller = class _AddonInstaller {
417
137
  addonsDir;
418
138
  registry;
419
139
  workspacePackagesDir;
@@ -425,28 +145,29 @@ var require_addon_installer = __commonJS({
425
145
  /** Required addon packages that must be installed for the server to function */
426
146
  static REQUIRED_PACKAGES = [
427
147
  "@camstack/core",
428
- "@camstack/addon-pipeline",
148
+ "@camstack/addon-stream-broker",
149
+ "@camstack/addon-recording",
429
150
  "@camstack/addon-vision",
430
151
  "@camstack/addon-admin-ui",
431
152
  "@camstack/addon-webrtc-adaptive",
432
- "@camstack/addon-pipeline-analysis",
153
+ "@camstack/addon-analytics",
433
154
  "@camstack/addon-scene-intelligence",
434
155
  "@camstack/addon-advanced-notifier"
435
156
  ];
436
157
  /** Ensure the addons directory exists */
437
158
  async initialize() {
438
- (0, fs_utils_js_1.ensureDir)(this.addonsDir);
159
+ ensureDir(this.addonsDir);
439
160
  }
440
161
  /**
441
162
  * Ensure all required packages are installed in the addons directory.
442
163
  * This replaces the standalone first-boot-installer.ts.
443
164
  */
444
165
  async ensureRequiredPackages() {
445
- (0, fs_utils_js_1.ensureDir)(this.addonsDir);
166
+ ensureDir(this.addonsDir);
446
167
  if (this.workspacePackagesDir) {
447
168
  console.log(`[AddonInstaller] Workspace detected: ${this.workspacePackagesDir}`);
448
- (0, fs_utils_js_1.ensureLibraryBuilt)("@camstack/kernel", this.workspacePackagesDir);
449
- (0, fs_utils_js_1.ensureLibraryBuilt)("@camstack/types", this.workspacePackagesDir);
169
+ ensureLibraryBuilt("@camstack/kernel", this.workspacePackagesDir);
170
+ ensureLibraryBuilt("@camstack/types", this.workspacePackagesDir);
450
171
  }
451
172
  for (const packageName of _AddonInstaller.REQUIRED_PACKAGES) {
452
173
  const addonDir = path2.join(this.addonsDir, packageName);
@@ -455,10 +176,12 @@ var require_addon_installer = __commonJS({
455
176
  const useWorkspace = this.workspacePackagesDir && !forceNpm;
456
177
  if (fs2.existsSync(pkgJsonPath)) {
457
178
  if (!useWorkspace) {
179
+ console.log(`[AddonInstaller] ${packageName} \u2014 already installed (npm), skipping`);
458
180
  continue;
459
181
  }
460
182
  const srcPkgDir = this.findWorkspacePackage(packageName);
461
- if (srcPkgDir && !(0, fs_utils_js_1.isSourceNewer)(srcPkgDir)) {
183
+ if (srcPkgDir && !isSourceNewer(srcPkgDir)) {
184
+ console.log(`[AddonInstaller] ${packageName} \u2014 workspace up-to-date, skipping`);
462
185
  continue;
463
186
  }
464
187
  }
@@ -466,10 +189,12 @@ var require_addon_installer = __commonJS({
466
189
  if (useWorkspace) {
467
190
  const pkgDir = this.findWorkspacePackage(packageName);
468
191
  if (pkgDir) {
192
+ console.log(`[AddonInstaller] ${packageName} \u2014 installing from workspace`);
469
193
  await this.installFromWorkspace(packageName);
470
194
  continue;
471
195
  }
472
196
  }
197
+ console.log(`[AddonInstaller] ${packageName} \u2014 installing from npm`);
473
198
  await this.installFromNpm(packageName);
474
199
  } catch (err) {
475
200
  const msg = err instanceof Error ? err.message : String(err);
@@ -481,16 +206,14 @@ var require_addon_installer = __commonJS({
481
206
  }
482
207
  }
483
208
  findWorkspacePackage(packageName) {
484
- if (!this.workspacePackagesDir)
485
- return null;
209
+ if (!this.workspacePackagesDir) return null;
486
210
  const shortName = packageName.replace("@camstack/", "");
487
211
  for (const dirName of [shortName, `addon-${shortName}`, shortName.replace("addon-", "")]) {
488
212
  const candidate = path2.join(this.workspacePackagesDir, dirName);
489
213
  if (fs2.existsSync(path2.join(candidate, "package.json"))) {
490
214
  try {
491
215
  const pkg = JSON.parse(fs2.readFileSync(path2.join(candidate, "package.json"), "utf-8"));
492
- if (pkg.name === packageName)
493
- return candidate;
216
+ if (pkg.name === packageName) return candidate;
494
217
  } catch {
495
218
  }
496
219
  }
@@ -513,14 +236,14 @@ var require_addon_installer = __commonJS({
513
236
  }
514
237
  const targetDir = path2.join(this.addonsDir, pkgJson.name);
515
238
  fs2.rmSync(targetDir, { recursive: true, force: true });
516
- (0, fs_utils_js_1.ensureDir)(targetDir);
239
+ ensureDir(targetDir);
517
240
  const sourceDir = path2.dirname(pkgJsonPath);
518
241
  fs2.copyFileSync(pkgJsonPath, path2.join(targetDir, "package.json"));
519
242
  const sourceDist = path2.join(sourceDir, "dist");
520
243
  if (fs2.existsSync(sourceDist)) {
521
- (0, fs_utils_js_1.copyDirRecursive)(sourceDist, path2.join(targetDir, "dist"));
244
+ copyDirRecursive(sourceDist, path2.join(targetDir, "dist"));
522
245
  }
523
- (0, fs_utils_js_1.copyExtraFileDirs)(pkgJson, sourceDir, targetDir);
246
+ copyExtraFileDirs(pkgJson, sourceDir, targetDir);
524
247
  return { name: pkgJson.name, version: pkgJson.version };
525
248
  } finally {
526
249
  fs2.rmSync(tmpDir, { recursive: true, force: true });
@@ -560,12 +283,13 @@ var require_addon_installer = __commonJS({
560
283
  }
561
284
  const targetDir = path2.join(this.addonsDir, packageName);
562
285
  fs2.rmSync(targetDir, { recursive: true, force: true });
563
- (0, fs_utils_js_1.ensureDir)(targetDir);
286
+ ensureDir(targetDir);
564
287
  const pkgData = JSON.parse(fs2.readFileSync(sourcePkgJson, "utf-8"));
565
- const strippedPkg = (0, fs_utils_js_1.stripCamstackDeps)(pkgData);
288
+ const strippedPkg = stripCamstackDeps(pkgData);
566
289
  fs2.writeFileSync(path2.join(targetDir, "package.json"), JSON.stringify(strippedPkg, null, 2));
567
- (0, fs_utils_js_1.copyDirRecursive)(distDir, path2.join(targetDir, "dist"));
568
- (0, fs_utils_js_1.copyExtraFileDirs)(pkgData, sourceDir, targetDir);
290
+ copyDirRecursive(distDir, path2.join(targetDir, "dist"));
291
+ copyExtraFileDirs(pkgData, sourceDir, targetDir);
292
+ fs2.writeFileSync(path2.join(targetDir, ".install-source"), "workspace");
569
293
  try {
570
294
  await execFileAsync("npm", ["install", "--omit=dev", "--ignore-scripts=false"], {
571
295
  cwd: targetDir,
@@ -583,16 +307,21 @@ var require_addon_installer = __commonJS({
583
307
  if (this.registry) {
584
308
  args.push("--registry", this.registry);
585
309
  }
310
+ console.log(`[AddonInstaller] npm pack ${packageSpec} \u2192 ${tmpDir}`);
586
311
  try {
587
312
  const { stdout } = await execFileAsync("npm", args, {
588
313
  timeout: 12e4
589
314
  });
590
315
  const tgzFiles = fs2.readdirSync(tmpDir).filter((f) => f.endsWith(".tgz"));
316
+ console.log(`[AddonInstaller] npm pack stdout: ${stdout.trim()}, tgzFiles: ${tgzFiles.join(", ")}`);
591
317
  if (tgzFiles.length === 0) {
592
318
  throw new Error(`npm pack produced no tgz file for ${packageSpec}. stdout: ${stdout.trim()}`);
593
319
  }
594
320
  const tgzPath = path2.join(tmpDir, tgzFiles[0]);
595
321
  const result = await this.installFromTgz(tgzPath);
322
+ console.log(`[AddonInstaller] installFromTgz result: ${result.name}@${result.version} \u2192 ${path2.join(this.addonsDir, result.name)}`);
323
+ const targetDir = path2.join(this.addonsDir, result.name);
324
+ fs2.writeFileSync(path2.join(targetDir, ".install-source"), "npm");
596
325
  return result;
597
326
  } finally {
598
327
  fs2.rmSync(tmpDir, { recursive: true, force: true });
@@ -612,17 +341,29 @@ var require_addon_installer = __commonJS({
612
341
  }
613
342
  /** List installed addons (directories with package.json containing camstack.addons) */
614
343
  listInstalled() {
615
- if (!fs2.existsSync(this.addonsDir))
616
- return [];
617
- return fs2.readdirSync(this.addonsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
618
- const pkgPath = path2.join(this.addonsDir, d.name, "package.json");
619
- if (!fs2.existsSync(pkgPath))
620
- return null;
344
+ if (!fs2.existsSync(this.addonsDir)) return [];
345
+ const packageDirs = [];
346
+ for (const entry of fs2.readdirSync(this.addonsDir, { withFileTypes: true })) {
347
+ if (!entry.isDirectory()) continue;
348
+ if (entry.name.startsWith("@")) {
349
+ const scopeDir = path2.join(this.addonsDir, entry.name);
350
+ for (const inner of fs2.readdirSync(scopeDir, { withFileTypes: true })) {
351
+ if (inner.isDirectory()) packageDirs.push(path2.join(scopeDir, inner.name));
352
+ }
353
+ } else {
354
+ packageDirs.push(path2.join(this.addonsDir, entry.name));
355
+ }
356
+ }
357
+ return packageDirs.map((dir) => {
358
+ const pkgPath = path2.join(dir, "package.json");
359
+ if (!fs2.existsSync(pkgPath)) return null;
621
360
  try {
622
361
  const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
623
- if (!pkg.camstack?.addons)
624
- return null;
625
- return { name: pkg.name, version: pkg.version, dir: path2.join(this.addonsDir, d.name) };
362
+ if (!pkg.camstack?.addons) return null;
363
+ const sourceFile = path2.join(dir, ".install-source");
364
+ const installSource = fs2.existsSync(sourceFile) ? fs2.readFileSync(sourceFile, "utf-8").trim() : void 0;
365
+ const result = { name: pkg.name, version: pkg.version, dir, ...installSource ? { installSource } : {} };
366
+ return result;
626
367
  } catch {
627
368
  return null;
628
369
  }
@@ -630,8 +371,7 @@ var require_addon_installer = __commonJS({
630
371
  }
631
372
  /** Check if an addon is installed */
632
373
  isInstalled(packageName) {
633
- if (fs2.existsSync(path2.join(this.addonsDir, packageName, "package.json")))
634
- return true;
374
+ if (fs2.existsSync(path2.join(this.addonsDir, packageName, "package.json"))) return true;
635
375
  const legacy = packageName.replace(/^@[^/]+\//, "");
636
376
  return fs2.existsSync(path2.join(this.addonsDir, legacy, "package.json"));
637
377
  }
@@ -641,8 +381,7 @@ var require_addon_installer = __commonJS({
641
381
  if (!fs2.existsSync(pkgPath)) {
642
382
  pkgPath = path2.join(this.addonsDir, packageName.replace(/^@[^/]+\//, ""), "package.json");
643
383
  }
644
- if (!fs2.existsSync(pkgPath))
645
- return null;
384
+ if (!fs2.existsSync(pkgPath)) return null;
646
385
  try {
647
386
  const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
648
387
  return { name: pkg.name, version: pkg.version, dir: path2.dirname(pkgPath) };
@@ -651,15 +390,146 @@ var require_addon_installer = __commonJS({
651
390
  }
652
391
  }
653
392
  };
654
- exports2.AddonInstaller = AddonInstaller;
655
393
  }
656
394
  });
657
395
 
658
396
  // src/worker/addon-worker-entry.ts
659
- var os = __toESM(require("os"));
660
- var fs = __toESM(require("fs"));
661
- var path = __toESM(require("path"));
662
- var import_worker_process_manager = __toESM(require_worker_process_manager());
397
+ var os3 = __toESM(require("os"));
398
+ var fs3 = __toESM(require("fs"));
399
+ var path3 = __toESM(require("path"));
400
+
401
+ // src/worker/worker-process-manager.ts
402
+ var import_node_child_process = require("child_process");
403
+ var WorkerProcessManager = class {
404
+ constructor(sendToMain) {
405
+ this.sendToMain = sendToMain;
406
+ }
407
+ processes = /* @__PURE__ */ new Map();
408
+ async spawn(config) {
409
+ const child = (0, import_node_child_process.spawn)(config.command, [...config.args ?? []], {
410
+ cwd: config.cwd,
411
+ env: config.env ? { ...process.env, ...config.env } : void 0,
412
+ stdio: ["pipe", "pipe", "pipe"]
413
+ });
414
+ const managed = new ManagedProcess(child, config, this.sendToMain);
415
+ this.processes.set(child.pid, managed);
416
+ this.sendToMain({
417
+ type: "SUB_PROCESS_SPAWNED",
418
+ pid: child.pid,
419
+ name: config.name,
420
+ command: config.command
421
+ });
422
+ child.on("exit", (code) => {
423
+ this.sendToMain({
424
+ type: "SUB_PROCESS_EXITED",
425
+ pid: child.pid,
426
+ code
427
+ });
428
+ if (config.autoRestart && managed.restartCount < (config.maxRestarts ?? 3)) {
429
+ managed.restartCount++;
430
+ setTimeout(() => {
431
+ this.spawn(config).catch(() => {
432
+ });
433
+ }, 2e3);
434
+ }
435
+ });
436
+ return managed;
437
+ }
438
+ listProcesses() {
439
+ return [...this.processes.values()].map((p) => p.toInfo());
440
+ }
441
+ getWorkerStats() {
442
+ const mem = process.memoryUsage();
443
+ const cpu = process.cpuUsage();
444
+ return {
445
+ pid: process.pid,
446
+ cpuPercent: (cpu.user + cpu.system) / 1e6,
447
+ memoryRss: mem.rss,
448
+ heapUsed: mem.heapUsed,
449
+ uptimeSeconds: Math.round(process.uptime()),
450
+ restartCount: 0,
451
+ state: "running"
452
+ };
453
+ }
454
+ async killAll() {
455
+ for (const [, proc] of this.processes) {
456
+ proc.kill("SIGTERM");
457
+ }
458
+ this.processes.clear();
459
+ }
460
+ };
461
+ var ManagedProcess = class {
462
+ constructor(child, config, sendToMain) {
463
+ this.child = child;
464
+ this.config = config;
465
+ this.sendToMain = sendToMain;
466
+ this.pid = child.pid;
467
+ this.name = config.name;
468
+ child.on("exit", (code) => {
469
+ for (const handler of this.exitHandlers) handler(code);
470
+ });
471
+ child.on("error", (err) => {
472
+ for (const handler of this.errorHandlers) handler(err);
473
+ });
474
+ }
475
+ pid;
476
+ name;
477
+ restartCount = 0;
478
+ startedAt = Date.now();
479
+ exitHandlers = [];
480
+ errorHandlers = [];
481
+ getStats() {
482
+ return {
483
+ pid: this.pid,
484
+ cpuPercent: 0,
485
+ memoryRss: 0,
486
+ uptimeSeconds: Math.round((Date.now() - this.startedAt) / 1e3),
487
+ restartCount: this.restartCount,
488
+ state: this.child.exitCode !== null ? "stopped" : "running"
489
+ };
490
+ }
491
+ write(data) {
492
+ this.child.stdin?.write(data);
493
+ }
494
+ get stdout() {
495
+ return this.child.stdout;
496
+ }
497
+ get stderr() {
498
+ return this.child.stderr;
499
+ }
500
+ kill(signal = "SIGTERM") {
501
+ this.child.kill(signal);
502
+ }
503
+ wait() {
504
+ if (this.child.exitCode !== null) {
505
+ return Promise.resolve({ code: this.child.exitCode, signal: null });
506
+ }
507
+ return new Promise((resolve2) => {
508
+ this.child.on("exit", (code, signal) => {
509
+ resolve2({ code, signal });
510
+ });
511
+ });
512
+ }
513
+ onExit(handler) {
514
+ this.exitHandlers.push(handler);
515
+ }
516
+ onError(handler) {
517
+ this.errorHandlers.push(handler);
518
+ }
519
+ toInfo() {
520
+ return {
521
+ pid: this.pid,
522
+ name: this.name,
523
+ command: this.config.command,
524
+ state: this.child.exitCode !== null ? "stopped" : "running",
525
+ cpuPercent: 0,
526
+ memoryRss: 0,
527
+ uptimeSeconds: Math.round((Date.now() - this.startedAt) / 1e3)
528
+ };
529
+ }
530
+ };
531
+
532
+ // src/worker/addon-worker-entry.ts
663
533
  async function boot() {
664
534
  const hubUrl = process.env.CAMSTACK_WORKER_HUB_URL;
665
535
  const token = process.env.CAMSTACK_WORKER_TOKEN ?? "";
@@ -687,14 +557,14 @@ async function boot() {
687
557
  id: workerId,
688
558
  name: addonId,
689
559
  capabilities: [],
690
- platform: os.platform(),
691
- arch: os.arch(),
692
- cpuCores: os.cpus().length,
693
- memoryMB: Math.round(os.totalmem() / 1024 / 1024),
560
+ platform: os3.platform(),
561
+ arch: os3.arch(),
562
+ cpuCores: os3.cpus().length,
563
+ memoryMB: Math.round(os3.totalmem() / 1024 / 1024),
694
564
  pythonRuntimes: [],
695
565
  installedAddons: [addonId],
696
566
  taskTypes: [],
697
- host: os.hostname(),
567
+ host: os3.hostname(),
698
568
  port: 0,
699
569
  httpPort: 0
700
570
  });
@@ -724,10 +594,10 @@ async function boot() {
724
594
  };
725
595
  }
726
596
  const logger = createScopedLogger([addonId]);
727
- const processManager = new import_worker_process_manager.WorkerProcessManager(() => {
597
+ const processManager = new WorkerProcessManager(() => {
728
598
  });
729
- const pkgJsonPath = path.join(addonDir, "package.json");
730
- const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
599
+ const pkgJsonPath = path3.join(addonDir, "package.json");
600
+ const pkgJson = JSON.parse(fs3.readFileSync(pkgJsonPath, "utf-8"));
731
601
  const manifest = pkgJson.camstack;
732
602
  if (!manifest?.addons?.length) {
733
603
  console.error(`[worker:${addonId}] No camstack addon manifest in ${pkgJsonPath}`);
@@ -739,10 +609,10 @@ async function boot() {
739
609
  process.exit(1);
740
610
  }
741
611
  const entryFile = declaration.entry.replace(/^\.\//, "").replace(/^src\//, "dist/").replace(/\.ts$/, ".js");
742
- let entryPath = path.resolve(addonDir, entryFile);
743
- if (!fs.existsSync(entryPath)) {
612
+ let entryPath = path3.resolve(addonDir, entryFile);
613
+ if (!fs3.existsSync(entryPath)) {
744
614
  const cjsPath = entryPath.replace(/\.js$/, ".cjs");
745
- if (fs.existsSync(cjsPath)) entryPath = cjsPath;
615
+ if (fs3.existsSync(cjsPath)) entryPath = cjsPath;
746
616
  }
747
617
  const mod = await import(entryPath);
748
618
  const firstKey = Object.keys(mod)[0];
@@ -799,8 +669,8 @@ async function boot() {
799
669
  try {
800
670
  let result;
801
671
  if (task.taskType === "addon.install") {
802
- const { AddonInstaller } = await Promise.resolve().then(() => __toESM(require_addon_installer()));
803
- const installer = new AddonInstaller({ addonsDir: path.dirname(addonDir) });
672
+ const { AddonInstaller: AddonInstaller2 } = await Promise.resolve().then(() => (init_addon_installer(), addon_installer_exports));
673
+ const installer = new AddonInstaller2({ addonsDir: path3.dirname(addonDir) });
804
674
  const payload = task.payload;
805
675
  result = await installer.installFromNpm(payload.package, payload.version);
806
676
  } else if (typeof addon.handleTask === "function") {