assistant-ui 0.0.120 → 0.0.121

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 (92) hide show
  1. package/README.md +60 -1
  2. package/dist/commands/cloud.d.ts +25 -0
  3. package/dist/commands/cloud.d.ts.map +1 -0
  4. package/dist/commands/cloud.js +194 -0
  5. package/dist/commands/cloud.js.map +1 -0
  6. package/dist/commands/create.d.ts.map +1 -1
  7. package/dist/commands/create.js +8 -0
  8. package/dist/commands/create.js.map +1 -1
  9. package/dist/commands/info.js +1 -1
  10. package/dist/lib/agent-skill.d.ts +2 -2
  11. package/dist/lib/agent-skill.js +1 -1
  12. package/dist/lib/agent-skill.js.map +1 -1
  13. package/dist/lib/cloud-abort.d.ts +2 -0
  14. package/dist/lib/cloud-abort.d.ts.map +1 -0
  15. package/dist/lib/cloud-abort.js +13 -0
  16. package/dist/lib/cloud-abort.js.map +1 -0
  17. package/dist/lib/cloud-auth.d.ts +28 -0
  18. package/dist/lib/cloud-auth.d.ts.map +1 -0
  19. package/dist/lib/cloud-auth.js +169 -0
  20. package/dist/lib/cloud-auth.js.map +1 -0
  21. package/dist/lib/cloud-config.d.ts +21 -0
  22. package/dist/lib/cloud-config.d.ts.map +1 -0
  23. package/dist/lib/cloud-config.js +76 -0
  24. package/dist/lib/cloud-config.js.map +1 -0
  25. package/dist/lib/cloud-provision.d.ts +20 -0
  26. package/dist/lib/cloud-provision.d.ts.map +1 -0
  27. package/dist/lib/cloud-provision.js +55 -0
  28. package/dist/lib/cloud-provision.js.map +1 -0
  29. package/dist/lib/cloud-setup-login.d.ts +9 -0
  30. package/dist/lib/cloud-setup-login.d.ts.map +1 -0
  31. package/dist/lib/cloud-setup-login.js +137 -0
  32. package/dist/lib/cloud-setup-login.js.map +1 -0
  33. package/dist/lib/cloud-url.d.ts +2 -0
  34. package/dist/lib/cloud-url.d.ts.map +1 -0
  35. package/dist/lib/cloud-url.js +14 -0
  36. package/dist/lib/cloud-url.js.map +1 -0
  37. package/dist/lib/transform.js +1 -1
  38. package/dist/program.d.ts.map +1 -1
  39. package/dist/program.js +2 -1
  40. package/dist/program.js.map +1 -1
  41. package/package.json +11 -7
  42. package/src/commands/cloud.test.ts +238 -0
  43. package/src/commands/cloud.ts +455 -0
  44. package/src/commands/create.ts +8 -0
  45. package/src/lib/agent-skill.ts +1 -1
  46. package/src/lib/cloud-abort.ts +12 -0
  47. package/src/lib/cloud-auth.test.ts +500 -0
  48. package/src/lib/cloud-auth.ts +273 -0
  49. package/src/lib/cloud-config.test.ts +82 -0
  50. package/src/lib/cloud-config.ts +138 -0
  51. package/src/lib/cloud-provision.test.ts +132 -0
  52. package/src/lib/cloud-provision.ts +93 -0
  53. package/src/lib/cloud-setup-login.test.ts +361 -0
  54. package/src/lib/cloud-setup-login.ts +240 -0
  55. package/src/lib/cloud-url.ts +19 -0
  56. package/src/program.ts +2 -0
  57. package/templates/cloud-harness/.assistant-ui/template.json +3 -0
  58. package/templates/cloud-harness/.env.example +1 -0
  59. package/templates/cloud-harness/.gitignore +42 -0
  60. package/templates/cloud-harness/README.md +28 -0
  61. package/templates/cloud-harness/app/api/chat/route.ts +13 -0
  62. package/templates/cloud-harness/app/api/credential/route.ts +14 -0
  63. package/templates/cloud-harness/app/assistant.tsx +83 -0
  64. package/templates/cloud-harness/app/favicon.ico +0 -0
  65. package/templates/cloud-harness/app/globals.css +139 -0
  66. package/templates/cloud-harness/app/layout.tsx +37 -0
  67. package/templates/cloud-harness/app/page.tsx +5 -0
  68. package/templates/cloud-harness/components/assistant-ui/elements/attachment.aui.tsx +279 -0
  69. package/templates/cloud-harness/components/assistant-ui/elements/markdown-text.tsx +238 -0
  70. package/templates/cloud-harness/components/assistant-ui/elements/thread.aui.tsx +521 -0
  71. package/templates/cloud-harness/components/assistant-ui/elements/tool-fallback.aui.tsx +1153 -0
  72. package/templates/cloud-harness/components/assistant-ui/elements/tooltip-icon-button.tsx +49 -0
  73. package/templates/cloud-harness/components/ui/avatar.tsx +109 -0
  74. package/templates/cloud-harness/components/ui/button.tsx +58 -0
  75. package/templates/cloud-harness/components/ui/collapsible.tsx +21 -0
  76. package/templates/cloud-harness/components/ui/dialog.tsx +159 -0
  77. package/templates/cloud-harness/components/ui/textarea.tsx +18 -0
  78. package/templates/cloud-harness/components/ui/tooltip.tsx +69 -0
  79. package/templates/cloud-harness/components.json +24 -0
  80. package/templates/cloud-harness/hooks/use-attachment-src.ts +46 -0
  81. package/templates/cloud-harness/hooks/use-copy-to-clipboard.ts +54 -0
  82. package/templates/cloud-harness/lib/utils.ts +1 -0
  83. package/templates/cloud-harness/next.config.ts +6 -0
  84. package/templates/cloud-harness/package.json +51 -0
  85. package/templates/cloud-harness/postcss.config.mjs +5 -0
  86. package/templates/cloud-harness/tsconfig.json +33 -0
  87. package/templates/cloud-harness/vendor/README.md +13 -0
  88. package/templates/cloud-harness/vendor/assistant-ui-react-harness-sdk-0.0.2.tgz +0 -0
  89. package/templates/cloud-harness/vendor/harness-sdk-0.3.1.tgz +0 -0
  90. package/templates/cloud-harness/vendor/provenance.json +38 -0
  91. package/templates/cloud-harness/vendor/statewire-0.19.3.tgz +0 -0
  92. /package/{LICENSE → templates/cloud-harness/vendor/LICENSE} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"agent-skill.js","names":[],"sources":["../../src/lib/agent-skill.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { downloadTemplate } from \"giget\";\nimport {\n DOWNLOAD_TIMEOUT_MS,\n dlxCommand,\n resolveGitHubAuthToken,\n} from \"./create-project\";\nimport { type PackageManagerName } from \"./utils/package-manager\";\n\nexport const SKILLS_PACKAGE = \"assistant-ui/skills\";\nexport const SKILLS_COMMIT = \"4007601e2e16762060f02d45ad82f453eed5409a\";\nexport const SKILLS_PLUGIN_SOURCE = `gh:${SKILLS_PACKAGE}/assistant-ui#${SKILLS_COMMIT}`;\n\nexport function resolveSkillsInstall(params: {\n skills?: boolean;\n stdinIsTTY?: boolean;\n}): boolean | undefined {\n const { skills, stdinIsTTY = process.stdin.isTTY } = params;\n if (skills !== undefined) return skills;\n if (!stdinIsTTY) return true;\n return undefined;\n}\n\nexport function buildSkillsAddCommand(\n pm: PackageManagerName,\n params: { stdinIsTTY?: boolean } = {},\n): [string, string[]] {\n const { stdinIsTTY = process.stdin.isTTY } = params;\n const [command, dlxArgs] = dlxCommand(pm);\n const args = [...dlxArgs, \"skills\", \"add\", SKILLS_PACKAGE];\n\n // Without a TTY the skills CLI cannot prompt for agent platforms, so skip its\n // confirmation; with a TTY it owns the platform selection interactively.\n if (!stdinIsTTY) args.push(\"--yes\");\n\n return [command, args];\n}\n\nexport function skillsPluginDir(): string {\n const cacheHome =\n process.env.XDG_CACHE_HOME || path.join(os.homedir(), \".cache\");\n return path.join(cacheHome, \"assistant-ui\", \"skills\", SKILLS_COMMIT);\n}\n\nexport async function ensureSkillsPlugin(): Promise<string> {\n const dir = skillsPluginDir();\n const marker = path.join(dir, \".claude-plugin\", \"plugin.json\");\n if (fs.existsSync(marker)) return dir;\n\n const parent = path.dirname(dir);\n await fs.promises.mkdir(parent, { recursive: true });\n const staging = await fs.promises.mkdtemp(path.join(parent, \".staging-\"));\n // Node emits no \"exit\" when a signal kills the process, so the staging\n // directory is removed on the signal and the signal re-raised. The\n // listeners stay armed until the removal runs, which a download that\n // outlives the timeout defers past the caller's own exit.\n const removeStaging = () => {\n process.removeListener(\"exit\", removeStaging);\n process.removeListener(\"SIGINT\", removeStagingOnSignal);\n process.removeListener(\"SIGTERM\", removeStagingOnSignal);\n fs.rmSync(staging, { recursive: true, force: true });\n };\n const removeStagingOnSignal = (signal: NodeJS.Signals) => {\n removeStaging();\n process.kill(process.pid, signal);\n };\n process.once(\"exit\", removeStaging);\n process.once(\"SIGINT\", removeStagingOnSignal);\n process.once(\"SIGTERM\", removeStagingOnSignal);\n\n // giget logs to console.debug whenever DEBUG is set, which the `debug`\n // package does at module load for an unrelated namespace.\n const origDebug = process.env.DEBUG;\n delete process.env.DEBUG;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let downloadSettled = false;\n let download: Promise<unknown> | undefined;\n try {\n const authToken = resolveGitHubAuthToken();\n download = downloadTemplate(SKILLS_PLUGIN_SOURCE, {\n dir: staging,\n preferOffline: true,\n silent: true,\n ...(authToken ? { auth: authToken } : {}),\n }).finally(() => {\n downloadSettled = true;\n });\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(\"Download timed out.\")),\n DOWNLOAD_TIMEOUT_MS,\n );\n });\n await Promise.race([download, timeout]).catch((error: unknown) => {\n throw new Error(\n \"Could not fetch the assistant-ui skills from GitHub. Check your network, or set GITHUB_TOKEN if you are rate limited.\",\n { cause: error },\n );\n });\n await publish(staging, dir, marker);\n } finally {\n clearTimeout(timer);\n if (origDebug !== undefined) process.env.DEBUG = origDebug;\n if (!download || downloadSettled) removeStaging();\n else void download.then(removeStaging, removeStaging);\n }\n\n return dir;\n}\n\n// A rename fails when the target directory exists and is not empty. A\n// concurrent invocation that published the same commit meanwhile wins and\n// the staged copy is dropped; a directory without the marker is a stale\n// partial cache and is replaced.\nasync function publish(\n staging: string,\n dir: string,\n marker: string,\n retry = true,\n): Promise<void> {\n try {\n await fs.promises.rename(staging, dir);\n } catch (error) {\n if (fs.existsSync(marker)) return;\n if (!retry) throw error;\n await fs.promises.rm(dir, { recursive: true, force: true });\n await publish(staging, dir, marker, false);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agent-skill.js","names":[],"sources":["../../src/lib/agent-skill.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { downloadTemplate } from \"giget\";\nimport {\n DOWNLOAD_TIMEOUT_MS,\n dlxCommand,\n resolveGitHubAuthToken,\n} from \"./create-project\";\nimport { type PackageManagerName } from \"./utils/package-manager\";\n\nexport const SKILLS_PACKAGE = \"assistant-ui/skills\";\nexport const SKILLS_COMMIT = \"139674dc888ee076982b6726e8e6f5d0fe0b5f67\";\nexport const SKILLS_PLUGIN_SOURCE = `gh:${SKILLS_PACKAGE}/assistant-ui#${SKILLS_COMMIT}`;\n\nexport function resolveSkillsInstall(params: {\n skills?: boolean;\n stdinIsTTY?: boolean;\n}): boolean | undefined {\n const { skills, stdinIsTTY = process.stdin.isTTY } = params;\n if (skills !== undefined) return skills;\n if (!stdinIsTTY) return true;\n return undefined;\n}\n\nexport function buildSkillsAddCommand(\n pm: PackageManagerName,\n params: { stdinIsTTY?: boolean } = {},\n): [string, string[]] {\n const { stdinIsTTY = process.stdin.isTTY } = params;\n const [command, dlxArgs] = dlxCommand(pm);\n const args = [...dlxArgs, \"skills\", \"add\", SKILLS_PACKAGE];\n\n // Without a TTY the skills CLI cannot prompt for agent platforms, so skip its\n // confirmation; with a TTY it owns the platform selection interactively.\n if (!stdinIsTTY) args.push(\"--yes\");\n\n return [command, args];\n}\n\nexport function skillsPluginDir(): string {\n const cacheHome =\n process.env.XDG_CACHE_HOME || path.join(os.homedir(), \".cache\");\n return path.join(cacheHome, \"assistant-ui\", \"skills\", SKILLS_COMMIT);\n}\n\nexport async function ensureSkillsPlugin(): Promise<string> {\n const dir = skillsPluginDir();\n const marker = path.join(dir, \".claude-plugin\", \"plugin.json\");\n if (fs.existsSync(marker)) return dir;\n\n const parent = path.dirname(dir);\n await fs.promises.mkdir(parent, { recursive: true });\n const staging = await fs.promises.mkdtemp(path.join(parent, \".staging-\"));\n // Node emits no \"exit\" when a signal kills the process, so the staging\n // directory is removed on the signal and the signal re-raised. The\n // listeners stay armed until the removal runs, which a download that\n // outlives the timeout defers past the caller's own exit.\n const removeStaging = () => {\n process.removeListener(\"exit\", removeStaging);\n process.removeListener(\"SIGINT\", removeStagingOnSignal);\n process.removeListener(\"SIGTERM\", removeStagingOnSignal);\n fs.rmSync(staging, { recursive: true, force: true });\n };\n const removeStagingOnSignal = (signal: NodeJS.Signals) => {\n removeStaging();\n process.kill(process.pid, signal);\n };\n process.once(\"exit\", removeStaging);\n process.once(\"SIGINT\", removeStagingOnSignal);\n process.once(\"SIGTERM\", removeStagingOnSignal);\n\n // giget logs to console.debug whenever DEBUG is set, which the `debug`\n // package does at module load for an unrelated namespace.\n const origDebug = process.env.DEBUG;\n delete process.env.DEBUG;\n let timer: ReturnType<typeof setTimeout> | undefined;\n let downloadSettled = false;\n let download: Promise<unknown> | undefined;\n try {\n const authToken = resolveGitHubAuthToken();\n download = downloadTemplate(SKILLS_PLUGIN_SOURCE, {\n dir: staging,\n preferOffline: true,\n silent: true,\n ...(authToken ? { auth: authToken } : {}),\n }).finally(() => {\n downloadSettled = true;\n });\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(\"Download timed out.\")),\n DOWNLOAD_TIMEOUT_MS,\n );\n });\n await Promise.race([download, timeout]).catch((error: unknown) => {\n throw new Error(\n \"Could not fetch the assistant-ui skills from GitHub. Check your network, or set GITHUB_TOKEN if you are rate limited.\",\n { cause: error },\n );\n });\n await publish(staging, dir, marker);\n } finally {\n clearTimeout(timer);\n if (origDebug !== undefined) process.env.DEBUG = origDebug;\n if (!download || downloadSettled) removeStaging();\n else void download.then(removeStaging, removeStaging);\n }\n\n return dir;\n}\n\n// A rename fails when the target directory exists and is not empty. A\n// concurrent invocation that published the same commit meanwhile wins and\n// the staged copy is dropped; a directory without the marker is a stale\n// partial cache and is replaced.\nasync function publish(\n staging: string,\n dir: string,\n marker: string,\n retry = true,\n): Promise<void> {\n try {\n await fs.promises.rename(staging, dir);\n } catch (error) {\n if (fs.existsSync(marker)) return;\n if (!retry) throw error;\n await fs.promises.rm(dir, { recursive: true, force: true });\n await publish(staging, dir, marker, false);\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const abortable: <T>(promise: Promise<T>, signal: AbortSignal) => Promise<T>;
2
+ //# sourceMappingURL=cloud-abort.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-abort.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-abort.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,SAAS,GAAI,CAAC,EACzB,SAAS,OAAO,CAAC,CAAC,CAAC,EACnB,QAAQ,WAAW,KAClB,OAAO,CAAC,CAAC,CAQR,CAAC"}
@@ -0,0 +1,13 @@
1
+ //#region src/lib/cloud-abort.ts
2
+ const abortable = (promise, signal) => new Promise((resolve, reject) => {
3
+ const abort = () => reject(signal.reason);
4
+ signal.addEventListener("abort", abort, { once: true });
5
+ if (signal.aborted) abort();
6
+ promise.then(resolve, reject).finally(() => {
7
+ signal.removeEventListener("abort", abort);
8
+ });
9
+ });
10
+ //#endregion
11
+ export { abortable };
12
+
13
+ //# sourceMappingURL=cloud-abort.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-abort.js","names":[],"sources":["../../src/lib/cloud-abort.ts"],"sourcesContent":["export const abortable = <T>(\n promise: Promise<T>,\n signal: AbortSignal,\n): Promise<T> =>\n new Promise((resolve, reject) => {\n const abort = () => reject(signal.reason);\n signal.addEventListener(\"abort\", abort, { once: true });\n if (signal.aborted) abort();\n promise.then(resolve, reject).finally(() => {\n signal.removeEventListener(\"abort\", abort);\n });\n });\n"],"mappings":";AAAA,MAAa,aACX,SACA,WAEA,IAAI,SAAS,SAAS,WAAW;CAC/B,MAAM,cAAc,OAAO,OAAO,MAAM;CACxC,OAAO,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;CACtD,IAAI,OAAO,SAAS,MAAM;CAC1B,QAAQ,KAAK,SAAS,MAAM,CAAC,CAAC,cAAc;EAC1C,OAAO,oBAAoB,SAAS,KAAK;CAC3C,CAAC;AACH,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { type DeviceCredentials } from "aui-auth/device";
2
+ export { validateCloudUrl } from "./cloud-url.js";
3
+ export type CloudAuthConfig = {
4
+ issuer: string;
5
+ clientId: string;
6
+ credentialsFile: string;
7
+ };
8
+ type SavedCredentials = DeviceCredentials & {
9
+ issuer: string;
10
+ clientId: string;
11
+ };
12
+ export declare const cloudAuthConfig: () => CloudAuthConfig;
13
+ export declare const readCloudCredentials: (config: CloudAuthConfig, options?: {
14
+ matchConfig?: boolean;
15
+ }) => Promise<SavedCredentials | null>;
16
+ export declare const saveCloudCredentials: (config: CloudAuthConfig, credentials: DeviceCredentials, signal?: AbortSignal) => Promise<void>;
17
+ export declare const loginToCloud: (config: CloudAuthConfig, options?: {
18
+ noOpen?: boolean;
19
+ setupUrl?: string;
20
+ print?: (text: string) => void;
21
+ }) => Promise<DeviceCredentials>;
22
+ export declare const cloudAccessToken: (config: CloudAuthConfig, options?: {
23
+ noOpen?: boolean;
24
+ setupUrl?: string;
25
+ print?: (text: string) => void;
26
+ }) => Promise<string>;
27
+ export declare const logoutFromCloud: (config: CloudAuthConfig) => Promise<void>;
28
+ //# sourceMappingURL=cloud-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-auth.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-auth.ts"],"names":[],"mappings":"AAcA,OAAO,EAGL,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAAE,gBAAgB,EAAE,uBAAoB;AAE/C,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,KAAK,gBAAgB,GAAG,iBAAiB,GAAG;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,eAAO,MAAM,eAAe,QAAO,eAWjC,CAAC;AAEH,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,eAAe,EACvB,UAAS;IAAE,WAAW,CAAC,EAAE,OAAO,CAAA;CAAO,KACtC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CA8BjC,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,QAAQ,eAAe,EACvB,aAAa,iBAAiB,EAC9B,SAAS,WAAW,KACnB,OAAO,CAAC,IAAI,CA8Cd,CAAC;AAgBF,eAAO,MAAM,YAAY,GACvB,QAAQ,eAAe,EACvB,UAAS;IACP,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3B,KACL,OAAO,CAAC,iBAAiB,CA0E3B,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,QAAQ,eAAe,EACvB,UAAS;IACP,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3B,KACL,OAAO,CAAC,MAAM,CAoBhB,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,QAAQ,eAAe,KACtB,OAAO,CAAC,IAAI,CASd,CAAC"}
@@ -0,0 +1,169 @@
1
+ import { abortable } from "./cloud-abort.js";
2
+ import { validateCloudUrl } from "./cloud-url.js";
3
+ import { renameSync, statSync, unlinkSync } from "node:fs";
4
+ import path from "node:path";
5
+ import os from "node:os";
6
+ import { chmod, link, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
7
+ import { spawn } from "node:child_process";
8
+ import { randomUUID } from "node:crypto";
9
+ import { createDeviceLogin, isAccessDenied } from "aui-auth/device";
10
+ //#region src/lib/cloud-auth.ts
11
+ const cloudAuthConfig = () => ({
12
+ issuer: validateCloudUrl(process.env.ASSISTANT_UI_ACCOUNTS_URL ?? "https://accounts.assistant-ui.com"),
13
+ clientId: process.env.ASSISTANT_UI_OIDC_CLIENT_ID ?? "assistant-ui-cli",
14
+ credentialsFile: path.join(process.env.ASSISTANT_UI_CONFIG_DIR ?? path.join(os.homedir(), ".assistant-ui"), "credentials.json")
15
+ });
16
+ const readCloudCredentials = async (config, options = {}) => {
17
+ let contents;
18
+ try {
19
+ contents = await readFile(config.credentialsFile, "utf8");
20
+ } catch (error) {
21
+ if (error.code === "ENOENT") return null;
22
+ throw error;
23
+ }
24
+ let saved;
25
+ try {
26
+ saved = JSON.parse(contents);
27
+ } catch {
28
+ throw new Error("Invalid saved login. Run assistant-ui cloud login again.");
29
+ }
30
+ if (!saved || typeof saved !== "object") throw new Error("Invalid saved login. Run assistant-ui cloud login again.");
31
+ if (options.matchConfig !== false && (saved.issuer !== config.issuer || saved.clientId !== config.clientId)) return null;
32
+ if (!saved.tokens?.accessToken || !saved.user?.id || typeof saved.tokens.accessTokenExpiresAt !== "number") throw new Error("Invalid saved login. Run assistant-ui cloud login again.");
33
+ return saved;
34
+ };
35
+ const saveCloudCredentials = async (config, credentials, signal) => {
36
+ await mkdir(path.dirname(config.credentialsFile), {
37
+ recursive: true,
38
+ mode: 448
39
+ });
40
+ const temporary = `${config.credentialsFile}.${randomUUID()}.tmp`;
41
+ const backup = `${temporary}.previous`;
42
+ let previous = false;
43
+ let replacement;
44
+ let renamed = false;
45
+ try {
46
+ try {
47
+ await link(config.credentialsFile, backup);
48
+ previous = true;
49
+ } catch (error) {
50
+ if (error.code !== "ENOENT") throw error;
51
+ }
52
+ await writeFile(temporary, `${JSON.stringify({
53
+ ...credentials,
54
+ issuer: config.issuer,
55
+ clientId: config.clientId
56
+ }, null, 2)}\n`, {
57
+ mode: 384,
58
+ flag: "wx"
59
+ });
60
+ await chmod(temporary, 384);
61
+ replacement = statSync(temporary);
62
+ signal?.throwIfAborted();
63
+ await rename(temporary, config.credentialsFile);
64
+ renamed = true;
65
+ signal?.throwIfAborted();
66
+ } catch (error) {
67
+ if (renamed && replacement) {
68
+ let current;
69
+ try {
70
+ current = statSync(config.credentialsFile);
71
+ } catch (failure) {
72
+ if (failure.code !== "ENOENT") throw failure;
73
+ }
74
+ if (current?.dev === replacement.dev && current.ino === replacement.ino) {
75
+ if (previous) renameSync(backup, config.credentialsFile);
76
+ else unlinkSync(config.credentialsFile);
77
+ }
78
+ }
79
+ throw error;
80
+ } finally {
81
+ await rm(temporary, { force: true });
82
+ await rm(backup, { force: true });
83
+ }
84
+ };
85
+ const openBrowser = (url) => {
86
+ const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32" : "xdg-open";
87
+ const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url] : [url];
88
+ const child = spawn(command, args, {
89
+ detached: true,
90
+ stdio: "ignore"
91
+ });
92
+ child.on("error", () => {});
93
+ child.unref();
94
+ };
95
+ const loginToCloud = async (config, options = {}) => {
96
+ if (options.setupUrl && !["assistant-ui-cli", "assistant-ui-cli-dev"].includes(config.clientId)) throw new Error("Wizard sign-in requires the assistant-ui CLI OAuth client.");
97
+ const print = options.print ?? console.log;
98
+ const setup = options.setupUrl ? await (await import("./cloud-setup-login.js")).connectCloudLoginSetup(options.setupUrl) : void 0;
99
+ const signal = AbortSignal.any([AbortSignal.timeout(6e5), ...setup ? [setup.signal] : []]);
100
+ const login = createDeviceLogin({
101
+ issuer: config.issuer,
102
+ clientId: config.clientId,
103
+ fetch: (input, init) => {
104
+ const url = new URL(input instanceof Request ? input.url : String(input));
105
+ return fetch(input, url.pathname === "/api/auth/device/code" ? {
106
+ ...init,
107
+ signal: AbortSignal.any([signal, ...init?.signal ? [init.signal] : []])
108
+ } : init);
109
+ }
110
+ });
111
+ let credentials;
112
+ let committed = false;
113
+ try {
114
+ signal.throwIfAborted();
115
+ const authorization = await abortable(login.start(), signal);
116
+ signal.throwIfAborted();
117
+ const approvalUrl = await setup?.publish(authorization, config.issuer) ?? authorization.verificationUriComplete;
118
+ print(`Confirm code ${authorization.userCode} at ${approvalUrl}`);
119
+ if (!options.noOpen && !setup) openBrowser(authorization.verificationUriComplete);
120
+ credentials = await login.wait(authorization, { signal });
121
+ signal.throwIfAborted();
122
+ await saveCloudCredentials(config, credentials, signal);
123
+ committed = true;
124
+ await setup?.complete("signed-in").catch(() => {
125
+ print("Signed in locally. The setup connection could not be updated.");
126
+ });
127
+ print(`Signed in as ${credentials.user.email || credentials.user.id}.`);
128
+ return credentials;
129
+ } catch (error) {
130
+ if (credentials && !committed) await login.revoke(credentials).catch(() => {
131
+ print("The cancelled login could not be revoked. End it from your Accounts Sessions page.");
132
+ });
133
+ await setup?.complete(signal.aborted ? "cancelled" : "failed").catch(() => {});
134
+ throw error;
135
+ } finally {
136
+ await setup?.dispose();
137
+ }
138
+ };
139
+ const cloudAccessToken = async (config, options = {}) => {
140
+ const saved = await readCloudCredentials(config);
141
+ if (!saved) return (await loginToCloud(config, options)).tokens.accessToken;
142
+ const login = createDeviceLogin({
143
+ issuer: config.issuer,
144
+ clientId: config.clientId
145
+ });
146
+ try {
147
+ const credentials = await login.ensureFresh(saved);
148
+ if (credentials !== saved) await saveCloudCredentials(config, credentials);
149
+ return credentials.tokens.accessToken;
150
+ } catch (error) {
151
+ if (isAccessDenied(error)) {
152
+ await rm(config.credentialsFile, { force: true });
153
+ throw new Error("Your login expired. Run assistant-ui cloud login again.");
154
+ }
155
+ throw error;
156
+ }
157
+ };
158
+ const logoutFromCloud = async (config) => {
159
+ const saved = await readCloudCredentials(config, { matchConfig: false });
160
+ if (saved) await createDeviceLogin({
161
+ issuer: validateCloudUrl(saved.issuer),
162
+ clientId: saved.clientId
163
+ }).revoke(saved);
164
+ await rm(config.credentialsFile, { force: true });
165
+ };
166
+ //#endregion
167
+ export { cloudAccessToken, cloudAuthConfig, loginToCloud, logoutFromCloud, readCloudCredentials, saveCloudCredentials, validateCloudUrl };
168
+
169
+ //# sourceMappingURL=cloud-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-auth.js","names":[],"sources":["../../src/lib/cloud-auth.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { renameSync, statSync, unlinkSync } from \"node:fs\";\nimport {\n chmod,\n link,\n mkdir,\n readFile,\n rename,\n rm,\n writeFile,\n} from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport {\n createDeviceLogin,\n isAccessDenied,\n type DeviceCredentials,\n} from \"aui-auth/device\";\nimport { abortable } from \"./cloud-abort\";\nimport { validateCloudUrl } from \"./cloud-url\";\n\nexport { validateCloudUrl } from \"./cloud-url\";\n\nexport type CloudAuthConfig = {\n issuer: string;\n clientId: string;\n credentialsFile: string;\n};\n\ntype SavedCredentials = DeviceCredentials & {\n issuer: string;\n clientId: string;\n};\n\nexport const cloudAuthConfig = (): CloudAuthConfig => ({\n issuer: validateCloudUrl(\n process.env.ASSISTANT_UI_ACCOUNTS_URL ??\n \"https://accounts.assistant-ui.com\",\n ),\n clientId: process.env.ASSISTANT_UI_OIDC_CLIENT_ID ?? \"assistant-ui-cli\",\n credentialsFile: path.join(\n process.env.ASSISTANT_UI_CONFIG_DIR ??\n path.join(os.homedir(), \".assistant-ui\"),\n \"credentials.json\",\n ),\n});\n\nexport const readCloudCredentials = async (\n config: CloudAuthConfig,\n options: { matchConfig?: boolean } = {},\n): Promise<SavedCredentials | null> => {\n let contents: string;\n try {\n contents = await readFile(config.credentialsFile, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n }\n let saved: SavedCredentials;\n try {\n saved = JSON.parse(contents) as SavedCredentials;\n } catch {\n throw new Error(\"Invalid saved login. Run assistant-ui cloud login again.\");\n }\n if (!saved || typeof saved !== \"object\")\n throw new Error(\"Invalid saved login. Run assistant-ui cloud login again.\");\n if (\n options.matchConfig !== false &&\n (saved.issuer !== config.issuer || saved.clientId !== config.clientId)\n ) {\n return null;\n }\n if (\n !saved.tokens?.accessToken ||\n !saved.user?.id ||\n typeof saved.tokens.accessTokenExpiresAt !== \"number\"\n ) {\n throw new Error(\"Invalid saved login. Run assistant-ui cloud login again.\");\n }\n return saved;\n};\n\nexport const saveCloudCredentials = async (\n config: CloudAuthConfig,\n credentials: DeviceCredentials,\n signal?: AbortSignal,\n): Promise<void> => {\n await mkdir(path.dirname(config.credentialsFile), {\n recursive: true,\n mode: 0o700,\n });\n const temporary = `${config.credentialsFile}.${randomUUID()}.tmp`;\n const backup = `${temporary}.previous`;\n let previous = false;\n let replacement: ReturnType<typeof statSync> | undefined;\n let renamed = false;\n try {\n try {\n await link(config.credentialsFile, backup);\n previous = true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n }\n await writeFile(\n temporary,\n `${JSON.stringify({ ...credentials, issuer: config.issuer, clientId: config.clientId }, null, 2)}\\n`,\n { mode: 0o600, flag: \"wx\" },\n );\n await chmod(temporary, 0o600);\n replacement = statSync(temporary);\n signal?.throwIfAborted();\n await rename(temporary, config.credentialsFile);\n renamed = true;\n signal?.throwIfAborted();\n } catch (error) {\n if (renamed && replacement) {\n let current: ReturnType<typeof statSync> | undefined;\n try {\n current = statSync(config.credentialsFile);\n } catch (failure) {\n if ((failure as NodeJS.ErrnoException).code !== \"ENOENT\") throw failure;\n }\n if (current?.dev === replacement.dev && current.ino === replacement.ino) {\n if (previous) renameSync(backup, config.credentialsFile);\n else unlinkSync(config.credentialsFile);\n }\n }\n throw error;\n } finally {\n await rm(temporary, { force: true });\n await rm(backup, { force: true });\n }\n};\n\nconst openBrowser = (url: string): void => {\n const command =\n process.platform === \"darwin\"\n ? \"open\"\n : process.platform === \"win32\"\n ? \"rundll32\"\n : \"xdg-open\";\n const args =\n process.platform === \"win32\" ? [\"url.dll,FileProtocolHandler\", url] : [url];\n const child = spawn(command, args, { detached: true, stdio: \"ignore\" });\n child.on(\"error\", () => {});\n child.unref();\n};\n\nexport const loginToCloud = async (\n config: CloudAuthConfig,\n options: {\n noOpen?: boolean;\n setupUrl?: string;\n print?: (text: string) => void;\n } = {},\n): Promise<DeviceCredentials> => {\n if (\n options.setupUrl &&\n ![\"assistant-ui-cli\", \"assistant-ui-cli-dev\"].includes(config.clientId)\n ) {\n throw new Error(\n \"Wizard sign-in requires the assistant-ui CLI OAuth client.\",\n );\n }\n const print = options.print ?? console.log;\n const setup = options.setupUrl\n ? await (\n await import(\"./cloud-setup-login\")\n ).connectCloudLoginSetup(options.setupUrl)\n : undefined;\n const signal = AbortSignal.any([\n AbortSignal.timeout(10 * 60 * 1000),\n ...(setup ? [setup.signal] : []),\n ]);\n const login = createDeviceLogin({\n issuer: config.issuer,\n clientId: config.clientId,\n fetch: (input, init) => {\n const url = new URL(input instanceof Request ? input.url : String(input));\n return fetch(\n input,\n url.pathname === \"/api/auth/device/code\"\n ? {\n ...init,\n signal: AbortSignal.any([\n signal,\n ...(init?.signal ? [init.signal] : []),\n ]),\n }\n : init,\n );\n },\n });\n let credentials: DeviceCredentials | undefined;\n let committed = false;\n try {\n signal.throwIfAborted();\n const authorization = await abortable(login.start(), signal);\n signal.throwIfAborted();\n const approvalUrl =\n (await setup?.publish(authorization, config.issuer)) ??\n authorization.verificationUriComplete;\n print(`Confirm code ${authorization.userCode} at ${approvalUrl}`);\n if (!options.noOpen && !setup)\n openBrowser(authorization.verificationUriComplete);\n credentials = await login.wait(authorization, { signal });\n signal.throwIfAborted();\n await saveCloudCredentials(config, credentials, signal);\n committed = true;\n await setup?.complete(\"signed-in\").catch(() => {\n print(\"Signed in locally. The setup connection could not be updated.\");\n });\n print(`Signed in as ${credentials.user.email || credentials.user.id}.`);\n return credentials;\n } catch (error) {\n if (credentials && !committed) {\n await login.revoke(credentials).catch(() => {\n print(\n \"The cancelled login could not be revoked. End it from your Accounts Sessions page.\",\n );\n });\n }\n await setup\n ?.complete(signal.aborted ? \"cancelled\" : \"failed\")\n .catch(() => {});\n throw error;\n } finally {\n await setup?.dispose();\n }\n};\n\nexport const cloudAccessToken = async (\n config: CloudAuthConfig,\n options: {\n noOpen?: boolean;\n setupUrl?: string;\n print?: (text: string) => void;\n } = {},\n): Promise<string> => {\n const saved = await readCloudCredentials(config);\n if (!saved) return (await loginToCloud(config, options)).tokens.accessToken;\n const login = createDeviceLogin({\n issuer: config.issuer,\n clientId: config.clientId,\n });\n try {\n const credentials = await login.ensureFresh(saved);\n if (credentials !== saved) await saveCloudCredentials(config, credentials);\n return credentials.tokens.accessToken;\n } catch (error) {\n if (isAccessDenied(error)) {\n await rm(config.credentialsFile, { force: true });\n throw new Error(\n \"Your login expired. Run assistant-ui cloud login again.\",\n );\n }\n throw error;\n }\n};\n\nexport const logoutFromCloud = async (\n config: CloudAuthConfig,\n): Promise<void> => {\n const saved = await readCloudCredentials(config, { matchConfig: false });\n if (saved) {\n await createDeviceLogin({\n issuer: validateCloudUrl(saved.issuer),\n clientId: saved.clientId,\n }).revoke(saved);\n }\n await rm(config.credentialsFile, { force: true });\n};\n"],"mappings":";;;;;;;;;;AAmCA,MAAa,yBAA0C;CACrD,QAAQ,iBACN,QAAQ,IAAI,6BACV,mCACJ;CACA,UAAU,QAAQ,IAAI,+BAA+B;CACrD,iBAAiB,KAAK,KACpB,QAAQ,IAAI,2BACV,KAAK,KAAK,GAAG,QAAQ,GAAG,eAAe,GACzC,kBACF;AACF;AAEA,MAAa,uBAAuB,OAClC,QACA,UAAqC,CAAC,MACD;CACrC,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,SAAS,OAAO,iBAAiB,MAAM;CAC1D,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU,OAAO;EAC/D,MAAM;CACR;CACA,IAAI;CACJ,IAAI;EACF,QAAQ,KAAK,MAAM,QAAQ;CAC7B,QAAQ;EACN,MAAM,IAAI,MAAM,0DAA0D;CAC5E;CACA,IAAI,CAAC,SAAS,OAAO,UAAU,UAC7B,MAAM,IAAI,MAAM,0DAA0D;CAC5E,IACE,QAAQ,gBAAgB,UACvB,MAAM,WAAW,OAAO,UAAU,MAAM,aAAa,OAAO,WAE7D,OAAO;CAET,IACE,CAAC,MAAM,QAAQ,eACf,CAAC,MAAM,MAAM,MACb,OAAO,MAAM,OAAO,yBAAyB,UAE7C,MAAM,IAAI,MAAM,0DAA0D;CAE5E,OAAO;AACT;AAEA,MAAa,uBAAuB,OAClC,QACA,aACA,WACkB;CAClB,MAAM,MAAM,KAAK,QAAQ,OAAO,eAAe,GAAG;EAChD,WAAW;EACX,MAAM;CACR,CAAC;CACD,MAAM,YAAY,GAAG,OAAO,gBAAgB,GAAG,WAAW,EAAE;CAC5D,MAAM,SAAS,GAAG,UAAU;CAC5B,IAAI,WAAW;CACf,IAAI;CACJ,IAAI,UAAU;CACd,IAAI;EACF,IAAI;GACF,MAAM,KAAK,OAAO,iBAAiB,MAAM;GACzC,WAAW;EACb,SAAS,OAAO;GACd,IAAK,MAAgC,SAAS,UAAU,MAAM;EAChE;EACA,MAAM,UACJ,WACA,GAAG,KAAK,UAAU;GAAE,GAAG;GAAa,QAAQ,OAAO;GAAQ,UAAU,OAAO;EAAS,GAAG,MAAM,CAAC,EAAE,KACjG;GAAE,MAAM;GAAO,MAAM;EAAK,CAC5B;EACA,MAAM,MAAM,WAAW,GAAK;EAC5B,cAAc,SAAS,SAAS;EAChC,QAAQ,eAAe;EACvB,MAAM,OAAO,WAAW,OAAO,eAAe;EAC9C,UAAU;EACV,QAAQ,eAAe;CACzB,SAAS,OAAO;EACd,IAAI,WAAW,aAAa;GAC1B,IAAI;GACJ,IAAI;IACF,UAAU,SAAS,OAAO,eAAe;GAC3C,SAAS,SAAS;IAChB,IAAK,QAAkC,SAAS,UAAU,MAAM;GAClE;GACA,IAAI,SAAS,QAAQ,YAAY,OAAO,QAAQ,QAAQ,YAAY,KAAK;IACvE,IAAI,UAAU,WAAW,QAAQ,OAAO,eAAe;SAClD,WAAW,OAAO,eAAe;GACxC;EACF;EACA,MAAM;CACR,UAAU;EACR,MAAM,GAAG,WAAW,EAAE,OAAO,KAAK,CAAC;EACnC,MAAM,GAAG,QAAQ,EAAE,OAAO,KAAK,CAAC;CAClC;AACF;AAEA,MAAM,eAAe,QAAsB;CACzC,MAAM,UACJ,QAAQ,aAAa,WACjB,SACA,QAAQ,aAAa,UACnB,aACA;CACR,MAAM,OACJ,QAAQ,aAAa,UAAU,CAAC,+BAA+B,GAAG,IAAI,CAAC,GAAG;CAC5E,MAAM,QAAQ,MAAM,SAAS,MAAM;EAAE,UAAU;EAAM,OAAO;CAAS,CAAC;CACtE,MAAM,GAAG,eAAe,CAAC,CAAC;CAC1B,MAAM,MAAM;AACd;AAEA,MAAa,eAAe,OAC1B,QACA,UAII,CAAC,MAC0B;CAC/B,IACE,QAAQ,YACR,CAAC,CAAC,oBAAoB,sBAAsB,CAAC,CAAC,SAAS,OAAO,QAAQ,GAEtE,MAAM,IAAI,MACR,4DACF;CAEF,MAAM,QAAQ,QAAQ,SAAS,QAAQ;CACvC,MAAM,QAAQ,QAAQ,WAClB,OACE,MAAM,OAAO,0BAAA,CACb,uBAAuB,QAAQ,QAAQ,IACzC,KAAA;CACJ,MAAM,SAAS,YAAY,IAAI,CAC7B,YAAY,QAAQ,GAAc,GAClC,GAAI,QAAQ,CAAC,MAAM,MAAM,IAAI,CAAC,CAChC,CAAC;CACD,MAAM,QAAQ,kBAAkB;EAC9B,QAAQ,OAAO;EACf,UAAU,OAAO;EACjB,QAAQ,OAAO,SAAS;GACtB,MAAM,MAAM,IAAI,IAAI,iBAAiB,UAAU,MAAM,MAAM,OAAO,KAAK,CAAC;GACxE,OAAO,MACL,OACA,IAAI,aAAa,0BACb;IACE,GAAG;IACH,QAAQ,YAAY,IAAI,CACtB,QACA,GAAI,MAAM,SAAS,CAAC,KAAK,MAAM,IAAI,CAAC,CACtC,CAAC;GACH,IACA,IACN;EACF;CACF,CAAC;CACD,IAAI;CACJ,IAAI,YAAY;CAChB,IAAI;EACF,OAAO,eAAe;EACtB,MAAM,gBAAgB,MAAM,UAAU,MAAM,MAAM,GAAG,MAAM;EAC3D,OAAO,eAAe;EACtB,MAAM,cACH,MAAM,OAAO,QAAQ,eAAe,OAAO,MAAM,KAClD,cAAc;EAChB,MAAM,gBAAgB,cAAc,SAAS,MAAM,aAAa;EAChE,IAAI,CAAC,QAAQ,UAAU,CAAC,OACtB,YAAY,cAAc,uBAAuB;EACnD,cAAc,MAAM,MAAM,KAAK,eAAe,EAAE,OAAO,CAAC;EACxD,OAAO,eAAe;EACtB,MAAM,qBAAqB,QAAQ,aAAa,MAAM;EACtD,YAAY;EACZ,MAAM,OAAO,SAAS,WAAW,CAAC,CAAC,YAAY;GAC7C,MAAM,+DAA+D;EACvE,CAAC;EACD,MAAM,gBAAgB,YAAY,KAAK,SAAS,YAAY,KAAK,GAAG,EAAE;EACtE,OAAO;CACT,SAAS,OAAO;EACd,IAAI,eAAe,CAAC,WAClB,MAAM,MAAM,OAAO,WAAW,CAAC,CAAC,YAAY;GAC1C,MACE,oFACF;EACF,CAAC;EAEH,MAAM,OACF,SAAS,OAAO,UAAU,cAAc,QAAQ,CAAC,CAClD,YAAY,CAAC,CAAC;EACjB,MAAM;CACR,UAAU;EACR,MAAM,OAAO,QAAQ;CACvB;AACF;AAEA,MAAa,mBAAmB,OAC9B,QACA,UAII,CAAC,MACe;CACpB,MAAM,QAAQ,MAAM,qBAAqB,MAAM;CAC/C,IAAI,CAAC,OAAO,QAAQ,MAAM,aAAa,QAAQ,OAAO,EAAA,CAAG,OAAO;CAChE,MAAM,QAAQ,kBAAkB;EAC9B,QAAQ,OAAO;EACf,UAAU,OAAO;CACnB,CAAC;CACD,IAAI;EACF,MAAM,cAAc,MAAM,MAAM,YAAY,KAAK;EACjD,IAAI,gBAAgB,OAAO,MAAM,qBAAqB,QAAQ,WAAW;EACzE,OAAO,YAAY,OAAO;CAC5B,SAAS,OAAO;EACd,IAAI,eAAe,KAAK,GAAG;GACzB,MAAM,GAAG,OAAO,iBAAiB,EAAE,OAAO,KAAK,CAAC;GAChD,MAAM,IAAI,MACR,yDACF;EACF;EACA,MAAM;CACR;AACF;AAEA,MAAa,kBAAkB,OAC7B,WACkB;CAClB,MAAM,QAAQ,MAAM,qBAAqB,QAAQ,EAAE,aAAa,MAAM,CAAC;CACvE,IAAI,OACF,MAAM,kBAAkB;EACtB,QAAQ,iBAAiB,MAAM,MAAM;EACrC,UAAU,MAAM;CAClB,CAAC,CAAC,CAAC,OAAO,KAAK;CAEjB,MAAM,GAAG,OAAO,iBAAiB,EAAE,OAAO,KAAK,CAAC;AAClD"}
@@ -0,0 +1,21 @@
1
+ export type CloudProjectConfig = {
2
+ organizationId: string;
3
+ organizationSlug: string;
4
+ projectId: string;
5
+ projectSlug: string;
6
+ harnessId: string;
7
+ harnessOrigin: string;
8
+ apiOrigin: string;
9
+ workspaceId: string;
10
+ backendUrl: string;
11
+ };
12
+ export declare const writeCloudFile: (file: string, contents: string, options?: {
13
+ exclusive?: boolean;
14
+ mode?: number;
15
+ }) => Promise<void>;
16
+ export declare const cloudProjectConfigPath: (directory: string) => string;
17
+ export declare const readCloudProjectConfig: (directory: string) => Promise<CloudProjectConfig | null>;
18
+ export declare const readOptionalFile: (file: string) => Promise<string>;
19
+ export declare const mergeCloudEnvironment: (contents: string, values: Record<string, string>) => string;
20
+ export declare const writeCloudProjectConfig: (directory: string, config: CloudProjectConfig, apiKey: string) => Promise<void>;
21
+ //# sourceMappingURL=cloud-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-config.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-config.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,MAAM,MAAM,EACZ,UAAU,MAAM,EAChB,UAAS;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,KACnD,OAAO,CAAC,IAAI,CAad,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAI,WAAW,MAAM,KAAG,MACN,CAAC;AAEtD,eAAO,MAAM,sBAAsB,GACjC,WAAW,MAAM,KAChB,OAAO,CAAC,kBAAkB,GAAG,IAAI,CASnC,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAU,MAAM,MAAM,KAAG,OAAO,CAAC,MAAM,CAOnE,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,UAAU,MAAM,EAChB,QAAQ,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC7B,MA6BF,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,WAAW,MAAM,EACjB,QAAQ,kBAAkB,EAC1B,QAAQ,MAAM,KACb,OAAO,CAAC,IAAI,CA+Bd,CAAC"}
@@ -0,0 +1,76 @@
1
+ import path from "node:path";
2
+ import { link, mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
3
+ import { randomUUID } from "node:crypto";
4
+ //#region src/lib/cloud-config.ts
5
+ const writeCloudFile = async (file, contents, options = {}) => {
6
+ await mkdir(path.dirname(file), { recursive: true });
7
+ const temporary = `${file}.${randomUUID()}.tmp`;
8
+ try {
9
+ await writeFile(temporary, contents, {
10
+ flag: "wx",
11
+ mode: options.mode ?? 384
12
+ });
13
+ if (options.exclusive) await link(temporary, file);
14
+ else await rename(temporary, file);
15
+ } finally {
16
+ await rm(temporary, { force: true });
17
+ }
18
+ };
19
+ const cloudProjectConfigPath = (directory) => path.join(directory, ".assistant-ui", "cloud.json");
20
+ const readCloudProjectConfig = async (directory) => {
21
+ try {
22
+ return JSON.parse(await readFile(cloudProjectConfigPath(directory), "utf8"));
23
+ } catch (error) {
24
+ if (error.code === "ENOENT") return null;
25
+ throw error;
26
+ }
27
+ };
28
+ const readOptionalFile = async (file) => {
29
+ try {
30
+ return await readFile(file, "utf8");
31
+ } catch (error) {
32
+ if (error.code === "ENOENT") return "";
33
+ throw error;
34
+ }
35
+ };
36
+ const mergeCloudEnvironment = (contents, values) => {
37
+ const lines = contents.split(/\r?\n/);
38
+ const pending = new Map(Object.entries(values));
39
+ for (const line of lines) {
40
+ const match = line.match(/^\s*(?:export\s+)?([A-Z_][A-Z0-9_]*)\s*=\s*(.*)$/);
41
+ if (!match) continue;
42
+ const key = match[1];
43
+ const expected = values[key];
44
+ if (expected === void 0) continue;
45
+ const existing = match[2].trim();
46
+ const quoted = JSON.stringify(expected);
47
+ if (existing !== expected && existing !== quoted && existing !== `'${expected}'`) throw new Error(`${key} is already configured in .env.local. Keep your existing configuration or use a new directory.`);
48
+ pending.delete(key);
49
+ }
50
+ if (pending.size === 0) return contents;
51
+ const additions = [...pending].map(([key, value]) => `${key}=${JSON.stringify(value)}`);
52
+ return `${contents}${contents && !contents.endsWith("\n") ? "\n" : ""}${additions.join("\n")}\n`;
53
+ };
54
+ const writeCloudProjectConfig = async (directory, config, apiKey) => {
55
+ const envFile = path.join(directory, ".env.local");
56
+ const contents = await readOptionalFile(envFile);
57
+ const environment = mergeCloudEnvironment(contents, {
58
+ ASSISTANT_API_KEY: apiKey,
59
+ NEXT_PUBLIC_ASSISTANT_BASE_URL: config.apiOrigin,
60
+ NEXT_PUBLIC_ASSISTANT_HARNESS_URL: config.harnessOrigin,
61
+ NEXT_PUBLIC_ASSISTANT_WORKSPACE_ID: config.workspaceId
62
+ });
63
+ const gitignoreFile = path.join(directory, ".gitignore");
64
+ const gitignore = await readOptionalFile(gitignoreFile);
65
+ const ignored = gitignore.split(/\r?\n/).map((line) => line.trim());
66
+ if (!ignored.includes(".env.local") && !ignored.includes(".env*.local")) await writeCloudFile(gitignoreFile, `${gitignore}${gitignore && !gitignore.endsWith("\n") ? "\n" : ""}.env.local\n`, { mode: await stat(gitignoreFile).then((info) => info.mode & 511).catch((error) => {
67
+ if (error.code === "ENOENT") return 420;
68
+ throw error;
69
+ }) });
70
+ await writeCloudFile(envFile, environment);
71
+ await writeCloudFile(cloudProjectConfigPath(directory), `${JSON.stringify(config, null, 2)}\n`);
72
+ };
73
+ //#endregion
74
+ export { cloudProjectConfigPath, mergeCloudEnvironment, readCloudProjectConfig, readOptionalFile, writeCloudFile, writeCloudProjectConfig };
75
+
76
+ //# sourceMappingURL=cloud-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-config.js","names":[],"sources":["../../src/lib/cloud-config.ts"],"sourcesContent":["import {\n mkdir,\n readFile,\n writeFile,\n link,\n rename,\n rm,\n stat,\n} from \"node:fs/promises\";\nimport { randomUUID } from \"node:crypto\";\nimport path from \"node:path\";\n\nexport type CloudProjectConfig = {\n organizationId: string;\n organizationSlug: string;\n projectId: string;\n projectSlug: string;\n harnessId: string;\n harnessOrigin: string;\n apiOrigin: string;\n workspaceId: string;\n backendUrl: string;\n};\n\nexport const writeCloudFile = async (\n file: string,\n contents: string,\n options: { exclusive?: boolean; mode?: number } = {},\n): Promise<void> => {\n await mkdir(path.dirname(file), { recursive: true });\n const temporary = `${file}.${randomUUID()}.tmp`;\n try {\n await writeFile(temporary, contents, {\n flag: \"wx\",\n mode: options.mode ?? 0o600,\n });\n if (options.exclusive) await link(temporary, file);\n else await rename(temporary, file);\n } finally {\n await rm(temporary, { force: true });\n }\n};\n\nexport const cloudProjectConfigPath = (directory: string): string =>\n path.join(directory, \".assistant-ui\", \"cloud.json\");\n\nexport const readCloudProjectConfig = async (\n directory: string,\n): Promise<CloudProjectConfig | null> => {\n try {\n return JSON.parse(\n await readFile(cloudProjectConfigPath(directory), \"utf8\"),\n ) as CloudProjectConfig;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return null;\n throw error;\n }\n};\n\nexport const readOptionalFile = async (file: string): Promise<string> => {\n try {\n return await readFile(file, \"utf8\");\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return \"\";\n throw error;\n }\n};\n\nexport const mergeCloudEnvironment = (\n contents: string,\n values: Record<string, string>,\n): string => {\n const lines = contents.split(/\\r?\\n/);\n const pending = new Map(Object.entries(values));\n for (const line of lines) {\n const match = line.match(\n /^\\s*(?:export\\s+)?([A-Z_][A-Z0-9_]*)\\s*=\\s*(.*)$/,\n );\n if (!match) continue;\n const key = match[1]!;\n const expected = values[key];\n if (expected === undefined) continue;\n const existing = match[2]!.trim();\n const quoted = JSON.stringify(expected);\n if (\n existing !== expected &&\n existing !== quoted &&\n existing !== `'${expected}'`\n ) {\n throw new Error(\n `${key} is already configured in .env.local. Keep your existing configuration or use a new directory.`,\n );\n }\n pending.delete(key);\n }\n if (pending.size === 0) return contents;\n const additions = [...pending].map(\n ([key, value]) => `${key}=${JSON.stringify(value)}`,\n );\n return `${contents}${contents && !contents.endsWith(\"\\n\") ? \"\\n\" : \"\"}${additions.join(\"\\n\")}\\n`;\n};\n\nexport const writeCloudProjectConfig = async (\n directory: string,\n config: CloudProjectConfig,\n apiKey: string,\n): Promise<void> => {\n const envFile = path.join(directory, \".env.local\");\n const contents = await readOptionalFile(envFile);\n const environment = mergeCloudEnvironment(contents, {\n ASSISTANT_API_KEY: apiKey,\n NEXT_PUBLIC_ASSISTANT_BASE_URL: config.apiOrigin,\n NEXT_PUBLIC_ASSISTANT_HARNESS_URL: config.harnessOrigin,\n NEXT_PUBLIC_ASSISTANT_WORKSPACE_ID: config.workspaceId,\n });\n const gitignoreFile = path.join(directory, \".gitignore\");\n const gitignore = await readOptionalFile(gitignoreFile);\n const ignored = gitignore.split(/\\r?\\n/).map((line) => line.trim());\n if (!ignored.includes(\".env.local\") && !ignored.includes(\".env*.local\")) {\n await writeCloudFile(\n gitignoreFile,\n `${gitignore}${gitignore && !gitignore.endsWith(\"\\n\") ? \"\\n\" : \"\"}.env.local\\n`,\n {\n mode: await stat(gitignoreFile)\n .then((info) => info.mode & 0o777)\n .catch((error: NodeJS.ErrnoException) => {\n if (error.code === \"ENOENT\") return 0o644;\n throw error;\n }),\n },\n );\n }\n await writeCloudFile(envFile, environment);\n await writeCloudFile(\n cloudProjectConfigPath(directory),\n `${JSON.stringify(config, null, 2)}\\n`,\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export type CloudProvisionRequest = {
2
+ org_id: string;
3
+ project_id: string;
4
+ harness_name: string;
5
+ backend_urls: string[];
6
+ allow_localhost: boolean;
7
+ idempotency_key: string;
8
+ access_code?: string;
9
+ };
10
+ export type CloudProvisionResult = {
11
+ project_id: string;
12
+ workspace_id: string;
13
+ harness_id: string;
14
+ harness_origin: string;
15
+ api_key: string;
16
+ api_origin: string;
17
+ };
18
+ export declare const resolveProvisionRequest: (directory: string, input: Omit<CloudProvisionRequest, "idempotency_key" | "access_code">, accessCode?: string) => Promise<CloudProvisionRequest>;
19
+ export declare const provisionCloudHarness: (managementUrl: string, accessToken: string, request: CloudProvisionRequest) => Promise<CloudProvisionResult>;
20
+ //# sourceMappingURL=cloud-provision.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-provision.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-provision.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,WAAW,MAAM,EACjB,OAAO,IAAI,CAAC,qBAAqB,EAAE,iBAAiB,GAAG,aAAa,CAAC,EACrE,aAAa,MAAM,KAClB,OAAO,CAAC,qBAAqB,CAmB/B,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,eAAe,MAAM,EACrB,aAAa,MAAM,EACnB,SAAS,qBAAqB,KAC7B,OAAO,CAAC,oBAAoB,CAuC9B,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { validateCloudUrl } from "./cloud-url.js";
2
+ import { readOptionalFile, writeCloudFile } from "./cloud-config.js";
3
+ import path from "node:path";
4
+ import { randomUUID } from "node:crypto";
5
+ //#region src/lib/cloud-provision.ts
6
+ const resolveProvisionRequest = async (directory, input, accessCode) => {
7
+ const file = path.join(directory, ".assistant-ui", "provision.json");
8
+ const previous = await readOptionalFile(file);
9
+ let request;
10
+ if (previous) {
11
+ request = JSON.parse(previous);
12
+ const { idempotency_key: _key, ...savedInput } = request;
13
+ if (JSON.stringify(savedInput) !== JSON.stringify(input)) throw new Error("This directory has a pending setup for another project or backend. Resume that setup or use a new directory.");
14
+ } else {
15
+ request = {
16
+ ...input,
17
+ idempotency_key: randomUUID()
18
+ };
19
+ await writeCloudFile(file, `${JSON.stringify(request, null, 2)}\n`, { exclusive: true });
20
+ }
21
+ return {
22
+ ...request,
23
+ ...accessCode ? { access_code: accessCode } : {}
24
+ };
25
+ };
26
+ const provisionCloudHarness = async (managementUrl, accessToken, request) => {
27
+ const response = await fetch(`${validateCloudUrl(managementUrl)}/api/cli/provision`, {
28
+ method: "POST",
29
+ headers: {
30
+ Authorization: `Bearer ${accessToken}`,
31
+ "Content-Type": "application/json"
32
+ },
33
+ body: JSON.stringify(request),
34
+ signal: AbortSignal.timeout(3e4),
35
+ redirect: "error"
36
+ });
37
+ if (!response.ok) {
38
+ const body = await response.json().catch(() => null);
39
+ const detail = body?.message ?? body?.error ?? response.statusText;
40
+ throw new Error(`Cloud setup failed (${response.status}): ${detail}`);
41
+ }
42
+ const result = await response.json();
43
+ if (!result || [
44
+ result.api_key,
45
+ result.harness_id,
46
+ result.workspace_id,
47
+ result.harness_origin,
48
+ result.api_origin
49
+ ].some((value) => typeof value !== "string" || value.length === 0) || result.project_id !== request.project_id) throw new Error("Cloud returned an incomplete setup response. Rerun the same command to resume safely.");
50
+ return result;
51
+ };
52
+ //#endregion
53
+ export { provisionCloudHarness, resolveProvisionRequest };
54
+
55
+ //# sourceMappingURL=cloud-provision.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-provision.js","names":[],"sources":["../../src/lib/cloud-provision.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport path from \"node:path\";\nimport { readOptionalFile, writeCloudFile } from \"./cloud-config\";\nimport { validateCloudUrl } from \"./cloud-auth\";\n\nexport type CloudProvisionRequest = {\n org_id: string;\n project_id: string;\n harness_name: string;\n backend_urls: string[];\n allow_localhost: boolean;\n idempotency_key: string;\n access_code?: string;\n};\n\nexport type CloudProvisionResult = {\n project_id: string;\n workspace_id: string;\n harness_id: string;\n harness_origin: string;\n api_key: string;\n api_origin: string;\n};\n\nexport const resolveProvisionRequest = async (\n directory: string,\n input: Omit<CloudProvisionRequest, \"idempotency_key\" | \"access_code\">,\n accessCode?: string,\n): Promise<CloudProvisionRequest> => {\n const file = path.join(directory, \".assistant-ui\", \"provision.json\");\n const previous = await readOptionalFile(file);\n let request: CloudProvisionRequest;\n if (previous) {\n request = JSON.parse(previous) as CloudProvisionRequest;\n const { idempotency_key: _key, ...savedInput } = request;\n if (JSON.stringify(savedInput) !== JSON.stringify(input)) {\n throw new Error(\n \"This directory has a pending setup for another project or backend. Resume that setup or use a new directory.\",\n );\n }\n } else {\n request = { ...input, idempotency_key: randomUUID() };\n await writeCloudFile(file, `${JSON.stringify(request, null, 2)}\\n`, {\n exclusive: true,\n });\n }\n return { ...request, ...(accessCode ? { access_code: accessCode } : {}) };\n};\n\nexport const provisionCloudHarness = async (\n managementUrl: string,\n accessToken: string,\n request: CloudProvisionRequest,\n): Promise<CloudProvisionResult> => {\n const response = await fetch(\n `${validateCloudUrl(managementUrl)}/api/cli/provision`,\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request),\n signal: AbortSignal.timeout(30_000),\n redirect: \"error\",\n },\n );\n if (!response.ok) {\n const body = (await response.json().catch(() => null)) as {\n error?: string;\n message?: string;\n } | null;\n const detail = body?.message ?? body?.error ?? response.statusText;\n throw new Error(`Cloud setup failed (${response.status}): ${detail}`);\n }\n const result = (await response.json()) as CloudProvisionResult;\n if (\n !result ||\n [\n result.api_key,\n result.harness_id,\n result.workspace_id,\n result.harness_origin,\n result.api_origin,\n ].some((value) => typeof value !== \"string\" || value.length === 0) ||\n result.project_id !== request.project_id\n ) {\n throw new Error(\n \"Cloud returned an incomplete setup response. Rerun the same command to resume safely.\",\n );\n }\n return result;\n};\n"],"mappings":";;;;;AAwBA,MAAa,0BAA0B,OACrC,WACA,OACA,eACmC;CACnC,MAAM,OAAO,KAAK,KAAK,WAAW,iBAAiB,gBAAgB;CACnE,MAAM,WAAW,MAAM,iBAAiB,IAAI;CAC5C,IAAI;CACJ,IAAI,UAAU;EACZ,UAAU,KAAK,MAAM,QAAQ;EAC7B,MAAM,EAAE,iBAAiB,MAAM,GAAG,eAAe;EACjD,IAAI,KAAK,UAAU,UAAU,MAAM,KAAK,UAAU,KAAK,GACrD,MAAM,IAAI,MACR,8GACF;CAEJ,OAAO;EACL,UAAU;GAAE,GAAG;GAAO,iBAAiB,WAAW;EAAE;EACpD,MAAM,eAAe,MAAM,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,EAAE,KAAK,EAClE,WAAW,KACb,CAAC;CACH;CACA,OAAO;EAAE,GAAG;EAAS,GAAI,aAAa,EAAE,aAAa,WAAW,IAAI,CAAC;CAAG;AAC1E;AAEA,MAAa,wBAAwB,OACnC,eACA,aACA,YACkC;CAClC,MAAM,WAAW,MAAM,MACrB,GAAG,iBAAiB,aAAa,EAAE,qBACnC;EACE,QAAQ;EACR,SAAS;GACP,eAAe,UAAU;GACzB,gBAAgB;EAClB;EACA,MAAM,KAAK,UAAU,OAAO;EAC5B,QAAQ,YAAY,QAAQ,GAAM;EAClC,UAAU;CACZ,CACF;CACA,IAAI,CAAC,SAAS,IAAI;EAChB,MAAM,OAAQ,MAAM,SAAS,KAAK,CAAC,CAAC,YAAY,IAAI;EAIpD,MAAM,SAAS,MAAM,WAAW,MAAM,SAAS,SAAS;EACxD,MAAM,IAAI,MAAM,uBAAuB,SAAS,OAAO,KAAK,QAAQ;CACtE;CACA,MAAM,SAAU,MAAM,SAAS,KAAK;CACpC,IACE,CAAC,UACD;EACE,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;CACT,CAAC,CAAC,MAAM,UAAU,OAAO,UAAU,YAAY,MAAM,WAAW,CAAC,KACjE,OAAO,eAAe,QAAQ,YAE9B,MAAM,IAAI,MACR,uFACF;CAEF,OAAO;AACT"}
@@ -0,0 +1,9 @@
1
+ import type { DeviceAuthorization } from "aui-auth/device";
2
+ export declare const setupLoginUrl: (authorization: DeviceAuthorization, issuer: string, attempt?: string) => string;
3
+ export declare const connectCloudLoginSetup: (url: string) => Promise<{
4
+ signal: AbortSignal;
5
+ publish(authorization: DeviceAuthorization, issuer: string): Promise<string>;
6
+ complete(result: "signed-in" | "cancelled" | "failed"): Promise<void>;
7
+ dispose: () => Promise<void>;
8
+ }>;
9
+ //# sourceMappingURL=cloud-setup-login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-setup-login.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-setup-login.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAqC3D,eAAO,MAAM,aAAa,GACxB,eAAe,mBAAmB,EAClC,QAAQ,MAAM,EACd,UAAS,MAAqB,WA8B/B,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAAU,KAAK,MAAM;;2BAgIvB,mBAAmB,UAAU,MAAM;qBAgCzC,WAAW,GAAG,WAAW,GAAG,QAAQ;;EAM9D,CAAC"}