@shmulikdav/solix 1.4.2 → 1.5.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/dist/index.js +374 -14
- package/dist/web/assets/{GalaxyPanel-BOv3eUQ6.js → GalaxyPanel-CsENB_VZ.js} +1 -1
- package/dist/web/assets/{Scene-CUyQyFqH.js → Scene-DrvagUWn.js} +2 -2
- package/dist/web/assets/{TimelineDrawer-B4MB3hvB.js → TimelineDrawer-DsGkgssp.js} +1 -1
- package/dist/web/assets/{index-BUrmJuUb.js → index-2P2elsQX.js} +25 -25
- package/dist/web/assets/index-BxR0uU1p.css +1 -0
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/dist/web/assets/index-CzhFbCNn.css +0 -1
package/dist/index.js
CHANGED
|
@@ -343,6 +343,46 @@ async function probeWrappers(port) {
|
|
|
343
343
|
};
|
|
344
344
|
}
|
|
345
345
|
}
|
|
346
|
+
async function probeAgentView(port) {
|
|
347
|
+
try {
|
|
348
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/system/preflight`, {
|
|
349
|
+
signal: AbortSignal.timeout(1500)
|
|
350
|
+
});
|
|
351
|
+
if (!res.ok) {
|
|
352
|
+
return {
|
|
353
|
+
ok: true,
|
|
354
|
+
label: "Agent View available",
|
|
355
|
+
detail: "server too old to report"
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
const data = await res.json();
|
|
359
|
+
if (!data.claudeAvailable) {
|
|
360
|
+
return {
|
|
361
|
+
ok: false,
|
|
362
|
+
label: "Agent View available",
|
|
363
|
+
detail: "claude not on PATH"
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
if (data.agentViewAvailable) {
|
|
367
|
+
return {
|
|
368
|
+
ok: true,
|
|
369
|
+
label: "Agent View available",
|
|
370
|
+
detail: `yes (${data.version ?? "unknown version"})`
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
ok: true,
|
|
375
|
+
label: "Agent View available",
|
|
376
|
+
detail: `no \u2014 need Claude Code 2.1.139+ (have ${data.version ?? "?"})`
|
|
377
|
+
};
|
|
378
|
+
} catch {
|
|
379
|
+
return {
|
|
380
|
+
ok: true,
|
|
381
|
+
label: "Agent View available",
|
|
382
|
+
detail: "unknown \u2014 server unreachable"
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
}
|
|
346
386
|
async function doctor() {
|
|
347
387
|
const port = Number(process.env.SOLIX_PORT ?? 4242);
|
|
348
388
|
const checks = [];
|
|
@@ -425,6 +465,7 @@ async function doctor() {
|
|
|
425
465
|
});
|
|
426
466
|
checks.push(await probeHealth(port));
|
|
427
467
|
checks.push(await probeWrappers(port));
|
|
468
|
+
checks.push(await probeAgentView(port));
|
|
428
469
|
console.log("\nSolix Diagnostics\n");
|
|
429
470
|
let allOk = true;
|
|
430
471
|
for (const c of checks) {
|
|
@@ -1175,6 +1216,10 @@ function getDb() {
|
|
|
1175
1216
|
ensureColumn(db, "sessions", "advisor_role", "advisor_role TEXT");
|
|
1176
1217
|
ensureColumn(db, "sessions", "worktree_path", "worktree_path TEXT");
|
|
1177
1218
|
ensureColumn(db, "sessions", "wrapper_socket_path", "wrapper_socket_path TEXT");
|
|
1219
|
+
ensureColumn(db, "sessions", "agent_view_id", "agent_view_id TEXT");
|
|
1220
|
+
ensureColumn(db, "sessions", "agent_view_summary", "agent_view_summary TEXT");
|
|
1221
|
+
ensureColumn(db, "sessions", "pr_url", "pr_url TEXT");
|
|
1222
|
+
ensureColumn(db, "sessions", "pr_check_status", "pr_check_status TEXT");
|
|
1178
1223
|
ensureColumn(db, "advisors", "texture_pack", "texture_pack TEXT");
|
|
1179
1224
|
ensureColumn(db, "missions", "error_summary", "error_summary TEXT");
|
|
1180
1225
|
_db = db;
|
|
@@ -1262,7 +1307,11 @@ function rowToSession(row) {
|
|
|
1262
1307
|
orbitSlot: row.orbit_slot,
|
|
1263
1308
|
name: row.name ?? void 0,
|
|
1264
1309
|
worktreePath: row.worktree_path ?? void 0,
|
|
1265
|
-
wrapperSocketPath: row.wrapper_socket_path ?? void 0
|
|
1310
|
+
wrapperSocketPath: row.wrapper_socket_path ?? void 0,
|
|
1311
|
+
agentViewId: row.agent_view_id ?? void 0,
|
|
1312
|
+
agentViewSummary: row.agent_view_summary ?? void 0,
|
|
1313
|
+
prUrl: row.pr_url ?? void 0,
|
|
1314
|
+
prCheckStatus: row.pr_check_status ?? void 0
|
|
1266
1315
|
};
|
|
1267
1316
|
}
|
|
1268
1317
|
function nextOrbitSlot(db, projectId) {
|
|
@@ -1297,9 +1346,11 @@ function upsertSession(db, input) {
|
|
|
1297
1346
|
`INSERT INTO sessions (
|
|
1298
1347
|
id, pid, project_id, parent_session_id, origin, model, status,
|
|
1299
1348
|
context_usage_pct, orbit_slot, cwd, name, kind, advisor_role,
|
|
1300
|
-
worktree_path, wrapper_socket_path,
|
|
1349
|
+
worktree_path, wrapper_socket_path,
|
|
1350
|
+
agent_view_id, agent_view_summary, pr_url, pr_check_status,
|
|
1351
|
+
created_at, updated_at
|
|
1301
1352
|
)
|
|
1302
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?, NULL, ?, ?, ?, ?, ?, ?)`
|
|
1353
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 0, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
1303
1354
|
).run(
|
|
1304
1355
|
input.id,
|
|
1305
1356
|
input.pid,
|
|
@@ -1314,6 +1365,10 @@ function upsertSession(db, input) {
|
|
|
1314
1365
|
input.advisorRole ?? null,
|
|
1315
1366
|
input.worktreePath ?? null,
|
|
1316
1367
|
input.wrapperSocketPath ?? null,
|
|
1368
|
+
input.agentViewId ?? null,
|
|
1369
|
+
input.agentViewSummary ?? null,
|
|
1370
|
+
input.prUrl ?? null,
|
|
1371
|
+
input.prCheckStatus ?? null,
|
|
1317
1372
|
ts2,
|
|
1318
1373
|
ts2
|
|
1319
1374
|
);
|
|
@@ -1333,9 +1388,39 @@ function upsertSession(db, input) {
|
|
|
1333
1388
|
contextUsagePct: 0,
|
|
1334
1389
|
orbitSlot,
|
|
1335
1390
|
worktreePath: input.worktreePath,
|
|
1336
|
-
wrapperSocketPath: input.wrapperSocketPath
|
|
1391
|
+
wrapperSocketPath: input.wrapperSocketPath,
|
|
1392
|
+
agentViewId: input.agentViewId,
|
|
1393
|
+
agentViewSummary: input.agentViewSummary,
|
|
1394
|
+
prUrl: input.prUrl,
|
|
1395
|
+
prCheckStatus: input.prCheckStatus
|
|
1337
1396
|
};
|
|
1338
1397
|
}
|
|
1398
|
+
function setAgentViewFields(db, sessionId, fields) {
|
|
1399
|
+
const ts2 = now();
|
|
1400
|
+
const updates = ["updated_at = ?"];
|
|
1401
|
+
const values = [ts2];
|
|
1402
|
+
if (fields.status !== void 0) {
|
|
1403
|
+
updates.push("status = ?");
|
|
1404
|
+
values.push(fields.status);
|
|
1405
|
+
}
|
|
1406
|
+
if (fields.agentViewSummary !== void 0) {
|
|
1407
|
+
updates.push("agent_view_summary = ?");
|
|
1408
|
+
values.push(fields.agentViewSummary);
|
|
1409
|
+
}
|
|
1410
|
+
if (fields.prUrl !== void 0) {
|
|
1411
|
+
updates.push("pr_url = ?");
|
|
1412
|
+
values.push(fields.prUrl);
|
|
1413
|
+
}
|
|
1414
|
+
if (fields.prCheckStatus !== void 0) {
|
|
1415
|
+
updates.push("pr_check_status = ?");
|
|
1416
|
+
values.push(fields.prCheckStatus);
|
|
1417
|
+
}
|
|
1418
|
+
values.push(sessionId);
|
|
1419
|
+
db.prepare(
|
|
1420
|
+
`UPDATE sessions SET ${updates.join(", ")} WHERE id = ?`
|
|
1421
|
+
).run(...values);
|
|
1422
|
+
return getSession(db, sessionId);
|
|
1423
|
+
}
|
|
1339
1424
|
function setSessionStatus(db, sessionId, status) {
|
|
1340
1425
|
const ts2 = now();
|
|
1341
1426
|
if (status === "terminated") {
|
|
@@ -2362,6 +2447,19 @@ var RegistryClient = class {
|
|
|
2362
2447
|
};
|
|
2363
2448
|
|
|
2364
2449
|
// ../server/src/http.ts
|
|
2450
|
+
function isAgentViewVersion(version) {
|
|
2451
|
+
if (!version) return false;
|
|
2452
|
+
const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
2453
|
+
if (!match) return false;
|
|
2454
|
+
const major = Number(match[1]);
|
|
2455
|
+
const minor = Number(match[2]);
|
|
2456
|
+
const patch = Number(match[3]);
|
|
2457
|
+
if (major > 2) return true;
|
|
2458
|
+
if (major < 2) return false;
|
|
2459
|
+
if (minor > 1) return true;
|
|
2460
|
+
if (minor < 1) return false;
|
|
2461
|
+
return patch >= 139;
|
|
2462
|
+
}
|
|
2365
2463
|
function createHttpApp(opts) {
|
|
2366
2464
|
const app = new Hono();
|
|
2367
2465
|
app.use("*", cors());
|
|
@@ -2637,15 +2735,17 @@ function createHttpApp(opts) {
|
|
|
2637
2735
|
encoding: "utf8"
|
|
2638
2736
|
});
|
|
2639
2737
|
if (res.status === 0) {
|
|
2738
|
+
const version = (res.stdout ?? "").trim() || void 0;
|
|
2640
2739
|
preflightCache = {
|
|
2641
2740
|
claudeAvailable: true,
|
|
2642
|
-
version
|
|
2741
|
+
version,
|
|
2742
|
+
agentViewAvailable: isAgentViewVersion(version)
|
|
2643
2743
|
};
|
|
2644
2744
|
} else {
|
|
2645
|
-
preflightCache = { claudeAvailable: false };
|
|
2745
|
+
preflightCache = { claudeAvailable: false, agentViewAvailable: false };
|
|
2646
2746
|
}
|
|
2647
2747
|
} catch {
|
|
2648
|
-
preflightCache = { claudeAvailable: false };
|
|
2748
|
+
preflightCache = { claudeAvailable: false, agentViewAvailable: false };
|
|
2649
2749
|
}
|
|
2650
2750
|
return c.json(preflightCache);
|
|
2651
2751
|
});
|
|
@@ -2961,6 +3061,14 @@ var Launcher = class {
|
|
|
2961
3061
|
*/
|
|
2962
3062
|
launch(opts) {
|
|
2963
3063
|
if (!opts.initialPrompt.trim()) return { ok: false };
|
|
3064
|
+
if (opts.useAgentView) {
|
|
3065
|
+
return this.dispatchAgentView({
|
|
3066
|
+
cwd: opts.cwd,
|
|
3067
|
+
model: opts.model,
|
|
3068
|
+
initialPrompt: opts.initialPrompt,
|
|
3069
|
+
agentName: opts.agentName
|
|
3070
|
+
});
|
|
3071
|
+
}
|
|
2964
3072
|
let spawnCwd = opts.cwd;
|
|
2965
3073
|
let worktreePath;
|
|
2966
3074
|
if (opts.worktreeBranch?.trim()) {
|
|
@@ -3014,6 +3122,62 @@ var Launcher = class {
|
|
|
3014
3122
|
worktreePath
|
|
3015
3123
|
});
|
|
3016
3124
|
}
|
|
3125
|
+
/**
|
|
3126
|
+
* Sprint L: dispatch via Anthropic's Agent View daemon. Runs
|
|
3127
|
+
* `claude --bg "<prompt>"` so the session is hosted by the
|
|
3128
|
+
* supervisor and picked up by Solix's filesystem watcher within ~1s.
|
|
3129
|
+
* Returns the short id parsed from claude's output line:
|
|
3130
|
+
* backgrounded · 7c5dcf5d
|
|
3131
|
+
*/
|
|
3132
|
+
dispatchAgentView(opts) {
|
|
3133
|
+
if (FAKE_CLAUDE) {
|
|
3134
|
+
this.broadcaster.broadcast({
|
|
3135
|
+
type: "toast",
|
|
3136
|
+
level: "info",
|
|
3137
|
+
message: "(SOLIX_FAKE_CLAUDE=1) Agent View dispatch skipped"
|
|
3138
|
+
});
|
|
3139
|
+
return { ok: true };
|
|
3140
|
+
}
|
|
3141
|
+
if (!existsSync9(opts.cwd)) {
|
|
3142
|
+
this.broadcaster.broadcast({
|
|
3143
|
+
type: "toast",
|
|
3144
|
+
level: "error",
|
|
3145
|
+
message: `Agent View dispatch failed: cwd does not exist (${opts.cwd})`
|
|
3146
|
+
});
|
|
3147
|
+
return { ok: false };
|
|
3148
|
+
}
|
|
3149
|
+
const args = [];
|
|
3150
|
+
if (opts.agentName) args.push("--agent", opts.agentName);
|
|
3151
|
+
if (opts.model) args.push("--model", String(opts.model));
|
|
3152
|
+
args.push("--bg", opts.initialPrompt);
|
|
3153
|
+
let child;
|
|
3154
|
+
try {
|
|
3155
|
+
child = spawn("claude", args, {
|
|
3156
|
+
cwd: opts.cwd,
|
|
3157
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3158
|
+
detached: false
|
|
3159
|
+
});
|
|
3160
|
+
} catch (err) {
|
|
3161
|
+
this.broadcaster.broadcast({
|
|
3162
|
+
type: "toast",
|
|
3163
|
+
level: "error",
|
|
3164
|
+
message: `claude --bg spawn failed: ${err.message}`
|
|
3165
|
+
});
|
|
3166
|
+
return { ok: false };
|
|
3167
|
+
}
|
|
3168
|
+
let stdout = "";
|
|
3169
|
+
child.stdout?.setEncoding("utf8").on("data", (c) => stdout += c);
|
|
3170
|
+
child.on("exit", () => {
|
|
3171
|
+
const match = stdout.match(/backgrounded[^a-z0-9]+([a-f0-9]{6,16})/i);
|
|
3172
|
+
const shortId = match?.[1];
|
|
3173
|
+
this.broadcaster.broadcast({
|
|
3174
|
+
type: "toast",
|
|
3175
|
+
level: "info",
|
|
3176
|
+
message: shortId ? `Dispatched to Agent View \xB7 ${shortId}` : "Dispatched to Agent View"
|
|
3177
|
+
});
|
|
3178
|
+
});
|
|
3179
|
+
return { ok: true };
|
|
3180
|
+
}
|
|
3017
3181
|
sendPromptToInternal(sessionId, text) {
|
|
3018
3182
|
if (!text.trim()) return false;
|
|
3019
3183
|
const session = this.db.prepare(
|
|
@@ -3579,7 +3743,9 @@ var EventRouter = class {
|
|
|
3579
3743
|
model: opts.model,
|
|
3580
3744
|
initialPrompt: opts.initialPrompt,
|
|
3581
3745
|
worktreeBranch: opts.worktreeBranch,
|
|
3582
|
-
worktreeBaseRef: opts.worktreeBaseRef
|
|
3746
|
+
worktreeBaseRef: opts.worktreeBaseRef,
|
|
3747
|
+
useAgentView: opts.useAgentView,
|
|
3748
|
+
agentName: opts.agentName
|
|
3583
3749
|
});
|
|
3584
3750
|
}
|
|
3585
3751
|
sendPromptToSession(sessionId, text) {
|
|
@@ -3726,7 +3892,9 @@ function handleClientMessage(ctx, _ws, msg) {
|
|
|
3726
3892
|
model: msg.model,
|
|
3727
3893
|
initialPrompt: msg.initialPrompt,
|
|
3728
3894
|
worktreeBranch: msg.worktreeBranch,
|
|
3729
|
-
worktreeBaseRef: msg.worktreeBaseRef
|
|
3895
|
+
worktreeBaseRef: msg.worktreeBaseRef,
|
|
3896
|
+
useAgentView: msg.useAgentView,
|
|
3897
|
+
agentName: msg.agentName
|
|
3730
3898
|
});
|
|
3731
3899
|
break;
|
|
3732
3900
|
case "invoke_advisor":
|
|
@@ -3991,6 +4159,196 @@ ${text.slice(0, 600)}`);
|
|
|
3991
4159
|
}
|
|
3992
4160
|
};
|
|
3993
4161
|
|
|
4162
|
+
// ../server/src/state/agentview.ts
|
|
4163
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7, readdirSync as readdirSync5, statSync as statSync6, watch as watch2 } from "fs";
|
|
4164
|
+
import { homedir as homedir10 } from "os";
|
|
4165
|
+
import { join as join14 } from "path";
|
|
4166
|
+
var ROSTER_PATH = join14(homedir10(), ".claude", "daemon", "roster.json");
|
|
4167
|
+
var JOBS_DIR = join14(homedir10(), ".claude", "jobs");
|
|
4168
|
+
function mapStatus(state) {
|
|
4169
|
+
switch (state) {
|
|
4170
|
+
case "working":
|
|
4171
|
+
return "active";
|
|
4172
|
+
case "needs_input":
|
|
4173
|
+
return "awaiting_input";
|
|
4174
|
+
case "idle":
|
|
4175
|
+
return "idle";
|
|
4176
|
+
case "completed":
|
|
4177
|
+
return "terminated";
|
|
4178
|
+
case "failed":
|
|
4179
|
+
return "error";
|
|
4180
|
+
case "stopped":
|
|
4181
|
+
return "terminated";
|
|
4182
|
+
default:
|
|
4183
|
+
return "idle";
|
|
4184
|
+
}
|
|
4185
|
+
}
|
|
4186
|
+
function mapPrStatus(s) {
|
|
4187
|
+
if (!s) return void 0;
|
|
4188
|
+
if (s === "pending" || s === "success" || s === "failure" || s === "neutral")
|
|
4189
|
+
return s;
|
|
4190
|
+
return void 0;
|
|
4191
|
+
}
|
|
4192
|
+
function readRoster() {
|
|
4193
|
+
if (!existsSync11(ROSTER_PATH)) return [];
|
|
4194
|
+
try {
|
|
4195
|
+
const raw = readFileSync7(ROSTER_PATH, "utf8");
|
|
4196
|
+
const parsed = JSON.parse(raw);
|
|
4197
|
+
if (Array.isArray(parsed)) return parsed;
|
|
4198
|
+
if (parsed && Array.isArray(parsed.sessions)) return parsed.sessions;
|
|
4199
|
+
return [];
|
|
4200
|
+
} catch {
|
|
4201
|
+
return [];
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
function readJobIds() {
|
|
4205
|
+
if (!existsSync11(JOBS_DIR)) return [];
|
|
4206
|
+
try {
|
|
4207
|
+
return readdirSync5(JOBS_DIR).filter((entry) => {
|
|
4208
|
+
try {
|
|
4209
|
+
return statSync6(join14(JOBS_DIR, entry)).isDirectory();
|
|
4210
|
+
} catch {
|
|
4211
|
+
return false;
|
|
4212
|
+
}
|
|
4213
|
+
});
|
|
4214
|
+
} catch {
|
|
4215
|
+
return [];
|
|
4216
|
+
}
|
|
4217
|
+
}
|
|
4218
|
+
function readJobState(jobId) {
|
|
4219
|
+
const p = join14(JOBS_DIR, jobId, "state.json");
|
|
4220
|
+
if (!existsSync11(p)) return null;
|
|
4221
|
+
try {
|
|
4222
|
+
return JSON.parse(readFileSync7(p, "utf8"));
|
|
4223
|
+
} catch {
|
|
4224
|
+
return null;
|
|
4225
|
+
}
|
|
4226
|
+
}
|
|
4227
|
+
function syncFromDisk({ db, broadcaster }) {
|
|
4228
|
+
const roster = readRoster();
|
|
4229
|
+
const jobIds = readJobIds();
|
|
4230
|
+
const liveIds = /* @__PURE__ */ new Set();
|
|
4231
|
+
for (const e of roster) if (e.id) liveIds.add(e.id);
|
|
4232
|
+
for (const id of jobIds) liveIds.add(id);
|
|
4233
|
+
for (const agentViewId of liveIds) {
|
|
4234
|
+
const state = readJobState(agentViewId);
|
|
4235
|
+
if (!state) continue;
|
|
4236
|
+
const cwd = state.cwd ?? "";
|
|
4237
|
+
if (!cwd) continue;
|
|
4238
|
+
const solixId = `av-${agentViewId}`;
|
|
4239
|
+
const existing = getSession(db, solixId);
|
|
4240
|
+
const status = mapStatus(state.state);
|
|
4241
|
+
const summary = state.summary ?? null;
|
|
4242
|
+
const prUrl = state.pr_url ?? null;
|
|
4243
|
+
const prCheckStatus = mapPrStatus(state.pr_check_status) ?? null;
|
|
4244
|
+
if (!existing) {
|
|
4245
|
+
const project = ensureProject(db, cwd);
|
|
4246
|
+
const session = upsertSession(db, {
|
|
4247
|
+
id: solixId,
|
|
4248
|
+
pid: 0,
|
|
4249
|
+
// we don't know the pid; supervisor owns it
|
|
4250
|
+
projectId: project.id,
|
|
4251
|
+
cwd,
|
|
4252
|
+
origin: "agentview",
|
|
4253
|
+
model: state.model ?? "default",
|
|
4254
|
+
kind: "user",
|
|
4255
|
+
worktreePath: state.worktree_path ?? void 0,
|
|
4256
|
+
agentViewId,
|
|
4257
|
+
agentViewSummary: summary ?? void 0,
|
|
4258
|
+
prUrl: prUrl ?? void 0,
|
|
4259
|
+
prCheckStatus: prCheckStatus ?? void 0
|
|
4260
|
+
});
|
|
4261
|
+
const updated2 = setAgentViewFields(db, solixId, { status });
|
|
4262
|
+
broadcaster.broadcast({
|
|
4263
|
+
type: "session_upsert",
|
|
4264
|
+
session: updated2 ?? session
|
|
4265
|
+
});
|
|
4266
|
+
continue;
|
|
4267
|
+
}
|
|
4268
|
+
const changed = existing.status !== status || (existing.agentViewSummary ?? null) !== summary || (existing.prUrl ?? null) !== prUrl || (existing.prCheckStatus ?? null) !== prCheckStatus;
|
|
4269
|
+
if (!changed) continue;
|
|
4270
|
+
const updated = setAgentViewFields(db, solixId, {
|
|
4271
|
+
status,
|
|
4272
|
+
agentViewSummary: summary,
|
|
4273
|
+
prUrl,
|
|
4274
|
+
prCheckStatus
|
|
4275
|
+
});
|
|
4276
|
+
if (updated) broadcaster.broadcast({ type: "session_upsert", session: updated });
|
|
4277
|
+
}
|
|
4278
|
+
const rows = db.prepare(
|
|
4279
|
+
`SELECT id, agent_view_id FROM sessions
|
|
4280
|
+
WHERE origin = 'agentview' AND status != 'terminated'`
|
|
4281
|
+
).all();
|
|
4282
|
+
for (const r of rows) {
|
|
4283
|
+
if (r.agent_view_id && !liveIds.has(r.agent_view_id)) {
|
|
4284
|
+
const updated = setAgentViewFields(db, r.id, { status: "terminated" });
|
|
4285
|
+
if (updated)
|
|
4286
|
+
broadcaster.broadcast({ type: "session_upsert", session: updated });
|
|
4287
|
+
}
|
|
4288
|
+
}
|
|
4289
|
+
}
|
|
4290
|
+
function debounce(fn, ms) {
|
|
4291
|
+
let h = null;
|
|
4292
|
+
return () => {
|
|
4293
|
+
if (h) clearTimeout(h);
|
|
4294
|
+
h = setTimeout(() => {
|
|
4295
|
+
h = null;
|
|
4296
|
+
fn();
|
|
4297
|
+
}, ms);
|
|
4298
|
+
};
|
|
4299
|
+
}
|
|
4300
|
+
function startAgentViewBridge(opts) {
|
|
4301
|
+
const claudeRoot = join14(homedir10(), ".claude");
|
|
4302
|
+
if (!existsSync11(claudeRoot)) return () => {
|
|
4303
|
+
};
|
|
4304
|
+
const sync = () => {
|
|
4305
|
+
try {
|
|
4306
|
+
syncFromDisk(opts);
|
|
4307
|
+
} catch (err) {
|
|
4308
|
+
console.warn("[agentview] sync failed:", err.message);
|
|
4309
|
+
}
|
|
4310
|
+
};
|
|
4311
|
+
const debounced = debounce(sync, 50);
|
|
4312
|
+
sync();
|
|
4313
|
+
const watchers = [];
|
|
4314
|
+
const daemonDir = join14(homedir10(), ".claude", "daemon");
|
|
4315
|
+
if (existsSync11(daemonDir)) {
|
|
4316
|
+
try {
|
|
4317
|
+
watchers.push(watch2(daemonDir, { persistent: false }, debounced));
|
|
4318
|
+
} catch (err) {
|
|
4319
|
+
console.warn(
|
|
4320
|
+
"[agentview] could not watch daemon dir:",
|
|
4321
|
+
err.message
|
|
4322
|
+
);
|
|
4323
|
+
}
|
|
4324
|
+
}
|
|
4325
|
+
if (existsSync11(JOBS_DIR)) {
|
|
4326
|
+
try {
|
|
4327
|
+
watchers.push(watch2(JOBS_DIR, { recursive: true, persistent: false }, debounced));
|
|
4328
|
+
} catch (err) {
|
|
4329
|
+
console.warn("[agentview] recursive watch unsupported; falling back to poll");
|
|
4330
|
+
const poll = setInterval(sync, 3e3);
|
|
4331
|
+
return () => {
|
|
4332
|
+
clearInterval(poll);
|
|
4333
|
+
for (const w of watchers) {
|
|
4334
|
+
try {
|
|
4335
|
+
w.close();
|
|
4336
|
+
} catch {
|
|
4337
|
+
}
|
|
4338
|
+
}
|
|
4339
|
+
};
|
|
4340
|
+
}
|
|
4341
|
+
}
|
|
4342
|
+
return () => {
|
|
4343
|
+
for (const w of watchers) {
|
|
4344
|
+
try {
|
|
4345
|
+
w.close();
|
|
4346
|
+
} catch {
|
|
4347
|
+
}
|
|
4348
|
+
}
|
|
4349
|
+
};
|
|
4350
|
+
}
|
|
4351
|
+
|
|
3994
4352
|
// ../server/src/create.ts
|
|
3995
4353
|
async function createSolixServer(opts = {}) {
|
|
3996
4354
|
const port = opts.port ?? 4242;
|
|
@@ -4020,10 +4378,12 @@ async function createSolixServer(opts = {}) {
|
|
|
4020
4378
|
router,
|
|
4021
4379
|
broadcaster
|
|
4022
4380
|
});
|
|
4381
|
+
const stopAgentViewBridge = startAgentViewBridge({ db, broadcaster });
|
|
4023
4382
|
return {
|
|
4024
4383
|
port,
|
|
4025
4384
|
hostname,
|
|
4026
4385
|
close: () => new Promise((resolve4) => {
|
|
4386
|
+
stopAgentViewBridge();
|
|
4027
4387
|
transcripts.shutdownAll();
|
|
4028
4388
|
launcher.shutdownAll();
|
|
4029
4389
|
server.close(() => resolve4());
|
|
@@ -4071,20 +4431,20 @@ async function start(opts = {}) {
|
|
|
4071
4431
|
}
|
|
4072
4432
|
|
|
4073
4433
|
// src/uninstall.ts
|
|
4074
|
-
import { copyFileSync as copyFileSync2, existsSync as
|
|
4434
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync12, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "fs";
|
|
4075
4435
|
function uninstall() {
|
|
4076
4436
|
uninstallShim();
|
|
4077
|
-
if (
|
|
4437
|
+
if (existsSync12(CLAUDE_BACKUP)) {
|
|
4078
4438
|
copyFileSync2(CLAUDE_BACKUP, CLAUDE_SETTINGS);
|
|
4079
4439
|
console.log(`[solix] restored settings.json from backup`);
|
|
4080
4440
|
return;
|
|
4081
4441
|
}
|
|
4082
|
-
if (!
|
|
4442
|
+
if (!existsSync12(CLAUDE_SETTINGS)) {
|
|
4083
4443
|
console.log("[solix] nothing to uninstall (no settings.json found)");
|
|
4084
4444
|
return;
|
|
4085
4445
|
}
|
|
4086
4446
|
const cur = JSON.parse(
|
|
4087
|
-
|
|
4447
|
+
readFileSync8(CLAUDE_SETTINGS, "utf8")
|
|
4088
4448
|
);
|
|
4089
4449
|
if (cur.hooks) {
|
|
4090
4450
|
for (const [evt, entries] of Object.entries(cur.hooks)) {
|
|
@@ -4100,7 +4460,7 @@ function uninstall() {
|
|
|
4100
4460
|
|
|
4101
4461
|
// src/index.ts
|
|
4102
4462
|
var program = new Command();
|
|
4103
|
-
program.name("solix").description("Solix \u2014 a solar-system command center for Claude Code agents").version("1.
|
|
4463
|
+
program.name("solix").description("Solix \u2014 a solar-system command center for Claude Code agents").version("1.5.0");
|
|
4104
4464
|
program.command("start", { isDefault: true }).description("Start the Solix server and open the browser").option("-p, --port <port>", "port to listen on", (v) => parseInt(v, 10), 4242).option("--no-open", "do not open browser automatically").action(async (opts) => {
|
|
4105
4465
|
await start({ port: opts.port, noOpen: !opts.open });
|
|
4106
4466
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as c,a9 as $,j as e}from"./index-BUrmJuUb.js";function H(s,i){const a=new Map(s.advisors.map(t=>[t.role,t])),n=new Map(i.advisors.map(t=>[t.role,t])),h=[...n.keys()].filter(t=>!a.has(t)),v=[...a.keys()].filter(t=>!n.has(t)),j=[...n.keys()].filter(t=>a.has(t)).map(t=>({role:t,from:a.get(t).pinned,to:n.get(t).pinned})).filter(t=>t.from!==t.to),x=new Set(s.skills.map(t=>t.id)),m=new Set(i.skills.map(t=>t.id)),r=[...m].filter(t=>!x.has(t)),b=[...x].filter(t=>!m.has(t)),d=new Set(s.projects.map(t=>t.name)),f=new Set(i.projects.map(t=>t.name)),u=[...f].filter(t=>!d.has(t)),g=[...d].filter(t=>!f.has(t));return{advisors:{added:h.sort(),removed:v.sort(),pinChanged:j.sort((t,S)=>t.role.localeCompare(S.role))},skills:{added:r.sort(),removed:b.sort()},projects:{added:u.sort(),removed:g.sort()}}}function W({open:s,onClose:i}){const[a,n]=c.useState("share"),[h,v]=c.useState("My Galaxy"),[j,x]=c.useState(""),[m,r]=c.useState(""),[b,d]=c.useState(!1),[f,u]=c.useState(null),[g,t]=c.useState(null),S=$(o=>Object.keys(o.sessions).length),I=$(o=>Object.values(o.advisors).filter(y=>y.enabled).length),l=$(o=>Object.keys(o.skills).length);if(!s)return null;const p=async()=>{d(!0),u(null);try{const o=new URLSearchParams({name:h}),y=await fetch(`/api/galaxy/export?${o.toString()}`);if(!y.ok)throw new Error(`HTTP ${y.status}`);const N=await y.json(),C=new Blob([JSON.stringify(N,null,2)],{type:"application/json"}),w=URL.createObjectURL(C),k=document.createElement("a");k.href=w,k.download=`${h.toLowerCase().replace(/\s+/g,"-")}.galaxy.json`,k.click(),URL.revokeObjectURL(w),u("Downloaded.")}catch(o){u(`Export failed: ${String(o)}`)}finally{d(!1)}},U=async o=>{d(!0),u(null);try{const N=await(await fetch("/api/galaxy/import",{method:"POST",headers:{"Content-Type":"application/json"},body:o})).json();N.ok?(u(`Imported: ${N.advisorsEnabled} enabled, ${N.advisorsDisabled} disabled, ${N.projectsHinted} projects.`),x(""),r("")):u(`Import failed: ${N.error??"unknown"}`)}catch(y){u(`Import failed: ${String(y)}`)}finally{d(!1)}},E=async(o,y,N)=>{u(null);let C,w=N;if(w)try{const k=await fetch("/api/galaxy/export?preview=1");if(k.ok){const B=await k.json();C=H(B,w)}}catch{}t({body:o,label:y,diff:C,manifest:w})},O=()=>{let o;try{o=JSON.parse(j)}catch{u("Could not parse JSON.");return}E(j,"pasted manifest",o)},D=()=>{E(JSON.stringify({url:m}),`URL: ${m}`,void 0)},_=()=>{if(!g)return;const o=g.body;t(null),U(o)},J=()=>{t(null)};return e.jsxs("div",{className:"absolute top-0 right-0 h-full w-full sm:w-[480px] bg-solix-panel border-l border-solix-border backdrop-blur-md flex flex-col z-30",children:[e.jsxs("div",{className:"px-4 py-3 border-b border-solix-border flex items-start justify-between",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-xs uppercase tracking-wide text-solix-accent",children:"Galaxy"}),e.jsx("div",{className:"text-lg font-semibold",children:"Share your space"}),e.jsxs("div",{className:"text-xs text-slate-400 mt-0.5",children:[I," advisors · ",l," skills ·"," ",S," sessions"]})]}),e.jsx("button",{onClick:i,className:"text-slate-400 hover:text-slate-100",children:"✕"})]}),e.jsxs("div",{className:"flex border-b border-solix-border text-xs",children:[e.jsx(P,{active:a==="share",onClick:()=>n("share"),children:"Sharing"}),e.jsx(P,{active:a==="versions",onClick:()=>n("versions"),children:"Versions"}),e.jsx(P,{active:a==="audit",onClick:()=>n("audit"),children:"Audit"})]}),a==="audit"?e.jsx(G,{open:s}):a==="versions"?e.jsx(V,{open:s}):e.jsxs("div",{className:"flex-1 overflow-y-auto p-4 space-y-6",children:[e.jsxs("section",{children:[e.jsx("div",{className:"text-xs uppercase tracking-wide text-slate-400 mb-2",children:"Export"}),e.jsx("input",{value:h,onChange:o=>v(o.target.value),placeholder:"Galaxy name",className:"w-full text-sm bg-black/40 border border-solix-border rounded p-2 text-slate-100 placeholder-slate-600 focus:outline-none focus:border-solix-accent"}),e.jsx("button",{onClick:()=>void p(),disabled:b,className:"mt-2 w-full py-2 rounded bg-solix-accent/20 border border-solix-accent text-solix-accent text-sm hover:bg-solix-accent/30 disabled:opacity-50",children:"Download manifest (.galaxy.json)"})]}),e.jsxs("section",{children:[e.jsx("div",{className:"text-xs uppercase tracking-wide text-slate-400 mb-2",children:"Import from URL"}),e.jsx("input",{value:m,onChange:o=>r(o.target.value),placeholder:"https://… or local server URL",className:"w-full text-sm bg-black/40 border border-solix-border rounded p-2 text-slate-100 placeholder-slate-600 focus:outline-none focus:border-solix-accent"}),e.jsx("button",{onClick:D,disabled:b||!m.trim(),className:"mt-2 w-full py-2 rounded bg-cyan-500/15 border border-cyan-400/40 text-cyan-200 text-sm hover:bg-cyan-500/25 disabled:opacity-50",children:"Pull and import"})]}),e.jsxs("section",{children:[e.jsx("div",{className:"text-xs uppercase tracking-wide text-slate-400 mb-2",children:"Import from JSON"}),e.jsx("textarea",{value:j,onChange:o=>x(o.target.value),placeholder:"Paste a galaxy manifest JSON here…",rows:10,className:"w-full text-xs bg-black/40 border border-solix-border rounded p-2 text-slate-100 placeholder-slate-600 focus:outline-none focus:border-solix-accent font-mono resize-none"}),e.jsx("button",{onClick:O,disabled:b||!j.trim(),className:"mt-2 w-full py-2 rounded bg-cyan-500/15 border border-cyan-400/40 text-cyan-200 text-sm hover:bg-cyan-500/25 disabled:opacity-50",children:"Apply manifest"})]}),g&&e.jsx(K,{label:g.label,diff:g.diff,manifest:g.manifest,busy:b,onConfirm:_,onCancel:J}),f&&e.jsx("div",{className:"text-xs text-slate-300 border border-solix-border rounded p-2 bg-black/30",children:f})]}),e.jsx("div",{className:"px-4 py-3 border-t border-solix-border text-xs text-slate-500",children:a==="audit"?"Append-only history. Read-only.":a==="versions"?"Each export snapshots a version. Identical re-exports are deduped.":"Imports never spawn pinned advisors or run shell commands. You're in control."})]})}function P({active:s,onClick:i,children:a}){return e.jsx("button",{onClick:i,className:`flex-1 px-3 py-2 ${s?"text-solix-accent border-b-2 border-solix-accent":"text-slate-400 hover:text-slate-200 border-b-2 border-transparent"}`,children:a})}const F=["permission_approved","permission_denied","advisor_invoked","advisor_pinned","advisor_unpinned","galaxy_imported"];function G({open:s}){const[i,a]=c.useState([]),[n,h]=c.useState("all"),[v,j]=c.useState(!1),[x,m]=c.useState(null);return c.useEffect(()=>{if(!s)return;let r=!1;j(!0),m(null);const b=`/api/audit${n==="all"?"":`?kind=${n}`}`;return fetch(b).then(d=>d.ok?d.json():Promise.reject(new Error(`HTTP ${d.status}`))).then(d=>{r||a(d)}).catch(d=>{r||m(d.message)}).finally(()=>{r||j(!1)}),()=>{r=!0}},[s,n]),e.jsxs("div",{className:"flex-1 overflow-y-auto p-4 space-y-3",children:[e.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap",children:[e.jsx(T,{label:"all",active:n==="all",onClick:()=>h("all")}),F.map(r=>e.jsx(T,{label:A(r),active:n===r,onClick:()=>h(r)},r))]}),v&&e.jsx("div",{className:"text-xs text-slate-500 italic",children:"Loading…"}),x&&e.jsxs("div",{className:"text-xs text-solix-danger italic",children:["Could not load audit events: ",x]}),!v&&i.length===0&&e.jsx("div",{className:"text-xs text-slate-500 italic",children:"No audit events yet. Approve a permission or invoke an advisor and they'll start appearing here."}),e.jsx("ul",{className:"space-y-1.5",children:i.map(r=>e.jsxs("li",{className:"rounded border border-solix-border bg-black/20 p-2",children:[e.jsxs("div",{className:"flex items-center justify-between text-[10px]",children:[e.jsx("span",{className:`uppercase tracking-wide ${M(r.kind)}`,children:A(r.kind)}),e.jsx("span",{className:"text-slate-500 font-mono",children:new Date(r.ts).toLocaleString("en-US",{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1,month:"short",day:"numeric"})})]}),e.jsx("div",{className:"text-[12px] text-slate-100 mt-1 leading-snug",children:r.summary})]},r.id))})]})}function T({label:s,active:i,onClick:a}){return e.jsx("button",{onClick:a,className:`text-[10px] px-2 py-0.5 rounded border ${i?"bg-solix-accent/15 border-solix-accent text-solix-accent":"border-solix-border text-slate-400 hover:text-slate-200"}`,children:s})}function A(s){return s==="all"?"all":s.replace(/_/g," ")}function M(s){return s==="permission_approved"?"text-solix-ok":s==="permission_denied"?"text-solix-danger":s==="galaxy_imported"?"text-cyan-300":s.startsWith("advisor_")?"text-amber-300":"text-slate-300"}function V({open:s}){const[i,a]=c.useState([]),[n,h]=c.useState(!1),[v,j]=c.useState(null),[x,m]=c.useState(null),[r,b]=c.useState(null),[d,f]=c.useState(null),[u,g]=c.useState(!1);c.useEffect(()=>{if(!s)return;let l=!1;return h(!0),fetch("/api/galaxy/versions").then(p=>p.ok?p.json():Promise.reject(new Error(`HTTP ${p.status}`))).then(p=>{l||a(p)}).catch(p=>{l||j(p.message)}).finally(()=>{l||h(!1)}),()=>{l=!0}},[s]),c.useEffect(()=>{if(!x||!r){f(null);return}if(x===r){f(null);return}let l=!1;return g(!0),fetch(`/api/galaxy/diff?from=${x}&to=${r}`).then(p=>p.ok?p.json():Promise.reject(new Error(`HTTP ${p.status}`))).then(p=>{l||f(p)}).catch(()=>{l||f(null)}).finally(()=>{l||g(!1)}),()=>{l=!0}},[x,r]);const t=l=>{x?!r&&l!==x?b(l):(m(l),b(null),f(null)):m(l)},S=()=>{m(null),b(null),f(null)},I=l=>l.id===x?"from":l.id===r?"to":null;return e.jsxs("div",{className:"flex-1 overflow-y-auto p-4 space-y-3",children:[(x||r)&&e.jsxs("div",{className:"flex items-center justify-between text-[11px] text-slate-400",children:[e.jsxs("div",{children:[x&&!r&&"Pick a second version to diff…",x&&r&&u&&"Computing diff…",x&&r&&!u&&d&&e.jsxs(e.Fragment,{children:["v",d.from.ordinal," → v",d.to.ordinal]})]}),e.jsx("button",{onClick:S,className:"text-slate-500 hover:text-slate-100",children:"clear"})]}),d&&e.jsx(R,{diff:d.diff}),n&&e.jsx("div",{className:"text-xs text-slate-500 italic",children:"Loading…"}),v&&e.jsxs("div",{className:"text-xs text-solix-danger italic",children:["Could not load versions: ",v]}),!n&&i.length===0&&e.jsx("div",{className:"text-xs text-slate-500 italic",children:'No versions yet. Hit "Download manifest" on the Sharing tab to create one.'}),e.jsx("ul",{className:"space-y-1.5",children:i.map(l=>{const p=I(l);return e.jsx("li",{children:e.jsxs("button",{onClick:()=>t(l.id),className:`w-full text-left rounded border p-2 ${p==="from"?"border-solix-accent bg-solix-accent/10":p==="to"?"border-cyan-400 bg-cyan-400/10":"border-solix-border bg-black/20 hover:bg-solix-border/30"}`,children:[e.jsxs("div",{className:"flex items-center justify-between text-[10px]",children:[e.jsxs("span",{className:"uppercase tracking-wide text-slate-400",children:["v",l.ordinal," · ",l.name]}),e.jsx("span",{className:"text-slate-500 font-mono",children:new Date(l.ts).toLocaleString("en-US",{hour:"2-digit",minute:"2-digit",month:"short",day:"numeric"})})]}),e.jsxs("div",{className:"text-[11px] text-slate-300 mt-1",children:[l.manifest.advisors.length," advisors ·"," ",l.manifest.skills.length," skills ·"," ",l.manifest.projects.length," projects",p&&e.jsxs("span",{className:"ml-2 text-[9px] uppercase tracking-wider text-slate-400",children:["[",p,"]"]})]})]})},l.id)})})]})}function R({diff:s}){return s.advisors.added.length===0&&s.advisors.removed.length===0&&s.advisors.pinChanged.length===0&&s.skills.added.length===0&&s.skills.removed.length===0&&s.projects.added.length===0&&s.projects.removed.length===0?e.jsx("div",{className:"text-xs text-slate-500 italic border border-solix-border rounded p-2 bg-black/20",children:"No changes between these versions."}):e.jsxs("div",{className:"rounded border border-solix-border bg-black/30 p-2 space-y-2 text-xs",children:[e.jsx(L,{label:"Advisors",added:s.advisors.added,removed:s.advisors.removed}),s.advisors.pinChanged.length>0&&e.jsxs("div",{children:[e.jsx("div",{className:"text-[10px] uppercase tracking-wide text-slate-500",children:"Advisor pin changes"}),e.jsx("ul",{className:"mt-1 space-y-0.5",children:s.advisors.pinChanged.map(a=>e.jsxs("li",{className:"text-slate-200",children:[e.jsx("span",{className:"font-mono",children:a.role}),":"," ",a.from?"pinned":"unpinned"," →"," ",a.to?"pinned":"unpinned"]},a.role))})]}),e.jsx(L,{label:"Skills",added:s.skills.added,removed:s.skills.removed}),e.jsx(L,{label:"Projects",added:s.projects.added,removed:s.projects.removed})]})}function K({label:s,diff:i,manifest:a,busy:n,onConfirm:h,onCancel:v}){return e.jsxs("div",{className:"rounded border border-amber-300/60 bg-amber-500/10 p-3 space-y-2",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("div",{className:"text-[11px] uppercase tracking-wide text-amber-200",children:"confirm import"}),e.jsx("div",{className:"text-[10px] text-slate-400 font-mono truncate max-w-[55%]",children:s})]}),a&&e.jsxs("div",{className:"text-xs text-slate-200",children:[e.jsx("span",{className:"font-semibold",children:a.name}),a.author&&e.jsxs("span",{className:"text-slate-400",children:[" · by ",a.author]})]}),i?e.jsx(R,{diff:i}):a?e.jsx("div",{className:"text-xs text-slate-400 italic",children:"Could not compute a diff against the current galaxy. Apply will still proceed if you confirm."}):e.jsx("div",{className:"text-xs text-slate-300",children:"Solix will fetch the manifest from this URL and apply it. Diff preview is only available for pasted JSON."}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx("button",{onClick:h,disabled:n,className:"flex-1 py-1.5 rounded bg-amber-500/20 border border-amber-300 text-amber-100 text-xs hover:bg-amber-500/30 disabled:opacity-50",children:"Apply"}),e.jsx("button",{onClick:v,disabled:n,className:"px-3 py-1.5 rounded border border-solix-border text-slate-300 text-xs hover:text-white disabled:opacity-50",children:"Cancel"})]})]})}function L({label:s,added:i,removed:a}){return i.length===0&&a.length===0?null:e.jsxs("div",{children:[e.jsx("div",{className:"text-[10px] uppercase tracking-wide text-slate-500",children:s}),e.jsxs("ul",{className:"mt-1 space-y-0.5",children:[i.map(n=>e.jsxs("li",{className:"text-solix-ok",children:["+ ",n]},`+${n}`)),a.map(n=>e.jsxs("li",{className:"text-solix-danger",children:["− ",n]},`-${n}`))]})]})}export{W as GalaxyPanel};
|
|
1
|
+
import{r as c,a9 as $,j as e}from"./index-2P2elsQX.js";function H(s,i){const a=new Map(s.advisors.map(t=>[t.role,t])),n=new Map(i.advisors.map(t=>[t.role,t])),h=[...n.keys()].filter(t=>!a.has(t)),v=[...a.keys()].filter(t=>!n.has(t)),j=[...n.keys()].filter(t=>a.has(t)).map(t=>({role:t,from:a.get(t).pinned,to:n.get(t).pinned})).filter(t=>t.from!==t.to),x=new Set(s.skills.map(t=>t.id)),m=new Set(i.skills.map(t=>t.id)),r=[...m].filter(t=>!x.has(t)),b=[...x].filter(t=>!m.has(t)),d=new Set(s.projects.map(t=>t.name)),f=new Set(i.projects.map(t=>t.name)),u=[...f].filter(t=>!d.has(t)),g=[...d].filter(t=>!f.has(t));return{advisors:{added:h.sort(),removed:v.sort(),pinChanged:j.sort((t,S)=>t.role.localeCompare(S.role))},skills:{added:r.sort(),removed:b.sort()},projects:{added:u.sort(),removed:g.sort()}}}function W({open:s,onClose:i}){const[a,n]=c.useState("share"),[h,v]=c.useState("My Galaxy"),[j,x]=c.useState(""),[m,r]=c.useState(""),[b,d]=c.useState(!1),[f,u]=c.useState(null),[g,t]=c.useState(null),S=$(o=>Object.keys(o.sessions).length),I=$(o=>Object.values(o.advisors).filter(y=>y.enabled).length),l=$(o=>Object.keys(o.skills).length);if(!s)return null;const p=async()=>{d(!0),u(null);try{const o=new URLSearchParams({name:h}),y=await fetch(`/api/galaxy/export?${o.toString()}`);if(!y.ok)throw new Error(`HTTP ${y.status}`);const N=await y.json(),C=new Blob([JSON.stringify(N,null,2)],{type:"application/json"}),w=URL.createObjectURL(C),k=document.createElement("a");k.href=w,k.download=`${h.toLowerCase().replace(/\s+/g,"-")}.galaxy.json`,k.click(),URL.revokeObjectURL(w),u("Downloaded.")}catch(o){u(`Export failed: ${String(o)}`)}finally{d(!1)}},U=async o=>{d(!0),u(null);try{const N=await(await fetch("/api/galaxy/import",{method:"POST",headers:{"Content-Type":"application/json"},body:o})).json();N.ok?(u(`Imported: ${N.advisorsEnabled} enabled, ${N.advisorsDisabled} disabled, ${N.projectsHinted} projects.`),x(""),r("")):u(`Import failed: ${N.error??"unknown"}`)}catch(y){u(`Import failed: ${String(y)}`)}finally{d(!1)}},E=async(o,y,N)=>{u(null);let C,w=N;if(w)try{const k=await fetch("/api/galaxy/export?preview=1");if(k.ok){const B=await k.json();C=H(B,w)}}catch{}t({body:o,label:y,diff:C,manifest:w})},O=()=>{let o;try{o=JSON.parse(j)}catch{u("Could not parse JSON.");return}E(j,"pasted manifest",o)},D=()=>{E(JSON.stringify({url:m}),`URL: ${m}`,void 0)},_=()=>{if(!g)return;const o=g.body;t(null),U(o)},J=()=>{t(null)};return e.jsxs("div",{className:"absolute top-0 right-0 h-full w-full sm:w-[480px] bg-solix-panel border-l border-solix-border backdrop-blur-md flex flex-col z-30",children:[e.jsxs("div",{className:"px-4 py-3 border-b border-solix-border flex items-start justify-between",children:[e.jsxs("div",{children:[e.jsx("div",{className:"text-xs uppercase tracking-wide text-solix-accent",children:"Galaxy"}),e.jsx("div",{className:"text-lg font-semibold",children:"Share your space"}),e.jsxs("div",{className:"text-xs text-slate-400 mt-0.5",children:[I," advisors · ",l," skills ·"," ",S," sessions"]})]}),e.jsx("button",{onClick:i,className:"text-slate-400 hover:text-slate-100",children:"✕"})]}),e.jsxs("div",{className:"flex border-b border-solix-border text-xs",children:[e.jsx(P,{active:a==="share",onClick:()=>n("share"),children:"Sharing"}),e.jsx(P,{active:a==="versions",onClick:()=>n("versions"),children:"Versions"}),e.jsx(P,{active:a==="audit",onClick:()=>n("audit"),children:"Audit"})]}),a==="audit"?e.jsx(G,{open:s}):a==="versions"?e.jsx(V,{open:s}):e.jsxs("div",{className:"flex-1 overflow-y-auto p-4 space-y-6",children:[e.jsxs("section",{children:[e.jsx("div",{className:"text-xs uppercase tracking-wide text-slate-400 mb-2",children:"Export"}),e.jsx("input",{value:h,onChange:o=>v(o.target.value),placeholder:"Galaxy name",className:"w-full text-sm bg-black/40 border border-solix-border rounded p-2 text-slate-100 placeholder-slate-600 focus:outline-none focus:border-solix-accent"}),e.jsx("button",{onClick:()=>void p(),disabled:b,className:"mt-2 w-full py-2 rounded bg-solix-accent/20 border border-solix-accent text-solix-accent text-sm hover:bg-solix-accent/30 disabled:opacity-50",children:"Download manifest (.galaxy.json)"})]}),e.jsxs("section",{children:[e.jsx("div",{className:"text-xs uppercase tracking-wide text-slate-400 mb-2",children:"Import from URL"}),e.jsx("input",{value:m,onChange:o=>r(o.target.value),placeholder:"https://… or local server URL",className:"w-full text-sm bg-black/40 border border-solix-border rounded p-2 text-slate-100 placeholder-slate-600 focus:outline-none focus:border-solix-accent"}),e.jsx("button",{onClick:D,disabled:b||!m.trim(),className:"mt-2 w-full py-2 rounded bg-cyan-500/15 border border-cyan-400/40 text-cyan-200 text-sm hover:bg-cyan-500/25 disabled:opacity-50",children:"Pull and import"})]}),e.jsxs("section",{children:[e.jsx("div",{className:"text-xs uppercase tracking-wide text-slate-400 mb-2",children:"Import from JSON"}),e.jsx("textarea",{value:j,onChange:o=>x(o.target.value),placeholder:"Paste a galaxy manifest JSON here…",rows:10,className:"w-full text-xs bg-black/40 border border-solix-border rounded p-2 text-slate-100 placeholder-slate-600 focus:outline-none focus:border-solix-accent font-mono resize-none"}),e.jsx("button",{onClick:O,disabled:b||!j.trim(),className:"mt-2 w-full py-2 rounded bg-cyan-500/15 border border-cyan-400/40 text-cyan-200 text-sm hover:bg-cyan-500/25 disabled:opacity-50",children:"Apply manifest"})]}),g&&e.jsx(K,{label:g.label,diff:g.diff,manifest:g.manifest,busy:b,onConfirm:_,onCancel:J}),f&&e.jsx("div",{className:"text-xs text-slate-300 border border-solix-border rounded p-2 bg-black/30",children:f})]}),e.jsx("div",{className:"px-4 py-3 border-t border-solix-border text-xs text-slate-500",children:a==="audit"?"Append-only history. Read-only.":a==="versions"?"Each export snapshots a version. Identical re-exports are deduped.":"Imports never spawn pinned advisors or run shell commands. You're in control."})]})}function P({active:s,onClick:i,children:a}){return e.jsx("button",{onClick:i,className:`flex-1 px-3 py-2 ${s?"text-solix-accent border-b-2 border-solix-accent":"text-slate-400 hover:text-slate-200 border-b-2 border-transparent"}`,children:a})}const F=["permission_approved","permission_denied","advisor_invoked","advisor_pinned","advisor_unpinned","galaxy_imported"];function G({open:s}){const[i,a]=c.useState([]),[n,h]=c.useState("all"),[v,j]=c.useState(!1),[x,m]=c.useState(null);return c.useEffect(()=>{if(!s)return;let r=!1;j(!0),m(null);const b=`/api/audit${n==="all"?"":`?kind=${n}`}`;return fetch(b).then(d=>d.ok?d.json():Promise.reject(new Error(`HTTP ${d.status}`))).then(d=>{r||a(d)}).catch(d=>{r||m(d.message)}).finally(()=>{r||j(!1)}),()=>{r=!0}},[s,n]),e.jsxs("div",{className:"flex-1 overflow-y-auto p-4 space-y-3",children:[e.jsxs("div",{className:"flex items-center gap-1.5 flex-wrap",children:[e.jsx(T,{label:"all",active:n==="all",onClick:()=>h("all")}),F.map(r=>e.jsx(T,{label:A(r),active:n===r,onClick:()=>h(r)},r))]}),v&&e.jsx("div",{className:"text-xs text-slate-500 italic",children:"Loading…"}),x&&e.jsxs("div",{className:"text-xs text-solix-danger italic",children:["Could not load audit events: ",x]}),!v&&i.length===0&&e.jsx("div",{className:"text-xs text-slate-500 italic",children:"No audit events yet. Approve a permission or invoke an advisor and they'll start appearing here."}),e.jsx("ul",{className:"space-y-1.5",children:i.map(r=>e.jsxs("li",{className:"rounded border border-solix-border bg-black/20 p-2",children:[e.jsxs("div",{className:"flex items-center justify-between text-[10px]",children:[e.jsx("span",{className:`uppercase tracking-wide ${M(r.kind)}`,children:A(r.kind)}),e.jsx("span",{className:"text-slate-500 font-mono",children:new Date(r.ts).toLocaleString("en-US",{hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1,month:"short",day:"numeric"})})]}),e.jsx("div",{className:"text-[12px] text-slate-100 mt-1 leading-snug",children:r.summary})]},r.id))})]})}function T({label:s,active:i,onClick:a}){return e.jsx("button",{onClick:a,className:`text-[10px] px-2 py-0.5 rounded border ${i?"bg-solix-accent/15 border-solix-accent text-solix-accent":"border-solix-border text-slate-400 hover:text-slate-200"}`,children:s})}function A(s){return s==="all"?"all":s.replace(/_/g," ")}function M(s){return s==="permission_approved"?"text-solix-ok":s==="permission_denied"?"text-solix-danger":s==="galaxy_imported"?"text-cyan-300":s.startsWith("advisor_")?"text-amber-300":"text-slate-300"}function V({open:s}){const[i,a]=c.useState([]),[n,h]=c.useState(!1),[v,j]=c.useState(null),[x,m]=c.useState(null),[r,b]=c.useState(null),[d,f]=c.useState(null),[u,g]=c.useState(!1);c.useEffect(()=>{if(!s)return;let l=!1;return h(!0),fetch("/api/galaxy/versions").then(p=>p.ok?p.json():Promise.reject(new Error(`HTTP ${p.status}`))).then(p=>{l||a(p)}).catch(p=>{l||j(p.message)}).finally(()=>{l||h(!1)}),()=>{l=!0}},[s]),c.useEffect(()=>{if(!x||!r){f(null);return}if(x===r){f(null);return}let l=!1;return g(!0),fetch(`/api/galaxy/diff?from=${x}&to=${r}`).then(p=>p.ok?p.json():Promise.reject(new Error(`HTTP ${p.status}`))).then(p=>{l||f(p)}).catch(()=>{l||f(null)}).finally(()=>{l||g(!1)}),()=>{l=!0}},[x,r]);const t=l=>{x?!r&&l!==x?b(l):(m(l),b(null),f(null)):m(l)},S=()=>{m(null),b(null),f(null)},I=l=>l.id===x?"from":l.id===r?"to":null;return e.jsxs("div",{className:"flex-1 overflow-y-auto p-4 space-y-3",children:[(x||r)&&e.jsxs("div",{className:"flex items-center justify-between text-[11px] text-slate-400",children:[e.jsxs("div",{children:[x&&!r&&"Pick a second version to diff…",x&&r&&u&&"Computing diff…",x&&r&&!u&&d&&e.jsxs(e.Fragment,{children:["v",d.from.ordinal," → v",d.to.ordinal]})]}),e.jsx("button",{onClick:S,className:"text-slate-500 hover:text-slate-100",children:"clear"})]}),d&&e.jsx(R,{diff:d.diff}),n&&e.jsx("div",{className:"text-xs text-slate-500 italic",children:"Loading…"}),v&&e.jsxs("div",{className:"text-xs text-solix-danger italic",children:["Could not load versions: ",v]}),!n&&i.length===0&&e.jsx("div",{className:"text-xs text-slate-500 italic",children:'No versions yet. Hit "Download manifest" on the Sharing tab to create one.'}),e.jsx("ul",{className:"space-y-1.5",children:i.map(l=>{const p=I(l);return e.jsx("li",{children:e.jsxs("button",{onClick:()=>t(l.id),className:`w-full text-left rounded border p-2 ${p==="from"?"border-solix-accent bg-solix-accent/10":p==="to"?"border-cyan-400 bg-cyan-400/10":"border-solix-border bg-black/20 hover:bg-solix-border/30"}`,children:[e.jsxs("div",{className:"flex items-center justify-between text-[10px]",children:[e.jsxs("span",{className:"uppercase tracking-wide text-slate-400",children:["v",l.ordinal," · ",l.name]}),e.jsx("span",{className:"text-slate-500 font-mono",children:new Date(l.ts).toLocaleString("en-US",{hour:"2-digit",minute:"2-digit",month:"short",day:"numeric"})})]}),e.jsxs("div",{className:"text-[11px] text-slate-300 mt-1",children:[l.manifest.advisors.length," advisors ·"," ",l.manifest.skills.length," skills ·"," ",l.manifest.projects.length," projects",p&&e.jsxs("span",{className:"ml-2 text-[9px] uppercase tracking-wider text-slate-400",children:["[",p,"]"]})]})]})},l.id)})})]})}function R({diff:s}){return s.advisors.added.length===0&&s.advisors.removed.length===0&&s.advisors.pinChanged.length===0&&s.skills.added.length===0&&s.skills.removed.length===0&&s.projects.added.length===0&&s.projects.removed.length===0?e.jsx("div",{className:"text-xs text-slate-500 italic border border-solix-border rounded p-2 bg-black/20",children:"No changes between these versions."}):e.jsxs("div",{className:"rounded border border-solix-border bg-black/30 p-2 space-y-2 text-xs",children:[e.jsx(L,{label:"Advisors",added:s.advisors.added,removed:s.advisors.removed}),s.advisors.pinChanged.length>0&&e.jsxs("div",{children:[e.jsx("div",{className:"text-[10px] uppercase tracking-wide text-slate-500",children:"Advisor pin changes"}),e.jsx("ul",{className:"mt-1 space-y-0.5",children:s.advisors.pinChanged.map(a=>e.jsxs("li",{className:"text-slate-200",children:[e.jsx("span",{className:"font-mono",children:a.role}),":"," ",a.from?"pinned":"unpinned"," →"," ",a.to?"pinned":"unpinned"]},a.role))})]}),e.jsx(L,{label:"Skills",added:s.skills.added,removed:s.skills.removed}),e.jsx(L,{label:"Projects",added:s.projects.added,removed:s.projects.removed})]})}function K({label:s,diff:i,manifest:a,busy:n,onConfirm:h,onCancel:v}){return e.jsxs("div",{className:"rounded border border-amber-300/60 bg-amber-500/10 p-3 space-y-2",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("div",{className:"text-[11px] uppercase tracking-wide text-amber-200",children:"confirm import"}),e.jsx("div",{className:"text-[10px] text-slate-400 font-mono truncate max-w-[55%]",children:s})]}),a&&e.jsxs("div",{className:"text-xs text-slate-200",children:[e.jsx("span",{className:"font-semibold",children:a.name}),a.author&&e.jsxs("span",{className:"text-slate-400",children:[" · by ",a.author]})]}),i?e.jsx(R,{diff:i}):a?e.jsx("div",{className:"text-xs text-slate-400 italic",children:"Could not compute a diff against the current galaxy. Apply will still proceed if you confirm."}):e.jsx("div",{className:"text-xs text-slate-300",children:"Solix will fetch the manifest from this URL and apply it. Diff preview is only available for pasted JSON."}),e.jsxs("div",{className:"flex gap-2",children:[e.jsx("button",{onClick:h,disabled:n,className:"flex-1 py-1.5 rounded bg-amber-500/20 border border-amber-300 text-amber-100 text-xs hover:bg-amber-500/30 disabled:opacity-50",children:"Apply"}),e.jsx("button",{onClick:v,disabled:n,className:"px-3 py-1.5 rounded border border-solix-border text-slate-300 text-xs hover:text-white disabled:opacity-50",children:"Cancel"})]})]})}function L({label:s,added:i,removed:a}){return i.length===0&&a.length===0?null:e.jsxs("div",{children:[e.jsx("div",{className:"text-[10px] uppercase tracking-wide text-slate-500",children:s}),e.jsxs("ul",{className:"mt-1 space-y-0.5",children:[i.map(n=>e.jsxs("li",{className:"text-solix-ok",children:["+ ",n]},`+${n}`)),a.map(n=>e.jsxs("li",{className:"text-solix-danger",children:["− ",n]},`-${n}`))]})]})}export{W as GalaxyPanel};
|