@sprid/cli 0.1.2
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/CHANGELOG.md +12 -0
- package/LICENSE +14 -0
- package/README-post.md +615 -0
- package/README.md +249 -0
- package/SECURITY.md +18 -0
- package/bin.mjs +12 -0
- package/package.json +47 -0
- package/src/args.mjs +134 -0
- package/src/browser.mjs +28 -0
- package/src/cli.mjs +257 -0
- package/src/commands/auth.mjs +192 -0
- package/src/commands/completion.mjs +90 -0
- package/src/commands/connect.mjs +586 -0
- package/src/commands/docs.mjs +53 -0
- package/src/commands/family.mjs +96 -0
- package/src/commands/init.mjs +137 -0
- package/src/commands/local-tools.mjs +31 -0
- package/src/commands/marketing-review.mjs +106 -0
- package/src/commands/mcp.mjs +53 -0
- package/src/commands/misc.mjs +94 -0
- package/src/commands/pinterest.mjs +120 -0
- package/src/commands/plan.mjs +230 -0
- package/src/commands/post.mjs +41 -0
- package/src/commands/reviews.mjs +149 -0
- package/src/commands/setup.mjs +220 -0
- package/src/commands/status.mjs +217 -0
- package/src/commands/studio.mjs +69 -0
- package/src/commands/update.mjs +69 -0
- package/src/creds.mjs +126 -0
- package/src/docs/commands.mjs +152 -0
- package/src/docs/guides.generated.mjs +1178 -0
- package/src/docs/help.mjs +77 -0
- package/src/docs/index.d.mts +18 -0
- package/src/docs/index.mjs +45 -0
- package/src/docs/queries.d.mts +11 -0
- package/src/docs/queries.mjs +77 -0
- package/src/endpoint.mjs +17 -0
- package/src/evidence.mjs +15 -0
- package/src/format.mjs +71 -0
- package/src/http.mjs +117 -0
- package/src/pending.mjs +27 -0
- package/src/post/cli.mjs +2285 -0
- package/src/post/json-worker.mjs +12 -0
- package/src/post/preview-server.mjs +58 -0
- package/src/post/preview.mjs +660 -0
- package/src/post/recipes/screen.mjs +290 -0
- package/src/post/recipes/stills.mjs +146 -0
- package/src/post/rules/platform-rules.d.mts +27 -0
- package/src/post/rules/platform-rules.mjs +164 -0
- package/src/post/screen/captions.mjs +131 -0
- package/src/post/screen/compose.mjs +284 -0
- package/src/post/screen/input.mjs +163 -0
- package/src/post/screen/sim.mjs +443 -0
- package/src/post/screen/simkit.swift +328 -0
- package/src/profiles.mjs +61 -0
- package/src/release.ts +2 -0
- package/src/screenshots.ts +1 -0
- package/src/updates.mjs +152 -0
package/README.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# sprid
|
|
2
|
+
|
|
3
|
+
The Sprid command line. Log in once, connect the stores and the channels, and see
|
|
4
|
+
what needs you. Node 18+. Screenshot composition and releases also need Bun.
|
|
5
|
+
|
|
6
|
+
Sprid runs the marketing loop for an app: the agent does the work, the server
|
|
7
|
+
remembers, watches and publishes, the person approves. Connected workflows share
|
|
8
|
+
server records across REST, Sprid MCP and the dashboard. The CLI adds terminal access
|
|
9
|
+
and local media production. Credentials stay out of conversations: CLI key-file
|
|
10
|
+
commands and secure Sprid forms handle them.
|
|
11
|
+
|
|
12
|
+
Read `sprid docs local` for the complete local-to-chat handoff, or `sprid docs chat`
|
|
13
|
+
for a browser-only workflow. `sprid media` builds and uploads; `sprid post create`,
|
|
14
|
+
`get`, `update`, `preview --id` and `deliveries` operate on shared posts.
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
bunx @sprid/cli login # or: npm i -g @sprid/cli && sprid login
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Local tools
|
|
21
|
+
|
|
22
|
+
Install once with `npm install -g @sprid/cli`. The support packages install with it:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
sprid screenshots --config aso.config.ts
|
|
26
|
+
sprid release metadata --config release.config.ts # dry run
|
|
27
|
+
sprid release metadata --execute # send reviewed changes
|
|
28
|
+
sprid post help
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`npm install -D @sprid/cli` adds config helpers at `@sprid/cli/screenshots` (types) and
|
|
32
|
+
`@sprid/cli/release` (`defineReleaseConfig`) to an app repo. The underlying
|
|
33
|
+
`@sprid/shots` and `@sprid/release` remain importable libraries. Both subpaths
|
|
34
|
+
are TypeScript source, so they are for Bun or a TypeScript build; Node cannot
|
|
35
|
+
import them directly. The `sprid-shots` and `sprid-release` binaries run under
|
|
36
|
+
Node and re-exec under Bun, so a missing Bun says so. The commands above
|
|
37
|
+
need Bun for TypeScript configs. Configs are executable code; use trusted repos.
|
|
38
|
+
`--json` wraps local-tool output with `ok`, `exitCode`, `output` and `outputTruncated`.
|
|
39
|
+
Repo `apiUrl` settings must match the service selected by login or `SPRID_URL`;
|
|
40
|
+
they cannot silently send your token to another host.
|
|
41
|
+
|
|
42
|
+
## First connection
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
$ sprid login
|
|
46
|
+
Open https://app.sprid.studio/device?code=FQRK-9M2P and enter FQRK-9M2P
|
|
47
|
+
(opening browser…)
|
|
48
|
+
✓ user@example.com · workspace example-team · Pulse
|
|
49
|
+
|
|
50
|
+
$ sprid init # or /sprid:bootstrap in the agent; same result
|
|
51
|
+
✓ Created App Profile myapp (My App) from .sprid/app.json
|
|
52
|
+
appStoreId 6740000000
|
|
53
|
+
playPackageName com.example.myapp
|
|
54
|
+
Still missing:
|
|
55
|
+
– App Store Connect → sprid connect asc --app myapp --key ~/Downloads/AuthKey_XXXX.p8 --key-id XXXX --issuer YYYY
|
|
56
|
+
– Google Play → sprid connect play --app myapp --key ~/Downloads/play-service-account.json
|
|
57
|
+
|
|
58
|
+
$ sprid connect
|
|
59
|
+
Missing keys
|
|
60
|
+
– Google Play · My App → sprid connect play --app myapp --key ~/Downloads/play-service-account.json
|
|
61
|
+
Channels not connected
|
|
62
|
+
– instagram @myapp → sprid connect instagram --account myapp
|
|
63
|
+
Optional (no id on the profile yet)
|
|
64
|
+
myapp: PostHog, RevenueCat, Search Console, Cloudflare
|
|
65
|
+
|
|
66
|
+
$ sprid connect instagram --account myapp
|
|
67
|
+
Open https://www.facebook.com/…
|
|
68
|
+
Opening the browser… waiting for Instagram
|
|
69
|
+
✓ @myapp_social connected to myapp
|
|
70
|
+
|
|
71
|
+
$ sprid status
|
|
72
|
+
NEXT 1 Approve "you opened the app to write" sprid open post 412
|
|
73
|
+
2 Reply to a 2★ review from yesterday sprid reviews reply 9
|
|
74
|
+
3 Queue runs dry Thu 12 Sep sprid open calendar
|
|
75
|
+
CONNECTIONS myapp instagram ✓ tiktok ✗ (revoked)
|
|
76
|
+
My App asc ✓ 07:00 play ✓ 07:00
|
|
77
|
+
QUEUE 2 scheduled · next Tue 09:00 @myapp_social · 0 failed
|
|
78
|
+
APPS myapp asc ✓ play ✓ posthog – revenuecat – gsc – cloudflare –
|
|
79
|
+
PLAN Pulse trial · 1/1 apps · 1/2 accounts · 1/4 channels
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Connections are grouped one line per account (channels) and one per App Profile
|
|
83
|
+
(sensors): `✓` ok, `~` expiring, `✗` revoked or the last pull failed, `–` key missing. The
|
|
84
|
+
PLAN line ends with `· BYOK anthropic ✓` or `· BYOK –` once the server reports the
|
|
85
|
+
workspace's model key. When the token sees
|
|
86
|
+
several workspaces, the first line names the one you are reading.
|
|
87
|
+
|
|
88
|
+
Every line of `status` is one field of the server's `GET /api/status`. The web Home
|
|
89
|
+
card, the Monday mail and the plugin's closing message print the same list.
|
|
90
|
+
|
|
91
|
+
## Investigate connected data
|
|
92
|
+
|
|
93
|
+
`sprid marketing-review` collects the review packet. Follow up through the same saved connections:
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
sprid docs queries --offline
|
|
97
|
+
sprid marketing-review capabilities --app myapp --source gsc --json
|
|
98
|
+
sprid marketing-review query --app myapp --source gsc --operation search --params-file search.json --json
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The catalogue supplies the supported operations and JSON parameter schemas. The server pins resource IDs to the App Profile; provider keys stay on Sprid. Follow `next.params`, keep coverage/truncation with the results, and preserve provider units. Social reads use stored metrics and collected comments. Repository database reads remain local. See [query documentation](https://sprid.studio/docs/queries).
|
|
102
|
+
|
|
103
|
+
## Commands and setup guides
|
|
104
|
+
|
|
105
|
+
`sprid help` and the [public CLI reference](https://sprid.studio/docs/cli) read the
|
|
106
|
+
same command catalog. `sprid docs cli` always describes the installed executable,
|
|
107
|
+
even when the server is newer. Help works offline without login.
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
sprid help post create # focus on one operation
|
|
111
|
+
sprid post create --help # equivalent; never creates a draft
|
|
112
|
+
sprid help --json # installed syntax and summaries for agents
|
|
113
|
+
sprid docs search posthog permissions
|
|
114
|
+
sprid docs # list guides and references
|
|
115
|
+
sprid docs posthog # current setup steps, permissions and checks
|
|
116
|
+
sprid docs revenuecat --json
|
|
117
|
+
sprid docs posthog --offline # bundled copy, without a network request
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Guides require no login. When the current guide cannot be fetched, output identifies
|
|
121
|
+
the bundled fallback. JSON includes `source` and each connection guide’s `revision`. Search uses bundled
|
|
122
|
+
content and shows ten matches by default; `--limit N` changes that. JSON search
|
|
123
|
+
includes the full match count and a command to read each result.
|
|
124
|
+
|
|
125
|
+
Load completion in the current shell:
|
|
126
|
+
|
|
127
|
+
```sh
|
|
128
|
+
source <(sprid completion bash) # Bash
|
|
129
|
+
source <(sprid completion zsh) # Zsh, after compinit
|
|
130
|
+
sprid completion fish | source # Fish
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
These commands print a script and do not edit your shell configuration. Completion
|
|
134
|
+
suggests catalog commands, subcommands, documented flags and guide names. Dynamic
|
|
135
|
+
account names and IDs still come from `sprid apps` and connected reads. Save the
|
|
136
|
+
script in your shell's completion directory to retain it; regenerate after updating.
|
|
137
|
+
|
|
138
|
+
Always pass a key file when connecting through an agent. A successful sensor connect
|
|
139
|
+
means the key was saved: JSON returns `verified: false` and a `verifyCommand`. Run
|
|
140
|
+
that command without `--check`; inspect the individual source’s data and errors.
|
|
141
|
+
|
|
142
|
+
`--app` is optional when there is one App Profile or `.sprid/app.json` identifies it.
|
|
143
|
+
|
|
144
|
+
### Which workspace
|
|
145
|
+
|
|
146
|
+
Workspace selection: explicit `--workspace`, the app profile's `workspaceId`, saved
|
|
147
|
+
login choice, sole available workspace, then the app profile's workspace slug.
|
|
148
|
+
Use `sprid use <slug>` to save a choice; it also works with an environment token.
|
|
149
|
+
|
|
150
|
+
## Credentials
|
|
151
|
+
|
|
152
|
+
`sprid login` writes `~/.sprid/credentials.json` (mode `0600`, directory `0700`):
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{ "apiUrl": "https://api.sprid.studio", "token": "sprd_…", "tokenId": 7,
|
|
156
|
+
"workspace": { "id": 1, "slug": "example-team", "name": "Example Team", "tier": "starter" },
|
|
157
|
+
"email": "you@example.com" }
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The token is an ordinary personal access token named `sprid <version> on <hostname>`,
|
|
161
|
+
pinned to the workspace you picked in the browser. Revoke it with `sprid logout` or in
|
|
162
|
+
Settings → Tokens.
|
|
163
|
+
|
|
164
|
+
Resolution order, every command: `SPRID_PAT` in the environment wins; otherwise the
|
|
165
|
+
file; otherwise a clear "run `sprid login`". `SPRID_URL` overrides the API base
|
|
166
|
+
(`http://localhost:4005` for a dev server). `SPRID_APP_URL` overrides the web app URL,
|
|
167
|
+
which is otherwise derived by turning `api.` into `app.`.
|
|
168
|
+
|
|
169
|
+
## `--json` for agents
|
|
170
|
+
|
|
171
|
+
Commands take `--json` and write one JSON document to stdout,
|
|
172
|
+
including on failure (`{"ok":false,"error":"…","status":401}`), with the exit code
|
|
173
|
+
preserved. Things a person needs to act on during a run (the login code, the OAuth
|
|
174
|
+
URL) go to stderr so stdout stays parseable. `sprid connect asc --json` reports which
|
|
175
|
+
secret keys are now set, never their values. Unknown commands and missing flag values
|
|
176
|
+
also return JSON. The `mcp` command is a streaming protocol transport and is exempt.
|
|
177
|
+
Local screenshot and release tools return an envelope with `ok`, `exitCode` and
|
|
178
|
+
captured `output`; local media can include per-post check failures.
|
|
179
|
+
|
|
180
|
+
Exit codes: `0` success, `1` runtime/auth/API failure, `2` usage or an explicit choice
|
|
181
|
+
needed. Local tools can propagate other nonzero codes. Use `sprid help exit-codes`
|
|
182
|
+
and `sprid help environment` for the scripting contract and configuration precedence.
|
|
183
|
+
Read saved state before retrying a failed mutation: a network error does not prove
|
|
184
|
+
that the server did nothing.
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
sprid status --json | jq '.next[0].cli'
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Using `sprid mcp`
|
|
191
|
+
|
|
192
|
+
Most MCP clients can talk to `https://api.sprid.studio/api/mcp` over HTTP directly.
|
|
193
|
+
For one that only speaks stdio:
|
|
194
|
+
|
|
195
|
+
```
|
|
196
|
+
claude mcp add sprid -- sprid mcp
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
It uses the `sprid login` credentials (or `SPRID_PAT`). `--surface all` lists the full
|
|
200
|
+
catalog instead of the ~25 core verbs.
|
|
201
|
+
|
|
202
|
+
## What it touches
|
|
203
|
+
|
|
204
|
+
Reads: the key files you name on the command line, `.sprid/app.json` in the current
|
|
205
|
+
directory, `~/.sprid/secrets/<slug>.json` with `--from-local`, `~/.sprid/credentials.json`.
|
|
206
|
+
|
|
207
|
+
Writes: `~/.sprid/credentials.json` on login, `.sprid/app.json` when `sprid init` is run
|
|
208
|
+
with flags. Setup and connection commands also save local progress. `sprid post`
|
|
209
|
+
writes the media registry, captions and previews; `sprid screenshots` writes store
|
|
210
|
+
images; `sprid release` writes configured build artifacts and logs. Update checks
|
|
211
|
+
cache registry metadata in `~/.sprid/updates/`; explicit updates replace installed
|
|
212
|
+
packages and, for project installations, update dependency declarations and locks.
|
|
213
|
+
|
|
214
|
+
Sends: key file contents go to your App Profile over TLS with your token, in the request
|
|
215
|
+
body of `PATCH /api/app-profiles/:id`. They are stored encrypted and no route ever
|
|
216
|
+
returns them. `sprid connect <platform>` only opens the platform’s OAuth page and waits for the
|
|
217
|
+
result. Publishing requires the relevant approval or publishing command.
|
|
218
|
+
|
|
219
|
+
No background telemetry. Credentials stay out of normal output. Build tools can
|
|
220
|
+
print their own logs; do not include secrets in custom build commands.
|
|
221
|
+
|
|
222
|
+
## Updates
|
|
223
|
+
|
|
224
|
+
`sprid doctor --json` reports the running version, npm's recommended `latest`
|
|
225
|
+
version, compatibility and the detected installation. Checks are cached for one day;
|
|
226
|
+
`--offline` reads cache only. Registry failures do not block ordinary work. `status`
|
|
227
|
+
and `next` also report available updates on stderr, keeping stdout JSON unchanged.
|
|
228
|
+
|
|
229
|
+
Use `sprid update --check` to inspect and `sprid update` to install. It updates the
|
|
230
|
+
running installation, pins the selected version, and verifies a new CLI process.
|
|
231
|
+
Global npm installs keep their npm prefix; project dependencies use the detected
|
|
232
|
+
npm, pnpm, Bun or Yarn manager and preserve dev/optional dependency placement.
|
|
233
|
+
Project updates change package.json and the lockfile. Lifecycle builds are skipped;
|
|
234
|
+
package-manager configuration is still trusted code. Source checkouts, linked or
|
|
235
|
+
ambiguous installations and Windows self-updates are refused with instructions.
|
|
236
|
+
Use the original package manager manually for those installations.
|
|
237
|
+
|
|
238
|
+
Automatic installation is off by default. `sprid update --auto on` opts in for this
|
|
239
|
+
installation; `--auto off` disables it. Settings and cache live in `~/.sprid/updates/`.
|
|
240
|
+
At a job boundary, `sprid doctor --apply-updates --json` installs a verified compatible
|
|
241
|
+
update if opted in. Ordinary commands never install updates. Start a new process
|
|
242
|
+
after `updated: true`; do not retry completed publishing operations.
|
|
243
|
+
|
|
244
|
+
Major releases, minor releases before 1.0, and all 0.0.x changes require explicit
|
|
245
|
+
`sprid update --yes`. Automatic updates never cross those boundaries. A failed
|
|
246
|
+
installation may have changed files: inspect the installation/lockfile before retrying.
|
|
247
|
+
The plugin updates separately through the agent client's plugin manager.
|
|
248
|
+
|
|
249
|
+
© 2026 Väder AB. All rights reserved. Use is subject to Sprid's [Terms of Service](https://sprid.studio/terms).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Security
|
|
2
|
+
|
|
3
|
+
Email hello@sprid.studio with `security` in the subject. Please do not open a
|
|
4
|
+
public issue for anything exploitable. Say what you did, what happened, and
|
|
5
|
+
the version (`sprid --version`). We reply within three working days. No bug
|
|
6
|
+
bounty.
|
|
7
|
+
|
|
8
|
+
**In scope:** anything that exposes a credential this tool holds or reads (the
|
|
9
|
+
token in `~/.sprid/credentials.json`, a store or provider key named by a
|
|
10
|
+
config) in output, a log, an error or a request to somewhere it does not
|
|
11
|
+
belong. Also a path that lets a server response write outside its directory,
|
|
12
|
+
run code, or widen what a token may do.
|
|
13
|
+
|
|
14
|
+
**Not a bug:** a config file is executable code. `release.config.ts`,
|
|
15
|
+
`aso.config.ts` and a post spec are run by the tool that reads them, so an
|
|
16
|
+
untrusted repository runs its author's code. Use trusted repositories.
|
|
17
|
+
|
|
18
|
+
Only the latest published version is patched.
|
package/bin.mjs
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { run } from "./src/cli.mjs";
|
|
3
|
+
|
|
4
|
+
run(process.argv.slice(2), { notifyUpdates: true }).then(
|
|
5
|
+
// Let stdout drain. An explicit exit truncates large review packets when
|
|
6
|
+
// another process is reading the CLI through a pipe.
|
|
7
|
+
(code) => { process.exitCode = code ?? 0; },
|
|
8
|
+
(e) => {
|
|
9
|
+
process.stderr.write(`\n ✗ ${e && e.message ? e.message : e}\n\n`);
|
|
10
|
+
process.exitCode = 1;
|
|
11
|
+
},
|
|
12
|
+
);
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sprid/cli",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "The Sprid command line: connect your app, review results, prepare posts and store screenshots, and release mobile apps.",
|
|
5
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"homepage": "https://sprid.studio",
|
|
8
|
+
"bugs": {
|
|
9
|
+
"email": "hello@sprid.studio"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"sprid",
|
|
13
|
+
"cli",
|
|
14
|
+
"mcp",
|
|
15
|
+
"app-store",
|
|
16
|
+
"marketing",
|
|
17
|
+
"instagram",
|
|
18
|
+
"tiktok"
|
|
19
|
+
],
|
|
20
|
+
"bin": {
|
|
21
|
+
"sprid": "./bin.mjs"
|
|
22
|
+
},
|
|
23
|
+
"exports": {
|
|
24
|
+
"./screenshots": "./src/screenshots.ts",
|
|
25
|
+
"./release": "./src/release.ts"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"bin.mjs",
|
|
29
|
+
"src",
|
|
30
|
+
"!src/**/__tests__/**",
|
|
31
|
+
"README.md",
|
|
32
|
+
"README-post.md",
|
|
33
|
+
"LICENSE",
|
|
34
|
+
"SECURITY.md",
|
|
35
|
+
"CHANGELOG.md"
|
|
36
|
+
],
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@sprid/shots": "0.1.1",
|
|
42
|
+
"@sprid/release": "0.1.1"
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/args.mjs
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// Argument parsing. One rule that has already been wrong elsewhere: a flag's
|
|
2
|
+
// value is not a positional argument, so every flag that takes a value is
|
|
3
|
+
// named here and skipped when collecting positionals.
|
|
4
|
+
|
|
5
|
+
export const VALUE_FLAGS = new Set([
|
|
6
|
+
'revision', 'search', 'directory',
|
|
7
|
+
'history-before', 'history-before-id', 'history-limit',
|
|
8
|
+
'before', 'before-id',
|
|
9
|
+
'id', 'request-id',
|
|
10
|
+
"api",
|
|
11
|
+
"auto",
|
|
12
|
+
"file",
|
|
13
|
+
"task",
|
|
14
|
+
"payload",
|
|
15
|
+
"platform",
|
|
16
|
+
"entry",
|
|
17
|
+
"account",
|
|
18
|
+
"page",
|
|
19
|
+
"app",
|
|
20
|
+
"key",
|
|
21
|
+
"key-id",
|
|
22
|
+
"issuer",
|
|
23
|
+
"property",
|
|
24
|
+
"project",
|
|
25
|
+
"host",
|
|
26
|
+
"token",
|
|
27
|
+
"zone",
|
|
28
|
+
"role",
|
|
29
|
+
"surface",
|
|
30
|
+
"limit",
|
|
31
|
+
"days",
|
|
32
|
+
"query-file",
|
|
33
|
+
"view",
|
|
34
|
+
"sources",
|
|
35
|
+
"enabled",
|
|
36
|
+
"source",
|
|
37
|
+
"operation",
|
|
38
|
+
"params-file",
|
|
39
|
+
"since",
|
|
40
|
+
"end",
|
|
41
|
+
"workspace",
|
|
42
|
+
"max-rating",
|
|
43
|
+
"text",
|
|
44
|
+
// init
|
|
45
|
+
"name",
|
|
46
|
+
"slug",
|
|
47
|
+
"app-store-id",
|
|
48
|
+
"play-package",
|
|
49
|
+
"gsc-property",
|
|
50
|
+
"posthog-project",
|
|
51
|
+
"posthog-host",
|
|
52
|
+
"revenuecat-project",
|
|
53
|
+
"cloudflare-zone",
|
|
54
|
+
"repo-url",
|
|
55
|
+
"website-url",
|
|
56
|
+
"channel",
|
|
57
|
+
"board",
|
|
58
|
+
"section",
|
|
59
|
+
"title",
|
|
60
|
+
"description",
|
|
61
|
+
"link",
|
|
62
|
+
"alt-text",
|
|
63
|
+
"ai-disclosures",
|
|
64
|
+
"bookmark",
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
export const BOOL_FLAGS = new Set(["json", "offline", "check", "no-browser", "from-local", "help", "version", "unanswered", "yes", "clear", "refresh", "cached-only"]);
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* `sprid connect asc --app myapp --key ./k.p8 --json`
|
|
71
|
+
* → { command: "connect", positionals: ["asc"], flags: { app: "myapp", key: "./k.p8", json: true } }
|
|
72
|
+
*
|
|
73
|
+
* Accepts `--flag value` and `--flag=value`. Unknown `--flags` are kept as
|
|
74
|
+
* booleans so a command can refuse them by name.
|
|
75
|
+
*/
|
|
76
|
+
export function parseArgs(argv) {
|
|
77
|
+
const flags = {};
|
|
78
|
+
const positionals = [];
|
|
79
|
+
let commandIndex = -1;
|
|
80
|
+
for (let i = 0; i < argv.length; i++) {
|
|
81
|
+
const a = argv[i];
|
|
82
|
+
if (a === "--") {
|
|
83
|
+
if (commandIndex < 0 && argv[i + 1]) commandIndex = i + 1;
|
|
84
|
+
positionals.push(...argv.slice(i + 1));
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
if (a.startsWith("--")) {
|
|
88
|
+
const eq = a.indexOf("=");
|
|
89
|
+
if (eq > 0) {
|
|
90
|
+
flags[a.slice(2, eq)] = a.slice(eq + 1);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const name = a.slice(2);
|
|
94
|
+
if (VALUE_FLAGS.has(name)) {
|
|
95
|
+
const v = argv[i + 1];
|
|
96
|
+
if (v === undefined || v.startsWith("--")) {
|
|
97
|
+
throw new UsageError(`--${name} needs a value`);
|
|
98
|
+
}
|
|
99
|
+
flags[name] = v;
|
|
100
|
+
i++;
|
|
101
|
+
} else {
|
|
102
|
+
flags[name] = true;
|
|
103
|
+
}
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (a === "-w") {
|
|
107
|
+
const v = argv[i + 1];
|
|
108
|
+
if (v === undefined || v.startsWith("-")) throw new UsageError("-w needs a workspace slug or id");
|
|
109
|
+
flags.workspace = v;
|
|
110
|
+
i++;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (a === "-h") {
|
|
114
|
+
flags.help = true;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (a === "-v") {
|
|
118
|
+
flags.version = true;
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (commandIndex < 0) commandIndex = i;
|
|
122
|
+
positionals.push(a);
|
|
123
|
+
}
|
|
124
|
+
const [command, ...rest] = positionals;
|
|
125
|
+
return { command, positionals: rest, flags, commandArgv: argv.slice(commandIndex + 1) };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export class UsageError extends Error {
|
|
129
|
+
constructor(message) {
|
|
130
|
+
super(message);
|
|
131
|
+
this.name = "UsageError";
|
|
132
|
+
this.exitCode = 2;
|
|
133
|
+
}
|
|
134
|
+
}
|
package/src/browser.mjs
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Open a URL in the person's browser, and never fail because of it: over SSH
|
|
2
|
+
// or in a container there is no browser, and the URL is printed anyway.
|
|
3
|
+
|
|
4
|
+
import { spawn } from "node:child_process";
|
|
5
|
+
|
|
6
|
+
export async function openBrowser(url) {
|
|
7
|
+
let parsed;
|
|
8
|
+
try { parsed = new URL(url); } catch { return false; }
|
|
9
|
+
if (!['https:', 'http:'].includes(parsed.protocol) || parsed.username || parsed.password) return false;
|
|
10
|
+
const [cmd, args] =
|
|
11
|
+
process.platform === "darwin"
|
|
12
|
+
? ["open", [url]]
|
|
13
|
+
: process.platform === "win32"
|
|
14
|
+
? ['powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', 'Start-Process -FilePath $env:SPRID_OPEN_URL']]
|
|
15
|
+
: ["xdg-open", [url]];
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
try {
|
|
18
|
+
const child = spawn(cmd, args, { stdio: "ignore", detached: true, env: { ...process.env, SPRID_OPEN_URL: parsed.href } });
|
|
19
|
+
child.on("error", () => resolve(false));
|
|
20
|
+
child.on("spawn", () => {
|
|
21
|
+
child.unref();
|
|
22
|
+
resolve(true);
|
|
23
|
+
});
|
|
24
|
+
} catch {
|
|
25
|
+
resolve(false);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|