oh-my-opencode 5.1.29 → 5.1.32

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 (146) hide show
  1. package/README.ja.md +2 -0
  2. package/README.ko.md +2 -0
  3. package/README.md +2 -0
  4. package/README.ru.md +2 -0
  5. package/README.zh-cn.md +2 -0
  6. package/dist/cli/cli-program.d.ts +1 -20
  7. package/dist/cli/index.js +950 -51
  8. package/dist/cli/install-command-options.d.ts +20 -0
  9. package/dist/cli/service-auth/browser.d.ts +3 -0
  10. package/dist/cli/service-auth/cli-keystore.fixture.d.ts +7 -0
  11. package/dist/cli/service-auth/commands.d.ts +19 -0
  12. package/dist/cli/service-auth/device-flow.d.ts +15 -0
  13. package/dist/cli/service-auth/device-keys.d.ts +10 -0
  14. package/dist/cli/service-auth/device-name.d.ts +3 -0
  15. package/dist/cli/service-auth/keystore.d.ts +20 -0
  16. package/dist/cli/service-auth/loopback.d.ts +11 -0
  17. package/dist/cli/service-auth/protocol.d.ts +86 -0
  18. package/dist/cli/service-auth/refresh-child.fixture.d.ts +1 -0
  19. package/dist/cli/service-auth/session.d.ts +17 -0
  20. package/dist/cli/service-auth/terminal-text.d.ts +1 -0
  21. package/dist/cli/service-auth/test-support.d.ts +43 -0
  22. package/dist/cli/service-auth/tls-test-certificate.d.ts +4 -0
  23. package/dist/cli-node/index.js +950 -51
  24. package/dist/features/background-agent/task-poller.d.ts +3 -0
  25. package/dist/features/opencode-skill-loader/opencode-shell-skill-content.d.ts +8 -0
  26. package/dist/features/opencode-skill-loader/skill-content.d.ts +1 -0
  27. package/dist/features/team-mode/tools/lifecycle-inline-spec.d.ts +1 -0
  28. package/dist/hooks/keyword-detector/constants.d.ts +1 -0
  29. package/dist/hooks/non-interactive-env/non-interactive-env-hook.d.ts +5 -1
  30. package/dist/index.js +40791 -40412
  31. package/dist/shared/disabled-agents.d.ts +1 -0
  32. package/dist/shared/opencode-shell.d.ts +27 -0
  33. package/dist/skills/browser/SKILL.md +1 -1
  34. package/dist/skills/browser/runtime/omowright/manifest.json +1 -1
  35. package/dist/skills/ultimate-browsing/SKILL.md +14 -16
  36. package/dist/skills/ulw-execute/SKILL.md +2 -2
  37. package/dist/skills/ulw-research/SKILL.md +17 -18
  38. package/dist/skills/ulw-research/scripts/cli-support.test.ts +5 -3
  39. package/dist/skills/ulw-research/scripts/outcome.test.ts +10 -4
  40. package/dist/skills/ulw-research/scripts/report-tools.test.ts +3 -2
  41. package/dist/skills/visual-qa/scripts/cli.test.ts +5 -3
  42. package/dist/tools/delegate-task/executor-types.d.ts +1 -0
  43. package/dist/tools/delegate-task/sync-session-creator.d.ts +1 -0
  44. package/dist/tools/delegate-task/types.d.ts +2 -0
  45. package/dist/tools/session-manager/sdk-storage.d.ts +15 -2
  46. package/dist/tools/session-manager/session-error.d.ts +7 -0
  47. package/dist/tools/session-manager/storage.d.ts +4 -2
  48. package/dist/tui.js +89 -21
  49. package/package.json +15 -14
  50. package/packages/git-bash-mcp/dist/cli.js +1 -1
  51. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +90 -0
  52. package/packages/lsp-core/src/lsp/client.ts +3 -1
  53. package/packages/lsp-core/src/lsp/workspace-apply-edit-test-support.ts +5 -2
  54. package/packages/lsp-daemon/dist/cli.js +1 -1
  55. package/packages/lsp-daemon/dist/client.js +1 -1
  56. package/packages/lsp-daemon/dist/index.js +1 -1
  57. package/packages/lsp-tools-mcp/dist/cli.js +1 -1
  58. package/packages/lsp-tools-mcp/dist/lsp/manager.js +1 -1
  59. package/packages/lsp-tools-mcp/dist/mcp.js +1 -1
  60. package/packages/lsp-tools-mcp/dist/tools.js +1 -1
  61. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  62. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +7 -4
  63. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  64. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  65. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  66. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  67. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  68. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  69. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  70. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  71. package/packages/omo-codex/plugin/components/lcx/skills/lcx-contribute-bug-fix/SKILL.md +7 -4
  72. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  73. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +1 -1
  74. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  75. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  76. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  77. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  78. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  79. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  80. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +3 -3
  81. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  82. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  83. package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +18 -15
  84. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  85. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  86. package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +1 -1
  87. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  88. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  89. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +18 -15
  90. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  91. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  92. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +2 -2
  93. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +2 -2
  94. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  95. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  96. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  97. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  98. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  99. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  100. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  101. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  102. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  103. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  104. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  105. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  106. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  107. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  108. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  109. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  110. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  111. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  112. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  113. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  114. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  115. package/packages/omo-codex/plugin/package-lock.json +12 -12
  116. package/packages/omo-codex/plugin/package.json +1 -1
  117. package/packages/omo-codex/plugin/scripts/auto-update-plan.mjs +2 -0
  118. package/packages/omo-codex/plugin/scripts/auto-update.mjs +2 -0
  119. package/packages/omo-codex/plugin/scripts/build-bundled-mcp-runtimes.mjs +2 -0
  120. package/packages/omo-codex/plugin/scripts/build-components.mjs +1 -0
  121. package/packages/omo-codex/plugin/scripts/materialize-shared-upstreams.mjs +42 -10
  122. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +2 -2
  123. package/packages/omo-codex/plugin/skills/browser/SKILL.md +1 -1
  124. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +1 -1
  125. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +7 -4
  126. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +3 -3
  127. package/packages/omo-codex/plugin/skills/ultimate-browsing/SKILL.md +14 -16
  128. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +18 -15
  129. package/packages/omo-codex/plugin/skills/ulw-execute/SKILL.md +2 -2
  130. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +2 -2
  131. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +2 -2
  132. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +17 -18
  133. package/packages/omo-codex/plugin/test/materialize-shared-upstreams.test.mjs +179 -0
  134. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +2 -2
  135. package/packages/omo-codex/plugin/test/windows-console-hide.test.mjs +150 -0
  136. package/packages/omo-codex/scripts/install-dist/install-local.mjs +11 -6
  137. package/packages/prompts-core/prompts/ultrawork/codex.md +18 -15
  138. package/packages/shared-skills/skills/browser/SKILL.md +1 -1
  139. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +1 -1
  140. package/packages/shared-skills/skills/ultimate-browsing/SKILL.md +14 -16
  141. package/packages/shared-skills/skills/ulw-execute/SKILL.md +2 -2
  142. package/packages/shared-skills/skills/ulw-research/SKILL.md +17 -18
  143. package/packages/shared-skills/skills/ulw-research/scripts/cli-support.test.ts +5 -3
  144. package/packages/shared-skills/skills/ulw-research/scripts/outcome.test.ts +10 -4
  145. package/packages/shared-skills/skills/ulw-research/scripts/report-tools.test.ts +3 -2
  146. package/packages/shared-skills/skills/visual-qa/scripts/cli.test.ts +5 -3
package/dist/cli/index.js CHANGED
@@ -64,7 +64,7 @@ var package_default;
64
64
  var init_package = __esm(() => {
65
65
  package_default = {
66
66
  name: "oh-my-opencode",
67
- version: "5.1.29",
67
+ version: "5.1.32",
68
68
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
69
69
  main: "./dist/index.js",
70
70
  types: "dist/index.d.ts",
@@ -241,7 +241,7 @@ var init_package = __esm(() => {
241
241
  },
242
242
  devDependencies: {
243
243
  "@clack/prompts": "^1.8.1",
244
- "@code-yeongyu/senpi": "2026.10.10-11",
244
+ "@code-yeongyu/senpi": "2026.10.12",
245
245
  "@modelcontextprotocol/sdk": "^1.30.0",
246
246
  "@oh-my-opencode/agents-md-core": "workspace:*",
247
247
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
@@ -283,6 +283,7 @@ var init_package = __esm(() => {
283
283
  "@types/js-yaml": "^4.0.9",
284
284
  "@types/picomatch": "^4.0.3",
285
285
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
286
+ "@typescript/typescript6": "6.0.2",
286
287
  "@xterm/addon-unicode11": "^0.9.0",
287
288
  "@xterm/xterm": "^6.0.0",
288
289
  "bun-types": "1.4.2",
@@ -299,18 +300,18 @@ var init_package = __esm(() => {
299
300
  typescript: "^7.0.2"
300
301
  },
301
302
  optionalDependencies: {
302
- "oh-my-opencode-darwin-arm64": "5.1.29",
303
- "oh-my-opencode-darwin-x64": "5.1.29",
304
- "oh-my-opencode-darwin-x64-baseline": "5.1.29",
305
- "oh-my-opencode-linux-arm64": "5.1.29",
306
- "oh-my-opencode-linux-arm64-musl": "5.1.29",
307
- "oh-my-opencode-linux-x64": "5.1.29",
308
- "oh-my-opencode-linux-x64-baseline": "5.1.29",
309
- "oh-my-opencode-linux-x64-musl": "5.1.29",
310
- "oh-my-opencode-linux-x64-musl-baseline": "5.1.29",
311
- "oh-my-opencode-windows-arm64": "5.1.29",
312
- "oh-my-opencode-windows-x64": "5.1.29",
313
- "oh-my-opencode-windows-x64-baseline": "5.1.29"
303
+ "oh-my-opencode-darwin-arm64": "5.1.32",
304
+ "oh-my-opencode-darwin-x64": "5.1.32",
305
+ "oh-my-opencode-darwin-x64-baseline": "5.1.32",
306
+ "oh-my-opencode-linux-arm64": "5.1.32",
307
+ "oh-my-opencode-linux-arm64-musl": "5.1.32",
308
+ "oh-my-opencode-linux-x64": "5.1.32",
309
+ "oh-my-opencode-linux-x64-baseline": "5.1.32",
310
+ "oh-my-opencode-linux-x64-musl": "5.1.32",
311
+ "oh-my-opencode-linux-x64-musl-baseline": "5.1.32",
312
+ "oh-my-opencode-windows-arm64": "5.1.32",
313
+ "oh-my-opencode-windows-x64": "5.1.32",
314
+ "oh-my-opencode-windows-x64-baseline": "5.1.32"
314
315
  },
315
316
  overrides: {
316
317
  hono: "^4.13.8",
@@ -5083,6 +5084,9 @@ function resolveStdio(options) {
5083
5084
  }
5084
5085
  return [options.stdin ?? "ignore", options.stdout ?? "pipe", options.stderr ?? "inherit"];
5085
5086
  }
5087
+ function createBunSpawnOptions(options) {
5088
+ return { ...options, windowsHide: true };
5089
+ }
5086
5090
  function createNodeSpawnOptions(options, platform = process.platform) {
5087
5091
  const nodeOptions = {
5088
5092
  stdio: resolveStdio(options),
@@ -5171,7 +5175,7 @@ function spawn(cmdOrOpts, opts) {
5171
5175
  const { cmd, opts: options } = resolveCommand(cmdOrOpts, opts);
5172
5176
  const bun = getBunRuntime();
5173
5177
  if (bun)
5174
- return wrapBunProcess(bun.spawn(cmd, options));
5178
+ return wrapBunProcess(bun.spawn(cmd, createBunSpawnOptions(options)));
5175
5179
  const [bin, ...args] = cmd;
5176
5180
  if (!bin)
5177
5181
  throw new Error("spawn requires a command");
@@ -5316,7 +5320,7 @@ function isKnownNonGitBashLauncher(path) {
5316
5320
  }
5317
5321
  function whereCommand(command) {
5318
5322
  try {
5319
- return execFileSync("where", [command], { encoding: "utf8" }).split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
5323
+ return execFileSync("where", [command], { encoding: "utf8", windowsHide: true }).split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
5320
5324
  } catch (error) {
5321
5325
  if (error instanceof Error)
5322
5326
  return [];
@@ -5759,7 +5763,8 @@ function defaultVersionProbe(binaryPath) {
5759
5763
  return String(execFileSync2(binaryPath, ["--version"], {
5760
5764
  encoding: "utf8",
5761
5765
  stdio: ["ignore", "pipe", "ignore"],
5762
- timeout: SG_VERSION_PROBE_TIMEOUT_MS
5766
+ timeout: SG_VERSION_PROBE_TIMEOUT_MS,
5767
+ windowsHide: true
5763
5768
  }));
5764
5769
  }
5765
5770
  function probePasses(binaryPath, deps) {
@@ -7462,10 +7467,11 @@ var init_category_model_requirements = __esm(() => {
7462
7467
  },
7463
7468
  { providers: ["xiaomi", "opencode-go"], model: "mimo-v2.6-pro", variant: "max" },
7464
7469
  { providers: ["xai", "github-copilot", "opencode-go"], model: "grok-4.7", variant: "xhigh" },
7470
+ { providers: ["openai", "chatgpt-subscription"], model: "gpt-6.1-sol", variant: "medium" },
7465
7471
  {
7466
7472
  providers: ["openai", "chatgpt-subscription", "github-copilot", "opencode"],
7467
- model: "gpt-5.6-terra",
7468
- variant: "high"
7473
+ model: "gpt-5.6-sol",
7474
+ variant: "medium"
7469
7475
  },
7470
7476
  {
7471
7477
  providers: ["anthropic", "anthropic-api", "github-copilot", "opencode"],
@@ -74743,7 +74749,8 @@ var init_memory = __esm(() => {
74743
74749
  }).strict();
74744
74750
  OmoMemoryFactsSchema = object({
74745
74751
  enabled: boolean2().default(true),
74746
- debounce_settles: number2().int().min(1).default(4)
74752
+ debounce_settles: number2().int().min(1).default(4),
74753
+ model: string2().min(1).optional()
74747
74754
  }).strict();
74748
74755
  OmoMemoryDreamSchema = object({
74749
74756
  enabled: boolean2().default(true),
@@ -74805,7 +74812,8 @@ var init_memory = __esm(() => {
74805
74812
  }).strict();
74806
74813
  OmoMemoryFactsLayerSchema = object({
74807
74814
  enabled: boolean2().optional(),
74808
- debounce_settles: number2().int().min(1).optional()
74815
+ debounce_settles: number2().int().min(1).optional(),
74816
+ model: string2().min(1).optional()
74809
74817
  }).strict();
74810
74818
  OmoMemoryDreamLayerSchema = object({
74811
74819
  enabled: boolean2().optional(),
@@ -89894,7 +89902,7 @@ var package_default2;
89894
89902
  var init_package2 = __esm(() => {
89895
89903
  package_default2 = {
89896
89904
  name: "@oh-my-opencode/omo-codex",
89897
- version: "5.1.29",
89905
+ version: "5.1.32",
89898
89906
  type: "module",
89899
89907
  private: true,
89900
89908
  description: "Codex harness adapter for oh-my-openagent. Vendored Codex plugin namespace (omo) + TypeScript installer + telemetry.",
@@ -110304,6 +110312,921 @@ function createMcpOAuthCommand() {
110304
110312
  return mcp;
110305
110313
  }
110306
110314
 
110315
+ // packages/omo-opencode/src/cli/service-auth/browser.ts
110316
+ import { execFile as execFile5 } from "child_process";
110317
+ var execute = (file, args, env) => new Promise((resolve) => {
110318
+ execFile5(file, [...args], { env, timeout: 1e4, windowsHide: true }, (error) => resolve(error === null));
110319
+ });
110320
+ async function openSignInBrowser(url, executeBrowser = execute, platform = process.platform, environment = process.env) {
110321
+ const env = {};
110322
+ for (const key of ["PATH", "Path", "HOME", "USERPROFILE", "SystemRoot", "WINDIR", "DISPLAY", "WAYLAND_DISPLAY", "XDG_RUNTIME_DIR", "DBUS_SESSION_BUS_ADDRESS"]) {
110323
+ const value = environment[key];
110324
+ if (value !== undefined)
110325
+ env[key] = value;
110326
+ }
110327
+ if (platform === "darwin")
110328
+ return executeBrowser("/usr/bin/open", [url], env);
110329
+ if (platform === "win32")
110330
+ return executeBrowser("rundll32.exe", ["url.dll,FileProtocolHandler", url], env);
110331
+ if (platform === "linux" && (env.DISPLAY || env.WAYLAND_DISPLAY))
110332
+ return executeBrowser("xdg-open", [url], env);
110333
+ return false;
110334
+ }
110335
+
110336
+ // packages/omo-opencode/src/cli/service-auth/device-name.ts
110337
+ import { hostname } from "os";
110338
+
110339
+ // packages/omo-opencode/src/cli/service-auth/protocol.ts
110340
+ init_zod();
110341
+
110342
+ // packages/omo-opencode/src/cli/service-auth/terminal-text.ts
110343
+ import { stripVTControlCharacters as stripVTControlCharacters4 } from "util";
110344
+ function terminalText(value) {
110345
+ return stripVTControlCharacters4(value).replace(/[\p{C}\p{Zl}\p{Zp}]/gu, "");
110346
+ }
110347
+
110348
+ // packages/omo-opencode/src/cli/service-auth/protocol.ts
110349
+ class SignInError extends Error {
110350
+ retryable;
110351
+ name = "SignInError";
110352
+ constructor(message, retryable = false) {
110353
+ super(message);
110354
+ this.retryable = retryable;
110355
+ }
110356
+ }
110357
+
110358
+ class RequestFailure extends SignInError {
110359
+ notSent;
110360
+ constructor(notSent) {
110361
+ super("Unable to complete the service request. Check the connection and retry.", true);
110362
+ this.notSent = notSent;
110363
+ }
110364
+ }
110365
+ var platformSchema = _enum(["macos", "linux", "windows"]);
110366
+ var nonempty = string2().min(1);
110367
+ var timestamp = string2().datetime({ offset: true });
110368
+ var tokenSchema = object({
110369
+ accessToken: nonempty,
110370
+ accessTokenExpiresAt: timestamp,
110371
+ refreshToken: nonempty
110372
+ });
110373
+ var grantSchema = tokenSchema.extend({
110374
+ device: object({ id: nonempty, name: nonempty }),
110375
+ offlineGrant: nonempty.optional()
110376
+ });
110377
+ var credentialSchema = grantSchema.extend({
110378
+ signingPrivateKey: nonempty,
110379
+ sealingPrivateKey: nonempty,
110380
+ refreshState: literal("uncertain").optional()
110381
+ });
110382
+ var deviceCodeSchema = object({
110383
+ deviceCode: string2().regex(/^dc\.[A-Za-z0-9_-]{43}$/),
110384
+ userCode: string2().regex(/^[BCDFGHJKLMNPQRSTVWXZ]{4}-[BCDFGHJKLMNPQRSTVWXZ]{4}$/),
110385
+ matchingCode: string2().regex(/^\d{3} \d{3}$/),
110386
+ verificationUri: string2().transform(terminalText).pipe(string2().url()),
110387
+ expiresAt: timestamp,
110388
+ intervalSeconds: number2().int().min(1).max(60)
110389
+ });
110390
+ var envelopeSchema = object({
110391
+ error: object({
110392
+ code: nonempty,
110393
+ message: string2(),
110394
+ retryable: boolean2(),
110395
+ requestId: string2(),
110396
+ detail: unknown().optional()
110397
+ })
110398
+ });
110399
+ var limitSchema = object({
110400
+ reason: literal("device_limit"),
110401
+ managementToken: nonempty,
110402
+ used: number2().int().nonnegative(),
110403
+ limit: number2().int().nonnegative()
110404
+ });
110405
+ var deviceListSchema = object({
110406
+ devices: array(object({ id: nonempty, name: nonempty, revokedAt: timestamp.nullable() }))
110407
+ });
110408
+
110409
+ class ApiRefusal extends SignInError {
110410
+ status;
110411
+ code;
110412
+ #detail;
110413
+ #retryAfter;
110414
+ constructor(code, status, detail, retryAfter = null) {
110415
+ super("The service refused this request. Sign in again or check your account.", status === 429 || status >= 500);
110416
+ this.status = status;
110417
+ this.code = [
110418
+ "unauthorized",
110419
+ "account_deleted",
110420
+ "reauth_required",
110421
+ "invalid_grant",
110422
+ "entitlement_required",
110423
+ "authorization_pending",
110424
+ "slow_down",
110425
+ "access_denied",
110426
+ "expired_token",
110427
+ "rate_limited",
110428
+ "internal",
110429
+ "unavailable",
110430
+ "forbidden",
110431
+ "invalid_request",
110432
+ "not_found"
110433
+ ].includes(code) ? code : "unknown";
110434
+ this.#detail = detail;
110435
+ this.#retryAfter = retryAfter;
110436
+ }
110437
+ get detail() {
110438
+ return this.#detail;
110439
+ }
110440
+ retryDelay(now, fallback) {
110441
+ const raw = this.#retryAfter;
110442
+ const delay = raw === null ? Number.NaN : /^\d+$/.test(raw) ? Number(raw) * 1000 : Date.parse(raw) - now;
110443
+ return Math.min(60000, Math.max(fallback, Number.isFinite(delay) ? delay : fallback));
110444
+ }
110445
+ }
110446
+ function parseReply(schema, value) {
110447
+ const result = schema.safeParse(value);
110448
+ if (!result.success)
110449
+ throw new SignInError("The service returned an invalid response.");
110450
+ return result.data;
110451
+ }
110452
+ function serviceOrigin(value) {
110453
+ let url;
110454
+ try {
110455
+ url = new URL(value);
110456
+ } catch {
110457
+ throw new SignInError("Invalid service URL.");
110458
+ }
110459
+ const loopback = url.protocol === "http:" && url.hostname === "127.0.0.1" && url.port !== "";
110460
+ if (url.protocol !== "https:" && !loopback || url.username || url.password || url.search || url.hash || url.pathname !== "/") {
110461
+ throw new SignInError("Service URLs must be HTTPS origins, or http://127.0.0.1:<port> for local testing.");
110462
+ }
110463
+ return url.origin;
110464
+ }
110465
+ async function requestApi(api, path, init, signal) {
110466
+ let response;
110467
+ let body;
110468
+ try {
110469
+ response = await fetch(`${api}${path}`, {
110470
+ ...init,
110471
+ redirect: "error",
110472
+ signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(30000)]) : AbortSignal.timeout(30000)
110473
+ });
110474
+ } catch (error) {
110475
+ const cause = error instanceof Error && error.cause instanceof Error ? error.cause : error;
110476
+ const code = cause instanceof Error ? Reflect.get(cause, "code") : undefined;
110477
+ throw new RequestFailure(typeof code === "string" && [
110478
+ "ENOTFOUND",
110479
+ "ECONNREFUSED",
110480
+ "ENETUNREACH",
110481
+ "EHOSTUNREACH",
110482
+ "EAI_AGAIN",
110483
+ "ConnectionRefused",
110484
+ "EADDRNOTAVAIL",
110485
+ "ENETDOWN",
110486
+ "EHOSTDOWN",
110487
+ "UND_ERR_CONNECT_TIMEOUT",
110488
+ "ERR_TLS_HANDSHAKE_TIMEOUT",
110489
+ "DEPTH_ZERO_SELF_SIGNED_CERT",
110490
+ "CERT_HAS_EXPIRED",
110491
+ "ERR_TLS_CERT_ALTNAME_INVALID",
110492
+ "UNABLE_TO_VERIFY_LEAF_SIGNATURE",
110493
+ "SELF_SIGNED_CERT_IN_CHAIN",
110494
+ "CERT_NOT_YET_VALID",
110495
+ "UNABLE_TO_GET_ISSUER_CERT",
110496
+ "UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
110497
+ "CERT_SIGNATURE_FAILURE",
110498
+ "CERT_UNTRUSTED",
110499
+ "CERT_REJECTED"
110500
+ ].includes(code));
110501
+ }
110502
+ if (response.status === 204)
110503
+ return null;
110504
+ try {
110505
+ body = await response.json();
110506
+ } catch {
110507
+ throw new RequestFailure(false);
110508
+ }
110509
+ if (response.ok)
110510
+ return body;
110511
+ const envelope = envelopeSchema.safeParse(body);
110512
+ if (!envelope.success)
110513
+ throw new SignInError("The service returned an invalid error response.");
110514
+ const error = envelope.data.error;
110515
+ const detail = limitSchema.safeParse(error.detail);
110516
+ throw new ApiRefusal(error.code, response.status, error.code === "entitlement_required" && detail.success ? detail.data : undefined, response.headers.get("retry-after"));
110517
+ }
110518
+ var jsonRequest = (body) => ({
110519
+ method: "POST",
110520
+ headers: { "content-type": "application/json" },
110521
+ body: JSON.stringify(body)
110522
+ });
110523
+
110524
+ // packages/omo-opencode/src/cli/service-auth/device-name.ts
110525
+ function deviceName(host = hostname()) {
110526
+ const clean = `omo CLI on ${host}`.replace(/[\p{Cc}\p{Zl}\p{Zp}\p{Cf}\u3164\u115F\u1160\uFFA0]/gu, "").trim();
110527
+ return [...clean].slice(0, 64).join("").trim();
110528
+ }
110529
+ function devicePlatform() {
110530
+ switch (process.platform) {
110531
+ case "darwin":
110532
+ return "macos";
110533
+ case "linux":
110534
+ return "linux";
110535
+ case "win32":
110536
+ return "windows";
110537
+ default:
110538
+ throw new SignInError("This platform is not supported for OmO sign-in.");
110539
+ }
110540
+ }
110541
+
110542
+ // packages/omo-opencode/src/cli/service-auth/device-flow.ts
110543
+ import { setTimeout as setTimeout2 } from "timers/promises";
110544
+
110545
+ // packages/omo-opencode/src/cli/service-auth/device-keys.ts
110546
+ import { generateKeyPairSync } from "crypto";
110547
+ function generateDeviceKeys() {
110548
+ const signing = generateKeyPairSync("ed25519");
110549
+ const sealing = generateKeyPairSync("x25519");
110550
+ const signingKey = signing.publicKey.export({ format: "jwk" }).x;
110551
+ const sealingKey = sealing.publicKey.export({ format: "jwk" }).x;
110552
+ if (!signingKey || !sealingKey)
110553
+ throw new SignInError("Unable to generate device keys.");
110554
+ return {
110555
+ public: { signingKey, sealingKey },
110556
+ private: {
110557
+ signingPrivateKey: signing.privateKey.export({ format: "der", type: "pkcs8" }).toString("base64"),
110558
+ sealingPrivateKey: sealing.privateKey.export({ format: "der", type: "pkcs8" }).toString("base64")
110559
+ }
110560
+ };
110561
+ }
110562
+
110563
+ // packages/omo-opencode/src/cli/service-auth/device-flow.ts
110564
+ async function loginDevice(session, options) {
110565
+ await session.ready();
110566
+ const keys = generateDeviceKeys();
110567
+ const minted = parseReply(deviceCodeSchema, await session.request("/v1/device/code", jsonRequest({ client: { name: options.name, platform: options.platform } })));
110568
+ const uri = new URL(minted.verificationUri);
110569
+ if (uri.protocol !== "https:" && !(uri.protocol === "http:" && uri.hostname === "127.0.0.1" && uri.port !== "")) {
110570
+ throw new SignInError("The service returned an unsafe verification URL.");
110571
+ }
110572
+ options.show({ verificationUri: minted.verificationUri, userCode: minted.userCode, matchingCode: minted.matchingCode });
110573
+ const now = options.now ?? Date.now;
110574
+ const sleep = options.sleep ?? ((ms) => setTimeout2(ms, undefined, { signal: session.signal }));
110575
+ const expiresAt = Date.parse(minted.expiresAt);
110576
+ let interval = minted.intervalSeconds * 1000;
110577
+ while (now() < expiresAt) {
110578
+ await sleep(Math.min(interval, expiresAt - now()));
110579
+ session.signal?.throwIfAborted();
110580
+ if (now() >= expiresAt)
110581
+ break;
110582
+ try {
110583
+ const grant = parseReply(grantSchema, await session.request("/v1/device/token", jsonRequest({
110584
+ deviceCode: minted.deviceCode,
110585
+ ...keys.public
110586
+ })));
110587
+ await session.save({ ...grant, ...keys.private });
110588
+ return;
110589
+ } catch (error) {
110590
+ if (!(error instanceof ApiRefusal))
110591
+ throw error;
110592
+ if (error.status === 400 && error.code === "authorization_pending")
110593
+ continue;
110594
+ if (error.status === 400 && error.code === "slow_down") {
110595
+ interval += 5000;
110596
+ continue;
110597
+ }
110598
+ if (error.status === 429) {
110599
+ interval = error.retryDelay(now(), Math.min(60000, interval + 5000));
110600
+ continue;
110601
+ }
110602
+ if (error.detail !== undefined) {
110603
+ if (await session.removeForLimit(error, options.chooseDevice))
110604
+ continue;
110605
+ throw new SignInError("Sign-in cancelled. No device was removed.");
110606
+ }
110607
+ if (["access_denied", "expired_token", "invalid_grant"].includes(error.code)) {
110608
+ throw new SignInError("Device sign-in was denied or expired. Run omo login again for a new code.");
110609
+ }
110610
+ throw error;
110611
+ }
110612
+ }
110613
+ throw new SignInError("Device sign-in expired. Run omo login again for a new code.");
110614
+ }
110615
+
110616
+ // packages/omo-opencode/src/cli/service-auth/keystore.ts
110617
+ import { createHash as createHash9 } from "crypto";
110618
+ function credentialId(api) {
110619
+ return createHash9("sha256").update(serviceOrigin(api)).digest("hex");
110620
+ }
110621
+ function createCredentialStore(api, secrets = typeof Bun === "undefined" ? null : Bun.secrets, platform = process.platform) {
110622
+ if (!secrets)
110623
+ throw new SignInError("OmO sign-in needs Bun 1.4 or newer with OS credential storage. No file fallback is available.");
110624
+ const platformName = platform === "darwin" ? "macOS Keychain" : platform === "linux" ? "Linux Secret Service (libsecret and an unlocked keyring)" : platform === "win32" ? "Windows Credential Manager" : null;
110625
+ if (platformName === null)
110626
+ throw new SignInError("This platform has no supported OS credential store.");
110627
+ const key = { service: "omo-cli", name: credentialId(api) };
110628
+ const unavailable = () => new SignInError(`Cannot access ${platformName}. Unlock or enable the OS credential store and try again. Credentials are never saved to a file.`);
110629
+ return {
110630
+ async read() {
110631
+ let value;
110632
+ try {
110633
+ value = await secrets.get(key);
110634
+ } catch {
110635
+ throw unavailable();
110636
+ }
110637
+ if (value === null)
110638
+ return null;
110639
+ try {
110640
+ return credentialSchema.parse(JSON.parse(value));
110641
+ } catch {
110642
+ throw new SignInError("Stored sign-in data is invalid. Run omo logout, then omo login.");
110643
+ }
110644
+ },
110645
+ async write(value) {
110646
+ try {
110647
+ await secrets.set({ ...key, value: JSON.stringify(credentialSchema.parse(value)) });
110648
+ } catch {
110649
+ throw unavailable();
110650
+ }
110651
+ },
110652
+ async clear() {
110653
+ try {
110654
+ await secrets.delete(key);
110655
+ } catch {
110656
+ throw unavailable();
110657
+ }
110658
+ }
110659
+ };
110660
+ }
110661
+
110662
+ // packages/omo-opencode/src/cli/service-auth/loopback.ts
110663
+ import { createHash as createHash10, randomBytes as randomBytes3, timingSafeEqual } from "crypto";
110664
+ import { createServer as createServer3 } from "http";
110665
+ class BrowserUnavailable extends SignInError {
110666
+ }
110667
+ async function authorize(options) {
110668
+ const verifier = randomBytes3(32).toString("base64url");
110669
+ const state = randomBytes3(32).toString("base64url");
110670
+ const answer = Promise.withResolvers();
110671
+ const abort = () => answer.reject(new SignInError("Browser sign-in cancelled or timed out."));
110672
+ let accepted = false;
110673
+ let origin = "";
110674
+ const server = createServer3((request, response) => {
110675
+ response.setHeader("cache-control", "no-store");
110676
+ response.setHeader("referrer-policy", "no-referrer");
110677
+ response.setHeader("content-type", "text/plain; charset=utf-8");
110678
+ const url = new URL(request.url ?? "/", origin);
110679
+ if (request.method !== "GET" || request.headers.host !== new URL(origin).host || url.origin !== origin || url.pathname !== "/callback" || request.headers.forwarded || request.headers["x-forwarded-for"]) {
110680
+ response.writeHead(404).end("Not found");
110681
+ return;
110682
+ }
110683
+ const code = url.searchParams.get("code");
110684
+ const suppliedState = Buffer.from(url.searchParams.get("state") ?? "");
110685
+ const expectedState = Buffer.from(state);
110686
+ if (accepted || url.searchParams.getAll("state").length !== 1 || suppliedState.length !== expectedState.length || !timingSafeEqual(suppliedState, expectedState) || url.searchParams.getAll("code").length !== 1 || !code) {
110687
+ response.writeHead(400).end("Invalid sign-in response");
110688
+ return;
110689
+ }
110690
+ accepted = true;
110691
+ response.writeHead(200).end("Return to the terminal to finish signing in.");
110692
+ answer.resolve(code);
110693
+ });
110694
+ try {
110695
+ await new Promise((resolve, reject) => {
110696
+ server.once("error", reject);
110697
+ server.listen(0, "127.0.0.1", () => {
110698
+ server.off("error", reject);
110699
+ resolve();
110700
+ });
110701
+ });
110702
+ const address = server.address();
110703
+ if (!address || typeof address === "string")
110704
+ throw new SignInError("Unable to start the sign-in callback.");
110705
+ origin = `http://127.0.0.1:${address.port}`;
110706
+ const url = new URL(serviceOrigin(options.accounts));
110707
+ url.search = new URLSearchParams({
110708
+ redirect_uri: `${origin}/callback`,
110709
+ code_challenge: createHash10("sha256").update(verifier).digest("base64url"),
110710
+ code_challenge_method: "S256",
110711
+ state
110712
+ }).toString();
110713
+ const timer = setTimeout(abort, 5 * 60000);
110714
+ options.signal?.addEventListener("abort", abort, { once: true });
110715
+ try {
110716
+ options.signal?.throwIfAborted();
110717
+ const openerFailed = () => {
110718
+ if (!accepted)
110719
+ throw new BrowserUnavailable("No browser is available; use device sign-in.");
110720
+ };
110721
+ const [code] = await Promise.all([
110722
+ answer.promise,
110723
+ options.open(url.href).then((opened) => {
110724
+ if (!opened)
110725
+ openerFailed();
110726
+ }, openerFailed)
110727
+ ]);
110728
+ return { code, codeVerifier: verifier };
110729
+ } finally {
110730
+ clearTimeout(timer);
110731
+ options.signal?.removeEventListener("abort", abort);
110732
+ }
110733
+ } finally {
110734
+ server.closeAllConnections();
110735
+ await new Promise((resolve) => server.close(() => resolve()));
110736
+ }
110737
+ }
110738
+ async function loginLoopback(session, options) {
110739
+ await session.ready();
110740
+ const keys = generateDeviceKeys();
110741
+ for (;; ) {
110742
+ const authorization = await authorize({ accounts: options.accounts, open: options.open, signal: session.signal });
110743
+ try {
110744
+ const grant = parseReply(grantSchema, await session.request("/v1/session/exchange", jsonRequest({
110745
+ ...authorization,
110746
+ kind: "cli",
110747
+ deviceName: options.name,
110748
+ platform: options.platform,
110749
+ ...keys.public
110750
+ })));
110751
+ await session.save({ ...grant, ...keys.private });
110752
+ return;
110753
+ } catch (error) {
110754
+ if (await session.removeForLimit(error, options.chooseDevice))
110755
+ continue;
110756
+ throw new SignInError("Sign-in cancelled. No device was removed.");
110757
+ }
110758
+ }
110759
+ }
110760
+
110761
+ // packages/omo-opencode/src/cli/service-auth/session.ts
110762
+ import { mkdir as mkdir10 } from "fs/promises";
110763
+ import { homedir as homedir27 } from "os";
110764
+ import { join as join97 } from "path";
110765
+
110766
+ // packages/team-core/src/team-state-store/lock-publish.ts
110767
+ import { access as access3, open as open2, unlink as unlink2 } from "fs/promises";
110768
+ import { dirname as dirname38 } from "path";
110769
+
110770
+ // packages/team-core/src/team-state-store/lock-record.ts
110771
+ import { randomUUID as randomUUID4 } from "crypto";
110772
+ import { readFile as readFile33, stat as stat8 } from "fs/promises";
110773
+ var lockOwnerInstanceId = randomUUID4();
110774
+ var heldLockRecords = new Set;
110775
+ function buildLockRecord(ownerTag) {
110776
+ return `${ownerTag} ${randomUUID4()}
110777
+ ${process.pid}
110778
+ ${Date.now()}
110779
+ ${lockOwnerInstanceId}
110780
+ `;
110781
+ }
110782
+ function markLockRecordHeld(content) {
110783
+ heldLockRecords.add(content);
110784
+ }
110785
+ function markLockRecordReleased(content) {
110786
+ heldLockRecords.delete(content);
110787
+ }
110788
+ function errorCode(error) {
110789
+ if (!(error instanceof Error) || !("code" in error))
110790
+ return null;
110791
+ return typeof error.code === "string" ? error.code : null;
110792
+ }
110793
+ function isPathAbsenceError(error) {
110794
+ const code = errorCode(error);
110795
+ return code === "ENOENT" || code === "ENOTDIR";
110796
+ }
110797
+ function parseLockOwner(content) {
110798
+ const lines = content.split(/\r?\n/).filter((line) => line.length > 0);
110799
+ if (lines.length !== 3 && lines.length !== 4)
110800
+ return null;
110801
+ const ownerPid = Number.parseInt(lines[1] ?? "", 10);
110802
+ const acquiredAtEpochMs = Number.parseInt(lines[2] ?? "", 10);
110803
+ if (!Number.isInteger(ownerPid) || ownerPid <= 0)
110804
+ return null;
110805
+ if (!Number.isInteger(acquiredAtEpochMs) || acquiredAtEpochMs <= 0)
110806
+ return null;
110807
+ return {
110808
+ ownerPid,
110809
+ acquiredAtEpochMs,
110810
+ instanceId: lines.length === 4 ? lines[3] ?? null : null
110811
+ };
110812
+ }
110813
+ function isPidAlive(pid) {
110814
+ try {
110815
+ process.kill(pid, 0);
110816
+ return true;
110817
+ } catch (error) {
110818
+ if (!(error instanceof Error)) {
110819
+ throw error;
110820
+ }
110821
+ return false;
110822
+ }
110823
+ }
110824
+ async function readLockRecord(lockPath) {
110825
+ try {
110826
+ const content = await readFile33(lockPath, "utf8");
110827
+ const { mtimeMs } = await stat8(lockPath);
110828
+ return { content, modifiedAtEpochMs: mtimeMs };
110829
+ } catch (error) {
110830
+ if (!(error instanceof Error))
110831
+ throw error;
110832
+ return null;
110833
+ }
110834
+ }
110835
+ function isLockRecordStale(record, staleAfterMs) {
110836
+ const owner = parseLockOwner(record.content);
110837
+ if (owner === null)
110838
+ return Date.now() - record.modifiedAtEpochMs > staleAfterMs;
110839
+ if (owner.instanceId !== null && owner.ownerPid === process.pid) {
110840
+ return owner.instanceId !== lockOwnerInstanceId || !heldLockRecords.has(record.content);
110841
+ }
110842
+ if (isPidAlive(owner.ownerPid))
110843
+ return false;
110844
+ if (owner.instanceId !== null)
110845
+ return true;
110846
+ return Date.now() - owner.acquiredAtEpochMs > staleAfterMs;
110847
+ }
110848
+
110849
+ // packages/team-core/src/team-state-store/lock-publish.ts
110850
+ async function pathMayExist(path, deps = {}) {
110851
+ const accessPath = deps.access ?? access3;
110852
+ try {
110853
+ await accessPath(path);
110854
+ return true;
110855
+ } catch (error) {
110856
+ if (!(error instanceof Error))
110857
+ throw error;
110858
+ return !isPathAbsenceError(error);
110859
+ }
110860
+ }
110861
+ async function assertRetryableLockOpenError(lockPath, error, deps) {
110862
+ const code = errorCode(error);
110863
+ if (code === "EEXIST")
110864
+ return;
110865
+ if (code === "EPERM") {
110866
+ if (await pathMayExist(lockPath, deps))
110867
+ return;
110868
+ if ((deps?.platform ?? process.platform) === "win32" && await pathMayExist(dirname38(lockPath), deps))
110869
+ return;
110870
+ }
110871
+ throw error;
110872
+ }
110873
+ async function publishLockRecord(lockPath, record) {
110874
+ markLockRecordHeld(record);
110875
+ let fileHandle;
110876
+ try {
110877
+ fileHandle = await open2(lockPath, "wx");
110878
+ } catch (error) {
110879
+ markLockRecordReleased(record);
110880
+ await assertRetryableLockOpenError(lockPath, error);
110881
+ return false;
110882
+ }
110883
+ try {
110884
+ await fileHandle.writeFile(record);
110885
+ } catch (error) {
110886
+ markLockRecordReleased(record);
110887
+ await fileHandle.close();
110888
+ await unlink2(lockPath).catch(() => {
110889
+ return;
110890
+ });
110891
+ throw error;
110892
+ }
110893
+ await fileHandle.close();
110894
+ return true;
110895
+ }
110896
+
110897
+ // packages/team-core/src/team-state-store/lock-reclaim.ts
110898
+ import { createHash as createHash11 } from "crypto";
110899
+ import { unlink as unlink3 } from "fs/promises";
110900
+ var RECLAIM_UNLINK_ATTEMPTS = 3;
110901
+ var RECLAIM_UNLINK_RETRY_MS = 25;
110902
+ function delay2(ms) {
110903
+ return new Promise((resolve) => {
110904
+ setTimeout(resolve, ms);
110905
+ });
110906
+ }
110907
+ function reclaimGuardPath(lockPath, record) {
110908
+ return `${lockPath}.reclaim-${createHash11("sha256").update(record).digest("hex").slice(0, 16)}`;
110909
+ }
110910
+ async function unlinkWithRetry(path, deps) {
110911
+ const wait = deps.delay ?? delay2;
110912
+ const unlinkFile = deps.unlink ?? unlink3;
110913
+ for (let attempt = 1;; attempt += 1) {
110914
+ try {
110915
+ await unlinkFile(path);
110916
+ return "removed";
110917
+ } catch (error) {
110918
+ if (!(error instanceof Error))
110919
+ throw error;
110920
+ if (isPathAbsenceError(error))
110921
+ return "absent";
110922
+ const code = errorCode(error);
110923
+ if (code !== "EPERM" && code !== "EBUSY" || attempt === RECLAIM_UNLINK_ATTEMPTS)
110924
+ return "stuck";
110925
+ await wait(RECLAIM_UNLINK_RETRY_MS);
110926
+ }
110927
+ }
110928
+ }
110929
+ async function removeLockRecordIfUnchanged(lockPath, expectedRecord, staleAfterMs, deps = {}) {
110930
+ const guardPath = reclaimGuardPath(lockPath, expectedRecord);
110931
+ const guardRecord = buildLockRecord("lock-reclaim");
110932
+ if (!await publishLockRecord(guardPath, guardRecord)) {
110933
+ const guard = await readLockRecord(guardPath);
110934
+ if (guard !== null && isLockRecordStale(guard, staleAfterMs)) {
110935
+ await removeLockRecordIfUnchanged(guardPath, guard.content, staleAfterMs, deps);
110936
+ }
110937
+ return "busy";
110938
+ }
110939
+ try {
110940
+ const current = await readLockRecord(lockPath);
110941
+ if (current === null)
110942
+ return "absent";
110943
+ if (current.content !== expectedRecord || !isLockRecordStale(current, staleAfterMs))
110944
+ return "changed";
110945
+ const unlinked = await unlinkWithRetry(lockPath, deps);
110946
+ return unlinked === "absent" ? "absent" : unlinked;
110947
+ } finally {
110948
+ markLockRecordReleased(guardRecord);
110949
+ await unlinkWithRetry(guardPath, deps);
110950
+ }
110951
+ }
110952
+
110953
+ // packages/team-core/src/team-state-store/locks.ts
110954
+ var LOCK_RETRY_MS = 50;
110955
+ var LOCK_WAIT_TIMEOUT_MS = 15000;
110956
+ var DEFAULT_STALE_AFTER_MS = 300000;
110957
+ function delay3(ms) {
110958
+ return new Promise((resolve) => {
110959
+ setTimeout(resolve, ms);
110960
+ });
110961
+ }
110962
+ async function acquireLock(lockPath, ownerTag, staleAfterMs) {
110963
+ const startedAt = Date.now();
110964
+ for (;; ) {
110965
+ if (Date.now() - startedAt > LOCK_WAIT_TIMEOUT_MS) {
110966
+ throw new Error(`Timed out acquiring lock: ${lockPath}`);
110967
+ }
110968
+ const record = buildLockRecord(ownerTag);
110969
+ if (await publishLockRecord(lockPath, record))
110970
+ return record;
110971
+ const outcome = await reapStaleLock(lockPath, staleAfterMs);
110972
+ if (outcome === "removed" || outcome === "absent")
110973
+ continue;
110974
+ await delay3(LOCK_RETRY_MS);
110975
+ }
110976
+ }
110977
+ async function withLock(lockPath, fn, opts) {
110978
+ const staleAfterMs = opts?.staleAfterMs ?? DEFAULT_STALE_AFTER_MS;
110979
+ const ownerTag = opts?.ownerTag ?? "owner";
110980
+ const record = await acquireLock(lockPath, ownerTag, staleAfterMs);
110981
+ try {
110982
+ return await fn();
110983
+ } finally {
110984
+ markLockRecordReleased(record);
110985
+ await removeLockRecordIfUnchanged(lockPath, record, staleAfterMs);
110986
+ }
110987
+ }
110988
+ async function reapStaleLock(lockPath, staleAfterMs = DEFAULT_STALE_AFTER_MS, deps = {}) {
110989
+ const observed = await readLockRecord(lockPath);
110990
+ if (observed === null)
110991
+ return "absent";
110992
+ if (!isLockRecordStale(observed, staleAfterMs))
110993
+ return "changed";
110994
+ return await removeLockRecordIfUnchanged(lockPath, observed.content, staleAfterMs, deps);
110995
+ }
110996
+
110997
+ // packages/omo-opencode/src/cli/service-auth/session.ts
110998
+ class SessionEnded extends SignInError {
110999
+ deviceRevoked;
111000
+ constructor(message, deviceRevoked) {
111001
+ super(message);
111002
+ this.deviceRevoked = deviceRevoked;
111003
+ }
111004
+ }
111005
+ var DEVICE_REVOKING_REFUSALS = new Set(["account_deleted", "reauth_required"]);
111006
+ function createSession(options) {
111007
+ const api = serviceOrigin(options.api);
111008
+ const { store, signal } = options;
111009
+ const directory = join97(options.home ?? homedir27(), ".omo", "session-locks");
111010
+ const lockPath = join97(directory, `${credentialId(api)}.lock`);
111011
+ async function locked(action) {
111012
+ await mkdir10(directory, { recursive: true, mode: 448 });
111013
+ signal?.throwIfAborted();
111014
+ return withLock(lockPath, async () => {
111015
+ signal?.throwIfAborted();
111016
+ return action();
111017
+ }, { ownerTag: "cli-session", staleAfterMs: Number.POSITIVE_INFINITY });
111018
+ }
111019
+ const request = (path, init) => requestApi(api, path, init, signal);
111020
+ const uncertain = () => new SignInError("The refresh outcome is unknown. Saved credentials and keys were retained, but this refresh token will not be sent again. Retry sign-in with omo login.", true);
111021
+ async function refreshedAccess(current) {
111022
+ if (current.refreshState === "uncertain")
111023
+ throw uncertain();
111024
+ if (Date.parse(current.accessTokenExpiresAt) > Date.now() + 30000)
111025
+ return current.accessToken;
111026
+ await store.write({ ...current, refreshState: "uncertain" });
111027
+ try {
111028
+ const tokens = parseReply(tokenSchema, await request("/v1/session/refresh", jsonRequest({ refreshToken: current.refreshToken })));
111029
+ await store.write({ ...current, ...tokens });
111030
+ return tokens.accessToken;
111031
+ } catch (error) {
111032
+ if (error instanceof ApiRefusal && error.status < 500 && ["unauthorized", "account_deleted", "reauth_required", "invalid_grant"].includes(error.code)) {
111033
+ await store.clear();
111034
+ throw new SessionEnded(error.code === "reauth_required" ? "This session was ended because its refresh token was reused. Sign in again with omo login." : "Sign in again with omo login.", DEVICE_REVOKING_REFUSALS.has(error.code));
111035
+ }
111036
+ if (error instanceof RequestFailure && error.notSent || error instanceof ApiRefusal && error.status === 429 && error.code === "rate_limited") {
111037
+ await store.write(current);
111038
+ throw error;
111039
+ }
111040
+ throw uncertain();
111041
+ }
111042
+ }
111043
+ return {
111044
+ request,
111045
+ signal,
111046
+ async ready() {
111047
+ await store.read();
111048
+ },
111049
+ async save(value) {
111050
+ await locked(() => store.write(value));
111051
+ },
111052
+ async logout() {
111053
+ return locked(async () => {
111054
+ try {
111055
+ const current = await store.read();
111056
+ if (current !== null) {
111057
+ if (current.refreshState === "uncertain")
111058
+ return false;
111059
+ const access = Date.parse(current.accessTokenExpiresAt) <= Date.now() ? await refreshedAccess(current) : current.accessToken;
111060
+ await request(`/v1/devices/${encodeURIComponent(current.device.id)}`, {
111061
+ method: "DELETE",
111062
+ headers: { authorization: `Bearer ${access}` }
111063
+ });
111064
+ }
111065
+ return true;
111066
+ } catch (error) {
111067
+ if (error instanceof SessionEnded)
111068
+ return error.deviceRevoked;
111069
+ if (error instanceof SignInError)
111070
+ return false;
111071
+ throw error;
111072
+ } finally {
111073
+ await store.clear();
111074
+ }
111075
+ });
111076
+ },
111077
+ async accessToken() {
111078
+ return locked(async () => {
111079
+ const current = await store.read();
111080
+ if (current === null)
111081
+ throw new SignInError("Not signed in. Run omo login.");
111082
+ return refreshedAccess(current);
111083
+ });
111084
+ },
111085
+ async removeForLimit(error, choose) {
111086
+ if (!(error instanceof ApiRefusal) || error.detail === undefined)
111087
+ throw error;
111088
+ const headers = { authorization: `Bearer ${error.detail.managementToken}` };
111089
+ const listed = parseReply(deviceListSchema, await request("/v1/devices", { headers }));
111090
+ const devices = listed.devices.filter((device) => device.revokedAt === null);
111091
+ const chosen = await choose(devices);
111092
+ if (chosen === null)
111093
+ return false;
111094
+ if (!devices.some((device) => device.id === chosen))
111095
+ throw new SignInError("No matching device was selected.");
111096
+ await request(`/v1/devices/${encodeURIComponent(chosen)}`, { method: "DELETE", headers });
111097
+ return true;
111098
+ }
111099
+ };
111100
+ }
111101
+
111102
+ // packages/omo-opencode/src/cli/service-auth/commands.ts
111103
+ async function ensureServiceAvailable(session, api) {
111104
+ try {
111105
+ await session.request("/v1/health", { method: "GET" });
111106
+ } catch (error) {
111107
+ if (!(error instanceof SignInError))
111108
+ throw error;
111109
+ if (error instanceof RequestFailure) {
111110
+ throw new SignInError(`Can't reach OmO accounts at ${api}. The service may not be available yet, or your network, proxy or certificate settings are blocking it.`);
111111
+ }
111112
+ throw new SignInError(`OmO accounts at ${api} aren't available right now. Try again later.`);
111113
+ }
111114
+ }
111115
+ function deviceChoices(devices) {
111116
+ return devices.map((device) => ({ value: device.id, label: terminalText(device.name) }));
111117
+ }
111118
+ var chooseDevice = async (devices) => {
111119
+ if (!process.stdin.isTTY || devices.length === 0) {
111120
+ console.error("Device limit reached. Run omo login in an interactive terminal to choose a device to remove.");
111121
+ return null;
111122
+ }
111123
+ const chosen = await select({
111124
+ message: "Device limit reached. Remove a device to sign in?",
111125
+ options: [{ value: "", label: "Cancel (keep all devices)" }, ...deviceChoices(devices)]
111126
+ });
111127
+ if (isCancel(chosen) || chosen === "")
111128
+ return null;
111129
+ const agreed = await confirm({ message: "Sign out the selected device?", initialValue: false });
111130
+ return agreed === true ? chosen : null;
111131
+ };
111132
+ var DEFAULT_ACCOUNTS_ORIGIN = "https://signin.omo.dev";
111133
+ function accountsOrigin(options, env) {
111134
+ return serviceOrigin(options.accounts ?? env.OMO_SERVICE_ACCOUNTS_URL ?? DEFAULT_ACCOUNTS_ORIGIN);
111135
+ }
111136
+ async function run2(action, options) {
111137
+ const controller = new AbortController;
111138
+ const cancel = () => controller.abort();
111139
+ process.once("SIGINT", cancel);
111140
+ process.once("SIGTERM", cancel);
111141
+ try {
111142
+ const api = serviceOrigin(options.api ?? process.env.OMO_SERVICE_API_URL ?? "https://api.omo.dev");
111143
+ const store = createCredentialStore(api);
111144
+ const session = createSession({ api, store, signal: controller.signal });
111145
+ if (action === "logout") {
111146
+ const revoked = await session.logout();
111147
+ console.log("Signed out of OmO. Stored credentials and device keys were removed.");
111148
+ if (!revoked)
111149
+ console.error("Server device revocation could not be confirmed. Remove this device from the Devices page of your OmO account when connected.");
111150
+ return;
111151
+ }
111152
+ if (action === "whoami") {
111153
+ await session.accessToken();
111154
+ const saved = await store.read();
111155
+ console.log(saved ? `Signed in to ${api} as ${terminalText(saved.device.name)}.` : "Not signed in.");
111156
+ return;
111157
+ }
111158
+ await ensureServiceAvailable(session, api);
111159
+ const name = deviceName();
111160
+ const platform = devicePlatform();
111161
+ if (!options.device && options.browser !== false) {
111162
+ try {
111163
+ await loginLoopback(session, {
111164
+ accounts: accountsOrigin(options, process.env),
111165
+ name,
111166
+ platform,
111167
+ open: openSignInBrowser,
111168
+ chooseDevice
111169
+ });
111170
+ console.log("Signed in to OmO.");
111171
+ return;
111172
+ } catch (error) {
111173
+ if (!(error instanceof BrowserUnavailable))
111174
+ throw error;
111175
+ }
111176
+ }
111177
+ await loginDevice(session, {
111178
+ name,
111179
+ platform,
111180
+ chooseDevice,
111181
+ show: (code) => {
111182
+ console.log(`Open ${code.verificationUri}`);
111183
+ console.log(`User code: ${code.userCode}`);
111184
+ console.log(`Matching code: ${code.matchingCode}`);
111185
+ }
111186
+ });
111187
+ console.log("Signed in to OmO.");
111188
+ } catch (error) {
111189
+ console.error(controller.signal.aborted ? "Sign-in cancelled." : error instanceof SignInError ? error.message : "Unable to complete OmO sign-in. Check the OS credential store and try again.");
111190
+ process.exitCode = 1;
111191
+ } finally {
111192
+ process.removeListener("SIGINT", cancel);
111193
+ process.removeListener("SIGTERM", cancel);
111194
+ }
111195
+ }
111196
+ function configureServiceAuthCommands(program) {
111197
+ program.command("login").description("Sign in to OmO using the OS credential store").option("--device", "Use a device code instead of a browser callback").option("--no-browser", "Sign in by device code without opening a browser").option("--api <url>", "OmO API origin (or OMO_SERVICE_API_URL)").option("--accounts <url>", "OmO sign-in page origin (or OMO_SERVICE_ACCOUNTS_URL)").action((options) => run2("login", options));
111198
+ for (const action of ["logout", "whoami"]) {
111199
+ program.command(action).description(action === "logout" ? "Remove stored OmO credentials and device keys" : "Show the current OmO device sign-in").option("--api <url>", "OmO API origin (or OMO_SERVICE_API_URL)").action((options) => run2(action, options));
111200
+ }
111201
+ }
111202
+
111203
+ // packages/omo-opencode/src/cli/install-command-options.ts
111204
+ function resolveInstallArgs(options, invocationName = process.env.OMO_INVOCATION_NAME) {
111205
+ const defaultPlatform = process.env.OMO_EDITION === "codex" || invocationName === "lazycodex" || invocationName === "lazycodex-ai" ? "codex" : undefined;
111206
+ const platform = options.platform ?? defaultPlatform;
111207
+ if (platform === "native-dev" && !isNativeDevPlatformEnabled()) {
111208
+ throw new Error(`The native-dev install platform is not available in this release. Set ${NATIVE_DEV_PLATFORM_ENV_FLAG}=1 to enable it from a source checkout.`);
111209
+ }
111210
+ return {
111211
+ tui: options.tui !== false,
111212
+ claude: options.claude,
111213
+ openai: options.openai,
111214
+ gemini: options.gemini,
111215
+ copilot: options.copilot,
111216
+ platform,
111217
+ opencodeZen: options.opencodeZen,
111218
+ zaiCodingPlan: options.zaiCodingPlan,
111219
+ kimiForCoding: options.kimiForCoding,
111220
+ opencodeGo: options.opencodeGo,
111221
+ bailianCodingPlan: options.bailianCodingPlan,
111222
+ minimaxCnCodingPlan: options.minimaxCnCodingPlan,
111223
+ minimaxCodingPlan: options.minimaxCodingPlan,
111224
+ vercelAiGateway: options.vercelAiGateway,
111225
+ codexAutonomous: options.codexAutonomous,
111226
+ skipAuth: options.skipAuth ?? false
111227
+ };
111228
+ }
111229
+
110307
111230
  // packages/omo-opencode/src/cli/boulder/boulder.ts
110308
111231
  import { existsSync as existsSync64 } from "fs";
110309
111232
 
@@ -110464,11 +111387,11 @@ async function boulder(options) {
110464
111387
  // packages/omo-opencode/src/cli/codex-ulw-loop.ts
110465
111388
  import { spawn as spawn5 } from "child_process";
110466
111389
  import { existsSync as existsSync65, readFileSync as readFileSync43, realpathSync as realpathSync11 } from "fs";
110467
- import { homedir as homedir27 } from "os";
111390
+ import { homedir as homedir28 } from "os";
110468
111391
  var ULW_LOOP_DELEGATION_SENTINEL = "OMO_ULW_LOOP_DELEGATED";
110469
111392
  function resolveCodexUlwLoopCommand(input = {}) {
110470
111393
  const env = input.env ?? process.env;
110471
- const homeDir = input.homeDir ?? homedir27();
111394
+ const homeDir = input.homeDir ?? homedir28();
110472
111395
  const localComponentBin = resolveLocalUlwLoopBin(env, homeDir);
110473
111396
  if (localComponentBin !== null)
110474
111397
  return { executable: localComponentBin, argsPrefix: [] };
@@ -110709,7 +111632,7 @@ function linkedWorktrees(records) {
110709
111632
  }
110710
111633
  // packages/omo-opencode/src/cli/worktree-sweep/sweep.ts
110711
111634
  import { existsSync as existsSync66 } from "fs";
110712
- import { stat as stat8 } from "fs/promises";
111635
+ import { stat as stat9 } from "fs/promises";
110713
111636
  import os7 from "os";
110714
111637
  import { normalize as normalize2 } from "path";
110715
111638
 
@@ -110792,7 +111715,7 @@ async function isOlderThan(worktreePath, days, now) {
110792
111715
  if (days <= 0)
110793
111716
  return false;
110794
111717
  try {
110795
- const stats = await stat8(worktreePath);
111718
+ const stats = await stat9(worktreePath);
110796
111719
  return now - stats.mtimeMs > days * DAY_MS;
110797
111720
  } catch {
110798
111721
  return false;
@@ -111009,31 +111932,6 @@ function runConfigMigrate(options = {}) {
111009
111932
  init_package();
111010
111933
  var VERSION3 = package_default.version;
111011
111934
  var program2 = new Command;
111012
- function resolveInstallArgs(options, invocationName = process.env.OMO_INVOCATION_NAME) {
111013
- const defaultPlatform = process.env.OMO_EDITION === "codex" || invocationName === "lazycodex" || invocationName === "lazycodex-ai" ? "codex" : undefined;
111014
- const platform = options.platform ?? defaultPlatform;
111015
- if (platform === "native-dev" && !isNativeDevPlatformEnabled()) {
111016
- throw new Error(`The native-dev install platform is not available in this release. Set ${NATIVE_DEV_PLATFORM_ENV_FLAG}=1 to enable it from a source checkout.`);
111017
- }
111018
- return {
111019
- tui: options.tui !== false,
111020
- claude: options.claude,
111021
- openai: options.openai,
111022
- gemini: options.gemini,
111023
- copilot: options.copilot,
111024
- platform,
111025
- opencodeZen: options.opencodeZen,
111026
- zaiCodingPlan: options.zaiCodingPlan,
111027
- kimiForCoding: options.kimiForCoding,
111028
- opencodeGo: options.opencodeGo,
111029
- bailianCodingPlan: options.bailianCodingPlan,
111030
- minimaxCnCodingPlan: options.minimaxCnCodingPlan,
111031
- minimaxCodingPlan: options.minimaxCodingPlan,
111032
- vercelAiGateway: options.vercelAiGateway,
111033
- codexAutonomous: options.codexAutonomous,
111034
- skipAuth: options.skipAuth ?? false
111035
- };
111036
- }
111037
111935
  program2.name("oh-my-opencode").description("The ultimate OpenCode plugin - multi-model orchestration, LSP tools, and more").version(VERSION3, "-v, --version", "Show version number").helpOption("-h, --help", "Display help for command").addOption(new Option("--platform <platform>", `Install target platform: ${availableInstallPlatforms().join(", ")}`).choices(availableInstallPlatforms()).hideHelp()).enablePositionalOptions();
111038
111936
  program2.command("install").alias("setup").description("Install and configure oh-my-opencode with interactive setup").option("--no-tui", "Run in non-interactive mode (requires all options)").option("--claude <value>", "Claude subscription: no, yes, max20").option("--openai <value>", "OpenAI/ChatGPT subscription: no, yes (default: no)").option("--gemini <value>", "Gemini integration: no, yes").option("--copilot <value>", "GitHub Copilot subscription: no, yes").addOption(new Option("--platform <platform>", `Install target platform: ${availableInstallPlatforms().join(", ")}`).choices(availableInstallPlatforms())).option("--opencode-zen <value>", "OpenCode Zen access: no, yes (default: no)").option("--zai-coding-plan <value>", "Z.ai Coding Plan subscription: no, yes (default: no)").option("--kimi-for-coding <value>", "Kimi For Coding subscription: no, yes (default: no)").option("--opencode-go <value>", "OpenCode Go subscription: no, yes (default: no)").option("--bailian-coding-plan <value>", "Bailian Coding Plan subscription: no, yes (default: no)").option("--minimax-cn-coding-plan <value>", "MiniMax Coding Plan (minimaxi.com) subscription: no, yes (default: no)").option("--minimax-coding-plan <value>", "MiniMax Coding Plan (minimax.io) subscription: no, yes (default: no)").option("--vercel-ai-gateway <value>", "Vercel AI Gateway: no, yes (default: no)").option("--codex-autonomous", "Configure Codex with approval never, full filesystem access, and network enabled").option("--no-codex-autonomous", "Leave existing Codex permission settings unchanged").option("--skip-auth", "Skip authentication setup hints").addHelpText("after", `
111039
111937
  Examples:
@@ -111151,6 +112049,7 @@ program2.command("config").description("Manage unified OMO configuration").comma
111151
112049
  });
111152
112050
  configureRuntimeCommands(program2);
111153
112051
  program2.addCommand(createMcpOAuthCommand());
112052
+ configureServiceAuthCommands(program2);
111154
112053
  function runCli() {
111155
112054
  program2.parse();
111156
112055
  }