siclaw 0.1.1 → 0.1.3
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 +74 -114
- package/dist/agentbox/gateway-client.d.ts +2 -1
- package/dist/agentbox/gateway-client.js +6 -2
- package/dist/agentbox/gateway-client.js.map +1 -1
- package/dist/agentbox/http-server.js +184 -19
- package/dist/agentbox/http-server.js.map +1 -1
- package/dist/agentbox/resource-handlers.d.ts +1 -0
- package/dist/agentbox/resource-handlers.js +23 -23
- package/dist/agentbox/resource-handlers.js.map +1 -1
- package/dist/agentbox/session.js +85 -5
- package/dist/agentbox/session.js.map +1 -1
- package/dist/agentbox-main.d.ts +2 -1
- package/dist/agentbox-main.js +65 -18
- package/dist/agentbox-main.js.map +1 -1
- package/dist/cli-credentials.d.ts +1 -0
- package/dist/cli-credentials.js +109 -0
- package/dist/cli-credentials.js.map +1 -0
- package/dist/cli-first-run.d.ts +11 -0
- package/dist/cli-first-run.js +99 -0
- package/dist/cli-first-run.js.map +1 -0
- package/dist/cli-main.js +33 -11
- package/dist/cli-main.js.map +1 -1
- package/dist/cli-setup.d.ts +5 -11
- package/dist/cli-setup.js +12 -225
- package/dist/cli-setup.js.map +1 -1
- package/dist/core/agent-factory.d.ts +4 -0
- package/dist/core/agent-factory.js +102 -151
- package/dist/core/agent-factory.js.map +1 -1
- package/dist/core/config.d.ts +10 -3
- package/dist/core/config.js +12 -96
- package/dist/core/config.js.map +1 -1
- package/dist/core/extensions/deep-investigation.d.ts +2 -1
- package/dist/core/extensions/deep-investigation.js +144 -24
- package/dist/core/extensions/deep-investigation.js.map +1 -1
- package/dist/core/extensions/setup.d.ts +8 -0
- package/dist/core/extensions/setup.js +669 -0
- package/dist/core/extensions/setup.js.map +1 -0
- package/dist/core/llm-proxy.js +7 -3
- package/dist/core/llm-proxy.js.map +1 -1
- package/dist/core/mcp-client.d.ts +0 -10
- package/dist/core/mcp-client.js +0 -65
- package/dist/core/mcp-client.js.map +1 -1
- package/dist/core/prompt.d.ts +1 -1
- package/dist/core/prompt.js +42 -5
- package/dist/core/prompt.js.map +1 -1
- package/dist/core/provider-presets.d.ts +14 -0
- package/dist/core/provider-presets.js +81 -0
- package/dist/core/provider-presets.js.map +1 -0
- package/dist/cron/cron-coordinator.d.ts +2 -0
- package/dist/cron/cron-coordinator.js +46 -14
- package/dist/cron/cron-coordinator.js.map +1 -1
- package/dist/cron/cron-executor.js +33 -8
- package/dist/cron/cron-executor.js.map +1 -1
- package/dist/cron/cron-scheduler.d.ts +1 -1
- package/dist/cron/gateway-client.d.ts +5 -0
- package/dist/cron/gateway-client.js +43 -8
- package/dist/cron/gateway-client.js.map +1 -1
- package/dist/cron-main.js +39 -9
- package/dist/cron-main.js.map +1 -1
- package/dist/gateway/agentbox/client.d.ts +11 -0
- package/dist/gateway/agentbox/client.js +18 -0
- package/dist/gateway/agentbox/client.js.map +1 -1
- package/dist/gateway/agentbox/k8s-spawner.d.ts +13 -4
- package/dist/gateway/agentbox/k8s-spawner.js +101 -86
- package/dist/gateway/agentbox/k8s-spawner.js.map +1 -1
- package/dist/gateway/agentbox/local-spawner.d.ts +1 -1
- package/dist/gateway/agentbox/local-spawner.js +4 -2
- package/dist/gateway/agentbox/local-spawner.js.map +1 -1
- package/dist/gateway/agentbox/manager.d.ts +0 -10
- package/dist/gateway/agentbox/manager.js +11 -30
- package/dist/gateway/agentbox/manager.js.map +1 -1
- package/dist/gateway/agentbox/types.d.ts +6 -4
- package/dist/gateway/cron/cron-service.d.ts +49 -0
- package/dist/gateway/cron/cron-service.js +259 -0
- package/dist/gateway/cron/cron-service.js.map +1 -0
- package/dist/gateway/db/init-schema.js +44 -0
- package/dist/gateway/db/init-schema.js.map +1 -1
- package/dist/gateway/db/migrate-sqlite.js +73 -4
- package/dist/gateway/db/migrate-sqlite.js.map +1 -1
- package/dist/gateway/db/repositories/chat-repo.d.ts +56 -2
- package/dist/gateway/db/repositories/chat-repo.js +132 -2
- package/dist/gateway/db/repositories/chat-repo.js.map +1 -1
- package/dist/gateway/db/repositories/config-repo.d.ts +31 -2
- package/dist/gateway/db/repositories/config-repo.js +57 -7
- package/dist/gateway/db/repositories/config-repo.js.map +1 -1
- package/dist/gateway/db/repositories/env-repo.d.ts +14 -0
- package/dist/gateway/db/repositories/env-repo.js +15 -2
- package/dist/gateway/db/repositories/env-repo.js.map +1 -1
- package/dist/gateway/db/repositories/model-config-repo.js +6 -5
- package/dist/gateway/db/repositories/model-config-repo.js.map +1 -1
- package/dist/gateway/db/repositories/skill-repo.d.ts +0 -5
- package/dist/gateway/db/repositories/skill-review-repo.d.ts +1 -0
- package/dist/gateway/db/repositories/skill-review-repo.js +4 -1
- package/dist/gateway/db/repositories/skill-review-repo.js.map +1 -1
- package/dist/gateway/db/repositories/skill-version-repo.js +0 -1
- package/dist/gateway/db/repositories/skill-version-repo.js.map +1 -1
- package/dist/gateway/db/repositories/system-config-repo.d.ts +1 -1
- package/dist/gateway/db/repositories/system-config-repo.js +2 -1
- package/dist/gateway/db/repositories/system-config-repo.js.map +1 -1
- package/dist/gateway/db/repositories/user-env-config-repo.d.ts +13 -0
- package/dist/gateway/db/repositories/user-env-config-repo.js +11 -0
- package/dist/gateway/db/repositories/user-env-config-repo.js.map +1 -1
- package/dist/gateway/db/repositories/workspace-repo.d.ts +3 -2
- package/dist/gateway/db/repositories/workspace-repo.js +6 -2
- package/dist/gateway/db/repositories/workspace-repo.js.map +1 -1
- package/dist/gateway/db/schema-mysql.d.ts +473 -51
- package/dist/gateway/db/schema-mysql.js +35 -4
- package/dist/gateway/db/schema-mysql.js.map +1 -1
- package/dist/gateway/db/schema-sqlite.d.ts +522 -57
- package/dist/gateway/db/schema-sqlite.js +38 -6
- package/dist/gateway/db/schema-sqlite.js.map +1 -1
- package/dist/gateway/db/schema.d.ts +471 -51
- package/dist/gateway/db/schema.js +1 -1
- package/dist/gateway/db/schema.js.map +1 -1
- package/dist/gateway/metrics-aggregator.d.ts +65 -0
- package/dist/gateway/metrics-aggregator.js +244 -0
- package/dist/gateway/metrics-aggregator.js.map +1 -0
- package/dist/gateway/plugins/channel-bridge.d.ts +4 -1
- package/dist/gateway/plugins/channel-bridge.js +78 -86
- package/dist/gateway/plugins/channel-bridge.js.map +1 -1
- package/dist/gateway/rpc-methods.d.ts +4 -2
- package/dist/gateway/rpc-methods.js +851 -170
- package/dist/gateway/rpc-methods.js.map +1 -1
- package/dist/gateway/security/cert-manager.d.ts +2 -2
- package/dist/gateway/security/cert-manager.js +4 -2
- package/dist/gateway/security/cert-manager.js.map +1 -1
- package/dist/gateway/server.d.ts +4 -8
- package/dist/gateway/server.js +297 -261
- package/dist/gateway/server.js.map +1 -1
- package/dist/gateway/skills/file-writer.js +17 -11
- package/dist/gateway/skills/file-writer.js.map +1 -1
- package/dist/gateway/skills/script-evaluator.js +12 -9
- package/dist/gateway/skills/script-evaluator.js.map +1 -1
- package/dist/gateway/web/dist/assets/index-0p17ZeTP.js +740 -0
- package/dist/gateway/web/dist/assets/index-9eP6nPUq.js +741 -0
- package/dist/gateway/web/dist/assets/index-9eP6nPUq.js.map +1 -0
- package/dist/gateway/web/dist/assets/index-DyowBCEj.css +1 -0
- package/dist/gateway/web/dist/assets/index-PDK5JJDO.css +1 -0
- package/dist/gateway/web/dist/index.html +2 -2
- package/dist/gateway-main.js +25 -10
- package/dist/gateway-main.js.map +1 -1
- package/dist/memory/embeddings.js +5 -4
- package/dist/memory/embeddings.js.map +1 -1
- package/dist/memory/indexer.d.ts +23 -3
- package/dist/memory/indexer.js +235 -23
- package/dist/memory/indexer.js.map +1 -1
- package/dist/memory/schema.js +15 -1
- package/dist/memory/schema.js.map +1 -1
- package/dist/memory/types.d.ts +18 -0
- package/dist/memory/types.js +6 -1
- package/dist/memory/types.js.map +1 -1
- package/dist/shared/detect-language.d.ts +12 -0
- package/dist/shared/detect-language.js +78 -0
- package/dist/shared/detect-language.js.map +1 -0
- package/dist/shared/diagnostic-events.d.ts +70 -0
- package/dist/shared/diagnostic-events.js +38 -0
- package/dist/shared/diagnostic-events.js.map +1 -0
- package/dist/shared/local-collector.d.ts +56 -0
- package/dist/shared/local-collector.js +284 -0
- package/dist/shared/local-collector.js.map +1 -0
- package/dist/shared/metrics-types.d.ts +64 -0
- package/dist/shared/metrics-types.js +25 -0
- package/dist/shared/metrics-types.js.map +1 -0
- package/dist/shared/metrics.d.ts +19 -0
- package/dist/shared/metrics.js +185 -0
- package/dist/shared/metrics.js.map +1 -0
- package/dist/shared/path-utils.d.ts +15 -0
- package/dist/shared/path-utils.js +23 -0
- package/dist/shared/path-utils.js.map +1 -0
- package/dist/shared/retry.d.ts +35 -0
- package/dist/shared/retry.js +61 -0
- package/dist/shared/retry.js.map +1 -0
- package/dist/tools/command-sets.d.ts +18 -2
- package/dist/tools/command-sets.js +207 -32
- package/dist/tools/command-sets.js.map +1 -1
- package/dist/tools/command-validator.d.ts +56 -0
- package/dist/tools/command-validator.js +357 -0
- package/dist/tools/command-validator.js.map +1 -0
- package/dist/tools/create-skill.js +26 -1
- package/dist/tools/create-skill.js.map +1 -1
- package/dist/tools/credential-list.js +1 -23
- package/dist/tools/credential-list.js.map +1 -1
- package/dist/tools/credential-manager.d.ts +98 -0
- package/dist/tools/credential-manager.js +313 -0
- package/dist/tools/credential-manager.js.map +1 -0
- package/dist/tools/deep-search/engine.js +184 -127
- package/dist/tools/deep-search/engine.js.map +1 -1
- package/dist/tools/deep-search/prompts.d.ts +10 -2
- package/dist/tools/deep-search/prompts.js +37 -36
- package/dist/tools/deep-search/prompts.js.map +1 -1
- package/dist/tools/deep-search/schemas.d.ts +87 -0
- package/dist/tools/deep-search/schemas.js +85 -0
- package/dist/tools/deep-search/schemas.js.map +1 -0
- package/dist/tools/deep-search/sub-agent.d.ts +21 -0
- package/dist/tools/deep-search/sub-agent.js +153 -4
- package/dist/tools/deep-search/sub-agent.js.map +1 -1
- package/dist/tools/deep-search/tool.js +1 -0
- package/dist/tools/deep-search/tool.js.map +1 -1
- package/dist/tools/deep-search/types.d.ts +2 -0
- package/dist/tools/deep-search/types.js.map +1 -1
- package/dist/tools/dp-tools.js +29 -5
- package/dist/tools/dp-tools.js.map +1 -1
- package/dist/tools/exec-utils.d.ts +85 -0
- package/dist/tools/exec-utils.js +294 -0
- package/dist/tools/exec-utils.js.map +1 -0
- package/dist/tools/fork-skill.js +14 -2
- package/dist/tools/fork-skill.js.map +1 -1
- package/dist/tools/investigation-feedback.d.ts +3 -0
- package/dist/tools/investigation-feedback.js +71 -0
- package/dist/tools/investigation-feedback.js.map +1 -0
- package/dist/tools/manage-schedule.js +16 -6
- package/dist/tools/manage-schedule.js.map +1 -1
- package/dist/tools/netns-script.js +28 -283
- package/dist/tools/netns-script.js.map +1 -1
- package/dist/tools/node-exec.d.ts +2 -14
- package/dist/tools/node-exec.js +20 -228
- package/dist/tools/node-exec.js.map +1 -1
- package/dist/tools/node-script.js +16 -171
- package/dist/tools/node-script.js.map +1 -1
- package/dist/tools/pod-exec.d.ts +1 -1
- package/dist/tools/pod-exec.js +10 -26
- package/dist/tools/pod-exec.js.map +1 -1
- package/dist/tools/pod-nsenter-exec.js +23 -228
- package/dist/tools/pod-nsenter-exec.js.map +1 -1
- package/dist/tools/pod-script.js +10 -19
- package/dist/tools/pod-script.js.map +1 -1
- package/dist/tools/restricted-bash.d.ts +1 -17
- package/dist/tools/restricted-bash.js +38 -252
- package/dist/tools/restricted-bash.js.map +1 -1
- package/dist/tools/run-skill.d.ts +3 -1
- package/dist/tools/run-skill.js +21 -1
- package/dist/tools/run-skill.js.map +1 -1
- package/dist/tools/script-resolver.d.ts +3 -1
- package/dist/tools/script-resolver.js +74 -30
- package/dist/tools/script-resolver.js.map +1 -1
- package/dist/tools/update-skill.js +17 -6
- package/dist/tools/update-skill.js.map +1 -1
- package/package.json +4 -2
- package/siclaw.mjs +10 -1
- package/skills/core/cluster-events/SKILL.md +1 -1
- package/skills/core/deep-investigation/SKILL.md +11 -0
- package/skills/core/deployment-rollout-debug/SKILL.md +1 -1
- package/skills/core/dns-debug/SKILL.md +1 -0
- package/skills/core/meta.json +12 -1
- package/skills/core/networkpolicy-debug/SKILL.md +332 -0
- package/skills/core/node-logs/scripts/get-node-logs.sh +19 -9
- package/skills/core/pod-pending-debug/SKILL.md +1 -0
- package/skills/core/quota-debug/SKILL.md +203 -0
- package/skills/core/service-debug/SKILL.md +1 -0
- package/skills/core/statefulset-debug/SKILL.md +280 -0
- package/skills/core/volcano-diagnose-pod/SKILL.md +196 -0
- package/skills/core/volcano-diagnose-pod/scripts/diagnose-pod.sh +175 -0
- package/skills/core/volcano-gang-scheduling/SKILL.md +299 -0
- package/skills/core/volcano-job-diagnose/SKILL.md +319 -0
- package/skills/core/volcano-job-diagnose/scripts/diagnose-job.sh +253 -0
- package/skills/core/volcano-node-resources/SKILL.md +334 -0
- package/skills/core/volcano-node-resources/scripts/get-node-resources.sh +281 -0
- package/skills/core/volcano-queue-diagnose/SKILL.md +294 -0
- package/skills/core/volcano-queue-diagnose/scripts/diagnose-queue.sh +283 -0
- package/skills/core/volcano-resource-insufficient/SKILL.md +315 -0
- package/skills/core/volcano-scheduler-config/SKILL.md +371 -0
- package/skills/core/volcano-scheduler-config/scripts/get-scheduler-config.sh +297 -0
- package/skills/core/volcano-scheduler-logs/SKILL.md +241 -0
- package/skills/core/volcano-scheduler-logs/scripts/get-scheduler-logs.sh +159 -0
- package/skills/platform/create-skill/SKILL.md +35 -3
- package/skills/platform/manage-skill/SKILL.md +9 -2
- package/skills/platform/update-skill/SKILL.md +17 -6
package/README.md
CHANGED
|
@@ -4,32 +4,37 @@
|
|
|
4
4
|
|
|
5
5
|
# Siclaw
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**Read-only investigation copilot for SRE teams**
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/siclaw)
|
|
10
10
|
[](https://github.com/scitix/siclaw/actions/workflows/ci.yml)
|
|
11
11
|
[](https://nodejs.org/)
|
|
12
12
|
[](https://www.typescriptlang.org/)
|
|
13
13
|
[](LICENSE)
|
|
14
|
+
[](https://join.slack.com/t/siclaw-scitix/shared_invite/zt-3rrsoc2ic-JIfbfvT1_04sqgQorSRfmw)
|
|
14
15
|
|
|
16
|
+
[Website](https://www.siclaw.ai) | [Documentation](https://docs.siclaw.ai) | [Slack](https://join.slack.com/t/siclaw-scitix/shared_invite/zt-3rrsoc2ic-JIfbfvT1_04sqgQorSRfmw)
|
|
15
17
|
|
|
16
18
|
</div>
|
|
17
19
|
|
|
18
20
|
---
|
|
19
21
|
|
|
20
|
-
Siclaw is an AI
|
|
22
|
+
Siclaw is an open-source AI agent for DevOps and SRE teams. It is built for **read-only infrastructure diagnostics**: gather evidence, form hypotheses, validate them, and return a clear root-cause analysis without changing your environment directly. Describe a problem in plain language and Siclaw investigates it from the terminal, the web UI, or your team's chat channels.
|
|
23
|
+
|
|
24
|
+
<div align="center">
|
|
25
|
+
<img src="docs/assets/demo.gif" alt="Siclaw Demo" width="800" />
|
|
26
|
+
<p><em>Deep investigation: diagnosing a CrashLoopBackOff in seconds</em></p>
|
|
27
|
+
</div>
|
|
21
28
|
|
|
22
29
|
## Features
|
|
23
30
|
|
|
24
|
-
- **Deep Investigation** —
|
|
25
|
-
- **Investigation Memory** —
|
|
26
|
-
- **
|
|
27
|
-
- **Team
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
30
|
-
- **
|
|
31
|
-
- **Extensible** — [MCP](https://modelcontextprotocol.io) tool servers for custom data source integrations
|
|
32
|
-
- **Multi-Channel Access** — Terminal TUI, Web UI, or IM bots (Slack, Discord, Telegram, Lark)
|
|
31
|
+
- **Deep Investigation** — A 4-phase workflow for evidence gathering, hypothesis testing, and root-cause analysis
|
|
32
|
+
- **Investigation Memory** — Learns from past incidents to improve future investigations
|
|
33
|
+
- **Read-Only by Default** — Investigates and recommends next steps without changing your environment directly
|
|
34
|
+
- **Team Workflows** — Shared web UI, credentials, channels, triggers, and scheduled patrols
|
|
35
|
+
- **Reusable Skills** — Turn repeated diagnostic playbooks into reviewable runbooks
|
|
36
|
+
- **Extensible** — Connect external tools and data sources through [MCP](https://modelcontextprotocol.io)
|
|
37
|
+
- **Multi-Channel Access** — Use Siclaw from the terminal, web UI, or chat channels
|
|
33
38
|
|
|
34
39
|
## Architecture
|
|
35
40
|
|
|
@@ -44,28 +49,33 @@ Siclaw is an AI Agent platform for DevOps / SRE, inspired by [OpenClaw](https://
|
|
|
44
49
|
|
|
45
50
|
- **Node.js >= 22.12.0** — [Download](https://nodejs.org/)
|
|
46
51
|
- **npm** — Comes with Node.js
|
|
47
|
-
- **kubectl**
|
|
52
|
+
- **kubectl** — Optional, only needed if you want Siclaw to investigate Kubernetes clusters
|
|
48
53
|
|
|
49
54
|
## Quick Start
|
|
50
55
|
|
|
51
|
-
Siclaw supports three deployment profiles.
|
|
56
|
+
Siclaw supports three deployment profiles. For local usage, start from a dedicated working directory because Siclaw stores most runtime data in `.siclaw/` relative to where you launch it.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
mkdir -p ~/siclaw-work
|
|
60
|
+
cd ~/siclaw-work
|
|
61
|
+
```
|
|
52
62
|
|
|
53
63
|
### 1. TUI Mode — Personal, local, lowest barrier
|
|
54
64
|
|
|
55
65
|
Run the agent directly in your terminal. No server, no database. All operations are read-only by default — safe to run on your workstation.
|
|
56
66
|
|
|
57
67
|
```bash
|
|
58
|
-
# Install
|
|
59
|
-
npm install siclaw
|
|
68
|
+
# Install globally
|
|
69
|
+
npm install -g siclaw
|
|
60
70
|
|
|
61
71
|
# Run (interactive — prompts for LLM provider on first launch)
|
|
62
|
-
|
|
72
|
+
siclaw
|
|
63
73
|
|
|
64
74
|
# Single-shot
|
|
65
|
-
|
|
75
|
+
siclaw --prompt "Why is pod nginx-abc in CrashLoopBackOff?"
|
|
66
76
|
|
|
67
77
|
# Continue last session
|
|
68
|
-
|
|
78
|
+
siclaw --continue
|
|
69
79
|
```
|
|
70
80
|
|
|
71
81
|
<details>
|
|
@@ -73,7 +83,7 @@ npx siclaw --continue
|
|
|
73
83
|
|
|
74
84
|
```bash
|
|
75
85
|
git clone https://github.com/scitix/siclaw.git && cd siclaw
|
|
76
|
-
npm ci && npm run build
|
|
86
|
+
npm ci && npm run build:web && npm run build
|
|
77
87
|
npm link # register `siclaw` command globally
|
|
78
88
|
|
|
79
89
|
siclaw # TUI mode
|
|
@@ -88,17 +98,18 @@ siclaw --prompt "..." # single-shot mode
|
|
|
88
98
|
|
|
89
99
|
### 2. Local Server — VM or laptop, recommended for daily use
|
|
90
100
|
|
|
91
|
-
A lightweight web UI backed by SQLite. No MySQL, no Docker required
|
|
101
|
+
A lightweight web UI backed by SQLite. No MySQL, no Docker required.
|
|
92
102
|
|
|
93
103
|
```bash
|
|
94
|
-
npm install siclaw
|
|
104
|
+
npm install -g siclaw
|
|
95
105
|
|
|
96
|
-
# Start the server
|
|
97
|
-
|
|
106
|
+
# Start the server
|
|
107
|
+
siclaw local
|
|
98
108
|
|
|
99
109
|
# Open http://localhost:3000
|
|
100
110
|
# Login: admin / admin (default credentials)
|
|
101
|
-
#
|
|
111
|
+
# Configure providers in Models
|
|
112
|
+
# Import kubeconfigs in Credentials
|
|
102
113
|
```
|
|
103
114
|
|
|
104
115
|
<details>
|
|
@@ -106,7 +117,7 @@ npx siclaw local
|
|
|
106
117
|
|
|
107
118
|
```bash
|
|
108
119
|
git clone https://github.com/scitix/siclaw.git && cd siclaw
|
|
109
|
-
npm ci && npm run build && npm run build
|
|
120
|
+
npm ci && npm run build:web && npm run build
|
|
110
121
|
npm link # register `siclaw` command globally
|
|
111
122
|
|
|
112
123
|
siclaw local # start local server
|
|
@@ -116,43 +127,47 @@ siclaw local # start local server
|
|
|
116
127
|
|
|
117
128
|
</details>
|
|
118
129
|
|
|
119
|
-
|
|
130
|
+
On first startup, Siclaw creates a local admin account:
|
|
131
|
+
|
|
132
|
+
- Username: `admin`
|
|
133
|
+
- Password: `admin`
|
|
134
|
+
|
|
135
|
+
Set `SICLAW_ADMIN_PASSWORD` before first launch if you want a different bootstrap password.
|
|
120
136
|
|
|
121
137
|
### 3. Kubernetes — Team / enterprise
|
|
122
138
|
|
|
123
|
-
|
|
139
|
+
Production deployment uses Helm plus three container images: `gateway`, `agentbox`, and `cron`.
|
|
140
|
+
|
|
141
|
+
Build and push images if you are using your own registry:
|
|
124
142
|
|
|
125
143
|
```bash
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
--set database.url="mysql://user:pass@host:3306/siclaw"
|
|
144
|
+
make docker REGISTRY=registry.example.com/myteam TAG=latest
|
|
145
|
+
make push REGISTRY=registry.example.com/myteam TAG=latest
|
|
129
146
|
```
|
|
130
147
|
|
|
131
|
-
|
|
132
|
-
<summary><b>Using a custom image registry</b></summary>
|
|
133
|
-
|
|
134
|
-
If you need to build and push images to your own registry:
|
|
148
|
+
Then deploy the chart with a MySQL URL:
|
|
135
149
|
|
|
136
150
|
```bash
|
|
137
|
-
# Build and push images
|
|
138
|
-
make docker push REGISTRY=registry.example.com/myteam
|
|
139
|
-
|
|
140
|
-
# Deploy with custom registry
|
|
141
151
|
helm upgrade --install siclaw ./helm/siclaw \
|
|
142
|
-
--namespace siclaw
|
|
143
|
-
--
|
|
152
|
+
--namespace siclaw \
|
|
153
|
+
--create-namespace \
|
|
154
|
+
--set image.registry=registry.example.com/myteam \
|
|
155
|
+
--set image.tag=latest \
|
|
144
156
|
--set database.url="mysql://user:pass@host:3306/siclaw"
|
|
145
157
|
```
|
|
146
158
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
See [`helm/siclaw/`](helm/siclaw/) for values reference, and [`k8s/README.md`](k8s/README.md) for the full deployment guide.
|
|
159
|
+
The default chart exposes the Gateway Service on service port `80` and NodePort `31000`.
|
|
150
160
|
|
|
151
161
|
## Configuration
|
|
152
162
|
|
|
153
|
-
###
|
|
163
|
+
### TUI / CLI
|
|
164
|
+
|
|
165
|
+
- TUI reads `.siclaw/config/settings.json`
|
|
166
|
+
- The first-run wizard can generate this file for you
|
|
167
|
+
- Kubernetes credentials should be imported through `/setup`
|
|
168
|
+
- Investigation reports are written to `~/.siclaw/reports/`
|
|
154
169
|
|
|
155
|
-
Minimal example
|
|
170
|
+
Minimal example:
|
|
156
171
|
|
|
157
172
|
```json
|
|
158
173
|
{
|
|
@@ -160,82 +175,28 @@ Minimal example — copy `settings.example.json` to `.siclaw/config/settings.jso
|
|
|
160
175
|
"default": {
|
|
161
176
|
"baseUrl": "https://api.openai.com/v1",
|
|
162
177
|
"apiKey": "sk-YOUR-KEY",
|
|
178
|
+
"api": "openai-completions",
|
|
163
179
|
"models": [{ "id": "gpt-4o", "name": "GPT-4o" }]
|
|
164
180
|
}
|
|
165
181
|
}
|
|
166
182
|
}
|
|
167
183
|
```
|
|
168
184
|
|
|
169
|
-
|
|
170
|
-
<summary><b>Full settings.json reference</b></summary>
|
|
171
|
-
|
|
172
|
-
```json
|
|
173
|
-
{
|
|
174
|
-
"providers": {
|
|
175
|
-
"provider-name": {
|
|
176
|
-
"baseUrl": "https://api.example.com/v1",
|
|
177
|
-
"apiKey": "your-key",
|
|
178
|
-
"api": "openai-completions",
|
|
179
|
-
"authHeader": true,
|
|
180
|
-
"models": [
|
|
181
|
-
{
|
|
182
|
-
"id": "model-id",
|
|
183
|
-
"name": "Display Name",
|
|
184
|
-
"reasoning": false,
|
|
185
|
-
"contextWindow": 128000,
|
|
186
|
-
"maxTokens": 16384,
|
|
187
|
-
"cost": { "input": 2.5, "output": 10.0, "cacheRead": 0.5, "cacheWrite": 3.0 }
|
|
188
|
-
}
|
|
189
|
-
]
|
|
190
|
-
}
|
|
191
|
-
},
|
|
192
|
-
"default": { "provider": "provider-name", "modelId": "model-id" },
|
|
193
|
-
"embedding": {
|
|
194
|
-
"baseUrl": "https://api.example.com/v1",
|
|
195
|
-
"apiKey": "your-key",
|
|
196
|
-
"model": "BAAI/bge-m3",
|
|
197
|
-
"dimensions": 1024
|
|
198
|
-
},
|
|
199
|
-
"mcpServers": {
|
|
200
|
-
"server-name": {
|
|
201
|
-
"command": "npx",
|
|
202
|
-
"args": ["-y", "@some/mcp-server"]
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
"debugImage": "busybox:latest",
|
|
206
|
-
"debug": false
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
</details>
|
|
211
|
-
|
|
212
|
-
<details>
|
|
213
|
-
<summary><b>IM Channels — Slack / Discord / Telegram / Lark</b></summary>
|
|
214
|
-
|
|
215
|
-
### Slack
|
|
216
|
-
|
|
217
|
-
Configure a Slack bot in **Settings > Channels**. You'll need:
|
|
218
|
-
- Bot token and signing secret from the [Slack API](https://api.slack.com/apps)
|
|
219
|
-
|
|
220
|
-
### Discord
|
|
185
|
+
### Local Server / Kubernetes
|
|
221
186
|
|
|
222
|
-
Configure
|
|
223
|
-
-
|
|
224
|
-
-
|
|
187
|
+
- Configure providers in the **Models** page
|
|
188
|
+
- Import kubeconfigs, API tokens, and SSH credentials in **Credentials**
|
|
189
|
+
- Configure Slack, Lark, Discord, and Telegram in **Channels**
|
|
190
|
+
- Create inbound webhook endpoints in **Triggers**
|
|
191
|
+
- Configure MCP servers in **MCP Servers**
|
|
225
192
|
|
|
226
|
-
|
|
193
|
+
## Documentation
|
|
227
194
|
|
|
228
|
-
|
|
229
|
-
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
Configure a Lark bot in **Settings > Channels** of the web UI. You'll need:
|
|
234
|
-
- App ID and App Secret from the [Lark Open Platform](https://open.larksuite.com/)
|
|
235
|
-
- Event subscription URL: `https://your-domain/api/channels/feishu/event`
|
|
236
|
-
- Scopes: `im:message`, `im:message.group_at_msg`, `im:resource`
|
|
237
|
-
|
|
238
|
-
</details>
|
|
195
|
+
- [Getting Started](https://docs.siclaw.ai/start/getting-started)
|
|
196
|
+
- [CLI & Local Server](https://docs.siclaw.ai/install/cli)
|
|
197
|
+
- [Kubernetes Deployment](https://docs.siclaw.ai/install/kubernetes)
|
|
198
|
+
- [LLM Providers](https://docs.siclaw.ai/configuration/providers)
|
|
199
|
+
- [MCP Servers](https://docs.siclaw.ai/configuration/mcp)
|
|
239
200
|
|
|
240
201
|
## Tech Stack
|
|
241
202
|
|
|
@@ -253,11 +214,10 @@ Configure a Lark bot in **Settings > Channels** of the web UI. You'll need:
|
|
|
253
214
|
|
|
254
215
|
## Community
|
|
255
216
|
|
|
217
|
+
- [Slack](https://join.slack.com/t/siclaw-scitix/shared_invite/zt-3rrsoc2ic-JIfbfvT1_04sqgQorSRfmw) — Chat with the team and other users
|
|
256
218
|
- [GitHub Issues](https://github.com/scitix/siclaw/issues) — Bug reports and feature requests
|
|
257
219
|
- [GitHub Discussions](https://github.com/scitix/siclaw/discussions) — Questions, ideas, and general discussion
|
|
258
220
|
|
|
259
|
-
<!-- TODO: Add Discord invite link once server is created -->
|
|
260
|
-
|
|
261
221
|
## Contributing
|
|
262
222
|
|
|
263
223
|
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, architecture overview, and pull request guidelines.
|
|
@@ -16,6 +16,7 @@ export interface CronJob {
|
|
|
16
16
|
description?: string | null;
|
|
17
17
|
lastRunAt?: string | null;
|
|
18
18
|
lastResult?: string | null;
|
|
19
|
+
workspaceId?: string | null;
|
|
19
20
|
}
|
|
20
21
|
export declare class GatewayClient {
|
|
21
22
|
private gatewayUrl;
|
|
@@ -28,7 +29,7 @@ export declare class GatewayClient {
|
|
|
28
29
|
/**
|
|
29
30
|
* List cron jobs for a user
|
|
30
31
|
*/
|
|
31
|
-
listCronJobs(userId: string): Promise<CronJob[]>;
|
|
32
|
+
listCronJobs(userId: string, workspaceId?: string): Promise<CronJob[]>;
|
|
32
33
|
/**
|
|
33
34
|
* Return a GatewayClientLike adapter for use with resource handlers.
|
|
34
35
|
* Keeps `request()` private while exposing a minimal interface.
|
|
@@ -40,8 +40,12 @@ export class GatewayClient {
|
|
|
40
40
|
/**
|
|
41
41
|
* List cron jobs for a user
|
|
42
42
|
*/
|
|
43
|
-
async listCronJobs(userId) {
|
|
44
|
-
|
|
43
|
+
async listCronJobs(userId, workspaceId) {
|
|
44
|
+
let url = `/api/internal/cron-list?userId=${encodeURIComponent(userId)}`;
|
|
45
|
+
if (workspaceId) {
|
|
46
|
+
url += `&workspaceId=${encodeURIComponent(workspaceId)}`;
|
|
47
|
+
}
|
|
48
|
+
const data = await this.request(url, "GET");
|
|
45
49
|
return data.jobs || [];
|
|
46
50
|
}
|
|
47
51
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-client.js","sourceRoot":"","sources":["../../src/agentbox/gateway-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"gateway-client.js","sourceRoot":"","sources":["../../src/agentbox/gateway-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,17 +11,44 @@ import { hasOpenAIProvider, ensureProxy } from "../core/llm-proxy.js";
|
|
|
11
11
|
import { deepSearchEvents } from "../tools/deep-search/events.js";
|
|
12
12
|
import { createChecklist, buildActivationMessage } from "../tools/dp-tools.js";
|
|
13
13
|
import { loadConfig } from "../core/config.js";
|
|
14
|
+
import { emitDiagnostic } from "../shared/diagnostic-events.js";
|
|
15
|
+
import { checkMetricsAuth } from "../shared/metrics.js"; // also registers metrics subscriber (side-effect)
|
|
14
16
|
import { GatewayClient } from "./gateway-client.js";
|
|
15
17
|
import { getResourceHandler } from "./resource-handlers.js";
|
|
16
18
|
import { RESOURCE_DESCRIPTORS } from "../shared/resource-sync.js";
|
|
19
|
+
import { detectLanguage } from "../shared/detect-language.js";
|
|
20
|
+
import { resolveUnderDir } from "../shared/path-utils.js";
|
|
17
21
|
/**
|
|
18
22
|
* Parse JSON body
|
|
19
23
|
*/
|
|
24
|
+
const MAX_BODY_SIZE = 10 * 1024 * 1024; // 10MB
|
|
25
|
+
const BODY_TIMEOUT_MS = 30_000; // 30s
|
|
20
26
|
async function parseJsonBody(req) {
|
|
21
27
|
return new Promise((resolve, reject) => {
|
|
22
28
|
let body = "";
|
|
23
|
-
|
|
29
|
+
let size = 0;
|
|
30
|
+
let timedOut = false;
|
|
31
|
+
const timer = setTimeout(() => {
|
|
32
|
+
timedOut = true;
|
|
33
|
+
req.destroy();
|
|
34
|
+
reject(new Error("Body read timeout"));
|
|
35
|
+
}, BODY_TIMEOUT_MS);
|
|
36
|
+
req.on("data", (chunk) => {
|
|
37
|
+
if (timedOut)
|
|
38
|
+
return;
|
|
39
|
+
size += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
|
|
40
|
+
if (size > MAX_BODY_SIZE) {
|
|
41
|
+
clearTimeout(timer);
|
|
42
|
+
req.destroy();
|
|
43
|
+
reject(new Error(`Body exceeds ${MAX_BODY_SIZE} byte limit`));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
body += chunk;
|
|
47
|
+
});
|
|
24
48
|
req.on("end", () => {
|
|
49
|
+
clearTimeout(timer);
|
|
50
|
+
if (timedOut)
|
|
51
|
+
return;
|
|
25
52
|
try {
|
|
26
53
|
resolve(body ? JSON.parse(body) : {});
|
|
27
54
|
}
|
|
@@ -29,7 +56,10 @@ async function parseJsonBody(req) {
|
|
|
29
56
|
reject(new Error("Invalid JSON"));
|
|
30
57
|
}
|
|
31
58
|
});
|
|
32
|
-
req.on("error",
|
|
59
|
+
req.on("error", (err) => {
|
|
60
|
+
clearTimeout(timer);
|
|
61
|
+
reject(err);
|
|
62
|
+
});
|
|
33
63
|
});
|
|
34
64
|
}
|
|
35
65
|
/**
|
|
@@ -91,7 +121,56 @@ export function createHttpServer(sessionManager) {
|
|
|
91
121
|
handler,
|
|
92
122
|
});
|
|
93
123
|
}
|
|
124
|
+
// ── Credential materialization helper (shared by prompt and reload-credentials) ──
|
|
125
|
+
// Writes new files with .new suffix first, then atomically renames them into place.
|
|
126
|
+
// This prevents data loss if the process crashes between delete and write.
|
|
127
|
+
function materializeCredentials(payload, kubeconfigRef) {
|
|
128
|
+
const credDir = path.resolve(process.cwd(), loadConfig().paths.credentialsDir);
|
|
129
|
+
fs.mkdirSync(credDir, { recursive: true });
|
|
130
|
+
// Phase 1: Write new files with .new suffix (staging)
|
|
131
|
+
const stagedFiles = [];
|
|
132
|
+
for (const file of payload.files) {
|
|
133
|
+
const resolved = resolveUnderDir(credDir, file.name);
|
|
134
|
+
const staged = resolved + ".new";
|
|
135
|
+
fs.writeFileSync(staged, file.content, file.mode ? { mode: file.mode } : undefined);
|
|
136
|
+
stagedFiles.push(file.name);
|
|
137
|
+
}
|
|
138
|
+
// Stage manifest
|
|
139
|
+
const manifestPath = path.join(credDir, "manifest.json");
|
|
140
|
+
fs.writeFileSync(manifestPath + ".new", JSON.stringify(payload.manifest, null, 2));
|
|
141
|
+
// Phase 2: Remove old files
|
|
142
|
+
for (const entry of fs.readdirSync(credDir)) {
|
|
143
|
+
if (entry.endsWith(".new"))
|
|
144
|
+
continue; // skip staged files
|
|
145
|
+
fs.rmSync(path.join(credDir, entry), { recursive: true });
|
|
146
|
+
}
|
|
147
|
+
// Phase 3: Rename staged files into place (atomic per-file on same filesystem)
|
|
148
|
+
for (const file of payload.files) {
|
|
149
|
+
const resolved = resolveUnderDir(credDir, file.name);
|
|
150
|
+
fs.renameSync(resolved + ".new", resolved);
|
|
151
|
+
}
|
|
152
|
+
fs.renameSync(manifestPath + ".new", manifestPath);
|
|
153
|
+
kubeconfigRef.credentialsDir = credDir;
|
|
154
|
+
return payload.files.length;
|
|
155
|
+
}
|
|
94
156
|
// ==================== Routes ====================
|
|
157
|
+
/**
|
|
158
|
+
* GET /metrics - Prometheus metrics endpoint
|
|
159
|
+
*/
|
|
160
|
+
addRoute("GET", "/metrics", async (req, res) => {
|
|
161
|
+
if (!checkMetricsAuth(req, res))
|
|
162
|
+
return;
|
|
163
|
+
const { metricsRegistry } = await import("../shared/metrics.js");
|
|
164
|
+
res.writeHead(200, { "Content-Type": metricsRegistry.contentType });
|
|
165
|
+
res.end(await metricsRegistry.metrics());
|
|
166
|
+
});
|
|
167
|
+
/**
|
|
168
|
+
* GET /api/internal/metrics-snapshot - export metrics snapshot for Gateway pull (K8s mode)
|
|
169
|
+
*/
|
|
170
|
+
addRoute("GET", "/api/internal/metrics-snapshot", async (_req, res) => {
|
|
171
|
+
const { localCollector } = await import("../shared/local-collector.js");
|
|
172
|
+
sendJson(res, 200, localCollector.exportSnapshot());
|
|
173
|
+
});
|
|
95
174
|
/**
|
|
96
175
|
* GET /health - health check
|
|
97
176
|
*/
|
|
@@ -128,22 +207,21 @@ export function createHttpServer(sessionManager) {
|
|
|
128
207
|
return;
|
|
129
208
|
}
|
|
130
209
|
const managed = await sessionManager.getOrCreate(body.sessionId, body.mode, body.brainType);
|
|
131
|
-
// Materialize credential files from payload (sent by gateway in prompt body)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
console.log(`[agentbox-http] Materialized ${body.credentials.files.length} credential files to ${credDir}`);
|
|
210
|
+
// Materialize credential files from payload (sent by gateway in prompt body).
|
|
211
|
+
// Always call when credentials payload is present — even with empty files —
|
|
212
|
+
// so stale credential files from prior sessions are cleaned up.
|
|
213
|
+
if (body.credentials) {
|
|
214
|
+
try {
|
|
215
|
+
const count = materializeCredentials(body.credentials, managed.kubeconfigRef);
|
|
216
|
+
if (count > 0) {
|
|
217
|
+
console.log(`[agentbox-http] Materialized ${count} credential files`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch (err) {
|
|
221
|
+
console.error(`[agentbox-http] Failed to materialize credentials for session ${managed.id}:`, err);
|
|
222
|
+
sendJson(res, 500, { error: "Failed to materialize credentials" });
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
147
225
|
}
|
|
148
226
|
// Dynamically register provider config from gateway DB (before findModel)
|
|
149
227
|
if (body.modelConfig && body.modelProvider && managed.brain.registerProvider) {
|
|
@@ -238,10 +316,52 @@ export function createHttpServer(sessionManager) {
|
|
|
238
316
|
console.log(`[agentbox-http] DP exited for SDK brain, session ${managed.id}`);
|
|
239
317
|
}
|
|
240
318
|
}
|
|
319
|
+
// --- Language detection: inject explicit instruction so model doesn't guess ---
|
|
320
|
+
const detectedLang = detectLanguage(body.text);
|
|
321
|
+
if (detectedLang !== "English") {
|
|
322
|
+
promptText = `[System: respond in ${detectedLang}]\n${promptText}`;
|
|
323
|
+
}
|
|
324
|
+
// Programmatically update PROFILE.md Language field (code-level, not model-dependent).
|
|
325
|
+
// Only update on non-English detection to avoid flapping: English is the default,
|
|
326
|
+
// so we only persist when the user actively uses another language.
|
|
327
|
+
if (detectedLang !== "English") {
|
|
328
|
+
try {
|
|
329
|
+
const cfg = loadConfig();
|
|
330
|
+
const userDataDir = process.env.SICLAW_USER_DATA_DIR || cfg.paths.userDataDir;
|
|
331
|
+
const profilePath = path.resolve(userDataDir, "memory", "PROFILE.md");
|
|
332
|
+
if (fs.existsSync(profilePath)) {
|
|
333
|
+
const content = fs.readFileSync(profilePath, "utf-8");
|
|
334
|
+
const currentLangMatch = content.match(/\*\*Language\*\*:\s*(.+)/i);
|
|
335
|
+
const currentLang = currentLangMatch?.[1]?.trim();
|
|
336
|
+
if (currentLang !== detectedLang) {
|
|
337
|
+
const updated = content.replace(/(\*\*Language\*\*:\s*).+/i, `$1${detectedLang}`);
|
|
338
|
+
fs.writeFileSync(profilePath, updated);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
catch { /* best-effort, don't block prompt */ }
|
|
343
|
+
}
|
|
241
344
|
// Execute prompt asynchronously; notify SSE to close on completion
|
|
242
|
-
console.log(`[agentbox-http] Starting prompt for session ${managed.id}`);
|
|
345
|
+
console.log(`[agentbox-http] Starting prompt for session ${managed.id} [lang=${detectedLang}]`);
|
|
346
|
+
// Metrics: snapshot stats before prompt for delta calculation
|
|
347
|
+
const prevStats = managed.brain.getSessionStats();
|
|
348
|
+
const promptStartTime = Date.now();
|
|
349
|
+
let promptOutcome = "completed";
|
|
243
350
|
const actuallyFinish = () => {
|
|
244
351
|
managed._promptDone = true;
|
|
352
|
+
// Emit prompt metrics via diagnostic event bus
|
|
353
|
+
const currStats = managed.brain.getSessionStats();
|
|
354
|
+
const model = managed.brain.getModel();
|
|
355
|
+
emitDiagnostic({
|
|
356
|
+
type: "prompt_complete",
|
|
357
|
+
sessionId: managed.id,
|
|
358
|
+
prev: prevStats,
|
|
359
|
+
curr: currStats,
|
|
360
|
+
model,
|
|
361
|
+
durationMs: Date.now() - promptStartTime,
|
|
362
|
+
outcome: promptOutcome,
|
|
363
|
+
userId: sessionManager.userId,
|
|
364
|
+
});
|
|
245
365
|
// Stop buffering
|
|
246
366
|
if (managed._bufferUnsub) {
|
|
247
367
|
managed._bufferUnsub();
|
|
@@ -281,9 +401,11 @@ export function createHttpServer(sessionManager) {
|
|
|
281
401
|
};
|
|
282
402
|
managed.brain.prompt(promptText).then(() => {
|
|
283
403
|
console.log(`[agentbox-http] Prompt completed for session ${managed.id}`);
|
|
404
|
+
promptOutcome = "completed";
|
|
284
405
|
onPromptFinish();
|
|
285
406
|
}).catch((err) => {
|
|
286
407
|
console.error(`[agentbox-http] Prompt error for session ${managed.id}:`, err);
|
|
408
|
+
promptOutcome = "error";
|
|
287
409
|
onPromptFinish();
|
|
288
410
|
});
|
|
289
411
|
sendJson(res, 200, { ok: true, sessionId: managed.id, brainType: managed.brainType });
|
|
@@ -528,6 +650,49 @@ export function createHttpServer(sessionManager) {
|
|
|
528
650
|
}
|
|
529
651
|
});
|
|
530
652
|
}
|
|
653
|
+
/**
|
|
654
|
+
* POST /api/reload-credentials — push-based credential update from Gateway
|
|
655
|
+
*
|
|
656
|
+
* Accepts the same {manifest, files} payload as the prompt body's credentials
|
|
657
|
+
* field and re-materializes credential files on disk. This allows the Gateway
|
|
658
|
+
* to push credential changes (kubeconfig upload/delete, credential CRUD)
|
|
659
|
+
* without waiting for the next prompt.
|
|
660
|
+
*/
|
|
661
|
+
addRoute("POST", "/api/reload-credentials", async (req, res) => {
|
|
662
|
+
const body = (await parseJsonBody(req));
|
|
663
|
+
if (!body.manifest) {
|
|
664
|
+
sendJson(res, 400, { error: "Missing 'manifest' field" });
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
// Each AgentBox pod serves exactly one user (one-pod-per-workspace in K8s mode,
|
|
668
|
+
// one in-process instance per user in local mode). All sessions within this
|
|
669
|
+
// AgentBox belong to the same user, so updating any session's kubeconfigRef is correct.
|
|
670
|
+
const sessions = sessionManager.list();
|
|
671
|
+
const kubeconfigRef = sessions.length > 0
|
|
672
|
+
? sessions[0].kubeconfigRef
|
|
673
|
+
: { credentialsDir: undefined };
|
|
674
|
+
const payload = { manifest: body.manifest, files: body.files ?? [] };
|
|
675
|
+
try {
|
|
676
|
+
// Use atomic materializeCredentials for both populate and clear paths
|
|
677
|
+
const count = materializeCredentials(payload, kubeconfigRef);
|
|
678
|
+
if (count > 0) {
|
|
679
|
+
console.log(`[agentbox-http] Credentials reloaded: ${count} files materialized`);
|
|
680
|
+
}
|
|
681
|
+
else {
|
|
682
|
+
console.log("[agentbox-http] Credentials cleared (empty payload)");
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
catch (err) {
|
|
686
|
+
console.error("[agentbox-http] Failed to reload credentials:", err);
|
|
687
|
+
sendJson(res, 500, { error: "Failed to materialize credentials" });
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
// Update kubeconfigRef on all active sessions
|
|
691
|
+
for (const session of sessions) {
|
|
692
|
+
session.kubeconfigRef.credentialsDir = kubeconfigRef.credentialsDir;
|
|
693
|
+
}
|
|
694
|
+
sendJson(res, 200, { ok: true, count: payload.files.length });
|
|
695
|
+
});
|
|
531
696
|
/**
|
|
532
697
|
* GET /api/models - list available models (read from settings.json)
|
|
533
698
|
*/
|