lazycodex-ai 5.1.63 → 5.1.65

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 (69) hide show
  1. package/dist/cli/index.js +98 -60
  2. package/dist/cli/service-auth/closed-device-login.d.ts +16 -0
  3. package/dist/cli/service-auth/commands.d.ts +9 -2
  4. package/dist/cli/service-auth/session.d.ts +12 -0
  5. package/dist/cli-node/index.js +98 -60
  6. package/package.json +1 -1
  7. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  8. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +19 -0
  9. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  10. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  11. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  12. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  13. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  14. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  15. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  16. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  17. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
  18. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  19. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  20. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  21. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  22. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  23. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  24. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  25. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  26. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  27. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  28. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +1 -1
  29. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  30. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +5 -5
  31. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  32. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  33. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  34. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  35. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  36. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  37. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  38. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  39. package/packages/omo-codex/plugin/hooks/post-tool-use-recording-spawn-admission.json +1 -1
  40. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  41. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  42. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  43. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  44. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  45. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  46. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  47. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  48. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  49. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  50. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  51. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  52. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  53. package/packages/omo-codex/plugin/package-lock.json +12 -12
  54. package/packages/omo-codex/plugin/package.json +1 -1
  55. package/packages/omo-codex/plugin/skills/browser/SKILL.md +3 -1
  56. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/index.js +232 -341
  57. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/manifest.json +5 -5
  58. package/packages/omo-codex/plugin/skills/browser/runtime/omowright/page-bundle.js +1586 -1394
  59. package/packages/omo-codex/plugin/skills/browser/scripts/browser-doctor-report.mjs +68 -0
  60. package/packages/omo-codex/plugin/skills/browser/scripts/browser-doctor.mjs +8 -43
  61. package/packages/omo-codex/plugin/skills/browser/scripts/omowright.mjs +7 -1
  62. package/packages/omo-codex/scripts/install-dist/install-local.mjs +21 -2
  63. package/packages/shared-skills/skills/browser/SKILL.md +3 -1
  64. package/packages/shared-skills/skills/browser/runtime/omowright/index.js +232 -341
  65. package/packages/shared-skills/skills/browser/runtime/omowright/manifest.json +5 -5
  66. package/packages/shared-skills/skills/browser/runtime/omowright/page-bundle.js +1586 -1394
  67. package/packages/shared-skills/skills/browser/scripts/browser-doctor-report.mjs +68 -0
  68. package/packages/shared-skills/skills/browser/scripts/browser-doctor.mjs +8 -43
  69. package/packages/shared-skills/skills/browser/scripts/omowright.mjs +7 -1
package/dist/cli/index.js CHANGED
@@ -2132,7 +2132,7 @@ var package_default;
2132
2132
  var init_package = __esm(() => {
2133
2133
  package_default = {
2134
2134
  name: "oh-my-opencode",
2135
- version: "5.1.63",
2135
+ version: "5.1.65",
2136
2136
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
2137
2137
  main: "./dist/index.js",
2138
2138
  types: "dist/index.d.ts",
@@ -2319,7 +2319,7 @@ var init_package = __esm(() => {
2319
2319
  },
2320
2320
  devDependencies: {
2321
2321
  "@clack/prompts": "^1.8.1",
2322
- "@code-yeongyu/senpi": "2026.11.5-1",
2322
+ "@code-yeongyu/senpi": "2026.11.6-3",
2323
2323
  "@modelcontextprotocol/sdk": "^1.30.0",
2324
2324
  "@oh-my-opencode/agents-md-core": "workspace:*",
2325
2325
  "@oh-my-opencode/ast-grep-mcp": "workspace:*",
@@ -2374,7 +2374,7 @@ var init_package = __esm(() => {
2374
2374
  "jsonc-parser": "^3.3.1",
2375
2375
  knip: "6.43.0",
2376
2376
  "node-pty": "^1.1.0",
2377
- omowright: "github:code-yeongyu/omowright#73c4898ca492e6e12e3f13948be7151806457a43",
2377
+ omowright: "^0.3.0",
2378
2378
  "oxc-parser": "0.148.0",
2379
2379
  oxlint: "1.89.0",
2380
2380
  picocolors: "^1.1.1",
@@ -2384,18 +2384,18 @@ var init_package = __esm(() => {
2384
2384
  typescript: "^7.0.2"
2385
2385
  },
2386
2386
  optionalDependencies: {
2387
- "oh-my-opencode-darwin-arm64": "5.1.63",
2388
- "oh-my-opencode-darwin-x64": "5.1.63",
2389
- "oh-my-opencode-darwin-x64-baseline": "5.1.63",
2390
- "oh-my-opencode-linux-arm64": "5.1.63",
2391
- "oh-my-opencode-linux-arm64-musl": "5.1.63",
2392
- "oh-my-opencode-linux-x64": "5.1.63",
2393
- "oh-my-opencode-linux-x64-baseline": "5.1.63",
2394
- "oh-my-opencode-linux-x64-musl": "5.1.63",
2395
- "oh-my-opencode-linux-x64-musl-baseline": "5.1.63",
2396
- "oh-my-opencode-windows-arm64": "5.1.63",
2397
- "oh-my-opencode-windows-x64": "5.1.63",
2398
- "oh-my-opencode-windows-x64-baseline": "5.1.63"
2387
+ "oh-my-opencode-darwin-arm64": "5.1.65",
2388
+ "oh-my-opencode-darwin-x64": "5.1.65",
2389
+ "oh-my-opencode-darwin-x64-baseline": "5.1.65",
2390
+ "oh-my-opencode-linux-arm64": "5.1.65",
2391
+ "oh-my-opencode-linux-arm64-musl": "5.1.65",
2392
+ "oh-my-opencode-linux-x64": "5.1.65",
2393
+ "oh-my-opencode-linux-x64-baseline": "5.1.65",
2394
+ "oh-my-opencode-linux-x64-musl": "5.1.65",
2395
+ "oh-my-opencode-linux-x64-musl-baseline": "5.1.65",
2396
+ "oh-my-opencode-windows-arm64": "5.1.65",
2397
+ "oh-my-opencode-windows-x64": "5.1.65",
2398
+ "oh-my-opencode-windows-x64-baseline": "5.1.65"
2399
2399
  },
2400
2400
  overrides: {
2401
2401
  hono: "^4.13.8",
@@ -77120,7 +77120,7 @@ var init_memory_agreements = __esm(() => {
77120
77120
  });
77121
77121
 
77122
77122
  // packages/omo-config-core/src/schema/memory.ts
77123
- var OmoMemoryReflectionTriggerSchema, OmoMemoryReflectionSchema, OmoMemorySyncSchema, OmoMemorySearchSchema, OmoMemoryRecallEventCapsSchema, OmoMemoryRecallSchema, OmoMemoryProjectScopeSchema, OmoMemoryProjectScopeLayerSchema, OmoMemoryNudgeSchema, OmoMemoryFactsSchema, OmoMemoryDreamSchema, OmoMemoryPeopleSchema, OmoMemorySoulSchema, OmoMemoryWriteNoticeSchema, OmoMemoryReflectionTriggerLayerSchema, OmoMemoryReflectionLayerSchema, OmoMemorySyncLayerSchema, OmoMemorySearchLayerSchema, OmoMemoryRecallEventCapsLayerSchema, OmoMemoryRecallLayerSchema, OmoMemoryNudgeLayerSchema, OmoMemoryFactsLayerSchema, OmoMemoryDreamLayerSchema, OmoMemoryPeopleLayerSchema, OmoMemorySoulLayerSchema, OmoMemoryWriteNoticeLayerSchema, OmoMemoryProjectionSchema, OmoMemoryProjectionLayerSchema, OmoMemoryAgentOverridesSchema, OmoMemoryAutoIdentitySchema, OmoMemorySettingsSchema, OmoMemorySettingsLayerSchema;
77123
+ var OmoMemoryReflectionTriggerSchema, OmoMemoryReflectionSchema, OmoMemorySyncSchema, OmoMemorySearchSchema, OmoMemoryRecallEventCapsSchema, OmoMemoryRecallSchema, OmoMemoryProjectScopeSchema, OmoMemoryProjectScopeLayerSchema, OmoMemoryNudgeSchema, OmoMemoryFactsSchema, OmoMemoryDreamSchema, OmoMemoryPeopleSchema, OmoMemorySoulSchema, OmoMemoryWriteNoticeSchema, OmoMemoryReflectionTriggerLayerSchema, OmoMemoryReflectionLayerSchema, OmoMemorySyncLayerSchema, OmoMemorySearchLayerSchema, OmoMemoryRecallEventCapsLayerSchema, OmoMemoryRecallLayerSchema, OmoMemoryNudgeLayerSchema, OmoMemoryFactsLayerSchema, OmoMemoryDreamLayerSchema, OmoMemoryPeopleLayerSchema, OmoMemorySoulLayerSchema, OmoMemoryWriteNoticeLayerSchema, OmoMemoryProjectionSchema, OmoMemoryProjectionLayerSchema, OmoMemoryRuntimeSchema, OmoMemoryRuntimeLayerSchema, OmoMemoryAgentOverridesSchema, OmoMemoryAutoIdentitySchema, OmoMemorySettingsSchema, OmoMemorySettingsLayerSchema;
77124
77124
  var init_memory = __esm(() => {
77125
77125
  init_zod();
77126
77126
  init_memory_agreements();
@@ -77269,6 +77269,22 @@ var init_memory = __esm(() => {
77269
77269
  max_entries_per_directory: number2().int().min(0).optional(),
77270
77270
  max_bytes: number2().int().min(0).optional()
77271
77271
  }).strict();
77272
+ OmoMemoryRuntimeSchema = object({
77273
+ retention_days: number2().int().min(1).default(7),
77274
+ abandoned_journal_days: number2().int().min(1).default(14),
77275
+ completions_days: number2().int().min(1).default(30),
77276
+ pass_budget_ms: number2().int().min(1).default(60000),
77277
+ discard_unreflected_journals: boolean2().default(false),
77278
+ max_bytes: number2().int().min(0).default(2 * 1024 * 1024 * 1024)
77279
+ }).strict();
77280
+ OmoMemoryRuntimeLayerSchema = object({
77281
+ retention_days: number2().int().min(1).optional(),
77282
+ abandoned_journal_days: number2().int().min(1).optional(),
77283
+ completions_days: number2().int().min(1).optional(),
77284
+ pass_budget_ms: number2().int().min(1).optional(),
77285
+ discard_unreflected_journals: boolean2().optional(),
77286
+ max_bytes: number2().int().min(0).optional()
77287
+ }).strict();
77272
77288
  OmoMemoryAgentOverridesSchema = object({
77273
77289
  enabled: boolean2().optional(),
77274
77290
  agent: string2().min(1).optional(),
@@ -77284,6 +77300,7 @@ var init_memory = __esm(() => {
77284
77300
  search: OmoMemorySearchLayerSchema.optional(),
77285
77301
  recall: OmoMemoryRecallLayerSchema.optional(),
77286
77302
  projection: OmoMemoryProjectionLayerSchema.optional(),
77303
+ runtime: OmoMemoryRuntimeLayerSchema.optional(),
77287
77304
  compile_warn_tokens: number2().int().positive().optional()
77288
77305
  }).strict();
77289
77306
  OmoMemoryAutoIdentitySchema = _enum(["path", "git"]);
@@ -77328,6 +77345,7 @@ var init_memory = __esm(() => {
77328
77345
  }),
77329
77346
  project_scope: OmoMemoryProjectScopeSchema.default({ nudge: true, recall: true }),
77330
77347
  projection: OmoMemoryProjectionSchema.default({ max_entries_per_directory: 0, max_bytes: 40960 }),
77348
+ runtime: OmoMemoryRuntimeSchema.default({ retention_days: 7, abandoned_journal_days: 14, completions_days: 30, pass_budget_ms: 60000, discard_unreflected_journals: false, max_bytes: 2 * 1024 * 1024 * 1024 }),
77331
77349
  compile_warn_tokens: number2().int().positive().default(30000),
77332
77350
  agents: record(string2(), OmoMemoryAgentOverridesSchema).default({})
77333
77351
  }).strict();
@@ -77348,6 +77366,7 @@ var init_memory = __esm(() => {
77348
77366
  recall: OmoMemoryRecallLayerSchema.optional(),
77349
77367
  project_scope: OmoMemoryProjectScopeLayerSchema.optional(),
77350
77368
  projection: OmoMemoryProjectionLayerSchema.optional(),
77369
+ runtime: OmoMemoryRuntimeLayerSchema.optional(),
77351
77370
  compile_warn_tokens: number2().int().positive().optional(),
77352
77371
  agents: record(string2(), OmoMemoryAgentOverridesSchema).optional()
77353
77372
  }).strict();
@@ -92739,7 +92758,7 @@ var package_default2;
92739
92758
  var init_package2 = __esm(() => {
92740
92759
  package_default2 = {
92741
92760
  name: "@oh-my-opencode/omo-codex",
92742
- version: "5.1.63",
92761
+ version: "5.1.65",
92743
92762
  type: "module",
92744
92763
  private: true,
92745
92764
  description: "Codex harness adapter for oh-my-openagent. Vendored Codex plugin namespace (omo) + TypeScript installer + telemetry.",
@@ -109502,8 +109521,8 @@ var package_default3 = {
109502
109521
  },
109503
109522
  dependencies: {
109504
109523
  "@clack/prompts": "^1.8.1",
109505
- "@code-yeongyu/senpi": "2026.11.5-1",
109506
- "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.5-1",
109524
+ "@code-yeongyu/senpi": "2026.11.6-3",
109525
+ "@earendil-works/pi-ai": "npm:@code-yeongyu/senpi-ai@2026.11.6-3",
109507
109526
  "@modelcontextprotocol/sdk": "^1.30.0",
109508
109527
  "@oh-my-opencode/agents-md-core": "workspace:*",
109509
109528
  "@oh-my-opencode/boulder-state": "workspace:*",
@@ -112356,6 +112375,15 @@ function signInHint(when, route = signInRoute()) {
112356
112375
  return SIGN_IN_HINTS[route][when];
112357
112376
  }
112358
112377
 
112378
+ // packages/omo-opencode/src/cli/service-auth/closed-device-login.ts
112379
+ init_commander();
112380
+ function withClosedDeviceLoginOptions(command) {
112381
+ return command.helpOption(false).allowUnknownOption().allowExcessArguments().option("--device").option("--no-browser").option("--api <url>");
112382
+ }
112383
+ function isClosedDeviceLogin(options, leftover) {
112384
+ return (options.device === true || options.browser === false) && leftover.length === 0;
112385
+ }
112386
+
112359
112387
  // packages/omo-opencode/src/cli/service-auth/browser.ts
112360
112388
  import { execFile as execFile6 } from "child_process";
112361
112389
  var execute = (file, args, env) => new Promise((resolve) => {
@@ -115085,6 +115113,7 @@ var discardedOf = (credentials, cause) => ({
115085
115113
  });
115086
115114
  function createSession(options) {
115087
115115
  const api = serviceOrigin(options.api);
115116
+ const deviceService = options.deviceService;
115088
115117
  const now = options.now ?? Date.now;
115089
115118
  const { store, signal, beforeKeysDiscarded } = options;
115090
115119
  const markerPath = signedOutMarkerPath(options.home ?? homedir35(), credentialId(api));
@@ -115121,6 +115150,7 @@ function createSession(options) {
115121
115150
  if (error instanceof ApiRefusal && error.status < 500 && ["unauthorized", "account_deleted", "reauth_required", "invalid_grant", "device_revoked"].includes(error.code)) {
115122
115151
  await beforeKeysDiscarded(discardedOf(current, "refused"));
115123
115152
  await store.clear();
115153
+ await deviceService?.forget();
115124
115154
  throw new SessionEnded(error.code === "reauth_required" ? `This session was ended because its refresh token was reused. ${signInHint("again")}` : refusalMessage(refusalGuidance(error.code, error.status)), DEVICE_REVOKING_REFUSALS.has(error.code), error.code);
115125
115155
  }
115126
115156
  if (error instanceof RequestFailure && error.notSent || error instanceof ApiRefusal && error.status === 429 && error.code === "rate_limited") {
@@ -115162,6 +115192,7 @@ function createSession(options) {
115162
115192
  await store.write(value, "fresh-sign-in", () => beforeKeysDiscarded({ kind: "unreadable" }));
115163
115193
  await clearSignedOutMarker(markerPath);
115164
115194
  });
115195
+ await deviceService?.adopt(value);
115165
115196
  },
115166
115197
  signedOutMarker: () => readSignedOutMarker(markerPath),
115167
115198
  clearSignedOutMarker: () => clearSignedOutMarker(markerPath),
@@ -115180,41 +115211,46 @@ function createSession(options) {
115180
115211
  },
115181
115212
  async forget() {
115182
115213
  await locked(() => store.clear());
115214
+ await deviceService?.forget();
115183
115215
  },
115184
115216
  accessSnapshot: snapshot,
115185
115217
  async logout() {
115186
- return locked(async () => {
115187
- let current = null;
115188
- let account;
115189
- try {
115190
- current = await store.readAuthoritative();
115191
- if (current !== null) {
115192
- if (current.refreshState === "uncertain")
115193
- return false;
115194
- const access = Date.parse(current.accessTokenExpiresAt) <= now() ? (await refreshed(current)).accessToken : current.accessToken;
115195
- if (current.accountId === undefined && current.owner === undefined && await beforeKeysDiscarded.wantsAccount?.() === true) {
115196
- account = await accountOf(access);
115218
+ try {
115219
+ return await locked(async () => {
115220
+ let current = null;
115221
+ let account;
115222
+ try {
115223
+ current = await store.readAuthoritative();
115224
+ if (current !== null) {
115225
+ if (current.refreshState === "uncertain")
115226
+ return false;
115227
+ const access = Date.parse(current.accessTokenExpiresAt) <= now() ? (await refreshed(current)).accessToken : current.accessToken;
115228
+ if (current.accountId === undefined && current.owner === undefined && await beforeKeysDiscarded.wantsAccount?.() === true) {
115229
+ account = await accountOf(access);
115230
+ }
115231
+ await request(`/v1/devices/${encodeURIComponent(current.device.id)}`, {
115232
+ method: "DELETE",
115233
+ headers: { authorization: `Bearer ${access}` }
115234
+ });
115197
115235
  }
115198
- await request(`/v1/devices/${encodeURIComponent(current.device.id)}`, {
115199
- method: "DELETE",
115200
- headers: { authorization: `Bearer ${access}` }
115201
- });
115236
+ return true;
115237
+ } catch (error) {
115238
+ if (error instanceof SessionEnded)
115239
+ return error.deviceRevoked;
115240
+ if (error instanceof SignInError)
115241
+ return false;
115242
+ throw error;
115243
+ } finally {
115244
+ await writeSignedOutMarker(markerPath, current?.device.id ?? null);
115245
+ const stored = current === null ? null : await store.read().catch(() => current);
115246
+ if (stored !== null)
115247
+ await beforeKeysDiscarded(discardedOf(stored.accountId === undefined && account !== undefined ? { ...stored, accountId: account } : stored, "logout"));
115248
+ await store.clear();
115202
115249
  }
115203
- return true;
115204
- } catch (error) {
115205
- if (error instanceof SessionEnded)
115206
- return error.deviceRevoked;
115207
- if (error instanceof SignInError)
115208
- return false;
115209
- throw error;
115210
- } finally {
115211
- await writeSignedOutMarker(markerPath, current?.device.id ?? null);
115212
- const stored = current === null ? null : await store.read().catch(() => current);
115213
- if (stored !== null)
115214
- await beforeKeysDiscarded(discardedOf(stored.accountId === undefined && account !== undefined ? { ...stored, accountId: account } : stored, "logout"));
115215
- await store.clear();
115216
- }
115217
- });
115250
+ });
115251
+ } finally {
115252
+ await deviceService?.forget();
115253
+ }
115218
115254
  },
115219
115255
  async accessToken(expect) {
115220
115256
  signal?.throwIfAborted();
@@ -115303,7 +115339,7 @@ var DEFAULT_ACCOUNTS_ORIGIN = "https://signin.omo.dev";
115303
115339
  function accountsOrigin(options, env) {
115304
115340
  return serviceOrigin(options.accounts ?? env.OMO_SERVICE_ACCOUNTS_URL ?? DEFAULT_ACCOUNTS_ORIGIN);
115305
115341
  }
115306
- async function run2(action, options) {
115342
+ async function run2(action, options, hooks = {}) {
115307
115343
  const controller = new AbortController;
115308
115344
  const cancel = () => controller.abort();
115309
115345
  process.once("SIGINT", cancel);
@@ -115311,10 +115347,12 @@ async function run2(action, options) {
115311
115347
  try {
115312
115348
  const api = serviceOrigin(options.api ?? process.env.OMO_SERVICE_API_URL ?? "https://api.omo.dev");
115313
115349
  const store = createCredentialStore(api, { wait: INTERACTIVE_READ_WAIT });
115350
+ const deviceService = hooks.deviceService?.(api);
115314
115351
  const session = createSession({
115315
115352
  api,
115316
115353
  store,
115317
115354
  signal: controller.signal,
115355
+ ...deviceService === undefined ? {} : { deviceService },
115318
115356
  beforeKeysDiscarded: retireSyncPinOnDiscard({ pinDir: () => syncPinDir(resolveMemoryRoot(process.env, process.cwd())), log: (line) => console.error(line) })
115319
115357
  });
115320
115358
  if (action === "logout") {
@@ -115374,10 +115412,10 @@ function refuseClosedServiceAuth() {
115374
115412
  console.error(SERVICE_ACCOUNTS_CLOSED_MESSAGE);
115375
115413
  process.exit(1);
115376
115414
  }
115377
- function configureClosedServiceAuthCommands(program) {
115415
+ function configureClosedServiceAuthCommands(program, hooks) {
115378
115416
  for (const name of SERVICE_AUTH_COMMANDS) {
115379
115417
  if (name === "login") {
115380
- configureClosedDeviceLogin(program);
115418
+ configureClosedDeviceLogin(program, hooks);
115381
115419
  continue;
115382
115420
  }
115383
115421
  const command = program.command(name, { hidden: true }).helpOption(false).allowUnknownOption().allowExcessArguments().action(() => {
@@ -115387,25 +115425,25 @@ function configureClosedServiceAuthCommands(program) {
115387
115425
  command.help = refuseClosedServiceAuth;
115388
115426
  }
115389
115427
  }
115390
- function configureClosedDeviceLogin(program) {
115391
- const command = program.command("login", { hidden: true }).helpOption(false).allowUnknownOption().allowExcessArguments().option("--device").option("--no-browser").option("--api <url>").action((options) => {
115392
- if (options.device !== true && options.browser !== false || command.args.length > 0) {
115428
+ function configureClosedDeviceLogin(program, hooks) {
115429
+ const command = withClosedDeviceLoginOptions(program.command("login", { hidden: true })).action((options) => {
115430
+ if (!isClosedDeviceLogin(options, command.args)) {
115393
115431
  console.error(SERVICE_ACCOUNTS_CLOSED_MESSAGE);
115394
115432
  process.exitCode = 1;
115395
115433
  return;
115396
115434
  }
115397
- return run2("login", { ...options, device: true });
115435
+ return run2("login", { ...options, device: true }, hooks);
115398
115436
  });
115399
115437
  command.help = refuseClosedServiceAuth;
115400
115438
  }
115401
- function configureServiceAuthCommands(program, open = serviceAccountsOpen()) {
115439
+ function configureServiceAuthCommands(program, open = serviceAccountsOpen(), hooks = {}) {
115402
115440
  if (!open) {
115403
- configureClosedServiceAuthCommands(program);
115441
+ configureClosedServiceAuthCommands(program, hooks);
115404
115442
  return;
115405
115443
  }
115406
- 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));
115444
+ 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, hooks));
115407
115445
  for (const action of ["logout", "whoami"]) {
115408
- 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));
115446
+ 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, hooks));
115409
115447
  }
115410
115448
  }
115411
115449
 
@@ -0,0 +1,16 @@
1
+ import { Command } from "commander";
2
+ /**
3
+ * The one parse of a closed-accounts `login` (#12639, #12646). While accounts are closed the device-code sign-in
4
+ * is the only terminal sign-in that runs. `commands.ts` refuses every other closed `login` argv, and the compiled
5
+ * entry (`omo-native/compile-args.ts`) decides from the same argv whether to answer with the static refusal. Both
6
+ * layers read it through this module, so `login -- --device` (an operand, not the option) cannot be a device login
7
+ * to one layer and a refusal to the other.
8
+ */
9
+ export declare function withClosedDeviceLoginOptions(command: Command): Command;
10
+ /** The device flow runs only for `--device` or `--no-browser` with nothing else left over (no operand, no unknown option). */
11
+ export declare function isClosedDeviceLogin(options: {
12
+ readonly device?: unknown;
13
+ readonly browser?: unknown;
14
+ }, leftover: readonly string[]): boolean;
15
+ /** Whether a whole `omo …` argv is the closed-accounts device-code login, parsed exactly as `commands.ts` parses it. */
16
+ export declare function parsesAsClosedDeviceLogin(args: readonly string[]): boolean;
@@ -1,11 +1,16 @@
1
1
  import { Command } from "commander";
2
2
  import { type ListedDevice } from "./protocol";
3
+ import { type DeviceServiceHandoff } from "./session";
3
4
  type Options = {
4
5
  readonly api?: string;
5
6
  readonly accounts?: string;
6
7
  readonly device?: boolean;
7
8
  readonly browser?: boolean;
8
9
  };
10
+ /** What the host adds to the account commands: its device service for an API origin (design D r9 sections 4-5). */
11
+ export type ServiceAuthHooks = {
12
+ readonly deviceService?: (api: string) => DeviceServiceHandoff;
13
+ };
9
14
  export declare function deviceChoices(devices: readonly ListedDevice[]): {
10
15
  value: string;
11
16
  label: string;
@@ -16,5 +21,7 @@ export declare const DEFAULT_ACCOUNTS_ORIGIN = "https://signin.omo.dev";
16
21
  export declare function accountsOrigin(options: Pick<Options, "accounts">, env: Record<string, string | undefined>): string;
17
22
  export { SERVICE_ACCOUNTS_OPEN } from "./accounts-gate";
18
23
  export declare const SERVICE_ACCOUNTS_CLOSED_MESSAGE = "Signing in from the terminal isn't available yet. Sign in from the OmO desktop app on this computer; omo uses that sign-in.";
19
- export declare function configureServiceAuthCommands(program: Command, open?: boolean): void;
20
- export declare function runServiceAuthCommand(args: readonly string[]): Promise<void>;
24
+ export declare function configureServiceAuthCommands(program: Command, open?: boolean, hooks?: ServiceAuthHooks): void;
25
+ /** The root program the service-auth bundle parses `omo login|logout|whoami` with. */
26
+ export declare function serviceAuthProgram(open?: boolean, hooks?: ServiceAuthHooks): Command;
27
+ export declare function runServiceAuthCommand(args: readonly string[], hooks?: ServiceAuthHooks): Promise<void>;
@@ -24,6 +24,16 @@ export type BeforeKeysDiscarded = ((discarded: DiscardedCredential) => Promise<v
24
24
  /** Whether a sign-in that names no account should have it read before logout revokes the device. */
25
25
  readonly wantsAccount?: () => Promise<boolean>;
26
26
  };
27
+ /**
28
+ * This machine's device service, told about the sign-in from memory (design D r9 sections 4-5): `adopt` gets a fresh
29
+ * sign-in's credential after the signed-out marker is gone, `forget` runs at every local end of a sign-in (logout on
30
+ * every path, forget, a session the service ended). Neither ever reads the credential store, and neither throws: a
31
+ * device service that is down or refuses never fails a sign-in or a sign-out.
32
+ */
33
+ export type DeviceServiceHandoff = {
34
+ readonly adopt: (credentials: Credentials) => Promise<void>;
35
+ readonly forget: () => Promise<void>;
36
+ };
27
37
  export declare const discardedOf: (credentials: Credentials, cause: "refused" | "forget" | "logout" | "replaced") => DiscardedCredential;
28
38
  export declare function createSession(options: {
29
39
  readonly api: string;
@@ -33,6 +43,8 @@ export declare function createSession(options: {
33
43
  readonly now?: () => number;
34
44
  /** Runs inside the credential lock immediately before a stored principal's keys are discarded. */
35
45
  readonly beforeKeysDiscarded: BeforeKeysDiscarded;
46
+ /** This machine's device service; absent, no device service is told anything. */
47
+ readonly deviceService?: DeviceServiceHandoff;
36
48
  }): {
37
49
  request: (path: string, init: RequestInit) => Promise<unknown>;
38
50
  signal: AbortSignal | undefined;