claude-smart 0.2.30 → 0.2.32
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/README.md +2 -2
- package/bin/claude-smart.js +172 -18
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/dashboard/app/api/config/route.ts +11 -2
- package/plugin/dashboard/app/api/health/route.ts +45 -6
- package/plugin/dashboard/app/api/reflexio/[...path]/route.ts +15 -7
- package/plugin/dashboard/app/api/rules/applied/route.ts +27 -0
- package/plugin/dashboard/app/configure/env/page.tsx +36 -31
- package/plugin/dashboard/app/configure/layout.tsx +1 -1
- package/plugin/dashboard/app/configure/server/page.tsx +8 -14
- package/plugin/dashboard/app/dashboard/page.tsx +311 -115
- package/plugin/dashboard/app/globals.css +80 -66
- package/plugin/dashboard/app/layout.tsx +13 -10
- package/plugin/dashboard/app/preferences/[id]/page.tsx +21 -32
- package/plugin/dashboard/app/preferences/page.tsx +154 -54
- package/plugin/dashboard/app/preferences/project/[id]/page.tsx +1 -0
- package/plugin/dashboard/app/rules/[id]/page.tsx +51 -0
- package/plugin/dashboard/app/sessions/[sessionId]/page.tsx +4 -4
- package/plugin/dashboard/app/sessions/page.tsx +14 -10
- package/plugin/dashboard/app/skills/page.tsx +175 -56
- package/plugin/dashboard/app/skills/project/[id]/page.tsx +22 -38
- package/plugin/dashboard/app/skills/shared/[id]/page.tsx +20 -37
- package/plugin/dashboard/components/common/delete-learning-danger-zone.tsx +166 -0
- package/plugin/dashboard/components/common/empty-state.tsx +4 -2
- package/plugin/dashboard/components/common/learnings-badge.tsx +1 -1
- package/plugin/dashboard/components/common/page-header.tsx +5 -3
- package/plugin/dashboard/components/common/page-tabs.tsx +4 -4
- package/plugin/dashboard/components/common/stat-card.tsx +9 -5
- package/plugin/dashboard/components/layout/sidebar.tsx +24 -9
- package/plugin/dashboard/components/layout/top-bar.tsx +37 -25
- package/plugin/dashboard/components/ui/input.tsx +1 -0
- package/plugin/dashboard/hooks/use-settings.tsx +30 -61
- package/plugin/dashboard/hooks/use-stall-state.ts +5 -9
- package/plugin/dashboard/lib/config-file.ts +2 -0
- package/plugin/dashboard/lib/reflexio-client.ts +23 -48
- package/plugin/dashboard/lib/session-reader.ts +222 -6
- package/plugin/dashboard/lib/types.ts +20 -1
- package/plugin/dashboard/package-lock.json +70 -95
- package/plugin/dashboard/package.json +5 -2
- package/plugin/hooks/hooks.json +1 -1
- package/plugin/pyproject.toml +1 -1
- package/plugin/scripts/_lib.sh +126 -0
- package/plugin/scripts/backend-service.sh +32 -7
- package/plugin/scripts/cli.sh +4 -2
- package/plugin/scripts/codex-hook.js +100 -3
- package/plugin/scripts/dashboard-service.sh +98 -19
- package/plugin/scripts/hook_entry.sh +32 -11
- package/plugin/scripts/smart-install.sh +27 -44
- package/plugin/src/claude_smart/cli.py +204 -20
- package/plugin/src/claude_smart/context_format.py +244 -6
- package/plugin/src/claude_smart/context_inject.py +8 -1
- package/plugin/src/claude_smart/cs_cite.py +186 -34
- package/plugin/src/claude_smart/env_config.py +102 -0
- package/plugin/src/claude_smart/events/session_end.py +171 -6
- package/plugin/src/claude_smart/events/session_start.py +26 -2
- package/plugin/src/claude_smart/events/stop.py +48 -9
- package/plugin/src/claude_smart/hook.py +62 -4
- package/plugin/src/claude_smart/hook_log.py +301 -0
- package/plugin/src/claude_smart/internal_call.py +30 -0
- package/plugin/src/claude_smart/publish.py +5 -0
- package/plugin/src/claude_smart/reflexio_adapter.py +102 -26
- package/plugin/uv.lock +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License">
|
|
14
14
|
</a>
|
|
15
15
|
<a href="plugin/pyproject.toml">
|
|
16
|
-
<img src="https://img.shields.io/badge/version-0.2.
|
|
16
|
+
<img src="https://img.shields.io/badge/version-0.2.32-green.svg" alt="Version">
|
|
17
17
|
</a>
|
|
18
18
|
<a href="plugin/pyproject.toml">
|
|
19
19
|
<img src="https://img.shields.io/badge/python-%3E%3D3.12-brightgreen.svg" alt="Python">
|
|
@@ -120,7 +120,7 @@ To uninstall:
|
|
|
120
120
|
npx claude-smart uninstall --host codex
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
-
Restart Codex after uninstalling.
|
|
123
|
+
Restart Codex after uninstalling. The uninstaller stops local claude-smart services and removes plugin/cache/config state; learned data under `~/.reflexio/` and `~/.claude-smart/` is preserved and shared with Claude Code, so you can switch between hosts without losing skills or preferences.
|
|
124
124
|
|
|
125
125
|
Developing the plugin itself? See [DEVELOPER.md](./DEVELOPER.md#developing-locally) for what the installer does, manual toggles via `/plugins`, and clone-based development.
|
|
126
126
|
|
package/bin/claude-smart.js
CHANGED
|
@@ -11,10 +11,11 @@
|
|
|
11
11
|
*/
|
|
12
12
|
"use strict";
|
|
13
13
|
|
|
14
|
-
const { execSync, spawn } = require("child_process");
|
|
14
|
+
const { execSync, spawn, spawnSync } = require("child_process");
|
|
15
15
|
const crypto = require("crypto");
|
|
16
16
|
const {
|
|
17
17
|
appendFileSync,
|
|
18
|
+
chmodSync,
|
|
18
19
|
cpSync,
|
|
19
20
|
existsSync,
|
|
20
21
|
lstatSync,
|
|
@@ -37,6 +38,7 @@ const CODEX_MARKETPLACE_NAME = "reflexioai";
|
|
|
37
38
|
const CODEX_MARKETPLACE_DISPLAY_NAME = "ReflexioAI";
|
|
38
39
|
const CODEX_PLUGIN_ID = `claude-smart@${CODEX_MARKETPLACE_NAME}`;
|
|
39
40
|
const REFLEXIO_ENV_PATH = join(homedir(), ".reflexio", ".env");
|
|
41
|
+
const MANAGED_REFLEXIO_URL = "https://www.reflexio.ai/";
|
|
40
42
|
const REFLEXIO_DIR = join(homedir(), ".reflexio");
|
|
41
43
|
const CLAUDE_SMART_STATE_DIR = join(homedir(), ".claude-smart");
|
|
42
44
|
const CODEX_CONFIG_PATH = join(homedir(), ".codex", "config.toml");
|
|
@@ -48,7 +50,7 @@ const CODEX_MARKETPLACE_DIR = join(
|
|
|
48
50
|
"marketplaces",
|
|
49
51
|
CODEX_MARKETPLACE_NAME,
|
|
50
52
|
);
|
|
51
|
-
const CODEX_MARKETPLACE_PLUGIN_PATH =
|
|
53
|
+
const CODEX_MARKETPLACE_PLUGIN_PATH = "plugin";
|
|
52
54
|
const CODEX_PLUGIN_CACHE_DIR = join(
|
|
53
55
|
homedir(),
|
|
54
56
|
".codex",
|
|
@@ -57,6 +59,14 @@ const CODEX_PLUGIN_CACHE_DIR = join(
|
|
|
57
59
|
CODEX_MARKETPLACE_NAME,
|
|
58
60
|
"claude-smart",
|
|
59
61
|
);
|
|
62
|
+
const LOCAL_DATA_NOTICE = [
|
|
63
|
+
"Local data was kept so reinstalling claude-smart can reuse your learned rules, sessions, logs, and local Reflexio data.",
|
|
64
|
+
"Kept folders:",
|
|
65
|
+
" ~/.claude-smart",
|
|
66
|
+
" ~/.reflexio",
|
|
67
|
+
"Delete them only if you want a full reset or need to remove local claude-smart data from this machine:",
|
|
68
|
+
" rm -rf ~/.claude-smart ~/.reflexio",
|
|
69
|
+
];
|
|
60
70
|
const CODEX_REQUIRED_FILES = [
|
|
61
71
|
".agents/plugins/marketplace.json",
|
|
62
72
|
"plugin/.codex-plugin/plugin.json",
|
|
@@ -68,6 +78,7 @@ const CODEX_REQUIRED_FILES = [
|
|
|
68
78
|
"plugin/scripts/_codex_env.sh",
|
|
69
79
|
];
|
|
70
80
|
const CODEX_CLI_TIMEOUT_MS = 30_000;
|
|
81
|
+
const PLUGIN_SERVICE_TIMEOUT_MS = 15_000;
|
|
71
82
|
const COPYTREE_IGNORE_NAMES = new Set([
|
|
72
83
|
"__pycache__",
|
|
73
84
|
".venv",
|
|
@@ -197,9 +208,82 @@ function seedReflexioEnv() {
|
|
|
197
208
|
const prefix = existing && !existing.endsWith("\n") ? "\n" : "";
|
|
198
209
|
const body = missing.map((f) => `${f}=1`).join("\n") + "\n";
|
|
199
210
|
appendFileSync(REFLEXIO_ENV_PATH, prefix + body);
|
|
211
|
+
chmodSync(REFLEXIO_ENV_PATH, 0o600);
|
|
200
212
|
return missing;
|
|
201
213
|
}
|
|
202
214
|
|
|
215
|
+
function escapeEnvValue(value) {
|
|
216
|
+
return String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function parseEnvLine(line) {
|
|
220
|
+
let trimmed = String(line || "").trim();
|
|
221
|
+
if (!trimmed || trimmed.startsWith("#")) return null;
|
|
222
|
+
if (trimmed.startsWith("export ")) trimmed = trimmed.slice("export ".length).trimStart();
|
|
223
|
+
const eq = trimmed.indexOf("=");
|
|
224
|
+
if (eq < 0) return null;
|
|
225
|
+
const key = trimmed.slice(0, eq).trim();
|
|
226
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) return null;
|
|
227
|
+
return { key };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function setReflexioEnvVars(values) {
|
|
231
|
+
mkdirSync(dirname(REFLEXIO_ENV_PATH), { recursive: true });
|
|
232
|
+
const existing = existsSync(REFLEXIO_ENV_PATH)
|
|
233
|
+
? readFileSync(REFLEXIO_ENV_PATH, "utf8")
|
|
234
|
+
: "";
|
|
235
|
+
const lines = existing ? existing.split(/\r?\n/) : [];
|
|
236
|
+
const seen = new Set();
|
|
237
|
+
const out = [];
|
|
238
|
+
for (const line of lines) {
|
|
239
|
+
const parsed = parseEnvLine(line);
|
|
240
|
+
if (parsed && Object.prototype.hasOwnProperty.call(values, parsed.key)) {
|
|
241
|
+
out.push(`${parsed.key}="${escapeEnvValue(values[parsed.key])}"`);
|
|
242
|
+
seen.add(parsed.key);
|
|
243
|
+
} else {
|
|
244
|
+
out.push(line);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const added = [];
|
|
248
|
+
for (const [key, value] of Object.entries(values)) {
|
|
249
|
+
if (seen.has(key)) continue;
|
|
250
|
+
out.push(`${key}="${escapeEnvValue(value)}"`);
|
|
251
|
+
added.push(key);
|
|
252
|
+
}
|
|
253
|
+
const content = out.join("\n").replace(/\n*$/, "\n");
|
|
254
|
+
writeFileSync(REFLEXIO_ENV_PATH, content);
|
|
255
|
+
chmodSync(REFLEXIO_ENV_PATH, 0o600);
|
|
256
|
+
return added;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function maskSecret(value) {
|
|
260
|
+
if (!value) return "";
|
|
261
|
+
if (value.length <= 8) return "*".repeat(value.length);
|
|
262
|
+
const prefix = value.slice(0, 8).includes("-") ? value.slice(0, 5) : value.slice(0, 4);
|
|
263
|
+
return `${prefix}****${value.slice(-4)}`;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function configureReflexioSetup(args) {
|
|
267
|
+
const apiKey = parseOptionalArg(args, "--api-key").trim();
|
|
268
|
+
if (apiKey) {
|
|
269
|
+
const reflexioUrl = parseOptionalArg(args, "--reflexio-url") || MANAGED_REFLEXIO_URL;
|
|
270
|
+
const added = setReflexioEnvVars({
|
|
271
|
+
REFLEXIO_URL: reflexioUrl,
|
|
272
|
+
REFLEXIO_API_KEY: apiKey,
|
|
273
|
+
});
|
|
274
|
+
const changed = added.length > 0 ? added.join(", ") : "managed Reflexio settings";
|
|
275
|
+
process.stdout.write(
|
|
276
|
+
`Configured ${REFLEXIO_ENV_PATH} for managed Reflexio (${changed}; API key ${maskSecret(apiKey)}).\n`,
|
|
277
|
+
);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const added = seedReflexioEnv();
|
|
282
|
+
if (added.length > 0) {
|
|
283
|
+
process.stdout.write(`Seeded ${REFLEXIO_ENV_PATH} with ${added.join(", ")}.\n`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
203
287
|
function findClaudeCodePluginRoot() {
|
|
204
288
|
const cacheRoot = join(homedir(), ".claude", "plugins", "cache", CODEX_MARKETPLACE_NAME, "claude-smart");
|
|
205
289
|
const candidates = [];
|
|
@@ -342,6 +426,45 @@ function runChecked(command, args, options = {}) {
|
|
|
342
426
|
});
|
|
343
427
|
}
|
|
344
428
|
|
|
429
|
+
function runPluginService(pluginRoot, scriptName, subcommand) {
|
|
430
|
+
const script = join(pluginRoot, "scripts", scriptName);
|
|
431
|
+
if (!existsSync(script)) return false;
|
|
432
|
+
const bash = resolveCommand(isWindows() ? ["bash.exe", "bash"] : ["bash"]);
|
|
433
|
+
if (!bash) return false;
|
|
434
|
+
const result = spawnSync(bash, [script, subcommand], {
|
|
435
|
+
cwd: pluginRoot,
|
|
436
|
+
env: runtimeEnv(),
|
|
437
|
+
stdio: "ignore",
|
|
438
|
+
windowsHide: true,
|
|
439
|
+
timeout: PLUGIN_SERVICE_TIMEOUT_MS,
|
|
440
|
+
killSignal: "SIGTERM",
|
|
441
|
+
});
|
|
442
|
+
if (result.error || result.signal) {
|
|
443
|
+
const reason = result.error && result.error.code === "ETIMEDOUT"
|
|
444
|
+
? `timed out after ${PLUGIN_SERVICE_TIMEOUT_MS / 1000}s`
|
|
445
|
+
: result.error
|
|
446
|
+
? result.error.message
|
|
447
|
+
: `terminated by ${result.signal}`;
|
|
448
|
+
process.stderr.write(
|
|
449
|
+
`warning: ${scriptName} ${subcommand} ${reason}; continuing.\n`,
|
|
450
|
+
);
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
return result.status === 0;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
function refreshDashboardService(pluginRoot) {
|
|
457
|
+
// dashboard-service.sh is marker-gated: stop only reaps a listener that
|
|
458
|
+
// identifies as claude-smart, so foreign apps on 3001 are left alone.
|
|
459
|
+
runPluginService(pluginRoot, "dashboard-service.sh", "stop");
|
|
460
|
+
return runPluginService(pluginRoot, "dashboard-service.sh", "start");
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function stopClaudeSmartServices(pluginRoot) {
|
|
464
|
+
runPluginService(pluginRoot, "dashboard-service.sh", "stop");
|
|
465
|
+
runPluginService(pluginRoot, "backend-service.sh", "stop");
|
|
466
|
+
}
|
|
467
|
+
|
|
345
468
|
function downloadFile(url, dest) {
|
|
346
469
|
return new Promise((resolve, reject) => {
|
|
347
470
|
const request = https.get(url, (response) => {
|
|
@@ -676,6 +799,7 @@ function printHelp() {
|
|
|
676
799
|
" npx claude-smart install Install the plugin into Claude Code",
|
|
677
800
|
" npx claude-smart install --host codex Register the plugin marketplace for Codex",
|
|
678
801
|
" npx claude-smart install --source <owner/repo> Override the marketplace source",
|
|
802
|
+
" npx claude-smart install --api-key <key> Use managed Reflexio service",
|
|
679
803
|
" npx claude-smart uninstall --host codex Remove the Codex marketplace registration",
|
|
680
804
|
" npx claude-smart --help Show this help",
|
|
681
805
|
"",
|
|
@@ -684,6 +808,7 @@ function printHelp() {
|
|
|
684
808
|
` 2. claude plugin install ${PLUGIN_SPEC}`,
|
|
685
809
|
" 3. Appends CLAUDE_SMART_USE_LOCAL_CLI=1 and CLAUDE_SMART_USE_LOCAL_EMBEDDING=1",
|
|
686
810
|
" to ~/.reflexio/.env (idempotent).",
|
|
811
|
+
" Passing --api-key writes REFLEXIO_URL and REFLEXIO_API_KEY instead.",
|
|
687
812
|
"",
|
|
688
813
|
"Codex install:",
|
|
689
814
|
` 1. Copies the bundled marketplace to ${CODEX_MARKETPLACE_DIR}`,
|
|
@@ -715,6 +840,17 @@ function parseSource(args) {
|
|
|
715
840
|
return value;
|
|
716
841
|
}
|
|
717
842
|
|
|
843
|
+
function parseOptionalArg(args, flag) {
|
|
844
|
+
const idx = args.indexOf(flag);
|
|
845
|
+
if (idx === -1) return "";
|
|
846
|
+
const value = args[idx + 1];
|
|
847
|
+
if (!value) {
|
|
848
|
+
process.stderr.write(`error: ${flag} requires a value\n`);
|
|
849
|
+
process.exit(1);
|
|
850
|
+
}
|
|
851
|
+
return value;
|
|
852
|
+
}
|
|
853
|
+
|
|
718
854
|
function parseHost(args) {
|
|
719
855
|
const idx = args.indexOf("--host");
|
|
720
856
|
if (idx === -1) return "claude-code";
|
|
@@ -791,6 +927,21 @@ function copyCodexMarketplace() {
|
|
|
791
927
|
return CODEX_MARKETPLACE_DIR;
|
|
792
928
|
}
|
|
793
929
|
|
|
930
|
+
function codexMarketplacePluginRoot(marketplaceRoot) {
|
|
931
|
+
const manifestPath = join(marketplaceRoot, ".agents", "plugins", "marketplace.json");
|
|
932
|
+
const fallback = join(marketplaceRoot, CODEX_MARKETPLACE_PLUGIN_PATH);
|
|
933
|
+
try {
|
|
934
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
935
|
+
const entry = (manifest.plugins || []).find((plugin) => plugin.name === "claude-smart");
|
|
936
|
+
const rawPath = entry && entry.source && entry.source.path;
|
|
937
|
+
if (typeof rawPath !== "string" || !rawPath) return fallback;
|
|
938
|
+
const relPath = rawPath.replace(/^\.\//, "");
|
|
939
|
+
return join(marketplaceRoot, relPath);
|
|
940
|
+
} catch {
|
|
941
|
+
return fallback;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
794
945
|
function removeTomlSections(path, { exact, prefixes = [] }) {
|
|
795
946
|
if (!existsSync(path)) return true;
|
|
796
947
|
const text = readFileSync(path, "utf8");
|
|
@@ -1138,12 +1289,13 @@ async function runUninstall(args) {
|
|
|
1138
1289
|
);
|
|
1139
1290
|
process.exit(code);
|
|
1140
1291
|
}
|
|
1292
|
+
stopClaudeSmartServices(join(PACKAGE_ROOT, "plugin"));
|
|
1141
1293
|
|
|
1142
1294
|
process.stdout.write(
|
|
1143
1295
|
[
|
|
1144
1296
|
"",
|
|
1145
1297
|
"claude-smart uninstalled. Restart Claude Code to apply.",
|
|
1146
|
-
|
|
1298
|
+
...LOCAL_DATA_NOTICE,
|
|
1147
1299
|
"",
|
|
1148
1300
|
].join("\n"),
|
|
1149
1301
|
);
|
|
@@ -1151,7 +1303,7 @@ async function runUninstall(args) {
|
|
|
1151
1303
|
|
|
1152
1304
|
async function runInstall(args) {
|
|
1153
1305
|
if (parseHost(args) === "codex") {
|
|
1154
|
-
await runInstallCodex();
|
|
1306
|
+
await runInstallCodex(args);
|
|
1155
1307
|
return;
|
|
1156
1308
|
}
|
|
1157
1309
|
|
|
@@ -1179,15 +1331,13 @@ async function runInstall(args) {
|
|
|
1179
1331
|
}
|
|
1180
1332
|
}
|
|
1181
1333
|
|
|
1182
|
-
|
|
1183
|
-
if (added.length > 0) {
|
|
1184
|
-
process.stdout.write(
|
|
1185
|
-
`Seeded ${REFLEXIO_ENV_PATH} with ${added.join(", ")}.\n`,
|
|
1186
|
-
);
|
|
1187
|
-
}
|
|
1334
|
+
configureReflexioSetup(args);
|
|
1188
1335
|
try {
|
|
1189
1336
|
const pluginRoot = await bootstrapClaudeCodeInstall();
|
|
1190
1337
|
process.stdout.write(`Prepared claude-smart runtime at ${pluginRoot}.\n`);
|
|
1338
|
+
if (refreshDashboardService(pluginRoot)) {
|
|
1339
|
+
process.stdout.write("Refreshed claude-smart dashboard service.\n");
|
|
1340
|
+
}
|
|
1191
1341
|
} catch (err) {
|
|
1192
1342
|
process.stderr.write(
|
|
1193
1343
|
`error: claude-smart installed, but dependency bootstrap failed: ${err && err.message ? err.message : err}\n`,
|
|
@@ -1209,7 +1359,7 @@ async function runInstall(args) {
|
|
|
1209
1359
|
);
|
|
1210
1360
|
}
|
|
1211
1361
|
|
|
1212
|
-
async function runInstallCodex() {
|
|
1362
|
+
async function runInstallCodex(args) {
|
|
1213
1363
|
if (!hasCli("codex")) {
|
|
1214
1364
|
process.stderr.write("error: 'codex' CLI not found on PATH. Install Codex first.\n");
|
|
1215
1365
|
process.exit(1);
|
|
@@ -1254,9 +1404,13 @@ async function runInstallCodex() {
|
|
|
1254
1404
|
let trustedHookCount = 0;
|
|
1255
1405
|
let trustError = null;
|
|
1256
1406
|
try {
|
|
1257
|
-
cacheDir = installCodexPluginCache(
|
|
1407
|
+
cacheDir = installCodexPluginCache(codexMarketplacePluginRoot(marketplaceRoot));
|
|
1258
1408
|
process.stdout.write(`Installed Codex plugin cache at ${cacheDir}.\n`);
|
|
1409
|
+
configureReflexioSetup(args);
|
|
1259
1410
|
await bootstrapPluginRuntime(cacheDir);
|
|
1411
|
+
if (refreshDashboardService(cacheDir)) {
|
|
1412
|
+
process.stdout.write("Refreshed claude-smart dashboard service.\n");
|
|
1413
|
+
}
|
|
1260
1414
|
} catch (err) {
|
|
1261
1415
|
process.stderr.write(
|
|
1262
1416
|
`error: automatic Codex plugin install failed: ${err && err.message ? err.message : err}\n`,
|
|
@@ -1289,11 +1443,6 @@ async function runInstallCodex() {
|
|
|
1289
1443
|
process.stdout.write(`Trusted and enabled ${trustedHookCount} claude-smart Codex hooks.\n`);
|
|
1290
1444
|
}
|
|
1291
1445
|
|
|
1292
|
-
const added = seedReflexioEnv();
|
|
1293
|
-
if (added.length > 0) {
|
|
1294
|
-
process.stdout.write(`Seeded ${REFLEXIO_ENV_PATH} with ${added.join(", ")}.\n`);
|
|
1295
|
-
}
|
|
1296
|
-
|
|
1297
1446
|
process.stdout.write(
|
|
1298
1447
|
[
|
|
1299
1448
|
"",
|
|
@@ -1306,6 +1455,7 @@ async function runInstallCodex() {
|
|
|
1306
1455
|
}
|
|
1307
1456
|
|
|
1308
1457
|
async function runUninstallCodex() {
|
|
1458
|
+
stopClaudeSmartServices(join(PACKAGE_ROOT, "plugin"));
|
|
1309
1459
|
if (!hasCli("codex")) {
|
|
1310
1460
|
process.stdout.write("Codex CLI not found; skipping marketplace removal.\n");
|
|
1311
1461
|
cleanupCodexInstallState();
|
|
@@ -1324,7 +1474,8 @@ async function runUninstallCodex() {
|
|
|
1324
1474
|
[
|
|
1325
1475
|
"",
|
|
1326
1476
|
"claude-smart Codex plugin and marketplace state removed. Restart Codex to apply.",
|
|
1327
|
-
"Codex's global hook feature flags
|
|
1477
|
+
"Codex's global hook feature flags were left in place.",
|
|
1478
|
+
...LOCAL_DATA_NOTICE,
|
|
1328
1479
|
"",
|
|
1329
1480
|
].join("\n"),
|
|
1330
1481
|
);
|
|
@@ -1370,8 +1521,11 @@ if (require.main === module) {
|
|
|
1370
1521
|
module.exports = {
|
|
1371
1522
|
assertSupportedRuntimePlatform,
|
|
1372
1523
|
bootstrapPluginRuntime,
|
|
1524
|
+
codexMarketplacePluginRoot,
|
|
1525
|
+
copyCodexMarketplace,
|
|
1373
1526
|
ensurePrivateNode,
|
|
1374
1527
|
ensureUv,
|
|
1528
|
+
configureReflexioSetup,
|
|
1375
1529
|
patchCodexHooksForNode,
|
|
1376
1530
|
platformSupportError,
|
|
1377
1531
|
};
|
package/package.json
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
import { readConfig, writeConfig } from "@/lib/config-file";
|
|
3
|
+
import type { ClaudeSmartConfig } from "@/lib/types";
|
|
3
4
|
|
|
4
5
|
export const dynamic = "force-dynamic";
|
|
5
6
|
|
|
7
|
+
function publicConfig(config: ClaudeSmartConfig): ClaudeSmartConfig {
|
|
8
|
+
return {
|
|
9
|
+
...config,
|
|
10
|
+
REFLEXIO_API_KEY: "",
|
|
11
|
+
REFLEXIO_API_KEY_SET: Boolean(config.REFLEXIO_API_KEY),
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
6
15
|
export async function GET() {
|
|
7
16
|
const config = await readConfig();
|
|
8
|
-
return NextResponse.json(config);
|
|
17
|
+
return NextResponse.json(publicConfig(config));
|
|
9
18
|
}
|
|
10
19
|
|
|
11
20
|
export async function PUT(req: Request) {
|
|
12
21
|
const body = await req.json();
|
|
13
22
|
await writeConfig(body);
|
|
14
23
|
const config = await readConfig();
|
|
15
|
-
return NextResponse.json(config);
|
|
24
|
+
return NextResponse.json(publicConfig(config));
|
|
16
25
|
}
|
|
@@ -1,10 +1,49 @@
|
|
|
1
|
-
|
|
1
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
|
|
6
|
+
function realpathOrSelf(value: string): string {
|
|
7
|
+
try {
|
|
8
|
+
return realpathSync(value);
|
|
9
|
+
} catch {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function pluginVersion(pluginRoot: string): string {
|
|
15
|
+
try {
|
|
16
|
+
const manifest = JSON.parse(
|
|
17
|
+
readFileSync(path.join(pluginRoot, ".codex-plugin", "plugin.json"), "utf8"),
|
|
18
|
+
);
|
|
19
|
+
return typeof manifest.version === "string" && manifest.version
|
|
20
|
+
? manifest.version
|
|
21
|
+
: "unknown";
|
|
22
|
+
} catch {
|
|
23
|
+
return "unknown";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
2
26
|
|
|
3
27
|
export function GET() {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
28
|
+
const dashboardDir = realpathOrSelf(process.cwd());
|
|
29
|
+
const pluginRoot = realpathOrSelf(path.dirname(dashboardDir));
|
|
30
|
+
const version = pluginVersion(pluginRoot);
|
|
31
|
+
|
|
32
|
+
return new Response(
|
|
33
|
+
JSON.stringify({
|
|
34
|
+
service: "claude-smart-dashboard",
|
|
35
|
+
pluginRoot,
|
|
36
|
+
dashboardDir,
|
|
37
|
+
version,
|
|
38
|
+
}),
|
|
39
|
+
{
|
|
40
|
+
headers: {
|
|
41
|
+
"content-type": "application/json",
|
|
42
|
+
"x-claude-smart-dashboard": "1",
|
|
43
|
+
"x-claude-smart-plugin-root": pluginRoot,
|
|
44
|
+
"x-claude-smart-dashboard-dir": dashboardDir,
|
|
45
|
+
"x-claude-smart-version": version,
|
|
46
|
+
},
|
|
8
47
|
},
|
|
9
|
-
|
|
48
|
+
);
|
|
10
49
|
}
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
import { originOnly } from "@/lib/reflexio-url";
|
|
3
|
+
import { readConfig } from "@/lib/config-file";
|
|
3
4
|
|
|
4
5
|
export const dynamic = "force-dynamic";
|
|
5
6
|
|
|
6
7
|
const DEFAULT_URL = "http://localhost:8071";
|
|
7
8
|
|
|
8
|
-
function
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
if (fromHeader) return fromHeader;
|
|
9
|
+
async function reflexioConfig(): Promise<{ base: string; apiKey: string }> {
|
|
10
|
+
const config = await readConfig();
|
|
11
|
+
const apiKey = config.REFLEXIO_API_KEY || process.env.REFLEXIO_API_KEY || "";
|
|
12
12
|
const fromEnv = originOnly(process.env.REFLEXIO_URL ?? "");
|
|
13
|
-
|
|
13
|
+
const fromConfig = originOnly(config.REFLEXIO_URL ?? "");
|
|
14
|
+
const configuredBase = fromEnv ?? fromConfig;
|
|
15
|
+
return {
|
|
16
|
+
base: configuredBase ?? DEFAULT_URL,
|
|
17
|
+
apiKey: configuredBase ? apiKey : "",
|
|
18
|
+
};
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
async function proxy(
|
|
@@ -20,12 +25,15 @@ async function proxy(
|
|
|
20
25
|
const { path } = await context.params;
|
|
21
26
|
const targetPath = path.join("/");
|
|
22
27
|
const url = new URL(req.url);
|
|
23
|
-
const
|
|
28
|
+
const { base, apiKey } = await reflexioConfig();
|
|
29
|
+
const target = `${base}/${targetPath}${url.search}`;
|
|
24
30
|
|
|
25
31
|
const headers = new Headers(req.headers);
|
|
26
32
|
headers.delete("host");
|
|
27
|
-
headers.delete("x-reflexio-url");
|
|
28
33
|
headers.delete("connection");
|
|
34
|
+
headers.delete("authorization");
|
|
35
|
+
headers.set("user-agent", "claude-smart");
|
|
36
|
+
if (apiKey) headers.set("authorization", `Bearer ${apiKey}`);
|
|
29
37
|
|
|
30
38
|
const init: RequestInit = {
|
|
31
39
|
method: req.method,
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { listAppliedRules } from "@/lib/session-reader";
|
|
3
|
+
|
|
4
|
+
export const dynamic = "force-dynamic";
|
|
5
|
+
|
|
6
|
+
const MAX_DAYS_BACK = 365;
|
|
7
|
+
const MAX_LIMIT = 500;
|
|
8
|
+
|
|
9
|
+
function positiveInt(value: string | null, fallback: number): number {
|
|
10
|
+
if (!value) return fallback;
|
|
11
|
+
const parsed = Number.parseInt(value, 10);
|
|
12
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function GET(req: Request) {
|
|
16
|
+
const url = new URL(req.url);
|
|
17
|
+
const daysBack = Math.min(
|
|
18
|
+
positiveInt(url.searchParams.get("daysBack"), 30),
|
|
19
|
+
MAX_DAYS_BACK,
|
|
20
|
+
);
|
|
21
|
+
const limit = Math.min(
|
|
22
|
+
positiveInt(url.searchParams.get("limit"), 20),
|
|
23
|
+
MAX_LIMIT,
|
|
24
|
+
);
|
|
25
|
+
const stats = await listAppliedRules({ daysBack, limit });
|
|
26
|
+
return NextResponse.json({ success: true, stats });
|
|
27
|
+
}
|