@sprigr/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/README.md +84 -0
- package/dist/api.d.ts +234 -0
- package/dist/api.js +220 -0
- package/dist/api.js.map +1 -0
- package/dist/auth.d.ts +103 -0
- package/dist/auth.js +225 -0
- package/dist/auth.js.map +1 -0
- package/dist/bin.d.ts +21 -0
- package/dist/bin.js +300 -0
- package/dist/bin.js.map +1 -0
- package/dist/bundler.d.ts +49 -0
- package/dist/bundler.js +123 -0
- package/dist/bundler.js.map +1 -0
- package/dist/commands/app.d.ts +223 -0
- package/dist/commands/app.js +539 -0
- package/dist/commands/app.js.map +1 -0
- package/dist/commands/builds.d.ts +22 -0
- package/dist/commands/builds.js +76 -0
- package/dist/commands/builds.js.map +1 -0
- package/dist/commands/deploy.d.ts +32 -0
- package/dist/commands/deploy.js +96 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/login.d.ts +54 -0
- package/dist/commands/login.js +192 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/whoami.d.ts +10 -0
- package/dist/commands/whoami.js +54 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/config.d.ts +23 -0
- package/dist/config.js +65 -0
- package/dist/config.js.map +1 -0
- package/dist/manifest.d.ts +115 -0
- package/dist/manifest.js +262 -0
- package/dist/manifest.js.map +1 -0
- package/package.json +54 -0
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# @sprigr/cli
|
|
2
|
+
|
|
3
|
+
Sprigr Team CLI — deploy static sites and Next.js apps to **Sprigr Tenant Hosting** from your terminal.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @sprigr/cli
|
|
9
|
+
# or run without installing
|
|
10
|
+
npx @sprigr/cli --help
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Requires Node.js **20+**.
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# 1. Get an API key from https://team.sprigr.com → Settings → API keys
|
|
19
|
+
export SPRIGR_API_KEY=sk_...
|
|
20
|
+
|
|
21
|
+
# 2. Deploy a static site
|
|
22
|
+
sprigr deploy my-site --dir ./public
|
|
23
|
+
|
|
24
|
+
# 3. Or deploy a Next.js app (built with @opennextjs/cloudflare)
|
|
25
|
+
sprigr deploy my-app --dir ./.open-next --framework next
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Commands
|
|
29
|
+
|
|
30
|
+
### `sprigr deploy <siteId> --dir <dir>`
|
|
31
|
+
|
|
32
|
+
Bundle a directory and deploy it as a new build for the given website.
|
|
33
|
+
|
|
34
|
+
| Flag | Description | Default |
|
|
35
|
+
| --- | --- | --- |
|
|
36
|
+
| `--dir <path>` | Directory to bundle | required |
|
|
37
|
+
| `--framework <static\|next\|astro\|remix>` | Framework hint | `static` |
|
|
38
|
+
| `--endpoint <url>` | API endpoint override | `https://api.team.sprigr.com` |
|
|
39
|
+
| `--api-key <key>` | API key (overrides env) | from `SPRIGR_API_KEY` |
|
|
40
|
+
|
|
41
|
+
The CLI streams progress and prints the build id. Use `sprigr builds get` to follow status, or watch the portal's Builds panel.
|
|
42
|
+
|
|
43
|
+
### `sprigr builds list <siteId>`
|
|
44
|
+
|
|
45
|
+
List recent builds for a site.
|
|
46
|
+
|
|
47
|
+
| Flag | Description | Default |
|
|
48
|
+
| --- | --- | --- |
|
|
49
|
+
| `--limit <N>` | Number of rows | `20` |
|
|
50
|
+
|
|
51
|
+
### `sprigr builds get <siteId> <buildId>`
|
|
52
|
+
|
|
53
|
+
Fetch a single build's status, including logs once available.
|
|
54
|
+
|
|
55
|
+
## Environment variables
|
|
56
|
+
|
|
57
|
+
| Variable | Purpose |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| `SPRIGR_API_KEY` | Authentication. Create one in the portal. |
|
|
60
|
+
| `SPRIGR_ENDPOINT` | API endpoint. Accepts a full URL, or the shortcuts `staging` / `prod`. |
|
|
61
|
+
|
|
62
|
+
## How deploys work
|
|
63
|
+
|
|
64
|
+
1. The CLI tarballs your `--dir` (gzip).
|
|
65
|
+
2. It POSTs to the Sprigr Team **provisioning** worker, which enqueues a build job.
|
|
66
|
+
3. The build farm container picks it up, runs the framework adapter (no-op for `static`, `@opennextjs/cloudflare build` for `next`), and streams output back.
|
|
67
|
+
4. On success the bundle is uploaded to **Workers for Platforms** for SSR sites or to **R2 + website-serve** for static, then the site's production deployment pointer flips atomically.
|
|
68
|
+
|
|
69
|
+
Static sites are billed by request count; SSR sites are billed by request count + container-seconds during the build. Plan limits (max sites, retention, custom domains) are enforced at deploy time.
|
|
70
|
+
|
|
71
|
+
## Roadmap
|
|
72
|
+
|
|
73
|
+
- PKCE login (`sprigr login`) so you don't need to copy an API key from the portal.
|
|
74
|
+
- Binary file support in the bundler (currently text + UTF-8 only).
|
|
75
|
+
- `sprigr logs <buildId> --follow` for live tailing.
|
|
76
|
+
|
|
77
|
+
## Links
|
|
78
|
+
|
|
79
|
+
- Sprigr — <https://sprigr.com>
|
|
80
|
+
- Issues — <https://github.com/greben99/sprigr-team/issues>
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
MIT
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin HTTP client for the Sprigr public API. All routes go through the
|
|
3
|
+
* gateway (`/api/v1/...`), authenticated by the same `sk_mcp_*` Bearer
|
|
4
|
+
* token the MCP layer accepts. The gateway resolves the key to a
|
|
5
|
+
* `companyId` and rewrites the path to `/api/data/...` before forwarding
|
|
6
|
+
* to the provisioning worker.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Wire shape for an individual file in a build manifest. UTF-8 text files
|
|
10
|
+
* ride as bare strings (back-compat); binary files (images, fonts, etc.)
|
|
11
|
+
* ride as `{ encoding: 'base64', content }`. The server enforces the same
|
|
12
|
+
* 5 MiB-per-file cap on decoded bytes regardless of variant.
|
|
13
|
+
*/
|
|
14
|
+
export type SourceFile = string | {
|
|
15
|
+
encoding: 'base64';
|
|
16
|
+
content: string;
|
|
17
|
+
};
|
|
18
|
+
export interface ApiClientOptions {
|
|
19
|
+
endpoint: string;
|
|
20
|
+
apiKey: string;
|
|
21
|
+
/** Override the global fetch — useful for tests. */
|
|
22
|
+
fetchImpl?: typeof fetch;
|
|
23
|
+
}
|
|
24
|
+
export interface BuildSummary {
|
|
25
|
+
id: string;
|
|
26
|
+
status: 'pending' | 'running' | 'succeeded' | 'failed' | 'cancelled' | 'timeout';
|
|
27
|
+
framework: string | null;
|
|
28
|
+
source: string;
|
|
29
|
+
deploymentId: string | null;
|
|
30
|
+
containerSeconds: number | null;
|
|
31
|
+
durationMs: number | null;
|
|
32
|
+
errorSummary: string | null;
|
|
33
|
+
/** R2 key for the captured stdout/stderr. Null for static builds (no
|
|
34
|
+
* shell-out) and for early-failure builds where the container never
|
|
35
|
+
* responded. Pass through `getBuildLog` to fetch the bytes. */
|
|
36
|
+
logR2Key: string | null;
|
|
37
|
+
startedAt: string | null;
|
|
38
|
+
finishedAt: string | null;
|
|
39
|
+
createdAt: string;
|
|
40
|
+
}
|
|
41
|
+
export declare class ApiError extends Error {
|
|
42
|
+
readonly status: number;
|
|
43
|
+
readonly body?: unknown | undefined;
|
|
44
|
+
constructor(status: number, message: string, body?: unknown | undefined);
|
|
45
|
+
}
|
|
46
|
+
export declare class ApiClient {
|
|
47
|
+
private readonly endpoint;
|
|
48
|
+
private readonly apiKey;
|
|
49
|
+
private readonly fetchImpl;
|
|
50
|
+
constructor(opts: ApiClientOptions);
|
|
51
|
+
/** POST /api/v1/data/websites/:siteId/builds */
|
|
52
|
+
startBuild(args: {
|
|
53
|
+
siteId: string;
|
|
54
|
+
files: Record<string, SourceFile>;
|
|
55
|
+
framework?: 'static' | 'next' | 'astro' | 'remix';
|
|
56
|
+
source?: 'upload' | 'cli' | 'agent';
|
|
57
|
+
}): Promise<{
|
|
58
|
+
buildId: string;
|
|
59
|
+
status: string;
|
|
60
|
+
framework: string;
|
|
61
|
+
source: string;
|
|
62
|
+
}>;
|
|
63
|
+
/**
|
|
64
|
+
* POST /api/v1/data/marketplace/apps/publish
|
|
65
|
+
*
|
|
66
|
+
* Publish a new version of a marketplace app. The body carries the
|
|
67
|
+
* parsed manifest separately from the bundle so the server doesn't
|
|
68
|
+
* re-parse the bundle to route the build. Server re-runs all the
|
|
69
|
+
* validators a second time (defence in depth) and returns the new
|
|
70
|
+
* version + an optional buildId when SSR builds are queued.
|
|
71
|
+
*/
|
|
72
|
+
publishMarketplaceApp(args: {
|
|
73
|
+
manifest: unknown;
|
|
74
|
+
files: Record<string, SourceFile>;
|
|
75
|
+
}): Promise<{
|
|
76
|
+
slug: string;
|
|
77
|
+
version: string;
|
|
78
|
+
appId: string;
|
|
79
|
+
versionId: string;
|
|
80
|
+
buildId: string | null;
|
|
81
|
+
/**
|
|
82
|
+
* Auto-upgrade fan-out outcome. Present on every publish into an existing
|
|
83
|
+
* app; omitted for the new-app branch (no installs to fan out to). When
|
|
84
|
+
* `failed > 0` the publisher should expect some installs are pinned to
|
|
85
|
+
* the new version but still serving the old WFP bundle — recover with
|
|
86
|
+
* `sprigr app upgrade <slug> --force` on those tenants, or grep
|
|
87
|
+
* system_logs AE for `marketplace.upgrade.fanout_failed` /
|
|
88
|
+
* `marketplace.upgrade.fanout_threw` by `app_slug`.
|
|
89
|
+
*/
|
|
90
|
+
fanout?: {
|
|
91
|
+
upgraded: number;
|
|
92
|
+
skipped: number;
|
|
93
|
+
failed: number;
|
|
94
|
+
threw?: string;
|
|
95
|
+
};
|
|
96
|
+
}>;
|
|
97
|
+
/**
|
|
98
|
+
* POST /api/v1/data/apps/:slug/install/upgrade
|
|
99
|
+
*
|
|
100
|
+
* Manually upgrade this company's install of `appSlug` to the latest
|
|
101
|
+
* approved version. Bumps `pinned_version_id` and enqueues a fresh
|
|
102
|
+
* build via `enqueueBuildForMarketplaceVersion`, so the per-install
|
|
103
|
+
* WFP script picks up the new bundle. Returns the `buildId` for
|
|
104
|
+
* status polling.
|
|
105
|
+
*
|
|
106
|
+
* Idempotent — returns HTTP 400 "Already on latest version" if the
|
|
107
|
+
* install is already pinned to current_version (unless `force=true`).
|
|
108
|
+
* Integration/tool kind only; agent templates use a different update
|
|
109
|
+
* path.
|
|
110
|
+
*
|
|
111
|
+
* Use this to recover from a silent publish-fanout enqueue failure
|
|
112
|
+
* (the symptom: a fresh version exists but no build with
|
|
113
|
+
* `triggeredBy: 'publish-fanout'` appears in the install's
|
|
114
|
+
* website_status after publish). Pass `force=true` when the pin
|
|
115
|
+
* already moved but no build deployed — the endpoint will re-run
|
|
116
|
+
* `applyMarketplaceInstallUpgrade` to re-enqueue the build.
|
|
117
|
+
*/
|
|
118
|
+
upgradeMarketplaceInstall(args: {
|
|
119
|
+
appSlug: string;
|
|
120
|
+
/** Bypass the "Already on latest version" guard. See doc above. */
|
|
121
|
+
force?: boolean;
|
|
122
|
+
}): Promise<{
|
|
123
|
+
ok: true;
|
|
124
|
+
installationId: string;
|
|
125
|
+
oldVersionId: string | null;
|
|
126
|
+
newVersionId: string;
|
|
127
|
+
newVersion: string;
|
|
128
|
+
buildId: string;
|
|
129
|
+
}>;
|
|
130
|
+
/**
|
|
131
|
+
* DELETE /api/v1/data/apps/:slug
|
|
132
|
+
*
|
|
133
|
+
* Hard-deletes a marketplace app + all its versions + any leftover
|
|
134
|
+
* install rows owned by the publisher. Releases the slug so it can be
|
|
135
|
+
* republished under a different publisher. Only the current publisher
|
|
136
|
+
* can call this (server-side check on company_id).
|
|
137
|
+
*
|
|
138
|
+
* Use case: migrating an app between publishers (e.g. personal
|
|
139
|
+
* staging tenant -> sprigr-hq). Walk the runbook at
|
|
140
|
+
* sprigr-apps/docs/migrate-app-publisher.md; uninstall on every
|
|
141
|
+
* installer tenant first, THEN delete from the old publisher, THEN
|
|
142
|
+
* re-publish under the new publisher.
|
|
143
|
+
*
|
|
144
|
+
* Returns `{ ok: true }` on success. 403 if the calling profile is
|
|
145
|
+
* not the current publisher. 404 if the slug doesn't exist.
|
|
146
|
+
*/
|
|
147
|
+
deleteMarketplaceApp(args: {
|
|
148
|
+
appSlug: string;
|
|
149
|
+
}): Promise<{
|
|
150
|
+
ok: true;
|
|
151
|
+
}>;
|
|
152
|
+
/**
|
|
153
|
+
* POST /api/v1/data/apps/:slug/install
|
|
154
|
+
*
|
|
155
|
+
* Install a marketplace app on the calling tenant. Body shape is
|
|
156
|
+
* snake_case (granted_scopes, install_secrets); camelCase parses but
|
|
157
|
+
* the server silently ignores unknown keys and then 400s with
|
|
158
|
+
* "Missing required secrets: ..." — the publisher-migration footgun
|
|
159
|
+
* documented in sprigr-apps/docs/migrate-app-publisher.md.
|
|
160
|
+
*
|
|
161
|
+
* `grantedScopes` MUST be a subset of manifest.permissions.scopes.
|
|
162
|
+
* `installSecrets` keys MUST appear in manifest.secrets[]; every
|
|
163
|
+
* `required: true` entry must be present and non-empty.
|
|
164
|
+
*
|
|
165
|
+
* Returns the install row id + the per-install website + the
|
|
166
|
+
* triggered build id (for SSR apps).
|
|
167
|
+
*/
|
|
168
|
+
/**
|
|
169
|
+
* POST /api/v1/data/apps/:slug/share
|
|
170
|
+
*
|
|
171
|
+
* Generate a share token + auto-bump trust_tier from `private` to
|
|
172
|
+
* `shared` so other tenants in the same env can install the app
|
|
173
|
+
* without the publisher's credentials. Publisher-only — 404 if the
|
|
174
|
+
* caller doesn't own the app.
|
|
175
|
+
*
|
|
176
|
+
* Calling again rotates the token (the previous one stops working)
|
|
177
|
+
* but the trust_tier stays at whatever it already is — only the
|
|
178
|
+
* private→shared promotion happens automatically.
|
|
179
|
+
*/
|
|
180
|
+
shareMarketplaceApp(args: {
|
|
181
|
+
appSlug: string;
|
|
182
|
+
}): Promise<{
|
|
183
|
+
share_token: string;
|
|
184
|
+
share_url: string;
|
|
185
|
+
}>;
|
|
186
|
+
installMarketplaceApp(args: {
|
|
187
|
+
appSlug: string;
|
|
188
|
+
grantedScopes: string[];
|
|
189
|
+
installSecrets?: Record<string, string>;
|
|
190
|
+
agentIds?: string[] | null;
|
|
191
|
+
config?: Record<string, unknown>;
|
|
192
|
+
}): Promise<{
|
|
193
|
+
installation: {
|
|
194
|
+
id: string;
|
|
195
|
+
integration_id?: string;
|
|
196
|
+
website_id?: string | null;
|
|
197
|
+
website_slug?: string | null;
|
|
198
|
+
build_id?: string | null;
|
|
199
|
+
};
|
|
200
|
+
}>;
|
|
201
|
+
/**
|
|
202
|
+
* POST /api/v1/data/apps/:slug/publisher-secrets
|
|
203
|
+
*
|
|
204
|
+
* Seed or rotate the publisher-provided secret bag on the app row.
|
|
205
|
+
* Auth: caller must be the app's publisher. Plaintext values are
|
|
206
|
+
* never returned — `updated_keys` and `total_keys` confirm what
|
|
207
|
+
* landed without revealing values.
|
|
208
|
+
*/
|
|
209
|
+
setMarketplaceAppPublisherSecrets(args: {
|
|
210
|
+
appSlug: string;
|
|
211
|
+
secrets: Record<string, string>;
|
|
212
|
+
}): Promise<{
|
|
213
|
+
ok: true;
|
|
214
|
+
updated_keys: string[];
|
|
215
|
+
total_keys: number;
|
|
216
|
+
}>;
|
|
217
|
+
/** GET /api/v1/data/websites/:siteId/builds */
|
|
218
|
+
listBuilds(siteId: string, limit?: number): Promise<{
|
|
219
|
+
builds: BuildSummary[];
|
|
220
|
+
}>;
|
|
221
|
+
/** GET /api/v1/data/websites/:siteId/builds/:buildId */
|
|
222
|
+
getBuild(siteId: string, buildId: string): Promise<{
|
|
223
|
+
build: BuildSummary;
|
|
224
|
+
}>;
|
|
225
|
+
/**
|
|
226
|
+
* GET /api/v1/data/websites/:siteId/builds/:buildId/log
|
|
227
|
+
*
|
|
228
|
+
* Returns the captured stdout/stderr as plain text, or `null` when the
|
|
229
|
+
* server reports no log exists (404). Other failures throw `ApiError`
|
|
230
|
+
* so the caller can distinguish "no log" from "unreachable".
|
|
231
|
+
*/
|
|
232
|
+
getBuildLog(siteId: string, buildId: string): Promise<string | null>;
|
|
233
|
+
private request;
|
|
234
|
+
}
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin HTTP client for the Sprigr public API. All routes go through the
|
|
3
|
+
* gateway (`/api/v1/...`), authenticated by the same `sk_mcp_*` Bearer
|
|
4
|
+
* token the MCP layer accepts. The gateway resolves the key to a
|
|
5
|
+
* `companyId` and rewrites the path to `/api/data/...` before forwarding
|
|
6
|
+
* to the provisioning worker.
|
|
7
|
+
*/
|
|
8
|
+
export class ApiError extends Error {
|
|
9
|
+
status;
|
|
10
|
+
body;
|
|
11
|
+
constructor(status, message, body) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.status = status;
|
|
14
|
+
this.body = body;
|
|
15
|
+
this.name = 'ApiError';
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class ApiClient {
|
|
19
|
+
endpoint;
|
|
20
|
+
apiKey;
|
|
21
|
+
fetchImpl;
|
|
22
|
+
constructor(opts) {
|
|
23
|
+
this.endpoint = opts.endpoint;
|
|
24
|
+
this.apiKey = opts.apiKey;
|
|
25
|
+
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
26
|
+
}
|
|
27
|
+
/** POST /api/v1/data/websites/:siteId/builds */
|
|
28
|
+
async startBuild(args) {
|
|
29
|
+
return this.request('POST', `/api/v1/data/websites/${encodeURIComponent(args.siteId)}/builds`, {
|
|
30
|
+
files: args.files,
|
|
31
|
+
framework: args.framework ?? 'static',
|
|
32
|
+
source: args.source ?? 'cli',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* POST /api/v1/data/marketplace/apps/publish
|
|
37
|
+
*
|
|
38
|
+
* Publish a new version of a marketplace app. The body carries the
|
|
39
|
+
* parsed manifest separately from the bundle so the server doesn't
|
|
40
|
+
* re-parse the bundle to route the build. Server re-runs all the
|
|
41
|
+
* validators a second time (defence in depth) and returns the new
|
|
42
|
+
* version + an optional buildId when SSR builds are queued.
|
|
43
|
+
*/
|
|
44
|
+
async publishMarketplaceApp(args) {
|
|
45
|
+
return this.request('POST', '/api/v1/data/marketplace/apps/publish', {
|
|
46
|
+
manifest: args.manifest,
|
|
47
|
+
files: args.files,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* POST /api/v1/data/apps/:slug/install/upgrade
|
|
52
|
+
*
|
|
53
|
+
* Manually upgrade this company's install of `appSlug` to the latest
|
|
54
|
+
* approved version. Bumps `pinned_version_id` and enqueues a fresh
|
|
55
|
+
* build via `enqueueBuildForMarketplaceVersion`, so the per-install
|
|
56
|
+
* WFP script picks up the new bundle. Returns the `buildId` for
|
|
57
|
+
* status polling.
|
|
58
|
+
*
|
|
59
|
+
* Idempotent — returns HTTP 400 "Already on latest version" if the
|
|
60
|
+
* install is already pinned to current_version (unless `force=true`).
|
|
61
|
+
* Integration/tool kind only; agent templates use a different update
|
|
62
|
+
* path.
|
|
63
|
+
*
|
|
64
|
+
* Use this to recover from a silent publish-fanout enqueue failure
|
|
65
|
+
* (the symptom: a fresh version exists but no build with
|
|
66
|
+
* `triggeredBy: 'publish-fanout'` appears in the install's
|
|
67
|
+
* website_status after publish). Pass `force=true` when the pin
|
|
68
|
+
* already moved but no build deployed — the endpoint will re-run
|
|
69
|
+
* `applyMarketplaceInstallUpgrade` to re-enqueue the build.
|
|
70
|
+
*/
|
|
71
|
+
async upgradeMarketplaceInstall(args) {
|
|
72
|
+
return this.request('POST', `/api/v1/data/apps/${encodeURIComponent(args.appSlug)}/install/upgrade`, args.force ? { force: true } : {});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* DELETE /api/v1/data/apps/:slug
|
|
76
|
+
*
|
|
77
|
+
* Hard-deletes a marketplace app + all its versions + any leftover
|
|
78
|
+
* install rows owned by the publisher. Releases the slug so it can be
|
|
79
|
+
* republished under a different publisher. Only the current publisher
|
|
80
|
+
* can call this (server-side check on company_id).
|
|
81
|
+
*
|
|
82
|
+
* Use case: migrating an app between publishers (e.g. personal
|
|
83
|
+
* staging tenant -> sprigr-hq). Walk the runbook at
|
|
84
|
+
* sprigr-apps/docs/migrate-app-publisher.md; uninstall on every
|
|
85
|
+
* installer tenant first, THEN delete from the old publisher, THEN
|
|
86
|
+
* re-publish under the new publisher.
|
|
87
|
+
*
|
|
88
|
+
* Returns `{ ok: true }` on success. 403 if the calling profile is
|
|
89
|
+
* not the current publisher. 404 if the slug doesn't exist.
|
|
90
|
+
*/
|
|
91
|
+
async deleteMarketplaceApp(args) {
|
|
92
|
+
return this.request('DELETE', `/api/v1/data/apps/${encodeURIComponent(args.appSlug)}`);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* POST /api/v1/data/apps/:slug/install
|
|
96
|
+
*
|
|
97
|
+
* Install a marketplace app on the calling tenant. Body shape is
|
|
98
|
+
* snake_case (granted_scopes, install_secrets); camelCase parses but
|
|
99
|
+
* the server silently ignores unknown keys and then 400s with
|
|
100
|
+
* "Missing required secrets: ..." — the publisher-migration footgun
|
|
101
|
+
* documented in sprigr-apps/docs/migrate-app-publisher.md.
|
|
102
|
+
*
|
|
103
|
+
* `grantedScopes` MUST be a subset of manifest.permissions.scopes.
|
|
104
|
+
* `installSecrets` keys MUST appear in manifest.secrets[]; every
|
|
105
|
+
* `required: true` entry must be present and non-empty.
|
|
106
|
+
*
|
|
107
|
+
* Returns the install row id + the per-install website + the
|
|
108
|
+
* triggered build id (for SSR apps).
|
|
109
|
+
*/
|
|
110
|
+
/**
|
|
111
|
+
* POST /api/v1/data/apps/:slug/share
|
|
112
|
+
*
|
|
113
|
+
* Generate a share token + auto-bump trust_tier from `private` to
|
|
114
|
+
* `shared` so other tenants in the same env can install the app
|
|
115
|
+
* without the publisher's credentials. Publisher-only — 404 if the
|
|
116
|
+
* caller doesn't own the app.
|
|
117
|
+
*
|
|
118
|
+
* Calling again rotates the token (the previous one stops working)
|
|
119
|
+
* but the trust_tier stays at whatever it already is — only the
|
|
120
|
+
* private→shared promotion happens automatically.
|
|
121
|
+
*/
|
|
122
|
+
async shareMarketplaceApp(args) {
|
|
123
|
+
return this.request('POST', `/api/v1/data/apps/${encodeURIComponent(args.appSlug)}/share`, {});
|
|
124
|
+
}
|
|
125
|
+
async installMarketplaceApp(args) {
|
|
126
|
+
const body = { granted_scopes: args.grantedScopes };
|
|
127
|
+
if (args.installSecrets)
|
|
128
|
+
body.install_secrets = args.installSecrets;
|
|
129
|
+
if (args.agentIds !== undefined)
|
|
130
|
+
body.agent_ids = args.agentIds;
|
|
131
|
+
if (args.config !== undefined)
|
|
132
|
+
body.config = args.config;
|
|
133
|
+
return this.request('POST', `/api/v1/data/apps/${encodeURIComponent(args.appSlug)}/install`, body);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* POST /api/v1/data/apps/:slug/publisher-secrets
|
|
137
|
+
*
|
|
138
|
+
* Seed or rotate the publisher-provided secret bag on the app row.
|
|
139
|
+
* Auth: caller must be the app's publisher. Plaintext values are
|
|
140
|
+
* never returned — `updated_keys` and `total_keys` confirm what
|
|
141
|
+
* landed without revealing values.
|
|
142
|
+
*/
|
|
143
|
+
async setMarketplaceAppPublisherSecrets(args) {
|
|
144
|
+
return this.request('POST', `/api/v1/data/apps/${encodeURIComponent(args.appSlug)}/publisher-secrets`, { secrets: args.secrets });
|
|
145
|
+
}
|
|
146
|
+
/** GET /api/v1/data/websites/:siteId/builds */
|
|
147
|
+
async listBuilds(siteId, limit = 20) {
|
|
148
|
+
return this.request('GET', `/api/v1/data/websites/${encodeURIComponent(siteId)}/builds?limit=${limit}`);
|
|
149
|
+
}
|
|
150
|
+
/** GET /api/v1/data/websites/:siteId/builds/:buildId */
|
|
151
|
+
async getBuild(siteId, buildId) {
|
|
152
|
+
return this.request('GET', `/api/v1/data/websites/${encodeURIComponent(siteId)}/builds/${encodeURIComponent(buildId)}`);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* GET /api/v1/data/websites/:siteId/builds/:buildId/log
|
|
156
|
+
*
|
|
157
|
+
* Returns the captured stdout/stderr as plain text, or `null` when the
|
|
158
|
+
* server reports no log exists (404). Other failures throw `ApiError`
|
|
159
|
+
* so the caller can distinguish "no log" from "unreachable".
|
|
160
|
+
*/
|
|
161
|
+
async getBuildLog(siteId, buildId) {
|
|
162
|
+
const path = `/api/v1/data/websites/${encodeURIComponent(siteId)}/builds/${encodeURIComponent(buildId)}/log`;
|
|
163
|
+
const res = await this.fetchImpl(`${this.endpoint}${path}`, {
|
|
164
|
+
method: 'GET',
|
|
165
|
+
headers: {
|
|
166
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
167
|
+
Accept: 'text/plain, application/json',
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
if (res.status === 404)
|
|
171
|
+
return null;
|
|
172
|
+
const text = await res.text();
|
|
173
|
+
if (!res.ok) {
|
|
174
|
+
let parsed = text;
|
|
175
|
+
try {
|
|
176
|
+
parsed = text.length > 0 ? JSON.parse(text) : undefined;
|
|
177
|
+
}
|
|
178
|
+
catch { /* keep as text */ }
|
|
179
|
+
const p = parsed;
|
|
180
|
+
const message = typeof p === 'object' && p && typeof p.message === 'string'
|
|
181
|
+
? p.message
|
|
182
|
+
: (typeof p === 'object' && p && typeof p.error === 'string'
|
|
183
|
+
? p.error
|
|
184
|
+
: `HTTP ${res.status} ${res.statusText}`);
|
|
185
|
+
throw new ApiError(res.status, message, parsed);
|
|
186
|
+
}
|
|
187
|
+
return text;
|
|
188
|
+
}
|
|
189
|
+
async request(method, path, body) {
|
|
190
|
+
const headers = {
|
|
191
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
192
|
+
Accept: 'application/json',
|
|
193
|
+
};
|
|
194
|
+
let bodyText;
|
|
195
|
+
if (body !== undefined) {
|
|
196
|
+
headers['Content-Type'] = 'application/json';
|
|
197
|
+
bodyText = JSON.stringify(body);
|
|
198
|
+
}
|
|
199
|
+
const res = await this.fetchImpl(`${this.endpoint}${path}`, { method, headers, body: bodyText });
|
|
200
|
+
const text = await res.text();
|
|
201
|
+
let parsed;
|
|
202
|
+
try {
|
|
203
|
+
parsed = text.length > 0 ? JSON.parse(text) : undefined;
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
parsed = text;
|
|
207
|
+
}
|
|
208
|
+
if (!res.ok) {
|
|
209
|
+
const p = parsed;
|
|
210
|
+
const message = typeof p === 'object' && p && typeof p.message === 'string'
|
|
211
|
+
? p.message
|
|
212
|
+
: (typeof p === 'object' && p && typeof p.error === 'string'
|
|
213
|
+
? p.error
|
|
214
|
+
: `HTTP ${res.status} ${res.statusText}`);
|
|
215
|
+
throw new ApiError(res.status, message, parsed);
|
|
216
|
+
}
|
|
217
|
+
return parsed;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
//# 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":"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"}
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the Sprigr API key.
|
|
3
|
+
*
|
|
4
|
+
* Resolution order:
|
|
5
|
+
* 1. `--api-key <key>` flag
|
|
6
|
+
* 2. `SPRIGR_API_KEY` env var
|
|
7
|
+
* 3. Credential file
|
|
8
|
+
* - When `profile` is set: `~/.config/sprigr/credentials/<profile>.json`
|
|
9
|
+
* - Otherwise (legacy default): `~/.config/sprigr/credentials.json`
|
|
10
|
+
*
|
|
11
|
+
* If none are set, returns null. Callers must surface a clear "run
|
|
12
|
+
* `sprigr login` or set SPRIGR_API_KEY" message rather than failing
|
|
13
|
+
* silently.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveApiKey(opts: {
|
|
16
|
+
flag?: string;
|
|
17
|
+
profile?: string;
|
|
18
|
+
}): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* Compute the credentials file path. Honors $XDG_CONFIG_HOME so users
|
|
21
|
+
* who customize their config layout still land in the right place;
|
|
22
|
+
* falls back to ~/.config/sprigr otherwise (Linux + macOS convention).
|
|
23
|
+
*
|
|
24
|
+
* When `profile` is given (e.g. `--profile staging` or `SPRIGR_PROFILE=...`),
|
|
25
|
+
* the file lives under `<config>/sprigr/credentials/<profile>.json` so
|
|
26
|
+
* multiple tenants can be logged in side-by-side without overwriting each
|
|
27
|
+
* other. When omitted, returns the legacy single-file path so existing
|
|
28
|
+
* setups keep working unchanged.
|
|
29
|
+
*/
|
|
30
|
+
export declare function credentialsFilePath(profile?: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Profile names map to filenames on disk; reject anything that could
|
|
33
|
+
* escape the credentials directory or look like a path traversal. The
|
|
34
|
+
* regex below also doubles as a sanity check on length (max 64 chars).
|
|
35
|
+
*/
|
|
36
|
+
export declare function assertValidProfileName(name: string): void;
|
|
37
|
+
/**
|
|
38
|
+
* Stored credentials. Only `apiKey` is required to authenticate; the
|
|
39
|
+
* rest is metadata the CLI surfaces to the user (e.g. `sprigr whoami`)
|
|
40
|
+
* and to make audit-style writes attributable.
|
|
41
|
+
*/
|
|
42
|
+
export interface StoredCredentials {
|
|
43
|
+
apiKey: string;
|
|
44
|
+
keyPrefix?: string;
|
|
45
|
+
keyId?: string;
|
|
46
|
+
companyId?: string;
|
|
47
|
+
userId?: string;
|
|
48
|
+
endpoint?: string;
|
|
49
|
+
/** ISO 8601 timestamp from when the file was written. */
|
|
50
|
+
loggedInAt: string;
|
|
51
|
+
/**
|
|
52
|
+
* The profile this credential set was saved under. Stamped on write
|
|
53
|
+
* so `sprigr whoami` can echo it back; never read for authentication
|
|
54
|
+
* (the disk path is the source of truth for which profile is active).
|
|
55
|
+
*/
|
|
56
|
+
profile?: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Read the `endpoint` field from the credentials file written by
|
|
60
|
+
* `sprigr login`. Used by `resolveEndpoint` so a `--endpoint` passed
|
|
61
|
+
* to `login` sticks for subsequent commands in the same session
|
|
62
|
+
* without the user having to re-pass it (or export SPRIGR_ENDPOINT).
|
|
63
|
+
*
|
|
64
|
+
* Without this, `sprigr login --endpoint https://staging-api-team.sprigr.com`
|
|
65
|
+
* mints a key against staging, but the next `sprigr app publish` (no
|
|
66
|
+
* flag) sends that staging key to the prod gateway — which rejects it
|
|
67
|
+
* 401 because the key row only exists in the staging registry DB.
|
|
68
|
+
* The CLI then surfaces "HTTP 401 — Unauthorized" with no hint that
|
|
69
|
+
* the user is talking to the wrong env.
|
|
70
|
+
*/
|
|
71
|
+
export declare function readEndpointFromFileSync(profile?: string): string | null;
|
|
72
|
+
/** Read the full credentials record (used by `whoami`). Returns null on miss. */
|
|
73
|
+
export declare function readCredentialsSync(profile?: string): StoredCredentials | null;
|
|
74
|
+
/**
|
|
75
|
+
* List every profile we have credentials for. Returns the literal names
|
|
76
|
+
* (sans .json suffix); when only the legacy single-file credentials exist
|
|
77
|
+
* the special name `'(default)'` is included so `sprigr whoami --all` can
|
|
78
|
+
* show it. Returns an empty array if nothing is logged in.
|
|
79
|
+
*/
|
|
80
|
+
export declare function listProfilesSync(): Array<{
|
|
81
|
+
name: string;
|
|
82
|
+
isLegacy: boolean;
|
|
83
|
+
}>;
|
|
84
|
+
/**
|
|
85
|
+
* Write credentials to disk with mode 0600 (owner read/write only).
|
|
86
|
+
* The directory is created with mode 0700 if it doesn't yet exist —
|
|
87
|
+
* sibling tools like `vercel`, `gh`, `wrangler` follow the same
|
|
88
|
+
* convention. Overwrites any existing credentials.
|
|
89
|
+
*
|
|
90
|
+
* When `profile` is set the file lives at `<config>/sprigr/credentials/<profile>.json`
|
|
91
|
+
* (and the parent `credentials/` dir is created at 0700 too). Without a
|
|
92
|
+
* profile we write the legacy `<config>/sprigr/credentials.json` so
|
|
93
|
+
* existing setups stay byte-identical.
|
|
94
|
+
*/
|
|
95
|
+
export declare function writeCredentials(creds: StoredCredentials, profile?: string): Promise<string>;
|
|
96
|
+
/**
|
|
97
|
+
* Delete the credential file. Returns true if a file was removed,
|
|
98
|
+
* false if no file was there to begin with. Other errors (permission,
|
|
99
|
+
* etc.) propagate so the caller can surface a useful message.
|
|
100
|
+
*/
|
|
101
|
+
export declare function deleteCredentials(profile?: string): Promise<boolean>;
|
|
102
|
+
/** Friendly message shown when no API key is configured. */
|
|
103
|
+
export declare const MISSING_API_KEY_MESSAGE = "No API key found.\n\nRun `sprigr login` to authenticate via the portal, or set\nSPRIGR_API_KEY in your shell:\n\n export SPRIGR_API_KEY=sk_mcp_xxxxx\n\nYou can also pass --api-key on the command line. Manage keys at\nteam.sprigr.com under Settings \u2192 API keys.\n\nTo log in to a specific tenant without overwriting another, use a profile:\n\n sprigr login --profile staging --endpoint staging\n sprigr login --profile prod --endpoint prod\n sprigr --profile staging app publish --dir <dir>\n SPRIGR_PROFILE=prod sprigr app publish --dir <dir>\n";
|