@facilio/cli 0.3.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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +120 -0
  3. package/dist/commands/login.js +75 -0
  4. package/dist/commands/login.js.map +1 -0
  5. package/dist/commands/logout.js +41 -0
  6. package/dist/commands/logout.js.map +1 -0
  7. package/dist/commands/whoami.js +26 -0
  8. package/dist/commands/whoami.js.map +1 -0
  9. package/dist/core/api.js +158 -0
  10. package/dist/core/api.js.map +1 -0
  11. package/dist/core/constants.js +53 -0
  12. package/dist/core/constants.js.map +1 -0
  13. package/dist/core/credentials.js +209 -0
  14. package/dist/core/credentials.js.map +1 -0
  15. package/dist/core/identity.js +162 -0
  16. package/dist/core/identity.js.map +1 -0
  17. package/dist/core/logger.js +9 -0
  18. package/dist/core/logger.js.map +1 -0
  19. package/dist/core/prompt.js +29 -0
  20. package/dist/core/prompt.js.map +1 -0
  21. package/dist/core/regions.js +45 -0
  22. package/dist/core/regions.js.map +1 -0
  23. package/dist/core/secretStore.js +251 -0
  24. package/dist/core/secretStore.js.map +1 -0
  25. package/dist/core/wrap.js +17 -0
  26. package/dist/core/wrap.js.map +1 -0
  27. package/dist/index.js +53 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/products/connections/client.js +125 -0
  30. package/dist/products/connections/client.js.map +1 -0
  31. package/dist/products/connections/commands.js +324 -0
  32. package/dist/products/connections/commands.js.map +1 -0
  33. package/dist/products/connections/index.js +63 -0
  34. package/dist/products/connections/index.js.map +1 -0
  35. package/dist/products/connections/state.js +44 -0
  36. package/dist/products/connections/state.js.map +1 -0
  37. package/dist/products/types.js +2 -0
  38. package/dist/products/types.js.map +1 -0
  39. package/dist/products/vibe/app.js +97 -0
  40. package/dist/products/vibe/app.js.map +1 -0
  41. package/dist/products/vibe/config.js +57 -0
  42. package/dist/products/vibe/config.js.map +1 -0
  43. package/dist/products/vibe/db.js +155 -0
  44. package/dist/products/vibe/db.js.map +1 -0
  45. package/dist/products/vibe/deploy.js +92 -0
  46. package/dist/products/vibe/deploy.js.map +1 -0
  47. package/dist/products/vibe/function.js +240 -0
  48. package/dist/products/vibe/function.js.map +1 -0
  49. package/dist/products/vibe/index.js +112 -0
  50. package/dist/products/vibe/index.js.map +1 -0
  51. package/package.json +57 -0
@@ -0,0 +1,251 @@
1
+ /*
2
+ * Cross-platform secret storage. Ported from identity-service's
3
+ * docs/device-auth/keychain.js — shells out to the OS-provided secret CLI so
4
+ * we don't need a native node module (no node-gyp, no prebuilds).
5
+ *
6
+ * macOS -> Keychain via `security`
7
+ * Linux -> Secret Service via `secret-tool` (libsecret; needs `libsecret-tools`)
8
+ * Windows -> Credential Manager via `cmdkey` + DPAPI (per-user encryption, built-in)
9
+ *
10
+ * Falls back to a 0600 JSON file at ~/.facilio/credentials.json when no OS
11
+ * store is available (headless Linux, missing libsecret, etc.). Set
12
+ * `FACILIO_NO_KEYCHAIN=1` to force the file path (useful in CI).
13
+ *
14
+ * Stores ONE entry per (service, account) — the whole credentials JSON blob.
15
+ *
16
+ * Known caveat (matches the reference impl): on macOS, the secret is passed as
17
+ * `-w <secret>` on the argv of `security`, so it's briefly visible in `ps`.
18
+ * The macOS `security` CLI has no clean stdin-fed mode; if we ever care, the
19
+ * fix is a small Swift/Obj-C helper or a native module.
20
+ */
21
+ import { execFileSync } from 'node:child_process';
22
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
23
+ import os from 'node:os';
24
+ import path from 'node:path';
25
+ const SERVICE = 'facilio-cli';
26
+ const ACCOUNT = 'default';
27
+ const DIR = path.join(os.homedir(), '.facilio');
28
+ const FILE_FALLBACK = path.join(DIR, 'credentials.json');
29
+ const WIN_FILE = path.join(DIR, 'credentials.dpapi');
30
+ /* Pre-rename (@facilio/vibe-cli) locations — read/delete only, for one-time
31
+ * migration of an existing `vibe login` session into the new store. */
32
+ const LEGACY_SERVICE = 'facilio-vibe-cli';
33
+ const LEGACY_DIR = path.join(os.homedir(), '.vibe');
34
+ const LEGACY_FILE_FALLBACK = path.join(LEGACY_DIR, 'credentials.json');
35
+ const LEGACY_WIN_FILE = path.join(LEGACY_DIR, 'credentials.dpapi');
36
+ const forceFileEnv = (process.env.FACILIO_NO_KEYCHAIN ??
37
+ process.env.VIBE_NO_KEYCHAIN ??
38
+ '').toLowerCase();
39
+ const FORCE_FILE = forceFileEnv === '1' || forceFileEnv === 'true' || forceFileEnv === 'yes';
40
+ function writeFileSecure(file, data) {
41
+ mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
42
+ writeFileSync(file, data, { mode: 0o600 });
43
+ }
44
+ function fileSink() {
45
+ return `file (${FILE_FALLBACK})`;
46
+ }
47
+ /** Best-effort delete of the file fallback, used after a successful OS-store write. */
48
+ function dropFileFallback() {
49
+ try {
50
+ if (existsSync(FILE_FALLBACK))
51
+ unlinkSync(FILE_FALLBACK);
52
+ }
53
+ catch {
54
+ /* leave the file in place if we can't remove it; not fatal */
55
+ }
56
+ }
57
+ export function setSecret(secret) {
58
+ if (!FORCE_FILE) {
59
+ try {
60
+ if (process.platform === 'darwin') {
61
+ execFileSync('security', ['add-generic-password', '-U', '-a', ACCOUNT, '-s', SERVICE, '-w', secret], { stdio: ['ignore', 'ignore', 'ignore'] });
62
+ dropFileFallback();
63
+ return 'macOS Keychain';
64
+ }
65
+ if (process.platform === 'linux') {
66
+ execFileSync('secret-tool', ['store', '--label=Facilio CLI', 'service', SERVICE, 'account', ACCOUNT], { input: secret, stdio: ['pipe', 'ignore', 'ignore'] });
67
+ dropFileFallback();
68
+ return 'Linux secret-service';
69
+ }
70
+ if (process.platform === 'win32') {
71
+ // DPAPI encrypts with a per-user key derived from the login credentials.
72
+ // The ciphertext is fine to keep on disk; only this user (on this machine)
73
+ // can decrypt it. `cmdkey` only registers a marker — not a real Credential
74
+ // Manager entry. For first-class Credential Manager storage, swap in a
75
+ // native module.
76
+ const enc = execFileSync('powershell', [
77
+ '-NoProfile',
78
+ '-Command',
79
+ 'ConvertTo-SecureString -String $env:FC_SECRET -AsPlainText -Force | ConvertFrom-SecureString',
80
+ ], {
81
+ env: { ...process.env, FC_SECRET: secret },
82
+ encoding: 'utf8',
83
+ }).trim();
84
+ writeFileSecure(WIN_FILE, enc);
85
+ try {
86
+ execFileSync('cmdkey', [`/generic:${SERVICE}`, `/user:${ACCOUNT}`, '/pass:dpapi'], {
87
+ stdio: 'ignore',
88
+ });
89
+ }
90
+ catch {
91
+ /* marker is best-effort */
92
+ }
93
+ dropFileFallback();
94
+ return 'Windows Credential Manager (DPAPI)';
95
+ }
96
+ }
97
+ catch {
98
+ /* OS store unavailable — fall through to file */
99
+ }
100
+ }
101
+ writeFileSecure(FILE_FALLBACK, secret);
102
+ return fileSink();
103
+ }
104
+ function readOsSecret(service) {
105
+ try {
106
+ if (process.platform === 'darwin') {
107
+ const out = execFileSync('security', ['find-generic-password', '-a', ACCOUNT, '-s', service, '-w'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
108
+ return out.trim() || null;
109
+ }
110
+ if (process.platform === 'linux') {
111
+ const out = execFileSync('secret-tool', ['lookup', 'service', service, 'account', ACCOUNT], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
112
+ return (out ? out.trim() : '') || null;
113
+ }
114
+ }
115
+ catch {
116
+ /* not found in OS store */
117
+ }
118
+ return null;
119
+ }
120
+ function readDpapiFile(file) {
121
+ if (process.platform !== 'win32' || !existsSync(file))
122
+ return null;
123
+ try {
124
+ const enc = readFileSync(file, 'utf8');
125
+ const out = execFileSync('powershell', [
126
+ '-NoProfile',
127
+ '-Command',
128
+ '$s = $env:FC_ENC | ConvertTo-SecureString; ' +
129
+ '[Runtime.InteropServices.Marshal]::PtrToStringAuto(' +
130
+ '[Runtime.InteropServices.Marshal]::SecureStringToBSTR($s))',
131
+ ], {
132
+ env: { ...process.env, FC_ENC: enc },
133
+ encoding: 'utf8',
134
+ });
135
+ return out.trim() || null;
136
+ }
137
+ catch {
138
+ return null;
139
+ }
140
+ }
141
+ export function getSecret() {
142
+ if (!FORCE_FILE) {
143
+ const v = readOsSecret(SERVICE) ?? readDpapiFile(WIN_FILE);
144
+ if (v)
145
+ return v;
146
+ }
147
+ if (existsSync(FILE_FALLBACK)) {
148
+ const v = readFileSync(FILE_FALLBACK, 'utf8').trim();
149
+ return v || null;
150
+ }
151
+ return null;
152
+ }
153
+ export function deleteSecret() {
154
+ if (!FORCE_FILE) {
155
+ try {
156
+ if (process.platform === 'darwin') {
157
+ execFileSync('security', ['delete-generic-password', '-a', ACCOUNT, '-s', SERVICE], {
158
+ stdio: 'ignore',
159
+ });
160
+ }
161
+ else if (process.platform === 'linux') {
162
+ execFileSync('secret-tool', ['clear', 'service', SERVICE, 'account', ACCOUNT], {
163
+ stdio: 'ignore',
164
+ });
165
+ }
166
+ else if (process.platform === 'win32') {
167
+ if (existsSync(WIN_FILE))
168
+ unlinkSync(WIN_FILE);
169
+ try {
170
+ execFileSync('cmdkey', [`/delete:${SERVICE}`], { stdio: 'ignore' });
171
+ }
172
+ catch {
173
+ /* marker may not exist */
174
+ }
175
+ }
176
+ }
177
+ catch {
178
+ /* not present in OS store — fine */
179
+ }
180
+ }
181
+ if (existsSync(FILE_FALLBACK)) {
182
+ try {
183
+ unlinkSync(FILE_FALLBACK);
184
+ }
185
+ catch {
186
+ /* best effort */
187
+ }
188
+ }
189
+ }
190
+ /**
191
+ * Read a credentials blob left behind by @facilio/vibe-cli (old keychain
192
+ * service name, then ~/.vibe file fallbacks). Used once per machine to migrate
193
+ * an existing session into the new store; returns null when there's nothing.
194
+ */
195
+ export function getLegacySecret() {
196
+ if (!FORCE_FILE) {
197
+ const v = readOsSecret(LEGACY_SERVICE) ?? readDpapiFile(LEGACY_WIN_FILE);
198
+ if (v)
199
+ return v;
200
+ }
201
+ if (existsSync(LEGACY_FILE_FALLBACK)) {
202
+ const v = readFileSync(LEGACY_FILE_FALLBACK, 'utf8').trim();
203
+ return v || null;
204
+ }
205
+ return null;
206
+ }
207
+ /** Remove the legacy entry after a successful migration so stores can't diverge. */
208
+ export function deleteLegacySecret() {
209
+ try {
210
+ if (process.platform === 'darwin') {
211
+ execFileSync('security', ['delete-generic-password', '-a', ACCOUNT, '-s', LEGACY_SERVICE], {
212
+ stdio: 'ignore',
213
+ });
214
+ }
215
+ else if (process.platform === 'linux') {
216
+ execFileSync('secret-tool', ['clear', 'service', LEGACY_SERVICE, 'account', ACCOUNT], {
217
+ stdio: 'ignore',
218
+ });
219
+ }
220
+ else if (process.platform === 'win32') {
221
+ if (existsSync(LEGACY_WIN_FILE))
222
+ unlinkSync(LEGACY_WIN_FILE);
223
+ try {
224
+ execFileSync('cmdkey', [`/delete:${LEGACY_SERVICE}`], { stdio: 'ignore' });
225
+ }
226
+ catch {
227
+ /* marker may not exist */
228
+ }
229
+ }
230
+ }
231
+ catch {
232
+ /* not present — fine */
233
+ }
234
+ try {
235
+ if (existsSync(LEGACY_FILE_FALLBACK))
236
+ unlinkSync(LEGACY_FILE_FALLBACK);
237
+ }
238
+ catch {
239
+ /* best effort */
240
+ }
241
+ }
242
+ /** Path of the file-fallback location (only used if the OS keychain is unavailable). */
243
+ export function fallbackFilePath() {
244
+ return FILE_FALLBACK;
245
+ }
246
+ /** Config/state directory for the unified CLI (~/.facilio), created on demand. */
247
+ export function stateDir() {
248
+ mkdirSync(DIR, { recursive: true, mode: 0o700 });
249
+ return DIR;
250
+ }
251
+ //# sourceMappingURL=secretStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secretStore.js","sourceRoot":"","sources":["../../src/core/secretStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { logger } from './logger.js';
2
+ /**
3
+ * Command-action wrapper: funnels thrown errors through the logger and sets a
4
+ * non-zero exit code instead of dumping a stack trace at the user.
5
+ */
6
+ export function wrap(fn) {
7
+ return async (...args) => {
8
+ try {
9
+ await fn(...args);
10
+ }
11
+ catch (err) {
12
+ logger.error(err instanceof Error ? err.message : String(err));
13
+ process.exit(1);
14
+ }
15
+ };
16
+ }
17
+ //# sourceMappingURL=wrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wrap.js","sourceRoot":"","sources":["../../src/core/wrap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC;;;GAGG;AACH,MAAM,UAAU,IAAI,CAAsB,EAAiC;IACzE,OAAO,KAAK,EAAE,GAAG,IAAO,EAAE,EAAE;QAC1B,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * `facilio` — the unified Facilio CLI.
4
+ *
5
+ * Universal commands (login / logout / whoami) live here; every product
6
+ * contributes a namespace via the ProductModule contract in products/types.ts:
7
+ *
8
+ * facilio vibe … Vibe apps (deploy / app / db / function)
9
+ * facilio connections … Facilio Connections (search / execute / link …)
10
+ */
11
+ import { Command } from 'commander';
12
+ import { createRequire } from 'node:module';
13
+ import { logger } from './core/logger.js';
14
+ import { wrap } from './core/wrap.js';
15
+ import { loginCommand } from './commands/login.js';
16
+ import { logoutCommand } from './commands/logout.js';
17
+ import { whoamiCommand } from './commands/whoami.js';
18
+ import { vibeProduct } from './products/vibe/index.js';
19
+ import { connectionsProduct } from './products/connections/index.js';
20
+ const require = createRequire(import.meta.url);
21
+ const pkg = require('../package.json');
22
+ /** Product registry — add a new Facilio product CLI with one entry here. */
23
+ const PRODUCTS = [vibeProduct, connectionsProduct];
24
+ const program = new Command();
25
+ program
26
+ .name('facilio')
27
+ .description('Facilio CLI — one login for every Facilio product')
28
+ .version(pkg.version);
29
+ program
30
+ .command('login')
31
+ .description('Sign in to Facilio — OAuth2 device flow (works over SSH / headless), or --api-key for CI. One session covers every product.')
32
+ .option('--server <url>', 'Pin the vibe-server URL for this session (overrides the region table)')
33
+ .option('--region <key>', 'Region for --api-key logins (US, UK, AE, AU, …) — resolves vibe + MCP URLs. Defaults to US.')
34
+ .option('--api-key <key>', 'Log in with an API key instead of the browser flow. Use `-` to read the key from stdin so it is not stored in shell history.')
35
+ .option('--client-id <id>', 'Override the OAuth client id (default: FACILIO_CLIENT_ID)')
36
+ .option('--identity-url <url>', 'Identity-service base URL (default: FACILIO_IDENTITY_URL)')
37
+ .action(wrap(loginCommand));
38
+ program
39
+ .command('logout')
40
+ .description('Revoke the session server-side (best effort) and remove stored credentials')
41
+ .action(wrap(logoutCommand));
42
+ program
43
+ .command('whoami')
44
+ .description('Show the currently logged-in user')
45
+ .action(wrap(whoamiCommand));
46
+ for (const product of PRODUCTS) {
47
+ product.register(program, { version: pkg.version });
48
+ }
49
+ program.parseAsync(process.argv).catch((err) => {
50
+ logger.error(err instanceof Error ? err.message : String(err));
51
+ process.exit(1);
52
+ });
53
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;GAQG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAErE,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAC;AAE9D,4EAA4E;AAC5E,MAAM,QAAQ,GAAoB,CAAC,WAAW,EAAE,kBAAkB,CAAC,CAAC;AAEpE,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAC9B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CAAC,mDAAmD,CAAC;KAChE,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAExB,OAAO;KACJ,OAAO,CAAC,OAAO,CAAC;KAChB,WAAW,CACV,6HAA6H,CAC9H;KACA,MAAM,CAAC,gBAAgB,EAAE,uEAAuE,CAAC;KACjG,MAAM,CACL,gBAAgB,EAChB,6FAA6F,CAC9F;KACA,MAAM,CACL,iBAAiB,EACjB,8HAA8H,CAC/H;KACA,MAAM,CAAC,kBAAkB,EAAE,2DAA2D,CAAC;KACvF,MAAM,CAAC,sBAAsB,EAAE,2DAA2D,CAAC;KAC3F,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;AAE9B,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,4EAA4E,CAAC;KACzF,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;AAE/B,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,mCAAmC,CAAC;KAChD,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;AAE/B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;AACtD,CAAC;AAED,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC7C,MAAM,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Thin MCP client for the Facilio Connections server (Streamable HTTP).
3
+ *
4
+ * Auth model: the raw identity access token goes up as a direct bearer —
5
+ * the server's `FacilioOAuthProxy` accepts identity-issued JWTs alongside its
6
+ * own DCR/OAuth-proxy tokens, so the CLI never runs a browser OAuth dance of
7
+ * its own. No transparent refresh server-side on this path: on a 401 the CLI
8
+ * refreshes against identity-service once (same semantics as core/api.ts)
9
+ * and retries.
10
+ */
11
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
12
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
13
+ import { loadCredentials, refreshCredentials, SessionExpiredError, } from '../../core/credentials.js';
14
+ import { mcpUrlFor } from '../../core/regions.js';
15
+ /**
16
+ * `/mcp` → `/<slug>/mcp` on the same host, preserving any path prefix in
17
+ * front of the `/mcp` segment (reverse proxies may mount the server deeper).
18
+ */
19
+ export function resolveMcpUrl(base, app) {
20
+ const normalized = base.replace(/\/+$/, '');
21
+ if (!app)
22
+ return normalized;
23
+ const u = new URL(normalized);
24
+ if (!/\/mcp$/.test(u.pathname)) {
25
+ throw new Error(`MCP URL must end in /mcp to be connection-scoped (got ${base}).`);
26
+ }
27
+ u.pathname = `${u.pathname.replace(/\/mcp$/, '')}/${app}/mcp`;
28
+ return u.toString();
29
+ }
30
+ /** Load stored credentials and insist on an OAuth (refreshable) session. */
31
+ async function requireOAuthCredentials() {
32
+ const creds = await loadCredentials();
33
+ if (!creds) {
34
+ throw new Error('Not logged in. Run `facilio login` first.');
35
+ }
36
+ if (creds.apiKey && !creds.accessToken) {
37
+ throw new Error('`facilio connections` requires a browser sign-in (OAuth). API-key sessions only cover vibe commands — run `facilio login` without --api-key.');
38
+ }
39
+ return creds;
40
+ }
41
+ function is401(err) {
42
+ if (!(err instanceof Error))
43
+ return false;
44
+ const code = err.code;
45
+ if (code === 401)
46
+ return true;
47
+ return /\b401\b|unauthorized/i.test(err.message);
48
+ }
49
+ const PROACTIVE_REFRESH_SKEW_MS = 60_000;
50
+ /**
51
+ * One tool call = one short-lived MCP session (the server runs stateless
52
+ * HTTP, so this is cheap and avoids keeping sockets open between commands).
53
+ * Returns the tool's structured payload.
54
+ */
55
+ export async function callConnectionsTool(opts, toolName, args) {
56
+ let creds = await requireOAuthCredentials();
57
+ const canRefresh = !!creds.refreshToken && !!creds.tokenUrl && !!creds.clientId;
58
+ if (canRefresh &&
59
+ creds.expiresAt &&
60
+ creds.expiresAt - Date.now() < PROACTIVE_REFRESH_SKEW_MS) {
61
+ try {
62
+ creds = await refreshCredentials(creds);
63
+ }
64
+ catch (err) {
65
+ if (err instanceof SessionExpiredError)
66
+ throw err;
67
+ // Transient refresh failure — try the call with the stale token; the
68
+ // reactive 401 path below is the safety net.
69
+ }
70
+ }
71
+ // --mcp-url flag → FACILIO_MCP_URL env → region table → default.
72
+ const url = resolveMcpUrl(opts.mcpUrl || mcpUrlFor(creds.region), opts.app);
73
+ try {
74
+ return await callOnce(url, creds, opts.version, toolName, args);
75
+ }
76
+ catch (err) {
77
+ if (is401(err) && canRefresh) {
78
+ creds = await refreshCredentials(creds);
79
+ return await callOnce(url, creds, opts.version, toolName, args);
80
+ }
81
+ throw err;
82
+ }
83
+ }
84
+ async function callOnce(url, creds, version, toolName, args) {
85
+ const transport = new StreamableHTTPClientTransport(new URL(url), {
86
+ requestInit: {
87
+ headers: { authorization: `Bearer ${creds.accessToken}` },
88
+ },
89
+ });
90
+ const client = new Client({ name: 'facilio-cli', version });
91
+ try {
92
+ await client.connect(transport);
93
+ // Server tools take a single pydantic `payload` parameter.
94
+ const res = await client.callTool({ name: toolName, arguments: { payload: args } });
95
+ return parseToolResult(toolName, res);
96
+ }
97
+ finally {
98
+ await client.close().catch(() => {
99
+ /* socket already gone — fine */
100
+ });
101
+ }
102
+ }
103
+ /**
104
+ * FastMCP returns pydantic outputs both as `structuredContent` and as a JSON
105
+ * text block; prefer the structured form, fall back to parsing the text.
106
+ */
107
+ export function parseToolResult(toolName, res) {
108
+ const text = res.content?.find((c) => c.type === 'text')?.text;
109
+ if (res.isError) {
110
+ throw new Error(`${toolName} failed: ${text || 'unknown tool error'}`);
111
+ }
112
+ if (res.structuredContent !== undefined && res.structuredContent !== null) {
113
+ return res.structuredContent;
114
+ }
115
+ if (text) {
116
+ try {
117
+ return JSON.parse(text);
118
+ }
119
+ catch {
120
+ return text;
121
+ }
122
+ }
123
+ throw new Error(`${toolName} returned no content.`);
124
+ }
125
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/products/connections/client.ts"],"names":[],"mappings":"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"}