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.
- package/config/skills/agent/core/whatsapp-draft/SKILL.md +80 -0
- package/config/skills/agent/core/whatsapp-draft/execute.sh +84 -0
- package/config/skills/agent/core/whatsapp-draft/execute.test.sh +70 -0
- package/config/skills/agent/core/whatsapp-inbox/SKILL.md +75 -0
- package/config/skills/agent/core/whatsapp-inbox/execute.sh +87 -0
- package/config/skills/agent/core/whatsapp-inbox/execute.test.sh +68 -0
- package/config/skills/agent/core/whatsapp-read/SKILL.md +80 -0
- package/config/skills/agent/core/whatsapp-read/execute.sh +116 -0
- package/config/skills/agent/core/whatsapp-read/execute.test.sh +84 -0
- package/config/skills/agent/core/whatsapp-send/SKILL.md +70 -0
- package/config/skills/agent/core/whatsapp-send/execute.sh +84 -0
- package/config/skills/agent/core/whatsapp-send/execute.test.sh +79 -0
- package/dist/backend/backend/src/constants.d.ts +225 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +223 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/harness/harness.controller.d.ts +54 -0
- package/dist/backend/backend/src/controllers/harness/harness.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/harness/harness.controller.js +204 -0
- package/dist/backend/backend/src/controllers/harness/harness.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/harness/harness.routes.d.ts +27 -0
- package/dist/backend/backend/src/controllers/harness/harness.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/harness/harness.routes.js +43 -0
- package/dist/backend/backend/src/controllers/harness/harness.routes.js.map +1 -0
- package/dist/backend/backend/src/controllers/whatsapp/whatsapp-inbox.controller.d.ts +73 -0
- package/dist/backend/backend/src/controllers/whatsapp/whatsapp-inbox.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/whatsapp/whatsapp-inbox.controller.js +310 -0
- package/dist/backend/backend/src/controllers/whatsapp/whatsapp-inbox.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/whatsapp/whatsapp.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/whatsapp/whatsapp.controller.js +53 -4
- package/dist/backend/backend/src/controllers/whatsapp/whatsapp.controller.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +4 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +5 -0
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.js +7 -0
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +5 -0
- package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-client.service.d.ts +2 -1
- package/dist/backend/backend/src/services/cloud/cloud-client.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-client.service.js +4 -3
- package/dist/backend/backend/src/services/cloud/cloud-client.service.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js +43 -1
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js.map +1 -1
- package/dist/backend/backend/src/services/harness/claude-config.utils.d.ts +67 -0
- package/dist/backend/backend/src/services/harness/claude-config.utils.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/claude-config.utils.js +118 -0
- package/dist/backend/backend/src/services/harness/claude-config.utils.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-api-key.service.d.ts +99 -0
- package/dist/backend/backend/src/services/harness/harness-api-key.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-api-key.service.js +157 -0
- package/dist/backend/backend/src/services/harness/harness-api-key.service.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-credentials.store.d.ts +135 -0
- package/dist/backend/backend/src/services/harness/harness-credentials.store.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-credentials.store.js +208 -0
- package/dist/backend/backend/src/services/harness/harness-credentials.store.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-exec.utils.d.ts +85 -0
- package/dist/backend/backend/src/services/harness/harness-exec.utils.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-exec.utils.js +183 -0
- package/dist/backend/backend/src/services/harness/harness-exec.utils.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-install.service.d.ts +106 -0
- package/dist/backend/backend/src/services/harness/harness-install.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-install.service.js +205 -0
- package/dist/backend/backend/src/services/harness/harness-install.service.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-registry.d.ts +86 -0
- package/dist/backend/backend/src/services/harness/harness-registry.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-registry.js +121 -0
- package/dist/backend/backend/src/services/harness/harness-registry.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-status.service.d.ts +173 -0
- package/dist/backend/backend/src/services/harness/harness-status.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness-status.service.js +324 -0
- package/dist/backend/backend/src/services/harness/harness-status.service.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness.service.d.ts +146 -0
- package/dist/backend/backend/src/services/harness/harness.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness.service.js +193 -0
- package/dist/backend/backend/src/services/harness/harness.service.js.map +1 -0
- package/dist/backend/backend/src/services/harness/harness.types.d.ts +138 -0
- package/dist/backend/backend/src/services/harness/harness.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/harness.types.js +51 -0
- package/dist/backend/backend/src/services/harness/harness.types.js.map +1 -0
- package/dist/backend/backend/src/services/harness/login-broker.service.d.ts +238 -0
- package/dist/backend/backend/src/services/harness/login-broker.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/login-broker.service.js +495 -0
- package/dist/backend/backend/src/services/harness/login-broker.service.js.map +1 -0
- package/dist/backend/backend/src/services/harness/login-rules.d.ts +162 -0
- package/dist/backend/backend/src/services/harness/login-rules.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/login-rules.js +344 -0
- package/dist/backend/backend/src/services/harness/login-rules.js.map +1 -0
- package/dist/backend/backend/src/services/harness/orc-harness.store.d.ts +59 -0
- package/dist/backend/backend/src/services/harness/orc-harness.store.d.ts.map +1 -0
- package/dist/backend/backend/src/services/harness/orc-harness.store.js +108 -0
- package/dist/backend/backend/src/services/harness/orc-harness.store.js.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/index.d.ts +5 -1
- package/dist/backend/backend/src/services/whatsapp/index.d.ts.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/index.js +5 -1
- package/dist/backend/backend/src/services/whatsapp/index.js.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp-connection-config.d.ts +59 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-connection-config.d.ts.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-connection-config.js +108 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-connection-config.js.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-draft-gate.d.ts +74 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-draft-gate.d.ts.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-draft-gate.js +82 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-draft-gate.js.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox-capture.d.ts +184 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox-capture.d.ts.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox-capture.js +420 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox-capture.js.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox.store.d.ts +243 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox.store.d.ts.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox.store.js +538 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-inbox.store.js.map +1 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.d.ts +32 -2
- package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.d.ts.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.js +65 -9
- package/dist/backend/backend/src/services/whatsapp/whatsapp-initializer.js.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.d.ts.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.js +11 -0
- package/dist/backend/backend/src/services/whatsapp/whatsapp-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp.service.d.ts +59 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/whatsapp/whatsapp.service.js +159 -9
- package/dist/backend/backend/src/services/whatsapp/whatsapp.service.js.map +1 -1
- package/dist/backend/backend/src/types/whatsapp.types.d.ts +108 -0
- package/dist/backend/backend/src/types/whatsapp.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/whatsapp.types.js +9 -0
- package/dist/backend/backend/src/types/whatsapp.types.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +225 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +223 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts +5 -0
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.js +7 -0
- package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +5 -0
- package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-client.service.d.ts +2 -1
- package/dist/cli/backend/src/services/cloud/cloud-client.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-client.service.js +4 -3
- package/dist/cli/backend/src/services/cloud/cloud-client.service.js.map +1 -1
- package/dist/cli/backend/src/services/harness/claude-config.utils.d.ts +67 -0
- package/dist/cli/backend/src/services/harness/claude-config.utils.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/claude-config.utils.js +118 -0
- package/dist/cli/backend/src/services/harness/claude-config.utils.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-api-key.service.d.ts +99 -0
- package/dist/cli/backend/src/services/harness/harness-api-key.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-api-key.service.js +157 -0
- package/dist/cli/backend/src/services/harness/harness-api-key.service.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-credentials.store.d.ts +135 -0
- package/dist/cli/backend/src/services/harness/harness-credentials.store.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-credentials.store.js +208 -0
- package/dist/cli/backend/src/services/harness/harness-credentials.store.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-exec.utils.d.ts +85 -0
- package/dist/cli/backend/src/services/harness/harness-exec.utils.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-exec.utils.js +183 -0
- package/dist/cli/backend/src/services/harness/harness-exec.utils.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-install.service.d.ts +106 -0
- package/dist/cli/backend/src/services/harness/harness-install.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-install.service.js +205 -0
- package/dist/cli/backend/src/services/harness/harness-install.service.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-registry.d.ts +86 -0
- package/dist/cli/backend/src/services/harness/harness-registry.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-registry.js +121 -0
- package/dist/cli/backend/src/services/harness/harness-registry.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-status.service.d.ts +173 -0
- package/dist/cli/backend/src/services/harness/harness-status.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness-status.service.js +324 -0
- package/dist/cli/backend/src/services/harness/harness-status.service.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness.service.d.ts +146 -0
- package/dist/cli/backend/src/services/harness/harness.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness.service.js +193 -0
- package/dist/cli/backend/src/services/harness/harness.service.js.map +1 -0
- package/dist/cli/backend/src/services/harness/harness.types.d.ts +138 -0
- package/dist/cli/backend/src/services/harness/harness.types.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/harness.types.js +51 -0
- package/dist/cli/backend/src/services/harness/harness.types.js.map +1 -0
- package/dist/cli/backend/src/services/harness/login-broker.service.d.ts +238 -0
- package/dist/cli/backend/src/services/harness/login-broker.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/login-broker.service.js +495 -0
- package/dist/cli/backend/src/services/harness/login-broker.service.js.map +1 -0
- package/dist/cli/backend/src/services/harness/login-rules.d.ts +162 -0
- package/dist/cli/backend/src/services/harness/login-rules.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/login-rules.js +344 -0
- package/dist/cli/backend/src/services/harness/login-rules.js.map +1 -0
- package/dist/cli/backend/src/services/harness/orc-harness.store.d.ts +59 -0
- package/dist/cli/backend/src/services/harness/orc-harness.store.d.ts.map +1 -0
- package/dist/cli/backend/src/services/harness/orc-harness.store.js +108 -0
- package/dist/cli/backend/src/services/harness/orc-harness.store.js.map +1 -0
- package/dist/cli/cli/src/commands/harness-setup.d.ts +159 -0
- package/dist/cli/cli/src/commands/harness-setup.d.ts.map +1 -0
- package/dist/cli/cli/src/commands/harness-setup.js +377 -0
- package/dist/cli/cli/src/commands/harness-setup.js.map +1 -0
- package/dist/cli/cli/src/commands/harness.d.ts +56 -0
- package/dist/cli/cli/src/commands/harness.d.ts.map +1 -0
- package/dist/cli/cli/src/commands/harness.js +97 -0
- package/dist/cli/cli/src/commands/harness.js.map +1 -0
- package/dist/cli/cli/src/commands/onboard.d.ts +108 -50
- package/dist/cli/cli/src/commands/onboard.d.ts.map +1 -1
- package/dist/cli/cli/src/commands/onboard.js +200 -191
- package/dist/cli/cli/src/commands/onboard.js.map +1 -1
- package/dist/cli/cli/src/constants.d.ts +18 -0
- package/dist/cli/cli/src/constants.d.ts.map +1 -1
- package/dist/cli/cli/src/constants.js +18 -0
- package/dist/cli/cli/src/constants.js.map +1 -1
- package/dist/cli/cli/src/index.js +22 -3
- package/dist/cli/cli/src/index.js.map +1 -1
- package/dist/cli/cli/src/utils/harness-engine.d.ts +99 -0
- package/dist/cli/cli/src/utils/harness-engine.d.ts.map +1 -0
- package/dist/cli/cli/src/utils/harness-engine.js +143 -0
- package/dist/cli/cli/src/utils/harness-engine.js.map +1 -0
- package/dist/cli/cli/src/utils/prompt-io.d.ts +25 -0
- package/dist/cli/cli/src/utils/prompt-io.d.ts.map +1 -0
- package/dist/cli/cli/src/utils/prompt-io.js +47 -0
- package/dist/cli/cli/src/utils/prompt-io.js.map +1 -0
- package/frontend/dist/assets/{index-8327e63e.css → index-0a2f783c.css} +1 -1
- package/frontend/dist/assets/{index-07e338a9.js → index-aaf65055.js} +309 -309
- package/frontend/dist/index.html +2 -2
- 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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,iCAAiC,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,EAA2B,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AACrG,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC7E,OAAO,EACN,qBAAqB,EACrB,oBAAoB,GAMpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,uBAAuB,EAAE,aAAa,EAAqB,MAAM,kBAAkB,CAAC;AAiBhI,+FAA+F;AAC/F,MAAM,CAAC,MAAM,mBAAmB,GAAG;IAClC,8BAA8B;IAC9B,MAAM,EAAE,QAAQ;IAChB,yCAAyC;IACzC,QAAQ,EAAE,UAAU;CACX,CAAC;AAKX,iDAAiD;AACjD,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAMzB;IALjB;;;OAGG;IACH,YACiB,IAA0B,EAC1C,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,SAAI,GAAJ,IAAI,CAAsB;QAI1C,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IAChC,CAAC;CACD;AAoCD;;;;;;;GAOG;AACH,MAAM,eAAe,GAAqB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,CACjE,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AAE/H,qEAAqE;AACrE,MAAM,OAAO,kBAAmB,SAAQ,YAAY;IAClC,QAAQ,CAAmB;IAC3B,cAAc,CAAsD;IACpE,GAAG,CAAoB;IACvB,OAAO,CAAS;IAChB,WAAW,CAA0B;IACrC,MAAM,CAA6C;IACnD,mBAAmB,CAAa;IAChC,GAAG,CAAe;IAClB,SAAS,CAAe;IACxB,MAAM,CAAgB;IACtB,SAAS,CAAS;IAClB,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC7D,gDAAgD;IAC/B,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/C;;OAEG;IACH,YAAY,OAAwB,EAAE;QACrC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,eAAe,CAAC;QACjD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,cAAc,IAAI,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC,iBAAiB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;QACzG,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;QACnC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;QAC5C,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,WAAW,IAAI,0BAA0B,EAAE,CAAC;QACpE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,mBAAmB,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,iCAAiC,EAAE,CAAC,CAAC;QACxG,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;QAChC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,UAAU,CAAC;QAC9C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,qBAAqB,CAAC;QACnD,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,iBAAiB,CAAC,KAAK,CAAC,UAAU,CAAC;IACvE,CAAC;IAED;;;;;;OAMG;IACH,QAAQ;QACP,MAAM,OAAO,GAAG,2BAA2B,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7D,MAAM,GAAG,GAA2B,EAAE,CAAC;QACvC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACpD,IAAI,KAAK,KAAK,SAAS,IAAI,GAAG,KAAK,sBAAsB,CAAC,GAAG,CAAC,SAAS;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAC3F,CAAC;QACD,GAAG,CAAC,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QACzD,GAAG,CAAC,OAAO,GAAG,iBAAiB,CAAC,KAAK,CAAC,sBAAsB,CAAC;QAC7D,GAAG,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC5B,OAAO,GAAG,CAAC;IACZ,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,SAAiB,EAAE,MAAc;QACtC,MAAM,GAAG,GAAG,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,gBAAgB,CAAC,iBAAiB,EAAE,oBAAoB,SAAS,EAAE,CAAC,CAAC;QACzF,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9E,MAAM,IAAI,gBAAgB,CAAC,oBAAoB,EAAE,GAAG,GAAG,CAAC,WAAW,YAAY,MAAM,6BAA6B,CAAC,CAAC;QACrH,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3C,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QACtB,IAAI,CAAC,KAAK,EAAE,CAAC;QAEb,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QACvE,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,MAAM,IAAI,gBAAgB,CAAC,eAAe,EAAE,GAAG,GAAG,CAAC,WAAW,wBAAwB,SAAS,CAAC,MAAM,CAAC,OAAO,eAAe,CAAC,CAAC;QAChI,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAClD,IAAI,WAAW,GAAoC,GAAG,EAAE,CAAC,SAAS,CAAC;QACnE,MAAM,IAAI,GAAG,IAAI,OAAO,CAAe,CAAC,OAAO,EAAE,EAAE;YAClD,WAAW,GAAG,OAAO,CAAC;QACvB,CAAC,CAAC,CAAC;QACH,MAAM,MAAM,GAAkB;YAC7B,OAAO,EAAE;gBACR,EAAE,EAAE,IAAI,CAAC,SAAS,EAAE;gBACpB,SAAS,EAAE,GAAG,CAAC,EAAE;gBACjB,MAAM,EAAE,MAAuB;gBAC/B,KAAK,EAAE,UAAU;gBACjB,GAAG,EAAE,IAAI;gBACT,QAAQ,EAAE,IAAI;gBACd,UAAU,EAAE,KAAK;gBACjB,OAAO,EAAE,IAAI;gBACb,MAAM,EAAE,EAAE;gBACV,SAAS,EAAE,MAAM;gBACjB,SAAS,EAAE,MAAM;aACjB;YACD,KAAK;YACL,GAAG,EAAE,IAAI;YACT,GAAG,EAAE,EAAE;YACP,WAAW,EAAE,CAAC;YACd,OAAO,EAAE,EAAE;YACX,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,KAAK;YACb,UAAU,EAAE,IAAI;YAChB,IAAI;YACJ,WAAW;SACX,CAAC;QAEF,IAAI,CAAC;YACJ,MAAM,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;gBAC9D,IAAI,EAAE,iBAAiB,CAAC,KAAK,CAAC,QAAQ;gBACtC,IAAI,EAAE,iBAAiB,CAAC,KAAK,CAAC,QAAQ;gBACtC,GAAG,EAAE,IAAI,CAAC,OAAO;gBACjB,GAAG;aACH,CAAC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,IAAI,gBAAgB,CAAC,cAAc,EAAE,qBAAqB,SAAS,CAAC,MAAM,CAAC,OAAO,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1J,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAC7C,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;QAC3D,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;YAClC,KAAK,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,sCAAsC,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC1H,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;QACvB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,8BAA8B,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAC9G,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;IAC9B,CAAC;IAED;;;;;;OAMG;IACH,GAAG,CAAC,SAAiB;QACpB,OAAO,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;IACrD,CAAC;IAED;;;;;OAKG;IACH,gBAAgB,CAAC,SAAiB;QACjC,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC7C,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;QACzH,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,SAAiB,EAAE,IAAY;QACpC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAChF,MAAM,IAAI,gBAAgB,CAAC,YAAY,EAAE,iCAAiC,CAAC,CAAC;QAC7E,CAAC;QACD,MAAM,CAAC,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;QACvC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,GAAG,iBAAiB,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;QACpF,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9E,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;IAC9B,CAAC;IAED;;;;;;OAMG;IACH,MAAM,CAAC,SAAiB;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,kBAAkB,CAAC,CAAC;QACtG,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;IAC9B,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,iBAAiB,CAAC,SAAiB;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;QAC7E,OAAO,MAAM,CAAC,IAAI,CAAC;IACpB,CAAC;IAED,8DAA8D;IAC9D,QAAQ;QACP,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YAC7C,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,2CAA2C,CAAC,CAAC;QAChI,CAAC;IACF,CAAC;IAED;;;;;;OAMG;IACK,aAAa,CAAC,SAAiB;QACtC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,gBAAgB,CAAC,WAAW,EAAE,4BAA4B,SAAS,EAAE,CAAC,CAAC;QAC9F,OAAO,MAAM,CAAC;IACf,CAAC;IAED;;;;;OAKG;IACK,UAAU,CAAC,MAAqB,EAAE,IAAY;QACrD,IAAI,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO;QACvD,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC;QACnB,MAAM,GAAG,GAAG,iBAAiB,CAAC,KAAK,CAAC,oBAAoB,CAAC;QACzD,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;YACrC,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACpC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED;;;;;OAKG;IACK,QAAQ,CAAC,MAAqB,EAAE,KAAc;QACrD,MAAM,MAAM,GAAG,uBAAuB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACnD,MAAM,YAAY,GAAG,uBAAuB,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;QACnF,MAAM,KAAK,GAAG,kBAAkB,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;QAChF,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAE9F,MAAM,KAAK,GAA0B;YACpC,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG;YACpC,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ;YACnD,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC;SAC/C,CAAC;QAEF,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACrB,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAC3B,KAAK,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;YACzC,OAAO;QACR,CAAC;QACD,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;YAC1B,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;gBACtB,iDAAiD;gBACjD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC;YAC1F,CAAC;iBAAM,CAAC;gBACP,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;gBAC3B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,cAAc,CAAC,CAAC;YACrD,CAAC;YACD,OAAO;QACR,CAAC;QACD,IAAI,KAAK,GAAsB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;QACpD,IAAI,KAAK,KAAK,UAAU,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,UAAU,CAAC;YAAE,KAAK,GAAG,eAAe,CAAC;QACvG,IAAI,KAAK,KAAK,WAAW,IAAI,KAAK,CAAC,UAAU;YAAE,KAAK,GAAG,eAAe,CAAC;QACvE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,QAAQ,CAAC,MAAqB,EAAE,MAAqB;QAClE,IAAI,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO;QACvD,IAAI,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAC;YACxC,IAAI,CAAC,MAAM;gBAAE,OAAO;YACpB,IAAI,CAAC;gBACJ,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAClC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,sDAAsD,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAC9I,OAAO;YACR,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,mDAAmD,CAAC,CAAC;YACtF,OAAO;QACR,CAAC;QACD,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,OAAO,CAAC,MAAqB;QAC1C,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO;QAChG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACtC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;QAC/D,IAAI,CAAC;YACJ,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAChG,IAAI,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;gBAAE,OAAO;YACvD,IAAI,EAAE;gBAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC;;gBAClD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,gFAAgF,CAAC,CAAC;QACtH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,gCAAgC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACzH,CAAC;QACF,CAAC;gBAAS,CAAC;YACV,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC1C,CAAC;IACF,CAAC;IAED;;;;;OAKG;IACK,WAAW,CAAC,MAAqB,EAAE,MAAc;QACxD,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,KAAK,iBAAiB,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YACpE,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,CAAC;gBACJ,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC5B,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,4CAA4C,EAAE,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACnI,CAAC;QACF,CAAC;QACD,8DAA8D;QAC9D,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC7E,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,2BAA2B,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IACtH,CAAC;IAED;;;;;OAKG;IACK,KAAK,CAAC,UAAU,CAAC,MAAqB,EAAE,QAAgB;QAC/D,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC;QACrB,IAAI,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO;QACvD,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC5B,IAAI,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YAAE,OAAO;QAChG,IAAI,MAAM,CAAC,KAAK,CAAC,iBAAiB,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;YACtD,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC3B,OAAO;QACR,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,OAAO,IAAI,kCAAkC,QAAQ,8BAA8B,CAAC,CAAC;IACnI,CAAC;IAED;;;;;;OAMG;IACK,MAAM,CAAC,MAAqB,EAAE,KAAwB,EAAE,OAAe;QAC9E,IAAI,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO;QACvD,IAAI,MAAM,CAAC,KAAK;YAAE,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC7C,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC;QACpB,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC/B,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,IAAI,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YAClC,IAAI,CAAC;gBACJ,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACnB,CAAC;YAAC,MAAM,CAAC;gBACR,gBAAgB;YACjB,CAAC;QACF,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,+BAA+B,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QAChI,MAAM,QAAQ,GAAG,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;QACvC,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC7B,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACnD,CAAC;IAED;;;;;OAKG;IACK,MAAM,CAAC,MAAqB,EAAE,KAA4B;QACjE,MAAM,CAAC,OAAO,GAAG,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;QAChG,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;IAC9D,CAAC;IAED;;;;;;OAMG;IACK,aAAa,CAAC,MAAqB,EAAE,IAAY;QACxD,MAAM,QAAQ,GAAG,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;QACrD,MAAM,GAAG,GAAG,iBAAiB,CAAC,KAAK,CAAC,gBAAgB,CAAC;QACrD,OAAO,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IACjF,CAAC;IAED,gEAAgE;IACxD,KAAK;QACZ,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,iBAAiB,CAAC,KAAK,CAAC,oBAAoB,CAAC;QACzE,KAAK,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC1C,IAAI,MAAM,CAAC,UAAU,KAAK,IAAI,IAAI,MAAM,CAAC,UAAU,GAAG,MAAM;gBAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxF,CAAC;IACF,CAAC;CACD"}
|
|
@@ -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
|