run-smoke.drive.mjs 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. import { defineScript } from "opencode-drive"
  2. import { mkdir } from "node:fs/promises"
  3. import path from "node:path"
  4. export default defineScript({
  5. launch: "manual",
  6. setup({ config }) {
  7. config.autoupdate = false
  8. },
  9. async run({ artifacts, llm, server }) {
  10. await configureServicePort(artifacts)
  11. llm.queue(llm.text("drive noninteractive smoke ok"))
  12. await server.launch()
  13. const registration = await serviceRegistration(artifacts)
  14. const root = path.resolve(import.meta.dir, "../../../..")
  15. const directory = path.join(artifacts, "files")
  16. const child = Bun.spawn(
  17. [
  18. process.execPath,
  19. path.join(root, "packages/cli/src/index.ts"),
  20. "run",
  21. "--server",
  22. registration.url,
  23. "drive smoke",
  24. ],
  25. {
  26. cwd: path.join(root, "packages/cli"),
  27. env: {
  28. ...process.env,
  29. PWD: directory,
  30. OPENCODE_PASSWORD: registration.password,
  31. OPENCODE_CONFIG_DIR: path.join(directory, ".opencode"),
  32. OPENCODE_DISABLE_AUTOUPDATE: "1",
  33. },
  34. stdin: "ignore",
  35. stdout: "pipe",
  36. stderr: "pipe",
  37. },
  38. )
  39. const [exitCode, stdout, stderr] = await Promise.all([
  40. child.exited,
  41. new Response(child.stdout).text(),
  42. new Response(child.stderr).text(),
  43. ])
  44. if (exitCode !== 0) throw new Error(`run exited ${exitCode}: ${stderr}`)
  45. if (stdout !== "drive noninteractive smoke ok\n") throw new Error(`unexpected run output: ${stdout}`)
  46. },
  47. })
  48. /** @param {string} artifacts */
  49. async function configureServicePort(artifacts) {
  50. const probe = Bun.serve({ hostname: "127.0.0.1", port: 0, fetch: () => new Response() })
  51. const port = probe.port
  52. await probe.stop(true)
  53. if (!port) throw new Error("Failed to allocate a Drive service port")
  54. const file = path.join(artifacts, "files/.opencode/service-local.json")
  55. await mkdir(path.dirname(file), { recursive: true })
  56. await Bun.write(file, JSON.stringify({ port }))
  57. }
  58. /** @param {string} artifacts */
  59. async function serviceRegistration(artifacts) {
  60. const directory = path.join(artifacts, "home/.local/state/opencode")
  61. for (let attempt = 0; attempt < 200; attempt++) {
  62. for (const name of ["service-local.json", "service.json"]) {
  63. const value = await Bun.file(path.join(directory, name))
  64. .json()
  65. .catch(() => undefined)
  66. if (isRegistration(value)) return value
  67. }
  68. await Bun.sleep(50)
  69. }
  70. throw new Error("Drive service registration was not written")
  71. }
  72. /** @param {unknown} value */
  73. function isRegistration(value) {
  74. return (
  75. typeof value === "object" &&
  76. value !== null &&
  77. "url" in value &&
  78. typeof value.url === "string" &&
  79. "password" in value &&
  80. typeof value.password === "string"
  81. )
  82. }