@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.
Files changed (293) hide show
  1. package/README.md +93 -460
  2. package/dist/api/accounts.d.ts +69 -0
  3. package/dist/api/accounts.d.ts.map +1 -0
  4. package/dist/api/accounts.js +42 -0
  5. package/dist/api/accounts.js.map +1 -0
  6. package/dist/api/apps.d.ts +50 -0
  7. package/dist/api/apps.d.ts.map +1 -0
  8. package/dist/api/apps.js +62 -0
  9. package/dist/api/apps.js.map +1 -0
  10. package/dist/api/billing.d.ts +131 -0
  11. package/dist/api/billing.d.ts.map +1 -0
  12. package/dist/api/billing.js +42 -0
  13. package/dist/api/billing.js.map +1 -0
  14. package/dist/api/flags.d.ts +73 -0
  15. package/dist/api/flags.d.ts.map +1 -0
  16. package/dist/api/flags.js +108 -0
  17. package/dist/api/flags.js.map +1 -0
  18. package/dist/api/keys.d.ts +48 -0
  19. package/dist/api/keys.d.ts.map +1 -0
  20. package/dist/api/keys.js +87 -0
  21. package/dist/api/keys.js.map +1 -0
  22. package/dist/auth/browser-open.d.ts +21 -4
  23. package/dist/auth/browser-open.d.ts.map +1 -1
  24. package/dist/auth/browser-open.js +45 -65
  25. package/dist/auth/browser-open.js.map +1 -1
  26. package/dist/auth/lock.d.ts +8 -0
  27. package/dist/auth/lock.d.ts.map +1 -0
  28. package/dist/auth/lock.js +43 -0
  29. package/dist/auth/lock.js.map +1 -0
  30. package/dist/auth/loopback.d.ts +8 -12
  31. package/dist/auth/loopback.d.ts.map +1 -1
  32. package/dist/auth/loopback.js +40 -45
  33. package/dist/auth/loopback.js.map +1 -1
  34. package/dist/auth/session.d.ts +61 -0
  35. package/dist/auth/session.d.ts.map +1 -0
  36. package/dist/auth/session.js +142 -0
  37. package/dist/auth/session.js.map +1 -0
  38. package/dist/auth/sign-in.d.ts +67 -0
  39. package/dist/auth/sign-in.d.ts.map +1 -0
  40. package/dist/auth/sign-in.js +113 -0
  41. package/dist/auth/sign-in.js.map +1 -0
  42. package/dist/auth/store.d.ts +89 -0
  43. package/dist/auth/store.d.ts.map +1 -0
  44. package/dist/auth/store.js +214 -0
  45. package/dist/auth/store.js.map +1 -0
  46. package/dist/bin.js +0 -2
  47. package/dist/bin.js.map +1 -1
  48. package/dist/cli.d.ts +1 -2
  49. package/dist/cli.d.ts.map +1 -1
  50. package/dist/cli.js +13 -17
  51. package/dist/cli.js.map +1 -1
  52. package/dist/client.d.ts +31 -0
  53. package/dist/client.d.ts.map +1 -0
  54. package/dist/client.js +67 -0
  55. package/dist/client.js.map +1 -0
  56. package/dist/commands/accounts.command.d.ts +4 -0
  57. package/dist/commands/accounts.command.d.ts.map +1 -0
  58. package/dist/commands/accounts.command.js +116 -0
  59. package/dist/commands/accounts.command.js.map +1 -0
  60. package/dist/commands/app.command.d.ts +3 -2
  61. package/dist/commands/app.command.d.ts.map +1 -1
  62. package/dist/commands/app.command.js +132 -233
  63. package/dist/commands/app.command.js.map +1 -1
  64. package/dist/commands/auth.command.d.ts +2 -12
  65. package/dist/commands/auth.command.d.ts.map +1 -1
  66. package/dist/commands/auth.command.js +147 -20
  67. package/dist/commands/auth.command.js.map +1 -1
  68. package/dist/commands/billing.command.d.ts +4 -0
  69. package/dist/commands/billing.command.d.ts.map +1 -0
  70. package/dist/commands/billing.command.js +185 -0
  71. package/dist/commands/billing.command.js.map +1 -0
  72. package/dist/commands/check.command.d.ts +2 -1
  73. package/dist/commands/check.command.d.ts.map +1 -1
  74. package/dist/commands/check.command.js +11 -14
  75. package/dist/commands/check.command.js.map +1 -1
  76. package/dist/commands/context.d.ts +58 -0
  77. package/dist/commands/context.d.ts.map +1 -0
  78. package/dist/commands/context.js +78 -0
  79. package/dist/commands/context.js.map +1 -0
  80. package/dist/commands/flag.command.d.ts +3 -62
  81. package/dist/commands/flag.command.d.ts.map +1 -1
  82. package/dist/commands/flag.command.js +181 -864
  83. package/dist/commands/flag.command.js.map +1 -1
  84. package/dist/commands/key.command.d.ts +4 -0
  85. package/dist/commands/key.command.d.ts.map +1 -0
  86. package/dist/commands/key.command.js +44 -0
  87. package/dist/commands/key.command.js.map +1 -0
  88. package/dist/commands/plan.command.d.ts +3 -203
  89. package/dist/commands/plan.command.d.ts.map +1 -1
  90. package/dist/commands/plan.command.js +200 -759
  91. package/dist/commands/plan.command.js.map +1 -1
  92. package/dist/config.d.ts +19 -101
  93. package/dist/config.d.ts.map +1 -1
  94. package/dist/config.js +51 -364
  95. package/dist/config.js.map +1 -1
  96. package/dist/core/discovery.d.ts +27 -0
  97. package/dist/core/discovery.d.ts.map +1 -0
  98. package/dist/core/discovery.js +97 -0
  99. package/dist/core/discovery.js.map +1 -0
  100. package/dist/core/errors.d.ts +26 -0
  101. package/dist/core/errors.d.ts.map +1 -0
  102. package/dist/core/errors.js +33 -0
  103. package/dist/core/errors.js.map +1 -0
  104. package/dist/core/http.d.ts +23 -0
  105. package/dist/core/http.d.ts.map +1 -0
  106. package/dist/core/http.js +153 -0
  107. package/dist/core/http.js.map +1 -0
  108. package/dist/core/private-files.d.ts +6 -0
  109. package/dist/core/private-files.d.ts.map +1 -0
  110. package/dist/core/private-files.js +96 -0
  111. package/dist/core/private-files.js.map +1 -0
  112. package/dist/core/routes.d.ts +94 -0
  113. package/dist/core/routes.d.ts.map +1 -0
  114. package/dist/core/routes.js +91 -0
  115. package/dist/core/routes.js.map +1 -0
  116. package/dist/core/urls.d.ts +15 -0
  117. package/dist/core/urls.d.ts.map +1 -0
  118. package/dist/core/urls.js +55 -0
  119. package/dist/core/urls.js.map +1 -0
  120. package/dist/gate-rules.d.ts +1 -2
  121. package/dist/gate-rules.d.ts.map +1 -1
  122. package/dist/gate-rules.js +4 -5
  123. package/dist/gate-rules.js.map +1 -1
  124. package/dist/index.d.ts +23 -3
  125. package/dist/index.d.ts.map +1 -1
  126. package/dist/index.js +18 -3
  127. package/dist/index.js.map +1 -1
  128. package/dist/io.d.ts +21 -0
  129. package/dist/io.d.ts.map +1 -0
  130. package/dist/io.js +59 -0
  131. package/dist/io.js.map +1 -0
  132. package/dist/money.d.ts +42 -0
  133. package/dist/money.d.ts.map +1 -0
  134. package/dist/money.js +112 -0
  135. package/dist/money.js.map +1 -0
  136. package/dist/program.d.ts +5 -24
  137. package/dist/program.d.ts.map +1 -1
  138. package/dist/program.js +25 -113
  139. package/dist/program.js.map +1 -1
  140. package/package.json +7 -8
  141. package/dist/auth/api-client.d.ts +0 -50
  142. package/dist/auth/api-client.d.ts.map +0 -1
  143. package/dist/auth/api-client.js +0 -109
  144. package/dist/auth/api-client.js.map +0 -1
  145. package/dist/auth/relative-time.d.ts +0 -2
  146. package/dist/auth/relative-time.d.ts.map +0 -1
  147. package/dist/auth/relative-time.js +0 -27
  148. package/dist/auth/relative-time.js.map +0 -1
  149. package/dist/auth/scopes.d.ts +0 -30
  150. package/dist/auth/scopes.d.ts.map +0 -1
  151. package/dist/auth/scopes.js +0 -39
  152. package/dist/auth/scopes.js.map +0 -1
  153. package/dist/auth/workspace-client.d.ts +0 -70
  154. package/dist/auth/workspace-client.d.ts.map +0 -1
  155. package/dist/auth/workspace-client.js +0 -105
  156. package/dist/auth/workspace-client.js.map +0 -1
  157. package/dist/commands/auth/config.command.d.ts +0 -8
  158. package/dist/commands/auth/config.command.d.ts.map +0 -1
  159. package/dist/commands/auth/config.command.js +0 -60
  160. package/dist/commands/auth/config.command.js.map +0 -1
  161. package/dist/commands/auth/login.command.d.ts +0 -46
  162. package/dist/commands/auth/login.command.d.ts.map +0 -1
  163. package/dist/commands/auth/login.command.js +0 -166
  164. package/dist/commands/auth/login.command.js.map +0 -1
  165. package/dist/commands/auth/logout.command.d.ts +0 -21
  166. package/dist/commands/auth/logout.command.d.ts.map +0 -1
  167. package/dist/commands/auth/logout.command.js +0 -97
  168. package/dist/commands/auth/logout.command.js.map +0 -1
  169. package/dist/commands/auth/methods.command.d.ts +0 -68
  170. package/dist/commands/auth/methods.command.d.ts.map +0 -1
  171. package/dist/commands/auth/methods.command.js +0 -91
  172. package/dist/commands/auth/methods.command.js.map +0 -1
  173. package/dist/commands/auth/status.command.d.ts +0 -18
  174. package/dist/commands/auth/status.command.d.ts.map +0 -1
  175. package/dist/commands/auth/status.command.js +0 -104
  176. package/dist/commands/auth/status.command.js.map +0 -1
  177. package/dist/commands/auth/use.command.d.ts +0 -17
  178. package/dist/commands/auth/use.command.d.ts.map +0 -1
  179. package/dist/commands/auth/use.command.js +0 -47
  180. package/dist/commands/auth/use.command.js.map +0 -1
  181. package/dist/commands/branding.command.d.ts +0 -3
  182. package/dist/commands/branding.command.d.ts.map +0 -1
  183. package/dist/commands/branding.command.js +0 -42
  184. package/dist/commands/branding.command.js.map +0 -1
  185. package/dist/commands/event.command.d.ts +0 -3
  186. package/dist/commands/event.command.d.ts.map +0 -1
  187. package/dist/commands/event.command.js +0 -27
  188. package/dist/commands/event.command.js.map +0 -1
  189. package/dist/commands/flag-init.command.d.ts +0 -13
  190. package/dist/commands/flag-init.command.d.ts.map +0 -1
  191. package/dist/commands/flag-init.command.js +0 -463
  192. package/dist/commands/flag-init.command.js.map +0 -1
  193. package/dist/commands/guide.command.d.ts +0 -61
  194. package/dist/commands/guide.command.d.ts.map +0 -1
  195. package/dist/commands/guide.command.js +0 -655
  196. package/dist/commands/guide.command.js.map +0 -1
  197. package/dist/commands/info.command.d.ts +0 -3
  198. package/dist/commands/info.command.d.ts.map +0 -1
  199. package/dist/commands/info.command.js +0 -70
  200. package/dist/commands/info.command.js.map +0 -1
  201. package/dist/commands/integration.command.d.ts +0 -35
  202. package/dist/commands/integration.command.d.ts.map +0 -1
  203. package/dist/commands/integration.command.js +0 -290
  204. package/dist/commands/integration.command.js.map +0 -1
  205. package/dist/commands/journeys.d.ts +0 -43
  206. package/dist/commands/journeys.d.ts.map +0 -1
  207. package/dist/commands/journeys.js +0 -221
  208. package/dist/commands/journeys.js.map +0 -1
  209. package/dist/commands/ops.command.d.ts +0 -17
  210. package/dist/commands/ops.command.d.ts.map +0 -1
  211. package/dist/commands/ops.command.js +0 -129
  212. package/dist/commands/ops.command.js.map +0 -1
  213. package/dist/commands/privilege.command.d.ts +0 -16
  214. package/dist/commands/privilege.command.d.ts.map +0 -1
  215. package/dist/commands/privilege.command.js +0 -86
  216. package/dist/commands/privilege.command.js.map +0 -1
  217. package/dist/commands/role.command.d.ts +0 -3
  218. package/dist/commands/role.command.d.ts.map +0 -1
  219. package/dist/commands/role.command.js +0 -101
  220. package/dist/commands/role.command.js.map +0 -1
  221. package/dist/commands/runtime-dir.d.ts +0 -16
  222. package/dist/commands/runtime-dir.d.ts.map +0 -1
  223. package/dist/commands/runtime-dir.js +0 -18
  224. package/dist/commands/runtime-dir.js.map +0 -1
  225. package/dist/commands/setup.command.d.ts +0 -63
  226. package/dist/commands/setup.command.d.ts.map +0 -1
  227. package/dist/commands/setup.command.js +0 -148
  228. package/dist/commands/setup.command.js.map +0 -1
  229. package/dist/commands/stripe.command.d.ts +0 -29
  230. package/dist/commands/stripe.command.d.ts.map +0 -1
  231. package/dist/commands/stripe.command.js +0 -99
  232. package/dist/commands/stripe.command.js.map +0 -1
  233. package/dist/commands/tenant.command.d.ts +0 -3
  234. package/dist/commands/tenant.command.d.ts.map +0 -1
  235. package/dist/commands/tenant.command.js +0 -135
  236. package/dist/commands/tenant.command.js.map +0 -1
  237. package/dist/commands/token.command.d.ts +0 -3
  238. package/dist/commands/token.command.d.ts.map +0 -1
  239. package/dist/commands/token.command.js +0 -54
  240. package/dist/commands/token.command.js.map +0 -1
  241. package/dist/commands/user.command.d.ts +0 -3
  242. package/dist/commands/user.command.d.ts.map +0 -1
  243. package/dist/commands/user.command.js +0 -117
  244. package/dist/commands/user.command.js.map +0 -1
  245. package/dist/credentials.d.ts +0 -141
  246. package/dist/credentials.d.ts.map +0 -1
  247. package/dist/credentials.js +0 -348
  248. package/dist/credentials.js.map +0 -1
  249. package/dist/flag-coherence.d.ts +0 -42
  250. package/dist/flag-coherence.d.ts.map +0 -1
  251. package/dist/flag-coherence.js +0 -103
  252. package/dist/flag-coherence.js.map +0 -1
  253. package/dist/output.d.ts +0 -17
  254. package/dist/output.d.ts.map +0 -1
  255. package/dist/output.js +0 -107
  256. package/dist/output.js.map +0 -1
  257. package/dist/preview.d.ts +0 -9
  258. package/dist/preview.d.ts.map +0 -1
  259. package/dist/preview.js +0 -63
  260. package/dist/preview.js.map +0 -1
  261. package/dist/product-decisions.d.ts +0 -40
  262. package/dist/product-decisions.d.ts.map +0 -1
  263. package/dist/product-decisions.js +0 -52
  264. package/dist/product-decisions.js.map +0 -1
  265. package/dist/prompt-urls.d.ts +0 -31
  266. package/dist/prompt-urls.d.ts.map +0 -1
  267. package/dist/prompt-urls.js +0 -35
  268. package/dist/prompt-urls.js.map +0 -1
  269. package/dist/prompts/auth-master-integration-prompt.md +0 -255
  270. package/dist/prompts/billing/master.md +0 -332
  271. package/dist/prompts/decisions/feature-control.md +0 -131
  272. package/dist/prompts/decisions/going-live.md +0 -60
  273. package/dist/prompts/decisions/login.md +0 -71
  274. package/dist/prompts/decisions/look-and-feel.md +0 -68
  275. package/dist/prompts/decisions/payments.md +0 -97
  276. package/dist/prompts/decisions/roles.md +0 -78
  277. package/dist/prompts/decisions/teams.md +0 -61
  278. package/dist/prompts/fit-together.md +0 -97
  279. package/dist/prompts/flags/master.md +0 -220
  280. package/dist/prompts/guide-coverage.json +0 -17
  281. package/dist/prompts/integration-success.md +0 -106
  282. package/dist/prompts/mcp-tools.json +0 -90
  283. package/dist/prompts/mechanisms.md +0 -304
  284. package/dist/prompts/orientation.md +0 -42
  285. package/dist/prompts/product-decisions.json +0 -338
  286. package/dist/resolve.d.ts +0 -110
  287. package/dist/resolve.d.ts.map +0 -1
  288. package/dist/resolve.js +0 -260
  289. package/dist/resolve.js.map +0 -1
  290. package/dist/shape.d.ts +0 -25
  291. package/dist/shape.d.ts.map +0 -1
  292. package/dist/shape.js +0 -74
  293. 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