文件历史

59 次代码提交

作者 SHA1 备注 提交日期
litiantian03 95d6faad73 fix: resource skill upload and team member numbering 2026-06-02 16:22:11 +08:00
litiantian03 7fa5c94093 feat(k8s): manage instance workloads with Deployments
Create instance workloads as single-replica Deployments instead of bare Pods so Kubernetes can recreate Pods after deletion, eviction, or node/runtime failures.
Update stop/delete and orphan cleanup to remove Deployments before residual Pods, and improve status sync to keep instances creating while a Deployment exists without a Pod and avoid stale Failed/Evicted Pods affecting instance state.
2026-06-02 10:14:23 +08:00
litiantian03 29375f04f2 fix(instance): mount Hermes/desktop PVCs at /config with legacy layout migration 2026-06-02 09:15:59 +08:00
litiantian03 6f02978997 Tune desktop clipboard and shared memory defaults
- Disable Webtop/KasmVNC bidirectional clipboard sync by default to reduce accidental paste during remote desktop input
- Derive desktop runtime /dev/shm defaults from instance memory: 1G for 4G, 2G for 8G, and 4G for 12G+
- Preserve explicit SHM_SIZE_GB overrides and keep shell runtime behavior unchanged
- Add tests for openclaw/hermes clipboard policy and /dev/shm default selection
- Support multiple OpenClaw channel resources and fix alias injection
2026-05-28 16:25:24 +08:00
litiantian03 561b263c8b feat: add Bundle Skills, WeCom channel, configurable archive limit, fix #123
- Bundle Skills: attach skills to config bundles, auto-resolve on creation. Migration 022 adds openclaw_config_bundle_skills table. Fix #123
- WeCom channel: wecom connector with botId/secret/dmPolicy/allowFrom
- CLAWMANAGER_WORKSPACE_ARCHIVE_MAX_MIB controls archive size limit, synced to nginx client_max_body_size via start.sh
- Update deployment manifests, i18n, and frontend UI
2026-05-26 17:17:07 +08:00
litiantian03 a64fa20997 feat(team): add paginated task/event history and improve chat deduplication
Add cursor-based GET /teams/:id/tasks and GET /teams/:id/events APIs.
Team detail page loads older messages on scroll/click and deduplicates
collaboration chat messages with improved thread ordering.
2026-05-25 10:29:25 +08:00
Qingshan Chen d47c5f0b8c feat: add openclaw shell runtime preset 2026-05-22 19:21:11 +08:00
Qingshan Chen c192908d95 feat: add shell runtime and harden team shared storage 2026-05-22 15:06:59 +08:00
Qingshan Chen 8bfd6340e9 Merge remote-tracking branch 'upstream/main' into codex/shell
# Conflicts:
#	backend/internal/services/instance_service.go
#	backend/internal/services/k8s/pod_service.go
#	backend/internal/services/k8s/pod_service_test.go
#	frontend/src/components/InstanceAccess.tsx
2026-05-18 22:12:46 +08:00
litiantian03 3fe1418d5e feat(team): Multi-agent Team control plane across runtimes (Leader-mediated collaboration)
- Add migrations plus Team/Member/Task/Event APIs; Redis Streams consumer projects inbox/events into DB as source of truth
- Inject Team Secret (Redis URL, team token) via envFrom; shared RWX PVC at /team; sync ConfigMap roster to /team/team.json
- Create member Pods through InstanceService; extend K8s (PVC/Secret/Pod/ConfigMap); stale-task sweep and Team/member deletion with cleanup
- Add /teams, /teams/new, /teams/🆔 creation wizard (roster, presets, shared env/OpenClaw plan), per-member desktops, collaboration timeline, debug dispatch (defaults to Leader when target omitted)
2026-05-18 16:30:26 +08:00
Qingshan Chen 7ceefbf6a8 shell init 2026-05-13 20:19:44 +08:00
Qingshan Chen a3bbedbbdc fix: paginate user management list 2026-05-12 20:35:37 +08:00
litiantian03 c0c6b763af fix build issue 2026-05-07 11:00:33 +08:00
litiantian03 054a2b9b44 fix: resolve OpenClaw upload crash with least-privilege pod settings
- add configurable /dev/shm mounts for instance pods with a bounded SHM_SIZE_GB override
- introduce pod security modes and use chromium-compat for OpenClaw instead of privileged
- keep privileged mode only as an explicit admin fallback
- remove the node-level clawmanager-node-tuner manifest to avoid changing host security defaults
- fix k3s HTTPS and API/proxy service port mappings
- add tests for SHM parsing and pod security mode behavior
2026-05-07 10:34:37 +08:00
hippoley 963a1c378e fix: use persistent path for PV hostPath instead of /tmp/
BREAKING: PV hostPath prefix changed from /tmp/clawreef/ to /data/clawreef/

Problem:
- PV hostPath was hardcoded to /tmp/clawreef/user-{id}/instance-{id}
- /tmp/ is a volatile directory that may be cleaned by systemd-tmpfiles
  or cleared on node reboot (depending on OS configuration)
- This caused data loss when worker-02 was rebooted by the hypervisor

Fix:
- Add configurable hostPathPrefix to RuntimePVCConfig (default: /data/clawreef)
- Support K8S_PV_HOST_PATH_PREFIX environment variable override
- Update deployment manifests to use /data/clawmanager/ for MySQL and MinIO

Migration:
- Existing deployments should move data from /tmp/clawreef/ to /data/clawreef/
  and create a symlink for backward compatibility:
    mkdir -p /data/clawreef
    mv /tmp/clawreef/* /data/clawreef/
    rm -rf /tmp/clawreef && ln -s /data/clawreef /tmp/clawreef
2026-05-07 07:57:44 +08:00
Qingshan Chen 55e9edfb83 fix: make instance deletion asynchronous 2026-04-30 19:24:17 +08:00
Qingshan Chen 6c8bf5180c fix: normalize agents runtime image references 2026-04-29 17:24:49 +08:00
Qingshan Chen 58c35f6ecd Merge pull request #88 from naiqus/fix/openclaw-channel-editor-field-dropping
fix: channel editors silently dropping unknown fields on save
2026-04-29 15:14:37 +08:00
Qingshan Chen e2613b4c45 feat: add hermes runtime integration 2026-04-29 15:00:57 +08:00
Suqian Zhang 08dce2a29d fix: remove duplicate test function declarations
TestNormalizeOpenClawResourceContentPreservesUnknownChannelFields and
TestNormalizeOpenClawResourceContentPreservesFeishuSiblingAccounts were
declared twice causing build failure.
2026-04-27 23:48:07 +02:00
Suqian Zhang 032c038275 Merge branch 'fix/openclaw-channel-editor-field-dropping' of github.com:naiqus/ClawManager into fix/openclaw-channel-editor-field-dropping 2026-04-27 23:08:58 +02:00
Suqian Zhang c39df72d51 fix(openclaw): preserve unknown fields in channel config save path
Every channel form editor (telegram, dingtalk-connector, slack, feishu)
silently drops fields on Save. The user flow that loses data:

  1. Open a channel resource in the JSON tab, add any field not owned
     by the form (e.g. webhook, custom capabilities, additional feishu
     accounts).
  2. Switch to the Form tab and change anything (e.g. rotate a secret).
  3. Click Save.
  4. The field from step 1 is gone from the saved config — and stays
     gone after a reload.

The data loss occurs in two places; both must be fixed:

Frontend (OpenClawConfigCenterPage.tsx):
  Each update*ChannelContentText rebuilt the output from a hard-coded
  allowlist instead of merging form fields into the parsed config. Any
  field not in the allowlist was dropped on Save. For feishu this
  collapsed a multi-account config to a single `main` account.

Backend (openclaw_config_service.go):
  normalizeOpenClawResourceContent calls normalize*ChannelConfigForEnv
  on every create/update to validate and rebuild the stored config.
  Those helpers use the same allowlist pattern, so even if the
  frontend sends a complete config, the backend strips tenant-authored
  keys before persisting.

Fix:

  - Frontend: introduce a single mergeChannelConfig(existing, allowlisted)
    helper and route every editor's output through it. Each handler
    builds an allowlisted object containing only its known keys; the
    helper overlays it onto the parsed existing config so unknown keys
    survive. The merge point is now structural rather than a per-handler
    convention, so a new editor (e.g. an MS Teams handler added in a
    downstream patch) can't regress by forgetting the spread. Feishu's
    accounts deep-merge is preserved by building the merged accounts
    inside the allowlisted object before handing off to the helper.

  - Backend: introduce mergeOpenClawChannelConfigForStorage, which
    overlays the allowlist-normalized output onto the original parsed
    payload at storage time. Normalized keys still win; unknown keys
    pass through. For feishu the accounts map is merged member-wise.

Env-rendering semantics are unchanged. appendCompiledOpenClawEnvPayload
continues to call the *ForEnv helpers at render time against the
stored content, so the runtime env payload sees the same allowlist as
before.

Tests:

  - Two existing ResourcePayloadFromModelNormalizesStored*JSON tests
    asserted the pre-fix behavior (dropping `legacyField`) and are
    updated to assert it survives.
  - New TestNormalizeOpenClawResourceContentPreservesUnknownChannelFields
    (table-driven, three editors) guards the preservation invariant.
  - New TestNormalizeOpenClawResourceContentPreservesFeishuSiblingAccounts
    guards the feishu deep-merge.

Manual verification against a live instance: add `webhook` via the
JSON tab, switch to Form tab, rotate a secret, Save, reload — the
webhook field is retained.
2026-04-27 23:07:24 +02:00
Qingshan Chen 30abf94c45 Merge pull request #95 from hippoley/fix/image-pull-policy-configurable
Release / Prepare Scheduled Release (push) Has been skipped
Release / Publish Release (push) Failing after 0s
fix: default imagePullPolicy to IfNotPresent for air-gapped environments
2026-04-27 21:46:35 +08:00
hippoley 584b071e35 fix: default imagePullPolicy to IfNotPresent for air-gapped environments
Kubernetes defaults imagePullPolicy to Always when the image tag is
:latest. This causes pod creation failures in air-gapped and enterprise
environments where nodes cannot reach external registries.

Changes:
- Add ImagePullPolicy field to PodConfig struct
- Default to IfNotPresent in CreatePod when not explicitly set
- Support IMAGE_PULL_POLICY env var for operator override
- Apply policy to both CreateInstance and StartInstance paths

Closes #94
2026-04-27 16:32:30 +08:00
litiantian03 bf0125ab23 fix(instances): delete legacy network policy before create/start instead of ensuring default 2026-04-27 09:43:52 +08:00
Suqian Zhang 87fe5a6bdf fix(openclaw): preserve unknown fields in channel config save path
Every channel form editor (telegram, dingtalk-connector, slack, feishu)
silently drops fields on Save. The user flow that loses data:

  1. Open a channel resource in the JSON tab, add any field not owned
     by the form (e.g. webhook, custom capabilities, additional feishu
     accounts).
  2. Switch to the Form tab and change anything (e.g. rotate a secret).
  3. Click Save.
  4. The field from step 1 is gone from the saved config — and stays
     gone after a reload.

The data loss occurs in two places; both must be fixed:

Frontend (OpenClawConfigCenterPage.tsx):
  Each update*ChannelContentText rebuilt the output from a hard-coded
  allowlist instead of merging form fields into the parsed config. Any
  field not in the allowlist was dropped on Save. For feishu this
  collapsed a multi-account config to a single `main` account.

Backend (openclaw_config_service.go):
  normalizeOpenClawResourceContent calls normalize*ChannelConfigForEnv
  on every create/update to validate and rebuild the stored config.
  Those helpers use the same allowlist pattern, so even if the
  frontend sends a complete config, the backend strips tenant-authored
  keys before persisting.

Fix:

  - Frontend: introduce a single mergeChannelConfig(existing, allowlisted)
    helper and route every editor's output through it. Each handler
    builds an allowlisted object containing only its known keys; the
    helper overlays it onto the parsed existing config so unknown keys
    survive. The merge point is now structural rather than a per-handler
    convention, so a new editor (e.g. an MS Teams handler added in a
    downstream patch) can't regress by forgetting the spread. Feishu's
    accounts deep-merge is preserved by building the merged accounts
    inside the allowlisted object before handing off to the helper.

  - Backend: introduce mergeOpenClawChannelConfigForStorage, which
    overlays the allowlist-normalized output onto the original parsed
    payload at storage time. Normalized keys still win; unknown keys
    pass through. For feishu the accounts map is merged member-wise.

Env-rendering semantics are unchanged. appendCompiledOpenClawEnvPayload
continues to call the *ForEnv helpers at render time against the
stored content, so the runtime env payload sees the same allowlist as
before.

Tests:

  - Two existing ResourcePayloadFromModelNormalizesStored*JSON tests
    asserted the pre-fix behavior (dropping `legacyField`) and are
    updated to assert it survives.
  - New TestNormalizeOpenClawResourceContentPreservesUnknownChannelFields
    (table-driven, three editors) guards the preservation invariant.
  - New TestNormalizeOpenClawResourceContentPreservesFeishuSiblingAccounts
    guards the feishu deep-merge.

Manual verification against a live instance: add `webhook` via the
JSON tab, switch to Form tab, rotate a secret, Save, reload — the
webhook field is retained.
2026-04-26 17:50:08 +02:00
Qingshan Chen 3a44141d97 Merge pull request #92 from hippoley/fix/graceful-shutdown-and-goroutine-leaks
Release / Prepare Scheduled Release (push) Has been skipped
Release / Publish Release (push) Failing after 1s
fix: add graceful shutdown, fix WebSocket Hub race, plug goroutine leaks
2026-04-26 00:42:45 +08:00
Qingshan Chen 49725a8e24 Merge pull request #87 from naiqus/fix/openclaw-transfer-path
fix: .openclaw workspace transfer path and upload handling
2026-04-26 00:27:20 +08:00
Qingshan Chen 313cc57ddc Merge pull request #89 from naiqus/fix/admin-instance-view-leakage
fix: admin sees all users' instances in workspace view
2026-04-26 00:04:30 +08:00
hippoley cfd9c46ca8 fix: add graceful shutdown, fix WebSocket Hub race, plug goroutine leaks
Address three HIGH-severity issues from the memory-leak and bloat
analysis (issue #56).

1. Graceful shutdown (main.go)
   - Replace gin's r.Run() with an explicit http.Server so the process
     can intercept SIGINT / SIGTERM.
   - On signal: drain active HTTP requests (10 s timeout), then stop
     SyncService, WebSocket Hub, and InstanceAccessService cleanup
     goroutine in order.
   - Ensures database connections, K8s watchers, and background loops
     are released cleanly on deploy or restart.

2. WebSocket Hub init race (websocket_service.go)
   - GetHub() used a bare nil-check with no synchronisation; two
     goroutines could each create a Hub and start a Run() loop.
   - Replaced with sync.Once to guarantee exactly one Hub instance.
   - Added a stop channel to Hub.Run() so the hub can be shut down
     gracefully, closing all connected clients.

3. InstanceAccessService goroutine leak (instance_access_service.go)
   - cleanupExpiredTokens() looped on ticker.C with no exit path,
     leaking the goroutine for the lifetime of the process.
   - Added a stopChan; cleanupExpiredTokens now selects on both the
     ticker and the stop signal.
   - Exposed Stop() on the service; InstanceHandler.Shutdown() calls
     it during graceful shutdown.

Tests:
- TestGetHubReturnsSameInstance: singleton guarantee
- TestGetHubConcurrentAccess: 50-goroutine race test
- TestHubStopClosesClients: verifies client cleanup on Stop()
- TestInstanceAccessServiceStopTerminatesCleanup: Stop() is safe and
  the service remains functional for token ops afterward

All existing tests continue to pass; full project build and regression
verified.

Ref: #56
2026-04-24 20:07:13 +08:00
hippoley 7bb6a19b85 fix: cascade recompile active snapshots when a resource is updated
When a resource is updated, all compiled/active snapshots that reference
it (directly or via dependsOn) are now automatically recompiled so their
rendered_env_json stays in sync.

Key changes:
- snapshotReferencesResource checks ResolvedResourcesJSON first (full
  resolved set including dependsOn), falling back to
  SelectedResourceIDsJSON for backward compatibility with older snapshots
- Repository-layer CAS via UPDATE ... WHERE updated_at = ? prevents
  concurrent cascades from clobbering each other
- ListActiveSnapshots queries both 'compiled' and 'active' statuses
- Cascade runs in a background goroutine; errors are logged but never
  fail the resource update itself
2026-04-24 18:08:03 +08:00
naiqus a33f3fee16 fix(instances): admin sees all users' instances in workspace view
Summary
-------
Logging in as an admin and navigating to Workspace → My Instances
returned every instance in the cluster instead of the admin's own.
Role was overloaded to both widen the admin console surface AND
widen the self-scoped list endpoint, so the workspace view broke
the owner-isolation contract users expect.

Root cause
----------
`InstanceService.GetVisibleInstances(userID, role, ...)` branched
on role: admin callers fell through to `instanceRepo.GetAll`,
non-admin callers fell through to `GetByUserID`. The single
`GET /instances` handler passed the caller's role into that
function, so the same URL meant "my instances" for users and
"every instance in the system" for admins.

Fix
---
Split the two views at the API surface, not inside the service:

- `GET /instances` is now always caller-scoped. The handler calls
  `GetByUserID` unconditionally and never reads the caller's role.
- `GET /admin/instances` is a new route, gated by the existing
  admin middleware trio (`Auth` + `SetUserInfo` + `NewAdminAuth`).
  It calls a new `InstanceService.GetAllInstances` that does not
  look at any userID.
- `GetVisibleInstances` is removed; nothing else in the codebase
  called it.

Frontend follows: `AdminDashboard` and `InstanceManagementPage`
switch to a new `adminInstanceService.getInstances()` that hits
`/admin/instances`. Per-instance admin actions (start, stop,
delete, proxy, etc.) are intentionally out of scope for this PR
and continue to use the existing endpoints.

Tests
-----
Two new service tests in `instance_visibility_test.go`:

- `TestGetByUserIDFiltersByCaller` pins the workspace contract:
  regardless of how many admins or other users exist, a caller
  only ever sees their own instances through this path.
- `TestGetAllInstancesReturnsEveryUser` covers the admin-console
  path, including pagination.

`go build ./...`, `go test ./...`, and `npm run build` all pass.

Scope note
----------
This PR fixes the listing leak only. Per-instance handlers in
`instance_handler.go` retain their existing inline role checks
and will be audited separately.
2026-04-24 03:37:26 +02:00
naiqus 0a722773da fix(openclaw): align upload limit to 50 MiB 2026-04-24 02:08:35 +02:00
naiqus 255a5c6d0d fix(openclaw): return structured 413 for oversize import uploads
Wrap c.Request.Body with http.MaxBytesReader(512 MiB) so oversize
uploads that bypass the edge (or when nginx is swapped for another
proxy) produce a JSON 413 with a clear message rather than a generic
500 or a parser-side surprise. Also guards against header-declared
sizes exceeding the cap.

Aligns backend and nginx limits on a single named constant.
2026-04-24 02:00:09 +02:00
naiqus 78411ad611 test(openclaw): integration tests for transfer service
Adds build-tagged (//go:build integration) tests that exercise the
Export and Import commands against a real OpenClaw pod:

- Export_RoundTrip: seeds a marker file under /config/.openclaw, runs
  buildExportCommand(), asserts the marker is present in the streamed
  tar.gz.
- Export_MissingWorkspace: moves .openclaw aside and asserts the
  command exits with code 42 (maps to ErrOpenClawWorkspaceMissing).
- Import_OwnershipIsAbc: imports a minimal tar.gz and asserts the
  restored file is owned by uid:gid 1000:1000, verifying the
  `su abc` wrap.

Skipped by default (unit tests cover the command-string contract);
enable with `go test -tags integration` and OPENCLAW_TEST_POD set.
2026-04-24 01:43:39 +02:00
naiqus 08eb5b67ae fix(openclaw): surface empty export as 404 instead of silent zero stream
ExportOpenClaw now maps services.ErrOpenClawWorkspaceMissing to HTTP 404
with an explicit message, and rejects archives below a conservative
minimum-size threshold (100 bytes) as HTTP 500 rather than streaming an
empty body to the client with Content-Type: application/gzip.

Together with the service-side base-dir fix, this closes the
silent-failure path where downloading .openclaw from an instance without
a workspace produced a 200 OK with zero payload.
2026-04-24 01:42:14 +02:00
naiqus cb6cb00d5c fix(openclaw): resolve transfer base dir via CLAWMANAGER_AGENT_PERSISTENT_DIR
Previously Export/Import expanded ${HOME:-/home/user} to locate the
.openclaw workspace. kubectl exec spawns a fresh process as root with
HOME=/root, while the real workspace lives at /config/.openclaw (the
linuxserver entrypoint runs as user abc with HOME=/config). The result
was a silent failure: Export returned an empty 200 stream and Import
extracted into /root/ where OpenClaw never reads.

Resolve the workspace base via ${CLAWMANAGER_AGENT_PERSISTENT_DIR:-/config},
honoring the env var ClawManager already injects at pod creation. When
the workspace is missing, Export exits with code 42 and the service
layer maps it to ErrOpenClawWorkspaceMissing so handlers can surface
a proper HTTP status instead of streaming zero bytes.

Import now re-execs the extract as user abc via `exec su abc -s /bin/sh`
so restored files land owned by uid 1000, matching pod runtime ownership.
2026-04-24 01:41:29 +02:00
naiqus 21a0e2982d refactor(openclaw): extract transfer command builders
Pure refactor introducing buildExportCommand() and buildImportCommand()
helpers. Behavior unchanged; the buggy ${HOME:-/home/user} base dir is
preserved. Makes the commands testable without a cluster and sets up
the subsequent fix commit to change them in one place.
2026-04-24 01:39:30 +02:00
hippoley 95d3862786 fix: renew agent session expiry on each heartbeat
The agent session token issued during Register() has a fixed 24-hour
TTL that is never extended. After exactly 24 hours of continuous
operation, AuthenticateSession() rejects the token and every subsequent
heartbeat returns 401. Because the agent shim does not detect non-2xx
responses, it keeps sending heartbeats silently into the void and the
agent appears permanently offline — recoverable only by a full pod
restart.

Fix: roll the SessionExpiresAt forward by 24 hours on every successful
Heartbeat() call, turning the fixed deadline into a sliding window.
As long as the agent keeps heartbeating (every 30s by default), the
session never expires.

Discovered in production with 12 openclaw instances going offline
simultaneously after exactly 24 hours of uptime.
2026-04-21 15:24:53 +08:00
Qingshan Chen 32bd842470 feat: support multiple runtime images per instance type 2026-04-20 19:26:56 +08:00
Qingshan Chen d14a4bccf4 fix: wait for pod deletion before recreate 2026-04-20 10:27:10 +08:00
Qingshan Chen 8c9216cfa0 perf: optimize desktop proxy access flow 2026-04-20 10:27:04 +08:00
Qingshan Chen c345d9077d feat: inject AI gateway model catalog into openclaw env 2026-04-16 23:07:50 +08:00
Qingshan Chen ab4c7bc990 feat: add dingtalk openclaw channel support 2026-04-16 00:12:08 +08:00
Qingshan Chen 366bae194a feat: improve instance creation environment workflow 2026-04-15 18:20:24 +08:00
River 946388724b chore(docker): remove hardcoded GOPROXY/GOSUMDB from backend Dockerfile 2026-04-14 15:42:52 +08:00
River d5398ee671 feat(instance): support fractional CPU cores in instance creation
- Change cpu_cores from int to float64 in backend models, handlers, services
- Update frontend CreateInstancePage to accept decimal CPU values (step 0.1)
- Add DB migration 009_cpu_cores_decimal.sql for DECIMAL(10,2) columns
- Update deployment manifests (k8s, k3s, incluster) schema definitions
- Add GOFLAGS/GOPROXY/GOSUMDB build args to Dockerfile so that
  --build-arg values passed by build scripts take effect during go mod download
2026-04-14 15:21:56 +08:00
litiantian03 6e4d1dc96b fix(runtime): remove webtop default credentials; set TITLE/SUBFOLDER for ubuntu/openclaw 2026-04-13 14:14:30 +08:00
iamlovingit c847c6c6dc feat(skill-management): add skill management and skill-scanner integration 2026-04-08 16:02:18 +08:00
iamlovingit 4ebb5f46d1 Add instance agent control plane and runtime console 2026-04-05 01:02:53 +08:00