dexto 1.14.5 → 2.0.0-beta.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 (256) hide show
  1. package/dist/cli/auth/billing.d.ts +0 -6
  2. package/dist/cli/auth/billing.d.ts.map +1 -1
  3. package/dist/cli/auth/billing.js +0 -23
  4. package/dist/cli/auth/constants.d.ts +3 -31
  5. package/dist/cli/auth/constants.d.ts.map +1 -1
  6. package/dist/cli/auth/constants.js +9 -33
  7. package/dist/cli/auth/index.d.ts +2 -9
  8. package/dist/cli/auth/index.d.ts.map +1 -1
  9. package/dist/cli/auth/index.js +2 -7
  10. package/dist/cli/auth/sign-in-runtime.d.ts +12 -0
  11. package/dist/cli/auth/sign-in-runtime.d.ts.map +1 -0
  12. package/dist/cli/auth/sign-in-runtime.js +59 -0
  13. package/dist/cli/cloud/app-scopes.d.ts +4 -0
  14. package/dist/cli/cloud/app-scopes.d.ts.map +1 -0
  15. package/dist/cli/cloud/app-scopes.js +19 -0
  16. package/dist/cli/cloud/command.d.ts +143 -0
  17. package/dist/cli/cloud/command.d.ts.map +1 -0
  18. package/dist/cli/cloud/command.js +202 -0
  19. package/dist/cli/cloud/commands/account.d.ts +11 -0
  20. package/dist/cli/cloud/commands/account.d.ts.map +1 -0
  21. package/dist/cli/cloud/commands/account.js +128 -0
  22. package/dist/cli/cloud/commands/agents.d.ts +3 -0
  23. package/dist/cli/cloud/commands/agents.d.ts.map +1 -0
  24. package/dist/cli/cloud/commands/agents.js +524 -0
  25. package/dist/cli/cloud/commands/api.d.ts +3 -0
  26. package/dist/cli/cloud/commands/api.d.ts.map +1 -0
  27. package/dist/cli/cloud/commands/api.js +26 -0
  28. package/dist/cli/cloud/commands/app-files.d.ts +29 -0
  29. package/dist/cli/cloud/commands/app-files.d.ts.map +1 -0
  30. package/dist/cli/cloud/commands/app-files.js +72 -0
  31. package/dist/cli/cloud/commands/automations.d.ts +3 -0
  32. package/dist/cli/cloud/commands/automations.d.ts.map +1 -0
  33. package/dist/cli/cloud/commands/automations.js +232 -0
  34. package/dist/cli/cloud/commands/chats.d.ts +3 -0
  35. package/dist/cli/cloud/commands/chats.d.ts.map +1 -0
  36. package/dist/cli/cloud/commands/chats.js +189 -0
  37. package/dist/cli/cloud/commands/data-deletions.d.ts +3 -0
  38. package/dist/cli/cloud/commands/data-deletions.d.ts.map +1 -0
  39. package/dist/cli/cloud/commands/data-deletions.js +54 -0
  40. package/dist/cli/cloud/commands/deploys.d.ts +10 -0
  41. package/dist/cli/cloud/commands/deploys.d.ts.map +1 -0
  42. package/dist/cli/cloud/commands/deploys.js +223 -0
  43. package/dist/cli/cloud/commands/drive.d.ts +12 -0
  44. package/dist/cli/cloud/commands/drive.d.ts.map +1 -0
  45. package/dist/cli/cloud/commands/drive.js +320 -0
  46. package/dist/cli/cloud/commands/files.d.ts +24 -0
  47. package/dist/cli/cloud/commands/files.d.ts.map +1 -0
  48. package/dist/cli/cloud/commands/files.js +282 -0
  49. package/dist/cli/cloud/commands/find.d.ts +3 -0
  50. package/dist/cli/cloud/commands/find.d.ts.map +1 -0
  51. package/dist/cli/cloud/commands/find.js +63 -0
  52. package/dist/cli/cloud/commands/models.d.ts +3 -0
  53. package/dist/cli/cloud/commands/models.d.ts.map +1 -0
  54. package/dist/cli/cloud/commands/models.js +248 -0
  55. package/dist/cli/cloud/commands/profile.d.ts +3 -0
  56. package/dist/cli/cloud/commands/profile.d.ts.map +1 -0
  57. package/dist/cli/cloud/commands/profile.js +119 -0
  58. package/dist/cli/cloud/commands/projects.d.ts +5 -0
  59. package/dist/cli/cloud/commands/projects.d.ts.map +1 -0
  60. package/dist/cli/cloud/commands/projects.js +66 -0
  61. package/dist/cli/cloud/commands/publish.d.ts +3 -0
  62. package/dist/cli/cloud/commands/publish.d.ts.map +1 -0
  63. package/dist/cli/cloud/commands/publish.js +138 -0
  64. package/dist/cli/cloud/commands/sandboxes.d.ts +3 -0
  65. package/dist/cli/cloud/commands/sandboxes.d.ts.map +1 -0
  66. package/dist/cli/cloud/commands/sandboxes.js +265 -0
  67. package/dist/cli/cloud/commands/secrets.d.ts +3 -0
  68. package/dist/cli/cloud/commands/secrets.d.ts.map +1 -0
  69. package/dist/cli/cloud/commands/secrets.js +110 -0
  70. package/dist/cli/cloud/commands/skills.d.ts +3 -0
  71. package/dist/cli/cloud/commands/skills.d.ts.map +1 -0
  72. package/dist/cli/cloud/commands/skills.js +202 -0
  73. package/dist/cli/cloud/commands/web.d.ts +3 -0
  74. package/dist/cli/cloud/commands/web.d.ts.map +1 -0
  75. package/dist/cli/cloud/commands/web.js +53 -0
  76. package/dist/cli/cloud/commands/workspace-apps.d.ts +58 -0
  77. package/dist/cli/cloud/commands/workspace-apps.d.ts.map +1 -0
  78. package/dist/cli/cloud/commands/workspace-apps.js +656 -0
  79. package/dist/cli/cloud/commands/workspace.d.ts +36 -0
  80. package/dist/cli/cloud/commands/workspace.d.ts.map +1 -0
  81. package/dist/cli/cloud/commands/workspace.js +279 -0
  82. package/dist/cli/cloud/connection.d.ts +33 -0
  83. package/dist/cli/cloud/connection.d.ts.map +1 -0
  84. package/dist/cli/cloud/connection.js +38 -0
  85. package/dist/cli/cloud/events.d.ts +63 -0
  86. package/dist/cli/cloud/events.d.ts.map +1 -0
  87. package/dist/cli/cloud/events.js +72 -0
  88. package/dist/cli/cloud/host.d.ts +9 -0
  89. package/dist/cli/cloud/host.d.ts.map +1 -0
  90. package/dist/cli/cloud/host.js +105 -0
  91. package/dist/cli/cloud/local-chats.d.ts +73 -0
  92. package/dist/cli/cloud/local-chats.d.ts.map +1 -0
  93. package/dist/cli/cloud/local-chats.js +441 -0
  94. package/dist/cli/cloud/output.d.ts +34 -0
  95. package/dist/cli/cloud/output.d.ts.map +1 -0
  96. package/dist/cli/cloud/output.js +76 -0
  97. package/dist/cli/cloud/run.d.ts +26 -0
  98. package/dist/cli/cloud/run.d.ts.map +1 -0
  99. package/dist/cli/cloud/run.js +444 -0
  100. package/dist/cli/cloud/runtime.d.ts +33 -0
  101. package/dist/cli/cloud/runtime.d.ts.map +1 -0
  102. package/dist/cli/cloud/runtime.js +86 -0
  103. package/dist/cli/cloud/tar.d.ts +9 -0
  104. package/dist/cli/cloud/tar.d.ts.map +1 -0
  105. package/dist/cli/cloud/tar.js +69 -0
  106. package/dist/cli/cloud/transfer.d.ts +13 -0
  107. package/dist/cli/cloud/transfer.d.ts.map +1 -0
  108. package/dist/cli/cloud/transfer.js +35 -0
  109. package/dist/cli/cloud/v1.d.ts +19 -0
  110. package/dist/cli/cloud/v1.d.ts.map +1 -0
  111. package/dist/cli/cloud/v1.js +65 -0
  112. package/dist/cli/cloud/zip.d.ts +8 -0
  113. package/dist/cli/cloud/zip.d.ts.map +1 -0
  114. package/dist/cli/cloud/zip.js +66 -0
  115. package/dist/cli/commands/auth/sign-in-commands.d.ts +62 -0
  116. package/dist/cli/commands/auth/sign-in-commands.d.ts.map +1 -0
  117. package/dist/cli/commands/auth/sign-in-commands.js +247 -0
  118. package/dist/cli/commands/auth/sign-in-context.d.ts +16 -0
  119. package/dist/cli/commands/auth/sign-in-context.d.ts.map +1 -0
  120. package/dist/cli/commands/auth/sign-in-context.js +68 -0
  121. package/dist/cli/commands/cloud/register.d.ts +5 -0
  122. package/dist/cli/commands/cloud/register.d.ts.map +1 -1
  123. package/dist/cli/commands/cloud/register.js +42 -24
  124. package/dist/cli/commands/create-app.js +5 -6
  125. package/dist/cli/commands/create-image.d.ts.map +1 -1
  126. package/dist/cli/commands/create-image.js +9 -10
  127. package/dist/cli/commands/index.d.ts +0 -2
  128. package/dist/cli/commands/index.d.ts.map +1 -1
  129. package/dist/cli/commands/index.js +0 -2
  130. package/dist/cli/commands/init.d.ts +0 -2
  131. package/dist/cli/commands/init.d.ts.map +1 -1
  132. package/dist/cli/commands/init.js +5 -26
  133. package/dist/cli/commands/run/headless.js +2 -2
  134. package/dist/cli/commands/run/output.d.ts +29 -10
  135. package/dist/cli/commands/run/output.d.ts.map +1 -1
  136. package/dist/cli/commands/run/output.js +44 -22
  137. package/dist/cli/commands/run/register.d.ts.map +1 -1
  138. package/dist/cli/commands/run/register.js +21 -9
  139. package/dist/cli/commands/session/register.d.ts.map +1 -1
  140. package/dist/cli/commands/session/register.js +51 -21
  141. package/dist/cli/commands/session-commands.js +1 -1
  142. package/dist/cli/commands/setup.js +17 -14
  143. package/dist/cli/help-json.d.ts +22 -0
  144. package/dist/cli/help-json.d.ts.map +1 -0
  145. package/dist/cli/help-json.js +20 -0
  146. package/dist/cli/modes/cli.d.ts.map +1 -1
  147. package/dist/cli/modes/cli.js +42 -9
  148. package/dist/cli/modes/server.js +8 -8
  149. package/dist/cli/modes/web.js +3 -3
  150. package/dist/cli/utils/dexto-auth-check.d.ts +1 -1
  151. package/dist/cli/utils/dexto-auth-check.d.ts.map +1 -1
  152. package/dist/cli/utils/dexto-auth-check.js +4 -5
  153. package/dist/cli/utils/dexto-setup.d.ts +4 -5
  154. package/dist/cli/utils/dexto-setup.d.ts.map +1 -1
  155. package/dist/cli/utils/dexto-setup.js +17 -14
  156. package/dist/cli/utils/provider-setup.js +1 -1
  157. package/dist/cli/utils/scaffolding-utils.d.ts +9 -3
  158. package/dist/cli/utils/scaffolding-utils.d.ts.map +1 -1
  159. package/dist/cli/utils/scaffolding-utils.js +48 -40
  160. package/dist/cli/utils/version-check.d.ts.map +1 -1
  161. package/dist/cli/utils/version-check.js +2 -1
  162. package/dist/index-main.js +33 -65
  163. package/dist/webui/assets/index-D9xiOs62.js +1649 -0
  164. package/dist/webui/index.html +1 -1
  165. package/package.json +15 -14
  166. package/dist/cli/auth/api-client.d.ts +0 -125
  167. package/dist/cli/auth/api-client.d.ts.map +0 -1
  168. package/dist/cli/auth/api-client.js +0 -585
  169. package/dist/cli/auth/device.d.ts +0 -14
  170. package/dist/cli/auth/device.d.ts.map +0 -1
  171. package/dist/cli/auth/device.js +0 -84
  172. package/dist/cli/auth/dexto-api-key.d.ts +0 -34
  173. package/dist/cli/auth/dexto-api-key.d.ts.map +0 -1
  174. package/dist/cli/auth/dexto-api-key.js +0 -160
  175. package/dist/cli/auth/login-persistence.d.ts +0 -15
  176. package/dist/cli/auth/login-persistence.d.ts.map +0 -1
  177. package/dist/cli/auth/login-persistence.js +0 -72
  178. package/dist/cli/auth/oauth.d.ts +0 -39
  179. package/dist/cli/auth/oauth.d.ts.map +0 -1
  180. package/dist/cli/auth/oauth.js +0 -434
  181. package/dist/cli/auth/service.d.ts +0 -24
  182. package/dist/cli/auth/service.d.ts.map +0 -1
  183. package/dist/cli/auth/service.js +0 -161
  184. package/dist/cli/auth/types.d.ts +0 -11
  185. package/dist/cli/auth/types.d.ts.map +0 -1
  186. package/dist/cli/auth/types.js +0 -1
  187. package/dist/cli/cloud-chat.d.ts +0 -18
  188. package/dist/cli/cloud-chat.d.ts.map +0 -1
  189. package/dist/cli/cloud-chat.js +0 -734
  190. package/dist/cli/commands/auth/index.d.ts +0 -4
  191. package/dist/cli/commands/auth/index.d.ts.map +0 -1
  192. package/dist/cli/commands/auth/index.js +0 -4
  193. package/dist/cli/commands/auth/login.d.ts +0 -12
  194. package/dist/cli/commands/auth/login.d.ts.map +0 -1
  195. package/dist/cli/commands/auth/login.js +0 -153
  196. package/dist/cli/commands/auth/logout.d.ts +0 -5
  197. package/dist/cli/commands/auth/logout.d.ts.map +0 -1
  198. package/dist/cli/commands/auth/logout.js +0 -81
  199. package/dist/cli/commands/auth/register.d.ts +0 -3
  200. package/dist/cli/commands/auth/register.d.ts.map +0 -1
  201. package/dist/cli/commands/auth/register.js +0 -96
  202. package/dist/cli/commands/auth/status.d.ts +0 -2
  203. package/dist/cli/commands/auth/status.d.ts.map +0 -1
  204. package/dist/cli/commands/auth/status.js +0 -22
  205. package/dist/cli/commands/billing/index.d.ts +0 -2
  206. package/dist/cli/commands/billing/index.d.ts.map +0 -1
  207. package/dist/cli/commands/billing/index.js +0 -2
  208. package/dist/cli/commands/billing/register.d.ts +0 -3
  209. package/dist/cli/commands/billing/register.d.ts.map +0 -1
  210. package/dist/cli/commands/billing/register.js +0 -20
  211. package/dist/cli/commands/billing/status.d.ts +0 -8
  212. package/dist/cli/commands/billing/status.d.ts.map +0 -1
  213. package/dist/cli/commands/billing/status.js +0 -71
  214. package/dist/cli/commands/cloud/discovery.d.ts +0 -17
  215. package/dist/cli/commands/cloud/discovery.d.ts.map +0 -1
  216. package/dist/cli/commands/cloud/discovery.js +0 -88
  217. package/dist/cli/commands/deploy/client.d.ts +0 -113
  218. package/dist/cli/commands/deploy/client.d.ts.map +0 -1
  219. package/dist/cli/commands/deploy/client.js +0 -519
  220. package/dist/cli/commands/deploy/config.d.ts +0 -37
  221. package/dist/cli/commands/deploy/config.d.ts.map +0 -1
  222. package/dist/cli/commands/deploy/config.js +0 -144
  223. package/dist/cli/commands/deploy/entry-agent.d.ts +0 -3
  224. package/dist/cli/commands/deploy/entry-agent.d.ts.map +0 -1
  225. package/dist/cli/commands/deploy/entry-agent.js +0 -70
  226. package/dist/cli/commands/deploy/index.d.ts +0 -12
  227. package/dist/cli/commands/deploy/index.d.ts.map +0 -1
  228. package/dist/cli/commands/deploy/index.js +0 -370
  229. package/dist/cli/commands/deploy/links.d.ts +0 -3
  230. package/dist/cli/commands/deploy/links.d.ts.map +0 -1
  231. package/dist/cli/commands/deploy/links.js +0 -53
  232. package/dist/cli/commands/deploy/register.d.ts +0 -6
  233. package/dist/cli/commands/deploy/register.d.ts.map +0 -1
  234. package/dist/cli/commands/deploy/register.js +0 -128
  235. package/dist/cli/commands/deploy/snapshot.d.ts +0 -12
  236. package/dist/cli/commands/deploy/snapshot.d.ts.map +0 -1
  237. package/dist/cli/commands/deploy/snapshot.js +0 -76
  238. package/dist/cli/commands/deploy/state.d.ts +0 -13
  239. package/dist/cli/commands/deploy/state.d.ts.map +0 -1
  240. package/dist/cli/commands/deploy/state.js +0 -122
  241. package/dist/cli/commands/span/register.d.ts +0 -6
  242. package/dist/cli/commands/span/register.d.ts.map +0 -1
  243. package/dist/cli/commands/span/register.js +0 -25
  244. package/dist/cli/commands/trace/client.d.ts +0 -98
  245. package/dist/cli/commands/trace/client.d.ts.map +0 -1
  246. package/dist/cli/commands/trace/client.js +0 -150
  247. package/dist/cli/commands/trace/format.d.ts +0 -5
  248. package/dist/cli/commands/trace/format.d.ts.map +0 -1
  249. package/dist/cli/commands/trace/format.js +0 -58
  250. package/dist/cli/commands/trace/index.d.ts +0 -21
  251. package/dist/cli/commands/trace/index.d.ts.map +0 -1
  252. package/dist/cli/commands/trace/index.js +0 -49
  253. package/dist/cli/commands/trace/register.d.ts +0 -6
  254. package/dist/cli/commands/trace/register.d.ts.map +0 -1
  255. package/dist/cli/commands/trace/register.js +0 -71
  256. package/dist/webui/assets/index-DEfRFmFT.js +0 -1667
@@ -0,0 +1,35 @@
1
+ // Sends local bytes to a URL the server handed out for them, outside MCP JSON.
2
+ import { CommandFailure, isJsonObject } from './command.js';
3
+ /**
4
+ * Streams `body` to the target with its length, and returns the JSON reply (null when there is
5
+ * none). A refusal fails with the server's own message.
6
+ */
7
+ export async function sendBytes(context, target, body) {
8
+ const response = await context.runtime.fetch(target.url, {
9
+ body,
10
+ headers: { ...target.headers, 'content-length': String(body.size) },
11
+ method: target.method,
12
+ signal: context.runtime.signal,
13
+ });
14
+ const text = await response.text();
15
+ const parsed = parseJson(text);
16
+ if (response.ok)
17
+ return parsed;
18
+ throw new CommandFailure(response.status === 404 ? 'not_found' : 'failed', `The upload was refused (HTTP ${response.status}): ${refusalMessage(parsed) ?? (text || 'no reason given')}`, null);
19
+ }
20
+ function parseJson(text) {
21
+ try {
22
+ return JSON.parse(text);
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ }
28
+ /** `{"error":{"message"}}` from the API, or `{"error_description"}` from an upload route. */
29
+ function refusalMessage(body) {
30
+ if (!isJsonObject(body))
31
+ return null;
32
+ if (isJsonObject(body.error) && typeof body.error.message === 'string')
33
+ return body.error.message;
34
+ return typeof body.error_description === 'string' ? body.error_description : null;
35
+ }
@@ -0,0 +1,19 @@
1
+ import { CommandFailure, type CommandContext, type SignInContext } from './command.js';
2
+ /**
3
+ * Sends one request to a `/v1` path with the sign-in's credential and Cloud project. The path is
4
+ * resolved against the API and checked, so no path can send the credential elsewhere.
5
+ */
6
+ export declare function fetchV1(context: CommandContext, method: string, requestPath: string, body: string | null): Promise<Response>;
7
+ /**
8
+ * Sends one request to a `/v1` path with its own headers and body, such as a file or a request
9
+ * for an event stream; `signal` ends it early.
10
+ */
11
+ export declare function sendV1(context: CommandContext, method: string, requestPath: string, content: {
12
+ readonly body: string | globalThis.Blob | null;
13
+ readonly headers: Readonly<Record<string, string>>;
14
+ } | null, signal?: AbortSignal): Promise<Response>;
15
+ /** As `fetchV1`, returning the JSON body of a success and failing with the error's kind otherwise. */
16
+ export declare function callV1(context: CommandContext, method: string, requestPath: string, body: string | null): Promise<unknown>;
17
+ /** A failed call as the contract's failure, by its status and the error's message. */
18
+ export declare function v1Failure(context: SignInContext, status: number, text: string): CommandFailure;
19
+ //# sourceMappingURL=v1.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v1.d.ts","sourceRoot":"","sources":["../../../src/cli/cloud/v1.ts"],"names":[],"mappings":"AACA,OAAO,EACH,cAAc,EAId,KAAK,cAAc,EACnB,KAAK,aAAa,EACrB,MAAM,cAAc,CAAC;AAEtB;;;GAGG;AACH,wBAAsB,OAAO,CACzB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAAC,QAAQ,CAAC,CAOnB;AAED;;;GAGG;AACH,wBAAsB,MAAM,CACxB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE;IACL,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC,IAAI,GAAG,IAAI,CAAC;IAC/C,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACtD,GAAG,IAAI,EACR,MAAM,GAAE,WAAoC,GAC7C,OAAO,CAAC,QAAQ,CAAC,CAsBnB;AAED,sGAAsG;AACtG,wBAAsB,MAAM,CACxB,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GAAG,IAAI,GACpB,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED,sFAAsF;AACtF,wBAAgB,SAAS,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,cAAc,CA0B9F"}
@@ -0,0 +1,65 @@
1
+ // Calls the public /v1 API as the sign-in, for `cloud api` and what the typed client does not cover.
2
+ import { CommandFailure, isJsonObject, signInCommand, UsageError, } from './command.js';
3
+ /**
4
+ * Sends one request to a `/v1` path with the sign-in's credential and Cloud project. The path is
5
+ * resolved against the API and checked, so no path can send the credential elsewhere.
6
+ */
7
+ export async function fetchV1(context, method, requestPath, body) {
8
+ return await sendV1(context, method, requestPath, body === null ? null : { body, headers: { 'content-type': 'application/json' } });
9
+ }
10
+ /**
11
+ * Sends one request to a `/v1` path with its own headers and body, such as a file or a request
12
+ * for an event stream; `signal` ends it early.
13
+ */
14
+ export async function sendV1(context, method, requestPath, content, signal = context.runtime.signal) {
15
+ const connection = await context.connection();
16
+ const apiOrigin = new URL(connection.apiBaseUrl).origin;
17
+ const url = new URL(requestPath, `${connection.apiBaseUrl}/`);
18
+ if (!requestPath.startsWith('/v1/') ||
19
+ url.origin !== apiOrigin ||
20
+ !url.pathname.startsWith('/v1/'))
21
+ throw new UsageError('The path must start with /v1/; the CLI only calls the public API.');
22
+ return await context.runtime.fetch(url, {
23
+ headers: {
24
+ ...content?.headers,
25
+ authorization: `Bearer ${connection.token}`,
26
+ ...(connection.kind === 'oauth' && connection.cloudProject !== null
27
+ ? { 'dexto-cloud-project': connection.cloudProject }
28
+ : {}),
29
+ },
30
+ method,
31
+ ...(content === null || content.body === null ? {} : { body: content.body }),
32
+ signal,
33
+ });
34
+ }
35
+ /** As `fetchV1`, returning the JSON body of a success and failing with the error's kind otherwise. */
36
+ export async function callV1(context, method, requestPath, body) {
37
+ const response = await fetchV1(context, method, requestPath, body);
38
+ const text = await response.text();
39
+ if (!response.ok)
40
+ throw v1Failure(context, response.status, text);
41
+ return JSON.parse(text);
42
+ }
43
+ /** A failed call as the contract's failure, by its status and the error's message. */
44
+ export function v1Failure(context, status, text) {
45
+ let message = `HTTP ${status}`;
46
+ try {
47
+ const body = JSON.parse(text);
48
+ if (isJsonObject(body) &&
49
+ isJsonObject(body.error) &&
50
+ typeof body.error.message === 'string')
51
+ message = `HTTP ${status}: ${body.error.message}`;
52
+ }
53
+ catch {
54
+ // Not JSON: the status says enough.
55
+ }
56
+ if (status === 401)
57
+ return new CommandFailure('needs_sign_in', message, signInCommand(context, null));
58
+ return new CommandFailure(status === 403
59
+ ? 'needs_permission'
60
+ : status === 404
61
+ ? 'not_found'
62
+ : status === 400 || status === 413 || status === 422
63
+ ? 'invalid_input'
64
+ : 'failed', message, null);
65
+ }
@@ -0,0 +1,8 @@
1
+ export interface ZipFile {
2
+ readonly bytes: Uint8Array;
3
+ /** Relative, with `/` between folders. */
4
+ readonly path: string;
5
+ }
6
+ /** The archive of these files in order, with its central directory. */
7
+ export declare function zipArchive(files: readonly ZipFile[]): Uint8Array<ArrayBuffer>;
8
+ //# sourceMappingURL=zip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zip.d.ts","sourceRoot":"","sources":["../../../src/cli/cloud/zip.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,OAAO;IACpB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACzB;AAED,uEAAuE;AACvE,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,GAAG,UAAU,CAAC,WAAW,CAAC,CAqC7E"}
@@ -0,0 +1,66 @@
1
+ // Writes a .zip of files, as Dexto takes files to publish: each deflated unless that's no smaller.
2
+ import { crc32, deflateRawSync } from 'node:zlib';
3
+ const encoder = new globalThis.TextEncoder();
4
+ /** 1980-01-01, the first date a .zip can hold, so the same files make the same archive. */
5
+ const DOS_DATE = 0x21;
6
+ /** Entry names are UTF-8. */
7
+ const UTF8_FLAG = 0x0800;
8
+ /** The archive of these files in order, with its central directory. */
9
+ export function zipArchive(files) {
10
+ const parts = [];
11
+ const directory = [];
12
+ let offset = 0;
13
+ for (const file of files) {
14
+ const name = encoder.encode(file.path);
15
+ const deflated = deflateRawSync(file.bytes);
16
+ const stored = deflated.byteLength >= file.bytes.byteLength;
17
+ const body = stored ? file.bytes : deflated;
18
+ const entry = {
19
+ checksum: crc32(file.bytes),
20
+ method: stored ? 0 : 8,
21
+ nameLength: name.byteLength,
22
+ size: body.byteLength,
23
+ unpackedSize: file.bytes.byteLength,
24
+ };
25
+ const local = new DataView(new ArrayBuffer(30));
26
+ local.setUint32(0, 0x04034b50, true);
27
+ writeEntryFields(local, 4, entry);
28
+ parts.push(new Uint8Array(local.buffer), name, body);
29
+ const central = new DataView(new ArrayBuffer(46));
30
+ central.setUint32(0, 0x02014b50, true);
31
+ central.setUint16(4, 20, true);
32
+ writeEntryFields(central, 6, entry);
33
+ central.setUint32(42, offset, true);
34
+ directory.push(new Uint8Array(central.buffer), name);
35
+ offset += 30 + name.byteLength + body.byteLength;
36
+ }
37
+ const directorySize = directory.reduce((total, part) => total + part.byteLength, 0);
38
+ const end = new DataView(new ArrayBuffer(22));
39
+ end.setUint32(0, 0x06054b50, true);
40
+ end.setUint16(8, files.length, true);
41
+ end.setUint16(10, files.length, true);
42
+ end.setUint32(12, directorySize, true);
43
+ end.setUint32(16, offset, true);
44
+ return concat([...parts, ...directory, new Uint8Array(end.buffer)]);
45
+ }
46
+ /** The fields a local header and a central directory entry share, from the version needed on. */
47
+ function writeEntryFields(view, at, entry) {
48
+ view.setUint16(at, 20, true);
49
+ view.setUint16(at + 2, UTF8_FLAG, true);
50
+ view.setUint16(at + 4, entry.method, true);
51
+ view.setUint16(at + 6, 0, true);
52
+ view.setUint16(at + 8, DOS_DATE, true);
53
+ view.setUint32(at + 10, entry.checksum, true);
54
+ view.setUint32(at + 14, entry.size, true);
55
+ view.setUint32(at + 18, entry.unpackedSize, true);
56
+ view.setUint16(at + 22, entry.nameLength, true);
57
+ }
58
+ function concat(parts) {
59
+ const out = new Uint8Array(parts.reduce((total, part) => total + part.byteLength, 0));
60
+ let at = 0;
61
+ for (const part of parts) {
62
+ out.set(part, at);
63
+ at += part.byteLength;
64
+ }
65
+ return out;
66
+ }
@@ -0,0 +1,62 @@
1
+ import { dextoAccounts, type dextoCloud, type dextoSignIn } from '@dexto/agent-management';
2
+ type OAuthLogin = dextoAccounts.OAuthLogin;
3
+ type CloudProject = dextoCloud.CloudProject;
4
+ type BrowserSignInInput = dextoSignIn.BrowserSignInInput;
5
+ type EndedSignIn = dextoSignIn.EndedSignIn;
6
+ type SignInInput = dextoSignIn.SignInInput;
7
+ /** Dexto's side of signing in, injected so commands can be tested without a server. */
8
+ export interface DextoSignIn {
9
+ readonly signInWithBrowser: (input: BrowserSignInInput) => Promise<OAuthLogin>;
10
+ readonly signInWithDevice: (input: SignInInput) => Promise<OAuthLogin>;
11
+ readonly endSignIn: (login: OAuthLogin) => Promise<EndedSignIn>;
12
+ readonly refreshSignIn: (login: OAuthLogin) => Promise<OAuthLogin>;
13
+ /** The Cloud projects the signed-in organization has. */
14
+ readonly listProjects: (login: OAuthLogin) => Promise<readonly CloudProject[]>;
15
+ /** Whether the public API accepts the key; false on 401. */
16
+ readonly acceptsApiKey: (apiBaseUrl: string, apiKey: string) => Promise<boolean>;
17
+ }
18
+ export interface SignInContext {
19
+ /** ~/.dexto/accounts */
20
+ readonly dir: string;
21
+ readonly environment: Readonly<Record<string, string | undefined>>;
22
+ /** The Dexto app a new sign-in goes to. */
23
+ readonly appUrl: string;
24
+ /** The public API a new sign-in is saved for. */
25
+ readonly apiBaseUrl: string;
26
+ /** A command's result: the value for JSON output, the text for people. */
27
+ readonly result: (value: unknown, text: string) => void;
28
+ /** Anything else, on stderr. */
29
+ readonly note: (text: string) => void;
30
+ /** Asks the person; false when there is no one to ask. */
31
+ readonly confirm: (question: string) => Promise<boolean>;
32
+ /** False over SSH or without a display: a loopback redirect can't reach this machine. */
33
+ readonly canOpenBrowser: boolean;
34
+ readonly dexto: DextoSignIn;
35
+ }
36
+ export interface LoginOptions {
37
+ readonly profile: string | null;
38
+ readonly org: string | null;
39
+ readonly device: boolean;
40
+ readonly browser: boolean;
41
+ readonly scopes: readonly string[];
42
+ readonly apiKey: string | null;
43
+ }
44
+ export declare function login(context: SignInContext, options: LoginOptions): Promise<void>;
45
+ /** Who the current profile acts as, in a few words, or null when signed out. */
46
+ export declare function describeSignIn(context: SignInContext): Promise<string | null>;
47
+ export declare function logout(context: SignInContext, options: {
48
+ readonly profile: string | null;
49
+ }): Promise<void>;
50
+ export declare function whoami(context: SignInContext, options: {
51
+ readonly profile: string | null;
52
+ readonly apiKey: string | null;
53
+ }): Promise<void>;
54
+ export declare function listAccounts(context: SignInContext, options: {
55
+ readonly profile: string | null;
56
+ }): Promise<void>;
57
+ export declare function useAccount(context: SignInContext, wanted: string): Promise<void>;
58
+ export declare function removeAccount(context: SignInContext, wanted: string, options: {
59
+ readonly yes: boolean;
60
+ }): Promise<void>;
61
+ export {};
62
+ //# sourceMappingURL=sign-in-commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign-in-commands.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/auth/sign-in-commands.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,247 @@
1
+ // `dexto login`, `logout`, `whoami` and `accounts`: signing in to Dexto and the saved profiles.
2
+ import { dextoAccounts } from '@dexto/agent-management';
3
+ import { CommandFailure, UsageError } from '../../cloud/command.js';
4
+ const { DEFAULT_PROFILE, forgetLogin, listAccounts: readAccounts, profileNameFor, readCurrentProfile, readLogin, requireProfileName, resolveCredential, resolveProfile, writeCurrentProfile, writeLogin, } = dextoAccounts;
5
+ export async function login(context, options) {
6
+ const named = options.profile ?? nonEmpty(context.environment.DEXTO_PROFILE);
7
+ // --org names the profile and switches to it, unless --profile or DEXTO_PROFILE chose one.
8
+ const switches = options.org !== null && named === null;
9
+ const profile = options.org !== null && switches
10
+ ? profileNameFor(options.org)
11
+ : await resolveProfile(context.dir, named, {});
12
+ const saved = await readLogin(context.dir, profile);
13
+ if (options.apiKey !== null) {
14
+ if (options.org !== null)
15
+ throw new UsageError('An API key acts as itself; --org is for signing in.');
16
+ await saveApiKey(context, profile, options.apiKey);
17
+ return;
18
+ }
19
+ const previous = saved?.kind === 'oauth' ? saved : null;
20
+ const input = {
21
+ appUrl: previous?.appUrl ?? context.appUrl,
22
+ apiBaseUrl: previous?.apiBaseUrl ?? context.apiBaseUrl,
23
+ // Signing in again starts at the organization the profile was signed in for.
24
+ organization: options.org ?? previous?.signedInAs?.organization.id ?? null,
25
+ // ...and keeps the permissions granted before.
26
+ extraScopes: [...(previous?.scopes ?? []), ...options.scopes.flatMap(splitList)],
27
+ };
28
+ // A browser elsewhere can't reach this machine's loopback redirect, so use a code instead.
29
+ const device = options.device || (options.browser && !context.canOpenBrowser);
30
+ if (device && !options.device)
31
+ context.note('No browser here, so sign in with a code on any device.');
32
+ const signedIn = device
33
+ ? await context.dexto.signInWithDevice(input)
34
+ : await context.dexto.signInWithBrowser({ ...input, openBrowser: options.browser });
35
+ // A name made from --org can match another organization's profile: keep that sign-in.
36
+ const savedOrganization = previous?.signedInAs?.organization ?? null;
37
+ const newOrganization = signedIn.signedInAs?.organization ?? null;
38
+ if (switches &&
39
+ savedOrganization !== null &&
40
+ newOrganization !== null &&
41
+ savedOrganization.id !== newOrganization.id)
42
+ throw new UsageError(`The profile ${profile} already signs in to ${savedOrganization.name}. Sign in to ${newOrganization.name} with --profile <name> to keep both.`);
43
+ const withProject = {
44
+ ...signedIn,
45
+ cloudProject: chooseProject(await context.dexto.listProjects(signedIn), previous?.cloudProject ?? null),
46
+ };
47
+ await writeLogin(context.dir, profile, withProject);
48
+ if (switches)
49
+ await writeCurrentProfile(context.dir, profile);
50
+ const current = (await resolveProfile(context.dir, null, context.environment)) === profile;
51
+ context.result({
52
+ profile,
53
+ current,
54
+ account: signedIn.signedInAs?.account ?? null,
55
+ organization: signedIn.signedInAs?.organization ?? null,
56
+ cloud_project: withProject.cloudProject,
57
+ scopes: withProject.scopes,
58
+ }, [
59
+ `Signed in${describeSignedInAs(signedIn.signedInAs)}${profile === DEFAULT_PROFILE ? '' : ` (profile ${profile})`}.`,
60
+ withProject.cloudProject === null
61
+ ? 'This organization has no Cloud project for Dexto models to bill.'
62
+ : `Dexto models bill Cloud project ${withProject.cloudProject.name}.`,
63
+ ...(current
64
+ ? []
65
+ : [
66
+ `Other commands still use another profile: pass --profile ${profile}, or run dexto accounts use ${profile}.`,
67
+ ]),
68
+ ].join(' '));
69
+ }
70
+ /**
71
+ * The project chosen before while it is still live, so signing in again doesn't move anyone;
72
+ * otherwise the organization's default project.
73
+ */
74
+ function chooseProject(projects, previous) {
75
+ const chosen = projects.find((project) => project.id === previous?.id && !project.archived) ??
76
+ projects.find((project) => project.isDefault && !project.archived) ??
77
+ null;
78
+ return chosen === null ? null : { id: chosen.id, name: chosen.name };
79
+ }
80
+ async function saveApiKey(context, profile, apiKey) {
81
+ if (!(await context.dexto.acceptsApiKey(context.apiBaseUrl, apiKey)))
82
+ throw new CommandFailure('needs_sign_in', 'Dexto refused this API key.', null);
83
+ await writeLogin(context.dir, profile, {
84
+ kind: 'api_key',
85
+ apiKey,
86
+ apiBaseUrl: context.apiBaseUrl,
87
+ savedAt: new Date().toISOString(),
88
+ });
89
+ context.result({ profile, key: maskApiKey(apiKey) }, `Saved API key ${maskApiKey(apiKey)}${profile === DEFAULT_PROFILE ? '' : ` (profile ${profile})`}.`);
90
+ }
91
+ /** Who the current profile acts as, in a few words, or null when signed out. */
92
+ export async function describeSignIn(context) {
93
+ const environmentKey = nonEmpty(context.environment.DEXTO_API_KEY);
94
+ if (environmentKey !== null)
95
+ return `API key ${maskApiKey(environmentKey)} from DEXTO_API_KEY`;
96
+ const profile = await resolveProfile(context.dir, null, context.environment);
97
+ const saved = await readLogin(context.dir, profile);
98
+ if (saved === null)
99
+ return null;
100
+ const suffix = profile === DEFAULT_PROFILE ? '' : ` (profile ${profile})`;
101
+ if (saved.kind === 'api_key')
102
+ return `API key ${maskApiKey(saved.apiKey)}${suffix}`;
103
+ const who = saved.signedInAs === null
104
+ ? saved.appUrl
105
+ : `${saved.signedInAs.account.email} in ${saved.signedInAs.organization.name}`;
106
+ return `${who}${suffix}`;
107
+ }
108
+ export async function logout(context, options) {
109
+ const profile = await resolveProfile(context.dir, options.profile, context.environment);
110
+ const removed = await logOutProfile(context, profile);
111
+ const named = profile === DEFAULT_PROFILE ? '' : ` for profile ${profile}`;
112
+ context.result({ profile, logged_out: removed }, removed ? `Logged out${named}.` : `No saved sign-in${named}.`);
113
+ }
114
+ /**
115
+ * Ends a profile's sign-in on its server, then forgets it here. A sign-in the server didn't end
116
+ * keeps working until it expires, so a note says where to end it.
117
+ */
118
+ async function logOutProfile(context, profile) {
119
+ const saved = await readLogin(context.dir, profile);
120
+ if (saved?.kind === 'oauth') {
121
+ const ended = await context.dexto.endSignIn(saved);
122
+ if (ended.status === 'failed')
123
+ context.note(`Couldn't end the sign-in on ${saved.appUrl} (${ended.reason}). Sign it out at ${new URL('/settings/sign-ins', saved.appUrl).href}, or it keeps working until it expires.`);
124
+ }
125
+ return await forgetLogin(context.dir, profile);
126
+ }
127
+ export async function whoami(context, options) {
128
+ const credential = await resolveCredential({
129
+ dir: context.dir,
130
+ apiKey: options.apiKey,
131
+ profile: options.profile,
132
+ environment: context.environment,
133
+ refresh: context.dexto.refreshSignIn,
134
+ });
135
+ if (credential === null)
136
+ throw new CommandFailure('needs_sign_in', 'Not signed in.', 'dexto login');
137
+ if (credential.kind === 'api_key') {
138
+ const from = credential.source === 'flag'
139
+ ? ' (from --api-key)'
140
+ : credential.source === 'environment'
141
+ ? ' (from DEXTO_API_KEY)'
142
+ : `, profile ${credential.profile}`;
143
+ context.result({ as: 'api_key', key: maskApiKey(credential.token), source: credential.source }, field('api key', `${maskApiKey(credential.token)}${from}`));
144
+ return;
145
+ }
146
+ const { login: saved } = credential;
147
+ context.result({
148
+ as: 'person',
149
+ profile: credential.profile,
150
+ account: saved.signedInAs?.account ?? null,
151
+ organization: saved.signedInAs?.organization ?? null,
152
+ app_url: saved.appUrl,
153
+ cloud_project: saved.cloudProject,
154
+ scopes: saved.scopes,
155
+ }, [
156
+ field('profile', credential.profile),
157
+ field('account', saved.signedInAs?.account.email ?? 'unknown'),
158
+ field('organization', saved.signedInAs === null
159
+ ? 'unknown'
160
+ : `${saved.signedInAs.organization.name} (${saved.signedInAs.organization.slug})`),
161
+ field('app', saved.appUrl),
162
+ field('scopes', saved.scopes.join(' ')),
163
+ ].join('\n'));
164
+ }
165
+ export async function listAccounts(context, options) {
166
+ const inUse = await resolveProfile(context.dir, options.profile, context.environment);
167
+ const accounts = (await readAccounts(context.dir)).map(({ name, login: saved }) => ({
168
+ profile: name,
169
+ current: name === inUse,
170
+ kind: saved.kind,
171
+ account: saved.kind === 'oauth' ? (saved.signedInAs?.account ?? null) : null,
172
+ organization: saved.kind === 'oauth' ? (saved.signedInAs?.organization ?? null) : null,
173
+ key: saved.kind === 'api_key' ? maskApiKey(saved.apiKey) : null,
174
+ url: saved.kind === 'oauth' ? saved.appUrl : saved.apiBaseUrl,
175
+ }));
176
+ const rows = accounts.map((account) => [
177
+ `${account.current ? '*' : ' '} ${account.profile}`,
178
+ account.key !== null ? `API key ${account.key}` : (account.account?.email ?? account.url),
179
+ account.organization?.name ?? '',
180
+ ]);
181
+ context.result({ data: accounts }, rows.length === 0 ? 'No saved sign-ins. Run dexto login.' : table(rows));
182
+ }
183
+ export async function useAccount(context, wanted) {
184
+ const { name, login: saved } = await requireSaved(context, wanted);
185
+ await writeCurrentProfile(context.dir, name);
186
+ const overridden = (await resolveProfile(context.dir, null, context.environment)) !== name;
187
+ context.result({ current: name }, `Now using ${describeProfile(name, saved)}.${overridden ? ' DEXTO_PROFILE still chooses another profile here.' : ''}`);
188
+ }
189
+ export async function removeAccount(context, wanted, options) {
190
+ const { name, login: saved } = await requireSaved(context, wanted);
191
+ const question = saved.kind === 'oauth'
192
+ ? `Log ${describeProfile(name, saved)} out and forget it on this machine?`
193
+ : `Forget ${describeProfile(name, saved)} on this machine?`;
194
+ if (!options.yes && !(await context.confirm(question)))
195
+ throw new CommandFailure('needs_confirmation', `${question} Pass --yes once your person has agreed.`, `dexto accounts remove ${name} --yes`);
196
+ const wasCurrent = (await readCurrentProfile(context.dir)) === name;
197
+ await logOutProfile(context, name);
198
+ if (wasCurrent)
199
+ await writeCurrentProfile(context.dir, DEFAULT_PROFILE);
200
+ context.result({ removed: name, current: wasCurrent ? DEFAULT_PROFILE : null }, `Removed ${name}.${wasCurrent && name !== DEFAULT_PROFILE ? ` Now using ${DEFAULT_PROFILE}.` : ''}`);
201
+ }
202
+ async function requireSaved(context, wanted) {
203
+ const name = requireProfileName(wanted);
204
+ const saved = await readLogin(context.dir, name);
205
+ if (saved === null)
206
+ throw new CommandFailure('not_found', `No saved sign-in is named ${name}.`, `dexto login --profile ${name}`);
207
+ return { name, login: saved };
208
+ }
209
+ /** `work (ada@example.com in Acme)`, or the key or app a profile signs in with. */
210
+ function describeProfile(name, saved) {
211
+ if (saved.kind === 'api_key')
212
+ return `${name} (API key ${maskApiKey(saved.apiKey)})`;
213
+ return saved.signedInAs === null
214
+ ? `${name} (${saved.appUrl})`
215
+ : `${name} (${saved.signedInAs.account.email} in ${saved.signedInAs.organization.name})`;
216
+ }
217
+ /** ` as ada@example.com in Acme`, or nothing when the server didn't say. */
218
+ function describeSignedInAs(signedInAs) {
219
+ return signedInAs === null
220
+ ? ''
221
+ : ` as ${signedInAs.account.email} in ${signedInAs.organization.name}`;
222
+ }
223
+ /** `dxt_123…abcd`: enough to recognize a key without showing it. */
224
+ function maskApiKey(apiKey) {
225
+ return apiKey.length <= 12 ? '…' : `${apiKey.slice(0, 7)}…${apiKey.slice(-4)}`;
226
+ }
227
+ function field(name, value) {
228
+ return `${name.padEnd(13)} ${value}`;
229
+ }
230
+ function table(rows) {
231
+ const widths = rows[0]?.map((_, column) => Math.max(...rows.map((row) => row[column]?.length ?? 0)));
232
+ return rows
233
+ .map((row) => row
234
+ .map((cell, column) => column === row.length - 1 ? cell : cell.padEnd(widths?.[column] ?? 0))
235
+ .join(' ')
236
+ .trimEnd())
237
+ .join('\n');
238
+ }
239
+ function splitList(value) {
240
+ return value
241
+ .split(/[\s,]+/u)
242
+ .map((each) => each.trim())
243
+ .filter((each) => each.length > 0);
244
+ }
245
+ function nonEmpty(value) {
246
+ return value === undefined || value.length === 0 ? null : value;
247
+ }
@@ -0,0 +1,16 @@
1
+ import type { SignInContext } from './sign-in-commands.js';
2
+ /** Where results and notes go, and what cancels a sign-in; the terminal's by default. */
3
+ export interface SignInIo {
4
+ readonly result: (value: unknown, text: string) => void;
5
+ readonly note: (text: string) => void;
6
+ /** Asks the person; false when there is no one to ask. */
7
+ readonly confirm: (question: string) => Promise<boolean>;
8
+ readonly signal: AbortSignal;
9
+ }
10
+ /**
11
+ * Carries this machine's earlier sign-ins into ~/.dexto/accounts, once: the `dexto-cloud` preview
12
+ * CLI's, else dexto 1.x's saved API key.
13
+ */
14
+ export declare function carryOverEarlierSignIns(): Promise<void>;
15
+ export declare function nodeSignInContext(io?: SignInIo): Promise<SignInContext>;
16
+ //# sourceMappingURL=sign-in-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sign-in-context.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/auth/sign-in-context.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,yFAAyF;AACzF,MAAM,WAAW,QAAQ;IACrB,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACzD,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAChC;AAmBD;;;GAGG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAQ7D;AAED,wBAAsB,iBAAiB,CAAC,EAAE,GAAE,QAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAiCvF"}
@@ -0,0 +1,68 @@
1
+ // The sign-in context for this machine: ~/.dexto/accounts, Dexto's OAuth server and the browser.
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import * as p from '@clack/prompts';
5
+ import { dextoAccounts, dextoCloud, dextoSignIn } from '@dexto/agent-management';
6
+ import { getDextoGlobalPath } from '@dexto/core';
7
+ import { shouldAttemptBrowserLaunch } from '../../auth/browser-launch.js';
8
+ import { DEXTO_PLATFORM_URL, DEXTO_PUBLIC_API_URL } from '../../auth/constants.js';
9
+ import { nodeSignInRuntime } from '../../auth/sign-in-runtime.js';
10
+ const TERMINAL = {
11
+ result: (_value, text) => process.stdout.write(`${text}\n`),
12
+ note: (text) => process.stderr.write(`${text}\n`),
13
+ confirm: async (question) => {
14
+ if (!process.stdin.isTTY)
15
+ return false;
16
+ const answer = await p.confirm({ message: question, initialValue: false });
17
+ return answer === true;
18
+ },
19
+ signal: new AbortController().signal,
20
+ };
21
+ /** The folder the internal `dexto-cloud` CLI saved its sign-ins in. */
22
+ function cloudCliDirectory() {
23
+ const configHome = process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), '.config');
24
+ return path.join(configHome, 'dexto-cloud');
25
+ }
26
+ /**
27
+ * Carries this machine's earlier sign-ins into ~/.dexto/accounts, once: the `dexto-cloud` preview
28
+ * CLI's, else dexto 1.x's saved API key.
29
+ */
30
+ export async function carryOverEarlierSignIns() {
31
+ const dir = getDextoGlobalPath('accounts');
32
+ await dextoAccounts.carryOverCloudCliLogins(dir, cloudCliDirectory());
33
+ await dextoAccounts.carryOverDexto1ApiKey(dir, getDextoGlobalPath('', 'auth.json'), DEXTO_PUBLIC_API_URL);
34
+ }
35
+ export async function nodeSignInContext(io = TERMINAL) {
36
+ const dir = getDextoGlobalPath('accounts');
37
+ await carryOverEarlierSignIns();
38
+ const runtime = { ...nodeSignInRuntime(io.signal), note: io.note };
39
+ return {
40
+ dir,
41
+ environment: process.env,
42
+ appUrl: DEXTO_PLATFORM_URL,
43
+ apiBaseUrl: DEXTO_PUBLIC_API_URL,
44
+ result: io.result,
45
+ note: io.note,
46
+ confirm: io.confirm,
47
+ canOpenBrowser: shouldAttemptBrowserLaunch(),
48
+ dexto: {
49
+ signInWithBrowser: (input) => dextoSignIn.signInWithBrowser(runtime, input),
50
+ signInWithDevice: (input) => dextoSignIn.signInWithDevice(runtime, input),
51
+ endSignIn: (login) => dextoSignIn.endSignIn(runtime, login),
52
+ refreshSignIn: (login) => dextoSignIn.refreshSignIn(runtime, login),
53
+ listProjects: (login) => dextoCloud.listCloudProjects(runtime.fetch, login.apiBaseUrl, login.accessToken),
54
+ acceptsApiKey: async (apiBaseUrl, apiKey) => {
55
+ const response = await fetch(`${apiBaseUrl}/v1/models`, {
56
+ headers: { authorization: `Bearer ${apiKey}` },
57
+ signal: AbortSignal.timeout(10_000),
58
+ });
59
+ await response.body?.cancel();
60
+ if (response.status === 401)
61
+ return false;
62
+ if (!response.ok)
63
+ throw new Error(`Couldn't check the API key: HTTP ${response.status}.`);
64
+ return true;
65
+ },
66
+ },
67
+ };
68
+ }
@@ -1,3 +1,8 @@
1
1
  import type { Command } from 'commander';
2
+ /**
3
+ * These words run on the cloud command engine, which reads its own arguments and global options
4
+ * (--json, --api-key, --profile, --project). Analytics records only the command name, never its
5
+ * arguments, which can hold a key.
6
+ */
2
7
  export declare function registerCloudCommand(program: Command): void;
3
8
  //# sourceMappingURL=register.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/cloud/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAazC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgC3D"}
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/cloud/register.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAmBzC;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAuB3D"}