@widgetic/cli 0.0.0-stage → 0.1.1

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 (50) hide show
  1. package/LICENSE +122 -0
  2. package/README.md +184 -2
  3. package/bin/widgetic.js +5 -0
  4. package/dist/cli.js +106 -0
  5. package/dist/commands/auth.js +269 -0
  6. package/dist/commands/call.js +97 -0
  7. package/dist/commands/completion.js +148 -0
  8. package/dist/commands/doctor.js +291 -0
  9. package/dist/commands/schema.js +214 -0
  10. package/dist/commands/update.js +159 -0
  11. package/dist/commands/verify.js +64 -0
  12. package/dist/config.js +98 -0
  13. package/dist/confirm.js +15 -0
  14. package/dist/constants.js +51 -0
  15. package/dist/contract-note.js +42 -0
  16. package/dist/credentials.js +111 -0
  17. package/dist/environment.js +150 -0
  18. package/dist/errors.js +67 -0
  19. package/dist/http.js +122 -0
  20. package/dist/input.js +37 -0
  21. package/dist/main.js +30 -0
  22. package/dist/manifest/load.js +39 -0
  23. package/dist/manifest/types.js +14 -0
  24. package/dist/mcp/plan.js +32 -0
  25. package/dist/mcp/register.js +112 -0
  26. package/dist/mcp/session.js +185 -0
  27. package/dist/mcp/wait.js +68 -0
  28. package/dist/options.js +228 -0
  29. package/dist/output.js +90 -0
  30. package/dist/overlay.js +31 -0
  31. package/dist/patterns.js +11 -0
  32. package/dist/rest/execute.js +67 -0
  33. package/dist/rest/mime.js +37 -0
  34. package/dist/rest/plan.js +185 -0
  35. package/dist/rest/register.js +139 -0
  36. package/dist/retry.js +49 -0
  37. package/dist/secrets.js +149 -0
  38. package/dist/state.js +37 -0
  39. package/dist/update/installation.js +83 -0
  40. package/dist/update/lookup.js +128 -0
  41. package/dist/update/offer.js +165 -0
  42. package/dist/update/perform.js +33 -0
  43. package/dist/update/registry.js +68 -0
  44. package/dist/update/version.js +39 -0
  45. package/dist/validate.js +93 -0
  46. package/dist/version.js +59 -0
  47. package/generated/contracts.json +1 -0
  48. package/generated/mcp-manifest.json +1 -0
  49. package/generated/rest-manifest.json +1 -0
  50. package/package.json +56 -5
package/LICENSE ADDED
@@ -0,0 +1,122 @@
1
+ Widgetic Integration License
2
+ Version 1.0
3
+
4
+ Copyright (c) Widgetic. All rights reserved.
5
+
6
+ This license covers the software package that it came with, in source and
7
+ compiled form, together with its documentation (the "Software"). By
8
+ installing, copying or using the Software you accept this license. If you do
9
+ not accept it, do not install or use the Software.
10
+
11
+ In this license, "Widgetic" is the company that publishes the Software, "you"
12
+ is the person or organization that uses it, and "Your Application" is an
13
+ application, website, product or service that you build or operate, for
14
+ yourself or for a client. "Widgetic Services" are the services that Widgetic
15
+ operates: the Widgetic API, widgets, compositions, embeds, editors, file
16
+ browser, chat and AI creator, and the other services that can be reached from
17
+ widgetic.com or its API hosts.
18
+
19
+ 1. What you may do
20
+
21
+ Widgetic grants you a worldwide, non-exclusive, non-transferable and
22
+ non-sublicensable license to use the Software, unmodified, for one purpose: to
23
+ integrate the Widgetic Services into Your Application. Within that purpose you
24
+ may:
25
+
26
+ (a) install the Software and run it in Your Application, in development,
27
+ testing and production;
28
+
29
+ (b) use it to call the Widgetic API, to show Widgetic widgets and
30
+ compositions, and to open Widgetic's editors, file browser, chat and AI
31
+ creator inside Your Application;
32
+
33
+ (c) bundle, minify and tree-shake the Software with the build tools of Your
34
+ Application, deliver the result to the people who use Your Application as a
35
+ part of it, and let them run it there;
36
+
37
+ (d) keep the copies of the Software that (a), (b) and (c) need, including
38
+ copies in your version control, your build systems and your backups.
39
+
40
+ 2. What you may not do
41
+
42
+ You may not, and may not allow anyone else to:
43
+
44
+ (a) use the Software for any purpose other than the one in section 1;
45
+
46
+ (b) use the Software to build, run or improve a service that offers what the
47
+ Widgetic Services offer, or to connect Your Application to anything other than
48
+ the Widgetic Services (a proxy of yours that forwards calls to the Widgetic
49
+ Services is fine);
50
+
51
+ (c) copy, modify, translate, adapt or make derivative works of the Software,
52
+ except as section 1(c) allows;
53
+
54
+ (d) publish, sell, rent, lease, sublicense or distribute the Software on its
55
+ own, or inside a library, SDK, template or product that others could use for
56
+ anything other than integrating the Widgetic Services, including by
57
+ publishing it, or a copy of it, to a package registry or a public CDN;
58
+
59
+ (e) reverse engineer, decompile or disassemble the Software, or try to get at
60
+ its source code, except as far as the law lets you despite this restriction;
61
+
62
+ (f) remove or change a copyright, license or attribution notice in the
63
+ Software;
64
+
65
+ (g) bypass, disable or interfere with the authentication, rate limits, quotas,
66
+ credits, plan limits or other protections of the Widgetic Services, or help
67
+ anyone else to;
68
+
69
+ (h) use the name, logo or trademarks of Widgetic, except to say truthfully
70
+ that Your Application integrates Widgetic.
71
+
72
+ 3. The Widgetic Services are separate
73
+
74
+ This license is for the Software only. It does not give you the right to use
75
+ the Widgetic Services. That right comes from your account or agreement with
76
+ Widgetic and from Widgetic's Terms of Service (https://widgetic.com/terms),
77
+ which this license does not change.
78
+
79
+ 4. Ownership
80
+
81
+ The Software is licensed to you, not sold. Widgetic and its licensors keep all
82
+ rights in it. Any right that this license does not give you in plain words is
83
+ reserved to Widgetic.
84
+
85
+ 5. Third-party software
86
+
87
+ The Software may include or depend on software from others. That software is
88
+ under its own license, which applies to it instead of this one, and nothing in
89
+ this license limits your rights under it.
90
+
91
+ 6. How long it lasts
92
+
93
+ This license lasts while you follow it and while you are entitled to use the
94
+ Widgetic Services. It ends on its own if you break it or that entitlement
95
+ ends. When it ends, you stop using the Software and delete your copies of it,
96
+ except copies that the law requires you to keep. Sections 4, 8 and 9 still
97
+ apply after it has ended.
98
+
99
+ 7. Versions
100
+
101
+ This license applies to the version of the Software that it came with and to
102
+ later versions that carry it. A version that Widgetic published under another
103
+ license stays under that license, for the copies of it that you already have.
104
+
105
+ 8. No warranty
106
+
107
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
108
+ IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
109
+ PURPOSE AND NON-INFRINGEMENT.
110
+
111
+ 9. Limit of liability
112
+
113
+ TO THE EXTENT THE LAW ALLOWS, WIDGETIC IS NOT LIABLE FOR ANY INDIRECT,
114
+ INCIDENTAL, SPECIAL, CONSEQUENTIAL OR PUNITIVE DAMAGES, OR FOR LOST PROFITS,
115
+ REVENUE, DATA OR GOODWILL, THAT ARISE FROM THE SOFTWARE OR FROM THIS LICENSE,
116
+ EVEN IF IT HAS BEEN TOLD THAT THEY ARE POSSIBLE. NOTHING IN THIS LICENSE
117
+ LIMITS A LIABILITY THAT THE LAW DOES NOT ALLOW TO BE LIMITED.
118
+
119
+ 10. Questions
120
+
121
+ If you need to do something that this license does not allow, ask first:
122
+ support@widgetic.com.
package/README.md CHANGED
@@ -1,3 +1,185 @@
1
- # Temporary Holding Version
1
+ # @widgetic/cli
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The Widgetic command line. One binary, two namespaces:
4
+
5
+ - `widgetic api <group> <command>`: every operation of the public REST API.
6
+ - `widgetic mcp <command>`: every AI build tool of the Widgetic MCP server.
7
+
8
+ Every command is generated from the backend's own contracts, not written by hand, and every command takes `--json` (machine-readable output) and `--dry-run` (print the exact request, send nothing).
9
+
10
+ ## Quick start
11
+
12
+ Needs Node 22.12 or later.
13
+
14
+ ```bash
15
+ npm install -g @widgetic/cli
16
+
17
+ widgetic auth login # asks for an API key without showing it, checks it, saves it
18
+ widgetic doctor # the install, the key, the REST API and the MCP server
19
+ widgetic api widgets get-widgets # a REST call
20
+ widgetic mcp create-widget --title "Launch countdown" --brief "A countdown to 14 March 2027" --wait
21
+ ```
22
+
23
+ `widgetic --help`, `widgetic api --help`, `widgetic api <group> --help` and `widgetic <command> --help` list what is there. The whole reference, one page per group, is generated into [`docs/commands`](docs/commands/index.mdx).
24
+
25
+ ## The commands
26
+
27
+ | Command | What it does |
28
+ | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
29
+ | `widgetic api <group> <command>` | One REST operation. The group is the tag of the OpenAPI description, the command is its operation (`get-widgets`, `create-widget`). |
30
+ | `widgetic mcp <command>` | One MCP tool (`create_widget` is `widgetic mcp create-widget`). |
31
+ | `widgetic api call <operationId>`, `widgetic mcp call <tool>` | Run a command by the name its contract gives it. It also reaches the public embed operations, which `--help` does not list. |
32
+ | `widgetic schema api <group> <command>`, `widgetic schema mcp <command>` | The whole description of one command and the JSON Schema its input is checked against: the cheap way for an agent to learn one command. |
33
+ | `widgetic auth login`, `logout`, `status` | Save, forget and check the API key. |
34
+ | `widgetic doctor` | Check the CLI, the key and both servers, and say what is wrong (exit 1 when a check fails; `--json` prints every check). |
35
+ | `widgetic update` | Look on npm for a newer version of the CLI, show it beside the installed one, and install it after a yes (see [Updates](#updates)). |
36
+ | `widgetic completion bash\|zsh\|fish` | The script that completes commands and flags with the Tab key. |
37
+
38
+ ### Every command takes
39
+
40
+ | Flag | What it does |
41
+ | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
42
+ | `--json` | The result, and only the result, as one line of JSON on stdout. An error is one line on stderr: `{"error":{"code","message","status","requestId"}}`. It never asks anything. |
43
+ | `--dry-run` | Print the exact request (the API key is left out) and send nothing. It needs no key. |
44
+ | `--yes` | Go ahead without asking. Only a command that deletes or changes something asks (on a terminal), and without a terminal it needs `--yes`. |
45
+ | `--profile <name>` | The saved key to use (default: `WIDGETIC_PROFILE`, else `default`). |
46
+ | `--timeout <seconds>` | Give up on a call after this long (default 60). |
47
+ | `--data` / `--input` | The whole request document as JSON, `@file.json`, or `-` for standard input. The flags of the command override its keys. |
48
+
49
+ What goes on stdout with `--json`: for a REST command the response body as the server sent it; for an MCP tool its `structuredContent`.
50
+
51
+ ### Build tools
52
+
53
+ The tools that build a widget (`create-widget`, `replicate-widget`, `revise-widget`) answer at once and the build goes on in the background. `--wait` keeps asking `get_widget` until the build has finished (up to 15 minutes) and prints the finished widget; a build that fails or is cancelled ends with exit code 5 and the widget in the error. A tool that takes `publish` sends `publish: false` unless you pass `--publish`, so what the CLI builds stays a draft.
54
+
55
+ ### Retries
56
+
57
+ A call is tried at most three times. A `429` is always waited out (a `Retry-After` of up to 30 seconds is honoured; a longer one is reported with exit code 6). A dropped connection, a timeout, a `502`, `503` or `504` is tried again only when repeating cannot do anything twice: the call reads, or it carries an idempotency key. The CLI makes the key of a call that creates something, and sends the same one on every try.
58
+
59
+ ## What it can reach
60
+
61
+ The CLI is generated from the **public** contracts and from nothing else. `@widgetic/api-contracts` carries the public OpenAPI description (the file the partners' SDKs, `@widgetic/api-sdk-ts` and its siblings, are generated from, and the published API reference) and the MCP tool contract. It is not generated from the private description that Widgetic's own apps use (`@widgetic/api-sdk`, which also lists the administrator and internal operations), so none of those is a command. A mistake in the gateway cannot add one: the package is only built from a description in which every operation is marked `x-visibility: public` and has a level that an API key passes, and the build stops at the first operation that is not.
62
+
63
+ | Level of the operation (`x-access-level`) | Who calls it | In the CLI |
64
+ | ----------------------------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
65
+ | `user` | Any API key: your widgets, your account | Listed. Its help names no special key. |
66
+ | `partner` | A partner key (`wgt_prt_…`): managed users, credits, storage, analytics | Listed. Its help says `Needs a partner API key (wgt_prt_…).` |
67
+ | `public` | Browsers, with no key: the runtime of an embed | Left out of `--help`; `widgetic api call <operationId>` runs it. No key is sent. |
68
+
69
+ The CLI does not decide who may do what. The API does, on every call, from the key: a key without the scope or the level gets a 403 (exit code 3), the same answer as from the SDK or from `curl`. The CLI only makes what the key may already do easy to find and to call.
70
+
71
+ ## The key
72
+
73
+ A command that needs a key uses, in this order:
74
+
75
+ 1. `WIDGETIC_API_KEY` in the environment: used as it is, never written anywhere. Use it in CI and in agents.
76
+ 2. The key of the profile that `widgetic auth login` saved in `credentials.json` (mode 0600, in a folder of mode 0700).
77
+
78
+ `widgetic auth login` reads the key from a hidden prompt, or from standard input with `--with-key` (`echo "$WIDGETIC_API_KEY" | widgetic auth login --with-key`); a key is never taken from an argument, because an argument is in the shell history. It checks the key with the server before it saves it. The help of a command says when it needs a partner key (`wgt_prt_…`) or none at all.
79
+
80
+ The key is sent to the API's own address and to nowhere else: a redirect to another origin does not carry it along ([`test/key-stays-home.test.ts`](test/key-stays-home.test.ts) holds that with the real `fetch`).
81
+
82
+ ### Secrets in a command
83
+
84
+ A value the contract marks as a secret (the key of an adapter, the secret half of a storage key, a database connection string: `format: password` or `writeOnly`) is never typed as the text of a flag, because what is typed stays in the shell's history and in the list of processes. Its flag takes `@path` (a file) or `-` (a hidden prompt on a terminal, standard input otherwise):
85
+
86
+ ```bash
87
+ widgetic api partners set-partner-adapter-connection --partner-id <id> --adapter-id <adapter> --credential-key <name> --credential @./key.txt
88
+ pass show provider/key | widgetic api partners set-partner-adapter-connection --partner-id <id> --adapter-id <adapter> --credential-key <name> --credential -
89
+ ```
90
+
91
+ A secret typed as the text of the flag is refused (exit code 2), nothing is sent, and the error does not repeat it. `--dry-run` prints `[redacted]` in its place, whether it came from a flag or from the document of `--data`. Which values are secrets is the contract's word, not a list in the CLI ([`src/secrets.ts`](src/secrets.ts)): the generator stops at a secret it has no safe way to take (one in a URL, a header, a form field or an MCP tool argument).
92
+
93
+ ### Environment variables
94
+
95
+ | Variable | What it is |
96
+ | ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
97
+ | `WIDGETIC_API_KEY` | The API key. The one secret. |
98
+ | `WIDGETIC_PROFILE` | The profile of the saved key when `--profile` is not given. |
99
+ | `WIDGETIC_API_URL` | The REST address, to call another environment (https; http only for localhost). Default: the server of the OpenAPI description. |
100
+ | `WIDGETIC_MCP_URL` | The MCP address. Default: `https://mcp.widgetic.com/`. |
101
+ | `WIDGETIC_CONFIG_DIR` | The folder of the CLI's own files. Default: `~/.config/widgetic` (`$XDG_CONFIG_HOME/widgetic`, `%APPDATA%\widgetic` on Windows). |
102
+ | `WIDGETIC_DEBUG=1` | Print a stack with an unexpected error, and say why the CLI did not look for a newer version. |
103
+ | `NO_UPDATE_NOTIFIER`, `WIDGETIC_NO_UPDATE_CHECK` | Set to anything but `0`, `false`, `no` or `off`: never look for a newer version or ask about one (`CI` does the same). |
104
+ | `npm_config_registry` | The registry that the look for a newer version asks (default `https://registry.npmjs.org`); npm's own variable, https or http for localhost. |
105
+
106
+ ## Exit codes
107
+
108
+ | Code | Meaning |
109
+ | ---- | -------------------------------------------------------------------------------------- |
110
+ | 0 | ok |
111
+ | 1 | anything else (a bug, an unexpected 5xx, a build that did not finish in time) |
112
+ | 2 | usage or validation: the command line or a JSON document is wrong; nothing sent |
113
+ | 3 | authentication or plan: no key, 401, 402, 403 |
114
+ | 4 | not found (404, 410) |
115
+ | 5 | rejected by the server (400, 409, 422, other 4xx; an MCP tool result with error) |
116
+ | 6 | rate limited or unavailable (429, 502, 503, 504, timeout, no network) |
117
+ | 7 | this CLI is too old (426): `widgetic update`, or `npm install -g @widgetic/cli@latest` |
118
+
119
+ ## Updates
120
+
121
+ After a command, at a terminal, the CLI says when a newer version is on npm, shows both versions and asks:
122
+
123
+ ```text
124
+ A new version of the Widgetic CLI is available: 0.1.0 → 0.2.0
125
+ Update now? (runs `npm install -g @widgetic/cli@0.2.0`) [y/N]
126
+ ```
127
+
128
+ - **It never gets in the way.** The look for the newest version is one small request to `https://registry.npmjs.org/@widgetic%2fcli/latest` (no key and nothing from your account: npm sees your address and the user agent `@widgetic/cli/<version> (update check)`). It starts with the command and runs beside it, with a 2 second limit, and its answer is kept for 24 hours (1 hour after a failure) under `updateCheck` in `state.json`. The question comes after the command has printed its result, on stderr, and nothing that happens in it changes the command's exit code.
129
+ - **It asks a person, and only a person.** Not with `--json` or `--yes`; not when standard input, output or errors is not a terminal; not in CI (`CI`); not with `NO_UPDATE_NOTIFIER` or `WIDGETIC_NO_UPDATE_CHECK` set; not for `--help`, `--version`, `help`, `completion` (the shell calls it on every Tab) or `update`; and not for a source checkout or a copy that `npx` fetched to run once. `WIDGETIC_DEBUG=1` says which of these applied.
130
+ - **The default is no.** Enter, Ctrl-C and 20 seconds of silence are all "no". A "no" is remembered for 7 days for that version, and a newer version asks at once. A server that refuses the CLI as too old (exit code 7) always gets the question.
131
+ - **It installs what it showed.** On yes it runs the global install command of the package manager that installed this copy (`npm install -g`, `pnpm add -g`, `yarn global add` or `bun add -g`), pinned to the version it showed, and then reads the installed copy again to check that it changed. A copy it cannot place (inside a project's `node_modules`, a custom prefix) is not touched: the CLI prints the new version and the command to run.
132
+
133
+ `widgetic update` does the same whenever you ask. It asks npm itself instead of using what was kept; `--check` only reports, `--yes` installs without asking, and `--json` prints `{package, current, latest, updateAvailable, updated, installedWith, command}` and never asks (add `--yes` to install). The code is in [`src/update`](src/update) and [`src/commands/update.ts`](src/commands/update.ts); the question lives in `runProcess` ([`src/main.ts`](src/main.ts)), so the commands themselves, and their tests, never ask it.
134
+
135
+ ## Where the commands come from
136
+
137
+ The gateway (`Services/api-gateway-service`) builds a package, [`@widgetic/api-contracts`](https://www.npmjs.com/package/@widgetic/api-contracts): the public OpenAPI description and the MCP tool contract, version-stamped with the gateway's own version. This repository keeps **no copy** of either.
138
+
139
+ - `npm run generate` reads the installed `@widgetic/api-contracts` and writes `generated/` (git-ignored): the REST command manifest, the MCP command manifest and a small record of the contracts' versions.
140
+ - `npm run build` generates, then compiles `src/` to `dist/`. The package ships `dist/` and `generated/`.
141
+ - At run time the CLI builds its commands from `generated/`. A new endpoint or a new MCP tool is a new command the day the contracts package is bumped and the CLI is released; nobody writes it.
142
+
143
+ ### Keeping the CLI and the MCP in step
144
+
145
+ There is nothing to keep in step by hand: the commands are a function of the contracts, and four guards fail when they are not.
146
+
147
+ - **Tests.** `test/command-snapshot.test.ts` holds the command tree (names, flags, groups) to a checked-in snapshot, so a new endpoint, a renamed tool or a changed flag shows up as a diff somebody reads; `test/command-reference.test.ts` does the same for the generated reference in `docs/commands`; the generator tests fail on an operation or a tool that has no command, or two with one name.
148
+ - **The release gate** (`npm run check:release`, run by the publish script before the version is raised, and by the `publish` job of `.gitlab-ci.yml`). It fails unless the contracts the commands were generated from are a release, are the newest `latest` of `@widgetic/api-contracts` on npm, and the tool names and the tool contract version are the ones production's MCP `tools/list` answers. It asks production with the key of `WIDGETIC_API_KEY` or of `widgetic auth login`; a read-only key is enough. Nothing is published and no version is raised when it fails.
149
+ - **At run time.** When an MCP command opens a session and the server's tool contract is not the one the CLI was built for, the CLI says so once in 24 hours on stderr (never with `--json`), and `widgetic doctor` always shows the two versions.
150
+ - **The server can refuse an old CLI.** Every request carries `X-SDK-Version: @widgetic/cli/<version>` (MCP: the client's `clientInfo`). The gateway counts the versions it sees and can answer `426` to one that is too old: exit code 7.
151
+
152
+ ## Development
153
+
154
+ Needs Node 22.18 or later (the scripts run TypeScript directly with Node's type stripping; the published package is plain JavaScript and needs Node 22.12).
155
+
156
+ ```bash
157
+ npm install
158
+ npm run check # typecheck, tests, prettier
159
+ npm run build # dist/ (and generated/)
160
+ npm run docs # write docs/commands from the installed contracts
161
+ npm run snapshot # write the command snapshot from the installed contracts
162
+ npm run check:release # the release gate (needs the network and a key)
163
+ ```
164
+
165
+ Tests run the real program in-process against a fake environment (`test/helpers/fake-environment.ts`): a fetch that answers from a function and records what it was asked, streams that collect the output, no terminal, no real network, and a package manager that is a function (nothing is ever installed by a test). A fake MCP server (`test/helpers/fake-mcp-server.ts`) answers the MCP tests. The question on the terminal is the one thing the fake replaces whole, so `test/terminal.test.ts` runs the real one on streams of its own.
166
+
167
+ ### A new version of the contracts
168
+
169
+ ```bash
170
+ npm update @widgetic/api-contracts # the gateway's release put a new version on npm
171
+ npm run generate && npm run snapshot && npm run docs
172
+ npm run check # the diff of the snapshot and of docs/commands is the change
173
+ ```
174
+
175
+ Commit the lock file, the snapshot and `docs/commands` together. To try a change of the gateway's description before it is published, build the contracts there (`npm run build-api-contracts`) and put them here with `npm run contracts:local -- <folder or tarball>`; it changes nothing but `node_modules`.
176
+
177
+ ### Release
178
+
179
+ `npm run publish-to-npm` (the script of `Frontend/dev-tools`) merges what is on `origin`, runs `npm run check` and `npm run check:release`, raises the patch version, builds, publishes with `npm publish --ignore-scripts --access public`, commits the version, tags `v<version>` and pushes. A release is the owner's decision: nothing publishes by itself, and the `publish` job of the pipeline is manual.
180
+
181
+ Before a release, copy `docs/commands/*.mdx` into the docs site (`Frontend/docs-site/docs/cli/commands/`) and list the pages in its `docs.json`.
182
+
183
+ ## Licence
184
+
185
+ Widgetic Integration License: see `LICENSE`.
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ // The `widgetic` command. All of it is in dist/ (built from src/); this file only starts it.
3
+ import { main } from '../dist/main.js';
4
+
5
+ await main();
package/dist/cli.js ADDED
@@ -0,0 +1,106 @@
1
+ import { Command, CommanderError } from 'commander';
2
+ import { registerAuthCommands } from "./commands/auth.js";
3
+ import { registerApiCall, registerMcpCall } from "./commands/call.js";
4
+ import { registerCompletionCommands } from "./commands/completion.js";
5
+ import { registerDoctorCommand } from "./commands/doctor.js";
6
+ import { registerSchemaCommand } from "./commands/schema.js";
7
+ import { registerUpdateCommand } from "./commands/update.js";
8
+ import { CliError, EXIT_CODES, usageError } from "./errors.js";
9
+ import { loadMcpManifest, loadRestManifest } from "./manifest/load.js";
10
+ import { registerMcpCommands } from "./mcp/register.js";
11
+ import { asCliError, printError } from "./output.js";
12
+ import { registerRestCommands } from "./rest/register.js";
13
+ import { cliVersion } from "./version.js";
14
+ /**
15
+ * The program: the root `widgetic` command and what hangs from it. `run` parses an argument list against an
16
+ * Environment and returns the exit code; nothing here calls `process.exit`, so a test runs the real program in-process.
17
+ */
18
+ /**
19
+ * Commander codes that mean "nothing went wrong, the text was printed": --help, --version, and the help of a command
20
+ * that was run without its subcommand (`widgetic api` lists what is under `api`, it is not a failure).
21
+ */
22
+ const CLEAN_COMMANDER_CODES = new Set(['commander.helpDisplayed', 'commander.version', 'commander.help']);
23
+ export function createRootCommand(environment) {
24
+ const root = new Command('widgetic');
25
+ root
26
+ .description('The Widgetic command line: the public REST API and the AI build tools, in one binary.')
27
+ .version(cliVersion(), '-V, --version', 'print the version of this CLI')
28
+ // A subcommand has its own options (a tool may have a `--version` of its own), so the root's are read only
29
+ // before the subcommand: `widgetic mcp publish-widget --version 3` is the tool's flag, never the CLI's.
30
+ .enablePositionalOptions()
31
+ .allowExcessArguments(false)
32
+ .showSuggestionAfterError(true)
33
+ .exitOverride()
34
+ .configureOutput({
35
+ writeOut: (text) => environment.stdout.write(text),
36
+ // Commander writes to this stream only to show the help of a command that was run without its subcommand
37
+ // (`widgetic api`). That is a question ("what can I do here?"), and its answer belongs on stdout.
38
+ writeErr: (text) => environment.stdout.write(text),
39
+ // Commander's own error line is not printed: `run` prints every error itself, the same way, so that
40
+ // `--json` gets JSON for a flag that is missing as well as for a server that refused.
41
+ outputError: () => { }
42
+ });
43
+ return root;
44
+ }
45
+ /**
46
+ * The namespace the arguments are about (`api` or `mcp`), when they are about one: the first word, or the one after
47
+ * `help`. Only that namespace's commands are built, so `widgetic mcp …` never reads the 500 KB REST manifest.
48
+ */
49
+ export function namespaceOf(argv) {
50
+ const words = argv.filter((word) => !word.startsWith('-'));
51
+ const first = words[0] === 'help' ? words[1] : words[0];
52
+ return first === 'api' || first === 'mcp' ? first : undefined;
53
+ }
54
+ /**
55
+ * The whole program for `argv`. The namespaces are listed in the help of `widgetic` itself, and their commands are
56
+ * added only for the one the arguments name; the commands that are not generated (`auth`, `doctor`, `update`,
57
+ * `schema`, `completion`) are always there.
58
+ */
59
+ export function buildProgram(environment, argv = []) {
60
+ const root = createRootCommand(environment);
61
+ const api = root
62
+ .command('api')
63
+ .description('Call the public REST API: one command for every operation of its OpenAPI description.');
64
+ const mcp = root
65
+ .command('mcp')
66
+ .description('Run the AI build tools of the Widgetic MCP server: one command for every tool.');
67
+ const namespace = namespaceOf(argv);
68
+ if (namespace === 'api') {
69
+ const manifest = loadRestManifest();
70
+ registerRestCommands(api, manifest, environment);
71
+ registerApiCall(api, manifest);
72
+ }
73
+ if (namespace === 'mcp') {
74
+ const manifest = loadMcpManifest();
75
+ registerMcpCommands(mcp, manifest, environment);
76
+ registerMcpCall(mcp, manifest, { environment, manifest });
77
+ }
78
+ registerAuthCommands(root, environment);
79
+ registerDoctorCommand(root, environment);
80
+ registerUpdateCommand(root, environment);
81
+ registerSchemaCommand(root, environment);
82
+ registerCompletionCommands(root, environment, (words) => buildProgram(environment, words));
83
+ return root;
84
+ }
85
+ /** What a Commander error becomes: `null` when it only means "the help or the version was printed". */
86
+ function fromCommanderError(error) {
87
+ if (CLEAN_COMMANDER_CODES.has(error.code))
88
+ return null;
89
+ const message = error.message.replace(/^error:\s*/i, '');
90
+ return usageError(message, 'Run the command with --help to see what it takes.');
91
+ }
92
+ export async function run(argv, environment) {
93
+ const json = argv.includes('--json');
94
+ try {
95
+ const program = buildProgram(environment, argv);
96
+ await program.parseAsync([...argv], { from: 'user' });
97
+ return EXIT_CODES.ok;
98
+ }
99
+ catch (error) {
100
+ const failure = error instanceof CommanderError ? fromCommanderError(error) : asCliError(error, environment);
101
+ if (failure === null)
102
+ return EXIT_CODES.ok;
103
+ printError(environment, failure, json);
104
+ return failure.exitCode;
105
+ }
106
+ }