# OpenAPI import and packs

## Import from OpenAPI

One action per operation. Writes (anything but `GET`) are governed with one exact resource mutation each; `GET`s are plain actions.

```typescript
const spec = await fetch('https://api.acme.com/openapi.json').then((r) => r.json());

// 1. Dry run: creates nothing, reports everything
const { data: plan } = await ops.actions.importFromOpenApi(spec, {
  auth: { headerName: 'Authorization', secretName: 'ACME_KEY', valueTemplate: 'Bearer {{secret}}' },
  dryRun: true,
});
for (const a of plan!.created) console.log(a.name, a.governed, a.suggestion, a.proposedVerify ?? a.verifyNote);
console.log(plan!.skipped); // [{ path, method, reason }]: oneOf bodies, multipart uploads

// 2. Import, overriding what the suggestions got wrong
await ops.actions.importFromOpenApi(spec, {
  auth: { headerName: 'Authorization', secretName: 'ACME_KEY', valueTemplate: 'Bearer {{secret}}' },
  operations: {
    'POST /v1/refunds': {
      requiresApproval: true,
      impact: { dimension: 'money', unit: 'usd', amount: 'input.amount', bound: 'exact' },
    },
    listInvoices: { impact: null }, // by operationId; null imports it as a plain action
  },
});
```

- Each operation's `suggestion` (risk class `read` / `write` / `irreversible` / `destructive`, whether to require approval, an impact) is never applied by itself. Review it, then pass it in `operations`.
- A spec can raise the risk its method implies, never lower it (`x-anlyon-risk`, `x-anlyon-impact` extensions).
- `proposedVerify` is a read-back suggestion. Check it reads back what this write changed, then apply it with `ops.actions.update(name, { verify: action.proposedVerify })`. Until then receipts grade `acknowledged`.
- `suggestedAllowedHosts`: set them as the secret's `allowedHosts`.
- Start by storing the secret, from an environment variable: `ops.secrets.put('ACME_KEY', { value: process.env.ACME_KEY!, allowedHosts: [...] })`.

The console has the same flow: **Actions → Import OpenAPI**.

## Packs

A pack is an integration's operations, or a workflow, in one `pack.json` (`anlyon-pack/v1`): the secrets it needs, reads, governed writes with read-backs, approval policies (installed disabled) and suggested limits. Installing makes the same API calls you could make by hand; it changes nothing when refused.

**Console (simplest):** **Integrations →** the integration **→** connect the account and switch on the operations the agent may use. Each switch installs that operation from the integration's catalogue pack.

**CLI** (`npm install -g @anlyonhq/cli`), with an operator key holding `secrets:read`, `secrets:write`, `actions:read`, `actions:write`, `policies:read`, `policies:write` (and `impact-limits:write` for `--with-limits`):

```bash
printf '%s' "$ANLYON_OPERATOR_KEY" | anlyon auth login --api-key-stdin
anlyon packs plan ./path/to/pack            # what install would do; sends nothing
anlyon packs install ./path/to/pack --yes   # add --with-limits to create the suggested limits
anlyon policies list                        # review, then enable in the console
```

Useful flags: `--action <name>` (repeatable, install some operations), `--secret-name PACK=YOURS`, `--param key=value`, `--update` (new version of a changed definition), `--no-prefix`, `--credential secret`.

**SDK (TypeScript):**

```typescript
const manifest = JSON.parse(await readFile('pack.json', 'utf8'));
const plan = await ops.packs.plan(manifest);                 // nothing sent
const result = await ops.packs.install(manifest, { withLimits: true, actions: ['create-company'] });
console.log(result.disabledPolicies);                        // review, then enable
```

Pack actions use `{{connection:<integration>}}`, so the same install serves the workspace's account and each end user's own account. The Python SDK has no `packs` yet.
