@fargowealth/fargo-ai-cli 0.7.1 → 0.7.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 +10 -0
- package/README.md +13 -1
- package/package.json +1 -1
- package/src/cli.mjs +87 -1
- package/src/constants.mjs +1 -1
- package/src/deployment-artifact.mjs +28 -1
- package/src/lego-deploy-artifacts.mjs +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.2
|
|
4
|
+
|
|
5
|
+
- Added `fargo lego deploy --help` with the complete Worker Static Assets
|
|
6
|
+
contract and an executable full-stack example.
|
|
7
|
+
- Worker deploys now fail early for manual `.fargo-assets/`, frontend
|
|
8
|
+
`index.html` files inside `--deploy-dir`, overlapping module/assets
|
|
9
|
+
directories, and asset options missing `--assets-dir`.
|
|
10
|
+
- Dry-run deployment summaries now show manifest schema version and separate
|
|
11
|
+
Worker-module and Static Asset file counts.
|
|
12
|
+
|
|
3
13
|
## 0.7.1
|
|
4
14
|
|
|
5
15
|
- Added Worker Static Assets packaging with explicit asset directory, binding,
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Requires Node.js 20.19 or newer.
|
|
|
9
9
|
## Install or upgrade
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
|
-
npm install -g @fargowealth/fargo-ai-cli@0.7.
|
|
12
|
+
npm install -g @fargowealth/fargo-ai-cli@0.7.2
|
|
13
13
|
fargo version
|
|
14
14
|
```
|
|
15
15
|
|
|
@@ -108,6 +108,18 @@ boolean flag when only selected routes should invoke the Worker first. The
|
|
|
108
108
|
hosted LEGO publisher uploads the assets and attaches the binding; Cloudflare
|
|
109
109
|
credentials remain server-side.
|
|
110
110
|
|
|
111
|
+
`.fargo-assets/` is an internal archive directory reserved for Fargo CLI. Do
|
|
112
|
+
not create it, copy frontend files into it, or place browser output inside the
|
|
113
|
+
Worker `--deploy-dir`. Always provide the original frontend build directory
|
|
114
|
+
through `--assets-dir`; the CLI packages it under `.fargo-assets/` and emits the
|
|
115
|
+
required schema v2 metadata. Worker deploys fail early when they contain a
|
|
116
|
+
manual `.fargo-assets/`, a root `index.html`, overlapping deploy/assets
|
|
117
|
+
directories, or asset options without `--assets-dir`.
|
|
118
|
+
|
|
119
|
+
Run `fargo lego deploy --help` for the complete option list and a full-stack
|
|
120
|
+
Worker example. A dry run reports the manifest schema version and separate
|
|
121
|
+
Worker-module and Static Asset file counts.
|
|
122
|
+
|
|
111
123
|
Pass `--source-exclude <gitignore-pattern>` once or multiple times when a
|
|
112
124
|
release command must make additional source exclusions explicit. Secure
|
|
113
125
|
defaults already exclude `node_modules/`, build output, credentials, and common
|
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -15,6 +15,14 @@ export async function runCli(args, options = {}) {
|
|
|
15
15
|
const stderr = options.stderr ?? process.stderr;
|
|
16
16
|
const store = options.store ?? createCredentialStore(options.storeOptions);
|
|
17
17
|
try {
|
|
18
|
+
if (isHelpOption(args) && args[0] === "lego" && args[1] === "deploy") {
|
|
19
|
+
stdout.write(LEGO_DEPLOY_HELP);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if ((isHelpOption(args) && args.length === 1) || (args[0] === "help" && args.length === 1)) {
|
|
23
|
+
stdout.write(ROOT_HELP);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
18
26
|
if (args[0] === "version" || args[0] === "--version" || args[0] === "-v") {
|
|
19
27
|
stdout.write(`fargo-cli version ${CLI_VERSION}\n`);
|
|
20
28
|
return;
|
|
@@ -59,6 +67,8 @@ export async function runCli(args, options = {}) {
|
|
|
59
67
|
}
|
|
60
68
|
if (args[0] === "lego" && args[1] === "deploy") {
|
|
61
69
|
const appId = requiredOption(args, "--app");
|
|
70
|
+
const assetsDirectory = optionValue(args, "--assets-dir")?.trim();
|
|
71
|
+
assertAssetsDirectoryProvided(args, assetsDirectory);
|
|
62
72
|
const deployDir = requiredOption(args, "--deploy-dir");
|
|
63
73
|
const target = requiredOption(args, "--target");
|
|
64
74
|
const version = requiredVersionOption(args, "--version", 80);
|
|
@@ -68,7 +78,7 @@ export async function runCli(args, options = {}) {
|
|
|
68
78
|
const result = await deployLocalLegoApp(store, {
|
|
69
79
|
appId,
|
|
70
80
|
assetsBinding: optionValue(args, "--assets-binding"),
|
|
71
|
-
assetsDirectory
|
|
81
|
+
assetsDirectory,
|
|
72
82
|
assetsHtmlHandling: optionValue(args, "--assets-html-handling"),
|
|
73
83
|
assetsNotFoundHandling: optionValue(args, "--assets-not-found-handling"),
|
|
74
84
|
assetsRunWorkerFirst: workerFirstRoutes.length > 0 ? workerFirstRoutes : args.includes("--assets-run-worker-first"),
|
|
@@ -129,6 +139,82 @@ export async function runCli(args, options = {}) {
|
|
|
129
139
|
}
|
|
130
140
|
}
|
|
131
141
|
|
|
142
|
+
const ROOT_HELP = `Usage: fargo <command> [options]
|
|
143
|
+
|
|
144
|
+
Commands:
|
|
145
|
+
auth login|device|status|logout
|
|
146
|
+
mcp serve|tools|call
|
|
147
|
+
lego snapshot upload|list|download
|
|
148
|
+
lego deploy
|
|
149
|
+
lego d1 migrate plan|apply
|
|
150
|
+
workbuddy install
|
|
151
|
+
version
|
|
152
|
+
|
|
153
|
+
Run "fargo lego deploy --help" for deployment options.
|
|
154
|
+
`;
|
|
155
|
+
|
|
156
|
+
const LEGO_DEPLOY_HELP = `Usage: fargo lego deploy [options]
|
|
157
|
+
|
|
158
|
+
Required:
|
|
159
|
+
--app <appId> LEGO App ID
|
|
160
|
+
--deploy-dir <directory> Built Pages files or Worker modules
|
|
161
|
+
--target <pages|worker> Deployment target
|
|
162
|
+
--version <version> Release version
|
|
163
|
+
--name <name> Release name
|
|
164
|
+
--notes <text> Release notes (or --notes-file <path>)
|
|
165
|
+
|
|
166
|
+
Source and execution:
|
|
167
|
+
--source <directory> Source snapshot root (default: .)
|
|
168
|
+
--source-exclude <pattern> Repeatable source exclusion
|
|
169
|
+
--entrypoint <path> Default: index.html for Pages, index.js for Worker
|
|
170
|
+
--skip-source Do not upload a source snapshot
|
|
171
|
+
--dry-run Validate and summarize without network writes
|
|
172
|
+
|
|
173
|
+
Worker Static Assets:
|
|
174
|
+
--assets-dir <directory> Built browser assets; required for Worker frontends
|
|
175
|
+
--assets-binding <name> Assets binding (default: ASSETS)
|
|
176
|
+
--assets-html-handling <mode> auto-trailing-slash, force-trailing-slash,
|
|
177
|
+
drop-trailing-slash, or none
|
|
178
|
+
--assets-not-found-handling <mode>
|
|
179
|
+
none, 404-page, or single-page-application
|
|
180
|
+
--assets-run-worker-first Run the Worker before static asset routing
|
|
181
|
+
--assets-run-worker-first-route <glob>
|
|
182
|
+
Repeatable Worker-first route
|
|
183
|
+
|
|
184
|
+
Example:
|
|
185
|
+
fargo lego deploy --app app_xxx --source . \\
|
|
186
|
+
--deploy-dir .build/worker --target worker --entrypoint index.js \\
|
|
187
|
+
--assets-dir dist --assets-binding ASSETS \\
|
|
188
|
+
--assets-not-found-handling single-page-application \\
|
|
189
|
+
--assets-run-worker-first --version 1.4.0 --name "Full-stack release" \\
|
|
190
|
+
--notes-file CHANGELOG.md
|
|
191
|
+
|
|
192
|
+
.fargo-assets/ is an internal archive directory reserved for Fargo CLI.
|
|
193
|
+
Do not create or copy it manually; always pass browser output with --assets-dir.
|
|
194
|
+
`;
|
|
195
|
+
|
|
196
|
+
function isHelpOption(args) {
|
|
197
|
+
return args.includes("--help") || args.includes("-h");
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function assertAssetsDirectoryProvided(args, assetsDirectory) {
|
|
201
|
+
const assetOptions = [
|
|
202
|
+
"--assets-binding",
|
|
203
|
+
"--assets-dir",
|
|
204
|
+
"--assets-html-handling",
|
|
205
|
+
"--assets-not-found-handling",
|
|
206
|
+
"--assets-run-worker-first",
|
|
207
|
+
"--assets-run-worker-first-route"
|
|
208
|
+
];
|
|
209
|
+
if (!assetsDirectory && assetOptions.some((name) => hasOption(args, name))) {
|
|
210
|
+
throw new Error("Worker Static Assets options require --assets-dir <directory>; .fargo-assets/ is reserved for Fargo CLI and must not be created manually");
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function hasOption(args, name) {
|
|
215
|
+
return args.some((value) => value === name || value.startsWith(`${name}=`));
|
|
216
|
+
}
|
|
217
|
+
|
|
132
218
|
async function authorizeWithPkce(args, stdout, store, options) {
|
|
133
219
|
const browserMode = optionValue(args, "--open-browser") ?? "auto";
|
|
134
220
|
await authorizePkce(store, {
|
package/src/constants.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const CLI_VERSION = "0.7.
|
|
1
|
+
export const CLI_VERSION = "0.7.2";
|
|
2
2
|
export const DEVICE_CODE_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:device_code";
|
|
3
3
|
export const DEFAULT_AUTH_URL = "https://auth.fargowealth.app";
|
|
4
4
|
export const DEFAULT_API_URL = "https://mcp.fargowealth.app";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { lstat, opendir, readFile } from "node:fs/promises";
|
|
3
|
-
import { basename, extname, join, resolve, sep } from "node:path";
|
|
3
|
+
import { basename, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
4
|
import { strToU8, zipSync } from "fflate";
|
|
5
5
|
import ignore from "ignore";
|
|
6
6
|
|
|
@@ -38,6 +38,7 @@ export async function createDeploymentArtifact(deployDirectory, options = {}) {
|
|
|
38
38
|
const target = deploymentTarget(options.target);
|
|
39
39
|
const entrypoint = normalizePath(options.entrypoint || (target === "pages" ? "index.html" : "index.js"));
|
|
40
40
|
const assets = normalizeWorkerAssets(options.assets, target);
|
|
41
|
+
await assertReservedArchiveDirectoryAbsent(root);
|
|
41
42
|
const matcher = ignore().add(DEFAULT_IGNORE_RULES);
|
|
42
43
|
const customRules = await readOptionalText(join(root, ".legodeployignore"));
|
|
43
44
|
if (customRules) matcher.add(customRules);
|
|
@@ -45,12 +46,18 @@ export async function createDeploymentArtifact(deployDirectory, options = {}) {
|
|
|
45
46
|
const candidates = [];
|
|
46
47
|
const skippedSymlinks = [];
|
|
47
48
|
await walk(root, "", matcher, "");
|
|
49
|
+
if (target === "worker" && candidates.some((file) => file.path === "index.html")) {
|
|
50
|
+
throw new Error("Worker deployment output contains browser entry file 'index.html'. Keep Worker modules in --deploy-dir and pass frontend output separately with --assets-dir <directory>");
|
|
51
|
+
}
|
|
48
52
|
if (assets) {
|
|
49
53
|
const assetsRoot = resolve(assets.sourceDirectory);
|
|
50
54
|
const assetsRootStat = await lstat(assetsRoot).catch(() => undefined);
|
|
51
55
|
if (!assetsRootStat?.isDirectory()) {
|
|
52
56
|
throw new Error(`Worker assets output is not a directory: ${assetsRoot}`);
|
|
53
57
|
}
|
|
58
|
+
if (directoriesOverlap(root, assetsRoot)) {
|
|
59
|
+
throw new Error("--deploy-dir and --assets-dir must be separate, non-overlapping directories so browser assets are not uploaded as Worker modules");
|
|
60
|
+
}
|
|
54
61
|
const assetsMatcher = ignore().add(DEFAULT_IGNORE_RULES);
|
|
55
62
|
const assetsIgnore = await readOptionalText(join(assetsRoot, ".assetsignore"));
|
|
56
63
|
if (assetsIgnore) assetsMatcher.add(assetsIgnore);
|
|
@@ -161,6 +168,26 @@ export async function createDeploymentArtifact(deployDirectory, options = {}) {
|
|
|
161
168
|
}
|
|
162
169
|
}
|
|
163
170
|
|
|
171
|
+
async function assertReservedArchiveDirectoryAbsent(root) {
|
|
172
|
+
const reservedPath = join(root, WORKER_ASSET_ARCHIVE_DIRECTORY);
|
|
173
|
+
const reservedStat = await lstat(reservedPath).catch((error) => {
|
|
174
|
+
if (error?.code === "ENOENT") return undefined;
|
|
175
|
+
throw error;
|
|
176
|
+
});
|
|
177
|
+
if (reservedStat) {
|
|
178
|
+
throw new Error("Deployment output contains reserved path '.fargo-assets'. Do not create it manually; pass browser output with --assets-dir <directory> and Fargo CLI will package it");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function directoriesOverlap(left, right) {
|
|
183
|
+
return isSameOrInside(left, right) || isSameOrInside(right, left);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function isSameOrInside(parent, candidate) {
|
|
187
|
+
const path = relative(parent, candidate);
|
|
188
|
+
return path === "" || (!path.startsWith("..") && !isAbsolute(path));
|
|
189
|
+
}
|
|
190
|
+
|
|
164
191
|
function normalizeWorkerAssets(value, target) {
|
|
165
192
|
if (!value?.sourceDirectory) return undefined;
|
|
166
193
|
if (target !== "worker") throw new Error("Static assets are supported only for Worker deployments");
|
|
@@ -27,6 +27,10 @@ export async function uploadLegoDeployArtifact(store, input, options = {}) {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
export function deploymentArtifactSummary(artifact) {
|
|
30
|
+
const assetPrefix = artifact.assets ? `${artifact.assets.directory}/` : undefined;
|
|
31
|
+
const staticAssetFileCount = assetPrefix
|
|
32
|
+
? artifact.manifest.files.filter((file) => file.path.startsWith(assetPrefix)).length
|
|
33
|
+
: 0;
|
|
30
34
|
return {
|
|
31
35
|
archiveName: artifact.archiveName,
|
|
32
36
|
archiveSha256: artifact.archiveSha256,
|
|
@@ -35,9 +39,13 @@ export function deploymentArtifactSummary(artifact) {
|
|
|
35
39
|
contentSha256: artifact.contentSha256,
|
|
36
40
|
contentSizeBytes: artifact.contentSizeBytes,
|
|
37
41
|
entrypoint: artifact.entrypoint,
|
|
42
|
+
fileBreakdown: artifact.target === "worker"
|
|
43
|
+
? { staticAssets: staticAssetFileCount, workerModules: artifact.fileCount - staticAssetFileCount }
|
|
44
|
+
: { pagesFiles: artifact.fileCount },
|
|
38
45
|
fileCount: artifact.fileCount,
|
|
39
46
|
root: artifact.root,
|
|
40
47
|
skippedSymlinkCount: artifact.skippedSymlinkCount,
|
|
48
|
+
schemaVersion: artifact.manifest.schemaVersion,
|
|
41
49
|
target: artifact.target
|
|
42
50
|
};
|
|
43
51
|
}
|