@epilot/cli 0.1.194 → 0.1.196

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 (85) hide show
  1. package/README.md +89 -1
  2. package/dist/{access-token-3XCBQDFI.js → access-token-7DZIIHDK.js} +4 -4
  3. package/dist/{add-component-GTIUBZP6.js → add-component-M2NRQPLH.js} +4 -4
  4. package/dist/{add-function-5RIKXAEC.js → add-function-ZA7DR3NM.js} +4 -4
  5. package/dist/{address-ACXTZTLB.js → address-OEHR5MKW.js} +4 -4
  6. package/dist/{address-suggestions-MIUHAFVV.js → address-suggestions-SF3YNSJN.js} +4 -4
  7. package/dist/agent-auth-GV2T4GET.js +42 -0
  8. package/dist/{ai-agents-OWQRQK5A.js → ai-agents-3SI3KJ5H.js} +4 -4
  9. package/dist/{api-PQZEGSUV.js → api-7HGTYTZV.js} +4 -4
  10. package/dist/app-JLV2WOYK.js +26 -0
  11. package/dist/{audit-logs-3DVFWVWL.js → audit-logs-I2MPPQ3V.js} +4 -4
  12. package/dist/auth-6NBJQ6NB.js +235 -0
  13. package/dist/auth-login-7D6YJLCA.js +34 -0
  14. package/dist/{auth-token-APXLIQAO.js → auth-token-CI5L3CCJ.js} +2 -2
  15. package/dist/{automation-NHRRP6J4.js → automation-GTVLSKXA.js} +4 -4
  16. package/dist/{billing-PCSW2TQP.js → billing-TNGDZAJ2.js} +4 -4
  17. package/dist/bin/epilot.js +84 -60
  18. package/dist/{blueprint-manifest-BPWLPHIU.js → blueprint-manifest-KNZRON75.js} +4 -4
  19. package/dist/{calendar-Y6AGKVWR.js → calendar-4RMKSNHL.js} +4 -4
  20. package/dist/{chat-567O35XV.js → chat-3AKMQXQG.js} +4 -4
  21. package/dist/{chunk-5KHD2F3J.js → chunk-BIOWN64A.js} +3 -3
  22. package/dist/chunk-G6KI5Z7X.js +606 -0
  23. package/dist/{chunk-KTCBDUL2.js → chunk-JQW3WOJB.js} +1 -1
  24. package/dist/{auth-login-3AY5U4JU.js → chunk-OI6GYEIX.js} +378 -11
  25. package/dist/{chunk-YHQA2AVG.js → chunk-QPUCHKXS.js} +1 -0
  26. package/dist/{chunk-M3M3C5WH.js → chunk-SQ55BTMV.js} +17 -3
  27. package/dist/{chunk-WWO5RPHF.js → chunk-Y4B3K4IO.js} +7 -7
  28. package/dist/{config-DGZIMLZK.js → config-PJWYWLQH.js} +1 -1
  29. package/dist/{configuration-hub-YPOS6ELC.js → configuration-hub-6HKPZL7E.js} +4 -4
  30. package/dist/{consent-SFI4EUGQ.js → consent-3TMN7LZO.js} +4 -4
  31. package/dist/{customer-portal-UUKBB67Z.js → customer-portal-HS3PVMHL.js} +4 -4
  32. package/dist/{dashboard-GERIZIZV.js → dashboard-WL7NVOF5.js} +4 -4
  33. package/dist/{data-governance-XVJWDVER.js → data-governance-VVEJZIN3.js} +4 -4
  34. package/dist/{deduplication-JJ46Y2CW.js → deduplication-WXROVSZU.js} +4 -4
  35. package/dist/{deploy-GZMQOG6O.js → deploy-Y2EQYBAB.js} +4 -4
  36. package/dist/{design-LYYI4DLQ.js → design-YWSM4GN6.js} +4 -4
  37. package/dist/{dev-HDXS3UPB.js → dev-OFEMDQSS.js} +4 -4
  38. package/dist/{document-5MD75ESX.js → document-HAUPZTDG.js} +4 -4
  39. package/dist/{email-settings-UKM5LYDQ.js → email-settings-47SYOOPL.js} +4 -4
  40. package/dist/{email-template-KT7YPDBF.js → email-template-JN3H6VFT.js} +4 -4
  41. package/dist/{entity-AG73SZKS.js → entity-5T4F2PEC.js} +4 -4
  42. package/dist/{entity-mapping-6MIHNIDB.js → entity-mapping-3L52T4IU.js} +4 -4
  43. package/dist/{environments-W7LRHBDB.js → environments-MKFLKYTI.js} +4 -4
  44. package/dist/{event-catalog-NCAHETDE.js → event-catalog-PZ4SFMRO.js} +4 -4
  45. package/dist/{export-2B5BAXHF.js → export-RNPPIDBT.js} +4 -4
  46. package/dist/{external-values-NHZ2EZMQ.js → external-values-FZHZGV2J.js} +4 -4
  47. package/dist/{file-Y6B2UFNC.js → file-AIIKVSDS.js} +4 -4
  48. package/dist/{iban-33N4BJUC.js → iban-2SDYV2Q6.js} +4 -4
  49. package/dist/{identity-GQ5VOASS.js → identity-CQXELR2Y.js} +4 -4
  50. package/dist/{init-5C4IJDPX.js → init-5PYFSAQA.js} +4 -4
  51. package/dist/{integration-toolkit-KTUXC24K.js → integration-toolkit-CTRGST6J.js} +4 -4
  52. package/dist/{journey-3X6BQOOO.js → journey-3CVYG5KT.js} +4 -4
  53. package/dist/{kanban-F7PRPBGV.js → kanban-2J3VBDUG.js} +4 -4
  54. package/dist/{message-K4ZMKQJE.js → message-PGJ5XXDI.js} +4 -4
  55. package/dist/{metering-ZK6VMFZ7.js → metering-DA5HJTIB.js} +4 -4
  56. package/dist/{notes-6Z42UHWN.js → notes-CUCXKOK6.js} +4 -4
  57. package/dist/{notification-NPY7FMPG.js → notification-3BE3GJLR.js} +4 -4
  58. package/dist/org-YNGJNLKY.js +427 -0
  59. package/dist/{organization-NK3CCP7F.js → organization-5MKK22PI.js} +4 -4
  60. package/dist/{partner-directory-WSFRXNTY.js → partner-directory-4CI3UQXB.js} +4 -4
  61. package/dist/{permissions-Q36SZHXQ.js → permissions-XNFMEENQ.js} +4 -4
  62. package/dist/{pricing-JSFN62BD.js → pricing-3C6LGOLN.js} +4 -4
  63. package/dist/{pricing-tier-XURRWW25.js → pricing-tier-I67ZQHPI.js} +4 -4
  64. package/dist/{profile-OZJL5ZPT.js → profile-G2FH3DXR.js} +1 -1
  65. package/dist/{purpose-GKPVUTBK.js → purpose-BSPOIU7I.js} +4 -4
  66. package/dist/{query-JZUAVWPY.js → query-5KMRWBMY.js} +4 -4
  67. package/dist/{remove-component-KLWF3QZO.js → remove-component-W2E6ZA2E.js} +4 -4
  68. package/dist/{review-L4QLVEXC.js → review-Y5RBCWJX.js} +4 -4
  69. package/dist/{sandbox-EKAOWJSR.js → sandbox-SUR4RZFD.js} +4 -4
  70. package/dist/{sharing-TKHVDJ5B.js → sharing-UZ4QWKBD.js} +4 -4
  71. package/dist/{snapshot-A2FU3NYU.js → snapshot-UAZZXLQF.js} +4 -4
  72. package/dist/{submission-NTQTZ2MG.js → submission-2ISK6IJV.js} +4 -4
  73. package/dist/{targeting-7HCCTEIJ.js → targeting-4RDTQAXT.js} +4 -4
  74. package/dist/{template-variables-ABHPJ6QR.js → template-variables-4Z3MZEGG.js} +4 -4
  75. package/dist/{upgrade-YRZHOL6I.js → upgrade-FPJAHNOK.js} +1 -1
  76. package/dist/{user-WEW5SBST.js → user-YTQDFHMV.js} +4 -4
  77. package/dist/{validate-SX4DIEWH.js → validate-7TSPYKFI.js} +4 -4
  78. package/dist/{validation-rules-PNUE7RIC.js → validation-rules-BCXYX5QO.js} +4 -4
  79. package/dist/{versions-D5NPDKRX.js → versions-LBLHNSOL.js} +4 -4
  80. package/dist/{webhooks-ECQT77EA.js → webhooks-KHULCU4Z.js} +4 -4
  81. package/dist/{workflow-JOZVUSX2.js → workflow-Q74OHJKH.js} +4 -4
  82. package/dist/{workflow-definition-GPBXEKKH.js → workflow-definition-GWMKIEUE.js} +4 -4
  83. package/package.json +3 -2
  84. package/dist/app-EJONBOTI.js +0 -26
  85. package/dist/auth-WMXFMPWE.js +0 -119
@@ -0,0 +1,606 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ saveCredentials
4
+ } from "./chunk-SQ55BTMV.js";
5
+ import {
6
+ getConfigDir,
7
+ getResolvedProfile,
8
+ resolveProfileName
9
+ } from "./chunk-QPUCHKXS.js";
10
+ import {
11
+ BOLD,
12
+ DIM,
13
+ RESET,
14
+ YELLOW
15
+ } from "./chunk-7ZQ666ZQ.js";
16
+
17
+ // src/lib/agent-auth.ts
18
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
19
+ import { join } from "path";
20
+
21
+ // ../agent-auth/dist/index.js
22
+ import { createHash, generateKeyPairSync } from "crypto";
23
+ import { createPrivateKey, randomUUID, sign as cryptoSign } from "crypto";
24
+ var AgentAuthError = class extends Error {
25
+ constructor(status, code, message, details) {
26
+ super(message);
27
+ this.status = status;
28
+ this.code = code;
29
+ this.details = details;
30
+ this.name = "AgentAuthError";
31
+ }
32
+ };
33
+ var jwkThumbprint = (jwk) => createHash("sha256").update(JSON.stringify({ crv: jwk.crv, kty: jwk.kty, x: jwk.x })).digest("base64url");
34
+ var publicJwk = (jwk) => ({ kty: "OKP", crv: "Ed25519", x: jwk.x });
35
+ var generateKeyPair = () => {
36
+ const { privateKey } = generateKeyPairSync("ed25519");
37
+ const jwk = privateKey.export({ format: "jwk" });
38
+ const privateJwk = { kty: "OKP", crv: "Ed25519", x: jwk.x, d: jwk.d };
39
+ return { publicKey: publicJwk(privateJwk), privateKey: privateJwk, thumbprint: jwkThumbprint(privateJwk) };
40
+ };
41
+ var keyPairFromPrivateJwk = (privateJwk) => {
42
+ if (!privateJwk.d) throw new Error('A private Ed25519 JWK (with "d") is required.');
43
+ return { publicKey: publicJwk(privateJwk), privateKey: privateJwk, thumbprint: jwkThumbprint(privateJwk) };
44
+ };
45
+ var b64url = (input) => Buffer.from(input).toString("base64url");
46
+ var nowSeconds = () => Math.floor(Date.now() / 1e3);
47
+ var signJwt = (privateJwk, header, payload) => {
48
+ const key = createPrivateKey({
49
+ key: { kty: "OKP", crv: "Ed25519", x: privateJwk.x, d: privateJwk.d },
50
+ format: "jwk"
51
+ });
52
+ const encodedHeader = b64url(JSON.stringify({ alg: "EdDSA", ...header }));
53
+ const encodedPayload = b64url(JSON.stringify(payload));
54
+ const signature = cryptoSign(null, Buffer.from(`${encodedHeader}.${encodedPayload}`), key);
55
+ return `${encodedHeader}.${encodedPayload}.${b64url(signature)}`;
56
+ };
57
+ var createHostJwt = ({ hostKey, audience, agentPublicKey, ttlSeconds = 60 }) => {
58
+ const iat = nowSeconds();
59
+ return signJwt(
60
+ hostKey.privateKey,
61
+ { typ: "host+jwt" },
62
+ {
63
+ iss: hostKey.thumbprint,
64
+ aud: audience,
65
+ iat,
66
+ exp: iat + ttlSeconds,
67
+ jti: randomUUID(),
68
+ host_public_key: hostKey.publicKey,
69
+ ...agentPublicKey ? { agent_public_key: publicJwk(agentPublicKey) } : {}
70
+ }
71
+ );
72
+ };
73
+ var createAgentJwt = ({
74
+ agentKey,
75
+ hostThumbprint,
76
+ agentId,
77
+ audience,
78
+ capabilities,
79
+ ttlSeconds = 60
80
+ }) => {
81
+ const iat = nowSeconds();
82
+ return signJwt(
83
+ agentKey.privateKey,
84
+ { typ: "agent+jwt" },
85
+ {
86
+ iss: hostThumbprint,
87
+ sub: agentId,
88
+ aud: audience,
89
+ iat,
90
+ exp: iat + Math.min(ttlSeconds, 60),
91
+ jti: randomUUID(),
92
+ ...capabilities ? { capabilities } : {}
93
+ }
94
+ );
95
+ };
96
+ var trimSlash = (value) => {
97
+ let end = value.length;
98
+ while (end > 0 && value[end - 1] === "/") end--;
99
+ return value.slice(0, end);
100
+ };
101
+ var safeJson = (text) => {
102
+ try {
103
+ return JSON.parse(text);
104
+ } catch {
105
+ return void 0;
106
+ }
107
+ };
108
+ var AgentAuthClient = class {
109
+ baseUrl;
110
+ fetchImpl;
111
+ discoveryTtlMs;
112
+ timeoutMs;
113
+ discovery;
114
+ constructor(options) {
115
+ this.baseUrl = trimSlash(options.baseUrl);
116
+ this.fetchImpl = options.fetch ?? fetch;
117
+ this.discoveryTtlMs = options.discoveryTtlMs ?? 36e5;
118
+ this.timeoutMs = options.timeoutMs ?? 15e3;
119
+ }
120
+ /** GET /.well-known/agent-configuration (cached). */
121
+ async discover(force = false) {
122
+ if (!force && this.discovery && Date.now() - this.discovery.fetchedAt < this.discoveryTtlMs) {
123
+ return this.discovery.value;
124
+ }
125
+ const value = await this.request("GET", `${this.baseUrl}/.well-known/agent-configuration`);
126
+ this.discovery = { value, fetchedAt: Date.now() };
127
+ return value;
128
+ }
129
+ /** Resolve an endpoint from discovery; relative paths are resolved against the issuer. */
130
+ async endpoint(name) {
131
+ const config = await this.discover();
132
+ const path = config.endpoints[name];
133
+ return /^https?:\/\//.test(path) ? path : `${trimSlash(config.issuer)}${path.startsWith("/") ? "" : "/"}${path}`;
134
+ }
135
+ /** POST /agent/register with a host JWT that carries the agent's public key. */
136
+ async registerAgent(hostKey, agentKey, body) {
137
+ const config = await this.discover();
138
+ const jwt = createHostJwt({ hostKey, audience: config.issuer, agentPublicKey: agentKey.publicKey });
139
+ return this.request("POST", await this.endpoint("register"), body, jwt);
140
+ }
141
+ /** POST /agent/request-capability with an agent JWT. */
142
+ async requestCapability(identity, body) {
143
+ const config = await this.discover();
144
+ const jwt = createAgentJwt({
145
+ agentKey: identity.agentKey,
146
+ hostThumbprint: identity.hostKey.thumbprint,
147
+ agentId: identity.agentId,
148
+ audience: config.issuer
149
+ });
150
+ return this.request(
151
+ "POST",
152
+ await this.endpoint("request_capability"),
153
+ body,
154
+ jwt
155
+ );
156
+ }
157
+ /** GET /agent/status?agent_id=… with a host JWT. */
158
+ async getAgentStatus(hostKey, agentId) {
159
+ const config = await this.discover();
160
+ const jwt = createHostJwt({ hostKey, audience: config.issuer });
161
+ const url = new URL(await this.endpoint("status"));
162
+ url.searchParams.set("agent_id", agentId);
163
+ return this.request("GET", url.toString(), void 0, jwt);
164
+ }
165
+ /**
166
+ * Poll /agent/status until the agent leaves `pending` (or, when `pendingGrantIds`
167
+ * is given, until none of those grants is pending). Honors the approval
168
+ * interval and stops at the approval expiry.
169
+ */
170
+ async waitForApproval(hostKey, agentId, approval, options = {}) {
171
+ const intervalMs = Math.max(1, approval?.interval ?? 5) * 1e3;
172
+ const deadline = Date.now() + (approval?.expires_in ?? 600) * 1e3;
173
+ for (; ; ) {
174
+ if (options.signal?.aborted) throw new AgentAuthError(499, "aborted", "Approval wait was aborted.");
175
+ const status = await this.getAgentStatus(hostKey, agentId);
176
+ options.onPoll?.(status);
177
+ const stillPending = options.pendingGrantIds?.length ? status.agent_capability_grants.some(
178
+ (grant) => grant.id && options.pendingGrantIds?.includes(grant.id) && grant.status === "pending"
179
+ ) : status.status === "pending";
180
+ if (!stillPending) return status;
181
+ if (Date.now() + intervalMs > deadline) {
182
+ throw new AgentAuthError(408, "approval_expired", "The user did not approve the request in time.");
183
+ }
184
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
185
+ }
186
+ }
187
+ /** POST /capability/execute with an agent JWT whose `aud` is the capability location. */
188
+ async execute(identity, body, location) {
189
+ const target = location ?? (await this.discover()).default_location;
190
+ const jwt = createAgentJwt({
191
+ agentKey: identity.agentKey,
192
+ hostThumbprint: identity.hostKey.thumbprint,
193
+ agentId: identity.agentId,
194
+ audience: target,
195
+ capabilities: [body.capability]
196
+ });
197
+ const result = await this.request("POST", target, body, jwt);
198
+ if (result && typeof result === "object" && "data" in result) return result.data;
199
+ if (result?.status === "completed") return result.result;
200
+ return result;
201
+ }
202
+ /** GET /capability/list — anonymous, or with grant status when authenticated. */
203
+ async listCapabilities(auth, query = {}) {
204
+ const config = await this.discover();
205
+ const url = new URL(await this.endpoint("capabilities"));
206
+ for (const [key, value] of Object.entries(query)) {
207
+ if (value !== void 0) url.searchParams.set(key, String(value));
208
+ }
209
+ const jwt = auth ? this.jwtFor(auth, config.issuer) : void 0;
210
+ return this.request("GET", url.toString(), void 0, jwt);
211
+ }
212
+ /** GET /capability/describe?name=… */
213
+ async describeCapability(name, auth) {
214
+ const config = await this.discover();
215
+ const url = new URL(await this.endpoint("describe_capability"));
216
+ url.searchParams.set("name", name);
217
+ const jwt = auth ? this.jwtFor(auth, config.issuer) : void 0;
218
+ return this.request("GET", url.toString(), void 0, jwt);
219
+ }
220
+ async reactivateAgent(hostKey, agentId) {
221
+ return this.hostPost("reactivate", hostKey, { agent_id: agentId });
222
+ }
223
+ async revokeAgent(hostKey, agentId) {
224
+ return this.hostPost("revoke", hostKey, { agent_id: agentId });
225
+ }
226
+ async rotateAgentKey(hostKey, agentId, newAgentKey) {
227
+ return this.hostPost("rotate_key", hostKey, { agent_id: agentId, public_key: newAgentKey.publicKey });
228
+ }
229
+ async rotateHostKey(hostKey, newHostKey) {
230
+ return this.hostPost("rotate_host_key", hostKey, { public_key: newHostKey.publicKey });
231
+ }
232
+ async revokeHost(hostKey) {
233
+ return this.hostPost("revoke_host", hostKey, {});
234
+ }
235
+ /** Server-to-server: validate an agent JWT. `bearer` is whatever the server requires (epilot: an epilot token). */
236
+ async introspect(token, bearer) {
237
+ return this.request("POST", await this.endpoint("introspect"), { token }, bearer);
238
+ }
239
+ jwtFor(auth, audience) {
240
+ return "agentId" in auth ? createAgentJwt({
241
+ agentKey: auth.agentKey,
242
+ hostThumbprint: auth.hostKey.thumbprint,
243
+ agentId: auth.agentId,
244
+ audience
245
+ }) : createHostJwt({ hostKey: auth.hostKey, audience });
246
+ }
247
+ async hostPost(name, hostKey, body) {
248
+ const config = await this.discover();
249
+ const jwt = createHostJwt({ hostKey, audience: config.issuer });
250
+ return this.request("POST", await this.endpoint(name), body, jwt);
251
+ }
252
+ async request(method, url, body, bearer) {
253
+ const controller = new AbortController();
254
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
255
+ let response;
256
+ try {
257
+ response = await this.fetchImpl(url, {
258
+ method,
259
+ headers: {
260
+ accept: "application/json",
261
+ ...body !== void 0 ? { "content-type": "application/json" } : {},
262
+ ...bearer ? { authorization: `Bearer ${bearer}` } : {}
263
+ },
264
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
265
+ signal: controller.signal
266
+ });
267
+ } catch (error) {
268
+ throw new AgentAuthError(0, "network_error", `Agent Auth request failed: ${String(error)}`);
269
+ } finally {
270
+ clearTimeout(timer);
271
+ }
272
+ const text = await response.text();
273
+ const json = text ? safeJson(text) : void 0;
274
+ if (!response.ok) {
275
+ const error = json ?? {};
276
+ throw new AgentAuthError(
277
+ response.status,
278
+ error.error ?? `http_${response.status}`,
279
+ error.message ?? error.error_description ?? `Agent Auth server responded with ${response.status}.`,
280
+ error
281
+ );
282
+ }
283
+ return json;
284
+ }
285
+ };
286
+ var EPILOT_CAPABILITIES = {
287
+ /** The linked user's organizations annotated with this agent's grants. Host default capability. */
288
+ organizationsList: "epilot.organizations.list",
289
+ /** Mint a short-lived epilot access token for an organization the agent has a grant for. */
290
+ accessTokenIssue: "epilot.access_token.issue"
291
+ };
292
+ var ACCESS_PROFILES = ["read", "config:read", "config:write", "data:read", "data:write", "full"];
293
+ var DAY = 86400;
294
+ var ACCESS_PROFILE_INFO = {
295
+ read: {
296
+ title: "Read everything you can see",
297
+ description: "Look at data and configuration you have access to. Nothing can be created or changed.",
298
+ readOnly: true,
299
+ anonymizeAllowed: true
300
+ },
301
+ "config:read": {
302
+ title: "Read configuration",
303
+ description: "Look at journeys, automations, workflows, schemas, portals, designs and other configuration. No business data, nothing changed.",
304
+ readOnly: true,
305
+ anonymizeAllowed: true,
306
+ escalationTtlSeconds: 7 * DAY
307
+ },
308
+ "config:write": {
309
+ title: "Change configuration",
310
+ description: "Create and change journeys, automations, workflows, schemas, portals, designs and other configuration. No business data.",
311
+ readOnly: false,
312
+ anonymizeAllowed: false,
313
+ escalationTtlSeconds: DAY
314
+ },
315
+ "data:read": {
316
+ title: "Read business data",
317
+ description: "Look at contacts, opportunities, orders, files, messages and other business data. No configuration changes.",
318
+ readOnly: true,
319
+ anonymizeAllowed: true,
320
+ escalationTtlSeconds: 7 * DAY
321
+ },
322
+ "data:write": {
323
+ title: "Change business data",
324
+ description: "Create and change contacts, opportunities, orders, files, messages and other business data. No configuration changes.",
325
+ readOnly: false,
326
+ anonymizeAllowed: false,
327
+ escalationTtlSeconds: DAY
328
+ },
329
+ full: {
330
+ title: "Everything you can do",
331
+ description: "Everything your own account can do, including changing data and configuration.",
332
+ readOnly: false,
333
+ anonymizeAllowed: false,
334
+ escalationTtlSeconds: DAY
335
+ }
336
+ };
337
+ var isAccessProfile = (value) => typeof value === "string" && ACCESS_PROFILES.includes(value);
338
+ var isReadProfile = (profile) => ACCESS_PROFILE_INFO[profile].readOnly;
339
+ var PROFILE_RANK = {
340
+ read: 0,
341
+ "config:read": 1,
342
+ "data:read": 2,
343
+ "config:write": 3,
344
+ "data:write": 4,
345
+ full: 5
346
+ };
347
+ var mostPermissiveProfile = (profiles) => profiles.length ? [...profiles].sort((a, b) => PROFILE_RANK[b] - PROFILE_RANK[a])[0] : void 0;
348
+ var REASON_MIN_LENGTH = 10;
349
+ var REASON_MAX_LENGTH = 200;
350
+ var organizationAccessCapability = (options = {}) => {
351
+ if (options.anonymize === true && options.profile && !ACCESS_PROFILE_INFO[options.profile].anonymizeAllowed) {
352
+ throw new AgentAuthError(400, "invalid_capabilities", "anonymize is only available with read profiles", {
353
+ access_profile: options.profile
354
+ });
355
+ }
356
+ const constraints = {
357
+ ...options.organizationId !== void 0 ? { organization_id: options.organizationId } : {},
358
+ ...options.profile !== void 0 ? { access_profile: options.profile } : {},
359
+ ...options.readOnly !== void 0 ? { read_only: options.readOnly } : {},
360
+ ...options.anonymize !== void 0 ? { anonymize: options.anonymize } : {}
361
+ };
362
+ return Object.keys(constraints).length ? { name: EPILOT_CAPABILITIES.accessTokenIssue, constraints } : { name: EPILOT_CAPABILITIES.accessTokenIssue };
363
+ };
364
+ var listEpilotOrganizations = (client, identity) => client.execute(identity, {
365
+ capability: EPILOT_CAPABILITIES.organizationsList
366
+ });
367
+ var issueEpilotAccessToken = (client, identity, args) => client.execute(identity, {
368
+ capability: EPILOT_CAPABILITIES.accessTokenIssue,
369
+ arguments: { ...args }
370
+ });
371
+ var validateReason = (profile, reason) => {
372
+ const trimmed = reason?.trim() || void 0;
373
+ if (profile !== "read" && (!trimmed || trimmed.length < REASON_MIN_LENGTH)) {
374
+ throw new AgentAuthError(
375
+ 400,
376
+ "reason_required",
377
+ `A reason of at least ${REASON_MIN_LENGTH} characters is required when requesting the ${profile} profile.`,
378
+ { access_profile: profile }
379
+ );
380
+ }
381
+ if (trimmed && trimmed.length > REASON_MAX_LENGTH) {
382
+ throw new AgentAuthError(400, "invalid_request", `The reason must not exceed ${REASON_MAX_LENGTH} characters.`);
383
+ }
384
+ return trimmed;
385
+ };
386
+ var requestOrganizationAccess = async (client, identity, options) => {
387
+ const profile = options.profile ?? "read";
388
+ const anonymize = options.anonymize ?? ACCESS_PROFILE_INFO[profile].anonymizeAllowed;
389
+ const reason = validateReason(profile, options.reason);
390
+ const capability = organizationAccessCapability({ organizationId: options.organizationId, profile, anonymize });
391
+ return client.requestCapability(identity, {
392
+ capabilities: [capability],
393
+ ...reason ? { reason } : {},
394
+ ...options.approvalReturnUri ? { approval_return_uri: options.approvalReturnUri } : {}
395
+ });
396
+ };
397
+ var organizationGrants = (grants) => grants.filter((grant) => grant.capability === EPILOT_CAPABILITIES.accessTokenIssue).map((grant) => {
398
+ const organizationId = grant.constraints?.organization_id;
399
+ const rawProfile = grant.constraints?.access_profile;
400
+ const profile = isAccessProfile(rawProfile) ? rawProfile : "read";
401
+ const info = ACCESS_PROFILE_INFO[profile];
402
+ const readOnly = typeof grant.constraints?.read_only === "boolean" ? grant.constraints.read_only : info.readOnly;
403
+ return {
404
+ grant,
405
+ organizationId: typeof organizationId === "string" ? organizationId : void 0,
406
+ profile,
407
+ readOnly,
408
+ anonymized: info.anonymizeAllowed && grant.constraints?.anonymize !== false,
409
+ expiresAt: grant.expires_at,
410
+ reason: grant.reason
411
+ };
412
+ });
413
+ var isGrantUsable = (grant, now = Date.now()) => grant.grant.status === "active" && (!grant.expiresAt || new Date(grant.expiresAt).getTime() > now);
414
+ var epilotAgentAuthIssuer = (stage = "production") => stage === "production" ? "https://access-token.sls.epilot.io/v1/access-tokens/agent-auth" : `https://access-token.${stage}.sls.epilot.io/v1/access-tokens/agent-auth`;
415
+
416
+ // src/lib/agent-auth.ts
417
+ var REFRESH_WINDOW_MS = 2 * 60 * 1e3;
418
+ var agentAuthDir = () => join(getConfigDir(), "agent-auth");
419
+ var hostPath = () => join(agentAuthDir(), "host.json");
420
+ var agentsDir = () => join(agentAuthDir(), "agents");
421
+ var agentProfileKey = (profileName) => resolveProfileName(profileName) ?? "default";
422
+ var agentPath = (profileName) => join(agentsDir(), `${agentProfileKey(profileName)}.json`);
423
+ var readJson = (path) => {
424
+ if (!existsSync(path)) return null;
425
+ try {
426
+ return JSON.parse(readFileSync(path, "utf-8"));
427
+ } catch {
428
+ return null;
429
+ }
430
+ };
431
+ var writeSecret = (path, value) => {
432
+ mkdirSync(join(path, ".."), { recursive: true, mode: 448 });
433
+ writeFileSync(path, JSON.stringify(value, null, 2), { mode: 384 });
434
+ };
435
+ var loadHostKey = () => {
436
+ const record = readJson(hostPath());
437
+ if (!record?.privateKey?.d) return null;
438
+ try {
439
+ return keyPairFromPrivateJwk(record.privateKey);
440
+ } catch {
441
+ return null;
442
+ }
443
+ };
444
+ var ensureHostKey = () => {
445
+ const existing = loadHostKey();
446
+ if (existing) return existing;
447
+ const key = generateKeyPair();
448
+ const record = {
449
+ privateKey: key.privateKey,
450
+ thumbprint: key.thumbprint,
451
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
452
+ };
453
+ writeSecret(hostPath(), record);
454
+ return key;
455
+ };
456
+ var loadAgentRecord = (profileName) => {
457
+ const record = readJson(agentPath(profileName));
458
+ return record?.agent_id && record.privateKey?.d ? record : null;
459
+ };
460
+ var saveAgentRecord = (record, profileName) => {
461
+ writeSecret(agentPath(profileName), record);
462
+ };
463
+ var deleteAgentRecord = (profileName) => {
464
+ const path = agentPath(profileName);
465
+ if (!existsSync(path)) return false;
466
+ rmSync(path);
467
+ return true;
468
+ };
469
+ var resolveAgentAuthIssuer = (env = "production") => process.env.EPILOT_AGENT_AUTH_ISSUER || epilotAgentAuthIssuer(env);
470
+ var getAgentAuthClient = (envOrIssuer = "production") => {
471
+ const baseUrl = /^https?:\/\//.test(envOrIssuer) ? envOrIssuer : resolveAgentAuthIssuer(envOrIssuer);
472
+ return new AgentAuthClient({ baseUrl });
473
+ };
474
+ var loadAgentIdentity = (profileName) => {
475
+ const record = loadAgentRecord(profileName);
476
+ const hostKey = loadHostKey();
477
+ if (!record || !hostKey) return null;
478
+ try {
479
+ const agentKey = keyPairFromPrivateJwk(record.privateKey);
480
+ return {
481
+ identity: { hostKey, agentKey, agentId: record.agent_id },
482
+ record,
483
+ client: getAgentAuthClient(record.issuer),
484
+ profileKey: agentProfileKey(profileName)
485
+ };
486
+ } catch {
487
+ return null;
488
+ }
489
+ };
490
+ var forgetAgentForProfile = async (profileName, out = (s) => process.stdout.write(s)) => {
491
+ const loaded = loadAgentIdentity(profileName);
492
+ if (!loaded) return false;
493
+ try {
494
+ await loaded.client.revokeAgent(loaded.identity.hostKey, loaded.identity.agentId);
495
+ out(`${DIM}Agent ${loaded.record.agent_id} from a previous --agent login revoked.${RESET}
496
+ `);
497
+ } catch (error) {
498
+ const reason = error instanceof AgentAuthError ? error.code : "error";
499
+ out(`${DIM}Could not revoke agent ${loaded.record.agent_id} (${reason}); removing it locally.${RESET}
500
+ `);
501
+ }
502
+ deleteAgentRecord(profileName);
503
+ return true;
504
+ };
505
+ var issueTokenForOrg = async (loaded, orgId, options = {}) => {
506
+ const issued = await issueEpilotAccessToken(loaded.client, loaded.identity, {
507
+ organization_id: orgId,
508
+ ...options.profile !== void 0 ? { access_profile: options.profile } : {},
509
+ ...options.readOnly !== void 0 ? { read_only: options.readOnly } : {},
510
+ ...options.anonymize !== void 0 ? { anonymize: options.anonymize } : {}
511
+ });
512
+ const accessProfile = isAccessProfile(issued.access_profile) ? issued.access_profile : options.profile;
513
+ const creds = {
514
+ token: issued.token,
515
+ org_id: issued.organization_id ?? orgId,
516
+ user_id: issued.user_id,
517
+ expires_at: issued.expires_at,
518
+ ...accessProfile ? { access_profile: accessProfile } : {},
519
+ ...issued.email ? { name: issued.email } : {}
520
+ };
521
+ saveCredentials(creds, options.profileName);
522
+ saveAgentRecord(
523
+ {
524
+ ...loaded.record,
525
+ org_id: creds.org_id,
526
+ access_profile: accessProfile,
527
+ read_only: issued.read_only ?? options.readOnly,
528
+ anonymize: issued.anonymize ?? options.anonymize
529
+ },
530
+ options.profileName
531
+ );
532
+ return issued;
533
+ };
534
+ var expiresSoon = (expiresAt) => {
535
+ if (!expiresAt) return false;
536
+ const expiry = new Date(expiresAt).getTime();
537
+ return Number.isNaN(expiry) || expiry - Date.now() < REFRESH_WINDOW_MS;
538
+ };
539
+ var refreshTokenIfNeeded = async (flagProfile) => {
540
+ const loaded = loadAgentIdentity(flagProfile);
541
+ if (!loaded) return null;
542
+ const profile = getResolvedProfile(flagProfile);
543
+ const current = profile?.token ? profile : loadStoredCredentials();
544
+ if (current?.token && !expiresSoon(current.expires_at)) return current.token;
545
+ const orgId = current?.org_id ?? loaded.record.org_id;
546
+ if (!orgId) return null;
547
+ const issued = await issueTokenForOrg(loaded, orgId, {
548
+ profile: loaded.record.access_profile,
549
+ readOnly: loaded.record.read_only,
550
+ anonymize: loaded.record.anonymize,
551
+ profileName: flagProfile
552
+ });
553
+ return issued.token;
554
+ };
555
+ var loadStoredCredentials = () => readJson(join(getConfigDir(), "credentials.json"));
556
+ var isAgentGoneError = (error) => error instanceof AgentAuthError && ["agent_revoked", "agent_expired", "agent_rejected", "agent_not_found", "host_revoked"].includes(error.code);
557
+ var printLoginAgainHint = (error) => {
558
+ process.stderr.write(`${YELLOW}Agent session is no longer valid (${error.code}).${RESET} `);
559
+ process.stderr.write(`Run ${BOLD}epilot auth login --agent${RESET} to authenticate again.
560
+ `);
561
+ };
562
+ var formatExpiresIn = (expiresAt, now = Date.now()) => {
563
+ if (!expiresAt) return void 0;
564
+ const diffMs = new Date(expiresAt).getTime() - now;
565
+ if (Number.isNaN(diffMs)) return void 0;
566
+ if (diffMs <= 0) return "expired";
567
+ const minutes = Math.floor(diffMs / 6e4);
568
+ if (minutes < 60) return `expires in ${Math.max(1, minutes)}m`;
569
+ const hours = Math.floor(minutes / 60);
570
+ if (hours < 48) return `expires in ${hours}h`;
571
+ return `expires in ${Math.floor(hours / 24)}d`;
572
+ };
573
+
574
+ export {
575
+ AgentAuthError,
576
+ generateKeyPair,
577
+ EPILOT_CAPABILITIES,
578
+ ACCESS_PROFILES,
579
+ ACCESS_PROFILE_INFO,
580
+ isAccessProfile,
581
+ isReadProfile,
582
+ mostPermissiveProfile,
583
+ organizationAccessCapability,
584
+ listEpilotOrganizations,
585
+ validateReason,
586
+ requestOrganizationAccess,
587
+ organizationGrants,
588
+ isGrantUsable,
589
+ REFRESH_WINDOW_MS,
590
+ agentProfileKey,
591
+ agentPath,
592
+ loadHostKey,
593
+ ensureHostKey,
594
+ loadAgentRecord,
595
+ saveAgentRecord,
596
+ deleteAgentRecord,
597
+ resolveAgentAuthIssuer,
598
+ getAgentAuthClient,
599
+ loadAgentIdentity,
600
+ forgetAgentForProfile,
601
+ issueTokenForOrg,
602
+ refreshTokenIfNeeded,
603
+ isAgentGoneError,
604
+ printLoginAgainHint,
605
+ formatExpiresIn
606
+ };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/lib/user-agent.ts
4
- var CLI_USER_AGENT = `epilot-cli/${true ? "0.1.194" : "dev"}`;
4
+ var CLI_USER_AGENT = `epilot-cli/${true ? "0.1.196" : "dev"}`;
5
5
 
6
6
  export {
7
7
  CLI_USER_AGENT