Add secret binding commands - #103
Conversation
There was a problem hiding this comment.
1 issue found across 2 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="src/commands/secrets.ts">
<violation number="1" location="src/commands/secrets.ts:134">
P2: When a binding was created without `--source-name`, `b.sourceName` is absent and this line prints a literal `.undefined` suffix, e.g. `api <- postgres/mydb.undefined`. The `bind` command's own message guards the suffix with a ternary (`opts.sourceName ? `.${opts.sourceName}` : ''`), so the listing should too. Only append the source name when it is defined.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| const bindings = res.body.bindings ?? [] | ||
| if (opts.json) return printJson(bindings) | ||
| if (!bindings.length) return info(`(no secret bindings for ${opts.target} on ${branch})`) | ||
| for (const b of bindings) info(`${b.envName} <- ${b.source.type}/${b.source.name}.${b.sourceName}`) |
There was a problem hiding this comment.
P2: When a binding was created without --source-name, b.sourceName is absent and this line prints a literal .undefined suffix, e.g. api <- postgres/mydb.undefined. The bind command's own message guards the suffix with a ternary (opts.sourceName ? .${opts.sourceName} : ''), so the listing should too. Only append the source name when it is defined.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/commands/secrets.ts, line 134:
<comment>When a binding was created without `--source-name`, `b.sourceName` is absent and this line prints a literal `.undefined` suffix, e.g. `api <- postgres/mydb.undefined`. The `bind` command's own message guards the suffix with a ternary (`opts.sourceName ? `.${opts.sourceName}` : ''`), so the listing should too. Only append the source name when it is defined.</comment>
<file context>
@@ -94,6 +94,58 @@ export async function secretsUnset(name: string, opts: { branch?: string }): Pro
+ const bindings = res.body.bindings ?? []
+ if (opts.json) return printJson(bindings)
+ if (!bindings.length) return info(`(no secret bindings for ${opts.target} on ${branch})`)
+ for (const b of bindings) info(`${b.envName} <- ${b.source.type}/${b.source.name}.${b.sourceName}`)
+}
+
</file context>
| for (const b of bindings) info(`${b.envName} <- ${b.source.type}/${b.source.name}.${b.sourceName}`) | |
| for (const b of bindings) info(`${b.envName} <- ${b.source.type}/${b.source.name}${b.sourceName ? `.${b.sourceName}` : ''}`) |
jwfing
left a comment
There was a problem hiding this comment.
Summary
Adds four insta secrets subcommands (bind, unbind, bindings, sources) that wrap the platform secret-binding endpoints; the code is small, idiomatic, and consistent with the existing secrets.ts command family — no blocking issues.
Requirements context
No matching spec/plan found — this repo has no /docs/superpowers/ (or docs/specs/) directory, so I assessed against the PR description and the existing src/commands/secrets.ts conventions alone.
Findings
Critical
(none)
Suggestion
Software engineering — no tests for the new commands (src/commands/secrets.ts:97-147). Every other command area in this repo carries tests (test/services.test.ts, test/storage.test.ts, test/run-secrets.test.ts, …) and the repo follows TDD. The four new functions contain testable behavior worth pinning: the required-flag guards (die('--to …'), --from, --target), the query-string assembly in secretsUnbind/secretsBindings/secretsSources, the empty-list branches ((no secret bindings …) / (no credential sources …)), and the --json array output. A small unit test injecting a fake rawRequest (as run-secrets.test.ts does with fetchBundle) would lock these in. Non-blocking, but recommended before merge.
Functionality — sourceName can render as .undefined (src/commands/secrets.ts:134). bindings prints ${b.envName} <- ${b.source.type}/${b.source.name}.${b.sourceName}, but --source-name is documented as optional ("when the source exposes more than one"), so a binding without a sourceName will render as NAME <- postgres/db.undefined. Consider omitting the .${sourceName} suffix when it's absent, mirroring the conditional suffix you already use in the bind success message (${opts.sourceName ? \.${opts.sourceName}` : ''}`, line 110).
Functionality — platform contract coupling is unverified from this repo. These commands assume specific response envelopes/field names: res.body.bindings[] with { envName, source: { type, name }, sourceName } (line 131-134) and res.body.sources[] with { service: { type, name }, secrets: [] } (line 143-146), plus request fields target/source/sourceName on the PUT (line 102-107). Please confirm these match the merged platform secret-bindings/secret-sources endpoints (and the equivalent insta-mcp binding tools) — a mismatch would silently degrade to the empty-list path rather than error. If the platform endpoints aren't merged yet, land them first to avoid shipping a CLI that 404s.
Information
- Flag naming differs across sibling subcommands —
bind --to,unbind --from,bindings --targetall denote the same "target compute service." The variation is semantically reasonable (reads naturally per verb) but is worth a note in--help/docs so users aren't surprised. (src/index.ts:165-186) --json+ approval interaction: when the platform gates the action (HTTP 202),handleApprovalprints a human-readable line and returns beforeprintJson, so--jsoncallers get a non-JSON message on the approval path. This matches the existingsecretsSetbehavior, so it's consistent rather than a regression — just flagging for scriptability awareness. (src/commands/secrets.ts:108-109,util.ts:32-38)
Security
No security-relevant concerns. The commands operate on secret names and bindings, never values; sources prints only secret names (same as the existing tree). All path/query segments are encodeURIComponent-escaped, auth flows through the standard bearer client, and 202 approval gating is respected via handleApproval.
Performance
No concerns — each command issues a single control-plane request with trivial client-side formatting.
Verdict
approved (informational — a human still approves via the GitHub approve flow). Clean, well-scoped addition; the suggestions above (add tests, guard the .undefined render, confirm the platform contract) are worth addressing but none are blocking.
Summary
Validation
Summary by cubic
Adds CLI commands to bind/unbind service credentials to compute env vars and to list available bindings and sources. This makes credential management per branch and compute target explicit and scriptable.
Review notes
secsubcommands:bind <env-name> <source>,unbind <env-name>,bindings,sources.bindneeds--to <compute-service>;unbindneeds--from <compute-service>;bindingsneeds--target <compute-service>. Optional:--branch(default: current),--json;bindsupports--source-name.PUT /projects/:id/secret-bindings/:envName,DELETE /projects/:id/secret-bindings/:envName,GET /projects/:id/secret-bindings,GET /projects/:id/secret-sources. All respect approvals viahandleApproval.envName <- type/name.sourceName; sources print astype/name: secret1, secret2; empty lists show(no …). JSON mode returns arrays;bind/unbindreturn{ ok: true }.Written for commit 623956b. Summary will update on new commits.