@fargowealth/fargo-ai-cli 0.8.0 → 1.1.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 +7 -0
- package/README.md +3 -3
- package/package.json +1 -1
- package/src/agent-install.mjs +16 -8
- package/src/cli.mjs +45 -0
- package/src/constants.mjs +1 -1
- package/src/lego-deploy.mjs +7 -2
- package/src/lego-migrations.mjs +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
- Added Worker Cron Trigger and Queue Consumer deployment options, including
|
|
6
|
+
bounded batch, retry, delay, timeout, and concurrency settings.
|
|
7
|
+
- Deployment preparation now persists runtime topology so Cron and Queue
|
|
8
|
+
changes are applied only when the prepared Worker version is published.
|
|
9
|
+
|
|
3
10
|
## 0.8.0
|
|
4
11
|
|
|
5
12
|
- Replaced regex-based D1 migration checks with token-structured parsing that
|
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@
|
|
12
|
+
npm install -g @fargowealth/fargo-ai-cli@latest
|
|
13
13
|
fargo version
|
|
14
14
|
```
|
|
15
15
|
|
|
@@ -43,8 +43,8 @@ reused by both MCP and local CLI operations.
|
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
45
|
fargo mcp tools
|
|
46
|
-
fargo mcp call
|
|
47
|
-
fargo mcp call
|
|
46
|
+
fargo mcp call lego_app_read --args '{"action":"get","appId":"app_xxx"}'
|
|
47
|
+
fargo mcp call lego_app_read --args-file ./arguments.json
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
## LEGO source snapshots
|
package/package.json
CHANGED
package/src/agent-install.mjs
CHANGED
|
@@ -7,14 +7,15 @@ import { normalizeAgentType } from "./auth.mjs";
|
|
|
7
7
|
import { CLI_VERSION } from "./constants.mjs";
|
|
8
8
|
|
|
9
9
|
const PACKAGE_NAME = "@fargowealth/fargo-ai-cli";
|
|
10
|
-
const SERVER_NAME = "fargo-
|
|
10
|
+
const SERVER_NAME = "fargo-pass";
|
|
11
|
+
const LEGACY_SERVER_NAME = "fargo-ai";
|
|
11
12
|
|
|
12
13
|
export async function installAgentMcp(rawAgent, options = {}) {
|
|
13
14
|
const agent = normalizeAgentType(rawAgent);
|
|
14
15
|
const platform = options.platform ?? process.platform;
|
|
15
16
|
const env = options.env ?? process.env;
|
|
16
17
|
const homeDirectory = options.home ?? homedir();
|
|
17
|
-
const packageSpec = `${PACKAGE_NAME}@${options.packageVersion ??
|
|
18
|
+
const packageSpec = `${PACKAGE_NAME}@${options.packageVersion ?? "latest"}`;
|
|
18
19
|
const npxCommand = options.npxCommand ?? (platform === "darwin"
|
|
19
20
|
? await resolveNpxCommand({ env, execPath: options.execPath, platform })
|
|
20
21
|
: "npx");
|
|
@@ -96,10 +97,14 @@ async function installJsonMcp(agent, server, options) {
|
|
|
96
97
|
env: server.env
|
|
97
98
|
};
|
|
98
99
|
const previousServer = config.mcpServers?.[SERVER_NAME];
|
|
99
|
-
const
|
|
100
|
+
const hadLegacyServer = Boolean(config.mcpServers?.[LEGACY_SERVER_NAME]);
|
|
101
|
+
const changed = hadLegacyServer || JSON.stringify(previousServer) !== JSON.stringify(nextServer);
|
|
100
102
|
|
|
101
103
|
if (changed) {
|
|
102
|
-
|
|
104
|
+
const nextServers = { ...(config.mcpServers ?? {}) };
|
|
105
|
+
delete nextServers[LEGACY_SERVER_NAME];
|
|
106
|
+
nextServers[SERVER_NAME] = nextServer;
|
|
107
|
+
config.mcpServers = nextServers;
|
|
103
108
|
await writeConfig(options.configPath, config);
|
|
104
109
|
}
|
|
105
110
|
|
|
@@ -114,9 +119,12 @@ async function installHostCliMcp(agent, server, options) {
|
|
|
114
119
|
const removeArgs = agent === "codex"
|
|
115
120
|
? ["mcp", "remove", SERVER_NAME]
|
|
116
121
|
: ["mcp", "remove", "--scope", "user", SERVER_NAME];
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
122
|
+
for (const candidate of [SERVER_NAME, LEGACY_SERVER_NAME]) {
|
|
123
|
+
const candidateRemoveArgs = removeArgs.map((value) => value === SERVER_NAME ? candidate : value);
|
|
124
|
+
const removeResult = await runHostCommand(command, candidateRemoveArgs, options);
|
|
125
|
+
if (removeResult.exitCode !== 0 && !isMissingServer(removeResult)) {
|
|
126
|
+
throw new Error(`${agentLabel(agent)} could not replace the existing ${candidate} MCP entry: ${commandError(removeResult)}`);
|
|
127
|
+
}
|
|
120
128
|
}
|
|
121
129
|
|
|
122
130
|
const environmentArgs = Object.entries(server.env).flatMap(([key, value]) => agent === "codex"
|
|
@@ -181,7 +189,7 @@ function installationResult(agent, packageSpec, changed, extra) {
|
|
|
181
189
|
package: packageSpec,
|
|
182
190
|
...extra,
|
|
183
191
|
next_action: agent === "workbuddy"
|
|
184
|
-
? "Open WorkBuddy Custom Connectors, trust and enable fargo-
|
|
192
|
+
? "Open WorkBuddy Custom Connectors, trust and enable fargo-pass, then start a new task."
|
|
185
193
|
: `Restart ${agentLabel(agent)} or start a new Agent session, then call ping_who_am_i.`
|
|
186
194
|
};
|
|
187
195
|
}
|
package/src/cli.mjs
CHANGED
|
@@ -75,6 +75,7 @@ export async function runCli(args, options = {}) {
|
|
|
75
75
|
const versionName = requiredTextOption(args, "--name", 160);
|
|
76
76
|
const releaseNotes = await requiredNotesOption(args);
|
|
77
77
|
const workerFirstRoutes = optionValues(args, "--assets-run-worker-first-route").filter(Boolean);
|
|
78
|
+
const queueConsumer = queueConsumerOption(args);
|
|
78
79
|
const result = await deployLocalLegoApp(store, {
|
|
79
80
|
appId,
|
|
80
81
|
assetsBinding: optionValue(args, "--assets-binding"),
|
|
@@ -82,10 +83,12 @@ export async function runCli(args, options = {}) {
|
|
|
82
83
|
assetsHtmlHandling: optionValue(args, "--assets-html-handling"),
|
|
83
84
|
assetsNotFoundHandling: optionValue(args, "--assets-not-found-handling"),
|
|
84
85
|
assetsRunWorkerFirst: workerFirstRoutes.length > 0 ? workerFirstRoutes : args.includes("--assets-run-worker-first"),
|
|
86
|
+
cronSchedules: optionValues(args, "--cron").filter(Boolean),
|
|
85
87
|
deployDir,
|
|
86
88
|
dryRun: args.includes("--dry-run"),
|
|
87
89
|
entrypoint: optionValue(args, "--entrypoint"),
|
|
88
90
|
releaseNotes,
|
|
91
|
+
queueConsumer,
|
|
89
92
|
skipSource: args.includes("--skip-source"),
|
|
90
93
|
source: optionValue(args, "--source") ?? ".",
|
|
91
94
|
sourceExcludes: optionValues(args, "--source-exclude"),
|
|
@@ -173,6 +176,15 @@ Worker Static Assets:
|
|
|
173
176
|
--assets-run-worker-first-route <glob>
|
|
174
177
|
Repeatable Worker-first route
|
|
175
178
|
|
|
179
|
+
Worker Runtime Topology:
|
|
180
|
+
--cron <expression> Repeatable Cron trigger; requires cron_trigger
|
|
181
|
+
--queue-consumer <binding> Configure this App Queue binding as a consumer
|
|
182
|
+
--queue-max-batch-size <n> Default: 10; range: 1-100
|
|
183
|
+
--queue-max-batch-timeout <s> Default: 5; range: 0-60 seconds
|
|
184
|
+
--queue-max-retries <n> Default: 3; range: 0-100
|
|
185
|
+
--queue-max-concurrency <n> Optional maximum concurrent consumers
|
|
186
|
+
--queue-retry-delay <s> Optional retry delay in seconds
|
|
187
|
+
|
|
176
188
|
Example:
|
|
177
189
|
fargo lego deploy --app app_xxx --source . \\
|
|
178
190
|
--deploy-dir .build/worker --target worker --entrypoint index.js \\
|
|
@@ -220,6 +232,39 @@ function assertAssetsDirectoryProvided(args, assetsDirectory) {
|
|
|
220
232
|
}
|
|
221
233
|
}
|
|
222
234
|
|
|
235
|
+
function queueConsumerOption(args) {
|
|
236
|
+
const bindingName = optionValue(args, "--queue-consumer")?.trim();
|
|
237
|
+
const settingNames = [
|
|
238
|
+
"--queue-max-batch-size",
|
|
239
|
+
"--queue-max-batch-timeout",
|
|
240
|
+
"--queue-max-retries",
|
|
241
|
+
"--queue-max-concurrency",
|
|
242
|
+
"--queue-retry-delay"
|
|
243
|
+
];
|
|
244
|
+
if (!bindingName) {
|
|
245
|
+
if (settingNames.some((name) => hasOption(args, name))) {
|
|
246
|
+
throw new Error("Queue consumer settings require --queue-consumer <binding>");
|
|
247
|
+
}
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
bindingName,
|
|
252
|
+
...integerOption(args, "--queue-max-batch-size", "maxBatchSize"),
|
|
253
|
+
...integerOption(args, "--queue-max-batch-timeout", "maxBatchTimeout"),
|
|
254
|
+
...integerOption(args, "--queue-max-retries", "maxRetries"),
|
|
255
|
+
...integerOption(args, "--queue-max-concurrency", "maxConcurrency"),
|
|
256
|
+
...integerOption(args, "--queue-retry-delay", "retryDelay")
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function integerOption(args, optionName, propertyName) {
|
|
261
|
+
const value = optionValue(args, optionName);
|
|
262
|
+
if (value === undefined) return {};
|
|
263
|
+
const parsed = Number(value);
|
|
264
|
+
if (!Number.isInteger(parsed)) throw new Error(`${optionName} must be an integer`);
|
|
265
|
+
return { [propertyName]: parsed };
|
|
266
|
+
}
|
|
267
|
+
|
|
223
268
|
function hasOption(args, name) {
|
|
224
269
|
return args.some((value) => value === name || value.startsWith(`${name}=`));
|
|
225
270
|
}
|
package/src/constants.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const CLI_VERSION = "0.
|
|
1
|
+
export const CLI_VERSION = "1.0.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
|
@@ -7,6 +7,9 @@ import { createSourceSnapshot } from "./source-snapshot.mjs";
|
|
|
7
7
|
|
|
8
8
|
export async function deployLocalLegoApp(store, input, options = {}) {
|
|
9
9
|
const target = requiredTarget(input.target);
|
|
10
|
+
if (target !== "worker" && (input.cronSchedules?.length || input.queueConsumer)) {
|
|
11
|
+
throw new Error("Cron and Queue consumer topology is supported only for Worker deployments");
|
|
12
|
+
}
|
|
10
13
|
const entrypoint = input.entrypoint || (target === "pages" ? "index.html" : "index.js");
|
|
11
14
|
const [snapshot, artifact] = await Promise.all([
|
|
12
15
|
input.skipSource ? Promise.resolve(undefined) : createSourceSnapshot(input.source || ".", {
|
|
@@ -60,17 +63,19 @@ export async function deployLocalLegoApp(store, input, options = {}) {
|
|
|
60
63
|
const deployArtifactId = uploadedArtifact.deployArtifact?.id;
|
|
61
64
|
if (!deployArtifactId) throw new Error("Deployment artifact upload did not return deployArtifact.id");
|
|
62
65
|
|
|
63
|
-
const tool = target === "pages" ? "lego_pages_deploy" : "lego_worker_deploy";
|
|
64
66
|
const deploymentInput = {
|
|
67
|
+
action: target,
|
|
65
68
|
appId: input.appId,
|
|
69
|
+
...(input.cronSchedules?.length ? { cronSchedules: input.cronSchedules } : {}),
|
|
66
70
|
deployArtifactId,
|
|
67
71
|
entrypoint: artifact.entrypoint,
|
|
68
72
|
releaseNotes: input.releaseNotes,
|
|
73
|
+
...(input.queueConsumer ? { queueConsumer: input.queueConsumer } : {}),
|
|
69
74
|
version: input.version,
|
|
70
75
|
versionName: input.versionName,
|
|
71
76
|
...(sourceSnapshotId ? { sourceSnapshotId } : {})
|
|
72
77
|
};
|
|
73
|
-
const deployment = mcpToolPayload(await callMcpTool(store,
|
|
78
|
+
const deployment = mcpToolPayload(await callMcpTool(store, "lego_deploy_prepare", deploymentInput, options));
|
|
74
79
|
|
|
75
80
|
return {
|
|
76
81
|
appId: input.appId,
|
package/src/lego-migrations.mjs
CHANGED
|
@@ -14,7 +14,7 @@ export async function planLegoMigrations(store, input, options = {}) {
|
|
|
14
14
|
|
|
15
15
|
async function buildMigrationPlan(store, input, options) {
|
|
16
16
|
const local = await readMigrationDirectory(input.directory, { tablePrefix: input.tablePrefix });
|
|
17
|
-
const remote = mcpToolPayload(await callMcpTool(store, "
|
|
17
|
+
const remote = mcpToolPayload(await callMcpTool(store, "lego_database", { action: "migrations_list", appId: input.appId }, options));
|
|
18
18
|
const applied = new Map((remote.migrations ?? []).map((migration) => [String(migration.version), migration]));
|
|
19
19
|
const migrations = local.map((migration) => {
|
|
20
20
|
const existing = applied.get(migration.version);
|
|
@@ -62,7 +62,8 @@ export async function applyLegoMigrations(store, input, options = {}) {
|
|
|
62
62
|
|
|
63
63
|
const applied = [];
|
|
64
64
|
for (const migration of plan.migrations.filter((item) => item.status === "pending")) {
|
|
65
|
-
const result = mcpToolPayload(await callMcpTool(store, "
|
|
65
|
+
const result = mcpToolPayload(await callMcpTool(store, "lego_database", {
|
|
66
|
+
action: "migration_apply",
|
|
66
67
|
appId: input.appId,
|
|
67
68
|
allowDataMigrations: migration.impact.dataStatementCount > 0 && input.allowDataMigrations === true,
|
|
68
69
|
contentSha256: migration.contentSha256,
|