@diagonal-hq/cli 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.md +260 -2
- package/bin/diagonal.mjs +11 -0
- package/dist/capabilities.generated.d.ts +397 -0
- package/dist/cli.mjs +301074 -0
- package/dist/fields.generated.d.ts +416 -0
- package/dist/fields.generated.js +35 -0
- package/dist/globals.d.ts +72 -0
- package/dist/index.d.ts +368 -0
- package/dist/index.js +26 -0
- package/dist/sdk-contract.d.ts +14050 -0
- package/dist/workflow/console.d.ts +10 -0
- package/dist/workflow/data.d.ts +124 -0
- package/dist/workflow/errors.d.ts +23 -0
- package/dist/workflow/generated/capabilities.d.ts +2158 -0
- package/dist/workflow/index.d.ts +22 -0
- package/dist/workflow/input.d.ts +168 -0
- package/dist/workflow/notifications.d.ts +47 -0
- package/dist/workflow/serial-transaction-transport.d.ts +10 -0
- package/dist/workflow/testing/index.d.ts +26 -0
- package/dist/workflow/transaction.d.ts +1 -0
- package/dist/workflow/transport.d.ts +5 -0
- package/dist/workflow/workflow.d.ts +105 -0
- package/dist/workflow/workspace.d.ts +3 -0
- package/package.json +41 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Copyright (c) 2026 Diagonal Software Corp. All rights reserved.
|
|
2
|
+
|
|
3
|
+
This software is the proprietary property of Diagonal Software Corp. ("Diagonal"). It is
|
|
4
|
+
licensed, not sold.
|
|
5
|
+
|
|
6
|
+
Diagonal grants you a limited, non-exclusive, non-transferable, revocable
|
|
7
|
+
license to install and use this software only to develop, check, build, and
|
|
8
|
+
deploy workspaces for the Diagonal platform, in accordance with your agreement
|
|
9
|
+
with Diagonal.
|
|
10
|
+
|
|
11
|
+
You may not:
|
|
12
|
+
|
|
13
|
+
- modify, adapt, translate, or create derivative works of this software;
|
|
14
|
+
- copy, distribute, sublicense, sell, or otherwise make this software available
|
|
15
|
+
to any third party, except as an unmodified dependency of a Diagonal
|
|
16
|
+
workspace;
|
|
17
|
+
- use this software with any service other than the Diagonal platform;
|
|
18
|
+
- remove or change this notice.
|
|
19
|
+
|
|
20
|
+
This license ends automatically when you stop using the Diagonal platform or
|
|
21
|
+
when you breach its terms.
|
|
22
|
+
|
|
23
|
+
THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
25
|
+
FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL DIAGONAL BE
|
|
26
|
+
LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
27
|
+
CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE
|
|
28
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,261 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@diagonal-hq/cli`
|
|
2
2
|
|
|
3
|
-
This
|
|
3
|
+
This package is the authoring SDK for file-based workspaces. A workspace repository installs it as a dev dependency. It gives these things:
|
|
4
|
+
|
|
5
|
+
- The configuration helpers: `defineConfig`, `defineResource`, `definePage`, `defineNavigation`, `defineNotificationType`, `defineNotificationTemplate`, `defineEmailLayout`, and the `field` builders. Each `define*` function returns its first argument. Each `field.<type>(options)` call returns `{ $field, ...options }`, where `$field` is the stored field type. The package root exports them as ESM JavaScript with declarations, so a Vite build or a Node script can import a configuration file.
|
|
6
|
+
- Types for the helpers, `WorkflowConfig`, and the `Sdk` and `React` globals.
|
|
7
|
+
- Editor types for the workflow script SDK: `@diagonal/workflow`, `@diagonal/workflow/testing`, and `@diagonal/workflow/workspace`.
|
|
8
|
+
- The `diagonal check` command. It runs the same build and checks that a platform deploy runs, including the workflow tests.
|
|
9
|
+
- The `diagonal workflows` and `diagonal run` commands. They list the deployed workflows and run a manual workflow by its file path.
|
|
10
|
+
|
|
11
|
+
## Check a workspace
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install
|
|
15
|
+
npm run check
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`npm run check` runs `diagonal check .`. The command options are:
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
diagonal check [dir] [--json]
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- `dir` is the workspace root. The default is the current directory.
|
|
25
|
+
- `--json` prints `{ "ok": boolean, "diagnostics": [...] }`.
|
|
26
|
+
|
|
27
|
+
Each diagnostic prints as `path:line:column severity message`. The command exits with 1 when an error exists, with 2 for a usage error, and with 0 otherwise.
|
|
28
|
+
|
|
29
|
+
## Run a deployed workflow
|
|
30
|
+
|
|
31
|
+
`diagonal workflows` and `diagonal run` call the runtime API of a deployed workspace. Set two environment variables first:
|
|
32
|
+
|
|
33
|
+
- `DIAGONAL_URL` is the workspace URL, for example `https://acme.diagonal.app`.
|
|
34
|
+
- `DIAGONAL_API_KEY` is an API key of an account with `manage_workflows`.
|
|
35
|
+
|
|
36
|
+
```text
|
|
37
|
+
diagonal workflows [--json]
|
|
38
|
+
diagonal run <path> [--input '<json object>'] [--json]
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- `diagonal workflows` prints the path, the trigger type, and the title of each workflow of the active deployment. `--json` prints the API response, which also has each workflow id and version id.
|
|
42
|
+
- `diagonal run workflows/seed-catalog.ts` runs the manual workflow at that path in the active deployment and waits for it. It prints the terminal status, the execution id, and the output. `--input` gives the workflow input as a JSON object. `--json` prints the API response.
|
|
43
|
+
|
|
44
|
+
`diagonal run` exits with 0 when the run succeeds, with 1 when the request or the run fails, and with 2 for a usage error or a missing environment variable. A workflow that is not manual, or a path that the active deployment does not have, is a request failure.
|
|
45
|
+
|
|
46
|
+
## What the checks do
|
|
47
|
+
|
|
48
|
+
The build reads each file with a parser. It runs only the workflow tests.
|
|
49
|
+
|
|
50
|
+
- `defineConfig`, `defineResource`, `defineNavigation`, and the first argument of `definePage` must be static literals. The `field.<type>({...})` calls are the only calls that the build accepts in this data.
|
|
51
|
+
- Each `resources/*.ts` file goes through the resource checks in [Resources and fields](#resources-and-fields).
|
|
52
|
+
- The build removes imports from `@diagonal-hq/cli`. Pages, layouts, components, and workflow scripts can import workspace files through `@app/`. Workflow scripts can also import `@diagonal/workflow` and `@diagonal/workflow/workspace`. Other external imports in these source files are rejected.
|
|
53
|
+
- The second argument of `definePage`, and the default export of a layout or component file, must be one function.
|
|
54
|
+
- Pages, layouts, and components go through the page lint gate: syntax, scope, the component return shape, and a type check against the page Sdk contract and the workspace resources. A page can name a resource by id or by slug, for example `Sdk.resource('members')`.
|
|
55
|
+
- Each `workflows/<name>.ts` is a script workflow. It has one `export const config = { title, trigger, runAs? } satisfies WorkflowConfig` and one `export default defineWorkflow(...)`. The config must be a static literal. The build validates it: the trigger fields, the resources (by slug, name, or id), the workflow paths of `workflowEvent` triggers, and the run-as rules. A `recordEvent`, `recordSchedule`, `scheduled`, or `workflowEvent` workflow needs `runAs`.
|
|
56
|
+
- The build removes the config and the `@diagonal-hq/cli` imports from the workflow, with spaces in their place, and compiles the rest with the workflow script compiler against the workspace resources. A compiler error has the line and column of the workflow file.
|
|
57
|
+
- `workflows/<name>.test.ts` tests `workflows/<name>.ts` with `node:test` and `node:assert`. It imports the workflow, `runWorkflow` and `MockTransport` from `@diagonal/workflow/testing`, and the resource ids from `@diagonal/workflow/workspace`. The build transpiles the test and the workflows and runs them in one Node process. The process can read only its own temporary directory, cannot start processes, and has an empty environment. Each failing test is an error at the test. A test file without a workflow is an error.
|
|
58
|
+
- The build ignores the files at the root other than `diagonal.config.ts`, dotfiles, and `node_modules`.
|
|
59
|
+
- The build does not read `notifications/` or `email-layouts/` yet. The editor type-checks the shape of their definitions. See [Notifications](#notifications).
|
|
60
|
+
|
|
61
|
+
## Notifications
|
|
62
|
+
|
|
63
|
+
A notification has a type, one or more templates, and optionally an email layout. Each definition is a static literal. Its Liquid is inline text or a `.liquid` file next to it, written as `{ file: './standard.email.liquid' }`.
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
notifications/
|
|
67
|
+
member-welcome/
|
|
68
|
+
type.ts
|
|
69
|
+
standard.template.ts
|
|
70
|
+
standard.email.liquid
|
|
71
|
+
standard.sms.liquid
|
|
72
|
+
fixtures/
|
|
73
|
+
default.json
|
|
74
|
+
email-layouts/
|
|
75
|
+
standard/
|
|
76
|
+
layout.ts
|
|
77
|
+
body.liquid
|
|
78
|
+
fixtures/
|
|
79
|
+
default.json
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The type sets the recipient resource, the variables, and the default template. The recipient resource maps the email and phone fields. A type without `recipient` sends to email addresses. A variable uses a `field` builder other than `field.relation`, or `{ resource }` for one record of a resource. Templates read a record variable's fields, for example `{{ first_event.name }}`.
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { defineNotificationType, field } from '@diagonal-hq/cli';
|
|
86
|
+
|
|
87
|
+
export default defineNotificationType({
|
|
88
|
+
key: 'member-welcome',
|
|
89
|
+
name: 'Member welcome',
|
|
90
|
+
recipient: { resource: 'members' },
|
|
91
|
+
variables: {
|
|
92
|
+
joined_on: field.date({ label: 'Joined on', required: true }),
|
|
93
|
+
first_event: { resource: 'events', label: 'First event' }
|
|
94
|
+
},
|
|
95
|
+
defaultTemplate: 'standard'
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
A template has an email, an SMS, or both. A channel is on when the template has it. The email gives a Liquid value for each field of its layout. `layout` defaults to `'default'`, the built-in layout, which has one field, `content`. `references` adds generators that the template reads as `{{ ref.<key> }}`.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { defineNotificationTemplate } from '@diagonal-hq/cli';
|
|
103
|
+
|
|
104
|
+
export default defineNotificationTemplate({
|
|
105
|
+
key: 'standard',
|
|
106
|
+
type: 'member-welcome',
|
|
107
|
+
name: 'Standard welcome',
|
|
108
|
+
email: {
|
|
109
|
+
subject: 'Welcome to {{ workspace.name }}, {{ record.name }}',
|
|
110
|
+
layout: 'standard',
|
|
111
|
+
fields: { heading: 'Welcome aboard', body: { file: './standard.email.liquid' } }
|
|
112
|
+
},
|
|
113
|
+
sms: { body: { file: './standard.sms.liquid' } },
|
|
114
|
+
references: {
|
|
115
|
+
portal_link: {
|
|
116
|
+
label: 'Member portal',
|
|
117
|
+
generator: 'page.url',
|
|
118
|
+
settings: { page: 'portal/home' }
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
An email layout is the frame that templates fill in. Each field is a slot that a template fills with Liquid.
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { defineEmailLayout } from '@diagonal-hq/cli';
|
|
128
|
+
|
|
129
|
+
export default defineEmailLayout({
|
|
130
|
+
key: 'standard',
|
|
131
|
+
name: 'Standard',
|
|
132
|
+
body: { file: './body.liquid' },
|
|
133
|
+
fields: {
|
|
134
|
+
heading: { label: 'Heading', required: true },
|
|
135
|
+
body: { label: 'Body', required: true }
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Rules
|
|
141
|
+
|
|
142
|
+
The editor checks the shape of each definition. These rules need other files or server data, so the editor does not check them yet:
|
|
143
|
+
|
|
144
|
+
- A type cannot have a variable named `record` or `ref`. Templates read the recipient as `{{ record.* }}` and the generators as `{{ ref.* }}`.
|
|
145
|
+
- A template's `type` names an existing type, and the type's `defaultTemplate` names one of its templates.
|
|
146
|
+
- An email's `fields` match the fields of its layout, and fill each required field.
|
|
147
|
+
- A layout cannot use the key `default`.
|
|
148
|
+
- Each reference key starts with a lowercase letter. A template has at most 50 references, and each one names a generator that the server has.
|
|
149
|
+
- The `recipient` resource can receive notifications.
|
|
150
|
+
|
|
151
|
+
### Fixtures
|
|
152
|
+
|
|
153
|
+
Fixtures are sample data for previews. They are not tests, and they are never sent. A type folder and a layout folder can each have a `fixtures/` folder of JSON files. `default.json` is the case that a preview opens first.
|
|
154
|
+
|
|
155
|
+
- A layout fixture holds a value for each layout field, for example `{ "heading": "Welcome aboard", "body": "<p>Hi Avery.</p>" }`.
|
|
156
|
+
- A notification fixture holds a template key, a sample recipient, and sample variables, for example `{ "template": "standard", "recipient": { "name": "Avery Shaw", "email": "avery@example.com", "phone": null }, "variables": { "joined_on": "2026-11-02", "first_event": { "name": "Open house" } } }`. A record variable holds the record's fields.
|
|
157
|
+
|
|
158
|
+
## Resources and fields
|
|
159
|
+
|
|
160
|
+
`defineResource` takes `id`, `slug`, `name`, `singularName`, `displayField`, `searchableFields`, `fieldOrder`, `fields`, `indexes`, `embeddings`, `dataApi`, `static`, `policies`, and `capabilities`. The build refuses an unknown property, an unknown setting, and a duplicate key, and gives the line and column. It does not remove anything without a diagnostic.
|
|
161
|
+
|
|
162
|
+
- `searchableFields` lists the field keys and relation paths that record search matches, for example `['title', 'author.full_name']`. The build checks each path against the related resources. Without it, search matches the display field and every text and markdown field.
|
|
163
|
+
- `fieldOrder` lists every field key once. It sets the order of the fields in forms and tables. The default is the order of `fields`.
|
|
164
|
+
- `embeddings` is `{ enabled, fields }`. `fields` names fields of the resource, not relation paths.
|
|
165
|
+
- `dataApi.endpoints` turns off data API endpoints, for example `{ delete_many: { enabled: false } }`. An omitted endpoint stays enabled.
|
|
166
|
+
- `static: true` refuses normal record writes to the resource.
|
|
167
|
+
|
|
168
|
+
There is one `field.<type>` builder for each resource field type, in camel case: `field.eventSchedule()` stores an `event_schedule` field. `field.file()` stores a `file_reference` field, and `field.longText()` stores a `text` field. `fields.generated.js` implements the builders, `fields.generated.d.ts` declares them, and `field-catalog.json` lists their settings. `npm run generate-types` generates both from the platform field types. Do not edit them.
|
|
169
|
+
|
|
170
|
+
A builder takes these options:
|
|
171
|
+
|
|
172
|
+
- `label`, `description`, `required`, `default`, `unique`, and `index`.
|
|
173
|
+
- `guard`: more validation rules, for example `['max:255']`. Use `required` for the `required` and `nullable` rules.
|
|
174
|
+
- `settings`: the settings of the field type, with their stored names, and `settings.ui` for the record form. The seeder settings `seed_type`, `faker_method`, `faker_parameters`, and `seed_data` apply to every type.
|
|
175
|
+
- `computed`: a resolver tree that computes the value when a record is read. A computed field is not stored, so it cannot have `default`, `getter`, `setter`, `unique`, or `index`.
|
|
176
|
+
- `getter`: a resolver tree that transforms the stored value when it is read.
|
|
177
|
+
- `setter`: a resolver tree that computes the stored value before each create and update. `setterWhenEmpty: true` runs it only when the value is null or empty.
|
|
178
|
+
|
|
179
|
+
`field.select` takes `options` and `field.relation` takes `resource` (an id or slug) and `type` as options. Other settings go in `settings`. A setting that an option writes, such as `settings.unique`, is refused. A relation can declare `settings.inverse: { key, label?, settings? }`. The build then adds the inverse relation field to the target resource. A target that declares a field with the same key is an error.
|
|
180
|
+
|
|
181
|
+
The platform deploy validates every field again with the rules of the resource builder, so a value that the build cannot check, such as an unknown guard rule, fails the deploy with the file path.
|
|
182
|
+
|
|
183
|
+
## Capabilities
|
|
184
|
+
|
|
185
|
+
A resource gets a platform capability, such as Authenticatable, Notifiable, Payable, Subscription, or Eventable, from its `capabilities` key. The settings have the names that the resource builder uses. A field reference names a field of the same resource by its key:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
export default defineResource({
|
|
189
|
+
id: '0199f300-0000-7000-8000-000000000005',
|
|
190
|
+
slug: 'members',
|
|
191
|
+
name: 'Members',
|
|
192
|
+
singularName: 'Member',
|
|
193
|
+
fields: {
|
|
194
|
+
name: field.text({ required: true }),
|
|
195
|
+
email: field.email({ required: true, unique: true }),
|
|
196
|
+
password: field.password()
|
|
197
|
+
},
|
|
198
|
+
policies: [],
|
|
199
|
+
capabilities: {
|
|
200
|
+
authenticatable: { email_field: 'email', password_field: 'password' },
|
|
201
|
+
notifiable: { email_field: 'email' }
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
`capabilities.generated.d.ts` declares the settings of each capability, and `capability-catalog.json` lists them. `npm run generate-types` generates both from the platform capabilities. Do not edit them.
|
|
207
|
+
|
|
208
|
+
- The types accept only a field key whose field type the setting allows. For example, `notifiable.email_field` accepts only an `email` field.
|
|
209
|
+
- A file cannot declare `role` or `permission`. Authorization provisioning owns the Role and Permission resources. It also maps `authenticatable.roles_field`, so a file cannot set it.
|
|
210
|
+
- A file cannot set a page reference, such as `authenticatable.login_page`. A page reference names a page row, and the pages of a file-based workspace are files.
|
|
211
|
+
- The build checks the setting names, the required settings, the select values, and each field reference. A relation path, such as an Itemizable offering path, names a relation of each resource on the way.
|
|
212
|
+
- Each field reference is stored as the field id, as the resource builder stores it. This includes a nested reference, such as an Eventable excluded field, and each key of a relation path.
|
|
213
|
+
- The deploy validates each capability with the resource builder's rules against the deployed schema. These rules include the required select options, the field types, and the capability of each resource that a relation setting targets. A capability that fails rejects the deploy, and the previous deployment keeps serving.
|
|
214
|
+
- After the deploy, the platform runs the same provisioning as the resource builder. It creates the system fields of a Subscription, the accounting entity fields, the billing relations, the record indexes, and the booking and schedule setup. For an Authenticatable resource, it creates the Role and Permission resources, so members can sign in.
|
|
215
|
+
- A setting that provisioning fills can be left out. A Subscription starts from the builder's default settings, so set only the defaults that change.
|
|
216
|
+
- Provisioning can create a field, for example `accounting_entity`. The files cannot declare a field with the same key later. The deploy refuses it until you delete the field in the resource builder or use another key.
|
|
217
|
+
- A capability change in the resource builder is drift. The next deploy reports it and restores the capability from the files.
|
|
218
|
+
|
|
219
|
+
A workflow script reads the capabilities of each resource from `@diagonal/workflow/workspace`. An input that needs a capability, such as the `resource` of `notification.setConsent`, accepts only a resource that declares it.
|
|
220
|
+
|
|
221
|
+
## Editor types
|
|
222
|
+
|
|
223
|
+
A page, layout, component, or workflow can import any other file of the workspace by its path from the root with `@app/`, for example `import { formatPrice } from '@app/lib/money'`. The extension is optional, and a folder's `index` file is found. The build links each imported file into the importing page or workflow, so the files need no folder of their own; a file outside the build's folders, such as `lib/`, is built only when something imports it. An imported module can import other `@app/` files, `@diagonal-hq/cli`, and, in a workflow, `@diagonal/workflow`. Add `"@app/*": ["./*"]` to the `paths` of `tsconfig.json` so the editor resolves them too.
|
|
224
|
+
|
|
225
|
+
Add a `tsconfig.json` with `"types": ["@diagonal-hq/cli", "node"]` and `@types/node` as a dev dependency. `dist/globals.d.ts` gives the `Sdk` global from the generated page Sdk contract (`dist/sdk-contract.d.ts`). The editor does not know the workspace resources and components, so `Sdk.resource(...)` and `Sdk.Custom.*` have loose types there. `diagonal check` types them.
|
|
226
|
+
|
|
227
|
+
`dist/workflow/` holds the declarations of the workflow script SDK. Map the module names to them with `paths`:
|
|
228
|
+
|
|
229
|
+
```json
|
|
230
|
+
"paths": {
|
|
231
|
+
"@diagonal/workflow": ["./node_modules/@diagonal-hq/cli/dist/workflow/index.d.ts"],
|
|
232
|
+
"@diagonal/workflow/testing": ["./node_modules/@diagonal-hq/cli/dist/workflow/testing/index.d.ts"],
|
|
233
|
+
"@diagonal/workflow/workspace": ["./node_modules/@diagonal-hq/cli/dist/workflow/workspace.d.ts"]
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
In the editor, `resources` from `@diagonal/workflow/workspace` maps every name to a string, and record data is untyped. `diagonal check` compiles against the workspace resources and their fields.
|
|
238
|
+
|
|
239
|
+
`globals.d.ts` at the package root has loose `Sdk` and `React` types. The source `index.d.ts` uses them before the package is built.
|
|
240
|
+
|
|
241
|
+
## Build the package
|
|
242
|
+
|
|
243
|
+
`npm run build:cli-package` (`scripts/build-cli-package.mjs`) writes `dist/` and packs `scripts/dist/npm/diagonal-hq-cli-<version>.tgz`. `dist/cli.mjs` bundles every dependency, the TypeScript lib files, the workflow script compiler with the script SDK sources, and the bundled script SDK that workflow tests import, so the command needs no Diagonal checkout. The script needs the page Sdk contract and `scripts/generated/typeCheckLibs.mjs`. `npm run generate-typescript` makes both and then runs the script.
|
|
244
|
+
|
|
245
|
+
`node scripts/smoke-cli-package.mjs [tarball]` installs the packed tarball in a copy of the starter template outside the repository, with one workflow and its test added. It checks the packed file list, imports the helpers through the `exports` map, runs `tsc` against the packed declarations, and runs `diagonal check`. The `generated-assets` job of PR CI runs it.
|
|
246
|
+
|
|
247
|
+
## Publish the package
|
|
248
|
+
|
|
249
|
+
The `Publish npm package` workflow (`.github/workflows/publish-npm-package.yml`) publishes the package to npm after each push to `main`. You can also run it from the Actions tab.
|
|
250
|
+
|
|
251
|
+
1. The build job builds and packs the package. It compares the packed files with the tarball that npm has for the version in `package.json`. The `version` field does not count.
|
|
252
|
+
2. When the files differ, a model reads the diff and the commit subjects since that publish. It classifies the change as breaking, feature, or fix. Before 1.0.0, a breaking change raises the minor part, and other changes raise the patch part. From 1.0.0, the workflow follows semantic versioning.
|
|
253
|
+
3. The build job packs the package again at the new version and runs the smoke test on the tarball.
|
|
254
|
+
4. The commit job commits the new version to `main` with an `[automated] release:` commit.
|
|
255
|
+
5. The publish job runs in the `npm-publish` environment and publishes the tested bytes with npm trusted publishing. It does not build again. It refuses a version that npm already has.
|
|
256
|
+
|
|
257
|
+
To choose a version yourself, such as 1.0.0 or a prerelease, set `version` in `package.json` to a version that npm does not have, and merge the change. The workflow publishes that version as it is. A version with a prerelease part, such as `0.2.0-next.1`, gets the `next` tag. Other versions get `latest`. The workflow does not raise a prerelease version. Set the next version by hand.
|
|
258
|
+
|
|
259
|
+
The model call uses the `OPENROUTER_API_KEY` secret. When the call fails, the release fails. The workflow does not guess a version.
|
|
260
|
+
|
|
261
|
+
npm must list `Diagonal-HQ/diagonal`, the `publish-npm-package.yml` workflow, and the `npm-publish` environment as the trusted publisher of `@diagonal-hq/cli`.
|
package/bin/diagonal.mjs
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The installed package has no path aliases.
|
|
3
|
+
// oxlint-disable-next-line local/no-relative-imports
|
|
4
|
+
import { main } from '../dist/cli.mjs';
|
|
5
|
+
|
|
6
|
+
process.exitCode = await main(process.argv.slice(2), {
|
|
7
|
+
cwd: process.cwd(),
|
|
8
|
+
env: process.env,
|
|
9
|
+
stdout: process.stdout,
|
|
10
|
+
stderr: process.stderr
|
|
11
|
+
});
|