@wu529778790/open-im 1.8.0 → 1.8.1-beta.1
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.
|
@@ -118,6 +118,16 @@ export declare const PAGE_TEXTS: {
|
|
|
118
118
|
readonly saveOk: "Configuration saved.";
|
|
119
119
|
readonly startOk: "Bridge started.";
|
|
120
120
|
readonly stopOk: "Bridge stopped.";
|
|
121
|
+
readonly configEditorTitle: "Config Editor";
|
|
122
|
+
readonly configEditorHint: "Edit ~/.open-im/config.json directly";
|
|
123
|
+
readonly claudeSettingsLabel: "~/.claude/settings.json";
|
|
124
|
+
readonly configJson: "~/.open-im/config.json";
|
|
125
|
+
readonly configJsonHint: "Edit the configuration JSON. Changes will be saved when you click \"Save Config\" in the Service section.";
|
|
126
|
+
readonly formatJson: "Format";
|
|
127
|
+
readonly resetJson: "Reset";
|
|
128
|
+
readonly saveBtn: "Save";
|
|
129
|
+
readonly jsonValid: "Valid JSON";
|
|
130
|
+
readonly jsonInvalid: "Invalid JSON: {error}";
|
|
121
131
|
};
|
|
122
132
|
readonly zh: {
|
|
123
133
|
readonly pageTitle: "open-im 本地控制台";
|
|
@@ -232,5 +242,15 @@ export declare const PAGE_TEXTS: {
|
|
|
232
242
|
readonly saveOk: "配置已保存。";
|
|
233
243
|
readonly startOk: "桥接已启动。";
|
|
234
244
|
readonly stopOk: "桥接已停止。";
|
|
245
|
+
readonly configEditorTitle: "JSON 配置编辑器";
|
|
246
|
+
readonly configEditorHint: "直接编辑 ~/.open-im/config.json";
|
|
247
|
+
readonly claudeSettingsLabel: "~/.claude/settings.json";
|
|
248
|
+
readonly configJson: "~/.open-im/config.json";
|
|
249
|
+
readonly configJsonHint: "编辑配置 JSON。点击服务区的“保存配置”按钮后侘存更改。";
|
|
250
|
+
readonly formatJson: "格式化";
|
|
251
|
+
readonly resetJson: "重置";
|
|
252
|
+
readonly saveBtn: "保存";
|
|
253
|
+
readonly jsonValid: "JSON 有效";
|
|
254
|
+
readonly jsonInvalid: "JSON 无效:{error}";
|
|
235
255
|
};
|
|
236
256
|
};
|
|
@@ -118,6 +118,16 @@ export const PAGE_TEXTS = {
|
|
|
118
118
|
saveOk: "Configuration saved.",
|
|
119
119
|
startOk: "Bridge started.",
|
|
120
120
|
stopOk: "Bridge stopped.",
|
|
121
|
+
configEditorTitle: "Config Editor",
|
|
122
|
+
configEditorHint: "Edit ~/.open-im/config.json directly",
|
|
123
|
+
claudeSettingsLabel: "~/.claude/settings.json",
|
|
124
|
+
configJson: "~/.open-im/config.json",
|
|
125
|
+
configJsonHint: "Edit the configuration JSON. Changes will be saved when you click \"Save Config\" in the Service section.",
|
|
126
|
+
formatJson: "Format",
|
|
127
|
+
resetJson: "Reset",
|
|
128
|
+
saveBtn: "Save",
|
|
129
|
+
jsonValid: "Valid JSON",
|
|
130
|
+
jsonInvalid: "Invalid JSON: {error}",
|
|
121
131
|
},
|
|
122
132
|
zh: {
|
|
123
133
|
pageTitle: "open-im \u672c\u5730\u63a7\u5236\u53f0",
|
|
@@ -232,5 +242,15 @@ export const PAGE_TEXTS = {
|
|
|
232
242
|
saveOk: "\u914d\u7f6e\u5df2\u4fdd\u5b58\u3002",
|
|
233
243
|
startOk: "\u6865\u63a5\u5df2\u542f\u52a8\u3002",
|
|
234
244
|
stopOk: "\u6865\u63a5\u5df2\u505c\u6b62\u3002",
|
|
245
|
+
configEditorTitle: "JSON \u914d\u7f6e\u7f16\u8f91\u5668",
|
|
246
|
+
configEditorHint: "\u76f4\u63a5\u7f16\u8f91 ~/.open-im/config.json",
|
|
247
|
+
claudeSettingsLabel: "~/.claude/settings.json",
|
|
248
|
+
configJson: "~/.open-im/config.json",
|
|
249
|
+
configJsonHint: "\u7f16\u8f91\u914d\u7f6e JSON\u3002\u70b9\u51fb\u670d\u52a1\u533a\u7684\u201c\u4fdd\u5b58\u914d\u7f6e\u201d\u6309\u94ae\u540e\u4f98\u5b58\u66f4\u6539\u3002",
|
|
250
|
+
formatJson: "\u683c\u5f0f\u5316",
|
|
251
|
+
resetJson: "\u91cd\u7f6e",
|
|
252
|
+
saveBtn: "\u4fdd\u5b58",
|
|
253
|
+
jsonValid: "JSON \u6709\u6548",
|
|
254
|
+
jsonInvalid: "JSON \u65e0\u6548\uff1a{error}",
|
|
235
255
|
}
|
|
236
256
|
};
|
|
@@ -159,6 +159,12 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
|
|
|
159
159
|
{ id: "statConfiguredLabel", key: "statConfiguredLabel" },
|
|
160
160
|
{ id: "statEnabledLabel", key: "statEnabledLabel" },
|
|
161
161
|
{ id: "statServiceLabel", key: "statServiceLabel" },
|
|
162
|
+
{ id: "openImConfigSummary", key: "configJson" },
|
|
163
|
+
{ id: "claudeSettingsSummary", key: "claudeSettingsLabel" },
|
|
164
|
+
{ id: "formatJsonButtonText", key: "formatJson" },
|
|
165
|
+
{ id: "resetJsonButtonText", key: "resetJson" },
|
|
166
|
+
{ id: "saveClaudeSettingsBtnText", key: "saveBtn" },
|
|
167
|
+
{ id: "saveOpenImConfigBtnText", key: "saveBtn" },
|
|
162
168
|
],
|
|
163
169
|
platformLabels: {
|
|
164
170
|
enabled: { suffix: "-label", key: "enabled" },
|
|
@@ -445,6 +451,70 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
|
|
|
445
451
|
}
|
|
446
452
|
}
|
|
447
453
|
|
|
454
|
+
// Open-im Config Editor functions
|
|
455
|
+
let originalConfigJson = "";
|
|
456
|
+
|
|
457
|
+
async function loadOpenImConfig() {
|
|
458
|
+
const textarea = document.getElementById("configJson");
|
|
459
|
+
if (!(textarea instanceof HTMLTextAreaElement)) return;
|
|
460
|
+
try {
|
|
461
|
+
const data = await request("/api/config/file");
|
|
462
|
+
const raw = (data.contents || "").trim();
|
|
463
|
+
originalConfigJson = raw;
|
|
464
|
+
try {
|
|
465
|
+
const parsed = JSON.parse(raw);
|
|
466
|
+
textarea.value = JSON.stringify(parsed, null, 2) + "\n";
|
|
467
|
+
} catch {
|
|
468
|
+
textarea.value = raw;
|
|
469
|
+
}
|
|
470
|
+
validateJson();
|
|
471
|
+
} catch (error) {
|
|
472
|
+
showJsonValidationMessage(error.message || String(error), "error");
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function validateJson() {
|
|
477
|
+
const textarea = document.getElementById("configJson");
|
|
478
|
+
const message = document.getElementById("jsonValidationMessage");
|
|
479
|
+
if (!(textarea instanceof HTMLTextAreaElement) || !message) return;
|
|
480
|
+
|
|
481
|
+
const json = textarea.value;
|
|
482
|
+
try {
|
|
483
|
+
JSON.parse(json);
|
|
484
|
+
showJsonValidationMessage("Valid JSON", "success");
|
|
485
|
+
} catch (err) {
|
|
486
|
+
showJsonValidationMessage("Invalid JSON: " + (err.message || String(err)), "error");
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function showJsonValidationMessage(text, type) {
|
|
491
|
+
const message = document.getElementById("jsonValidationMessage");
|
|
492
|
+
if (!message) return;
|
|
493
|
+
message.textContent = text;
|
|
494
|
+
message.className = "message";
|
|
495
|
+
if (type) message.classList.add("message-" + type);
|
|
496
|
+
message.classList.remove("hidden");
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function formatJson() {
|
|
500
|
+
const textarea = document.getElementById("configJson");
|
|
501
|
+
if (!(textarea instanceof HTMLTextAreaElement)) return;
|
|
502
|
+
try {
|
|
503
|
+
const parsed = JSON.parse(textarea.value);
|
|
504
|
+
textarea.value = JSON.stringify(parsed, null, 2) + "\n";
|
|
505
|
+
validateJson();
|
|
506
|
+
} catch (err) {
|
|
507
|
+
showJsonValidationMessage("Cannot format: Invalid JSON", "error");
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function resetJson() {
|
|
512
|
+
const textarea = document.getElementById("configJson");
|
|
513
|
+
if (!(textarea instanceof HTMLTextAreaElement)) return;
|
|
514
|
+
textarea.value = originalConfigJson;
|
|
515
|
+
validateJson();
|
|
516
|
+
}
|
|
517
|
+
|
|
448
518
|
// Fill form with data
|
|
449
519
|
const AI_FIELD_MAPPINGS = [
|
|
450
520
|
{ id: "ai-aiCommand", key: "aiCommand" },
|
|
@@ -475,6 +545,9 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
|
|
|
475
545
|
}
|
|
476
546
|
});
|
|
477
547
|
|
|
548
|
+
// Load JSON editor content
|
|
549
|
+
void loadOpenImConfig();
|
|
550
|
+
|
|
478
551
|
updateVisualState();
|
|
479
552
|
}
|
|
480
553
|
|
|
@@ -537,16 +610,37 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
|
|
|
537
610
|
}
|
|
538
611
|
});
|
|
539
612
|
|
|
540
|
-
//
|
|
613
|
+
// Open-im config.json: load when expanded
|
|
614
|
+
const openImConfigContainer = document.getElementById("openImConfigContainer");
|
|
615
|
+
if (openImConfigContainer && openImConfigContainer instanceof HTMLDetailsElement) {
|
|
616
|
+
openImConfigContainer.addEventListener("toggle", () => {
|
|
617
|
+
if (openImConfigContainer.open) void loadOpenImConfig();
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
// Claude settings.json: load when expanded
|
|
541
621
|
const claudeSettingsContainer = document.getElementById("claudeSettingsContainer");
|
|
542
622
|
if (claudeSettingsContainer && claudeSettingsContainer instanceof HTMLDetailsElement) {
|
|
543
623
|
claudeSettingsContainer.addEventListener("toggle", () => {
|
|
544
|
-
if (claudeSettingsContainer.open)
|
|
545
|
-
void loadClaudeSettings();
|
|
546
|
-
}
|
|
624
|
+
if (claudeSettingsContainer.open) void loadClaudeSettings();
|
|
547
625
|
});
|
|
548
626
|
}
|
|
549
627
|
|
|
628
|
+
el("saveClaudeSettingsBtn").onclick = async () => {
|
|
629
|
+
try {
|
|
630
|
+
await saveClaudeSettings();
|
|
631
|
+
} catch (e) {
|
|
632
|
+
setMessage(e && e.message ? e.message : String(e), "error");
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
el("saveOpenImConfigBtn").onclick = async () => {
|
|
636
|
+
try {
|
|
637
|
+
await saveOpenImConfig();
|
|
638
|
+
setMessage(t("saveOk"), "success");
|
|
639
|
+
} catch (e) {
|
|
640
|
+
setMessage(e && e.message ? e.message : String(e), "error");
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
|
|
550
644
|
// AI tool switcher
|
|
551
645
|
document.querySelectorAll(".tab[data-tool]").forEach((tab) => {
|
|
552
646
|
tab.addEventListener("click", () => {
|
|
@@ -558,6 +652,24 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
|
|
|
558
652
|
});
|
|
559
653
|
});
|
|
560
654
|
|
|
655
|
+
// Config editor JSON textarea
|
|
656
|
+
const configJsonTextarea = document.getElementById("configJson");
|
|
657
|
+
if (configJsonTextarea) {
|
|
658
|
+
configJsonTextarea.addEventListener("input", validateJson);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Config editor buttons
|
|
662
|
+
const formatJsonButton = document.getElementById("formatJsonButton");
|
|
663
|
+
if (formatJsonButton) {
|
|
664
|
+
formatJsonButton.addEventListener("click", formatJson);
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
const resetJsonButton = document.getElementById("resetJsonButton");
|
|
668
|
+
if (resetJsonButton) {
|
|
669
|
+
resetJsonButton.addEventListener("click", resetJson);
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
|
|
561
673
|
// Navigation
|
|
562
674
|
el("navOverviewBtn").onclick = () => scrollToSection("dashboardSection", "navOverviewBtn");
|
|
563
675
|
el("navPlatformsBtn").onclick = () => scrollToSection("configSection", "navPlatformsBtn");
|
|
@@ -617,6 +729,9 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
|
|
|
617
729
|
async function save() {
|
|
618
730
|
setBusy(true);
|
|
619
731
|
try {
|
|
732
|
+
// First save JSON editor content if changed
|
|
733
|
+
await saveOpenImConfig();
|
|
734
|
+
// Then save form data
|
|
620
735
|
await request("/api/config/save?final=1", { method: "POST", body: JSON.stringify(payload()) });
|
|
621
736
|
setMessage(t("saveOk"), "success");
|
|
622
737
|
} catch (error) {
|
|
@@ -626,6 +741,32 @@ export const PAGE_SCRIPT = String.raw ` const platformDefinitions = [
|
|
|
626
741
|
}
|
|
627
742
|
}
|
|
628
743
|
|
|
744
|
+
async function saveOpenImConfig() {
|
|
745
|
+
const textarea = document.getElementById("configJson");
|
|
746
|
+
if (!(textarea instanceof HTMLTextAreaElement)) return;
|
|
747
|
+
const json = textarea.value.trim();
|
|
748
|
+
if (!json) return;
|
|
749
|
+
|
|
750
|
+
// Validate JSON
|
|
751
|
+
try {
|
|
752
|
+
JSON.parse(json);
|
|
753
|
+
} catch (err) {
|
|
754
|
+
showJsonValidationMessage("Invalid JSON: " + (err.message || String(err)), "error");
|
|
755
|
+
throw new Error("Invalid JSON: " + (err.message || String(err)));
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
try {
|
|
759
|
+
await request("/api/config/file", {
|
|
760
|
+
method: "POST",
|
|
761
|
+
body: JSON.stringify({ contents: json }),
|
|
762
|
+
});
|
|
763
|
+
originalConfigJson = json;
|
|
764
|
+
} catch (error) {
|
|
765
|
+
showJsonValidationMessage(error.message || String(error), "error");
|
|
766
|
+
throw error;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
629
770
|
async function startService() {
|
|
630
771
|
setBusy(true);
|
|
631
772
|
try {
|
|
@@ -1128,18 +1128,31 @@ export const PAGE_HTML_PREFIX = String.raw `<!doctype html>
|
|
|
1128
1128
|
<div class="form-hint" id="ai-claudeConfigPath-hint">Environment variables are saved to ~/.claude/settings.json</div>
|
|
1129
1129
|
</div>
|
|
1130
1130
|
<div class="form-group">
|
|
1131
|
-
<details id="
|
|
1132
|
-
<summary class="form-label" style="cursor: pointer;"
|
|
1133
|
-
<div
|
|
1134
|
-
|
|
1131
|
+
<details id="openImConfigContainer">
|
|
1132
|
+
<summary class="form-label" style="cursor: pointer;" id="openImConfigSummary">~/.open-im/config.json</summary>
|
|
1133
|
+
<div style="margin-top: 12px;">
|
|
1134
|
+
<div style="display:flex; justify-content:flex-end; gap:8px; margin-bottom:8px;">
|
|
1135
|
+
<button type="button" id="formatJsonButton" class="btn btn-sm btn-ghost"><span id="formatJsonButtonText">Format</span></button>
|
|
1136
|
+
<button type="button" id="resetJsonButton" class="btn btn-sm btn-ghost"><span id="resetJsonButtonText">Reset</span></button>
|
|
1137
|
+
</div>
|
|
1138
|
+
<textarea id="configJson" class="form-input mono" rows="16" style="font-family:monospace; font-size:13px; line-height:1.5; min-height:320px; resize:vertical; white-space:pre;" spellcheck="false"></textarea>
|
|
1139
|
+
<div id="jsonValidationMessage" class="message hidden" style="margin-top:6px;" aria-live="polite"></div>
|
|
1140
|
+
<div style="margin-top: 8px;">
|
|
1141
|
+
<button type="button" id="saveOpenImConfigBtn" class="btn btn-secondary btn-sm"><span id="saveOpenImConfigBtnText">Save</span></button>
|
|
1142
|
+
</div>
|
|
1135
1143
|
</div>
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1144
|
+
</details>
|
|
1145
|
+
<details id="claudeSettingsContainer" style="margin-top: 12px;">
|
|
1146
|
+
<summary class="form-label" style="cursor: pointer;" id="claudeSettingsSummary">~/.claude/settings.json</summary>
|
|
1147
|
+
<div style="margin-top: 12px;">
|
|
1148
|
+
<textarea
|
|
1149
|
+
id="claudeSettingsEditor"
|
|
1150
|
+
class="form-input mono"
|
|
1151
|
+
style="min-height: 180px; white-space: pre; font-family: var(--font-mono);"
|
|
1152
|
+
></textarea>
|
|
1153
|
+
<div style="margin-top: 8px;">
|
|
1154
|
+
<button type="button" id="saveClaudeSettingsBtn" class="btn btn-secondary btn-sm"><span id="saveClaudeSettingsBtnText">Save</span></button>
|
|
1155
|
+
</div>
|
|
1143
1156
|
</div>
|
|
1144
1157
|
</details>
|
|
1145
1158
|
</div>
|
|
@@ -1196,6 +1209,7 @@ export const PAGE_HTML_PREFIX = String.raw `<!doctype html>
|
|
|
1196
1209
|
</div>
|
|
1197
1210
|
</div>
|
|
1198
1211
|
</section>
|
|
1212
|
+
|
|
1199
1213
|
</div>
|
|
1200
1214
|
</main>
|
|
1201
1215
|
</div>
|
package/dist/config-web.js
CHANGED
|
@@ -673,6 +673,47 @@ export async function startWebConfigServer(options) {
|
|
|
673
673
|
});
|
|
674
674
|
return;
|
|
675
675
|
}
|
|
676
|
+
if (request.method === "GET" && requestUrl.pathname === "/api/config/file") {
|
|
677
|
+
try {
|
|
678
|
+
let contents = "{}";
|
|
679
|
+
if (existsSync(CONFIG_PATH)) {
|
|
680
|
+
contents = readFileSync(CONFIG_PATH, "utf-8");
|
|
681
|
+
}
|
|
682
|
+
json(response, 200, { path: CONFIG_PATH, contents });
|
|
683
|
+
}
|
|
684
|
+
catch (error) {
|
|
685
|
+
json(response, 500, { error: error instanceof Error ? error.message : String(error) });
|
|
686
|
+
}
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
if (request.method === "POST" && requestUrl.pathname === "/api/config/file") {
|
|
690
|
+
try {
|
|
691
|
+
const body = await readJson(request);
|
|
692
|
+
const raw = body.contents ?? "";
|
|
693
|
+
if (!raw.trim()) {
|
|
694
|
+
json(response, 400, { error: "contents is required" });
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
try {
|
|
698
|
+
JSON.parse(raw);
|
|
699
|
+
}
|
|
700
|
+
catch {
|
|
701
|
+
json(response, 400, { error: "Invalid JSON" });
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
const dir = dirname(CONFIG_PATH);
|
|
705
|
+
if (!existsSync(dir)) {
|
|
706
|
+
mkdirSync(dir, { recursive: true });
|
|
707
|
+
}
|
|
708
|
+
writeFileSync(CONFIG_PATH, raw, "utf-8");
|
|
709
|
+
loadConfig();
|
|
710
|
+
json(response, 200, { message: "Config file saved.", path: CONFIG_PATH });
|
|
711
|
+
}
|
|
712
|
+
catch (error) {
|
|
713
|
+
json(response, 500, { error: error instanceof Error ? error.message : String(error) });
|
|
714
|
+
}
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
676
717
|
if (request.method === "POST" && requestUrl.pathname === "/api/config/validate") {
|
|
677
718
|
try {
|
|
678
719
|
const body = await readJson(request);
|