crewly 1.20.122 → 1.20.124

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 (225) hide show
  1. package/config/skills/agent/core/whatsapp-draft/SKILL.md +80 -0
  2. package/config/skills/agent/core/whatsapp-draft/execute.sh +84 -0
  3. package/config/skills/agent/core/whatsapp-draft/execute.test.sh +70 -0
  4. package/config/skills/agent/core/whatsapp-inbox/SKILL.md +75 -0
  5. package/config/skills/agent/core/whatsapp-inbox/execute.sh +87 -0
  6. package/config/skills/agent/core/whatsapp-inbox/execute.test.sh +68 -0
  7. package/config/skills/agent/core/whatsapp-read/SKILL.md +80 -0
  8. package/config/skills/agent/core/whatsapp-read/execute.sh +116 -0
  9. package/config/skills/agent/core/whatsapp-read/execute.test.sh +84 -0
  10. package/config/skills/agent/core/whatsapp-send/SKILL.md +70 -0
  11. package/config/skills/agent/core/whatsapp-send/execute.sh +84 -0
  12. package/config/skills/agent/core/whatsapp-send/execute.test.sh +79 -0
  13. package/dist/backend/backend/src/constants.d.ts +225 -0
  14. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  15. package/dist/backend/backend/src/constants.js +223 -0
  16. package/dist/backend/backend/src/constants.js.map +1 -1
  17. package/dist/backend/backend/src/controllers/harness/harness.controller.d.ts +54 -0
  18. package/dist/backend/backend/src/controllers/harness/harness.controller.d.ts.map +1 -0
  19. package/dist/backend/backend/src/controllers/harness/harness.controller.js +204 -0
  20. package/dist/backend/backend/src/controllers/harness/harness.controller.js.map +1 -0
  21. package/dist/backend/backend/src/controllers/harness/harness.routes.d.ts +27 -0
  22. package/dist/backend/backend/src/controllers/harness/harness.routes.d.ts.map +1 -0
  23. package/dist/backend/backend/src/controllers/harness/harness.routes.js +43 -0
  24. package/dist/backend/backend/src/controllers/harness/harness.routes.js.map +1 -0
  25. package/dist/backend/backend/src/controllers/whatsapp/whatsapp-inbox.controller.d.ts +73 -0
  26. package/dist/backend/backend/src/controllers/whatsapp/whatsapp-inbox.controller.d.ts.map +1 -0
  27. package/dist/backend/backend/src/controllers/whatsapp/whatsapp-inbox.controller.js +310 -0
  28. package/dist/backend/backend/src/controllers/whatsapp/whatsapp-inbox.controller.js.map +1 -0
  29. package/dist/backend/backend/src/controllers/whatsapp/whatsapp.controller.d.ts.map +1 -1
  30. package/dist/backend/backend/src/controllers/whatsapp/whatsapp.controller.js +53 -4
  31. package/dist/backend/backend/src/controllers/whatsapp/whatsapp.controller.js.map +1 -1
  32. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  33. package/dist/backend/backend/src/routes/api.routes.js +4 -0
  34. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  35. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +5 -0
  36. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  37. package/dist/backend/backend/src/services/agent/agent-registration.service.js +7 -0
  38. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  39. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  40. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +5 -0
  41. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  42. package/dist/backend/backend/src/services/cloud/cloud-client.service.d.ts +2 -1
  43. package/dist/backend/backend/src/services/cloud/cloud-client.service.d.ts.map +1 -1
  44. package/dist/backend/backend/src/services/cloud/cloud-client.service.js +4 -3
  45. package/dist/backend/backend/src/services/cloud/cloud-client.service.js.map +1 -1
  46. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.d.ts.map +1 -1
  47. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js +43 -1
  48. package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js.map +1 -1
  49. package/dist/backend/backend/src/services/harness/claude-config.utils.d.ts +67 -0
  50. package/dist/backend/backend/src/services/harness/claude-config.utils.d.ts.map +1 -0
  51. package/dist/backend/backend/src/services/harness/claude-config.utils.js +118 -0
  52. package/dist/backend/backend/src/services/harness/claude-config.utils.js.map +1 -0
  53. package/dist/backend/backend/src/services/harness/harness-api-key.service.d.ts +99 -0
  54. package/dist/backend/backend/src/services/harness/harness-api-key.service.d.ts.map +1 -0
  55. package/dist/backend/backend/src/services/harness/harness-api-key.service.js +157 -0
  56. package/dist/backend/backend/src/services/harness/harness-api-key.service.js.map +1 -0
  57. package/dist/backend/backend/src/services/harness/harness-credentials.store.d.ts +135 -0
  58. package/dist/backend/backend/src/services/harness/harness-credentials.store.d.ts.map +1 -0
  59. package/dist/backend/backend/src/services/harness/harness-credentials.store.js +208 -0
  60. package/dist/backend/backend/src/services/harness/harness-credentials.store.js.map +1 -0
  61. package/dist/backend/backend/src/services/harness/harness-exec.utils.d.ts +85 -0
  62. package/dist/backend/backend/src/services/harness/harness-exec.utils.d.ts.map +1 -0
  63. package/dist/backend/backend/src/services/harness/harness-exec.utils.js +183 -0
  64. package/dist/backend/backend/src/services/harness/harness-exec.utils.js.map +1 -0
  65. package/dist/backend/backend/src/services/harness/harness-install.service.d.ts +106 -0
  66. package/dist/backend/backend/src/services/harness/harness-install.service.d.ts.map +1 -0
  67. package/dist/backend/backend/src/services/harness/harness-install.service.js +205 -0
  68. package/dist/backend/backend/src/services/harness/harness-install.service.js.map +1 -0
  69. package/dist/backend/backend/src/services/harness/harness-registry.d.ts +86 -0
  70. package/dist/backend/backend/src/services/harness/harness-registry.d.ts.map +1 -0
  71. package/dist/backend/backend/src/services/harness/harness-registry.js +121 -0
  72. package/dist/backend/backend/src/services/harness/harness-registry.js.map +1 -0
  73. package/dist/backend/backend/src/services/harness/harness-status.service.d.ts +173 -0
  74. package/dist/backend/backend/src/services/harness/harness-status.service.d.ts.map +1 -0
  75. package/dist/backend/backend/src/services/harness/harness-status.service.js +324 -0
  76. package/dist/backend/backend/src/services/harness/harness-status.service.js.map +1 -0
  77. package/dist/backend/backend/src/services/harness/harness.service.d.ts +146 -0
  78. package/dist/backend/backend/src/services/harness/harness.service.d.ts.map +1 -0
  79. package/dist/backend/backend/src/services/harness/harness.service.js +193 -0
  80. package/dist/backend/backend/src/services/harness/harness.service.js.map +1 -0
  81. package/dist/backend/backend/src/services/harness/harness.types.d.ts +138 -0
  82. package/dist/backend/backend/src/services/harness/harness.types.d.ts.map +1 -0
  83. package/dist/backend/backend/src/services/harness/harness.types.js +51 -0
  84. package/dist/backend/backend/src/services/harness/harness.types.js.map +1 -0
  85. package/dist/backend/backend/src/services/harness/login-broker.service.d.ts +238 -0
  86. package/dist/backend/backend/src/services/harness/login-broker.service.d.ts.map +1 -0
  87. package/dist/backend/backend/src/services/harness/login-broker.service.js +495 -0
  88. package/dist/backend/backend/src/services/harness/login-broker.service.js.map +1 -0
  89. package/dist/backend/backend/src/services/harness/login-rules.d.ts +162 -0
  90. package/dist/backend/backend/src/services/harness/login-rules.d.ts.map +1 -0
  91. package/dist/backend/backend/src/services/harness/login-rules.js +344 -0
  92. package/dist/backend/backend/src/services/harness/login-rules.js.map +1 -0
  93. package/dist/backend/backend/src/services/harness/orc-harness.store.d.ts +59 -0
  94. package/dist/backend/backend/src/services/harness/orc-harness.store.d.ts.map +1 -0
  95. package/dist/backend/backend/src/services/harness/orc-harness.store.js +108 -0
  96. package/dist/backend/backend/src/services/harness/orc-harness.store.js.map +1 -0
  97. package/dist/backend/backend/src/services/whatsapp/index.d.ts +5 -1
  98. package/dist/backend/backend/src/services/whatsapp/index.d.ts.map +1 -1
  99. package/dist/backend/backend/src/services/whatsapp/index.js +5 -1
  100. package/dist/backend/backend/src/services/whatsapp/index.js.map +1 -1
  101. package/dist/backend/backend/src/services/whatsapp/whatsapp-connection-config.d.ts +59 -0
  102. package/dist/backend/backend/src/services/whatsapp/whatsapp-connection-config.d.ts.map +1 -0
  103. package/dist/backend/backend/src/services/whatsapp/whatsapp-connection-config.js +108 -0
  104. package/dist/backend/backend/src/services/whatsapp/whatsapp-connection-config.js.map +1 -0
  105. package/dist/backend/backend/src/services/whatsapp/whatsapp-draft-gate.d.ts +74 -0
  106. package/dist/backend/backend/src/services/whatsapp/whatsapp-draft-gate.d.ts.map +1 -0
  107. package/dist/backend/backend/src/services/whatsapp/whatsapp-draft-gate.js +82 -0
  108. package/dist/backend/backend/src/services/whatsapp/whatsapp-draft-gate.js.map +1 -0
  109. package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox-capture.d.ts +184 -0
  110. package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox-capture.d.ts.map +1 -0
  111. package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox-capture.js +420 -0
  112. package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox-capture.js.map +1 -0
  113. package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox.store.d.ts +243 -0
  114. package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox.store.d.ts.map +1 -0
  115. package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox.store.js +538 -0
  116. package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox.store.js.map +1 -0
  117. package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.d.ts +32 -2
  118. package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.d.ts.map +1 -1
  119. package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.js +65 -9
  120. package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.js.map +1 -1
  121. package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.d.ts.map +1 -1
  122. package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.js +11 -0
  123. package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.js.map +1 -1
  124. package/dist/backend/backend/src/services/whatsapp/whatsapp.service.d.ts +59 -1
  125. package/dist/backend/backend/src/services/whatsapp/whatsapp.service.d.ts.map +1 -1
  126. package/dist/backend/backend/src/services/whatsapp/whatsapp.service.js +159 -9
  127. package/dist/backend/backend/src/services/whatsapp/whatsapp.service.js.map +1 -1
  128. package/dist/backend/backend/src/types/whatsapp.types.d.ts +108 -0
  129. package/dist/backend/backend/src/types/whatsapp.types.d.ts.map +1 -1
  130. package/dist/backend/backend/src/types/whatsapp.types.js +9 -0
  131. package/dist/backend/backend/src/types/whatsapp.types.js.map +1 -1
  132. package/dist/backend/build-info.json +2 -2
  133. package/dist/cli/backend/src/constants.d.ts +225 -0
  134. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  135. package/dist/cli/backend/src/constants.js +223 -0
  136. package/dist/cli/backend/src/constants.js.map +1 -1
  137. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts +5 -0
  138. package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  139. package/dist/cli/backend/src/services/agent/agent-registration.service.js +7 -0
  140. package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
  141. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  142. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +5 -0
  143. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  144. package/dist/cli/backend/src/services/cloud/cloud-client.service.d.ts +2 -1
  145. package/dist/cli/backend/src/services/cloud/cloud-client.service.d.ts.map +1 -1
  146. package/dist/cli/backend/src/services/cloud/cloud-client.service.js +4 -3
  147. package/dist/cli/backend/src/services/cloud/cloud-client.service.js.map +1 -1
  148. package/dist/cli/backend/src/services/harness/claude-config.utils.d.ts +67 -0
  149. package/dist/cli/backend/src/services/harness/claude-config.utils.d.ts.map +1 -0
  150. package/dist/cli/backend/src/services/harness/claude-config.utils.js +118 -0
  151. package/dist/cli/backend/src/services/harness/claude-config.utils.js.map +1 -0
  152. package/dist/cli/backend/src/services/harness/harness-api-key.service.d.ts +99 -0
  153. package/dist/cli/backend/src/services/harness/harness-api-key.service.d.ts.map +1 -0
  154. package/dist/cli/backend/src/services/harness/harness-api-key.service.js +157 -0
  155. package/dist/cli/backend/src/services/harness/harness-api-key.service.js.map +1 -0
  156. package/dist/cli/backend/src/services/harness/harness-credentials.store.d.ts +135 -0
  157. package/dist/cli/backend/src/services/harness/harness-credentials.store.d.ts.map +1 -0
  158. package/dist/cli/backend/src/services/harness/harness-credentials.store.js +208 -0
  159. package/dist/cli/backend/src/services/harness/harness-credentials.store.js.map +1 -0
  160. package/dist/cli/backend/src/services/harness/harness-exec.utils.d.ts +85 -0
  161. package/dist/cli/backend/src/services/harness/harness-exec.utils.d.ts.map +1 -0
  162. package/dist/cli/backend/src/services/harness/harness-exec.utils.js +183 -0
  163. package/dist/cli/backend/src/services/harness/harness-exec.utils.js.map +1 -0
  164. package/dist/cli/backend/src/services/harness/harness-install.service.d.ts +106 -0
  165. package/dist/cli/backend/src/services/harness/harness-install.service.d.ts.map +1 -0
  166. package/dist/cli/backend/src/services/harness/harness-install.service.js +205 -0
  167. package/dist/cli/backend/src/services/harness/harness-install.service.js.map +1 -0
  168. package/dist/cli/backend/src/services/harness/harness-registry.d.ts +86 -0
  169. package/dist/cli/backend/src/services/harness/harness-registry.d.ts.map +1 -0
  170. package/dist/cli/backend/src/services/harness/harness-registry.js +121 -0
  171. package/dist/cli/backend/src/services/harness/harness-registry.js.map +1 -0
  172. package/dist/cli/backend/src/services/harness/harness-status.service.d.ts +173 -0
  173. package/dist/cli/backend/src/services/harness/harness-status.service.d.ts.map +1 -0
  174. package/dist/cli/backend/src/services/harness/harness-status.service.js +324 -0
  175. package/dist/cli/backend/src/services/harness/harness-status.service.js.map +1 -0
  176. package/dist/cli/backend/src/services/harness/harness.service.d.ts +146 -0
  177. package/dist/cli/backend/src/services/harness/harness.service.d.ts.map +1 -0
  178. package/dist/cli/backend/src/services/harness/harness.service.js +193 -0
  179. package/dist/cli/backend/src/services/harness/harness.service.js.map +1 -0
  180. package/dist/cli/backend/src/services/harness/harness.types.d.ts +138 -0
  181. package/dist/cli/backend/src/services/harness/harness.types.d.ts.map +1 -0
  182. package/dist/cli/backend/src/services/harness/harness.types.js +51 -0
  183. package/dist/cli/backend/src/services/harness/harness.types.js.map +1 -0
  184. package/dist/cli/backend/src/services/harness/login-broker.service.d.ts +238 -0
  185. package/dist/cli/backend/src/services/harness/login-broker.service.d.ts.map +1 -0
  186. package/dist/cli/backend/src/services/harness/login-broker.service.js +495 -0
  187. package/dist/cli/backend/src/services/harness/login-broker.service.js.map +1 -0
  188. package/dist/cli/backend/src/services/harness/login-rules.d.ts +162 -0
  189. package/dist/cli/backend/src/services/harness/login-rules.d.ts.map +1 -0
  190. package/dist/cli/backend/src/services/harness/login-rules.js +344 -0
  191. package/dist/cli/backend/src/services/harness/login-rules.js.map +1 -0
  192. package/dist/cli/backend/src/services/harness/orc-harness.store.d.ts +59 -0
  193. package/dist/cli/backend/src/services/harness/orc-harness.store.d.ts.map +1 -0
  194. package/dist/cli/backend/src/services/harness/orc-harness.store.js +108 -0
  195. package/dist/cli/backend/src/services/harness/orc-harness.store.js.map +1 -0
  196. package/dist/cli/cli/src/commands/harness-setup.d.ts +159 -0
  197. package/dist/cli/cli/src/commands/harness-setup.d.ts.map +1 -0
  198. package/dist/cli/cli/src/commands/harness-setup.js +377 -0
  199. package/dist/cli/cli/src/commands/harness-setup.js.map +1 -0
  200. package/dist/cli/cli/src/commands/harness.d.ts +56 -0
  201. package/dist/cli/cli/src/commands/harness.d.ts.map +1 -0
  202. package/dist/cli/cli/src/commands/harness.js +97 -0
  203. package/dist/cli/cli/src/commands/harness.js.map +1 -0
  204. package/dist/cli/cli/src/commands/onboard.d.ts +108 -50
  205. package/dist/cli/cli/src/commands/onboard.d.ts.map +1 -1
  206. package/dist/cli/cli/src/commands/onboard.js +200 -191
  207. package/dist/cli/cli/src/commands/onboard.js.map +1 -1
  208. package/dist/cli/cli/src/constants.d.ts +18 -0
  209. package/dist/cli/cli/src/constants.d.ts.map +1 -1
  210. package/dist/cli/cli/src/constants.js +18 -0
  211. package/dist/cli/cli/src/constants.js.map +1 -1
  212. package/dist/cli/cli/src/index.js +22 -3
  213. package/dist/cli/cli/src/index.js.map +1 -1
  214. package/dist/cli/cli/src/utils/harness-engine.d.ts +99 -0
  215. package/dist/cli/cli/src/utils/harness-engine.d.ts.map +1 -0
  216. package/dist/cli/cli/src/utils/harness-engine.js +143 -0
  217. package/dist/cli/cli/src/utils/harness-engine.js.map +1 -0
  218. package/dist/cli/cli/src/utils/prompt-io.d.ts +25 -0
  219. package/dist/cli/cli/src/utils/prompt-io.d.ts.map +1 -0
  220. package/dist/cli/cli/src/utils/prompt-io.js +47 -0
  221. package/dist/cli/cli/src/utils/prompt-io.js.map +1 -0
  222. package/frontend/dist/assets/{index-8327e63e.css → index-0a2f783c.css} +1 -1
  223. package/frontend/dist/assets/{index-07e338a9.js → index-aaf65055.js} +309 -309
  224. package/frontend/dist/index.html +2 -2
  225. package/package.json +1 -1
@@ -0,0 +1,495 @@
1
+ /**
2
+ * Login broker — runs a harness's own login command in a PTY and relays it.
3
+ *
4
+ * The broker knows nothing about harness APIs. It spawns the login command
5
+ * (`claude setup-token`, `codex login --device-auth`) in a very wide PTY,
6
+ * normalizes the screen and applies the harness's rule set (login-rules.ts)
7
+ * to find the sign-in URL, a one-time code, a "paste the code" prompt,
8
+ * success and failure. Front ends read the resulting {@link LoginSession}
9
+ * and send the user's reply back with {@link LoginBrokerService.input}.
10
+ *
11
+ * Front ends are consumers of the same events: the REST API polls
12
+ * {@link LoginBrokerService.get}, the CLI awaits
13
+ * {@link LoginBrokerService.waitForCompletion}, and Phase 2 (re-login over
14
+ * Slack DM) subscribes to the `update` / `finished` events.
15
+ *
16
+ * Secrets: Claude's long-lived token is captured from the screen, stored with
17
+ * {@link HarnessCredentialsStore.setClaudeOauthToken}, redacted from every
18
+ * exposed screen and scrubbed from the raw buffer. It is never logged or
19
+ * emitted.
20
+ *
21
+ * State machine:
22
+ * `starting` → `awaiting_user` (URL / code / prompt seen) → `verifying`
23
+ * (input sent, or the command reported success and is being confirmed) →
24
+ * `succeeded` | `failed`; any live state → `timed_out` (15 min) | `cancelled`.
25
+ * A prompt that reappears after input returns the session to `awaiting_user`.
26
+ *
27
+ * @module services/harness/login-broker.service
28
+ */
29
+ import { randomUUID } from 'crypto';
30
+ import { EventEmitter } from 'events';
31
+ import * as os from 'os';
32
+ import * as pty from 'node-pty';
33
+ import { API_SECURITY_CONSTANTS, HARNESS_CONSTANTS } from '../../constants.js';
34
+ import { stripNestedClaudeSessionEnv } from '../agent/runtime-session-recovery.js';
35
+ import { prepareClaudeConfigForCrewlyLogin } from './claude-config.utils.js';
36
+ import { getHarnessCredentialsStore } from './harness-credentials.store.js';
37
+ import { buildHarnessPath, resolveExecutable } from './harness-exec.utils.js';
38
+ import { getHarnessDefinition, getLoginMethod } from './harness-registry.js';
39
+ import { SILENT_HARNESS_LOGGER, isTerminalLoginState, } from './harness.types.js';
40
+ import { evaluateLoginRules, getLoginRules, normalizeTerminalOutput, redactSecrets } from './login-rules.js';
41
+ /** Events emitted by the broker. Listeners receive a {@link LoginSession} (never a secret). */
42
+ export const LOGIN_BROKER_EVENTS = {
43
+ /** Any change to a session */
44
+ UPDATE: 'update',
45
+ /** A session reached a terminal state */
46
+ FINISHED: 'finished',
47
+ };
48
+ /** Broker error with a machine-readable code. */
49
+ export class LoginBrokerError extends Error {
50
+ code;
51
+ /**
52
+ * @param code - Machine-readable code
53
+ * @param message - Human-readable message (never contains a secret)
54
+ */
55
+ constructor(code, message) {
56
+ super(message);
57
+ this.code = code;
58
+ this.name = 'LoginBrokerError';
59
+ }
60
+ }
61
+ /**
62
+ * Default PTY spawner (node-pty).
63
+ *
64
+ * @param file - Executable
65
+ * @param args - Arguments
66
+ * @param options - Size, cwd, env
67
+ * @returns The PTY
68
+ */
69
+ const defaultSpawnPty = (file, args, options) => pty.spawn(file, args, { name: 'xterm-256color', cols: options.cols, rows: options.rows, cwd: options.cwd, env: options.env });
70
+ /** Runs harness login commands in PTYs and tracks their sessions. */
71
+ export class LoginBrokerService extends EventEmitter {
72
+ spawnPty;
73
+ resolveCommand;
74
+ env;
75
+ homeDir;
76
+ credentials;
77
+ verify;
78
+ prepareClaudeConfig;
79
+ now;
80
+ idFactory;
81
+ logger;
82
+ timeoutMs;
83
+ sessions = new Map();
84
+ /** Sessions whose verification is in flight. */
85
+ verifying = new Set();
86
+ /**
87
+ * @param deps - Injectable dependencies (all optional)
88
+ */
89
+ constructor(deps = {}) {
90
+ super();
91
+ this.spawnPty = deps.spawnPty ?? defaultSpawnPty;
92
+ this.resolveCommand = deps.resolveCommand ?? ((command, envPath) => resolveExecutable(command, envPath));
93
+ this.env = deps.env ?? process.env;
94
+ this.homeDir = deps.homeDir ?? os.homedir();
95
+ this.credentials = deps.credentials ?? getHarnessCredentialsStore();
96
+ this.verify = deps.verify ?? (async () => true);
97
+ this.prepareClaudeConfig = deps.prepareClaudeConfig ?? (() => void prepareClaudeConfigForCrewlyLogin());
98
+ this.now = deps.now ?? Date.now;
99
+ this.idFactory = deps.idFactory ?? randomUUID;
100
+ this.logger = deps.logger ?? SILENT_HARNESS_LOGGER;
101
+ this.timeoutMs = deps.timeoutMs ?? HARNESS_CONSTANTS.LOGIN.TIMEOUT_MS;
102
+ }
103
+ /**
104
+ * Environment for the login PTY: the harness PATH, no owner API token, no
105
+ * nested-Claude-session markers, and a no-op BROWSER so the harness does
106
+ * not try to open a browser on the server.
107
+ *
108
+ * @returns String-only env map
109
+ */
110
+ buildEnv() {
111
+ const cleaned = stripNestedClaudeSessionEnv({ ...this.env });
112
+ const env = {};
113
+ for (const [key, value] of Object.entries(cleaned)) {
114
+ if (value !== undefined && key !== API_SECURITY_CONSTANTS.ENV.API_TOKEN)
115
+ env[key] = value;
116
+ }
117
+ env.PATH = buildHarnessPath(this.env.PATH, this.homeDir);
118
+ env.BROWSER = HARNESS_CONSTANTS.LOGIN.BROWSER_SUPPRESS_VALUE;
119
+ env.TERM = 'xterm-256color';
120
+ return env;
121
+ }
122
+ /**
123
+ * Start a login session, or return the live one for this harness.
124
+ *
125
+ * @param harnessId - Harness id
126
+ * @param method - Broker login method (`subscription`, `device`)
127
+ * @returns The session
128
+ * @throws LoginBrokerError unknown_harness | unsupported_method | not_installed | spawn_failed
129
+ */
130
+ start(harnessId, method) {
131
+ const def = getHarnessDefinition(harnessId);
132
+ if (!def)
133
+ throw new LoginBrokerError('unknown_harness', `Unknown harness: ${harnessId}`);
134
+ const methodDef = getLoginMethod(def.id, method);
135
+ const rules = getLoginRules(def.id, method);
136
+ if (!methodDef || methodDef.kind !== 'broker' || !methodDef.broker || !rules) {
137
+ throw new LoginBrokerError('unsupported_method', `${def.displayName} has no "${method}" login that Crewly can run`);
138
+ }
139
+ const live = this.getActiveSession(def.id);
140
+ if (live)
141
+ return live;
142
+ this.prune();
143
+ const env = this.buildEnv();
144
+ const binary = this.resolveCommand(methodDef.broker.command, env.PATH);
145
+ if (!binary) {
146
+ throw new LoginBrokerError('not_installed', `${def.displayName} is not installed (\`${methodDef.broker.command}\` not found)`);
147
+ }
148
+ const nowIso = new Date(this.now()).toISOString();
149
+ let resolveDone = () => undefined;
150
+ const done = new Promise((resolve) => {
151
+ resolveDone = resolve;
152
+ });
153
+ const record = {
154
+ session: {
155
+ id: this.idFactory(),
156
+ harnessId: def.id,
157
+ method: method,
158
+ state: 'starting',
159
+ url: null,
160
+ userCode: null,
161
+ needsInput: false,
162
+ message: null,
163
+ screen: '',
164
+ startedAt: nowIso,
165
+ updatedAt: nowIso,
166
+ },
167
+ rules,
168
+ pty: null,
169
+ raw: '',
170
+ inputOffset: 0,
171
+ secrets: [],
172
+ timer: null,
173
+ exited: false,
174
+ finishedAt: null,
175
+ done,
176
+ resolveDone,
177
+ };
178
+ try {
179
+ record.pty = this.spawnPty(binary, [...methodDef.broker.args], {
180
+ cols: HARNESS_CONSTANTS.LOGIN.PTY_COLS,
181
+ rows: HARNESS_CONSTANTS.LOGIN.PTY_ROWS,
182
+ cwd: this.homeDir,
183
+ env,
184
+ });
185
+ }
186
+ catch (error) {
187
+ throw new LoginBrokerError('spawn_failed', `Could not start \`${methodDef.broker.command}\`: ${error instanceof Error ? error.message : String(error)}`);
188
+ }
189
+ this.sessions.set(record.session.id, record);
190
+ record.pty.onData((data) => this.handleData(record, data));
191
+ record.pty.onExit(({ exitCode }) => {
192
+ void this.handleExit(record, exitCode);
193
+ });
194
+ record.timer = setTimeout(() => this.finish(record, 'timed_out', 'The login was not completed in time.'), this.timeoutMs);
195
+ record.timer.unref?.();
196
+ this.logger.info('Login broker session started', { sessionId: record.session.id, harnessId: def.id, method });
197
+ return { ...record.session };
198
+ }
199
+ /**
200
+ * Read a session.
201
+ *
202
+ * @param sessionId - Session id
203
+ * @returns The session
204
+ * @throws LoginBrokerError not_found
205
+ */
206
+ get(sessionId) {
207
+ return { ...this.requireRecord(sessionId).session };
208
+ }
209
+ /**
210
+ * The live (non-terminal) session for a harness, if any.
211
+ *
212
+ * @param harnessId - Harness id
213
+ * @returns The session, or null
214
+ */
215
+ getActiveSession(harnessId) {
216
+ for (const record of this.sessions.values()) {
217
+ if (record.session.harnessId === harnessId && !isTerminalLoginState(record.session.state))
218
+ return { ...record.session };
219
+ }
220
+ return null;
221
+ }
222
+ /**
223
+ * Type the user's reply into the PTY, followed by Enter.
224
+ *
225
+ * @param sessionId - Session id
226
+ * @param text - What the user typed (e.g. the code from platform.claude.com)
227
+ * @returns The session
228
+ * @throws LoginBrokerError not_found | not_active
229
+ */
230
+ input(sessionId, text) {
231
+ const record = this.requireRecord(sessionId);
232
+ if (isTerminalLoginState(record.session.state) || !record.pty || record.exited) {
233
+ throw new LoginBrokerError('not_active', 'This login has already finished');
234
+ }
235
+ record.inputOffset = record.raw.length;
236
+ record.pty.write(`${text.replace(/[\r\n]+/g, '')}${HARNESS_CONSTANTS.LOGIN.ENTER}`);
237
+ this.update(record, { state: 'verifying', needsInput: false, message: null });
238
+ return { ...record.session };
239
+ }
240
+ /**
241
+ * Cancel a session (kills the PTY). Cancelling a finished session is a no-op.
242
+ *
243
+ * @param sessionId - Session id
244
+ * @returns The session
245
+ * @throws LoginBrokerError not_found
246
+ */
247
+ cancel(sessionId) {
248
+ const record = this.requireRecord(sessionId);
249
+ if (!isTerminalLoginState(record.session.state))
250
+ this.finish(record, 'cancelled', 'Login cancelled.');
251
+ return { ...record.session };
252
+ }
253
+ /**
254
+ * Resolve when the session reaches a terminal state.
255
+ *
256
+ * @param sessionId - Session id
257
+ * @returns The final session
258
+ * @throws LoginBrokerError not_found
259
+ */
260
+ async waitForCompletion(sessionId) {
261
+ const record = this.requireRecord(sessionId);
262
+ if (isTerminalLoginState(record.session.state))
263
+ return { ...record.session };
264
+ return record.done;
265
+ }
266
+ /** Cancel every live session (backend shutdown, CLI exit). */
267
+ shutdown() {
268
+ for (const record of this.sessions.values()) {
269
+ if (!isTerminalLoginState(record.session.state))
270
+ this.finish(record, 'cancelled', 'Login cancelled: Crewly is shutting down.');
271
+ }
272
+ }
273
+ /**
274
+ * Session record or a not_found error.
275
+ *
276
+ * @param sessionId - Session id
277
+ * @returns The record
278
+ * @throws LoginBrokerError not_found
279
+ */
280
+ requireRecord(sessionId) {
281
+ const record = this.sessions.get(sessionId);
282
+ if (!record)
283
+ throw new LoginBrokerError('not_found', `Login session not found: ${sessionId}`);
284
+ return record;
285
+ }
286
+ /**
287
+ * Append PTY output and re-evaluate the rules.
288
+ *
289
+ * @param record - Session record
290
+ * @param data - Output chunk
291
+ */
292
+ handleData(record, data) {
293
+ if (isTerminalLoginState(record.session.state))
294
+ return;
295
+ record.raw += data;
296
+ const max = HARNESS_CONSTANTS.LOGIN.RAW_BUFFER_MAX_CHARS;
297
+ if (record.raw.length > max) {
298
+ const drop = record.raw.length - max;
299
+ record.raw = record.raw.slice(drop);
300
+ record.inputOffset = Math.max(0, record.inputOffset - drop);
301
+ }
302
+ this.evaluate(record, false);
303
+ }
304
+ /**
305
+ * Apply the rule set to the current screen and move the state machine.
306
+ *
307
+ * @param record - Session record
308
+ * @param final - The process has exited
309
+ */
310
+ evaluate(record, final) {
311
+ const screen = normalizeTerminalOutput(record.raw);
312
+ const promptScreen = normalizeTerminalOutput(record.raw.slice(record.inputOffset));
313
+ const match = evaluateLoginRules(record.rules, screen, { promptScreen, final });
314
+ if (match.secret && !record.secrets.includes(match.secret))
315
+ record.secrets.push(match.secret);
316
+ const patch = {
317
+ url: match.url ?? record.session.url,
318
+ userCode: match.userCode ?? record.session.userCode,
319
+ needsInput: match.needsInput,
320
+ screen: this.exposedScreen(record, screen.text),
321
+ };
322
+ if (match.succeeded) {
323
+ this.update(record, patch);
324
+ void this.complete(record, match.secret);
325
+ return;
326
+ }
327
+ if (match.failureMessage) {
328
+ if (match.needsInput) {
329
+ // The harness rejected the reply and asks again.
330
+ this.update(record, { ...patch, state: 'awaiting_user', message: match.failureMessage });
331
+ }
332
+ else {
333
+ this.update(record, patch);
334
+ this.finish(record, 'failed', match.failureMessage);
335
+ }
336
+ return;
337
+ }
338
+ let state = record.session.state;
339
+ if (state === 'starting' && (patch.url || patch.userCode || match.needsInput))
340
+ state = 'awaiting_user';
341
+ if (state === 'verifying' && match.needsInput)
342
+ state = 'awaiting_user';
343
+ this.update(record, { ...patch, state });
344
+ }
345
+ /**
346
+ * Handle a reported success: store the secret (Claude) or confirm (Codex).
347
+ *
348
+ * @param record - Session record
349
+ * @param secret - Captured credential, when the rule set has one
350
+ */
351
+ async complete(record, secret) {
352
+ if (isTerminalLoginState(record.session.state))
353
+ return;
354
+ if (record.rules.successRequiresSecret) {
355
+ if (!secret)
356
+ return;
357
+ try {
358
+ this.storeSecret(record, secret);
359
+ }
360
+ catch (error) {
361
+ this.finish(record, 'failed', `Login worked, but Crewly could not save the token: ${error instanceof Error ? error.message : String(error)}`);
362
+ return;
363
+ }
364
+ this.finish(record, 'succeeded', 'Logged in. Crewly saved the token for its agents.');
365
+ return;
366
+ }
367
+ await this.confirm(record);
368
+ }
369
+ /**
370
+ * Confirm a login with the harness's status command.
371
+ *
372
+ * @param record - Session record
373
+ */
374
+ async confirm(record) {
375
+ if (this.verifying.has(record.session.id) || isTerminalLoginState(record.session.state))
376
+ return;
377
+ this.verifying.add(record.session.id);
378
+ this.update(record, { state: 'verifying', needsInput: false });
379
+ try {
380
+ const ok = record.rules.verifyAfterSuccess ? await this.verify(record.session.harnessId) : true;
381
+ if (isTerminalLoginState(record.session.state))
382
+ return;
383
+ if (ok)
384
+ this.finish(record, 'succeeded', 'Logged in.');
385
+ else
386
+ this.finish(record, 'failed', 'The login command finished, but the harness still reports it is not logged in.');
387
+ }
388
+ catch (error) {
389
+ if (!isTerminalLoginState(record.session.state)) {
390
+ this.finish(record, 'failed', `Could not confirm the login: ${error instanceof Error ? error.message : String(error)}`);
391
+ }
392
+ }
393
+ finally {
394
+ this.verifying.delete(record.session.id);
395
+ }
396
+ }
397
+ /**
398
+ * Save a captured credential. Never logs it.
399
+ *
400
+ * @param record - Session record
401
+ * @param secret - The credential
402
+ */
403
+ storeSecret(record, secret) {
404
+ if (record.session.harnessId === HARNESS_CONSTANTS.IDS.CLAUDE_CODE) {
405
+ this.credentials.setClaudeOauthToken(secret);
406
+ try {
407
+ this.prepareClaudeConfig();
408
+ }
409
+ catch (error) {
410
+ this.logger.warn('Could not update Claude config after login', { error: error instanceof Error ? error.message : String(error) });
411
+ }
412
+ }
413
+ // Scrub the secret from the raw buffer now that it is stored.
414
+ record.raw = record.raw.split(secret).join(HARNESS_CONSTANTS.LOGIN.REDACTED);
415
+ this.logger.info('Harness credential stored', { sessionId: record.session.id, harnessId: record.session.harnessId });
416
+ }
417
+ /**
418
+ * The login command exited.
419
+ *
420
+ * @param record - Session record
421
+ * @param exitCode - Process exit code
422
+ */
423
+ async handleExit(record, exitCode) {
424
+ record.exited = true;
425
+ if (isTerminalLoginState(record.session.state))
426
+ return;
427
+ this.evaluate(record, true);
428
+ if (isTerminalLoginState(record.session.state) || this.verifying.has(record.session.id))
429
+ return;
430
+ if (record.rules.successOnExitZero && exitCode === 0) {
431
+ await this.confirm(record);
432
+ return;
433
+ }
434
+ this.finish(record, 'failed', record.session.message ?? `The login command exited (code ${exitCode}) before the login finished.`);
435
+ }
436
+ /**
437
+ * Move a session to a terminal state and release the PTY.
438
+ *
439
+ * @param record - Session record
440
+ * @param state - Terminal state
441
+ * @param message - Message for the user
442
+ */
443
+ finish(record, state, message) {
444
+ if (isTerminalLoginState(record.session.state))
445
+ return;
446
+ if (record.timer)
447
+ clearTimeout(record.timer);
448
+ record.timer = null;
449
+ record.finishedAt = this.now();
450
+ this.update(record, { state, message, needsInput: false });
451
+ if (record.pty && !record.exited) {
452
+ try {
453
+ record.pty.kill();
454
+ }
455
+ catch {
456
+ // Already gone.
457
+ }
458
+ }
459
+ this.logger.info('Login broker session finished', { sessionId: record.session.id, harnessId: record.session.harnessId, state });
460
+ const snapshot = { ...record.session };
461
+ record.resolveDone(snapshot);
462
+ this.emit(LOGIN_BROKER_EVENTS.FINISHED, snapshot);
463
+ }
464
+ /**
465
+ * Apply a patch, bump `updatedAt` and emit `update`.
466
+ *
467
+ * @param record - Session record
468
+ * @param patch - Fields to change
469
+ */
470
+ update(record, patch) {
471
+ record.session = { ...record.session, ...patch, updatedAt: new Date(this.now()).toISOString() };
472
+ this.emit(LOGIN_BROKER_EVENTS.UPDATE, { ...record.session });
473
+ }
474
+ /**
475
+ * Tail of the screen, secrets redacted.
476
+ *
477
+ * @param record - Session record
478
+ * @param text - Normalized text
479
+ * @returns Exposed screen text
480
+ */
481
+ exposedScreen(record, text) {
482
+ const redacted = redactSecrets(text, record.secrets);
483
+ const max = HARNESS_CONSTANTS.LOGIN.SCREEN_MAX_CHARS;
484
+ return redacted.length > max ? redacted.slice(redacted.length - max) : redacted;
485
+ }
486
+ /** Forget finished sessions older than the retention window. */
487
+ prune() {
488
+ const cutoff = this.now() - HARNESS_CONSTANTS.LOGIN.SESSION_RETENTION_MS;
489
+ for (const [id, record] of this.sessions) {
490
+ if (record.finishedAt !== null && record.finishedAt < cutoff)
491
+ this.sessions.delete(id);
492
+ }
493
+ }
494
+ }
495
+ //# sourceMappingURL=login-broker.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-broker.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/harness/login-broker.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Login rules — per-harness patterns the login broker applies to terminal text.
3
+ *
4
+ * The broker runs a harness's own login command in a PTY and learns what is
5
+ * happening purely from the screen: the sign-in URL, a one-time code, a
6
+ * "paste the code" prompt, success and failure. Nothing here talks to a
7
+ * harness API.
8
+ *
9
+ * Terminal text is messy, so it is normalized first:
10
+ * - ANSI/OSC escapes are removed; OSC 8 hyperlink targets are kept as URL
11
+ * candidates; cursor-forward (`CSI n C`) and cursor-to-column (`CSI n G`)
12
+ * become spaces, which restores the gaps TUIs draw between words.
13
+ * - TUIs such as Claude Code's may render words without spaces, so prompt,
14
+ * success and failure patterns are matched against both the normalized text
15
+ * and a copy with all spaces removed (patterns use `\s*` between words).
16
+ * - A URL (or token) that a TUI wrapped at the terminal width continues on
17
+ * the next line(s); {@link extractWrappedRuns} joins those back.
18
+ *
19
+ * Values are only accepted once they are complete — followed by more output —
20
+ * so a URL that arrives split across two PTY chunks is never exposed half-way.
21
+ *
22
+ * Rule sets are built from real output captured on 2026-09-25 (Claude Code
23
+ * 2.1.282 `claude setup-token`, codex-cli 0.156.1 `codex login --device-auth`);
24
+ * see login-rules.test.ts.
25
+ *
26
+ * @module services/harness/login-rules
27
+ */
28
+ import type { HarnessId, LoginMethodId } from './harness.types.js';
29
+ /** Normalized terminal text. */
30
+ export interface NormalizedScreen {
31
+ /** Text with escapes removed, `\n` line breaks, trailing spaces trimmed */
32
+ text: string;
33
+ /** `text` with every space and tab removed (for TUIs that drop spaces) */
34
+ spaceless: string;
35
+ /** Targets of OSC 8 hyperlinks seen in the raw output */
36
+ hyperlinks: string[];
37
+ }
38
+ /** Patterns for one harness login method. */
39
+ export interface LoginRuleSet {
40
+ harnessId: HarnessId;
41
+ method: LoginMethodId;
42
+ /** A URL candidate is the sign-in URL when this matches it */
43
+ urlPattern: RegExp;
44
+ /** Capture group 1 is the one-time code the user types elsewhere */
45
+ userCodePattern?: RegExp;
46
+ /** The harness is waiting for the user to type/paste something */
47
+ inputPromptPattern?: RegExp;
48
+ /** Success text */
49
+ successPattern?: RegExp;
50
+ /** Any match fails the login; the matched line becomes the message */
51
+ failurePatterns: readonly RegExp[];
52
+ /** A credential the harness prints on success (group 0), e.g. Claude's long-lived token */
53
+ secretPattern?: RegExp;
54
+ /** Success needs the secret: the harness does not store it itself (Claude setup-token) */
55
+ successRequiresSecret: boolean;
56
+ /** Exit code 0 counts as success (Codex writes its own credentials) */
57
+ successOnExitZero: boolean;
58
+ /** Confirm success with the harness's own status command before reporting it */
59
+ verifyAfterSuccess: boolean;
60
+ }
61
+ /** What the rules found on the screen. */
62
+ export interface LoginRuleMatch {
63
+ url: string | null;
64
+ userCode: string | null;
65
+ needsInput: boolean;
66
+ succeeded: boolean;
67
+ /** Line that matched a failure pattern */
68
+ failureMessage: string | null;
69
+ /** Captured credential — never log or expose it */
70
+ secret: string | null;
71
+ }
72
+ /**
73
+ * Patterns that look like credentials. Screen text exposed to front ends has
74
+ * every match replaced with {@link HARNESS_CONSTANTS.LOGIN.REDACTED}.
75
+ */
76
+ export declare const SECRET_PATTERNS: readonly RegExp[];
77
+ /**
78
+ * Normalize raw PTY output for rule matching.
79
+ *
80
+ * @param raw - Raw bytes from the PTY, as a string
81
+ * @returns Normalized text, a spaceless copy and OSC 8 hyperlink targets
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * normalizeTerminalOutput('\x1b[1mPaste\x1b[1Ccode\x1b[0m\r\n').text; // 'Paste code\n'
86
+ * ```
87
+ */
88
+ export declare function normalizeTerminalOutput(raw: string): NormalizedScreen;
89
+ /**
90
+ * Find runs (URLs, tokens) and join the ones a TUI wrapped across lines.
91
+ *
92
+ * A run that reaches the end of a line at least `minWrappedLineLength` long
93
+ * continues on the next line when that line consists only of continuation
94
+ * characters. Joining stops at the first line that is shorter than the
95
+ * minimum (the last piece of a wrapped run) or that is not a continuation.
96
+ *
97
+ * A run is only returned when it is complete: it ends before the end of its
98
+ * line, or a non-empty line follows it. A run still being written at the end
99
+ * of the buffer (possibly continued by the next PTY chunk) is skipped — unless
100
+ * `final` is set because the process has exited.
101
+ *
102
+ * @param text - Normalized text
103
+ * @param runPattern - Pattern matching a run on one line (the `g` flag is added)
104
+ * @param continuation - Pattern a whole continuation line must match
105
+ * @param options - `minWrappedLineLength` (Infinity disables joining), `final` (no more output will come)
106
+ * @returns Complete runs, in order of appearance
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * extractWrappedRuns('https://x.io/' + 'a'.repeat(40) + '\nbcd\nnext line\n', /https:\/\/\S+/, /^[a-z]+$/);
111
+ * // ['https://x.io/aaaa…abcd']
112
+ * ```
113
+ */
114
+ export declare function extractWrappedRuns(text: string, runPattern: RegExp, continuation: RegExp, options?: {
115
+ minWrappedLineLength?: number;
116
+ final?: boolean;
117
+ }): string[];
118
+ /**
119
+ * All complete URLs on the screen (wrapped ones joined), plus hyperlink targets.
120
+ *
121
+ * @param screen - Normalized screen
122
+ * @param final - The process has exited: accept runs at the end of the buffer
123
+ * @returns URL candidates
124
+ */
125
+ export declare function extractUrlCandidates(screen: NormalizedScreen, final?: boolean): string[];
126
+ /**
127
+ * Apply a rule set to the screen.
128
+ *
129
+ * @param rules - Rule set for the harness login method
130
+ * @param screen - Whole session screen, normalized
131
+ * @param options - `promptScreen`: screen since the user's last input (input prompt and failures are read
132
+ * from it; defaults to `screen`); `final`: the process has exited
133
+ * @returns What was found
134
+ */
135
+ export declare function evaluateLoginRules(rules: LoginRuleSet, screen: NormalizedScreen, options?: {
136
+ promptScreen?: NormalizedScreen;
137
+ final?: boolean;
138
+ }): LoginRuleMatch;
139
+ /**
140
+ * Replace anything that looks like a credential with a placeholder.
141
+ *
142
+ * Besides {@link SECRET_PATTERNS}, every known secret is removed — including
143
+ * the pieces of one a TUI wrapped over several lines.
144
+ *
145
+ * @param text - Text to redact
146
+ * @param knownSecrets - Secrets captured in this session
147
+ * @returns Redacted text
148
+ */
149
+ export declare function redactSecrets(text: string, knownSecrets?: readonly string[]): string;
150
+ /** Claude Code `claude setup-token` (subscription login). */
151
+ export declare const CLAUDE_SETUP_TOKEN_RULES: LoginRuleSet;
152
+ /** Codex `codex login --device-auth` (ChatGPT device-code login). */
153
+ export declare const CODEX_DEVICE_AUTH_RULES: LoginRuleSet;
154
+ /**
155
+ * The rule set for a harness login method.
156
+ *
157
+ * @param harnessId - Harness id
158
+ * @param method - Login method id
159
+ * @returns The rule set, or undefined when the method is not brokered
160
+ */
161
+ export declare function getLoginRules(harnessId: string, method: string): LoginRuleSet | undefined;
162
+ //# sourceMappingURL=login-rules.d.ts.map