siclaw 0.1.0 → 0.1.2
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 +75 -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 +11 -95
- 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 +11 -2
- package/dist/gateway/agentbox/k8s-spawner.js +95 -52
- 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.d.ts +1 -1
- package/dist/gateway/db/repositories/model-config-repo.js +26 -12
- 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 +962 -163
- 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-CAmSY91d.js +675 -0
- package/dist/gateway/web/dist/assets/index-DMFEh8Pp.css +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 +27 -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 +27 -281
- package/dist/tools/netns-script.js.map +1 -1
- package/dist/tools/node-exec.d.ts +2 -14
- package/dist/tools/node-exec.js +18 -225
- package/dist/tools/node-exec.js.map +1 -1
- package/dist/tools/node-script.js +14 -168
- 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 +21 -225
- 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 +8 -6
- 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,31 +4,37 @@
|
|
|
4
4
|
|
|
5
5
|
# Siclaw
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**Read-only investigation copilot for SRE teams**
|
|
8
8
|
|
|
9
|
+
[](https://www.npmjs.com/package/siclaw)
|
|
9
10
|
[](https://github.com/scitix/siclaw/actions/workflows/ci.yml)
|
|
10
11
|
[](https://nodejs.org/)
|
|
11
12
|
[](https://www.typescriptlang.org/)
|
|
12
13
|
[](LICENSE)
|
|
14
|
+
[](https://join.slack.com/t/siclaw-scitix/shared_invite/zt-3rrsoc2ic-JIfbfvT1_04sqgQorSRfmw)
|
|
13
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)
|
|
14
17
|
|
|
15
18
|
</div>
|
|
16
19
|
|
|
17
20
|
---
|
|
18
21
|
|
|
19
|
-
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>
|
|
20
28
|
|
|
21
29
|
## Features
|
|
22
30
|
|
|
23
|
-
- **Deep Investigation** —
|
|
24
|
-
- **Investigation Memory** —
|
|
25
|
-
- **
|
|
26
|
-
- **Team
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
30
|
-
- **Extensible** — [MCP](https://modelcontextprotocol.io) tool servers for custom data source integrations
|
|
31
|
-
- **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
|
|
32
38
|
|
|
33
39
|
## Architecture
|
|
34
40
|
|
|
@@ -43,28 +49,33 @@ Siclaw is an AI Agent platform for DevOps / SRE, inspired by [OpenClaw](https://
|
|
|
43
49
|
|
|
44
50
|
- **Node.js >= 22.12.0** — [Download](https://nodejs.org/)
|
|
45
51
|
- **npm** — Comes with Node.js
|
|
46
|
-
- **kubectl**
|
|
52
|
+
- **kubectl** — Optional, only needed if you want Siclaw to investigate Kubernetes clusters
|
|
47
53
|
|
|
48
54
|
## Quick Start
|
|
49
55
|
|
|
50
|
-
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
|
+
```
|
|
51
62
|
|
|
52
63
|
### 1. TUI Mode — Personal, local, lowest barrier
|
|
53
64
|
|
|
54
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.
|
|
55
66
|
|
|
56
67
|
```bash
|
|
57
|
-
# Install
|
|
58
|
-
npm install siclaw
|
|
68
|
+
# Install globally
|
|
69
|
+
npm install -g siclaw
|
|
59
70
|
|
|
60
71
|
# Run (interactive — prompts for LLM provider on first launch)
|
|
61
|
-
|
|
72
|
+
siclaw
|
|
62
73
|
|
|
63
74
|
# Single-shot
|
|
64
|
-
|
|
75
|
+
siclaw --prompt "Why is pod nginx-abc in CrashLoopBackOff?"
|
|
65
76
|
|
|
66
77
|
# Continue last session
|
|
67
|
-
|
|
78
|
+
siclaw --continue
|
|
68
79
|
```
|
|
69
80
|
|
|
70
81
|
<details>
|
|
@@ -72,7 +83,7 @@ npx siclaw --continue
|
|
|
72
83
|
|
|
73
84
|
```bash
|
|
74
85
|
git clone https://github.com/scitix/siclaw.git && cd siclaw
|
|
75
|
-
npm ci && npm run build
|
|
86
|
+
npm ci && npm run build:web && npm run build
|
|
76
87
|
npm link # register `siclaw` command globally
|
|
77
88
|
|
|
78
89
|
siclaw # TUI mode
|
|
@@ -87,17 +98,18 @@ siclaw --prompt "..." # single-shot mode
|
|
|
87
98
|
|
|
88
99
|
### 2. Local Server — VM or laptop, recommended for daily use
|
|
89
100
|
|
|
90
|
-
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.
|
|
91
102
|
|
|
92
103
|
```bash
|
|
93
|
-
npm install siclaw
|
|
104
|
+
npm install -g siclaw
|
|
94
105
|
|
|
95
|
-
# Start the server
|
|
96
|
-
|
|
106
|
+
# Start the server
|
|
107
|
+
siclaw local
|
|
97
108
|
|
|
98
109
|
# Open http://localhost:3000
|
|
99
110
|
# Login: admin / admin (default credentials)
|
|
100
|
-
#
|
|
111
|
+
# Configure providers in Models
|
|
112
|
+
# Import kubeconfigs in Credentials
|
|
101
113
|
```
|
|
102
114
|
|
|
103
115
|
<details>
|
|
@@ -105,7 +117,7 @@ npx siclaw local
|
|
|
105
117
|
|
|
106
118
|
```bash
|
|
107
119
|
git clone https://github.com/scitix/siclaw.git && cd siclaw
|
|
108
|
-
npm ci && npm run build && npm run build
|
|
120
|
+
npm ci && npm run build:web && npm run build
|
|
109
121
|
npm link # register `siclaw` command globally
|
|
110
122
|
|
|
111
123
|
siclaw local # start local server
|
|
@@ -115,43 +127,47 @@ siclaw local # start local server
|
|
|
115
127
|
|
|
116
128
|
</details>
|
|
117
129
|
|
|
118
|
-
|
|
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.
|
|
119
136
|
|
|
120
137
|
### 3. Kubernetes — Team / enterprise
|
|
121
138
|
|
|
122
|
-
|
|
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:
|
|
123
142
|
|
|
124
143
|
```bash
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
--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
|
|
128
146
|
```
|
|
129
147
|
|
|
130
|
-
|
|
131
|
-
<summary><b>Using a custom image registry</b></summary>
|
|
132
|
-
|
|
133
|
-
If you need to build and push images to your own registry:
|
|
148
|
+
Then deploy the chart with a MySQL URL:
|
|
134
149
|
|
|
135
150
|
```bash
|
|
136
|
-
# Build and push images
|
|
137
|
-
make docker push REGISTRY=registry.example.com/myteam
|
|
138
|
-
|
|
139
|
-
# Deploy with custom registry
|
|
140
151
|
helm upgrade --install siclaw ./helm/siclaw \
|
|
141
|
-
--namespace siclaw
|
|
142
|
-
--
|
|
152
|
+
--namespace siclaw \
|
|
153
|
+
--create-namespace \
|
|
154
|
+
--set image.registry=registry.example.com/myteam \
|
|
155
|
+
--set image.tag=latest \
|
|
143
156
|
--set database.url="mysql://user:pass@host:3306/siclaw"
|
|
144
157
|
```
|
|
145
158
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
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`.
|
|
149
160
|
|
|
150
161
|
## Configuration
|
|
151
162
|
|
|
152
|
-
###
|
|
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/`
|
|
153
169
|
|
|
154
|
-
Minimal example
|
|
170
|
+
Minimal example:
|
|
155
171
|
|
|
156
172
|
```json
|
|
157
173
|
{
|
|
@@ -159,82 +175,28 @@ Minimal example — copy `settings.example.json` to `.siclaw/config/settings.jso
|
|
|
159
175
|
"default": {
|
|
160
176
|
"baseUrl": "https://api.openai.com/v1",
|
|
161
177
|
"apiKey": "sk-YOUR-KEY",
|
|
178
|
+
"api": "openai-completions",
|
|
162
179
|
"models": [{ "id": "gpt-4o", "name": "GPT-4o" }]
|
|
163
180
|
}
|
|
164
181
|
}
|
|
165
182
|
}
|
|
166
183
|
```
|
|
167
184
|
|
|
168
|
-
|
|
169
|
-
<summary><b>Full settings.json reference</b></summary>
|
|
170
|
-
|
|
171
|
-
```json
|
|
172
|
-
{
|
|
173
|
-
"providers": {
|
|
174
|
-
"provider-name": {
|
|
175
|
-
"baseUrl": "https://api.example.com/v1",
|
|
176
|
-
"apiKey": "your-key",
|
|
177
|
-
"api": "openai-completions",
|
|
178
|
-
"authHeader": true,
|
|
179
|
-
"models": [
|
|
180
|
-
{
|
|
181
|
-
"id": "model-id",
|
|
182
|
-
"name": "Display Name",
|
|
183
|
-
"reasoning": false,
|
|
184
|
-
"contextWindow": 128000,
|
|
185
|
-
"maxTokens": 16384,
|
|
186
|
-
"cost": { "input": 2.5, "output": 10.0, "cacheRead": 0.5, "cacheWrite": 3.0 }
|
|
187
|
-
}
|
|
188
|
-
]
|
|
189
|
-
}
|
|
190
|
-
},
|
|
191
|
-
"default": { "provider": "provider-name", "modelId": "model-id" },
|
|
192
|
-
"embedding": {
|
|
193
|
-
"baseUrl": "https://api.example.com/v1",
|
|
194
|
-
"apiKey": "your-key",
|
|
195
|
-
"model": "BAAI/bge-m3",
|
|
196
|
-
"dimensions": 1024
|
|
197
|
-
},
|
|
198
|
-
"mcpServers": {
|
|
199
|
-
"server-name": {
|
|
200
|
-
"command": "npx",
|
|
201
|
-
"args": ["-y", "@some/mcp-server"]
|
|
202
|
-
}
|
|
203
|
-
},
|
|
204
|
-
"debugImage": "busybox:latest",
|
|
205
|
-
"debug": false
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
</details>
|
|
210
|
-
|
|
211
|
-
<details>
|
|
212
|
-
<summary><b>IM Channels — Slack / Discord / Telegram / Lark</b></summary>
|
|
213
|
-
|
|
214
|
-
### Slack
|
|
215
|
-
|
|
216
|
-
Configure a Slack bot in **Settings > Channels**. You'll need:
|
|
217
|
-
- Bot token and signing secret from the [Slack API](https://api.slack.com/apps)
|
|
218
|
-
|
|
219
|
-
### Discord
|
|
185
|
+
### Local Server / Kubernetes
|
|
220
186
|
|
|
221
|
-
Configure
|
|
222
|
-
-
|
|
223
|
-
-
|
|
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**
|
|
224
192
|
|
|
225
|
-
|
|
193
|
+
## Documentation
|
|
226
194
|
|
|
227
|
-
|
|
228
|
-
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
Configure a Lark bot in **Settings > Channels** of the web UI. You'll need:
|
|
233
|
-
- App ID and App Secret from the [Lark Open Platform](https://open.larksuite.com/)
|
|
234
|
-
- Event subscription URL: `https://your-domain/api/channels/feishu/event`
|
|
235
|
-
- Scopes: `im:message`, `im:message.group_at_msg`, `im:resource`
|
|
236
|
-
|
|
237
|
-
</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)
|
|
238
200
|
|
|
239
201
|
## Tech Stack
|
|
240
202
|
|
|
@@ -252,11 +214,10 @@ Configure a Lark bot in **Settings > Channels** of the web UI. You'll need:
|
|
|
252
214
|
|
|
253
215
|
## Community
|
|
254
216
|
|
|
217
|
+
- [Slack](https://join.slack.com/t/siclaw-scitix/shared_invite/zt-3rrsoc2ic-JIfbfvT1_04sqgQorSRfmw) — Chat with the team and other users
|
|
255
218
|
- [GitHub Issues](https://github.com/scitix/siclaw/issues) — Bug reports and feature requests
|
|
256
219
|
- [GitHub Discussions](https://github.com/scitix/siclaw/discussions) — Questions, ideas, and general discussion
|
|
257
220
|
|
|
258
|
-
<!-- TODO: Add Discord invite link once server is created -->
|
|
259
|
-
|
|
260
221
|
## Contributing
|
|
261
222
|
|
|
262
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
|
*/
|