@nebulr-group/bridge-cli 0.6.6 → 1.0.0-alpha.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/README.md +93 -460
- package/dist/api/accounts.d.ts +69 -0
- package/dist/api/accounts.d.ts.map +1 -0
- package/dist/api/accounts.js +42 -0
- package/dist/api/accounts.js.map +1 -0
- package/dist/api/apps.d.ts +50 -0
- package/dist/api/apps.d.ts.map +1 -0
- package/dist/api/apps.js +62 -0
- package/dist/api/apps.js.map +1 -0
- package/dist/api/billing.d.ts +131 -0
- package/dist/api/billing.d.ts.map +1 -0
- package/dist/api/billing.js +42 -0
- package/dist/api/billing.js.map +1 -0
- package/dist/api/flags.d.ts +73 -0
- package/dist/api/flags.d.ts.map +1 -0
- package/dist/api/flags.js +108 -0
- package/dist/api/flags.js.map +1 -0
- package/dist/api/keys.d.ts +48 -0
- package/dist/api/keys.d.ts.map +1 -0
- package/dist/api/keys.js +87 -0
- package/dist/api/keys.js.map +1 -0
- package/dist/auth/browser-open.d.ts +21 -4
- package/dist/auth/browser-open.d.ts.map +1 -1
- package/dist/auth/browser-open.js +45 -65
- package/dist/auth/browser-open.js.map +1 -1
- package/dist/auth/lock.d.ts +8 -0
- package/dist/auth/lock.d.ts.map +1 -0
- package/dist/auth/lock.js +43 -0
- package/dist/auth/lock.js.map +1 -0
- package/dist/auth/loopback.d.ts +8 -12
- package/dist/auth/loopback.d.ts.map +1 -1
- package/dist/auth/loopback.js +40 -45
- package/dist/auth/loopback.js.map +1 -1
- package/dist/auth/session.d.ts +61 -0
- package/dist/auth/session.d.ts.map +1 -0
- package/dist/auth/session.js +142 -0
- package/dist/auth/session.js.map +1 -0
- package/dist/auth/sign-in.d.ts +67 -0
- package/dist/auth/sign-in.d.ts.map +1 -0
- package/dist/auth/sign-in.js +113 -0
- package/dist/auth/sign-in.js.map +1 -0
- package/dist/auth/store.d.ts +89 -0
- package/dist/auth/store.d.ts.map +1 -0
- package/dist/auth/store.js +214 -0
- package/dist/auth/store.js.map +1 -0
- package/dist/bin.js +0 -2
- package/dist/bin.js.map +1 -1
- package/dist/cli.d.ts +1 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +13 -17
- package/dist/cli.js.map +1 -1
- package/dist/client.d.ts +31 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +67 -0
- package/dist/client.js.map +1 -0
- package/dist/commands/accounts.command.d.ts +4 -0
- package/dist/commands/accounts.command.d.ts.map +1 -0
- package/dist/commands/accounts.command.js +116 -0
- package/dist/commands/accounts.command.js.map +1 -0
- package/dist/commands/app.command.d.ts +3 -2
- package/dist/commands/app.command.d.ts.map +1 -1
- package/dist/commands/app.command.js +132 -233
- package/dist/commands/app.command.js.map +1 -1
- package/dist/commands/auth.command.d.ts +2 -12
- package/dist/commands/auth.command.d.ts.map +1 -1
- package/dist/commands/auth.command.js +147 -20
- package/dist/commands/auth.command.js.map +1 -1
- package/dist/commands/billing.command.d.ts +4 -0
- package/dist/commands/billing.command.d.ts.map +1 -0
- package/dist/commands/billing.command.js +185 -0
- package/dist/commands/billing.command.js.map +1 -0
- package/dist/commands/check.command.d.ts +2 -1
- package/dist/commands/check.command.d.ts.map +1 -1
- package/dist/commands/check.command.js +11 -14
- package/dist/commands/check.command.js.map +1 -1
- package/dist/commands/context.d.ts +58 -0
- package/dist/commands/context.d.ts.map +1 -0
- package/dist/commands/context.js +78 -0
- package/dist/commands/context.js.map +1 -0
- package/dist/commands/flag.command.d.ts +3 -62
- package/dist/commands/flag.command.d.ts.map +1 -1
- package/dist/commands/flag.command.js +181 -864
- package/dist/commands/flag.command.js.map +1 -1
- package/dist/commands/key.command.d.ts +4 -0
- package/dist/commands/key.command.d.ts.map +1 -0
- package/dist/commands/key.command.js +44 -0
- package/dist/commands/key.command.js.map +1 -0
- package/dist/commands/plan.command.d.ts +3 -203
- package/dist/commands/plan.command.d.ts.map +1 -1
- package/dist/commands/plan.command.js +200 -759
- package/dist/commands/plan.command.js.map +1 -1
- package/dist/config.d.ts +19 -101
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +51 -364
- package/dist/config.js.map +1 -1
- package/dist/core/discovery.d.ts +27 -0
- package/dist/core/discovery.d.ts.map +1 -0
- package/dist/core/discovery.js +97 -0
- package/dist/core/discovery.js.map +1 -0
- package/dist/core/errors.d.ts +26 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +33 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/http.d.ts +23 -0
- package/dist/core/http.d.ts.map +1 -0
- package/dist/core/http.js +153 -0
- package/dist/core/http.js.map +1 -0
- package/dist/core/private-files.d.ts +6 -0
- package/dist/core/private-files.d.ts.map +1 -0
- package/dist/core/private-files.js +96 -0
- package/dist/core/private-files.js.map +1 -0
- package/dist/core/routes.d.ts +94 -0
- package/dist/core/routes.d.ts.map +1 -0
- package/dist/core/routes.js +91 -0
- package/dist/core/routes.js.map +1 -0
- package/dist/core/urls.d.ts +15 -0
- package/dist/core/urls.d.ts.map +1 -0
- package/dist/core/urls.js +55 -0
- package/dist/core/urls.js.map +1 -0
- package/dist/gate-rules.d.ts +1 -2
- package/dist/gate-rules.d.ts.map +1 -1
- package/dist/gate-rules.js +4 -5
- package/dist/gate-rules.js.map +1 -1
- package/dist/index.d.ts +23 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -3
- package/dist/index.js.map +1 -1
- package/dist/io.d.ts +21 -0
- package/dist/io.d.ts.map +1 -0
- package/dist/io.js +59 -0
- package/dist/io.js.map +1 -0
- package/dist/money.d.ts +42 -0
- package/dist/money.d.ts.map +1 -0
- package/dist/money.js +112 -0
- package/dist/money.js.map +1 -0
- package/dist/program.d.ts +5 -24
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +25 -113
- package/dist/program.js.map +1 -1
- package/package.json +7 -8
- package/dist/auth/api-client.d.ts +0 -50
- package/dist/auth/api-client.d.ts.map +0 -1
- package/dist/auth/api-client.js +0 -109
- package/dist/auth/api-client.js.map +0 -1
- package/dist/auth/relative-time.d.ts +0 -2
- package/dist/auth/relative-time.d.ts.map +0 -1
- package/dist/auth/relative-time.js +0 -27
- package/dist/auth/relative-time.js.map +0 -1
- package/dist/auth/scopes.d.ts +0 -30
- package/dist/auth/scopes.d.ts.map +0 -1
- package/dist/auth/scopes.js +0 -39
- package/dist/auth/scopes.js.map +0 -1
- package/dist/auth/workspace-client.d.ts +0 -70
- package/dist/auth/workspace-client.d.ts.map +0 -1
- package/dist/auth/workspace-client.js +0 -105
- package/dist/auth/workspace-client.js.map +0 -1
- package/dist/commands/auth/config.command.d.ts +0 -8
- package/dist/commands/auth/config.command.d.ts.map +0 -1
- package/dist/commands/auth/config.command.js +0 -60
- package/dist/commands/auth/config.command.js.map +0 -1
- package/dist/commands/auth/login.command.d.ts +0 -46
- package/dist/commands/auth/login.command.d.ts.map +0 -1
- package/dist/commands/auth/login.command.js +0 -166
- package/dist/commands/auth/login.command.js.map +0 -1
- package/dist/commands/auth/logout.command.d.ts +0 -21
- package/dist/commands/auth/logout.command.d.ts.map +0 -1
- package/dist/commands/auth/logout.command.js +0 -97
- package/dist/commands/auth/logout.command.js.map +0 -1
- package/dist/commands/auth/methods.command.d.ts +0 -68
- package/dist/commands/auth/methods.command.d.ts.map +0 -1
- package/dist/commands/auth/methods.command.js +0 -91
- package/dist/commands/auth/methods.command.js.map +0 -1
- package/dist/commands/auth/status.command.d.ts +0 -18
- package/dist/commands/auth/status.command.d.ts.map +0 -1
- package/dist/commands/auth/status.command.js +0 -104
- package/dist/commands/auth/status.command.js.map +0 -1
- package/dist/commands/auth/use.command.d.ts +0 -17
- package/dist/commands/auth/use.command.d.ts.map +0 -1
- package/dist/commands/auth/use.command.js +0 -47
- package/dist/commands/auth/use.command.js.map +0 -1
- package/dist/commands/branding.command.d.ts +0 -3
- package/dist/commands/branding.command.d.ts.map +0 -1
- package/dist/commands/branding.command.js +0 -42
- package/dist/commands/branding.command.js.map +0 -1
- package/dist/commands/event.command.d.ts +0 -3
- package/dist/commands/event.command.d.ts.map +0 -1
- package/dist/commands/event.command.js +0 -27
- package/dist/commands/event.command.js.map +0 -1
- package/dist/commands/flag-init.command.d.ts +0 -13
- package/dist/commands/flag-init.command.d.ts.map +0 -1
- package/dist/commands/flag-init.command.js +0 -463
- package/dist/commands/flag-init.command.js.map +0 -1
- package/dist/commands/guide.command.d.ts +0 -61
- package/dist/commands/guide.command.d.ts.map +0 -1
- package/dist/commands/guide.command.js +0 -655
- package/dist/commands/guide.command.js.map +0 -1
- package/dist/commands/info.command.d.ts +0 -3
- package/dist/commands/info.command.d.ts.map +0 -1
- package/dist/commands/info.command.js +0 -70
- package/dist/commands/info.command.js.map +0 -1
- package/dist/commands/integration.command.d.ts +0 -35
- package/dist/commands/integration.command.d.ts.map +0 -1
- package/dist/commands/integration.command.js +0 -290
- package/dist/commands/integration.command.js.map +0 -1
- package/dist/commands/journeys.d.ts +0 -43
- package/dist/commands/journeys.d.ts.map +0 -1
- package/dist/commands/journeys.js +0 -221
- package/dist/commands/journeys.js.map +0 -1
- package/dist/commands/ops.command.d.ts +0 -17
- package/dist/commands/ops.command.d.ts.map +0 -1
- package/dist/commands/ops.command.js +0 -129
- package/dist/commands/ops.command.js.map +0 -1
- package/dist/commands/privilege.command.d.ts +0 -16
- package/dist/commands/privilege.command.d.ts.map +0 -1
- package/dist/commands/privilege.command.js +0 -86
- package/dist/commands/privilege.command.js.map +0 -1
- package/dist/commands/role.command.d.ts +0 -3
- package/dist/commands/role.command.d.ts.map +0 -1
- package/dist/commands/role.command.js +0 -101
- package/dist/commands/role.command.js.map +0 -1
- package/dist/commands/runtime-dir.d.ts +0 -16
- package/dist/commands/runtime-dir.d.ts.map +0 -1
- package/dist/commands/runtime-dir.js +0 -18
- package/dist/commands/runtime-dir.js.map +0 -1
- package/dist/commands/setup.command.d.ts +0 -63
- package/dist/commands/setup.command.d.ts.map +0 -1
- package/dist/commands/setup.command.js +0 -148
- package/dist/commands/setup.command.js.map +0 -1
- package/dist/commands/stripe.command.d.ts +0 -29
- package/dist/commands/stripe.command.d.ts.map +0 -1
- package/dist/commands/stripe.command.js +0 -99
- package/dist/commands/stripe.command.js.map +0 -1
- package/dist/commands/tenant.command.d.ts +0 -3
- package/dist/commands/tenant.command.d.ts.map +0 -1
- package/dist/commands/tenant.command.js +0 -135
- package/dist/commands/tenant.command.js.map +0 -1
- package/dist/commands/token.command.d.ts +0 -3
- package/dist/commands/token.command.d.ts.map +0 -1
- package/dist/commands/token.command.js +0 -54
- package/dist/commands/token.command.js.map +0 -1
- package/dist/commands/user.command.d.ts +0 -3
- package/dist/commands/user.command.d.ts.map +0 -1
- package/dist/commands/user.command.js +0 -117
- package/dist/commands/user.command.js.map +0 -1
- package/dist/credentials.d.ts +0 -141
- package/dist/credentials.d.ts.map +0 -1
- package/dist/credentials.js +0 -348
- package/dist/credentials.js.map +0 -1
- package/dist/flag-coherence.d.ts +0 -42
- package/dist/flag-coherence.d.ts.map +0 -1
- package/dist/flag-coherence.js +0 -103
- package/dist/flag-coherence.js.map +0 -1
- package/dist/output.d.ts +0 -17
- package/dist/output.d.ts.map +0 -1
- package/dist/output.js +0 -107
- package/dist/output.js.map +0 -1
- package/dist/preview.d.ts +0 -9
- package/dist/preview.d.ts.map +0 -1
- package/dist/preview.js +0 -63
- package/dist/preview.js.map +0 -1
- package/dist/product-decisions.d.ts +0 -40
- package/dist/product-decisions.d.ts.map +0 -1
- package/dist/product-decisions.js +0 -52
- package/dist/product-decisions.js.map +0 -1
- package/dist/prompt-urls.d.ts +0 -31
- package/dist/prompt-urls.d.ts.map +0 -1
- package/dist/prompt-urls.js +0 -35
- package/dist/prompt-urls.js.map +0 -1
- package/dist/prompts/auth-master-integration-prompt.md +0 -255
- package/dist/prompts/billing/master.md +0 -332
- package/dist/prompts/decisions/feature-control.md +0 -131
- package/dist/prompts/decisions/going-live.md +0 -60
- package/dist/prompts/decisions/login.md +0 -71
- package/dist/prompts/decisions/look-and-feel.md +0 -68
- package/dist/prompts/decisions/payments.md +0 -97
- package/dist/prompts/decisions/roles.md +0 -78
- package/dist/prompts/decisions/teams.md +0 -61
- package/dist/prompts/fit-together.md +0 -97
- package/dist/prompts/flags/master.md +0 -220
- package/dist/prompts/guide-coverage.json +0 -17
- package/dist/prompts/integration-success.md +0 -106
- package/dist/prompts/mcp-tools.json +0 -90
- package/dist/prompts/mechanisms.md +0 -304
- package/dist/prompts/orientation.md +0 -42
- package/dist/prompts/product-decisions.json +0 -338
- package/dist/resolve.d.ts +0 -110
- package/dist/resolve.d.ts.map +0 -1
- package/dist/resolve.js +0 -260
- package/dist/resolve.js.map +0 -1
- package/dist/shape.d.ts +0 -25
- package/dist/shape.d.ts.map +0 -1
- package/dist/shape.js +0 -74
- package/dist/shape.js.map +0 -1
|
@@ -1,655 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { commandsDir } from './runtime-dir.js';
|
|
5
|
-
import { outputSuccess, outputPrompt, outputError } from '../output.js';
|
|
6
|
-
import { AUTH_MASTER_PROMPT_FILENAME, AUTH_MASTER_PROMPT_URL, pluginGuideUrl } from '../prompt-urls.js';
|
|
7
|
-
import { DECISION_DOMAINS, fetchDecisionGuide, registerJourneyCommands, unknownGuideMessage, } from './journeys.js';
|
|
8
|
-
/**
|
|
9
|
-
* Locate the directory containing this module on disk so the bundled prompts
|
|
10
|
-
* (shipped at `dist/prompts/`) can be read relative to it. The `import.meta`
|
|
11
|
-
* resolution is isolated in `runtime-dir.ts` (and stubbed for jest) — see that
|
|
12
|
-
* file for the dual-runtime rationale.
|
|
13
|
-
*
|
|
14
|
-
* The previous in-line `new Function('return import.meta.url')()` trick could
|
|
15
|
-
* never work: a Function-constructor body runs in global *script* scope where
|
|
16
|
-
* `import.meta` is a SyntaxError, so it threw even under ESM and silently fell
|
|
17
|
-
* through to a `process.cwd()`-based branch — which only resolved correctly by
|
|
18
|
-
* accident when the CWD happened to be the package root.
|
|
19
|
-
*/
|
|
20
|
-
function thisDir() {
|
|
21
|
-
return commandsDir;
|
|
22
|
-
}
|
|
23
|
-
const GUIDE_REPOS = {
|
|
24
|
-
react: 'bridge-react/main',
|
|
25
|
-
svelte: 'bridge-svelte/main',
|
|
26
|
-
angular: 'bridge-angular/main',
|
|
27
|
-
nextjs: 'bridge-nextjs/main',
|
|
28
|
-
express: 'bridge-express/main',
|
|
29
|
-
nestjs: 'bridge-nestjs/main',
|
|
30
|
-
};
|
|
31
|
-
/**
|
|
32
|
-
* Which per-framework guides exist, from `prompts/guide-coverage.json`.
|
|
33
|
-
*
|
|
34
|
-
* That file is the one copy (TBP-706): this command lists it, and the Bridge
|
|
35
|
-
* MCP server reads the same file from GitHub for its "no such guide" answers.
|
|
36
|
-
* Before it, the CLI, the MCP server and the auth master prompt gave three
|
|
37
|
-
* different answers, and the CLI's was the most out of date.
|
|
38
|
-
*/
|
|
39
|
-
export function loadGuideCoverage() {
|
|
40
|
-
const here = thisDir();
|
|
41
|
-
const candidates = [
|
|
42
|
-
join(here, '..', 'prompts', 'guide-coverage.json'),
|
|
43
|
-
join(here, '..', '..', 'prompts', 'guide-coverage.json'),
|
|
44
|
-
];
|
|
45
|
-
for (const path of candidates) {
|
|
46
|
-
try {
|
|
47
|
-
const { aliases, frameworks, topics } = JSON.parse(readFileSync(path, 'utf-8'));
|
|
48
|
-
return { aliases, frameworks, topics: topics ?? {} };
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
/* try next */
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
throw new Error(`guide-coverage.json not found. Tried: ${candidates.join(', ')}`);
|
|
55
|
-
}
|
|
56
|
-
const FLAGS_FRAMEWORKS = ['svelte', 'react', 'nextjs', 'angular', 'nestjs', 'express'];
|
|
57
|
-
const BILLING_FRAMEWORKS = ['svelte', 'react', 'nextjs', 'angular', 'nestjs', 'express'];
|
|
58
|
-
/**
|
|
59
|
-
* Per-framework prompts are the responsibility of each plugin repo
|
|
60
|
-
* (`bridge-<framework>/mcp/feature-flags-prompt.md` and `mcp/billing-prompt.md`).
|
|
61
|
-
* Bridge-cli only bundles the generic master orchestrators
|
|
62
|
-
* (`prompts/flags/master.md`, `prompts/billing/master.md`) — never per-framework
|
|
63
|
-
* copies, which would diverge from the plugin's source of truth.
|
|
64
|
-
*
|
|
65
|
-
* Both alias sets are now exhaustive: every framework routes through fetchGuide
|
|
66
|
-
* to its plugin repo's mcp/ folder. If the plugin repo doesn't have the file
|
|
67
|
-
* yet, the user gets a clear "HTTP 404 — file may not exist yet in the plugin
|
|
68
|
-
* repo" error pointing them at the right destination.
|
|
69
|
-
*/
|
|
70
|
-
const FLAGS_ALIAS_FRAMEWORKS = new Set(FLAGS_FRAMEWORKS);
|
|
71
|
-
const BILLING_ALIAS_FRAMEWORKS = new Set(BILLING_FRAMEWORKS);
|
|
72
|
-
function guideFilename(feature) {
|
|
73
|
-
if (!feature)
|
|
74
|
-
return 'integration-prompt.md';
|
|
75
|
-
return `${feature}-prompt.md`;
|
|
76
|
-
}
|
|
77
|
-
const guideCache = new Map();
|
|
78
|
-
const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
|
79
|
-
export function registerGuideCommands(program) {
|
|
80
|
-
const guide = program.command('guide')
|
|
81
|
-
.description('Integration guides — run with no arguments for the master integration prompt')
|
|
82
|
-
.option('--json', 'Emit a JSON envelope instead of the raw markdown prompt')
|
|
83
|
-
.action(async (_opts, command) => {
|
|
84
|
-
try {
|
|
85
|
-
const opts = command.optsWithGlobals();
|
|
86
|
-
// TBP-712: `bridge guide <anything unknown>` used to fall through to
|
|
87
|
-
// here and print the master as if the lookup had worked. Name what
|
|
88
|
-
// exists instead.
|
|
89
|
-
const [unknown] = command.args;
|
|
90
|
-
if (unknown) {
|
|
91
|
-
throw Object.assign(new Error(unknownGuideMessage(unknown, Object.keys(GUIDE_REPOS))), {
|
|
92
|
-
code: 'GUIDE_NOT_FOUND',
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
// No login here: reading a guide needs no session, and the commands
|
|
96
|
-
// the guide goes on to run ask for one themselves (TBP-706).
|
|
97
|
-
const guide = await fetchMasterPrompt();
|
|
98
|
-
if (opts.json)
|
|
99
|
-
outputSuccess({ guide });
|
|
100
|
-
else
|
|
101
|
-
outputPrompt(guide);
|
|
102
|
-
}
|
|
103
|
-
catch (err) {
|
|
104
|
-
outputError(err);
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
const technologies = [...Object.keys(GUIDE_REPOS), 'custom'];
|
|
108
|
-
guide.command('list')
|
|
109
|
-
.description('List available integration guides and feature guides')
|
|
110
|
-
.action(() => {
|
|
111
|
-
const { aliases, frameworks, topics } = loadGuideCoverage();
|
|
112
|
-
outputSuccess({ technologies, features: frameworks, aliases, topics });
|
|
113
|
-
});
|
|
114
|
-
// TBP-206 — `bridge guide flags [--framework <name>]`
|
|
115
|
-
guide.command('flags')
|
|
116
|
-
.description('Feature Flags 2.0 integration prompt (use --framework for per-framework guide)')
|
|
117
|
-
.option('--framework <name>', `Framework: ${FLAGS_FRAMEWORKS.join(' | ')} (auto-detected from package.json when omitted)`)
|
|
118
|
-
.option('--cwd <path>', 'Project dir used for framework detection', process.cwd())
|
|
119
|
-
.option('--json', 'Emit a JSON envelope instead of the raw markdown prompt')
|
|
120
|
-
.action(async (_opts, command) => {
|
|
121
|
-
try {
|
|
122
|
-
const opts = command.optsWithGlobals();
|
|
123
|
-
// Always return the master when no --framework flag is given.
|
|
124
|
-
// The master orchestrates discovery then calls bridge guide flags --framework <name> in Step 4.
|
|
125
|
-
const content = opts.framework
|
|
126
|
-
? await fetchGuide(await resolveFlagsFramework(opts.framework, opts.cwd ?? process.cwd()) ?? opts.framework, 'feature-flags')
|
|
127
|
-
: await fetchFlagsMasterPrompt();
|
|
128
|
-
if (opts.json) {
|
|
129
|
-
outputSuccess({
|
|
130
|
-
feature: 'flags',
|
|
131
|
-
framework: opts.framework ?? 'generic',
|
|
132
|
-
guide: content,
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
outputPrompt(content);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
catch (err) {
|
|
140
|
-
outputError(err);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
// Billing 2.0 — `bridge guide billing [--framework <name>]`. Mirrors the
|
|
144
|
-
// flags command shape. Auto-detects from package.json when --framework is
|
|
145
|
-
// omitted; falls back to `prompts/billing/master.md` when detection fails.
|
|
146
|
-
guide.command('billing')
|
|
147
|
-
.description('Billing 2.0 integration prompt (use --framework for per-framework guide)')
|
|
148
|
-
.option('--framework <name>', `Framework: ${BILLING_FRAMEWORKS.join(' | ')} (auto-detected from package.json when omitted)`)
|
|
149
|
-
.option('--cwd <path>', 'Project dir used for framework detection', process.cwd())
|
|
150
|
-
.option('--json', 'Emit a JSON envelope instead of the raw markdown prompt')
|
|
151
|
-
.action(async (_opts, command) => {
|
|
152
|
-
try {
|
|
153
|
-
const opts = command.optsWithGlobals();
|
|
154
|
-
// Always return the master when no --framework flag is given.
|
|
155
|
-
// The master orchestrates discovery, pricing-model elicitation, and
|
|
156
|
-
// calls `bridge guide billing --framework <name>` itself in Step 4.
|
|
157
|
-
const content = opts.framework
|
|
158
|
-
? await fetchGuide(await resolveBillingFramework(opts.framework, opts.cwd ?? process.cwd()) ?? opts.framework, 'billing')
|
|
159
|
-
: await fetchBillingMasterPrompt();
|
|
160
|
-
if (opts.json) {
|
|
161
|
-
outputSuccess({
|
|
162
|
-
feature: 'billing',
|
|
163
|
-
framework: opts.framework ?? 'generic',
|
|
164
|
-
guide: content,
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
outputPrompt(content);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
catch (err) {
|
|
172
|
-
outputError(err);
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
for (const tech of Object.keys(GUIDE_REPOS)) {
|
|
176
|
-
guide.command(tech)
|
|
177
|
-
.argument('[feature]', 'Feature guide — `bridge guide list` shows which exist per framework')
|
|
178
|
-
.description(`Integration guide for ${tech}`)
|
|
179
|
-
.option('--json', 'Emit a JSON envelope instead of the raw markdown prompt')
|
|
180
|
-
.action(async (feature, _opts, command) => {
|
|
181
|
-
try {
|
|
182
|
-
const opts = command.optsWithGlobals();
|
|
183
|
-
// `flags` is a synonym for `feature-flags`. Both routes — `bridge
|
|
184
|
-
// guide flags --framework <name>` and `bridge guide <name> flags` —
|
|
185
|
-
// resolve to the same plugin-repo source of truth at
|
|
186
|
-
// `bridge-<name>/mcp/feature-flags-prompt.md`. `billing` follows the
|
|
187
|
-
// same pattern. There is no per-framework bundled fallback by design.
|
|
188
|
-
let content;
|
|
189
|
-
let resolvedFeature = feature ?? 'default';
|
|
190
|
-
if (feature === 'flags' && FLAGS_FRAMEWORKS.includes(tech)) {
|
|
191
|
-
content = await fetchGuide(tech, 'feature-flags');
|
|
192
|
-
resolvedFeature = 'flags';
|
|
193
|
-
}
|
|
194
|
-
else if (feature === 'billing' && BILLING_FRAMEWORKS.includes(tech)) {
|
|
195
|
-
content = await fetchGuide(tech, 'billing');
|
|
196
|
-
resolvedFeature = 'billing';
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
content = await fetchGuide(tech, feature);
|
|
200
|
-
if (!feature) {
|
|
201
|
-
const preface = authModesPreface(tech, loadGuideCoverage().frameworks);
|
|
202
|
-
if (preface)
|
|
203
|
-
content = `${preface}\n\n${content}`;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
if (opts.json) {
|
|
207
|
-
outputSuccess({ technology: tech, feature: resolvedFeature, guide: content });
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
outputPrompt(content);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
catch (err) {
|
|
214
|
-
outputError(err);
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
// TBP-712 — the seven journeys by name, composed like the MCP prompts.
|
|
219
|
-
registerJourneyCommands(guide, {
|
|
220
|
-
decision: fetchDecisionGuide,
|
|
221
|
-
master: (m) => m === 'auth' ? fetchMasterPrompt() : m === 'flags' ? fetchFlagsMasterPrompt() : fetchBillingMasterPrompt(),
|
|
222
|
-
frameworkGuide: (tech, feature) => fetchGuide(tech, feature),
|
|
223
|
-
coverage: () => loadGuideCoverage().frameworks,
|
|
224
|
-
}, resolveFlagsFramework);
|
|
225
|
-
guide.command('custom')
|
|
226
|
-
.description('Universal integration guide for any technology using REST API + JWKS')
|
|
227
|
-
.option('--json', 'Emit a JSON envelope instead of the raw markdown prompt')
|
|
228
|
-
.action((_opts, command) => {
|
|
229
|
-
const opts = command.optsWithGlobals();
|
|
230
|
-
if (opts.json)
|
|
231
|
-
outputSuccess({ technology: 'custom', guide: CUSTOM_GUIDE });
|
|
232
|
-
else
|
|
233
|
-
outputPrompt(CUSTOM_GUIDE);
|
|
234
|
-
});
|
|
235
|
-
// TBP-705 — the mechanisms every guide builds on: the server decides, a POST
|
|
236
|
-
// increments the limit, counter vs gauge, the three UI levels, the four
|
|
237
|
-
// customisation rungs, self-reported usage. The MCP server serves the same
|
|
238
|
-
// file (bridge-cli/main/bridge-cli/prompts/mechanisms.md) as
|
|
239
|
-
// `get_integration_guide topic=mechanisms`.
|
|
240
|
-
guide.command('mechanisms')
|
|
241
|
-
.description('How Bridge works: limits, counter vs gauge, UI levels, customisation rungs')
|
|
242
|
-
.option('--json', 'Emit a JSON envelope instead of the raw markdown prompt')
|
|
243
|
-
.action(async (_opts, command) => {
|
|
244
|
-
try {
|
|
245
|
-
const opts = command.optsWithGlobals();
|
|
246
|
-
const content = await fetchMechanismsGuide();
|
|
247
|
-
if (opts.json)
|
|
248
|
-
outputSuccess({ topic: 'mechanisms', guide: content });
|
|
249
|
-
else
|
|
250
|
-
outputPrompt(content);
|
|
251
|
-
}
|
|
252
|
-
catch (err) {
|
|
253
|
-
outputError(err);
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
// TBP-540 — the level-1 map: what Bridge does, one short paragraph per
|
|
257
|
-
// area, for an agent to relay as is. The MCP server serves the same file.
|
|
258
|
-
guide.command('orientation')
|
|
259
|
-
.description('What Bridge does: a short map to relay to a developer, with where to go next')
|
|
260
|
-
.option('--json', 'Emit a JSON envelope instead of the raw markdown prompt')
|
|
261
|
-
.action(async (_opts, command) => {
|
|
262
|
-
try {
|
|
263
|
-
const opts = command.optsWithGlobals();
|
|
264
|
-
const content = await fetchOrientationGuide();
|
|
265
|
-
if (opts.json)
|
|
266
|
-
outputSuccess({ topic: 'orientation', guide: content });
|
|
267
|
-
else
|
|
268
|
-
outputPrompt(content);
|
|
269
|
-
}
|
|
270
|
-
catch (err) {
|
|
271
|
-
outputError(err);
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
// TBP-705 — how roles, plans, limits and flags fit together: the one rule
|
|
275
|
-
// every decision guide and the orientation point to. The MCP server serves
|
|
276
|
-
// the same file (bridge-cli/main/bridge-cli/prompts/fit-together.md).
|
|
277
|
-
guide.command('fit-together')
|
|
278
|
-
.description('How roles, plans, limits and flags fit together: the rule every decision guide builds on')
|
|
279
|
-
.option('--json', 'Emit a JSON envelope instead of the raw markdown prompt')
|
|
280
|
-
.action(async (_opts, command) => {
|
|
281
|
-
try {
|
|
282
|
-
const opts = command.optsWithGlobals();
|
|
283
|
-
const content = await fetchFitTogetherGuide();
|
|
284
|
-
if (opts.json)
|
|
285
|
-
outputSuccess({ topic: 'fit-together', guide: content });
|
|
286
|
-
else
|
|
287
|
-
outputPrompt(content);
|
|
288
|
-
}
|
|
289
|
-
catch (err) {
|
|
290
|
-
outputError(err);
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
// TBP-540 — each decision guide on its own. Five of the seven also open a
|
|
294
|
-
// journey (`bridge guide add-login` …); `roles` and `look-and-feel` open
|
|
295
|
-
// none, so without this command the CLI could not reach them.
|
|
296
|
-
guide.command('decision')
|
|
297
|
-
.argument('<domain>', `One of: ${DECISION_DOMAINS.join(', ')}`)
|
|
298
|
-
.description('A decision guide: the questions to ask the developer and the defaults to apply')
|
|
299
|
-
.option('--json', 'Emit a JSON envelope instead of the raw markdown prompt')
|
|
300
|
-
.action(async (domain, _opts, command) => {
|
|
301
|
-
try {
|
|
302
|
-
const opts = command.optsWithGlobals();
|
|
303
|
-
if (!DECISION_DOMAINS.includes(domain)) {
|
|
304
|
-
throw Object.assign(new Error(`No decision guide named '${domain}'. Decision guides: ${DECISION_DOMAINS.join(', ')}.`), { code: 'GUIDE_NOT_FOUND' });
|
|
305
|
-
}
|
|
306
|
-
const content = await fetchDecisionGuide(domain);
|
|
307
|
-
if (!content) {
|
|
308
|
-
throw Object.assign(new Error(`The '${domain}' decision guide could not be read.`), { code: 'GUIDE_NOT_FOUND' });
|
|
309
|
-
}
|
|
310
|
-
if (opts.json)
|
|
311
|
-
outputSuccess({ domain, guide: content });
|
|
312
|
-
else
|
|
313
|
-
outputPrompt(content);
|
|
314
|
-
}
|
|
315
|
-
catch (err) {
|
|
316
|
-
outputError(err);
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
guide.command('integration-success')
|
|
320
|
-
.description('Integration success message template — output at the end of a completed integration')
|
|
321
|
-
.option('--json', 'Emit a JSON envelope instead of the raw markdown prompt')
|
|
322
|
-
.action(async (_opts, command) => {
|
|
323
|
-
try {
|
|
324
|
-
const opts = command.optsWithGlobals();
|
|
325
|
-
const content = await fetchIntegrationSuccess();
|
|
326
|
-
if (opts.json)
|
|
327
|
-
outputSuccess({ guide: content });
|
|
328
|
-
else
|
|
329
|
-
outputPrompt(content);
|
|
330
|
-
}
|
|
331
|
-
catch (err) {
|
|
332
|
-
outputError(err);
|
|
333
|
-
}
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* TBP-540 (folds TBP-599) — `bridge guide <framework>` prints the HOSTED
|
|
338
|
-
* sign-in guide, and used to say nothing about the other mode. A developer who
|
|
339
|
-
* wanted in-app forms never learned `bridge guide svelte sdk-auth` existed,
|
|
340
|
-
* hand-wrote a login form against `/auth/login` (a 404) and shipped it. So a
|
|
341
|
-
* framework that has an SDK-auth guide opens by naming both modes; one that
|
|
342
|
-
* has none (the backends) is unchanged.
|
|
343
|
-
*
|
|
344
|
-
* TBP-807 — the per-framework guide now sets up in-app (SDK) sign-in itself,
|
|
345
|
-
* so there is no mode to pick; the preface only points at the guide for the
|
|
346
|
-
* sign-in pages and their design.
|
|
347
|
-
*/
|
|
348
|
-
export function authModesPreface(tech, coverage) {
|
|
349
|
-
if (!coverage[tech]?.includes('sdk-auth'))
|
|
350
|
-
return null;
|
|
351
|
-
return [
|
|
352
|
-
`> **The sign-in pages, matching the app's design:** \`bridge guide ${tech} sdk-auth\`. The SDK handles password, magic link, passkeys, SSO, two-factor and workspace choice inside the app's own layout.`,
|
|
353
|
-
'>',
|
|
354
|
-
'> Never write a login form or call Bridge auth endpoints by hand: the SDK does it, and a guessed endpoint fails as a silent 404.',
|
|
355
|
-
].join('\n');
|
|
356
|
-
}
|
|
357
|
-
/** The per-framework guides that exist for `tech`, or null when the list cannot be read. */
|
|
358
|
-
function guidesFor(tech) {
|
|
359
|
-
try {
|
|
360
|
-
return loadGuideCoverage().frameworks[tech] ?? null;
|
|
361
|
-
}
|
|
362
|
-
catch {
|
|
363
|
-
return null;
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
export const MECHANISMS_PROMPT_FILENAME = 'mechanisms.md';
|
|
367
|
-
export const ORIENTATION_PROMPT_FILENAME = 'orientation.md';
|
|
368
|
-
export const FIT_TOGETHER_PROMPT_FILENAME = 'fit-together.md';
|
|
369
|
-
/** The mechanisms page, bundled with the CLI (`BRIDGE_GUIDE_LOCAL_DIR` wins in development). */
|
|
370
|
-
export async function fetchMechanismsGuide() {
|
|
371
|
-
return fetchBundledPage(MECHANISMS_PROMPT_FILENAME, 'mechanisms guide');
|
|
372
|
-
}
|
|
373
|
-
/** The orientation map, bundled with the CLI (`BRIDGE_GUIDE_LOCAL_DIR` wins in development). */
|
|
374
|
-
export async function fetchOrientationGuide() {
|
|
375
|
-
return fetchBundledPage(ORIENTATION_PROMPT_FILENAME, 'orientation guide');
|
|
376
|
-
}
|
|
377
|
-
/** How roles, plans, limits and flags fit together, bundled with the CLI (`BRIDGE_GUIDE_LOCAL_DIR` wins in development). */
|
|
378
|
-
export async function fetchFitTogetherGuide() {
|
|
379
|
-
return fetchBundledPage(FIT_TOGETHER_PROMPT_FILENAME, 'fit-together guide');
|
|
380
|
-
}
|
|
381
|
-
async function fetchBundledPage(filename, label) {
|
|
382
|
-
const localDir = process.env.BRIDGE_GUIDE_LOCAL_DIR;
|
|
383
|
-
if (localDir) {
|
|
384
|
-
try {
|
|
385
|
-
return await readFile(join(localDir, 'bridge-cli', 'bridge-cli', 'prompts', filename), 'utf-8');
|
|
386
|
-
}
|
|
387
|
-
catch {
|
|
388
|
-
/* fall through to bundled */
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
const here = thisDir();
|
|
392
|
-
const candidates = [
|
|
393
|
-
join(here, '..', 'prompts', filename),
|
|
394
|
-
join(here, '..', '..', 'prompts', filename),
|
|
395
|
-
];
|
|
396
|
-
for (const path of candidates) {
|
|
397
|
-
try {
|
|
398
|
-
return await readFile(path, 'utf-8');
|
|
399
|
-
}
|
|
400
|
-
catch {
|
|
401
|
-
/* try next */
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
throw new Error(`${label} not found. Tried: ${candidates.join(', ')}`);
|
|
405
|
-
}
|
|
406
|
-
async function fetchIntegrationSuccess() {
|
|
407
|
-
const here = thisDir();
|
|
408
|
-
const candidates = [
|
|
409
|
-
join(here, '..', 'prompts', 'integration-success.md'),
|
|
410
|
-
join(here, '..', '..', 'prompts', 'integration-success.md'),
|
|
411
|
-
];
|
|
412
|
-
for (const path of candidates) {
|
|
413
|
-
try {
|
|
414
|
-
return await readFile(path, 'utf-8');
|
|
415
|
-
}
|
|
416
|
-
catch {
|
|
417
|
-
/* try next */
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
throw new Error(`integration-success prompt not found. Tried: ${candidates.join(', ')}`);
|
|
421
|
-
}
|
|
422
|
-
/**
|
|
423
|
-
* Detect framework from package.json so `bridge guide flags` can be called
|
|
424
|
-
* bare. Same heuristic order as `bridge flag init`.
|
|
425
|
-
*/
|
|
426
|
-
export async function resolveFlagsFramework(override, cwd) {
|
|
427
|
-
if (override) {
|
|
428
|
-
if (!FLAGS_FRAMEWORKS.includes(override)) {
|
|
429
|
-
throw new Error(`Unknown --framework: ${override}. Must be one of ${FLAGS_FRAMEWORKS.join(', ')}.`);
|
|
430
|
-
}
|
|
431
|
-
return override;
|
|
432
|
-
}
|
|
433
|
-
try {
|
|
434
|
-
const raw = await readFile(join(cwd, 'package.json'), 'utf-8');
|
|
435
|
-
const pkg = JSON.parse(raw);
|
|
436
|
-
const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
|
|
437
|
-
if ('next' in deps)
|
|
438
|
-
return 'nextjs';
|
|
439
|
-
if ('@sveltejs/kit' in deps || 'svelte' in deps)
|
|
440
|
-
return 'svelte';
|
|
441
|
-
if ('@nestjs/core' in deps)
|
|
442
|
-
return 'nestjs';
|
|
443
|
-
if ('@angular/core' in deps)
|
|
444
|
-
return 'angular';
|
|
445
|
-
if ('react' in deps)
|
|
446
|
-
return 'react';
|
|
447
|
-
if ('express' in deps)
|
|
448
|
-
return 'express';
|
|
449
|
-
}
|
|
450
|
-
catch {
|
|
451
|
-
/* no package.json — fall through */
|
|
452
|
-
}
|
|
453
|
-
return undefined;
|
|
454
|
-
}
|
|
455
|
-
/**
|
|
456
|
-
* Bridge-cli only bundles the generic master orchestrators. Per-framework
|
|
457
|
-
* prompts always come from the plugin repos' mcp/ folder via `fetchGuide`.
|
|
458
|
-
*/
|
|
459
|
-
export async function fetchFlagsMasterPrompt() {
|
|
460
|
-
return fetchBundledMaster('flags');
|
|
461
|
-
}
|
|
462
|
-
export async function fetchBillingMasterPrompt() {
|
|
463
|
-
return fetchBundledMaster('billing');
|
|
464
|
-
}
|
|
465
|
-
async function fetchBundledMaster(feature) {
|
|
466
|
-
const filename = 'master.md';
|
|
467
|
-
// Local override for development (same env var as the rest of the guides).
|
|
468
|
-
const localDir = process.env.BRIDGE_GUIDE_LOCAL_DIR;
|
|
469
|
-
if (localDir) {
|
|
470
|
-
const localPath = join(localDir, 'bridge-cli', 'bridge-cli', 'prompts', feature, filename);
|
|
471
|
-
try {
|
|
472
|
-
return await readFile(localPath, 'utf-8');
|
|
473
|
-
}
|
|
474
|
-
catch {
|
|
475
|
-
/* fall through to bundled */
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
// Two possible runtime locations:
|
|
479
|
-
// - Built CLI: dist/commands/guide.command.js → ../prompts/<feature>/master.md
|
|
480
|
-
// - Jest (ts-jest, run from src/): src/commands/guide.command.ts → ../../prompts/<feature>/master.md
|
|
481
|
-
const here = thisDir();
|
|
482
|
-
const candidates = [
|
|
483
|
-
join(here, '..', 'prompts', feature, filename),
|
|
484
|
-
join(here, '..', '..', 'prompts', feature, filename),
|
|
485
|
-
];
|
|
486
|
-
for (const path of candidates) {
|
|
487
|
-
try {
|
|
488
|
-
return await readFile(path, 'utf-8');
|
|
489
|
-
}
|
|
490
|
-
catch {
|
|
491
|
-
/* try next */
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
const label = feature === 'flags' ? 'Flags' : 'Billing';
|
|
495
|
-
throw new Error(`${label} master prompt not found. Tried: ${candidates.join(', ')}`);
|
|
496
|
-
}
|
|
497
|
-
/**
|
|
498
|
-
* Detect framework for Billing 2.0. Mirrors resolveFlagsFramework — same
|
|
499
|
-
* package.json heuristic order, validated against BILLING_FRAMEWORKS.
|
|
500
|
-
*/
|
|
501
|
-
export async function resolveBillingFramework(override, cwd) {
|
|
502
|
-
if (override) {
|
|
503
|
-
if (!BILLING_FRAMEWORKS.includes(override)) {
|
|
504
|
-
throw new Error(`Unknown --framework: ${override}. Must be one of ${BILLING_FRAMEWORKS.join(', ')}.`);
|
|
505
|
-
}
|
|
506
|
-
return override;
|
|
507
|
-
}
|
|
508
|
-
try {
|
|
509
|
-
const raw = await readFile(join(cwd, 'package.json'), 'utf-8');
|
|
510
|
-
const pkg = JSON.parse(raw);
|
|
511
|
-
const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
|
|
512
|
-
if ('next' in deps)
|
|
513
|
-
return 'nextjs';
|
|
514
|
-
if ('@sveltejs/kit' in deps || 'svelte' in deps)
|
|
515
|
-
return 'svelte';
|
|
516
|
-
if ('@nestjs/core' in deps)
|
|
517
|
-
return 'nestjs';
|
|
518
|
-
if ('@angular/core' in deps)
|
|
519
|
-
return 'angular';
|
|
520
|
-
if ('react' in deps)
|
|
521
|
-
return 'react';
|
|
522
|
-
if ('express' in deps)
|
|
523
|
-
return 'express';
|
|
524
|
-
}
|
|
525
|
-
catch {
|
|
526
|
-
/* no package.json — fall through */
|
|
527
|
-
}
|
|
528
|
-
return undefined;
|
|
529
|
-
}
|
|
530
|
-
async function fetchGuide(tech, feature) {
|
|
531
|
-
const cacheKey = feature ? `${tech}:${feature}` : tech;
|
|
532
|
-
const cached = guideCache.get(cacheKey);
|
|
533
|
-
if (cached && Date.now() - cached.fetchedAt < CACHE_TTL_MS) {
|
|
534
|
-
return cached.content;
|
|
535
|
-
}
|
|
536
|
-
const repoPrefix = GUIDE_REPOS[tech];
|
|
537
|
-
if (!repoPrefix)
|
|
538
|
-
throw new Error(`Unknown technology: ${tech}`);
|
|
539
|
-
const filename = guideFilename(feature);
|
|
540
|
-
// Local override for development: reads from filesystem before hitting GitHub
|
|
541
|
-
const localDir = process.env.BRIDGE_GUIDE_LOCAL_DIR;
|
|
542
|
-
if (localDir) {
|
|
543
|
-
const localPath = join(localDir, `bridge-${tech}`, 'mcp', filename);
|
|
544
|
-
try {
|
|
545
|
-
const content = await readFile(localPath, 'utf-8');
|
|
546
|
-
guideCache.set(cacheKey, { content, fetchedAt: Date.now() });
|
|
547
|
-
return content;
|
|
548
|
-
}
|
|
549
|
-
catch {
|
|
550
|
-
// Local file not found — fall through to remote fetch
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
const url = pluginGuideUrl(repoPrefix, filename);
|
|
554
|
-
const response = await fetch(url);
|
|
555
|
-
if (!response.ok) {
|
|
556
|
-
const label = feature ? `${tech}/${feature}` : tech;
|
|
557
|
-
// TBP-540 — a failed lookup names what does exist, so the next try is right.
|
|
558
|
-
const available = response.status === 404 ? guidesFor(tech) : null;
|
|
559
|
-
throw Object.assign(new Error(`Guide not available for ${label} (HTTP ${response.status}). ` +
|
|
560
|
-
(available
|
|
561
|
-
? `Guides for ${tech}: ${available.join(', ')} (\`bridge guide ${tech} <guide>\`; \`bridge guide list\` shows every framework).`
|
|
562
|
-
: 'The prompt file may not exist yet in the plugin repo.')), response.status === 404 ? { code: 'GUIDE_NOT_FOUND' } : {});
|
|
563
|
-
}
|
|
564
|
-
const content = await response.text();
|
|
565
|
-
guideCache.set(cacheKey, { content, fetchedAt: Date.now() });
|
|
566
|
-
return content;
|
|
567
|
-
}
|
|
568
|
-
async function fetchMasterPrompt() {
|
|
569
|
-
const filename = AUTH_MASTER_PROMPT_FILENAME;
|
|
570
|
-
// Local override for development
|
|
571
|
-
const localDir = process.env.BRIDGE_GUIDE_LOCAL_DIR;
|
|
572
|
-
if (localDir) {
|
|
573
|
-
const localPath = join(localDir, 'bridge-cli', 'bridge-cli', 'prompts', filename);
|
|
574
|
-
try {
|
|
575
|
-
return await readFile(localPath, 'utf-8');
|
|
576
|
-
}
|
|
577
|
-
catch { /* fall through */ }
|
|
578
|
-
}
|
|
579
|
-
// Resolved-on-disk fallback (when the CLI runs from its own checkout).
|
|
580
|
-
const here = thisDir();
|
|
581
|
-
const candidates = [
|
|
582
|
-
join(here, '..', 'prompts', filename),
|
|
583
|
-
join(here, '..', '..', 'prompts', filename),
|
|
584
|
-
];
|
|
585
|
-
for (const path of candidates) {
|
|
586
|
-
try {
|
|
587
|
-
return await readFile(path, 'utf-8');
|
|
588
|
-
}
|
|
589
|
-
catch {
|
|
590
|
-
/* try next */
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
// Remote fetch
|
|
594
|
-
const response = await fetch(AUTH_MASTER_PROMPT_URL);
|
|
595
|
-
if (!response.ok) {
|
|
596
|
-
throw new Error(`Auth master integration prompt not available (HTTP ${response.status})`);
|
|
597
|
-
}
|
|
598
|
-
return response.text();
|
|
599
|
-
}
|
|
600
|
-
const CUSTOM_GUIDE = `# Universal Bridge Integration Guide
|
|
601
|
-
|
|
602
|
-
This guide covers integrating Bridge into any language/framework using the REST API directly.
|
|
603
|
-
|
|
604
|
-
## 1. Authentication — JWKS-based JWT Verification
|
|
605
|
-
|
|
606
|
-
Bridge issues JWTs signed with PS256. Verify them using your app's JWKS endpoint:
|
|
607
|
-
|
|
608
|
-
\`\`\`
|
|
609
|
-
GET https://api.thebridge.dev/v1/account/app/.well-known/jwks.json
|
|
610
|
-
\`\`\`
|
|
611
|
-
|
|
612
|
-
Use a JWT library in your language that supports JWKS auto-refresh:
|
|
613
|
-
- Node.js: \`jose\`
|
|
614
|
-
- Python: \`PyJWKClient\` from \`PyJWT\`
|
|
615
|
-
- Go: \`github.com/lestrrat-go/jwx\`
|
|
616
|
-
- Java: \`com.nimbusds:nimbus-jose-jwt\`
|
|
617
|
-
- Ruby: \`jwt\` gem
|
|
618
|
-
|
|
619
|
-
## 2. Token Structure
|
|
620
|
-
|
|
621
|
-
The access token contains:
|
|
622
|
-
- \`sub\` — User ID
|
|
623
|
-
- \`tid\` — Tenant ID
|
|
624
|
-
- \`role\` — User's role
|
|
625
|
-
- \`privileges\` — Array of privilege strings
|
|
626
|
-
- \`plan\` — Tenant's plan key
|
|
627
|
-
|
|
628
|
-
## 3. Feature Flags
|
|
629
|
-
|
|
630
|
-
Evaluate flags server-side for a signed-in user (the Bridge evaluates; you get values).
|
|
631
|
-
This is not the endpoint the SDKs load flag rules from — that one returns the rules
|
|
632
|
-
for local evaluation, and the SDKs call it for you:
|
|
633
|
-
\`\`\`
|
|
634
|
-
POST https://api.thebridge.dev/cloud-views/flags/bulkEvaluate/{appId}
|
|
635
|
-
Authorization: Bearer <user-access-token>
|
|
636
|
-
\`\`\`
|
|
637
|
-
|
|
638
|
-
## 4. User/Tenant Management
|
|
639
|
-
|
|
640
|
-
Use the Bridge REST API with an API token:
|
|
641
|
-
\`\`\`
|
|
642
|
-
GET https://api.thebridge.dev/v1/account/tenant
|
|
643
|
-
x-api-key: <your-api-token>
|
|
644
|
-
\`\`\`
|
|
645
|
-
|
|
646
|
-
## 5. Webhooks
|
|
647
|
-
|
|
648
|
-
Configure a webhook URL in your app settings to receive events:
|
|
649
|
-
- User created/updated/deleted
|
|
650
|
-
- Tenant created/updated
|
|
651
|
-
- Subscription changes
|
|
652
|
-
|
|
653
|
-
See the Bridge API documentation for the full webhook payload format.
|
|
654
|
-
`;
|
|
655
|
-
//# sourceMappingURL=guide.command.js.map
|