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/package.json
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
type UpdateAutomationInput,
|
|
21
21
|
} from "../api/automations.js";
|
|
22
22
|
import { ApiClient } from "../client.js";
|
|
23
|
-
import { defaultConfigPath,
|
|
23
|
+
import { defaultConfigPath, readRuntimeConfig } from "../config.js";
|
|
24
24
|
import { parseJsonAssignment, parseKeyValue } from "../parsers/kv.js";
|
|
25
25
|
import {
|
|
26
26
|
normalizeUuidOrSlug,
|
|
@@ -210,7 +210,7 @@ function createAutomationsApi(
|
|
|
210
210
|
filePath: string,
|
|
211
211
|
): { api: AutomationsApi; json: boolean; readOnly: boolean } {
|
|
212
212
|
try {
|
|
213
|
-
const config =
|
|
213
|
+
const config = readRuntimeConfig(filePath);
|
|
214
214
|
const runtime = resolveRuntimeContext(config, getGlobalOptions(command));
|
|
215
215
|
|
|
216
216
|
if (!runtime.baseUrl) {
|
package/src/commands/comments.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
type CreateCommentInput,
|
|
9
9
|
} from "../api/comments.js";
|
|
10
10
|
import { ApiClient } from "../client.js";
|
|
11
|
-
import { defaultConfigPath,
|
|
11
|
+
import { defaultConfigPath, readRuntimeConfig } from "../config.js";
|
|
12
12
|
import {
|
|
13
13
|
normalizeUuidOrSlug,
|
|
14
14
|
type NormalizedUuidOrSlug,
|
|
@@ -85,7 +85,7 @@ function createCommentsApi(
|
|
|
85
85
|
filePath: string,
|
|
86
86
|
): { api: CommentsApi; json: boolean; readOnly: boolean } {
|
|
87
87
|
try {
|
|
88
|
-
const config =
|
|
88
|
+
const config = readRuntimeConfig(filePath);
|
|
89
89
|
const runtime = resolveRuntimeContext(config, getGlobalOptions(command));
|
|
90
90
|
|
|
91
91
|
if (!runtime.baseUrl) {
|
package/src/commands/displays.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Command, InvalidArgumentError } from "commander";
|
|
|
5
5
|
|
|
6
6
|
import { DisplaysApi, type DisplayResource, type DisplaySummary } from "../api/displays.js";
|
|
7
7
|
import { ApiClient } from "../client.js";
|
|
8
|
-
import { defaultConfigPath,
|
|
8
|
+
import { defaultConfigPath, readRuntimeConfig } from "../config.js";
|
|
9
9
|
import { normalizeUuidOrSlug, type NormalizedUuidOrSlug } from "../parsers/selectors.js";
|
|
10
10
|
import { resolveRuntimeContext } from "../runtime-context.js";
|
|
11
11
|
import type { RuntimeOverrides } from "../types.js";
|
|
@@ -148,7 +148,7 @@ function createDisplaysApi(
|
|
|
148
148
|
filePath: string,
|
|
149
149
|
): { api: DisplaysApi; json: boolean; readOnly: boolean } {
|
|
150
150
|
try {
|
|
151
|
-
const config =
|
|
151
|
+
const config = readRuntimeConfig(filePath);
|
|
152
152
|
const runtime = resolveRuntimeContext(config, getGlobalOptions(command));
|
|
153
153
|
|
|
154
154
|
if (!runtime.baseUrl) {
|
package/src/commands/files.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { Command, InvalidArgumentError } from "commander";
|
|
|
6
6
|
|
|
7
7
|
import { FilesApi } from "../api/files.js";
|
|
8
8
|
import { ApiClient } from "../client.js";
|
|
9
|
-
import { defaultConfigPath,
|
|
9
|
+
import { defaultConfigPath, readRuntimeConfig } from "../config.js";
|
|
10
10
|
import { normalizeUuidOrSlug, type NormalizedUuidOrSlug } from "../parsers/selectors.js";
|
|
11
11
|
import { resolveRuntimeContext } from "../runtime-context.js";
|
|
12
12
|
import type { JsonObject, RuntimeOverrides } from "../types.js";
|
|
@@ -83,7 +83,7 @@ function createFilesApi(
|
|
|
83
83
|
filePath: string,
|
|
84
84
|
): { api: FilesApi; json: boolean; readOnly: boolean } {
|
|
85
85
|
try {
|
|
86
|
-
const config =
|
|
86
|
+
const config = readRuntimeConfig(filePath);
|
|
87
87
|
const runtime = resolveRuntimeContext(config, getGlobalOptions(command));
|
|
88
88
|
|
|
89
89
|
if (!runtime.baseUrl) {
|
|
@@ -5,7 +5,7 @@ import { Command, InvalidArgumentError } from "commander";
|
|
|
5
5
|
|
|
6
6
|
import { NavigationApi, type NavigationFolder } from "../api/navigation.js";
|
|
7
7
|
import { ApiClient } from "../client.js";
|
|
8
|
-
import { defaultConfigPath,
|
|
8
|
+
import { defaultConfigPath, readRuntimeConfig } from "../config.js";
|
|
9
9
|
import { normalizeUuidOrSlug, type NormalizedUuidOrSlug } from "../parsers/selectors.js";
|
|
10
10
|
import { resolveRuntimeContext } from "../runtime-context.js";
|
|
11
11
|
import type { RuntimeOverrides } from "../types.js";
|
|
@@ -72,7 +72,7 @@ function createNavigationApi(
|
|
|
72
72
|
filePath: string,
|
|
73
73
|
): { api: NavigationApi; json: boolean; readOnly: boolean } {
|
|
74
74
|
try {
|
|
75
|
-
const config =
|
|
75
|
+
const config = readRuntimeConfig(filePath);
|
|
76
76
|
const runtime = resolveRuntimeContext(config, getGlobalOptions(command));
|
|
77
77
|
|
|
78
78
|
if (!runtime.baseUrl) {
|
package/src/commands/records.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from "../api/records.js";
|
|
15
15
|
import { SchemasApi } from "../api/schemas.js";
|
|
16
16
|
import { ApiClient } from "../client.js";
|
|
17
|
-
import { defaultConfigPath,
|
|
17
|
+
import { defaultConfigPath, readRuntimeConfig } from "../config.js";
|
|
18
18
|
import {
|
|
19
19
|
type ParsedExpression,
|
|
20
20
|
parseExpression,
|
|
@@ -128,7 +128,7 @@ function createRecordsApi(
|
|
|
128
128
|
readOnly: boolean;
|
|
129
129
|
} {
|
|
130
130
|
try {
|
|
131
|
-
const config =
|
|
131
|
+
const config = readRuntimeConfig(filePath);
|
|
132
132
|
const runtime = resolveRuntimeContext(config, getGlobalOptions(command));
|
|
133
133
|
|
|
134
134
|
if (!runtime.baseUrl) {
|
package/src/commands/schemas.ts
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from "../api/schemas.js";
|
|
15
15
|
import { ApiClientError } from "../client.js";
|
|
16
16
|
import { ApiClient } from "../client.js";
|
|
17
|
-
import { defaultConfigPath,
|
|
17
|
+
import { defaultConfigPath, readRuntimeConfig } from "../config.js";
|
|
18
18
|
import {
|
|
19
19
|
normalizeUuidOrSlug,
|
|
20
20
|
type NormalizedUuidOrSlug,
|
|
@@ -124,7 +124,7 @@ function createSchemasApi(
|
|
|
124
124
|
filePath: string,
|
|
125
125
|
): { api: SchemasApi; json: boolean; profile: string; readOnly: boolean } {
|
|
126
126
|
try {
|
|
127
|
-
const config =
|
|
127
|
+
const config = readRuntimeConfig(filePath);
|
|
128
128
|
const runtime = resolveRuntimeContext(config, getGlobalOptions(command));
|
|
129
129
|
|
|
130
130
|
if (!runtime.baseUrl) {
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
type UpdateTeamspaceFolderInput,
|
|
11
11
|
} from "../api/teamspace.js";
|
|
12
12
|
import { ApiClient } from "../client.js";
|
|
13
|
-
import { defaultConfigPath,
|
|
13
|
+
import { defaultConfigPath, readRuntimeConfig } from "../config.js";
|
|
14
14
|
import {
|
|
15
15
|
normalizeUuidOrSlug,
|
|
16
16
|
type NormalizedUuidOrSlug,
|
|
@@ -98,7 +98,7 @@ function createTeamspaceApi(
|
|
|
98
98
|
filePath: string,
|
|
99
99
|
): { api: TeamspaceApi; json: boolean; readOnly: boolean } {
|
|
100
100
|
try {
|
|
101
|
-
const config =
|
|
101
|
+
const config = readRuntimeConfig(filePath);
|
|
102
102
|
const runtime = resolveRuntimeContext(config, getGlobalOptions(command));
|
|
103
103
|
|
|
104
104
|
if (!runtime.baseUrl) {
|
package/src/config.test.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
ENV_API_KEY,
|
|
5
|
+
ENV_BASE_URL,
|
|
6
|
+
ENV_PROFILE,
|
|
4
7
|
parseConfig,
|
|
5
8
|
redactConfig,
|
|
6
9
|
redactSecret,
|
|
10
|
+
readRuntimeConfig,
|
|
11
|
+
readEnvApiKey,
|
|
12
|
+
readEnvBaseUrl,
|
|
13
|
+
readEnvProfile,
|
|
7
14
|
removeProfileConfig,
|
|
8
15
|
resolveProfile,
|
|
9
16
|
serializeConfig,
|
|
@@ -30,6 +37,14 @@ retry_count = 3
|
|
|
30
37
|
});
|
|
31
38
|
});
|
|
32
39
|
|
|
40
|
+
describe("readRuntimeConfig", () => {
|
|
41
|
+
it("returns an empty runtime config when the file does not exist", () => {
|
|
42
|
+
expect(readRuntimeConfig("/tmp/definitely-missing-zinkee-config.toml")).toEqual({
|
|
43
|
+
profiles: {},
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
33
48
|
describe("resolveProfile", () => {
|
|
34
49
|
it("prefers explicit profile over default_profile", () => {
|
|
35
50
|
const config = {
|
|
@@ -89,8 +104,123 @@ describe("resolveRuntimeContext", () => {
|
|
|
89
104
|
apiKey: "override-token",
|
|
90
105
|
readOnly: true,
|
|
91
106
|
json: false,
|
|
107
|
+
profileSource: "flag",
|
|
108
|
+
baseUrlSource: "flag",
|
|
109
|
+
apiKeySource: "flag",
|
|
92
110
|
});
|
|
93
111
|
});
|
|
112
|
+
|
|
113
|
+
it("resolves runtime directly from env vars without config", () => {
|
|
114
|
+
const previousApiKey = process.env[ENV_API_KEY];
|
|
115
|
+
const previousBaseUrl = process.env[ENV_BASE_URL];
|
|
116
|
+
const previousProfile = process.env[ENV_PROFILE];
|
|
117
|
+
process.env[ENV_API_KEY] = "env-token";
|
|
118
|
+
process.env[ENV_BASE_URL] = "http://localhost:8088/";
|
|
119
|
+
delete process.env[ENV_PROFILE];
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
expect(resolveRuntimeContext({ profiles: {} }, {})).toEqual({
|
|
123
|
+
profile: "runtime",
|
|
124
|
+
baseUrl: "http://localhost:8088",
|
|
125
|
+
apiKey: "env-token",
|
|
126
|
+
readOnly: false,
|
|
127
|
+
json: false,
|
|
128
|
+
profileSource: undefined,
|
|
129
|
+
baseUrlSource: "env",
|
|
130
|
+
apiKeySource: "env",
|
|
131
|
+
});
|
|
132
|
+
} finally {
|
|
133
|
+
if (previousApiKey === undefined) delete process.env[ENV_API_KEY];
|
|
134
|
+
else process.env[ENV_API_KEY] = previousApiKey;
|
|
135
|
+
if (previousBaseUrl === undefined) delete process.env[ENV_BASE_URL];
|
|
136
|
+
else process.env[ENV_BASE_URL] = previousBaseUrl;
|
|
137
|
+
if (previousProfile === undefined) delete process.env[ENV_PROFILE];
|
|
138
|
+
else process.env[ENV_PROFILE] = previousProfile;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("prefers env vars over config values", () => {
|
|
143
|
+
const previousApiKey = process.env[ENV_API_KEY];
|
|
144
|
+
const previousBaseUrl = process.env[ENV_BASE_URL];
|
|
145
|
+
const previousProfile = process.env[ENV_PROFILE];
|
|
146
|
+
process.env[ENV_API_KEY] = "env-token";
|
|
147
|
+
process.env[ENV_BASE_URL] = "http://localhost:8088";
|
|
148
|
+
process.env[ENV_PROFILE] = "local";
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
expect(
|
|
152
|
+
resolveRuntimeContext({
|
|
153
|
+
defaultProfile: "prod",
|
|
154
|
+
profiles: {
|
|
155
|
+
prod: { apiKey: "prod-token", baseUrl: "https://api.zinkee.com" },
|
|
156
|
+
local: { apiKey: "local-token", baseUrl: "http://127.0.0.1:9999" },
|
|
157
|
+
},
|
|
158
|
+
}, {}),
|
|
159
|
+
).toEqual({
|
|
160
|
+
profile: "local",
|
|
161
|
+
baseUrl: "http://localhost:8088",
|
|
162
|
+
apiKey: "env-token",
|
|
163
|
+
readOnly: false,
|
|
164
|
+
json: false,
|
|
165
|
+
profileSource: "env",
|
|
166
|
+
baseUrlSource: "env",
|
|
167
|
+
apiKeySource: "env",
|
|
168
|
+
});
|
|
169
|
+
} finally {
|
|
170
|
+
if (previousApiKey === undefined) delete process.env[ENV_API_KEY];
|
|
171
|
+
else process.env[ENV_API_KEY] = previousApiKey;
|
|
172
|
+
if (previousBaseUrl === undefined) delete process.env[ENV_BASE_URL];
|
|
173
|
+
else process.env[ENV_BASE_URL] = previousBaseUrl;
|
|
174
|
+
if (previousProfile === undefined) delete process.env[ENV_PROFILE];
|
|
175
|
+
else process.env[ENV_PROFILE] = previousProfile;
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("throws a clear error when runtime api key is provided without base url", () => {
|
|
180
|
+
const previousApiKey = process.env[ENV_API_KEY];
|
|
181
|
+
const previousBaseUrl = process.env[ENV_BASE_URL];
|
|
182
|
+
process.env[ENV_API_KEY] = "env-token";
|
|
183
|
+
delete process.env[ENV_BASE_URL];
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
expect(() => resolveRuntimeContext({ profiles: {} }, {})).toThrowError(
|
|
187
|
+
`Runtime API key was provided, but ${ENV_BASE_URL} or --base-url is missing.`,
|
|
188
|
+
);
|
|
189
|
+
} finally {
|
|
190
|
+
if (previousApiKey === undefined) delete process.env[ENV_API_KEY];
|
|
191
|
+
else process.env[ENV_API_KEY] = previousApiKey;
|
|
192
|
+
if (previousBaseUrl === undefined) delete process.env[ENV_BASE_URL];
|
|
193
|
+
else process.env[ENV_BASE_URL] = previousBaseUrl;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("throws a clear error when runtime base url is provided without api key", () => {
|
|
198
|
+
const previousApiKey = process.env[ENV_API_KEY];
|
|
199
|
+
const previousBaseUrl = process.env[ENV_BASE_URL];
|
|
200
|
+
delete process.env[ENV_API_KEY];
|
|
201
|
+
process.env[ENV_BASE_URL] = "http://localhost:8088";
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
expect(() => resolveRuntimeContext({ profiles: {} }, {})).toThrowError(
|
|
205
|
+
`Runtime base URL was provided, but ${ENV_API_KEY} or --api-key is missing.`,
|
|
206
|
+
);
|
|
207
|
+
} finally {
|
|
208
|
+
if (previousApiKey === undefined) delete process.env[ENV_API_KEY];
|
|
209
|
+
else process.env[ENV_API_KEY] = previousApiKey;
|
|
210
|
+
if (previousBaseUrl === undefined) delete process.env[ENV_BASE_URL];
|
|
211
|
+
else process.env[ENV_BASE_URL] = previousBaseUrl;
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe("env readers", () => {
|
|
217
|
+
it("normalizes env values", () => {
|
|
218
|
+
expect(readEnvApiKey({ [ENV_API_KEY]: " token " } as NodeJS.ProcessEnv)).toBe("token");
|
|
219
|
+
expect(readEnvProfile({ [ENV_PROFILE]: " local " } as NodeJS.ProcessEnv)).toBe("local");
|
|
220
|
+
expect(readEnvBaseUrl({ [ENV_BASE_URL]: " http://localhost:8088/ " } as NodeJS.ProcessEnv)).toBe(
|
|
221
|
+
"http://localhost:8088",
|
|
222
|
+
);
|
|
223
|
+
});
|
|
94
224
|
});
|
|
95
225
|
|
|
96
226
|
describe("config mutation helpers", () => {
|
package/src/config.ts
CHANGED
|
@@ -8,6 +8,9 @@ import type { AppConfig, ProfileConfig, RuntimeOverrides, ResolvedProfile } from
|
|
|
8
8
|
|
|
9
9
|
export const defaultConfigPath = join(homedir(), ".config", "zinkee", "config.toml");
|
|
10
10
|
export const redactedSecretMask = "********";
|
|
11
|
+
export const ENV_API_KEY = "ZINKEE_API_KEY";
|
|
12
|
+
export const ENV_BASE_URL = "ZINKEE_API_BASE_URL";
|
|
13
|
+
export const ENV_PROFILE = "ZINKEE_PROFILE";
|
|
11
14
|
|
|
12
15
|
export class ProfileResolutionError extends Error {
|
|
13
16
|
override name = "ProfileResolutionError";
|
|
@@ -25,6 +28,11 @@ function toStringValue(value: unknown): string | undefined {
|
|
|
25
28
|
return typeof value === "string" ? value : undefined;
|
|
26
29
|
}
|
|
27
30
|
|
|
31
|
+
function toNonEmptyString(value: string | undefined): string | undefined {
|
|
32
|
+
const normalized = value?.trim();
|
|
33
|
+
return normalized ? normalized : undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
28
36
|
function parseProfileConfig(value: unknown): ProfileConfig {
|
|
29
37
|
const profile: ProfileConfig = {};
|
|
30
38
|
|
|
@@ -102,6 +110,10 @@ export function readConfigIfExists(filePath: string = defaultConfigPath): AppCon
|
|
|
102
110
|
}
|
|
103
111
|
}
|
|
104
112
|
|
|
113
|
+
export function readRuntimeConfig(filePath: string = defaultConfigPath): AppConfig {
|
|
114
|
+
return readConfigIfExists(filePath) ?? { profiles: {} };
|
|
115
|
+
}
|
|
116
|
+
|
|
105
117
|
function serializeProfileConfig(profile: ProfileConfig): Record<string, unknown> {
|
|
106
118
|
const serialized: Record<string, unknown> = {};
|
|
107
119
|
|
|
@@ -246,3 +258,40 @@ export function resolveProfile(
|
|
|
246
258
|
|
|
247
259
|
throw new ProfileResolutionError("No profile could be resolved from config.");
|
|
248
260
|
}
|
|
261
|
+
|
|
262
|
+
export function validateBaseUrl(value: string): string {
|
|
263
|
+
let parsed: URL;
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
parsed = new URL(value);
|
|
267
|
+
} catch {
|
|
268
|
+
throw new ProfileResolutionError(
|
|
269
|
+
`Environment variable ${ENV_BASE_URL} must be an absolute http(s) URL.`,
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (!["http:", "https:"].includes(parsed.protocol) || !parsed.host) {
|
|
274
|
+
throw new ProfileResolutionError(
|
|
275
|
+
`Environment variable ${ENV_BASE_URL} must be an absolute http(s) URL.`,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return value.replace(/\/+$/, "");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function readEnvApiKey(env: NodeJS.ProcessEnv = process.env): string | undefined {
|
|
283
|
+
return toNonEmptyString(env[ENV_API_KEY]);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function readEnvBaseUrl(env: NodeJS.ProcessEnv = process.env): string | undefined {
|
|
287
|
+
const baseUrl = toNonEmptyString(env[ENV_BASE_URL]);
|
|
288
|
+
if (!baseUrl) {
|
|
289
|
+
return undefined;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return validateBaseUrl(baseUrl);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export function readEnvProfile(env: NodeJS.ProcessEnv = process.env): string | undefined {
|
|
296
|
+
return toNonEmptyString(env[ENV_PROFILE]);
|
|
297
|
+
}
|
package/src/runtime-context.ts
CHANGED
|
@@ -1,18 +1,96 @@
|
|
|
1
1
|
import type { AppConfig, RuntimeContext, RuntimeOverrides } from "./types.js";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ENV_API_KEY,
|
|
5
|
+
ENV_BASE_URL,
|
|
6
|
+
readEnvApiKey,
|
|
7
|
+
readEnvBaseUrl,
|
|
8
|
+
readEnvProfile,
|
|
9
|
+
resolveProfile,
|
|
10
|
+
ProfileResolutionError,
|
|
11
|
+
} from "./config.js";
|
|
4
12
|
|
|
5
13
|
export function resolveRuntimeContext(
|
|
6
14
|
config: AppConfig,
|
|
7
15
|
overrides: RuntimeOverrides = {},
|
|
8
16
|
): RuntimeContext {
|
|
9
|
-
const
|
|
17
|
+
const envProfile = readEnvProfile();
|
|
18
|
+
const envApiKey = readEnvApiKey();
|
|
19
|
+
const envBaseUrl = readEnvBaseUrl();
|
|
20
|
+
|
|
21
|
+
const profileOverride = overrides.profile ?? envProfile;
|
|
22
|
+
const apiKeyOverride = overrides.apiKey ?? envApiKey;
|
|
23
|
+
const baseUrlOverride = overrides.baseUrl ?? envBaseUrl;
|
|
24
|
+
|
|
25
|
+
if (apiKeyOverride && !baseUrlOverride) {
|
|
26
|
+
throw new ProfileResolutionError(
|
|
27
|
+
`Runtime API key was provided, but ${ENV_BASE_URL} or --base-url is missing.`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (baseUrlOverride && !apiKeyOverride) {
|
|
32
|
+
throw new ProfileResolutionError(
|
|
33
|
+
`Runtime base URL was provided, but ${ENV_API_KEY} or --api-key is missing.`,
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (apiKeyOverride && baseUrlOverride) {
|
|
38
|
+
return {
|
|
39
|
+
profile: profileOverride ?? "runtime",
|
|
40
|
+
baseUrl: baseUrlOverride,
|
|
41
|
+
apiKey: apiKeyOverride,
|
|
42
|
+
readOnly: overrides.readOnly ?? false,
|
|
43
|
+
json: overrides.json ?? false,
|
|
44
|
+
profileSource:
|
|
45
|
+
overrides.profile !== undefined
|
|
46
|
+
? "flag"
|
|
47
|
+
: envProfile !== undefined
|
|
48
|
+
? "env"
|
|
49
|
+
: undefined,
|
|
50
|
+
baseUrlSource:
|
|
51
|
+
overrides.baseUrl !== undefined
|
|
52
|
+
? "flag"
|
|
53
|
+
: envBaseUrl !== undefined
|
|
54
|
+
? "env"
|
|
55
|
+
: undefined,
|
|
56
|
+
apiKeySource:
|
|
57
|
+
overrides.apiKey !== undefined
|
|
58
|
+
? "flag"
|
|
59
|
+
: envApiKey !== undefined
|
|
60
|
+
? "env"
|
|
61
|
+
: undefined,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const profile = resolveProfile(config, {
|
|
66
|
+
profile: profileOverride,
|
|
67
|
+
baseUrl: baseUrlOverride,
|
|
68
|
+
apiKey: apiKeyOverride,
|
|
69
|
+
});
|
|
10
70
|
|
|
11
71
|
return {
|
|
12
72
|
profile: profile.name,
|
|
13
|
-
baseUrl:
|
|
14
|
-
apiKey:
|
|
73
|
+
baseUrl: baseUrlOverride ?? profile.baseUrl,
|
|
74
|
+
apiKey: apiKeyOverride ?? profile.apiKey,
|
|
15
75
|
readOnly: overrides.readOnly ?? false,
|
|
16
76
|
json: overrides.json ?? false,
|
|
77
|
+
profileSource:
|
|
78
|
+
overrides.profile !== undefined
|
|
79
|
+
? "flag"
|
|
80
|
+
: envProfile !== undefined
|
|
81
|
+
? "env"
|
|
82
|
+
: "config",
|
|
83
|
+
baseUrlSource:
|
|
84
|
+
overrides.baseUrl !== undefined
|
|
85
|
+
? "flag"
|
|
86
|
+
: envBaseUrl !== undefined
|
|
87
|
+
? "env"
|
|
88
|
+
: "config",
|
|
89
|
+
apiKeySource:
|
|
90
|
+
overrides.apiKey !== undefined
|
|
91
|
+
? "flag"
|
|
92
|
+
: envApiKey !== undefined
|
|
93
|
+
? "env"
|
|
94
|
+
: "config",
|
|
17
95
|
};
|
|
18
96
|
}
|
package/src/types.ts
CHANGED
|
@@ -26,12 +26,17 @@ export interface RuntimeOverrides {
|
|
|
26
26
|
json?: boolean;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
export type RuntimeValueSource = "flag" | "env" | "config";
|
|
30
|
+
|
|
29
31
|
export interface RuntimeContext {
|
|
30
32
|
profile: string;
|
|
31
33
|
baseUrl?: string;
|
|
32
34
|
apiKey?: string;
|
|
33
35
|
readOnly: boolean;
|
|
34
36
|
json: boolean;
|
|
37
|
+
profileSource?: RuntimeValueSource;
|
|
38
|
+
baseUrlSource?: RuntimeValueSource;
|
|
39
|
+
apiKeySource?: RuntimeValueSource;
|
|
35
40
|
}
|
|
36
41
|
|
|
37
42
|
export type JsonScalar = string | number | boolean | null;
|
package/src/utils/examples.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AutomationsApi } from "../api/automations.js";
|
|
2
2
|
import { SchemasApi, type ApiSchema, type ApiSchemaField } from "../api/schemas.js";
|
|
3
3
|
import { ApiClient } from "../client.js";
|
|
4
|
-
import { defaultConfigPath,
|
|
4
|
+
import { defaultConfigPath, readRuntimeConfig } from "../config.js";
|
|
5
5
|
import type { NormalizedUuidOrSlug } from "../parsers/selectors.js";
|
|
6
6
|
import { resolveRuntimeContext } from "../runtime-context.js";
|
|
7
7
|
import type { RuntimeOverrides } from "../types.js";
|
|
@@ -466,7 +466,7 @@ async function createExampleAutomationContext(
|
|
|
466
466
|
}
|
|
467
467
|
|
|
468
468
|
try {
|
|
469
|
-
const config =
|
|
469
|
+
const config = readRuntimeConfig(options.configPath ?? defaultConfigPath);
|
|
470
470
|
const runtime = resolveRuntimeContext(config, options.overrides);
|
|
471
471
|
|
|
472
472
|
if (!runtime.baseUrl) {
|