@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/dist/bundler.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
const MAX_FILES = 500;
|
|
4
|
+
const MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
5
|
+
const DEFAULT_IGNORES = [
|
|
6
|
+
'node_modules',
|
|
7
|
+
'.git',
|
|
8
|
+
'.DS_Store',
|
|
9
|
+
'.sprigrignore',
|
|
10
|
+
'dist',
|
|
11
|
+
'.next',
|
|
12
|
+
'.turbo',
|
|
13
|
+
];
|
|
14
|
+
export async function bundleDirectory(rootDir, framework = 'static') {
|
|
15
|
+
const errors = [];
|
|
16
|
+
const files = {};
|
|
17
|
+
let totalBytes = 0;
|
|
18
|
+
let binaryCount = 0;
|
|
19
|
+
const stat = await fs.stat(rootDir).catch(() => null);
|
|
20
|
+
if (!stat) {
|
|
21
|
+
return { errors: [`Directory not found: ${rootDir}`] };
|
|
22
|
+
}
|
|
23
|
+
if (!stat.isDirectory()) {
|
|
24
|
+
return { errors: [`${rootDir} is not a directory`] };
|
|
25
|
+
}
|
|
26
|
+
const ignorePatterns = await loadIgnore(rootDir);
|
|
27
|
+
async function walk(currentDir, relPrefix) {
|
|
28
|
+
const entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
const relPath = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
|
|
31
|
+
if (shouldIgnore(entry.name, relPath, ignorePatterns))
|
|
32
|
+
continue;
|
|
33
|
+
const absPath = path.join(currentDir, entry.name);
|
|
34
|
+
if (entry.isDirectory()) {
|
|
35
|
+
await walk(absPath, relPath);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (!entry.isFile())
|
|
39
|
+
continue; // skip symlinks, sockets, etc.
|
|
40
|
+
const buf = await fs.readFile(absPath);
|
|
41
|
+
if (buf.byteLength > MAX_FILE_BYTES) {
|
|
42
|
+
errors.push(`${relPath}: exceeds 5 MiB limit (${(buf.byteLength / 1024 / 1024).toFixed(2)} MiB)`);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
// Re-encode round-trip detects non-UTF-8: utf8 decode is lossy on
|
|
46
|
+
// invalid bytes (replaced with U+FFFD), so byte-length comparison
|
|
47
|
+
// catches binaries unambiguously. UTF-8 round-trip is what tells us
|
|
48
|
+
// whether to ship the bare string or the tagged base64 envelope.
|
|
49
|
+
const utf8 = buf.toString('utf8');
|
|
50
|
+
if (Buffer.byteLength(utf8, 'utf8') === buf.byteLength) {
|
|
51
|
+
files[relPath] = utf8;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
files[relPath] = { encoding: 'base64', content: buf.toString('base64') };
|
|
55
|
+
binaryCount++;
|
|
56
|
+
}
|
|
57
|
+
totalBytes += buf.byteLength;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
await walk(rootDir, '');
|
|
61
|
+
const fileCount = Object.keys(files).length;
|
|
62
|
+
if (fileCount === 0)
|
|
63
|
+
errors.push('No files to upload after applying ignores');
|
|
64
|
+
if (fileCount > MAX_FILES)
|
|
65
|
+
errors.push(`Too many files (${fileCount}) — maximum 500 per build`);
|
|
66
|
+
// Framework-aware root-file check.
|
|
67
|
+
// - static: the bundle IS the deployment, so index.html is mandatory.
|
|
68
|
+
// - next/astro/remix: the bundle is the SOURCE, the build produces
|
|
69
|
+
// index.html. We require package.json instead so the user catches
|
|
70
|
+
// "wrong directory" mistakes before round-tripping a doomed build.
|
|
71
|
+
if (framework === 'static') {
|
|
72
|
+
if (!('index.html' in files)) {
|
|
73
|
+
errors.push('Missing index.html — every static deployment must include an index.html at the bundle root');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
if (!('package.json' in files)) {
|
|
78
|
+
errors.push(`Missing package.json — ${framework} builds need a package.json at the bundle root (you may have pointed --dir at a build output instead of the source tree)`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (errors.length > 0)
|
|
82
|
+
return { errors };
|
|
83
|
+
return { files, totalBytes, fileCount, binaryCount };
|
|
84
|
+
}
|
|
85
|
+
async function loadIgnore(rootDir) {
|
|
86
|
+
const patterns = [...DEFAULT_IGNORES];
|
|
87
|
+
try {
|
|
88
|
+
const raw = await fs.readFile(path.join(rootDir, '.sprigrignore'), 'utf8');
|
|
89
|
+
for (const line of raw.split('\n')) {
|
|
90
|
+
const trimmed = line.trim();
|
|
91
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
92
|
+
continue;
|
|
93
|
+
patterns.push(trimmed);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
// No ignore file — defaults only.
|
|
98
|
+
}
|
|
99
|
+
return patterns;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Match an entry name and its full relative path against the ignore list.
|
|
103
|
+
* Patterns can be:
|
|
104
|
+
* - bare names ("node_modules") — match anywhere in the tree
|
|
105
|
+
* - rooted paths ("dist/cache") — match exact segment
|
|
106
|
+
* - leading-dot literals (".env.local") — match by name
|
|
107
|
+
* Glob support is intentionally minimal in MVP.
|
|
108
|
+
*/
|
|
109
|
+
function shouldIgnore(name, relPath, patterns) {
|
|
110
|
+
for (const p of patterns) {
|
|
111
|
+
if (p === name)
|
|
112
|
+
return true;
|
|
113
|
+
if (p === relPath)
|
|
114
|
+
return true;
|
|
115
|
+
if (relPath.startsWith(`${p}/`))
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
// Top-level dotfiles default-hidden so secrets in .env etc. don't ship.
|
|
119
|
+
if (!relPath.includes('/') && name.startsWith('.'))
|
|
120
|
+
return true;
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=bundler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundler.js","sourceRoot":"","sources":["../src/bundler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sprigr app publish` and `sprigr app validate`.
|
|
3
|
+
*
|
|
4
|
+
* Phase 2.1b of the ASCS platform implementation plan.
|
|
5
|
+
*
|
|
6
|
+
* sprigr app publish --dir <bundle-dir> [--manifest <path>]
|
|
7
|
+
* Reads the manifest, runs publish-time validation locally, bundles
|
|
8
|
+
* the directory the same way `sprigr deploy` does, and POSTs to
|
|
9
|
+
* /api/v1/data/marketplace/apps/publish. The server re-validates +
|
|
10
|
+
* stores the version + triggers the build-runner. We intentionally
|
|
11
|
+
* run validation client-side too — the publish API is slow (uploads
|
|
12
|
+
* a bundle to R2) so a clear local error beats a server round-trip.
|
|
13
|
+
*
|
|
14
|
+
* sprigr app validate --dir <bundle-dir> [--manifest <path>]
|
|
15
|
+
* Same validation as publish, no upload. Suitable for CI / local dev.
|
|
16
|
+
*
|
|
17
|
+
* Manifest format note (v1):
|
|
18
|
+
* The plan calls for sprigr-app.yaml. The CLI v1 ships with JSON
|
|
19
|
+
* support only (sprigr-app.json) so the npm-published bundle stays
|
|
20
|
+
* dependency-light. YAML support is a follow-up that adds the `yaml`
|
|
21
|
+
* package and detects sprigr-app.yaml in addition to sprigr-app.json.
|
|
22
|
+
* Server-side handling is unchanged either way — the wire payload is
|
|
23
|
+
* always JSON.
|
|
24
|
+
*
|
|
25
|
+
* Why the manifest is read separately from the bundle dir:
|
|
26
|
+
* The bundler walks the dir and uploads every file under it, so the
|
|
27
|
+
* manifest naturally lands in the bundle too — but we ALSO need the
|
|
28
|
+
* parsed manifest object to validate locally and to send as a separate
|
|
29
|
+
* field on the publish payload (the server reads it from there to
|
|
30
|
+
* route the build, not by parsing the bundle). Hence a separate read.
|
|
31
|
+
*/
|
|
32
|
+
import { type AppManifest } from '../manifest.js';
|
|
33
|
+
export interface RunAppPublishArgs {
|
|
34
|
+
dir: string;
|
|
35
|
+
manifestPath?: string;
|
|
36
|
+
endpoint: string;
|
|
37
|
+
apiKey: string;
|
|
38
|
+
/** Replace the default fetch (test seam). */
|
|
39
|
+
fetchImpl?: typeof fetch;
|
|
40
|
+
}
|
|
41
|
+
export interface RunAppValidateArgs {
|
|
42
|
+
dir: string;
|
|
43
|
+
manifestPath?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface AppCommandResult {
|
|
46
|
+
exitCode: 0 | 1 | 2;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Read + parse the manifest. Defaults to `<dir>/sprigr-app.json`. Returns
|
|
50
|
+
* a structured error object on failure so the caller can surface a clean
|
|
51
|
+
* message rather than a stack trace.
|
|
52
|
+
*/
|
|
53
|
+
export declare function loadManifest(args: {
|
|
54
|
+
dir: string;
|
|
55
|
+
manifestPath?: string;
|
|
56
|
+
}): Promise<{
|
|
57
|
+
manifest: AppManifest;
|
|
58
|
+
resolvedPath: string;
|
|
59
|
+
} | {
|
|
60
|
+
error: string;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Run the publish-time validators against a parsed manifest. Mirrors
|
|
64
|
+
* the server's validateManifest in workers/provisioning so that local
|
|
65
|
+
* `app validate` produces the same verdict the publish API would. The
|
|
66
|
+
* legacy fields (sprigr_app, metadata, etc.) are checked here too —
|
|
67
|
+
* the shared package's per-field validators only cover the new
|
|
68
|
+
* marketplace-hosting fields.
|
|
69
|
+
*/
|
|
70
|
+
export declare function validateManifestLocally(manifest: AppManifest): string | null;
|
|
71
|
+
/**
|
|
72
|
+
* Per-framework filesystem checks (#1022). Runs after manifest validation
|
|
73
|
+
* and BEFORE bundle upload, so authors hear about missing required files
|
|
74
|
+
* locally instead of after the round-trip through publish → build → install
|
|
75
|
+
* → failure. Currently only Next.js has a checkable contract; other
|
|
76
|
+
* frameworks return null.
|
|
77
|
+
*
|
|
78
|
+
* Async because the check stats the customer's bundle dir for the
|
|
79
|
+
* required config files. Returns null on success or an error message
|
|
80
|
+
* suitable for stderr.
|
|
81
|
+
*/
|
|
82
|
+
export declare function validateFrameworkFiles(manifest: AppManifest, dir: string): Promise<string | null>;
|
|
83
|
+
export declare function runAppValidate(args: RunAppValidateArgs): Promise<AppCommandResult>;
|
|
84
|
+
export declare function runAppPublish(args: RunAppPublishArgs): Promise<AppCommandResult>;
|
|
85
|
+
export interface RunAppUpgradeArgs {
|
|
86
|
+
/** Marketplace app slug to upgrade this company's install of. */
|
|
87
|
+
slug: string;
|
|
88
|
+
endpoint: string;
|
|
89
|
+
apiKey: string;
|
|
90
|
+
/**
|
|
91
|
+
* Bypass the server-side "Already on latest version" guard. Re-runs the
|
|
92
|
+
* upgrade flow even when `pinned_version_id` already matches the current
|
|
93
|
+
* approved version. Use when the previous publish-fanout silently dropped
|
|
94
|
+
* the build enqueue (the pin moved but no build deployed).
|
|
95
|
+
*/
|
|
96
|
+
force?: boolean;
|
|
97
|
+
/** Override fetch (tests). */
|
|
98
|
+
fetchImpl?: typeof fetch;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* `sprigr app upgrade <slug>` — manually bump this company's install of
|
|
102
|
+
* a marketplace app to the latest approved version. Hits
|
|
103
|
+
* POST /api/v1/data/apps/:slug/install/upgrade which:
|
|
104
|
+
* 1. Bumps `pinned_version_id` to current_version
|
|
105
|
+
* 2. Refreshes the integration row's snapshot manifest
|
|
106
|
+
* 3. Syncs the company's integration KV cache + reinits agent DOs
|
|
107
|
+
* 4. Enqueues a fresh build via `enqueueBuildForMarketplaceVersion`
|
|
108
|
+
*
|
|
109
|
+
* Returns the buildId for status polling. Idempotent — returns HTTP 400
|
|
110
|
+
* "Already on latest version" if the install is already pinned to the
|
|
111
|
+
* current version.
|
|
112
|
+
*
|
|
113
|
+
* Use this to recover from a silent publish-fanout enqueue failure (the
|
|
114
|
+
* symptom: a fresh version exists in `current_version` but no
|
|
115
|
+
* publish-fanout build appears in website_status after the publish).
|
|
116
|
+
*/
|
|
117
|
+
export declare function runAppUpgrade(args: RunAppUpgradeArgs): Promise<AppCommandResult>;
|
|
118
|
+
export interface RunAppDeleteArgs {
|
|
119
|
+
slug?: string;
|
|
120
|
+
endpoint: string;
|
|
121
|
+
apiKey: string;
|
|
122
|
+
/** Skip the interactive "are you sure" confirmation prompt. Required when
|
|
123
|
+
* running non-interactively (CI, scripts) since the prompt would hang. */
|
|
124
|
+
yes?: boolean;
|
|
125
|
+
/** Override fetch (tests). */
|
|
126
|
+
fetchImpl?: typeof fetch;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* `sprigr app delete <slug>` — hard-delete a marketplace app + all its
|
|
130
|
+
* versions + any leftover install rows owned by the publisher. Releases
|
|
131
|
+
* the slug for re-publication.
|
|
132
|
+
*
|
|
133
|
+
* Use this when migrating an app between publishers (personal staging
|
|
134
|
+
* → sprigr-hq, etc.). The runbook lives at
|
|
135
|
+
* sprigr-apps/docs/migrate-app-publisher.md — uninstall on every
|
|
136
|
+
* installer tenant first, THEN delete from the old publisher, THEN
|
|
137
|
+
* re-publish under the new publisher.
|
|
138
|
+
*
|
|
139
|
+
* The endpoint hard-deletes everything: all marketplace_app_versions
|
|
140
|
+
* rows, R2 bundle objects, any orphaned app_installations. Only the
|
|
141
|
+
* current publisher can call it (403 otherwise).
|
|
142
|
+
*
|
|
143
|
+
* Pass --yes to skip the confirmation prompt. CI / non-interactive
|
|
144
|
+
* callers must always pass --yes.
|
|
145
|
+
*/
|
|
146
|
+
export declare function runAppDelete(args: RunAppDeleteArgs): Promise<AppCommandResult>;
|
|
147
|
+
export interface RunAppShareArgs {
|
|
148
|
+
slug?: string;
|
|
149
|
+
endpoint: string;
|
|
150
|
+
apiKey: string;
|
|
151
|
+
/** Override fetch (tests). */
|
|
152
|
+
fetchImpl?: typeof fetch;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* `sprigr app share <slug>` — generate a share token + auto-bump
|
|
156
|
+
* trust_tier from `private` to `shared`.
|
|
157
|
+
*
|
|
158
|
+
* Publisher-only. Lets other tenants in the same env install the app
|
|
159
|
+
* via their own CLI/MCP/portal without sharing the publisher's
|
|
160
|
+
* credentials. Calling again rotates the token but the trust tier
|
|
161
|
+
* stays at whatever it already is.
|
|
162
|
+
*
|
|
163
|
+
* The share URL prints to stdout (paste into a tenant browser to
|
|
164
|
+
* install via the share-link flow), but the more common path post-
|
|
165
|
+
* share is `sprigr app install <slug> --profile <other-tenant>` since
|
|
166
|
+
* trust_tier='shared' allows any tenant to install.
|
|
167
|
+
*/
|
|
168
|
+
export declare function runAppShare(args: RunAppShareArgs): Promise<AppCommandResult>;
|
|
169
|
+
export interface RunAppInstallArgs {
|
|
170
|
+
slug?: string;
|
|
171
|
+
endpoint: string;
|
|
172
|
+
apiKey: string;
|
|
173
|
+
/** Comma-separated scope list (e.g. "tools:register,knowledge:read").
|
|
174
|
+
* Must be a subset of manifest.permissions.scopes. */
|
|
175
|
+
scopes?: string;
|
|
176
|
+
/** Per-install secret values: repeatable --secret KEY=VALUE. Each KEY
|
|
177
|
+
* must appear in manifest.secrets[]. */
|
|
178
|
+
secrets?: Record<string, string>;
|
|
179
|
+
/** Override fetch (tests). */
|
|
180
|
+
fetchImpl?: typeof fetch;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* `sprigr app install <slug> --scopes ... [--secret KEY=VALUE ...]`
|
|
184
|
+
*
|
|
185
|
+
* Install a marketplace app on the calling tenant. Publisher-migration
|
|
186
|
+
* step 5 from sprigr-apps/docs/migrate-app-publisher.md.
|
|
187
|
+
*
|
|
188
|
+
* Body shape gotcha (documented at the runbook level too): the API is
|
|
189
|
+
* snake_case (granted_scopes, install_secrets). Sending camelCase
|
|
190
|
+
* silently parses + ignores the unknown keys → 400 "Missing required
|
|
191
|
+
* secrets". The CLI passes the right shape so callers don't have to
|
|
192
|
+
* remember.
|
|
193
|
+
*/
|
|
194
|
+
export declare function runAppInstall(args: RunAppInstallArgs): Promise<AppCommandResult>;
|
|
195
|
+
export interface RunAppSetPublisherSecretsArgs {
|
|
196
|
+
slug?: string;
|
|
197
|
+
endpoint: string;
|
|
198
|
+
apiKey: string;
|
|
199
|
+
/** Plaintext map of secret key → value. Keys must match
|
|
200
|
+
* manifest.secrets[].key entries marked publisher_provides=true. */
|
|
201
|
+
secrets: Record<string, string>;
|
|
202
|
+
/** Override fetch (tests). */
|
|
203
|
+
fetchImpl?: typeof fetch;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* `sprigr app set-publisher-secrets <slug> --secrets '<json>'`
|
|
207
|
+
*
|
|
208
|
+
* Seed or rotate the publisher-provided secret bag on the app row.
|
|
209
|
+
* The publisher's OAuth-app credentials (SHOPIFY_CLIENT_ID,
|
|
210
|
+
* SHOPIFY_CLIENT_SECRET, etc.) flow through this command — every
|
|
211
|
+
* install of the app picks them up automatically at install time.
|
|
212
|
+
*
|
|
213
|
+
* Auth: caller must be the app's publisher (the API checks
|
|
214
|
+
* publisher_company_id matches the calling tenant). Errors:
|
|
215
|
+
* - 404: caller doesn't own the app, OR slug doesn't exist
|
|
216
|
+
* - 400: a key isn't declared / isn't `publisher_provides: true` /
|
|
217
|
+
* value isn't a non-empty string
|
|
218
|
+
* - 500: WEBSITE_SECRETS_MASTER_KEY not bound on the platform
|
|
219
|
+
*
|
|
220
|
+
* No value is ever returned in plaintext from the API. Rotation =
|
|
221
|
+
* "set a new value"; there's no "show me the current value" path.
|
|
222
|
+
*/
|
|
223
|
+
export declare function runAppSetPublisherSecrets(args: RunAppSetPublisherSecretsArgs): Promise<AppCommandResult>;
|