devops-agent
v1.4.0
Published
An agent-first multicloud DevOps SDK and CLI for Cloudflare, AWS, and Authix.
Readme
devops-agent
An agent-first DevOps SDK and CLI with Cloudflare, AWS, and Authix adapters, an extensible task registry, JSON workflows, and encrypted file recovery through @x12i/safe-actions.
Node.js 22+, ESM. Install Wrangler 4 separately for Pages and Worker deployments:
npm install devops-agent
npm install --save-dev wrangler@4Cloudflare access comes from either of two places. Run devops-agent login once: it uses the OAuth 2.0 authorization code flow with PKCE that wrangler login uses, listens on http://localhost:8976/oauth/callback, lets you pick an account (--account-id skips the picker, --no-browser prints the link instead of opening it), and keeps its own refreshable session in ~/.config/x12i/devops-agent/cloudflare-session.json (private to your user, outside every project, never read from or written to Wrangler's files). whoami shows the account and logout revokes and deletes the session. Or set CLOUDFLARE_ACCOUNT_ID and CLOUDFLARE_API_TOKEN in the process environment or .env for CI and headless use. When both are set they win over a session, and login skips the browser. workflow plan and --dry-run need neither. R2 credential creation additionally requires an account token with Account API Tokens Write and permission to grant the R2 object permissions. Access tasks and R2 credential tasks need that API token rather than the browser session, and require the Access organization, service token, and apps-and-policies permissions named on each task. A person creates that token once; this package does not mint it.
Authix tasks use AUTHIX_API_URL and AUTHIX_API_KEY. A person creates that API key once. It is the bootstrap credential for the Authix HTTP API, not an app secret and not a bearer token. authix.app.ensure returns appSecretKey only when it creates the app. authix.token.ensure returns token only when it issues one. authix.manifest.apply runs both from a JSON manifest and redacts appSecretKey and grants. explain, appId, issuer, and identityId stay visible. Dry-run does not call Authix.
AWS tasks use the AWS SDK v3 default credential chain and AWS_REGION or AWS_DEFAULT_REGION. Optional AWS_ACCOUNT_ID must match the account STS returns. STS runs only for a real AWS task. Dry-run does not contact AWS. Save minted IAM keys under names other than AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, so a later run does not replace the bootstrap credentials.
CLI
devops-agent login
devops-agent whoami
devops-agent logout
devops-agent tasks list
devops-agent tasks schema cloudflare.r2.bucket.create
devops-agent cloudflare r2 bucket create --name uploads \
--save name=R2_BUCKET --save endpoint=R2_ENDPOINT
devops-agent cloudflare r2 object put --bucket uploads --key audio/intro.mp3 \
--file assets/audio/intro.mp3 --content-type audio/mpeg
devops-agent run local.secret.ensure --name ADMIN_SECRET
devops-agent cloudflare d1 database create --name app-db \
--save id=D1_DATABASE_ID
devops-agent cloudflare r2 credentials create --name app-access \
--buckets '["uploads"]' \
--save credentials.accessKeyId=R2_ACCESS_KEY_ID \
--save credentials.secretAccessKey=R2_SECRET_ACCESS_KEY
devops-agent cloudflare pages project create --name app-site
devops-agent cloudflare pages deploy --project app-site --directory dist --branch main
devops-agent cloudflare access service_token create --name configix-doctor-x \
--save clientId=CF_ACCESS_CLIENT_ID --save clientSecret=CF_ACCESS_CLIENT_SECRET
devops-agent cloudflare access app create --name configix --domain configix-api.x12i.com \
--policies '["policy-id"]' --save-json aud=deploy/wrangler.production.json#/vars/CONFIGIX_ACCESS_AUD
devops-agent authix app ensure --app-id memorix-api --title "Memorix API" \
--save appSecretKey=AUTHIX_APP_SECRET_KEY
devops-agent aws s3 bucket create --name uploads \
--save name=S3_BUCKET --save endpoint=S3_ENDPOINT
devops-agent aws iam credentials create --name app-access \
--buckets '["uploads"]' --permission read-write \
--save credentials.accessKeyId=S3_ACCESS_KEY_ID \
--save credentials.secretAccessKey=S3_SECRET_ACCESS_KEY
devops-agent aws dynamodb table create --name Items --partition-key id \
--save name=DYNAMODB_TABLE --save arn=DYNAMODB_TABLE_ARN
devops-agent run cloudflare.workers.secret.put --input secrets-input.json
devops-agent history
devops-agent inspect 1
devops-agent undo 1run <task-id> --input <JSON-file|-> supports every registered task. A registered provider id is also a command prefix: aws s3 bucket create is aws.s3.bucket.create. - consumes stdin. Input secret values can use {"$env":"NAME"}. Schema-defined secret-valued CLI flags are rejected; use stdin or --value-env NAME. Results are JSON and secret outputs are redacted. Errors are JSON on stderr. Exit status: 0 success; 2 invalid command/input; 3 missing, expired, or unreachable auth (AUTH_*); 5 conflict or blocked action; 1 other failure.
Global options include --root, --env-file, --no-env, --key-file, --dry-run, --allow-destructive, and repeatable --plugin. login also accepts --account-id, --no-browser, and --scopes. Existing environment variables take precedence over dotenv. Dry-run performs local validation only and does not initialize backups or call Cloudflare, AWS, or Authix.
SDK
import { DevOpsAgent } from 'devops-agent';
const agent = await DevOpsAgent.create({ root: process.cwd() });
const result = await agent.run('cloudflare.d1.database.create', { name: 'app-db' }, {
save: [{ from: 'id', to: { kind: 'env', path: '.env', variable: 'D1_DATABASE_ID' } }],
});
await agent.undo(result.actionId!);SDK values include actual secrets for programmatic use. Use agent.publicResult(result) when displaying them. listTasks() exposes task input/output schemas, risk, permissions, and recovery type.
Supported Cloudflare task groups: R2 bucket create/list/delete, R2 object put (one file or a manifest, replacing existing keys), R2 credentials create/revoke, D1 database create/list/delete and parameterized SQL, Pages project create/list/delete and deploy/deployment-list/rollback, KV namespace create/list/delete, Worker deploy and secret-put, and Access organization get/create, service token create/list/rotate/delete, reusable policy create/update/list, and self-hosted application create/list/delete. Supported AWS task groups: private S3 bucket create/list/delete, bucket-scoped IAM credentials create/revoke, Lambda function create/update/list/delete, Lambda secret put through Secrets Manager, DynamoDB table create/list/delete, and S3 website create/deploy/delete. Supported Authix tasks: authix.app.ensure, authix.token.ensure, and authix.manifest.apply. local.values.save persists caller values with the same backup mechanism. local.secret.ensure generates a secret into a dotenv file only when that name is empty and keeps it afterwards; its value output is secret.
D1 returns an ID and binding metadata. It does not expose a traditional database connection string. R2 returns the jurisdiction-specific S3 endpoint and, through credential creation, a scoped access key and secret key. An Access service token returns clientId and, only when the token is created, clientSecret. Reuse does not rotate the token. A self-hosted Access application returns its audience tag aud, which is not a secret. policy.update changes only the include rules named in the call. An Authix app ensure returns appSecretKey only for a new app. An Authix token ensure returns token only for a new grant. A manifest apply adds explain and redacts both secrets. Access service tokens and Authix tokens are different credentials. AWS private buckets return a virtual-hosted S3 endpoint. IAM credential creation returns an access key for a user under the /devops-agent/ path. Lambda secrets are stored in Secrets Manager; the function environment receives the secret ARN, not the secret value. DynamoDB returns the table ARN. S3 website creation returns the website endpoint and makes the bucket world-readable.
Save destinations
const bindings = [
{ from: 'id', to: { kind: 'env', path: 'config/.env', variable: 'DB_ID' } },
{ from: 'id', to: { kind: 'json', path: 'settings.json', pointer: '/database/id' } },
{ from: 'connectionString', to: { kind: 'file', path: 'config/connection.txt' } },
];CLI: --save id=DB_ID, --save-json id=settings.json#/database/id, --save-file connectionString=config/connection.txt, or --save-bindings bindings.json. Default dotenv output is .env or the specified --env-file. Binding selectors use dotted property paths. Destinations must be within --root. Existing unrelated dotenv entries and JSON fields are preserved. JSON pointers target object fields and existing array elements (/d1_databases/0/database_id); an array index must be in range, and elements are never appended. Unsupported lossless dotenv strings must use JSON or a raw file.
Workflows and extensions
{
"version": 1,
"name": "database-and-settings",
"variables": { "databaseName": "app-db" },
"steps": [
{ "id": "database", "task": "cloudflare.d1.database.create", "input": { "name": "${variables.databaseName}" } },
{ "id": "settings", "task": "local.values.save", "input": { "values": { "id": "${steps.database.id}" } }, "save": [{ "from": "id", "to": { "kind": "env", "path": ".env", "variable": "D1_DATABASE_ID" } }] }
]
}Use workflow plan <file> or workflow run <file>; SDK: runWorkflow(workflow) and workflowFile(path). Workflows stop on failure and report completed action IDs. Static validation happens before the first mutation; output-dependent values are validated at execution time. There is no automatic replay or distributed transaction rollback.
Use registerTask(definition) and registerProvider({id,client,identity?,tasks}) to extend the SDK. The CLI loads explicit trusted files using --plugin module.mjs. Export register(agent), a default provider object, or a default task array. Namespaced tasks work through the generic run command and through devops-agent <provider-id> ... after the provider is registered. Provider clients are available via context.providers.get(providerId). Cloudflare is context.cloudflare. AWS is context.aws. Authix is context.authix. Declare local mutation targets through files(input) and checkpoint remote side effects with context.checkpoint(output) before another remote step. Validators must only validate or read, never mutate.
Encryption and recovery
Preimages and action records are saved under .safe-actions/, encrypted by default with AES-256-GCM. A 32-byte SDK key, base64 SAFE_ACTIONS_KEY, or an external SAFE_ACTIONS_KEY_FILE may be supplied. Otherwise a private key is generated under ~/.config/x12i/safe-actions/<workspace-id>.key. Preserve that key separately; backup files alone cannot recover a workspace. Never store the backup key in the backup folder or operational .env.
undo <id> restores files, checks for later edits, and records displaced versions as another action. --force overrides conflicts and is required for interrupted prepared actions. undo <id> --cloud --allow-destructive additionally compensates supported cloud creates; --cloud-only performs only that compensation. File conflicts are checked before combined recovery. Previously reused resources are not deleted. Authix compensation revokes an issued token id and leaves the app in place. Cloud compensation can delete data added since creation and is explicitly destructive.
outputs save <id> --save ... recovers known successful outputs from the encrypted journal. SDK: saveOutputs(id, bindings). SQL, remote deletes, secret overwrites, and deployments require their own provider recovery; Pages offers an explicit deployment rollback task. Network-lost mutation outcomes are recorded as uncertain and are not automatically retried.
Pages publishing scans the input directory for dotenv files, private key files, backup state, Git metadata, and symlinks. Use a clean build output directory. This is a filename-based check, not a content classifier. Wrangler 4 is resolved from the project or package dependency tree; the adapter never downloads a CLI or runs it through a shell. Existing Worker configurations may contain build hooks whose file writes are outside declared destination backups.
MIT licensed.
