@hitch42/applet 0.1.1-beta.20 → 0.1.1-beta.21

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 (94) hide show
  1. package/dist/_internal/client.d.ts +0 -2
  2. package/dist/_internal/client.js +3 -5
  3. package/dist/_internal/log-context.d.ts +0 -2
  4. package/dist/_internal/log-context.js +1 -2
  5. package/dist/_internal/server.d.ts +2 -10
  6. package/dist/_internal/server.js +9 -14
  7. package/dist/applet.js +64 -23
  8. package/dist/auth-DuMRqSGe.js +49 -0
  9. package/dist/{batch-_ZjAg-Kk.js → batch-CiLZ177G.js} +3 -4
  10. package/dist/build-C7hi2yg4.js +1 -0
  11. package/dist/{build-D47vK5aa.js → build-FAQSHJgm.js} +4 -3
  12. package/dist/{ci-BTFTCBgy.js → ci-oWlUJKm9.js} +5 -6
  13. package/dist/client/hitch/client.d.ts +1 -1
  14. package/dist/client/hitch/client.js +1 -2
  15. package/dist/client/hitch/phone.d.ts +1 -1
  16. package/dist/client/hitch/phone.js +1 -2
  17. package/dist/client/hitch.d.ts +2 -2
  18. package/dist/client/hitch.js +2 -2
  19. package/dist/client/react/markdown-editor.d.ts +1 -1
  20. package/dist/client/react/markdown-editor.js +1 -2
  21. package/dist/client/react/markdown.d.ts +1 -1
  22. package/dist/client/react/markdown.js +1 -2
  23. package/dist/client/react.d.ts +2 -2
  24. package/dist/client/react.js +2 -2
  25. package/dist/client.d.ts +2 -4
  26. package/dist/client.js +3 -5
  27. package/dist/{contract-CJv-n1l3.d.ts → contract-CUc0B2rl.d.ts} +1 -3
  28. package/dist/{contract-BeKz1Qcs.js → contract-D9LCNGjD.js} +1 -2
  29. package/dist/{create-C2UrZYSK.d.ts → create-BJqLT8Uq.d.ts} +0 -4
  30. package/dist/{deploy-BbqyK5Ik.js → deploy-BM55ZoSp.js} +160 -152
  31. package/dist/{device-BgIfytaR.js → device-CZWWckCq.js} +54 -34
  32. package/dist/{email-protocol-B9u_dh2p.d.ts → email-protocol-CEqzASSx.d.ts} +0 -6
  33. package/dist/{generate-BLD0DoCk.js → generate-48pqFh5M.js} +31 -37
  34. package/dist/generate-BYK4aNud.js +1 -0
  35. package/dist/generated-agents-BUnJ0FQA.js +215 -0
  36. package/dist/git-credential-CU7-Erzs.js +24 -0
  37. package/dist/git-deploy-C12nMNB6.js +290 -0
  38. package/dist/git-deploy-CNz6mxiF.js +1 -0
  39. package/dist/git-migration-CIq-sN61.js +629 -0
  40. package/dist/git-safety-Ck3X7jvA.js +13 -0
  41. package/dist/{headers-CeRX-Xji.js → headers-BcEMIVCo.js} +1 -2
  42. package/dist/{hitch-client-DtyTZzP5.d.ts → hitch-client-D33JOEOi.d.ts} +5 -7
  43. package/dist/{hitch-client-Cw-WZ_8v.js → hitch-client-Dph_kzDd.js} +5 -7
  44. package/dist/{hitch-file-url-BPKj9zVG.js → hitch-file-url--AMjDOzP.js} +1 -2
  45. package/dist/index.d.ts +8 -15
  46. package/dist/index.js +32 -37
  47. package/dist/{init-BKUBf6i9.js → init-Bb6WT8UB.js} +70 -18
  48. package/dist/{lazy-client-CwIzYAoE.js → lazy-client--QCL0wiu.js} +1 -2
  49. package/dist/{load-config-DGALUExv.js → load-config-Cffvj-aM.js} +24 -26
  50. package/dist/{log-context-DWznGjfz.js → log-context-BepwdjrR.js} +1 -2
  51. package/dist/login-BWZKo7Ui.js +21 -0
  52. package/dist/{logs-Cv5z1e2w.js → logs-glCVHI-8.js} +6 -6
  53. package/dist/{manifest-BBHD_EY9.d.ts → manifest-CpghlaJc.d.ts} +6 -7
  54. package/dist/manifest-patterns-BfxqImlp.js +4 -0
  55. package/dist/{markdown-CwlqzBtP.js → markdown-BA_XzFxq.js} +2 -3
  56. package/dist/{markdown-DSHOQIOb.d.ts → markdown-BXt65dDo.d.ts} +1 -3
  57. package/dist/{markdown-editor-BmEaBzd7.d.ts → markdown-editor-CB_nWLzj.d.ts} +1 -5
  58. package/dist/{markdown-editor-C5mLHGqu.js → markdown-editor-CaUuphfc.js} +3 -5
  59. package/dist/parent-D52Kxp8U.js +1 -0
  60. package/dist/parent-Zx8Eg1Ki.js +75 -0
  61. package/dist/{project-CrxdZR0B.js → project-DZGk765E.js} +63 -54
  62. package/dist/{prompt-ChS-grYO.js → prompt-CW98R-Un.js} +1 -2
  63. package/dist/{query-cache-CXmAyLv3.js → query-cache-DQJy45RT.js} +1 -2
  64. package/dist/{query-utils-zz9pVjgp.d.ts → query-utils-okxIoKM3.d.ts} +0 -2
  65. package/dist/{schedules-Dajgni_z.js → schedules-CSUb84e2.js} +5 -5
  66. package/dist/server.d.ts +5 -13
  67. package/dist/server.js +17 -20
  68. package/dist/source-hash-BbvtRaus.js +107 -0
  69. package/dist/store-E5NhITHl.js +448 -0
  70. package/dist/{upload-D_kkmne_.d.ts → upload-DXZyjZSS.d.ts} +6 -10
  71. package/dist/{upload-DRbVhuDm.js → upload-DtMavo8Q.js} +4 -6
  72. package/dist/{use-phone-ug4B5gQp.js → use-phone-CB_idmrz.js} +9 -13
  73. package/dist/{use-phone-M7c_HsB7.d.ts → use-phone-DBAkasPq.d.ts} +0 -4
  74. package/dist/{variables-B-CLu8cY.js → variables-CgZstC7I.js} +7 -8
  75. package/dist/vite.d.ts +1 -3
  76. package/dist/vite.js +41 -50
  77. package/dist/{workflow-DurM6Fwx.d.ts → workflow-BnBrJIzu.d.ts} +0 -2
  78. package/dist/{workflow-engine-BOTzURKM.js → workflow-engine-CyyaVENo.js} +2 -4
  79. package/dist/{workflow-CTo2kZF1.js → workflow-l2SlVePB.js} +2 -4
  80. package/dist/workspace-CtIKSgMD.js +76 -0
  81. package/package.json +6 -5
  82. package/scaffold/.agents/skills/hitch-applets/SKILL.md +27 -9
  83. package/scaffold/.agents/skills/hitch-applets/references/files.md +2 -2
  84. package/scaffold/.agents/skills/hitch-applets/references/manifest.md +8 -6
  85. package/scaffold/.agents/skills/hitch-applets/references/performance.md +2 -2
  86. package/scaffold/.agents/skills/hitch-applets/references/records.md +8 -8
  87. package/scaffold/.agents/skills/hitch-cli/SKILL.md +52 -43
  88. package/scaffold/AGENTS.md +6 -0
  89. package/dist/auth-D3CrCtZq.js +0 -21
  90. package/dist/build-JtbY8q2p.js +0 -2
  91. package/dist/generate-COCRy1Bs.js +0 -2
  92. package/dist/login-CceGvlMi.js +0 -14
  93. package/dist/store-B8N_8JIN.js +0 -196
  94. package/dist/workspace-8yqXsvce.js +0 -31
@@ -50,8 +50,8 @@ await hitch.batch(
50
50
  (tx) => {
51
51
  tx.companies.create({ body: { id: companyId, name: "Acme" } });
52
52
  tx.deals.create({ body: { name: "Acme renewal", company_id: companyId } });
53
- tx.deals.update({ params: { id: dealId }, body: { stage: "won" } });
54
- tx.deals.delete({ params: { id: staleDealId } });
53
+ tx.deals.update({ params: { record: dealId }, body: { stage: "won" } });
54
+ tx.deals.delete({ params: { record: staleDealId } });
55
55
  tx.deals.updateWhere({ body: { filter: { stage: "open" }, set: { stage: "lost" } } });
56
56
  },
57
57
  { idempotencyKey: `import-${runId}-${chunkIndex}` },
@@ -15,15 +15,15 @@ Use the generated Hitch contract as the typed data plane.
15
15
 
16
16
  - `list` sends `GET /v1/<objectCode>` and lists records with query parameters.
17
17
  - `create` sends `POST /v1/<objectCode>` and creates one record or an array of records.
18
- - `get` sends `GET /v1/<objectCode>/{id}` and gets one record.
19
- - `history` sends `GET /v1/<objectCode>/{id}/history` and gets record changes.
20
- - `update` sends `PATCH /v1/<objectCode>/{id}` and updates one record.
21
- - `delete` sends `DELETE /v1/<objectCode>/{id}` and deletes one record.
18
+ - `get` sends `GET /v1/<objectCode>/{record}` and gets one record.
19
+ - `history` sends `GET /v1/<objectCode>/{record}/history` and gets record changes.
20
+ - `update` sends `PATCH /v1/<objectCode>/{record}` and updates one record.
21
+ - `delete` sends `DELETE /v1/<objectCode>/{record}` and deletes one record.
22
22
  - `query` sends `POST /v1/<objectCode>/query` and lists records with a structured body.
23
- - `updateWhere` sends `POST /v1/<objectCode>/update` and updates matching records.
23
+ - `updateWhere` sends `POST /v1/<objectCode>/bulk-update` and updates matching records.
24
24
  - `aggregate` sends `POST /v1/<objectCode>/aggregate` and calculates aggregate values.
25
25
 
26
- - Pass a record ID in `params.id` for `get`, `history`, `update`, and `delete`.
26
+ - Pass a record ID in `params.record` for `get`, `history`, `update`, and `delete`.
27
27
  - Pass record fields in `body` for `create` and `update`.
28
28
  - Pass structured query data in `body` for `query`.
29
29
 
@@ -67,7 +67,7 @@ const deals = await hitch.records.deals.query({
67
67
  });
68
68
 
69
69
  const deal = await hitch.records.deals.get({
70
- params: { id: dealId },
70
+ params: { record: dealId },
71
71
  });
72
72
  ```
73
73
 
@@ -93,7 +93,7 @@ The first record has index 0. An empty array returns an empty array and requires
93
93
 
94
94
  ```ts
95
95
  const page = await hitch.records.deals.history({
96
- params: { id },
96
+ params: { record: id },
97
97
  query: { limit: 25, cursor },
98
98
  });
99
99
 
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: hitch-cli
3
- description: Use the `hitch` command line to sign in, select an organization, and read or write Hitch data and administration from the shell. Use when a task needs to inspect records, try a filter or aggregate, create test data, manage teams, grants, or capabilities, apply a changeset, or check applet access without writing applet code.
3
+ description: Use the `hitch` command line to sign in, select an organization, and read or write Hitch data and settings from the shell. Use when a task needs to inspect records, try a filter or aggregate, create test data, manage teams, grants, or capabilities, apply a changeset, or check applet access without writing applet code.
4
4
  ---
5
5
 
6
6
  # Hitch CLI
7
7
 
8
- The `hitch` command talks to one organization on one Hitch instance. Its data and admin commands are generated from that organization's API, so the command set matches the organization's objects.
8
+ The `hitch` command talks to one organization on one Hitch instance. Its commands are generated from that organization's API, so the command set matches the organization's objects.
9
9
 
10
10
  `hitch` is separate from `applet`. Use `applet` to create, generate, build, and deploy applets. Use `hitch` to work with the organization's data and settings. Read the `hitch-applets` skill for `applet`.
11
11
 
@@ -31,84 +31,93 @@ There is no remembered active organization. When `--org` and the workspace disag
31
31
 
32
32
  ## Refresh the commands
33
33
 
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.`
34
+ - `hitch login` syncs the commands. Run `hitch sync` after a schema change, after a CLI upgrade, 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
- - 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
36
 
38
37
  ## Read the command names
39
38
 
40
- Every generated command follows the API route.
39
+ Every generated command comes from an API route. The words of the command are the words of the route path, and the verb is last.
41
40
 
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
- - 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 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"}'`.
41
+ - Data commands: `hitch <object> <verb>`. The object is its code, for example `deals`. The verbs are `list`, `get`, `create`, `update`, `delete`, `query`, `aggregate`, and `bulk-update`. `hitch <object> history get` shows the changes to one record.
42
+ - `hitch objects list`, `hitch objects get`, and `hitch objects schema get` read the objects and their attributes. `hitch batch run` sends a batch.
43
+ - `hitch resource ...` works with platform features: files, email, SMS and calls, email trackers, and the directory of teams and members.
44
+ - `hitch manage ...` changes the organization: objects and attributes, changesets, grants, teams, members, invitations, capabilities, API keys, applets, and the theme. Each command needs a capability.
45
+ - `hitch me get` shows who you are. `hitch me teams list` and `hitch me applets list` show your teams and the applets that you can open.
46
+ - The verb comes from the HTTP method: `list` or `get` for GET, `create` for POST, `update` for PATCH, `set` for PUT, `delete` for DELETE. A POST to an action uses the action as the verb: `hitch manage changesets apply`.
47
+ - Every path, query, and body field becomes a flag in kebab case. A path field names its thing: `--object`, `--record`, `--team`, `--grant`, `--changeset`. 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
48
  - A required field is a required flag. The CLI does not prompt for a missing value. It prints the usage error.
46
- - Run `hitch <object> <verb> --help` to see the flags for that organization's schema.
49
+ - Run `hitch <command> --help` to see its flags. The flags of a data command follow that organization's schema.
47
50
  - 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 JSON object with `data` and `next_cursor`. A command with no result prints nothing.
51
+ - A command prints its result as JSON. A record list prints one JSON object with `data` and `next_cursor`. A command with no result prints nothing.
49
52
  - An error prints `hitch: <message>` and exits with code 1.
50
53
 
51
54
  ## Work with records
52
55
 
53
- Examples use an object called `deals` with attributes `amount`, `stage`, and `owner_id`. Ids are UUIDs.
56
+ Examples use an object called `deals` with attributes `amount`, `stage`, and `owner_id`. Record ids are UUIDs.
54
57
 
55
58
  ```sh
56
59
  hitch deals list --limit 20 --include owner
57
- hitch deals get --id 0192f1a2-3b4c-7d5e-8f60-123456789abc --include owner
60
+ hitch deals get --record 0192f1a2-3b4c-7d5e-8f60-123456789abc --include owner
58
61
  hitch deals create --name "Acme renewal" --amount 5000 --stage open
59
- hitch deals update --id 0192f1a2-3b4c-7d5e-8f60-123456789abc --stage won
60
- hitch deals delete --id 0192f1a2-3b4c-7d5e-8f60-123456789abc --yes
62
+ hitch deals update --record 0192f1a2-3b4c-7d5e-8f60-123456789abc --stage won
63
+ hitch deals delete --record 0192f1a2-3b4c-7d5e-8f60-123456789abc --yes
64
+ hitch deals history get --record 0192f1a2-3b4c-7d5e-8f60-123456789abc
61
65
  hitch deals query --filter '{"stage":{"$in":["open","won"]}}' --sort '{"attribute":"createdAt","direction":"desc"}' --limit 50
62
- hitch deals update-where --filter '{"stage":"open"}' --set '{"stage":"lost"}' --expect 3
66
+ hitch deals bulk-update --filter '{"stage":"open"}' --set '{"stage":"lost"}' --expect 3
63
67
  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"}}'
68
+ hitch batch run --requests '{"method":"POST","path":"/v1/deals","body":{"name":"A","stage":"open"}}'
69
+ hitch objects schema get --object deals
65
70
  ```
66
71
 
67
72
  - 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.
68
73
  - A list returns one page of at most 200 records and a cursor. Pass `--cursor <value>` for the next page.
69
74
  - Data commands act as the signed-in person with that person's grants. An API key acts as the member who made it.
70
75
 
71
- ## Administer the organization
76
+ ## Manage the organization
72
77
 
73
- Admin commands need the matching capability. Examples use fake ids.
78
+ `hitch manage` commands need the matching capability. Examples use the team `sales` and the member `james@acme.com`.
74
79
 
75
80
  ```sh
76
- hitch admin teams list
77
- hitch admin teams create --name Sales
78
- hitch admin teams members set --id <teamId> --user-id <userId>
79
- hitch admin capabilities list
80
- hitch admin capabilities grants set --capability objects:manage --team-id <teamId>
81
- hitch admin objects list
82
- hitch admin objects access list --object-id <objectId>
83
- hitch admin teams object-grants set --team-id <teamId> --object-id <objectId> --can-create --can-read --can-update --no-can-delete
84
- hitch admin teams object-grants conditions create --team-id <teamId> --object-id <objectId> --operator equals --operand current_user --path <attributeId>
85
- hitch admin objects default-grant set --object-id <objectId> --can-read --no-can-create --no-can-update --no-can-delete
86
- hitch admin applets access list --code sales_portal
87
- hitch admin applets grants set --code sales_portal --team-id <teamId>
81
+ hitch resource teams list
82
+ hitch manage teams create --name Sales
83
+ hitch manage teams members set --team sales --member james@acme.com
84
+ hitch manage invitations create --email bob@acme.com
85
+ hitch manage capabilities list
86
+ hitch manage capabilities grants set --capability objects:manage --grant sales
87
+ hitch manage objects access get --object deals
88
+ hitch manage objects grants set --object deals --grant sales --can-create --can-read --can-update --conditions '{"path":["owner"],"operator":"equals","operand":"current_user"}'
89
+ hitch manage objects grants set --object deals --grant default --can-read
90
+ hitch manage objects grants delete --object deals --grant sales --yes
91
+ hitch manage applets access get --applet sales_portal
92
+ hitch manage applets grants set --applet sales_portal --grant sales
88
93
  ```
89
94
 
90
- - Object grants for a team live under `admin teams object-grants`. The default grant that every member holds lives under `admin objects default-grant`. Each has `conditions` and `attributes` subcommands.
91
- - Add `--delegate-attribute-id <attributeId>` to an object grant to delegate through a reference attribute.
92
- - `admin objects access list` shows which grantees cannot read an object. `admin applets access list` shows which grantees cannot use what an applet reads.
95
+ - Paths name things by key, not by id. `--team` and `--grant` take a team code. `--member` and `--invitation` take an email. `--api-key` takes the key name, for example `CI_DEPLOY`. `--object` and `--attribute` take a code. `--mailbox` takes the mailbox address.
96
+ - A team code is the team name folded to ASCII and lower case, with each run of other characters as one `_`. `Sales Team` has the code `sales_team`. A rename changes the code.
97
+ - `manage objects grants set` sets the whole object grant of one grantee: a verb that you leave out is off, and the conditions and attribute rules that you pass replace the saved ones. `--grant` is a team code, or `default` for the grant that every member holds.
98
+ - Pass each condition and each attribute rule as one JSON value, for example `--attributes '{"attribute":"owner","canUpdate":true}'`. Name attributes by code.
99
+ - Add `--delegate '"company"'` to delegate through a reference attribute. The value is the attribute code as a JSON string.
100
+ - `manage objects access get` shows each object grant in the shape that `manage objects grants set` takes. `manage applets access get` shows which grantees cannot use what an applet reads.
93
101
 
94
102
  ## Change the schema
95
103
 
96
- Schema changes go through a changeset. The CLI names the lifecycle as state, not as actions.
104
+ Schema changes go through a changeset.
97
105
 
98
106
  ```sh
99
- hitch admin changesets create
100
- hitch admin objects create --name Deal --changeset-id <changesetId>
101
- hitch admin objects attributes create --code deals --name Amount --type currency --changeset-id <changesetId>
102
- hitch admin changesets blockers list --id <changesetId>
103
- hitch admin changesets update --id <changesetId> --status applied
104
- hitch admin changesets update --id <changesetId> --status abandoned
105
- hitch admin changesets update --id <changesetId> --adopt <otherChangesetId>
107
+ hitch manage changesets create
108
+ hitch manage objects create --name Deals --changeset-id <changesetId>
109
+ hitch manage objects attributes create --object deals --name Amount --type currency --changeset-id <changesetId>
110
+ hitch manage changesets check --changeset <changesetId>
111
+ hitch manage changesets apply --changeset <changesetId>
112
+ hitch manage changesets merge --changeset <changesetId> --from <otherChangesetId>
113
+ hitch manage changesets abandon --changeset <changesetId>
106
114
  ```
107
115
 
108
116
  - 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
117
  - Add `--dry-run` to evaluate a schema command without a change.
110
118
  - 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
- - Run `blockers list` before an apply. Any blocker stops the apply with HTTP 409.
119
+ - Run `changesets check` before an apply. It shows what blocks the apply. Any blocker stops the apply with HTTP 409.
120
+ - `changesets merge` moves the changes of `--from` into the changeset, after its own changes, and abandons `--from`.
112
121
  - Run `hitch sync` after an apply, so the new objects and attributes appear as commands.
113
122
  - Run `pnpm --dir applets/<code> run generate` after an apply, so applet types match.
114
123
  - To build applet code before the apply, set `requiresChangeset` in `applet.manifest.ts` to the changeset ID. Generate, build, and deploy then use the changeset schema, and the release goes live when the changeset applies.
@@ -126,4 +135,4 @@ hitch admin changesets update --id <changesetId> --adopt <otherChangesetId>
126
135
  | `Refusing to delete without --yes` | Add `--yes` in a script. |
127
136
  | `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
137
  | `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. |
138
+ | `This endpoint requires the x capability.` | The identity does not hold capability `x`. Ask for a capability grant to one of your teams. |
@@ -4,6 +4,12 @@ Before any command or file edit, read and follow `.agents/skills/hitch-applets/S
4
4
 
5
5
  Use `pnpm` for package commands.
6
6
 
7
+ With Hitch git on, each applet repo has `applet.manifest.ts` and `src/` at its root.
8
+ The manifest contains `org` and an exact `sdk` version, such as `"0.2.0"`.
9
+ A `.hitchroot` parent holds child applet repos. Run commands in a child for that applet,
10
+ or deploy and bootstrap in the parent for all children. Commit per child.
11
+ Legacy workspaces use `hitch.config.ts` and `applets/<code>/`.
12
+
7
13
  The applet CLI refreshes its generated support files.
8
14
  The CLI adds missing MCP servers without changing existing entries.
9
15
 
@@ -1,21 +0,0 @@
1
- import { a as organizationInstanceUrl, c as resolveInstanceUrl, i as getCredentialsForOrg, o as rejectEnvironmentKey, r as bearerToken, s as resolveAuthForOrg, t as AuthenticationError } from "./store-B8N_8JIN.js";
2
- import { t as loginWithDevice } from "./device-BgIfytaR.js";
3
- import "./workspace-8yqXsvce.js";
4
- //#region src/auth.ts
5
- async function fetchWithOrgAuth(orgCode, request) {
6
- const instanceUrl = await resolveInstanceUrl();
7
- const organizationUrl = organizationInstanceUrl(instanceUrl, orgCode);
8
- const auth = await resolveAuthForOrg(instanceUrl, orgCode);
9
- if (auth !== void 0) {
10
- const response = await request(organizationUrl, bearerToken(auth));
11
- rejectEnvironmentKey(response, auth);
12
- if (response.status !== 401) return response;
13
- }
14
- if (process.stdin.isTTY !== true) throw new AuthenticationError("Not logged in. Run applet login, or set HITCH_API_KEY.");
15
- await loginWithDevice(instanceUrl, { expectedOrg: orgCode });
16
- const credentials = await getCredentialsForOrg(instanceUrl, orgCode);
17
- if (credentials === void 0) throw new Error(`login did not provide credentials for organization "${orgCode}"`);
18
- return request(organizationUrl, bearerToken(credentials));
19
- }
20
- //#endregion
21
- export { fetchWithOrgAuth as t };
@@ -1,2 +0,0 @@
1
- import { i as runBuild } from "./build-D47vK5aa.js";
2
- export { runBuild };
@@ -1,2 +0,0 @@
1
- import { r as runGenerate } from "./generate-BLD0DoCk.js";
2
- export { runGenerate };
@@ -1,14 +0,0 @@
1
- import { c as resolveInstanceUrl, t as AuthenticationError } from "./store-B8N_8JIN.js";
2
- import { t as loginWithDevice } from "./device-BgIfytaR.js";
3
- import { t as findWorkspaceConfig } from "./workspace-8yqXsvce.js";
4
- //#region src/login.ts
5
- async function runLogin(options) {
6
- if (process.env.HITCH_API_KEY !== void 0) throw new AuthenticationError("HITCH_API_KEY is set — unset it to manage stored logins.");
7
- const { me, membership } = await loginWithDevice(await resolveInstanceUrl(), {
8
- expectedOrg: findWorkspaceConfig(process.cwd())?.org,
9
- noBrowser: options.noBrowser
10
- });
11
- console.log(`Signed in as ${me.person.email} to ${membership.name}.`);
12
- }
13
- //#endregion
14
- export { runLogin };
@@ -1,196 +0,0 @@
1
- import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
2
- import { join } from "node:path";
3
- import { homedir } from "node:os";
4
- //#region ../shared/auth/src/store.mts
5
- const DEFAULT_INSTANCE_URL = "https://app.hitch42.com";
6
- var AuthenticationError = class extends Error {};
7
- function isErrno(error, code) {
8
- return error instanceof Error && "code" in error && error.code === code;
9
- }
10
- async function readJson(path) {
11
- try {
12
- return JSON.parse(await readFile(path, "utf8"));
13
- } catch (error) {
14
- if (isErrno(error, "ENOENT")) return void 0;
15
- throw new Error(`Failed to read ${path}.`, { cause: error });
16
- }
17
- }
18
- function isObject(value) {
19
- return value !== null && typeof value === "object" && !Array.isArray(value);
20
- }
21
- function emptyCredentialStore() {
22
- return { instances: {} };
23
- }
24
- async function healCredentialStore(credentialsPath, source, store, unrecognizedCount) {
25
- const backupPath = `${credentialsPath}.bak`;
26
- try {
27
- try {
28
- await writeFile(backupPath, source, {
29
- flag: "wx",
30
- mode: 384
31
- });
32
- } catch (error) {
33
- if (!isErrno(error, "EEXIST")) throw error;
34
- }
35
- await chmod(backupPath, 384);
36
- await writeFile(credentialsPath, `${JSON.stringify(store, null, 2)}\n`, { mode: 384 });
37
- await chmod(credentialsPath, 384);
38
- } catch {
39
- console.error(unrecognizedCount === void 0 ? `hitch: failed to heal credential store at ${credentialsPath}; using an empty in-memory store.` : `hitch: failed to heal credential store at ${credentialsPath}; using recognized credentials in memory only.`);
40
- return store;
41
- }
42
- console.error(unrecognizedCount === void 0 ? `hitch: reset unreadable credentials file (backup: ${backupPath}).` : unrecognizedCount === 0 ? `hitch: migrated credentials to per-organization storage (backup: ${backupPath}).` : `hitch: removed ${unrecognizedCount} unrecognized credential ${unrecognizedCount === 1 ? "entry" : "entries"} (backup: ${backupPath}).`);
43
- return store;
44
- }
45
- async function readRawCredentialStore() {
46
- const { credentialsPath } = storePaths();
47
- let source;
48
- try {
49
- source = await readFile(credentialsPath, "utf8");
50
- } catch (error) {
51
- if (isErrno(error, "ENOENT")) return emptyCredentialStore();
52
- throw new Error(`Failed to read ${credentialsPath}.`, { cause: error });
53
- }
54
- let value;
55
- try {
56
- value = JSON.parse(source);
57
- } catch {
58
- return healCredentialStore(credentialsPath, source, emptyCredentialStore());
59
- }
60
- if (!isObject(value) || !isObject(value.instances)) return healCredentialStore(credentialsPath, source, emptyCredentialStore());
61
- const instances = Object.fromEntries(Object.entries(value.instances).flatMap(([instanceUrl, entry]) => {
62
- if (isObject(entry) && typeof entry.organizationId === "string") {
63
- const credentials = parseCredentials(entry);
64
- return credentials === void 0 ? [] : [[instanceUrl, { [credentials.orgCode]: entry }]];
65
- }
66
- if (!isObject(entry)) return [];
67
- return [[instanceUrl, Object.fromEntries(Object.entries(entry).filter(([orgCode, credentials]) => parseCredentials(credentials)?.orgCode === orgCode))]];
68
- }));
69
- const reshaped = Object.values(value.instances).some((entry) => !isObject(entry) || typeof entry.organizationId === "string");
70
- const countLeaves = (store) => Object.values(store).reduce((total, entry) => total + (isObject(entry) && typeof entry.organizationId !== "string" ? Object.keys(entry).length : 1), 0);
71
- const unrecognizedCount = countLeaves(value.instances) - countLeaves(instances);
72
- if (!reshaped && unrecognizedCount === 0) return value;
73
- return healCredentialStore(credentialsPath, source, { instances }, unrecognizedCount);
74
- }
75
- function parseCredentials(value) {
76
- if (!isObject(value) || typeof value.organizationId !== "string" || typeof value.orgCode !== "string") return;
77
- const organization = {
78
- organizationId: value.organizationId,
79
- orgCode: value.orgCode
80
- };
81
- if ((value.type === void 0 || value.type === "session") && typeof value.accessToken === "string" && (value.expiresAt === void 0 || typeof value.expiresAt === "number")) return {
82
- type: "session",
83
- accessToken: value.accessToken,
84
- ...value.expiresAt === void 0 ? {} : { expiresAt: value.expiresAt },
85
- ...organization
86
- };
87
- if (value.type === "apiKey" && typeof value.apiKey === "string") return {
88
- type: "apiKey",
89
- apiKey: value.apiKey,
90
- ...organization
91
- };
92
- }
93
- function normalizeInstanceUrl(instanceUrl) {
94
- let url;
95
- try {
96
- url = new URL(instanceUrl);
97
- } catch (error) {
98
- throw new Error(`Invalid Hitch instance URL: ${instanceUrl}`, { cause: error });
99
- }
100
- if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error(`Invalid Hitch instance URL protocol: ${url.protocol}`);
101
- url.pathname = url.pathname.replace(/\/+$/, "");
102
- url.search = "";
103
- url.hash = "";
104
- return url.toString().replace(/\/$/, "");
105
- }
106
- function organizationInstanceUrl(instanceUrl, orgCode) {
107
- const url = new URL(normalizeInstanceUrl(instanceUrl));
108
- const labels = url.hostname.split(".");
109
- if (labels[0] !== "app" || labels.length < 2) return url.toString().replace(/\/$/, "");
110
- url.hostname = [orgCode, ...labels.slice(1)].join(".");
111
- return url.toString().replace(/\/$/, "");
112
- }
113
- function storePaths() {
114
- const env = process.env;
115
- const home = homedir();
116
- const configDir = join(env.XDG_CONFIG_HOME || join(home, ".config"), "hitch");
117
- return {
118
- configDir,
119
- cacheDir: join(env.XDG_CACHE_HOME || join(home, ".cache"), "hitch"),
120
- configPath: join(configDir, "config.json"),
121
- credentialsPath: join(configDir, "credentials.json")
122
- };
123
- }
124
- async function resolveInstanceUrl(override) {
125
- if (override !== void 0) return normalizeInstanceUrl(override);
126
- const envInstanceUrl = process.env.HITCH_API_URL;
127
- if (envInstanceUrl !== void 0 && envInstanceUrl.length > 0) return normalizeInstanceUrl(envInstanceUrl);
128
- const { configPath } = storePaths();
129
- const config = await readJson(configPath);
130
- if (config === void 0) return DEFAULT_INSTANCE_URL;
131
- if (!isObject(config) || config.instanceUrl !== void 0 && typeof config.instanceUrl !== "string") throw new Error(`${configPath}: expected { instanceUrl?: string }.`);
132
- return normalizeInstanceUrl(config.instanceUrl ?? "https://app.hitch42.com");
133
- }
134
- async function readCredentialStore() {
135
- const store = await readRawCredentialStore();
136
- return { instances: Object.fromEntries(Object.entries(store.instances).map(([instanceUrl, credentialsByOrg]) => [instanceUrl, Object.fromEntries(Object.entries(credentialsByOrg).flatMap(([orgCode, value]) => {
137
- const credentials = parseCredentials(value);
138
- return credentials === void 0 ? [] : [[orgCode, credentials]];
139
- }))])) };
140
- }
141
- async function getCredentialsForOrg(instanceUrl, orgCode) {
142
- return (await readCredentialStore()).instances[normalizeInstanceUrl(instanceUrl)]?.[orgCode];
143
- }
144
- function environmentAuth() {
145
- const apiKey = process.env.HITCH_API_KEY;
146
- return apiKey !== void 0 && apiKey.length > 0 ? {
147
- accessToken: apiKey,
148
- source: "environment"
149
- } : void 0;
150
- }
151
- function resolvedStoredAuth(credentials) {
152
- return credentials === void 0 ? void 0 : {
153
- accessToken: credentials.type === "apiKey" ? credentials.apiKey : credentials.accessToken,
154
- credentialType: credentials.type,
155
- organizationId: credentials.organizationId,
156
- orgCode: credentials.orgCode,
157
- source: "store"
158
- };
159
- }
160
- function resolveStoredAuthForOrg(credentialsByOrg, orgCode) {
161
- const exact = resolvedStoredAuth(credentialsByOrg[orgCode]);
162
- if (exact !== void 0) return exact;
163
- const sessions = Object.values(credentialsByOrg).filter((credentials) => credentials.type === "session").filter((credentials, index, all) => all.findIndex(({ accessToken }) => accessToken === credentials.accessToken) === index);
164
- if (sessions.length === 1) return resolvedStoredAuth(sessions[0]);
165
- if (sessions.length > 1) throw new AuthenticationError(`Multiple sessions are stored. Run hitch login for organization "${orgCode}".`);
166
- const apiKeyOrgs = Object.values(credentialsByOrg).filter((credentials) => credentials.type === "apiKey").map(({ orgCode: storedOrg }) => storedOrg).sort();
167
- if (apiKeyOrgs.length > 0) throw new AuthenticationError(`Stored API ${apiKeyOrgs.length === 1 ? "key is" : "keys are"} scoped to ${apiKeyOrgs.map((storedOrg) => `"${storedOrg}"`).join(", ")}, not "${orgCode}".`);
168
- }
169
- async function resolveAuthForOrg(instanceUrl, orgCode) {
170
- const auth = environmentAuth();
171
- if (auth !== void 0) return auth;
172
- const normalizedUrl = normalizeInstanceUrl(instanceUrl);
173
- const credentialsByOrg = Object.entries((await readCredentialStore()).instances).find(([instance]) => instance === normalizedUrl)?.[1];
174
- return resolveStoredAuthForOrg(credentialsByOrg ?? {}, orgCode);
175
- }
176
- function rejectEnvironmentKey(response, auth) {
177
- if (auth.source === "environment" && response.status === 401) throw new AuthenticationError("HITCH_API_KEY was rejected.");
178
- }
179
- function bearerToken(credentials) {
180
- return "apiKey" in credentials ? credentials.apiKey : credentials.accessToken;
181
- }
182
- async function setCredentials(instanceUrl, credentials) {
183
- const paths = storePaths();
184
- const store = await readRawCredentialStore();
185
- const normalizedUrl = normalizeInstanceUrl(instanceUrl);
186
- const credentialsByOrg = store.instances[normalizedUrl];
187
- store.instances[normalizedUrl] = {
188
- ...isObject(credentialsByOrg) ? credentialsByOrg : {},
189
- [credentials.orgCode]: credentials
190
- };
191
- await mkdir(paths.configDir, { recursive: true });
192
- await writeFile(paths.credentialsPath, `${JSON.stringify(store, null, 2)}\n`, { mode: 384 });
193
- await chmod(paths.credentialsPath, 384);
194
- }
195
- //#endregion
196
- export { organizationInstanceUrl as a, resolveInstanceUrl as c, getCredentialsForOrg as i, setCredentials as l, DEFAULT_INSTANCE_URL as n, rejectEnvironmentKey as o, bearerToken as r, resolveAuthForOrg as s, AuthenticationError as t };
@@ -1,31 +0,0 @@
1
- import { createRequire } from "node:module";
2
- import { existsSync } from "node:fs";
3
- import { basename, dirname, resolve } from "node:path";
4
- //#region ../shared/auth/src/workspace.mts
5
- const ORG_CODE_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
6
- const require = createRequire(import.meta.url);
7
- function readWorkspaceConfig(dir) {
8
- const path = resolve(dir, "hitch.config.ts");
9
- if (!existsSync(path)) return void 0;
10
- let module;
11
- try {
12
- module = require(path);
13
- } catch (error) {
14
- throw new Error(`Failed to load ${path}.`, { cause: error });
15
- }
16
- const config = module !== null && typeof module === "object" && "default" in module ? module.default : module;
17
- if (config === null || typeof config !== "object" || !("sdk" in config) || typeof config.sdk !== "string" || config.sdk.length === 0 || !("org" in config) || typeof config.org !== "string" || !ORG_CODE_PATTERN.test(config.org)) throw new Error(`Invalid Hitch config in ${path}: "sdk" must be a package spec and "org" an organization code.`);
18
- const githubWorkflow = "githubWorkflow" in config ? config.githubWorkflow : void 0;
19
- if (githubWorkflow !== void 0 && typeof githubWorkflow !== "boolean") throw new Error(`Invalid Hitch config in ${path}: "githubWorkflow" must be a boolean.`);
20
- return {
21
- sdk: config.sdk,
22
- org: config.org,
23
- githubWorkflow
24
- };
25
- }
26
- function findWorkspaceConfig(cwd) {
27
- const appletsDir = dirname(cwd);
28
- return readWorkspaceConfig(cwd) ?? (basename(appletsDir) === "applets" ? readWorkspaceConfig(dirname(appletsDir)) : void 0);
29
- }
30
- //#endregion
31
- export { readWorkspaceConfig as n, findWorkspaceConfig as t };