@hitch42/applet 0.1.1-beta.7 → 0.1.1-beta.8

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 (49) hide show
  1. package/dist/_internal/server.d.ts +14 -1
  2. package/dist/_internal/server.js +23 -74
  3. package/dist/applet.js +22 -11
  4. package/dist/build-CFCKKMn7.js +2 -0
  5. package/dist/{build-DauirTcY.js → build-DANx9Hgg.js} +1 -1
  6. package/dist/{ci-gPrwo6C0.js → ci-5qWl7lT3.js} +1 -1
  7. package/dist/client.d.ts +2 -2
  8. package/dist/{contract-DB9QQhD9.d.ts → contract-rP_eV9PK.d.ts} +1 -1
  9. package/dist/{deploy-CqrtNJ1x.js → deploy-MQWLG2EA.js} +3 -3
  10. package/dist/email-DBTVhNB_.js +62 -0
  11. package/dist/{generate-BIifT6PK.js → generate-BxYHdoAQ.js} +9 -8
  12. package/dist/generate-CQ07Hdpo.js +2 -0
  13. package/dist/index.d.ts +114 -3
  14. package/dist/index.js +141 -26
  15. package/dist/{init-Wu6XmyVc.js → init-jDEgYIXg.js} +5 -5
  16. package/dist/{load-config-BknBkdRs.js → load-config-BYSxDVAl.js} +2 -2
  17. package/dist/{login-iT9MRuaw.js → login-C7RKmmTe.js} +1 -1
  18. package/dist/{logs-B-qZGXSK.js → logs-DgjgAxj2.js} +3 -3
  19. package/dist/{manifest-Bd04cWhu.js → manifest-Bp87Uz0a.js} +3 -0
  20. package/dist/{manifest-C489nYUy.d.ts → manifest-DD_SGkS6.d.ts} +3 -0
  21. package/dist/me-BW7Paary.js +32 -0
  22. package/dist/{project-C_rtpjp8.js → project-CbYPtpPq.js} +6 -5
  23. package/dist/prompt-ChS-grYO.js +29 -0
  24. package/dist/schedules-dhU1yyFr.js +42 -0
  25. package/dist/server.d.ts +2 -2
  26. package/dist/telephony-protocol-DRgiYjqQ.d.ts +74 -0
  27. package/dist/telephony.d.ts +77 -0
  28. package/dist/telephony.js +607 -0
  29. package/dist/{variables-DcwMeIau.js → variables-Bwnr7oxf.js} +3 -28
  30. package/dist/vite.d.ts +1 -1
  31. package/dist/vite.js +457 -22
  32. package/dist/workflow-engine-MogejNfF.js +117 -0
  33. package/dist/{workspace-B6lenAS5.js → workspace-XVtM12ZC.js} +5 -2
  34. package/package.json +6 -1
  35. package/scaffold/.agents/skills/hitch-applets/SKILL.md +24 -18
  36. package/scaffold/.agents/skills/hitch-applets/references/email-tracking.md +49 -0
  37. package/scaffold/.agents/skills/hitch-applets/references/files.md +3 -3
  38. package/scaffold/.agents/skills/hitch-applets/references/manifest.md +4 -2
  39. package/scaffold/.agents/skills/hitch-applets/references/performance.md +1 -1
  40. package/scaffold/.agents/skills/hitch-applets/references/records.md +4 -3
  41. package/scaffold/.agents/skills/hitch-applets/references/schedules.md +7 -2
  42. package/scaffold/.agents/skills/hitch-applets/references/server.md +0 -3
  43. package/scaffold/.agents/skills/hitch-applets/references/workflows.md +11 -3
  44. package/scaffold/.agents/skills/hitch-cli/SKILL.md +20 -20
  45. package/scaffold/.agents/skills/hitch-data/SKILL.md +1 -1
  46. package/scaffold/.agents/skills/hitch-data/references/limits.md +10 -10
  47. package/scaffold/.agents/skills/hitch-theory/SKILL.md +150 -0
  48. package/dist/build-DhNDp7ji.js +0 -2
  49. package/dist/generate-DSk8ihsC.js +0 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hitch42/applet",
3
- "version": "0.1.1-beta.7",
3
+ "version": "0.1.1-beta.8",
4
4
  "license": "MIT",
5
5
  "bin": {
6
6
  "applet": "./dist/applet.js"
@@ -27,6 +27,10 @@
27
27
  "source": "./src/react/index.ts",
28
28
  "default": "./dist/react.js"
29
29
  },
30
+ "./telephony": {
31
+ "source": "./src/telephony/index.ts",
32
+ "default": "./dist/telephony.js"
33
+ },
30
34
  "./vite": {
31
35
  "source": "./src/vite.ts",
32
36
  "default": "./dist/vite.js"
@@ -53,6 +57,7 @@
53
57
  "@tanstack/react-query-persist-client": "5.101.4",
54
58
  "@tanstack/router-cli": "^1.167.24",
55
59
  "@tanstack/router-plugin": "^1.168.23",
60
+ "@telnyx/webrtc": "^2.27.10",
56
61
  "@tiptap/core": "^3.30.3",
57
62
  "@tiptap/extension-image": "^3.30.3",
58
63
  "@tiptap/extension-list": "^3.30.3",
@@ -13,17 +13,18 @@ Treat each applet as an isolated microfrontend. Keep it separate from the data s
13
13
 
14
14
  This file holds the common rules. Read each matching reference before you edit an applet. Paths are relative to this file.
15
15
 
16
- | Read this | When the task involves |
17
- | --------------------------- | -------------------------------------------------------------------------------------------------------- |
18
- | `references/client.md` | Client routes, navigation, page layout, forms, controls, or other user interface work |
19
- | `references/records.md` | Listing, querying, creating, or updating records; filters, includes, aggregates; a missing record method |
20
- | `references/files.md` | Uploading or reading files, attachment attributes, download links |
21
- | `references/markdown.md` | Rendering or editing markdown, image uploads, Hitch file links |
22
- | `references/server.md` | The applet server: secrets, env variables, external services, the `api` client |
23
- | `references/schedules.md` | Cron schedules and background runs |
24
- | `references/workflows.md` | Multi-step server work: `createWorkflow`, `step.do`, `step.sleep`, starting, reading, and listing runs |
25
- | `references/manifest.md` | The applet definition in `applet.manifest.ts`: objects, access, variables, and packages |
26
- | `references/performance.md` | Data access design: client versus server, N+1 reads, includes, aggregates, pagination, batch writes |
16
+ | Read this | When the task involves |
17
+ | ------------------------------ | -------------------------------------------------------------------------------------------------------- |
18
+ | `references/client.md` | Client routes, navigation, page layout, forms, controls, or other user interface work |
19
+ | `references/records.md` | Listing, querying, creating, or updating records; filters, includes, aggregates; a missing record method |
20
+ | `references/files.md` | Uploading or reading files, attachment attributes, download links |
21
+ | `references/email-tracking.md` | Creating pixels and reading raw email hits |
22
+ | `references/markdown.md` | Rendering or editing markdown, image uploads, Hitch file links |
23
+ | `references/server.md` | The applet server: secrets, env variables, external services, the `api` client |
24
+ | `references/schedules.md` | Cron schedules and background runs |
25
+ | `references/workflows.md` | Multi-step server work: `createWorkflow`, `step.do`, `step.sleep`, starting, reading, and listing runs |
26
+ | `references/manifest.md` | The applet definition in `applet.manifest.ts`: objects, access, variables, and packages |
27
+ | `references/performance.md` | Data access design: client versus server, N+1 reads, includes, aggregates, pagination, batch writes |
27
28
 
28
29
  For attribute types, filters, and API limits, read `../hitch-data/SKILL.md`. To inspect or change Hitch data and settings from the shell, read `../hitch-cli/SKILL.md`.
29
30
 
@@ -31,8 +32,8 @@ For attribute types, filters, and API limits, read `../hitch-data/SKILL.md`. To
31
32
 
32
33
  - Run package commands with `pnpm`.
33
34
  - Run `pnpm exec applet login` from the workspace root or an applet directory to sign in. It uses the organization in `hitch.config.ts` and shares credentials with `hitch login`. Add `--no-browser` to print the sign-in URL without opening a browser.
34
- - A server permission error is not a login request. After an applet has a CI release, the server rejects local deployments; deploy subsequent releases through CI.
35
- - If `package.json` is absent, read `sdk` from `hitch.config.ts`. Run `pnpm dlx <sdk> bootstrap` from the workspace root.
35
+ - A server permission error is not a login request.
36
+ - After a workspace is linked to a repository, deploy in two ways. Run `pnpm exec applet deploy` to release a change immediately. Push to the production branch to deploy through CI and keep releases in sync with the repository.
36
37
  - Run `pnpm exec applet new <code>` from the workspace root to create a full applet.
37
38
  - Add `--client-only` or `--server-only` when the applet needs only one part.
38
39
  - Use `--client-only` for a user interface that uses the current user's Hitch access and needs no server.
@@ -46,16 +47,21 @@ For attribute types, filters, and API limits, read `../hitch-data/SKILL.md`. To
46
47
  - Run `pnpm --dir applets/<code> run generate` after a Hitch object, operation, or theme changes.
47
48
  - Run generation at the start of a session when the task depends on current Hitch types.
48
49
  - Run `pnpm --dir applets/<code> run dev` and `pnpm --dir applets/<code> run build` from the workspace root.
50
+ - The dev server runs as you in the production org. Reads and writes use real data.
51
+ - The dev server prints a preview link. Open the link to see the local applet inside Hitch.
52
+ - Chrome asks once to allow local network access for the Hitch site.
53
+ - The preview lasts until you select Stop preview or close the tab.
54
+ - A pending `requiresChangeset` schema is not available in dev. Deploy to test it.
49
55
  - Run `pnpm --dir applets/<code> run deploy` only when the user requests a deployment.
50
- - Edit only `hitch.config.ts`, `applet.manifest.ts`, and files in `src`.
51
- - Treat all other workspace files as generated output. The applet CLI replaces them.
56
+ - Edit only `hitch.config.ts`, `applet.manifest.ts`, files in `src`, and `.gitignore` rules below the generated block.
57
+ - Treat all other workspace files as generated output. The applet CLI replaces them. It creates `.github/workflows/hitch.yml` only when the file is absent. To stop it, set `githubWorkflow: false` in `hitch.config.ts` and delete the file.
52
58
  - Remember that generation refreshes the local theme copy. Deployed applets load the public org theme from its Hitch `.com` URL.
53
59
 
54
60
  ## Use Vite and Vitest
55
61
 
56
62
  - Import configuration APIs from `vite`.
57
63
  - Import test APIs from `vitest`.
58
- - Keep Vite configuration in the generated `vite.config.ts` file.
64
+ - Do not edit `vite.config.ts`. The applet CLI rewrites it. Do not add another Vite config.
59
65
  - Add applet packages to `dependencies` or `devDependencies` in `applet.manifest.ts`.
60
66
 
61
67
  ## Access Hitch data
@@ -123,7 +129,7 @@ Read `references/files.md` before download links, attachment attributes, or serv
123
129
  - Add useful `console.<level>` calls around important server work and failures. Deployed logs often provide the clearest evidence for applet issues.
124
130
  - Choose `console.log`, `console.info`, `console.warn`, or `console.error` deliberately. The console method sets the entry level used by log filters.
125
131
  - Run `pnpm --dir applets/<code> exec applet logs --filter '{"type":"applet"}'` from the workspace root to inspect applet code logs.
126
- - Include the applet type filter during diagnosis. Without it, Cloudflare invocation entries can hide the useful applet entries.
132
+ - Include the applet type filter during diagnosis. Without it, Cloudflare invocation entries can hide the useful applet entries. For a workflow run, use `--run <id>` or `--filter '{"type":"workflow"}'` instead.
127
133
  - Do not log secrets, tokens, or personal data.
128
134
 
129
135
  ## Verify changes
@@ -144,7 +150,7 @@ If a command fails, report the exact blocker. Do not deploy as a verification st
144
150
  ## Deploy from GitHub
145
151
 
146
152
  - A push to the production branch runs `applet deploy` in the workspace root through GitHub Actions.
147
- - An applet deploys when its own folder changed since its current CI release. Every applet deploys when a file outside `applets/` changed. An applet without a CI release always deploys.
153
+ - An applet deploys when its own folder changed since its current CI release. Every applet deploys when a file outside `applets/` changed. An applet without a CI release always deploys. An applet also deploys when CI cannot load the commit of its current release.
148
154
  - The deploy skips other applets and logs the reason. A skip is expected, not a failure.
149
155
  - A schema change in Hitch alone does not trigger CI.
150
156
  - Do not import from a sibling applet folder. The change check does not follow such imports.
@@ -0,0 +1,49 @@
1
+ # Email tracking
2
+
3
+ Create one pixel for each email. Put its URL in the HTML before send.
4
+ Add the pixel next to CID images. A CID image cannot make a tracking request.
5
+
6
+ ```ts
7
+ const { id, url } = await hitch.email.tracking.createPixel({
8
+ tags: { campaignId: "fall", buyerId: "acme", version: "v2" },
9
+ });
10
+ const html = `<p>Hello</p><img src="${url}" width="1" height="1" alt="">`;
11
+ // Send the email with the applet's email provider.
12
+ await hitch.email.tracking.markSent({ id });
13
+ ```
14
+
15
+ `markSent({ id, sentAt })` accepts an ISO time. A later call replaces the sent time.
16
+ `get({ id })` returns one tracker. `list()` returns up to 100 trackers by default.
17
+ Use `filter`, `sentAt`, `cursor`, and `limit` to select a page.
18
+
19
+ ```ts
20
+ const tracker = await hitch.email.tracking.get({ id });
21
+ const query = {
22
+ filter: { buyerId: "acme" },
23
+ sentAt: { $gte: "2026-10-01T00:00:00Z" },
24
+ limit: 100,
25
+ };
26
+ const page = await hitch.email.tracking.list(query);
27
+ const next = page.nextCursor
28
+ ? await hitch.email.tracking.list({ ...query, cursor: page.nextCursor })
29
+ : null;
30
+ const opened =
31
+ tracker.lastHitAt !== null &&
32
+ new Date(tracker.lastHitAt).getTime() >
33
+ new Date(tracker.sentAt ?? tracker.createdAt).getTime() + 60_000;
34
+ ```
35
+
36
+ The example rule counts a tracker as opened when `lastHitAt` is more than 60 seconds after send or creation.
37
+ Hitch records `firstHitAt` and `lastHitAt`. It does not classify opens.
38
+ Each page must repeat the same filter, `sentAt`, and limit.
39
+
40
+ Tags stay fixed after creation. Use at most 10 tags. A key starts with an ASCII letter.
41
+ The rest contains ASCII letters, digits, or underscores, up to 64 characters total.
42
+ Each value has 1 to 200 characters. A filter matches every tag by exact equality.
43
+ Everyone with applet access sees all its trackers.
44
+
45
+ Gmail can fetch through `GoogleImageProxy` a few seconds after send.
46
+ A real Gmail open has the same user agent. A Gmail draft preview can fetch before send.
47
+ Apple Mail Privacy Protection fetches on delivery with `Mozilla/5.0`.
48
+ That fetch looks like a real open. Proton fetches on delivery.
49
+ Clients with images off never fetch the pixel.
@@ -67,7 +67,7 @@ Use `download` when a component needs one fresh link without query state:
67
67
 
68
68
  ```ts
69
69
  const file = await hitch.files.download(id);
70
- file.downloadUrl; // a download link, valid for one hour
70
+ file.downloadUrl; // a download link, valid for at least a day
71
71
  ```
72
72
 
73
73
  Use the `get` query utilities when React needs file metadata as query state.
@@ -86,11 +86,11 @@ if (fileQuery.data) {
86
86
  row.filename; // "report.csv"
87
87
  row.contentType; // "text/csv"
88
88
  row.sizeBytes; // 1024
89
- downloadUrl; // a download link, valid for one hour
89
+ downloadUrl; // a download link, valid for at least a day
90
90
  }
91
91
  ```
92
92
 
93
- A `downloadUrl` expires after one hour. Call `get` again for a fresh link instead of storing one.
93
+ A `downloadUrl` expires after 24 to 48 hours. Call `get` again for a fresh link instead of storing one.
94
94
 
95
95
  ## Link a file to a record
96
96
 
@@ -92,14 +92,16 @@ Each object entry has five required fields.
92
92
 
93
93
  The object declaration caps record access for the applet identity.
94
94
 
95
- - Declare only the objects that scheduled applet work needs.
96
- - Declare only the attributes that scheduled applet work needs.
95
+ - Declare only the objects that the applet uses.
96
+ - Declare only the attributes that the applet uses.
97
+ - The declaration sets both the generated contract and the applet identity's cap.
97
98
  - Use `readOnly` for attributes that the applet identity must not change.
98
99
  - Use `readWrite` for attributes that the applet identity can change.
99
100
  - Set `canCreate` and `canDelete` to the required values.
100
101
  - The generated contract omits undeclared objects.
101
102
  - The generated contract removes `create` when `canCreate` is false.
102
103
  - The generated contract removes `delete` when `canDelete` is false.
104
+ - The generated contract removes `update` and `updateWhere` when `readWrite` is empty.
103
105
  - Regenerate after an object grant changes.
104
106
  - Do not restore a removed object or method with a cast.
105
107
 
@@ -33,7 +33,7 @@ Never call `get` once per row of a list. Use one of these instead:
33
33
  ## Page every list
34
34
 
35
35
  - Every list request returns at most 200 records. A larger `limit` is clamped to 200 with no error.
36
- - Show a page size or a bounded scroll area in the product, with a next-page control that passes the returned `cursor`.
36
+ - Show a page size or a bounded scroll area in the product, with a next-page control that passes the returned `next_cursor` as `cursor`.
37
37
  - Loop the cursor only when the code must visit the whole dataset. Name the bound in a comment, and stop at it.
38
38
  - Do not write a whole-dataset loop for a number that `aggregate` can return.
39
39
 
@@ -97,7 +97,7 @@ const page = await hitch.records.deals.query({
97
97
  - Use a direct field value as shorthand for `$eq`.
98
98
  - Use `$eq`, `$in`, or `$not_empty` for identity fields.
99
99
  - Use `$eq` or `$not_empty` for checkbox fields.
100
- - Use only `$not_empty` for attachment fields.
100
+ - Use `$eq`, `$in`, or `$not_empty` for attachment fields. Pass file IDs.
101
101
  - Use `$eq`, `$in`, `$not_empty`, `$contains`, `$starts_with`, or `$ends_with` for text fields.
102
102
  - Use `$eq`, `$in`, `$not_empty`, `$lt`, `$lte`, `$gt`, or `$gte` for ordered fields.
103
103
  - Use `$contains`, `$starts_with`, `$ends_with`, `$lt`, `$lte`, `$gt`, `$gte`, and `sort` only on an attribute with the `indexed` flag. Read `../../hitch-data/references/limits.md`.
@@ -106,11 +106,11 @@ const page = await hitch.records.deals.query({
106
106
  - Pass a string to `$contains`, `$starts_with`, and `$ends_with`.
107
107
  - Sort with `{ attribute, direction }`.
108
108
  - Use `asc` or `desc` as the sort direction.
109
- - Do not sort by multi-value, reference, user, or attachment attributes.
109
+ - Do not sort by multi-value, reference, user, attachment, or rich text attributes.
110
110
  - Omit `sort` to sort by `createdAt` in descending order.
111
111
  - Use a positive integer for `limit`.
112
112
  - The service caps `limit` at 200 and uses 50 by default.
113
- - Pass the returned pagination cursor back as `cursor`.
113
+ - Pass the returned `next_cursor` back as `cursor`. A null `next_cursor` means the last page.
114
114
  - Pass `include` as one string. Use the include name, which is the code without its `_id` or `_ids` suffix. Separate several names with commas.
115
115
 
116
116
  ## Aggregate records
@@ -148,6 +148,7 @@ The generated contract contains only objects declared in the manifest.
148
148
 
149
149
  - The contract removes `create` when `canCreate` is false.
150
150
  - The contract removes `delete` when `canDelete` is false.
151
+ - The contract removes `update` and `updateWhere` when `readWrite` is empty.
151
152
  - Treat a missing method as a missing manifest grant, and edit the manifest when required.
152
153
  - Run generation after a manifest or Hitch schema change.
153
154
  - Do not bypass the contract with a cast.
@@ -2,6 +2,12 @@
2
2
 
3
3
  Use a schedule to call an applet server procedure from a cron expression.
4
4
 
5
+ ## Local dev
6
+
7
+ The dev server does not run cron.
8
+ Run `pnpm --dir applets/<code> exec applet schedules run <schedule>` from the workspace root while `pnpm --dir applets/<code> run dev` runs.
9
+ The run acts as you, not as the applet. The run writes real data.
10
+
5
11
  ## Define the target procedure
6
12
 
7
13
  - Add the target to the applet's server router.
@@ -44,7 +50,7 @@ schedules: [
44
50
  - The five fields are minute, hour, day of month, month, and day of week.
45
51
  - The registered router type checks the path after type generation.
46
52
  - A nested procedure uses its nested router path.
47
- - The scheduler invokes the named router procedure with no input.
53
+ - The scheduler invokes the named router procedure with the input `{}`. Accept an empty object or declare no input.
48
54
 
49
55
  ## Understand the run identity
50
56
 
@@ -82,5 +88,4 @@ const uploaded = await hitch.files.upload(file);
82
88
  - Read the stored status, path, HTTP status, and error details.
83
89
  - Check the applet logs for procedure details.
84
90
  - Follow the log commands in the main Hitch applet skill.
85
- - Include `--level log` during diagnosis.
86
91
  - Do not log secrets, tokens, or personal data.
@@ -29,9 +29,6 @@ export default { router };
29
29
  ```
30
30
 
31
31
  - Nest procedure objects when the applet needs nested paths.
32
- - Do not name a top-level procedure `hitch`.
33
- - The client reserves `hitch` for the Hitch data client.
34
- - The generated API client type rejects a router with that reserved key.
35
32
 
36
33
  ## Call the router from the client
37
34
 
@@ -2,6 +2,15 @@
2
2
 
3
3
  Use a workflow for server work that runs in steps, retries a step, or waits between steps.
4
4
 
5
+ ## Local dev
6
+
7
+ In `pnpm --dir applets/<code> run dev`, workflows run in the dev server as you. The server keeps runs in memory.
8
+ Runs disappear when the dev server stops.
9
+ In local dev, a step fails when it runs longer than 5 minutes. A deployed step can run for 15 minutes.
10
+ Set `HITCH_DEV_SKIP_SLEEP=1` in the shell that runs `pnpm --dir applets/<code> run dev` to skip sleeps and step retry delays.
11
+ A `.env` file does not set this value for the dev server.
12
+ With this value, a polling loop that sleeps between checks calls Hitch without pause.
13
+
5
14
  ## Define a workflow
6
15
 
7
16
  - Import `createWorkflow` from `@hitch42/applet/server`.
@@ -63,11 +72,10 @@ export default { router, workflows };
63
72
  - Each step has the same limits as a scheduled run: 30,000 milliseconds of CPU and 1,000 subrequests.
64
73
  - A step that still fails after its retries throws inside the workflow function. Catch it to run a compensating step. An error you do not catch fails the run.
65
74
  - A run keeps the release it started on. A new release does not stop or change a run that is in progress or asleep.
66
- - An owner or admin can terminate an open run from the applet settings page. A terminated run has the status `terminated` and does not resume.
75
+ - The owner or a member with the `applets:manage` capability can terminate an open run from the applet settings page. A terminated run has the status `terminated` and does not resume.
67
76
 
68
77
  ## Diagnose a failed run
69
78
 
70
79
  - Call `get(id)` and read `error`.
71
80
  - Workflow step output is in the applet logs with the type `workflow`, the run id, and the workflow code.
72
- - Run `applet logs <code> --run <id>` to see one run.
73
- - Include `--level log` during diagnosis.
81
+ - Run `pnpm --dir applets/<code> exec applet logs --run <id>` from the workspace root to see one run.
@@ -16,8 +16,8 @@ The `hitch` command talks to one organization on one Hitch instance. Its data an
16
16
  - Run `hitch login --api-key` to store an API key instead. The command prompts for the key. Pass `--api-key <key>` to skip the prompt.
17
17
  - Set `HITCH_API_KEY` to use a key for one shell session. It overrides every stored login. While it is set, `hitch login`, `applet login`, and `hitch logout` refuse to run.
18
18
  - Run `hitch whoami` to print the current person, email, organization name, and organization id as JSON.
19
- - Run `hitch logout` to remove every stored login and cached command set.
20
- - Set `HITCH_API_URL` or pass `--instance <url>` to `hitch login` for an instance other than the default.
19
+ - Run `hitch logout` to remove the stored logins and cached command sets for the current instance.
20
+ - Set `HITCH_API_URL` for an instance other than the default. `hitch login --instance <url>` affects only that login. Set `HITCH_API_URL` too, so later commands use the same instance.
21
21
 
22
22
  ## Select the organization
23
23
 
@@ -31,7 +31,7 @@ There is no remembered active organization. When `--org` and the workspace disag
31
31
 
32
32
  ## Refresh the commands
33
33
 
34
- - Run `hitch sync` after a sign-in, after a schema change, and whenever `hitch --help` lacks an object. The command downloads the organization's API and caches the generated commands. It prints `Synced 3 objects.`
34
+ - `hitch login` syncs the commands. Run `hitch sync` after a schema change, and whenever `hitch --help` lacks an object. The command downloads the organization's API and caches the generated commands. It prints `Synced 3 objects.`
35
35
  - If the CLI prints `the cached commands ... no longer load — run hitch sync`, run `hitch sync`. Until then only the built-in commands work.
36
36
  - The `admin` commands appear only when the signed-in identity holds an admin capability. A missing `admin` command means no capability, not a broken CLI.
37
37
 
@@ -41,11 +41,11 @@ Every generated command follows the API route.
41
41
 
42
42
  - Data commands: `hitch <object> <verb>`. The object is its code, for example `deals`. The verbs are `list`, `get`, `create`, `update`, `delete`, `query`, `update-where`, and `aggregate`. `hitch batch` sends a batch.
43
43
  - Admin commands: `hitch admin <route segments> <verb>`. The verb comes from the HTTP method: `list` or `get` for GET, `create` for POST, `update` for PATCH, `set` for PUT, `delete` for DELETE. For example `PUT /admin/v1/teams/{id}/members/{userId}` is `hitch admin teams members set`.
44
- - Every path, query, and body field becomes a flag in kebab case: `--id`, `--team-id`, `--can-read`. An object or array field takes JSON: `--filter '{"stage":"won"}'`.
44
+ - Every path, query, and body field becomes a flag in kebab case: `--id`, `--team-id`, `--can-read`. An object field takes JSON: `--filter '{"stage":"won"}'`. An array field takes one JSON value per item: `--aggregates '{"op":"sum","attribute":"amount","as":"total"}' '{"op":"count","as":"n"}'`.
45
45
  - A required field is a required flag. The CLI does not prompt for a missing value. It prints the usage error.
46
46
  - Run `hitch <object> <verb> --help` to see the flags for that organization's schema.
47
47
  - Every `delete` command asks for confirmation. Pass `--yes` to skip it. A script without a terminal must pass `--yes`.
48
- - A command prints its result as JSON. A list prints one item per line. A command with no result prints nothing.
48
+ - A command prints its result as JSON. A list prints one JSON object with `data` and `next_cursor`. A command with no result prints nothing.
49
49
  - An error prints `hitch: <message>` and exits with code 1.
50
50
 
51
51
  ## Work with records
@@ -60,8 +60,8 @@ hitch deals update --id 0192f1a2-3b4c-7d5e-8f60-123456789abc --stage won
60
60
  hitch deals delete --id 0192f1a2-3b4c-7d5e-8f60-123456789abc --yes
61
61
  hitch deals query --filter '{"stage":{"$in":["open","won"]}}' --sort '{"attribute":"createdAt","direction":"desc"}' --limit 50
62
62
  hitch deals update-where --filter '{"stage":"open"}' --set '{"stage":"lost"}' --expect 3
63
- hitch deals aggregate --aggregates '[{"op":"sum","attribute":"amount","as":"total"}]' --group-by '[{"attribute":"stage"}]'
64
- hitch batch --requests '[{"method":"POST","path":"/v1/deals","body":{"name":"A","stage":"open"}}]'
63
+ hitch deals aggregate --aggregates '{"op":"sum","attribute":"amount","as":"total"}' --group-by '[{"attribute":"stage"}]'
64
+ hitch batch --requests '{"method":"POST","path":"/v1/deals","body":{"name":"A","stage":"open"}}'
65
65
  ```
66
66
 
67
67
  - The filter, sort, include, aggregate, and batch bodies use the same shapes as the SDK. Read the `hitch-data` skill for the rules and limits.
@@ -107,7 +107,7 @@ hitch admin changesets update --id <changesetId> --adopt <otherChangesetId>
107
107
 
108
108
  - A schema command without `--changeset-id` applies at once when it has no blockers. With a blocker, Hitch stages it in a new pending changeset and names it in the error.
109
109
  - Add `--dry-run` to evaluate a schema command without a change.
110
- - Add `--indexed` to an attribute that a query must sort or filter with ordered or text operators. Add `--is-multi` for a multi-value attribute. Pass `--type-config` and `--options` as JSON.
110
+ - Add `--indexed` to an attribute that a query must sort or filter with ordered or text operators. Add `--is-multi` for a multi-value attribute. Pass `--type-config` as JSON. Pass `--options` as one JSON value per option.
111
111
  - Run `blockers list` before an apply. Any blocker stops the apply with HTTP 409.
112
112
  - Run `hitch sync` after an apply, so the new objects and attributes appear as commands.
113
113
  - Run `pnpm --dir applets/<code> run generate` after an apply, so applet types match.
@@ -115,15 +115,15 @@ hitch admin changesets update --id <changesetId> --adopt <otherChangesetId>
115
115
 
116
116
  ## Fix common errors
117
117
 
118
- | Message | Cause and fix |
119
- | ---------------------------------------------------- | ---------------------------------------------------------------------------------------- |
120
- | `Not logged in. Run hitch login.` | No stored login and no `HITCH_API_KEY`. Sign in. |
121
- | `HITCH_API_KEY was rejected.` | The key in the environment is invalid or revoked. Unset it or replace it. |
122
- | `API key is scoped to organization "x", not "y".` | The key belongs to another organization. Select `x` or use another key. |
123
- | `--org "x" does not match workspace organization` | Run the command outside the workspace, or drop `--org`. |
124
- | `Multiple organizations are stored ...` | Pass `--org`, use the workspace `hitch.config.ts`, or set `HITCH_DEFAULT_ORG`. |
125
- | `the cached commands ... no longer load` | Run `hitch sync`. |
126
- | `Refusing to delete without --yes` | Add `--yes` in a script. |
127
- | `hitch <object>` is an unknown command | Run `hitch sync`. The object is new or the cache is stale. |
128
- | `User's teams lack the `read` grant on this object.` | The identity has no grant on that object. Ask for a team grant, or use another identity. |
129
- | `hitch admin` is missing | The identity holds no admin capability. |
118
+ | Message | Cause and fix |
119
+ | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
120
+ | `Not logged in. Run hitch login.` | No stored login and no `HITCH_API_KEY`. Sign in. |
121
+ | `HITCH_API_KEY was rejected.` | The key in the environment is invalid or revoked. Unset it or replace it. |
122
+ | `API key is scoped to organization "x", not "y".` | The key belongs to another organization. Select `x` or use another key. |
123
+ | `--org "x" does not match workspace organization` | Run the command outside the workspace, or drop `--org`. |
124
+ | `Multiple organizations are stored ...` | Pass `--org`, use the workspace `hitch.config.ts`, or set `HITCH_DEFAULT_ORG`. |
125
+ | `the cached commands ... no longer load` | Run `hitch sync`. |
126
+ | `Refusing to delete without --yes` | Add `--yes` in a script. |
127
+ | `hitch <object>` is an unknown command | Run `hitch whoami`. If you are signed in, run `hitch sync`. The object is new or the cache is stale. |
128
+ | `User's teams lack the `read` grant on this object.` | The identity has no grant on that object. Ask for a team grant, or use another identity. |
129
+ | `hitch admin` is missing | The identity holds no admin capability. |
@@ -42,7 +42,7 @@ An organization's data lives in Hitch objects. Each object has attributes. Each
42
42
  - Every attribute accepts `$eq` and `$not_empty`. Every attribute except checkbox accepts `$in`.
43
43
  - Ordered operators, text-match operators, and `sort` need the `indexed` flag on the attribute. A new attribute starts with the flag off. Ask for the flag on the attributes a query needs, not on every attribute.
44
44
  - A filter can nest through a reference, through an inverse face, and can express an anti-join with `$not`.
45
- - An aggregate filter does not need the flag. Sum, average, minimum, and maximum need a single-value numeric attribute. A group dimension is a checkbox, select, reference, user, or a bucketed date or datetime.
45
+ - Aggregating or grouping by an attribute does not need the flag. An aggregate filter follows the same flag rules as a query filter. Sum, average, minimum, and maximum need a single-value numeric attribute. A group dimension is a checkbox, select, reference, user, or a bucketed date or datetime.
46
46
 
47
47
  ## Schema changes
48
48
 
@@ -8,15 +8,15 @@ Some limits clamp with no error. Some reject the request. Design for both.
8
8
 
9
9
  A page is the unit of a read. A screen shows one page and asks for the next one. A page holds at most 200 root records. An included reference has no cap on linked records, so include only small links.
10
10
 
11
- | Limit | Value | Past the limit |
12
- | ---------------------------------- | ----------- | -------------------------------- |
13
- | `limit` on `list` and `query` | 50 default | Clamped to 200, no error |
14
- | `limit` on `list` and `query`, max | 200 | Clamped to 200, no error |
15
- | Filter nesting depth | 20 | HTTP 400 |
16
- | `include` depth | 1 | HTTP 400 for a dotted path |
17
- | `include` on an inverse reference | Not allowed | HTTP 400 |
18
- | Records per included reference | No cap | All linked records return |
19
- | IDs in one `$in` array | No cap | Only 200 records return per page |
11
+ | Limit | Value | Past the limit |
12
+ | ---------------------------------- | ----------- | ----------------------------------------------------- |
13
+ | `limit` on `list` and `query` | 50 default | Clamped to 200, no error |
14
+ | `limit` on `list` and `query`, max | 200 | `list` clamps to 200; the `query` schema rejects more |
15
+ | Filter nesting depth | 20 | HTTP 400 |
16
+ | `include` depth | 1 | HTTP 400 for a dotted path |
17
+ | `include` on an inverse reference | Not allowed | HTTP 400 |
18
+ | Records per included reference | No cap | Every linked record the caller can read returns |
19
+ | IDs in one `$in` array | No cap | Only 200 records return per page |
20
20
 
21
21
  ## Aggregates
22
22
 
@@ -56,7 +56,7 @@ Advanced filters and sorting are opt-in per attribute. This keeps each record ch
56
56
  - Ordered operators, text-match operators, and `sort` need the `indexed` flag on the attribute. Without it the request fails with HTTP 400 and a message that starts with `Enable advanced filters and sort for`.
57
57
  - New attributes start with `indexed` off. Ask the organization admin to turn it on for the attributes a query needs. Do not ask for it on every attribute.
58
58
  - No attribute allows `sort` when it is multi-value, rich text, reference, user, or attachment.
59
- - An aggregate filter does not need `indexed`. Sum, average, minimum, and maximum need a single-value numeric attribute.
59
+ - Aggregating or grouping by an attribute does not need `indexed`. An aggregate filter follows the same `indexed` rules as a query filter. Sum, average, minimum, and maximum need a single-value numeric attribute.
60
60
 
61
61
  ## Grant conditions
62
62
 
@@ -0,0 +1,150 @@
1
+ ---
2
+ name: hitch-theory
3
+ description: The mental model of Hitch. Read before you design, review, or explain anything that touches Hitch data, applets, access, or query cost. Use when a decision depends on what Hitch is for, what Hitch handles versus what an applet handles, how attributes and references model data, how changesets and releases move together, or who a call acts as. Pairs with hitch-applets, which holds the build procedure.
4
+ ---
5
+
6
+ # Theory of Hitch
7
+
8
+ This skill explains how Hitch works and why. For the build procedure, read the `hitch-applets` skill.
9
+
10
+ ## 1. What Hitch is
11
+
12
+ Hitch is a platform for internal applications. It is not a CRM, a rostering product, a reporting product, or any other vertical. An organization builds its own rostering, job, or invoice applets on Hitch.
13
+
14
+ Hitch is two layers:
15
+
16
+ 1. A durable data system that the organization owns: objects, attributes, records, files, identity, and grants.
17
+ 2. A governed runtime for replaceable software: applets that Hitch builds, releases, hosts, and runs.
18
+
19
+ ## 2. What an applet is
20
+
21
+ An applet is not an app. An app is the whole machine from the old days of software: its own authentication, its own database, its own permissions, a frontend, a server, caches, a deployment pipeline, and monitoring. Every app rebuilds that machine from scratch. Every app then spends most of its life on the machine, not on the work. The organization never wanted the machine. It wanted the business logic.
22
+
23
+ AI has changed the economics of software. Code is now cheap to write and cheap to change, so software can be personal to one organization, one team, and one workflow. Hitch is built for that world. Hitch flips the model. Instead of one monolithic app designed to serve many unrelated users, an organization gets a set of many applets, completely custom and personalised to its needs. An applet is the closest thing to pure business logic: one workflow, the screens it needs, and the rules that make it correct. Hitch handles everything underneath: the data and its schema, identity, permissions, files, immutable releases, hosting, schedules, and durable workflows. The author never touches the machine. The author builds only what matters to the user.
24
+
25
+ Hitch takes the machine away for correctness and trust as much as for speed. Authentication, access control, schema validation, and release safety are the parts an agent gets wrong, and the parts an organization cannot afford to have wrong. Hitch handles them, so your organisation has the confidence it needs to fully adopt vibe coded software and move off of legacy SaaS systems.
26
+
27
+ In practice an applet is a client-only, server-only, or full-stack application that Hitch builds, releases, hosts, and runs inside the platform trust boundary. It is not a third-party plugin.
28
+
29
+ Every organization has its own tenant on Hitch. Its data, its users, its grants, and its applets all live there. When Hitch runs an applet, it runs it inside that tenant, the way AWS runs a workload inside a customer's account: the provider operates the infrastructure, but the account, the identities, and the data are the customer's. An applet sees its organization's tenant and nothing else.
30
+
31
+ Applets can be used standalone via their `.app` URL or within the Hitch shell. Standalone and embedded operation use the same identity, release, routes, and theme.
32
+
33
+ ## 3. What Hitch handles and what an applet handles
34
+
35
+ | Hitch handles | The applet handles |
36
+ | --------------------------------------------------------------- | --------------------------------------------- |
37
+ | Canonical organization records and files | One focused workflow and its presentation |
38
+ | Object and attribute schema, and references | Domain-specific UI composition |
39
+ | Validation, normalization, constraints, versions, audit | External API adapters and translation |
40
+ | Organization identity, authentication, grants | User interactions and app-specific validation |
41
+ | Immutable releases, schedules, the workflow engine, the runtime | Derived views and deliberate derived records |
42
+ | Theme delivery and platform navigation | Ephemeral client state |
43
+
44
+ ## 4. Why code and data are separate
45
+
46
+ The organization's Hitch data is the single source of truth. An applet reads and writes that data. It does not keep its own copy.
47
+
48
+ The data layer stands on its own so that every way of using it is a client of the same thing. An applet, the `hitch` command line, an MCP server for an AI agent, an API key in a script, and an import all speak to the same objects, the same schema, and the same permissions. None of them is a bridge bolted on later. Each one is generated from the organization's own schema, so it knows the organization's objects by name. Each one acts as the person or applet behind it, so it sees exactly what that caller may see. The result is that a person in a browser, an agent in a chat window, and a script in a terminal all work on the same records under the same rules, and each gets the interface that suits them. Software stops being one product with one screen and becomes your organizational data with many doors.
49
+
50
+ The boundary gives you:
51
+
52
+ - One source of truth that many applets share.
53
+ - One set of schema and integrity rules for every writer: applets, API keys, imports, and future code.
54
+ - Central permissions that applet UI code cannot weaken.
55
+ - Independent applet release and rollback with no data fork.
56
+ - Direct user-scoped reads from the browser, with no applet server proxy.
57
+ - One place for integrations: sync an external system into Hitch once, then let other applets use the same data.
58
+
59
+ Consequences:
60
+
61
+ - Do not add an applet-owned database, a parallel cache, or a copied object set. If an applet seems to need its own storage, Hitch has a gap. Raise the gap instead.
62
+ - Code rollback is not data recovery. A release switch never moves or restores records.
63
+ - When you install, upgrade, or remove an applet, the canonical data stays where it is.
64
+ - A domain rule can live in applet code when it controls one workflow. If the rule must hold for every writer, express it in the schema, so Hitch enforces it for every applet, API key, and import.
65
+ - Data normalization and UI hierarchy are separate decisions. Model distinct concepts as objects plus references when their lifecycle or permissions differ. Group them on one screen when that matches the user. Do not invent child objects to solve navigation.
66
+
67
+ ## 5. Attributes are schema
68
+
69
+ An attribute is schema, not a free JSON field. Hitch validates each value against its type and configuration, normalizes it, stores it in the record, and indexes it when asked. A value that should be a select or a reference, but is stored as free text, cannot be grouped or filtered well later.
70
+
71
+ - Types: text, rich text, integer, decimal, currency, checkbox, date, datetime, time, domain, url, email, phone, select, reference, user, attachment.
72
+ - Cardinality is configuration, not a type. Text, select, reference, user, and attachment can be multi-value. A multi-value is an ordered array.
73
+ - An empty attribute reads as null. A required multi-value attribute rejects an empty array.
74
+ - Uniqueness is a real constraint, per attribute, per organization.
75
+ - Advanced filters and sorting are opt-in per attribute with the `indexed` flag. New attributes default to off. Every attribute still supports equality and not-empty filters. Aggregating or grouping by an attribute needs no flag. Type rules still apply: a reference, user, attachment, rich text, or multi-value attribute never sorts. Do not turn the flag on for every attribute. Turn it on for the attributes a query needs.
76
+
77
+ References model relationships:
78
+
79
+ - A reference attribute names a target object. Cardinality on each side comes from the single or multi setting of each face.
80
+ - A reference can expose an inverse face on the target object. One face owns and stores the edges. The other face is derived. Write the owner face only.
81
+ - Hitch keeps the links typed, clears inbound links when a target is deleted, applies grants through the inverse, and lets you filter through either face. Do not invent a join object for a plain relationship. Use a join object only when the relationship needs its own fields or lifecycle.
82
+
83
+ Special cases:
84
+
85
+ - A user attribute points at a membership in the same organization. Removal of the member sweeps those values.
86
+ - An attachment attribute holds file IDs. Files live outside records, in their own storage. Create the file, upload it, then store the ID on the record.
87
+
88
+ ## 6. Changesets and releases move together
89
+
90
+ All schema mutations go through a changeset: an ordered list of object and attribute operations with a stored evaluation.
91
+
92
+ - A hard blocker means invalid schema or data. Examples: a unique constraint that finds duplicates, a required attribute with empty records, a tightened range with violators.
93
+ - A soft blocker means a dependency. Examples: applets, grant conditions, or delegations that depend on the object or attribute.
94
+ - Any blocker stops the apply. A submit with a blocker stages a pending changeset. The apply succeeds after the blocker is resolved.
95
+
96
+ An applet release can bind to a pending changeset. Hitch checks the manifest against the schema the changeset will produce. A mismatch is a deploy warning, not a denial. The unresolved warning becomes a hard blocker on the changeset. A bound release does not go live on deploy. When the changeset applies, the schema and the newest bound release of each applet become active together. Schema and code change together.
97
+
98
+ Adoption folds one pending changeset into another and abandons the source. The source must have no bound releases.
99
+
100
+ The applet manifest lists the objects the applet uses, with create and delete flags, read-only attributes, and read-write attributes. That list is the access declaration. It caps what the applet identity can do: the object actions, and the attributes it can update. It does not change what a user can see.
101
+
102
+ ## 7. Identity and access
103
+
104
+ Hitch enforces access control on every Hitch API call. An applet never re-implements it. A UI filter or a hidden control is convenience, never a boundary. An applet's own server procedure is applet code, so it must check the business rules it introduces. Every call carries a principal and Hitch applies that principal's grants.
105
+
106
+ Principals:
107
+
108
+ - A person, through a session or a personal API key. A personal API key inherits the member's capabilities.
109
+ - An impersonated person. The target's capabilities must be a proper subset of the caller's.
110
+ - An applet. The applet identity has no role and no bypass. Its authority is exactly the grants projected from its manifest.
111
+
112
+ The organization roles are member and owner. There is no admin role. The owner holds every capability, bypasses every grant, and cannot be removed or impersonated. Capabilities are ten named org-wide powers: manage members, teams, objects, permissions, applets, and the organization; read all records; write all records; use applets; impersonate members. A capability grant goes to a team only, never to a person or to everyone. When one person needs a capability, put them in a one-person team. A caller can grant only a capability the caller holds.
113
+
114
+ Object grants are the matrix: one row per object and grantee with create, read, update, and delete flags. A grantee is a team, an applet, or the default row that every member holds. A member's access is the union of the default row and their team rows. The read-all and write-all capabilities bypass object grants. Object grants set the verbs. Conditions narrow rows. Attribute grants restrict updates to protected attributes and applet writes. Reads are never masked per attribute.
115
+
116
+ An object grant can delegate through one single-valued owner reference. The linked record then uses the same grantee's grant on the target object.
117
+
118
+ Grant conditions scope rows to the current user. A condition path is a user attribute on the object, or one reference followed by a user attribute on the target object. A record whose user attribute is empty is invisible to that grant. An invisible record behaves as missing. A condition applies to read, update, and delete, never to create. A condition cannot compare a value, such as "where status equals X".
119
+
120
+ Who a call acts as:
121
+
122
+ - A client applet call acts as the current user. The manifest grants do not apply.
123
+ - A schedule run acts as the applet, capped by the manifest.
124
+ - A workflow run acts as whoever started it: the invoking user, or the applet when a schedule started it.
125
+
126
+ Because Hitch enforces access, a client applet needs no server proxy for filtering or access control. Add a server only for secrets, external systems, schedules, workflows, or bounded fan-in.
127
+
128
+ ## 8. What makes a good applet
129
+
130
+ A good applet:
131
+
132
+ - Solves one related workflow for one clear group of users.
133
+ - Uses the organization's existing Hitch data, not a private copy.
134
+ - Presents the smallest useful set of decisions and actions. It does not reproduce a legacy product screen for screen.
135
+ - Uses Hitch primitives before custom infrastructure.
136
+ - Feels native through semantic theme tokens, shared interaction patterns, useful empty, loading, and error states, and viewport-safe layouts.
137
+ - Has explicit access, performance, failure, and recovery behavior.
138
+ - Can be replaced or rolled back with no move or rewrite of the canonical data.
139
+
140
+ Split an applet when workflows, audiences, privileges, release cadence, or failure modes differ. Do not split because the UI has another page. A sync applet and a reporting applet are a good split. Ingestion has secrets, schedules, broad write grants, retries, and external failure modes. Reports read already-synced data with narrow grants.
141
+
142
+ ## 9. Product-design principles
143
+
144
+ - Build to user outcomes, not to the screens or tables of an older system.
145
+ - Start with the important nouns, relationships, permissions, and failure cases. Design screens after that.
146
+ - Simplify critically. Defer speculative channels and infrastructure.
147
+ - Ship the smallest complete workflow. Add nothing unrequested.
148
+ - Treat thresholds, status rules, timing, channels, and recipients as product decisions for the user.
149
+ - Confirm destructive or external writes. Show the target, the intended change, the stale-source check, and the outcome. A Hitch mutation and a mutation of an external source are not equivalent.
150
+ - Record rejected designs and the reason. Do not rebuild what was deliberately cut.
@@ -1,2 +0,0 @@
1
- import { i as runBuild } from "./build-DauirTcY.js";
2
- export { runBuild };
@@ -1,2 +0,0 @@
1
- import { n as runGenerate } from "./generate-BIifT6PK.js";
2
- export { runGenerate };