crewly 1.20.147 → 1.20.149
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/constants.ts +43 -0
- package/config/roles/developer/prompt.md +2 -1
- package/config/roles/orchestrator/prompt.md +3 -2
- package/config/roles/product-manager/prompt.md +2 -1
- package/config/roles/team-leader/tl-addon.md +11 -2
- package/config/skills/team-leader/set-member-model/SKILL.md +60 -0
- package/config/skills/team-leader/set-member-model/execute.sh +55 -0
- package/config/skills/team-leader/set-member-model/execute.test.sh +76 -0
- package/dist/backend/backend/src/constants.d.ts +75 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +58 -1
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js +21 -0
- package/dist/backend/backend/src/controllers/monitoring/terminal.controller.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts +11 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +86 -11
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +9 -6
- 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 +22 -16
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.d.ts +248 -0
- package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.js +572 -0
- package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.js.map +1 -0
- package/dist/backend/backend/src/services/agent/runtime-session-recovery.d.ts +8 -0
- package/dist/backend/backend/src/services/agent/runtime-session-recovery.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/runtime-session-recovery.js +12 -1
- package/dist/backend/backend/src/services/agent/runtime-session-recovery.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-disconnect-notice.service.d.ts +200 -0
- package/dist/backend/backend/src/services/cloud/cloud-disconnect-notice.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-disconnect-notice.service.js +485 -0
- package/dist/backend/backend/src/services/cloud/cloud-disconnect-notice.service.js.map +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-disconnect-notice.utils.d.ts +163 -0
- package/dist/backend/backend/src/services/cloud/cloud-disconnect-notice.utils.d.ts.map +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-disconnect-notice.utils.js +176 -0
- package/dist/backend/backend/src/services/cloud/cloud-disconnect-notice.utils.js.map +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-login-runner.d.ts +106 -0
- package/dist/backend/backend/src/services/cloud/cloud-login-runner.d.ts.map +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-login-runner.js +238 -0
- package/dist/backend/backend/src/services/cloud/cloud-login-runner.js.map +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-sync.service.d.ts +16 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.service.js +38 -0
- package/dist/backend/backend/src/services/cloud/cloud-sync.service.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts +14 -0
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.d.ts +10 -0
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.js +41 -3
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.js.map +1 -1
- package/dist/backend/backend/src/services/session/session-state-persistence.d.ts +9 -0
- package/dist/backend/backend/src/services/session/session-state-persistence.d.ts.map +1 -1
- package/dist/backend/backend/src/services/session/session-state-persistence.js +20 -0
- package/dist/backend/backend/src/services/session/session-state-persistence.js.map +1 -1
- package/dist/backend/backend/src/services/skill/skill-permission.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/skill/skill-permission.service.js +1 -0
- package/dist/backend/backend/src/services/skill/skill-permission.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-owner-direct-dm.d.ts +55 -0
- package/dist/backend/backend/src/services/slack/slack-owner-direct-dm.d.ts.map +1 -0
- package/dist/backend/backend/src/services/slack/slack-owner-direct-dm.js +114 -0
- package/dist/backend/backend/src/services/slack/slack-owner-direct-dm.js.map +1 -0
- package/dist/backend/backend/src/services/v3/tl-auto-verify.service.d.ts +25 -1
- package/dist/backend/backend/src/services/v3/tl-auto-verify.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/tl-auto-verify.service.js +82 -2
- package/dist/backend/backend/src/services/v3/tl-auto-verify.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.d.ts +27 -0
- package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js +48 -2
- package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js.map +1 -1
- package/dist/backend/backend/src/types/settings.types.d.ts +8 -0
- package/dist/backend/backend/src/types/settings.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/settings.types.js +4 -0
- package/dist/backend/backend/src/types/settings.types.js.map +1 -1
- package/dist/backend/backend/src/utils/member-default-model.utils.d.ts +66 -0
- package/dist/backend/backend/src/utils/member-default-model.utils.d.ts.map +1 -0
- package/dist/backend/backend/src/utils/member-default-model.utils.js +95 -0
- package/dist/backend/backend/src/utils/member-default-model.utils.js.map +1 -0
- package/dist/backend/backend/src/utils/trigger-workitem-dedupe.utils.d.ts +53 -0
- package/dist/backend/backend/src/utils/trigger-workitem-dedupe.utils.d.ts.map +1 -0
- package/dist/backend/backend/src/utils/trigger-workitem-dedupe.utils.js +75 -0
- package/dist/backend/backend/src/utils/trigger-workitem-dedupe.utils.js.map +1 -0
- package/dist/backend/build-info.json +2 -2
- package/dist/backend/config/constants.d.ts +42 -0
- package/dist/backend/config/constants.d.ts.map +1 -1
- package/dist/backend/config/constants.js +42 -0
- package/dist/backend/config/constants.js.map +1 -1
- package/dist/cli/backend/src/constants.d.ts +75 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +58 -1
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts +9 -6
- 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 +22 -16
- package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/cli/backend/src/services/agent/runtime-session-recovery.d.ts +8 -0
- package/dist/cli/backend/src/services/agent/runtime-session-recovery.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/runtime-session-recovery.js +12 -1
- package/dist/cli/backend/src/services/agent/runtime-session-recovery.js.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.service.d.ts +16 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.service.js +38 -0
- package/dist/cli/backend/src/services/cloud/cloud-sync.service.js.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts +14 -0
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
- package/dist/cli/backend/src/services/session/session-state-persistence.d.ts +9 -0
- package/dist/cli/backend/src/services/session/session-state-persistence.d.ts.map +1 -1
- package/dist/cli/backend/src/services/session/session-state-persistence.js +20 -0
- package/dist/cli/backend/src/services/session/session-state-persistence.js.map +1 -1
- package/dist/cli/backend/src/types/settings.types.d.ts +8 -0
- package/dist/cli/backend/src/types/settings.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/settings.types.js +4 -0
- package/dist/cli/backend/src/types/settings.types.js.map +1 -1
- package/dist/cli/backend/src/utils/member-default-model.utils.d.ts +66 -0
- package/dist/cli/backend/src/utils/member-default-model.utils.d.ts.map +1 -0
- package/dist/cli/backend/src/utils/member-default-model.utils.js +95 -0
- package/dist/cli/backend/src/utils/member-default-model.utils.js.map +1 -0
- package/dist/cli/config/constants.d.ts +42 -0
- package/dist/cli/config/constants.d.ts.map +1 -1
- package/dist/cli/config/constants.js +42 -0
- package/dist/cli/config/constants.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-disconnect-notice.utils.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/cloud-disconnect-notice.utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,6CAA6C;AAC7C,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,aAAa,CAAC;AAEtD,4CAA4C;AAC5C,MAAM,WAAW,eAAe;IAC/B,sFAAsF;IACtF,QAAQ,EAAE,OAAO,CAAC;IAClB,8BAA8B;IAC9B,MAAM,EAAE,eAAe,CAAC;IACxB,6EAA6E;IAC7E,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAC1B;IAAE,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,SAAS,CAAA;CAAE,GAClD;IAAE,MAAM,EAAE,cAAc,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,iBAAiB,CAO5E;AAED,6EAA6E;AAC7E,MAAM,WAAW,qBAAqB;IACrC,+CAA+C;IAC/C,gBAAgB,EAAE,MAAM,CAAC;IACzB,uBAAuB;IACvB,MAAM,EAAE,gBAAgB,CAAC;IACzB,6DAA6D;IAC7D,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,8EAA8E;IAC9E,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAKxG;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAGlE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAIlD;AAQD,iDAAiD;AACjD,MAAM,WAAW,qBAAqB;IACrC,0BAA0B;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU;IACV,MAAM,EAAE,gBAAgB,CAAC;IACzB,+BAA+B;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,qBAAqB,GAAG,MAAM,CAY5E;AAED,0CAA0C;AAC1C,eAAO,MAAM,kBAAkB,2GAA2B,CAAC;AAE3D;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,GAAG,IAAI,CAgB1E;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,IAAI,CAGjF;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMnD"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloud disconnect notice — pure rules.
|
|
3
|
+
*
|
|
4
|
+
* Detection ({@link evaluateDisconnect}), the notice rate limit
|
|
5
|
+
* ({@link shouldNotify}), the owner-facing Chinese texts and the persisted
|
|
6
|
+
* state file. No timers, no network: {@link CloudDisconnectNoticeService}
|
|
7
|
+
* drives these.
|
|
8
|
+
*
|
|
9
|
+
* Background (2026-09-27): a second Mac stopped heart-beating to Crewly
|
|
10
|
+
* Cloud and never reconnected, while its agents kept posting to Slack with
|
|
11
|
+
* their own bot tokens. Inbound Slack only reaches a machine through Cloud →
|
|
12
|
+
* relay, so every DM to its agents queued in Cloud for hours and nobody was
|
|
13
|
+
* told.
|
|
14
|
+
*
|
|
15
|
+
* @module services/cloud/cloud-disconnect-notice.utils
|
|
16
|
+
*/
|
|
17
|
+
import * as fs from 'fs';
|
|
18
|
+
import * as path from 'path';
|
|
19
|
+
/**
|
|
20
|
+
* Decide whether this machine is disconnected from Cloud.
|
|
21
|
+
*
|
|
22
|
+
* Disconnected = signed in AND (sync is `auth_expired`, OR Cloud has not
|
|
23
|
+
* answered a request for `thresholdMs`). Never fires on a machine that was
|
|
24
|
+
* never signed in or was logged out on purpose — both leave no Cloud config.
|
|
25
|
+
* `connected` needs a real answer from Cloud, so a restart that has not
|
|
26
|
+
* reached Cloud yet reads `pending`, not `connected`.
|
|
27
|
+
*
|
|
28
|
+
* @param input - Sign-in, health, clock and threshold
|
|
29
|
+
* @returns The verdict
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* evaluateDisconnect({ signedIn: true, health: { state: 'auth_expired', lastContactAt: t0, startedAt: t0, authRejected: true },
|
|
34
|
+
* monitorStartedAt: t0, now: t0 + 60_000, thresholdMs: 900_000 });
|
|
35
|
+
* // { status: 'disconnected', reason: 'auth', since: t0 }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export function evaluateDisconnect(input) {
|
|
39
|
+
const { signedIn, health, monitorStartedAt, now, thresholdMs } = input;
|
|
40
|
+
if (!signedIn)
|
|
41
|
+
return { status: 'signed_out' };
|
|
42
|
+
const since = health.lastContactAt ?? health.startedAt ?? monitorStartedAt;
|
|
43
|
+
if (health.state === 'auth_expired')
|
|
44
|
+
return { status: 'disconnected', reason: 'auth', since };
|
|
45
|
+
if (now - since < thresholdMs)
|
|
46
|
+
return { status: health.lastContactAt !== null ? 'connected' : 'pending' };
|
|
47
|
+
return { status: 'disconnected', reason: health.authRejected ? 'auth' : 'unreachable', since };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Whether a new notice may go out: none yet in this episode, or the last one
|
|
51
|
+
* is at least `repeatMs` old.
|
|
52
|
+
*
|
|
53
|
+
* @param state - Persisted state (null = no episode yet)
|
|
54
|
+
* @param now - Now (epoch ms)
|
|
55
|
+
* @param repeatMs - Minimum gap between notices
|
|
56
|
+
* @returns True when a notice may be sent
|
|
57
|
+
*/
|
|
58
|
+
export function shouldNotify(state, now, repeatMs) {
|
|
59
|
+
if (!state?.lastNotifiedAt)
|
|
60
|
+
return true;
|
|
61
|
+
const last = Date.parse(state.lastNotifiedAt);
|
|
62
|
+
if (Number.isNaN(last))
|
|
63
|
+
return true;
|
|
64
|
+
return now - last >= repeatMs;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Whether the notice is switched on (`CREWLY_CLOUD_DISCONNECT_NOTICE`).
|
|
68
|
+
*
|
|
69
|
+
* @param value - The env value
|
|
70
|
+
* @returns False for `0`, `false`, `off`, `no`; true otherwise (default on)
|
|
71
|
+
*/
|
|
72
|
+
export function isNoticeEnabled(value) {
|
|
73
|
+
if (value === undefined)
|
|
74
|
+
return true;
|
|
75
|
+
return !['0', 'false', 'off', 'no'].includes(value.trim().toLowerCase());
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Local time in the owner's reading format, e.g. `9月27日 23:38`.
|
|
79
|
+
*
|
|
80
|
+
* @param ms - Epoch ms
|
|
81
|
+
* @returns Local month/day hour:minute
|
|
82
|
+
*/
|
|
83
|
+
export function formatLocalTime(ms) {
|
|
84
|
+
const d = new Date(ms);
|
|
85
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
86
|
+
return `${d.getMonth() + 1}月${d.getDate()}日 ${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
|
87
|
+
}
|
|
88
|
+
/** Owner-facing reason text. */
|
|
89
|
+
const REASON_TEXT = {
|
|
90
|
+
auth: '登录已过期',
|
|
91
|
+
unreachable: '网络连不上 Cloud',
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* The notice DMed to the owner.
|
|
95
|
+
*
|
|
96
|
+
* @param input - Device, reason, start time and the login link (if any)
|
|
97
|
+
* @returns Plain text (the sender escapes it for Slack)
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* composeDisconnectNotice({ deviceName: 'iriss-air.lan', reason: 'auth', since, loginUrl: 'https://crewlyai.com/cloud/pair?code=ABCD-2345' });
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export function composeDisconnectNotice(input) {
|
|
105
|
+
const head = `Crewly(本机:${input.deviceName})连不上 Crewly Cloud 了(${REASON_TEXT[input.reason]},从 ${formatLocalTime(input.since)} 起)。` +
|
|
106
|
+
'发给这台机器上 agent 的 Slack 消息会在 Cloud 排队,收不到。';
|
|
107
|
+
if (input.loginUrl) {
|
|
108
|
+
const code = input.userCode ? `,核对码 ${input.userCode}` : '';
|
|
109
|
+
return `${head}点这里重新登录:${input.loginUrl}(在手机上点一下即可${code}),登录后排队的消息会自动送到。`;
|
|
110
|
+
}
|
|
111
|
+
if (input.reason === 'auth') {
|
|
112
|
+
return `${head}暂时拿不到重新登录的链接,Crewly 会继续重试,拿到后会更新这条消息。`;
|
|
113
|
+
}
|
|
114
|
+
return `${head}Crewly 会继续自动重连,连上后排队的消息会自动送到。`;
|
|
115
|
+
}
|
|
116
|
+
/** Follow-up once the machine is back. */
|
|
117
|
+
export const RECONNECTED_NOTICE = '已重新连上 Cloud,排队的消息正在送达。';
|
|
118
|
+
/**
|
|
119
|
+
* Brief note when a re-login did not finish.
|
|
120
|
+
*
|
|
121
|
+
* @param detail - What happened (e.g. 登录被拒绝)
|
|
122
|
+
* @param retryAt - When the next link goes out (epoch ms)
|
|
123
|
+
* @returns The note
|
|
124
|
+
*/
|
|
125
|
+
export function composeLoginFailedNotice(detail, retryAt) {
|
|
126
|
+
return `重新登录没有完成(${detail})。Crewly 会在 ${formatLocalTime(retryAt)} 再发一次新链接。`;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Read the persisted state.
|
|
130
|
+
*
|
|
131
|
+
* @param file - State file
|
|
132
|
+
* @returns The state, or null when missing / unreadable
|
|
133
|
+
*/
|
|
134
|
+
export function readNoticeState(file) {
|
|
135
|
+
try {
|
|
136
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
137
|
+
if (typeof parsed.episodeStartedAt !== 'string')
|
|
138
|
+
return null;
|
|
139
|
+
return {
|
|
140
|
+
episodeStartedAt: parsed.episodeStartedAt,
|
|
141
|
+
reason: parsed.reason === 'auth' ? 'auth' : 'unreachable',
|
|
142
|
+
lastNotifiedAt: typeof parsed.lastNotifiedAt === 'string' ? parsed.lastNotifiedAt : null,
|
|
143
|
+
...(typeof parsed.channelId === 'string' ? { channelId: parsed.channelId } : {}),
|
|
144
|
+
...(typeof parsed.messageTs === 'string' ? { messageTs: parsed.messageTs } : {}),
|
|
145
|
+
...(typeof parsed.hasLink === 'boolean' ? { hasLink: parsed.hasLink } : {}),
|
|
146
|
+
...(typeof parsed.loginBlockedUntil === 'string' ? { loginBlockedUntil: parsed.loginBlockedUntil } : {}),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Persist the state.
|
|
155
|
+
*
|
|
156
|
+
* @param file - State file
|
|
157
|
+
* @param state - State to write
|
|
158
|
+
*/
|
|
159
|
+
export function writeNoticeState(file, state) {
|
|
160
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
161
|
+
fs.writeFileSync(file, JSON.stringify(state, null, 2), 'utf-8');
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Remove the state (episode over).
|
|
165
|
+
*
|
|
166
|
+
* @param file - State file
|
|
167
|
+
*/
|
|
168
|
+
export function clearNoticeState(file) {
|
|
169
|
+
try {
|
|
170
|
+
fs.unlinkSync(file);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// Already gone
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=cloud-disconnect-notice.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-disconnect-notice.utils.js","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/cloud-disconnect-notice.utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs `crewly cloud login` in a PTY on the owner's behalf.
|
|
3
|
+
*
|
|
4
|
+
* Same pattern as the harness login broker (login-broker.service.ts): spawn
|
|
5
|
+
* the real login command in a wide PTY, normalize the screen, read the
|
|
6
|
+
* sign-in link and code off it, type the owner's reply in when the command
|
|
7
|
+
* asks for one, and report success / failure from the output and exit code.
|
|
8
|
+
* The CLI does the rest itself — device pairing with crewly-auth, saving
|
|
9
|
+
* `$CREWLY_HOME/cloud/config.json`, and `POST /api/cloud/connect` on this
|
|
10
|
+
* backend.
|
|
11
|
+
*
|
|
12
|
+
* Secrets: the device-code flow prints no token. A reply typed into the PTY
|
|
13
|
+
* (a pasted token in the `--paste` flow) is echoed by the terminal, so the
|
|
14
|
+
* screen is never logged or exposed — only the link, the code and the state.
|
|
15
|
+
*
|
|
16
|
+
* @module services/cloud/cloud-login-runner
|
|
17
|
+
*/
|
|
18
|
+
import type { BrokerPtySpawner } from '../harness/login-broker.service.js';
|
|
19
|
+
/** Where a CLI login run stands. */
|
|
20
|
+
export type CloudLoginState = 'starting' | 'awaiting_user' | 'verifying' | 'succeeded' | 'failed' | 'expired' | 'timed_out' | 'cancelled';
|
|
21
|
+
/** Snapshot of a run (never contains the screen). */
|
|
22
|
+
export interface CloudLoginSnapshot {
|
|
23
|
+
state: CloudLoginState;
|
|
24
|
+
/** Approve page with the code filled in */
|
|
25
|
+
url: string | null;
|
|
26
|
+
/** Code shown on the approve page */
|
|
27
|
+
userCode: string | null;
|
|
28
|
+
/** The CLI is waiting for typed input */
|
|
29
|
+
needsInput: boolean;
|
|
30
|
+
/** Short reason for a failure (from the CLI's ✗ line) */
|
|
31
|
+
message: string | null;
|
|
32
|
+
}
|
|
33
|
+
/** A live CLI login run. */
|
|
34
|
+
export interface CloudLoginHandle {
|
|
35
|
+
/** Current snapshot */
|
|
36
|
+
get(): CloudLoginSnapshot;
|
|
37
|
+
/** Resolves once a link is shown, the run ends, or `timeoutMs` passes */
|
|
38
|
+
waitForLink(timeoutMs: number): Promise<CloudLoginSnapshot>;
|
|
39
|
+
/** Resolves when the run ends */
|
|
40
|
+
done: Promise<CloudLoginSnapshot>;
|
|
41
|
+
/** Type a reply followed by Enter */
|
|
42
|
+
input(text: string): void;
|
|
43
|
+
/** Kill the run */
|
|
44
|
+
cancel(): void;
|
|
45
|
+
/** Subscribe to changes */
|
|
46
|
+
onChange(listener: (snapshot: CloudLoginSnapshot) => void): void;
|
|
47
|
+
}
|
|
48
|
+
/** Dependencies (injectable for tests). */
|
|
49
|
+
export interface CloudLoginRunnerDeps {
|
|
50
|
+
/** Node executable (defaults to the running one — never trust PATH) */
|
|
51
|
+
nodePath?: string;
|
|
52
|
+
/** CLI entry script (`<package>/dist/cli/cli/src/index.js`) */
|
|
53
|
+
cliEntry: string;
|
|
54
|
+
env?: NodeJS.ProcessEnv;
|
|
55
|
+
spawnPty?: BrokerPtySpawner;
|
|
56
|
+
timeoutMs?: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Pick the sign-in link off the screen: the approve page with the code filled
|
|
60
|
+
* in when present, else the first link.
|
|
61
|
+
*
|
|
62
|
+
* @param text - Normalized screen
|
|
63
|
+
* @param hyperlinks - OSC 8 link targets
|
|
64
|
+
* @returns The link, or null
|
|
65
|
+
*/
|
|
66
|
+
export declare function extractLoginUrl(text: string, hyperlinks?: string[]): string | null;
|
|
67
|
+
/**
|
|
68
|
+
* Read the run's state off the screen.
|
|
69
|
+
*
|
|
70
|
+
* @param screenText - Normalized screen
|
|
71
|
+
* @param sinceInput - Normalized output after the last typed reply
|
|
72
|
+
* @param hyperlinks - OSC 8 link targets
|
|
73
|
+
* @returns Link, code, input prompt, success, expiry and the last ✗ line
|
|
74
|
+
*/
|
|
75
|
+
export declare function parseCloudLoginScreen(screenText: string, sinceInput: string, hyperlinks?: string[]): {
|
|
76
|
+
url: string | null;
|
|
77
|
+
userCode: string | null;
|
|
78
|
+
needsInput: boolean;
|
|
79
|
+
succeeded: boolean;
|
|
80
|
+
expired: boolean;
|
|
81
|
+
failure: string | null;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Environment for the CLI: the backend's own (CREWLY_HOME, CREWLY_CLOUD_URL
|
|
85
|
+
* carry over), no colours, and no browser on this machine.
|
|
86
|
+
*
|
|
87
|
+
* @param env - Source environment
|
|
88
|
+
* @returns String-only env map
|
|
89
|
+
*/
|
|
90
|
+
export declare function buildCloudLoginEnv(env: NodeJS.ProcessEnv): Record<string, string>;
|
|
91
|
+
/**
|
|
92
|
+
* Start `crewly cloud login --no-browser` in a PTY.
|
|
93
|
+
*
|
|
94
|
+
* @param deps - CLI location and injectables
|
|
95
|
+
* @returns The run
|
|
96
|
+
* @throws Error when the PTY cannot be spawned
|
|
97
|
+
*/
|
|
98
|
+
export declare function startCloudLogin(deps: CloudLoginRunnerDeps): CloudLoginHandle;
|
|
99
|
+
/**
|
|
100
|
+
* Whether a run has ended.
|
|
101
|
+
*
|
|
102
|
+
* @param state - Run state
|
|
103
|
+
* @returns True for a terminal state
|
|
104
|
+
*/
|
|
105
|
+
export declare function isCloudLoginFinished(state: CloudLoginState): boolean;
|
|
106
|
+
//# sourceMappingURL=cloud-login-runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-login-runner.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/cloud-login-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,OAAO,KAAK,EAAa,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAItF,oCAAoC;AACpC,MAAM,MAAM,eAAe,GACxB,UAAU,GACV,eAAe,GACf,WAAW,GACX,WAAW,GACX,QAAQ,GACR,SAAS,GACT,WAAW,GACX,WAAW,CAAC;AAEf,qDAAqD;AACrD,MAAM,WAAW,kBAAkB;IAClC,KAAK,EAAE,eAAe,CAAC;IACvB,2CAA2C;IAC3C,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,qCAAqC;IACrC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,yCAAyC;IACzC,UAAU,EAAE,OAAO,CAAC;IACpB,yDAAyD;IACzD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,4BAA4B;AAC5B,MAAM,WAAW,gBAAgB;IAChC,uBAAuB;IACvB,GAAG,IAAI,kBAAkB,CAAC;IAC1B,yEAAyE;IACzE,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC5D,iCAAiC;IACjC,IAAI,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAClC,qCAAqC;IACrC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,mBAAmB;IACnB,MAAM,IAAI,IAAI,CAAC;IACf,2BAA2B;IAC3B,QAAQ,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,IAAI,GAAG,IAAI,CAAC;CACjE;AAED,2CAA2C;AAC3C,MAAM,WAAW,oBAAoB;IACpC,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAiBD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,GAAE,MAAM,EAAO,GAAG,MAAM,GAAG,IAAI,CAItF;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACpC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,UAAU,GAAE,MAAM,EAAO,GACvB;IAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,UAAU,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAWpI;AAaD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUjF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,oBAAoB,GAAG,gBAAgB,CAsH5E;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAEpE"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs `crewly cloud login` in a PTY on the owner's behalf.
|
|
3
|
+
*
|
|
4
|
+
* Same pattern as the harness login broker (login-broker.service.ts): spawn
|
|
5
|
+
* the real login command in a wide PTY, normalize the screen, read the
|
|
6
|
+
* sign-in link and code off it, type the owner's reply in when the command
|
|
7
|
+
* asks for one, and report success / failure from the output and exit code.
|
|
8
|
+
* The CLI does the rest itself — device pairing with crewly-auth, saving
|
|
9
|
+
* `$CREWLY_HOME/cloud/config.json`, and `POST /api/cloud/connect` on this
|
|
10
|
+
* backend.
|
|
11
|
+
*
|
|
12
|
+
* Secrets: the device-code flow prints no token. A reply typed into the PTY
|
|
13
|
+
* (a pasted token in the `--paste` flow) is echoed by the terminal, so the
|
|
14
|
+
* screen is never logged or exposed — only the link, the code and the state.
|
|
15
|
+
*
|
|
16
|
+
* @module services/cloud/cloud-login-runner
|
|
17
|
+
*/
|
|
18
|
+
import * as os from 'os';
|
|
19
|
+
import * as pty from 'node-pty';
|
|
20
|
+
import { CLOUD_DISCONNECT_NOTICE_CONSTANTS } from '../../../../config/constants.js';
|
|
21
|
+
import { normalizeTerminalOutput } from '../harness/login-rules.js';
|
|
22
|
+
const C = CLOUD_DISCONNECT_NOTICE_CONSTANTS;
|
|
23
|
+
/** Terminal states. */
|
|
24
|
+
const TERMINAL = new Set(['succeeded', 'failed', 'expired', 'timed_out', 'cancelled']);
|
|
25
|
+
/** Lines the CLI prints once the login worked. */
|
|
26
|
+
const SUCCESS_PATTERNS = [/✓\s*Approved/, /✓\s*Connected to CrewlyAI Cloud/, /✓\s*Credentials saved/];
|
|
27
|
+
/** The pairing ran out before the owner approved. */
|
|
28
|
+
const EXPIRED_PATTERN = /link expired before it was approved/i;
|
|
29
|
+
/** A prompt waiting for typed input (paste flows). */
|
|
30
|
+
const INPUT_PROMPT_PATTERN = /(paste|enter)[^\n]*[:?]\s*$/i;
|
|
31
|
+
/** Approve-page code, e.g. ABCD-2345. */
|
|
32
|
+
const USER_CODE_PATTERN = /\b([A-Z0-9]{4}-[A-Z0-9]{4})\b/;
|
|
33
|
+
/**
|
|
34
|
+
* Pick the sign-in link off the screen: the approve page with the code filled
|
|
35
|
+
* in when present, else the first link.
|
|
36
|
+
*
|
|
37
|
+
* @param text - Normalized screen
|
|
38
|
+
* @param hyperlinks - OSC 8 link targets
|
|
39
|
+
* @returns The link, or null
|
|
40
|
+
*/
|
|
41
|
+
export function extractLoginUrl(text, hyperlinks = []) {
|
|
42
|
+
const urls = [...hyperlinks, ...(text.match(/https?:\/\/[^\s)'"<>]+/g) ?? [])];
|
|
43
|
+
if (urls.length === 0)
|
|
44
|
+
return null;
|
|
45
|
+
return urls.find((u) => /[?&]code=/.test(u)) ?? urls[0] ?? null;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Read the run's state off the screen.
|
|
49
|
+
*
|
|
50
|
+
* @param screenText - Normalized screen
|
|
51
|
+
* @param sinceInput - Normalized output after the last typed reply
|
|
52
|
+
* @param hyperlinks - OSC 8 link targets
|
|
53
|
+
* @returns Link, code, input prompt, success, expiry and the last ✗ line
|
|
54
|
+
*/
|
|
55
|
+
export function parseCloudLoginScreen(screenText, sinceInput, hyperlinks = []) {
|
|
56
|
+
const failureLines = screenText.split('\n').filter((l) => l.includes('✗'));
|
|
57
|
+
const lastFailure = failureLines.length > 0 ? failureLines[failureLines.length - 1].replace(/^.*✗\s*/, '').trim() : null;
|
|
58
|
+
return {
|
|
59
|
+
url: extractLoginUrl(screenText, hyperlinks),
|
|
60
|
+
userCode: screenText.match(USER_CODE_PATTERN)?.[1] ?? null,
|
|
61
|
+
needsInput: INPUT_PROMPT_PATTERN.test(sinceInput.trimEnd()),
|
|
62
|
+
succeeded: SUCCESS_PATTERNS.some((p) => p.test(screenText)),
|
|
63
|
+
expired: EXPIRED_PATTERN.test(screenText),
|
|
64
|
+
failure: lastFailure,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Default PTY spawner (node-pty).
|
|
69
|
+
*
|
|
70
|
+
* @param file - Executable
|
|
71
|
+
* @param args - Arguments
|
|
72
|
+
* @param options - Size, cwd, env
|
|
73
|
+
* @returns The PTY
|
|
74
|
+
*/
|
|
75
|
+
const defaultSpawnPty = (file, args, options) => pty.spawn(file, args, { name: 'xterm-256color', cols: options.cols, rows: options.rows, cwd: options.cwd, env: options.env });
|
|
76
|
+
/**
|
|
77
|
+
* Environment for the CLI: the backend's own (CREWLY_HOME, CREWLY_CLOUD_URL
|
|
78
|
+
* carry over), no colours, and no browser on this machine.
|
|
79
|
+
*
|
|
80
|
+
* @param env - Source environment
|
|
81
|
+
* @returns String-only env map
|
|
82
|
+
*/
|
|
83
|
+
export function buildCloudLoginEnv(env) {
|
|
84
|
+
const out = {};
|
|
85
|
+
for (const [key, value] of Object.entries(env)) {
|
|
86
|
+
if (value !== undefined)
|
|
87
|
+
out[key] = value;
|
|
88
|
+
}
|
|
89
|
+
out.BROWSER = 'true';
|
|
90
|
+
out.TERM = 'xterm-256color';
|
|
91
|
+
out.FORCE_COLOR = '0';
|
|
92
|
+
out.NO_COLOR = '1';
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Start `crewly cloud login --no-browser` in a PTY.
|
|
97
|
+
*
|
|
98
|
+
* @param deps - CLI location and injectables
|
|
99
|
+
* @returns The run
|
|
100
|
+
* @throws Error when the PTY cannot be spawned
|
|
101
|
+
*/
|
|
102
|
+
export function startCloudLogin(deps) {
|
|
103
|
+
const spawnPty = deps.spawnPty ?? defaultSpawnPty;
|
|
104
|
+
const timeoutMs = deps.timeoutMs ?? C.LOGIN_TIMEOUT_MS;
|
|
105
|
+
const listeners = [];
|
|
106
|
+
let snapshot = { state: 'starting', url: null, userCode: null, needsInput: false, message: null };
|
|
107
|
+
let raw = '';
|
|
108
|
+
let inputOffset = 0;
|
|
109
|
+
let exited = false;
|
|
110
|
+
let resolveDone = () => undefined;
|
|
111
|
+
const done = new Promise((resolve) => {
|
|
112
|
+
resolveDone = resolve;
|
|
113
|
+
});
|
|
114
|
+
const emit = () => {
|
|
115
|
+
for (const l of listeners) {
|
|
116
|
+
try {
|
|
117
|
+
l({ ...snapshot });
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// A listener must not break the run
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
const update = (patch) => {
|
|
125
|
+
const next = { ...snapshot, ...patch };
|
|
126
|
+
const changed = Object.keys(next).some((k) => next[k] !== snapshot[k]);
|
|
127
|
+
snapshot = next;
|
|
128
|
+
if (changed)
|
|
129
|
+
emit();
|
|
130
|
+
};
|
|
131
|
+
let term = null;
|
|
132
|
+
let timer = null;
|
|
133
|
+
const finish = (state, message) => {
|
|
134
|
+
if (TERMINAL.has(snapshot.state))
|
|
135
|
+
return;
|
|
136
|
+
if (timer)
|
|
137
|
+
clearTimeout(timer);
|
|
138
|
+
timer = null;
|
|
139
|
+
update({ state, message, needsInput: false });
|
|
140
|
+
if (term && !exited) {
|
|
141
|
+
try {
|
|
142
|
+
term.kill();
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// Already gone
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
resolveDone({ ...snapshot });
|
|
149
|
+
};
|
|
150
|
+
const evaluate = (final, exitCode) => {
|
|
151
|
+
if (TERMINAL.has(snapshot.state))
|
|
152
|
+
return;
|
|
153
|
+
const screen = normalizeTerminalOutput(raw);
|
|
154
|
+
const since = normalizeTerminalOutput(raw.slice(inputOffset)).text;
|
|
155
|
+
const parsed = parseCloudLoginScreen(screen.text, since, screen.hyperlinks);
|
|
156
|
+
const url = parsed.url ?? snapshot.url;
|
|
157
|
+
const needsInput = parsed.needsInput && !final;
|
|
158
|
+
let state = snapshot.state;
|
|
159
|
+
if (!final && ((state === 'starting' && (url || needsInput)) || (state === 'verifying' && needsInput)))
|
|
160
|
+
state = 'awaiting_user';
|
|
161
|
+
// One update, so listeners never see the link without its state.
|
|
162
|
+
update({ state, url, userCode: parsed.userCode ?? snapshot.userCode, needsInput });
|
|
163
|
+
if (!final)
|
|
164
|
+
return;
|
|
165
|
+
if (exitCode === 0 && parsed.succeeded)
|
|
166
|
+
finish('succeeded', null);
|
|
167
|
+
else if (parsed.expired)
|
|
168
|
+
finish('expired', parsed.failure);
|
|
169
|
+
else
|
|
170
|
+
finish('failed', parsed.failure ?? `crewly cloud login exited (code ${exitCode ?? 'unknown'})`);
|
|
171
|
+
};
|
|
172
|
+
term = spawnPty(deps.nodePath ?? process.execPath, [deps.cliEntry, ...C.CLI_LOGIN_ARGS], {
|
|
173
|
+
cols: C.PTY_COLS,
|
|
174
|
+
rows: C.PTY_ROWS,
|
|
175
|
+
cwd: os.homedir(),
|
|
176
|
+
env: buildCloudLoginEnv(deps.env ?? process.env),
|
|
177
|
+
});
|
|
178
|
+
term.onData((data) => {
|
|
179
|
+
raw += data;
|
|
180
|
+
if (raw.length > C.RAW_BUFFER_MAX_CHARS) {
|
|
181
|
+
const drop = raw.length - C.RAW_BUFFER_MAX_CHARS;
|
|
182
|
+
raw = raw.slice(drop);
|
|
183
|
+
inputOffset = Math.max(0, inputOffset - drop);
|
|
184
|
+
}
|
|
185
|
+
evaluate(false);
|
|
186
|
+
});
|
|
187
|
+
term.onExit(({ exitCode }) => {
|
|
188
|
+
exited = true;
|
|
189
|
+
evaluate(true, exitCode);
|
|
190
|
+
});
|
|
191
|
+
timer = setTimeout(() => finish('timed_out', null), timeoutMs);
|
|
192
|
+
timer.unref?.();
|
|
193
|
+
return {
|
|
194
|
+
get: () => ({ ...snapshot }),
|
|
195
|
+
done,
|
|
196
|
+
waitForLink: (ms) => new Promise((resolve) => {
|
|
197
|
+
if (snapshot.url || TERMINAL.has(snapshot.state)) {
|
|
198
|
+
resolve({ ...snapshot });
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
let settled = false;
|
|
202
|
+
const settle = () => {
|
|
203
|
+
if (settled)
|
|
204
|
+
return;
|
|
205
|
+
settled = true;
|
|
206
|
+
clearTimeout(t);
|
|
207
|
+
resolve({ ...snapshot });
|
|
208
|
+
};
|
|
209
|
+
const t = setTimeout(settle, ms);
|
|
210
|
+
t.unref?.();
|
|
211
|
+
listeners.push((s) => {
|
|
212
|
+
if (s.url || TERMINAL.has(s.state))
|
|
213
|
+
settle();
|
|
214
|
+
});
|
|
215
|
+
}),
|
|
216
|
+
input: (text) => {
|
|
217
|
+
if (TERMINAL.has(snapshot.state) || exited || !term)
|
|
218
|
+
return;
|
|
219
|
+
inputOffset = raw.length;
|
|
220
|
+
term.write(`${text.replace(/[\r\n]+/g, '')}\r`);
|
|
221
|
+
update({ state: 'verifying', needsInput: false });
|
|
222
|
+
},
|
|
223
|
+
cancel: () => finish('cancelled', null),
|
|
224
|
+
onChange: (listener) => {
|
|
225
|
+
listeners.push(listener);
|
|
226
|
+
},
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Whether a run has ended.
|
|
231
|
+
*
|
|
232
|
+
* @param state - Run state
|
|
233
|
+
* @returns True for a terminal state
|
|
234
|
+
*/
|
|
235
|
+
export function isCloudLoginFinished(state) {
|
|
236
|
+
return TERMINAL.has(state);
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=cloud-login-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-login-runner.js","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/cloud-login-runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* @module services/cloud/cloud-sync.service
|
|
16
16
|
*/
|
|
17
17
|
import { EventEmitter } from 'events';
|
|
18
|
-
import type { CloudSyncConfig, CloudSyncState, SyncDevice, MessageType } from './cloud-sync.types.js';
|
|
18
|
+
import type { CloudSyncConfig, CloudSyncHealth, CloudSyncState, SyncDevice, MessageType } from './cloud-sync.types.js';
|
|
19
19
|
export declare class CloudSyncService extends EventEmitter {
|
|
20
20
|
private static instance;
|
|
21
21
|
private readonly logger;
|
|
@@ -60,6 +60,12 @@ export declare class CloudSyncService extends EventEmitter {
|
|
|
60
60
|
* Keeps the last 500 IDs; pruned on overflow.
|
|
61
61
|
*/
|
|
62
62
|
private processedMessageIds;
|
|
63
|
+
/** Epoch ms of the last request Cloud answered successfully (0 = never) */
|
|
64
|
+
private lastContactAt;
|
|
65
|
+
/** Epoch ms of the last start() (0 = never started) */
|
|
66
|
+
private startedAt;
|
|
67
|
+
/** True when Cloud's last answer was 401/403 and the token refresh failed */
|
|
68
|
+
private authRejected;
|
|
63
69
|
/** Maximum size of the processedMessageIds dedup set */
|
|
64
70
|
private static readonly MAX_DEDUP_IDS;
|
|
65
71
|
private constructor();
|
|
@@ -100,6 +106,15 @@ export declare class CloudSyncService extends EventEmitter {
|
|
|
100
106
|
* @returns Current CloudSyncState
|
|
101
107
|
*/
|
|
102
108
|
getState(): CloudSyncState;
|
|
109
|
+
/**
|
|
110
|
+
* Connection health for the disconnect monitor: the state, when Cloud last
|
|
111
|
+
* answered, when sync was started and whether Cloud refused the sign-in.
|
|
112
|
+
*
|
|
113
|
+
* @returns Health snapshot (times are epoch ms, null when never)
|
|
114
|
+
*/
|
|
115
|
+
getHealth(): CloudSyncHealth;
|
|
116
|
+
/** Record a request Cloud answered successfully. */
|
|
117
|
+
private markContact;
|
|
103
118
|
/**
|
|
104
119
|
* Get all cached devices for this Cloud account.
|
|
105
120
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-sync.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/cloud-sync.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAQtC,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACd,UAAU,EAKV,WAAW,EACZ,MAAM,uBAAuB,CAAC;AAsF/B,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAiC;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkB;IAEzC,4BAA4B;IAC5B,OAAO,CAAC,KAAK,CAA6B;IAC1C,mCAAmC;IACnC,OAAO,CAAC,MAAM,CAAgC;IAC9C,wCAAwC;IACxC,OAAO,CAAC,OAAO,CAAoB;IACnC,4BAA4B;IAC5B,OAAO,CAAC,OAAO,CAAa;IAE5B,6BAA6B;IAC7B,OAAO,CAAC,cAAc,CAA+C;IACrE,+BAA+B;IAC/B,OAAO,CAAC,eAAe,CAA+C;IACtE,oEAAoE;IACpE,OAAO,CAAC,gBAAgB,CAA8C;IACtE,qEAAqE;IACrE,OAAO,CAAC,kBAAkB,CAAS;IACnC,kEAAkE;IAClE,OAAO,CAAC,iBAAiB,CAAK;IAC9B,4EAA4E;IAC5E,OAAO,CAAC,aAAa,CAA+C;IACpE,iFAAiF;IACjF,OAAO,CAAC,cAAc,CAAuB;IAE7C,0CAA0C;IAC1C,OAAO,CAAC,iBAAiB,CAAK;IAC9B,4CAA4C;IAC5C,OAAO,CAAC,kBAAkB,CAAK;IAC/B,6CAA6C;IAC7C,OAAO,CAAC,mBAAmB,CAAK;IAChC,kCAAkC;IAClC,OAAO,CAAC,kBAAkB,CAA+C;IACzE,qEAAqE;IACrE,OAAO,CAAC,qBAAqB,CAAK;IAClC,sFAAsF;IACtF,OAAO,CAAC,OAAO,CAAuB;IACtC,iEAAiE;IACjE,OAAO,CAAC,UAAU,CAAuB;IAEzC;;;OAGG;IACH,OAAO,CAAC,mBAAmB,CAAqB;IAChD,wDAAwD;IACxD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAO;IAE5C,OAAO;IAKP;;;;OAIG;IACH,MAAM,CAAC,WAAW,IAAI,gBAAgB;IAOtC;;OAEG;IACH,MAAM,CAAC,aAAa,IAAI,IAAI;IAW5B;;;;;;;OAOG;IACH,KAAK,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"cloud-sync.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/cloud-sync.service.ts"],"names":[],"mappings":"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"}
|