zinkee 0.1.1 → 0.1.2

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,9 @@ 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";
1320
1323
  var ProfileResolutionError = class extends Error {
1321
1324
  name = "ProfileResolutionError";
1322
1325
  constructor(message) {
@@ -1329,6 +1332,10 @@ function isRecord2(value) {
1329
1332
  function toStringValue(value) {
1330
1333
  return typeof value === "string" ? value : void 0;
1331
1334
  }
1335
+ function toNonEmptyString(value) {
1336
+ const normalized = value?.trim();
1337
+ return normalized ? normalized : void 0;
1338
+ }
1332
1339
  function parseProfileConfig(value) {
1333
1340
  const profile = {};
1334
1341
  if (!isRecord2(value)) {
@@ -1384,6 +1391,9 @@ function readConfigIfExists(filePath = defaultConfigPath) {
1384
1391
  throw error;
1385
1392
  }
1386
1393
  }
1394
+ function readRuntimeConfig(filePath = defaultConfigPath) {
1395
+ return readConfigIfExists(filePath) ?? { profiles: {} };
1396
+ }
1387
1397
  function serializeProfileConfig(profile) {
1388
1398
  const serialized = {};
1389
1399
  for (const [key, value] of Object.entries(profile)) {
@@ -1492,16 +1502,80 @@ function resolveProfile(config, overrides = {}) {
1492
1502
  }
1493
1503
  throw new ProfileResolutionError("No profile could be resolved from config.");
1494
1504
  }
1505
+ function validateBaseUrl(value) {
1506
+ let parsed;
1507
+ try {
1508
+ parsed = new URL(value);
1509
+ } catch {
1510
+ throw new ProfileResolutionError(
1511
+ `Environment variable ${ENV_BASE_URL} must be an absolute http(s) URL.`
1512
+ );
1513
+ }
1514
+ if (!["http:", "https:"].includes(parsed.protocol) || !parsed.host) {
1515
+ throw new ProfileResolutionError(
1516
+ `Environment variable ${ENV_BASE_URL} must be an absolute http(s) URL.`
1517
+ );
1518
+ }
1519
+ return value.replace(/\/+$/, "");
1520
+ }
1521
+ function readEnvApiKey(env = process.env) {
1522
+ return toNonEmptyString(env[ENV_API_KEY]);
1523
+ }
1524
+ function readEnvBaseUrl(env = process.env) {
1525
+ const baseUrl = toNonEmptyString(env[ENV_BASE_URL]);
1526
+ if (!baseUrl) {
1527
+ return void 0;
1528
+ }
1529
+ return validateBaseUrl(baseUrl);
1530
+ }
1531
+ function readEnvProfile(env = process.env) {
1532
+ return toNonEmptyString(env[ENV_PROFILE]);
1533
+ }
1495
1534
 
1496
1535
  // src/runtime-context.ts
1497
1536
  function resolveRuntimeContext(config, overrides = {}) {
1498
- const profile = resolveProfile(config, overrides);
1537
+ const envProfile = readEnvProfile();
1538
+ const envApiKey = readEnvApiKey();
1539
+ const envBaseUrl = readEnvBaseUrl();
1540
+ const profileOverride = overrides.profile ?? envProfile;
1541
+ const apiKeyOverride = overrides.apiKey ?? envApiKey;
1542
+ const baseUrlOverride = overrides.baseUrl ?? envBaseUrl;
1543
+ if (apiKeyOverride && !baseUrlOverride) {
1544
+ throw new ProfileResolutionError(
1545
+ `Runtime API key was provided, but ${ENV_BASE_URL} or --base-url is missing.`
1546
+ );
1547
+ }
1548
+ if (baseUrlOverride && !apiKeyOverride) {
1549
+ throw new ProfileResolutionError(
1550
+ `Runtime base URL was provided, but ${ENV_API_KEY} or --api-key is missing.`
1551
+ );
1552
+ }
1553
+ if (apiKeyOverride && baseUrlOverride) {
1554
+ return {
1555
+ profile: profileOverride ?? "runtime",
1556
+ baseUrl: baseUrlOverride,
1557
+ apiKey: apiKeyOverride,
1558
+ readOnly: overrides.readOnly ?? false,
1559
+ json: overrides.json ?? false,
1560
+ profileSource: overrides.profile !== void 0 ? "flag" : envProfile !== void 0 ? "env" : void 0,
1561
+ baseUrlSource: overrides.baseUrl !== void 0 ? "flag" : envBaseUrl !== void 0 ? "env" : void 0,
1562
+ apiKeySource: overrides.apiKey !== void 0 ? "flag" : envApiKey !== void 0 ? "env" : void 0
1563
+ };
1564
+ }
1565
+ const profile = resolveProfile(config, {
1566
+ profile: profileOverride,
1567
+ baseUrl: baseUrlOverride,
1568
+ apiKey: apiKeyOverride
1569
+ });
1499
1570
  return {
1500
1571
  profile: profile.name,
1501
- baseUrl: overrides.baseUrl ?? profile.baseUrl,
1502
- apiKey: overrides.apiKey ?? profile.apiKey,
1572
+ baseUrl: baseUrlOverride ?? profile.baseUrl,
1573
+ apiKey: apiKeyOverride ?? profile.apiKey,
1503
1574
  readOnly: overrides.readOnly ?? false,
1504
- json: overrides.json ?? false
1575
+ json: overrides.json ?? false,
1576
+ profileSource: overrides.profile !== void 0 ? "flag" : envProfile !== void 0 ? "env" : "config",
1577
+ baseUrlSource: overrides.baseUrl !== void 0 ? "flag" : envBaseUrl !== void 0 ? "env" : "config",
1578
+ apiKeySource: overrides.apiKey !== void 0 ? "flag" : envApiKey !== void 0 ? "env" : "config"
1505
1579
  };
1506
1580
  }
1507
1581
 
@@ -2288,7 +2362,7 @@ async function createExampleAutomationContext(options = {}) {
2288
2362
  return null;
2289
2363
  }
2290
2364
  try {
2291
- const config = readConfig(options.configPath ?? defaultConfigPath);
2365
+ const config = readRuntimeConfig(options.configPath ?? defaultConfigPath);
2292
2366
  const runtime = resolveRuntimeContext(config, options.overrides);
2293
2367
  if (!runtime.baseUrl) {
2294
2368
  return null;
@@ -2907,7 +2981,7 @@ function toConfigurationError2(filePath, error) {
2907
2981
  }
2908
2982
  function createAutomationsApi(command, filePath) {
2909
2983
  try {
2910
- const config = readConfig(filePath);
2984
+ const config = readRuntimeConfig(filePath);
2911
2985
  const runtime = resolveRuntimeContext(config, getGlobalOptions2(command));
2912
2986
  if (!runtime.baseUrl) {
2913
2987
  throw new CliError(
@@ -4144,7 +4218,7 @@ function toConfigurationError3(filePath, error) {
4144
4218
  }
4145
4219
  function createCommentsApi(command, filePath) {
4146
4220
  try {
4147
- const config = readConfig(filePath);
4221
+ const config = readRuntimeConfig(filePath);
4148
4222
  const runtime = resolveRuntimeContext(config, getGlobalOptions3(command));
4149
4223
  if (!runtime.baseUrl) {
4150
4224
  throw new CliError(
@@ -4497,7 +4571,7 @@ function toConfigurationError4(filePath, error) {
4497
4571
  }
4498
4572
  function createDisplaysApi(command, filePath) {
4499
4573
  try {
4500
- const config = readConfig(filePath);
4574
+ const config = readRuntimeConfig(filePath);
4501
4575
  const runtime = resolveRuntimeContext(config, getGlobalOptions4(command));
4502
4576
  if (!runtime.baseUrl) {
4503
4577
  throw new CliError("configuration_error", `Resolved profile "${runtime.profile}" is missing a base URL.`);
@@ -5092,7 +5166,7 @@ function toConfigurationError5(filePath, error) {
5092
5166
  }
5093
5167
  function createFilesApi(command, filePath) {
5094
5168
  try {
5095
- const config = readConfig(filePath);
5169
+ const config = readRuntimeConfig(filePath);
5096
5170
  const runtime = resolveRuntimeContext(config, getGlobalOptions5(command));
5097
5171
  if (!runtime.baseUrl) {
5098
5172
  throw new CliError(
@@ -5311,7 +5385,7 @@ function toConfigurationError6(filePath, error) {
5311
5385
  }
5312
5386
  function createNavigationApi(command, filePath) {
5313
5387
  try {
5314
- const config = readConfig(filePath);
5388
+ const config = readRuntimeConfig(filePath);
5315
5389
  const runtime = resolveRuntimeContext(config, getGlobalOptions6(command));
5316
5390
  if (!runtime.baseUrl) {
5317
5391
  throw new CliError("configuration_error", `Resolved profile "${runtime.profile}" is missing a base URL.`);
@@ -5861,7 +5935,7 @@ function toConfigurationError7(filePath, error) {
5861
5935
  }
5862
5936
  function createRecordsApi(command, filePath) {
5863
5937
  try {
5864
- const config = readConfig(filePath);
5938
+ const config = readRuntimeConfig(filePath);
5865
5939
  const runtime = resolveRuntimeContext(config, getGlobalOptions8(command));
5866
5940
  if (!runtime.baseUrl) {
5867
5941
  throw new CliError(
@@ -6283,7 +6357,7 @@ function toConfigurationError8(filePath, error) {
6283
6357
  }
6284
6358
  function createSchemasApi(command, filePath) {
6285
6359
  try {
6286
- const config = readConfig(filePath);
6360
+ const config = readRuntimeConfig(filePath);
6287
6361
  const runtime = resolveRuntimeContext(config, getGlobalOptions9(command));
6288
6362
  if (!runtime.baseUrl) {
6289
6363
  throw new CliError(
@@ -6777,7 +6851,7 @@ function toConfigurationError9(filePath, error) {
6777
6851
  }
6778
6852
  function createTeamspaceApi(command, filePath) {
6779
6853
  try {
6780
- const config = readConfig(filePath);
6854
+ const config = readRuntimeConfig(filePath);
6781
6855
  const runtime = resolveRuntimeContext(config, getGlobalOptions10(command));
6782
6856
  if (!runtime.baseUrl) {
6783
6857
  throw new CliError(