plugin-updater 1.0.36 → 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 +52 -3
- 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") {
|
|
@@ -464,14 +465,62 @@ function applyClaudeManifest(sourceDir, configDir, pluginName) {
|
|
|
464
465
|
settings.env = env;
|
|
465
466
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf8");
|
|
466
467
|
}
|
|
467
|
-
if (manifest.daemon?.script) {
|
|
468
|
-
writeLog(`${pluginName} defines a daemon (${manifest.daemon.script}) which the updater does not manage yet`, true);
|
|
469
|
-
}
|
|
470
468
|
}
|
|
471
469
|
catch (e) {
|
|
472
470
|
writeLog(`claudeHub manifest handling failed for ${pluginName}: ${e.message}`, true);
|
|
473
471
|
}
|
|
474
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
|
+
}
|
|
475
524
|
async function pluginUpdaterEntry(input) {
|
|
476
525
|
const appName = getAppName();
|
|
477
526
|
const configDir = getAppConfigDir(appName);
|