panopticon-cli 0.3.6 → 0.3.7
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/cli/index.js +112 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/public/assets/{index-C6PTmw-V.css → index-CUoYoWX_.css} +1 -1
- package/dist/dashboard/public/assets/{index-kezR_sk3.js → index-CY0Ew5B9.js} +35 -35
- package/dist/dashboard/public/index.html +2 -2
- package/dist/dashboard/server.js +442 -83
- package/package.json +2 -2
- package/templates/traefik/docker-compose.yml +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1328,12 +1328,12 @@ import chalk9 from "chalk";
|
|
|
1328
1328
|
async function killCommand(id, options) {
|
|
1329
1329
|
const agentId = id.startsWith("agent-") ? id : `agent-${id.toLowerCase()}`;
|
|
1330
1330
|
const state = getAgentState(agentId);
|
|
1331
|
-
const
|
|
1332
|
-
if (!state && !
|
|
1331
|
+
const isRunning3 = sessionExists(agentId);
|
|
1332
|
+
if (!state && !isRunning3) {
|
|
1333
1333
|
console.log(chalk9.yellow(`Agent ${agentId} not found.`));
|
|
1334
1334
|
return;
|
|
1335
1335
|
}
|
|
1336
|
-
if (!options.force &&
|
|
1336
|
+
if (!options.force && isRunning3) {
|
|
1337
1337
|
}
|
|
1338
1338
|
try {
|
|
1339
1339
|
stopAgent(agentId);
|
|
@@ -4052,7 +4052,7 @@ async function destroyCommand(issueId, options) {
|
|
|
4052
4052
|
import chalk22 from "chalk";
|
|
4053
4053
|
import ora12 from "ora";
|
|
4054
4054
|
import { execSync as execSync8 } from "child_process";
|
|
4055
|
-
import { existsSync as existsSync18, mkdirSync as mkdirSync12, readFileSync as readFileSync15, copyFileSync, readdirSync as readdirSync11, statSync } from "fs";
|
|
4055
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync12, writeFileSync as writeFileSync11, readFileSync as readFileSync15, copyFileSync, readdirSync as readdirSync11, statSync } from "fs";
|
|
4056
4056
|
import { join as join17 } from "path";
|
|
4057
4057
|
import { homedir as homedir6, platform } from "os";
|
|
4058
4058
|
function registerInstallCommand(program2) {
|
|
@@ -4273,6 +4273,18 @@ async function installCommand(options) {
|
|
|
4273
4273
|
} else {
|
|
4274
4274
|
spinner.info("Traefik configuration already exists (skipping)");
|
|
4275
4275
|
}
|
|
4276
|
+
const existingCompose = join17(TRAEFIK_DIR, "docker-compose.yml");
|
|
4277
|
+
if (existsSync18(existingCompose)) {
|
|
4278
|
+
const content = readFileSync15(existingCompose, "utf-8");
|
|
4279
|
+
if (content.includes("panopticon:") && !content.includes("external: true")) {
|
|
4280
|
+
const patched = content.replace(
|
|
4281
|
+
/networks:\s*\n\s*panopticon:\s*\n\s*name: panopticon\s*\n\s*driver: bridge/,
|
|
4282
|
+
"networks:\n panopticon:\n name: panopticon\n external: true # Network created by 'pan install'"
|
|
4283
|
+
);
|
|
4284
|
+
writeFileSync11(existingCompose, patched);
|
|
4285
|
+
spinner.info("Migrated Traefik config (added external: true to network)");
|
|
4286
|
+
}
|
|
4287
|
+
}
|
|
4276
4288
|
} catch (error) {
|
|
4277
4289
|
spinner.fail(`Failed to set up Traefik configuration: ${error}`);
|
|
4278
4290
|
console.log(chalk22.yellow("You can set up Traefik manually later"));
|
|
@@ -4475,8 +4487,8 @@ function evaluateHealthState(timeSinceActivityMs, thresholds) {
|
|
|
4475
4487
|
}
|
|
4476
4488
|
function getAgentHealth2(agentId, runtime, thresholds) {
|
|
4477
4489
|
const thresholdsMs = thresholds || getHealthThresholdsMs();
|
|
4478
|
-
const
|
|
4479
|
-
if (!
|
|
4490
|
+
const isRunning3 = runtime.isRunning(agentId);
|
|
4491
|
+
if (!isRunning3) {
|
|
4480
4492
|
return {
|
|
4481
4493
|
agentId,
|
|
4482
4494
|
state: "stuck",
|
|
@@ -4628,7 +4640,7 @@ function cleanupOldEvents(database = getHealthDatabase(), retentionDays = RETENT
|
|
|
4628
4640
|
}
|
|
4629
4641
|
|
|
4630
4642
|
// src/lib/cloister/specialists.ts
|
|
4631
|
-
import { readFileSync as readFileSync18, writeFileSync as writeFileSync13, existsSync as existsSync22, mkdirSync as mkdirSync15, readdirSync as readdirSync13, unlinkSync as unlinkSync2 } from "fs";
|
|
4643
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync13, existsSync as existsSync22, mkdirSync as mkdirSync15, readdirSync as readdirSync13, unlinkSync as unlinkSync2, appendFileSync as appendFileSync3 } from "fs";
|
|
4632
4644
|
import { join as join22, basename as basename4 } from "path";
|
|
4633
4645
|
import { execSync as execSync9 } from "child_process";
|
|
4634
4646
|
|
|
@@ -5079,9 +5091,66 @@ async function initializeEnabledSpecialists() {
|
|
|
5079
5091
|
}
|
|
5080
5092
|
return results;
|
|
5081
5093
|
}
|
|
5094
|
+
async function wakeSpecialist(name, taskPrompt, options = {}) {
|
|
5095
|
+
const { waitForReady = true, startIfNotRunning = true } = options;
|
|
5096
|
+
const tmuxSession = getTmuxSessionName(name);
|
|
5097
|
+
const sessionId = getSessionId(name);
|
|
5098
|
+
const wasAlreadyRunning = isRunning(name);
|
|
5099
|
+
if (!wasAlreadyRunning) {
|
|
5100
|
+
if (!startIfNotRunning) {
|
|
5101
|
+
return {
|
|
5102
|
+
success: false,
|
|
5103
|
+
message: `Specialist ${name} is not running`,
|
|
5104
|
+
wasAlreadyRunning: false,
|
|
5105
|
+
error: "not_running"
|
|
5106
|
+
};
|
|
5107
|
+
}
|
|
5108
|
+
const cwd = process.env.HOME || "/home/eltmon";
|
|
5109
|
+
try {
|
|
5110
|
+
const claudeCmd = sessionId ? `claude --resume "${sessionId}" --dangerously-skip-permissions` : `claude --dangerously-skip-permissions`;
|
|
5111
|
+
execSync9(
|
|
5112
|
+
`tmux new-session -d -s "${tmuxSession}" -c "${cwd}" "${claudeCmd}"`,
|
|
5113
|
+
{ encoding: "utf-8" }
|
|
5114
|
+
);
|
|
5115
|
+
if (waitForReady) {
|
|
5116
|
+
await new Promise((resolve2) => setTimeout(resolve2, 3e3));
|
|
5117
|
+
}
|
|
5118
|
+
} catch (error) {
|
|
5119
|
+
return {
|
|
5120
|
+
success: false,
|
|
5121
|
+
message: `Failed to start specialist ${name}: ${error.message}`,
|
|
5122
|
+
wasAlreadyRunning: false,
|
|
5123
|
+
error: error.message
|
|
5124
|
+
};
|
|
5125
|
+
}
|
|
5126
|
+
}
|
|
5127
|
+
try {
|
|
5128
|
+
const escapedPrompt = taskPrompt.replace(/'/g, "'\\''");
|
|
5129
|
+
execSync9(`tmux send-keys -t "${tmuxSession}" '${escapedPrompt}'`, { encoding: "utf-8" });
|
|
5130
|
+
await new Promise((resolve2) => setTimeout(resolve2, 200));
|
|
5131
|
+
execSync9(`tmux send-keys -t "${tmuxSession}" C-m`, { encoding: "utf-8" });
|
|
5132
|
+
recordWake(name, sessionId || void 0);
|
|
5133
|
+
return {
|
|
5134
|
+
success: true,
|
|
5135
|
+
message: wasAlreadyRunning ? `Sent task to running specialist ${name}` : `Started specialist ${name} and sent task`,
|
|
5136
|
+
tmuxSession,
|
|
5137
|
+
wasAlreadyRunning
|
|
5138
|
+
};
|
|
5139
|
+
} catch (error) {
|
|
5140
|
+
return {
|
|
5141
|
+
success: false,
|
|
5142
|
+
message: `Failed to send task to specialist ${name}: ${error.message}`,
|
|
5143
|
+
tmuxSession,
|
|
5144
|
+
wasAlreadyRunning,
|
|
5145
|
+
error: error.message
|
|
5146
|
+
};
|
|
5147
|
+
}
|
|
5148
|
+
}
|
|
5082
5149
|
function checkSpecialistQueue(specialistName) {
|
|
5083
5150
|
return checkHook(specialistName);
|
|
5084
5151
|
}
|
|
5152
|
+
var FEEDBACK_DIR = join22(PANOPTICON_HOME, "specialists", "feedback");
|
|
5153
|
+
var FEEDBACK_LOG = join22(FEEDBACK_DIR, "feedback.jsonl");
|
|
5085
5154
|
|
|
5086
5155
|
// src/lib/runtimes/claude-code.ts
|
|
5087
5156
|
import { existsSync as existsSync23, readFileSync as readFileSync19, statSync as statSync3, mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
|
|
@@ -5963,8 +6032,25 @@ async function performSpecialistWake(state, options) {
|
|
|
5963
6032
|
error: "Could not determine specialist name from agent ID"
|
|
5964
6033
|
};
|
|
5965
6034
|
}
|
|
5966
|
-
|
|
5967
|
-
|
|
6035
|
+
const sessionId = getSessionId(specialistName);
|
|
6036
|
+
const tmuxSession = getTmuxSessionName(specialistName);
|
|
6037
|
+
console.log(`[handoff] Waking specialist ${specialistName} (session: ${sessionId || "none"})`);
|
|
6038
|
+
const wakeResult = await wakeSpecialist(specialistName, prompt, {
|
|
6039
|
+
waitForReady: true,
|
|
6040
|
+
startIfNotRunning: true
|
|
6041
|
+
});
|
|
6042
|
+
if (!wakeResult.success) {
|
|
6043
|
+
console.error(`[handoff] Failed to wake specialist: ${wakeResult.error}`);
|
|
6044
|
+
console.warn(`[handoff] Falling back to kill-spawn`);
|
|
6045
|
+
return await performKillAndSpawn(state, options);
|
|
6046
|
+
}
|
|
6047
|
+
console.log(`[handoff] Successfully woke specialist ${specialistName}`);
|
|
6048
|
+
return {
|
|
6049
|
+
success: true,
|
|
6050
|
+
method: "specialist-wake",
|
|
6051
|
+
newSessionId: sessionId || void 0,
|
|
6052
|
+
context
|
|
6053
|
+
};
|
|
5968
6054
|
} catch (error) {
|
|
5969
6055
|
return {
|
|
5970
6056
|
success: false,
|
|
@@ -5997,7 +6083,7 @@ function sleep(ms) {
|
|
|
5997
6083
|
}
|
|
5998
6084
|
|
|
5999
6085
|
// src/lib/cloister/handoff-logger.ts
|
|
6000
|
-
import { existsSync as existsSync27, mkdirSync as mkdirSync18, appendFileSync as
|
|
6086
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync18, appendFileSync as appendFileSync4, readFileSync as readFileSync21, writeFileSync as writeFileSync16 } from "fs";
|
|
6001
6087
|
import { join as join27 } from "path";
|
|
6002
6088
|
var HANDOFF_LOG_FILE = join27(PANOPTICON_HOME, "logs", "handoffs.jsonl");
|
|
6003
6089
|
function ensureLogDir() {
|
|
@@ -6009,7 +6095,7 @@ function ensureLogDir() {
|
|
|
6009
6095
|
function logHandoffEvent(event) {
|
|
6010
6096
|
ensureLogDir();
|
|
6011
6097
|
const line = JSON.stringify(event) + "\n";
|
|
6012
|
-
|
|
6098
|
+
appendFileSync4(HANDOFF_LOG_FILE, line, "utf-8");
|
|
6013
6099
|
}
|
|
6014
6100
|
function createHandoffEvent(agentId, issueId, context, trigger, success, errorMessage) {
|
|
6015
6101
|
let stuckMinutes;
|
|
@@ -6521,7 +6607,7 @@ function registerCloisterCommands(program2) {
|
|
|
6521
6607
|
|
|
6522
6608
|
// src/cli/commands/setup/hooks.ts
|
|
6523
6609
|
import chalk26 from "chalk";
|
|
6524
|
-
import { readFileSync as readFileSync22, writeFileSync as
|
|
6610
|
+
import { readFileSync as readFileSync22, writeFileSync as writeFileSync17, existsSync as existsSync28, mkdirSync as mkdirSync19, copyFileSync as copyFileSync2, chmodSync as chmodSync2 } from "fs";
|
|
6525
6611
|
import { join as join28 } from "path";
|
|
6526
6612
|
import { execSync as execSync12 } from "child_process";
|
|
6527
6613
|
import { homedir as homedir9 } from "os";
|
|
@@ -6662,7 +6748,7 @@ async function setupHooksCommand() {
|
|
|
6662
6748
|
}
|
|
6663
6749
|
]
|
|
6664
6750
|
});
|
|
6665
|
-
|
|
6751
|
+
writeFileSync17(settingsPath, JSON.stringify(settings, null, 2));
|
|
6666
6752
|
console.log(chalk26.green("\u2713 Updated Claude Code settings.json"));
|
|
6667
6753
|
console.log(chalk26.green.bold("\n\u2713 Setup complete!\n"));
|
|
6668
6754
|
console.log(chalk26.dim("Heartbeat hooks are now active. When you run agents via"));
|
|
@@ -7451,6 +7537,19 @@ program.command("up").description("Start dashboard (and Traefik if enabled)").op
|
|
|
7451
7537
|
const traefikDir = join32(process.env.HOME || "", ".panopticon", "traefik");
|
|
7452
7538
|
if (existsSync31(traefikDir)) {
|
|
7453
7539
|
try {
|
|
7540
|
+
const composeFile = join32(traefikDir, "docker-compose.yml");
|
|
7541
|
+
if (existsSync31(composeFile)) {
|
|
7542
|
+
const content = readFileSync26(composeFile, "utf-8");
|
|
7543
|
+
if (!content.includes("external: true") && content.includes("panopticon:")) {
|
|
7544
|
+
const patched = content.replace(
|
|
7545
|
+
/networks:\s*\n\s*panopticon:\s*\n\s*name: panopticon\s*\n\s*driver: bridge/,
|
|
7546
|
+
"networks:\n panopticon:\n name: panopticon\n external: true # Network created by 'pan install'"
|
|
7547
|
+
);
|
|
7548
|
+
const { writeFileSync: writeFileSync18 } = await import("fs");
|
|
7549
|
+
writeFileSync18(composeFile, patched);
|
|
7550
|
+
console.log(chalk34.dim(" (migrated network config)"));
|
|
7551
|
+
}
|
|
7552
|
+
}
|
|
7454
7553
|
console.log(chalk34.dim("Starting Traefik..."));
|
|
7455
7554
|
execSync17("docker-compose up -d", {
|
|
7456
7555
|
cwd: traefikDir,
|