Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ vendor/

# local build
algolia
algolia-beta

.gocache/

Expand Down
5 changes: 5 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ linters:
- gosec
- gofumpt
- stylecheck

# tmp/ holds Taskfile outputs and ad-hoc QA probes; exclude from lint noise.
issues:
exclude-dirs:
- tmp
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,20 @@ Preferred:
task build
```

For a second binary next to a production install (same `.env` / ldflags as `build`, output `algolia-beta`, version string `main+beta` unless you pass `VERSION=…`):

```sh
task build-beta
```

```sh
go generate ./...
go build -ldflags "-s -w -X=github.com/algolia/cli/pkg/version.Version=main" -o algolia cmd/algolia/main.go
go build -v ./...
```

- `task build` runs generation first.
- `task build-beta` runs the same steps and embeds the same defaults; it also injects **`version.Distribution=beta`** so every `agents` leaf command prints a stderr reminder before running (including when showing `agents` subcommand help). Artifact name defaults to **`algolia-beta`** and **`VERSION`** defaults to **`main+beta`** (override with `VERSION=…`).
- CI also checks `go build -v ./...`.

## Test Commands
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ To build the Algolia CLI from source, you'll need:
1. Clone the repo: `git clone https://github.com/kai687/cli.git algolia-cli && cd algolia-cli`
1. Run: `task build`

To also produce **`algolia-beta`** (same options from `.env`, for use alongside `algolia`), run: `task build-beta`. That build tags the binary as **`beta`** and prints a short **stderr** reminder when you use any `algolia agents …` command.

## Support

If you found an issue with the Algolia CLI,
Expand Down
17 changes: 14 additions & 3 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ vars:
docs_output_dir: tmp
tasks:
build:
desc: Build the binary
desc: Build the algolia binary
deps: [generate]
cmd: >-
go build -ldflags
Expand All @@ -21,10 +21,21 @@ tasks:
-X github.com/algolia/cli/pkg/config.DefaultSearchHosts=$ALGOLIA_SEARCH_HOSTS
-X github.com/algolia/cli/pkg/auth.DefaultOAuthClientID=$ALGOLIA_OAUTH_CLIENT_ID
-X 'github.com/algolia/cli/api/dashboard.DefaultOAuthScope=$ALGOLIA_OAUTH_SCOPE'
-X github.com/algolia/cli/api/agentstudio.DefaultBaseURL=$ALGOLIA_AGENT_STUDIO_URL"
-o algolia cmd/algolia/main.go
-X github.com/algolia/cli/api/agentstudio.DefaultBaseURL=$ALGOLIA_AGENT_STUDIO_URL
-X github.com/algolia/cli/pkg/version.Distribution={{ .DIST }}"
-o {{ .OUTPUT }} cmd/algolia/main.go
vars:
OUTPUT: '{{ .OUTPUT | default "algolia" }}'
VERSION: '{{ .VERSION | default "main" }}'
DIST: '{{ .DIST | default "" }}'
build-beta:
desc: Build algolia-beta (same .env ldflags as build; use beside a prod algolia install)
cmds:
- task: build
vars:
OUTPUT: algolia-beta
VERSION: '{{ .VERSION | default "main+beta" }}'
DIST: beta
test:
desc: Run unit tests
run: always
Expand Down
15 changes: 15 additions & 0 deletions docs/agents.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,21 @@ Two distinct concepts share the name:

`agents try` therefore has no `--dry-run` flag — the whole command IS the dry-run. To preview the wire body without calling the backend, marshal `{"messages":[...], "configuration":{...}}` yourself. The dry-run e2e regression-asserts that `agents try --dry-run` is rejected.

## Providers: `-F` vs flags

`agents providers create` accepts either:

- **`-F <file>`** — full `ProviderAuthenticationCreate` JSON (all `providerName` variants, including `azure_openai` and `openai_compatible`), or
- **Flags** — `--name`, `--provider` (`openai` \| `anthropic` \| `google_genai` \| `deepseek`), plus exactly one of `--api-key`, `--api-key-stdin`, or `--api-key-env <VAR>`. Optional `--base-url` only for `openai` / `anthropic`.

`-F` and the shortcut flags are **mutually exclusive**.

`agents providers update <id>` accepts **`-F`** (patch JSON) **or** shortcut flags: any non-empty combination of `--name`, `--api-key` / `--api-key-stdin` / `--api-key-env`, and `--base-url`, with the same exclusivity rule against `-F`.

Prefer **`--api-key-env`** or **`--api-key-stdin`** over **`--api-key`** (shell history). `--dry-run` still shows the resolved body unredacted so authors can verify what would be sent.

Team sign-off and **Anya** QA checklist: [`docs/qa/arg_friendly_providers_SIGNOFF.md`](qa/arg_friendly_providers_SIGNOFF.md).

## Secret masking

`apiKey` (provider input) and `value` (secret-keys) are masked to `"***"` by default. Pass `--show-secret` to render verbatim. Masking happens at the cmd layer (`pkg/cmd/agents/shared/mask.go`), not the client. `--dry-run` does **not** mask: the user authored the file and is being shown what THEY are about to send. Three asterisks, no last-N preview — goal is "impossible to copy by accident", not "allow last-4 lookup". `secretFieldNames` is the closed set; extend alphabetically when new credential fields land.
Expand Down
146 changes: 146 additions & 0 deletions docs/qa/arg_friendly_providers_SIGNOFF.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# Arg-friendly `agents providers` — team sign-off & QA sheet

**Feature branch:** `feat/agents-providers-arg-friendly` (stacked on `lab_week_3` / [PR #212](https://github.com/algolia/cli/pull/212))

**Scope:** Optional flags for `agents providers create|update` so the common “single API key” case does not require `-F`. Full JSON via `-F` unchanged; Azure / `openai_compatible` still require `-F`.

**Planning reference:** Lab Week 3 roles (“The team (5 people)”: Iris, Marek, Yuki, Diego, Priya) — often kept locally as `tmp/lab_week_3_plan.md` beside other lab notes.

> This file lives under `docs/qa/` so it ships with the repo. Some checkouts list `tmp/` in `.git/info/exclude`; use this path as the canonical copy for PRs.

---

## Engineering sign-off (planning team)

| Person | Role | Position on this change |
|--------|------|-------------------------|
| **Iris** | Auth & identity | **On board.** Out of scope for OAuth; provider keys stay user-supplied. Flags do not change header auth (`X-Algolia-Application-Id` / `X-Algolia-API-Key`). |
| **Marek** | CLI / DX | **On board.** Matches Cobra patterns: `-F` vs flags mutually exclusive; long-help documents shell-history risk for `--api-key` and recommends `--api-key-env` / `--api-key-stdin`. |
| **Yuki** | Agent Studio backend | **On board.** Wire body is identical to JSON create/patch; CLI only assembles JSON for `openai`, `anthropic`, `google_genai`, `deepseek`. |
| **Diego** | Security | **On board.** Same threat model as `-F`: secrets can still hit shell history if the user passes `--api-key`; env/stdin paths preferred. No new persistence. |
| **Priya** | Search UI (aspirational track) | **N/A / on board.** No UI work; no objection to CLI ergonomics that reduce temp files. |

---

## QA owner: **Anya**

**Exit criteria**

- [ ] `task build` (or `go build` per repo) produces a binary; all of the commands below use that binary as `./algolia`.
- [ ] Agents e2e suite runs with app credentials: `go test ./e2e -tags=e2e -run TestAgents -count=1` includes `testscripts/agents/providers.txtar` and passes (or document skip if creds unavailable).
- [ ] Unit tests: `go test ./pkg/cmd/agents/providers/...` green.
- [ ] Live smoke (staging / beta app): at least one **create** and one **update** via flags succeed; **dry-run** paths show expected JSON and do not call mutating APIs.

---

## Anya — testing command sheet

Run from the **repository root** after `task build`. Replace placeholders.

### 0. Binary and profile

```bash
cd /path/to/cli
task build
./algolia version
# Ensure profile or env has app + key (same as other `algolia agents` commands)
```

### 1. Unit tests (no network)

```bash
go test ./pkg/cmd/agents/providers/... -count=1
```

### 2. Contract tests (e2e harness; needs credentials)

```bash
export ALGOLIA_APPLICATION_ID="YOUR_APP_ID"
export ALGOLIA_API_KEY="YOUR_API_KEY"
go test ./e2e -tags=e2e -run TestAgents -count=1
```

### 3. Help / usage sanity

```bash
./algolia agents providers create --help
./algolia agents providers update --help
```

### 4. Create — **dry-run** (flags), no API call

```bash
./algolia agents providers create \
--name "qa-cli-flags-smoke" \
--provider anthropic \
--api-key "sk-ant-REDACTED" \
--dry-run
```

Expect: human or structured dry-run summary; body includes `providerName`, `name`, `input.apiKey` (unmasked in dry-run per masking rules in `docs/agents.md`).

### 5. Create — **dry-run** via env (no key on command line)

```bash
export QA_ANTHROPIC_KEY="sk-ant-your-real-test-key"
./algolia agents providers create \
--name "qa-cli-flags-env" \
--provider anthropic \
--api-key-env QA_ANTHROPIC_KEY \
--dry-run
unset QA_ANTHROPIC_KEY
```

### 6. Create — **live** (optional; consumes provider quota)

```bash
export QA_ANTHROPIC_KEY="sk-ant-..."
PID=$(./algolia agents providers create \
--name "qa-live-$(date +%s)" \
--provider anthropic \
--api-key-env QA_ANTHROPIC_KEY \
--output json | jq -r .id)
echo "PID=$PID"
```

### 7. Update — **dry-run** (rename only)

```bash
./algolia agents providers update "$PID" --name "qa-renamed" --dry-run
```

### 8. Update — **live** (rotate key or rename)

```bash
./algolia agents providers update "$PID" --name "qa-renamed-final"
# or rotate:
# ./algolia agents providers update "$PID" --api-key-env QA_ANTHROPIC_KEY
```

### 9. Negative cases

```bash
# Must error: cannot mix -F and flags (use a real JSON file path):
# echo '{"name":"x","providerName":"openai","input":{"apiKey":"sk"}}' > /tmp/prov.json
# ./algolia agents providers create -F /tmp/prov.json --name x --provider openai --api-key sk 2>&1 | head -5

# Must error: unsupported provider for flag path
./algolia agents providers create --name x --provider azure_openai --api-key sk --dry-run 2>&1 | head -5

# Must error: create without -F and without full flag set
./algolia agents providers create 2>&1 | head -5
```

### 10. Cleanup

```bash
./algolia agents providers delete "$PID" -y
```

---

## Report back (local only — do **not** commit)

Anya drops a short vet note under **`tmp/qa/anya_arg_friendly_providers_vet/`** (e.g. `REPORT.md`) for Catalin to read: date, commit SHA, pass/fail per section, backend 422 snippets if any.

That path stays **out of git** (scratch / personal checkout). Nothing from Anya’s QA report belongs in PR commits—only the feature and the in-repo docs above.
4 changes: 2 additions & 2 deletions e2e/testscripts/agents/providers.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ stderr 'requires exactly 1 argument'
! exec algolia agents providers delete some-id
stderr '--confirm required'

# `create` requires --file
# `create` without -F or shortcut flags prints usage error
! exec algolia agents providers create
stderr 'required flag\(s\) "file" not set'
stderr 'specify a JSON body with -F, or pass --name, --provider, and an API key flag'

# --dry-run on create previews POST to /1/providers, no network
exec algolia agents providers create -F spec.json --dry-run
Expand Down
10 changes: 10 additions & 0 deletions pkg/cmd/agents/agents.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ func NewAgentsCmd(f *cmdutil.Factory) *cobra.Command {
`),
}

cmd.PersistentPreRunE = betaAgentsPreRunE(f)

cmd.AddCommand(list.NewListCmd(f, nil))
cmd.AddCommand(get.NewGetCmd(f, nil))
cmd.AddCommand(create.NewCreateCmd(f, nil))
Expand All @@ -67,5 +69,13 @@ func NewAgentsCmd(f *cmdutil.Factory) *cobra.Command {
cmd.AddCommand(userdata.NewUserDataCmd(f))
cmd.AddCommand(internalcmd.NewInternalCmd(f))

h := cmd.HelpFunc()
cmd.SetHelpFunc(func(c *cobra.Command, args []string) {
_ = betaAgentsPreRunE(f)(nil, nil)
if h != nil {
h(c, args)
}
})

return cmd
}
27 changes: 27 additions & 0 deletions pkg/cmd/agents/beta_warning.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package agents

import (
"fmt"

"github.com/spf13/cobra"

"github.com/algolia/cli/pkg/cmdutil"
"github.com/algolia/cli/pkg/version"
)

// betaAgentsPreRunE emits a stderr banner for non-release binaries (link-time
// version.Distribution set, e.g. "beta") whenever any `agents` subtree command runs.
func betaAgentsPreRunE(f *cmdutil.Factory) func(*cobra.Command, []string) error {
return func(*cobra.Command, []string) error {
if version.Distribution == "" {
return nil
}
fmt.Fprintf(
f.IOStreams.ErrOut,
"warning: %s CLI build — Algolia recommends the release `algolia` binary for "+
"production. `agents` defaults can follow your `.env` / build-time flags.\n\n",
version.Distribution,
)
return nil
}
}
33 changes: 33 additions & 0 deletions pkg/cmd/agents/beta_warning_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package agents

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/algolia/cli/pkg/version"
"github.com/algolia/cli/test"
)

func TestBetaAgentsPreRunE_skipWhenReleaseBuild(t *testing.T) {
prev := version.Distribution
t.Cleanup(func() { version.Distribution = prev })
version.Distribution = ""

f, bio := test.NewFactory(false, nil, nil, "")
require.NoError(t, betaAgentsPreRunE(f)(nil, nil))
assert.Empty(t, bio.ErrBuf.String())
}

func TestBetaAgentsPreRunE_warnWhenDistributionSet(t *testing.T) {
prev := version.Distribution
t.Cleanup(func() { version.Distribution = prev })
version.Distribution = "beta"

f, bio := test.NewFactory(false, nil, nil, "")
require.NoError(t, betaAgentsPreRunE(f)(nil, nil))
got := bio.ErrBuf.String()
assert.Contains(t, got, "beta CLI")
assert.Contains(t, got, "agents")
}
Loading