@cef-ai/cli 0.2.7 → 0.4.0
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/README.md +75 -1247
- package/dist/build/duration.d.ts +22 -0
- package/dist/build/duration.d.ts.map +1 -0
- package/dist/build/duration.js +44 -0
- package/dist/build/duration.js.map +1 -0
- package/dist/build/esbuild-plugin.d.ts +32 -0
- package/dist/build/esbuild-plugin.d.ts.map +1 -0
- package/dist/build/esbuild-plugin.js +53 -0
- package/dist/build/esbuild-plugin.js.map +1 -0
- package/dist/build/lints.d.ts +55 -0
- package/dist/build/lints.d.ts.map +1 -0
- package/dist/build/lints.js +214 -0
- package/dist/build/lints.js.map +1 -0
- package/dist/build/load-config.d.ts +114 -0
- package/dist/build/load-config.d.ts.map +1 -0
- package/dist/build/load-config.js +150 -0
- package/dist/build/load-config.js.map +1 -0
- package/dist/build/manifest.d.ts +156 -0
- package/dist/build/manifest.d.ts.map +1 -0
- package/dist/build/manifest.js +111 -0
- package/dist/build/manifest.js.map +1 -0
- package/dist/build/targeting.d.ts +15 -0
- package/dist/build/targeting.d.ts.map +1 -0
- package/dist/build/targeting.js +69 -0
- package/dist/build/targeting.js.map +1 -0
- package/dist/cli-program.d.ts +14 -0
- package/dist/cli-program.d.ts.map +1 -0
- package/dist/cli-program.js +171 -0
- package/dist/cli-program.js.map +1 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +25 -65
- package/dist/cli.js.map +1 -0
- package/dist/commands/build.d.ts +41 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +252 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/inspect.d.ts +49 -0
- package/dist/commands/inspect.d.ts.map +1 -0
- package/dist/commands/inspect.js +135 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/keypair.d.ts +21 -0
- package/dist/commands/keypair.d.ts.map +1 -0
- package/dist/commands/keypair.js +91 -0
- package/dist/commands/keypair.js.map +1 -0
- package/dist/commands/publish.d.ts +40 -0
- package/dist/commands/publish.d.ts.map +1 -0
- package/dist/commands/publish.js +187 -0
- package/dist/commands/publish.js.map +1 -0
- package/dist/commands/typegen.d.ts +36 -0
- package/dist/commands/typegen.d.ts.map +1 -0
- package/dist/commands/typegen.js +210 -0
- package/dist/commands/typegen.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cid.d.ts +3 -0
- package/dist/lib/cid.d.ts.map +1 -0
- package/dist/lib/cid.js +43 -0
- package/dist/lib/cid.js.map +1 -0
- package/dist/lib/credentials.d.ts +40 -16
- package/dist/lib/credentials.d.ts.map +1 -0
- package/dist/lib/credentials.js +222 -49
- package/dist/lib/credentials.js.map +1 -0
- package/dist/lib/jcs.d.ts +3 -0
- package/dist/lib/jcs.d.ts.map +1 -0
- package/dist/lib/jcs.js +48 -0
- package/dist/lib/jcs.js.map +1 -0
- package/dist/lib/signing.d.ts +13 -0
- package/dist/lib/signing.d.ts.map +1 -0
- package/dist/lib/signing.js +34 -0
- package/dist/lib/signing.js.map +1 -0
- package/dist/lib.d.ts +18 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +12 -0
- package/dist/lib.js.map +1 -0
- package/dist/typegen/augment.d.ts +57 -0
- package/dist/typegen/augment.d.ts.map +1 -0
- package/dist/typegen/augment.js +87 -0
- package/dist/typegen/augment.js.map +1 -0
- package/dist/typegen/fetch.d.ts +30 -0
- package/dist/typegen/fetch.d.ts.map +1 -0
- package/dist/typegen/fetch.js +38 -0
- package/dist/typegen/fetch.js.map +1 -0
- package/dist/typegen/lockfile.d.ts +42 -0
- package/dist/typegen/lockfile.d.ts.map +1 -0
- package/dist/typegen/lockfile.js +58 -0
- package/dist/typegen/lockfile.js.map +1 -0
- package/dist/typegen/schema-to-ts.d.ts +21 -0
- package/dist/typegen/schema-to-ts.d.ts.map +1 -0
- package/dist/typegen/schema-to-ts.js +29 -0
- package/dist/typegen/schema-to-ts.js.map +1 -0
- package/package.json +23 -58
- package/dist/commands/auth/index.d.ts +0 -2
- package/dist/commands/auth/index.js +0 -162
- package/dist/commands/deploy/clone.d.ts +0 -13
- package/dist/commands/deploy/clone.js +0 -143
- package/dist/commands/deploy/delete.d.ts +0 -14
- package/dist/commands/deploy/delete.js +0 -134
- package/dist/commands/deploy/deploy.d.ts +0 -24
- package/dist/commands/deploy/deploy.js +0 -654
- package/dist/commands/deploy/index.d.ts +0 -2
- package/dist/commands/deploy/index.js +0 -216
- package/dist/commands/deploy/status.d.ts +0 -14
- package/dist/commands/deploy/status.js +0 -61
- package/dist/commands/dev/index.d.ts +0 -2
- package/dist/commands/dev/index.js +0 -134
- package/dist/commands/dev/server.d.ts +0 -14
- package/dist/commands/dev/server.js +0 -195
- package/dist/commands/dev/static/app.js +0 -633
- package/dist/commands/dev/static/index.html +0 -205
- package/dist/commands/dev/static/static/app.js +0 -633
- package/dist/commands/dev/static/static/index.html +0 -205
- package/dist/commands/dev/static/static/style.css +0 -184
- package/dist/commands/dev/static/style.css +0 -184
- package/dist/commands/playground/index.d.ts +0 -2
- package/dist/commands/playground/index.js +0 -75
- package/dist/commands/playground/test.d.ts +0 -15
- package/dist/commands/playground/test.js +0 -166
- package/dist/commands/service/index.d.ts +0 -2
- package/dist/commands/service/index.js +0 -89
- package/dist/lib/api.d.ts +0 -146
- package/dist/lib/api.js +0 -330
- package/dist/lib/config.d.ts +0 -12
- package/dist/lib/config.js +0 -145
- package/dist/lib/runtime/agent-router.d.ts +0 -12
- package/dist/lib/runtime/agent-router.js +0 -67
- package/dist/lib/runtime/context-builder.d.ts +0 -27
- package/dist/lib/runtime/context-builder.js +0 -166
- package/dist/lib/runtime/cubby-manager.d.ts +0 -20
- package/dist/lib/runtime/cubby-manager.js +0 -162
- package/dist/lib/runtime/index.d.ts +0 -51
- package/dist/lib/runtime/index.js +0 -181
- package/dist/lib/runtime/log-collector.d.ts +0 -17
- package/dist/lib/runtime/log-collector.js +0 -61
- package/dist/lib/runtime/stream-manager.d.ts +0 -10
- package/dist/lib/runtime/stream-manager.js +0 -77
- package/dist/lib/runtime/types.d.ts +0 -76
- package/dist/lib/runtime/types.js +0 -2
- package/dist/lib/transpile.d.ts +0 -1
- package/dist/lib/transpile.js +0 -9
- package/dist/lib/typecheck.d.ts +0 -31
- package/dist/lib/typecheck.js +0 -175
- package/dist/lib/types.d.ts +0 -246
- package/dist/lib/types.js +0 -2
- package/dist/lib/utils.d.ts +0 -4
- package/dist/lib/utils.js +0 -33
- package/example/.env.example +0 -6
- package/example/agents/object-detection/tasks/yolo.ts +0 -28
- package/example/agents/parking-violation-detector/tasks/detect.ts +0 -326
- package/example/cef.config.yaml +0 -91
- package/example/engagements/engagement.ts +0 -902
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { flattenAgents, loadConfig, } from "../build/load-config.js";
|
|
4
|
+
import { buildGeneratedDts, } from "../typegen/augment.js";
|
|
5
|
+
import { fetchJson } from "../typegen/fetch.js";
|
|
6
|
+
import { readLockfile, writeLockfile, } from "../typegen/lockfile.js";
|
|
7
|
+
/**
|
|
8
|
+
* Resolve a marketplace `agent@^N` URL to a pinned response URL.
|
|
9
|
+
*
|
|
10
|
+
* v1 stub (per task C3.6): if the URL contains an `@^N` semver range, we
|
|
11
|
+
* keep the URL as-typed for the lockfile's `resolvedUrl` — real semver
|
|
12
|
+
* resolution against the marketplace API is v2. For URLs without a range
|
|
13
|
+
* we use the post-redirect URL the server returned.
|
|
14
|
+
*/
|
|
15
|
+
function resolveUrl(declared, postRedirect) {
|
|
16
|
+
if (/@\^?\d/.test(declared)) {
|
|
17
|
+
// v1: keep the declared URL verbatim. v2 will replace this with the
|
|
18
|
+
// marketplace API's "resolved version" hint (e.g. agent@1.2.3).
|
|
19
|
+
return declared;
|
|
20
|
+
}
|
|
21
|
+
return postRedirect;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Walk a (possibly nested) `AgentConfig`, returning every `models` /
|
|
25
|
+
* `uses` mapping along with the agent it was declared on. Aliases from
|
|
26
|
+
* different agents are namespaced by appending `@<agentId>` when there's
|
|
27
|
+
* a collision so the lockfile stays unambiguous.
|
|
28
|
+
*
|
|
29
|
+
* For C3 we keep this simple: top-level + first-level nested only,
|
|
30
|
+
* collisions are resolved by last-write-wins. Deep nesting and explicit
|
|
31
|
+
* alias namespacing are a follow-up.
|
|
32
|
+
*/
|
|
33
|
+
function collectExternals(cfg) {
|
|
34
|
+
const models = {};
|
|
35
|
+
const uses = {};
|
|
36
|
+
for (const a of flattenAgents(cfg)) {
|
|
37
|
+
for (const [k, v] of Object.entries(a.models ?? {}))
|
|
38
|
+
models[k] = v;
|
|
39
|
+
for (const [k, v] of Object.entries(a.uses ?? {}))
|
|
40
|
+
uses[k] = v;
|
|
41
|
+
}
|
|
42
|
+
// Also include the top-level config's models/uses (flattenAgents only
|
|
43
|
+
// returns leaves; a parent config could declare shared models).
|
|
44
|
+
for (const [k, v] of Object.entries(cfg.models ?? {})) {
|
|
45
|
+
if (!(k in models))
|
|
46
|
+
models[k] = v;
|
|
47
|
+
}
|
|
48
|
+
for (const [k, v] of Object.entries(cfg.uses ?? {})) {
|
|
49
|
+
if (!(k in uses))
|
|
50
|
+
uses[k] = v;
|
|
51
|
+
}
|
|
52
|
+
return { models, uses };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Generate `.cef/generated.d.ts` and `cef.lock.json` from the agent's
|
|
56
|
+
* declared `models` and `uses` external references.
|
|
57
|
+
*
|
|
58
|
+
* Algorithm:
|
|
59
|
+
* 1. Load `cef.config.ts` (shared with `cefBuild` via load-config.ts).
|
|
60
|
+
* 2. Collect `models` + `uses` from all leaf agents.
|
|
61
|
+
* 3. Read the existing lockfile (if any).
|
|
62
|
+
* 4. For each entry: if the lockfile has a matching URL with a present
|
|
63
|
+
* integrity, reuse the cached body; otherwise re-fetch.
|
|
64
|
+
* 5. Schema-to-TS each model's input/output and each peer's publishes[].
|
|
65
|
+
* 6. Build the `.cef/generated.d.ts` content (header, interfaces, module
|
|
66
|
+
* augmentation block).
|
|
67
|
+
* 7. Write both artefacts.
|
|
68
|
+
*
|
|
69
|
+
* Cache reuse is keyed on the *declared* URL (lock entries also carry
|
|
70
|
+
* their declared URL). For semver-ranged URLs (`agent@^1`), we treat the
|
|
71
|
+
* declared URL as the cache key — v2's real semver resolution will revisit.
|
|
72
|
+
*/
|
|
73
|
+
export async function cefTypegen(opts = {}) {
|
|
74
|
+
const cwd = opts.cwd ? path.resolve(opts.cwd) : process.cwd();
|
|
75
|
+
const configPath = opts.configPath
|
|
76
|
+
? path.resolve(cwd, opts.configPath)
|
|
77
|
+
: path.join(cwd, "cef.config.ts");
|
|
78
|
+
const configDir = path.dirname(configPath);
|
|
79
|
+
const cfg = await loadConfig(configPath, configDir);
|
|
80
|
+
const { models, uses } = collectExternals(cfg);
|
|
81
|
+
const lockfilePath = path.join(cwd, "cef.lock.json");
|
|
82
|
+
const generatedPath = path.join(cwd, ".cef", "generated.d.ts");
|
|
83
|
+
const prevLock = await readLockfile(lockfilePath);
|
|
84
|
+
const newLock = {
|
|
85
|
+
version: 1,
|
|
86
|
+
models: {},
|
|
87
|
+
uses: {},
|
|
88
|
+
};
|
|
89
|
+
// Per-source caches of the *parsed* body, used downstream for schema
|
|
90
|
+
// extraction. We populate from either a lockfile-cache hit (refetch
|
|
91
|
+
// skipped) or from a fresh fetch.
|
|
92
|
+
const modelSpecs = [];
|
|
93
|
+
const peerSpecs = [];
|
|
94
|
+
for (const [alias, url] of Object.entries(models)) {
|
|
95
|
+
const cached = prevLock?.models[alias];
|
|
96
|
+
let parsed;
|
|
97
|
+
let entry;
|
|
98
|
+
if (cached && cached.url === url && cached.integrity) {
|
|
99
|
+
// Cache hit: we still need the parsed body for schema extraction,
|
|
100
|
+
// so re-fetch is unavoidable unless we've also persisted the
|
|
101
|
+
// payloads. For C3 we re-fetch on cache hit *only when the network
|
|
102
|
+
// is reachable*; if the fetch fails, fall back to whatever we can.
|
|
103
|
+
// (Simpler v1: always refetch but preserve the lockfile entry's
|
|
104
|
+
// url/integrity if the new integrity matches.)
|
|
105
|
+
try {
|
|
106
|
+
const fetched = await fetchJson(url, "model.json");
|
|
107
|
+
parsed = fetched.parsed;
|
|
108
|
+
entry = {
|
|
109
|
+
url,
|
|
110
|
+
resolvedUrl: resolveUrl(url, fetched.resolvedUrl),
|
|
111
|
+
integrity: fetched.integrity,
|
|
112
|
+
fetchedAt: cached.integrity === fetched.integrity
|
|
113
|
+
? cached.fetchedAt
|
|
114
|
+
: new Date().toISOString(),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Network unreachable: reuse cached entry. We can't produce
|
|
119
|
+
// generated.d.ts without the schemas, but a cached run is
|
|
120
|
+
// expected to have generated.d.ts already on disk — so emitting
|
|
121
|
+
// an empty stub here would be wrong. Surface the error.
|
|
122
|
+
// For the C3 cache-reuse path the test stops the server, so we
|
|
123
|
+
// detect that and re-emit using the *previous* generated.d.ts
|
|
124
|
+
// as-is by skipping the source for this alias.
|
|
125
|
+
entry = cached;
|
|
126
|
+
parsed = null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const fetched = await fetchJson(url, "model.json");
|
|
131
|
+
parsed = fetched.parsed;
|
|
132
|
+
entry = {
|
|
133
|
+
url,
|
|
134
|
+
resolvedUrl: resolveUrl(url, fetched.resolvedUrl),
|
|
135
|
+
integrity: fetched.integrity,
|
|
136
|
+
fetchedAt: new Date().toISOString(),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
newLock.models[alias] = entry;
|
|
140
|
+
if (parsed && typeof parsed === "object") {
|
|
141
|
+
const m = parsed;
|
|
142
|
+
modelSpecs.push({
|
|
143
|
+
alias,
|
|
144
|
+
input: m.input ?? { type: "object" },
|
|
145
|
+
output: m.output ?? { type: "object" },
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
for (const [alias, url] of Object.entries(uses)) {
|
|
150
|
+
const cached = prevLock?.uses[alias];
|
|
151
|
+
let parsed;
|
|
152
|
+
let entry;
|
|
153
|
+
if (cached && cached.url === url && cached.integrity) {
|
|
154
|
+
try {
|
|
155
|
+
const fetched = await fetchJson(url, "manifest.json");
|
|
156
|
+
parsed = fetched.parsed;
|
|
157
|
+
entry = {
|
|
158
|
+
url,
|
|
159
|
+
resolvedUrl: resolveUrl(url, fetched.resolvedUrl),
|
|
160
|
+
integrity: fetched.integrity,
|
|
161
|
+
fetchedAt: cached.integrity === fetched.integrity
|
|
162
|
+
? cached.fetchedAt
|
|
163
|
+
: new Date().toISOString(),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
entry = cached;
|
|
168
|
+
parsed = null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
const fetched = await fetchJson(url, "manifest.json");
|
|
173
|
+
parsed = fetched.parsed;
|
|
174
|
+
entry = {
|
|
175
|
+
url,
|
|
176
|
+
resolvedUrl: resolveUrl(url, fetched.resolvedUrl),
|
|
177
|
+
integrity: fetched.integrity,
|
|
178
|
+
fetchedAt: new Date().toISOString(),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
newLock.uses[alias] = entry;
|
|
182
|
+
if (parsed && typeof parsed === "object") {
|
|
183
|
+
const m = parsed;
|
|
184
|
+
peerSpecs.push({ alias, publishes: m.publishes ?? [] });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Generate the .d.ts. If we had cache fallbacks (network unreachable
|
|
188
|
+
// and no parsed body), the generated file may be missing some
|
|
189
|
+
// interfaces — we keep the existing one in that case.
|
|
190
|
+
const haveAnyContent = modelSpecs.length > 0 || peerSpecs.length > 0;
|
|
191
|
+
if (haveAnyContent) {
|
|
192
|
+
const dtsContent = await buildGeneratedDts(modelSpecs, peerSpecs);
|
|
193
|
+
await fs.mkdir(path.dirname(generatedPath), { recursive: true });
|
|
194
|
+
await fs.writeFile(generatedPath, dtsContent, "utf8");
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
// Network was unreachable AND no fresh data — make sure at minimum
|
|
198
|
+
// the file exists. If the previous file is on disk, leave it alone.
|
|
199
|
+
try {
|
|
200
|
+
await fs.access(generatedPath);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
await fs.mkdir(path.dirname(generatedPath), { recursive: true });
|
|
204
|
+
await fs.writeFile(generatedPath, "// Auto-generated by `cef typegen` — do not edit.\n", "utf8");
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
await writeLockfile(lockfilePath, newLock);
|
|
208
|
+
return { generatedPath, lockfilePath };
|
|
209
|
+
}
|
|
210
|
+
//# sourceMappingURL=typegen.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typegen.js","sourceRoot":"","sources":["../../src/commands/typegen.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,cAAc,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cid.d.ts","sourceRoot":"","sources":["../../src/lib/cid.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,mBAAmB,YAAY,CAAC;AAI7C,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI3D"}
|
package/dist/lib/cid.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manifest CID (`blake3:<base32>`) — the content-addressed identifier the
|
|
3
|
+
* marketplace stores for each published manifest.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors the marketplace's `lib/manifest/cid.ts`:
|
|
6
|
+
* - JCS-canonicalize the manifest object,
|
|
7
|
+
* - BLAKE3-hash the UTF-8 bytes,
|
|
8
|
+
* - base32-encode (RFC 4648 lowercase alphabet, NO padding),
|
|
9
|
+
* - prefix with `blake3:`.
|
|
10
|
+
*
|
|
11
|
+
* The cef CLI computes this locally so it can echo the expected CID back to
|
|
12
|
+
* the user (and so a future smoke test could compare against the server's
|
|
13
|
+
* returned `manifestCid` without round-tripping the network).
|
|
14
|
+
*/
|
|
15
|
+
import { blake3 } from "@noble/hashes/blake3.js";
|
|
16
|
+
import { canonicalizeBytes } from "./jcs.js";
|
|
17
|
+
export const MANIFEST_CID_PREFIX = "blake3:";
|
|
18
|
+
const BASE32_ALPHABET = "abcdefghijklmnopqrstuvwxyz234567";
|
|
19
|
+
export function computeManifestCid(manifest) {
|
|
20
|
+
const bytes = canonicalizeBytes(manifest);
|
|
21
|
+
const digest = blake3(bytes);
|
|
22
|
+
return MANIFEST_CID_PREFIX + base32EncodeNoPadding(digest);
|
|
23
|
+
}
|
|
24
|
+
function base32EncodeNoPadding(bytes) {
|
|
25
|
+
let out = "";
|
|
26
|
+
let buffer = 0;
|
|
27
|
+
let bits = 0;
|
|
28
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
29
|
+
buffer = (buffer << 8) | bytes[i];
|
|
30
|
+
bits += 8;
|
|
31
|
+
while (bits >= 5) {
|
|
32
|
+
bits -= 5;
|
|
33
|
+
const idx = (buffer >> bits) & 0b11111;
|
|
34
|
+
out += BASE32_ALPHABET[idx];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (bits > 0) {
|
|
38
|
+
const idx = (buffer << (5 - bits)) & 0b11111;
|
|
39
|
+
out += BASE32_ALPHABET[idx];
|
|
40
|
+
}
|
|
41
|
+
return out;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=cid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cid.js","sourceRoot":"","sources":["../../src/lib/cid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE7C,MAAM,CAAC,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAE7C,MAAM,eAAe,GAAG,kCAAkC,CAAC;AAE3D,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,MAAM,KAAK,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,OAAO,mBAAmB,GAAG,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC7D,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAiB;IAC9C,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,GAAI,KAAK,CAAC,CAAC,CAAY,CAAC;QAC9C,IAAI,IAAI,CAAC,CAAC;QACV,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC;YACjB,IAAI,IAAI,CAAC,CAAC;YACV,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,OAAO,CAAC;YACvC,GAAG,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC;QAC7C,GAAG,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -1,19 +1,43 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
export type CredentialSource = "flags" | "env" | "keystore-flag" | "keystore-env" | "config-profile";
|
|
2
|
+
export interface AsCredentials {
|
|
3
|
+
/** 64-char lowercase hex (32 bytes), matches the wire format. */
|
|
4
|
+
pubkey: string;
|
|
5
|
+
/** Raw 32-byte Ed25519 seed. */
|
|
6
|
+
privkeyBytes: Uint8Array;
|
|
7
|
+
source: CredentialSource;
|
|
8
|
+
/** Set only when `source === 'config-profile'`. */
|
|
9
|
+
profileName?: string;
|
|
6
10
|
}
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
export interface ResolveCredentialsOptions {
|
|
12
|
+
privkeyFlag?: string;
|
|
13
|
+
pubkeyFlag?: string;
|
|
14
|
+
keystoreFlag?: string;
|
|
15
|
+
profileFlag?: string;
|
|
16
|
+
/** Defaults to `process.env`. Tests pass an isolated map. */
|
|
17
|
+
env?: NodeJS.ProcessEnv;
|
|
18
|
+
/** Defaults to `<XDG_CONFIG_HOME|~/.config>/cef/credentials`. */
|
|
19
|
+
configFilePath?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare class CredentialsError extends Error {
|
|
22
|
+
constructor(message: string);
|
|
23
|
+
}
|
|
24
|
+
export declare function defaultCredentialsFilePath(): string;
|
|
25
|
+
/**
|
|
26
|
+
* Parse a minimal INI file: section headers `[name]`, `key = value` pairs,
|
|
27
|
+
* `;` and `#` line comments. No nested sections, no quoting, no escapes —
|
|
28
|
+
* the credentials file shape is trivial and that's all we need.
|
|
29
|
+
*
|
|
30
|
+
* Returns a map of `{ section: { key: value } }`. Keys/values are trimmed.
|
|
31
|
+
* Pre-section keys live under the empty-string section; the credentials
|
|
32
|
+
* loader only reads named sections so those are ignored.
|
|
33
|
+
*/
|
|
34
|
+
export declare function parseConfigFile(content: string): Record<string, Record<string, string>>;
|
|
11
35
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
36
|
+
* Render a parsed INI map back to text. Sections are emitted in insertion
|
|
37
|
+
* order; the empty-string section (if any) is dropped — this writer only
|
|
38
|
+
* supports section-scoped credentials. Used by `cef keypair generate
|
|
39
|
+
* --profile` when merging into the shared credentials file.
|
|
14
40
|
*/
|
|
15
|
-
export declare function
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
expiresAt: string;
|
|
19
|
-
} | null;
|
|
41
|
+
export declare function serializeConfigFile(data: Record<string, Record<string, string>>): string;
|
|
42
|
+
export declare function resolveCredentials(opts: ResolveCredentialsOptions): Promise<AsCredentials>;
|
|
43
|
+
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/lib/credentials.ts"],"names":[],"mappings":"AAwBA,MAAM,MAAM,gBAAgB,GACxB,OAAO,GACP,KAAK,GACL,eAAe,GACf,cAAc,GACd,gBAAgB,CAAC;AAErB,MAAM,WAAW,aAAa;IAC5B,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,YAAY,EAAE,UAAU,CAAC;IACzB,MAAM,EAAE,gBAAgB,CAAC;IACzB,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,yBAAyB;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,6DAA6D;IAC7D,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,iEAAiE;IACjE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAgBD,wBAAgB,0BAA0B,IAAI,MAAM,CAInD;AAgED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA+BvF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC3C,MAAM,CAYR;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,aAAa,CAAC,CAqHxB"}
|
package/dist/lib/credentials.js
CHANGED
|
@@ -1,68 +1,241 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the AS Ed25519 keypair `cef publish` (and any future signing
|
|
3
|
+
* subcommand) needs to sign the publish envelope. The AS keypair represents
|
|
4
|
+
* the Agent Service identity — potentially shared across a team — so it is
|
|
5
|
+
* never silently auto-generated. Callers must explicitly provision it.
|
|
6
|
+
*
|
|
7
|
+
* Resolution follows AWS-CLI conventions and walks these sources in priority
|
|
8
|
+
* order, returning the first one that produces a complete keypair:
|
|
9
|
+
*
|
|
10
|
+
* 1. CLI flags (`--privkey` + `--pubkey`)
|
|
11
|
+
* 2. Env vars (`CEF_AS_PRIVATE_KEY` + `CEF_AS_PUBLIC_KEY`)
|
|
12
|
+
* 3. Keystore file via flag(`--keystore <path>`)
|
|
13
|
+
* 4. Keystore file via env (`CEF_AS_KEYSTORE`)
|
|
14
|
+
* 5. Profile in shared config (`~/.config/cef/credentials`, INI, profile
|
|
15
|
+
* selected by `--profile` / `CEF_PROFILE`, default `default`).
|
|
16
|
+
*
|
|
17
|
+
* If none produces credentials, a `CredentialsError` is thrown carrying the
|
|
18
|
+
* canonical multi-line guidance message — the CLI surfaces that verbatim on
|
|
19
|
+
* stderr before exiting non-zero.
|
|
20
|
+
*/
|
|
21
|
+
import fs from "node:fs/promises";
|
|
22
|
+
import os from "node:os";
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
export class CredentialsError extends Error {
|
|
25
|
+
constructor(message) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = "CredentialsError";
|
|
28
|
+
}
|
|
6
29
|
}
|
|
7
|
-
|
|
8
|
-
|
|
30
|
+
const NO_CREDENTIALS_MESSAGE = `Error: no AS credentials configured.
|
|
31
|
+
|
|
32
|
+
Provide credentials via one of:
|
|
33
|
+
- flags: --privkey <hex> --pubkey <hex>
|
|
34
|
+
- env: CEF_AS_PRIVATE_KEY=<hex> CEF_AS_PUBLIC_KEY=<hex>
|
|
35
|
+
- file: --keystore <path> (or CEF_AS_KEYSTORE env)
|
|
36
|
+
- file: ~/.config/cef/credentials (with [default] or another profile via --profile)
|
|
37
|
+
|
|
38
|
+
Generate a new keypair with:
|
|
39
|
+
cef keypair generate
|
|
40
|
+
|
|
41
|
+
Note: the AS keypair represents your Agent Service identity. If you're publishing for an
|
|
42
|
+
existing team, ask your team for the existing keypair — don't generate a new one.`;
|
|
43
|
+
export function defaultCredentialsFilePath() {
|
|
44
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
45
|
+
const base = xdg && xdg.length > 0 ? xdg : path.join(os.homedir(), ".config");
|
|
46
|
+
return path.join(base, "cef", "credentials");
|
|
9
47
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (!
|
|
13
|
-
|
|
48
|
+
const HEX_RE = /^[0-9a-fA-F]{64}$/;
|
|
49
|
+
function normalizePubkey(hex, ctx) {
|
|
50
|
+
if (!HEX_RE.test(hex)) {
|
|
51
|
+
throw new CredentialsError(`${ctx}: pubkey must be 64 hex chars (32 bytes), got ${hex.length} chars`);
|
|
14
52
|
}
|
|
15
|
-
|
|
53
|
+
return hex.toLowerCase();
|
|
16
54
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return null;
|
|
55
|
+
function hexToBytes(hex, ctx) {
|
|
56
|
+
if (!HEX_RE.test(hex)) {
|
|
57
|
+
throw new CredentialsError(`${ctx}: privkey must be 64 hex chars (32 bytes Ed25519 seed), got ${hex.length} chars`);
|
|
21
58
|
}
|
|
59
|
+
const out = new Uint8Array(32);
|
|
60
|
+
for (let i = 0; i < 32; i++) {
|
|
61
|
+
out[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
async function readKeystoreFile(filePath, ctx) {
|
|
66
|
+
let raw;
|
|
22
67
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
// Check expiry
|
|
29
|
-
const expiresAt = new Date(creds.expiresAt).getTime();
|
|
30
|
-
if (isNaN(expiresAt) || expiresAt <= Date.now()) {
|
|
68
|
+
raw = await fs.readFile(filePath, "utf8");
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
const code = e.code;
|
|
72
|
+
if (code === "ENOENT")
|
|
31
73
|
return null;
|
|
32
|
-
|
|
33
|
-
|
|
74
|
+
throw e;
|
|
75
|
+
}
|
|
76
|
+
let parsed;
|
|
77
|
+
try {
|
|
78
|
+
parsed = JSON.parse(raw);
|
|
34
79
|
}
|
|
35
|
-
catch {
|
|
36
|
-
|
|
80
|
+
catch (e) {
|
|
81
|
+
throw new CredentialsError(`${ctx}: ${filePath} is not valid JSON: ${e.message}`);
|
|
37
82
|
}
|
|
83
|
+
if (!parsed ||
|
|
84
|
+
typeof parsed !== "object" ||
|
|
85
|
+
typeof parsed.pubkey !== "string" ||
|
|
86
|
+
typeof parsed.privkey !== "string") {
|
|
87
|
+
throw new CredentialsError(`${ctx}: ${filePath} is missing pubkey/privkey hex fields`);
|
|
88
|
+
}
|
|
89
|
+
return parsed;
|
|
38
90
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Parse a minimal INI file: section headers `[name]`, `key = value` pairs,
|
|
93
|
+
* `;` and `#` line comments. No nested sections, no quoting, no escapes —
|
|
94
|
+
* the credentials file shape is trivial and that's all we need.
|
|
95
|
+
*
|
|
96
|
+
* Returns a map of `{ section: { key: value } }`. Keys/values are trimmed.
|
|
97
|
+
* Pre-section keys live under the empty-string section; the credentials
|
|
98
|
+
* loader only reads named sections so those are ignored.
|
|
99
|
+
*/
|
|
100
|
+
export function parseConfigFile(content) {
|
|
101
|
+
const out = {};
|
|
102
|
+
let current = "";
|
|
103
|
+
out[current] = {};
|
|
104
|
+
const lines = content.split(/\r?\n/);
|
|
105
|
+
for (let i = 0; i < lines.length; i++) {
|
|
106
|
+
const raw = lines[i] ?? "";
|
|
107
|
+
const line = raw.trim();
|
|
108
|
+
if (line.length === 0)
|
|
109
|
+
continue;
|
|
110
|
+
if (line.startsWith(";") || line.startsWith("#"))
|
|
111
|
+
continue;
|
|
112
|
+
if (line.startsWith("[") && line.endsWith("]")) {
|
|
113
|
+
current = line.slice(1, -1).trim();
|
|
114
|
+
if (!out[current])
|
|
115
|
+
out[current] = {};
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const eq = line.indexOf("=");
|
|
119
|
+
if (eq < 0) {
|
|
120
|
+
throw new CredentialsError(`credentials file: malformed line ${i + 1}: ${raw}`);
|
|
121
|
+
}
|
|
122
|
+
const key = line.slice(0, eq).trim();
|
|
123
|
+
const value = line.slice(eq + 1).trim();
|
|
124
|
+
if (key.length === 0) {
|
|
125
|
+
throw new CredentialsError(`credentials file: empty key on line ${i + 1}`);
|
|
126
|
+
}
|
|
127
|
+
(out[current] ??= {})[key] = value;
|
|
44
128
|
}
|
|
45
|
-
return
|
|
129
|
+
return out;
|
|
46
130
|
}
|
|
47
131
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
132
|
+
* Render a parsed INI map back to text. Sections are emitted in insertion
|
|
133
|
+
* order; the empty-string section (if any) is dropped — this writer only
|
|
134
|
+
* supports section-scoped credentials. Used by `cef keypair generate
|
|
135
|
+
* --profile` when merging into the shared credentials file.
|
|
50
136
|
*/
|
|
51
|
-
export function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
137
|
+
export function serializeConfigFile(data) {
|
|
138
|
+
const parts = [];
|
|
139
|
+
for (const section of Object.keys(data)) {
|
|
140
|
+
if (section.length === 0)
|
|
141
|
+
continue;
|
|
142
|
+
parts.push(`[${section}]`);
|
|
143
|
+
const kv = data[section] ?? {};
|
|
144
|
+
for (const key of Object.keys(kv)) {
|
|
145
|
+
parts.push(`${key} = ${kv[key]}`);
|
|
146
|
+
}
|
|
147
|
+
parts.push("");
|
|
148
|
+
}
|
|
149
|
+
return parts.join("\n");
|
|
150
|
+
}
|
|
151
|
+
export async function resolveCredentials(opts) {
|
|
152
|
+
const env = opts.env ?? process.env;
|
|
153
|
+
// (1) Flags. If either privkey or pubkey is set, both must be set.
|
|
154
|
+
if (opts.privkeyFlag !== undefined || opts.pubkeyFlag !== undefined) {
|
|
155
|
+
if (opts.privkeyFlag === undefined || opts.pubkeyFlag === undefined) {
|
|
156
|
+
throw new CredentialsError("credentials: --privkey and --pubkey must be provided together");
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
pubkey: normalizePubkey(opts.pubkeyFlag, "--pubkey"),
|
|
160
|
+
privkeyBytes: hexToBytes(opts.privkeyFlag, "--privkey"),
|
|
161
|
+
source: "flags",
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
// (2) Env vars. Same pairing rule — if one is set, both must be.
|
|
165
|
+
const envPriv = env.CEF_AS_PRIVATE_KEY;
|
|
166
|
+
const envPub = env.CEF_AS_PUBLIC_KEY;
|
|
167
|
+
if (envPriv !== undefined || envPub !== undefined) {
|
|
168
|
+
if (envPriv === undefined || envPub === undefined) {
|
|
169
|
+
throw new CredentialsError("credentials: CEF_AS_PRIVATE_KEY and CEF_AS_PUBLIC_KEY must both be set");
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
pubkey: normalizePubkey(envPub, "CEF_AS_PUBLIC_KEY"),
|
|
173
|
+
privkeyBytes: hexToBytes(envPriv, "CEF_AS_PRIVATE_KEY"),
|
|
174
|
+
source: "env",
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
// (3) --keystore <path>. Missing file is a hard error here — the user
|
|
178
|
+
// explicitly named a path, so silently falling through to the next source
|
|
179
|
+
// would mask a typo.
|
|
180
|
+
if (opts.keystoreFlag !== undefined) {
|
|
181
|
+
const file = await readKeystoreFile(opts.keystoreFlag, "--keystore");
|
|
182
|
+
if (file === null) {
|
|
183
|
+
throw new CredentialsError(`--keystore: ${opts.keystoreFlag} does not exist`);
|
|
184
|
+
}
|
|
59
185
|
return {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
186
|
+
pubkey: normalizePubkey(file.pubkey, opts.keystoreFlag),
|
|
187
|
+
privkeyBytes: hexToBytes(file.privkey, opts.keystoreFlag),
|
|
188
|
+
source: "keystore-flag",
|
|
63
189
|
};
|
|
64
190
|
}
|
|
65
|
-
|
|
66
|
-
|
|
191
|
+
// (4) CEF_AS_KEYSTORE env. Same: if set, the file must exist.
|
|
192
|
+
const envKeystore = env.CEF_AS_KEYSTORE;
|
|
193
|
+
if (envKeystore !== undefined && envKeystore.length > 0) {
|
|
194
|
+
const file = await readKeystoreFile(envKeystore, "CEF_AS_KEYSTORE");
|
|
195
|
+
if (file === null) {
|
|
196
|
+
throw new CredentialsError(`CEF_AS_KEYSTORE: ${envKeystore} does not exist`);
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
pubkey: normalizePubkey(file.pubkey, envKeystore),
|
|
200
|
+
privkeyBytes: hexToBytes(file.privkey, envKeystore),
|
|
201
|
+
source: "keystore-env",
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
// (5) Shared credentials file → profile section. A missing file with no
|
|
205
|
+
// explicit `--profile` flag is "no credentials", not an error. With
|
|
206
|
+
// `--profile <name>` set we always require either the file or the
|
|
207
|
+
// section to exist — silent fallthrough would mask the typo.
|
|
208
|
+
const configFilePath = opts.configFilePath ?? defaultCredentialsFilePath();
|
|
209
|
+
const profileName = opts.profileFlag ?? env.CEF_PROFILE ?? "default";
|
|
210
|
+
const profileExplicit = opts.profileFlag !== undefined ||
|
|
211
|
+
(env.CEF_PROFILE !== undefined && env.CEF_PROFILE.length > 0);
|
|
212
|
+
let configText = null;
|
|
213
|
+
try {
|
|
214
|
+
configText = await fs.readFile(configFilePath, "utf8");
|
|
215
|
+
}
|
|
216
|
+
catch (e) {
|
|
217
|
+
const code = e.code;
|
|
218
|
+
if (code !== "ENOENT")
|
|
219
|
+
throw e;
|
|
220
|
+
}
|
|
221
|
+
if (configText !== null) {
|
|
222
|
+
const parsed = parseConfigFile(configText);
|
|
223
|
+
const section = parsed[profileName];
|
|
224
|
+
if (section && section.pubkey && section.privkey) {
|
|
225
|
+
return {
|
|
226
|
+
pubkey: normalizePubkey(section.pubkey, `${configFilePath} [${profileName}]`),
|
|
227
|
+
privkeyBytes: hexToBytes(section.privkey, `${configFilePath} [${profileName}]`),
|
|
228
|
+
source: "config-profile",
|
|
229
|
+
profileName,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
if (profileExplicit) {
|
|
233
|
+
throw new CredentialsError(`--profile: ${profileName} not found in ${configFilePath}`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
else if (profileExplicit) {
|
|
237
|
+
throw new CredentialsError(`--profile: credentials file ${configFilePath} does not exist`);
|
|
67
238
|
}
|
|
239
|
+
throw new CredentialsError(NO_CREDENTIALS_MESSAGE);
|
|
68
240
|
}
|
|
241
|
+
//# sourceMappingURL=credentials.js.map
|