crewly 1.20.152 → 1.20.153

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/config/roles/orchestrator/prompt.md +7 -0
  2. package/config/roles/team-leader/prompt.md +6 -0
  3. package/config/sops/developer/git-workflow.md +1 -0
  4. package/dist/backend/backend/src/constants.d.ts +92 -0
  5. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  6. package/dist/backend/backend/src/constants.js +92 -0
  7. package/dist/backend/backend/src/constants.js.map +1 -1
  8. package/dist/backend/backend/src/controllers/worktree-janitor/worktree-janitor.routes.d.ts +29 -0
  9. package/dist/backend/backend/src/controllers/worktree-janitor/worktree-janitor.routes.d.ts.map +1 -0
  10. package/dist/backend/backend/src/controllers/worktree-janitor/worktree-janitor.routes.js +63 -0
  11. package/dist/backend/backend/src/controllers/worktree-janitor/worktree-janitor.routes.js.map +1 -0
  12. package/dist/backend/backend/src/index.d.ts +7 -0
  13. package/dist/backend/backend/src/index.d.ts.map +1 -1
  14. package/dist/backend/backend/src/index.js +114 -2
  15. package/dist/backend/backend/src/index.js.map +1 -1
  16. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  17. package/dist/backend/backend/src/routes/api.routes.js +3 -0
  18. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  19. package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts +18 -3
  20. package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts.map +1 -1
  21. package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.js +28 -4
  22. package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.js.map +1 -1
  23. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
  24. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
  25. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js +1 -0
  26. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
  27. package/dist/backend/backend/src/services/cloud/waiting-actions-inbound.service.d.ts +128 -0
  28. package/dist/backend/backend/src/services/cloud/waiting-actions-inbound.service.d.ts.map +1 -0
  29. package/dist/backend/backend/src/services/cloud/waiting-actions-inbound.service.js +279 -0
  30. package/dist/backend/backend/src/services/cloud/waiting-actions-inbound.service.js.map +1 -0
  31. package/dist/backend/backend/src/services/cloud/waiting-items-sync.service.d.ts +122 -0
  32. package/dist/backend/backend/src/services/cloud/waiting-items-sync.service.d.ts.map +1 -0
  33. package/dist/backend/backend/src/services/cloud/waiting-items-sync.service.js +261 -0
  34. package/dist/backend/backend/src/services/cloud/waiting-items-sync.service.js.map +1 -0
  35. package/dist/backend/backend/src/services/cloud/waiting-items.contract.d.ts +101 -0
  36. package/dist/backend/backend/src/services/cloud/waiting-items.contract.d.ts.map +1 -0
  37. package/dist/backend/backend/src/services/cloud/waiting-items.contract.js +113 -0
  38. package/dist/backend/backend/src/services/cloud/waiting-items.contract.js.map +1 -0
  39. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
  40. package/dist/backend/backend/src/services/slack/slack-initializer.js +2 -1
  41. package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
  42. package/dist/backend/backend/src/services/v3/request.service.d.ts +19 -0
  43. package/dist/backend/backend/src/services/v3/request.service.d.ts.map +1 -1
  44. package/dist/backend/backend/src/services/v3/request.service.js +36 -0
  45. package/dist/backend/backend/src/services/v3/request.service.js.map +1 -1
  46. package/dist/backend/backend/src/services/worktree/worktree-janitor.git.d.ts +131 -0
  47. package/dist/backend/backend/src/services/worktree/worktree-janitor.git.d.ts.map +1 -0
  48. package/dist/backend/backend/src/services/worktree/worktree-janitor.git.js +238 -0
  49. package/dist/backend/backend/src/services/worktree/worktree-janitor.git.js.map +1 -0
  50. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts +241 -0
  51. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts.map +1 -0
  52. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js +504 -0
  53. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js.map +1 -0
  54. package/dist/backend/build-info.json +2 -2
  55. package/dist/cli/backend/src/constants.d.ts +92 -0
  56. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  57. package/dist/cli/backend/src/constants.js +92 -0
  58. package/dist/cli/backend/src/constants.js.map +1 -1
  59. package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts +18 -3
  60. package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts.map +1 -1
  61. package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.js +28 -4
  62. package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.js.map +1 -1
  63. package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
  64. package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
  65. package/dist/cli/backend/src/services/cloud/cloud-sync.types.js +1 -0
  66. package/dist/cli/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
  67. package/dist/cli/backend/src/services/v3/request.service.d.ts +19 -0
  68. package/dist/cli/backend/src/services/v3/request.service.d.ts.map +1 -1
  69. package/dist/cli/backend/src/services/v3/request.service.js +36 -0
  70. package/dist/cli/backend/src/services/v3/request.service.js.map +1 -1
  71. package/package.json +1 -1
@@ -0,0 +1,279 @@
1
+ /**
2
+ * WaitingActionsInboundService — the machine side of "Waiting on you"
3
+ * actions (specs/unified-conversations-cloud-store.md §F, Phase 5).
4
+ *
5
+ * The owner taps "Looks good" (验过了) or "Send back" (打回, with what still
6
+ * needs fixing) in the Crewly Cloud portal. Cloud stores the action and
7
+ * pushes a `waiting_action` relay message to this machine with only ids in
8
+ * it (`{actionId, itemId, instanceId, ticketId}`) — the same secure pattern
9
+ * as Cloud Talk. This service:
10
+ *
11
+ * 1. fetches the action with this machine's own Cloud token
12
+ * (`GET /api/cloud/conversations/waiting/actions/:actionId?instanceId=`).
13
+ * Cloud answers only for an action stored on the token's account and
14
+ * addressed to this machine, so a push anybody else dropped in the relay
15
+ * queue is never run; the fetched ids must match the push;
16
+ * 2. runs it through the ticket review service — the same code as the
17
+ * board's `POST /api/tickets/:id/verify` / `…/reject` (a send-back from
18
+ * Cloud behaves like one from the board: the reason becomes an acceptance
19
+ * criterion and a rework WorkItem is queued for whoever answered);
20
+ * 3. reports the result (`POST …/actions/:actionId/result`) so Cloud removes
21
+ * the item — or shows the owner why it could not be done — and asks the
22
+ * waiting uploader for a fresh snapshot.
23
+ *
24
+ * Idempotent: an action already carried out is not run again when Cloud
25
+ * re-pushes it (it only re-reports), and the ticket service itself refuses
26
+ * to accept a ticket twice.
27
+ *
28
+ * Older OSS versions ignore `waiting_action` (every relay listener filters on
29
+ * its own `type`), and Cloud only sends it to machines advertising the
30
+ * `waiting_actions` capability, which this service turns on when it starts.
31
+ *
32
+ * @module services/cloud/waiting-actions-inbound.service
33
+ */
34
+ import { WAITING_SYNC_CONSTANTS } from '../../constants.js';
35
+ import { LoggerService } from '../core/logger.service.js';
36
+ import { parseWaitingActionFetchResponse, parseWaitingActionRelayData, } from './waiting-items.contract.js';
37
+ const C = WAITING_SYNC_CONSTANTS;
38
+ let active = false;
39
+ /**
40
+ * Capabilities to advertise to Cloud (heartbeat and uploads).
41
+ *
42
+ * @returns `['waiting_actions']` while the handler runs, else `[]`
43
+ */
44
+ export function waitingActionCapabilities() {
45
+ return active ? [C.CAPABILITY] : [];
46
+ }
47
+ /**
48
+ * Words for the owner when the machine could not do an action.
49
+ *
50
+ * @param reason - Ticket review refusal
51
+ * @param device - Machine name
52
+ * @returns Owner-readable text
53
+ */
54
+ export function refusalText(reason, device) {
55
+ switch (reason) {
56
+ case 'open_work':
57
+ return `Work on this is still running on ${device}. Try again when it has finished.`;
58
+ case 'invalid':
59
+ return 'Say what still needs fixing.';
60
+ case 'not_found':
61
+ return `${device} no longer has this.`;
62
+ case 'not_in_review':
63
+ return 'This is no longer waiting for your review.';
64
+ case 'already_done':
65
+ return 'This was already accepted.';
66
+ case 'cancelled':
67
+ return 'This was cancelled.';
68
+ default:
69
+ return `${device} could not do this.`;
70
+ }
71
+ }
72
+ /** Runs accept / send-back pushed from Crewly Cloud — see module docs. */
73
+ export class WaitingActionsInboundService {
74
+ deps;
75
+ logger;
76
+ fetchImpl;
77
+ sleep;
78
+ now;
79
+ inFlight = new Set();
80
+ /** Actions already carried out → the result to report again on a re-push. */
81
+ done = new Map();
82
+ listener = (msg) => {
83
+ void this.handle(msg);
84
+ };
85
+ started = false;
86
+ constructor(deps) {
87
+ this.deps = deps;
88
+ this.logger = deps.logger ?? LoggerService.getInstance().createComponentLogger('WaitingActionsInbound');
89
+ this.fetchImpl = deps.fetchImpl ?? ((input, init) => fetch(input, init));
90
+ this.sleep = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
91
+ this.now = deps.now ?? Date.now;
92
+ }
93
+ /** Subscribe to relay messages and advertise the capability. Idempotent. */
94
+ start() {
95
+ if (this.started)
96
+ return;
97
+ this.started = true;
98
+ this.deps.source.on('message', this.listener);
99
+ active = true;
100
+ this.logger.info('"Waiting on you" action handler started');
101
+ }
102
+ /** Unsubscribe; the capability is no longer advertised. */
103
+ stop() {
104
+ if (!this.started)
105
+ return;
106
+ this.started = false;
107
+ this.deps.source.off('message', this.listener);
108
+ active = false;
109
+ }
110
+ /**
111
+ * Handle one relay message. Anything but a well-formed `waiting_action` is
112
+ * ignored. Never throws.
113
+ *
114
+ * @param msg - Relay message from CloudSyncService
115
+ * @returns What was done
116
+ */
117
+ async handle(msg) {
118
+ if (msg?.type !== C.RELAY_MESSAGE_TYPE)
119
+ return 'ignored';
120
+ const data = parseWaitingActionRelayData(msg.payload);
121
+ if (!data) {
122
+ this.logger.warn('Ignored a malformed waiting_action relay message');
123
+ return 'ignored';
124
+ }
125
+ if (this.inFlight.has(data.actionId))
126
+ return 'duplicate';
127
+ this.inFlight.add(data.actionId);
128
+ try {
129
+ return await this.process(data);
130
+ }
131
+ catch (error) {
132
+ this.logger.warn('"Waiting on you" action failed', {
133
+ actionId: data.actionId,
134
+ error: error instanceof Error ? error.message : String(error),
135
+ });
136
+ return 'fetch_failed';
137
+ }
138
+ finally {
139
+ this.inFlight.delete(data.actionId);
140
+ }
141
+ }
142
+ async process(data) {
143
+ const { instanceId, deviceName } = await this.deps.identity();
144
+ if (data.instanceId !== instanceId) {
145
+ this.logger.warn('Ignored a waiting_action addressed to another machine', { target: data.instanceId });
146
+ return 'not_for_this_machine';
147
+ }
148
+ this.pruneDone();
149
+ const earlier = this.done.get(data.actionId);
150
+ if (earlier) {
151
+ // Cloud re-pushed an action already carried out (our report was lost): report again.
152
+ await this.report(data.actionId, earlier.result);
153
+ return 'duplicate';
154
+ }
155
+ const fetched = await this.fetchAction(data, instanceId);
156
+ if (fetched.kind === 'not_found') {
157
+ this.logger.warn('Ignored a waiting_action Crewly Cloud does not know for this machine', { actionId: data.actionId });
158
+ return 'unverified';
159
+ }
160
+ if (fetched.kind === 'expired')
161
+ return 'expired';
162
+ if (fetched.kind === 'error') {
163
+ this.logger.warn('Could not fetch a "waiting on you" action; Cloud will push it again', { actionId: data.actionId, error: fetched.error });
164
+ return 'fetch_failed';
165
+ }
166
+ const action = fetched.action;
167
+ if (action.actionId !== data.actionId || action.ticketId !== data.ticketId || action.instanceId !== instanceId || action.itemId !== data.itemId) {
168
+ this.logger.warn('Ignored a waiting_action that does not match what Cloud stored', { actionId: data.actionId });
169
+ return 'unverified';
170
+ }
171
+ const device = deviceName || 'this machine';
172
+ const review = this.deps.review();
173
+ if (!review) {
174
+ // Boot has not wired tickets yet — Cloud re-pushes in 5 minutes.
175
+ this.logger.warn('Ticket review is not ready; a "waiting on you" action will be retried', { actionId: data.actionId });
176
+ return 'fetch_failed';
177
+ }
178
+ const result = action.kind === 'accept' ? await review.verify(action.ticketId) : await review.reject(action.ticketId, action.reason ?? '', 'board');
179
+ const report = result.ok
180
+ ? { instanceId, ok: true }
181
+ : { instanceId, ok: false, code: result.reason, error: refusalText(result.reason, device) };
182
+ this.done.set(data.actionId, { at: this.now(), result: report });
183
+ this.logger.info(result.ok ? 'Ticket answered from Crewly Cloud' : 'Ticket action from Crewly Cloud refused', {
184
+ ticketId: action.ticketId,
185
+ kind: action.kind,
186
+ ...(result.ok ? {} : { reason: result.reason }),
187
+ });
188
+ await this.report(data.actionId, report);
189
+ this.deps.requestResync?.();
190
+ return result.ok ? 'done' : 'refused';
191
+ }
192
+ /** Forget carried-out actions after a day (bounded). */
193
+ pruneDone() {
194
+ const cutoff = this.now() - C.DONE_ACTIONS_TTL_MS;
195
+ for (const [id, entry] of this.done)
196
+ if (entry.at < cutoff)
197
+ this.done.delete(id);
198
+ while (this.done.size > C.DONE_ACTIONS_MAX) {
199
+ const oldest = this.done.keys().next().value;
200
+ this.done.delete(oldest);
201
+ }
202
+ }
203
+ url(suffix) {
204
+ const base = this.deps.cloud.getCloudUrl();
205
+ return base ? `${base.replace(/\/$/, '')}${C.ACTIONS_PATH}${suffix}` : null;
206
+ }
207
+ /**
208
+ * Fetch the action from Cloud with this machine's token; one token refresh
209
+ * on 401, a few retries on network / 5xx errors.
210
+ */
211
+ async fetchAction(data, instanceId) {
212
+ const url = this.url(`/${encodeURIComponent(data.actionId)}?instanceId=${encodeURIComponent(instanceId)}`);
213
+ if (!url)
214
+ return { kind: 'error', error: 'not signed in to Crewly Cloud' };
215
+ const delays = C.FETCH_RETRY_DELAYS_MS;
216
+ let refreshed = false;
217
+ let lastError = 'unknown';
218
+ for (let attempt = 0; attempt <= delays.length; attempt++) {
219
+ if (attempt > 0)
220
+ await this.sleep(delays[attempt - 1] ?? 0);
221
+ const token = this.deps.cloud.getToken();
222
+ if (!token)
223
+ return { kind: 'error', error: 'not signed in to Crewly Cloud' };
224
+ let response;
225
+ try {
226
+ response = await this.fetchImpl(url, {
227
+ method: 'GET',
228
+ headers: { Authorization: `Bearer ${token}` },
229
+ signal: AbortSignal.timeout(C.REQUEST_TIMEOUT_MS),
230
+ });
231
+ }
232
+ catch (error) {
233
+ lastError = error instanceof Error ? error.message : String(error);
234
+ continue;
235
+ }
236
+ if (response.status === 401 && !refreshed && this.deps.cloud.tryRefreshToken) {
237
+ refreshed = true;
238
+ if (await this.deps.cloud.tryRefreshToken().catch(() => false)) {
239
+ attempt--; // the retry with the new token is free
240
+ continue;
241
+ }
242
+ }
243
+ if (response.status === 404)
244
+ return { kind: 'not_found' };
245
+ if (response.status === 410)
246
+ return { kind: 'expired' };
247
+ if (response.ok) {
248
+ const parsed = parseWaitingActionFetchResponse(await response.json().catch(() => null));
249
+ return parsed ? { kind: 'ok', action: parsed } : { kind: 'error', error: 'unusable response from Crewly Cloud' };
250
+ }
251
+ lastError = `HTTP ${response.status}`;
252
+ if (response.status >= 400 && response.status < 500 && response.status !== 429)
253
+ break;
254
+ }
255
+ return { kind: 'error', error: lastError };
256
+ }
257
+ /** Report an action's result to Cloud (best effort; a re-push re-reports). */
258
+ async report(actionId, body) {
259
+ const url = this.url(`/${encodeURIComponent(actionId)}/result`);
260
+ const token = this.deps.cloud.getToken();
261
+ if (!url || !token)
262
+ return;
263
+ try {
264
+ const response = await this.fetchImpl(url, {
265
+ method: 'POST',
266
+ headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
267
+ body: JSON.stringify(body),
268
+ signal: AbortSignal.timeout(C.REQUEST_TIMEOUT_MS),
269
+ });
270
+ if (!response.ok)
271
+ this.logger.debug('Crewly Cloud did not take an action result', { actionId, status: response.status });
272
+ }
273
+ catch (error) {
274
+ // The uploader's next snapshot removes the item anyway.
275
+ this.logger.debug('Could not report an action result to Crewly Cloud', { actionId, error: error instanceof Error ? error.message : String(error) });
276
+ }
277
+ }
278
+ }
279
+ //# sourceMappingURL=waiting-actions-inbound.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waiting-actions-inbound.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/waiting-actions-inbound.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ /**
2
+ * WaitingItemsSyncService — keeps Crewly Cloud's "Waiting on you" list in
3
+ * step with this machine (specs/unified-conversations-cloud-store.md §F,
4
+ * Phase 5).
5
+ *
6
+ * "Waiting on you" = tickets in 待验收 (board column `to_review`): work an
7
+ * agent handed back for the owner's OK. The column is derived, not stored,
8
+ * so this service recomputes the set and uploads a text snapshot
9
+ * (`POST /api/cloud/conversations/waiting/ingest`, `full: true`) whenever it
10
+ * changed:
11
+ *
12
+ * - on a Request write (RequestService.onChange), gathered for 2 s;
13
+ * - on a 30 s check (a WorkItem finishing can move a ticket into 待验收
14
+ * without touching its Request file) — uploads only when the set differs
15
+ * from what Cloud last took;
16
+ * - every 5 minutes regardless (a full resync: Cloud refreshes the items'
17
+ * `syncedAt`, so the portal knows the machine is alive, and drops anything
18
+ * this machine no longer has);
19
+ * - after an accept / send-back from the portal was carried out.
20
+ *
21
+ * Every upload is the whole set, so a ticket that left 待验收 disappears in
22
+ * Cloud with the next upload. Uses the machine's own Cloud token (same as
23
+ * the conversation uploader); signed out = nothing is sent. The conversation
24
+ * sync kill switch (`CREWLY_CONVERSATION_SYNC=0`) turns this off too.
25
+ *
26
+ * @module services/cloud/waiting-items-sync.service
27
+ */
28
+ import { type ComponentLogger } from '../core/logger.service.js';
29
+ import type { TicketListItem } from '../v3/ticket-intake.service.js';
30
+ /** The Cloud session (CloudClientService). */
31
+ export interface WaitingSyncCloud {
32
+ getToken(): string | null;
33
+ getCloudUrl(): string | null;
34
+ tryRefreshToken?(): Promise<boolean>;
35
+ }
36
+ /** `fetch` signature (injectable for tests). */
37
+ export type WaitingSyncFetch = (input: string, init: RequestInit) => Promise<Response>;
38
+ /** Constructor dependencies. */
39
+ export interface WaitingItemsSyncDeps {
40
+ /** Tickets in 待验收 right now (board rows, `column: 'to_review'`). */
41
+ listWaiting: () => Promise<TicketListItem[]>;
42
+ cloud: WaitingSyncCloud;
43
+ /** This machine's id (device id = relay queue id = Cloud instance id) and name. */
44
+ identity: () => Promise<{
45
+ instanceId: string;
46
+ deviceName?: string;
47
+ }>;
48
+ crewlyVersion?: () => Promise<string>;
49
+ /** Display names of the machine's agents (session → name). */
50
+ agentNames?: () => Promise<Map<string, string>>;
51
+ /** What this machine handles right now (`waiting_actions`, …). */
52
+ capabilities?: () => string[];
53
+ /** Subscribe to ticket writes; returns an unsubscribe function. */
54
+ onTicketChange?: (listener: () => void) => () => void;
55
+ env?: NodeJS.ProcessEnv;
56
+ fetchImpl?: WaitingSyncFetch;
57
+ now?: () => number;
58
+ setTimeout?: (fn: () => void, ms: number) => ReturnType<typeof setTimeout>;
59
+ clearTimeout?: (handle: ReturnType<typeof setTimeout>) => void;
60
+ logger?: ComponentLogger;
61
+ }
62
+ /** What one {@link WaitingItemsSyncService.syncNow} did (tests, logs). */
63
+ export type WaitingSyncOutcome = 'uploaded' | 'unchanged' | 'skipped' | 'failed' | 'paused';
64
+ /** Uploads the machine's "waiting on you" set to Crewly Cloud. */
65
+ export declare class WaitingItemsSyncService {
66
+ private readonly deps;
67
+ private readonly logger;
68
+ private readonly fetchImpl;
69
+ private readonly now;
70
+ private readonly setTimer;
71
+ private readonly clearTimer;
72
+ private started;
73
+ private running;
74
+ private rerun;
75
+ private checkTimer;
76
+ private debounceTimer;
77
+ private unsubscribe;
78
+ /** Snapshot Cloud last took (items + capabilities), per account. */
79
+ private lastKey;
80
+ private lastAccount;
81
+ private lastFullAt;
82
+ private failures;
83
+ private pausedUntil;
84
+ private pauseLogged;
85
+ constructor(deps: WaitingItemsSyncDeps);
86
+ /** Start watching tickets and the periodic check. Idempotent; never throws. */
87
+ start(): void;
88
+ /** Stop timers and the ticket subscription. */
89
+ stop(): void;
90
+ /**
91
+ * Upload soon (after the 2 s gather) — a ticket changed, or an action from
92
+ * the portal was carried out.
93
+ */
94
+ requestSync(): void;
95
+ /** Keep the periodic check going. */
96
+ private scheduleCheck;
97
+ /**
98
+ * Recompute the set and upload it when it changed (or a full resync is
99
+ * due). One upload at a time; a call during one runs again after it.
100
+ * Never throws.
101
+ *
102
+ * @param force - Upload even when nothing changed
103
+ * @returns What happened
104
+ */
105
+ syncNow(force?: boolean): Promise<WaitingSyncOutcome>;
106
+ private pass;
107
+ /** POST the snapshot and book the outcome. */
108
+ private upload;
109
+ }
110
+ /**
111
+ * Install the process-wide instance (composition root).
112
+ *
113
+ * @param service - The service, or null
114
+ */
115
+ export declare function setWaitingItemsSyncService(service: WaitingItemsSyncService | null): void;
116
+ /**
117
+ * The process-wide instance, or null before wiring.
118
+ *
119
+ * @returns The service or null
120
+ */
121
+ export declare function getWaitingItemsSyncService(): WaitingItemsSyncService | null;
122
+ //# sourceMappingURL=waiting-items-sync.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"waiting-items-sync.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/cloud/waiting-items-sync.service.ts"],"names":[],"mappings":"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"}