@wave-ai-os/wave-cli 0.1.57 → 0.1.59

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
@@ -103,6 +103,22 @@ Config files are JSON objects using the same keys accepted by `--auth-json-stdin
103
103
  }
104
104
  ```
105
105
 
106
+ For a persistent production login, configure `https://api.ourwave.io` as the
107
+ GraphQL/API base and save only that API endpoint to the local CLI config. The
108
+ token remains in the configured credential store, not in `config.json`:
109
+
110
+ ```bash
111
+ wave --base-url https://api.ourwave.io login --save-base-url
112
+ ```
113
+
114
+ The API host is not the browser-login destination. When production returns a
115
+ relative authorization path such as `/cli/login?code=<code>`, the CLI opens the
116
+ Wave app login page at
117
+ `https://app.ourwave.io/login?next=%2Fcli%2Flogin%3Fcode%3D<encoded-code>`.
118
+
119
+ For a headless environment, add `--no-browser`. This is explicit by design:
120
+ it does not replace a developer's local API endpoint unless they request it.
121
+
106
122
  Entity targeting follows the same explicit-first rule: command flags and positional args, then stdin JSON where supported, then environment/config context for organization and runtime. If a target remains unresolved, the CLI returns a structured JSON error with `suggestedAction`, `suggestedCommands`, or both where the command can provide actionable guidance.
107
123
 
108
124
  ## Discoverability and Diagnostics
package/dist/index.cjs CHANGED
@@ -1793,7 +1793,6 @@ async function runGraphqlMutationCommand(input) {
1793
1793
  // src/commands/runtimeOptions.ts
1794
1794
  var import_node_fs3 = require("fs");
1795
1795
  var import_promises2 = require("fs/promises");
1796
- var import_node_os2 = require("os");
1797
1796
  var import_node_path2 = require("path");
1798
1797
 
1799
1798
  // src/authStorage.ts
@@ -1817,6 +1816,9 @@ function authHome(env = process.env) {
1817
1816
  function tokenPath(env = process.env) {
1818
1817
  return (0, import_node_path.join)(authHome(env), FILE_NAME);
1819
1818
  }
1819
+ function configPath(env = process.env) {
1820
+ return normalize2(env.WAVE_CONFIG_PATH) ?? (0, import_node_path.join)(authHome(env), "config.json");
1821
+ }
1820
1822
  function storageMode(env = process.env) {
1821
1823
  const raw = normalize2(env.WAVE_CLI_AUTH_STORAGE)?.toLowerCase();
1822
1824
  if (raw === "keychain" || raw === "file" || raw === "disabled") return raw;
@@ -1952,6 +1954,25 @@ async function writeStoredToken(token, baseUrl) {
1952
1954
  await writeFileToken(token, baseUrl);
1953
1955
  return { kind: "file", available: true, path: tokenPath() };
1954
1956
  }
1957
+ async function persistBaseUrl(baseUrl, env = process.env) {
1958
+ const path = configPath(env);
1959
+ let existing = {};
1960
+ if ((0, import_node_fs2.existsSync)(path)) {
1961
+ const raw = await (0, import_promises.readFile)(path, "utf8");
1962
+ const parsed = JSON.parse(raw);
1963
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1964
+ throw new Error(`Wave config file must contain a JSON object: ${path}`);
1965
+ }
1966
+ existing = parsed;
1967
+ }
1968
+ await (0, import_promises.mkdir)((0, import_node_path.dirname)(path), { recursive: true, mode: 448 });
1969
+ await (0, import_promises.writeFile)(path, `${JSON.stringify({ ...existing, baseUrl }, null, 2)}
1970
+ `, {
1971
+ mode: 384
1972
+ });
1973
+ await (0, import_promises.chmod)(path, 384);
1974
+ return path;
1975
+ }
1955
1976
  async function removeStoredToken(baseUrl) {
1956
1977
  const removedKeychain = await deleteKeychainToken(baseUrl);
1957
1978
  const removedFile = await deleteFileToken();
@@ -2252,7 +2273,7 @@ async function resolveWithStoredToken(context) {
2252
2273
  function defaultConfigPath(env) {
2253
2274
  const explicitEnv = normalize3(env.WAVE_CONFIG_PATH);
2254
2275
  if (explicitEnv) return explicitEnv;
2255
- const candidate = (0, import_node_path2.join)((0, import_node_os2.homedir)(), ".wave", "config.json");
2276
+ const candidate = (0, import_node_path2.join)(authHome(env), "config.json");
2256
2277
  return (0, import_node_fs3.existsSync)(candidate) ? candidate : void 0;
2257
2278
  }
2258
2279
  async function resolveConfigFileContext(options, env) {
@@ -10130,6 +10151,11 @@ function absoluteAuthorizeUrl(baseUrl, authorizeUrl, uiBaseUrl = explicitUiBaseU
10130
10151
  if (/^https?:\/\//i.test(authorizeUrl)) {
10131
10152
  return authorizeUrl;
10132
10153
  }
10154
+ if (new URL(baseUrl).hostname.toLowerCase() === "api.ourwave.io") {
10155
+ const loginUrl = new URL("https://app.ourwave.io/login");
10156
+ loginUrl.searchParams.set("next", authorizeUrl);
10157
+ return loginUrl.toString();
10158
+ }
10133
10159
  return new URL(authorizeUrl, uiBaseUrl ?? localUiBaseUrlFor(baseUrl) ?? baseUrl).toString();
10134
10160
  }
10135
10161
  function canOpenBrowser(noBrowser) {
@@ -10216,6 +10242,7 @@ async function runTokenLogin(cmd, opts) {
10216
10242
  }
10217
10243
  const statusData = asRecord6(status.result.data.cliAuthStatus) ?? {};
10218
10244
  const storage = await writeStoredToken(token, baseUrl);
10245
+ const configPath2 = opts.saveBaseUrl ? await persistBaseUrl(baseUrl) : void 0;
10219
10246
  return printEnvelopeAndExit({
10220
10247
  envelope: envelope({
10221
10248
  ok: true,
@@ -10226,6 +10253,8 @@ async function runTokenLogin(cmd, opts) {
10226
10253
  mode: "token",
10227
10254
  tokenStored: true,
10228
10255
  storage,
10256
+ baseUrlSaved: Boolean(configPath2),
10257
+ configPath: configPath2 ?? null,
10229
10258
  status: redactUnknown(statusData, [token])
10230
10259
  }
10231
10260
  })
@@ -10368,6 +10397,7 @@ async function runBrowserLogin(cmd, opts) {
10368
10397
  });
10369
10398
  }
10370
10399
  const storage = await writeStoredToken(cliToken, baseUrl);
10400
+ const configPath2 = opts.saveBaseUrl ? await persistBaseUrl(baseUrl) : void 0;
10371
10401
  return printEnvelopeAndExit({
10372
10402
  envelope: envelope({
10373
10403
  ok: true,
@@ -10379,7 +10409,9 @@ async function runBrowserLogin(cmd, opts) {
10379
10409
  authorizeUrl,
10380
10410
  expiresAt: expiresAt ?? null,
10381
10411
  tokenStored: true,
10382
- storage
10412
+ storage,
10413
+ baseUrlSaved: Boolean(configPath2),
10414
+ configPath: configPath2 ?? null
10383
10415
  }
10384
10416
  })
10385
10417
  });
@@ -10507,7 +10539,7 @@ async function runAuthStatus(cmd) {
10507
10539
  });
10508
10540
  }
10509
10541
  function registerAuthCommands(program) {
10510
- program.command("login").description("Authenticate Wave CLI with a browser login session or provided CLI token").option("--no-browser", "Do not open a browser; include authorizeUrl in the JSON envelope").option("--token [token]", "Store and probe a provided CLI token instead of using browser login").option("--timeout-ms <timeoutMs>", "Login timeout in milliseconds").option("--poll-interval-ms <pollIntervalMs>", "Browser login polling interval in milliseconds").action((opts, cmd) => {
10542
+ program.command("login").description("Authenticate Wave CLI with a browser login session or provided CLI token").option("--no-browser", "Do not open a browser; include authorizeUrl in the JSON envelope").option("--save-base-url", "Persist the resolved API base URL for future Wave CLI commands").option("--token [token]", "Store and probe a provided CLI token instead of using browser login").option("--timeout-ms <timeoutMs>", "Login timeout in milliseconds").option("--poll-interval-ms <pollIntervalMs>", "Browser login polling interval in milliseconds").action((opts, cmd) => {
10511
10543
  const globals = cmd.optsWithGlobals();
10512
10544
  if (opts.token !== void 0 || globals.token !== void 0) {
10513
10545
  return runTokenLogin(cmd, opts);
@@ -15766,7 +15798,7 @@ function attachDiscoveryHelp(program) {
15766
15798
  }
15767
15799
 
15768
15800
  // src/version.ts
15769
- var CLI_VERSION = "0.1.57";
15801
+ var CLI_VERSION = "0.1.59";
15770
15802
 
15771
15803
  // src/cli.ts
15772
15804
  function buildCli(options) {
package/dist/index.js CHANGED
@@ -1792,7 +1792,6 @@ async function runGraphqlMutationCommand(input) {
1792
1792
  // src/commands/runtimeOptions.ts
1793
1793
  import { existsSync as existsSync2 } from "fs";
1794
1794
  import { readFile as readFile2 } from "fs/promises";
1795
- import { homedir as homedir2 } from "os";
1796
1795
  import { join as join2 } from "path";
1797
1796
 
1798
1797
  // src/authStorage.ts
@@ -1800,7 +1799,7 @@ import { execFile } from "child_process";
1800
1799
  import { constants, existsSync } from "fs";
1801
1800
  import { access, mkdir, readFile, rm, stat, writeFile, chmod } from "fs/promises";
1802
1801
  import { homedir } from "os";
1803
- import { join } from "path";
1802
+ import { dirname, join } from "path";
1804
1803
  import { promisify } from "util";
1805
1804
  var execFileAsync = promisify(execFile);
1806
1805
  var KEYCHAIN_SERVICE = "wave-cli";
@@ -1816,6 +1815,9 @@ function authHome(env = process.env) {
1816
1815
  function tokenPath(env = process.env) {
1817
1816
  return join(authHome(env), FILE_NAME);
1818
1817
  }
1818
+ function configPath(env = process.env) {
1819
+ return normalize2(env.WAVE_CONFIG_PATH) ?? join(authHome(env), "config.json");
1820
+ }
1819
1821
  function storageMode(env = process.env) {
1820
1822
  const raw = normalize2(env.WAVE_CLI_AUTH_STORAGE)?.toLowerCase();
1821
1823
  if (raw === "keychain" || raw === "file" || raw === "disabled") return raw;
@@ -1951,6 +1953,25 @@ async function writeStoredToken(token, baseUrl) {
1951
1953
  await writeFileToken(token, baseUrl);
1952
1954
  return { kind: "file", available: true, path: tokenPath() };
1953
1955
  }
1956
+ async function persistBaseUrl(baseUrl, env = process.env) {
1957
+ const path = configPath(env);
1958
+ let existing = {};
1959
+ if (existsSync(path)) {
1960
+ const raw = await readFile(path, "utf8");
1961
+ const parsed = JSON.parse(raw);
1962
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1963
+ throw new Error(`Wave config file must contain a JSON object: ${path}`);
1964
+ }
1965
+ existing = parsed;
1966
+ }
1967
+ await mkdir(dirname(path), { recursive: true, mode: 448 });
1968
+ await writeFile(path, `${JSON.stringify({ ...existing, baseUrl }, null, 2)}
1969
+ `, {
1970
+ mode: 384
1971
+ });
1972
+ await chmod(path, 384);
1973
+ return path;
1974
+ }
1954
1975
  async function removeStoredToken(baseUrl) {
1955
1976
  const removedKeychain = await deleteKeychainToken(baseUrl);
1956
1977
  const removedFile = await deleteFileToken();
@@ -2251,7 +2272,7 @@ async function resolveWithStoredToken(context) {
2251
2272
  function defaultConfigPath(env) {
2252
2273
  const explicitEnv = normalize3(env.WAVE_CONFIG_PATH);
2253
2274
  if (explicitEnv) return explicitEnv;
2254
- const candidate = join2(homedir2(), ".wave", "config.json");
2275
+ const candidate = join2(authHome(env), "config.json");
2255
2276
  return existsSync2(candidate) ? candidate : void 0;
2256
2277
  }
2257
2278
  async function resolveConfigFileContext(options, env) {
@@ -10129,6 +10150,11 @@ function absoluteAuthorizeUrl(baseUrl, authorizeUrl, uiBaseUrl = explicitUiBaseU
10129
10150
  if (/^https?:\/\//i.test(authorizeUrl)) {
10130
10151
  return authorizeUrl;
10131
10152
  }
10153
+ if (new URL(baseUrl).hostname.toLowerCase() === "api.ourwave.io") {
10154
+ const loginUrl = new URL("https://app.ourwave.io/login");
10155
+ loginUrl.searchParams.set("next", authorizeUrl);
10156
+ return loginUrl.toString();
10157
+ }
10132
10158
  return new URL(authorizeUrl, uiBaseUrl ?? localUiBaseUrlFor(baseUrl) ?? baseUrl).toString();
10133
10159
  }
10134
10160
  function canOpenBrowser(noBrowser) {
@@ -10215,6 +10241,7 @@ async function runTokenLogin(cmd, opts) {
10215
10241
  }
10216
10242
  const statusData = asRecord6(status.result.data.cliAuthStatus) ?? {};
10217
10243
  const storage = await writeStoredToken(token, baseUrl);
10244
+ const configPath2 = opts.saveBaseUrl ? await persistBaseUrl(baseUrl) : void 0;
10218
10245
  return printEnvelopeAndExit({
10219
10246
  envelope: envelope({
10220
10247
  ok: true,
@@ -10225,6 +10252,8 @@ async function runTokenLogin(cmd, opts) {
10225
10252
  mode: "token",
10226
10253
  tokenStored: true,
10227
10254
  storage,
10255
+ baseUrlSaved: Boolean(configPath2),
10256
+ configPath: configPath2 ?? null,
10228
10257
  status: redactUnknown(statusData, [token])
10229
10258
  }
10230
10259
  })
@@ -10367,6 +10396,7 @@ async function runBrowserLogin(cmd, opts) {
10367
10396
  });
10368
10397
  }
10369
10398
  const storage = await writeStoredToken(cliToken, baseUrl);
10399
+ const configPath2 = opts.saveBaseUrl ? await persistBaseUrl(baseUrl) : void 0;
10370
10400
  return printEnvelopeAndExit({
10371
10401
  envelope: envelope({
10372
10402
  ok: true,
@@ -10378,7 +10408,9 @@ async function runBrowserLogin(cmd, opts) {
10378
10408
  authorizeUrl,
10379
10409
  expiresAt: expiresAt ?? null,
10380
10410
  tokenStored: true,
10381
- storage
10411
+ storage,
10412
+ baseUrlSaved: Boolean(configPath2),
10413
+ configPath: configPath2 ?? null
10382
10414
  }
10383
10415
  })
10384
10416
  });
@@ -10506,7 +10538,7 @@ async function runAuthStatus(cmd) {
10506
10538
  });
10507
10539
  }
10508
10540
  function registerAuthCommands(program) {
10509
- program.command("login").description("Authenticate Wave CLI with a browser login session or provided CLI token").option("--no-browser", "Do not open a browser; include authorizeUrl in the JSON envelope").option("--token [token]", "Store and probe a provided CLI token instead of using browser login").option("--timeout-ms <timeoutMs>", "Login timeout in milliseconds").option("--poll-interval-ms <pollIntervalMs>", "Browser login polling interval in milliseconds").action((opts, cmd) => {
10541
+ program.command("login").description("Authenticate Wave CLI with a browser login session or provided CLI token").option("--no-browser", "Do not open a browser; include authorizeUrl in the JSON envelope").option("--save-base-url", "Persist the resolved API base URL for future Wave CLI commands").option("--token [token]", "Store and probe a provided CLI token instead of using browser login").option("--timeout-ms <timeoutMs>", "Login timeout in milliseconds").option("--poll-interval-ms <pollIntervalMs>", "Browser login polling interval in milliseconds").action((opts, cmd) => {
10510
10542
  const globals = cmd.optsWithGlobals();
10511
10543
  if (opts.token !== void 0 || globals.token !== void 0) {
10512
10544
  return runTokenLogin(cmd, opts);
@@ -15765,7 +15797,7 @@ function attachDiscoveryHelp(program) {
15765
15797
  }
15766
15798
 
15767
15799
  // src/version.ts
15768
- var CLI_VERSION = "0.1.57";
15800
+ var CLI_VERSION = "0.1.59";
15769
15801
 
15770
15802
  // src/cli.ts
15771
15803
  function buildCli(options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wave-ai-os/wave-cli",
3
- "version": "0.1.57",
3
+ "version": "0.1.59",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "wave": "dist/index.js"