@withandeo/cli 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/README.md +383 -0
  2. package/dist/andeo-environment.d.ts +2 -0
  3. package/dist/andeo-environment.d.ts.map +1 -0
  4. package/dist/andeo-environment.js +17 -0
  5. package/dist/andeo-environment.js.map +1 -0
  6. package/dist/api.d.ts +109 -0
  7. package/dist/api.d.ts.map +1 -0
  8. package/dist/api.js +141 -0
  9. package/dist/api.js.map +1 -0
  10. package/dist/artifact.d.ts +8 -0
  11. package/dist/artifact.d.ts.map +1 -0
  12. package/dist/artifact.js +205 -0
  13. package/dist/artifact.js.map +1 -0
  14. package/dist/auth-store.d.ts +51 -0
  15. package/dist/auth-store.d.ts.map +1 -0
  16. package/dist/auth-store.js +242 -0
  17. package/dist/auth-store.js.map +1 -0
  18. package/dist/cli.d.ts +3 -0
  19. package/dist/cli.d.ts.map +1 -0
  20. package/dist/cli.js +4 -0
  21. package/dist/cli.js.map +1 -0
  22. package/dist/contracts.d.ts +251 -0
  23. package/dist/contracts.d.ts.map +1 -0
  24. package/dist/contracts.js +5 -0
  25. package/dist/contracts.js.map +1 -0
  26. package/dist/errors.d.ts +10 -0
  27. package/dist/errors.d.ts.map +1 -0
  28. package/dist/errors.js +19 -0
  29. package/dist/errors.js.map +1 -0
  30. package/dist/files.d.ts +22 -0
  31. package/dist/files.d.ts.map +1 -0
  32. package/dist/files.js +285 -0
  33. package/dist/files.js.map +1 -0
  34. package/dist/index.d.ts +4 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +4 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/main.d.ts +38 -0
  39. package/dist/main.d.ts.map +1 -0
  40. package/dist/main.js +2140 -0
  41. package/dist/main.js.map +1 -0
  42. package/dist/oauth.d.ts +67 -0
  43. package/dist/oauth.d.ts.map +1 -0
  44. package/dist/oauth.js +187 -0
  45. package/dist/oauth.js.map +1 -0
  46. package/dist/processes.d.ts +12 -0
  47. package/dist/processes.d.ts.map +1 -0
  48. package/dist/processes.js +48 -0
  49. package/dist/processes.js.map +1 -0
  50. package/dist/profile-bindings.d.ts +26 -0
  51. package/dist/profile-bindings.d.ts.map +1 -0
  52. package/dist/profile-bindings.js +214 -0
  53. package/dist/profile-bindings.js.map +1 -0
  54. package/dist/starter.d.ts +20 -0
  55. package/dist/starter.d.ts.map +1 -0
  56. package/dist/starter.js +154 -0
  57. package/dist/starter.js.map +1 -0
  58. package/package.json +40 -0
  59. package/skill/tender-accounts/SKILL.md +384 -0
  60. package/templates/shopify-customer-account/AGENTS.md +15 -0
  61. package/templates/shopify-customer-account/CLAUDE.md +1 -0
  62. package/templates/shopify-customer-account/README.md +50 -0
  63. package/templates/shopify-customer-account/apps/gateway/AGENTS.md +10 -0
  64. package/templates/shopify-customer-account/apps/gateway/CLAUDE.md +1 -0
  65. package/templates/shopify-customer-account/apps/gateway/README.md +9 -0
  66. package/templates/shopify-customer-account/apps/gateway/database-schema.json +49 -0
  67. package/templates/shopify-customer-account/apps/gateway/dot.dev.vars.example +12 -0
  68. package/templates/shopify-customer-account/apps/gateway/migrations/0001_customer_auth.sql +76 -0
  69. package/templates/shopify-customer-account/apps/gateway/package.json +25 -0
  70. package/templates/shopify-customer-account/apps/gateway/scripts/build-worker.mjs +15 -0
  71. package/templates/shopify-customer-account/apps/gateway/scripts/pack-tender-artifact.mjs +85 -0
  72. package/templates/shopify-customer-account/apps/gateway/src/platform/customer-auth/hostname-policy.ts +76 -0
  73. package/templates/shopify-customer-account/apps/gateway/src/platform/customer-auth/schema.ts +90 -0
  74. package/templates/shopify-customer-account/apps/gateway/src/platform/customer-auth/session-store.ts +864 -0
  75. package/templates/shopify-customer-account/apps/gateway/src/platform/customer-auth/shopify-route-adapter.ts +1157 -0
  76. package/templates/shopify-customer-account/apps/gateway/src/platform/runtime-hostname.ts +46 -0
  77. package/templates/shopify-customer-account/apps/gateway/src/portal-service.test.ts +70 -0
  78. package/templates/shopify-customer-account/apps/gateway/src/portal-service.ts +71 -0
  79. package/templates/shopify-customer-account/apps/gateway/src/runtime-config.test.ts +30 -0
  80. package/templates/shopify-customer-account/apps/gateway/src/runtime-config.ts +58 -0
  81. package/templates/shopify-customer-account/apps/gateway/src/worker.ts +86 -0
  82. package/templates/shopify-customer-account/apps/gateway/tender-accounts.json +10 -0
  83. package/templates/shopify-customer-account/apps/gateway/tender-app.config.json +40 -0
  84. package/templates/shopify-customer-account/apps/gateway/tsconfig.json +13 -0
  85. package/templates/shopify-customer-account/apps/gateway/vitest.config.ts +8 -0
  86. package/templates/shopify-customer-account/apps/gateway/worker-configuration.d.ts +26 -0
  87. package/templates/shopify-customer-account/apps/gateway/wrangler.jsonc +42 -0
  88. package/templates/shopify-customer-account/apps/portal/AGENTS.md +10 -0
  89. package/templates/shopify-customer-account/apps/portal/CLAUDE.md +1 -0
  90. package/templates/shopify-customer-account/apps/portal/README.md +7 -0
  91. package/templates/shopify-customer-account/apps/portal/index.html +13 -0
  92. package/templates/shopify-customer-account/apps/portal/package.json +33 -0
  93. package/templates/shopify-customer-account/apps/portal/scripts/build-worker.mjs +11 -0
  94. package/templates/shopify-customer-account/apps/portal/scripts/pack-tender-artifact.mjs +63 -0
  95. package/templates/shopify-customer-account/apps/portal/scripts/portal-worker.mjs +84 -0
  96. package/templates/shopify-customer-account/apps/portal/scripts/portal-worker.test.mjs +14 -0
  97. package/templates/shopify-customer-account/apps/portal/src/App.tsx +111 -0
  98. package/templates/shopify-customer-account/apps/portal/src/main.tsx +10 -0
  99. package/templates/shopify-customer-account/apps/portal/src/styles.css +54 -0
  100. package/templates/shopify-customer-account/apps/portal/tender-accounts.json +10 -0
  101. package/templates/shopify-customer-account/apps/portal/tender-app.config.json +19 -0
  102. package/templates/shopify-customer-account/apps/portal/tsconfig.json +14 -0
  103. package/templates/shopify-customer-account/apps/portal/vite.config.ts +7 -0
  104. package/templates/shopify-customer-account/apps/portal/worker-configuration.d.ts +11 -0
  105. package/templates/shopify-customer-account/apps/portal/wrangler.jsonc +22 -0
  106. package/templates/shopify-customer-account/dot.dev.vars.example +4 -0
  107. package/templates/shopify-customer-account/dot.gitignore +9 -0
  108. package/templates/shopify-customer-account/package.json +22 -0
  109. package/templates/shopify-customer-account/scripts/artifact-contract.mjs +109 -0
package/README.md ADDED
@@ -0,0 +1,383 @@
1
+ # `@withandeo/cli`
2
+
3
+ Developer CLI for merchant-owned customer-account applications on Andeo.
4
+
5
+ The package exposes `andeo`. It runs the validation and packaging commands committed by your repository, verifies the resulting portable artifact, and creates exact previews for review. Your application keeps its own framework, architecture, commerce integrations, and source repository.
6
+
7
+ The legacy `@tenderprompt/accounts` package retains the `tender-accounts`
8
+ command. Existing links and login profiles keep their API origin; new Andeo
9
+ logins default to `https://agents.withandeo.com`. No credential is transferred
10
+ between origins automatically. `ANDEO_*` environment names are supported
11
+ alongside `TENDER_ACCOUNTS_*`; conflicting values fail closed.
12
+
13
+ `andeo.json` is also accepted as the project config filename. Keep the existing
14
+ schema and artifact paths during migration. If both config filenames exist,
15
+ their parsed contents must agree. Maintainers can find the full rollout in
16
+ `docs/runbooks/ANDEO_MIGRATION.md` in the platform repository.
17
+
18
+ ## Requirements
19
+
20
+ - Node.js 22 or newer
21
+ - an Andeo project
22
+ - an Andeo identity with developer access to the project you are changing
23
+
24
+ ## Install
25
+
26
+ Install the public package as a development dependency:
27
+
28
+ ```sh
29
+ npm install --save-dev @withandeo/cli
30
+ ```
31
+
32
+ Verify the installation:
33
+
34
+ ```sh
35
+ npx @withandeo/cli --version
36
+ npx @withandeo/cli --help
37
+ ```
38
+
39
+ ## Start from the template
40
+
41
+ The optional starter creates a merchant-owned two-service repository: a stable Shopify authentication gateway and an independently deployable React portal connected through `PORTAL_UI`.
42
+
43
+ Inspect the plan first, then create the project in an empty directory:
44
+
45
+ ```sh
46
+ npx @withandeo/cli init --name "Acme customer account" --directory ./acme-account --dry-run --json
47
+ npx @withandeo/cli init --name "Acme customer account" --directory ./acme-account --json
48
+ ```
49
+
50
+ The starter includes:
51
+
52
+ - Shopify Customer Account authentication with S256 PKCE;
53
+ - secure, exact-host shopper sessions;
54
+ - a React portal that receives shopper data through the gateway without holding confidential credentials;
55
+ - local development configuration and an explicit signed-out development state;
56
+ - independent `tender-accounts.json` contracts for the gateway and portal;
57
+ - immutable artifact packers, checks, `AGENTS.md`, and the bundled Andeo agent skill.
58
+
59
+ The starter does not install dependencies, initialize Git, link an Andeo project, contact Andeo, or write credentials. Existing applications do not need to use it.
60
+
61
+ ## Connect an existing application
62
+
63
+ Commit `tender-accounts.json` at the root of each independently deployable application:
64
+
65
+ ```json
66
+ {
67
+ "schema": "tender.accounts-cli/v1",
68
+ "commands": {
69
+ "dev": "npm run dev",
70
+ "check": "npm run check",
71
+ "package": "npm run pack:tender"
72
+ },
73
+ "artifactDirectory": ".tender/artifacts/current",
74
+ "returnPath": "/account"
75
+ }
76
+ ```
77
+
78
+ The commands remain yours:
79
+
80
+ - `dev` starts the repository's normal local development environment;
81
+ - `check` runs the checks that must pass before delivery;
82
+ - `package` writes the portable Andeo artifact to `artifactDirectory`.
83
+
84
+ Run `npx @withandeo/cli config example --json` to print the current contract.
85
+
86
+ ## Authenticate and link the checkout
87
+
88
+ For a normal terminal, sign in through Andeo. Give each merchant or work context a short profile name. The CLI opens a browser, uses Authorization Code with S256 PKCE, and asks you to select one organization and the exact apps this profile may preview:
89
+
90
+ ```sh
91
+ npx @withandeo/cli auth create acme
92
+ ```
93
+
94
+ For a coding agent or headless terminal, start a device authorization instead:
95
+
96
+ ```sh
97
+ npx @withandeo/cli auth create acme --device --no-open --json
98
+ ```
99
+
100
+ Open the returned URL, approve the organization and apps, then exchange the same pending request:
101
+
102
+ ```sh
103
+ npx @withandeo/cli auth status --profile acme --json
104
+ ```
105
+
106
+ The returned pending result includes the exact profile-aware `auth status` command to run after approval. Refreshable profiles are stored in one private mode-0600 user file outside the repository. Set `TENDER_ACCOUNTS_AUTH_FILE` to choose another private location. Access tokens last 15 minutes and rotate from a 30-day login; changing the human's project access takes effect on the next refresh or request authorization check.
107
+
108
+ When an agent already knows the exact project, use that project ID as a stable profile name and authorization hint:
109
+
110
+ ```sh
111
+ npx @withandeo/cli auth create prj_... --device --project prj_... --no-open --json
112
+ ```
113
+
114
+ List local profiles without exposing their credentials:
115
+
116
+ ```sh
117
+ npx @withandeo/cli auth list --json
118
+ ```
119
+
120
+ Activate the profile for this directory tree, validate it, then link the checkout:
121
+
122
+ ```sh
123
+ npx @withandeo/cli auth activate acme
124
+ npx @withandeo/cli auth status --profile acme --json
125
+ npx @withandeo/cli link --json
126
+ npx @withandeo/cli doctor --json
127
+ ```
128
+
129
+ The activation applies to this directory and all descendants; a closer child activation wins. It is stored outside the repository, so another terminal can work in a different merchant tree at the same time without changing this one. Set `TENDER_ACCOUNTS_PROFILE_BINDINGS_FILE` only when a controlled environment needs another private machine-local binding file. The link stores only the API origin and exact project ID, which remains a fail-closed target guard; a link containing `authProfile` is rejected because repository content cannot select a local identity. For one command, set `TENDER_ACCOUNTS_PROFILE=acme` or pass `--profile acme`. If named profiles exist but neither a directory activation nor the default profile selects one, the CLI fails closed instead of guessing.
130
+
131
+ If the credential can access more than one project, select the intended project explicitly:
132
+
133
+ ```sh
134
+ npx @withandeo/cli link --project prj_... --json
135
+ ```
136
+
137
+ CI may still supply an explicitly issued `TENDER_ACCOUNTS_TOKEN` from its protected secret store or over standard input:
138
+
139
+ ```sh
140
+ printf '%s' "$TENDER_ACCOUNTS_TOKEN" | npx @withandeo/cli auth status --token-stdin --json
141
+ ```
142
+
143
+ Revoke the stored login when the machine should no longer have access:
144
+
145
+ ```sh
146
+ npx @withandeo/cli auth delete acme --json
147
+ ```
148
+
149
+ `auth create` replaces only the named profile; `auth login --force` replaces only the default. `auth delete` removes one named profile and its directory activations. Removing every profile requires the explicit `auth logout --all` command. Refresh, replacement, and deletion use a per-profile compare-and-swap so a late refresh cannot recreate a profile after logout. Concurrent commands may reuse a winning refresh rotation only when the stored tenant, user, and exact project grants are unchanged; a real login/logout/profile race still fails closed as `auth_profile_changed` and revokes any losing newly issued session.
150
+
151
+ ## Use Andeo-managed Git
152
+
153
+ Managed Git is available to every linked deployable project, including a
154
+ protected account gateway and an independently editable service. Connect it
155
+ from the app directory after authentication and linking:
156
+
157
+ ```sh
158
+ npx @withandeo/cli source connect --publication preview-only --json
159
+ npx @withandeo/cli source status --json
160
+ ```
161
+
162
+ If the repository is already connected but the developer starts in an empty
163
+ workspace, authenticate to the exact project and let the CLI discover, clone,
164
+ and link it:
165
+
166
+ ```sh
167
+ npx @withandeo/cli source status --project prj_... --json
168
+ npx @withandeo/cli source pull --project prj_... --dry-run --json
169
+ npx @withandeo/cli source pull --project prj_... --json
170
+ ```
171
+
172
+ The same `source pull` command fast-forwards an existing clean checkout when
173
+ one of its remotes exactly matches the connected managed repository. It refuses
174
+ dirty, detached, mismatched, paused, and non-empty non-Git workspaces. The
175
+ repository credential is scoped to that one project, lives for 15 minutes, and
176
+ is passed only to the Git child process; the remote URL and local link contain
177
+ no credential. Merchant-hosted Git repositories remain merchant-owned and must
178
+ be opened through their own Git provider before `andeo link`.
179
+
180
+ The CLI derives the working directory, validation command, package command,
181
+ and portable artifact path from this checkout and `tender-accounts.json`. It
182
+ adds a token-free `tender` Git remote by default. In a monorepo, run the command
183
+ with the same `--cwd` used for `doctor` and `preview`.
184
+
185
+ Push the current committed revision without storing a repository credential:
186
+
187
+ ```sh
188
+ npx @withandeo/cli source push --json
189
+ ```
190
+
191
+ For repositories with reviewed changes enabled, `source push` uploads a bounded
192
+ Git bundle through Andeo. The trusted runner verifies the exact commit and
193
+ expected feature-branch ref before applying it; the CLI never receives a write
194
+ credential and the default branch is protected. Repositories that have not yet
195
+ enabled reviewed changes retain the legacy short-lived push credential during
196
+ migration. Uncommitted files are never included. `source token` is read-only:
197
+
198
+ ```sh
199
+ npx @withandeo/cli source token --ttl-seconds 3600 --json
200
+ ```
201
+
202
+ Use the review flow once an administrator enables reviewed changes:
203
+
204
+ ```sh
205
+ npx @withandeo/cli source push --branch feature/account-copy --json
206
+ npx @withandeo/cli source change create \
207
+ --head feature/account-copy \
208
+ --title "Update account copy" --json
209
+ npx @withandeo/cli source change list --state open --json
210
+ npx @withandeo/cli source change show --change scr_... --json
211
+ ```
212
+
213
+ Andeo attaches validation, the immutable release, and exact preview to the
214
+ current feature-branch snapshot. A merchant administrator opens that exact
215
+ preview, approves it, and selects **Land in main** in the admin. Approval is
216
+ invalidated if the base or head moves. Landing uses a compare-and-swap
217
+ fast-forward. Landing never publishes directly; the repository's already
218
+ configured default-branch policy may publish the same exact preview only after
219
+ the resulting `main` validation succeeds. Protected gateways remain
220
+ preview-only.
221
+
222
+ After landing, Andeo safely cleans up the reviewed feature branch in a
223
+ separate durable workflow. Inspect `branchCleanup` in `source change show`:
224
+
225
+ - `requested` or `running`: landing succeeded and cleanup is still progressing;
226
+ - `succeeded`: the exact landed ref was deleted, or was already absent;
227
+ - `retained`: Andeo kept the ref because it moved or no longer passed a safety
228
+ invariant;
229
+ - `failed`: the landing remains complete and a merchant administrator can retry
230
+ cleanup from the Andeo admin until the bounded retry budget is exhausted.
231
+
232
+ Cleanup never deletes immutable builds, releases, compositions, approvals, or
233
+ audit history, and it never publishes to production. While cleanup owns the
234
+ feature ref, Andeo rejects attempts to reuse it for a new reviewed change or
235
+ make it the default branch.
236
+
237
+ Inspect the managed repository without cloning it:
238
+
239
+ ```sh
240
+ npx @withandeo/cli source branches --json
241
+ npx @withandeo/cli source log --branch main --limit 20 --json
242
+ npx @withandeo/cli source compare --base main --head feature/account-copy --json
243
+ ```
244
+
245
+ These commands use an ephemeral read-only credential in a temporary Git
246
+ process. They report Andeo's authoritative default branch separately from the
247
+ provider's create-time default and never persist or print the credential.
248
+
249
+ Before reviewed changes are enabled, a merchant source administrator can use
250
+ the lower-level migration command to promote an already-previewed branch into
251
+ the current default with an exact expected-SHA lease:
252
+
253
+ ```sh
254
+ npx @withandeo/cli source promote \
255
+ --head feature/account-copy \
256
+ --expected-current <current-main-sha> \
257
+ --dry-run --json
258
+ npx @withandeo/cli source promote \
259
+ --head feature/account-copy \
260
+ --expected-current <current-main-sha> \
261
+ --idempotency-key account-copy-v1 --json
262
+ ```
263
+
264
+ `source default set` changes only Andeo's default-branch metadata. The
265
+ exceptional `source history replace` command is for repository migrations: it
266
+ requires the exact repository ID, a successful preview of the proposed head,
267
+ an expected current SHA, and a dry run. Andeo archives the previous target ref
268
+ before an exact compare-and-swap replacement and always suppresses automatic
269
+ production publication for that migration. There is no generic force-push
270
+ command.
271
+
272
+ ```sh
273
+ npx @withandeo/cli source history replace \
274
+ --head app-only-main \
275
+ --expected-current <current-main-sha> \
276
+ --confirm src_... \
277
+ --dry-run --json
278
+ ```
279
+
280
+ Every source mutation returns a durable `sop_` operation. Inspect it with
281
+ `source operation status`. If a transient provider or runner failure leaves
282
+ that exact operation failed or pending, resume the stored intent by ID; Andeo
283
+ does not ask the caller to reconstruct branches, SHAs, or confirmation values:
284
+
285
+ ```sh
286
+ npx @withandeo/cli source operation status --operation sop_... --json
287
+ npx @withandeo/cli source operation retry --operation sop_... --json
288
+ ```
289
+
290
+ Source administration can create validation, builds, releases, and exact
291
+ previews. It does not directly publish a shopper-facing composition. Protected
292
+ gateways remain preview-only, and multi-repository portal/gateway work is
293
+ reviewed as one exact composition after each repository produces its immutable
294
+ release.
295
+
296
+ Protected gateways are always preview-only and require a merchant
297
+ administrator to publish a reviewed exact composition. A service repository
298
+ may be connected with `--publication default-branch`; that policy promotes the
299
+ same successful default-branch preview without rebuilding it.
300
+
301
+ ## Develop and preview
302
+
303
+ Use the same loop locally or from a coding agent:
304
+
305
+ ```sh
306
+ npx @withandeo/cli dev
307
+ npx @withandeo/cli check --json
308
+ npx @withandeo/cli preview --dry-run --json
309
+ npx @withandeo/cli preview --json
310
+ ```
311
+
312
+ `preview --dry-run` runs the repository checks, packages the app, and verifies the complete artifact without uploading it. A normal preview creates an immutable release and returns an exact preview URL. It does not change production.
313
+
314
+ In a monorepo, target one independently deployable application with `--cwd`:
315
+
316
+ ```sh
317
+ npx @withandeo/cli doctor --cwd apps/portal --json
318
+ npx @withandeo/cli preview --cwd apps/portal --json
319
+ ```
320
+
321
+ One activated profile can serve independently linked descendant apps invoked with `--cwd`, and multiple merchant profiles can coexist safely. Each app has its own explicit `.tender/link.json` containing only its project guard; the machine-local directory binding selects the profile. CI may place one fallback `TENDER_ACCOUNTS_TOKEN` in an ignored Git-root `.dev.vars`, and credential discovery never searches beyond that repository boundary.
322
+
323
+ ## Inspect or retry a delivery
324
+
325
+ When a preview is still running, inspect it by its `dly_` developer-delivery ID:
326
+
327
+ ```sh
328
+ npx @withandeo/cli delivery status --delivery dly_... --json
329
+ ```
330
+
331
+ Retry a requested or failed delivery idempotently:
332
+
333
+ ```sh
334
+ npx @withandeo/cli delivery retry --delivery dly_... --json
335
+ ```
336
+
337
+ Mint a fresh 15-minute preview session for a completed delivery:
338
+
339
+ ```sh
340
+ npx @withandeo/cli delivery preview --delivery dly_... --return-path /account --json
341
+ ```
342
+
343
+ Do not pass a `dwf_` production workflow ID to these commands. Production workflows are shown separately in Andeo Activity; a merchant administrator can resume a waiting exact publish there without rebuilding or substituting a release.
344
+
345
+ ## Stream runtime logs
346
+
347
+ Stream the exact preview produced by one completed developer delivery:
348
+
349
+ ```sh
350
+ npx @withandeo/cli tail --delivery dly_... --json
351
+ ```
352
+
353
+ Merchant administrators can stream the current production runtime for the linked project:
354
+
355
+ ```sh
356
+ npx @withandeo/cli tail --production --status error --json
357
+ ```
358
+
359
+ Optional filters include `--status ok,error,canceled`, `--method GET,POST`, `--search TEXT`, and `--sampling-rate 0.25`. A tail session lasts at most 15 minutes and closes on Ctrl+C. JSON mode writes one sanitized event per stdout line; lifecycle messages go to stderr.
360
+
361
+ Sanitized fetch events include `correlation.rayId`. When the runtime supplies native trace context, JSON output also includes `correlation.traceId` and `correlation.spanId`; human output renders the available values as `ray=`, `trace=`, and `span=`. The current Tail Worker contract guarantees the request Ray ID but does not guarantee trace/span IDs for every event.
362
+
363
+ Andeo resolves the sealed preview composition or current production composition server-side. The CLI never receives infrastructure account IDs, Worker names, provider credentials, or a provider WebSocket URL. Request headers are omitted, secret-shaped object fields and sensitive query parameters are redacted, events are not persisted by the relay, and session capabilities are stored only as digests. Merchant application logs can still contain sensitive values when application code logs them as unlabelled strings, so do not log credentials or customer data.
364
+
365
+ ## Install the coding-agent skill
366
+
367
+ The package includes a skill that teaches a fresh Codex or Claude agent how to use the repository contract and preview workflow:
368
+
369
+ ```sh
370
+ npx @withandeo/cli skill install --agent codex --json
371
+ npx @withandeo/cli skill install --agent claude --json
372
+ ```
373
+
374
+ Installation is global for the current user by default: `~/.agents/skills` for Codex and `~/.claude/skills` for Claude. This lets one reviewed skill serve every merchant checkout without committing a machine-specific copy to a repository.
375
+
376
+ Use `--force` only when you intentionally want to replace an installed copy.
377
+ Use `--directory PATH` only when you explicitly need a custom or repository-local destination.
378
+
379
+ ## Production delivery
380
+
381
+ The developer CLI intentionally has no production publish command. Developer credentials can prepare and preview changes, while production publication remains controlled by the merchant's connected default-branch workflow or an authorized administrator in Andeo.
382
+
383
+ Run `npx @withandeo/cli <command> --help` for the layered command reference and `--json` for machine-readable output.
@@ -0,0 +1,2 @@
1
+ export declare function normalizeAndeoEnvironment(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
2
+ //# sourceMappingURL=andeo-environment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"andeo-environment.d.ts","sourceRoot":"","sources":["../src/andeo-environment.ts"],"names":[],"mappings":"AAIA,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAW3F"}
@@ -0,0 +1,17 @@
1
+ import { CliError } from "./errors.js";
2
+ // Normalize at the CLI boundary so every command, including profile management,
3
+ // sees the same credential precedence. Never silently choose between credentials.
4
+ export function normalizeAndeoEnvironment(environment) {
5
+ const normalized = { ...environment };
6
+ for (const suffix of ["TOKEN", "API_URL", "PROFILE", "AUTH_FILE", "PROFILE_BINDINGS_FILE"]) {
7
+ const current = environment[`ANDEO_${suffix}`];
8
+ const legacy = environment[`TENDER_ACCOUNTS_${suffix}`];
9
+ if (current !== undefined && legacy !== undefined && current !== legacy) {
10
+ throw new CliError("environment_conflict", `ANDEO_${suffix} and TENDER_ACCOUNTS_${suffix} disagree. Set only one.`);
11
+ }
12
+ if (current !== undefined)
13
+ normalized[`TENDER_ACCOUNTS_${suffix}`] = current;
14
+ }
15
+ return normalized;
16
+ }
17
+ //# sourceMappingURL=andeo-environment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"andeo-environment.js","sourceRoot":"","sources":["../src/andeo-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEvC,gFAAgF;AAChF,kFAAkF;AAClF,MAAM,UAAU,yBAAyB,CAAC,WAA8B;IACtE,MAAM,UAAU,GAAG,EAAE,GAAG,WAAW,EAAE,CAAC;IACtC,KAAK,MAAM,MAAM,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,uBAAuB,CAAC,EAAE,CAAC;QAC3F,MAAM,OAAO,GAAG,WAAW,CAAC,SAAS,MAAM,EAAE,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,WAAW,CAAC,mBAAmB,MAAM,EAAE,CAAC,CAAC;QACxD,IAAI,OAAO,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;YACxE,MAAM,IAAI,QAAQ,CAAC,sBAAsB,EAAE,SAAS,MAAM,wBAAwB,MAAM,0BAA0B,CAAC,CAAC;QACtH,CAAC;QACD,IAAI,OAAO,KAAK,SAAS;YAAE,UAAU,CAAC,mBAAmB,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC;IAC/E,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC"}
package/dist/api.d.ts ADDED
@@ -0,0 +1,109 @@
1
+ import type { AgentPrincipal, ArtifactSubmissionResponse, DeveloperDeliveryResponse, ManagedSourceRepositoryResponse, ManagedSourceChangeRequestResponse, ManagedSourceOperationResponse, ManagedSourcePushResponse, ManagedSourceTokenResponse, RuntimeLogSessionResponse } from "./contracts.js";
2
+ export declare class AgentApiClient {
3
+ private readonly baseUrl;
4
+ private readonly token;
5
+ private readonly fetcher;
6
+ constructor(baseUrl: string, token: string, fetcher?: typeof fetch);
7
+ whoami(): Promise<AgentPrincipal>;
8
+ sourceRepository(projectId: string): Promise<{
9
+ repository: ManagedSourceRepositoryResponse | null;
10
+ }>;
11
+ connectSourceRepository(projectId: string, input: {
12
+ repoName: string;
13
+ defaultBranch: string;
14
+ installCommand: string;
15
+ validateCommand: string;
16
+ buildCommand: string;
17
+ workingDirectory: string;
18
+ artifactLatestPath: string;
19
+ autoPublishDefaultBranch: boolean;
20
+ }): Promise<{
21
+ repository: ManagedSourceRepositoryResponse;
22
+ }>;
23
+ sourceToken(repositoryId: string, ttlSeconds: number, scope?: "read" | "write"): Promise<ManagedSourceTokenResponse>;
24
+ createSourcePush(repositoryId: string, input: {
25
+ idempotencyKey: string;
26
+ branch: string;
27
+ expectedCurrentSha: string | null;
28
+ sourceRevision: string;
29
+ bundleSha256: string;
30
+ bundleBytes: number;
31
+ }): Promise<{
32
+ created: boolean;
33
+ push: ManagedSourcePushResponse;
34
+ }>;
35
+ uploadSourceBundle(submissionId: string, bytes: Uint8Array): Promise<{
36
+ push: ManagedSourcePushResponse;
37
+ }>;
38
+ sourcePush(submissionId: string): Promise<{
39
+ push: ManagedSourcePushResponse;
40
+ }>;
41
+ createSourceChangeRequest(repositoryId: string, input: {
42
+ title: string;
43
+ description: string;
44
+ baseBranch: string;
45
+ headBranch: string;
46
+ baseSha: string;
47
+ headSha: string;
48
+ }): Promise<{
49
+ created: boolean;
50
+ changeRequest: ManagedSourceChangeRequestResponse;
51
+ }>;
52
+ sourceChangeRequests(repositoryId: string, state?: "open" | "closed" | "all"): Promise<{
53
+ changeRequests: ManagedSourceChangeRequestResponse[];
54
+ }>;
55
+ sourceChangeRequest(changeRequestId: string): Promise<{
56
+ changeRequest: ManagedSourceChangeRequestResponse;
57
+ }>;
58
+ closeSourceChangeRequest(changeRequestId: string): Promise<{
59
+ changeRequest: ManagedSourceChangeRequestResponse;
60
+ }>;
61
+ createSourceOperation(repositoryId: string, input: {
62
+ kind: "set_default" | "promote" | "history_replace";
63
+ idempotencyKey: string;
64
+ targetBranch: string;
65
+ headBranch?: string;
66
+ expectedCurrentSha?: string;
67
+ expectedDefaultBranch?: string;
68
+ dryRun: boolean;
69
+ }): Promise<{
70
+ created: boolean;
71
+ operation: ManagedSourceOperationResponse;
72
+ }>;
73
+ sourceOperation(operationId: string): Promise<{
74
+ operation: ManagedSourceOperationResponse;
75
+ }>;
76
+ retrySourceOperation(operationId: string): Promise<{
77
+ operation: ManagedSourceOperationResponse;
78
+ }>;
79
+ createSubmission(projectId: string, input: {
80
+ idempotencyKey: string;
81
+ envelope: unknown;
82
+ }): Promise<ArtifactSubmissionResponse>;
83
+ uploadFile(submissionId: string, fileIndex: number, bytes: Uint8Array): Promise<unknown>;
84
+ finalizeSubmission(submissionId: string): Promise<ArtifactSubmissionResponse>;
85
+ createDelivery(projectId: string, input: {
86
+ submissionId: string;
87
+ idempotencyKey: string;
88
+ }): Promise<DeveloperDeliveryResponse>;
89
+ delivery(deliveryId: string): Promise<DeveloperDeliveryResponse>;
90
+ retryDelivery(deliveryId: string): Promise<unknown>;
91
+ previewSession(deliveryId: string, returnPath: string): Promise<{
92
+ url: string;
93
+ grantId: string;
94
+ expiresAt: number;
95
+ }>;
96
+ createRuntimeLogSession(projectId: string, input: {
97
+ environment: "preview" | "production";
98
+ deliveryId?: string;
99
+ filters: {
100
+ statuses: string[];
101
+ methods: string[];
102
+ search: string | null;
103
+ samplingRate: number;
104
+ };
105
+ }): Promise<RuntimeLogSessionResponse>;
106
+ closeRuntimeLogSession(sessionId: string): Promise<unknown>;
107
+ private json;
108
+ }
109
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
package/dist/api.js ADDED
@@ -0,0 +1,141 @@
1
+ import { CliApiError } from "./errors.js";
2
+ export class AgentApiClient {
3
+ baseUrl;
4
+ token;
5
+ fetcher;
6
+ constructor(baseUrl, token, fetcher = fetch) {
7
+ this.baseUrl = baseUrl;
8
+ this.token = token;
9
+ this.fetcher = fetcher;
10
+ }
11
+ whoami() {
12
+ return this.json("/v1/whoami");
13
+ }
14
+ sourceRepository(projectId) {
15
+ return this.json(`/v1/projects/${encodeURIComponent(projectId)}/source-repository`);
16
+ }
17
+ connectSourceRepository(projectId, input) {
18
+ return this.json(`/v1/projects/${encodeURIComponent(projectId)}/source-repository`, {
19
+ method: "POST",
20
+ body: JSON.stringify(input),
21
+ });
22
+ }
23
+ sourceToken(repositoryId, ttlSeconds, scope = "write") {
24
+ return this.json(`/v1/source-repositories/${encodeURIComponent(repositoryId)}/tokens`, {
25
+ method: "POST",
26
+ body: JSON.stringify({ ttlSeconds, scope }),
27
+ });
28
+ }
29
+ createSourcePush(repositoryId, input) {
30
+ return this.json(`/v1/source-repositories/${encodeURIComponent(repositoryId)}/pushes`, {
31
+ method: "POST",
32
+ body: JSON.stringify(input),
33
+ });
34
+ }
35
+ uploadSourceBundle(submissionId, bytes) {
36
+ const body = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
37
+ return this.json(`/v1/source-pushes/${encodeURIComponent(submissionId)}/bundle`, {
38
+ method: "PUT",
39
+ headers: { "content-type": "application/x-git-bundle" },
40
+ body,
41
+ });
42
+ }
43
+ sourcePush(submissionId) {
44
+ return this.json(`/v1/source-pushes/${encodeURIComponent(submissionId)}`);
45
+ }
46
+ createSourceChangeRequest(repositoryId, input) {
47
+ return this.json(`/v1/source-repositories/${encodeURIComponent(repositoryId)}/change-requests`, {
48
+ method: "POST",
49
+ body: JSON.stringify(input),
50
+ });
51
+ }
52
+ sourceChangeRequests(repositoryId, state = "open") {
53
+ return this.json(`/v1/source-repositories/${encodeURIComponent(repositoryId)}/change-requests?state=${state}`);
54
+ }
55
+ sourceChangeRequest(changeRequestId) {
56
+ return this.json(`/v1/source-change-requests/${encodeURIComponent(changeRequestId)}`);
57
+ }
58
+ closeSourceChangeRequest(changeRequestId) {
59
+ return this.json(`/v1/source-change-requests/${encodeURIComponent(changeRequestId)}/close`, { method: "POST" });
60
+ }
61
+ createSourceOperation(repositoryId, input) {
62
+ return this.json(`/v1/source-repositories/${encodeURIComponent(repositoryId)}/operations`, {
63
+ method: "POST",
64
+ body: JSON.stringify(input),
65
+ });
66
+ }
67
+ sourceOperation(operationId) {
68
+ return this.json(`/v1/source-operations/${encodeURIComponent(operationId)}`);
69
+ }
70
+ retrySourceOperation(operationId) {
71
+ return this.json(`/v1/source-operations/${encodeURIComponent(operationId)}/retry`, { method: "POST" });
72
+ }
73
+ createSubmission(projectId, input) {
74
+ return this.json(`/v1/projects/${encodeURIComponent(projectId)}/artifact-submissions`, {
75
+ method: "POST",
76
+ body: JSON.stringify(input),
77
+ });
78
+ }
79
+ uploadFile(submissionId, fileIndex, bytes) {
80
+ const body = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
81
+ return this.json(`/v1/artifact-submissions/${encodeURIComponent(submissionId)}/files/${fileIndex}`, {
82
+ method: "PUT",
83
+ headers: { "content-type": "application/octet-stream" },
84
+ body,
85
+ });
86
+ }
87
+ finalizeSubmission(submissionId) {
88
+ return this.json(`/v1/artifact-submissions/${encodeURIComponent(submissionId)}/finalize`, { method: "POST" });
89
+ }
90
+ createDelivery(projectId, input) {
91
+ return this.json(`/v1/projects/${encodeURIComponent(projectId)}/deliveries`, {
92
+ method: "POST",
93
+ body: JSON.stringify(input),
94
+ });
95
+ }
96
+ delivery(deliveryId) {
97
+ return this.json(`/v1/deliveries/${encodeURIComponent(deliveryId)}`);
98
+ }
99
+ retryDelivery(deliveryId) {
100
+ return this.json(`/v1/deliveries/${encodeURIComponent(deliveryId)}/retry`, { method: "POST" });
101
+ }
102
+ previewSession(deliveryId, returnPath) {
103
+ return this.json(`/v1/deliveries/${encodeURIComponent(deliveryId)}/preview-session`, {
104
+ method: "POST",
105
+ body: JSON.stringify({ returnPath }),
106
+ });
107
+ }
108
+ createRuntimeLogSession(projectId, input) {
109
+ return this.json(`/v1/projects/${encodeURIComponent(projectId)}/runtime-log-sessions`, {
110
+ method: "POST",
111
+ body: JSON.stringify(input),
112
+ });
113
+ }
114
+ closeRuntimeLogSession(sessionId) {
115
+ return this.json(`/v1/runtime-log-sessions/${encodeURIComponent(sessionId)}`, { method: "DELETE" });
116
+ }
117
+ async json(path, init = {}) {
118
+ const headers = new Headers(init.headers);
119
+ headers.set("accept", "application/json");
120
+ headers.set("authorization", `Bearer ${this.token}`);
121
+ if (typeof init.body === "string")
122
+ headers.set("content-type", "application/json");
123
+ const response = await this.fetcher(new URL(path, this.baseUrl), { ...init, headers, redirect: "error" });
124
+ let value;
125
+ try {
126
+ value = await response.json();
127
+ }
128
+ catch {
129
+ throw new CliApiError("invalid_api_response", `Andeo returned HTTP ${response.status} without JSON.`);
130
+ }
131
+ if (!response.ok) {
132
+ const error = isRecord(value) && isRecord(value.error) ? value.error : null;
133
+ throw new CliApiError(typeof error?.code === "string" ? error.code : "api_request_failed", typeof error?.message === "string" ? error.message : `Andeo returned HTTP ${response.status}.`, typeof error?.requestId === "string" ? error.requestId : response.headers.get("x-request-id") ?? undefined);
134
+ }
135
+ return value;
136
+ }
137
+ }
138
+ function isRecord(value) {
139
+ return typeof value === "object" && value !== null && !Array.isArray(value);
140
+ }
141
+ //# sourceMappingURL=api.js.map