@fargowealth/fargo-ai-cli 0.6.0 → 0.7.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/CHANGELOG.md +9 -0
- package/README.md +11 -4
- package/package.json +1 -1
- package/src/cli.mjs +51 -7
- package/src/constants.mjs +1 -1
- package/src/lego-deploy.mjs +25 -12
- package/src/lego-snapshots.mjs +17 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0
|
|
4
|
+
|
|
5
|
+
- Added source-backup history metadata and Portal list/download support for
|
|
6
|
+
immutable source versions, including independent backup-only events.
|
|
7
|
+
- Deployment now requires a version number, version name, and upgrade notes;
|
|
8
|
+
history records show the deployer and expandable notes.
|
|
9
|
+
- Source backup remains the deploy default. Added explicit `--skip-source` for
|
|
10
|
+
deployments that intentionally omit a source archive.
|
|
11
|
+
|
|
3
12
|
## 0.6.0
|
|
4
13
|
|
|
5
14
|
- Added `fargo lego deploy` to create a source snapshot and deterministic
|
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.
|
|
12
|
+
npm install -g @fargowealth/fargo-ai-cli@0.7.0
|
|
13
13
|
fargo version
|
|
14
14
|
```
|
|
15
15
|
|
|
@@ -47,8 +47,8 @@ fargo mcp call lego_get_app --args-file ./arguments.json
|
|
|
47
47
|
## LEGO source snapshots
|
|
48
48
|
|
|
49
49
|
```sh
|
|
50
|
-
fargo lego snapshot upload --app app_xxx --source . --dry-run
|
|
51
|
-
fargo lego snapshot upload --app app_xxx --source . --version
|
|
50
|
+
fargo lego snapshot upload --app app_xxx --source . --version 1.4.0 --name "Pre-upgrade backup" --dry-run
|
|
51
|
+
fargo lego snapshot upload --app app_xxx --source . --version 1.4.0 --name "Pre-upgrade backup" --notes "Before migration"
|
|
52
52
|
fargo lego snapshot list --app app_xxx
|
|
53
53
|
fargo lego snapshot download --app app_xxx --snapshot snap_xxx
|
|
54
54
|
```
|
|
@@ -57,6 +57,8 @@ The CLI applies secure defaults plus `.legoignore`, skips symbolic links,
|
|
|
57
57
|
creates a deterministic ZIP, calculates SHA-256 digests, and uploads raw bytes
|
|
58
58
|
over authenticated HTTP. The ZIP includes
|
|
59
59
|
`.fargo-snapshot/manifest.json`; its default archive limit is 50 MB.
|
|
60
|
+
An upload creates a downloadable source-backup history event without deploying
|
|
61
|
+
or publishing the App.
|
|
60
62
|
|
|
61
63
|
## LEGO local deploy
|
|
62
64
|
|
|
@@ -70,11 +72,16 @@ fargo lego deploy \
|
|
|
70
72
|
--deploy-dir dist \
|
|
71
73
|
--target pages \
|
|
72
74
|
--entrypoint index.html \
|
|
73
|
-
--version
|
|
75
|
+
--version 1.4.0 \
|
|
76
|
+
--name "Navigation refresh" \
|
|
77
|
+
--notes-file CHANGELOG.md
|
|
74
78
|
```
|
|
75
79
|
|
|
76
80
|
For Workers use `--target worker`; the default entrypoint becomes `index.js`.
|
|
77
81
|
The CLI does not install dependencies, infer a framework, or run the build.
|
|
82
|
+
Version number, version name, and upgrade notes are required. Source upload is
|
|
83
|
+
the default; `--skip-source` is the explicit exception for a deployment that
|
|
84
|
+
must not retain a source archive.
|
|
78
85
|
|
|
79
86
|
The command creates and uploads two immutable archives:
|
|
80
87
|
|
package/package.json
CHANGED
package/src/cli.mjs
CHANGED
|
@@ -58,14 +58,23 @@ export async function runCli(args, options = {}) {
|
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
60
|
if (args[0] === "lego" && args[1] === "deploy") {
|
|
61
|
+
const appId = requiredOption(args, "--app");
|
|
62
|
+
const deployDir = requiredOption(args, "--deploy-dir");
|
|
63
|
+
const target = requiredOption(args, "--target");
|
|
64
|
+
const version = requiredVersionOption(args, "--version", 80);
|
|
65
|
+
const versionName = requiredTextOption(args, "--name", 160);
|
|
66
|
+
const releaseNotes = await requiredNotesOption(args);
|
|
61
67
|
const result = await deployLocalLegoApp(store, {
|
|
62
|
-
appId
|
|
63
|
-
deployDir
|
|
68
|
+
appId,
|
|
69
|
+
deployDir,
|
|
64
70
|
dryRun: args.includes("--dry-run"),
|
|
65
71
|
entrypoint: optionValue(args, "--entrypoint"),
|
|
72
|
+
releaseNotes,
|
|
73
|
+
skipSource: args.includes("--skip-source"),
|
|
66
74
|
source: optionValue(args, "--source") ?? ".",
|
|
67
|
-
target
|
|
68
|
-
version
|
|
75
|
+
target,
|
|
76
|
+
version,
|
|
77
|
+
versionName
|
|
69
78
|
}, options);
|
|
70
79
|
stdout.write(`${JSON.stringify(result)}\n`);
|
|
71
80
|
return;
|
|
@@ -138,8 +147,11 @@ async function runLegoSnapshotCommand(args, stdout, store, options) {
|
|
|
138
147
|
const result = await uploadLegoSourceSnapshot(store, {
|
|
139
148
|
appId,
|
|
140
149
|
dryRun: args.includes("--dry-run"),
|
|
150
|
+
notes: await optionalNotesOption(args),
|
|
151
|
+
purpose: "backup",
|
|
141
152
|
source: optionValue(args, "--source") ?? ".",
|
|
142
|
-
version:
|
|
153
|
+
version: requiredVersionOption(args, "--version", 120),
|
|
154
|
+
versionName: requiredTextOption(args, "--name", 160)
|
|
143
155
|
}, options);
|
|
144
156
|
stdout.write(`${JSON.stringify(result)}\n`);
|
|
145
157
|
return;
|
|
@@ -160,7 +172,25 @@ async function runLegoSnapshotCommand(args, stdout, store, options) {
|
|
|
160
172
|
stdout.write(`${JSON.stringify({ archiveSha256: result.archiveSha256, outputPath, sizeBytes: result.archive.byteLength, snapshotId })}\n`);
|
|
161
173
|
return;
|
|
162
174
|
}
|
|
163
|
-
throw new Error("Usage: fargo lego snapshot upload|list|download --app <appId>");
|
|
175
|
+
throw new Error("Usage: fargo lego snapshot upload --app <appId> --version <number> --name <name> [--notes TEXT | --notes-file PATH] | list|download --app <appId>");
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function requiredNotesOption(args) {
|
|
179
|
+
const notes = await optionalNotesOption(args);
|
|
180
|
+
if (!notes) throw new Error("Missing release notes; pass --notes <text> or --notes-file <path>");
|
|
181
|
+
if (notes.length > 4_000) throw new Error("Release notes must not exceed 4000 characters");
|
|
182
|
+
return notes;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function optionalNotesOption(args) {
|
|
186
|
+
const inline = optionValue(args, "--notes");
|
|
187
|
+
const file = optionValue(args, "--notes-file");
|
|
188
|
+
if (inline !== undefined && file !== undefined) throw new Error("Use only one of --notes or --notes-file");
|
|
189
|
+
const notes = file !== undefined
|
|
190
|
+
? (await readFile(resolve(file), "utf8")).trim()
|
|
191
|
+
: inline?.trim() ?? "";
|
|
192
|
+
if (notes.length > 4_000) throw new Error("Notes must not exceed 4000 characters");
|
|
193
|
+
return notes;
|
|
164
194
|
}
|
|
165
195
|
|
|
166
196
|
async function readToolArgs(args) {
|
|
@@ -192,7 +222,21 @@ async function pathExists(path) {
|
|
|
192
222
|
|
|
193
223
|
function requiredOption(args, name) {
|
|
194
224
|
const value = optionValue(args, name)?.trim();
|
|
195
|
-
if (!value) throw new Error(`Missing required option ${name}`);
|
|
225
|
+
if (!value || value.startsWith("--")) throw new Error(`Missing required option ${name}`);
|
|
226
|
+
return value;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function requiredTextOption(args, name, maxLength) {
|
|
230
|
+
const value = requiredOption(args, name);
|
|
231
|
+
if (value.length > maxLength) throw new Error(`${name} must not exceed ${maxLength} characters`);
|
|
232
|
+
return value;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function requiredVersionOption(args, name, maxLength) {
|
|
236
|
+
const value = requiredTextOption(args, name, maxLength);
|
|
237
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(value)) {
|
|
238
|
+
throw new Error(`${name} must use letters, numbers, dot, underscore, or hyphen`);
|
|
239
|
+
}
|
|
196
240
|
return value;
|
|
197
241
|
}
|
|
198
242
|
|
package/src/constants.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const CLI_VERSION = "0.
|
|
1
|
+
export const CLI_VERSION = "0.7.0";
|
|
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";
|
package/src/lego-deploy.mjs
CHANGED
|
@@ -9,25 +9,34 @@ export async function deployLocalLegoApp(store, input, options = {}) {
|
|
|
9
9
|
const target = requiredTarget(input.target);
|
|
10
10
|
const entrypoint = input.entrypoint || (target === "pages" ? "index.html" : "index.js");
|
|
11
11
|
const [snapshot, artifact] = await Promise.all([
|
|
12
|
-
createSourceSnapshot(input.source || ".", options.snapshotOptions),
|
|
12
|
+
input.skipSource ? Promise.resolve(undefined) : createSourceSnapshot(input.source || ".", options.snapshotOptions),
|
|
13
13
|
createDeploymentArtifact(input.deployDir, { ...options.artifactOptions, target, entrypoint })
|
|
14
14
|
]);
|
|
15
15
|
|
|
16
16
|
if (input.dryRun) {
|
|
17
17
|
return {
|
|
18
18
|
dryRun: true,
|
|
19
|
-
|
|
19
|
+
release: {
|
|
20
|
+
notes: input.releaseNotes,
|
|
21
|
+
version: input.version,
|
|
22
|
+
versionName: input.versionName
|
|
23
|
+
},
|
|
24
|
+
sourceSnapshot: snapshot ? sourceSnapshotSummary(snapshot) : undefined,
|
|
25
|
+
sourceSnapshotSkipped: !snapshot,
|
|
20
26
|
deployArtifact: deploymentArtifactSummary(artifact)
|
|
21
27
|
};
|
|
22
28
|
}
|
|
23
29
|
|
|
24
|
-
const uploadedSnapshot = await uploadLegoSourceSnapshot(store, {
|
|
30
|
+
const uploadedSnapshot = snapshot ? await uploadLegoSourceSnapshot(store, {
|
|
25
31
|
appId: input.appId,
|
|
32
|
+
notes: input.releaseNotes,
|
|
33
|
+
purpose: "deployment",
|
|
26
34
|
source: input.source || ".",
|
|
27
|
-
version: input.version
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
version: input.version,
|
|
36
|
+
versionName: input.versionName
|
|
37
|
+
}, { ...options, snapshot }) : undefined;
|
|
38
|
+
const sourceSnapshotId = uploadedSnapshot?.sourceSnapshot?.id;
|
|
39
|
+
if (snapshot && !sourceSnapshotId) throw new Error("Source snapshot upload did not return sourceSnapshot.id");
|
|
31
40
|
|
|
32
41
|
const uploadedArtifact = await uploadLegoDeployArtifact(store, {
|
|
33
42
|
appId: input.appId,
|
|
@@ -38,18 +47,22 @@ export async function deployLocalLegoApp(store, input, options = {}) {
|
|
|
38
47
|
if (!deployArtifactId) throw new Error("Deployment artifact upload did not return deployArtifact.id");
|
|
39
48
|
|
|
40
49
|
const tool = target === "pages" ? "lego_pages_deploy" : "lego_worker_deploy";
|
|
41
|
-
const
|
|
50
|
+
const deploymentInput = {
|
|
42
51
|
appId: input.appId,
|
|
43
52
|
deployArtifactId,
|
|
44
53
|
entrypoint: artifact.entrypoint,
|
|
45
|
-
|
|
46
|
-
version: input.version
|
|
47
|
-
|
|
54
|
+
releaseNotes: input.releaseNotes,
|
|
55
|
+
version: input.version,
|
|
56
|
+
versionName: input.versionName,
|
|
57
|
+
...(sourceSnapshotId ? { sourceSnapshotId } : {})
|
|
58
|
+
};
|
|
59
|
+
const deployment = mcpToolPayload(await callMcpTool(store, tool, deploymentInput, options));
|
|
48
60
|
|
|
49
61
|
return {
|
|
50
62
|
appId: input.appId,
|
|
51
63
|
target,
|
|
52
|
-
sourceSnapshot: uploadedSnapshot
|
|
64
|
+
sourceSnapshot: uploadedSnapshot?.sourceSnapshot,
|
|
65
|
+
sourceSnapshotSkipped: !snapshot,
|
|
53
66
|
deployArtifact: uploadedArtifact.deployArtifact,
|
|
54
67
|
deployment: deployment.deployment,
|
|
55
68
|
sourceVersion: deployment.sourceVersion,
|
package/src/lego-snapshots.mjs
CHANGED
|
@@ -5,11 +5,26 @@ import { createSourceSnapshot } from "./source-snapshot.mjs";
|
|
|
5
5
|
export async function uploadLegoSourceSnapshot(store, input, options = {}) {
|
|
6
6
|
const snapshot = options.snapshot ?? await createSourceSnapshot(input.source ?? ".", options.snapshotOptions);
|
|
7
7
|
if (input.dryRun) {
|
|
8
|
-
return {
|
|
8
|
+
return {
|
|
9
|
+
dryRun: true,
|
|
10
|
+
metadata: {
|
|
11
|
+
notes: input.notes ?? "",
|
|
12
|
+
purpose: input.purpose ?? "backup",
|
|
13
|
+
version: input.version ?? "",
|
|
14
|
+
versionName: input.versionName ?? ""
|
|
15
|
+
},
|
|
16
|
+
snapshot: snapshotSummary(snapshot)
|
|
17
|
+
};
|
|
9
18
|
}
|
|
10
19
|
|
|
11
20
|
const url = snapshotCollectionUrl(input.appId, options);
|
|
12
21
|
if (input.version) url.searchParams.set("version", input.version);
|
|
22
|
+
const metadata = Buffer.from(JSON.stringify({
|
|
23
|
+
notes: input.notes ?? "",
|
|
24
|
+
purpose: input.purpose ?? "backup",
|
|
25
|
+
version: input.version ?? "",
|
|
26
|
+
versionName: input.versionName ?? ""
|
|
27
|
+
}), "utf8").toString("base64url");
|
|
13
28
|
const response = await authenticatedRequest(store, url, {
|
|
14
29
|
method: "POST",
|
|
15
30
|
headers: {
|
|
@@ -19,6 +34,7 @@ export async function uploadLegoSourceSnapshot(store, input, options = {}) {
|
|
|
19
34
|
"X-Fargo-Source-Archive-Sha256": snapshot.archiveSha256,
|
|
20
35
|
"X-Fargo-Source-Content-Sha256": snapshot.contentSha256,
|
|
21
36
|
"X-Fargo-Source-File-Count": String(snapshot.fileCount),
|
|
37
|
+
"X-Fargo-Source-Metadata": metadata,
|
|
22
38
|
"X-Fargo-Source-Size": String(snapshot.sourceSizeBytes)
|
|
23
39
|
},
|
|
24
40
|
body: snapshot.archive
|