@softr/cli 0.0.1 → 0.1.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/README.md +93 -81
- package/dist/cli.mjs +377 -255
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# @softr/cli
|
|
2
2
|
|
|
3
|
-
`softr`
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
file
|
|
7
|
-
|
|
3
|
+
`softr` is the Softr command line: it works with your Softr workspace from files on your machine, by hand or through a
|
|
4
|
+
coding agent, and nothing runs locally. Its root commands cover the account and the workspace; each area of Softr is a
|
|
5
|
+
scope with a noun. Today that is `softr workflows`, which treats a workflow like a small project: the definition is a
|
|
6
|
+
`softr-workflows.jsonc` file, every **Run custom code** node is a real `.js` or `.py` file next to it, and the CLI
|
|
7
|
+
moves both to and from the Softr workflows service. More scopes follow.
|
|
8
8
|
|
|
9
|
-
Status:
|
|
9
|
+
Status: versions below 1.0 may change between releases; a single-binary build is planned.
|
|
10
10
|
|
|
11
11
|
## Install and run
|
|
12
12
|
|
|
@@ -19,27 +19,10 @@ npm run dev -- --help # from a checkout
|
|
|
19
19
|
Node 22 or newer. `npm update -g @softr/cli` updates a global install; `npx` fetches the latest release on
|
|
20
20
|
its own.
|
|
21
21
|
|
|
22
|
-
## Agent skill
|
|
23
|
-
|
|
24
|
-
The agent's guide to this tool is [`skills/softr-workflows/SKILL.md`](skills/softr-workflows/SKILL.md), in the
|
|
25
|
-
[Agent Skills](https://agentskills.io) format, with the code-file rules in
|
|
26
|
-
[`references/custom-code.md`](skills/softr-workflows/references/custom-code.md). It says what the CLI is for, when not
|
|
27
|
-
to use it, what it needs, how to install and update it, and how to work with it, so adding the skill is the only step
|
|
28
|
-
a user takes.
|
|
29
|
-
|
|
30
|
-
Users install it from the public skills repository, `softr-io/softr-io-skills`: as a plugin in Claude Code, which
|
|
31
|
-
keeps it updated, or with `npx skills add softr-io/softr-io-skills --skill softr-workflows` in every other agent. The
|
|
32
|
-
exact steps are on [docs.softr.io/skills/workflows-cli](https://docs.softr.io/skills/workflows-cli).
|
|
33
|
-
|
|
34
|
-
The skill is edited here and published from here: a merge to `master` that changes `skills/` runs
|
|
35
|
-
`.github/workflows/sync-skills.yml`, a call of the shared `sync-skills` workflow in `softr-io/github-workflows`, which
|
|
36
|
-
asks `softr-io/softr-io-skills` to open a sync pull request with the new copy (its `docs/sync-skills.md` is the
|
|
37
|
-
guide). Never edit the copy in the skills repository. `npm run check:skill` validates the format; CI runs it too.
|
|
38
|
-
|
|
39
22
|
## Log in
|
|
40
23
|
|
|
41
24
|
Two ways, both checked with the studio before the token is stored in `~/.softr/credentials.json` (mode 0600, keyed by
|
|
42
|
-
the
|
|
25
|
+
the environment, one login per environment). In a terminal, `login` asks which one; the browser is the recommended default:
|
|
43
26
|
|
|
44
27
|
```bash
|
|
45
28
|
softr login # asks: 1. browser (recommended, OAuth + PKCE) or 2. paste a personal API token
|
|
@@ -51,14 +34,28 @@ softr logout
|
|
|
51
34
|
|
|
52
35
|
The browser login grants the scopes registered for the CLI in the studio, on every workspace you can reach; the consent
|
|
53
36
|
page lists them. The access token renews itself, and a login is needed again after 30 days without use. A personal API
|
|
54
|
-
token needs a `workflows:*` action in its statements; `login` warns when it has none. `
|
|
37
|
+
token needs a `workflows:*` action in its statements; `login` warns when it has none. `SOFTR_TOKEN` overrides the
|
|
55
38
|
stored token for scripts and CI. The studio session cookie is not accepted.
|
|
56
39
|
|
|
57
|
-
The CLI targets Softr production. For development, `SOFTR_WORKFLOWS_API_URL`, `
|
|
58
|
-
`
|
|
40
|
+
The CLI targets Softr production. For development, `SOFTR_WORKFLOWS_API_URL`, `SOFTR_STUDIO_API_URL` and
|
|
41
|
+
`SOFTR_STUDIO_URL` point it at another workflows service, studio API and studio, for example locally running
|
|
59
42
|
ones. A workflow directory remembers the target it was pulled from.
|
|
60
43
|
|
|
61
|
-
##
|
|
44
|
+
## Global commands
|
|
45
|
+
|
|
46
|
+
| Command | What it does |
|
|
47
|
+
| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
48
|
+
| `login [--token <pat_...>] [--no-browser]` / `whoami` / `logout` | Log in through the browser (OAuth with PKCE), or store a personal API token after confirming it with the studio; show the stored identity and its grants; forget the token. |
|
|
49
|
+
| `workspaces` | The workspaces the stored token reaches, with your role in each and the default one. Commands that take `--workspace` default to `SOFTR_WORKSPACE_ID` when it is set, else to your only or default workspace. |
|
|
50
|
+
| `integrations [--workspace <id>] [--type <TYPE>]` | List the workspace's connected integrations (id, name, type, account, auth kind) from the studio API; the id goes into a node's `integrationId` input. Inside a workflow directory the workspace is the workflow's; `--type` filters. Credentials are never shown. |
|
|
51
|
+
|
|
52
|
+
Every command accepts `--json`. `SOFTR_TOKEN` overrides the stored token; `SOFTR_WORKSPACE_ID` is the default workspace.
|
|
53
|
+
|
|
54
|
+
## Workflows
|
|
55
|
+
|
|
56
|
+
The `softr workflows` commands; `softr workflows --help` lists them.
|
|
57
|
+
|
|
58
|
+
### Project layout
|
|
62
59
|
|
|
63
60
|
One directory holds exactly one workflow. A parent directory may group several.
|
|
64
61
|
|
|
@@ -74,7 +71,7 @@ my-project/
|
|
|
74
71
|
softr-workflows.jsonc
|
|
75
72
|
```
|
|
76
73
|
|
|
77
|
-
|
|
74
|
+
#### `softr-workflows.jsonc`
|
|
78
75
|
|
|
79
76
|
The file has the shape of the workflows API write model (`title`, `workspaceId`, `triggers`, `actions`, `paths`,
|
|
80
77
|
`configuration`) plus the server `id`. Two CLI conventions apply:
|
|
@@ -87,25 +84,37 @@ The file has the shape of the workflows API write model (`title`, `workspaceId`,
|
|
|
87
84
|
and `spec`: known node types get their inputs, enums and the path condition grammar; node types outside the
|
|
88
85
|
catalog are accepted with free inputs, and drafts may hold nulls. The service remains the authority at publish.
|
|
89
86
|
|
|
90
|
-
Validation stays in the service: `push` saves a draft
|
|
87
|
+
Validation stays in the service: `push` saves a draft; `publish` refuses while the readiness report lists validation
|
|
88
|
+
problems.
|
|
91
89
|
|
|
92
|
-
|
|
90
|
+
#### Code files
|
|
93
91
|
|
|
94
92
|
The service stores the _body_ of a function and runs it with a single variable, `inputData`, holding the node's
|
|
95
|
-
"Data to use in the code" map (`inputs.inputData` in the config)
|
|
96
|
-
|
|
93
|
+
"Data to use in the code" map (`inputs.inputData` in the config), and with `fetch` in scope, authenticated by the
|
|
94
|
+
integrations attached to the node (`inputs.integrations`, each with an alias). Locally the body lives inside a real
|
|
95
|
+
function so that editors, linters and agents see valid code. On the current version, CUSTOM_CODE 1.2.0:
|
|
97
96
|
|
|
98
97
|
```js
|
|
99
|
-
export default function (inputData) {
|
|
100
|
-
|
|
98
|
+
export default async function (inputData) {
|
|
99
|
+
// The CRM is attached to the node (inputs.integrations): the call to its API host is authenticated by itself.
|
|
100
|
+
const response = await fetch(`https://api.crm.example/contacts?email=${inputData.email}`);
|
|
101
|
+
const { items } = await response.json();
|
|
102
|
+
return { known: items.length > 0 };
|
|
101
103
|
}
|
|
102
104
|
```
|
|
103
105
|
|
|
104
106
|
```python
|
|
105
107
|
def main(inputData):
|
|
106
|
-
|
|
108
|
+
response = fetch(f"https://api.crm.example/contacts?email={inputData['email']}")
|
|
109
|
+
return {"known": len(response.json()["items"]) > 0}
|
|
107
110
|
```
|
|
108
111
|
|
|
112
|
+
The `integration` option of `fetch`, `{ integration: 'crm' }`, names an attached integration by its alias; it is needed
|
|
113
|
+
only for a REST API integration, which has no host of its own, or when two attached integrations share a host.
|
|
114
|
+
|
|
115
|
+
The generated header follows the version of the node, so a node still on an older version gets that runtime's rules;
|
|
116
|
+
this README always shows the latest.
|
|
117
|
+
|
|
109
118
|
`pull` writes a generated header above the function: the rules of the runtime and one typed line per `inputData`
|
|
110
119
|
entry. The types come from the saved test outputs of the referenced nodes (trusted), then from their declared output
|
|
111
120
|
metadata (marked as not seen in test data), and are `unknown` with a hint when the upstream node was never tested.
|
|
@@ -114,36 +123,35 @@ comments. The header is regenerated on every `pull` and is never uploaded.
|
|
|
114
123
|
|
|
115
124
|
`push` uploads only that function body. Anything outside it (imports, helpers, constants) is rejected with a
|
|
116
125
|
message. On CUSTOM_CODE 1.2.0 both languages call HTTP APIs with `fetch` and the JavaScript function is `async`; a
|
|
117
|
-
node still on 1.0.0 or 1.1.0 keeps its synchronous runtime without `fetch`, and its header says so until you
|
|
118
|
-
`
|
|
126
|
+
node still on 1.0.0 or 1.1.0 keeps its synchronous runtime without `fetch`, and its header says so until you run
|
|
127
|
+
`upgrade` on it. JavaScript has no imports; Python imports from an allowlist of standard modules inside
|
|
119
128
|
`main`. The return value becomes the node output at `$.body`. Never write workflow placeholders such as
|
|
120
129
|
`{outputs.x:::$.y}` inside code; map them in `inputData` instead. See the
|
|
121
130
|
[Run Custom Code](https://docs.softr.io/workflows/actions/run-custom-code) docs.
|
|
122
131
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
| Command
|
|
126
|
-
|
|
|
127
|
-
| `
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
140
|
-
| `test
|
|
141
|
-
| `
|
|
142
|
-
| `outputs
|
|
143
|
-
| `outputs
|
|
144
|
-
| `
|
|
145
|
-
| `
|
|
146
|
-
| `placeholder <nodeId> [keys...]` | Build the token for a node's output field from keys (`*` for every element, digits for an index, or one dotted path), checked against the saved test data: type and example when it exists, the keys that do exist when it does not. A loop id gives the current item. |
|
|
132
|
+
### Commands
|
|
133
|
+
|
|
134
|
+
| Command | What it does |
|
|
135
|
+
| -------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
136
|
+
| `workflows list [--workspace <id>]` | List the workflows you can access, one table per workspace. A workspace whose listing fails is reported at the end and skipped. |
|
|
137
|
+
| `workflows pull [<workflowId>] [--dir <path>] [--force]` | Download a workflow into a directory (new `./<title-slug>/` by default). Without an id, list what you can pull. |
|
|
138
|
+
| `workflows push [--dir <path>] [--dry-run] [--force]` | Inline `@@` references and code files, save as a new draft. Sends nothing when the files match the last sync, and refuses when the server copy changed since then (`--force` overrides both). Warns about placeholders that no saved test data backs. |
|
|
139
|
+
| `workflows init <title> [--workspace <id>] [--trigger <TYPE>] [--dir <path>]` | Create a workflow on the server with one trigger (default `WEBHOOK`) and the local directory for it. |
|
|
140
|
+
| `workflows upgrade [<nodeId>...] [--dir <path>]` | Move nodes on an older version of their type to the current one (CUSTOM_CODE 1.0.0 and 1.1.0 become 1.2.0: the integrations list is added, the code file keeps its body and gets the new header). Every command that touches a workflow lists such nodes in a note. |
|
|
141
|
+
| `workflows add <TYPE> [--id <id>] [--after <nodeId>] [--in-loop <loopId>] [--to <actionId>] [--lang JAVASCRIPT\|PYTHON] [--title <t>]` | Append an action after a node (default: the only node without a successor) and the path to it, or append a trigger (`--to` connects it). Custom code gets `actions/<id>.js\|py` with an empty function. Comments in the config survive. |
|
|
142
|
+
| `workflows replace <nodeId> <TYPE>` | Give a node another type of the same kind: id, title and paths stay, inputs start from the new template. |
|
|
143
|
+
| `workflows remove <nodeId>` | Remove a node and the paths to and from it; a loop goes with its body. Code files stay on disk. |
|
|
144
|
+
| `workflows spec [<TYPE>]` | The node types this CLI offers; with a type: inputs, output, notes, an example node and the docs URL. |
|
|
145
|
+
| `workflows schema [--write]` | Print the JSON schema of `softr-workflows.jsonc`, generated from the node catalog, or write it into the workflow directory. `pull` and `init` write it and set `$schema` in the config, so editors validate and complete the file. |
|
|
146
|
+
| `workflows status` | Local edits vs the last sync, the server's version and draft, the live version. |
|
|
147
|
+
| `workflows publish [--force]` / `unpublish` | Enable the latest version (the service validates the whole workflow) or disable the workflow. `publish` fetches the server copy first and refuses unpushed local edits and a server copy that changed since the last sync; then it lists nodes that are untested, failed, tested before the last save, or tested against outdated upstream data: a terminal asks for confirmation, a script or agent needs `--force`. |
|
|
148
|
+
| `workflows test <nodeId> [--mock] [--iteration <n>] [--sample <n>]` | Run one node in the workflows service and save its sample output. Triggers propose samples; `--sample` picks one (default the first). |
|
|
149
|
+
| `workflows test --all` | Run the trigger and then every action in execution order, predecessors first across parallel paths and loop bodies. Branches, filters and loops are skipped. Failures are reported at the end; the exit code is 1 when any test failed. |
|
|
150
|
+
| `workflows outputs` | One line per node: tested, failed, untested, with the field count. |
|
|
151
|
+
| `workflows outputs <nodeId>` | The node's referenceable variables with types and examples, ready to paste. |
|
|
152
|
+
| `workflows outputs --refresh` | Fetch the saved samples from the service and regenerate the code headers. |
|
|
153
|
+
| `workflows verify [--test-missing]` | One readiness report: code files parse, local files match the server, the service accepts the definition for publishing, every node tested on current data, every placeholder backed by test data. `--test-missing` first runs the tests that are missing, in execution order. Exit code 1 while something needs attention. |
|
|
154
|
+
| `workflows placeholder <nodeId> [keys...]` | Build the token for a node's output field from keys (`*` for every element, digits for an index, or one dotted path), checked against the saved test data: type and example when it exists, the keys that do exist when it does not. A loop id gives the current item. |
|
|
147
155
|
|
|
148
156
|
Every command accepts `--json`. After a `pull`, the target is remembered in `.softr-workflows/state.json`.
|
|
149
157
|
|
|
@@ -154,32 +162,44 @@ for a verdict on local code.
|
|
|
154
162
|
|
|
155
163
|
`verify`, `status` and the publish guard read the service's readiness report
|
|
156
164
|
(`GET /v1/workflows/{id}/readiness`): the publish validation as a dry run with every message, and per node the test
|
|
157
|
-
state, staleness against the last save, outdated references and placeholders no saved sample backs.
|
|
158
|
-
without that endpoint the CLI runs the same node checks itself and says so; the validation dry run is then missing.
|
|
165
|
+
state, staleness against the last save, outdated references and placeholders no saved sample backs.
|
|
159
166
|
|
|
160
167
|
`.softr-workflows/state.json` keeps the server version and draft of the last sync and a content hash of what was
|
|
161
168
|
synced. That is how `push` knows there is nothing to send, how it notices that the server copy moved on, and how
|
|
162
169
|
`test` tells edited from unedited without any server-side support.
|
|
163
170
|
|
|
164
|
-
|
|
171
|
+
#### The node types on offer
|
|
165
172
|
|
|
166
173
|
`spec` lists them. The set is small on purpose: the generic triggers (webhook, schedules, Softr Tables and Softr Apps
|
|
167
174
|
events, incoming email), the control nodes (branch, filter, loop, wait), `RESPONDED_TO_WEBHOOK`, `CALL_API`, and
|
|
168
175
|
`CUSTOM_CODE` for everything else. Other integrations stay in the studio. Each entry points to its public docs page.
|
|
169
176
|
|
|
170
|
-
|
|
177
|
+
#### A typical session
|
|
171
178
|
|
|
172
179
|
```bash
|
|
173
|
-
softr init "Lead scoring" # server + ./lead-scoring/
|
|
180
|
+
softr workflows init "Lead scoring" # server + ./lead-scoring/
|
|
174
181
|
cd lead-scoring
|
|
175
|
-
softr add CUSTOM_CODE --id score --lang python # actions/score.py + node + path
|
|
182
|
+
softr workflows add CUSTOM_CODE --id score --lang python # actions/score.py + node + path
|
|
176
183
|
# edit actions/score.py, set inputs.inputData in softr-workflows.jsonc
|
|
177
|
-
softr push
|
|
178
|
-
softr test --all # trigger first (send it one request), then score
|
|
179
|
-
softr outputs score # variables the next node can use
|
|
180
|
-
softr publish
|
|
184
|
+
softr workflows push
|
|
185
|
+
softr workflows test --all # trigger first (send it one request), then score
|
|
186
|
+
softr workflows outputs score # variables the next node can use
|
|
187
|
+
softr workflows publish
|
|
181
188
|
```
|
|
182
189
|
|
|
190
|
+
## Agent skills
|
|
191
|
+
|
|
192
|
+
Two skills in the [Agent Skills](https://agentskills.io) format, published in
|
|
193
|
+
[softr-io/softr-io-skills](https://github.com/softr-io/softr-io-skills):
|
|
194
|
+
|
|
195
|
+
- [`softr-cli`](https://github.com/softr-io/softr-io-skills/tree/master/skills/softr-cli): install and update the CLI,
|
|
196
|
+
log in, the root commands and the environment variables. Every other Softr skill builds on it.
|
|
197
|
+
- [`softr-workflows`](https://github.com/softr-io/softr-io-skills/tree/master/skills/softr-workflows): the
|
|
198
|
+
`softr workflows` scope, with the code-file rules in its `references/custom-code.md`.
|
|
199
|
+
|
|
200
|
+
Install them as a plugin in Claude Code, which keeps them updated, or with `npx skills add softr-io/softr-io-skills`
|
|
201
|
+
in every other agent. The exact steps are on [docs.softr.io/softr-cli](https://docs.softr.io/softr-cli).
|
|
202
|
+
|
|
183
203
|
## Development
|
|
184
204
|
|
|
185
205
|
```bash
|
|
@@ -189,15 +209,7 @@ node dist/cli.mjs --help
|
|
|
189
209
|
```
|
|
190
210
|
|
|
191
211
|
The build is one self-contained ESM file (`dist/cli.mjs`), which keeps a later `bun build --compile` possible: no
|
|
192
|
-
native addons, no runtime reads relative to `__dirname`.
|
|
193
|
-
|
|
194
|
-
CI (`.github/workflows/ci.yml`) runs `npm run check` on every pull request and every push to `master`. A pull request
|
|
195
|
-
gets an AI review on request: add the `ai-review` label or comment `/review` (`.github/workflows/ai-review.yml`;
|
|
196
|
-
[`AGENTS.md`](AGENTS.md) is what the reviewer reads first).
|
|
197
|
-
|
|
198
|
-
### Releasing
|
|
199
|
-
|
|
200
|
-
A release is a version bump merged to `master`; [docs/releasing.md](docs/releasing.md) has the mechanics.
|
|
212
|
+
native addons, no runtime reads relative to `__dirname`. A release is a version bump merged to `master`.
|
|
201
213
|
|
|
202
214
|
## Further reading
|
|
203
215
|
|