Commit Graph
10 Commits
Author SHA1 Message Date
chenchenandClaude Opus 4.8 8fe1f5e131 fix(agent,secret): enforce CRYPTO_SECRET for agent deploy (#31) and guard unscoped vault purge (#33)
#31: HeicodeDeployAgent now refuses to deploy unless CRYPTO_SECRET is explicitly
configured, so the per-agent access_token is sealed with a key that survives a
container restart. common.CryptoSecret is never literally "" (defaults to
uuid/SessionSecret), so the sealAgentToken plaintext fallback was effectively
unreachable; the real hazard is an ephemeral random seal key making tokens
undecryptable after restart. Dev-only override: HEICODE_ALLOW_PLAINTEXT_AGENT_TOKEN_IN_DEV=true.
Verified prod container has CRYPTO_SECRET set (64 chars) -> deploy stays allowed.

#33: StartSecretPurgeTask refuses to start a whole-vault purge when
HEICODE_SECRET_PURGE_NAME_PREFIX is empty unless HEICODE_SECRET_PURGE_VAULT_EXCLUSIVE=true,
so HM never permanently purges another tenant's soft-deleted secrets in a shared
vault. Logs the resolved purge scope at startup.

Both gates extracted into pure, unit-tested helpers (agentTokenSealKeyConfigured,
secretPurgeScopeAllowed). Affects: Manager only (Agent deploy + Secret lifecycle).
No Client/Swarm/billing/audit schema change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 00:33:47 +08:00
chenchenandClaude Opus 4.8 1d81002c2d fix(agent): default MODEL_NAME=gpt-5.4 for started agents (not template tier)
Preset templates carry a Claude-style frontmatter model (opus/sonnet) which is
NOT a model on the HM gateway. Passing it as MODEL_NAME would make the agent's
model calls fail. Now MODEL_NAME defaults to the gateway model gpt-5.4
(env AGENT_RUNTIME_DEFAULT_MODEL); a non-Claude-tier template model is honored.
Tests + AM contract doc updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 16:25:21 +08:00
chenchenandClaude Opus 4.8 557fb2b829 test(agent): fix status round-trip path assertion to /agents/{id}
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:48:09 +08:00
chenchenandClaude Opus 4.8 074a3cc7e7 feat(agent): align HM to AM's coding_a2a_agent contract
Adapt HM's template-agent integration to AM's actual CODING_A2A API (per their
doc), keeping it isolated in agent_template_runtime.go:

- start payload -> AM's POST /agents { name, template:"coding_a2a_agent",
  framework:"A2A", config:{user_id,...}, env } with the template .md folded into
  env.AGENT_INSTRUCTION_TEXT, template_key -> AGENT_ROLE_NAME, model gateway via
  OPENAI_BASE_URL + MODEL_NAME (OPENAI_API_KEY left to the client per A2A request).
- response parse -> access_info.domain/external_ip -> subdomain, namespace/name
  -> runtime_id; AM issues no access_token (client uses A2A api_key).
- env names aligned to AM: GIT_DEFAULT_BRANCH, POSTGRES_* (was PG_*),
  AZURE_BLOB_ACCOUNT_NAME/CONTAINER/ACCOUNT_KEY (was BLOB_*); source keys aligned
  to the resource-binding form (db_name/username/database_password/access_key).
  Only AM-supported types (git/mysql/postgres/azure-blob); vm/redis/mongo/bucket
  now rejected as unsupported until AM adds them.
- frontend: resources page splits DB into MySQL/PostgreSQL (correct provider),
  drops vm; deploy page hides unsupported resource types.
- docs: AM contract + client doc updated to the real env names, payload, and the
  A2A direct-connect (message/send · message/stream) + api_key auth.
- tests updated for the new env names + AM payload/response shape. All green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:30:33 +08:00
chenchenandClaude Opus 4.8 ac32a2db51 fix(agent): encrypt agent access_token at rest (AES-256-GCM via CryptoSecret)
Closes the last review finding: the AM-issued access_token was stored in plaintext
in agent_deployments. Now sealed with common.SealWithCryptoSecret on write and
unsealed on read — the client still receives the plaintext token (no contract
change), but a DB dump no longer exposes live agent tokens. Legacy/plaintext
values and an unset CryptoSecret degrade gracefully (pass-through). Round-trip test added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 14:35:15 +08:00
chenchenandClaude Opus 4.8 7a48155568 fix(agent): apply review findings (backend data-integrity + frontend error UX)
Two review agents (backend + frontend) audited the template-agent feature. Real
findings fixed (verified the one false-positive — okOrThrow already reads the
top-level success because the response interceptor returns response, not
response.data):

Backend:
- buildAgentEnvFromBindings now filters status='active' so a revoked/disabled
  binding can't still have its KV secret resolved + injected.
- same-type binding env collision (e.g. two git repos -> GIT_REPO_URL) now errors
  instead of silently overwriting.
- refreshAgentStatus uses a field-level Updates (status/updated_at) instead of
  Save(full row) to avoid clobbering a concurrent stop/delete.
- tests added: same-type collision rejected, inactive binding rejected.

Frontend:
- deploy/stop/delete now pass noBusinessError so the global interceptor stops
  double-toasting the raw English backend message; okOrThrow+onError give one
  friendly Chinese error. Extracted okOrThrow/noBusinessError into lib/api.ts.
- resources page revoke + create + KV-secret-write now check success (okOrThrow)
  so a failed unbind / credential write no longer falsely reports success.
- clipboard copy wrapped in try/catch (no false "copied" on failure).
- resources group label shows Chinese resource-type names.

Go + frontend builds/tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 14:24:25 +08:00
chenchenandClaude Opus 4.8 4605cf4f62 feat(agent): HM-maintained agent template library (md), sent to AM on deploy
Templates now live in HM (not AM). A template is a Claude-Code style subagent
.md definition; HM passes it to AM at deploy time.

- model AgentTemplate (agent_templates): template_key, name_zh / description_zh
  (Chinese display for the console), model, definition (full .md), source, status.
- 19 presets from oh-my-claudecode (MIT, NOTICE.md attribution) embedded via
  go:embed and idempotently seeded; Chinese name+desc mapping in code.
- GET /api/heicode/agent-templates now reads HM's library (Chinese name/desc),
  not AM. Admin CRUD at /api/agent-templates (AdminAuth).
- deploy loads the chosen template and sends {template_key, agent_definition (md),
  model, env, callback_url} to AM via a generic /api/agent/agents/start; removed
  the AM-template-listing path. AM adapter still isolated (amStartArgs).
- tests: frontmatter parse, seed (19 + 架构顾问), start round-trip asserts
  agent_definition in payload. All green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 00:05:08 +08:00
chenchenandClaude Opus 4.8 19fd4f3326 refactor(agent): built-in per-type env mapping; fix start timeout, orphan, guards
Replace the user-facing env_map mistake with a built-in env convention keyed by
resource type + provider — users never see/edit env names; they only fill plain
resource fields. Supports git (gitea/github/gitlab), vm, database
(mysql/pg/redis/mongo, with alias normalisation), storage (azure blob / bucket).
Lenient: missing optional fields are skipped; only unsupported type or a KV read
failure errors.

Other issues found in review and fixed:
- start timeout: template-agent start now uses a longer timeout (default 60s,
  AGENT_RUNTIME_START_TIMEOUT_SECONDS) since AM provisions synchronously — 5s
  would time out. amTemplateDo takes a per-call timeout.
- orphan agent: if AM start succeeds but the Manager record fails to persist, the
  orphan is rolled back (best-effort amDeleteTemplateAgent).
- findUserTemplateAgent now guards template_id<>'' so the new endpoints can't
  touch a legacy task deployment.
- binding_ids defaults to [] (not null).
- removed ResourceBinding.EnvMap field entirely.

Tests rewritten for the built-in convention (blob metadata-only, db provider
prefixes incl pg/mg aliases, git provider-agnostic names, ownership, unsupported
type, empty); adapter round-trip + router tests still pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 22:48:29 +08:00
chenchenandClaude Opus 4.8 65a71f6b35 feat(agent): pull live template-agent status from AM (no callback needed)
- amGetAgentStatus: HM pulls a running agent's status from AM on demand
  (is it alive / crashed), isolated in the AM adapter like the rest.
- GET /api/heicode/agents/:id/status + status refresh on GET /:id detail,
  best-effort (keeps last-known status if AM is unreachable; never blocks read).
- tests: httptest-backed round-trip tests for amStartTemplateAgent and
  amGetAgentStatus against a fake AM (validates the full request/parse path);
  router registration still clean. All pass.

Note: no required-resource validation added — HM injects whatever bindings the
user selected into the agent .env (per env_map); AM reads what it needs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 22:04:00 +08:00
chenchenandClaude Opus 4.8 cdbc43baeb feat(agent): template-agent deploy/list/stop API (AM contract isolated)
HM-side logic for the template-agent model, written against a proposed AM
contract that is fully isolated in agent_template_runtime.go — when AM ships its
real API, only the default paths + response field names in that one file change.

- agent_template_runtime.go: AM adapter (list templates, start template agent
  with env, stop/delete) reusing the existing runtime config/url/envelope helpers.
- agent_template_handlers.go: GET /api/heicode/agent-templates; POST /api/heicode/
  agents (resolve bindings -> env, start via AM, persist subdomain+token);
  GET /agents, GET/:id, POST/:id/stop, DELETE/:id. Owner-scoped; env never logged.
- reuses AgentDeployment as the agent record (TemplateID/Subdomain/AccessToken/
  BindingIDsJSON) and buildAgentEnvFromBindings for env assembly.
- routes wired under /api/heicode (UserOrV2DeviceAuth), verified no registration
  panic (router tests pass).
- agent_template_test.go: 7 independent unit tests (env assembly metadata-only,
  ownership, secret-without-ref, unknown-source, empty; path substitution;
  response mapping) — all pass without AM/KV.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:55:24 +08:00