Repository navigation
feat: secrets and automation writes from the CLI (write API, 0.1.33) - #53
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… (0.1.33) Write API (AGENT-AUTOMATIONS-PHASE3-WRITE-API.md, nan-cloud-api#348): secrets and automations written from a CLI session or a platform token with secrets:read, secrets:write or automations:write. Values only from a file or a pipe; webhook URL and signing secret printed once. Exit 64 and the portal when the account does not have the write API. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…_found, size edges; QA notes - the retry test now forces a fresh connection per request, so the second POST is the CLI's (net/http retries only on a reused one); lostAnswer unit test - a token with secrets:write and no secrets:read creates, and names the scope it lacks - set on an existing secret with --base-url, and a PATCH that fails after the PUT - not_found on PUT/PATCH/DELETE; 16 KiB boundary; non-UTF-8 - a pasted value as a subcommand is not echoed; reads with the write API off say reading - rotate --json prints the store-now note only when something was issued; --json and token tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s before reading the value Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oogle hint is asserted Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ng-secret-file/--signing-secret-stdin Stripe issues the whsec_, so cloud-api requires it in the rotate body and the CLI had no way to send it: the default rotate and --secret always failed for Stripe. The secret is read like a secret value (file or pipe, never a terminal or an argument), sent once, never printed, and a 400 on signing_secret now explains what to do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…GitHub/NaN one Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sre-helmcode
pushed a commit
that referenced
this pull request
Oct 20, 2026
Brings #50, #51, #52, #53, #54, #56 and #57 onto the new structure: - mimo-v2.6-flash, qwen3.8-flash at 1M, and the Codex model catalogue: the writer changes go to internal/tools, the catalogue and its embedded prompt to internal/tools/codexprompt. - BYOK runs, secrets and automation writes, and notifications: the commands go to internal/cli and hang off NewRootCmd; their goldens to internal/cli/testdata. - The --json contract test counts the commands with a --json of their own, not the one every command inherits from the root. - The embedded Codex prompt is Apache-2.0: its NOTICE and licence are now in THIRD-PARTY-NOTICES.txt. - Messages without em dashes, and the new code lint-clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The CLI half of the Phase 3 write API (spec: helmcode/nan-devops
AGENT-AUTOMATIONS-PHASE3-WRITE-API.md, server: helmcode/nan-cloud-api#348). Version 0.1.33 (merging publishes).New commands:
nan secrets show NAME|ID [--json],nan secrets set NAME (--value-file PATH | --value-stdin) [--kind generic|provider_key] [--provider P] [--base-url URL] [--description D] [--json],nan secrets edit NAME|ID [--description D] [--base-url URL] [--json],nan secrets rm NAME|ID [--yes](aliasesdelete,remove).nan automations create -f FILE|-,edit NAME|ID -f FILE|-,enable NAME|ID,disable NAME|ID,rm NAME|ID [--yes](YAML or JSON, sent as written;--jsonprints the API answer).nan automations triggers ls|add|edit|rotate|rm(rotatetakes--url,--secret(default both) and--no-grace).updated_byshown as "last changed from: Browser / CLI / API token" insecrets ls/show,automations showandtriggers ls.Secret values:
--value-fileor a pipe into--value-stdin;--value-stdinrefuses a terminal (exit 64). One trailing\n/\r\ndropped; empty, non-UTF-8 and >16 KiB refused. An extra positional argument or a NAME that looks like a key is refused without being echoed, with the "revoke it" hint.setcreates (POST with a generatedIdempotency-Key, re-sent with the same key once if the answer is lost) or replaces the value (PUT, plus PATCH for description/base URL). Kind/provider of an existing secret cannot change (usage error). Request types redact the value in%v.Webhook URL and signing secret: printed once to stdout (never stderr, never a file), with "Store these now: they are never shown again" on stderr;
--jsonprints the raw one-time object.Auth for the secrets and write commands:
--token-file, then the CLI session, thenNAN_TOKEN, then a saved platform token.sk-keys are refused locally, never sent. Gating: for a session,features.write_apifrom/api/auth/me; for a token, the request is sent and the refusal mapped. Write API off (flag false,/v1/secrets404, Phase 2browser_session_required) keeps exit 64 pointing at the portal. Mapped errors:credential_not_allowed,workspace_key_not_allowed,token_scope_denied(names the scope, says new scopes are added in the portal from a browser),rate_limited(69),name_taken,too_many_secrets,idempotency_key_reused,secret_in_use/secrets_too_large(list the automations),platform_access_required,secrets_unavailable, 404s naming the automation/trigger. Approving stays portal-only.Help and README: the new commands, the scopes, and the warning that a credential that can write automations can reach every secret (log in on your own machine; tokens with only the needed scopes in workspaces/CI).
Tests
cmd/writeapi_test.go: an httptest fake of the write API with cloud-api's golden responses (internal/api/testdata/phase2, refreshed from #348 incl.trigger_create.json,trigger_rotate.json): every verb end to end through the real command tree, TTY refusal, value sources and trimming, value never in stdout/stderr/errors/other requests/files under HOME, idempotency key reused on a lost answer, kind/provider fixed, flag off (session and token) exit 64, credential order andsk-refusal, every error mapping, one-time trigger secrets printed once and never written, rotate bodies, golden views (cmd/testdata/writeapi).gofmt,go vet ./...,go test ./..., cross-compile linux/windows: green locally.Deploy
Merge = release 0.1.33 (tag workflow). Safe before the server flips
WRITE_API_ENABLED: the commands exit 64 with the portal explanation untilfeatures.write_apiis true.🤖 Generated with Claude Code