@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.
Files changed (153) hide show
  1. package/README.md +75 -1247
  2. package/dist/build/duration.d.ts +22 -0
  3. package/dist/build/duration.d.ts.map +1 -0
  4. package/dist/build/duration.js +44 -0
  5. package/dist/build/duration.js.map +1 -0
  6. package/dist/build/esbuild-plugin.d.ts +32 -0
  7. package/dist/build/esbuild-plugin.d.ts.map +1 -0
  8. package/dist/build/esbuild-plugin.js +53 -0
  9. package/dist/build/esbuild-plugin.js.map +1 -0
  10. package/dist/build/lints.d.ts +55 -0
  11. package/dist/build/lints.d.ts.map +1 -0
  12. package/dist/build/lints.js +214 -0
  13. package/dist/build/lints.js.map +1 -0
  14. package/dist/build/load-config.d.ts +114 -0
  15. package/dist/build/load-config.d.ts.map +1 -0
  16. package/dist/build/load-config.js +150 -0
  17. package/dist/build/load-config.js.map +1 -0
  18. package/dist/build/manifest.d.ts +156 -0
  19. package/dist/build/manifest.d.ts.map +1 -0
  20. package/dist/build/manifest.js +111 -0
  21. package/dist/build/manifest.js.map +1 -0
  22. package/dist/build/targeting.d.ts +15 -0
  23. package/dist/build/targeting.d.ts.map +1 -0
  24. package/dist/build/targeting.js +69 -0
  25. package/dist/build/targeting.js.map +1 -0
  26. package/dist/cli-program.d.ts +14 -0
  27. package/dist/cli-program.d.ts.map +1 -0
  28. package/dist/cli-program.js +171 -0
  29. package/dist/cli-program.js.map +1 -0
  30. package/dist/cli.d.ts +2 -2
  31. package/dist/cli.d.ts.map +1 -0
  32. package/dist/cli.js +25 -65
  33. package/dist/cli.js.map +1 -0
  34. package/dist/commands/build.d.ts +41 -0
  35. package/dist/commands/build.d.ts.map +1 -0
  36. package/dist/commands/build.js +252 -0
  37. package/dist/commands/build.js.map +1 -0
  38. package/dist/commands/inspect.d.ts +49 -0
  39. package/dist/commands/inspect.d.ts.map +1 -0
  40. package/dist/commands/inspect.js +135 -0
  41. package/dist/commands/inspect.js.map +1 -0
  42. package/dist/commands/keypair.d.ts +21 -0
  43. package/dist/commands/keypair.d.ts.map +1 -0
  44. package/dist/commands/keypair.js +91 -0
  45. package/dist/commands/keypair.js.map +1 -0
  46. package/dist/commands/publish.d.ts +40 -0
  47. package/dist/commands/publish.d.ts.map +1 -0
  48. package/dist/commands/publish.js +187 -0
  49. package/dist/commands/publish.js.map +1 -0
  50. package/dist/commands/typegen.d.ts +36 -0
  51. package/dist/commands/typegen.d.ts.map +1 -0
  52. package/dist/commands/typegen.js +210 -0
  53. package/dist/commands/typegen.js.map +1 -0
  54. package/dist/index.d.ts +6 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +6 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/lib/cid.d.ts +3 -0
  59. package/dist/lib/cid.d.ts.map +1 -0
  60. package/dist/lib/cid.js +43 -0
  61. package/dist/lib/cid.js.map +1 -0
  62. package/dist/lib/credentials.d.ts +40 -16
  63. package/dist/lib/credentials.d.ts.map +1 -0
  64. package/dist/lib/credentials.js +222 -49
  65. package/dist/lib/credentials.js.map +1 -0
  66. package/dist/lib/jcs.d.ts +3 -0
  67. package/dist/lib/jcs.d.ts.map +1 -0
  68. package/dist/lib/jcs.js +48 -0
  69. package/dist/lib/jcs.js.map +1 -0
  70. package/dist/lib/signing.d.ts +13 -0
  71. package/dist/lib/signing.d.ts.map +1 -0
  72. package/dist/lib/signing.js +34 -0
  73. package/dist/lib/signing.js.map +1 -0
  74. package/dist/lib.d.ts +18 -0
  75. package/dist/lib.d.ts.map +1 -0
  76. package/dist/lib.js +12 -0
  77. package/dist/lib.js.map +1 -0
  78. package/dist/typegen/augment.d.ts +57 -0
  79. package/dist/typegen/augment.d.ts.map +1 -0
  80. package/dist/typegen/augment.js +87 -0
  81. package/dist/typegen/augment.js.map +1 -0
  82. package/dist/typegen/fetch.d.ts +30 -0
  83. package/dist/typegen/fetch.d.ts.map +1 -0
  84. package/dist/typegen/fetch.js +38 -0
  85. package/dist/typegen/fetch.js.map +1 -0
  86. package/dist/typegen/lockfile.d.ts +42 -0
  87. package/dist/typegen/lockfile.d.ts.map +1 -0
  88. package/dist/typegen/lockfile.js +58 -0
  89. package/dist/typegen/lockfile.js.map +1 -0
  90. package/dist/typegen/schema-to-ts.d.ts +21 -0
  91. package/dist/typegen/schema-to-ts.d.ts.map +1 -0
  92. package/dist/typegen/schema-to-ts.js +29 -0
  93. package/dist/typegen/schema-to-ts.js.map +1 -0
  94. package/package.json +23 -58
  95. package/dist/commands/auth/index.d.ts +0 -2
  96. package/dist/commands/auth/index.js +0 -162
  97. package/dist/commands/deploy/clone.d.ts +0 -13
  98. package/dist/commands/deploy/clone.js +0 -143
  99. package/dist/commands/deploy/delete.d.ts +0 -14
  100. package/dist/commands/deploy/delete.js +0 -134
  101. package/dist/commands/deploy/deploy.d.ts +0 -24
  102. package/dist/commands/deploy/deploy.js +0 -654
  103. package/dist/commands/deploy/index.d.ts +0 -2
  104. package/dist/commands/deploy/index.js +0 -216
  105. package/dist/commands/deploy/status.d.ts +0 -14
  106. package/dist/commands/deploy/status.js +0 -61
  107. package/dist/commands/dev/index.d.ts +0 -2
  108. package/dist/commands/dev/index.js +0 -134
  109. package/dist/commands/dev/server.d.ts +0 -14
  110. package/dist/commands/dev/server.js +0 -195
  111. package/dist/commands/dev/static/app.js +0 -633
  112. package/dist/commands/dev/static/index.html +0 -205
  113. package/dist/commands/dev/static/static/app.js +0 -633
  114. package/dist/commands/dev/static/static/index.html +0 -205
  115. package/dist/commands/dev/static/static/style.css +0 -184
  116. package/dist/commands/dev/static/style.css +0 -184
  117. package/dist/commands/playground/index.d.ts +0 -2
  118. package/dist/commands/playground/index.js +0 -75
  119. package/dist/commands/playground/test.d.ts +0 -15
  120. package/dist/commands/playground/test.js +0 -166
  121. package/dist/commands/service/index.d.ts +0 -2
  122. package/dist/commands/service/index.js +0 -89
  123. package/dist/lib/api.d.ts +0 -146
  124. package/dist/lib/api.js +0 -330
  125. package/dist/lib/config.d.ts +0 -12
  126. package/dist/lib/config.js +0 -145
  127. package/dist/lib/runtime/agent-router.d.ts +0 -12
  128. package/dist/lib/runtime/agent-router.js +0 -67
  129. package/dist/lib/runtime/context-builder.d.ts +0 -27
  130. package/dist/lib/runtime/context-builder.js +0 -166
  131. package/dist/lib/runtime/cubby-manager.d.ts +0 -20
  132. package/dist/lib/runtime/cubby-manager.js +0 -162
  133. package/dist/lib/runtime/index.d.ts +0 -51
  134. package/dist/lib/runtime/index.js +0 -181
  135. package/dist/lib/runtime/log-collector.d.ts +0 -17
  136. package/dist/lib/runtime/log-collector.js +0 -61
  137. package/dist/lib/runtime/stream-manager.d.ts +0 -10
  138. package/dist/lib/runtime/stream-manager.js +0 -77
  139. package/dist/lib/runtime/types.d.ts +0 -76
  140. package/dist/lib/runtime/types.js +0 -2
  141. package/dist/lib/transpile.d.ts +0 -1
  142. package/dist/lib/transpile.js +0 -9
  143. package/dist/lib/typecheck.d.ts +0 -31
  144. package/dist/lib/typecheck.js +0 -175
  145. package/dist/lib/types.d.ts +0 -246
  146. package/dist/lib/types.js +0 -2
  147. package/dist/lib/utils.d.ts +0 -4
  148. package/dist/lib/utils.js +0 -33
  149. package/example/.env.example +0 -6
  150. package/example/agents/object-detection/tasks/yolo.ts +0 -28
  151. package/example/agents/parking-violation-detector/tasks/detect.ts +0 -326
  152. package/example/cef.config.yaml +0 -91
  153. 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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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Public package entry. Re-exports the programmatic API from `./lib.ts`.
3
+ * The CLI binary lives in `./cli.ts` (wired up via `package.json`'s `bin`).
4
+ */
5
+ export * from "./lib.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -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,6 @@
1
+ /**
2
+ * Public package entry. Re-exports the programmatic API from `./lib.ts`.
3
+ * The CLI binary lives in `./cli.ts` (wired up via `package.json`'s `bin`).
4
+ */
5
+ export * from "./lib.js";
6
+ //# sourceMappingURL=index.js.map
@@ -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,3 @@
1
+ export declare const MANIFEST_CID_PREFIX = "blake3:";
2
+ export declare function computeManifestCid(manifest: object): string;
3
+ //# sourceMappingURL=cid.d.ts.map
@@ -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"}
@@ -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 interface StoredCredentials {
2
- email: string;
3
- publicKey: string;
4
- token: string;
5
- expiresAt: string;
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 declare function getCredentialsPath(): string;
8
- export declare function saveCredentials(creds: StoredCredentials): void;
9
- export declare function loadCredentials(): StoredCredentials | null;
10
- export declare function clearCredentials(): boolean;
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
- * Parse a wallet JWT token and extract payload fields.
13
- * Token format: base64(header).base64(payload).signature
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 parseToken(token: string): {
16
- email: string;
17
- publicKey: string;
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"}
@@ -1,68 +1,241 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import os from 'os';
4
- function getCefDir() {
5
- return path.join(os.homedir(), '.cef');
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
- export function getCredentialsPath() {
8
- return path.join(getCefDir(), 'credentials.json');
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
- export function saveCredentials(creds) {
11
- const dir = getCefDir();
12
- if (!fs.existsSync(dir)) {
13
- fs.mkdirSync(dir, { recursive: true });
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
- fs.writeFileSync(path.join(dir, 'credentials.json'), JSON.stringify(creds, null, 2), 'utf8');
53
+ return hex.toLowerCase();
16
54
  }
17
- export function loadCredentials() {
18
- const credFile = getCredentialsPath();
19
- if (!fs.existsSync(credFile)) {
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
- const raw = fs.readFileSync(credFile, 'utf8');
24
- const creds = JSON.parse(raw);
25
- if (!creds.token || !creds.expiresAt) {
26
- return null;
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
- return creds;
74
+ throw e;
75
+ }
76
+ let parsed;
77
+ try {
78
+ parsed = JSON.parse(raw);
34
79
  }
35
- catch {
36
- return null;
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
- export function clearCredentials() {
40
- const credFile = getCredentialsPath();
41
- if (fs.existsSync(credFile)) {
42
- fs.unlinkSync(credFile);
43
- return true;
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 false;
129
+ return out;
46
130
  }
47
131
  /**
48
- * Parse a wallet JWT token and extract payload fields.
49
- * Token format: base64(header).base64(payload).signature
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 parseToken(token) {
52
- try {
53
- const parts = token.split('.');
54
- if (parts.length !== 3 || !parts[1])
55
- return null;
56
- const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString('utf8'));
57
- if (!payload.email || !payload.publicKey || !payload.exp)
58
- return null;
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
- email: payload.email,
61
- publicKey: payload.publicKey,
62
- expiresAt: new Date(payload.exp).toISOString(),
186
+ pubkey: normalizePubkey(file.pubkey, opts.keystoreFlag),
187
+ privkeyBytes: hexToBytes(file.privkey, opts.keystoreFlag),
188
+ source: "keystore-flag",
63
189
  };
64
190
  }
65
- catch {
66
- return null;
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