plugin-updater 1.0.35 → 1.0.37
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/dist/index.js +61 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -416,6 +416,7 @@ async function deployToExecutionDir(pluginName, executionPath, changed, configDi
|
|
|
416
416
|
writeLog(`Copy failed for ${pluginName}: ${err.message}`, true);
|
|
417
417
|
}
|
|
418
418
|
applyClaudeManifest(sourceDir, configDir, pluginName);
|
|
419
|
+
await startDeclaredDaemon(sourceDir, configDir, pluginName);
|
|
419
420
|
// Claude Code never imports deployed plugin files, so under claude the
|
|
420
421
|
// updater is the runtime and invokes the plugin's activate() itself
|
|
421
422
|
if (getAppName() === "claude") {
|
|
@@ -423,7 +424,15 @@ async function deployToExecutionDir(pluginName, executionPath, changed, configDi
|
|
|
423
424
|
const deployed = await import(pluginExecutionFile);
|
|
424
425
|
if (typeof deployed.activate === "function") {
|
|
425
426
|
writeLog(`Activating ${pluginName}`);
|
|
426
|
-
|
|
427
|
+
// tells the plugin the updater is the caller, so it must not start
|
|
428
|
+
// another earlyLaunch and recurse back into the updater
|
|
429
|
+
process.env.PLUGIN_UPDATER_ACTIVATION = "1";
|
|
430
|
+
try {
|
|
431
|
+
await deployed.activate();
|
|
432
|
+
}
|
|
433
|
+
finally {
|
|
434
|
+
delete process.env.PLUGIN_UPDATER_ACTIVATION;
|
|
435
|
+
}
|
|
427
436
|
writeLog(`Activated ${pluginName}`);
|
|
428
437
|
}
|
|
429
438
|
}
|
|
@@ -456,14 +465,62 @@ function applyClaudeManifest(sourceDir, configDir, pluginName) {
|
|
|
456
465
|
settings.env = env;
|
|
457
466
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf8");
|
|
458
467
|
}
|
|
459
|
-
if (manifest.daemon?.script) {
|
|
460
|
-
writeLog(`${pluginName} defines a daemon (${manifest.daemon.script}) which the updater does not manage yet`, true);
|
|
461
|
-
}
|
|
462
468
|
}
|
|
463
469
|
catch (e) {
|
|
464
470
|
writeLog(`claudeHub manifest handling failed for ${pluginName}: ${e.message}`, true);
|
|
465
471
|
}
|
|
466
472
|
}
|
|
473
|
+
async function isDaemonHealthy(url) {
|
|
474
|
+
try {
|
|
475
|
+
const controller = new AbortController();
|
|
476
|
+
const timer = setTimeout(() => controller.abort(), 1500);
|
|
477
|
+
const res = await fetch(url, { signal: controller.signal });
|
|
478
|
+
clearTimeout(timer);
|
|
479
|
+
return res.ok;
|
|
480
|
+
}
|
|
481
|
+
catch {
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
// idempotent: health-check the declared endpoint, spawn detached only if down.
|
|
486
|
+
// the daemon outlives this process so the proxy persists across the session.
|
|
487
|
+
async function startDeclaredDaemon(sourceDir, configDir, pluginName) {
|
|
488
|
+
try {
|
|
489
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(sourceDir, "package.json"), "utf8"));
|
|
490
|
+
const daemon = pkg.claudeHub?.daemon;
|
|
491
|
+
if (!daemon?.script)
|
|
492
|
+
return;
|
|
493
|
+
const healthUrl = daemon.healthCheckUrl
|
|
494
|
+
|| (daemon.port ? `http://127.0.0.1:${daemon.port}/health` : "");
|
|
495
|
+
if (healthUrl && (await isDaemonHealthy(healthUrl))) {
|
|
496
|
+
writeLog(`Daemon for ${pluginName} already running`);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const scriptPath = path.join(sourceDir, daemon.script);
|
|
500
|
+
if (!fs.existsSync(scriptPath)) {
|
|
501
|
+
writeLog(`Daemon script missing for ${pluginName}: ${scriptPath}`, true);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
const runtime = daemon.runtime || "node";
|
|
505
|
+
const { spawn } = await import("child_process");
|
|
506
|
+
const child = spawn(runtime, [scriptPath], {
|
|
507
|
+
cwd: sourceDir,
|
|
508
|
+
detached: true,
|
|
509
|
+
stdio: "ignore",
|
|
510
|
+
env: {
|
|
511
|
+
...process.env,
|
|
512
|
+
HUB_CONFIG_DIR: configDir,
|
|
513
|
+
HUB_APP_NAME: getAppName() === "claude" ? "Claude Code" : "OpenCode",
|
|
514
|
+
...(daemon.port ? { HUB_PROXY_PORT: String(daemon.port) } : {}),
|
|
515
|
+
},
|
|
516
|
+
});
|
|
517
|
+
child.unref();
|
|
518
|
+
writeLog(`Started daemon for ${pluginName} (${runtime} ${daemon.script})`);
|
|
519
|
+
}
|
|
520
|
+
catch (e) {
|
|
521
|
+
writeLog(`Daemon start failed for ${pluginName}: ${e.message}`, true);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
467
524
|
async function pluginUpdaterEntry(input) {
|
|
468
525
|
const appName = getAppName();
|
|
469
526
|
const configDir = getAppConfigDir(appName);
|