assistant-ui 0.0.126 → 0.0.128

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 (86) hide show
  1. package/README.md +18 -2
  2. package/dist/codemods/utils/createTransformer.js +1 -2
  3. package/dist/codemods/utils/resolveBinding.js +1 -2
  4. package/dist/codemods/v0-11/content-part-to-message-part.js +1 -2
  5. package/dist/codemods/v0-12/assistant-api-to-aui.js +1 -2
  6. package/dist/codemods/v0-12/event-names-to-camelcase.js +1 -2
  7. package/dist/codemods/v0-12/primitive-if-to-aui-if.js +1 -2
  8. package/dist/codemods/v0-15/aui-accessor-calls-to-properties.js +1 -2
  9. package/dist/codemods/v0-8/ui-package-split.js +1 -2
  10. package/dist/codemods/v0-9/edge-package-split.js +1 -2
  11. package/dist/commands/add.js +1 -2
  12. package/dist/commands/agent.js +1 -2
  13. package/dist/commands/cloud.d.ts.map +1 -1
  14. package/dist/commands/cloud.js +70 -31
  15. package/dist/commands/cloud.js.map +1 -1
  16. package/dist/commands/create.js +1 -2
  17. package/dist/commands/doctor.js +1 -2
  18. package/dist/commands/info.js +1 -2
  19. package/dist/commands/init.js +1 -2
  20. package/dist/commands/mcp.js +1 -2
  21. package/dist/commands/update.js +1 -2
  22. package/dist/commands/upgrade.js +1 -2
  23. package/dist/index.js +1 -2
  24. package/dist/lib/agent-skill.d.ts +2 -2
  25. package/dist/lib/agent-skill.js +2 -3
  26. package/dist/lib/agent-skill.js.map +1 -1
  27. package/dist/lib/cloud-abort.js +1 -2
  28. package/dist/lib/cloud-auth.d.ts +1 -0
  29. package/dist/lib/cloud-auth.d.ts.map +1 -1
  30. package/dist/lib/cloud-auth.js +2 -3
  31. package/dist/lib/cloud-auth.js.map +1 -1
  32. package/dist/lib/cloud-config.d.ts +11 -1
  33. package/dist/lib/cloud-config.d.ts.map +1 -1
  34. package/dist/lib/cloud-config.js +52 -16
  35. package/dist/lib/cloud-config.js.map +1 -1
  36. package/dist/lib/cloud-provision.d.ts.map +1 -1
  37. package/dist/lib/cloud-provision.js +7 -3
  38. package/dist/lib/cloud-provision.js.map +1 -1
  39. package/dist/lib/cloud-remote.d.ts +16 -1
  40. package/dist/lib/cloud-remote.d.ts.map +1 -1
  41. package/dist/lib/cloud-remote.js +53 -9
  42. package/dist/lib/cloud-remote.js.map +1 -1
  43. package/dist/lib/cloud-setup-login.js +1 -2
  44. package/dist/lib/cloud-url.d.ts.map +1 -1
  45. package/dist/lib/cloud-url.js +3 -7
  46. package/dist/lib/cloud-url.js.map +1 -1
  47. package/dist/lib/create-project.js +1 -2
  48. package/dist/lib/handle-cli-error.js +1 -2
  49. package/dist/lib/install-ai-sdk-lib.js +1 -2
  50. package/dist/lib/install-edge-lib.js +1 -2
  51. package/dist/lib/run-spawn.js +1 -2
  52. package/dist/lib/safe-project-file.d.ts +2 -0
  53. package/dist/lib/safe-project-file.d.ts.map +1 -0
  54. package/dist/lib/safe-project-file.js +25 -0
  55. package/dist/lib/safe-project-file.js.map +1 -0
  56. package/dist/lib/transform.js +1 -2
  57. package/dist/lib/upgrade.js +1 -2
  58. package/dist/lib/utils/config.js +1 -2
  59. package/dist/lib/utils/download.js +1 -2
  60. package/dist/lib/utils/file-scanner.js +1 -2
  61. package/dist/lib/utils/logger.js +1 -2
  62. package/dist/lib/utils/package-installer.js +1 -2
  63. package/dist/lib/utils/package-json.js +1 -2
  64. package/dist/lib/utils/package-manager.js +1 -2
  65. package/dist/lib/utils/registry.js +1 -2
  66. package/dist/lib/utils/workspace.js +1 -2
  67. package/dist/program.js +1 -2
  68. package/dist/run.js +1 -2
  69. package/package.json +3 -3
  70. package/src/commands/cloud.test.ts +156 -0
  71. package/src/commands/cloud.ts +93 -41
  72. package/src/commands/mcp.test.ts +123 -0
  73. package/src/lib/agent-skill.ts +1 -1
  74. package/src/lib/cloud-auth.test.ts +7 -0
  75. package/src/lib/cloud-auth.ts +1 -1
  76. package/src/lib/cloud-config.test.ts +139 -1
  77. package/src/lib/cloud-config.ts +59 -8
  78. package/src/lib/cloud-provision.test.ts +26 -1
  79. package/src/lib/cloud-provision.ts +3 -0
  80. package/src/lib/cloud-remote.test.ts +99 -3
  81. package/src/lib/cloud-remote.ts +93 -7
  82. package/src/lib/cloud-url.ts +6 -4
  83. package/src/lib/safe-project-file.ts +31 -0
  84. package/templates/cloud-harness/components/assistant-ui/elements/thread.aui.tsx +1 -1
  85. package/templates/cloud-harness/components/assistant-ui/elements/tool-fallback.aui.tsx +7 -1
  86. package/templates/cloud-harness/package.json +7 -7
@@ -3,9 +3,9 @@ import { withStagedDownload } from "./utils/download.js";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import os from "node:os";
6
- //#region src/lib/agent-skill.ts
6
+ // src/lib/agent-skill.ts
7
7
  const SKILLS_PACKAGE = "assistant-ui/skills";
8
- const SKILLS_COMMIT = "1ba8e9105c504fe4ac2591e432783872d6b3c8ba";
8
+ const SKILLS_COMMIT = "b0609e66baff7e19f21053d5492464dcc22ec9c3";
9
9
  const SKILLS_PLUGIN_SOURCE = `gh:${SKILLS_PACKAGE}/assistant-ui#${SKILLS_COMMIT}`;
10
10
  function resolveSkillsInstall(params) {
11
11
  const { skills, stdinIsTTY = process.stdin.isTTY } = params;
@@ -76,7 +76,6 @@ async function publish(staging, dir, marker, retry = true) {
76
76
  await publish(staging, dir, marker, false);
77
77
  }
78
78
  }
79
- //#endregion
80
79
  export { SKILLS_COMMIT, SKILLS_PACKAGE, SKILLS_PLUGIN_SOURCE, buildSkillsAddCommand, ensureSkillsPlugin, resolveSkillsInstall, skillsPluginDir };
81
80
 
82
81
  //# sourceMappingURL=agent-skill.js.map
@@ -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 { withStagedDownload } from \"./utils/download\";\nimport { dlxCommand, type PackageManagerName } from \"./utils/package-manager\";\n\nexport const SKILLS_PACKAGE = \"assistant-ui/skills\";\nexport const SKILLS_COMMIT = \"1ba8e9105c504fe4ac2591e432783872d6b3c8ba\";\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 await withStagedDownload(\n SKILLS_PLUGIN_SOURCE,\n { dir: staging, preferOffline: true, silent: true },\n \"Download timed out.\",\n removeStaging,\n async (download) => {\n await download.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 },\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":";;;;;;AAMA,MAAa,iBAAiB;AAC9B,MAAa,gBAAgB;AAC7B,MAAa,uBAAuB,MAAM,eAAe,gBAAgB;AAEzE,SAAgB,qBAAqB,QAGb;CACtB,MAAM,EAAE,QAAQ,aAAa,QAAQ,MAAM,UAAU;CACrD,IAAI,WAAW,KAAA,GAAW,OAAO;CACjC,IAAI,CAAC,YAAY,OAAO;AAE1B;AAEA,SAAgB,sBACd,IACA,SAAmC,CAAC,GAChB;CACpB,MAAM,EAAE,aAAa,QAAQ,MAAM,UAAU;CAC7C,MAAM,CAAC,SAAS,WAAW,WAAW,EAAE;CACxC,MAAM,OAAO;EAAC,GAAG;EAAS;EAAU;EAAO;CAAc;CAIzD,IAAI,CAAC,YAAY,KAAK,KAAK,OAAO;CAElC,OAAO,CAAC,SAAS,IAAI;AACvB;AAEA,SAAgB,kBAA0B;CACxC,MAAM,YACJ,QAAQ,IAAI,kBAAkB,KAAK,KAAK,GAAG,QAAQ,GAAG,QAAQ;CAChE,OAAO,KAAK,KAAK,WAAW,gBAAgB,UAAU,aAAa;AACrE;AAEA,eAAsB,qBAAsC;CAC1D,MAAM,MAAM,gBAAgB;CAC5B,MAAM,SAAS,KAAK,KAAK,KAAK,kBAAkB,aAAa;CAC7D,IAAI,GAAG,WAAW,MAAM,GAAG,OAAO;CAElC,MAAM,SAAS,KAAK,QAAQ,GAAG;CAC/B,MAAM,GAAG,SAAS,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;CACnD,MAAM,UAAU,MAAM,GAAG,SAAS,QAAQ,KAAK,KAAK,QAAQ,WAAW,CAAC;CAKxE,MAAM,sBAAsB;EAC1B,QAAQ,eAAe,QAAQ,aAAa;EAC5C,QAAQ,eAAe,UAAU,qBAAqB;EACtD,QAAQ,eAAe,WAAW,qBAAqB;EACvD,GAAG,OAAO,SAAS;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CACrD;CACA,MAAM,yBAAyB,WAA2B;EACxD,cAAc;EACd,QAAQ,KAAK,QAAQ,KAAK,MAAM;CAClC;CACA,QAAQ,KAAK,QAAQ,aAAa;CAClC,QAAQ,KAAK,UAAU,qBAAqB;CAC5C,QAAQ,KAAK,WAAW,qBAAqB;CAE7C,MAAM,mBACJ,sBACA;EAAE,KAAK;EAAS,eAAe;EAAM,QAAQ;CAAK,GAClD,uBACA,eACA,OAAO,aAAa;EAClB,MAAM,SAAS,OAAO,UAAmB;GACvC,MAAM,IAAI,MACR,yHACA,EAAE,OAAO,MAAM,CACjB;EACF,CAAC;EACD,MAAM,QAAQ,SAAS,KAAK,MAAM;CACpC,CACF;CAEA,OAAO;AACT;AAMA,eAAe,QACb,SACA,KACA,QACA,QAAQ,MACO;CACf,IAAI;EACF,MAAM,GAAG,SAAS,OAAO,SAAS,GAAG;CACvC,SAAS,OAAO;EACd,IAAI,GAAG,WAAW,MAAM,GAAG;EAC3B,IAAI,CAAC,OAAO,MAAM;EAClB,MAAM,GAAG,SAAS,GAAG,KAAK;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;EAC1D,MAAM,QAAQ,SAAS,KAAK,QAAQ,KAAK;CAC3C;AACF"}
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 { withStagedDownload } from \"./utils/download\";\nimport { dlxCommand, type PackageManagerName } from \"./utils/package-manager\";\n\nexport const SKILLS_PACKAGE = \"assistant-ui/skills\";\nexport const SKILLS_COMMIT = \"b0609e66baff7e19f21053d5492464dcc22ec9c3\";\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 await withStagedDownload(\n SKILLS_PLUGIN_SOURCE,\n { dir: staging, preferOffline: true, silent: true },\n \"Download timed out.\",\n removeStaging,\n async (download) => {\n await download.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 },\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":";;;;;;AAMA,MAAa,iBAAiB;AAC9B,MAAa,gBAAgB;AAC7B,MAAa,uBAAuB,MAAM,eAAe,gBAAgB;AAEzE,SAAgB,qBAAqB,QAGb;CACtB,MAAM,EAAE,QAAQ,aAAa,QAAQ,MAAM,UAAU;CACrD,IAAI,WAAW,KAAA,GAAW,OAAO;CACjC,IAAI,CAAC,YAAY,OAAO;AAE1B;AAEA,SAAgB,sBACd,IACA,SAAmC,CAAC,GAChB;CACpB,MAAM,EAAE,aAAa,QAAQ,MAAM,UAAU;CAC7C,MAAM,CAAC,SAAS,WAAW,WAAW,EAAE;CACxC,MAAM,OAAO;EAAC,GAAG;EAAS;EAAU;EAAO;CAAc;CAIzD,IAAI,CAAC,YAAY,KAAK,KAAK,OAAO;CAElC,OAAO,CAAC,SAAS,IAAI;AACvB;AAEA,SAAgB,kBAA0B;CACxC,MAAM,YACJ,QAAQ,IAAI,kBAAkB,KAAK,KAAK,GAAG,QAAQ,GAAG,QAAQ;CAChE,OAAO,KAAK,KAAK,WAAW,gBAAgB,UAAU,aAAa;AACrE;AAEA,eAAsB,qBAAsC;CAC1D,MAAM,MAAM,gBAAgB;CAC5B,MAAM,SAAS,KAAK,KAAK,KAAK,kBAAkB,aAAa;CAC7D,IAAI,GAAG,WAAW,MAAM,GAAG,OAAO;CAElC,MAAM,SAAS,KAAK,QAAQ,GAAG;CAC/B,MAAM,GAAG,SAAS,MAAM,QAAQ,EAAE,WAAW,KAAK,CAAC;CACnD,MAAM,UAAU,MAAM,GAAG,SAAS,QAAQ,KAAK,KAAK,QAAQ,WAAW,CAAC;CAKxE,MAAM,sBAAsB;EAC1B,QAAQ,eAAe,QAAQ,aAAa;EAC5C,QAAQ,eAAe,UAAU,qBAAqB;EACtD,QAAQ,eAAe,WAAW,qBAAqB;EACvD,GAAG,OAAO,SAAS;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;CACrD;CACA,MAAM,yBAAyB,WAA2B;EACxD,cAAc;EACd,QAAQ,KAAK,QAAQ,KAAK,MAAM;CAClC;CACA,QAAQ,KAAK,QAAQ,aAAa;CAClC,QAAQ,KAAK,UAAU,qBAAqB;CAC5C,QAAQ,KAAK,WAAW,qBAAqB;CAE7C,MAAM,mBACJ,sBACA;EAAE,KAAK;EAAS,eAAe;EAAM,QAAQ;CAAK,GAClD,uBACA,eACA,OAAO,aAAa;EAClB,MAAM,SAAS,OAAO,UAAmB;GACvC,MAAM,IAAI,MACR,yHACA,EAAE,OAAO,MAAM,CACjB;EACF,CAAC;EACD,MAAM,QAAQ,SAAS,KAAK,MAAM;CACpC,CACF;CAEA,OAAO;AACT;AAMA,eAAe,QACb,SACA,KACA,QACA,QAAQ,MACO;CACf,IAAI;EACF,MAAM,GAAG,SAAS,OAAO,SAAS,GAAG;CACvC,SAAS,OAAO;EACd,IAAI,GAAG,WAAW,MAAM,GAAG;EAC3B,IAAI,CAAC,OAAO,MAAM;EAClB,MAAM,GAAG,SAAS,GAAG,KAAK;GAAE,WAAW;GAAM,OAAO;EAAK,CAAC;EAC1D,MAAM,QAAQ,SAAS,KAAK,QAAQ,KAAK;CAC3C;AACF"}
@@ -1,4 +1,4 @@
1
- //#region src/lib/cloud-abort.ts
1
+ // src/lib/cloud-abort.ts
2
2
  const abortable = (promise, signal) => new Promise((resolve, reject) => {
3
3
  const abort = () => reject(signal.reason);
4
4
  signal.addEventListener("abort", abort, { once: true });
@@ -7,7 +7,6 @@ const abortable = (promise, signal) => new Promise((resolve, reject) => {
7
7
  signal.removeEventListener("abort", abort);
8
8
  });
9
9
  });
10
- //#endregion
11
10
  export { abortable };
12
11
 
13
12
  //# sourceMappingURL=cloud-abort.js.map
@@ -14,6 +14,7 @@ export declare const readCloudCredentials: (config: CloudAuthConfig, options?: {
14
14
  matchConfig?: boolean;
15
15
  }) => Promise<SavedCredentials | null>;
16
16
  export declare const saveCloudCredentials: (config: CloudAuthConfig, credentials: DeviceCredentials, signal?: AbortSignal) => Promise<void>;
17
+ export declare const openBrowser: (url: string) => void;
17
18
  export declare const loginToCloud: (config: CloudAuthConfig, options?: {
18
19
  noOpen?: boolean;
19
20
  setupUrl?: string;
@@ -1 +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,qBAAqB,GAChC,QAAQ,eAAe,KACtB,OAAO,CAAC,MAAM,GAAG,IAAI,CAoBvB,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,CAEyC,CAAC;AAE3D,eAAO,MAAM,eAAe,GAC1B,QAAQ,eAAe,KACtB,OAAO,CAAC,IAAI,CASd,CAAC"}
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;AAEF,eAAO,MAAM,WAAW,GAAI,KAAK,MAAM,KAAG,IAYzC,CAAC;AAEF,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,qBAAqB,GAChC,QAAQ,eAAe,KACtB,OAAO,CAAC,MAAM,GAAG,IAAI,CAoBvB,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,CAEyC,CAAC;AAE3D,eAAO,MAAM,eAAe,GAC1B,QAAQ,eAAe,KACtB,OAAO,CAAC,IAAI,CASd,CAAC"}
@@ -7,7 +7,7 @@ import { chmod, link, mkdir, readFile, rename, rm, writeFile } from "node:fs/pro
7
7
  import { spawn } from "node:child_process";
8
8
  import { randomUUID } from "node:crypto";
9
9
  import { createDeviceLogin, isAccessDenied } from "aui-auth/device";
10
- //#region src/lib/cloud-auth.ts
10
+ // src/lib/cloud-auth.ts
11
11
  const cloudAuthConfig = () => ({
12
12
  issuer: validateCloudUrl(process.env.ASSISTANT_UI_ACCOUNTS_URL ?? "https://accounts.assistant-ui.com"),
13
13
  clientId: process.env.ASSISTANT_UI_OIDC_CLIENT_ID ?? "assistant-ui-cli",
@@ -164,7 +164,6 @@ const logoutFromCloud = async (config) => {
164
164
  }).revoke(saved);
165
165
  await rm(config.credentialsFile, { force: true });
166
166
  };
167
- //#endregion
168
- export { cloudAccessToken, cloudAuthConfig, loginToCloud, logoutFromCloud, readCloudCredentials, saveCloudCredentials, savedCloudAccessToken, validateCloudUrl };
167
+ export { cloudAccessToken, cloudAuthConfig, loginToCloud, logoutFromCloud, openBrowser, readCloudCredentials, saveCloudCredentials, savedCloudAccessToken, validateCloudUrl };
169
168
 
170
169
  //# sourceMappingURL=cloud-auth.js.map
@@ -1 +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 savedCloudAccessToken = async (\n config: CloudAuthConfig,\n): Promise<string | null> => {\n const saved = await readCloudCredentials(config);\n if (!saved) return null;\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 cloudAccessToken = async (\n config: CloudAuthConfig,\n options: {\n noOpen?: boolean;\n setupUrl?: string;\n print?: (text: string) => void;\n } = {},\n): Promise<string> =>\n (await savedCloudAccessToken(config)) ??\n (await loginToCloud(config, options)).tokens.accessToken;\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":"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"}
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\nexport const 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 savedCloudAccessToken = async (\n config: CloudAuthConfig,\n): Promise<string | null> => {\n const saved = await readCloudCredentials(config);\n if (!saved) return null;\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 cloudAccessToken = async (\n config: CloudAuthConfig,\n options: {\n noOpen?: boolean;\n setupUrl?: string;\n print?: (text: string) => void;\n } = {},\n): Promise<string> =>\n (await savedCloudAccessToken(config)) ??\n (await loginToCloud(config, options)).tokens.accessToken;\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":"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"}
@@ -9,12 +9,22 @@ export type CloudProjectConfig = {
9
9
  workspaceId: string;
10
10
  backendUrl: string;
11
11
  };
12
- export declare const writeCloudFile: (file: string, contents: string, options?: {
12
+ export declare const writeCloudFile: (file: string, contents: string, options: {
13
13
  exclusive?: boolean;
14
14
  mode?: number;
15
+ projectRoot: string;
15
16
  }) => Promise<void>;
16
17
  export declare const cloudProjectConfigPath: (directory: string) => string;
17
18
  export declare const readCloudProjectConfig: (directory: string) => Promise<CloudProjectConfig | null>;
19
+ /**
20
+ * The org and project `setup` configured for this directory or the nearest
21
+ * parent with an `.assistant-ui/cloud.json`, for remote commands to use when
22
+ * no --org or --project is given. A missing or unreadable file means none.
23
+ */
24
+ export declare const findCloudProjectDefaults: (directory: string) => Promise<{
25
+ org?: string;
26
+ project?: string;
27
+ } | null>;
18
28
  export declare const readOptionalFile: (file: string) => Promise<string>;
19
29
  export declare const mergeCloudEnvironment: (contents: string, values: Record<string, string>) => string;
20
30
  export declare const writeCloudProjectConfig: (directory: string, config: CloudProjectConfig, apiKey: string) => Promise<void>;
@@ -1 +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"}
1
+ {"version":3,"file":"cloud-config.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-config.ts"],"names":[],"mappings":"AAcA,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,SAAS;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,KACnE,OAAO,CAAC,IAAI,CA0Bd,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;;;;GAIG;AACH,eAAO,MAAM,wBAAwB,GACnC,WAAW,MAAM,KAChB,OAAO,CAAC;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAsBnD,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,CAoCd,CAAC"}
@@ -1,15 +1,23 @@
1
+ import { assertSafeProjectFile } from "./safe-project-file.js";
2
+ import { constants } from "node:fs";
1
3
  import path from "node:path";
2
- import { link, mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
4
+ import { link, mkdir, open, readFile, rename, rm, stat } from "node:fs/promises";
3
5
  import { randomUUID } from "node:crypto";
4
- //#region src/lib/cloud-config.ts
5
- const writeCloudFile = async (file, contents, options = {}) => {
6
+ // src/lib/cloud-config.ts
7
+ const writeCloudFile = async (file, contents, options) => {
8
+ const { projectRoot } = options;
9
+ assertSafeProjectFile(file, projectRoot);
6
10
  await mkdir(path.dirname(file), { recursive: true });
11
+ assertSafeProjectFile(file, projectRoot);
7
12
  const temporary = `${file}.${randomUUID()}.tmp`;
8
13
  try {
9
- await writeFile(temporary, contents, {
10
- flag: "wx",
11
- mode: options.mode ?? 384
12
- });
14
+ const handle = await open(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, options.mode ?? 384);
15
+ try {
16
+ await handle.writeFile(contents);
17
+ } finally {
18
+ await handle.close();
19
+ }
20
+ assertSafeProjectFile(file, projectRoot);
13
21
  if (options.exclusive) await link(temporary, file);
14
22
  else await rename(temporary, file);
15
23
  } finally {
@@ -25,6 +33,29 @@ const readCloudProjectConfig = async (directory) => {
25
33
  throw error;
26
34
  }
27
35
  };
36
+ /**
37
+ * The org and project `setup` configured for this directory or the nearest
38
+ * parent with an `.assistant-ui/cloud.json`, for remote commands to use when
39
+ * no --org or --project is given. A missing or unreadable file means none.
40
+ */
41
+ const findCloudProjectDefaults = async (directory) => {
42
+ for (let current = path.resolve(directory);;) {
43
+ try {
44
+ const config = JSON.parse(await readFile(cloudProjectConfigPath(current), "utf8"));
45
+ const org = typeof config?.organizationId === "string" ? config.organizationId : void 0;
46
+ const project = typeof config?.projectId === "string" ? config.projectId : void 0;
47
+ return org || project ? {
48
+ ...org && { org },
49
+ ...project && { project }
50
+ } : null;
51
+ } catch (error) {
52
+ if (error.code !== "ENOENT") return null;
53
+ }
54
+ const parent = path.dirname(current);
55
+ if (parent === current) return null;
56
+ current = parent;
57
+ }
58
+ };
28
59
  const readOptionalFile = async (file) => {
29
60
  try {
30
61
  return await readFile(file, "utf8");
@@ -53,6 +84,10 @@ const mergeCloudEnvironment = (contents, values) => {
53
84
  };
54
85
  const writeCloudProjectConfig = async (directory, config, apiKey) => {
55
86
  const envFile = path.join(directory, ".env.local");
87
+ const gitignoreFile = path.join(directory, ".gitignore");
88
+ assertSafeProjectFile(envFile, directory);
89
+ assertSafeProjectFile(gitignoreFile, directory);
90
+ assertSafeProjectFile(cloudProjectConfigPath(directory), directory);
56
91
  const contents = await readOptionalFile(envFile);
57
92
  const environment = mergeCloudEnvironment(contents, {
58
93
  ASSISTANT_API_KEY: apiKey,
@@ -60,17 +95,18 @@ const writeCloudProjectConfig = async (directory, config, apiKey) => {
60
95
  NEXT_PUBLIC_ASSISTANT_HARNESS_URL: config.harnessOrigin,
61
96
  NEXT_PUBLIC_ASSISTANT_WORKSPACE_ID: config.workspaceId
62
97
  });
63
- const gitignoreFile = path.join(directory, ".gitignore");
64
98
  const gitignore = await readOptionalFile(gitignoreFile);
65
99
  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`);
100
+ if (!ignored.includes(".env.local") && !ignored.includes(".env*.local")) await writeCloudFile(gitignoreFile, `${gitignore}${gitignore && !gitignore.endsWith("\n") ? "\n" : ""}.env.local\n`, {
101
+ projectRoot: directory,
102
+ mode: await stat(gitignoreFile).then((info) => info.mode & 511).catch((error) => {
103
+ if (error.code === "ENOENT") return 420;
104
+ throw error;
105
+ })
106
+ });
107
+ await writeCloudFile(envFile, environment, { projectRoot: directory });
108
+ await writeCloudFile(cloudProjectConfigPath(directory), `${JSON.stringify(config, null, 2)}\n`, { projectRoot: directory });
72
109
  };
73
- //#endregion
74
- export { cloudProjectConfigPath, mergeCloudEnvironment, readCloudProjectConfig, readOptionalFile, writeCloudFile, writeCloudProjectConfig };
110
+ export { cloudProjectConfigPath, findCloudProjectDefaults, mergeCloudEnvironment, readCloudProjectConfig, readOptionalFile, writeCloudFile, writeCloudProjectConfig };
75
111
 
76
112
  //# sourceMappingURL=cloud-config.js.map
@@ -1 +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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"}
1
+ {"version":3,"file":"cloud-config.js","names":[],"sources":["../../src/lib/cloud-config.ts"],"sourcesContent":["import {\n mkdir,\n open,\n readFile,\n link,\n rename,\n rm,\n stat,\n} from \"node:fs/promises\";\nimport { constants } from \"node:fs\";\nimport { randomUUID } from \"node:crypto\";\nimport path from \"node:path\";\nimport { assertSafeProjectFile } from \"./safe-project-file\";\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; projectRoot: string },\n): Promise<void> => {\n const { projectRoot } = options;\n assertSafeProjectFile(file, projectRoot);\n await mkdir(path.dirname(file), { recursive: true });\n assertSafeProjectFile(file, projectRoot);\n const temporary = `${file}.${randomUUID()}.tmp`;\n try {\n const handle = await open(\n temporary,\n constants.O_WRONLY |\n constants.O_CREAT |\n constants.O_EXCL |\n constants.O_NOFOLLOW,\n options.mode ?? 0o600,\n );\n try {\n await handle.writeFile(contents);\n } finally {\n await handle.close();\n }\n assertSafeProjectFile(file, projectRoot);\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\n/**\n * The org and project `setup` configured for this directory or the nearest\n * parent with an `.assistant-ui/cloud.json`, for remote commands to use when\n * no --org or --project is given. A missing or unreadable file means none.\n */\nexport const findCloudProjectDefaults = async (\n directory: string,\n): Promise<{ org?: string; project?: string } | null> => {\n for (let current = path.resolve(directory); ;) {\n try {\n const config = JSON.parse(\n await readFile(cloudProjectConfigPath(current), \"utf8\"),\n ) as Partial<CloudProjectConfig> | null;\n const org =\n typeof config?.organizationId === \"string\"\n ? config.organizationId\n : undefined;\n const project =\n typeof config?.projectId === \"string\" ? config.projectId : undefined;\n return org || project\n ? { ...(org && { org }), ...(project && { project }) }\n : null;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") return null;\n }\n const parent = path.dirname(current);\n if (parent === current) return null;\n current = parent;\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 gitignoreFile = path.join(directory, \".gitignore\");\n assertSafeProjectFile(envFile, directory);\n assertSafeProjectFile(gitignoreFile, directory);\n assertSafeProjectFile(cloudProjectConfigPath(directory), directory);\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 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 projectRoot: directory,\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, { projectRoot: directory });\n await writeCloudFile(\n cloudProjectConfigPath(directory),\n `${JSON.stringify(config, null, 2)}\\n`,\n { projectRoot: directory },\n );\n};\n"],"mappings":"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"}
@@ -1 +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"}
1
+ {"version":3,"file":"cloud-provision.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-provision.ts"],"names":[],"mappings":"AAMA,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,CAqB/B,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,eAAe,MAAM,EACrB,aAAa,MAAM,EACnB,SAAS,qBAAqB,KAC7B,OAAO,CAAC,oBAAoB,CAuC9B,CAAC"}
@@ -1,10 +1,12 @@
1
1
  import { validateCloudUrl } from "./cloud-url.js";
2
+ import { assertSafeProjectFile } from "./safe-project-file.js";
2
3
  import { readOptionalFile, writeCloudFile } from "./cloud-config.js";
3
4
  import path from "node:path";
4
5
  import { randomUUID } from "node:crypto";
5
- //#region src/lib/cloud-provision.ts
6
+ // src/lib/cloud-provision.ts
6
7
  const resolveProvisionRequest = async (directory, input, accessCode) => {
7
8
  const file = path.join(directory, ".assistant-ui", "provision.json");
9
+ assertSafeProjectFile(file, directory);
8
10
  const previous = await readOptionalFile(file);
9
11
  let request;
10
12
  if (previous) {
@@ -16,7 +18,10 @@ const resolveProvisionRequest = async (directory, input, accessCode) => {
16
18
  ...input,
17
19
  idempotency_key: randomUUID()
18
20
  };
19
- await writeCloudFile(file, `${JSON.stringify(request, null, 2)}\n`, { exclusive: true });
21
+ await writeCloudFile(file, `${JSON.stringify(request, null, 2)}\n`, {
22
+ exclusive: true,
23
+ projectRoot: directory
24
+ });
20
25
  }
21
26
  return {
22
27
  ...request,
@@ -49,7 +54,6 @@ const provisionCloudHarness = async (managementUrl, accessToken, request) => {
49
54
  ].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
55
  return result;
51
56
  };
52
- //#endregion
53
57
  export { provisionCloudHarness, resolveProvisionRequest };
54
58
 
55
59
  //# sourceMappingURL=cloud-provision.js.map
@@ -1 +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"}
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 { assertSafeProjectFile } from \"./safe-project-file\";\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 assertSafeProjectFile(file, directory);\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 projectRoot: directory,\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":";;;;;;AAyBA,MAAa,0BAA0B,OACrC,WACA,OACA,eACmC;CACnC,MAAM,OAAO,KAAK,KAAK,WAAW,iBAAiB,gBAAgB;CACnE,sBAAsB,MAAM,SAAS;CACrC,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;GAClE,WAAW;GACX,aAAa;EACf,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"}
@@ -3,6 +3,14 @@ export type CloudCliAction = {
3
3
  type: string;
4
4
  [key: string]: unknown;
5
5
  };
6
+ /**
7
+ * The organization and project a command uses when it is given no --org or
8
+ * --project, read from the `.assistant-ui/cloud.json` that setup writes.
9
+ */
10
+ export type CloudCliDefaults = {
11
+ org?: string;
12
+ project?: string;
13
+ };
6
14
  export type CloudCliResponse = {
7
15
  stdout?: string;
8
16
  stderr?: string;
@@ -11,12 +19,19 @@ export type CloudCliResponse = {
11
19
  };
12
20
  export declare const joinCloudArgs: (args: readonly string[]) => string;
13
21
  export declare const parseCloudCliResponse: (value: unknown) => CloudCliResponse | null;
14
- export declare const sendCloudCommand: (cloudUrl: string, args: readonly string[], accessToken: string | null) => Promise<CloudCliResponse>;
22
+ export declare const sendCloudCommand: (cloudUrl: string, args: readonly string[], accessToken: string | null, defaults?: CloudCliDefaults | null) => Promise<CloudCliResponse>;
15
23
  export declare const runCloudCommand: (args: readonly string[], deps: {
16
24
  cloudUrl: string;
17
25
  accessToken: () => Promise<string | null>;
18
26
  stdout: (text: string) => void;
19
27
  stderr: (text: string) => void;
20
28
  send?: typeof sendCloudCommand;
29
+ /** The default org/project for this directory, if setup configured one. */
30
+ defaults?: () => Promise<CloudCliDefaults | null>;
31
+ /** Whether someone is at the terminal to see a browser open. */
32
+ interactive?: boolean;
33
+ /** --no-open: print links without opening them. */
34
+ noOpen?: boolean;
35
+ openUrl?: (url: string) => void;
21
36
  }) => Promise<number>;
22
37
  //# sourceMappingURL=cloud-remote.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-remote.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-remote.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AAEjD,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAEtE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;CAC5B,CAAC;AAIF,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAG1C,CAAC;AAEf,eAAO,MAAM,qBAAqB,GAChC,OAAO,OAAO,KACb,gBAAgB,GAAG,IA+BrB,CAAC;AAOF,eAAO,MAAM,gBAAgB,GAC3B,UAAU,MAAM,EAChB,MAAM,SAAS,MAAM,EAAE,EACvB,aAAa,MAAM,GAAG,IAAI,KACzB,OAAO,CAAC,gBAAgB,CAkC1B,CAAC;AASF,eAAO,MAAM,eAAe,GAC1B,MAAM,SAAS,MAAM,EAAE,EACvB,MAAM;IACJ,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,IAAI,CAAC,EAAE,OAAO,gBAAgB,CAAC;CAChC,KACA,OAAO,CAAC,MAAM,CAiChB,CAAC"}
1
+ {"version":3,"file":"cloud-remote.d.ts","sourceRoot":"","sources":["../../src/lib/cloud-remote.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AAEjD,MAAM,MAAM,cAAc,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAEtE;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAElE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;CAC5B,CAAC;AAIF,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,MAAM,EAAE,KAAG,MAG1C,CAAC;AAEf,eAAO,MAAM,qBAAqB,GAChC,OAAO,OAAO,KACb,gBAAgB,GAAG,IA+BrB,CAAC;AAOF,eAAO,MAAM,gBAAgB,GAC3B,UAAU,MAAM,EAChB,MAAM,SAAS,MAAM,EAAE,EACvB,aAAa,MAAM,GAAG,IAAI,EAC1B,WAAW,gBAAgB,GAAG,IAAI,KACjC,OAAO,CAAC,gBAAgB,CAqC1B,CAAC;AA2DF,eAAO,MAAM,eAAe,GAC1B,MAAM,SAAS,MAAM,EAAE,EACvB,MAAM;IACJ,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC1C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,IAAI,CAAC,EAAE,OAAO,gBAAgB,CAAC;IAC/B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IAClD,gEAAgE;IAChE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mDAAmD;IACnD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC,KACA,OAAO,CAAC,MAAM,CAoDhB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { validateCloudUrl } from "./cloud-url.js";
2
- //#region src/lib/cloud-remote.ts
2
+ // src/lib/cloud-remote.ts
3
3
  const CLOUD_CLI_RUN_PATH = "/api/cli/run";
4
4
  const SAFE = /^[A-Za-z0-9_@%+=:,./-]+$/;
5
5
  const joinCloudArgs = (args) => args.map((arg) => SAFE.test(arg) ? arg : `'${arg.replaceAll("'", `'\\''`)}'`).join(" ");
@@ -13,8 +13,8 @@ const parseCloudCliResponse = (value) => {
13
13
  if (actions !== void 0 && !(Array.isArray(actions) && actions.every((action) => !!action && typeof action === "object" && typeof action.type === "string"))) return null;
14
14
  return body;
15
15
  };
16
- const cliVersion = "0.0.126";
17
- const sendCloudCommand = async (cloudUrl, args, accessToken) => {
16
+ const cliVersion = "0.0.128";
17
+ const sendCloudCommand = async (cloudUrl, args, accessToken, defaults) => {
18
18
  const url = `${validateCloudUrl(cloudUrl)}${CLOUD_CLI_RUN_PATH}`;
19
19
  let response;
20
20
  try {
@@ -29,8 +29,10 @@ const sendCloudCommand = async (cloudUrl, args, accessToken) => {
29
29
  client: {
30
30
  name: "assistant-ui",
31
31
  version: cliVersion,
32
- platform: `${process.platform}-${process.arch}`
33
- }
32
+ platform: `${process.platform}-${process.arch}`,
33
+ actions: Object.keys(ACTION_HANDLERS)
34
+ },
35
+ ...defaults && (defaults.org || defaults.project) ? { defaults } : {}
34
36
  }),
35
37
  redirect: "error",
36
38
  signal: AbortSignal.timeout(6e4)
@@ -43,14 +45,46 @@ const sendCloudCommand = async (cloudUrl, args, accessToken) => {
43
45
  if (!body) throw new Error(`Unexpected response from ${url} (${response.status} ${response.statusText}).`);
44
46
  return body;
45
47
  };
46
- const ACTION_HANDLERS = {};
48
+ const httpUrl = (value) => {
49
+ if (typeof value !== "string") return null;
50
+ try {
51
+ const url = new URL(value);
52
+ return url.protocol === "https:" || url.protocol === "http:" ? url : null;
53
+ } catch {
54
+ return null;
55
+ }
56
+ };
57
+ const urlAction = (action) => !!httpUrl(action["url"]) && (action["label"] === void 0 || typeof action["label"] === "string");
58
+ const printUrl = (action, context) => {
59
+ const label = action["label"] ?? "Open";
60
+ context.stderr(`${label}: ${action["url"]}\n`);
61
+ };
62
+ const ACTION_HANDLERS = {
63
+ print_url: {
64
+ valid: urlAction,
65
+ run: (action, context) => {
66
+ printUrl(action, context);
67
+ return 0;
68
+ }
69
+ },
70
+ open_url: {
71
+ valid: urlAction,
72
+ run: (action, context) => {
73
+ printUrl(action, context);
74
+ const url = httpUrl(action["url"]);
75
+ if (context.open && url.origin === new URL(context.cloudUrl).origin) context.openUrl(url.href);
76
+ return 0;
77
+ }
78
+ }
79
+ };
47
80
  const runCloudCommand = async (args, deps) => {
48
81
  let response;
49
82
  try {
83
+ const defaults = deps.defaults ? await deps.defaults().catch(() => null) : null;
50
84
  response = await (deps.send ?? sendCloudCommand)(deps.cloudUrl, args, await deps.accessToken().catch((error) => {
51
85
  deps.stderr(`${error instanceof Error ? error.message : String(error)}\n`);
52
86
  return null;
53
- }));
87
+ }), ...defaults ? [defaults] : []);
54
88
  } catch (error) {
55
89
  deps.stderr(`${error instanceof Error ? error.message : String(error)}\n`);
56
90
  return 1;
@@ -60,15 +94,25 @@ const runCloudCommand = async (args, deps) => {
60
94
  deps.stderr(`This command needs a newer assistant-ui (unsupported action "${unknown.type}"). Update the CLI and try again.\n`);
61
95
  return 1;
62
96
  }
97
+ const malformed = response.actions?.find((action) => !ACTION_HANDLERS[action.type].valid(action));
98
+ if (malformed) {
99
+ deps.stderr(`Unexpected response from the server (malformed "${malformed.type}" action).\n`);
100
+ return 1;
101
+ }
63
102
  if (response.stdout) deps.stdout(response.stdout);
64
103
  if (response.stderr) deps.stderr(response.stderr);
104
+ const context = {
105
+ cloudUrl: deps.cloudUrl,
106
+ open: !deps.noOpen && !!deps.interactive && !!deps.openUrl,
107
+ openUrl: deps.openUrl ?? (() => {}),
108
+ stderr: deps.stderr
109
+ };
65
110
  for (const action of response.actions ?? []) {
66
- const code = await ACTION_HANDLERS[action.type](action);
111
+ const code = await ACTION_HANDLERS[action.type].run(action, context);
67
112
  if (code !== 0) return code;
68
113
  }
69
114
  return response.exitCode;
70
115
  };
71
- //#endregion
72
116
  export { CLOUD_CLI_RUN_PATH, joinCloudArgs, parseCloudCliResponse, runCloudCommand, sendCloudCommand };
73
117
 
74
118
  //# sourceMappingURL=cloud-remote.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cloud-remote.js","names":[],"sources":["../../src/lib/cloud-remote.ts"],"sourcesContent":["import { validateCloudUrl } from \"./cloud-url\";\n\nexport const CLOUD_CLI_RUN_PATH = \"/api/cli/run\";\n\nexport type CloudCliAction = { type: string; [key: string]: unknown };\n\nexport type CloudCliResponse = {\n stdout?: string;\n stderr?: string;\n exitCode: number;\n actions?: CloudCliAction[];\n};\n\nconst SAFE = /^[A-Za-z0-9_@%+=:,./-]+$/;\n\nexport const joinCloudArgs = (args: readonly string[]): string =>\n args\n .map((arg) => (SAFE.test(arg) ? arg : `'${arg.replaceAll(\"'\", `'\\\\''`)}'`))\n .join(\" \");\n\nexport const parseCloudCliResponse = (\n value: unknown,\n): CloudCliResponse | null => {\n if (!value || typeof value !== \"object\") return null;\n const body = value as Record<string, unknown>;\n const exitCode = body[\"exitCode\"];\n if (\n typeof exitCode !== \"number\" ||\n !Number.isInteger(exitCode) ||\n exitCode < 0 ||\n exitCode > 255\n ) {\n return null;\n }\n for (const key of [\"stdout\", \"stderr\"]) {\n if (body[key] !== undefined && typeof body[key] !== \"string\") return null;\n }\n const actions = body[\"actions\"];\n if (\n actions !== undefined &&\n !(\n Array.isArray(actions) &&\n actions.every(\n (action) =>\n !!action &&\n typeof action === \"object\" &&\n typeof (action as CloudCliAction).type === \"string\",\n )\n )\n ) {\n return null;\n }\n return body as CloudCliResponse;\n};\n\nconst cliVersion: string =\n typeof __AUI_PACKAGE_VERSION__ === \"string\"\n ? __AUI_PACKAGE_VERSION__\n : \"0.0.0\";\n\nexport const sendCloudCommand = async (\n cloudUrl: string,\n args: readonly string[],\n accessToken: string | null,\n): Promise<CloudCliResponse> => {\n const url = `${validateCloudUrl(cloudUrl)}${CLOUD_CLI_RUN_PATH}`;\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),\n },\n body: JSON.stringify({\n command: joinCloudArgs(args),\n client: {\n name: \"assistant-ui\",\n version: cliVersion,\n platform: `${process.platform}-${process.arch}`,\n },\n }),\n redirect: \"error\",\n signal: AbortSignal.timeout(60_000),\n });\n } catch (error) {\n const cause = (error as Error).cause as NodeJS.ErrnoException | undefined;\n throw new Error(\n `Could not reach ${url}: ${cause?.message || cause?.code || (error as Error).message}`,\n );\n }\n const body = parseCloudCliResponse(await response.json().catch(() => null));\n if (!body) {\n throw new Error(\n `Unexpected response from ${url} (${response.status} ${response.statusText}).`,\n );\n }\n return body;\n};\n\n// A response naming an action this client lacks fails before printing, so the\n// server can roll a new action out behind the client version it is sent.\nconst ACTION_HANDLERS: Record<\n string,\n (action: CloudCliAction) => Promise<number>\n> = {};\n\nexport const runCloudCommand = async (\n args: readonly string[],\n deps: {\n cloudUrl: string;\n accessToken: () => Promise<string | null>;\n stdout: (text: string) => void;\n stderr: (text: string) => void;\n send?: typeof sendCloudCommand;\n },\n): Promise<number> => {\n let response: CloudCliResponse;\n try {\n response = await (deps.send ?? sendCloudCommand)(\n deps.cloudUrl,\n args,\n await deps.accessToken().catch((error: unknown) => {\n deps.stderr(\n `${error instanceof Error ? error.message : String(error)}\\n`,\n );\n return null;\n }),\n );\n } catch (error) {\n deps.stderr(`${error instanceof Error ? error.message : String(error)}\\n`);\n return 1;\n }\n const unknown = response.actions?.find(\n (action) => !Object.hasOwn(ACTION_HANDLERS, action.type),\n );\n if (unknown) {\n deps.stderr(\n `This command needs a newer assistant-ui (unsupported action \"${unknown.type}\"). Update the CLI and try again.\\n`,\n );\n return 1;\n }\n if (response.stdout) deps.stdout(response.stdout);\n if (response.stderr) deps.stderr(response.stderr);\n for (const action of response.actions ?? []) {\n const code = await ACTION_HANDLERS[action.type]!(action);\n if (code !== 0) return code;\n }\n return response.exitCode;\n};\n"],"mappings":";;AAEA,MAAa,qBAAqB;AAWlC,MAAM,OAAO;AAEb,MAAa,iBAAiB,SAC5B,KACG,KAAK,QAAS,KAAK,KAAK,GAAG,IAAI,MAAM,IAAI,IAAI,WAAW,KAAK,OAAO,EAAE,EAAG,CAAC,CAC1E,KAAK,GAAG;AAEb,MAAa,yBACX,UAC4B;CAC5B,IAAI,CAAC,SAAS,OAAO,UAAU,UAAU,OAAO;CAChD,MAAM,OAAO;CACb,MAAM,WAAW,KAAK;CACtB,IACE,OAAO,aAAa,YACpB,CAAC,OAAO,UAAU,QAAQ,KAC1B,WAAW,KACX,WAAW,KAEX,OAAO;CAET,KAAK,MAAM,OAAO,CAAC,UAAU,QAAQ,GACnC,IAAI,KAAK,SAAS,KAAA,KAAa,OAAO,KAAK,SAAS,UAAU,OAAO;CAEvE,MAAM,UAAU,KAAK;CACrB,IACE,YAAY,KAAA,KACZ,EACE,MAAM,QAAQ,OAAO,KACrB,QAAQ,OACL,WACC,CAAC,CAAC,UACF,OAAO,WAAW,YAClB,OAAQ,OAA0B,SAAS,QAC/C,IAGF,OAAO;CAET,OAAO;AACT;AAEA,MAAM,aAAA;AAKN,MAAa,mBAAmB,OAC9B,UACA,MACA,gBAC8B;CAC9B,MAAM,MAAM,GAAG,iBAAiB,QAAQ,IAAI;CAC5C,IAAI;CACJ,IAAI;EACF,WAAW,MAAM,MAAM,KAAK;GAC1B,QAAQ;GACR,SAAS;IACP,gBAAgB;IAChB,GAAI,cAAc,EAAE,eAAe,UAAU,cAAc,IAAI,CAAC;GAClE;GACA,MAAM,KAAK,UAAU;IACnB,SAAS,cAAc,IAAI;IAC3B,QAAQ;KACN,MAAM;KACN,SAAS;KACT,UAAU,GAAG,QAAQ,SAAS,GAAG,QAAQ;IAC3C;GACF,CAAC;GACD,UAAU;GACV,QAAQ,YAAY,QAAQ,GAAM;EACpC,CAAC;CACH,SAAS,OAAO;EACd,MAAM,QAAS,MAAgB;EAC/B,MAAM,IAAI,MACR,mBAAmB,IAAI,IAAI,OAAO,WAAW,OAAO,QAAS,MAAgB,SAC/E;CACF;CACA,MAAM,OAAO,sBAAsB,MAAM,SAAS,KAAK,CAAC,CAAC,YAAY,IAAI,CAAC;CAC1E,IAAI,CAAC,MACH,MAAM,IAAI,MACR,4BAA4B,IAAI,IAAI,SAAS,OAAO,GAAG,SAAS,WAAW,GAC7E;CAEF,OAAO;AACT;AAIA,MAAM,kBAGF,CAAC;AAEL,MAAa,kBAAkB,OAC7B,MACA,SAOoB;CACpB,IAAI;CACJ,IAAI;EACF,WAAW,OAAO,KAAK,QAAQ,iBAAA,CAC7B,KAAK,UACL,MACA,MAAM,KAAK,YAAY,CAAC,CAAC,OAAO,UAAmB;GACjD,KAAK,OACH,GAAG,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GAC5D;GACA,OAAO;EACT,CAAC,CACH;CACF,SAAS,OAAO;EACd,KAAK,OAAO,GAAG,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GAAG;EACzE,OAAO;CACT;CACA,MAAM,UAAU,SAAS,SAAS,MAC/B,WAAW,CAAC,OAAO,OAAO,iBAAiB,OAAO,IAAI,CACzD;CACA,IAAI,SAAS;EACX,KAAK,OACH,gEAAgE,QAAQ,KAAK,oCAC/E;EACA,OAAO;CACT;CACA,IAAI,SAAS,QAAQ,KAAK,OAAO,SAAS,MAAM;CAChD,IAAI,SAAS,QAAQ,KAAK,OAAO,SAAS,MAAM;CAChD,KAAK,MAAM,UAAU,SAAS,WAAW,CAAC,GAAG;EAC3C,MAAM,OAAO,MAAM,gBAAgB,OAAO,KAAK,CAAE,MAAM;EACvD,IAAI,SAAS,GAAG,OAAO;CACzB;CACA,OAAO,SAAS;AAClB"}
1
+ {"version":3,"file":"cloud-remote.js","names":[],"sources":["../../src/lib/cloud-remote.ts"],"sourcesContent":["import { validateCloudUrl } from \"./cloud-url\";\n\nexport const CLOUD_CLI_RUN_PATH = \"/api/cli/run\";\n\nexport type CloudCliAction = { type: string; [key: string]: unknown };\n\n/**\n * The organization and project a command uses when it is given no --org or\n * --project, read from the `.assistant-ui/cloud.json` that setup writes.\n */\nexport type CloudCliDefaults = { org?: string; project?: string };\n\nexport type CloudCliResponse = {\n stdout?: string;\n stderr?: string;\n exitCode: number;\n actions?: CloudCliAction[];\n};\n\nconst SAFE = /^[A-Za-z0-9_@%+=:,./-]+$/;\n\nexport const joinCloudArgs = (args: readonly string[]): string =>\n args\n .map((arg) => (SAFE.test(arg) ? arg : `'${arg.replaceAll(\"'\", `'\\\\''`)}'`))\n .join(\" \");\n\nexport const parseCloudCliResponse = (\n value: unknown,\n): CloudCliResponse | null => {\n if (!value || typeof value !== \"object\") return null;\n const body = value as Record<string, unknown>;\n const exitCode = body[\"exitCode\"];\n if (\n typeof exitCode !== \"number\" ||\n !Number.isInteger(exitCode) ||\n exitCode < 0 ||\n exitCode > 255\n ) {\n return null;\n }\n for (const key of [\"stdout\", \"stderr\"]) {\n if (body[key] !== undefined && typeof body[key] !== \"string\") return null;\n }\n const actions = body[\"actions\"];\n if (\n actions !== undefined &&\n !(\n Array.isArray(actions) &&\n actions.every(\n (action) =>\n !!action &&\n typeof action === \"object\" &&\n typeof (action as CloudCliAction).type === \"string\",\n )\n )\n ) {\n return null;\n }\n return body as CloudCliResponse;\n};\n\nconst cliVersion: string =\n typeof __AUI_PACKAGE_VERSION__ === \"string\"\n ? __AUI_PACKAGE_VERSION__\n : \"0.0.0\";\n\nexport const sendCloudCommand = async (\n cloudUrl: string,\n args: readonly string[],\n accessToken: string | null,\n defaults?: CloudCliDefaults | null,\n): Promise<CloudCliResponse> => {\n const url = `${validateCloudUrl(cloudUrl)}${CLOUD_CLI_RUN_PATH}`;\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),\n },\n body: JSON.stringify({\n command: joinCloudArgs(args),\n client: {\n name: \"assistant-ui\",\n version: cliVersion,\n platform: `${process.platform}-${process.arch}`,\n // The server sends only the actions listed here.\n actions: Object.keys(ACTION_HANDLERS),\n },\n ...(defaults && (defaults.org || defaults.project) ? { defaults } : {}),\n }),\n redirect: \"error\",\n signal: AbortSignal.timeout(60_000),\n });\n } catch (error) {\n const cause = (error as Error).cause as NodeJS.ErrnoException | undefined;\n throw new Error(\n `Could not reach ${url}: ${cause?.message || cause?.code || (error as Error).message}`,\n );\n }\n const body = parseCloudCliResponse(await response.json().catch(() => null));\n if (!body) {\n throw new Error(\n `Unexpected response from ${url} (${response.status} ${response.statusText}).`,\n );\n }\n return body;\n};\n\ntype ActionContext = {\n cloudUrl: string;\n /** False when the user passed --no-open or no one is at the terminal. */\n open: boolean;\n openUrl: (url: string) => void;\n stderr: (text: string) => void;\n};\n\ntype ActionHandler = {\n /** Checks the action's fields; a malformed action fails before printing. */\n valid(action: CloudCliAction): boolean;\n run(action: CloudCliAction, context: ActionContext): Promise<number> | number;\n};\n\nconst httpUrl = (value: unknown): URL | null => {\n if (typeof value !== \"string\") return null;\n try {\n const url = new URL(value);\n return url.protocol === \"https:\" || url.protocol === \"http:\" ? url : null;\n } catch {\n return null;\n }\n};\n\nconst urlAction = (action: CloudCliAction): boolean =>\n !!httpUrl(action[\"url\"]) &&\n (action[\"label\"] === undefined || typeof action[\"label\"] === \"string\");\n\nconst printUrl = (action: CloudCliAction, context: ActionContext): void => {\n const label = (action[\"label\"] as string | undefined) ?? \"Open\";\n context.stderr(`${label}: ${action[\"url\"] as string}\\n`);\n};\n\n// The request lists these (`client.actions`) and the server sends no others;\n// a response naming one this client lacks still fails before printing.\nconst ACTION_HANDLERS: Record<string, ActionHandler> = {\n print_url: {\n valid: urlAction,\n run: (action, context) => {\n printUrl(action, context);\n return 0;\n },\n },\n // Prints the link, then opens it in a browser when someone is at the\n // terminal. Only links on the Cloud the command ran against are opened.\n open_url: {\n valid: urlAction,\n run: (action, context) => {\n printUrl(action, context);\n const url = httpUrl(action[\"url\"])!;\n if (context.open && url.origin === new URL(context.cloudUrl).origin)\n context.openUrl(url.href);\n return 0;\n },\n },\n};\n\nexport const runCloudCommand = async (\n args: readonly string[],\n deps: {\n cloudUrl: string;\n accessToken: () => Promise<string | null>;\n stdout: (text: string) => void;\n stderr: (text: string) => void;\n send?: typeof sendCloudCommand;\n /** The default org/project for this directory, if setup configured one. */\n defaults?: () => Promise<CloudCliDefaults | null>;\n /** Whether someone is at the terminal to see a browser open. */\n interactive?: boolean;\n /** --no-open: print links without opening them. */\n noOpen?: boolean;\n openUrl?: (url: string) => void;\n },\n): Promise<number> => {\n let response: CloudCliResponse;\n try {\n const defaults = deps.defaults\n ? await deps.defaults().catch(() => null)\n : null;\n response = await (deps.send ?? sendCloudCommand)(\n deps.cloudUrl,\n args,\n await deps.accessToken().catch((error: unknown) => {\n deps.stderr(\n `${error instanceof Error ? error.message : String(error)}\\n`,\n );\n return null;\n }),\n ...(defaults ? [defaults] : []),\n );\n } catch (error) {\n deps.stderr(`${error instanceof Error ? error.message : String(error)}\\n`);\n return 1;\n }\n const unknown = response.actions?.find(\n (action) => !Object.hasOwn(ACTION_HANDLERS, action.type),\n );\n if (unknown) {\n deps.stderr(\n `This command needs a newer assistant-ui (unsupported action \"${unknown.type}\"). Update the CLI and try again.\\n`,\n );\n return 1;\n }\n const malformed = response.actions?.find(\n (action) => !ACTION_HANDLERS[action.type]!.valid(action),\n );\n if (malformed) {\n deps.stderr(\n `Unexpected response from the server (malformed \"${malformed.type}\" action).\\n`,\n );\n return 1;\n }\n if (response.stdout) deps.stdout(response.stdout);\n if (response.stderr) deps.stderr(response.stderr);\n const context: ActionContext = {\n cloudUrl: deps.cloudUrl,\n open: !deps.noOpen && !!deps.interactive && !!deps.openUrl,\n openUrl: deps.openUrl ?? (() => {}),\n stderr: deps.stderr,\n };\n for (const action of response.actions ?? []) {\n const code = await ACTION_HANDLERS[action.type]!.run(action, context);\n if (code !== 0) return code;\n }\n return response.exitCode;\n};\n"],"mappings":"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"}