@brass-build/cli 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/AGENTS.md +170 -0
- package/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/dist/api.d.ts +73 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +97 -0
- package/dist/api.js.map +1 -0
- package/dist/args.d.ts +10 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +94 -0
- package/dist/args.js.map +1 -0
- package/dist/auth.d.ts +5 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +12 -0
- package/dist/auth.js.map +1 -0
- package/dist/bin/brass.d.ts +3 -0
- package/dist/bin/brass.d.ts.map +1 -0
- package/dist/bin/brass.js +11 -0
- package/dist/bin/brass.js.map +1 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +364 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +94 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +559 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +40 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +76 -0
- package/dist/config.js.map +1 -0
- package/dist/log.d.ts +9 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +32 -0
- package/dist/log.js.map +1 -0
- package/dist/login.d.ts +21 -0
- package/dist/login.d.ts.map +1 -0
- package/dist/login.js +158 -0
- package/dist/login.js.map +1 -0
- package/dist/project.d.ts +32 -0
- package/dist/project.d.ts.map +1 -0
- package/dist/project.js +129 -0
- package/dist/project.js.map +1 -0
- package/dist/session.d.ts +47 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +224 -0
- package/dist/session.js.map +1 -0
- package/dist/store.d.ts +17 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +90 -0
- package/dist/store.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +8 -0
- package/dist/version.js.map +1 -0
- package/package.json +42 -0
- package/src/api.ts +195 -0
- package/src/args.ts +107 -0
- package/src/auth.ts +16 -0
- package/src/bin/brass.ts +11 -0
- package/src/cli.ts +422 -0
- package/src/commands.ts +864 -0
- package/src/config.ts +132 -0
- package/src/log.ts +41 -0
- package/src/login.ts +211 -0
- package/src/project.ts +176 -0
- package/src/session.ts +319 -0
- package/src/store.ts +123 -0
- package/src/version.ts +8 -0
package/AGENTS.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Instructions for AI coding agents
|
|
2
|
+
|
|
3
|
+
You are likely an AI coding agent publishing a Brass app you built against
|
|
4
|
+
`@brass-build/client`. This tool is how you ship it and how you pull a
|
|
5
|
+
schema to build against. The [README](./README.md) is the full reference;
|
|
6
|
+
this file is the short path for you.
|
|
7
|
+
|
|
8
|
+
## Follow your organization's coding instructions first
|
|
9
|
+
|
|
10
|
+
Before you start building, fetch the organization's own agentic-coding
|
|
11
|
+
instructions and follow them: the conventions it wants every app built on
|
|
12
|
+
Brass to follow (its `AGENTS.md` / `CLAUDE.md`). Run the pull rather than
|
|
13
|
+
deciding up front whether the org has any; nothing loads them for you, and an
|
|
14
|
+
empty result (the org set none) is something you learn by running it, not by
|
|
15
|
+
assuming it. Treat what it returns as an instruction file committed to the
|
|
16
|
+
repo.
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
npx @brass-build/cli agents pull --out AGENTS.md
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This writes the current org instructions to the file. Read it and apply it.
|
|
23
|
+
Keep it current one of two ways:
|
|
24
|
+
|
|
25
|
+
- **Always fetch (freshest):** run the pull at the start of each session and
|
|
26
|
+
re-read the file, so you are always on the latest instructions. Pull to a
|
|
27
|
+
gitignored path if you do not want to commit it, or `--stdout` to read the
|
|
28
|
+
body inline without writing a file at all (status stays on stderr, so
|
|
29
|
+
stdout is only the instructions).
|
|
30
|
+
- **Cache:** commit the pulled file and refresh it periodically. Add a
|
|
31
|
+
`CLAUDE.md` whose entire contents are the single line `@AGENTS.md` so
|
|
32
|
+
Claude Code picks it up too (Claude Code auto-loads `CLAUDE.md`, not
|
|
33
|
+
`AGENTS.md`); the command prints this reminder after a pull.
|
|
34
|
+
|
|
35
|
+
The pull needs a signed-in session (`brass login`); a service token is not an
|
|
36
|
+
organization member and cannot read the instructions. If you are not signed
|
|
37
|
+
in, run the sign-in yourself rather than skipping: `brass login --start`,
|
|
38
|
+
relay the printed URL and code to the human, then `brass login --check --wait`
|
|
39
|
+
until it reports approved (it is the same credential you will use to publish).
|
|
40
|
+
[Signing in](#signing-in) covers the whole flow, including what to do while
|
|
41
|
+
you wait for the approval.
|
|
42
|
+
`--org <organizationId>` is only needed when the human belongs to more than
|
|
43
|
+
one organization. An empty result means the organization has set no
|
|
44
|
+
instructions, and there is nothing to follow.
|
|
45
|
+
|
|
46
|
+
## Signing in
|
|
47
|
+
|
|
48
|
+
You cannot approve the sign-in, and the human approving it is not watching
|
|
49
|
+
your terminal. Start it, relay the code, then wait for the approval.
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
npx @brass-build/cli login --start # prints the approval URL + code
|
|
53
|
+
npx @brass-build/cli login --check --wait # polls until they approve
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`--start` prints an approval URL and a short code and exits. Give both to the
|
|
57
|
+
human in your next message, then run `--check --wait`. It polls for up to two
|
|
58
|
+
minutes (`--wait <seconds>` for a different bound) and stores the session the
|
|
59
|
+
moment they approve. A code that lapses is replaced and the new one printed,
|
|
60
|
+
so the sign-in outlives any single code and a session started early is still
|
|
61
|
+
good at publish time.
|
|
62
|
+
|
|
63
|
+
The session gates your organization's coding conventions, and those shape the
|
|
64
|
+
code you are about to write, so wait for it before you build rather than
|
|
65
|
+
building twice. If you do have work that does not depend on them, the wait
|
|
66
|
+
costs nothing to repeat: a bound reached with the code still approvable
|
|
67
|
+
reports `pending`, and the next `--check --wait` picks the same sign-in back
|
|
68
|
+
up where it left off.
|
|
69
|
+
|
|
70
|
+
Read the `state` in its `--json` result:
|
|
71
|
+
|
|
72
|
+
- `approved`: the session is stored, and every later command uses it.
|
|
73
|
+
- `pending`: the code you relayed is still good. Run the check again.
|
|
74
|
+
- `renewed`: the code changed. Relay the new one from the same output.
|
|
75
|
+
- `denied`: the human refused. Ask them why before starting another.
|
|
76
|
+
|
|
77
|
+
Run `brass login --start` again only when there is nothing in flight; it
|
|
78
|
+
resumes the sign-in already waiting rather than issuing a second code, so a
|
|
79
|
+
half-finished approval still completes. `brass status` reports an in-flight
|
|
80
|
+
sign-in and how long it has left, so you never have to guess.
|
|
81
|
+
|
|
82
|
+
## Publishing an app you built
|
|
83
|
+
|
|
84
|
+
Start with `brass status`, and act on the `Next:` line it prints:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
npx @brass-build/cli status
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
It reports the credential and app state for this environment and names the
|
|
91
|
+
one command to run next (obtain a credential, create the app on a first
|
|
92
|
+
publish, deploy, or open the live URL). Run it, do what it says, then run it
|
|
93
|
+
again. The steps below are what it walks you through:
|
|
94
|
+
|
|
95
|
+
1. Build the app to a static bundle (its `brass-app.json` capability
|
|
96
|
+
manifest must be in the output).
|
|
97
|
+
2. Make sure a credential is available: `BRASS_SERVICE_TOKEN` in the
|
|
98
|
+
environment (or `--token`) for CI, or a `brass login` session for local
|
|
99
|
+
development. When `brass status` reports none, run the sign-in yourself,
|
|
100
|
+
as [Signing in](#signing-in) describes.
|
|
101
|
+
Starting it, relaying the URL, and checking it through to approved is your
|
|
102
|
+
step, not a handoff you stop at. (Or the human mints a service token in
|
|
103
|
+
the dashboard: org Settings, then Service tokens.)
|
|
104
|
+
3. Publish:
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
npx @brass-build/cli publish ./dist
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
The first run creates the app and writes `.brass/project.json`; commit
|
|
111
|
+
that so re-runs target the same app. A service token creates the app in
|
|
112
|
+
its own organization, so no `--org` is needed. If the pipeline does not
|
|
113
|
+
persist `.brass/project.json` between runs, set a stable `client_token`
|
|
114
|
+
(a `"client_token"` in `brass-app.json`, or `--client-token`) so the
|
|
115
|
+
first create is idempotent and re-runs resolve the same app.
|
|
116
|
+
|
|
117
|
+
Run with `--json` to get the result (`app_id`, `url`) as JSON on stdout for
|
|
118
|
+
your own parsing. A non-zero exit means the publish failed; the reason is on
|
|
119
|
+
stderr.
|
|
120
|
+
|
|
121
|
+
## Pulling a schema to build against
|
|
122
|
+
|
|
123
|
+
To interoperate with documents another app or importer produces, do not
|
|
124
|
+
hand-write the schema. Open one of its documents and pull the real body:
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
npx @brass-build/cli schema pull --doc <docId> --out brass-app.json
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
This writes the schema verbatim into your manifest. Build your types against
|
|
131
|
+
that copy; do not re-approximate it from memory (a mismatched `required`
|
|
132
|
+
shape makes the platform read your app as a different shape). This is a
|
|
133
|
+
development-time step that needs a signed-in session (`brass login`), not a
|
|
134
|
+
service token; if you only have `BRASS_SERVICE_TOKEN`, ask the human to run
|
|
135
|
+
`brass login` or to copy the schema from the document's dashboard page.
|
|
136
|
+
|
|
137
|
+
## Verifying
|
|
138
|
+
|
|
139
|
+
`npx @brass-build/cli status` is the check to run before and after a publish:
|
|
140
|
+
it confirms the credential authenticates and reports whether the app is
|
|
141
|
+
deployed, with the next step to take. `npx @brass-build/cli whoami` is the
|
|
142
|
+
narrower check when you only need to confirm the credential works. After a
|
|
143
|
+
publish, open the reported URL to confirm the app loads.
|
|
144
|
+
|
|
145
|
+
## When a command fails
|
|
146
|
+
|
|
147
|
+
Read what the error says before drawing a conclusion about the machine you
|
|
148
|
+
are on. The CLI reports the state it observed, and the states have different
|
|
149
|
+
fixes:
|
|
150
|
+
|
|
151
|
+
- **A 401 or 403** means the credential is expired, missing, or not allowed.
|
|
152
|
+
A session expires, so `brass login` is the fix; a service token that is
|
|
153
|
+
rejected needs a valid `BRASS_SERVICE_TOKEN`. A fresh checkout or a fresh
|
|
154
|
+
container has no session at all, which is the ordinary starting state, not
|
|
155
|
+
a broken one.
|
|
156
|
+
- **"Network error reaching <url>"** is the only error meaning the request
|
|
157
|
+
never completed. It names the host it tried.
|
|
158
|
+
- **Any other status** is a fault on the server side. The credential is
|
|
159
|
+
still good and retrying is reasonable.
|
|
160
|
+
|
|
161
|
+
An HTTP status coming back at all proves the host answered you. So confirm
|
|
162
|
+
reachability directly before reporting that Brass is unreachable:
|
|
163
|
+
|
|
164
|
+
```sh
|
|
165
|
+
curl -sS -o /dev/null -w '%{http_code}\n' https://api.brass.build/health
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`/health` requires a credential, so an unauthenticated `401` here is a
|
|
169
|
+
reachable API. Run `brass status` for the full credential and app state
|
|
170
|
+
along with the next command to run.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@brass-build/cli` are recorded here. The format
|
|
4
|
+
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
|
|
5
|
+
package follows [semantic versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
While the version is below `1.0.0`, minor releases may introduce breaking
|
|
7
|
+
changes.
|
|
8
|
+
|
|
9
|
+
## 0.1.0
|
|
10
|
+
|
|
11
|
+
Initial public release. The `brass` command-line tool publishes apps and
|
|
12
|
+
pulls schemas from a terminal or CI. Ships as an ESM-only package.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Brass Software LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# @brass-build/cli
|
|
2
|
+
|
|
3
|
+
From a terminal or a CI pipeline, the `brass` command publishes a Brass
|
|
4
|
+
app's built bundle and pulls document schemas. It wraps the same data API the
|
|
5
|
+
dashboard uses, authenticated by a token instead of a browser session, so
|
|
6
|
+
the whole publish workflow runs unattended.
|
|
7
|
+
|
|
8
|
+
This package tracks the platform's `0.1.x` line.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
No install step. Run it with `npx`:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npx @brass-build/cli publish ./dist
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Authenticate
|
|
19
|
+
|
|
20
|
+
Two kinds of credential, for two audiences:
|
|
21
|
+
|
|
22
|
+
- A **signed-in session** from `brass login` is the developer credential.
|
|
23
|
+
It opens your browser, you approve once, and the CLI stores the session
|
|
24
|
+
under `~/.config/brass/`. It authorizes everything, including `schema pull`.
|
|
25
|
+
- A **service token** (mint one in the dashboard: org Settings, then Service
|
|
26
|
+
tokens) is the CI credential, supplied as `BRASS_SERVICE_TOKEN`. It
|
|
27
|
+
authorizes `publish` and `whoami`. It is an org-scoped machine identity
|
|
28
|
+
and, by design, cannot fetch schemas.
|
|
29
|
+
|
|
30
|
+
```sh
|
|
31
|
+
npx @brass-build/cli login # developer: sign in with your browser
|
|
32
|
+
export BRASS_SERVICE_TOKEN=brass_sk_... # CI: a pipeline secret
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`login` opens your browser to an approval page showing a short code, prints
|
|
36
|
+
the same URL and code to the terminal, and completes on its own once you
|
|
37
|
+
approve. Confirm the code on the page matches the one in your terminal, then
|
|
38
|
+
approve. On a machine with no browser (a remote box, an SSH session), open the
|
|
39
|
+
printed URL from any other device.
|
|
40
|
+
|
|
41
|
+
For automation that cannot hold a command open while you approve (an AI
|
|
42
|
+
agent driving the sign-in), the flow splits in two: `brass login --start`
|
|
43
|
+
prints the approval URL and code and exits immediately, and
|
|
44
|
+
`brass login --check` checks that sign-in, storing the session once you have
|
|
45
|
+
approved. Add `--wait` to poll until you approve, for two minutes by default
|
|
46
|
+
or `--wait <seconds>` for a different bound; a code that lapses inside the
|
|
47
|
+
wait is replaced and the new one printed, so a sign-in started long before it
|
|
48
|
+
is needed still completes. Reaching the bound with the code still good is
|
|
49
|
+
reported as `pending` and exits 0, so the next check picks the same sign-in
|
|
50
|
+
back up. A second `--start` resumes the sign-in already waiting instead of
|
|
51
|
+
issuing a second code, and `--new` overrides that when you have lost the
|
|
52
|
+
first. Both phases honor `--json` for a machine-readable result on stdout.
|
|
53
|
+
|
|
54
|
+
The CLI picks a credential most-explicit first: `--token <token>`, then
|
|
55
|
+
`BRASS_SERVICE_TOKEN`, then the stored `brass login` session. `brass logout`
|
|
56
|
+
forgets the stored session.
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
export BRASS_SERVICE_TOKEN=brass_sk_...
|
|
60
|
+
npx @brass-build/cli whoami
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Status
|
|
64
|
+
|
|
65
|
+
`status` reports the credential and app state for the target environment and
|
|
66
|
+
the one command to run next:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
npx @brass-build/cli status
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
It confirms whether your credential authenticates, whether an app id is
|
|
73
|
+
resolved for this directory, and whether that app is deployed, then prints a
|
|
74
|
+
`Next:` line naming the exact next step (sign in, publish to create the app,
|
|
75
|
+
deploy, or open the live URL). When a `brass login --start` sign-in is waiting
|
|
76
|
+
for approval, it reports the code, the approval URL, and how long the code has
|
|
77
|
+
left. Run it before a publish to see what will
|
|
78
|
+
happen, and after one to confirm the app is live. `whoami` is the narrower
|
|
79
|
+
check when you only need to know the credential works. Add `--json` for the
|
|
80
|
+
machine-readable result on stdout.
|
|
81
|
+
|
|
82
|
+
## Publish
|
|
83
|
+
|
|
84
|
+
`publish` deploys a built static bundle to the app's Brass hosting
|
|
85
|
+
(`https://<slug>.onbrass.app`):
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
npx @brass-build/cli publish ./dist
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
On the first publish with no app yet, it creates one (using `--name`, or the
|
|
92
|
+
`name` in `brass-app.json`) and records the new app id in `.brass/project.json`
|
|
93
|
+
so later publishes target the same app. Commit that file, or pin the app
|
|
94
|
+
explicitly with `--app <appId>` or `BRASS_APP_ID` for a stateless pipeline.
|
|
95
|
+
|
|
96
|
+
For a pipeline that stands the app up from scratch and does not persist
|
|
97
|
+
`.brass/project.json` between runs, set a stable `client_token` (a
|
|
98
|
+
`"client_token"` in `brass-app.json`, or `--client-token`). Repeated first
|
|
99
|
+
publishes then resolve the same app instead of creating a duplicate each run.
|
|
100
|
+
A service token creates the app in its own organization, so no `--org` is
|
|
101
|
+
needed.
|
|
102
|
+
|
|
103
|
+
The command enables hosting if needed, uploads the bundle, waits for it to go
|
|
104
|
+
live, and reports the URL. The platform reads your app's capabilities
|
|
105
|
+
(`opens` / `creates` / `schema`) from the `/.well-known/brass-app.json` you
|
|
106
|
+
serve, so keep that manifest in the bundle.
|
|
107
|
+
|
|
108
|
+
Flags: `--app`, `--name`, `--org` (organization to own a newly created app;
|
|
109
|
+
defaults to a service token's own org), `--client-token` (stable idempotency
|
|
110
|
+
key for a first create), `--slug` (preferred subdomain), `--manifest`
|
|
111
|
+
(default `brass-app.json`).
|
|
112
|
+
|
|
113
|
+
## Pull a schema
|
|
114
|
+
|
|
115
|
+
To build against a shape another app or importer produces, open one of its
|
|
116
|
+
documents and copy its schema into your manifest:
|
|
117
|
+
|
|
118
|
+
```sh
|
|
119
|
+
npx @brass-build/cli schema pull --doc <docId> --out brass-app.json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
This writes the document's schema body verbatim into the manifest's `schema`
|
|
123
|
+
field, preserving everything else. That is the same copy-verbatim step the
|
|
124
|
+
SDK's schema guide describes, done for you. It is a development-time action
|
|
125
|
+
that needs a signed-in session (`brass login`); a service token cannot fetch
|
|
126
|
+
schemas.
|
|
127
|
+
|
|
128
|
+
## Pull your organization's agent instructions
|
|
129
|
+
|
|
130
|
+
If your organization manages a shared set of agentic-coding instructions (its
|
|
131
|
+
AGENTS.md / CLAUDE.md), pull the current body into your repo:
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
npx @brass-build/cli agents pull --out AGENTS.md
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The instructions are stored once at the organization level (admins edit them
|
|
138
|
+
on the dashboard Settings tab) so every developer's coding agent works from
|
|
139
|
+
one source of truth. The pull writes the body verbatim, so it round-trips
|
|
140
|
+
byte-for-byte with what the dashboard stored. `--org <organizationId>` is
|
|
141
|
+
optional when you belong to a single organization; pass it when you belong to
|
|
142
|
+
more than one. This needs a signed-in session (`brass login`); a service token
|
|
143
|
+
is not an organization member, so it cannot read the instructions.
|
|
144
|
+
|
|
145
|
+
Pass `--stdout` instead of `--out` to print the instructions to stdout
|
|
146
|
+
without writing a file (status goes to stderr, so stdout carries only the
|
|
147
|
+
body). This is the always-fetch path: a coding agent reads the current
|
|
148
|
+
instructions inline each session and caches nothing.
|
|
149
|
+
|
|
150
|
+
The default `--out` is `AGENTS.md`, the cross-agent convention that Cursor,
|
|
151
|
+
Codex, and others read. **Claude Code reads `CLAUDE.md`, not `AGENTS.md`**, so
|
|
152
|
+
to use these instructions there either pull straight to it
|
|
153
|
+
(`agents pull --out CLAUDE.md`) or add a `CLAUDE.md` that imports the pulled
|
|
154
|
+
file with a single line, `@AGENTS.md`. The command prints this reminder
|
|
155
|
+
whenever you pull to any file other than `CLAUDE.md`.
|
|
156
|
+
|
|
157
|
+
## CI example
|
|
158
|
+
|
|
159
|
+
```yaml
|
|
160
|
+
- run: npm ci && npm run build
|
|
161
|
+
- run: npx @brass-build/cli publish ./dist
|
|
162
|
+
env:
|
|
163
|
+
BRASS_SERVICE_TOKEN: ${{ secrets.BRASS_SERVICE_TOKEN }}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
The service token creates the app in its own organization on the first run.
|
|
167
|
+
Give `brass-app.json` a stable `"client_token"` so that first create is
|
|
168
|
+
idempotent (repeated runs resolve the same app rather than duplicating it),
|
|
169
|
+
or commit `.brass/project.json` to pin the app id.
|
|
170
|
+
|
|
171
|
+
Add `--json` to any command to emit the machine-readable result on stdout
|
|
172
|
+
(human status stays on stderr).
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { AuthProvider } from './auth.js';
|
|
2
|
+
export interface BrassSchemaManifest {
|
|
3
|
+
family: string;
|
|
4
|
+
streams: Record<string, Record<string, unknown>>;
|
|
5
|
+
}
|
|
6
|
+
export type AppVisibility = 'private' | 'invitee_visible' | 'public';
|
|
7
|
+
export interface AppDetail {
|
|
8
|
+
app_id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
owner_organization_id?: string;
|
|
11
|
+
visibility?: AppVisibility;
|
|
12
|
+
}
|
|
13
|
+
export interface HostingStatus {
|
|
14
|
+
enabled: boolean;
|
|
15
|
+
slug: string | null;
|
|
16
|
+
url: string | null;
|
|
17
|
+
deployed: boolean;
|
|
18
|
+
active_version: string | null;
|
|
19
|
+
require_access?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface HostingUploadUrl {
|
|
22
|
+
upload_url: string;
|
|
23
|
+
version_id: string;
|
|
24
|
+
expires_in: number;
|
|
25
|
+
}
|
|
26
|
+
export type HostingVersionStatus = 'pending' | 'ready' | 'failed';
|
|
27
|
+
export interface HostingVersion {
|
|
28
|
+
version_id: string;
|
|
29
|
+
status: HostingVersionStatus;
|
|
30
|
+
failure_reason?: string;
|
|
31
|
+
active: boolean;
|
|
32
|
+
content_hash?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface DocumentStreams {
|
|
35
|
+
streams: {
|
|
36
|
+
name: string;
|
|
37
|
+
schema?: Record<string, unknown>;
|
|
38
|
+
}[];
|
|
39
|
+
predicted?: true;
|
|
40
|
+
}
|
|
41
|
+
export interface DocumentTypeSummary {
|
|
42
|
+
schema_type?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface AgentInstructionsResponse {
|
|
45
|
+
content: string;
|
|
46
|
+
updated_at?: string;
|
|
47
|
+
updated_by?: string;
|
|
48
|
+
}
|
|
49
|
+
export interface OrganizationSummary {
|
|
50
|
+
organization_id: string;
|
|
51
|
+
name: string;
|
|
52
|
+
}
|
|
53
|
+
export interface RefreshCapabilitiesResponse {
|
|
54
|
+
app_id: string;
|
|
55
|
+
manifest_origin: string;
|
|
56
|
+
opens: string[];
|
|
57
|
+
warnings?: string[];
|
|
58
|
+
}
|
|
59
|
+
export declare class BrassApiError extends Error {
|
|
60
|
+
readonly status: number;
|
|
61
|
+
constructor(status: number, message: string);
|
|
62
|
+
}
|
|
63
|
+
export declare class BrassApi {
|
|
64
|
+
private readonly apiBaseUrl;
|
|
65
|
+
private readonly auth;
|
|
66
|
+
constructor(apiBaseUrl: string, auth: AuthProvider);
|
|
67
|
+
get<T>(path: string): Promise<T>;
|
|
68
|
+
post<T>(path: string, body?: unknown): Promise<T>;
|
|
69
|
+
patch<T>(path: string, body?: unknown): Promise<T>;
|
|
70
|
+
private request;
|
|
71
|
+
}
|
|
72
|
+
export declare function putPresigned(url: string, bytes: Uint8Array, contentType: string): Promise<void>;
|
|
73
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// The HTTP client the commands share: a bearer-authenticated wrapper over
|
|
2
|
+
// the Brass data API, plus the narrow wire types the CLI reads. Wire shapes
|
|
3
|
+
// are declared here (not imported from `@brass-build/client`) so the CLI
|
|
4
|
+
// stays a standalone package with no browser-SDK dependency; the shapes it
|
|
5
|
+
// depends on are small and stable (the `/apps` + hosting surface).
|
|
6
|
+
import { CLI_CLIENT_ID } from './version.js';
|
|
7
|
+
// One error type for every API failure, carrying the HTTP status and the
|
|
8
|
+
// server-authored message so the CLI can render it verbatim (the API returns
|
|
9
|
+
// `{ error: string }` on the 4xx/5xx ladder).
|
|
10
|
+
export class BrassApiError extends Error {
|
|
11
|
+
status;
|
|
12
|
+
constructor(status, message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'BrassApiError';
|
|
15
|
+
this.status = status;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class BrassApi {
|
|
19
|
+
apiBaseUrl;
|
|
20
|
+
auth;
|
|
21
|
+
constructor(apiBaseUrl, auth) {
|
|
22
|
+
this.apiBaseUrl = apiBaseUrl;
|
|
23
|
+
this.auth = auth;
|
|
24
|
+
}
|
|
25
|
+
get(path) {
|
|
26
|
+
return this.request('GET', path);
|
|
27
|
+
}
|
|
28
|
+
post(path, body) {
|
|
29
|
+
return this.request('POST', path, body);
|
|
30
|
+
}
|
|
31
|
+
patch(path, body) {
|
|
32
|
+
return this.request('PATCH', path, body);
|
|
33
|
+
}
|
|
34
|
+
async request(method, path, body) {
|
|
35
|
+
const headers = {
|
|
36
|
+
...(await this.auth.headers()),
|
|
37
|
+
// Version telemetry; the api Lambda logs it so pinned CI copies
|
|
38
|
+
// stay visible in the deployed-version distribution.
|
|
39
|
+
'x-brass-client': CLI_CLIENT_ID,
|
|
40
|
+
};
|
|
41
|
+
let init = { method, headers };
|
|
42
|
+
if (body !== undefined) {
|
|
43
|
+
headers['content-type'] = 'application/json';
|
|
44
|
+
init = { ...init, body: JSON.stringify(body) };
|
|
45
|
+
}
|
|
46
|
+
let response;
|
|
47
|
+
try {
|
|
48
|
+
response = await fetch(`${this.apiBaseUrl}${path}`, init);
|
|
49
|
+
}
|
|
50
|
+
catch (cause) {
|
|
51
|
+
throw new BrassApiError(0, `Network error reaching ${this.apiBaseUrl}: ${String(cause)}`);
|
|
52
|
+
}
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
throw new BrassApiError(response.status, await errorMessage(response));
|
|
55
|
+
}
|
|
56
|
+
if (response.status === 204)
|
|
57
|
+
return undefined;
|
|
58
|
+
return (await response.json());
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Upload bytes to a presigned S3 URL. Deliberately carries NO Brass headers
|
|
62
|
+
// and no bearer: the presigned signature pins an exact header set, and a
|
|
63
|
+
// stray `authorization` header would break it (the same rule the SDK's
|
|
64
|
+
// `api-headers` module enforces on the document path).
|
|
65
|
+
export async function putPresigned(url, bytes, contentType) {
|
|
66
|
+
let response;
|
|
67
|
+
try {
|
|
68
|
+
response = await fetch(url, {
|
|
69
|
+
method: 'PUT',
|
|
70
|
+
headers: { 'content-type': contentType },
|
|
71
|
+
// fflate allocates a plain ArrayBuffer, so narrowing the generic off
|
|
72
|
+
// `ArrayBufferLike` is sound and satisfies fetch's `BufferSource`
|
|
73
|
+
// (which pins `ArrayBufferView<ArrayBuffer>`) across lib configs.
|
|
74
|
+
body: bytes,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch (cause) {
|
|
78
|
+
throw new BrassApiError(0, `Network error uploading the bundle: ${String(cause)}`);
|
|
79
|
+
}
|
|
80
|
+
if (!response.ok) {
|
|
81
|
+
throw new BrassApiError(response.status, `Bundle upload failed (${response.status})`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Pull the server's `{ error }` message off a failed response, falling back
|
|
85
|
+
// to the bare status when the body is not the expected shape.
|
|
86
|
+
async function errorMessage(response) {
|
|
87
|
+
try {
|
|
88
|
+
const body = (await response.json());
|
|
89
|
+
if (typeof body.error === 'string' && body.error !== '')
|
|
90
|
+
return body.error;
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Non-JSON body (an edge / gateway error); fall through to the status.
|
|
94
|
+
}
|
|
95
|
+
return `Request failed with status ${response.status}`;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,4EAA4E;AAC5E,yEAAyE;AACzE,2EAA2E;AAC3E,mEAAmE;AAGnE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AA6F7C,yEAAyE;AACzE,6EAA6E;AAC7E,8CAA8C;AAC9C,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,MAAM,CAAS;IACxB,YAAY,MAAc,EAAE,OAAe;QACzC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF;AAED,MAAM,OAAO,QAAQ;IACF,UAAU,CAAS;IACnB,IAAI,CAAe;IAEpC,YAAY,UAAkB,EAAE,IAAkB;QAChD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,GAAG,CAAI,IAAY;QACjB,OAAO,IAAI,CAAC,OAAO,CAAI,KAAK,EAAE,IAAI,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,CAAI,IAAY,EAAE,IAAc;QAClC,OAAO,IAAI,CAAC,OAAO,CAAI,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7C,CAAC;IACD,KAAK,CAAI,IAAY,EAAE,IAAc;QACnC,OAAO,IAAI,CAAC,OAAO,CAAI,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC9C,CAAC;IAEO,KAAK,CAAC,OAAO,CAAI,MAAc,EAAE,IAAY,EAAE,IAAc;QACnE,MAAM,OAAO,GAA2B;YACtC,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAC9B,gEAAgE;YAChE,qDAAqD;YACrD,gBAAgB,EAAE,aAAa;SAChC,CAAC;QACF,IAAI,IAAI,GAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;QAC5C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;YAC7C,IAAI,GAAG,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;QACjD,CAAC;QACD,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,UAAU,GAAG,IAAI,EAAE,EAAE,IAAI,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,aAAa,CAAC,CAAC,EAAE,0BAA0B,IAAI,CAAC,UAAU,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC5F,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,aAAa,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC;QACzE,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,SAAc,CAAC;QACnD,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAM,CAAC;IACtC,CAAC;CACF;AAED,4EAA4E;AAC5E,yEAAyE;AACzE,uEAAuE;AACvE,uDAAuD;AACvD,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAW,EACX,KAAiB,EACjB,WAAmB;IAEnB,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAC1B,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE;YACxC,qEAAqE;YACrE,kEAAkE;YAClE,kEAAkE;YAClE,IAAI,EAAE,KAAgC;SACvC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,aAAa,CAAC,CAAC,EAAE,uCAAuC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IACrF,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,aAAa,CAAC,QAAQ,CAAC,MAAM,EAAE,yBAAyB,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IACxF,CAAC;AACH,CAAC;AAED,4EAA4E;AAC5E,8DAA8D;AAC9D,KAAK,UAAU,YAAY,CAAC,QAAkB;IAC5C,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAwB,CAAC;QAC5D,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,EAAE;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC;IAC7E,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;IACzE,CAAC;IACD,OAAO,8BAA8B,QAAQ,CAAC,MAAM,EAAE,CAAC;AACzD,CAAC"}
|
package/dist/args.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface ParsedArgs {
|
|
2
|
+
positionals: string[];
|
|
3
|
+
flags: Record<string, string | true>;
|
|
4
|
+
}
|
|
5
|
+
export declare function unknownFlags(parsed: ParsedArgs): string[];
|
|
6
|
+
export declare function valuelessFlags(parsed: ParsedArgs, names: readonly string[]): string[];
|
|
7
|
+
export declare function parseArgs(argv: readonly string[]): ParsedArgs;
|
|
8
|
+
export declare function stringFlag(parsed: ParsedArgs, name: string): string | undefined;
|
|
9
|
+
export declare function boolFlag(parsed: ParsedArgs, name: string): boolean;
|
|
10
|
+
//# sourceMappingURL=args.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,EAAE,CAAC;IAGtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;CACtC;AAyCD,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAEzD;AAOD,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAErF;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CA6B7D;AAID,wBAAgB,UAAU,CACxB,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAGpB;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAElE"}
|
package/dist/args.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// A minimal argv parser: no dependency, and small enough to unit-test the
|
|
2
|
+
// exact precedence the commands rely on. Supports `--flag value`,
|
|
3
|
+
// `--flag=value`, and boolean `--flag`; everything else is a positional.
|
|
4
|
+
// Flags that never take a value, so `brass publish --yes ./dist` parses
|
|
5
|
+
// `./dist` as a positional rather than the value of `--yes`.
|
|
6
|
+
const BOOLEAN_FLAGS = new Set([
|
|
7
|
+
'json',
|
|
8
|
+
'yes',
|
|
9
|
+
'help',
|
|
10
|
+
'version',
|
|
11
|
+
'stdout',
|
|
12
|
+
'start',
|
|
13
|
+
'check',
|
|
14
|
+
'new',
|
|
15
|
+
]);
|
|
16
|
+
// Every flag any command reads. A flag outside this set is rejected rather
|
|
17
|
+
// than ignored, because ignoring one silently retargets the invocation: the
|
|
18
|
+
// origin flags default to production, so a misspelled `--api-ur1` publishes an
|
|
19
|
+
// app, or mints a sign-in, against the real platform while naming another
|
|
20
|
+
// stack on the command line.
|
|
21
|
+
const KNOWN_FLAGS = new Set([
|
|
22
|
+
...BOOLEAN_FLAGS,
|
|
23
|
+
'api-url',
|
|
24
|
+
'app',
|
|
25
|
+
'auth-url',
|
|
26
|
+
'client-token',
|
|
27
|
+
'dashboard-url',
|
|
28
|
+
'doc',
|
|
29
|
+
'gate',
|
|
30
|
+
'manifest',
|
|
31
|
+
'name',
|
|
32
|
+
'org',
|
|
33
|
+
'out',
|
|
34
|
+
'slug',
|
|
35
|
+
'token',
|
|
36
|
+
'visibility',
|
|
37
|
+
'wait',
|
|
38
|
+
]);
|
|
39
|
+
// The flag names this CLI does not know, in the order given, for an error
|
|
40
|
+
// naming all of them rather than one per run.
|
|
41
|
+
export function unknownFlags(parsed) {
|
|
42
|
+
return Object.keys(parsed.flags).filter((name) => !KNOWN_FLAGS.has(name));
|
|
43
|
+
}
|
|
44
|
+
// Which of `names` were given without a value (`--api-url --json`, or
|
|
45
|
+
// `--api-url` last on the line). The parser stores those as `true` and
|
|
46
|
+
// `stringFlag` reads that as absent, so an origin flag in this state resolves
|
|
47
|
+
// the production default while the command line names another stack. That is
|
|
48
|
+
// the same silent retarget `unknownFlags` catches for a misspelled name.
|
|
49
|
+
export function valuelessFlags(parsed, names) {
|
|
50
|
+
return names.filter((name) => parsed.flags[name] === true);
|
|
51
|
+
}
|
|
52
|
+
export function parseArgs(argv) {
|
|
53
|
+
const positionals = [];
|
|
54
|
+
const flags = {};
|
|
55
|
+
for (let i = 0; i < argv.length; i++) {
|
|
56
|
+
const arg = argv[i];
|
|
57
|
+
if (arg === undefined)
|
|
58
|
+
continue;
|
|
59
|
+
if (arg.startsWith('--')) {
|
|
60
|
+
const body = arg.slice(2);
|
|
61
|
+
const eq = body.indexOf('=');
|
|
62
|
+
if (eq !== -1) {
|
|
63
|
+
flags[body.slice(0, eq)] = body.slice(eq + 1);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (BOOLEAN_FLAGS.has(body)) {
|
|
67
|
+
flags[body] = true;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const next = argv[i + 1];
|
|
71
|
+
if (next !== undefined && !next.startsWith('--')) {
|
|
72
|
+
flags[body] = next;
|
|
73
|
+
i++;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
flags[body] = true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
positionals.push(arg);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { positionals, flags };
|
|
84
|
+
}
|
|
85
|
+
// Read a flag expected to carry a string value; a bare boolean flag (no
|
|
86
|
+
// value) is treated as absent so `--doc` alone doesn't resolve to `true`.
|
|
87
|
+
export function stringFlag(parsed, name) {
|
|
88
|
+
const v = parsed.flags[name];
|
|
89
|
+
return typeof v === 'string' ? v : undefined;
|
|
90
|
+
}
|
|
91
|
+
export function boolFlag(parsed, name) {
|
|
92
|
+
return parsed.flags[name] === true || typeof parsed.flags[name] === 'string';
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=args.js.map
|