@naturali/cli 0.1.1 → 0.27.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.
Files changed (3) hide show
  1. package/README.md +20 -29
  2. package/dist/index.mjs +24 -105
  3. package/package.json +6 -7
package/README.md CHANGED
@@ -10,16 +10,15 @@ naturali configure
10
10
 
11
11
  ## Configuration
12
12
 
13
- `naturali configure` saves a profile to `~/.naturali/config.json`: the API base
14
- URL, a token (`nat_sk_…` or a session JWT), and an optional default project.
13
+ `naturali configure` saves a profile to `~/.naturali/config.json`: a token
14
+ (`nat_sk_…` or a session JWT). The CLI always talks to `https://api.naturali.ai`
15
+ — the only naturali.ai API origin, and not configurable.
15
16
 
16
17
  Environment variables take precedence, which is what CI should use:
17
18
 
18
19
  | Variable | Purpose |
19
20
  | --- | --- |
20
- | `NATURALI_BASE_URL` | API origin (e.g. `https://api.naturali.ai`). Overrides a profile's stored URL. |
21
- | `NATURALI_TOKEN` | Bearer credential. With `NATURALI_BASE_URL` set, no profile is needed. |
22
- | `NATURALI_PROJECT` | Default `project_id`, so project-scoped commands can omit `--project-id`. |
21
+ | `NATURALI_TOKEN` | Bearer credential. When set, no profile is needed. |
23
22
  | `NATURALI_PROFILE` | Profile to use when `--profile` is not passed. Defaults to `default`. |
24
23
 
25
24
  Use `--profile <name>` to switch profiles per command.
@@ -62,28 +61,15 @@ the spec, so you never have to know.
62
61
 
63
62
  An array-typed field is always sent as a list, even with one value.
64
63
 
65
- ### Shorthands for path parameters
66
-
67
- With a default project configured, `--project-id` can be omitted:
68
-
69
- ```bash
70
- export NATURALI_PROJECT=proj_V1StGXR8Z5jdHi6B
71
-
72
- naturali list-agents
73
- naturali get-agent agent_V1StGXR8Z5jdHi6B # positional fills the last path param
74
- naturali get-agent --id agent_V1StGXR8Z5jdHi6B
75
- ```
76
-
77
- A positional argument or `--id` fills the **one** path parameter still missing;
78
- when more than one is unresolved the command fails and names them rather than
79
- guessing.
64
+ Every path parameter a command needs must be passed explicitly; a command that
65
+ leaves one out fails and names it rather than guessing.
80
66
 
81
67
  ## Output
82
68
 
83
69
  The response body is printed to stdout as formatted JSON, so it pipes into `jq`:
84
70
 
85
71
  ```bash
86
- naturali list-agents | jq -r '.data[].id'
72
+ naturali list-agents --project-id proj_V1StGXR8Z5jdHi6B | jq -r '.data[].id'
87
73
  ```
88
74
 
89
75
  A non-2xx response prints the platform's error envelope to **stderr** and exits
@@ -108,12 +94,17 @@ there is no per-endpoint code.
108
94
 
109
95
  ## Versioning
110
96
 
111
- Releases are automatic, and the version tracks the API rather than a hand-picked
112
- schedule. Any change to `api/openapi/v1/*.yaml` regenerates this package and
113
- publishes it new endpoints become new commands without anyone editing the CLI
114
- and so does a change to `@naturali/sdk`, which is bundled in at build time
115
- rather than resolved from npm.
97
+ Releases are automatic, and the version is **shared with the API and with
98
+ `@naturali/sdk`** one number, one git tag, three packages. `@naturali/cli@0.28.0`
99
+ speaks to API 0.28.0, and `naturali --help` is generated from the specs that
100
+ release was built from, so it is always the authority on what your version can do.
101
+
102
+ Any change to `api/openapi/v1/*.yaml` regenerates this package and publishes it —
103
+ new endpoints become new commands without anyone editing the CLI — and so does a
104
+ change to `@naturali/sdk`, which is bundled in at build time rather than resolved
105
+ from npm.
116
106
 
117
- `fix:`/`chore:` changes release a patch, `feat:` a minor, and a breaking change
118
- is held for human approval before it reaches npm. See the repository
119
- [README](../../README.md#releasing).
107
+ `fix:`/`chore:` changes release a patch and `feat:` a minor, inferred from the
108
+ squashed commit subject. Every case publishes on merge, breaking changes
109
+ included. See the repository [README](../../README.md#releasing) and
110
+ [`CHANGELOG.md`](../../CHANGELOG.md).
package/dist/index.mjs CHANGED
@@ -1,6 +1,5 @@
1
1
  import * as path from "node:path";
2
2
  import { fileURLToPath } from "node:url";
3
- import input from "@inquirer/input";
4
3
  import password from "@inquirer/password";
5
4
  import { Command } from "commander";
6
5
  import * as fs from "node:fs";
@@ -18,7 +17,7 @@ var __exportAll = (all, no_symbols) => {
18
17
  };
19
18
  //#endregion
20
19
  //#region package.json
21
- var version = "0.1.1";
20
+ var version = "0.27.1";
22
21
  //#endregion
23
22
  //#region ../sdk/src/generated/core/bodySerializer.gen.ts
24
23
  const jsonBodySerializer = { bodySerializer: (body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value) };
@@ -1735,6 +1734,8 @@ const bindResource = (SdkClass, client) => {
1735
1734
  return value;
1736
1735
  } });
1737
1736
  };
1737
+ /** The naturali.ai API origin. Not configurable — there is only one. */
1738
+ const API_BASE_URL$1 = "https://api.naturali.ai";
1738
1739
  /**
1739
1740
  * The naturali.ai API client.
1740
1741
  *
@@ -1745,7 +1746,6 @@ const bindResource = (SdkClass, client) => {
1745
1746
  * import { NaturaliClient } from '@naturali/sdk';
1746
1747
  *
1747
1748
  * const naturali = new NaturaliClient({
1748
- * baseUrl: 'https://api.naturali.ai',
1749
1749
  * token: process.env.NATURALI_TOKEN,
1750
1750
  * });
1751
1751
  *
@@ -1775,9 +1775,9 @@ var NaturaliClient = class {
1775
1775
  traces;
1776
1776
  /** The underlying HTTP client, for interceptors or one-off requests. */
1777
1777
  http;
1778
- constructor({ baseUrl, token, headers } = {}) {
1778
+ constructor({ token, headers } = {}) {
1779
1779
  this.http = createClient(createConfig({
1780
- baseUrl: baseUrl ?? "",
1780
+ baseUrl: API_BASE_URL$1,
1781
1781
  headers: {
1782
1782
  ...token ? { Authorization: `Bearer ${token}` } : {},
1783
1783
  ...headers
@@ -1820,6 +1820,8 @@ var src_exports = /* @__PURE__ */ __exportAll({
1820
1820
  });
1821
1821
  //#endregion
1822
1822
  //#region src/config.ts
1823
+ /** The naturali.ai API origin. Not configurable — there is only one. */
1824
+ const API_BASE_URL = "https://api.naturali.ai";
1823
1825
  const CONFIG_FILE = path.join(os.homedir(), ".naturali", "config.json");
1824
1826
  const readConfig = () => {
1825
1827
  try {
@@ -1838,57 +1840,28 @@ const writeProfile = (name, profile) => {
1838
1840
  const configFilePath = () => {
1839
1841
  return CONFIG_FILE;
1840
1842
  };
1841
- /**
1842
- * The resolved project is deliberately *not* sent as a header. It exists to fill
1843
- * the `project_id` **path parameter** a command left out; the API authorizes that
1844
- * path parameter and nothing else.
1845
- */
1846
1843
  const buildClient = (args) => {
1847
- const { baseUrl, token } = args;
1844
+ const { token } = args;
1848
1845
  return createClient(createConfig({
1849
- baseUrl,
1846
+ baseUrl: API_BASE_URL,
1850
1847
  headers: { ...token ? { Authorization: `Bearer ${token}` } : {} }
1851
1848
  }));
1852
1849
  };
1853
1850
  /**
1854
1851
  * Resolves credentials in precedence order:
1855
1852
  *
1856
- * 1. `NATURALI_BASE_URL` + `NATURALI_TOKEN` — both set, no profile needed.
1857
- * 2. A named profile (`--profile` → `NATURALI_PROFILE` → `default`), with
1858
- * `NATURALI_BASE_URL` / `NATURALI_PROJECT` overriding its stored values.
1859
- * 3. `NATURALI_BASE_URL` alone — an unauthenticated client, enough for the
1860
- * public endpoints (`/health`, `/v1/openapi.json`, login, signup).
1853
+ * 1. `NATURALI_TOKEN` — no profile needed.
1854
+ * 2. A named profile (`--profile` → `NATURALI_PROFILE` → `default`).
1861
1855
  *
1862
- * Exits with an actionable message when none of the three apply.
1856
+ * Exits with an actionable message when neither applies.
1863
1857
  */
1864
1858
  const resolveContext = (profileName) => {
1865
- const envBaseUrl = process.env["NATURALI_BASE_URL"];
1866
1859
  const envToken = process.env["NATURALI_TOKEN"];
1867
- const envProject = process.env["NATURALI_PROJECT"];
1868
- if (envBaseUrl && envToken) return {
1869
- client: buildClient({
1870
- baseUrl: envBaseUrl,
1871
- token: envToken
1872
- }),
1873
- project: envProject
1874
- };
1860
+ if (envToken) return { client: buildClient({ token: envToken }) };
1875
1861
  const name = profileName ?? process.env["NATURALI_PROFILE"] ?? "default";
1876
1862
  const profile = readConfig()[name];
1877
- if (profile) {
1878
- const project = envProject ?? profile.project;
1879
- return {
1880
- client: buildClient({
1881
- baseUrl: envBaseUrl ?? profile.baseUrl,
1882
- token: profile.token
1883
- }),
1884
- project
1885
- };
1886
- }
1887
- if (envBaseUrl) return {
1888
- client: buildClient({ baseUrl: envBaseUrl }),
1889
- project: envProject
1890
- };
1891
- console.error(`Profile "${name}" not found. Run: naturali configure${name !== "default" ? ` --profile ${name}` : ""}\nOr set NATURALI_BASE_URL and NATURALI_TOKEN in the environment.`);
1863
+ if (profile) return { client: buildClient({ token: profile.token }) };
1864
+ console.error(`Profile "${name}" not found. Run: naturali configure${name !== "default" ? ` --profile ${name}` : ""}\nOr set NATURALI_TOKEN in the environment.`);
1892
1865
  process.exit(1);
1893
1866
  };
1894
1867
  //#endregion
@@ -1922,28 +1895,6 @@ const parseUnknownWithRepeats = (args) => {
1922
1895
  };
1923
1896
  };
1924
1897
  /**
1925
- * Extracts bare (non-`--flag`) tokens, e.g. the `agent_123` in
1926
- * `naturali get-agent agent_123`. Walks the same flag/value pairing as
1927
- * {@link parseUnknownWithRepeats} so a flag's value is never mistaken for a
1928
- * standalone positional argument.
1929
- */
1930
- const extractPositionalArgs = (args) => {
1931
- const { cliArgs } = args;
1932
- const positional = [];
1933
- for (let i = 0; i < cliArgs.length; i++) {
1934
- const arg = cliArgs[i];
1935
- if (arg === void 0) continue;
1936
- if (!arg.startsWith("--")) {
1937
- positional.push(arg);
1938
- continue;
1939
- }
1940
- if (arg.indexOf("=") > 2) continue;
1941
- const next = cliArgs[i + 1];
1942
- if (next !== void 0 && !next.startsWith("--")) i++;
1943
- }
1944
- return positional;
1945
- };
1946
- /**
1947
1898
  * Normalizes a flag name to camelCase so kebab, snake and camel spellings of
1948
1899
  * the same parameter all match: `agent-id`, `agent_id` and `agentId` → `agentId`.
1949
1900
  */
@@ -2006,9 +1957,6 @@ const oneLine = (text) => {
2006
1957
  const GLOBAL_FLAGS = [{
2007
1958
  name: "profile",
2008
1959
  description: "config profile to use"
2009
- }, {
2010
- name: "id",
2011
- description: "alias for the command's remaining path parameter"
2012
1960
  }];
2013
1961
  /**
2014
1962
  * Renders `naturali <command> --help`: the operation's own description, a link
@@ -2058,12 +2006,8 @@ const renderCommandHelp = (args) => {
2058
2006
  };
2059
2007
  //#endregion
2060
2008
  //#region src/routeCall.ts
2061
- /**
2062
- * Flags the dispatcher owns, never forwarded to the API: `--profile` selects
2063
- * credentials, `--id` is the generic alias for a command's remaining path
2064
- * parameter.
2065
- */
2066
- const RESERVED_FLAGS = /* @__PURE__ */ new Set(["profile", "id"]);
2009
+ /** Flags the dispatcher owns, never forwarded to the API. */
2010
+ const RESERVED_FLAGS = /* @__PURE__ */ new Set(["profile"]);
2067
2011
  const placeFlags = (args) => {
2068
2012
  const { route, parsedFlags, call } = args;
2069
2013
  const typeByCanonical = new Map(route.flags.map((flag) => {
@@ -2097,21 +2041,11 @@ const placeFlags = (args) => {
2097
2041
  /**
2098
2042
  * Turns parsed flags into the `{ path, query, body }` an SDK method expects.
2099
2043
  *
2100
- * Two conveniences fill path parameters the caller left out, in this order —
2101
- * an explicit flag always wins over both:
2102
- *
2103
- * 1. `project_id` comes from the resolved default project (`NATURALI_PROJECT`
2104
- * or the profile's `project`), since almost every resource is nested under
2105
- * a project.
2106
- * 2. `--id <value>`, or a bare positional argument, fills the one path
2107
- * parameter still missing — so `naturali get-agent agent_123` works once a
2108
- * default project is configured.
2109
- *
2110
2044
  * A path parameter that stays unresolved is reported rather than sent: a
2111
2045
  * request with a literal `{agent_id}` left in the URL is never useful.
2112
2046
  */
2113
2047
  const buildRouteCall = (args) => {
2114
- const { route, parsedFlags, positionalArgs, defaultProject } = args;
2048
+ const { route, parsedFlags } = args;
2115
2049
  const call = {
2116
2050
  path: {},
2117
2051
  query: {},
@@ -2122,12 +2056,6 @@ const buildRouteCall = (args) => {
2122
2056
  parsedFlags,
2123
2057
  call
2124
2058
  });
2125
- if (defaultProject && route.pathParams.includes("project_id") && !("project_id" in call.path)) call.path["project_id"] = defaultProject;
2126
- const unresolved = route.pathParams.filter((param) => {
2127
- return !(param in call.path);
2128
- });
2129
- const idAlias = parsedFlags.single["id"] ?? positionalArgs[0];
2130
- if (idAlias !== void 0 && unresolved.length === 1) call.path[unresolved[0]] = parseFlagValue(idAlias);
2131
2059
  const missingPathParams = route.pathParams.filter((param) => {
2132
2060
  return !(param in call.path);
2133
2061
  });
@@ -2217,13 +2145,13 @@ const dispatchCommand = async (args) => {
2217
2145
  const context = resolveContext(parsedFlags.single["profile"] ?? profileFromProgram);
2218
2146
  const built = buildRouteCall({
2219
2147
  route,
2220
- parsedFlags,
2221
- positionalArgs: extractPositionalArgs({ cliArgs: rawArgs }),
2222
- defaultProject: context.project
2148
+ parsedFlags
2223
2149
  });
2224
2150
  if (!built.ok) {
2225
- const [first] = built.missingPathParams;
2226
- console.error(`Missing required path parameter(s): ${built.missingPathParams.join(", ")}.\nProvide with --${first?.replace(/_/g, "-")} <value>, --id <value>, or a positional argument: naturali ${commandName} <id>` + (built.missingPathParams.includes("project_id") ? `\nTo default it for every command, set NATURALI_PROJECT or run: naturali configure` : ""));
2151
+ const flagsHint = built.missingPathParams.map((param) => {
2152
+ return `--${param.replace(/_/g, "-")} <value>`;
2153
+ }).join(", ");
2154
+ console.error(`Missing required path parameter(s): ${built.missingPathParams.join(", ")}.\nProvide with ${flagsHint}.`);
2227
2155
  process.exit(1);
2228
2156
  }
2229
2157
  const serviceClass = resolveServiceClass(route.serviceClass);
@@ -5243,17 +5171,8 @@ const routes = {
5243
5171
  */
5244
5172
  const addConfigureCommand = (program) => {
5245
5173
  program.command("configure").description(`Save credentials to a named profile (${configFilePath()})`).option("-p, --profile <name>", "profile name", "default").action(async (opts) => {
5246
- const baseUrl = await input({
5247
- message: "Base URL:",
5248
- default: "https://api.naturali.ai"
5249
- });
5250
5174
  const token = await password({ message: "Token (nat_sk_… or a session JWT, hidden):" });
5251
- const project = await input({ message: "Default project id (optional):" });
5252
- writeProfile(opts.profile, {
5253
- baseUrl,
5254
- token,
5255
- ...project ? { project } : {}
5256
- });
5175
+ writeProfile(opts.profile, { token });
5257
5176
  console.log(`Profile "${opts.profile}" saved to ${configFilePath()}.`);
5258
5177
  });
5259
5178
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturali/cli",
3
- "version": "0.1.1",
3
+ "version": "0.27.1",
4
4
  "description": "Command-line interface for the naturali.ai API, generated from its OpenAPI specs",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,7 +21,6 @@
21
21
  "access": "public"
22
22
  },
23
23
  "dependencies": {
24
- "@inquirer/input": "^5.1.2",
25
24
  "@inquirer/password": "^5.1.1",
26
25
  "commander": "^15.0.0"
27
26
  },
@@ -32,12 +31,12 @@
32
31
  "tsx": "^4.23.1",
33
32
  "typescript": "~6.0.3",
34
33
  "vitest": "^4.1.10",
35
- "@naturali/sdk": "0.1.1"
34
+ "@naturali/sdk": "0.27.1"
36
35
  },
37
36
  "scripts": {
38
- "generate": "pnpm --filter @naturali/sdk generate && tsx scripts/generate.ts",
39
- "typecheck": "pnpm generate && tsc --noEmit",
40
- "test": "pnpm generate && vitest run",
41
- "build": "pnpm generate && tsdown"
37
+ "generate": "tsx scripts/generate.ts",
38
+ "typecheck": "tsc --noEmit",
39
+ "test": "vitest run",
40
+ "build": "tsdown"
42
41
  }
43
42
  }