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 +4 -0
- package/README.md +25 -0
- package/dist/index.js +87 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/automations.ts +2 -2
- package/src/commands/comments.ts +2 -2
- package/src/commands/displays.ts +2 -2
- package/src/commands/files.ts +2 -2
- package/src/commands/navigation.ts +2 -2
- package/src/commands/records.ts +2 -2
- package/src/commands/schemas.ts +2 -2
- package/src/commands/teamspace.ts +2 -2
- package/src/config.test.ts +130 -0
- package/src/config.ts +49 -0
- package/src/runtime-context.ts +82 -4
- package/src/types.ts +5 -0
- package/src/utils/examples.ts +2 -2
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
|
|
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:
|
|
1502
|
-
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
6854
|
+
const config = readRuntimeConfig(filePath);
|
|
6781
6855
|
const runtime = resolveRuntimeContext(config, getGlobalOptions10(command));
|
|
6782
6856
|
if (!runtime.baseUrl) {
|
|
6783
6857
|
throw new CliError(
|