@nurix/apollo 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -53,8 +53,9 @@ apollo doctor # key validity + per-credential health probes +
53
53
  apollo open <svc> [--env <name>] # open endpoint (default production) or repo URL
54
54
  ```
55
55
 
56
- The Apollo base URL is hardcoded to the hosted instance
57
- (`https://apollo.nurix-ai.co`) — there is no flag or env override.
56
+ Global options: `--apollo-url <url>` (defaults to `$APOLLO_URL`, then the
57
+ hosted instance `https://apollo.nurix-ai.co`) — the default means you never
58
+ need to pass the URL; override only for a non-hosted Apollo.
58
59
 
59
60
  ### `apollo init`
60
61
 
@@ -4,14 +4,15 @@
4
4
  // records the consumes edge (consumer-journey.md §4–§5).
5
5
  import path from "node:path";
6
6
  import * as p from "@clack/prompts";
7
- import { APOLLO_URL, exchangeServiceCredential, fetchCatalogue, } from "../lib/apollo_client.js";
7
+ import { exchangeServiceCredential, fetchCatalogue, resolveApolloUrl, } from "../lib/apollo_client.js";
8
8
  import { readManagedValues, upsertManagedValues } from "../lib/env_file.js";
9
9
  import { detectPackageManager, installPackage } from "../lib/package_manager.js";
10
10
  import { appendConsumesEdge, hasManifest, scaffoldManifest } from "../lib/manifest.js";
11
11
  import { confirmOrFallback, isInteractive } from "../lib/interactive.js";
12
12
  // Run the installer flow end to end for one service.
13
- export async function runAdd(serviceArg) {
13
+ export async function runAdd(serviceArg, options) {
14
14
  const cwd = process.cwd();
15
+ const baseUrl = resolveApolloUrl(options);
15
16
  p.intro(`apollo add${serviceArg ? ` ${serviceArg}` : ""}`);
16
17
  const managedValues = await readManagedValues(path.join(cwd, ".env"));
17
18
  const appKey = managedValues.APOLLO_APP_KEY ?? process.env.APOLLO_APP_KEY;
@@ -23,7 +24,7 @@ export async function runAdd(serviceArg) {
23
24
  }
24
25
  const spinner = p.spinner();
25
26
  spinner.start("Fetching the catalogue");
26
- const catalogue = await fetchCatalogue(APOLLO_URL).catch(() => null);
27
+ const catalogue = await fetchCatalogue(baseUrl).catch(() => null);
27
28
  const services = catalogue?.services ?? [];
28
29
  spinner.stop(`Catalogue loaded: ${services.length} service(s)`);
29
30
  const serviceName = serviceArg ?? (await pickService(services));
@@ -39,7 +40,7 @@ export async function runAdd(serviceArg) {
39
40
  spinner.start(`Exchanging credentials for '${serviceName}'`);
40
41
  let credential;
41
42
  try {
42
- credential = await exchangeServiceCredential(APOLLO_URL, appKey, serviceName);
43
+ credential = await exchangeServiceCredential(baseUrl, appKey, serviceName);
43
44
  }
44
45
  catch (error) {
45
46
  spinner.stop("Exchange failed", 1);
@@ -3,14 +3,15 @@
3
3
  // rejected, runs the login flow, then finishes with init's manifest scaffold.
4
4
  import path from "node:path";
5
5
  import * as p from "@clack/prompts";
6
- import { APOLLO_URL, validateAppKey } from "../lib/apollo_client.js";
6
+ import { resolveApolloUrl, validateAppKey } from "../lib/apollo_client.js";
7
7
  import { readManagedValues } from "../lib/env_file.js";
8
8
  import { isInteractive } from "../lib/interactive.js";
9
9
  import { performDeviceLogin } from "./login.js";
10
10
  import { finishInit } from "./init.js";
11
11
  // Run the bootstrap flow: ensure a valid app key (login when needed), then init.
12
- export async function runBootstrap() {
12
+ export async function runBootstrap(options) {
13
13
  const cwd = process.cwd();
14
+ const baseUrl = resolveApolloUrl(options);
14
15
  p.intro("apollo");
15
16
  if (!isInteractive()) {
16
17
  p.log.error("Bare `apollo` needs an interactive terminal (browser sign-in + prompts).");
@@ -23,7 +24,7 @@ export async function runBootstrap() {
23
24
  if (managedValues.APOLLO_APP_KEY) {
24
25
  const spinner = p.spinner();
25
26
  spinner.start("Checking the stored APOLLO_APP_KEY against Apollo");
26
- isSignedIn = await validateAppKey(APOLLO_URL, managedValues.APOLLO_APP_KEY).catch(() => false);
27
+ isSignedIn = await validateAppKey(baseUrl, managedValues.APOLLO_APP_KEY).catch(() => false);
27
28
  spinner.stop(isSignedIn
28
29
  ? "Signed in — the stored key is valid"
29
30
  : "Stored key rejected — signing in again", isSignedIn ? 0 : 1);
@@ -31,7 +32,7 @@ export async function runBootstrap() {
31
32
  else {
32
33
  p.log.info("No APOLLO_APP_KEY in the managed .env block — signing in");
33
34
  }
34
- if (!isSignedIn && !(await performDeviceLogin(cwd))) {
35
+ if (!isSignedIn && !(await performDeviceLogin(baseUrl, cwd))) {
35
36
  p.outro("Sign-in failed — nothing was written. Run `apollo` again, or `apollo login` to retry.");
36
37
  process.exitCode = 1;
37
38
  return;
@@ -1,13 +1,14 @@
1
1
  // packages/cli/src/commands/describe.ts - `apollo describe <service>`: the full
2
2
  // catalogue entry for one service, prettified or raw JSON.
3
3
  import * as p from "@clack/prompts";
4
- import { APOLLO_URL, fetchCatalogue } from "../lib/apollo_client.js";
4
+ import { fetchCatalogue, resolveApolloUrl } from "../lib/apollo_client.js";
5
5
  // Print one catalogue entry; --json emits the raw object for piping.
6
- export async function runDescribe(serviceName, options) {
7
- const catalogue = await fetchCatalogue(APOLLO_URL);
6
+ export async function runDescribe(serviceName, options, globals) {
7
+ const baseUrl = resolveApolloUrl(globals);
8
+ const catalogue = await fetchCatalogue(baseUrl);
8
9
  const entry = (catalogue.services ?? []).find((service) => service.name === serviceName);
9
10
  if (!entry) {
10
- p.log.error(`'${serviceName}' is not in the catalogue at ${APOLLO_URL} (${catalogue.services?.length ?? 0} services listed).`);
11
+ p.log.error(`'${serviceName}' is not in the catalogue at ${baseUrl} (${catalogue.services?.length ?? 0} services listed).`);
11
12
  process.exitCode = 1;
12
13
  return;
13
14
  }
@@ -5,19 +5,20 @@
5
5
  import path from "node:path";
6
6
  import { readFile } from "node:fs/promises";
7
7
  import * as p from "@clack/prompts";
8
- import { APOLLO_URL, fetchCatalogue, validateAppKey } from "../lib/apollo_client.js";
8
+ import { fetchCatalogue, resolveApolloUrl, validateAppKey, } from "../lib/apollo_client.js";
9
9
  import { readManagedValues } from "../lib/env_file.js";
10
10
  import { hasManifest, manifestPath } from "../lib/manifest.js";
11
11
  import { loadSchema, validateManifest } from "../lib/schema_validator.js";
12
12
  // Run every check; exit 1 when any error (not warning) was found.
13
- export async function runDoctor() {
13
+ export async function runDoctor(globals) {
14
14
  const cwd = process.cwd();
15
+ const baseUrl = resolveApolloUrl(globals);
15
16
  p.intro("apollo doctor");
16
17
  const counters = { errors: 0, warnings: 0 };
17
18
  const managedValues = await readManagedValues(path.join(cwd, ".env"));
18
- await checkAppKey(managedValues, counters);
19
+ await checkAppKey(managedValues, baseUrl, counters);
19
20
  await checkServiceCredentials(managedValues, counters);
20
- await checkManifest(cwd, counters);
21
+ await checkManifest(cwd, baseUrl, counters);
21
22
  const summary = `${counters.errors} error(s), ${counters.warnings} warning(s)`;
22
23
  if (counters.errors > 0) {
23
24
  p.outro(`Unhealthy — ${summary}.`);
@@ -28,14 +29,14 @@ export async function runDoctor() {
28
29
  }
29
30
  }
30
31
  // Check the APOLLO_APP_KEY exists and is still accepted by Apollo.
31
- async function checkAppKey(managedValues, counters) {
32
+ async function checkAppKey(managedValues, baseUrl, counters) {
32
33
  const appKey = managedValues.APOLLO_APP_KEY ?? process.env.APOLLO_APP_KEY;
33
34
  if (!appKey) {
34
35
  p.log.error("APOLLO_APP_KEY missing — run `apollo init`.");
35
36
  counters.errors += 1;
36
37
  return;
37
38
  }
38
- const isValid = await validateAppKey(APOLLO_URL, appKey).catch(() => false);
39
+ const isValid = await validateAppKey(baseUrl, appKey).catch(() => false);
39
40
  if (isValid) {
40
41
  p.log.success("APOLLO_APP_KEY accepted by Apollo");
41
42
  }
@@ -80,7 +81,7 @@ async function checkServiceCredentials(managedValues, counters) {
80
81
  }
81
82
  }
82
83
  // Validate the manifest and cross-check its consumes edges against the catalogue.
83
- async function checkManifest(cwd, counters) {
84
+ async function checkManifest(cwd, baseUrl, counters) {
84
85
  if (!hasManifest(cwd)) {
85
86
  p.log.warn("No apollo.service.json — the app has no bill of materials. `apollo init` scaffolds one.");
86
87
  counters.warnings += 1;
@@ -109,7 +110,7 @@ async function checkManifest(cwd, counters) {
109
110
  const edges = (manifest.services ?? []).flatMap((service) => service.consumes ?? []);
110
111
  if (edges.length === 0)
111
112
  return;
112
- const catalogue = await fetchCatalogue(APOLLO_URL).catch(() => null);
113
+ const catalogue = await fetchCatalogue(baseUrl).catch(() => null);
113
114
  const services = catalogue?.services ?? [];
114
115
  if (services.length === 0) {
115
116
  p.log.info("Catalogue empty — consumes edges not cross-checked.");
@@ -1,9 +1,10 @@
1
1
  // packages/cli/src/commands/graph.ts - `apollo graph`: render the catalogue's
2
2
  // consumes-graph as an ascii edge list or a mermaid block.
3
- import { APOLLO_URL, fetchCatalogue } from "../lib/apollo_client.js";
3
+ import { fetchCatalogue, resolveApolloUrl } from "../lib/apollo_client.js";
4
4
  // Print every consumes edge in the catalogue; --mermaid emits `graph LR`.
5
- export async function runGraph(options) {
6
- const catalogue = await fetchCatalogue(APOLLO_URL);
5
+ export async function runGraph(options, globals) {
6
+ const baseUrl = resolveApolloUrl(globals);
7
+ const catalogue = await fetchCatalogue(baseUrl);
7
8
  const services = catalogue.services ?? [];
8
9
  const edges = services.flatMap((service) => (service.consumes ?? []).map((edge) => ({
9
10
  from: service.name,
@@ -11,7 +12,7 @@ export async function runGraph(options) {
11
12
  via: edge.via ?? "http",
12
13
  })));
13
14
  if (services.length === 0) {
14
- console.log(`Catalogue at ${APOLLO_URL} is empty — nothing to graph.`);
15
+ console.log(`Catalogue at ${baseUrl} is empty — nothing to graph.`);
15
16
  return;
16
17
  }
17
18
  if (edges.length === 0) {
@@ -3,14 +3,15 @@
3
3
  // gitignore .env, and scaffold the repo manifest.
4
4
  import path from "node:path";
5
5
  import * as p from "@clack/prompts";
6
- import { APOLLO_URL, validateAppKey } from "../lib/apollo_client.js";
6
+ import { resolveApolloUrl, validateAppKey } from "../lib/apollo_client.js";
7
7
  import { ensureEnvGitignored, readManagedValues, upsertManagedValues } from "../lib/env_file.js";
8
8
  import { hasManifest, scaffoldManifest } from "../lib/manifest.js";
9
9
  import { isInteractive } from "../lib/interactive.js";
10
10
  import { performDeviceLogin } from "./login.js";
11
11
  // Run the init flow: collect + validate the key, write managed blocks, scaffold.
12
- export async function runInit() {
12
+ export async function runInit(options) {
13
13
  const cwd = process.cwd();
14
+ const baseUrl = resolveApolloUrl(options);
14
15
  p.intro("apollo init");
15
16
  if (!isInteractive()) {
16
17
  p.log.error("apollo init needs an interactive terminal (it prompts for the key).");
@@ -32,7 +33,7 @@ export async function runInit() {
32
33
  message: "Sign in via the browser to create your application & key? (No = paste a key)",
33
34
  });
34
35
  if (!p.isCancel(viaBrowser) && viaBrowser) {
35
- if (!(await performDeviceLogin(cwd))) {
36
+ if (!(await performDeviceLogin(baseUrl, cwd))) {
36
37
  p.outro("Sign-in failed — re-run `apollo init`, or pick the paste option.");
37
38
  process.exitCode = 1;
38
39
  return;
@@ -41,7 +42,7 @@ export async function runInit() {
41
42
  return;
42
43
  }
43
44
  const appKey = await p.password({
44
- message: `Paste your APOLLO_APP_KEY (create an application in the Apollo console: ${APOLLO_URL})`,
45
+ message: `Paste your APOLLO_APP_KEY (create an application in the Apollo console: ${baseUrl})`,
45
46
  validate: (value) => value?.startsWith("apollo_app_") ? undefined : "Expected a key starting with apollo_app_",
46
47
  });
47
48
  if (p.isCancel(appKey)) {
@@ -50,7 +51,7 @@ export async function runInit() {
50
51
  }
51
52
  const spinner = p.spinner();
52
53
  spinner.start("Validating the key against Apollo");
53
- const isValid = await validateAppKey(APOLLO_URL, appKey).catch(() => false);
54
+ const isValid = await validateAppKey(baseUrl, appKey).catch(() => false);
54
55
  if (!isValid) {
55
56
  spinner.stop("Key rejected by Apollo", 1);
56
57
  p.outro("No key, no install — fix the key and re-run `apollo init`.");
@@ -1,12 +1,13 @@
1
1
  // packages/cli/src/commands/list.ts - `apollo list`: print the discovery
2
2
  // catalogue, one line per service.
3
- import { APOLLO_URL, fetchCatalogue } from "../lib/apollo_client.js";
3
+ import { fetchCatalogue, resolveApolloUrl } from "../lib/apollo_client.js";
4
4
  // Print the catalogue as aligned columns: name, classifier, status, description.
5
- export async function runList() {
6
- const catalogue = await fetchCatalogue(APOLLO_URL);
5
+ export async function runList(options) {
6
+ const baseUrl = resolveApolloUrl(options);
7
+ const catalogue = await fetchCatalogue(baseUrl);
7
8
  const services = catalogue.services ?? [];
8
9
  if (services.length === 0) {
9
- console.log(`Catalogue at ${APOLLO_URL} is empty — the registry read path / refresh isn't wired yet.`);
10
+ console.log(`Catalogue at ${baseUrl} is empty — the registry read path / refresh isn't wired yet.`);
10
11
  return;
11
12
  }
12
13
  for (const service of services) {
@@ -5,7 +5,7 @@
5
5
  // the managed .env block — no key pasting.
6
6
  import path from "node:path";
7
7
  import * as p from "@clack/prompts";
8
- import { APOLLO_URL } from "../lib/apollo_client.js";
8
+ import { resolveApolloUrl } from "../lib/apollo_client.js";
9
9
  import { ensureEnvGitignored, readManagedValues, upsertManagedValues } from "../lib/env_file.js";
10
10
  import { launchBrowser } from "../lib/browser_opener.js";
11
11
  import { getOrCreateDeviceId } from "../lib/device_identity.js";
@@ -13,9 +13,9 @@ import { pollDeviceOnce, pollUntilIssued, startDeviceTrust, } from "../lib/devic
13
13
  // Browser-approval wait cap; the trust window itself is 24h server-side.
14
14
  const APPROVAL_TIMEOUT_SECONDS = 10 * 60;
15
15
  // Run the login flow and write the issued key; returns false on failure.
16
- export async function performDeviceLogin(cwd, flags = {}) {
16
+ export async function performDeviceLogin(baseUrl, cwd, flags = {}) {
17
17
  const deviceId = await getOrCreateDeviceId();
18
- const start = await startDeviceTrust(APOLLO_URL, deviceId);
18
+ const start = await startDeviceTrust(baseUrl, deviceId);
19
19
  const managedValues = await readManagedValues(path.join(cwd, ".env"));
20
20
  const pollParams = {
21
21
  deviceCode: start.device_code,
@@ -26,7 +26,7 @@ export async function performDeviceLogin(cwd, flags = {}) {
26
26
  // Trusted-device fast path: an approval inside the 24h window is not
27
27
  // consumed, so a single poll may already succeed with no browser at all.
28
28
  let issued = null;
29
- const firstAttempt = await pollDeviceOnce(APOLLO_URL, pollParams);
29
+ const firstAttempt = await pollDeviceOnce(baseUrl, pollParams);
30
30
  if (firstAttempt.kind === "issued") {
31
31
  issued = firstAttempt.issued;
32
32
  p.log.success("Device already trusted — no browser approval needed");
@@ -43,7 +43,7 @@ export async function performDeviceLogin(cwd, flags = {}) {
43
43
  const spinner = p.spinner();
44
44
  spinner.start("Waiting for approval in the browser");
45
45
  try {
46
- issued = await pollUntilIssued(APOLLO_URL, pollParams, start.interval, Math.min(start.expires_in, APPROVAL_TIMEOUT_SECONDS));
46
+ issued = await pollUntilIssued(baseUrl, pollParams, start.interval, Math.min(start.expires_in, APPROVAL_TIMEOUT_SECONDS));
47
47
  }
48
48
  catch (error) {
49
49
  spinner.stop("Sign-in failed", 1);
@@ -68,10 +68,11 @@ export async function performDeviceLogin(cwd, flags = {}) {
68
68
  return true;
69
69
  }
70
70
  // `apollo login` — the standalone command wrapper.
71
- export async function runLogin(flags = {}) {
71
+ export async function runLogin(options, flags = {}) {
72
72
  const cwd = process.cwd();
73
+ const baseUrl = resolveApolloUrl(options);
73
74
  p.intro("apollo login");
74
- const didLogin = await performDeviceLogin(cwd, flags);
75
+ const didLogin = await performDeviceLogin(baseUrl, cwd, flags);
75
76
  if (!didLogin) {
76
77
  p.outro("Nothing was written.");
77
78
  process.exitCode = 1;
@@ -1,14 +1,15 @@
1
1
  // packages/cli/src/commands/open.ts - `apollo open <service>`: open the
2
2
  // service's endpoint (or repository) in the default browser.
3
3
  import * as p from "@clack/prompts";
4
- import { APOLLO_URL, fetchCatalogue } from "../lib/apollo_client.js";
4
+ import { fetchCatalogue, resolveApolloUrl } from "../lib/apollo_client.js";
5
5
  import { launchBrowser } from "../lib/browser_opener.js";
6
6
  // Resolve the URL to open (chosen endpoint > production > any > repository) and launch it.
7
- export async function runOpen(serviceName, options) {
8
- const catalogue = await fetchCatalogue(APOLLO_URL);
7
+ export async function runOpen(serviceName, options, globals) {
8
+ const baseUrl = resolveApolloUrl(globals);
9
+ const catalogue = await fetchCatalogue(baseUrl);
9
10
  const entry = (catalogue.services ?? []).find((service) => service.name === serviceName);
10
11
  if (!entry) {
11
- p.log.error(`'${serviceName}' is not in the catalogue at ${APOLLO_URL}.`);
12
+ p.log.error(`'${serviceName}' is not in the catalogue at ${baseUrl}.`);
12
13
  process.exitCode = 1;
13
14
  return;
14
15
  }
@@ -4,11 +4,12 @@
4
4
  // credentials disappear).
5
5
  import path from "node:path";
6
6
  import * as p from "@clack/prompts";
7
- import { APOLLO_URL, fetchAppCredentials } from "../lib/apollo_client.js";
7
+ import { fetchAppCredentials, resolveApolloUrl } from "../lib/apollo_client.js";
8
8
  import { readManagedValues, upsertManagedValues, writeManagedValues } from "../lib/env_file.js";
9
9
  // Run the sync flow: fetch the projection, rewrite the managed block wholly.
10
- export async function runSync() {
10
+ export async function runSync(options) {
11
11
  const cwd = process.cwd();
12
+ const baseUrl = resolveApolloUrl(options);
12
13
  p.intro("apollo sync");
13
14
  const managedValues = await readManagedValues(path.join(cwd, ".env"));
14
15
  const appKey = managedValues.APOLLO_APP_KEY ?? process.env.APOLLO_APP_KEY;
@@ -22,7 +23,7 @@ export async function runSync() {
22
23
  spinner.start("Fetching the application's credential projection");
23
24
  let projection;
24
25
  try {
25
- projection = await fetchAppCredentials(APOLLO_URL, appKey);
26
+ projection = await fetchAppCredentials(baseUrl, appKey);
26
27
  }
27
28
  catch (error) {
28
29
  spinner.stop("Projection failed", 1);
package/dist/index.js CHANGED
@@ -21,14 +21,15 @@ program
21
21
  .name("apollo")
22
22
  .description("Bootstrap NuStack services into your repo via the Apollo discovery plane.")
23
23
  .version(version)
24
+ .option("--apollo-url <url>", "Apollo base URL (defaults to $APOLLO_URL, then the hosted instance)")
24
25
  // Bare `apollo`: bootstrap — check sign-in, login if needed, then init.
25
- .action(async () => runBootstrap());
26
+ .action(async () => runBootstrap(program.opts()));
26
27
  program.addHelpText("after", "\nRun `apollo` with no command to bootstrap: it checks your sign-in, runs login if needed, then init.");
27
28
  program
28
29
  .command("login")
29
30
  .description("Sign in via the browser (24h device trust) and issue your APOLLO_APP_KEY")
30
31
  .option("--no-open", "don't auto-open the browser; just print the approval URL")
31
- .action(async (options) => runLogin(options));
32
+ .action(async (options) => runLogin(program.opts(), options));
32
33
  program
33
34
  .command("logout")
34
35
  .description("Sign out: remove APOLLO_APP_KEY from the managed .env block and sever device trust")
@@ -36,24 +37,24 @@ program
36
37
  program
37
38
  .command("init")
38
39
  .description("Store your APOLLO_APP_KEY in the managed .env block and scaffold the manifest")
39
- .action(async () => runInit());
40
+ .action(async () => runInit(program.opts()));
40
41
  program
41
42
  .command("add [service]")
42
43
  .description("Install a stack service: exchange credentials, write .env, install the package")
43
- .action(async (service) => runAdd(service));
44
+ .action(async (service) => runAdd(service, program.opts()));
44
45
  program
45
46
  .command("list")
46
47
  .description("List the services in the Apollo catalogue")
47
- .action(async () => runList());
48
+ .action(async () => runList(program.opts()));
48
49
  program
49
50
  .command("sync")
50
51
  .description("Re-project all credentials issued to this application into the managed .env block")
51
- .action(async () => runSync());
52
+ .action(async () => runSync(program.opts()));
52
53
  program
53
54
  .command("describe <service>")
54
55
  .description("Show the full catalogue entry for a service")
55
56
  .option("--json", "emit the raw catalogue entry as JSON")
56
- .action(async (service, options) => runDescribe(service, options));
57
+ .action(async (service, options) => runDescribe(service, options, program.opts()));
57
58
  program
58
59
  .command("validate [manifest]")
59
60
  .description("Validate an apollo.service.json (path or URL) against the apollo.dev/v1 schema")
@@ -63,14 +64,14 @@ program
63
64
  .command("graph")
64
65
  .description("Render the catalogue's consumes-graph")
65
66
  .option("--mermaid", "emit a mermaid `graph LR` block instead of ascii")
66
- .action(async (options) => runGraph(options));
67
+ .action(async (options) => runGraph(options, program.opts()));
67
68
  program
68
69
  .command("doctor")
69
70
  .description("Check key validity, credential health, and manifest conformance")
70
- .action(async () => runDoctor());
71
+ .action(async () => runDoctor(program.opts()));
71
72
  program
72
73
  .command("open <service>")
73
74
  .description("Open the service's endpoint (or repository) in the browser")
74
75
  .option("--env <name>", "pick a specific endpoint environment (default: production)")
75
- .action(async (service, options) => runOpen(service, options));
76
+ .action(async (service, options) => runOpen(service, options, program.opts()));
76
77
  await program.parseAsync(process.argv);
@@ -1,9 +1,11 @@
1
1
  // packages/cli/src/lib/apollo_client.ts - HTTP client for the Apollo discovery
2
2
  // plane: catalogue fetch + credential exchange + key validation.
3
- // The discovery plane lives at one hosted location (consumer-journey §9.1 —
4
- // location baked in). Hardcoded; no flag or env override.
5
- export const APOLLO_URL = "https://apollo.nurix-ai.co";
3
+ const DEFAULT_APOLLO_URL = "https://apollo.nurix-ai.co";
6
4
  const REQUEST_TIMEOUT_MS = 10_000;
5
+ // Resolve the Apollo base URL: --apollo-url flag > APOLLO_URL env > hosted default.
6
+ export function resolveApolloUrl(options) {
7
+ return (options.apolloUrl ?? process.env.APOLLO_URL ?? DEFAULT_APOLLO_URL).replace(/\/+$/, "");
8
+ }
7
9
  // Fetch the public discovery catalogue.
8
10
  export async function fetchCatalogue(baseUrl) {
9
11
  const response = await fetch(`${baseUrl}/api/catalog.json`, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nurix/apollo",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "The apollo CLI — bootstrap NuStack services into a repo via the Apollo discovery plane.",
5
5
  "type": "module",
6
6
  "private": false,