@detergent-software/conductor 0.0.0-stage → 1.0.0-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +131 -2
  3. package/build/claude.js +102 -0
  4. package/build/claude.js.map +1 -0
  5. package/build/cli.js +450 -0
  6. package/build/cli.js.map +1 -0
  7. package/build/client.js +64 -0
  8. package/build/client.js.map +1 -0
  9. package/build/conditions.js +198 -0
  10. package/build/conditions.js.map +1 -0
  11. package/build/config.js +74 -0
  12. package/build/config.js.map +1 -0
  13. package/build/daemon.js +127 -0
  14. package/build/daemon.js.map +1 -0
  15. package/build/errors.js +17 -0
  16. package/build/errors.js.map +1 -0
  17. package/build/jsonc.js +24 -0
  18. package/build/jsonc.js.map +1 -0
  19. package/build/lock.js +64 -0
  20. package/build/lock.js.map +1 -0
  21. package/build/log.js +43 -0
  22. package/build/log.js.map +1 -0
  23. package/build/manager.js +614 -0
  24. package/build/manager.js.map +1 -0
  25. package/build/paths.js +16 -0
  26. package/build/paths.js.map +1 -0
  27. package/build/profile.js +74 -0
  28. package/build/profile.js.map +1 -0
  29. package/build/registry.js +33 -0
  30. package/build/registry.js.map +1 -0
  31. package/build/resolve.js +28 -0
  32. package/build/resolve.js.map +1 -0
  33. package/build/run.js +52 -0
  34. package/build/run.js.map +1 -0
  35. package/build/server.js +227 -0
  36. package/build/server.js.map +1 -0
  37. package/build/spend.js +134 -0
  38. package/build/spend.js.map +1 -0
  39. package/build/startup.js +78 -0
  40. package/build/startup.js.map +1 -0
  41. package/build/store.js +528 -0
  42. package/build/store.js.map +1 -0
  43. package/build/tab.js +26 -0
  44. package/build/tab.js.map +1 -0
  45. package/build/token.js +58 -0
  46. package/build/token.js.map +1 -0
  47. package/build/upgrade.js +93 -0
  48. package/build/upgrade.js.map +1 -0
  49. package/build/wait.js +149 -0
  50. package/build/wait.js.map +1 -0
  51. package/build/watchers/ado.js +167 -0
  52. package/build/watchers/ado.js.map +1 -0
  53. package/build/watchers/github.js +82 -0
  54. package/build/watchers/github.js.map +1 -0
  55. package/build/watchers/health.js +42 -0
  56. package/build/watchers/health.js.map +1 -0
  57. package/build/watchers/index.js +226 -0
  58. package/build/watchers/index.js.map +1 -0
  59. package/build/watchers/linear-key.js +56 -0
  60. package/build/watchers/linear-key.js.map +1 -0
  61. package/build/watchers/linear.js +218 -0
  62. package/build/watchers/linear.js.map +1 -0
  63. package/build/watchers/npm.js +35 -0
  64. package/build/watchers/npm.js.map +1 -0
  65. package/build/watchers/source.js +94 -0
  66. package/build/watchers/source.js.map +1 -0
  67. package/build/worktrees.js +143 -0
  68. package/build/worktrees.js.map +1 -0
  69. package/package.json +56 -5
@@ -0,0 +1,198 @@
1
+ import { ConductorError } from "./errors.js";
2
+ export const PR_EVENTS = ['merged', 'checks', 'comment'];
3
+ const ITEM_FIELDS = ['state', 'label', 'note'];
4
+ const ITEM = /^[A-Za-z][A-Za-z0-9]*-\d+$/;
5
+ const GITHUB_REPO = /^[\w.-]+\/[\w.-]+$/;
6
+ export function formatCondition(c) {
7
+ switch (c.kind) {
8
+ case 'approval':
9
+ return `approval:${c.tracker}:${c.item}`;
10
+ case 'health':
11
+ return `health:${c.env}:${c.field}=${c.value}`;
12
+ case 'item':
13
+ return `item:${c.tracker}:${c.item}:${c.field}=${c.value}`;
14
+ case 'npm':
15
+ return `npm:${c.package}@${c.version}:${c.tag}`;
16
+ case 'pr':
17
+ return `pr:${c.host}:${c.repo}#${c.number}:${c.on}`;
18
+ case 'run':
19
+ return `run:${c.host}:${c.pipeline}#${c.id}:${c.on}`;
20
+ case 'time':
21
+ return `time:${c.at}`;
22
+ }
23
+ }
24
+ // `conduct wait` verbs to a condition. A single argument with a colon is taken as the full form.
25
+ // pr <n> merged|checks|comment [--repo owner/name] run <pipeline> <id> [done]
26
+ // npm <version> [--tag <t>] [--package <p>] health <env> --version <x> | --commit <sha>
27
+ // approval [<item>] item <item> <state> | state|label|note <value…>
28
+ // time <when>
29
+ export function fromVerbs(args, { item, options, profile }) {
30
+ const [verb, ...rest] = args;
31
+ const usage = (line) => {
32
+ throw new ConductorError(`usage: conduct wait ${line}`);
33
+ };
34
+ switch (verb) {
35
+ case 'approval': {
36
+ const target = rest[0] ?? item;
37
+ if (!target)
38
+ throw new ConductorError('which item? run it in the item\'s worktree, or name it: conduct wait approval ATK-310');
39
+ return parseCondition(`approval:${trackerOf(profile)}:${target}`);
40
+ }
41
+ case 'health': {
42
+ const [env] = rest;
43
+ if (!env || (options.version === undefined) === (options.commit === undefined)) {
44
+ return usage('health <env> --version <x> | --commit <sha>');
45
+ }
46
+ if (profile && !profile.health?.[env]) {
47
+ throw new ConductorError(`the profile has no health URL for "${env}"`, [
48
+ `known: ${Object.keys(profile.health ?? {}).join(', ') || 'none'} (health.<env> in .claude/conductor.json)`,
49
+ ]);
50
+ }
51
+ const field = options.version === undefined ? 'commit' : 'version';
52
+ return parseCondition(`health:${env}:${field}=${options.version ?? options.commit}`);
53
+ }
54
+ case 'item': {
55
+ const [target, first, ...more] = rest;
56
+ if (!target || !first)
57
+ return usage('item <item> <state> | item <item> state|label|note <value>');
58
+ const named = ITEM_FIELDS.find((f) => f === first);
59
+ const field = named && more.length > 0 ? named : 'state';
60
+ const value = named && more.length > 0 ? more.join(' ') : [first, ...more].join(' ');
61
+ return parseCondition(`item:${trackerOf(profile)}:${target}:${field}=${value}`);
62
+ }
63
+ case 'npm': {
64
+ const [version] = rest;
65
+ const pkg = options.package ?? profile?.npm?.package;
66
+ if (!version)
67
+ return usage('npm <version> [--tag <dist-tag>] [--package <name>]');
68
+ if (!pkg)
69
+ throw new ConductorError('which package? pass --package, or set npm.package in .claude/conductor.json');
70
+ return parseCondition(`npm:${pkg}@${version}:${options.tag ?? 'latest'}`);
71
+ }
72
+ case 'pr': {
73
+ const [number, on] = rest;
74
+ if (!number || !on)
75
+ return usage('pr <n> merged|checks|comment [--repo <owner>/<repo>]');
76
+ if (options.repo)
77
+ return parseCondition(`pr:github:${options.repo}#${number}:${on}`);
78
+ if (profile?.codeHost.kind !== 'ado')
79
+ throw new ConductorError('no code host: run it in a worktree with .claude/conductor.json, or pass --repo');
80
+ return parseCondition(`pr:ado:${profile.codeHost.repository}#${number}:${on}`);
81
+ }
82
+ case 'run': {
83
+ const [pipeline, id, on = 'done'] = rest;
84
+ if (!pipeline || !id)
85
+ return usage('run <pipeline> <run id> [done]');
86
+ return parseCondition(`run:ado:${pipeline}#${id}:${on}`);
87
+ }
88
+ case 'time': {
89
+ const when = rest.join(' ');
90
+ // A time with no zone, such as 2026-10-10T09:00, is local, as Date reads it.
91
+ const at = new Date(when);
92
+ if (!when || Number.isNaN(at.getTime()))
93
+ return usage('time <when>, such as 2026-10-10T09:00 (local) or an ISO time');
94
+ return { at: at.toISOString(), kind: 'time' };
95
+ }
96
+ default:
97
+ if (verb !== undefined && rest.length === 0 && verb.includes(':'))
98
+ return parseCondition(verb);
99
+ throw new ConductorError(`unknown wait "${args.join(' ')}"`, [
100
+ 'verbs: pr, run, npm, health, approval, item, time; or a full form such as pr:ado:<repo>#<n>:merged',
101
+ ]);
102
+ }
103
+ }
104
+ // The full form back to a condition. Throws a ConductorError that says what the form should be.
105
+ export function parseCondition(text) {
106
+ const colon = text.indexOf(':');
107
+ const kind = colon === -1 ? text : text.slice(0, colon);
108
+ const rest = colon === -1 ? '' : text.slice(colon + 1);
109
+ const fail = (example) => {
110
+ throw new ConductorError(`"${text}" is not a condition`, [`the full form is ${example}`]);
111
+ };
112
+ switch (kind) {
113
+ case 'approval': {
114
+ const match = /^(linear):(\S+)$/.exec(rest);
115
+ if (!match?.[2] || !ITEM.test(match[2]))
116
+ return fail('approval:linear:<item>, such as approval:linear:ATK-310');
117
+ return { item: match[2].toUpperCase(), kind, tracker: 'linear' };
118
+ }
119
+ case 'health': {
120
+ const match = /^([a-z][\w-]*):(version|commit)=(\S+)$/.exec(rest);
121
+ const field = match?.[2] === 'commit' ? 'commit' : 'version';
122
+ // A commit prefix shorter than git's own short form could match another commit.
123
+ if (!match?.[1] || !match[3] || (field === 'commit' && !/^[0-9a-f]{7,40}$/i.test(match[3]))) {
124
+ return fail('health:<env>:version=<x> or health:<env>:commit=<sha, 7 or more hex digits>');
125
+ }
126
+ return { env: match[1], field, kind, value: match[3] };
127
+ }
128
+ case 'item': {
129
+ const match = /^(linear):([^:]+):(state|label|note)=(.+)$/.exec(rest);
130
+ const field = ITEM_FIELDS.find((f) => f === match?.[3]);
131
+ if (!match?.[2] || !ITEM.test(match[2]) || !field || !match[4]?.trim()) {
132
+ return fail('item:linear:<item>:state=<state>, label=<label> or note=<phase>');
133
+ }
134
+ return { field, item: match[2].toUpperCase(), kind, tracker: 'linear', value: match[4].trim() };
135
+ }
136
+ case 'npm': {
137
+ // A scoped package starts with @, so the version is after the last @.
138
+ const match = /^(@?[^@:\s]+)@([^@:\s]+):([^:\s]+)$/.exec(rest);
139
+ if (!match?.[1] || !match[2] || !match[3])
140
+ return fail('npm:<package>@<version>:<dist-tag>, such as npm:@detergent-software/atk@8.5.0-dev.2:dev');
141
+ return { kind, package: match[1], tag: match[3], version: match[2] };
142
+ }
143
+ case 'pr': {
144
+ const match = /^(ado|github):([^#\s]+)#(\d+):(\w+)$/.exec(rest);
145
+ const on = PR_EVENTS.find((e) => e === match?.[4]);
146
+ if (!match?.[2] || !on || (match[1] === 'github' && !GITHUB_REPO.test(match[2]))) {
147
+ return fail('pr:ado:<repo>#<n>:<merged|checks|comment> or pr:github:<owner>/<repo>#<n>:<…>');
148
+ }
149
+ return { host: match[1] === 'github' ? 'github' : 'ado', kind, number: Number(match[3]), on, repo: match[2] };
150
+ }
151
+ case 'run': {
152
+ const match = /^(ado):([^#]+)#(\d+):done$/.exec(rest);
153
+ if (!match?.[2])
154
+ return fail('run:ado:<pipeline>#<run id>:done');
155
+ return { host: 'ado', id: Number(match[3]), kind, on: 'done', pipeline: match[2] };
156
+ }
157
+ case 'time': {
158
+ const at = new Date(rest);
159
+ if (rest === '' || Number.isNaN(at.getTime()))
160
+ return fail('time:<ISO time>, such as time:2026-10-10T22:05:00Z');
161
+ return { at: at.toISOString(), kind };
162
+ }
163
+ default:
164
+ return fail('<kind>:<details>; kinds: pr, run, npm, health, approval, item, time');
165
+ }
166
+ }
167
+ export function sourceOf(c) {
168
+ switch (c.kind) {
169
+ case 'approval':
170
+ case 'item':
171
+ return 'linear';
172
+ case 'health':
173
+ return 'health';
174
+ case 'npm':
175
+ return 'npm';
176
+ case 'pr':
177
+ return c.host;
178
+ case 'run':
179
+ return 'ado';
180
+ case 'time':
181
+ return 'clock';
182
+ }
183
+ }
184
+ // Why a project with this profile can't check the condition, or null. The daemon checks waits
185
+ // against the profile registered with `conduct project add`, so it refuses one it could never
186
+ // resolve instead of parking a worker on it.
187
+ export function uncheckable(c, profile) {
188
+ if ((c.kind === 'pr' && c.host === 'ado') || c.kind === 'run') {
189
+ return profile?.codeHost.kind === 'ado' ? null : "the project's profile has no Azure DevOps code host";
190
+ }
191
+ if (c.kind === 'health' && !profile?.health?.[c.env])
192
+ return `the project's profile has no health URL for "${c.env}"`;
193
+ return null;
194
+ }
195
+ function trackerOf(profile) {
196
+ return profile?.tracker.kind ?? 'linear';
197
+ }
198
+ //# sourceMappingURL=conditions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conditions.js","sourceRoot":"","sources":["../src/conditions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { z } from 'zod';
3
+ import { isMissing } from "./errors.js";
4
+ import { parseJsonc } from "./jsonc.js";
5
+ // ~/.conductor/config.json: machine settings. Strict, so a misspelt key fails startup instead of
6
+ // being ignored. Later build steps add their settings here (slots, quiet hours, watchers …).
7
+ export const configSchema = z.strictObject({
8
+ // The rotating log in ~/.conductor/logs.
9
+ log: z
10
+ .strictObject({
11
+ keep: z.int().min(1).max(50).default(5),
12
+ maxBytes: z
13
+ .int()
14
+ .min(64 * 1024)
15
+ .default(5 * 1024 * 1024),
16
+ })
17
+ .prefault({}),
18
+ // The API's port on 127.0.0.1. 0 lets the OS pick one; clients find it in the port file.
19
+ port: z.int().min(0).max(65535).default(0),
20
+ // The session manager (design note, section 5).
21
+ sessions: z
22
+ .strictObject({
23
+ // Idle that long with no wait is a stall.
24
+ idleMinutes: z.number().positive().default(20),
25
+ // A permission prompt open that long is a stall.
26
+ permissionMinutes: z.number().positive().default(5),
27
+ // How often it reads `claude agents` and acts on what it sees.
28
+ pollSeconds: z.number().min(1).max(600).default(10),
29
+ // How long after a stop before a resume. A resume straight after `claude stop` has started a
30
+ // copy of the session instead of waking it (ATK-309).
31
+ settleSeconds: z.number().min(0).max(300).default(5),
32
+ })
33
+ .prefault({}),
34
+ // The watchers (design note, section 6): how often each API is asked about open waits, and its
35
+ // request budget per rolling minute, shared by every project. Nothing is asked while nothing waits.
36
+ watchers: z
37
+ .strictObject({
38
+ ado: watcher(30, 60),
39
+ github: watcher(30, 30),
40
+ health: watcher(30, 30),
41
+ // Linear allows a key 1500 requests an hour, and conduct-atk reads with the same key.
42
+ linear: watcher(30, 10),
43
+ npm: watcher(20, 30),
44
+ // How often a `watcher.stats` event records each API's passes and requests.
45
+ statsMinutes: z.number().min(1).max(1440).default(10),
46
+ })
47
+ .prefault({}),
48
+ });
49
+ // A missing file means every default; any other read error is a real problem.
50
+ export function loadConfig(file) {
51
+ let text;
52
+ try {
53
+ text = readFileSync(file, 'utf8');
54
+ }
55
+ catch (error) {
56
+ if (isMissing(error))
57
+ return configSchema.parse({});
58
+ throw error;
59
+ }
60
+ return parseConfig(text, file);
61
+ }
62
+ export function parseConfig(text, source) {
63
+ return parseJsonc(text, configSchema, source);
64
+ }
65
+ function watcher(intervalSeconds, perMinute) {
66
+ return z
67
+ .strictObject({
68
+ // perMinute is what holds an API's load down; the interval only spaces the passes.
69
+ intervalSeconds: z.number().min(1).max(3600).default(intervalSeconds),
70
+ perMinute: z.int().min(1).max(600).default(perMinute),
71
+ })
72
+ .prefault({});
73
+ }
74
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { claudeCli } from "./claude.js";
3
+ import { ping } from "./client.js";
4
+ import { loadConfig } from "./config.js";
5
+ import { ConductorError } from "./errors.js";
6
+ import { acquirePidLock, processAlive } from "./lock.js";
7
+ import { rotatingLog } from "./log.js";
8
+ import { sessionManager } from "./manager.js";
9
+ import { registerProject } from "./registry.js";
10
+ import { createApi } from "./server.js";
11
+ import { claudeProjectsDir } from "./spend.js";
12
+ import { openStore } from "./store.js";
13
+ import { windowsTerminalTab } from "./tab.js";
14
+ import { ensureToken } from "./token.js";
15
+ import { startWatchers } from "./watchers/index.js";
16
+ import {} from "./watchers/source.js";
17
+ import { gitWorktrees } from "./worktrees.js";
18
+ // A pid file younger than this belongs to a daemon that may still be starting (it writes the pid
19
+ // file before the port file), so a second start refuses even though nothing answers yet.
20
+ const STARTUP_GRACE_MS = 30_000;
21
+ // The pid file's process runs and either is still starting or answers the API. Checking the API too
22
+ // matters after a reboot, when a stale pid file can name a pid some other process now has.
23
+ export function daemonRunning(paths) {
24
+ return async ({ ageMs, pid }) => processAlive(pid) && (ageMs < STARTUP_GRACE_MS || (await ping(paths, pid)));
25
+ }
26
+ export async function startDaemon(paths, { keepaliveMs, ports = {}, version }) {
27
+ mkdirSync(paths.logs, { recursive: true });
28
+ const config = loadConfig(paths.config);
29
+ const lock = await acquirePidLock(paths.pid, daemonRunning(paths));
30
+ if (!lock.acquired)
31
+ throw new ConductorError(`already running (pid ${lock.pid})`);
32
+ const log = rotatingLog(paths.log, config.log);
33
+ let store;
34
+ let port = null;
35
+ try {
36
+ const token = ensureToken(paths.token);
37
+ const opened = openStore(paths.db);
38
+ store = opened;
39
+ let stopping;
40
+ let resolveClosed = () => { };
41
+ const closed = new Promise((resolve) => (resolveClosed = resolve));
42
+ const manager = sessionManager({
43
+ claude: ports.claude ?? claudeCli(),
44
+ config: config.sessions,
45
+ log,
46
+ openTab: ports.openTab ?? windowsTerminalTab(),
47
+ processAlive: ports.processAlive,
48
+ projectsDir: ports.projectsDir ?? claudeProjectsDir(),
49
+ store: opened,
50
+ worktrees: ports.worktrees ?? gitWorktrees(),
51
+ });
52
+ const watchers = startWatchers({
53
+ config: config.watchers,
54
+ log,
55
+ // A resolved wait wakes its worker on the next sweep; run that sweep now.
56
+ onResolved: () => void manager.sweep(),
57
+ sources: ports.sources,
58
+ store: opened,
59
+ });
60
+ const api = createApi({
61
+ keepaliveMs,
62
+ log,
63
+ manager,
64
+ registerProject: (path) => registerProject(opened, path),
65
+ requestShutdown: () => void stop('api'),
66
+ store: opened,
67
+ token,
68
+ });
69
+ const stop = (reason) => {
70
+ stopping ??= (async () => {
71
+ opened.appendEvent({ data: { pid: process.pid, reason }, kind: 'daemon.stopped' });
72
+ log.log('info', 'stopping', { reason });
73
+ // Launches in flight carry on as processes of their own; a restart marks them failed.
74
+ await watchers.stop();
75
+ await manager.stop();
76
+ api.endStreams();
77
+ await new Promise((resolve) => {
78
+ api.server.close(() => resolve());
79
+ api.server.closeAllConnections();
80
+ });
81
+ if (port !== null)
82
+ removePortFile(paths, port);
83
+ opened.close();
84
+ lock.release();
85
+ resolveClosed();
86
+ })();
87
+ return stopping;
88
+ };
89
+ await new Promise((resolve, reject) => {
90
+ api.server.once('error', reject);
91
+ api.server.listen(config.port, '127.0.0.1', () => resolve());
92
+ });
93
+ port = addressOf(api.server.address()).port;
94
+ writeFileSync(paths.port, `${port}\n`);
95
+ opened.appendEvent({ data: { pid: process.pid, port, version }, kind: 'daemon.started' });
96
+ manager.start();
97
+ watchers.start();
98
+ log.log('info', 'started', { home: paths.home, pid: process.pid, port, version });
99
+ return { closed, log, port, stop };
100
+ }
101
+ catch (error) {
102
+ store?.close();
103
+ if (port !== null)
104
+ removePortFile(paths, port);
105
+ lock.release();
106
+ log.log('error', 'start failed', { error: String(error) });
107
+ throw error;
108
+ }
109
+ }
110
+ function addressOf(address) {
111
+ if (address === null || typeof address === 'string')
112
+ throw new Error('the API is not listening on TCP');
113
+ return address;
114
+ }
115
+ // Only the daemon that wrote the port file removes it, so a stopping daemon never deletes the file
116
+ // of one that replaced it.
117
+ function removePortFile(paths, port) {
118
+ try {
119
+ if (readFileSync(paths.port, 'utf8').trim() !== String(port))
120
+ return;
121
+ }
122
+ catch {
123
+ return;
124
+ }
125
+ rmSync(paths.port, { force: true });
126
+ }
127
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAIzE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAkB,YAAY,EAAE,MAAM,WAAW,CAAC;AACzE,OAAO,EAAe,WAAW,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,EAAoB,cAAc,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAc,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAe,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,iGAAiG;AACjG,yFAAyF;AACzF,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAehC,oGAAoG;AACpG,2FAA2F;AAC3F,MAAM,UAAU,aAAa,CAAC,KAAqB;IACjD,OAAO,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,gBAAgB,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;AAC/G,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,KAAqB,EACrB,EAAE,WAAW,EAAE,KAAK,GAAG,EAAE,EAAE,OAAO,EAAmE;IAErG,SAAS,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,GAAG,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;IACnE,IAAI,CAAC,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,cAAc,CAAC,wBAAwB,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;IAElF,MAAM,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,KAAwB,CAAC;IAC7B,IAAI,IAAI,GAAkB,IAAI,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACnC,KAAK,GAAG,MAAM,CAAC;QACf,IAAI,QAAmC,CAAC;QACxC,IAAI,aAAa,GAAG,GAAS,EAAE,GAAE,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC;QAEzE,MAAM,OAAO,GAAG,cAAc,CAAC;YAC7B,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,SAAS,EAAE;YACnC,MAAM,EAAE,MAAM,CAAC,QAAQ;YACvB,GAAG;YACH,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,kBAAkB,EAAE;YAC9C,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,iBAAiB,EAAE;YACrD,KAAK,EAAE,MAAM;YACb,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,YAAY,EAAE;SAC7C,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,aAAa,CAAC;YAC7B,MAAM,EAAE,MAAM,CAAC,QAAQ;YACvB,GAAG;YACH,0EAA0E;YAC1E,UAAU,EAAE,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,KAAK,EAAE;YACtC,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,KAAK,EAAE,MAAM;SACd,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,SAAS,CAAC;YACpB,WAAW;YACX,GAAG;YACH,OAAO;YACP,eAAe,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC;YACxD,eAAe,EAAE,GAAG,EAAE,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC;YACvC,KAAK,EAAE,MAAM;YACb,KAAK;SACN,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,CAAC,MAAc,EAAiB,EAAE;YAC7C,QAAQ,KAAK,CAAC,KAAK,IAAI,EAAE;gBACvB,MAAM,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;gBACnF,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;gBACxC,sFAAsF;gBACtF,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBACtB,MAAM,OAAO,CAAC,IAAI,EAAE,CAAC;gBACrB,GAAG,CAAC,UAAU,EAAE,CAAC;gBACjB,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;oBAClC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;oBAClC,GAAG,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;gBACnC,CAAC,CAAC,CAAC;gBACH,IAAI,IAAI,KAAK,IAAI;oBAAE,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;gBAC/C,MAAM,CAAC,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,OAAO,EAAE,CAAC;gBACf,aAAa,EAAE,CAAC;YAClB,CAAC,CAAC,EAAE,CAAC;YACL,OAAO,QAAQ,CAAC;QAClB,CAAC,CAAC;QAEF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACjC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;QACH,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC;QAC5C,aAAa,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC;QACvC,MAAM,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC1F,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,QAAQ,CAAC,KAAK,EAAE,CAAC;QACjB,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;QAClF,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACrC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,KAAK,EAAE,KAAK,EAAE,CAAC;QACf,IAAI,IAAI,KAAK,IAAI;YAAE,cAAc,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC3D,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,OAAoC;IACrD,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACxG,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,mGAAmG;AACnG,2BAA2B;AAC3B,SAAS,cAAc,CAAC,KAAqB,EAAE,IAAY;IACzD,IAAI,CAAC;QACH,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,MAAM,CAAC,IAAI,CAAC;YAAE,OAAO;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IACD,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACtC,CAAC"}
@@ -0,0 +1,17 @@
1
+ // An error the user can act on: the CLI prints its message and exits 1, without a stack.
2
+ export class ConductorError extends Error {
3
+ details;
4
+ constructor(message, details = []) {
5
+ super(message);
6
+ this.name = 'ConductorError';
7
+ this.details = details;
8
+ }
9
+ }
10
+ // A Node system error with the given code, such as ENOENT or EEXIST.
11
+ export function hasCode(error, code) {
12
+ return error instanceof Error && 'code' in error && error.code === code;
13
+ }
14
+ export function isMissing(error) {
15
+ return hasCode(error, 'ENOENT');
16
+ }
17
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,MAAM,OAAO,cAAe,SAAQ,KAAK;IAC9B,OAAO,CAAoB;IAEpC,YAAY,OAAe,EAAE,UAA6B,EAAE;QAC1D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED,qEAAqE;AACrE,MAAM,UAAU,OAAO,CAAC,KAAc,EAAE,IAAY;IAClD,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,OAAO,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC"}
package/build/jsonc.js ADDED
@@ -0,0 +1,24 @@
1
+ import { parse, printParseErrorCode } from 'jsonc-parser';
2
+ import { ConductorError } from "./errors.js";
3
+ export function issueLines(error) {
4
+ return error.issues.map((issue) => `${issue.path.length > 0 ? issue.path.join('.') : '(root)'}: ${issue.message}`);
5
+ }
6
+ // JSON with comments and trailing commas, as tsconfig.json allows. Every conductor config file is
7
+ // read this way and then checked against its schema; both kinds of problem name the file.
8
+ export function parseJsonc(text, schema, source) {
9
+ const errors = [];
10
+ const value = parse(text, errors, { allowEmptyContent: false, allowTrailingComma: true });
11
+ if (errors.length > 0) {
12
+ throw new ConductorError(`${source} is not valid JSONC`, errors.map((e) => `${position(text, e.offset)}: ${printParseErrorCode(e.error)}`));
13
+ }
14
+ const result = schema.safeParse(value);
15
+ if (!result.success) {
16
+ throw new ConductorError(`${source} is invalid`, issueLines(result.error));
17
+ }
18
+ return result.data;
19
+ }
20
+ function position(text, offset) {
21
+ const before = text.slice(0, offset).split(/\r?\n/);
22
+ return `line ${before.length}, column ${(before.at(-1) ?? '').length + 1}`;
23
+ }
24
+ //# sourceMappingURL=jsonc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonc.js","sourceRoot":"","sources":["../src/jsonc.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAmB,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,UAAU,UAAU,CAAC,KAAiB;IAC1C,OAAO,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;AACrH,CAAC;AAED,kGAAkG;AAClG,0FAA0F;AAC1F,MAAM,UAAU,UAAU,CAAsB,IAAY,EAAE,MAAS,EAAE,MAAc;IACrF,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,MAAM,KAAK,GAAY,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,iBAAiB,EAAE,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;IACnG,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,cAAc,CACtB,GAAG,MAAM,qBAAqB,EAC9B,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAClF,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,cAAc,CAAC,GAAG,MAAM,aAAa,EAAE,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,MAAc;IAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACpD,OAAO,QAAQ,MAAM,CAAC,MAAM,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;AAC7E,CAAC"}
package/build/lock.js ADDED
@@ -0,0 +1,64 @@
1
+ import { readFileSync, statSync, unlinkSync, writeFileSync } from 'node:fs';
2
+ import { hasCode, isMissing } from "./errors.js";
3
+ // The single-instance lock: a pid file created exclusively. An existing file blocks the start
4
+ // while its holder runs; a stale one (holder gone, for example after a crash or a reboot) is
5
+ // replaced. The exclusive create settles two starts that race for a stale file.
6
+ export async function acquirePidLock(file, isRunning, pid = process.pid) {
7
+ for (let attempt = 0; attempt < 2; attempt++) {
8
+ try {
9
+ writeFileSync(file, `${pid}\n`, { flag: 'wx' });
10
+ return { acquired: true, release: () => releasePidLock(file, pid) };
11
+ }
12
+ catch (error) {
13
+ if (!hasCode(error, 'EEXIST'))
14
+ throw error;
15
+ }
16
+ const holder = readHolder(file);
17
+ if (holder && holder.pid > 0 && (await isRunning(holder)))
18
+ return { acquired: false, pid: holder.pid };
19
+ removeIfUnchanged(file, holder?.pid ?? null);
20
+ }
21
+ const holder = readHolder(file);
22
+ return { acquired: false, pid: holder?.pid ?? 0 };
23
+ }
24
+ export function processAlive(pid) {
25
+ try {
26
+ process.kill(pid, 0);
27
+ return true;
28
+ }
29
+ catch (error) {
30
+ // EPERM: it exists but belongs to someone else.
31
+ return hasCode(error, 'EPERM');
32
+ }
33
+ }
34
+ export function readPid(file) {
35
+ return readHolder(file)?.pid ?? null;
36
+ }
37
+ function readHolder(file) {
38
+ try {
39
+ const pid = Number.parseInt(readFileSync(file, 'utf8').trim(), 10);
40
+ const ageMs = Date.now() - statSync(file).mtimeMs;
41
+ return Number.isInteger(pid) && pid > 0 ? { ageMs, pid } : { ageMs, pid: 0 };
42
+ }
43
+ catch (error) {
44
+ if (isMissing(error))
45
+ return null;
46
+ throw error;
47
+ }
48
+ }
49
+ function releasePidLock(file, pid) {
50
+ if (readPid(file) === pid)
51
+ removeIfUnchanged(file, pid);
52
+ }
53
+ function removeIfUnchanged(file, pid) {
54
+ if ((readHolder(file)?.pid ?? null) !== pid)
55
+ return;
56
+ try {
57
+ unlinkSync(file);
58
+ }
59
+ catch (error) {
60
+ if (!isMissing(error))
61
+ throw error;
62
+ }
63
+ }
64
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../src/lock.ts"],"names":[],"mappings":"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"}
package/build/log.js ADDED
@@ -0,0 +1,43 @@
1
+ import { appendFileSync, renameSync, rmSync, statSync } from 'node:fs';
2
+ import { isMissing } from "./errors.js";
3
+ // A size-rotated log: conductor.log, then conductor.log.1 (newest) … conductor.log.<keep>. One line
4
+ // per entry: time, level, message, then the data as JSON.
5
+ export function rotatingLog(file, { keep, maxBytes }) {
6
+ let size = currentSize(file);
7
+ return {
8
+ log(level, message, data) {
9
+ const line = `${new Date().toISOString()} ${level} ${message}${data ? ` ${JSON.stringify(data)}` : ''}\n`;
10
+ const bytes = Buffer.byteLength(line);
11
+ if (size > 0 && size + bytes > maxBytes) {
12
+ rotate(file, keep);
13
+ size = 0;
14
+ }
15
+ appendFileSync(file, line);
16
+ size += bytes;
17
+ },
18
+ };
19
+ }
20
+ function currentSize(file) {
21
+ try {
22
+ return statSync(file).size;
23
+ }
24
+ catch (error) {
25
+ if (isMissing(error))
26
+ return 0;
27
+ throw error;
28
+ }
29
+ }
30
+ function rotate(file, keep) {
31
+ rmSync(`${file}.${keep}`, { force: true });
32
+ for (let n = keep - 1; n >= 1; n--) {
33
+ try {
34
+ renameSync(`${file}.${n}`, `${file}.${n + 1}`);
35
+ }
36
+ catch (error) {
37
+ if (!isMissing(error))
38
+ throw error;
39
+ }
40
+ }
41
+ renameSync(file, `${file}.1`);
42
+ }
43
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEvE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAQxC,oGAAoG;AACpG,0DAA0D;AAC1D,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAsC;IAC9F,IAAI,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO;QACL,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI;YACtB,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,KAAK,IAAI,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;YAC1G,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACtC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,GAAG,QAAQ,EAAE,CAAC;gBACxC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBACnB,IAAI,GAAG,CAAC,CAAC;YACX,CAAC;YACD,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC3B,IAAI,IAAI,KAAK,CAAC;QAChB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,SAAS,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QAC/B,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,IAAY,EAAE,IAAY;IACxC,MAAM,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,UAAU,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;QACrC,CAAC;IACH,CAAC;IACD,UAAU,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC;AAChC,CAAC"}