zinkee 0.1.1 → 0.1.4

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/AGENTS.md CHANGED
@@ -13,6 +13,10 @@ Use this order when reasoning about behavior:
13
13
  ## Key Runtime Rules
14
14
 
15
15
  - Existing config compatibility is mandatory: `~/.config/zinkee/config.toml`
16
+ - Runtime env compatibility is mandatory for agentic/backend use:
17
+ - `ZINKEE_PROFILE`
18
+ - `ZINKEE_API_BASE_URL`
19
+ - `ZINKEE_API_KEY`
16
20
  - `--read-only` must block write/destructive commands before network calls
17
21
  - `--example <command path>` should return concrete usage examples without running the command
18
22
  - `--json` must keep stdout machine-readable
package/README.md CHANGED
@@ -64,6 +64,31 @@ base_url = "https://api.zinkee.com"
64
64
  | `--example` | Show examples for a command |
65
65
  | `--json` | Emit JSON output |
66
66
 
67
+ ## Runtime Resolution
68
+
69
+ The CLI resolves runtime settings with this precedence:
70
+
71
+ 1. CLI flags: `--profile`, `--base-url`, `--api-key`
72
+ 2. Environment variables: `ZINKEE_PROFILE`, `ZINKEE_API_BASE_URL`, `ZINKEE_API_KEY`
73
+ 3. `~/.config/zinkee/config.toml`
74
+
75
+ If a runtime API key is provided, a runtime base URL must also be provided.
76
+ If a runtime base URL is provided, a runtime API key must also be provided.
77
+
78
+ Example for local development without config:
79
+
80
+ ```bash
81
+ ZINKEE_API_BASE_URL=http://localhost:8088 \
82
+ ZINKEE_API_KEY="<workspace_token>" \
83
+ zinkee --json schemas list
84
+ ```
85
+
86
+ Example using an env-selected profile:
87
+
88
+ ```bash
89
+ ZINKEE_PROFILE=local zinkee --json schemas list
90
+ ```
91
+
67
92
  ## JSON Contract
68
93
 
69
94
  Successful commands emit:
package/dist/index.js CHANGED
@@ -1317,6 +1317,10 @@ import { dirname, join } from "path";
1317
1317
  import { parseTOML, stringifyTOML } from "confbox/toml";
1318
1318
  var defaultConfigPath = join(homedir(), ".config", "zinkee", "config.toml");
1319
1319
  var redactedSecretMask = "********";
1320
+ var ENV_API_KEY = "ZINKEE_API_KEY";
1321
+ var ENV_BASE_URL = "ZINKEE_API_BASE_URL";
1322
+ var ENV_PROFILE = "ZINKEE_PROFILE";
1323
+ var DEFAULT_PRODUCTION_BASE_URL = "https://api-z2.zinkee.com";
1320
1324
  var ProfileResolutionError = class extends Error {
1321
1325
  name = "ProfileResolutionError";
1322
1326
  constructor(message) {
@@ -1329,6 +1333,10 @@ function isRecord2(value) {
1329
1333
  function toStringValue(value) {
1330
1334
  return typeof value === "string" ? value : void 0;
1331
1335
  }
1336
+ function toNonEmptyString(value) {
1337
+ const normalized = value?.trim();
1338
+ return normalized ? normalized : void 0;
1339
+ }
1332
1340
  function parseProfileConfig(value) {
1333
1341
  const profile = {};
1334
1342
  if (!isRecord2(value)) {
@@ -1384,6 +1392,9 @@ function readConfigIfExists(filePath = defaultConfigPath) {
1384
1392
  throw error;
1385
1393
  }
1386
1394
  }
1395
+ function readRuntimeConfig(filePath = defaultConfigPath) {
1396
+ return readConfigIfExists(filePath) ?? { profiles: {} };
1397
+ }
1387
1398
  function serializeProfileConfig(profile) {
1388
1399
  const serialized = {};
1389
1400
  for (const [key, value] of Object.entries(profile)) {
@@ -1492,16 +1503,80 @@ function resolveProfile(config, overrides = {}) {
1492
1503
  }
1493
1504
  throw new ProfileResolutionError("No profile could be resolved from config.");
1494
1505
  }
1506
+ function validateBaseUrl(value) {
1507
+ let parsed;
1508
+ try {
1509
+ parsed = new URL(value);
1510
+ } catch {
1511
+ throw new ProfileResolutionError(
1512
+ `Environment variable ${ENV_BASE_URL} must be an absolute http(s) URL.`
1513
+ );
1514
+ }
1515
+ if (!["http:", "https:"].includes(parsed.protocol) || !parsed.host) {
1516
+ throw new ProfileResolutionError(
1517
+ `Environment variable ${ENV_BASE_URL} must be an absolute http(s) URL.`
1518
+ );
1519
+ }
1520
+ return value.replace(/\/+$/, "");
1521
+ }
1522
+ function readEnvApiKey(env = process.env) {
1523
+ return toNonEmptyString(env[ENV_API_KEY]);
1524
+ }
1525
+ function readEnvBaseUrl(env = process.env) {
1526
+ const baseUrl = toNonEmptyString(env[ENV_BASE_URL]);
1527
+ if (!baseUrl) {
1528
+ return void 0;
1529
+ }
1530
+ return validateBaseUrl(baseUrl);
1531
+ }
1532
+ function readEnvProfile(env = process.env) {
1533
+ return toNonEmptyString(env[ENV_PROFILE]);
1534
+ }
1495
1535
 
1496
1536
  // src/runtime-context.ts
1497
1537
  function resolveRuntimeContext(config, overrides = {}) {
1498
- const profile = resolveProfile(config, overrides);
1538
+ const envProfile = readEnvProfile();
1539
+ const envApiKey = readEnvApiKey();
1540
+ const envBaseUrl = readEnvBaseUrl();
1541
+ const profileOverride = overrides.profile ?? envProfile;
1542
+ const apiKeyOverride = overrides.apiKey ?? envApiKey;
1543
+ const baseUrlOverride = overrides.baseUrl ?? envBaseUrl;
1544
+ if (apiKeyOverride && !baseUrlOverride) {
1545
+ throw new ProfileResolutionError(
1546
+ `Runtime API key was provided, but ${ENV_BASE_URL} or --base-url is missing.`
1547
+ );
1548
+ }
1549
+ if (baseUrlOverride && !apiKeyOverride) {
1550
+ throw new ProfileResolutionError(
1551
+ `Runtime base URL was provided, but ${ENV_API_KEY} or --api-key is missing.`
1552
+ );
1553
+ }
1554
+ if (apiKeyOverride && baseUrlOverride) {
1555
+ return {
1556
+ profile: profileOverride ?? "runtime",
1557
+ baseUrl: baseUrlOverride,
1558
+ apiKey: apiKeyOverride,
1559
+ readOnly: overrides.readOnly ?? false,
1560
+ json: overrides.json ?? false,
1561
+ profileSource: overrides.profile !== void 0 ? "flag" : envProfile !== void 0 ? "env" : void 0,
1562
+ baseUrlSource: overrides.baseUrl !== void 0 ? "flag" : envBaseUrl !== void 0 ? "env" : void 0,
1563
+ apiKeySource: overrides.apiKey !== void 0 ? "flag" : envApiKey !== void 0 ? "env" : void 0
1564
+ };
1565
+ }
1566
+ const profile = resolveProfile(config, {
1567
+ profile: profileOverride,
1568
+ baseUrl: baseUrlOverride,
1569
+ apiKey: apiKeyOverride
1570
+ });
1499
1571
  return {
1500
1572
  profile: profile.name,
1501
- baseUrl: overrides.baseUrl ?? profile.baseUrl,
1502
- apiKey: overrides.apiKey ?? profile.apiKey,
1573
+ baseUrl: baseUrlOverride ?? profile.baseUrl ?? DEFAULT_PRODUCTION_BASE_URL,
1574
+ apiKey: apiKeyOverride ?? profile.apiKey,
1503
1575
  readOnly: overrides.readOnly ?? false,
1504
- json: overrides.json ?? false
1576
+ json: overrides.json ?? false,
1577
+ profileSource: overrides.profile !== void 0 ? "flag" : envProfile !== void 0 ? "env" : "config",
1578
+ baseUrlSource: overrides.baseUrl !== void 0 ? "flag" : envBaseUrl !== void 0 ? "env" : "config",
1579
+ apiKeySource: overrides.apiKey !== void 0 ? "flag" : envApiKey !== void 0 ? "env" : "config"
1505
1580
  };
1506
1581
  }
1507
1582
 
@@ -2288,7 +2363,7 @@ async function createExampleAutomationContext(options = {}) {
2288
2363
  return null;
2289
2364
  }
2290
2365
  try {
2291
- const config = readConfig(options.configPath ?? defaultConfigPath);
2366
+ const config = readRuntimeConfig(options.configPath ?? defaultConfigPath);
2292
2367
  const runtime = resolveRuntimeContext(config, options.overrides);
2293
2368
  if (!runtime.baseUrl) {
2294
2369
  return null;
@@ -2907,7 +2982,7 @@ function toConfigurationError2(filePath, error) {
2907
2982
  }
2908
2983
  function createAutomationsApi(command, filePath) {
2909
2984
  try {
2910
- const config = readConfig(filePath);
2985
+ const config = readRuntimeConfig(filePath);
2911
2986
  const runtime = resolveRuntimeContext(config, getGlobalOptions2(command));
2912
2987
  if (!runtime.baseUrl) {
2913
2988
  throw new CliError(
@@ -4144,7 +4219,7 @@ function toConfigurationError3(filePath, error) {
4144
4219
  }
4145
4220
  function createCommentsApi(command, filePath) {
4146
4221
  try {
4147
- const config = readConfig(filePath);
4222
+ const config = readRuntimeConfig(filePath);
4148
4223
  const runtime = resolveRuntimeContext(config, getGlobalOptions3(command));
4149
4224
  if (!runtime.baseUrl) {
4150
4225
  throw new CliError(
@@ -4497,7 +4572,7 @@ function toConfigurationError4(filePath, error) {
4497
4572
  }
4498
4573
  function createDisplaysApi(command, filePath) {
4499
4574
  try {
4500
- const config = readConfig(filePath);
4575
+ const config = readRuntimeConfig(filePath);
4501
4576
  const runtime = resolveRuntimeContext(config, getGlobalOptions4(command));
4502
4577
  if (!runtime.baseUrl) {
4503
4578
  throw new CliError("configuration_error", `Resolved profile "${runtime.profile}" is missing a base URL.`);
@@ -5092,7 +5167,7 @@ function toConfigurationError5(filePath, error) {
5092
5167
  }
5093
5168
  function createFilesApi(command, filePath) {
5094
5169
  try {
5095
- const config = readConfig(filePath);
5170
+ const config = readRuntimeConfig(filePath);
5096
5171
  const runtime = resolveRuntimeContext(config, getGlobalOptions5(command));
5097
5172
  if (!runtime.baseUrl) {
5098
5173
  throw new CliError(
@@ -5311,7 +5386,7 @@ function toConfigurationError6(filePath, error) {
5311
5386
  }
5312
5387
  function createNavigationApi(command, filePath) {
5313
5388
  try {
5314
- const config = readConfig(filePath);
5389
+ const config = readRuntimeConfig(filePath);
5315
5390
  const runtime = resolveRuntimeContext(config, getGlobalOptions6(command));
5316
5391
  if (!runtime.baseUrl) {
5317
5392
  throw new CliError("configuration_error", `Resolved profile "${runtime.profile}" is missing a base URL.`);
@@ -5861,7 +5936,7 @@ function toConfigurationError7(filePath, error) {
5861
5936
  }
5862
5937
  function createRecordsApi(command, filePath) {
5863
5938
  try {
5864
- const config = readConfig(filePath);
5939
+ const config = readRuntimeConfig(filePath);
5865
5940
  const runtime = resolveRuntimeContext(config, getGlobalOptions8(command));
5866
5941
  if (!runtime.baseUrl) {
5867
5942
  throw new CliError(
@@ -6283,7 +6358,7 @@ function toConfigurationError8(filePath, error) {
6283
6358
  }
6284
6359
  function createSchemasApi(command, filePath) {
6285
6360
  try {
6286
- const config = readConfig(filePath);
6361
+ const config = readRuntimeConfig(filePath);
6287
6362
  const runtime = resolveRuntimeContext(config, getGlobalOptions9(command));
6288
6363
  if (!runtime.baseUrl) {
6289
6364
  throw new CliError(
@@ -6777,7 +6852,7 @@ function toConfigurationError9(filePath, error) {
6777
6852
  }
6778
6853
  function createTeamspaceApi(command, filePath) {
6779
6854
  try {
6780
- const config = readConfig(filePath);
6855
+ const config = readRuntimeConfig(filePath);
6781
6856
  const runtime = resolveRuntimeContext(config, getGlobalOptions10(command));
6782
6857
  if (!runtime.baseUrl) {
6783
6858
  throw new CliError(