@cubis/foundry 0.3.21 → 0.3.23
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 +28 -0
- package/README.md +37 -5
- package/bin/cubis.js +652 -79
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
|
|
5
|
+
## [0.3.23] - 2026-02-26
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added a root `CHANGELOG.md` and npm-visible changelog section in `README.md`.
|
|
10
|
+
- Included `CHANGELOG.md` in published npm package files.
|
|
11
|
+
|
|
12
|
+
## [0.3.22] - 2026-02-26
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Added Antigravity-only default `StitchMCP` runtime setup in Gemini settings.
|
|
17
|
+
- Added `--stitch-api-key` and `STITCH_API_KEY` support for Stitch MCP config.
|
|
18
|
+
- Added managed Stitch MCP definition output in `.cbx/mcp/antigravity/stitch.json`.
|
|
19
|
+
- Added Stitch and Postman API key setup links and guidance in `README.md`.
|
|
20
|
+
|
|
21
|
+
## [0.3.21] - 2026-02-26
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Split Postman MCP management from skill folder into managed `.cbx/mcp/<platform>/postman.json`.
|
|
26
|
+
- Added centralized Postman config via `cbx_config.json`.
|
|
27
|
+
- Added MCP scope controls (`project/workspace/global/user`) with platform-aware runtime placement.
|
|
28
|
+
- Kept rules/engineering artifacts in workspace scope while defaulting skills/powers install to global scope.
|
package/README.md
CHANGED
|
@@ -9,6 +9,11 @@ Primary support in this release:
|
|
|
9
9
|
- Codex
|
|
10
10
|
- Copilot (VS Code Chat + Copilot CLI)
|
|
11
11
|
|
|
12
|
+
## Changelog
|
|
13
|
+
|
|
14
|
+
- Latest release notes are visible here on npm and tracked in [`CHANGELOG.md`](./CHANGELOG.md).
|
|
15
|
+
- GitHub view: [CHANGELOG.md](https://github.com/CUBETIQ/cubis-foundry/blob/main/CHANGELOG.md)
|
|
16
|
+
|
|
12
17
|
## Install
|
|
13
18
|
|
|
14
19
|
```bash
|
|
@@ -26,8 +31,9 @@ Compatibility binaries are still shipped for migration:
|
|
|
26
31
|
# 1) Install CLI
|
|
27
32
|
npm install -g @cubis/foundry
|
|
28
33
|
|
|
29
|
-
# 2) Set
|
|
34
|
+
# 2) Set API keys once (recommended: env mode)
|
|
30
35
|
export POSTMAN_API_KEY="<your-postman-api-key>"
|
|
36
|
+
export STITCH_API_KEY="<your-stitch-api-key>" # Antigravity StitchMCP only
|
|
31
37
|
|
|
32
38
|
# 3) Install workflow bundle for your platform
|
|
33
39
|
cbx workflows install --platform codex --bundle agent-environment-setup --postman --yes
|
|
@@ -54,7 +60,10 @@ cbx workflows install --platform antigravity --terminal-integration --terminal-v
|
|
|
54
60
|
cbx workflows install --platform codex --postman
|
|
55
61
|
cbx workflows install --platform codex --postman --postman-workspace-id null
|
|
56
62
|
cbx workflows install --platform codex --postman --postman-api-key "<key>"
|
|
63
|
+
cbx workflows install --platform codex --postman --mcp-scope global
|
|
64
|
+
cbx workflows install --platform copilot --postman --mcp-scope project
|
|
57
65
|
cbx workflows install --platform antigravity --postman
|
|
66
|
+
cbx workflows install --platform antigravity --postman --stitch-api-key "<key>"
|
|
58
67
|
cbx workflows install --platform copilot --postman
|
|
59
68
|
```
|
|
60
69
|
|
|
@@ -63,15 +72,38 @@ Install bootstrap behavior:
|
|
|
63
72
|
- When install scope is `global` (default), skills/powers install to global paths, while workflows + agents stay in workspace (`project`) paths.
|
|
64
73
|
- Rule sync + engineering artifacts (`AGENTS.md`/`GEMINI.md`/Copilot instructions, `ENGINEERING_RULES.md`, `TECH.md`) are maintained in workspace (`project`) scope.
|
|
65
74
|
- Codex workflow templates are maintained in workspace `.agents/workflows` so workflow-wrapper routing remains discoverable in project rules.
|
|
66
|
-
- Optional `--postman` bootstrap creates `
|
|
75
|
+
- Optional `--postman` bootstrap creates `cbx_config.json`, stores managed MCP definitions in `.cbx/mcp/`, and installs/configures Postman MCP for Codex, Antigravity, and Copilot.
|
|
76
|
+
- For Antigravity only, `--postman` also installs a default `StitchMCP` entry (`stitch.googleapis.com/mcp`) using key from `--stitch-api-key` or `STITCH_API_KEY`.
|
|
77
|
+
- Use `--mcp-scope <project|workspace|global|user>` to choose where MCP runtime config is installed (interactive installs prompt for this when not provided).
|
|
67
78
|
- Use `cbx rules init --platform <platform> --overwrite` to force-regenerate both files.
|
|
68
79
|
|
|
69
|
-
Postman setup behavior:
|
|
70
|
-
- `
|
|
80
|
+
Postman + Antigravity Stitch setup behavior:
|
|
81
|
+
- `cbx_config.json` is generated in workspace root (project MCP scope) or `~/.cbx/cbx_config.json` (global MCP scope).
|
|
82
|
+
- Managed MCP definition files are generated under `.cbx/mcp/<platform>/postman.json` (workspace scope) or `~/.cbx/mcp/<platform>/postman.json` (global scope).
|
|
71
83
|
- Env-first auth is supported: when `POSTMAN_API_KEY` is set, generated settings keep `apiKey: null` and MCP config uses `Bearer ${POSTMAN_API_KEY}`.
|
|
72
84
|
- Inline auth is supported with `--postman-api-key <key>`.
|
|
73
85
|
- `--postman-workspace-id null` writes JSON `null` for `defaultWorkspaceId`.
|
|
74
|
-
-
|
|
86
|
+
- Antigravity gets an additional managed file at `.cbx/mcp/antigravity/stitch.json` (or `~/.cbx/mcp/antigravity/stitch.json` in global MCP scope).
|
|
87
|
+
- Stitch key source priority for Antigravity: `--stitch-api-key` then `STITCH_API_KEY`; when unset, generated config keeps placeholder `X-Goog-Api-Key: ur stitch key`.
|
|
88
|
+
- In project MCP scope, `cbx_config.json` and `.cbx/mcp/` are auto-added to `.gitignore` (no duplicate entries).
|
|
89
|
+
|
|
90
|
+
Platform runtime MCP placement:
|
|
91
|
+
- Codex:
|
|
92
|
+
- Global MCP scope: `~/.codex/config.toml` via `codex mcp add`.
|
|
93
|
+
- Workspace MCP scope: `.vscode/mcp.json`.
|
|
94
|
+
- Antigravity (Gemini CLI):
|
|
95
|
+
- Global MCP scope: `~/.gemini/settings.json` (`mcpServers`, includes `postman` + default `StitchMCP`).
|
|
96
|
+
- Workspace MCP scope: `.gemini/settings.json` (`mcpServers`, includes `postman` + default `StitchMCP`).
|
|
97
|
+
- Copilot:
|
|
98
|
+
- Workspace MCP scope: `.vscode/mcp.json`.
|
|
99
|
+
- Global MCP scope: `~/.copilot/mcp-config.json`.
|
|
100
|
+
|
|
101
|
+
API key docs:
|
|
102
|
+
- Google Stitch MCP setup docs: [stitch.withgoogle.com/docs/mcp/setup](https://stitch.withgoogle.com/docs/mcp/setup)
|
|
103
|
+
- Google Stitch settings (create API key): [stitch.withgoogle.com/settings](https://stitch.withgoogle.com/settings)
|
|
104
|
+
- Google Cloud API key guidance: [Authenticate to Google and Google Cloud MCP servers](https://docs.cloud.google.com/mcp/authenticate-mcp)
|
|
105
|
+
- Postman API key creation: [Generate and use Postman API keys](https://learning.postman.com/docs/developer/postman-api/authentication/)
|
|
106
|
+
- Postman MCP setup: [Set up the Postman MCP Server](https://learning.postman.com/docs/developer/postman-api/postman-mcp-server/set-up-postman-mcp-server)
|
|
75
107
|
|
|
76
108
|
`rules` manages strict engineering policy and a generated codebase tech map:
|
|
77
109
|
|
package/bin/cubis.js
CHANGED
|
@@ -13,13 +13,16 @@ import {
|
|
|
13
13
|
writeFile
|
|
14
14
|
} from "node:fs/promises";
|
|
15
15
|
import { createRequire } from "node:module";
|
|
16
|
+
import { execFile as execFileCallback } from "node:child_process";
|
|
16
17
|
import os from "node:os";
|
|
17
18
|
import path from "node:path";
|
|
18
19
|
import process from "node:process";
|
|
20
|
+
import { promisify } from "node:util";
|
|
19
21
|
import { fileURLToPath } from "node:url";
|
|
20
22
|
|
|
21
23
|
const require = createRequire(import.meta.url);
|
|
22
24
|
const { version: CLI_VERSION } = require("../package.json");
|
|
25
|
+
const execFile = promisify(execFileCallback);
|
|
23
26
|
|
|
24
27
|
const MANAGED_BLOCK_START_RE = /<!--\s*cbx:workflows:auto:start[^>]*-->/g;
|
|
25
28
|
const MANAGED_BLOCK_END_RE = /<!--\s*cbx:workflows:auto:end\s*-->/g;
|
|
@@ -125,9 +128,16 @@ const DEFAULT_TERMINAL_VERIFIER = "codex";
|
|
|
125
128
|
const POSTMAN_API_KEY_ENV_VAR = "POSTMAN_API_KEY";
|
|
126
129
|
const POSTMAN_MCP_URL = "https://mcp.postman.com/minimal";
|
|
127
130
|
const POSTMAN_SKILL_ID = "postman";
|
|
131
|
+
const STITCH_MCP_SERVER_ID = "StitchMCP";
|
|
132
|
+
const STITCH_API_KEY_ENV_VAR = "STITCH_API_KEY";
|
|
133
|
+
const STITCH_MCP_URL = "https://stitch.googleapis.com/mcp";
|
|
134
|
+
const STITCH_API_KEY_PLACEHOLDER = "ur stitch key";
|
|
135
|
+
const CBX_CONFIG_FILENAME = "cbx_config.json";
|
|
128
136
|
const POSTMAN_SETTINGS_FILENAME = "postman_setting.json";
|
|
129
137
|
const POSTMAN_API_KEY_MISSING_WARNING =
|
|
130
|
-
`Postman API key is not configured. Set ${POSTMAN_API_KEY_ENV_VAR} or update ${
|
|
138
|
+
`Postman API key is not configured. Set ${POSTMAN_API_KEY_ENV_VAR} or update ${CBX_CONFIG_FILENAME}.`;
|
|
139
|
+
const STITCH_API_KEY_MISSING_WARNING =
|
|
140
|
+
`Google Stitch API key is not configured. Set ${STITCH_API_KEY_ENV_VAR} or update ${CBX_CONFIG_FILENAME}.`;
|
|
131
141
|
const TECH_SCAN_MAX_FILES = 5000;
|
|
132
142
|
const TECH_SCAN_IGNORED_DIRS = new Set([
|
|
133
143
|
".git",
|
|
@@ -279,6 +289,14 @@ function normalizeScope(value) {
|
|
|
279
289
|
throw new Error(`Unknown scope '${value}'. Use --scope project or --scope global.`);
|
|
280
290
|
}
|
|
281
291
|
|
|
292
|
+
function normalizeMcpScope(value, fallback = "project") {
|
|
293
|
+
if (!value) return fallback;
|
|
294
|
+
const normalized = value.trim().toLowerCase();
|
|
295
|
+
if (normalized === "project" || normalized === "workspace") return "project";
|
|
296
|
+
if (normalized === "global" || normalized === "user") return "global";
|
|
297
|
+
throw new Error(`Unknown MCP scope '${value}'. Use project/workspace or global/user.`);
|
|
298
|
+
}
|
|
299
|
+
|
|
282
300
|
function normalizeTerminalVerifier(value) {
|
|
283
301
|
if (!value) return null;
|
|
284
302
|
const normalized = value.trim().toLowerCase();
|
|
@@ -2254,7 +2272,7 @@ async function writeGeneratedArtifact({ destination, content, dryRun = false })
|
|
|
2254
2272
|
return { action: exists ? "replaced" : "installed", path: destination };
|
|
2255
2273
|
}
|
|
2256
2274
|
|
|
2257
|
-
function
|
|
2275
|
+
function resolveLegacyPostmanSettingsPath({ scope, cwd = process.cwd() }) {
|
|
2258
2276
|
if (scope === "global") {
|
|
2259
2277
|
return path.join(os.homedir(), ".cbx", POSTMAN_SETTINGS_FILENAME);
|
|
2260
2278
|
}
|
|
@@ -2262,20 +2280,116 @@ function resolvePostmanSettingsPath({ scope, cwd = process.cwd() }) {
|
|
|
2262
2280
|
return path.join(workspaceRoot, POSTMAN_SETTINGS_FILENAME);
|
|
2263
2281
|
}
|
|
2264
2282
|
|
|
2265
|
-
function
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2283
|
+
function resolveCbxConfigPath({ scope, cwd = process.cwd() }) {
|
|
2284
|
+
if (scope === "global") {
|
|
2285
|
+
return path.join(os.homedir(), ".cbx", CBX_CONFIG_FILENAME);
|
|
2286
|
+
}
|
|
2287
|
+
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
2288
|
+
return path.join(workspaceRoot, CBX_CONFIG_FILENAME);
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
function resolveMcpRootPath({ scope, cwd = process.cwd() }) {
|
|
2292
|
+
if (scope === "global") {
|
|
2293
|
+
return path.join(os.homedir(), ".cbx", "mcp");
|
|
2294
|
+
}
|
|
2295
|
+
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
2296
|
+
return path.join(workspaceRoot, ".cbx", "mcp");
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2299
|
+
function resolvePostmanMcpDefinitionPath({ platform, scope, cwd = process.cwd() }) {
|
|
2300
|
+
return path.join(resolveMcpRootPath({ scope, cwd }), platform, `${POSTMAN_SKILL_ID}.json`);
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
function resolveStitchMcpDefinitionPath({ scope, cwd = process.cwd() }) {
|
|
2304
|
+
return path.join(resolveMcpRootPath({ scope, cwd }), "antigravity", "stitch.json");
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
function buildPostmanAuthHeader({ apiKey = null, apiKeyEnvVar = POSTMAN_API_KEY_ENV_VAR }) {
|
|
2308
|
+
return apiKey ? `Bearer ${apiKey}` : `Bearer \${${apiKeyEnvVar}}`;
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2311
|
+
function buildStitchApiHeader({ apiKey = null }) {
|
|
2312
|
+
return `X-Goog-Api-Key: ${apiKey || STITCH_API_KEY_PLACEHOLDER}`;
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
function buildPostmanMcpDefinition({
|
|
2316
|
+
apiKey = null,
|
|
2317
|
+
apiKeyEnvVar = POSTMAN_API_KEY_ENV_VAR,
|
|
2318
|
+
mcpUrl = POSTMAN_MCP_URL
|
|
2319
|
+
}) {
|
|
2269
2320
|
return {
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2321
|
+
schemaVersion: 1,
|
|
2322
|
+
server: POSTMAN_SKILL_ID,
|
|
2323
|
+
transport: "http",
|
|
2324
|
+
url: mcpUrl,
|
|
2325
|
+
headers: {
|
|
2326
|
+
Authorization: buildPostmanAuthHeader({ apiKey, apiKeyEnvVar })
|
|
2327
|
+
}
|
|
2328
|
+
};
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
function buildStitchMcpDefinition({
|
|
2332
|
+
apiKey = null,
|
|
2333
|
+
mcpUrl = STITCH_MCP_URL
|
|
2334
|
+
}) {
|
|
2335
|
+
return {
|
|
2336
|
+
schemaVersion: 1,
|
|
2337
|
+
server: STITCH_MCP_SERVER_ID,
|
|
2338
|
+
transport: "command",
|
|
2339
|
+
command: "npx",
|
|
2340
|
+
args: [
|
|
2341
|
+
"-y",
|
|
2342
|
+
"mcp-remote",
|
|
2343
|
+
mcpUrl,
|
|
2344
|
+
"--header",
|
|
2345
|
+
buildStitchApiHeader({ apiKey })
|
|
2346
|
+
],
|
|
2347
|
+
env: {}
|
|
2348
|
+
};
|
|
2349
|
+
}
|
|
2350
|
+
|
|
2351
|
+
function buildVsCodePostmanServer({
|
|
2352
|
+
apiKey = null,
|
|
2353
|
+
apiKeyEnvVar = POSTMAN_API_KEY_ENV_VAR,
|
|
2354
|
+
mcpUrl = POSTMAN_MCP_URL
|
|
2355
|
+
}) {
|
|
2356
|
+
return {
|
|
2357
|
+
type: "sse",
|
|
2358
|
+
url: mcpUrl,
|
|
2359
|
+
headers: {
|
|
2360
|
+
Authorization: buildPostmanAuthHeader({ apiKey, apiKeyEnvVar })
|
|
2361
|
+
}
|
|
2362
|
+
};
|
|
2363
|
+
}
|
|
2364
|
+
|
|
2365
|
+
function buildGeminiPostmanServer({
|
|
2366
|
+
apiKey = null,
|
|
2367
|
+
apiKeyEnvVar = POSTMAN_API_KEY_ENV_VAR,
|
|
2368
|
+
mcpUrl = POSTMAN_MCP_URL
|
|
2369
|
+
}) {
|
|
2370
|
+
return {
|
|
2371
|
+
httpUrl: mcpUrl,
|
|
2372
|
+
headers: {
|
|
2373
|
+
Authorization: buildPostmanAuthHeader({ apiKey, apiKeyEnvVar })
|
|
2374
|
+
}
|
|
2375
|
+
};
|
|
2376
|
+
}
|
|
2377
|
+
|
|
2378
|
+
function buildGeminiStitchServer({
|
|
2379
|
+
apiKey = null,
|
|
2380
|
+
mcpUrl = STITCH_MCP_URL
|
|
2381
|
+
}) {
|
|
2382
|
+
return {
|
|
2383
|
+
$typeName: "exa.cascade_plugins_pb.CascadePluginCommandTemplate",
|
|
2384
|
+
command: "npx",
|
|
2385
|
+
args: [
|
|
2386
|
+
"-y",
|
|
2387
|
+
"mcp-remote",
|
|
2388
|
+
mcpUrl,
|
|
2389
|
+
"--header",
|
|
2390
|
+
buildStitchApiHeader({ apiKey })
|
|
2391
|
+
],
|
|
2392
|
+
env: {}
|
|
2279
2393
|
};
|
|
2280
2394
|
}
|
|
2281
2395
|
|
|
@@ -2285,12 +2399,297 @@ function getPostmanApiKeySource({ apiKey, envApiKey }) {
|
|
|
2285
2399
|
return "unset";
|
|
2286
2400
|
}
|
|
2287
2401
|
|
|
2402
|
+
function getStitchApiKeySource({ apiKey, envApiKey }) {
|
|
2403
|
+
if (apiKey) return "inline";
|
|
2404
|
+
if (envApiKey) return "env";
|
|
2405
|
+
return "unset";
|
|
2406
|
+
}
|
|
2407
|
+
|
|
2288
2408
|
function normalizePostmanApiKey(value) {
|
|
2289
2409
|
if (value === undefined || value === null) return null;
|
|
2290
2410
|
const normalized = String(value).trim();
|
|
2291
2411
|
return normalized || null;
|
|
2292
2412
|
}
|
|
2293
2413
|
|
|
2414
|
+
function parseStoredPostmanConfig(raw) {
|
|
2415
|
+
if (!raw || typeof raw !== "object") return null;
|
|
2416
|
+
const source = raw.postman && typeof raw.postman === "object" ? raw.postman : raw;
|
|
2417
|
+
|
|
2418
|
+
const apiKey = normalizePostmanApiKey(source.apiKey);
|
|
2419
|
+
const apiKeyEnvVar = String(source.apiKeyEnvVar || POSTMAN_API_KEY_ENV_VAR).trim() || POSTMAN_API_KEY_ENV_VAR;
|
|
2420
|
+
const mcpUrl = String(source.mcpUrl || POSTMAN_MCP_URL).trim() || POSTMAN_MCP_URL;
|
|
2421
|
+
const defaultWorkspaceId = normalizePostmanWorkspaceId(source.defaultWorkspaceId);
|
|
2422
|
+
|
|
2423
|
+
return {
|
|
2424
|
+
apiKey,
|
|
2425
|
+
apiKeyEnvVar,
|
|
2426
|
+
mcpUrl,
|
|
2427
|
+
defaultWorkspaceId
|
|
2428
|
+
};
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
function parseStoredStitchConfig(raw) {
|
|
2432
|
+
if (!raw || typeof raw !== "object") return null;
|
|
2433
|
+
const source = raw.stitch && typeof raw.stitch === "object" ? raw.stitch : null;
|
|
2434
|
+
if (!source) return null;
|
|
2435
|
+
|
|
2436
|
+
const apiKey = normalizePostmanApiKey(source.apiKey);
|
|
2437
|
+
const apiKeyEnvVar = String(source.apiKeyEnvVar || STITCH_API_KEY_ENV_VAR).trim() || STITCH_API_KEY_ENV_VAR;
|
|
2438
|
+
const mcpUrl = String(source.mcpUrl || STITCH_MCP_URL).trim() || STITCH_MCP_URL;
|
|
2439
|
+
|
|
2440
|
+
return {
|
|
2441
|
+
apiKey,
|
|
2442
|
+
apiKeyEnvVar,
|
|
2443
|
+
mcpUrl
|
|
2444
|
+
};
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
async function readJsonFileIfExists(filePath) {
|
|
2448
|
+
if (!(await pathExists(filePath))) return { exists: false, value: null };
|
|
2449
|
+
try {
|
|
2450
|
+
const raw = await readFile(filePath, "utf8");
|
|
2451
|
+
return { exists: true, value: JSON.parse(raw) };
|
|
2452
|
+
} catch (error) {
|
|
2453
|
+
return { exists: true, value: null, error };
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
async function upsertJsonObjectFile({ targetPath, updater, dryRun = false }) {
|
|
2458
|
+
const exists = await pathExists(targetPath);
|
|
2459
|
+
const warnings = [];
|
|
2460
|
+
let parsed = {};
|
|
2461
|
+
|
|
2462
|
+
if (exists) {
|
|
2463
|
+
try {
|
|
2464
|
+
const raw = await readFile(targetPath, "utf8");
|
|
2465
|
+
const decoded = JSON.parse(raw);
|
|
2466
|
+
if (decoded && typeof decoded === "object" && !Array.isArray(decoded)) {
|
|
2467
|
+
parsed = decoded;
|
|
2468
|
+
} else {
|
|
2469
|
+
warnings.push(`Existing JSON at ${targetPath} was not an object. Resetting structure.`);
|
|
2470
|
+
}
|
|
2471
|
+
} catch {
|
|
2472
|
+
warnings.push(`Existing JSON at ${targetPath} could not be parsed. Resetting structure.`);
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
const nextValue = updater(parsed);
|
|
2477
|
+
const content = `${JSON.stringify(nextValue, null, 2)}\n`;
|
|
2478
|
+
const writeResult = await writeGeneratedArtifact({
|
|
2479
|
+
destination: targetPath,
|
|
2480
|
+
content,
|
|
2481
|
+
dryRun
|
|
2482
|
+
});
|
|
2483
|
+
|
|
2484
|
+
return {
|
|
2485
|
+
action: writeResult.action,
|
|
2486
|
+
filePath: targetPath,
|
|
2487
|
+
warnings
|
|
2488
|
+
};
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
async function removeGeneratedArtifactIfExists({ targetPath, dryRun = false }) {
|
|
2492
|
+
const exists = await pathExists(targetPath);
|
|
2493
|
+
if (!exists) {
|
|
2494
|
+
return {
|
|
2495
|
+
action: "missing",
|
|
2496
|
+
path: targetPath
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
if (!dryRun) {
|
|
2501
|
+
await rm(targetPath, { recursive: true, force: true });
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
return {
|
|
2505
|
+
action: dryRun ? "would-remove" : "removed",
|
|
2506
|
+
path: targetPath
|
|
2507
|
+
};
|
|
2508
|
+
}
|
|
2509
|
+
|
|
2510
|
+
async function applyPostmanMcpForPlatform({
|
|
2511
|
+
platform,
|
|
2512
|
+
mcpScope,
|
|
2513
|
+
apiKey,
|
|
2514
|
+
apiKeyEnvVar,
|
|
2515
|
+
mcpUrl,
|
|
2516
|
+
stitchApiKey,
|
|
2517
|
+
stitchMcpUrl,
|
|
2518
|
+
includeStitchMcp = false,
|
|
2519
|
+
dryRun = false,
|
|
2520
|
+
cwd = process.cwd()
|
|
2521
|
+
}) {
|
|
2522
|
+
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
2523
|
+
const warnings = [];
|
|
2524
|
+
|
|
2525
|
+
if (platform === "antigravity") {
|
|
2526
|
+
const settingsPath =
|
|
2527
|
+
mcpScope === "global"
|
|
2528
|
+
? path.join(os.homedir(), ".gemini", "settings.json")
|
|
2529
|
+
: path.join(workspaceRoot, ".gemini", "settings.json");
|
|
2530
|
+
const result = await upsertJsonObjectFile({
|
|
2531
|
+
targetPath: settingsPath,
|
|
2532
|
+
updater: (existing) => {
|
|
2533
|
+
const next = { ...existing };
|
|
2534
|
+
const mcpServers =
|
|
2535
|
+
next.mcpServers && typeof next.mcpServers === "object" && !Array.isArray(next.mcpServers)
|
|
2536
|
+
? { ...next.mcpServers }
|
|
2537
|
+
: {};
|
|
2538
|
+
mcpServers[POSTMAN_SKILL_ID] = buildGeminiPostmanServer({
|
|
2539
|
+
apiKey,
|
|
2540
|
+
apiKeyEnvVar,
|
|
2541
|
+
mcpUrl
|
|
2542
|
+
});
|
|
2543
|
+
if (includeStitchMcp) {
|
|
2544
|
+
mcpServers[STITCH_MCP_SERVER_ID] = buildGeminiStitchServer({
|
|
2545
|
+
apiKey: stitchApiKey,
|
|
2546
|
+
mcpUrl: stitchMcpUrl
|
|
2547
|
+
});
|
|
2548
|
+
}
|
|
2549
|
+
next.mcpServers = mcpServers;
|
|
2550
|
+
return next;
|
|
2551
|
+
},
|
|
2552
|
+
dryRun
|
|
2553
|
+
});
|
|
2554
|
+
return {
|
|
2555
|
+
kind: "gemini-settings",
|
|
2556
|
+
scope: mcpScope,
|
|
2557
|
+
path: settingsPath,
|
|
2558
|
+
action: result.action,
|
|
2559
|
+
warnings: [...warnings, ...result.warnings]
|
|
2560
|
+
};
|
|
2561
|
+
}
|
|
2562
|
+
|
|
2563
|
+
if (platform === "copilot") {
|
|
2564
|
+
const configPath =
|
|
2565
|
+
mcpScope === "global"
|
|
2566
|
+
? path.join(os.homedir(), ".copilot", "mcp-config.json")
|
|
2567
|
+
: path.join(workspaceRoot, ".vscode", "mcp.json");
|
|
2568
|
+
const result = await upsertJsonObjectFile({
|
|
2569
|
+
targetPath: configPath,
|
|
2570
|
+
updater: (existing) => {
|
|
2571
|
+
const next = { ...existing };
|
|
2572
|
+
const servers =
|
|
2573
|
+
next.servers && typeof next.servers === "object" && !Array.isArray(next.servers)
|
|
2574
|
+
? { ...next.servers }
|
|
2575
|
+
: {};
|
|
2576
|
+
servers[POSTMAN_SKILL_ID] = buildVsCodePostmanServer({
|
|
2577
|
+
apiKey,
|
|
2578
|
+
apiKeyEnvVar,
|
|
2579
|
+
mcpUrl
|
|
2580
|
+
});
|
|
2581
|
+
next.servers = servers;
|
|
2582
|
+
return next;
|
|
2583
|
+
},
|
|
2584
|
+
dryRun
|
|
2585
|
+
});
|
|
2586
|
+
return {
|
|
2587
|
+
kind: mcpScope === "global" ? "copilot-cli-mcp" : "vscode-mcp",
|
|
2588
|
+
scope: mcpScope,
|
|
2589
|
+
path: configPath,
|
|
2590
|
+
action: result.action,
|
|
2591
|
+
warnings: [...warnings, ...result.warnings]
|
|
2592
|
+
};
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
if (platform === "codex") {
|
|
2596
|
+
if (mcpScope === "project") {
|
|
2597
|
+
const vscodePath = path.join(workspaceRoot, ".vscode", "mcp.json");
|
|
2598
|
+
const result = await upsertJsonObjectFile({
|
|
2599
|
+
targetPath: vscodePath,
|
|
2600
|
+
updater: (existing) => {
|
|
2601
|
+
const next = { ...existing };
|
|
2602
|
+
const servers =
|
|
2603
|
+
next.servers && typeof next.servers === "object" && !Array.isArray(next.servers)
|
|
2604
|
+
? { ...next.servers }
|
|
2605
|
+
: {};
|
|
2606
|
+
servers[POSTMAN_SKILL_ID] = buildVsCodePostmanServer({
|
|
2607
|
+
apiKey,
|
|
2608
|
+
apiKeyEnvVar,
|
|
2609
|
+
mcpUrl
|
|
2610
|
+
});
|
|
2611
|
+
next.servers = servers;
|
|
2612
|
+
return next;
|
|
2613
|
+
},
|
|
2614
|
+
dryRun
|
|
2615
|
+
});
|
|
2616
|
+
return {
|
|
2617
|
+
kind: "vscode-mcp",
|
|
2618
|
+
scope: mcpScope,
|
|
2619
|
+
path: vscodePath,
|
|
2620
|
+
action: result.action,
|
|
2621
|
+
warnings: [...warnings, ...result.warnings]
|
|
2622
|
+
};
|
|
2623
|
+
}
|
|
2624
|
+
|
|
2625
|
+
const codexConfigPath = path.join(os.homedir(), ".codex", "config.toml");
|
|
2626
|
+
if (dryRun) {
|
|
2627
|
+
return {
|
|
2628
|
+
kind: "codex-cli",
|
|
2629
|
+
scope: mcpScope,
|
|
2630
|
+
path: codexConfigPath,
|
|
2631
|
+
action: "would-patch",
|
|
2632
|
+
warnings
|
|
2633
|
+
};
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
try {
|
|
2637
|
+
await execFile("codex", ["mcp", "remove", POSTMAN_SKILL_ID], { cwd });
|
|
2638
|
+
} catch {
|
|
2639
|
+
// Best effort. Add will still run and becomes source of truth.
|
|
2640
|
+
}
|
|
2641
|
+
|
|
2642
|
+
try {
|
|
2643
|
+
await execFile(
|
|
2644
|
+
"codex",
|
|
2645
|
+
[
|
|
2646
|
+
"mcp",
|
|
2647
|
+
"add",
|
|
2648
|
+
POSTMAN_SKILL_ID,
|
|
2649
|
+
"--url",
|
|
2650
|
+
mcpUrl,
|
|
2651
|
+
"--bearer-token-env-var",
|
|
2652
|
+
apiKeyEnvVar || POSTMAN_API_KEY_ENV_VAR
|
|
2653
|
+
],
|
|
2654
|
+
{ cwd }
|
|
2655
|
+
);
|
|
2656
|
+
} catch (error) {
|
|
2657
|
+
warnings.push(
|
|
2658
|
+
`Failed to register Postman MCP via Codex CLI. Ensure 'codex' is installed and rerun. (${error.message})`
|
|
2659
|
+
);
|
|
2660
|
+
return {
|
|
2661
|
+
kind: "codex-cli",
|
|
2662
|
+
scope: mcpScope,
|
|
2663
|
+
path: codexConfigPath,
|
|
2664
|
+
action: "failed",
|
|
2665
|
+
warnings
|
|
2666
|
+
};
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
if (apiKey) {
|
|
2670
|
+
warnings.push(
|
|
2671
|
+
"Codex global MCP uses bearer token env vars. Inline apiKey in cbx_config.json is not directly used by Codex."
|
|
2672
|
+
);
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
return {
|
|
2676
|
+
kind: "codex-cli",
|
|
2677
|
+
scope: mcpScope,
|
|
2678
|
+
path: codexConfigPath,
|
|
2679
|
+
action: "patched",
|
|
2680
|
+
warnings
|
|
2681
|
+
};
|
|
2682
|
+
}
|
|
2683
|
+
|
|
2684
|
+
return {
|
|
2685
|
+
kind: "unknown",
|
|
2686
|
+
scope: mcpScope,
|
|
2687
|
+
path: null,
|
|
2688
|
+
action: "skipped",
|
|
2689
|
+
warnings: [`Unsupported platform '${platform}' for Postman MCP installation.`]
|
|
2690
|
+
};
|
|
2691
|
+
}
|
|
2692
|
+
|
|
2294
2693
|
async function ensureGitIgnoreEntry({
|
|
2295
2694
|
filePath,
|
|
2296
2695
|
entry,
|
|
@@ -2323,13 +2722,15 @@ async function ensureGitIgnoreEntry({
|
|
|
2323
2722
|
}
|
|
2324
2723
|
|
|
2325
2724
|
async function resolvePostmanInstallSelection({
|
|
2725
|
+
platform,
|
|
2326
2726
|
scope,
|
|
2327
2727
|
options,
|
|
2328
2728
|
cwd = process.cwd()
|
|
2329
2729
|
}) {
|
|
2330
2730
|
const hasApiKeyOption = options.postmanApiKey !== undefined;
|
|
2331
2731
|
const hasWorkspaceOption = options.postmanWorkspaceId !== undefined;
|
|
2332
|
-
const
|
|
2732
|
+
const hasStitchApiKeyOption = options.stitchApiKey !== undefined;
|
|
2733
|
+
const enabled = Boolean(options.postman) || hasApiKeyOption || hasWorkspaceOption || hasStitchApiKeyOption;
|
|
2333
2734
|
if (!enabled) return { enabled: false };
|
|
2334
2735
|
|
|
2335
2736
|
const explicitApiKey = hasApiKeyOption ? String(options.postmanApiKey || "").trim() : "";
|
|
@@ -2338,7 +2739,14 @@ async function resolvePostmanInstallSelection({
|
|
|
2338
2739
|
let defaultWorkspaceId = hasWorkspaceOption
|
|
2339
2740
|
? normalizePostmanWorkspaceId(options.postmanWorkspaceId)
|
|
2340
2741
|
: null;
|
|
2742
|
+
const requestedMcpScope = options.mcpScope
|
|
2743
|
+
? normalizeMcpScope(options.mcpScope, normalizeMcpScope(scope, "project"))
|
|
2744
|
+
: null;
|
|
2745
|
+
let mcpScope = requestedMcpScope || normalizeMcpScope(scope, "project");
|
|
2341
2746
|
const warnings = [];
|
|
2747
|
+
const stitchEnabled = platform === "antigravity";
|
|
2748
|
+
let stitchApiKey = hasStitchApiKeyOption ? normalizePostmanApiKey(options.stitchApiKey) : null;
|
|
2749
|
+
const envStitchApiKey = normalizePostmanApiKey(process.env[STITCH_API_KEY_ENV_VAR]);
|
|
2342
2750
|
|
|
2343
2751
|
const canPrompt = !options.yes && process.stdin.isTTY;
|
|
2344
2752
|
if (canPrompt && !hasApiKeyOption && !apiKey && !envApiKey) {
|
|
@@ -2361,34 +2769,91 @@ async function resolvePostmanInstallSelection({
|
|
|
2361
2769
|
defaultWorkspaceId = normalizePostmanWorkspaceId(promptedWorkspaceId);
|
|
2362
2770
|
}
|
|
2363
2771
|
|
|
2772
|
+
if (canPrompt && stitchEnabled && !hasStitchApiKeyOption && !stitchApiKey && !envStitchApiKey) {
|
|
2773
|
+
const promptedStitchApiKey = String(
|
|
2774
|
+
await input({
|
|
2775
|
+
message: `Google Stitch API key (optional, leave blank to keep ${STITCH_API_KEY_ENV_VAR} env mode):`,
|
|
2776
|
+
default: ""
|
|
2777
|
+
})
|
|
2778
|
+
).trim();
|
|
2779
|
+
if (promptedStitchApiKey) {
|
|
2780
|
+
stitchApiKey = promptedStitchApiKey;
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
if (canPrompt && !requestedMcpScope) {
|
|
2785
|
+
mcpScope = await select({
|
|
2786
|
+
message: "Install MCP config in workspace or global scope?",
|
|
2787
|
+
choices: [
|
|
2788
|
+
{ name: scope === "global" ? "Global (recommended)" : "Global", value: "global" },
|
|
2789
|
+
{ name: scope === "project" ? "Workspace (recommended)" : "Workspace", value: "project" }
|
|
2790
|
+
],
|
|
2791
|
+
default: mcpScope
|
|
2792
|
+
});
|
|
2793
|
+
}
|
|
2794
|
+
|
|
2364
2795
|
const apiKeySource = getPostmanApiKeySource({ apiKey, envApiKey });
|
|
2365
2796
|
if (apiKeySource === "unset") {
|
|
2366
2797
|
warnings.push(POSTMAN_API_KEY_MISSING_WARNING);
|
|
2367
2798
|
}
|
|
2368
2799
|
|
|
2369
|
-
const
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2800
|
+
const stitchApiKeySource = stitchEnabled
|
|
2801
|
+
? getStitchApiKeySource({ apiKey: stitchApiKey, envApiKey: envStitchApiKey })
|
|
2802
|
+
: null;
|
|
2803
|
+
if (stitchEnabled && stitchApiKeySource === "unset") {
|
|
2804
|
+
warnings.push(STITCH_API_KEY_MISSING_WARNING);
|
|
2805
|
+
}
|
|
2806
|
+
if (!stitchEnabled && hasStitchApiKeyOption) {
|
|
2807
|
+
warnings.push("--stitch-api-key is only used for --platform antigravity.");
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2810
|
+
const cbxConfigPath = resolveCbxConfigPath({ scope: mcpScope, cwd });
|
|
2811
|
+
const legacySettingsPath = resolveLegacyPostmanSettingsPath({ scope: mcpScope, cwd });
|
|
2812
|
+
const cbxConfig = {
|
|
2813
|
+
schemaVersion: 1,
|
|
2376
2814
|
generatedBy: "cbx workflows install --postman",
|
|
2377
|
-
generatedAt: new Date().toISOString()
|
|
2815
|
+
generatedAt: new Date().toISOString(),
|
|
2816
|
+
mcp: {
|
|
2817
|
+
scope: mcpScope,
|
|
2818
|
+
server: POSTMAN_SKILL_ID,
|
|
2819
|
+
platform
|
|
2820
|
+
},
|
|
2821
|
+
postman: {
|
|
2822
|
+
apiKey: apiKey || null,
|
|
2823
|
+
apiKeyEnvVar: POSTMAN_API_KEY_ENV_VAR,
|
|
2824
|
+
apiKeySource,
|
|
2825
|
+
defaultWorkspaceId: defaultWorkspaceId ?? null,
|
|
2826
|
+
mcpUrl: POSTMAN_MCP_URL
|
|
2827
|
+
}
|
|
2378
2828
|
};
|
|
2829
|
+
if (stitchEnabled) {
|
|
2830
|
+
cbxConfig.stitch = {
|
|
2831
|
+
server: STITCH_MCP_SERVER_ID,
|
|
2832
|
+
apiKey: stitchApiKey || null,
|
|
2833
|
+
apiKeyEnvVar: STITCH_API_KEY_ENV_VAR,
|
|
2834
|
+
apiKeySource: stitchApiKeySource,
|
|
2835
|
+
mcpUrl: STITCH_MCP_URL
|
|
2836
|
+
};
|
|
2837
|
+
}
|
|
2379
2838
|
|
|
2380
2839
|
return {
|
|
2381
2840
|
enabled: true,
|
|
2382
2841
|
apiKey,
|
|
2383
2842
|
apiKeySource,
|
|
2843
|
+
stitchEnabled,
|
|
2844
|
+
stitchApiKey,
|
|
2845
|
+
stitchApiKeySource,
|
|
2384
2846
|
defaultWorkspaceId: defaultWorkspaceId ?? null,
|
|
2847
|
+
mcpScope,
|
|
2385
2848
|
warnings,
|
|
2386
|
-
|
|
2387
|
-
|
|
2849
|
+
cbxConfig,
|
|
2850
|
+
cbxConfigPath,
|
|
2851
|
+
legacySettingsPath
|
|
2388
2852
|
};
|
|
2389
2853
|
}
|
|
2390
2854
|
|
|
2391
2855
|
async function configurePostmanInstallArtifacts({
|
|
2856
|
+
platform,
|
|
2392
2857
|
scope,
|
|
2393
2858
|
profilePaths,
|
|
2394
2859
|
postmanSelection,
|
|
@@ -2398,34 +2863,55 @@ async function configurePostmanInstallArtifacts({
|
|
|
2398
2863
|
}) {
|
|
2399
2864
|
if (!postmanSelection?.enabled) return null;
|
|
2400
2865
|
|
|
2401
|
-
let warnings = postmanSelection.warnings.filter(
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2866
|
+
let warnings = postmanSelection.warnings.filter(
|
|
2867
|
+
(warning) => warning !== POSTMAN_API_KEY_MISSING_WARNING && warning !== STITCH_API_KEY_MISSING_WARNING
|
|
2868
|
+
);
|
|
2869
|
+
const cbxConfigContent = `${JSON.stringify(postmanSelection.cbxConfig, null, 2)}\n`;
|
|
2870
|
+
const cbxConfigResult = await writeTextFile({
|
|
2871
|
+
targetPath: postmanSelection.cbxConfigPath,
|
|
2872
|
+
content: cbxConfigContent,
|
|
2406
2873
|
overwrite,
|
|
2407
2874
|
dryRun
|
|
2408
2875
|
});
|
|
2409
2876
|
|
|
2410
|
-
let effectiveApiKey = normalizePostmanApiKey(postmanSelection.
|
|
2877
|
+
let effectiveApiKey = normalizePostmanApiKey(postmanSelection.cbxConfig?.postman?.apiKey);
|
|
2878
|
+
let effectiveApiKeyEnvVar = String(
|
|
2879
|
+
postmanSelection.cbxConfig?.postman?.apiKeyEnvVar || POSTMAN_API_KEY_ENV_VAR
|
|
2880
|
+
).trim();
|
|
2411
2881
|
let effectiveDefaultWorkspaceId = postmanSelection.defaultWorkspaceId ?? null;
|
|
2412
|
-
let effectiveMcpUrl = postmanSelection.
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2882
|
+
let effectiveMcpUrl = postmanSelection.cbxConfig?.postman?.mcpUrl || POSTMAN_MCP_URL;
|
|
2883
|
+
const shouldInstallStitch = Boolean(postmanSelection.stitchEnabled);
|
|
2884
|
+
let effectiveStitchApiKey = shouldInstallStitch
|
|
2885
|
+
? normalizePostmanApiKey(postmanSelection.cbxConfig?.stitch?.apiKey)
|
|
2886
|
+
: null;
|
|
2887
|
+
let effectiveStitchMcpUrl = shouldInstallStitch
|
|
2888
|
+
? postmanSelection.cbxConfig?.stitch?.mcpUrl || STITCH_MCP_URL
|
|
2889
|
+
: STITCH_MCP_URL;
|
|
2890
|
+
|
|
2891
|
+
if (cbxConfigResult.action === "skipped" || cbxConfigResult.action === "would-skip") {
|
|
2892
|
+
const existingCbxConfig = await readJsonFileIfExists(postmanSelection.cbxConfigPath);
|
|
2893
|
+
const existingLegacySettings = await readJsonFileIfExists(postmanSelection.legacySettingsPath);
|
|
2894
|
+
const storedPostmanConfig =
|
|
2895
|
+
parseStoredPostmanConfig(existingCbxConfig.value) || parseStoredPostmanConfig(existingLegacySettings.value);
|
|
2896
|
+
const storedStitchConfig =
|
|
2897
|
+
shouldInstallStitch &&
|
|
2898
|
+
(parseStoredStitchConfig(existingCbxConfig.value) || parseStoredStitchConfig(existingLegacySettings.value));
|
|
2899
|
+
|
|
2900
|
+
if (storedPostmanConfig) {
|
|
2901
|
+
effectiveApiKey = storedPostmanConfig.apiKey;
|
|
2902
|
+
effectiveApiKeyEnvVar = storedPostmanConfig.apiKeyEnvVar || POSTMAN_API_KEY_ENV_VAR;
|
|
2903
|
+
effectiveDefaultWorkspaceId = storedPostmanConfig.defaultWorkspaceId;
|
|
2904
|
+
effectiveMcpUrl = storedPostmanConfig.mcpUrl || POSTMAN_MCP_URL;
|
|
2905
|
+
} else {
|
|
2425
2906
|
warnings.push(
|
|
2426
|
-
`Existing ${POSTMAN_SETTINGS_FILENAME} could not be parsed. Using install-time Postman values for MCP config.`
|
|
2907
|
+
`Existing ${CBX_CONFIG_FILENAME} (or legacy ${POSTMAN_SETTINGS_FILENAME}) could not be parsed. Using install-time Postman values for MCP config.`
|
|
2427
2908
|
);
|
|
2428
2909
|
}
|
|
2910
|
+
|
|
2911
|
+
if (storedStitchConfig) {
|
|
2912
|
+
effectiveStitchApiKey = storedStitchConfig.apiKey;
|
|
2913
|
+
effectiveStitchMcpUrl = storedStitchConfig.mcpUrl || STITCH_MCP_URL;
|
|
2914
|
+
}
|
|
2429
2915
|
}
|
|
2430
2916
|
|
|
2431
2917
|
const envApiKey = normalizePostmanApiKey(process.env[POSTMAN_API_KEY_ENV_VAR]);
|
|
@@ -2436,52 +2922,112 @@ async function configurePostmanInstallArtifacts({
|
|
|
2436
2922
|
if (effectiveApiKeySource === "unset") {
|
|
2437
2923
|
warnings.push(POSTMAN_API_KEY_MISSING_WARNING);
|
|
2438
2924
|
}
|
|
2925
|
+
const envStitchApiKey = normalizePostmanApiKey(process.env[STITCH_API_KEY_ENV_VAR]);
|
|
2926
|
+
const effectiveStitchApiKeySource = shouldInstallStitch
|
|
2927
|
+
? getStitchApiKeySource({
|
|
2928
|
+
apiKey: effectiveStitchApiKey,
|
|
2929
|
+
envApiKey: envStitchApiKey
|
|
2930
|
+
})
|
|
2931
|
+
: null;
|
|
2932
|
+
if (shouldInstallStitch && effectiveStitchApiKeySource === "unset") {
|
|
2933
|
+
warnings.push(STITCH_API_KEY_MISSING_WARNING);
|
|
2934
|
+
}
|
|
2439
2935
|
|
|
2440
|
-
|
|
2441
|
-
if (
|
|
2936
|
+
const gitIgnoreResults = [];
|
|
2937
|
+
if (postmanSelection.mcpScope === "project") {
|
|
2442
2938
|
const workspaceRoot = findWorkspaceRoot(cwd);
|
|
2443
2939
|
const gitIgnorePath = path.join(workspaceRoot, ".gitignore");
|
|
2444
|
-
|
|
2940
|
+
const configIgnore = await ensureGitIgnoreEntry({
|
|
2445
2941
|
filePath: gitIgnorePath,
|
|
2446
|
-
entry:
|
|
2942
|
+
entry: CBX_CONFIG_FILENAME,
|
|
2447
2943
|
dryRun
|
|
2448
2944
|
});
|
|
2945
|
+
gitIgnoreResults.push(configIgnore);
|
|
2946
|
+
|
|
2947
|
+
const mcpIgnore = await ensureGitIgnoreEntry({
|
|
2948
|
+
filePath: gitIgnorePath,
|
|
2949
|
+
entry: ".cbx/mcp/",
|
|
2950
|
+
dryRun
|
|
2951
|
+
});
|
|
2952
|
+
gitIgnoreResults.push(mcpIgnore);
|
|
2449
2953
|
}
|
|
2450
2954
|
|
|
2451
|
-
const
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2955
|
+
const mcpDefinitionPath = resolvePostmanMcpDefinitionPath({
|
|
2956
|
+
platform,
|
|
2957
|
+
scope: postmanSelection.mcpScope,
|
|
2958
|
+
cwd
|
|
2959
|
+
});
|
|
2960
|
+
const mcpDefinitionContent = `${JSON.stringify(
|
|
2961
|
+
buildPostmanMcpDefinition({
|
|
2962
|
+
apiKey: effectiveApiKey,
|
|
2963
|
+
apiKeyEnvVar: effectiveApiKeyEnvVar,
|
|
2964
|
+
mcpUrl: effectiveMcpUrl
|
|
2965
|
+
}),
|
|
2966
|
+
null,
|
|
2967
|
+
2
|
|
2968
|
+
)}\n`;
|
|
2969
|
+
const mcpDefinitionResult = await writeGeneratedArtifact({
|
|
2970
|
+
destination: mcpDefinitionPath,
|
|
2971
|
+
content: mcpDefinitionContent,
|
|
2972
|
+
dryRun
|
|
2973
|
+
});
|
|
2974
|
+
let stitchMcpDefinitionPath = null;
|
|
2975
|
+
let stitchMcpDefinitionResult = null;
|
|
2976
|
+
if (shouldInstallStitch) {
|
|
2977
|
+
stitchMcpDefinitionPath = resolveStitchMcpDefinitionPath({
|
|
2978
|
+
scope: postmanSelection.mcpScope,
|
|
2979
|
+
cwd
|
|
2980
|
+
});
|
|
2981
|
+
const stitchMcpDefinitionContent = `${JSON.stringify(
|
|
2982
|
+
buildStitchMcpDefinition({
|
|
2983
|
+
apiKey: effectiveStitchApiKey,
|
|
2984
|
+
mcpUrl: effectiveStitchMcpUrl
|
|
2465
2985
|
}),
|
|
2466
2986
|
null,
|
|
2467
2987
|
2
|
|
2468
2988
|
)}\n`;
|
|
2469
|
-
|
|
2470
|
-
destination:
|
|
2471
|
-
content:
|
|
2989
|
+
stitchMcpDefinitionResult = await writeGeneratedArtifact({
|
|
2990
|
+
destination: stitchMcpDefinitionPath,
|
|
2991
|
+
content: stitchMcpDefinitionContent,
|
|
2472
2992
|
dryRun
|
|
2473
2993
|
});
|
|
2474
2994
|
}
|
|
2475
2995
|
|
|
2996
|
+
const mcpRuntimeResult = await applyPostmanMcpForPlatform({
|
|
2997
|
+
platform,
|
|
2998
|
+
mcpScope: postmanSelection.mcpScope,
|
|
2999
|
+
apiKey: effectiveApiKey,
|
|
3000
|
+
apiKeyEnvVar: effectiveApiKeyEnvVar,
|
|
3001
|
+
mcpUrl: effectiveMcpUrl,
|
|
3002
|
+
stitchApiKey: effectiveStitchApiKey,
|
|
3003
|
+
stitchMcpUrl: effectiveStitchMcpUrl,
|
|
3004
|
+
includeStitchMcp: shouldInstallStitch,
|
|
3005
|
+
dryRun,
|
|
3006
|
+
cwd
|
|
3007
|
+
});
|
|
3008
|
+
warnings.push(...(mcpRuntimeResult.warnings || []));
|
|
3009
|
+
|
|
3010
|
+
const legacySkillMcpCleanup = await removeGeneratedArtifactIfExists({
|
|
3011
|
+
targetPath: path.join(profilePaths.skillsDir, POSTMAN_SKILL_ID, "mcp.json"),
|
|
3012
|
+
dryRun
|
|
3013
|
+
});
|
|
3014
|
+
|
|
2476
3015
|
return {
|
|
2477
3016
|
enabled: true,
|
|
3017
|
+
mcpScope: postmanSelection.mcpScope,
|
|
2478
3018
|
apiKeySource: effectiveApiKeySource,
|
|
3019
|
+
stitchApiKeySource: effectiveStitchApiKeySource,
|
|
2479
3020
|
defaultWorkspaceId: effectiveDefaultWorkspaceId,
|
|
2480
3021
|
warnings,
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
3022
|
+
cbxConfigPath: postmanSelection.cbxConfigPath,
|
|
3023
|
+
cbxConfigResult,
|
|
3024
|
+
gitIgnoreResults,
|
|
3025
|
+
mcpDefinitionPath,
|
|
3026
|
+
mcpDefinitionResult,
|
|
3027
|
+
stitchMcpDefinitionPath,
|
|
3028
|
+
stitchMcpDefinitionResult,
|
|
3029
|
+
mcpRuntimeResult,
|
|
3030
|
+
legacySkillMcpCleanup
|
|
2485
3031
|
};
|
|
2486
3032
|
}
|
|
2487
3033
|
|
|
@@ -3051,18 +3597,35 @@ function printPostmanSetupSummary({ postmanSetup }) {
|
|
|
3051
3597
|
if (!postmanSetup?.enabled) return;
|
|
3052
3598
|
|
|
3053
3599
|
console.log("\nPostman setup:");
|
|
3054
|
-
console.log(`-
|
|
3055
|
-
console.log(`-
|
|
3600
|
+
console.log(`- MCP scope: ${postmanSetup.mcpScope}`);
|
|
3601
|
+
console.log(`- Config file: ${postmanSetup.cbxConfigResult.action} (${postmanSetup.cbxConfigPath})`);
|
|
3602
|
+
console.log(`- Postman API key source: ${postmanSetup.apiKeySource}`);
|
|
3603
|
+
if (postmanSetup.stitchApiKeySource) {
|
|
3604
|
+
console.log(`- Stitch API key source: ${postmanSetup.stitchApiKeySource}`);
|
|
3605
|
+
}
|
|
3056
3606
|
console.log(
|
|
3057
3607
|
`- Default workspace ID: ${postmanSetup.defaultWorkspaceId === null ? "null" : postmanSetup.defaultWorkspaceId}`
|
|
3058
3608
|
);
|
|
3059
|
-
|
|
3609
|
+
for (const ignoreResult of postmanSetup.gitIgnoreResults || []) {
|
|
3610
|
+
console.log(`- .gitignore (${ignoreResult.filePath}): ${ignoreResult.action}`);
|
|
3611
|
+
}
|
|
3612
|
+
console.log(
|
|
3613
|
+
`- Managed MCP definition (${postmanSetup.mcpDefinitionPath}): ${postmanSetup.mcpDefinitionResult.action}`
|
|
3614
|
+
);
|
|
3615
|
+
if (postmanSetup.stitchMcpDefinitionPath && postmanSetup.stitchMcpDefinitionResult) {
|
|
3060
3616
|
console.log(
|
|
3061
|
-
`-
|
|
3617
|
+
`- Managed Stitch MCP definition (${postmanSetup.stitchMcpDefinitionPath}): ${postmanSetup.stitchMcpDefinitionResult.action}`
|
|
3062
3618
|
);
|
|
3063
3619
|
}
|
|
3064
|
-
if (postmanSetup.
|
|
3065
|
-
console.log(
|
|
3620
|
+
if (postmanSetup.mcpRuntimeResult) {
|
|
3621
|
+
console.log(
|
|
3622
|
+
`- Platform MCP target (${postmanSetup.mcpRuntimeResult.path || "n/a"}): ${postmanSetup.mcpRuntimeResult.action}`
|
|
3623
|
+
);
|
|
3624
|
+
}
|
|
3625
|
+
if (postmanSetup.legacySkillMcpCleanup) {
|
|
3626
|
+
console.log(
|
|
3627
|
+
`- Legacy skill mcp.json cleanup (${postmanSetup.legacySkillMcpCleanup.path}): ${postmanSetup.legacySkillMcpCleanup.action}`
|
|
3628
|
+
);
|
|
3066
3629
|
}
|
|
3067
3630
|
|
|
3068
3631
|
if (postmanSetup.warnings.length > 0) {
|
|
@@ -3383,16 +3946,24 @@ function withInstallOptions(command) {
|
|
|
3383
3946
|
.option("--overwrite", "overwrite existing files")
|
|
3384
3947
|
.option(
|
|
3385
3948
|
"--postman",
|
|
3386
|
-
"optional: install Postman skill and generate
|
|
3949
|
+
"optional: install Postman skill and generate cbx_config.json"
|
|
3387
3950
|
)
|
|
3388
3951
|
.option(
|
|
3389
3952
|
"--postman-api-key <key>",
|
|
3390
|
-
"optional: set Postman API key inline for generated
|
|
3953
|
+
"optional: set Postman API key inline for generated cbx_config.json and installed Postman MCP config"
|
|
3391
3954
|
)
|
|
3392
3955
|
.option(
|
|
3393
3956
|
"--postman-workspace-id <id|null>",
|
|
3394
3957
|
"optional: set default Postman workspace ID (use 'null' for no default)"
|
|
3395
3958
|
)
|
|
3959
|
+
.option(
|
|
3960
|
+
"--stitch-api-key <key>",
|
|
3961
|
+
"optional: Antigravity only. Set Google Stitch API key inline for StitchMCP config"
|
|
3962
|
+
)
|
|
3963
|
+
.option(
|
|
3964
|
+
"--mcp-scope <scope>",
|
|
3965
|
+
"optional: MCP config scope for --postman (project|workspace|global|user)"
|
|
3966
|
+
)
|
|
3396
3967
|
.option(
|
|
3397
3968
|
"--terminal-integration",
|
|
3398
3969
|
"Antigravity only: enable terminal verification integration (prompts for verifier when interactive)"
|
|
@@ -3557,6 +4128,7 @@ async function runWorkflowInstall(options) {
|
|
|
3557
4128
|
options
|
|
3558
4129
|
});
|
|
3559
4130
|
const postmanSelection = await resolvePostmanInstallSelection({
|
|
4131
|
+
platform,
|
|
3560
4132
|
scope,
|
|
3561
4133
|
options,
|
|
3562
4134
|
cwd
|
|
@@ -3615,6 +4187,7 @@ async function runWorkflowInstall(options) {
|
|
|
3615
4187
|
cwd
|
|
3616
4188
|
});
|
|
3617
4189
|
const postmanSetupResult = await configurePostmanInstallArtifacts({
|
|
4190
|
+
platform,
|
|
3618
4191
|
scope,
|
|
3619
4192
|
profilePaths: installResult.profilePaths,
|
|
3620
4193
|
postmanSelection,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cubis/foundry",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.23",
|
|
4
4
|
"description": "Cubis Foundry CLI for workflow-first AI agent environments",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"files": [
|
|
12
12
|
"bin",
|
|
13
13
|
"Ai Agent Workflow",
|
|
14
|
-
"README.md"
|
|
14
|
+
"README.md",
|
|
15
|
+
"CHANGELOG.md"
|
|
15
16
|
],
|
|
16
17
|
"scripts": {
|
|
17
18
|
"start": "node bin/cubis.js",
|