@shmulikdav/solix 1.2.0 → 1.3.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 +126 -46
- package/dist/web/assets/GalaxyPanel-B1Y8EmJ-.js +1 -0
- package/dist/web/assets/Scene-BRE59mDX.js +292 -0
- package/dist/web/assets/TimelineDrawer-D164Rv-U.js +1 -0
- package/dist/web/assets/index-BB6Hys2b.js +3900 -0
- package/dist/web/index.html +1 -1
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/dist/web/assets/index-k3F_r2cs.js +0 -4191
package/dist/index.js
CHANGED
|
@@ -317,6 +317,32 @@ async function probeHealth(port) {
|
|
|
317
317
|
};
|
|
318
318
|
}
|
|
319
319
|
}
|
|
320
|
+
async function probeWrappers(port) {
|
|
321
|
+
try {
|
|
322
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/wrappers`, {
|
|
323
|
+
signal: AbortSignal.timeout(800)
|
|
324
|
+
});
|
|
325
|
+
if (!res.ok) {
|
|
326
|
+
return {
|
|
327
|
+
ok: true,
|
|
328
|
+
label: "Active solix run wrappers",
|
|
329
|
+
detail: "server too old to report (pre-1.2.1)"
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
const records = await res.json();
|
|
333
|
+
return {
|
|
334
|
+
ok: true,
|
|
335
|
+
label: "Active solix run wrappers",
|
|
336
|
+
detail: records.length === 0 ? "none registered" : `${records.length} active`
|
|
337
|
+
};
|
|
338
|
+
} catch {
|
|
339
|
+
return {
|
|
340
|
+
ok: true,
|
|
341
|
+
label: "Active solix run wrappers",
|
|
342
|
+
detail: "unknown \u2014 server unreachable"
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
}
|
|
320
346
|
async function doctor() {
|
|
321
347
|
const port = Number(process.env.SOLIX_PORT ?? 4242);
|
|
322
348
|
const checks = [];
|
|
@@ -398,6 +424,7 @@ async function doctor() {
|
|
|
398
424
|
detail: skillCount > 0 ? `${skillCount} skills in ${SOLIX_SKILLS_DIR}` : "none"
|
|
399
425
|
});
|
|
400
426
|
checks.push(await probeHealth(port));
|
|
427
|
+
checks.push(await probeWrappers(port));
|
|
401
428
|
console.log("\nSolix Diagnostics\n");
|
|
402
429
|
let allOk = true;
|
|
403
430
|
for (const c of checks) {
|
|
@@ -1155,8 +1182,8 @@ function getDb() {
|
|
|
1155
1182
|
}
|
|
1156
1183
|
|
|
1157
1184
|
// ../server/src/http.ts
|
|
1158
|
-
import { existsSync as
|
|
1159
|
-
import { dirname as dirname4, extname, join as
|
|
1185
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
|
|
1186
|
+
import { dirname as dirname4, extname, join as join11, resolve as resolve3 } from "path";
|
|
1160
1187
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
1161
1188
|
import { spawnSync } from "child_process";
|
|
1162
1189
|
import { Hono } from "hono";
|
|
@@ -1329,6 +1356,13 @@ function setSessionMission(db, sessionId, missionId) {
|
|
|
1329
1356
|
).run(missionId, ts2, sessionId);
|
|
1330
1357
|
return getSession(db, sessionId);
|
|
1331
1358
|
}
|
|
1359
|
+
function clearSessionWrapper(db, sessionId) {
|
|
1360
|
+
const ts2 = now();
|
|
1361
|
+
db.prepare(
|
|
1362
|
+
`UPDATE sessions SET wrapper_socket_path = NULL, updated_at = ? WHERE id = ?`
|
|
1363
|
+
).run(ts2, sessionId);
|
|
1364
|
+
return getSession(db, sessionId);
|
|
1365
|
+
}
|
|
1332
1366
|
function setSessionContextUsage(db, sessionId, pct) {
|
|
1333
1367
|
const clamped = Math.max(0, Math.min(100, pct));
|
|
1334
1368
|
const ts2 = now();
|
|
@@ -1795,13 +1829,40 @@ function readAdvisorAgentMd(advisor) {
|
|
|
1795
1829
|
|
|
1796
1830
|
// ../server/src/state/wrappers.ts
|
|
1797
1831
|
import { connect } from "net";
|
|
1832
|
+
import { existsSync as existsSync6, readdirSync as readdirSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
1833
|
+
import { homedir as homedir6 } from "os";
|
|
1834
|
+
import { join as join9 } from "path";
|
|
1798
1835
|
var wrappers = /* @__PURE__ */ new Map();
|
|
1836
|
+
var wrapperToSession = /* @__PURE__ */ new Map();
|
|
1799
1837
|
var FRESHNESS_WINDOW_MS = 6e4;
|
|
1800
1838
|
function registerWrapper(rec) {
|
|
1801
1839
|
wrappers.set(rec.wrapperId, rec);
|
|
1802
1840
|
}
|
|
1803
1841
|
function unregisterWrapper(wrapperId) {
|
|
1804
1842
|
wrappers.delete(wrapperId);
|
|
1843
|
+
const sessionId = wrapperToSession.get(wrapperId);
|
|
1844
|
+
wrapperToSession.delete(wrapperId);
|
|
1845
|
+
return sessionId;
|
|
1846
|
+
}
|
|
1847
|
+
function bindWrapperToSession(wrapperId, sessionId) {
|
|
1848
|
+
wrapperToSession.set(wrapperId, sessionId);
|
|
1849
|
+
}
|
|
1850
|
+
function listWrappers() {
|
|
1851
|
+
return [...wrappers.values()];
|
|
1852
|
+
}
|
|
1853
|
+
function cleanupOrphanedSockets() {
|
|
1854
|
+
const dir = join9(homedir6(), ".solix", "wrappers");
|
|
1855
|
+
if (!existsSync6(dir)) return 0;
|
|
1856
|
+
let removed = 0;
|
|
1857
|
+
for (const f of readdirSync3(dir)) {
|
|
1858
|
+
if (!f.endsWith(".sock")) continue;
|
|
1859
|
+
try {
|
|
1860
|
+
unlinkSync2(join9(dir, f));
|
|
1861
|
+
removed++;
|
|
1862
|
+
} catch {
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
return removed;
|
|
1805
1866
|
}
|
|
1806
1867
|
function claimWrapperForCwd(cwd) {
|
|
1807
1868
|
const now2 = Date.now();
|
|
@@ -1817,21 +1878,17 @@ function claimWrapperForCwd(cwd) {
|
|
|
1817
1878
|
return best;
|
|
1818
1879
|
}
|
|
1819
1880
|
function writeToWrapperSocket(socketPath, text) {
|
|
1881
|
+
if (!existsSync6(socketPath)) return false;
|
|
1820
1882
|
try {
|
|
1821
1883
|
const client = connect(socketPath);
|
|
1822
|
-
let settled = false;
|
|
1823
1884
|
client.on("error", () => {
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
client.destroy();
|
|
1828
|
-
} catch {
|
|
1829
|
-
}
|
|
1885
|
+
try {
|
|
1886
|
+
client.destroy();
|
|
1887
|
+
} catch {
|
|
1830
1888
|
}
|
|
1831
1889
|
});
|
|
1832
1890
|
client.write(JSON.stringify({ type: "send_prompt", text }) + "\n");
|
|
1833
1891
|
client.end();
|
|
1834
|
-
settled = true;
|
|
1835
1892
|
return true;
|
|
1836
1893
|
} catch {
|
|
1837
1894
|
return false;
|
|
@@ -1925,10 +1982,10 @@ function buildContextEnvelope(db, args) {
|
|
|
1925
1982
|
}
|
|
1926
1983
|
|
|
1927
1984
|
// ../server/src/state/skills.ts
|
|
1928
|
-
import { existsSync as
|
|
1929
|
-
import { dirname as dirname3, join as
|
|
1985
|
+
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
|
|
1986
|
+
import { dirname as dirname3, join as join10, resolve as resolve2 } from "path";
|
|
1930
1987
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1931
|
-
import { homedir as
|
|
1988
|
+
import { homedir as homedir7 } from "os";
|
|
1932
1989
|
function findSolixSkillsDir() {
|
|
1933
1990
|
const here = dirname3(fileURLToPath3(import.meta.url));
|
|
1934
1991
|
const candidates = [
|
|
@@ -1937,12 +1994,12 @@ function findSolixSkillsDir() {
|
|
|
1937
1994
|
resolve2(process.cwd(), "packages", "skills")
|
|
1938
1995
|
];
|
|
1939
1996
|
for (const c of candidates) {
|
|
1940
|
-
if (
|
|
1997
|
+
if (existsSync7(c)) return c;
|
|
1941
1998
|
}
|
|
1942
1999
|
return candidates[0];
|
|
1943
2000
|
}
|
|
1944
2001
|
var SOLIX_SKILLS_DIR2 = findSolixSkillsDir();
|
|
1945
|
-
var ANTHROPIC_SKILLS_DIR =
|
|
2002
|
+
var ANTHROPIC_SKILLS_DIR = join10(homedir7(), ".claude", "skills");
|
|
1946
2003
|
function parseSkillManifest(manifestPath, fallbackId) {
|
|
1947
2004
|
try {
|
|
1948
2005
|
const txt = readFileSync5(manifestPath, "utf8");
|
|
@@ -1994,9 +2051,9 @@ function discoverSkills(db) {
|
|
|
1994
2051
|
{ dir: SOLIX_SKILLS_DIR2, source: "solix" }
|
|
1995
2052
|
];
|
|
1996
2053
|
for (const { dir, source } of sources) {
|
|
1997
|
-
if (!
|
|
1998
|
-
for (const entry of
|
|
1999
|
-
const full =
|
|
2054
|
+
if (!existsSync7(dir)) continue;
|
|
2055
|
+
for (const entry of readdirSync4(dir)) {
|
|
2056
|
+
const full = join10(dir, entry);
|
|
2000
2057
|
let isDir = false;
|
|
2001
2058
|
try {
|
|
2002
2059
|
isDir = statSync3(full).isDirectory();
|
|
@@ -2004,8 +2061,8 @@ function discoverSkills(db) {
|
|
|
2004
2061
|
continue;
|
|
2005
2062
|
}
|
|
2006
2063
|
if (!isDir) continue;
|
|
2007
|
-
const manifestPath =
|
|
2008
|
-
if (!
|
|
2064
|
+
const manifestPath = join10(full, "SKILL.md");
|
|
2065
|
+
if (!existsSync7(manifestPath)) continue;
|
|
2009
2066
|
const parsed = parseSkillManifest(manifestPath, entry);
|
|
2010
2067
|
if (!parsed) continue;
|
|
2011
2068
|
const id = `${source}:${parsed.id}`;
|
|
@@ -2030,7 +2087,7 @@ function getSkill(db, id) {
|
|
|
2030
2087
|
return row ? rowToSkill(row) : null;
|
|
2031
2088
|
}
|
|
2032
2089
|
function readSkillManifest(skill) {
|
|
2033
|
-
if (!
|
|
2090
|
+
if (!existsSync7(skill.manifestPath)) return "";
|
|
2034
2091
|
return readFileSync5(skill.manifestPath, "utf8");
|
|
2035
2092
|
}
|
|
2036
2093
|
function recordSkillInstall(db, skillId, projectId) {
|
|
@@ -2561,9 +2618,16 @@ function createHttpApp(opts) {
|
|
|
2561
2618
|
return c.json({ ok: true });
|
|
2562
2619
|
});
|
|
2563
2620
|
app.post("/api/wrappers/:id/unregister", (c) => {
|
|
2564
|
-
unregisterWrapper(c.req.param("id"));
|
|
2621
|
+
const sessionId = unregisterWrapper(c.req.param("id"));
|
|
2622
|
+
if (sessionId) {
|
|
2623
|
+
const cleared = clearSessionWrapper(opts.db, sessionId);
|
|
2624
|
+
if (cleared) {
|
|
2625
|
+
opts.router.broadcastSessionUpsert(cleared);
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2565
2628
|
return c.json({ ok: true });
|
|
2566
2629
|
});
|
|
2630
|
+
app.get("/api/wrappers", (c) => c.json(listWrappers()));
|
|
2567
2631
|
let preflightCache = null;
|
|
2568
2632
|
app.get("/api/system/preflight", (c) => {
|
|
2569
2633
|
if (preflightCache) return c.json(preflightCache);
|
|
@@ -2593,16 +2657,16 @@ function createHttpApp(opts) {
|
|
|
2593
2657
|
return c.notFound();
|
|
2594
2658
|
}
|
|
2595
2659
|
const safe = url.pathname.replace(/\.\.+/g, ".");
|
|
2596
|
-
const candidate =
|
|
2660
|
+
const candidate = join11(webDist, safe === "/" ? "index.html" : safe);
|
|
2597
2661
|
let filePath = candidate;
|
|
2598
2662
|
try {
|
|
2599
|
-
if (!
|
|
2600
|
-
filePath =
|
|
2663
|
+
if (!existsSync8(filePath) || statSync4(filePath).isDirectory()) {
|
|
2664
|
+
filePath = join11(webDist, "index.html");
|
|
2601
2665
|
}
|
|
2602
2666
|
} catch {
|
|
2603
|
-
filePath =
|
|
2667
|
+
filePath = join11(webDist, "index.html");
|
|
2604
2668
|
}
|
|
2605
|
-
if (!
|
|
2669
|
+
if (!existsSync8(filePath)) return c.notFound();
|
|
2606
2670
|
const data = readFileSync6(filePath);
|
|
2607
2671
|
return new Response(data, {
|
|
2608
2672
|
headers: { "Content-Type": mimeFor(filePath) }
|
|
@@ -2661,7 +2725,7 @@ function createHttpApp(opts) {
|
|
|
2661
2725
|
}
|
|
2662
2726
|
function findWebDist() {
|
|
2663
2727
|
if (process.env.SOLIX_WEB_DIST) {
|
|
2664
|
-
return
|
|
2728
|
+
return existsSync8(process.env.SOLIX_WEB_DIST) ? process.env.SOLIX_WEB_DIST : null;
|
|
2665
2729
|
}
|
|
2666
2730
|
const here = dirname4(fileURLToPath4(import.meta.url));
|
|
2667
2731
|
const candidates = [
|
|
@@ -2674,7 +2738,7 @@ function findWebDist() {
|
|
|
2674
2738
|
resolve3(process.cwd(), "packages", "web", "dist")
|
|
2675
2739
|
];
|
|
2676
2740
|
for (const c of candidates) {
|
|
2677
|
-
if (
|
|
2741
|
+
if (existsSync8(join11(c, "index.html"))) return c;
|
|
2678
2742
|
}
|
|
2679
2743
|
return null;
|
|
2680
2744
|
}
|
|
@@ -2700,9 +2764,9 @@ function mimeFor(filePath) {
|
|
|
2700
2764
|
|
|
2701
2765
|
// ../server/src/launcher.ts
|
|
2702
2766
|
import { spawn, spawnSync as spawnSync2 } from "child_process";
|
|
2703
|
-
import { existsSync as
|
|
2704
|
-
import { homedir as
|
|
2705
|
-
import { basename as basename3, join as
|
|
2767
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4 } from "fs";
|
|
2768
|
+
import { homedir as homedir8 } from "os";
|
|
2769
|
+
import { basename as basename3, join as join12 } from "path";
|
|
2706
2770
|
import { nanoid as nanoid5 } from "nanoid";
|
|
2707
2771
|
function ensureWorktree(opts) {
|
|
2708
2772
|
const repoRoot = (() => {
|
|
@@ -2717,8 +2781,8 @@ function ensureWorktree(opts) {
|
|
|
2717
2781
|
})();
|
|
2718
2782
|
const repoName = basename3(repoRoot);
|
|
2719
2783
|
const safeBranch = opts.branch.replace(/[^a-zA-Z0-9._-]+/g, "-");
|
|
2720
|
-
const worktreesDir =
|
|
2721
|
-
const path =
|
|
2784
|
+
const worktreesDir = join12(homedir8(), ".solix", "worktrees");
|
|
2785
|
+
const path = join12(worktreesDir, `${repoName}-${safeBranch}`);
|
|
2722
2786
|
const list = spawnSync2("git", ["worktree", "list", "--porcelain"], {
|
|
2723
2787
|
cwd: repoRoot,
|
|
2724
2788
|
encoding: "utf8"
|
|
@@ -2930,7 +2994,7 @@ var Launcher = class {
|
|
|
2930
2994
|
worktreePath
|
|
2931
2995
|
});
|
|
2932
2996
|
}
|
|
2933
|
-
if (!
|
|
2997
|
+
if (!existsSync9(spawnCwd)) {
|
|
2934
2998
|
this.broadcaster.broadcast({
|
|
2935
2999
|
type: "toast",
|
|
2936
3000
|
level: "error",
|
|
@@ -3236,6 +3300,7 @@ var EventRouter = class {
|
|
|
3236
3300
|
worktreePath,
|
|
3237
3301
|
wrapperSocketPath: wrapper?.socketPath
|
|
3238
3302
|
});
|
|
3303
|
+
if (wrapper) bindWrapperToSession(wrapper.wrapperId, session.id);
|
|
3239
3304
|
this.broadcaster.broadcast({ type: "session_upsert", session });
|
|
3240
3305
|
if (!session.parentSessionId) {
|
|
3241
3306
|
this.transcripts?.startWatching(sessionId, event.cwd);
|
|
@@ -3535,10 +3600,13 @@ var EventRouter = class {
|
|
|
3535
3600
|
}
|
|
3536
3601
|
});
|
|
3537
3602
|
} else {
|
|
3603
|
+
const cleared = clearSessionWrapper(this.db, sessionId);
|
|
3604
|
+
if (cleared)
|
|
3605
|
+
this.broadcaster.broadcast({ type: "session_upsert", session: cleared });
|
|
3538
3606
|
this.broadcaster.broadcast({
|
|
3539
3607
|
type: "toast",
|
|
3540
3608
|
level: "warn",
|
|
3541
|
-
message:
|
|
3609
|
+
message: `Wrapper for ${session.name ?? session.id.slice(0, 8)} exited \u2014 chat is now read-only. Restart with \`solix run\`.`
|
|
3542
3610
|
});
|
|
3543
3611
|
}
|
|
3544
3612
|
return ok;
|
|
@@ -3551,6 +3619,11 @@ var EventRouter = class {
|
|
|
3551
3619
|
pendingPermissions() {
|
|
3552
3620
|
return [...this.permissions.values()];
|
|
3553
3621
|
}
|
|
3622
|
+
/** Public re-broadcast helper for cases where state mutates outside
|
|
3623
|
+
* the hook flow (e.g. wrapper unregister clearing the socket path). */
|
|
3624
|
+
broadcastSessionUpsert(session) {
|
|
3625
|
+
this.broadcaster.broadcast({ type: "session_upsert", session });
|
|
3626
|
+
}
|
|
3554
3627
|
broadcastGalaxyImported(manifest) {
|
|
3555
3628
|
this.broadcaster.broadcast({ type: "galaxy_imported", manifest });
|
|
3556
3629
|
this.broadcaster.broadcast({
|
|
@@ -3677,15 +3750,15 @@ function handleClientMessage(ctx, _ws, msg) {
|
|
|
3677
3750
|
// ../server/src/state/transcript.ts
|
|
3678
3751
|
import {
|
|
3679
3752
|
closeSync,
|
|
3680
|
-
existsSync as
|
|
3753
|
+
existsSync as existsSync10,
|
|
3681
3754
|
openSync,
|
|
3682
3755
|
readSync,
|
|
3683
3756
|
statSync as statSync5,
|
|
3684
3757
|
watch
|
|
3685
3758
|
} from "fs";
|
|
3686
|
-
import { homedir as
|
|
3687
|
-
import { join as
|
|
3688
|
-
var TRANSCRIPT_BASE =
|
|
3759
|
+
import { homedir as homedir9 } from "os";
|
|
3760
|
+
import { join as join13 } from "path";
|
|
3761
|
+
var TRANSCRIPT_BASE = join13(homedir9(), ".claude", "projects");
|
|
3689
3762
|
var CONTEXT_BUDGETS_BY_MODEL = {
|
|
3690
3763
|
"claude-opus-4-7": 2e5,
|
|
3691
3764
|
"claude-opus-4-6": 2e5,
|
|
@@ -3698,7 +3771,7 @@ function encodeProjectPath(cwd) {
|
|
|
3698
3771
|
return cwd.replace(/[/\\]/g, "-");
|
|
3699
3772
|
}
|
|
3700
3773
|
function transcriptPathFor(cwd, sessionId) {
|
|
3701
|
-
return
|
|
3774
|
+
return join13(TRANSCRIPT_BASE, encodeProjectPath(cwd), `${sessionId}.jsonl`);
|
|
3702
3775
|
}
|
|
3703
3776
|
var TranscriptWatcherManager = class {
|
|
3704
3777
|
constructor(db, broadcaster) {
|
|
@@ -3718,7 +3791,7 @@ var TranscriptWatcherManager = class {
|
|
|
3718
3791
|
startWatching(sessionId, cwd) {
|
|
3719
3792
|
if (this.records.has(sessionId)) return;
|
|
3720
3793
|
const filePath = transcriptPathFor(cwd, sessionId);
|
|
3721
|
-
if (!
|
|
3794
|
+
if (!existsSync10(filePath)) {
|
|
3722
3795
|
this.scheduleRetry(sessionId, cwd, 0);
|
|
3723
3796
|
return;
|
|
3724
3797
|
}
|
|
@@ -3729,7 +3802,7 @@ var TranscriptWatcherManager = class {
|
|
|
3729
3802
|
const t = setTimeout(() => {
|
|
3730
3803
|
this.deferredRetry.delete(sessionId);
|
|
3731
3804
|
const filePath = transcriptPathFor(cwd, sessionId);
|
|
3732
|
-
if (
|
|
3805
|
+
if (existsSync10(filePath)) {
|
|
3733
3806
|
this.attach(sessionId, filePath);
|
|
3734
3807
|
} else {
|
|
3735
3808
|
this.scheduleRetry(sessionId, cwd, attempt + 1);
|
|
@@ -3925,6 +3998,13 @@ async function createSolixServer(opts = {}) {
|
|
|
3925
3998
|
const db = getDb();
|
|
3926
3999
|
seedAdvisors(db);
|
|
3927
4000
|
discoverSkills(db);
|
|
4001
|
+
const cleared = cleanupOrphanedSockets();
|
|
4002
|
+
if (cleared > 0) {
|
|
4003
|
+
console.log(`[solix] cleaned up ${cleared} orphaned wrapper socket(s)`);
|
|
4004
|
+
}
|
|
4005
|
+
db.prepare(
|
|
4006
|
+
`UPDATE sessions SET wrapper_socket_path = NULL WHERE wrapper_socket_path IS NOT NULL`
|
|
4007
|
+
).run();
|
|
3928
4008
|
const broadcaster = new Broadcaster();
|
|
3929
4009
|
const launcher = new Launcher(db, broadcaster);
|
|
3930
4010
|
const transcripts = new TranscriptWatcherManager(db, broadcaster);
|
|
@@ -3991,15 +4071,15 @@ async function start(opts = {}) {
|
|
|
3991
4071
|
}
|
|
3992
4072
|
|
|
3993
4073
|
// src/uninstall.ts
|
|
3994
|
-
import { copyFileSync as copyFileSync2, existsSync as
|
|
4074
|
+
import { copyFileSync as copyFileSync2, existsSync as existsSync11, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
|
|
3995
4075
|
function uninstall() {
|
|
3996
4076
|
uninstallShim();
|
|
3997
|
-
if (
|
|
4077
|
+
if (existsSync11(CLAUDE_BACKUP)) {
|
|
3998
4078
|
copyFileSync2(CLAUDE_BACKUP, CLAUDE_SETTINGS);
|
|
3999
4079
|
console.log(`[solix] restored settings.json from backup`);
|
|
4000
4080
|
return;
|
|
4001
4081
|
}
|
|
4002
|
-
if (!
|
|
4082
|
+
if (!existsSync11(CLAUDE_SETTINGS)) {
|
|
4003
4083
|
console.log("[solix] nothing to uninstall (no settings.json found)");
|
|
4004
4084
|
return;
|
|
4005
4085
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{r as c,a8 as $,j as e}from"./index-BB6Hys2b.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};
|