@fargowealth/fargo-ai-cli 0.1.0 → 0.2.1

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 CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  ```sh
6
6
  npm install --global @fargowealth/fargo-ai-cli
7
+ fargo workbuddy install --open-browser=always --output=jsonl
7
8
  fargo auth device --open-browser=always --output=jsonl
8
9
  fargo auth status --output=json
9
10
  fargo mcp serve
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fargowealth/fargo-ai-cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Fargo AI authentication and stdio MCP bridge for WorkBuddy",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.mjs CHANGED
@@ -2,6 +2,7 @@ import { createCredentialStore } from "./credential-store.mjs";
2
2
  import { currentCredential, openBrowser, pollDeviceAuthorization, startDeviceAuthorization } from "./auth.mjs";
3
3
  import { CLI_VERSION } from "./constants.mjs";
4
4
  import { runMcpProxy } from "./mcp-proxy.mjs";
5
+ import { installWorkBuddyMcp } from "./workbuddy-install.mjs";
5
6
 
6
7
  export async function runCli(args, options = {}) {
7
8
  const stdout = options.stdout ?? process.stdout;
@@ -13,19 +14,7 @@ export async function runCli(args, options = {}) {
13
14
  return;
14
15
  }
15
16
  if (args[0] === "auth" && args[1] === "device") {
16
- const device = await startDeviceAuthorization(options);
17
- stdout.write(`${JSON.stringify({
18
- type: "device_authorization",
19
- user_code: device.user_code,
20
- verification_uri: device.verification_uri,
21
- verification_uri_complete: device.verification_uri_complete,
22
- expires_in: device.expires_in,
23
- interval: device.interval
24
- })}\n`);
25
- const browserMode = optionValue(args, "--open-browser") ?? "auto";
26
- if (browserMode !== "never" && options.openBrowser !== false) (options.browserOpener ?? openBrowser)(device.verification_uri_complete || device.verification_uri);
27
- await pollDeviceAuthorization(device, store, options);
28
- stdout.write(`${JSON.stringify({ type: "authorization_complete", logged_in: true, method: "oauth" })}\n`);
17
+ await authorizeDevice(args, stdout, store, options);
29
18
  return;
30
19
  }
31
20
  if (args[0] === "auth" && args[1] === "status") {
@@ -43,7 +32,21 @@ export async function runCli(args, options = {}) {
43
32
  await runMcpProxy(store, options);
44
33
  return;
45
34
  }
46
- throw new Error("Usage: fargo auth device|status|logout | fargo mcp serve | fargo version");
35
+ if (args[0] === "workbuddy" && args[1] === "install") {
36
+ let credential = await store.load();
37
+ if (credential) {
38
+ try {
39
+ credential = await currentCredential(store, options);
40
+ } catch {
41
+ await store.clear();
42
+ credential = undefined;
43
+ }
44
+ }
45
+ if (!credential?.refresh_token) await authorizeDevice(args, stdout, store, options);
46
+ stdout.write(`${JSON.stringify(await installWorkBuddyMcp(options))}\n`);
47
+ return;
48
+ }
49
+ throw new Error("Usage: fargo auth device|status|logout | fargo workbuddy install | fargo mcp serve | fargo version");
47
50
  } catch (error) {
48
51
  stderr.write(`fargo: ${error instanceof Error ? error.message : "unknown error"}\n`);
49
52
  if (options.throwOnError) throw error;
@@ -51,6 +54,22 @@ export async function runCli(args, options = {}) {
51
54
  }
52
55
  }
53
56
 
57
+ async function authorizeDevice(args, stdout, store, options) {
58
+ const device = await startDeviceAuthorization(options);
59
+ stdout.write(`${JSON.stringify({
60
+ type: "device_authorization",
61
+ user_code: device.user_code,
62
+ verification_uri: device.verification_uri,
63
+ verification_uri_complete: device.verification_uri_complete,
64
+ expires_in: device.expires_in,
65
+ interval: device.interval
66
+ })}\n`);
67
+ const browserMode = optionValue(args, "--open-browser") ?? "auto";
68
+ if (browserMode !== "never" && options.openBrowser !== false) (options.browserOpener ?? openBrowser)(device.verification_uri_complete || device.verification_uri);
69
+ await pollDeviceAuthorization(device, store, options);
70
+ stdout.write(`${JSON.stringify({ type: "authorization_complete", logged_in: true, method: "oauth" })}\n`);
71
+ }
72
+
54
73
  function optionValue(args, name) {
55
74
  const inline = args.find((value) => value.startsWith(`${name}=`));
56
75
  if (inline) return inline.slice(name.length + 1);
package/src/constants.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export const CLI_VERSION = "0.1.0";
1
+ export const CLI_VERSION = "0.2.1";
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_MCP_URL = "https://mcp.fargowealth.app/mcp";
@@ -0,0 +1,73 @@
1
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import { CLI_VERSION } from "./constants.mjs";
5
+
6
+ const PACKAGE_NAME = "@fargowealth/fargo-ai-cli";
7
+
8
+ export async function installWorkBuddyMcp(options = {}) {
9
+ const configPath = options.configPath
10
+ ?? process.env.FARGO_WORKBUDDY_MCP_CONFIG
11
+ ?? defaultWorkBuddyConfigPath();
12
+ const packageSpec = `${PACKAGE_NAME}@${options.packageVersion ?? CLI_VERSION}`;
13
+ const config = await readConfig(configPath);
14
+ const nextServer = {
15
+ type: "stdio",
16
+ command: "npx",
17
+ args: ["-y", packageSpec, "mcp", "serve"],
18
+ timeout: 60000
19
+ };
20
+ const previousServer = config.mcpServers?.["fargo-ai"];
21
+ const changed = JSON.stringify(previousServer) !== JSON.stringify(nextServer);
22
+
23
+ if (changed) {
24
+ config.mcpServers = { ...(config.mcpServers ?? {}), "fargo-ai": nextServer };
25
+ await writeConfig(configPath, config);
26
+ }
27
+
28
+ return {
29
+ type: "workbuddy_installation_complete",
30
+ installed: true,
31
+ changed,
32
+ config_path: configPath,
33
+ server_name: "fargo-ai",
34
+ package: packageSpec,
35
+ trust_required: true,
36
+ next_action: "Open WorkBuddy MCP management, trust and enable fargo-ai, then reload MCP or start a new task."
37
+ };
38
+ }
39
+
40
+ export function defaultWorkBuddyConfigPath(homeDirectory = homedir()) {
41
+ return join(homeDirectory, ".workbuddy-ai", "mcp.json");
42
+ }
43
+
44
+ async function readConfig(configPath) {
45
+ let text;
46
+ try {
47
+ text = await readFile(configPath, "utf8");
48
+ } catch (error) {
49
+ if (error?.code === "ENOENT") return {};
50
+ throw error;
51
+ }
52
+ if (!text.trim()) return {};
53
+ let parsed;
54
+ try {
55
+ parsed = JSON.parse(text);
56
+ } catch {
57
+ throw new Error(`WorkBuddy MCP config is not valid JSON: ${configPath}`);
58
+ }
59
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
60
+ throw new Error(`WorkBuddy MCP config must contain a JSON object: ${configPath}`);
61
+ }
62
+ if (parsed.mcpServers !== undefined && (!parsed.mcpServers || typeof parsed.mcpServers !== "object" || Array.isArray(parsed.mcpServers))) {
63
+ throw new Error(`WorkBuddy mcpServers must be a JSON object: ${configPath}`);
64
+ }
65
+ return parsed;
66
+ }
67
+
68
+ async function writeConfig(configPath, config) {
69
+ await mkdir(dirname(configPath), { recursive: true });
70
+ const temporaryPath = `${configPath}.${process.pid}.${Date.now()}.tmp`;
71
+ await writeFile(temporaryPath, `${JSON.stringify(config, null, 2)}\n`, { encoding: "utf8", mode: 0o600, flag: "wx" });
72
+ await rename(temporaryPath, configPath);
73
+ }