integry-app 0.1.0__tar.gz

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 (30) hide show
  1. integry_app-0.1.0/PKG-INFO +714 -0
  2. integry_app-0.1.0/README.md +690 -0
  3. integry_app-0.1.0/integry_app.egg-info/PKG-INFO +714 -0
  4. integry_app-0.1.0/integry_app.egg-info/SOURCES.txt +28 -0
  5. integry_app-0.1.0/integry_app.egg-info/dependency_links.txt +1 -0
  6. integry_app-0.1.0/integry_app.egg-info/entry_points.txt +2 -0
  7. integry_app-0.1.0/integry_app.egg-info/requires.txt +6 -0
  8. integry_app-0.1.0/integry_app.egg-info/top_level.txt +1 -0
  9. integry_app-0.1.0/integry_cli/__init__.py +1 -0
  10. integry_app-0.1.0/integry_cli/__main__.py +4 -0
  11. integry_app-0.1.0/integry_cli/auth.py +142 -0
  12. integry_app-0.1.0/integry_cli/cli.py +111 -0
  13. integry_app-0.1.0/integry_cli/client.py +98 -0
  14. integry_app-0.1.0/integry_cli/commands/__init__.py +0 -0
  15. integry_app-0.1.0/integry_cli/commands/accounts.py +165 -0
  16. integry_app-0.1.0/integry_cli/commands/auth.py +89 -0
  17. integry_app-0.1.0/integry_cli/commands/template_dispatch.py +300 -0
  18. integry_app-0.1.0/integry_cli/commands/template_steps.py +717 -0
  19. integry_app-0.1.0/integry_cli/commands/templates.py +1200 -0
  20. integry_app-0.1.0/integry_cli/config.py +107 -0
  21. integry_app-0.1.0/integry_cli/errors.py +37 -0
  22. integry_app-0.1.0/integry_cli/output.py +405 -0
  23. integry_app-0.1.0/pyproject.toml +40 -0
  24. integry_app-0.1.0/setup.cfg +4 -0
  25. integry_app-0.1.0/tests/test_accounts.py +348 -0
  26. integry_app-0.1.0/tests/test_auth.py +88 -0
  27. integry_app-0.1.0/tests/test_cli_root.py +100 -0
  28. integry_app-0.1.0/tests/test_output.py +115 -0
  29. integry_app-0.1.0/tests/test_template_steps.py +887 -0
  30. integry_app-0.1.0/tests/test_templates.py +1603 -0
@@ -0,0 +1,714 @@
1
+ Metadata-Version: 2.4
2
+ Name: integry-app
3
+ Version: 0.1.0
4
+ Summary: Agent-ergonomic CLI for the Integry backend
5
+ Author-email: Integry <moiz@integry.io>
6
+ License: MIT
7
+ Keywords: integry,ipaas,cli,automation
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: POSIX :: Linux
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ Requires-Dist: typer>=0.12
20
+ Requires-Dist: httpx>=0.27
21
+ Provides-Extra: dev
22
+ Requires-Dist: pytest>=8; extra == "dev"
23
+ Requires-Dist: pytest-httpx>=0.30; extra == "dev"
24
+
25
+ # Integry CLI
26
+
27
+ Agent-ergonomic CLI over the Integry backend. Wraps the template-management endpoints:
28
+
29
+ - `GET /api/v1/templates/` - list templates (with search + type filter)
30
+ - `POST /api/v1/templates/` - create a template
31
+ - `PATCH /api/v5/templates/<id>/` - partial update a template
32
+ - `GET /api/v2/templates/<id>/` - retrieve a template (or its draft payload)
33
+ - `GET /api/v1/templates/<id>/versions/` - list versions
34
+ - `GET /api/v1/templates/<id>/versions/<n>/` - retrieve a specific version
35
+ - `POST /api/v1/templates/<id>/versions/` - publish the draft as a new version
36
+ - `POST /functions/<machine_name>/call/` - test a FUNCTION template draft
37
+
38
+ Designed following the [AXI principles](https://axi.md): TOON output by default, minimal default schemas, content truncation, structured errors, content-first default invocation, contextual disclosure of next-step commands.
39
+
40
+ ## Install
41
+
42
+ End users (Linux + macOS):
43
+
44
+ ```
45
+ pipx install integry-app # or: uv tool install integry-app
46
+ integry --help
47
+ ```
48
+
49
+ Upgrade with `pipx upgrade integry-app` (or `uv tool upgrade integry-app`).
50
+
51
+ ## Develop
52
+
53
+ ```
54
+ nix-shell # enter dev shell (latest stable Python + deps)
55
+ python -m integry_cli # run the CLI from inside the shell
56
+ nix-shell --run pytest # run tests
57
+ nix-shell --run 'python -m integry_cli --help'
58
+ ```
59
+
60
+ If installed via `pip install -e web/cli`, an `integry` console script is exposed.
61
+
62
+ ## Publish
63
+
64
+ ```
65
+ export UV_PUBLISH_TOKEN=<pypi-token> # https://pypi.org/manage/account/token/
66
+ ./scripts/publish.sh # bump version in pyproject.toml first
67
+ ```
68
+
69
+ ## Configuration
70
+
71
+ | Variable | Purpose | Default |
72
+ |---|---|---|
73
+ | `INTEGRY_API_URL` | Backend base URL. Wins over stored config. | `https://api.integry.io` |
74
+ | `INTEGRY_HOME` | Config directory. | `~/.integry` |
75
+ | `INTEGRY_ACCOUNT_ID` | Account id sent on every request as the `X-Account-Id` header. Wins over stored config. | _unset_ |
76
+
77
+ Stored files (mode `0o600` on credentials):
78
+
79
+ - `$INTEGRY_HOME/config.json` - `{"api_url": "...", "account_id": <int>}` (set by `auth login --api-url` and `accounts use`)
80
+ - `$INTEGRY_HOME/credentials.json` - `{"access", "refresh", "obtained_at"}`
81
+
82
+ ## Output
83
+
84
+ - Default format is TOON (token-efficient). Pass `--format json` for a machine-readable envelope.
85
+ - Default schemas:
86
+ - Template detail → `id, name, type, machine_name, app_id, publishing_status, revision_number, live_or_latest_version, is_draft_empty`
87
+ - Template list rows → `id, name, type, machine_name, publishing_status, revision_number, live_or_latest_version`
88
+ - Template versions → `id, version, publishing_status, is_live, integration_count, changelog`
89
+ - Pass `--full` to skip field filtering and disable the 400-char string truncation.
90
+ - Successful mutations always end with one or more `next:` lines suggesting plausible follow-ups.
91
+ - Errors print to stdout as `error: <code>: <message>` with optional indented detail.
92
+
93
+ ## Exit codes
94
+
95
+ | Code | Meaning |
96
+ |---|---|
97
+ | `0` | Success |
98
+ | `1` | Backend API error (4xx/5xx other than 401) |
99
+ | `2` | Auth/credential problem (no credentials, or refresh failed) |
100
+ | `3` | Local usage error (bad flags, unreadable JSON file, mutually-exclusive flags) |
101
+
102
+ ---
103
+
104
+ ## Commands
105
+
106
+ ### `integry` (no args)
107
+
108
+ Prints ambient state and next-step hints - no help-text dump (AXI principle 8).
109
+
110
+ ```
111
+ integry [--format toon|json]
112
+ ```
113
+
114
+ Output (unauthenticated):
115
+
116
+ ```
117
+ api_url: "https://api.integry.io"
118
+ authenticated: false
119
+ access_expires_in: null
120
+ account_id: null
121
+ next: integry auth login --username <you>
122
+ next: integry accounts list
123
+ ```
124
+
125
+ Output (authenticated, no account selected):
126
+
127
+ ```
128
+ api_url: "https://api.integry.io"
129
+ authenticated: true
130
+ access_expires_in: 3140
131
+ account_id: null
132
+ next: integry accounts list
133
+ next: integry accounts use <id>
134
+ ```
135
+
136
+ Output (authenticated, account selected - templates hints kick in):
137
+
138
+ ```
139
+ api_url: "https://api.integry.io"
140
+ authenticated: true
141
+ access_expires_in: 3140
142
+ account_id: 7
143
+ next: integry templates create --input ./template.json
144
+ next: integry templates update <id> --input ./patch.json
145
+ ```
146
+
147
+ Exit code: `0` if authenticated, `2` if not.
148
+
149
+ ---
150
+
151
+ ### `integry auth login`
152
+
153
+ Obtain an access + refresh token pair from `POST /api/token/` and persist them.
154
+
155
+ ```
156
+ integry auth login --username <name> [--password <secret>] [--api-url <url>] [--format toon|json]
157
+ ```
158
+
159
+ | Flag | Effect |
160
+ |---|---|
161
+ | `--username, -u` (required) | Account username. |
162
+ | `--password, -p` | Password. Prompted (hidden) if omitted - the only interactive prompt anywhere in the CLI. |
163
+ | `--api-url` | Override and persist `api_url` to `$INTEGRY_HOME/config.json`. `INTEGRY_API_URL` env var still wins. |
164
+ | `--format` | `toon` (default) or `json`. |
165
+
166
+ Output:
167
+
168
+ ```
169
+ username: alice
170
+ api_url: "https://api.integry.io"
171
+ access_expires_in: 3599
172
+ refresh_expires_in: 86399
173
+ next: integry accounts list
174
+ ```
175
+
176
+ Picking an account is the very next step after login - see [`integry accounts`](#integry-accounts) below.
177
+
178
+ Exits `2` on bad credentials (backend `401`), `1` on other backend errors.
179
+
180
+ ---
181
+
182
+ ### `integry auth logout`
183
+
184
+ Delete stored credentials. Idempotent.
185
+
186
+ ```
187
+ integry auth logout [--format toon|json]
188
+ ```
189
+
190
+ Output:
191
+
192
+ ```
193
+ cleared: true # or false if there was nothing to clear
194
+ api_url: "https://api.integry.io"
195
+ ```
196
+
197
+ Always exits `0`.
198
+
199
+ ---
200
+
201
+ ### `integry auth status`
202
+
203
+ Show the currently stored credentials' state.
204
+
205
+ ```
206
+ integry auth status [--format toon|json]
207
+ ```
208
+
209
+ Output (authenticated):
210
+
211
+ ```
212
+ api_url: "https://api.integry.io"
213
+ user_id: 42
214
+ access_expires_in: 3140
215
+ refresh_expires_in: 86340
216
+ ```
217
+
218
+ If no credentials exist, prints `error: usage_error: not authenticated - run: integry auth login` and exits `3`.
219
+
220
+ ---
221
+
222
+ ### `integry accounts`
223
+
224
+ Selects which account is sent on every request as the `X-Account-Id` header. The backend reads this header in `app/permissions.py` to scope the request to a specific tenant.
225
+
226
+ The active account is resolved in this order:
227
+
228
+ 1. `INTEGRY_ACCOUNT_ID` env var
229
+ 2. `account_id` in `$INTEGRY_HOME/config.json` (set via `accounts use`)
230
+ 3. None - the header is omitted
231
+
232
+ The active id (and `null` if unset) is included in the output of bare `integry` and `integry auth status`.
233
+
234
+ #### `integry accounts list`
235
+
236
+ ```
237
+ integry accounts list [--search TEXT] [--page N] [--page-size N] [--format toon|json] [--full]
238
+ ```
239
+
240
+ | Flag | Effect |
241
+ |---|---|
242
+ | `--search, -s TEXT` | Case-insensitive substring filter on account `name`. Applied client-side. |
243
+ | `--page N` | 1-indexed page number. Sliced client-side after `--search`. |
244
+ | `--page-size N` | Results per page. Sliced client-side. |
245
+ | `--format` | `toon` (default) or `json`. |
246
+ | `--full` | Skip field filtering. |
247
+
248
+ Default schema per row: `id, name, is_currently_selected`.
249
+
250
+ #### `integry accounts use`
251
+
252
+ ```
253
+ integry accounts use <account_id> [--format toon|json]
254
+ ```
255
+
256
+ | Flag | Effect |
257
+ |---|---|
258
+ | `<account_id>` (positional, required) | Account primary key to send as `X-Account-Id`. |
259
+ | `--format` | `toon` (default) or `json`. |
260
+
261
+ #### `integry accounts clear`
262
+
263
+ ```
264
+ integry accounts clear [--format toon|json]
265
+ ```
266
+
267
+ | Flag | Effect |
268
+ |---|---|
269
+ | `--format` | `toon` (default) or `json`. |
270
+
271
+ #### `integry accounts current`
272
+
273
+ ```
274
+ integry accounts current [--format toon|json]
275
+ ```
276
+
277
+ | Flag | Effect |
278
+ |---|---|
279
+ | `--format` | `toon` (default) or `json`. |
280
+
281
+ Exits `3` if no account is selected.
282
+
283
+ ---
284
+
285
+ ### `integry templates list`
286
+
287
+ List templates owned by the active account via `GET /api/v1/templates/`.
288
+
289
+ ```
290
+ integry templates list \
291
+ [--search TEXT] [--type TYPE]... \
292
+ [--page N] [--page-size N] \
293
+ [--format toon|json] [--full]
294
+ ```
295
+
296
+ | Flag | Effect |
297
+ |---|---|
298
+ | `--search, -s TEXT` | `?search=` - server-side substring match against `name`, `machine_name`, and `branding_app__name`. |
299
+ | `--type, -t TYPE` | Repeatable, joined into `?types=A,B,C`. Valid: `STANDARD`, `QUERY`, `ACTION`, `TRIGGER`, `FUNCTION`, `INTEGRATION`, `AGENT`, `FUNCTION_TRIGGER`. |
300
+ | `--page N` | 1-indexed page number. |
301
+ | `--page-size N` | Results per page (backend default 10). |
302
+ | `--format` | `toon` (default) or `json`. |
303
+ | `--full` | Disable field filtering. |
304
+
305
+ Default schema per row: `id, name, type, machine_name, publishing_status, revision_number, live_or_latest_version` - `machine_name` is the identifier for `FUNCTION` templates and is `null` for most other types.
306
+
307
+ Output:
308
+
309
+ ```
310
+ total: 27
311
+ templates:
312
+ [10] id,name,type,machine_name,publishing_status,revision_number,live_or_latest_version:
313
+ 101,send_email,FUNCTION,send_email,PUB,r4,3
314
+ 99,Onboarding,STANDARD,,DR,r1,
315
+ ...
316
+ next: integry templates get <id>
317
+ ```
318
+
319
+ JSON envelope additionally carries `query` and `types` reflecting the active filters.
320
+
321
+ Empty states: `no templates found` (unfiltered), `no templates match the filter` (with `--search`/`--type`).
322
+
323
+ ---
324
+
325
+ ### `integry templates create`
326
+
327
+ Create a template by `POST /api/v1/templates/`. Body is forwarded verbatim - backend serializers are authoritative.
328
+
329
+ ```
330
+ integry templates create --input <file|-> [--publish] [--example] [--format toon|json] [--full]
331
+ ```
332
+
333
+ | Flag | Effect |
334
+ |---|---|
335
+ | `--input, -i` | Path to a JSON file, or `-` to read from stdin. Required unless `--example`. |
336
+ | `--publish` | Adds `?publish=true` so the template is created with status `PUB`. |
337
+ | `--example` | Print a comprehensive example body to stdout and exit. No API call. `--input` is ignored. |
338
+ | `--format` | `toon` (default) or `json`. |
339
+ | `--full` | Disable field filtering and string truncation. |
340
+
341
+ Body (forwarded verbatim):
342
+
343
+ ```json
344
+ {
345
+ "meta": {
346
+ "title": "My template",
347
+ "description": "...",
348
+ "app_id": 7,
349
+ "branding_app_id": 7,
350
+ "is_wizard": false,
351
+ "partial_trigger_response": false
352
+ },
353
+ "trigger": { /* validated server-side */ },
354
+ "actions": [ /* validated server-side */ ]
355
+ }
356
+ ```
357
+
358
+ Output:
359
+
360
+ ```
361
+ id: 99
362
+ name: "My template"
363
+ app_id: 7
364
+ publishing_status: DR
365
+ revision_number: r1
366
+ next: integry templates update 99 --input ./patch.json
367
+ ```
368
+
369
+ ---
370
+
371
+ ### `integry templates update`
372
+
373
+ Partial-update a template by `PATCH /api/v5/templates/<id>/`.
374
+
375
+ ```
376
+ integry templates update <id> --input <file|-> \
377
+ [--version N] [--publish] [--archive] [--meta-only] [--revision N] \
378
+ [--example] [--format toon|json] [--full]
379
+ ```
380
+
381
+ | Flag | Effect |
382
+ |---|---|
383
+ | `<id>` (positional, required) | Template primary key. |
384
+ | `--input, -i` | Path to a JSON file, or `-` for stdin. Body must be an object containing a `meta` object. Required unless `--example`. |
385
+ | `--version` | `?version=N` - patch a specific template version. |
386
+ | `--publish` | `?publish=true`. |
387
+ | `--archive` | `?archive=true`. |
388
+ | `--meta-only` | `?exclude_template_from_response=true`. Mutually exclusive with `steps` in the body - fails fast (exit `3`). |
389
+ | `--revision` | Convenience: sets `meta.revision_number` in the body before sending. Lets you keep a static patch file and bump the revision via flag. |
390
+ | `--example` | Print a comprehensive example body to stdout and exit. No API call. `--input` is ignored. |
391
+ | `--format` | `toon` (default) or `json`. |
392
+ | `--full` | Disable field filtering and string truncation. |
393
+
394
+ Body:
395
+
396
+ ```json
397
+ {
398
+ "meta": {
399
+ "revision_number": 7,
400
+ "app_id": 1,
401
+ "branding_app_id": 1,
402
+ "product_info": { /* optional */ },
403
+ "events": [ /* optional */ ]
404
+ },
405
+ "steps": [ /* optional; omit for meta-only updates */ ]
406
+ }
407
+ ```
408
+
409
+ `steps` is not partial - when included, the backend replaces all steps with what you send. Omitted steps are deleted. Fetch the full steps first via `get-draft` or `get --full`, modify, and send back the complete array. To update only `meta`, omit `steps` and use `--meta-only`.
410
+
411
+ Output (full update):
412
+
413
+ ```
414
+ id: 42
415
+ name: "My template"
416
+ app_id: 1
417
+ publishing_status: PUB
418
+ revision_number: 8
419
+ next: integry templates update 42 --input ./patch.json
420
+ ```
421
+
422
+ Output (`--meta-only`):
423
+
424
+ ```
425
+ success: true
426
+ next: integry templates update 42 --input ./patch.json
427
+ ```
428
+
429
+ Common backend rejections (exit `1`):
430
+ - `Revision number does not match` - your local revision is stale; refetch.
431
+ - `Template already being updated` - the template is locked.
432
+ - `Cannot update template while integration update job is in progress` - drop the `steps` and use `--meta-only`, or wait for the job.
433
+
434
+ ---
435
+
436
+ ### `integry templates get`
437
+
438
+ Retrieve a template or one of its versions.
439
+
440
+ ```
441
+ integry templates get <id> [--version N] [--format toon|json] [--full]
442
+ ```
443
+
444
+ | Flag | Effect |
445
+ |---|---|
446
+ | `<id>` (positional, required) | Template primary key. |
447
+ | `--version N` | Fetch version N via `GET /api/v1/templates/<id>/versions/<n>/`. |
448
+ | `--format` | `toon` (default) or `json`. |
449
+ | `--full` | Disable field filtering and string truncation. |
450
+
451
+ Output (no `--version`):
452
+
453
+ ```
454
+ id: 42
455
+ name: "My template"
456
+ app_id: 7
457
+ publishing_status: PUB
458
+ revision_number: 8
459
+ live_or_latest_version: 3
460
+ is_draft_empty: true
461
+ next: integry templates list-versions 42
462
+ ```
463
+
464
+ Output (`--version 3`):
465
+
466
+ ```
467
+ id: 901
468
+ version: 3
469
+ publishing_status: PUB
470
+ is_live: true
471
+ integration_count: 12
472
+ changelog: "Added retry logic"
473
+ next: integry templates get-draft 42 --version 3
474
+ ```
475
+
476
+ ---
477
+
478
+ ### `integry templates list-versions`
479
+
480
+ List published versions of a template via `GET /api/v1/templates/<id>/versions/`.
481
+
482
+ ```
483
+ integry templates list-versions <id> [--page N] [--page-size N] [--format toon|json] [--full]
484
+ ```
485
+
486
+ | Flag | Effect |
487
+ |---|---|
488
+ | `<id>` (positional, required) | Base template primary key. |
489
+ | `--page N` | 1-indexed page number. |
490
+ | `--page-size N` | Results per page (backend default 10). |
491
+ | `--format` | `toon` (default) or `json`. |
492
+ | `--full` | Emit each version's full payload instead of the default 6-field schema. |
493
+
494
+ Output:
495
+
496
+ ```
497
+ total: 3
498
+ versions:
499
+ [3] id,version,publishing_status,is_live,integration_count,changelog:
500
+ 901,3,PUB,true,12,"Added retry logic"
501
+ 877,2,PUB,false,4,"Bumped action timeout"
502
+ 803,1,PUB,false,0,"Initial release"
503
+ next: integry templates publish 42
504
+ ```
505
+
506
+ Empty state (no versions yet):
507
+
508
+ ```
509
+ no versions yet - publish a draft to create one
510
+ next: integry templates publish 42
511
+ ```
512
+
513
+ `--format json` always emits a structured envelope, including for the empty case (`{"total": 0, "versions": []}`).
514
+
515
+ ---
516
+
517
+ ### `integry templates publish`
518
+
519
+ Publish the current draft as a new version via `POST /api/v1/templates/<id>/versions/`.
520
+
521
+ ```
522
+ integry templates publish <id> \
523
+ [--changelog "text"] [--beta] [--upgrade-integrations] \
524
+ [--format toon|json] [--full]
525
+ ```
526
+
527
+ | Flag | Effect |
528
+ |---|---|
529
+ | `<id>` (positional, required) | Draft template primary key. |
530
+ | `--changelog TEXT` | Optional changelog string sent in the request body. |
531
+ | `--beta` | Adds `?publish_for_beta_users=true`. |
532
+ | `--upgrade-integrations` | Adds `?upgrade_integrations=true`. |
533
+ | `--format` | `toon` (default) or `json`. |
534
+ | `--full` | Disable field filtering and string truncation. |
535
+
536
+ Output (the `id` is the new version-template id, not the base id):
537
+
538
+ ```
539
+ id: 904
540
+ name: "My template"
541
+ app_id: 7
542
+ publishing_status: PUB
543
+ revision_number: 9
544
+ next: integry templates list-versions 42
545
+ ```
546
+
547
+ ---
548
+
549
+ ### `integry templates get-draft`
550
+
551
+ Fetch a template (or a specific published version) reshaped into a draft body suitable for `templates create`. Calls `GET /api/v2/templates/<base_id>/?return_as_draft=true[&version=N]`.
552
+
553
+ ```
554
+ integry templates get-draft <template_id> [--version N] [--format toon|json] [--full]
555
+ ```
556
+
557
+ | Flag | Effect |
558
+ |---|---|
559
+ | `<template_id>` (positional, required) | Base template primary key. |
560
+ | `--version N` | Specific version number to export as draft. Without it, returns the current draft state. |
561
+ | `--format` | `toon` (default) or `json`. |
562
+ | `--full` | Required if you intend to feed the output into `templates create`. Without it, only a 4-field summary is printed. |
563
+
564
+ Default output (summary):
565
+
566
+ ```
567
+ title: "My template"
568
+ app_id: 7
569
+ publishing_status: DR
570
+ step_count: 6
571
+ next: integry templates create --input <draft.json>
572
+ ```
573
+
574
+ Round-trip example:
575
+
576
+ ```bash
577
+ integry templates get-draft 42 --version 3 --full --format json \
578
+ | jq '.data' > /tmp/draft.json
579
+ integry templates create --input /tmp/draft.json
580
+ ```
581
+
582
+ ---
583
+
584
+ ### `integry templates diff`
585
+
586
+ Show differences between a published version and the current draft, ignoring IDs and read-only fields.
587
+
588
+ ```
589
+ integry templates diff <id> --version N [--context N] [--format toon|json]
590
+ ```
591
+
592
+ | Flag | Effect |
593
+ |---|---|
594
+ | `<id>` (positional, required) | Base template primary key. |
595
+ | `--version N` (required) | Version number to compare against the current draft. |
596
+ | `--context, -C N` | Number of context lines around each change. Default `5`. |
597
+ | `--format` | `toon` (default) or `json`. |
598
+
599
+ Strips `id`, `version_template_id`, `version_of`, `version_number`, `last_modified`, `last_modified_by`, `created`, `publishing_status`, `revision_number` from both sides before diffing. Output is unified diff format:
600
+
601
+ ```
602
+ --- version 3
603
+ +++ draft
604
+ @@ -1,7 +1,7 @@
605
+ {
606
+ "meta": {
607
+ "app_id": 7,
608
+ - "description": "Original description",
609
+ + "description": "Updated description",
610
+ ```
611
+
612
+ When there are no semantic changes: `no differences`. `--format json` emits `{"ok": true, "data": {"diff": "...", "has_changes": true/false}}`.
613
+
614
+ ---
615
+
616
+ ### `integry templates test`
617
+
618
+ Test a FUNCTION template's draft version by calling it via the signed-request API. Automatically fetches workspace app credentials, resolves connected accounts, and computes the HMAC hash.
619
+
620
+ ```
621
+ integry templates test <id> [--input <file|->] [--connected-account-id N] [--format toon|json] [--full]
622
+ ```
623
+
624
+ | Flag | Effect |
625
+ |---|---|
626
+ | `<id>` (positional, required) | Template primary key. Must be `type=FUNCTION`. |
627
+ | `--input, -i` | Path to a JSON file with function arguments, or `-` for stdin. Defaults to `{}` if omitted. |
628
+ | `--connected-account-id N` | Use this connected account directly - skips the app lookup and selection prompt. |
629
+ | `--format` | `toon` (default) or `json`. |
630
+ | `--full` | Disable field filtering and string truncation. |
631
+
632
+ Prerequisites: must be authenticated and have an account selected (`accounts use <id>`).
633
+
634
+ Flow:
635
+ 1. Fetches the template, verifies `type=FUNCTION`, extracts `machine_name`.
636
+ 2. Gets workspace app `key`/`secret` from `/accounts/me/`.
637
+ 3. Computes signed-request credentials (`HMAC-SHA256`).
638
+ 4. Fetches connected accounts for the app. Single account → auto-selected; multiple → interactive prompt.
639
+ 5. Calls the function with `version: DRAFT` header.
640
+
641
+ Output:
642
+
643
+ ```
644
+ result: "sent"
645
+ ```
646
+
647
+ Exits `1` on function call failure, `3` if the template is not FUNCTION, has no `machine_name`, no account is selected, or no connected accounts exist.
648
+
649
+ ---
650
+
651
+ ## Examples
652
+
653
+ ```bash
654
+ # Local backend, fresh login
655
+ export INTEGRY_API_URL=http://localhost:8000
656
+ integry auth login --username alice
657
+
658
+ # Discover the accounts you belong to, then pin one
659
+ integry accounts list
660
+ integry accounts list --search acme # narrow the list when you belong to many
661
+ integry accounts use 7
662
+
663
+ # List templates in the active account, narrow to FUNCTION templates
664
+ integry templates list
665
+ integry templates list --type FUNCTION
666
+ integry templates list --search send --type FUNCTION --type ACTION
667
+ # ...or one-shot via env (wins over the persisted value)
668
+ INTEGRY_ACCOUNT_ID=7 integry templates list-versions 42
669
+
670
+ # Create a template from a file
671
+ integry templates create --input ./examples/my-template.json
672
+
673
+ # Pipe a body through stdin
674
+ jq '.template' big.json | integry templates create --input -
675
+
676
+ # Bump a template's revision number and patch its name
677
+ integry templates update 42 --input ./patch.json --revision 11 --meta-only
678
+
679
+ # Ask for full JSON output for an agent
680
+ integry templates update 42 --input ./patch.json --meta-only --format json
681
+
682
+ # Publish a draft
683
+ integry templates update 42 --input ./empty-meta.json --publish --meta-only
684
+
685
+ # Inspect a template and its version history
686
+ integry templates get 42
687
+ integry templates list-versions 42
688
+ integry templates get 42 --version 3
689
+
690
+ # Publish the current draft as a new version with a changelog
691
+ integry templates publish 42 --changelog "Added retry logic"
692
+
693
+ # Clone a published version back into a fresh draft
694
+ integry templates get-draft 42 --version 3 --full --format json | jq '.data' > /tmp/draft.json
695
+ integry templates create --input /tmp/draft.json
696
+
697
+ # Diff a version against the current draft
698
+ integry templates diff 42 --version 3
699
+ integry templates diff 42 --version 3 --context 10
700
+
701
+ # Bootstrap a template body from --example
702
+ integry templates create --example > template.json
703
+ integry templates update 42 --example > patch.json
704
+
705
+ # Test a FUNCTION template draft
706
+ integry templates test 42
707
+ integry templates test 42 --input args.json
708
+ integry templates test 42 --input args.json --connected-account-id 12345
709
+ ```
710
+
711
+ ## Further reading
712
+
713
+ - [`docs/`](docs/) - architecture, conventions, version control, per-command feature docs
714
+ - [AXI principles](https://axi.md)