thepopebot 1.2.75-beta.2 → 1.2.75-beta.21
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 +1 -1
- package/api/CLAUDE.md +1 -1
- package/api/index.js +5 -12
- package/bin/CLAUDE.md +1 -1
- package/bin/cli.js +329 -14
- package/bin/docker-build.js +5 -0
- package/bin/managed-paths.js +0 -7
- package/bin/sync.js +84 -0
- package/config/CLAUDE.md +1 -29
- package/config/instrumentation.js +1 -1
- package/lib/CLAUDE.md +3 -3
- package/lib/ai/CLAUDE.md +24 -3
- package/lib/ai/agent.js +8 -5
- package/lib/ai/async-channel.js +51 -0
- package/lib/ai/headless-stream.js +3 -0
- package/lib/ai/index.js +149 -173
- package/lib/ai/line-mappers.js +72 -9
- package/lib/ai/tools.js +40 -28
- package/lib/chat/actions.js +34 -6
- package/lib/chat/api.js +17 -1
- package/lib/chat/components/chat-header.js +4 -0
- package/lib/chat/components/chat-header.jsx +4 -0
- package/lib/chat/components/chat-input.js +1 -0
- package/lib/chat/components/chat-input.jsx +1 -0
- package/lib/chat/components/chat.js +9 -1
- package/lib/chat/components/chat.jsx +15 -2
- package/lib/chat/components/chats-page.js +3 -3
- package/lib/chat/components/chats-page.jsx +4 -6
- package/lib/chat/components/crons-page.js +1 -1
- package/lib/chat/components/crons-page.jsx +1 -1
- package/lib/chat/components/message.js +12 -4
- package/lib/chat/components/message.jsx +17 -4
- package/lib/chat/components/settings-chat-page.js +2 -1
- package/lib/chat/components/settings-chat-page.jsx +4 -1
- package/lib/chat/components/settings-coding-agents-page.js +139 -1
- package/lib/chat/components/settings-coding-agents-page.jsx +160 -0
- package/lib/chat/components/settings-jobs-page.js +13 -2
- package/lib/chat/components/settings-jobs-page.jsx +15 -1
- package/lib/chat/components/settings-secrets-layout.js +1 -1
- package/lib/chat/components/settings-secrets-layout.jsx +1 -1
- package/lib/chat/components/sidebar-history-item.js +3 -3
- package/lib/chat/components/sidebar-history-item.jsx +4 -6
- package/lib/chat/components/triggers-page.js +1 -1
- package/lib/chat/components/triggers-page.jsx +1 -1
- package/lib/cluster/actions.js +4 -4
- package/lib/cluster/execute.js +3 -1
- package/lib/code/actions.js +34 -11
- package/lib/code/code-page.js +40 -40
- package/lib/code/code-page.jsx +36 -36
- package/lib/code/port-forwards.js +17 -3
- package/lib/code/terminal-view.js +16 -0
- package/lib/code/terminal-view.jsx +18 -0
- package/lib/config.js +4 -0
- package/lib/cron.js +3 -3
- package/lib/db/api-keys.js +22 -61
- package/lib/db/config.js +23 -0
- package/lib/db/index.js +3 -1
- package/lib/maintenance.js +34 -11
- package/lib/paths.js +1 -38
- package/lib/tools/create-agent-job.js +0 -4
- package/lib/tools/docker.js +23 -16
- package/lib/triggers.js +4 -3
- package/lib/utils/render-md.js +3 -1
- package/package.json +2 -1
- package/setup/setup-ssl.mjs +414 -0
- package/templates/.github/workflows/rebuild-event-handler.yml +3 -0
- package/templates/.github/workflows/upgrade-event-handler.yml +1 -1
- package/templates/.gitignore.template +7 -3
- package/templates/.tmp/CLAUDE.md.template +5 -0
- package/templates/CLAUDE.md +3 -2
- package/templates/CLAUDE.md.template +24 -357
- package/templates/agent-job/CLAUDE.md.template +57 -0
- package/templates/agent-job/CRONS.json +16 -0
- package/templates/{config/agent-job → agent-job}/SOUL.md +3 -3
- package/templates/agent-job/SYSTEM.md +60 -0
- package/templates/agents/CLAUDE.md.template +54 -0
- package/templates/data/CLAUDE.md.template +5 -0
- package/templates/docker-compose.custom.yml +41 -62
- package/templates/docker-compose.yml +14 -21
- package/templates/event-handler/CLAUDE.md.template +0 -0
- package/templates/logs/CLAUDE.md.template +5 -0
- package/templates/skills/CLAUDE.md.template +57 -32
- package/templates/skills/active/.gitkeep +0 -0
- package/templates/skills/library/agent-job-secrets/SKILL.md +23 -0
- package/templates/skills/library/agent-job-secrets/agent-job-secrets.js +62 -0
- package/templates/.pi/extensions/env-sanitizer/index.ts +0 -48
- package/templates/.pi/extensions/env-sanitizer/package.json +0 -5
- package/templates/README.md +0 -75
- package/templates/config/CLAUDE.md.template +0 -40
- package/templates/config/CRONS.json +0 -56
- package/templates/config/agent-job/AGENT_JOB.md +0 -30
- package/templates/cron/CLAUDE.md.template +0 -24
- package/templates/docker-compose.litellm.yml +0 -82
- package/templates/docs/CLAUDE.md.template +0 -12
- package/templates/docs/CLI.md +0 -59
- package/templates/docs/CLUSTERS.md +0 -151
- package/templates/docs/CONFIGURATION.md +0 -181
- package/templates/docs/CRONS_AND_TRIGGERS.md +0 -132
- package/templates/docs/GETTING_STARTED.md +0 -64
- package/templates/docs/SECURITY.md +0 -61
- package/templates/docs/SKILLS.md +0 -113
- package/templates/docs/UPGRADING.md +0 -92
- package/templates/skills/LICENSE +0 -21
- package/templates/skills/README.md +0 -117
- package/templates/skills/agent-job-secrets/SKILL.md +0 -25
- package/templates/skills/agent-job-secrets/agent-job-secrets.js +0 -66
- package/templates/traefik-dynamic.yml.example +0 -7
- package/templates/triggers/CLAUDE.md.template +0 -41
- /package/templates/{config → agent-job}/HEARTBEAT.md +0 -0
- /package/templates/{cron → data}/.gitkeep +0 -0
- /package/templates/{logs → data/clusters}/.gitkeep +0 -0
- /package/templates/{triggers → data/db}/.gitkeep +0 -0
- /package/templates/{config/agent-job → event-handler}/SUMMARY.md +0 -0
- /package/templates/{config → event-handler}/TRIGGERS.json +0 -0
- /package/templates/{config → event-handler}/agent-chat/SYSTEM.md +0 -0
- /package/templates/{config/cluster → event-handler/clusters}/ROLE.md +0 -0
- /package/templates/{config/cluster → event-handler/clusters}/SYSTEM.md +0 -0
- /package/templates/{config → event-handler}/code-chat/SYSTEM.md +0 -0
- /package/templates/{config → event-handler}/litellm/main.yaml +0 -0
- /package/templates/skills/{playwright-cli → library/playwright-cli}/SKILL.md +0 -0
|
@@ -52,6 +52,9 @@ function DefaultAgentSection({ settings, onReload }) {
|
|
|
52
52
|
if (settings.openCode?.enabled && isOpenCodeReady(settings)) {
|
|
53
53
|
available.push({ value: "opencode", label: "OpenCode" });
|
|
54
54
|
}
|
|
55
|
+
if (settings.kimiCli?.enabled && isKimiCliReady(settings)) {
|
|
56
|
+
available.push({ value: "kimi-cli", label: "Kimi CLI" });
|
|
57
|
+
}
|
|
55
58
|
const handleChange = async (e) => {
|
|
56
59
|
setSaving(true);
|
|
57
60
|
const result = await setCodingAgentDefault(e.target.value);
|
|
@@ -99,7 +102,8 @@ function AgentCards({ settings, onReload }) {
|
|
|
99
102
|
/* @__PURE__ */ jsx(PiCard, { settings, onReload }),
|
|
100
103
|
/* @__PURE__ */ jsx(GeminiCliCard, { settings, onReload }),
|
|
101
104
|
/* @__PURE__ */ jsx(CodexCliCard, { settings, onReload }),
|
|
102
|
-
/* @__PURE__ */ jsx(OpenCodeCard, { settings, onReload })
|
|
105
|
+
/* @__PURE__ */ jsx(OpenCodeCard, { settings, onReload }),
|
|
106
|
+
/* @__PURE__ */ jsx(KimiCliCard, { settings, onReload })
|
|
103
107
|
] })
|
|
104
108
|
] });
|
|
105
109
|
}
|
|
@@ -687,6 +691,136 @@ function OpenCodeCard({ settings, onReload }) {
|
|
|
687
691
|
) })
|
|
688
692
|
] });
|
|
689
693
|
}
|
|
694
|
+
function KimiCliCard({ settings, onReload }) {
|
|
695
|
+
const config = settings.kimiCli;
|
|
696
|
+
const [modelText, setModelText] = useState(config.model || "");
|
|
697
|
+
const [saved, setSaved] = useState(false);
|
|
698
|
+
const showSaved = () => {
|
|
699
|
+
setSaved(true);
|
|
700
|
+
setTimeout(() => setSaved(false), 2e3);
|
|
701
|
+
};
|
|
702
|
+
const handleToggle = async () => {
|
|
703
|
+
await updateCodingAgentConfig("kimi-cli", { enabled: !config.enabled });
|
|
704
|
+
await onReload();
|
|
705
|
+
};
|
|
706
|
+
const handleProviderChange = async (e) => {
|
|
707
|
+
const newProvider = e.target.value;
|
|
708
|
+
const cp = settings?.customProviders?.find((p) => p.key === newProvider);
|
|
709
|
+
const models = getAgentModels(settings, newProvider);
|
|
710
|
+
const newModel = models.length > 0 ? models[0].id : cp?.models?.[0] || "";
|
|
711
|
+
setModelText(newModel);
|
|
712
|
+
await updateCodingAgentConfig("kimi-cli", { provider: newProvider, model: newModel });
|
|
713
|
+
await onReload();
|
|
714
|
+
showSaved();
|
|
715
|
+
};
|
|
716
|
+
const handleModelChange = async (e) => {
|
|
717
|
+
await updateCodingAgentConfig("kimi-cli", { model: e.target.value });
|
|
718
|
+
await onReload();
|
|
719
|
+
showSaved();
|
|
720
|
+
};
|
|
721
|
+
const handleModelTextSave = async () => {
|
|
722
|
+
await updateCodingAgentConfig("kimi-cli", { model: modelText });
|
|
723
|
+
await onReload();
|
|
724
|
+
showSaved();
|
|
725
|
+
};
|
|
726
|
+
const availableProviders = [];
|
|
727
|
+
if (settings?.builtinProviders && settings?.credentialStatuses) {
|
|
728
|
+
const statusMap = new Map(settings.credentialStatuses.map((s) => [s.key, s.isSet]));
|
|
729
|
+
for (const [slug, prov] of Object.entries(settings.builtinProviders)) {
|
|
730
|
+
const hasKey = prov.credentials.some((c) => statusMap.get(c.key));
|
|
731
|
+
if (hasKey) {
|
|
732
|
+
availableProviders.push({ slug, name: prov.name });
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
if (settings?.customProviders) {
|
|
737
|
+
for (const cp of settings.customProviders) {
|
|
738
|
+
availableProviders.push({ slug: cp.key, name: cp.name });
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
const ready = isKimiCliReady(settings);
|
|
742
|
+
const selectedProviderReady = availableProviders.some((p) => p.slug === config.provider);
|
|
743
|
+
const builtinModels = config.provider ? getAgentModels(settings, config.provider) : [];
|
|
744
|
+
const customProvider = settings?.customProviders?.find((p) => p.key === config.provider);
|
|
745
|
+
const providerModels = builtinModels.length > 0 ? builtinModels : (customProvider?.models || []).map((m) => ({ id: m, name: m }));
|
|
746
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4", children: [
|
|
747
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-1", children: [
|
|
748
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
749
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Kimi CLI" }),
|
|
750
|
+
config.enabled && /* @__PURE__ */ jsx(StatusDot, { ready })
|
|
751
|
+
] }),
|
|
752
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
753
|
+
saved && /* @__PURE__ */ jsxs("span", { className: "text-xs text-green-500 inline-flex items-center gap-1", children: [
|
|
754
|
+
/* @__PURE__ */ jsx(CheckIcon, { size: 12 }),
|
|
755
|
+
" Saved"
|
|
756
|
+
] }),
|
|
757
|
+
/* @__PURE__ */ jsx(ToggleSwitch, { checked: config.enabled, onChange: handleToggle })
|
|
758
|
+
] })
|
|
759
|
+
] }),
|
|
760
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mb-3", children: "Coding agent by MoonshotAI with multi-provider support." }),
|
|
761
|
+
config.enabled && /* @__PURE__ */ jsx("div", { className: "border-t border-border pt-3 space-y-3", children: availableProviders.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
762
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
763
|
+
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: "Provider" }),
|
|
764
|
+
/* @__PURE__ */ jsxs(
|
|
765
|
+
"select",
|
|
766
|
+
{
|
|
767
|
+
value: config.provider || "",
|
|
768
|
+
onChange: handleProviderChange,
|
|
769
|
+
className: "w-48 rounded-md border border-border bg-background px-3 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-foreground",
|
|
770
|
+
children: [
|
|
771
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Select provider..." }),
|
|
772
|
+
availableProviders.map((p) => /* @__PURE__ */ jsx("option", { value: p.slug, children: p.name }, p.slug))
|
|
773
|
+
]
|
|
774
|
+
}
|
|
775
|
+
)
|
|
776
|
+
] }),
|
|
777
|
+
config.provider && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
778
|
+
/* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: "Model" }),
|
|
779
|
+
providerModels.length > 0 ? /* @__PURE__ */ jsx(
|
|
780
|
+
"select",
|
|
781
|
+
{
|
|
782
|
+
value: config.model || "",
|
|
783
|
+
onChange: handleModelChange,
|
|
784
|
+
className: "w-48 rounded-md border border-border bg-background px-3 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-foreground",
|
|
785
|
+
children: providerModels.map((m) => /* @__PURE__ */ jsx("option", { value: m.id, children: m.name }, m.id))
|
|
786
|
+
}
|
|
787
|
+
) : /* @__PURE__ */ jsx(
|
|
788
|
+
"input",
|
|
789
|
+
{
|
|
790
|
+
type: "text",
|
|
791
|
+
value: modelText,
|
|
792
|
+
onChange: (e) => setModelText(e.target.value),
|
|
793
|
+
onKeyDown: (e) => e.key === "Enter" && handleModelTextSave(),
|
|
794
|
+
placeholder: "Model name",
|
|
795
|
+
className: "w-48 rounded-md border border-border bg-background px-3 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-foreground"
|
|
796
|
+
}
|
|
797
|
+
)
|
|
798
|
+
] }),
|
|
799
|
+
config.provider && !selectedProviderReady && /* @__PURE__ */ jsx(
|
|
800
|
+
CredentialHint,
|
|
801
|
+
{
|
|
802
|
+
ready: false,
|
|
803
|
+
missingText: `${config.provider} API Key is not set. Configure it on the LLMs page.`
|
|
804
|
+
}
|
|
805
|
+
),
|
|
806
|
+
config.provider && providerModels.length === 0 && /* @__PURE__ */ jsx("div", { className: "flex justify-end mt-1", children: /* @__PURE__ */ jsx(
|
|
807
|
+
"button",
|
|
808
|
+
{
|
|
809
|
+
onClick: handleModelTextSave,
|
|
810
|
+
disabled: modelText === (config.model || ""),
|
|
811
|
+
className: "rounded-md px-3 py-1.5 text-sm font-medium bg-foreground text-background hover:bg-foreground/90 disabled:opacity-50 transition-colors",
|
|
812
|
+
children: "Save"
|
|
813
|
+
}
|
|
814
|
+
) })
|
|
815
|
+
] }) : /* @__PURE__ */ jsx(
|
|
816
|
+
CredentialHint,
|
|
817
|
+
{
|
|
818
|
+
ready: false,
|
|
819
|
+
missingText: "Configure at least one LLM provider on the LLMs page to use Kimi CLI"
|
|
820
|
+
}
|
|
821
|
+
) })
|
|
822
|
+
] });
|
|
823
|
+
}
|
|
690
824
|
function getAgentModels(settings, providerSlug) {
|
|
691
825
|
const provider = settings?.builtinProviders?.[providerSlug];
|
|
692
826
|
if (!provider?.models) return [];
|
|
@@ -720,6 +854,10 @@ function isOpenCodeReady(settings) {
|
|
|
720
854
|
if (!settings.openCode?.enabled || !settings.openCode?.provider) return false;
|
|
721
855
|
return isProviderReady(settings, settings.openCode.provider);
|
|
722
856
|
}
|
|
857
|
+
function isKimiCliReady(settings) {
|
|
858
|
+
if (!settings.kimiCli?.enabled || !settings.kimiCli?.provider) return false;
|
|
859
|
+
return isProviderReady(settings, settings.kimiCli.provider);
|
|
860
|
+
}
|
|
723
861
|
function isProviderReady(settings, provider) {
|
|
724
862
|
const statusMap = new Map((settings.credentialStatuses || []).map((s) => [s.key, s.isSet]));
|
|
725
863
|
const builtin = settings.builtinProviders?.[provider];
|
|
@@ -72,6 +72,9 @@ function DefaultAgentSection({ settings, onReload }) {
|
|
|
72
72
|
if (settings.openCode?.enabled && isOpenCodeReady(settings)) {
|
|
73
73
|
available.push({ value: 'opencode', label: 'OpenCode' });
|
|
74
74
|
}
|
|
75
|
+
if (settings.kimiCli?.enabled && isKimiCliReady(settings)) {
|
|
76
|
+
available.push({ value: 'kimi-cli', label: 'Kimi CLI' });
|
|
77
|
+
}
|
|
75
78
|
|
|
76
79
|
const handleChange = async (e) => {
|
|
77
80
|
setSaving(true);
|
|
@@ -133,6 +136,7 @@ function AgentCards({ settings, onReload }) {
|
|
|
133
136
|
<GeminiCliCard settings={settings} onReload={onReload} />
|
|
134
137
|
<CodexCliCard settings={settings} onReload={onReload} />
|
|
135
138
|
<OpenCodeCard settings={settings} onReload={onReload} />
|
|
139
|
+
<KimiCliCard settings={settings} onReload={onReload} />
|
|
136
140
|
</div>
|
|
137
141
|
</div>
|
|
138
142
|
);
|
|
@@ -834,6 +838,157 @@ function OpenCodeCard({ settings, onReload }) {
|
|
|
834
838
|
);
|
|
835
839
|
}
|
|
836
840
|
|
|
841
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
842
|
+
// Kimi CLI card
|
|
843
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
844
|
+
|
|
845
|
+
function KimiCliCard({ settings, onReload }) {
|
|
846
|
+
const config = settings.kimiCli;
|
|
847
|
+
const [modelText, setModelText] = useState(config.model || '');
|
|
848
|
+
const [saved, setSaved] = useState(false);
|
|
849
|
+
|
|
850
|
+
const showSaved = () => {
|
|
851
|
+
setSaved(true);
|
|
852
|
+
setTimeout(() => setSaved(false), 2000);
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
const handleToggle = async () => {
|
|
856
|
+
await updateCodingAgentConfig('kimi-cli', { enabled: !config.enabled });
|
|
857
|
+
await onReload();
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
const handleProviderChange = async (e) => {
|
|
861
|
+
const newProvider = e.target.value;
|
|
862
|
+
const cp = settings?.customProviders?.find((p) => p.key === newProvider);
|
|
863
|
+
const models = getAgentModels(settings, newProvider);
|
|
864
|
+
const newModel = models.length > 0 ? models[0].id : (cp?.models?.[0] || '');
|
|
865
|
+
setModelText(newModel);
|
|
866
|
+
await updateCodingAgentConfig('kimi-cli', { provider: newProvider, model: newModel });
|
|
867
|
+
await onReload();
|
|
868
|
+
showSaved();
|
|
869
|
+
};
|
|
870
|
+
|
|
871
|
+
const handleModelChange = async (e) => {
|
|
872
|
+
await updateCodingAgentConfig('kimi-cli', { model: e.target.value });
|
|
873
|
+
await onReload();
|
|
874
|
+
showSaved();
|
|
875
|
+
};
|
|
876
|
+
|
|
877
|
+
const handleModelTextSave = async () => {
|
|
878
|
+
await updateCodingAgentConfig('kimi-cli', { model: modelText });
|
|
879
|
+
await onReload();
|
|
880
|
+
showSaved();
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
const availableProviders = [];
|
|
884
|
+
if (settings?.builtinProviders && settings?.credentialStatuses) {
|
|
885
|
+
const statusMap = new Map(settings.credentialStatuses.map((s) => [s.key, s.isSet]));
|
|
886
|
+
for (const [slug, prov] of Object.entries(settings.builtinProviders)) {
|
|
887
|
+
const hasKey = prov.credentials.some((c) => statusMap.get(c.key));
|
|
888
|
+
if (hasKey) {
|
|
889
|
+
availableProviders.push({ slug, name: prov.name });
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
if (settings?.customProviders) {
|
|
894
|
+
for (const cp of settings.customProviders) {
|
|
895
|
+
availableProviders.push({ slug: cp.key, name: cp.name });
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
const ready = isKimiCliReady(settings);
|
|
900
|
+
const selectedProviderReady = availableProviders.some(p => p.slug === config.provider);
|
|
901
|
+
|
|
902
|
+
const builtinModels = config.provider ? getAgentModels(settings, config.provider) : [];
|
|
903
|
+
const customProvider = settings?.customProviders?.find((p) => p.key === config.provider);
|
|
904
|
+
const providerModels = builtinModels.length > 0 ? builtinModels : (customProvider?.models || []).map((m) => ({ id: m, name: m }));
|
|
905
|
+
|
|
906
|
+
return (
|
|
907
|
+
<div className="rounded-lg border bg-card p-4">
|
|
908
|
+
<div className="flex items-center justify-between mb-1">
|
|
909
|
+
<div className="flex items-center gap-2">
|
|
910
|
+
<span className="text-sm font-medium">Kimi CLI</span>
|
|
911
|
+
{config.enabled && <StatusDot ready={ready} />}
|
|
912
|
+
</div>
|
|
913
|
+
<div className="flex items-center gap-3">
|
|
914
|
+
{saved && <span className="text-xs text-green-500 inline-flex items-center gap-1"><CheckIcon size={12} /> Saved</span>}
|
|
915
|
+
<ToggleSwitch checked={config.enabled} onChange={handleToggle} />
|
|
916
|
+
</div>
|
|
917
|
+
</div>
|
|
918
|
+
<p className="text-xs text-muted-foreground mb-3">Coding agent by MoonshotAI with multi-provider support.</p>
|
|
919
|
+
|
|
920
|
+
{config.enabled && (
|
|
921
|
+
<div className="border-t border-border pt-3 space-y-3">
|
|
922
|
+
{availableProviders.length > 0 ? (
|
|
923
|
+
<>
|
|
924
|
+
<div className="flex items-center justify-between">
|
|
925
|
+
<label className="text-sm font-medium">Provider</label>
|
|
926
|
+
<select
|
|
927
|
+
value={config.provider || ''}
|
|
928
|
+
onChange={handleProviderChange}
|
|
929
|
+
className="w-48 rounded-md border border-border bg-background px-3 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-foreground"
|
|
930
|
+
>
|
|
931
|
+
<option value="">Select provider...</option>
|
|
932
|
+
{availableProviders.map((p) => (
|
|
933
|
+
<option key={p.slug} value={p.slug}>{p.name}</option>
|
|
934
|
+
))}
|
|
935
|
+
</select>
|
|
936
|
+
</div>
|
|
937
|
+
|
|
938
|
+
{config.provider && (
|
|
939
|
+
<div className="flex items-center justify-between">
|
|
940
|
+
<label className="text-sm font-medium">Model</label>
|
|
941
|
+
{providerModels.length > 0 ? (
|
|
942
|
+
<select
|
|
943
|
+
value={config.model || ''}
|
|
944
|
+
onChange={handleModelChange}
|
|
945
|
+
className="w-48 rounded-md border border-border bg-background px-3 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-foreground"
|
|
946
|
+
>
|
|
947
|
+
{providerModels.map((m) => (
|
|
948
|
+
<option key={m.id} value={m.id}>{m.name}</option>
|
|
949
|
+
))}
|
|
950
|
+
</select>
|
|
951
|
+
) : (
|
|
952
|
+
<input
|
|
953
|
+
type="text"
|
|
954
|
+
value={modelText}
|
|
955
|
+
onChange={(e) => setModelText(e.target.value)}
|
|
956
|
+
onKeyDown={(e) => e.key === 'Enter' && handleModelTextSave()}
|
|
957
|
+
placeholder="Model name"
|
|
958
|
+
className="w-48 rounded-md border border-border bg-background px-3 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-foreground"
|
|
959
|
+
/>
|
|
960
|
+
)}
|
|
961
|
+
</div>
|
|
962
|
+
)}
|
|
963
|
+
|
|
964
|
+
{config.provider && !selectedProviderReady && (
|
|
965
|
+
<CredentialHint
|
|
966
|
+
ready={false}
|
|
967
|
+
missingText={`${config.provider} API Key is not set. Configure it on the LLMs page.`}
|
|
968
|
+
/>
|
|
969
|
+
)}
|
|
970
|
+
|
|
971
|
+
{config.provider && providerModels.length === 0 && (
|
|
972
|
+
<div className="flex justify-end mt-1">
|
|
973
|
+
<button onClick={handleModelTextSave} disabled={modelText === (config.model || '')}
|
|
974
|
+
className="rounded-md px-3 py-1.5 text-sm font-medium bg-foreground text-background hover:bg-foreground/90 disabled:opacity-50 transition-colors">
|
|
975
|
+
Save
|
|
976
|
+
</button>
|
|
977
|
+
</div>
|
|
978
|
+
)}
|
|
979
|
+
</>
|
|
980
|
+
) : (
|
|
981
|
+
<CredentialHint
|
|
982
|
+
ready={false}
|
|
983
|
+
missingText="Configure at least one LLM provider on the LLMs page to use Kimi CLI"
|
|
984
|
+
/>
|
|
985
|
+
)}
|
|
986
|
+
</div>
|
|
987
|
+
)}
|
|
988
|
+
</div>
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
|
|
837
992
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
838
993
|
// Shared helpers
|
|
839
994
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -881,6 +1036,11 @@ function isOpenCodeReady(settings) {
|
|
|
881
1036
|
return isProviderReady(settings, settings.openCode.provider);
|
|
882
1037
|
}
|
|
883
1038
|
|
|
1039
|
+
function isKimiCliReady(settings) {
|
|
1040
|
+
if (!settings.kimiCli?.enabled || !settings.kimiCli?.provider) return false;
|
|
1041
|
+
return isProviderReady(settings, settings.kimiCli.provider);
|
|
1042
|
+
}
|
|
1043
|
+
|
|
884
1044
|
function isProviderReady(settings, provider) {
|
|
885
1045
|
// Check if selected provider has credentials
|
|
886
1046
|
const statusMap = new Map((settings.credentialStatuses || []).map(s => [s.key, s.isSet]));
|
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
getAgentJobSecrets,
|
|
9
9
|
updateAgentJobSecret,
|
|
10
10
|
deleteAgentJobSecretAction,
|
|
11
|
-
initiateOAuthFlow
|
|
11
|
+
initiateOAuthFlow,
|
|
12
|
+
getOAuthSecretCredentials
|
|
12
13
|
} from "../actions.js";
|
|
13
14
|
function buildProviderOptions() {
|
|
14
15
|
const options = [];
|
|
@@ -170,6 +171,16 @@ function AddSecretDialog({ open, onAdd, onCancel, onOAuthSuccess, editingSecret
|
|
|
170
171
|
setCopied(false);
|
|
171
172
|
setRedirectUri(`${window.location.origin}/api/oauth/callback`);
|
|
172
173
|
if (!editingSecret) setTimeout(() => nameRef.current?.focus(), 50);
|
|
174
|
+
if (editingSecret?.key) {
|
|
175
|
+
getOAuthSecretCredentials(editingSecret.key).then((creds) => {
|
|
176
|
+
if (creds && !creds.error) {
|
|
177
|
+
setClientId(creds.clientId);
|
|
178
|
+
setClientSecret(creds.clientSecret);
|
|
179
|
+
const match = PROVIDER_OPTIONS.find((o) => o.tokenUrl === creds.tokenUrl);
|
|
180
|
+
if (match) setSelectedOption(match.id);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
173
184
|
}
|
|
174
185
|
return () => {
|
|
175
186
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
@@ -236,7 +247,7 @@ function AddSecretDialog({ open, onAdd, onCancel, onOAuthSuccess, editingSecret
|
|
|
236
247
|
tokenUrl: opt.tokenUrl,
|
|
237
248
|
scopes,
|
|
238
249
|
secretType: "agent_job_secret",
|
|
239
|
-
returnPath: "/admin/event-handler/agent-
|
|
250
|
+
returnPath: "/admin/event-handler/agent-secrets"
|
|
240
251
|
});
|
|
241
252
|
if (result?.error) {
|
|
242
253
|
setError(result.error);
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
updateAgentJobSecret,
|
|
10
10
|
deleteAgentJobSecretAction,
|
|
11
11
|
initiateOAuthFlow,
|
|
12
|
+
getOAuthSecretCredentials,
|
|
12
13
|
} from '../actions.js';
|
|
13
14
|
|
|
14
15
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -225,6 +226,19 @@ function AddSecretDialog({ open, onAdd, onCancel, onOAuthSuccess, editingSecret
|
|
|
225
226
|
setCopied(false);
|
|
226
227
|
setRedirectUri(`${window.location.origin}/api/oauth/callback`);
|
|
227
228
|
if (!editingSecret) setTimeout(() => nameRef.current?.focus(), 50);
|
|
229
|
+
|
|
230
|
+
// Pre-fill OAuth credentials from stored secret
|
|
231
|
+
if (editingSecret?.key) {
|
|
232
|
+
getOAuthSecretCredentials(editingSecret.key).then((creds) => {
|
|
233
|
+
if (creds && !creds.error) {
|
|
234
|
+
setClientId(creds.clientId);
|
|
235
|
+
setClientSecret(creds.clientSecret);
|
|
236
|
+
// Auto-select provider by matching tokenUrl
|
|
237
|
+
const match = PROVIDER_OPTIONS.find((o) => o.tokenUrl === creds.tokenUrl);
|
|
238
|
+
if (match) setSelectedOption(match.id);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
228
242
|
}
|
|
229
243
|
return () => {
|
|
230
244
|
if (timeoutRef.current) clearTimeout(timeoutRef.current);
|
|
@@ -306,7 +320,7 @@ function AddSecretDialog({ open, onAdd, onCancel, onOAuthSuccess, editingSecret
|
|
|
306
320
|
tokenUrl: opt.tokenUrl,
|
|
307
321
|
scopes,
|
|
308
322
|
secretType: 'agent_job_secret',
|
|
309
|
-
returnPath: '/admin/event-handler/agent-
|
|
323
|
+
returnPath: '/admin/event-handler/agent-secrets',
|
|
310
324
|
});
|
|
311
325
|
|
|
312
326
|
if (result?.error) {
|
|
@@ -30,7 +30,7 @@ const EVENT_HANDLER_TABS = [
|
|
|
30
30
|
{ id: "llms", label: "LLMs", href: "/admin/event-handler/llms" },
|
|
31
31
|
{ id: "chat", label: "Chat", href: "/admin/event-handler/chat" },
|
|
32
32
|
{ id: "coding-agents", label: "Coding Agents", href: "/admin/event-handler/coding-agents" },
|
|
33
|
-
{ id: "agent-
|
|
33
|
+
{ id: "agent-secrets", label: "Agent Secrets", href: "/admin/event-handler/agent-secrets" },
|
|
34
34
|
{ id: "webhooks", label: "Webhooks", href: "/admin/event-handler/webhooks" },
|
|
35
35
|
{ id: "telegram", label: "Telegram", href: "/admin/event-handler/telegram" },
|
|
36
36
|
{ id: "voice", label: "Voice", href: "/admin/event-handler/voice" }
|
|
@@ -52,7 +52,7 @@ const EVENT_HANDLER_TABS = [
|
|
|
52
52
|
{ id: 'llms', label: 'LLMs', href: '/admin/event-handler/llms' },
|
|
53
53
|
{ id: 'chat', label: 'Chat', href: '/admin/event-handler/chat' },
|
|
54
54
|
{ id: 'coding-agents', label: 'Coding Agents', href: '/admin/event-handler/coding-agents' },
|
|
55
|
-
{ id: 'agent-
|
|
55
|
+
{ id: 'agent-secrets', label: 'Agent Secrets', href: '/admin/event-handler/agent-secrets' },
|
|
56
56
|
{ id: 'webhooks', label: 'Webhooks', href: '/admin/event-handler/webhooks' },
|
|
57
57
|
{ id: 'telegram', label: 'Telegram', href: '/admin/event-handler/telegram' },
|
|
58
58
|
{ id: 'voice', label: 'Voice', href: '/admin/event-handler/voice' },
|
|
@@ -40,10 +40,10 @@ function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename }) {
|
|
|
40
40
|
setOpenMobile(false);
|
|
41
41
|
},
|
|
42
42
|
children: [
|
|
43
|
-
|
|
44
|
-
/* @__PURE__ */ jsx(CodeIcon, { size: 14 }),
|
|
43
|
+
/* @__PURE__ */ jsxs("span", { className: "relative", children: [
|
|
44
|
+
chat.chatMode === "code" ? /* @__PURE__ */ jsx(CodeIcon, { size: 14 }) : /* @__PURE__ */ jsx(AgentIcon, { size: 14 }),
|
|
45
45
|
chat.hasChanges ? /* @__PURE__ */ jsx("span", { className: "absolute -bottom-0.5 -right-0.5 w-2 h-2 rounded-full bg-destructive" }) : null
|
|
46
|
-
] })
|
|
46
|
+
] }),
|
|
47
47
|
/* @__PURE__ */ jsx("span", { className: "truncate flex-1", children: chat.title })
|
|
48
48
|
]
|
|
49
49
|
}
|
|
@@ -40,12 +40,10 @@ export function SidebarHistoryItem({ chat, isActive, onDelete, onStar, onRename
|
|
|
40
40
|
setOpenMobile(false);
|
|
41
41
|
}}
|
|
42
42
|
>
|
|
43
|
-
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
</span>
|
|
48
|
-
) : <AgentIcon size={14} />}
|
|
43
|
+
<span className="relative">
|
|
44
|
+
{chat.chatMode === 'code' ? <CodeIcon size={14} /> : <AgentIcon size={14} />}
|
|
45
|
+
{chat.hasChanges ? <span className="absolute -bottom-0.5 -right-0.5 w-2 h-2 rounded-full bg-destructive" /> : null}
|
|
46
|
+
</span>
|
|
49
47
|
<span className="truncate flex-1">
|
|
50
48
|
{chat.title}
|
|
51
49
|
</span>
|
|
@@ -133,7 +133,7 @@ function TriggersPage() {
|
|
|
133
133
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium mb-1", children: "No triggers configured" }),
|
|
134
134
|
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground max-w-sm", children: [
|
|
135
135
|
"Add webhook triggers by editing ",
|
|
136
|
-
/* @__PURE__ */ jsx("span", { className: "font-mono", children: "
|
|
136
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", children: "event-handler/TRIGGERS.json" }),
|
|
137
137
|
" in your project."
|
|
138
138
|
] })
|
|
139
139
|
] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
@@ -193,7 +193,7 @@ export function TriggersPage() {
|
|
|
193
193
|
</div>
|
|
194
194
|
<p className="text-sm font-medium mb-1">No triggers configured</p>
|
|
195
195
|
<p className="text-xs text-muted-foreground max-w-sm">
|
|
196
|
-
Add webhook triggers by editing <span className="font-mono">
|
|
196
|
+
Add webhook triggers by editing <span className="font-mono">event-handler/TRIGGERS.json</span> in your project.
|
|
197
197
|
</p>
|
|
198
198
|
</div>
|
|
199
199
|
) : (
|
package/lib/cluster/actions.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { auth } from '../auth/index.js';
|
|
6
|
-
import {
|
|
6
|
+
import { PROJECT_ROOT } from '../paths.js';
|
|
7
7
|
import {
|
|
8
8
|
createCluster as dbCreateCluster,
|
|
9
9
|
getClusterById,
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
} from '../db/clusters.js';
|
|
26
26
|
|
|
27
27
|
function readDefault(filename) {
|
|
28
|
-
try { return fs.readFileSync(path.join(
|
|
28
|
+
try { return fs.readFileSync(path.join(PROJECT_ROOT, filename), 'utf8'); }
|
|
29
29
|
catch { return ''; }
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -62,7 +62,7 @@ export async function getCluster(clusterId) {
|
|
|
62
62
|
|
|
63
63
|
export async function createCluster(name = 'New Cluster') {
|
|
64
64
|
const user = await requireAuth();
|
|
65
|
-
const cluster = dbCreateCluster(user.id, { name, systemPrompt: readDefault('
|
|
65
|
+
const cluster = dbCreateCluster(user.id, { name, systemPrompt: readDefault('event-handler/clusters/SYSTEM.md') });
|
|
66
66
|
const { clusterDir } = await import('./execute.js');
|
|
67
67
|
const dir = clusterDir(cluster);
|
|
68
68
|
fs.mkdirSync(`${dir}/shared`, { recursive: true });
|
|
@@ -130,7 +130,7 @@ export async function createClusterRoleAction(clusterId, roleName, role = '') {
|
|
|
130
130
|
const user = await requireAuth();
|
|
131
131
|
const cluster = getClusterById(clusterId);
|
|
132
132
|
if (!cluster || cluster.userId !== user.id) return { success: false };
|
|
133
|
-
const record = dbCreateClusterRole(clusterId, { roleName, role: role || readDefault('
|
|
133
|
+
const record = dbCreateClusterRole(clusterId, { roleName, role: role || readDefault('event-handler/clusters/ROLE.md') });
|
|
134
134
|
// Create role directory
|
|
135
135
|
const { roleDir } = await import('./execute.js');
|
|
136
136
|
const dir = roleDir(cluster, record);
|
package/lib/cluster/execute.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import fs from 'fs';
|
|
4
|
-
import {
|
|
4
|
+
import { PROJECT_ROOT } from '../paths.js';
|
|
5
|
+
|
|
6
|
+
const clusterDataDir = process.env.CLUSTER_DATA_PATH || path.join(PROJECT_ROOT, 'data/clusters');
|
|
5
7
|
import { getConfig } from '../config.js';
|
|
6
8
|
import { stopContainer as dockerStopContainer, removeContainer, runClusterWorkerContainer, resolveHostPath, listContainers } from '../tools/docker.js';
|
|
7
9
|
import { getRoleWithCluster, getClusterRolesByCluster, roleShortId } from '../db/clusters.js';
|
package/lib/code/actions.js
CHANGED
|
@@ -543,13 +543,21 @@ export async function getWorkspaceDiffStats(id, authenticatedUser) {
|
|
|
543
543
|
const { execSync } = await import('child_process');
|
|
544
544
|
const opts = { cwd: repoPath, encoding: 'utf8', timeout: 5000 };
|
|
545
545
|
|
|
546
|
-
const featureBranch = workspace.featureBranch || workspace.branch || 'main';
|
|
547
546
|
const baseBranch = workspace.branch || 'main';
|
|
548
|
-
let
|
|
547
|
+
let currentBranch;
|
|
549
548
|
try {
|
|
550
|
-
execSync(`git -c safe.directory='*' rev-parse --
|
|
551
|
-
|
|
552
|
-
} catch {
|
|
549
|
+
const detected = execSync(`git -c safe.directory='*' rev-parse --abbrev-ref HEAD`, opts).trim();
|
|
550
|
+
currentBranch = (detected && detected !== 'HEAD') ? detected : null;
|
|
551
|
+
} catch { /* leave null */ }
|
|
552
|
+
let diffRef;
|
|
553
|
+
if (currentBranch) {
|
|
554
|
+
try {
|
|
555
|
+
execSync(`git -c safe.directory='*' rev-parse --verify origin/${currentBranch} 2>/dev/null`, opts);
|
|
556
|
+
diffRef = `origin/${currentBranch}`;
|
|
557
|
+
} catch {
|
|
558
|
+
diffRef = `origin/${baseBranch}`;
|
|
559
|
+
}
|
|
560
|
+
} else {
|
|
553
561
|
diffRef = `origin/${baseBranch}`;
|
|
554
562
|
}
|
|
555
563
|
|
|
@@ -584,7 +592,14 @@ export async function getWorkspaceDiffStats(id, authenticatedUser) {
|
|
|
584
592
|
}
|
|
585
593
|
|
|
586
594
|
updateHasChanges(id, insertions > 0 || deletions > 0);
|
|
587
|
-
|
|
595
|
+
|
|
596
|
+
// Sync featureBranch in DB if the actual branch differs
|
|
597
|
+
if (currentBranch && currentBranch !== workspace.featureBranch) {
|
|
598
|
+
const { updateFeatureBranch } = await import('../db/code-workspaces.js');
|
|
599
|
+
updateFeatureBranch(id, currentBranch);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return { success: true, insertions, deletions, currentBranch };
|
|
588
603
|
} catch (err) {
|
|
589
604
|
console.error(`[getWorkspaceDiffStats] workspace=${id}`, err);
|
|
590
605
|
return { success: false };
|
|
@@ -618,13 +633,21 @@ export async function getWorkspaceDiffFull(id, authenticatedUser) {
|
|
|
618
633
|
const { execSync } = await import('child_process');
|
|
619
634
|
const opts = { cwd: repoPath, encoding: 'utf8', timeout: 10000 };
|
|
620
635
|
|
|
621
|
-
const featureBranch = workspace.featureBranch || workspace.branch || 'main';
|
|
622
636
|
const baseBranch = workspace.branch || 'main';
|
|
623
|
-
let
|
|
637
|
+
let currentBranch;
|
|
624
638
|
try {
|
|
625
|
-
execSync(`git -c safe.directory='*' rev-parse --
|
|
626
|
-
|
|
627
|
-
} catch {
|
|
639
|
+
const detected = execSync(`git -c safe.directory='*' rev-parse --abbrev-ref HEAD`, opts).trim();
|
|
640
|
+
currentBranch = (detected && detected !== 'HEAD') ? detected : null;
|
|
641
|
+
} catch { /* leave null */ }
|
|
642
|
+
let diffRef;
|
|
643
|
+
if (currentBranch) {
|
|
644
|
+
try {
|
|
645
|
+
execSync(`git -c safe.directory='*' rev-parse --verify origin/${currentBranch} 2>/dev/null`, opts);
|
|
646
|
+
diffRef = `origin/${currentBranch}`;
|
|
647
|
+
} catch {
|
|
648
|
+
diffRef = `origin/${baseBranch}`;
|
|
649
|
+
}
|
|
650
|
+
} else {
|
|
628
651
|
diffRef = `origin/${baseBranch}`;
|
|
629
652
|
}
|
|
630
653
|
|