From 301210ff25e4b34fdd678434837cee56f278a164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Thu, 3 Sep 2026 02:43:55 -0400 Subject: [PATCH] docs(go): audit README and AGENTS Align the README API reference with the exported surface, add the upgrade note, document the shared modules and Linux test setup in AGENTS.md, and scope the Grok expandable-path assertion to Windows. Claude-Session: https://claude.ai/code/session_01Agoc9bRVE9KL7mmWtey9Vq --- AGENTS.md | 13 +++++- README.md | 75 +++++++++++++++++++++----------- cmd/elydora/plugins/grok_test.go | 2 +- 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index aabbc18..710bcca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,6 +47,15 @@ This repository owns the public Go SDK, the `cmd/elydora` CLI, local signing beh - Preserve JSONC comments, line endings, and unknown future hook fields. Reject trailing commas and duplicate keys, validate all 21 current events plus command, HTTP, and prompt handlers, reject settings-defined function handlers, and apply Node.js `RegExp` semantics only to the 15 matcher-enabled events. - Own one exact named matchless `PreToolUse`, `PostToolUse`, and `PostToolUseFailure` triple with 10,000 millisecond timeouts. Generate guard, strict runtime config, canonical private key, and native-payload audit runtime with user settings in one rollback-capable transaction. Propagate frozen and revoked agents through exit code `2`; require effective hooks to be enabled, one exact triple, physical runtime files, strict identity, and exact generated sources for healthy status; direct users to `/hooks` for review. +## Shared Modules + +- `cmd/elydora/plugins/managed_runtime.go` owns install-config validation, managed runtime paths, runtime config validation, runtime identity checks, and the strict and presence-only status contracts. `managed_changes.go` owns runtime config encoding, the four runtime file changes, and the shared directory-plus-commit writer. +- `cmd/elydora/plugins/shell_command.go` owns Node.js resolution, POSIX and PowerShell quoting, encoded Windows commands, the shell and encoded command builders and parsers, and the pre-2.1 quoted Windows parser. +- `cmd/elydora/plugins/jsonc.go` owns strict JSON object decoding, JSONC editing, and `cloneJSONObject`; `managed_files.go` owns physical file and directory inspection; `transaction*.go` owns the rollback-capable commit. +- Provider files keep only their hook document schema, ownership rules, rendering, and provider-specific preconditions. Add a provider-local helper only when the shared module cannot express the provider contract. +- `jsonutil.go` and `legacy_hook_entries.go` serve the Kiro CLI, Letta, and OpenCode adapters, which write settings without the managed transaction; moving them onto the managed model is a product decision. +- Recognizing pre-2.1 Elydora commands stays in place so `elydora install` replaces older entries during upgrades. + ## Code Quality - Preserve the minimum Go version declared in `go.mod`. @@ -65,7 +74,7 @@ This repository owns the public Go SDK, the `cmd/elydora` CLI, local signing beh Run the focused adapter test during development, then execute all release gates before commit: -```powershell +```sh go test ./cmd/elydora/plugins -run -count=1 go test ./... go test -race ./... @@ -75,6 +84,8 @@ govulncheck ./... git diff --check ``` +The Linux test environment leaks provider config into `$HOME`; run the suite with `HOME` and `XDG_CONFIG_HOME` pointed at a scratch directory while `GOPATH`, `GOMODCACHE`, and `GOCACHE` keep their real values. + Provider adapter tests must cover installation, idempotency, official event forwarding, blocking behavior, status, missing runtime files, uninstall ownership, and malformed configuration preservation. Commit and push one root issue before starting the next one. diff --git a/README.md b/README.md index fd01af5..4ee7ca0 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ go get github.com/Elydora-Infrastructure/Elydora-Go-SDK/v2 Requires Go 1.21+. The CLI uses focused TOML and JSONC parsers to preserve user-owned agent configuration. +Upgrading from 2.0.x: run `elydora install` again for every agent. The generated runtimes changed, and `elydora status` does not detect stale runtimes for every adapter. + ## Quick Start ```go @@ -23,18 +25,12 @@ import ( ) func main() { - // Authenticate - auth, err := elydora.Login("https://api.elydora.com", "user@example.com", "password") - if err != nil { - log.Fatal(err) - } - - // Create client + // Create client with an API token issued in the Console client, err := elydora.NewClient(&elydora.Config{ - OrgID: auth.User.OrgID, + OrgID: "org-123", AgentID: "my-agent-id", PrivateKey: "", - Token: auth.Token, + Token: "", }) if err != nil { log.Fatal(err) @@ -113,8 +109,9 @@ Qwen Code `0.20.0` installation writes native user hooks to `$QWEN_HOME/settings |---------|-------------| | `elydora install` | Install Elydora audit hook for a coding agent | | `elydora uninstall` | Remove Elydora audit hook for a coding agent | -| `elydora status` | Show installation status for all agents | +| `elydora status` | Show installation status for one agent or all agents | | `elydora agents` | List supported coding agents | +| `elydora version` | Print the CLI version | ### Supported Agents @@ -162,24 +159,23 @@ client.SetToken("new-api-token") ### Authentication ```go -// Register a new user and organization -reg, err := elydora.Register(baseURL, email, password, - elydora.WithDisplayName("Alice"), - elydora.WithOrgName("Acme Corp"), -) - -// Login and receive a session token -auth, err := elydora.Login(baseURL, email, password) - -// Get current authenticated user profile +// Get the authenticated user and current organization me, err := client.GetMe() +// me.User.Email, me.User.Role, me.CurrentOrganization.OrgID -// Issue a new API token (with optional TTL) +// Issue an API token; this call needs a client whose Token is a Console session token tokenResp, err := client.IssueApiToken(&elydora.IssueApiTokenRequest{ TTLSeconds: &ttlSeconds, }) +client.SetToken(tokenResp.Token) + +// Rotate the installed API token; the response carries the replacement +rotated, err := client.RotateApiToken() +client.SetToken(rotated.Token) ``` +`elydora.Register` and `elydora.Login` remain for password accounts and are deprecated; use the Console sign-in flow. + ### Operations ```go @@ -219,8 +215,11 @@ agent, err := client.RegisterAgent(&elydora.RegisterAgentRequest{ // Get agent details details, err := client.GetAgent(agentID) +// Change the integration type +updated, err := client.UpdateAgent(agentID, elydora.IntegrationTypeClaudecode) + // Freeze an agent -err := client.FreezeAgent(agentID, "security review") +frozen, err := client.FreezeAgent(agentID, "security review") // Revoke a key err := client.RevokeKey(agentID, kid, "key rotation") @@ -229,7 +228,7 @@ err := client.RevokeKey(agentID, kid, "key rotation") agents, err := client.ListAgents() // Unfreeze a previously frozen agent -err := client.UnfreezeAgent(agentID, "review complete") +unfrozen, err := client.UnfreezeAgent(agentID, "review complete") // Delete an agent permanently deleted, err := client.DeleteAgent(agentID) @@ -270,6 +269,25 @@ detail, err := client.GetExport(exportID) data, err := client.DownloadExport(exportID) ``` +### Webhooks + +```go +webhook, err := client.RegisterWebhook(&elydora.RegisterWebhookRequest{ + EndpointURL: "https://example.com/elydora", + Events: []string{"operation.accepted"}, + Secret: "", +}) +webhooks, err := client.ListWebhooks() +err = client.DeleteWebhook(webhook.Webhook.WebhookID) +``` + +### Organization + +```go +members, err := client.ListMembers() +events, err := client.ListAdminEvents(50) +``` + ### JWKS ```go @@ -281,7 +299,11 @@ jwks, err := client.GetJWKS() ```go // Check API health (no authentication required, does not need a client) health, err := elydora.Health("https://api.elydora.com") -// health.Status, health.Version, health.ProtocolVersion, health.Timestamp +// health.Status, health.Version, health.ProtocolVersion, health.Capabilities, health.Timestamp + +// Check dependency health (D1, auth storage, R2, KV) +deep, err := elydora.DeepHealth("https://api.elydora.com") +// deep.Dependencies.D1.Status, deep.Dependencies.KV.LatencyMs ``` ### Constants @@ -299,8 +321,11 @@ elydora.GenesisChainHash // "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | `KeyStatus` | `KeyStatusActive`, `KeyStatusRetired`, `KeyStatusRevoked` | | `ExportStatus` | `ExportStatusQueued`, `ExportStatusRunning`, `ExportStatusDone`, `ExportStatusFailed` | | `ExportFormat` | `ExportFormatJSON`, `ExportFormatPDF` | +| `WebhookStatus` | `WebhookStatusActive`, `WebhookStatusDisabled` | +| `IntegrationType` | One constant per supported agent key plus `IntegrationTypeEnterprise`, `IntegrationTypeGUI`, `IntegrationTypeSDK`, `IntegrationTypeOther` | +| `AdminAction` | One constant per admin event action, for example `AdminActionAgentFreeze` | | `RbacRole` | `RbacRoleOrgOwner`, `RbacRoleSecurityAdmin`, `RbacRoleComplianceAuditor`, `RbacRoleReadonlyInvestigator`, `RbacRoleIntegrationEngineer` | -| `ErrorCode` | `ErrorCodeInvalidSignature`, `ErrorCodeUnknownAgent`, `ErrorCodeKeyRevoked`, `ErrorCodeAgentFrozen`, `ErrorCodeTTLExpired`, `ErrorCodeReplayDetected`, `ErrorCodePrevHashMismatch`, `ErrorCodePayloadTooLarge`, `ErrorCodeRateLimited`, `ErrorCodeInternalError`, `ErrorCodeUnauthorized`, `ErrorCodeForbidden`, `ErrorCodeNotFound`, `ErrorCodeValidationError` | +| `ErrorCode` | `ErrorCodeInvalidSignature`, `ErrorCodeUnknownAgent`, `ErrorCodeKeyRevoked`, `ErrorCodeKeyRetired`, `ErrorCodeAgentFrozen`, `ErrorCodeAgentRevoked`, `ErrorCodeTTLExpired`, `ErrorCodeReplayDetected`, `ErrorCodePrevHashMismatch`, `ErrorCodePayloadTooLarge`, `ErrorCodeRateLimited`, `ErrorCodeInternalError`, `ErrorCodeUnauthorized`, `ErrorCodeForbidden`, `ErrorCodeNotFound`, `ErrorCodeValidationError` | ## Error Handling diff --git a/cmd/elydora/plugins/grok_test.go b/cmd/elydora/plugins/grok_test.go index 2dbe3a0..c7b595e 100644 --- a/cmd/elydora/plugins/grok_test.go +++ b/cmd/elydora/plugins/grok_test.go @@ -36,7 +36,7 @@ func TestGrokCommandRoundTripAndRejectsInjection(t *testing.T) { !sameManagedPath(script, fixture.guardPath) { t.Fatalf("parsed Grok command = %q, %q, %v", executable, script, ok) } - if strings.Contains(command, "%ELYDORA_HOOK_PATH%") { + if runtime.GOOS == "windows" && strings.Contains(command, "%ELYDORA_HOOK_PATH%") { t.Fatalf("Grok command exposes an expandable path: %q", command) } if _, err := buildEncodedCommand("Grok", "node", fixture.guardPath); err == nil {