@nonameolsson/nightjar 0.0.0-stage → 0.2.0

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 (185) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +405 -2
  3. package/dist/account.d.ts +12 -0
  4. package/dist/account.js +57 -0
  5. package/dist/account.js.map +1 -0
  6. package/dist/adapter-module.d.ts +18 -0
  7. package/dist/adapter-module.js +83 -0
  8. package/dist/adapter-module.js.map +1 -0
  9. package/dist/adapters.d.ts +15 -0
  10. package/dist/adapters.js +160 -0
  11. package/dist/adapters.js.map +1 -0
  12. package/dist/args.d.ts +13 -0
  13. package/dist/args.js +84 -0
  14. package/dist/args.js.map +1 -0
  15. package/dist/ask.d.ts +6 -0
  16. package/dist/ask.js +27 -0
  17. package/dist/ask.js.map +1 -0
  18. package/dist/azure-devops/api.d.ts +34 -0
  19. package/dist/azure-devops/api.js +98 -0
  20. package/dist/azure-devops/api.js.map +1 -0
  21. package/dist/azure-devops/code-host.d.ts +36 -0
  22. package/dist/azure-devops/code-host.js +185 -0
  23. package/dist/azure-devops/code-host.js.map +1 -0
  24. package/dist/azure-devops/fake-azure-devops.d.ts +90 -0
  25. package/dist/azure-devops/fake-azure-devops.js +728 -0
  26. package/dist/azure-devops/fake-azure-devops.js.map +1 -0
  27. package/dist/azure-devops/index.d.ts +8 -0
  28. package/dist/azure-devops/index.js +11 -0
  29. package/dist/azure-devops/index.js.map +1 -0
  30. package/dist/azure-devops/sign-in.d.ts +11 -0
  31. package/dist/azure-devops/sign-in.js +68 -0
  32. package/dist/azure-devops/sign-in.js.map +1 -0
  33. package/dist/azure-devops/text.d.ts +1 -0
  34. package/dist/azure-devops/text.js +41 -0
  35. package/dist/azure-devops/text.js.map +1 -0
  36. package/dist/azure-devops/tracker.d.ts +44 -0
  37. package/dist/azure-devops/tracker.js +333 -0
  38. package/dist/azure-devops/tracker.js.map +1 -0
  39. package/dist/build.d.ts +75 -0
  40. package/dist/build.js +698 -0
  41. package/dist/build.js.map +1 -0
  42. package/dist/check.d.ts +8 -0
  43. package/dist/check.js +25 -0
  44. package/dist/check.js.map +1 -0
  45. package/dist/checks.d.ts +45 -0
  46. package/dist/checks.js +243 -0
  47. package/dist/checks.js.map +1 -0
  48. package/dist/cli.d.ts +2 -0
  49. package/dist/cli.js +11 -0
  50. package/dist/cli.js.map +1 -0
  51. package/dist/code-host.d.ts +19 -0
  52. package/dist/code-host.js +2 -0
  53. package/dist/code-host.js.map +1 -0
  54. package/dist/config.d.ts +86 -0
  55. package/dist/config.js +294 -0
  56. package/dist/config.js.map +1 -0
  57. package/dist/contract/case.d.ts +4 -0
  58. package/dist/contract/case.js +2 -0
  59. package/dist/contract/case.js.map +1 -0
  60. package/dist/contract/code-host.d.ts +24 -0
  61. package/dist/contract/code-host.js +108 -0
  62. package/dist/contract/code-host.js.map +1 -0
  63. package/dist/contract/command.d.ts +2 -0
  64. package/dist/contract/command.js +36 -0
  65. package/dist/contract/command.js.map +1 -0
  66. package/dist/contract/feature-writer.d.ts +3 -0
  67. package/dist/contract/feature-writer.js +101 -0
  68. package/dist/contract/feature-writer.js.map +1 -0
  69. package/dist/contract/run.d.ts +18 -0
  70. package/dist/contract/run.js +89 -0
  71. package/dist/contract/run.js.map +1 -0
  72. package/dist/contract/tracker.d.ts +30 -0
  73. package/dist/contract/tracker.js +250 -0
  74. package/dist/contract/tracker.js.map +1 -0
  75. package/dist/create.d.ts +9 -0
  76. package/dist/create.js +112 -0
  77. package/dist/create.js.map +1 -0
  78. package/dist/doctor.d.ts +4 -0
  79. package/dist/doctor.js +42 -0
  80. package/dist/doctor.js.map +1 -0
  81. package/dist/fakes/code-host.d.ts +21 -0
  82. package/dist/fakes/code-host.js +42 -0
  83. package/dist/fakes/code-host.js.map +1 -0
  84. package/dist/fakes/harness.d.ts +7 -0
  85. package/dist/fakes/harness.js +68 -0
  86. package/dist/fakes/harness.js.map +1 -0
  87. package/dist/fakes/machine.d.ts +11 -0
  88. package/dist/fakes/machine.js +101 -0
  89. package/dist/fakes/machine.js.map +1 -0
  90. package/dist/fakes/tracker.d.ts +40 -0
  91. package/dist/fakes/tracker.js +116 -0
  92. package/dist/fakes/tracker.js.map +1 -0
  93. package/dist/feature-file.d.ts +12 -0
  94. package/dist/feature-file.js +84 -0
  95. package/dist/feature-file.js.map +1 -0
  96. package/dist/git.d.ts +9 -0
  97. package/dist/git.js +12 -0
  98. package/dist/git.js.map +1 -0
  99. package/dist/github/api.d.ts +37 -0
  100. package/dist/github/api.js +66 -0
  101. package/dist/github/api.js.map +1 -0
  102. package/dist/github/code-host.d.ts +19 -0
  103. package/dist/github/code-host.js +105 -0
  104. package/dist/github/code-host.js.map +1 -0
  105. package/dist/github/fake-gh.d.ts +17 -0
  106. package/dist/github/fake-gh.js +51 -0
  107. package/dist/github/fake-gh.js.map +1 -0
  108. package/dist/github/fake-github.d.ts +58 -0
  109. package/dist/github/fake-github.js +285 -0
  110. package/dist/github/fake-github.js.map +1 -0
  111. package/dist/github/labels.d.ts +3 -0
  112. package/dist/github/labels.js +44 -0
  113. package/dist/github/labels.js.map +1 -0
  114. package/dist/github/tracker.d.ts +28 -0
  115. package/dist/github/tracker.js +208 -0
  116. package/dist/github/tracker.js.map +1 -0
  117. package/dist/index.d.ts +16 -0
  118. package/dist/index.js +13 -0
  119. package/dist/index.js.map +1 -0
  120. package/dist/init.d.ts +19 -0
  121. package/dist/init.js +225 -0
  122. package/dist/init.js.map +1 -0
  123. package/dist/ledger.d.ts +13 -0
  124. package/dist/ledger.js +49 -0
  125. package/dist/ledger.js.map +1 -0
  126. package/dist/main.d.ts +11 -0
  127. package/dist/main.js +215 -0
  128. package/dist/main.js.map +1 -0
  129. package/dist/person-config.d.ts +9 -0
  130. package/dist/person-config.js +46 -0
  131. package/dist/person-config.js.map +1 -0
  132. package/dist/plan.d.ts +9 -0
  133. package/dist/plan.js +27 -0
  134. package/dist/plan.js.map +1 -0
  135. package/dist/ports.d.ts +16 -0
  136. package/dist/ports.js +104 -0
  137. package/dist/ports.js.map +1 -0
  138. package/dist/prompt.d.ts +166 -0
  139. package/dist/prompt.js +217 -0
  140. package/dist/prompt.js.map +1 -0
  141. package/dist/queue.d.ts +20 -0
  142. package/dist/queue.js +94 -0
  143. package/dist/queue.js.map +1 -0
  144. package/dist/say.d.ts +2 -0
  145. package/dist/say.js +4 -0
  146. package/dist/say.js.map +1 -0
  147. package/dist/serve.d.ts +10 -0
  148. package/dist/serve.js +85 -0
  149. package/dist/serve.js.map +1 -0
  150. package/dist/session.d.ts +39 -0
  151. package/dist/session.js +144 -0
  152. package/dist/session.js.map +1 -0
  153. package/dist/settings.d.ts +10 -0
  154. package/dist/settings.js +92 -0
  155. package/dist/settings.js.map +1 -0
  156. package/dist/signals.d.ts +5 -0
  157. package/dist/signals.js +13 -0
  158. package/dist/signals.js.map +1 -0
  159. package/dist/skills.d.ts +4 -0
  160. package/dist/skills.js +51 -0
  161. package/dist/skills.js.map +1 -0
  162. package/dist/status-shape.d.ts +53 -0
  163. package/dist/status-shape.js +3 -0
  164. package/dist/status-shape.js.map +1 -0
  165. package/dist/status.d.ts +23 -0
  166. package/dist/status.js +56 -0
  167. package/dist/status.js.map +1 -0
  168. package/dist/tracker.d.ts +56 -0
  169. package/dist/tracker.js +12 -0
  170. package/dist/tracker.js.map +1 -0
  171. package/dist/workflow.d.ts +33 -0
  172. package/dist/workflow.js +53 -0
  173. package/dist/workflow.js.map +1 -0
  174. package/dist/worktree.d.ts +44 -0
  175. package/dist/worktree.js +193 -0
  176. package/dist/worktree.js.map +1 -0
  177. package/docs/ticket-contract.md +95 -0
  178. package/package.json +74 -5
  179. package/plugin/nightjar-status/.claude-plugin/plugin.json +5 -0
  180. package/plugin/nightjar-status/hooks/format.ts +48 -0
  181. package/plugin/nightjar-status/hooks/hooks.json +1 -0
  182. package/plugin/nightjar-status/hooks/register.ts +51 -0
  183. package/schema/factory.schema.json +437 -0
  184. package/skills/nightjar-feature/SKILL.md +73 -0
  185. package/src/status-shape.ts +44 -0
package/dist/ledger.js ADDED
@@ -0,0 +1,49 @@
1
+ // .factory/runs.jsonl: one line per session with its cost, from which the day's spend is added up.
2
+ import { appendFileSync, mkdirSync, readFileSync } from 'node:fs';
3
+ import { dirname } from 'node:path';
4
+ import { z } from 'zod';
5
+ const EntrySchema = z.looseObject({
6
+ at: z.string(),
7
+ repo: z.string(),
8
+ // a number in a line written before ids were strings
9
+ feature: z.union([z.string(), z.int()]),
10
+ ticket: z.union([z.string(), z.int()]).nullable(),
11
+ branch: z.string(),
12
+ step: z.enum(['build', 'fix', 'review', 'step']),
13
+ session: z.string(),
14
+ costUsd: z.number(),
15
+ outcome: z.string()
16
+ });
17
+ export function record(path, entry) {
18
+ mkdirSync(dirname(path), { recursive: true });
19
+ appendFileSync(path, `${JSON.stringify(entry)}\n`);
20
+ }
21
+ const localDay = (d) => `${String(d.getFullYear())}-${String(d.getMonth() + 1)}-${String(d.getDate())}`;
22
+ // The estimated dollars spent on the local day of `now`. A line that does not parse counts nothing.
23
+ export function spentOn(path, now) {
24
+ let text;
25
+ try {
26
+ text = readFileSync(path, 'utf8');
27
+ }
28
+ catch {
29
+ return 0;
30
+ }
31
+ const day = localDay(now);
32
+ let sum = 0;
33
+ for (const line of text.split('\n')) {
34
+ if (line.trim() === '')
35
+ continue;
36
+ let value;
37
+ try {
38
+ value = JSON.parse(line);
39
+ }
40
+ catch {
41
+ continue;
42
+ }
43
+ const entry = EntrySchema.safeParse(value);
44
+ if (entry.success && localDay(new Date(entry.data.at)) === day)
45
+ sum += entry.data.costUsd;
46
+ }
47
+ return sum;
48
+ }
49
+ //# sourceMappingURL=ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.js","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC;IACjC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,qDAAqD;IACrD,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IACvC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;IACjD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;IAChD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;CACnB,CAAC,CAAC;AAcH,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,KAAY;IAChD,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,cAAc,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,CAAO,EAAE,EAAE,CAC5B,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;AAEjF,oGAAoG;AACpG,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,GAAS;IAC9C,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACJ,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,CAAC,CAAC;IACV,CAAC;IACD,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE;YAAE,SAAS;QACjC,IAAI,KAAc,CAAC;QACnB,IAAI,CAAC;YACJ,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACR,SAAS;QACV,CAAC;QACD,MAAM,KAAK,GAAG,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,KAAK,CAAC,OAAO,IAAI,QAAQ,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,GAAG;YAAE,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;IAC3F,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC"}
package/dist/main.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import type { AzureDevOpsRequest } from './azure-devops/index.js';
2
+ import type { Ask } from './ask.js';
3
+ import type { Env } from './git.js';
4
+ export interface Host {
5
+ readonly platform: NodeJS.Platform;
6
+ readonly env: Env;
7
+ readonly cwd: string;
8
+ readonly request?: AzureDevOpsRequest;
9
+ readonly ask?: Ask;
10
+ }
11
+ export declare function runNightjar(argv: readonly string[], given?: Partial<Host>): Promise<number>;
package/dist/main.js ADDED
@@ -0,0 +1,215 @@
1
+ // The `nightjar` command.
2
+ import { spawn } from 'node:child_process';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { homedir } from 'node:os';
5
+ import { parseArgs, platformProblem, USAGE } from './args.js';
6
+ import { buildFeature, ledgerPath, LIMITS } from './build.js';
7
+ import { runCheck } from './check.js';
8
+ import { checkMachine, Refusal } from './checks.js';
9
+ import { runDoctor } from './doctor.js';
10
+ import { runInitCommand } from './init.js';
11
+ import { runContractCommand } from './contract/command.js';
12
+ import { runCreateCommand } from './create.js';
13
+ import { ConfigError } from './config.js';
14
+ import { claimPorts, portEnv } from './ports.js';
15
+ import { buildOne, cannotClaim, handBack, queue } from './queue.js';
16
+ import { runSession } from './session.js';
17
+ import { startServe } from './serve.js';
18
+ import { StatusFile, statusPath } from './status.js';
19
+ import { worktreesFor } from './worktree.js';
20
+ import { say, why } from './say.js';
21
+ const sleep = (ms, signal) => new Promise((done) => {
22
+ const timer = setTimeout(done, ms);
23
+ signal?.addEventListener('abort', () => {
24
+ clearTimeout(timer);
25
+ done();
26
+ }, { once: true });
27
+ });
28
+ // The checks of a build stop at the first that fails, and the machine's findings are said in one line.
29
+ async function preflight(args, host) {
30
+ const checked = await checkMachine({
31
+ useApiKey: args.useApiKey,
32
+ ...(args.profile === undefined ? {} : { profile: args.profile }),
33
+ env: host.env,
34
+ cwd: host.cwd,
35
+ failFast: true,
36
+ ...(host.request === undefined ? {} : { request: host.request })
37
+ });
38
+ const failed = checked.lines.find((l) => l.status === 'fail' && l.only === undefined);
39
+ if (failed !== undefined)
40
+ throw new Refusal(failed.detail);
41
+ if (checked.ready === null)
42
+ throw new Refusal('the checks did not finish');
43
+ const { adapters, me } = checked.ready;
44
+ say(`${adapters.repo}, as ${me}, on ${checked.ready.login ?? 'ANTHROPIC_API_KEY'}`);
45
+ for (const l of checked.lines) {
46
+ if (l.status === 'fail')
47
+ say(`${l.name} cannot run: ${l.detail}`);
48
+ }
49
+ const found = checked.lines.flatMap((l) => (l.summary === null ? [] : [l.summary]));
50
+ say(`checked: ${found.join(', ')}`);
51
+ const { assignee, person } = checked.ready;
52
+ if (assignee !== me)
53
+ say(`building the Features assigned to ${assignee}`);
54
+ if (person.dailyUsd !== LIMITS.dailyUsd)
55
+ say(`daily limit $${String(person.dailyUsd)}`);
56
+ return checked.ready;
57
+ }
58
+ export async function runNightjar(argv, given = {}) {
59
+ const host = {
60
+ platform: process.platform,
61
+ env: process.env,
62
+ cwd: process.cwd(),
63
+ ...given
64
+ };
65
+ const unsupported = platformProblem(host.platform);
66
+ if (unsupported !== null) {
67
+ console.error(`nightjar: ${unsupported}`);
68
+ return 2;
69
+ }
70
+ if (argv.length === 1 && ['--help', '-h', 'help'].includes(argv[0] ?? '')) {
71
+ console.log(USAGE);
72
+ return 0;
73
+ }
74
+ if (argv[0] === 'init')
75
+ return runInitCommand(argv.slice(1), host);
76
+ if (argv[0] === 'contract')
77
+ return runContractCommand(argv.slice(1));
78
+ if (argv[0] === 'create')
79
+ return runCreateCommand(argv.slice(1));
80
+ const args = parseArgs(argv);
81
+ if (typeof args === 'string') {
82
+ console.error(`nightjar: ${args}\n\n${USAGE}`);
83
+ return 2;
84
+ }
85
+ const env = { ...host.env, GIT_TERMINAL_PROMPT: '0' };
86
+ if (args.mode === 'doctor') {
87
+ const { cwd, request } = host;
88
+ return runDoctor({
89
+ useApiKey: args.useApiKey,
90
+ ...(args.profile === undefined ? {} : { profile: args.profile }),
91
+ env,
92
+ cwd,
93
+ ...(request ? { request } : {})
94
+ });
95
+ }
96
+ let ready;
97
+ try {
98
+ ready = await preflight(args, { ...host, env });
99
+ }
100
+ catch (fault) {
101
+ if (!(fault instanceof Refusal) && !(fault instanceof ConfigError))
102
+ throw fault;
103
+ console.error(`nightjar: ${fault.message}`);
104
+ return 2;
105
+ }
106
+ const { root, config, adapters } = ready;
107
+ const status = new StatusFile({
108
+ path: statusPath(root),
109
+ ledger: ledgerPath(root),
110
+ now: () => new Date(),
111
+ pid: process.pid,
112
+ dailyUsd: ready.person.dailyUsd
113
+ });
114
+ // the reserved ports are held until the process ends, whatever ends it
115
+ const claim = config.ports === undefined ? null : await claimPorts({ count: config.ports.count });
116
+ if (config.ports !== undefined) {
117
+ if (claim === null) {
118
+ console.error(`nightjar: no ${String(config.ports.count)} free, unclaimed ports to reserve; stop another run or lower ports.count in factory.json.`);
119
+ return 2;
120
+ }
121
+ process.once('exit', () => claim.release());
122
+ say(`ports ${String(claim.port)} to ${String(claim.port + config.ports.count - 1)}`);
123
+ }
124
+ const stop = new AbortController();
125
+ const deps = {
126
+ tracker: adapters.tracker,
127
+ codeHost: adapters.codeHost,
128
+ session: (req) => runSession(req),
129
+ runCheck,
130
+ runSetup: runCheck,
131
+ serve: startServe,
132
+ now: () => new Date(),
133
+ newId: () => randomUUID(),
134
+ say,
135
+ status
136
+ };
137
+ const setup = {
138
+ root,
139
+ repo: adapters.repo,
140
+ worktrees: worktreesFor(homedir(), root),
141
+ baseBranch: ready.baseBranch,
142
+ check: config.check,
143
+ allowedCommands: config.allowedCommands,
144
+ ...(ready.rules === null ? {} : { rules: ready.rules }),
145
+ defaults: { model: config.model, effort: config.effort },
146
+ review: config.review,
147
+ ...(config.setup === undefined ? {} : { setupCommand: config.setup }),
148
+ ...(config.workflows === undefined ? {} : { workflows: config.workflows }),
149
+ ...(config.defaultWorkflow === undefined ? {} : { defaultWorkflow: config.defaultWorkflow }),
150
+ brokenWorkflows: ready.workflowProblems,
151
+ ...(config.ports === undefined || claim === null
152
+ ? {}
153
+ : { portEnv: portEnv(claim.port, config.ports.env) }),
154
+ allowApiKey: args.useApiKey,
155
+ env: ready.env,
156
+ ...(args.model === undefined ? {} : { model: args.model }),
157
+ ...(args.effort === undefined ? {} : { effort: args.effort }),
158
+ limits: { ...LIMITS, dailyUsd: ready.person.dailyUsd },
159
+ signal: stop.signal
160
+ };
161
+ // the Feature being built, so a fault that escapes every catch can still hand it back
162
+ let building = null;
163
+ const queueDeps = {
164
+ tracker: adapters.tracker,
165
+ person: ready.assignee,
166
+ build: async (id) => {
167
+ building = id;
168
+ try {
169
+ return await buildFeature(deps, setup, id);
170
+ }
171
+ finally {
172
+ building = null;
173
+ }
174
+ },
175
+ status,
176
+ now: () => new Date(),
177
+ say,
178
+ sleep,
179
+ signal: stop.signal
180
+ };
181
+ process.once('exit', () => status.doing({ kind: 'off' }));
182
+ // keep a Mac awake, so a long run is not put to sleep and a signing agent stays unlocked
183
+ if (process.platform === 'darwin') {
184
+ const awake = spawn('caffeinate', ['-di', '-w', String(process.pid)], { stdio: 'ignore' });
185
+ awake.on('error', () => undefined);
186
+ }
187
+ process.on('SIGINT', () => {
188
+ if (stop.signal.aborted)
189
+ process.exit(130);
190
+ say('stopping: the running session ends and its Ticket goes back; Ctrl-C again to quit at once');
191
+ stop.abort();
192
+ });
193
+ process.once('SIGTERM', () => stop.abort());
194
+ process.once('uncaughtException', (fault) => {
195
+ const now = building;
196
+ void (now === null ? Promise.resolve() : handBack(queueDeps, now, fault)).finally(() => process.exit(1));
197
+ });
198
+ if (args.mode === 'feature' && args.feature !== undefined) {
199
+ let refusal;
200
+ try {
201
+ refusal = await cannotClaim(queueDeps, args.feature);
202
+ }
203
+ catch (fault) {
204
+ refusal = why(fault);
205
+ }
206
+ if (refusal !== null) {
207
+ console.error(`nightjar: ${refusal}`);
208
+ return 2;
209
+ }
210
+ const outcome = await buildOne(queueDeps, args.feature);
211
+ return outcome?.kind === 'pull' || outcome?.kind === 'nothing' ? 0 : 1;
212
+ }
213
+ return queue(queueDeps, args.mode === 'once');
214
+ }
215
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Env } from './git.js';
2
+ export declare const DEFAULT_DAILY_USD = 60;
3
+ export interface PersonConfig {
4
+ readonly dailyUsd: number;
5
+ readonly assignee?: string;
6
+ readonly profile?: string;
7
+ }
8
+ export declare function personConfigPath(env: Env): string;
9
+ export declare function loadPersonConfig(env: Env): PersonConfig;
@@ -0,0 +1,46 @@
1
+ // ~/.config/nightjar/config.json: one person's own settings, which no repo's factory.json may set.
2
+ import { readFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { z } from 'zod';
6
+ import { ConfigError, where } from './config.js';
7
+ import { why } from './say.js';
8
+ export const DEFAULT_DAILY_USD = 60;
9
+ const PersonConfigSchema = z.strictObject({
10
+ dailyUsd: z.number().positive('a dollar amount above 0').optional(),
11
+ assignee: z.string().regex(/\S/, 'a login on the Tracker').optional(),
12
+ profile: z.string().regex(/\S/, 'a Claude profile: a name, a path, or default').optional()
13
+ });
14
+ export function personConfigPath(env) {
15
+ const base = env.XDG_CONFIG_HOME || join(env.HOME ?? homedir(), '.config');
16
+ return join(base, 'nightjar', 'config.json');
17
+ }
18
+ // The person's settings, the defaults where the file is missing or silent, or a ConfigError naming each wrong field.
19
+ export function loadPersonConfig(env) {
20
+ const path = personConfigPath(env);
21
+ let text;
22
+ try {
23
+ text = readFileSync(path, 'utf8');
24
+ }
25
+ catch {
26
+ return { dailyUsd: DEFAULT_DAILY_USD };
27
+ }
28
+ let value;
29
+ try {
30
+ value = JSON.parse(text);
31
+ }
32
+ catch (fault) {
33
+ throw new ConfigError(`${path} is not JSON: ${why(fault)}`);
34
+ }
35
+ const parsed = PersonConfigSchema.safeParse(value);
36
+ if (!parsed.success) {
37
+ throw new ConfigError(parsed.error.issues.map((i) => `${path}: ${where(i.path)}${i.message}`).join('\n'));
38
+ }
39
+ const { dailyUsd = DEFAULT_DAILY_USD, assignee, profile } = parsed.data;
40
+ return {
41
+ dailyUsd,
42
+ ...(assignee === undefined ? {} : { assignee }),
43
+ ...(profile === undefined ? {} : { profile })
44
+ };
45
+ }
46
+ //# sourceMappingURL=person-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"person-config.js","sourceRoot":"","sources":["../src/person-config.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEjD,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAC;AAEpC,MAAM,kBAAkB,GAAG,CAAC,CAAC,YAAY,CAAC;IACzC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC,QAAQ,EAAE;IACnE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,wBAAwB,CAAC,CAAC,QAAQ,EAAE;IACrE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,8CAA8C,CAAC,CAAC,QAAQ,EAAE;CAC1F,CAAC,CAAC;AAWH,MAAM,UAAU,gBAAgB,CAAC,GAAQ;IACxC,MAAM,IAAI,GAAG,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAC3E,OAAO,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;AAC9C,CAAC;AAED,qHAAqH;AACrH,MAAM,UAAU,gBAAgB,CAAC,GAAQ;IACxC,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACJ,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IACxC,CAAC;IACD,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACJ,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,WAAW,CAAC,GAAG,IAAI,iBAAiB,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,MAAM,MAAM,GAAG,kBAAkB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACnD,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACrB,MAAM,IAAI,WAAW,CACpB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAClF,CAAC;IACH,CAAC;IACD,MAAM,EAAE,QAAQ,GAAG,iBAAiB,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IACxE,OAAO;QACN,QAAQ;QACR,GAAG,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC;QAC/C,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;KAC7C,CAAC;AACH,CAAC"}
package/dist/plan.d.ts ADDED
@@ -0,0 +1,9 @@
1
+ import type { ItemId, Ticket } from './tracker.js';
2
+ export declare function nextTicket(tickets: readonly Ticket[], built: ReadonlySet<ItemId>): Ticket | null;
3
+ export interface Waiting {
4
+ readonly id: ItemId;
5
+ readonly title: string;
6
+ readonly why: string;
7
+ readonly blocker?: ItemId;
8
+ }
9
+ export declare function waiting(tickets: readonly Ticket[], built: ReadonlySet<ItemId>): Waiting[];
package/dist/plan.js ADDED
@@ -0,0 +1,27 @@
1
+ const unblocked = (ticket, built) => ticket.blockedBy.every((b) => !b.open || built.has(b.id));
2
+ const buildable = (ticket, built) => ticket.open &&
3
+ !built.has(ticket.id) &&
4
+ ticket.signals.includes('ready') &&
5
+ unblocked(ticket, built);
6
+ export function nextTicket(tickets, built) {
7
+ return tickets.find((t) => buildable(t, built)) ?? null;
8
+ }
9
+ // The open Tickets a run leaves unbuilt, for the pull request and the Feature's comment.
10
+ export function waiting(tickets, built) {
11
+ return tickets
12
+ .filter((t) => t.open && !built.has(t.id))
13
+ .map((t) => ({ id: t.id, title: t.title, ...whyWaiting(t, built) }));
14
+ }
15
+ function whyWaiting(ticket, built) {
16
+ if (ticket.signals.includes('human'))
17
+ return { why: 'needs a person' };
18
+ if (ticket.signals.includes('needsInfo'))
19
+ return { why: 'needs info' };
20
+ const blocker = ticket.blockedBy.find((b) => b.open && !built.has(b.id));
21
+ if (blocker)
22
+ return { why: 'blocked by', blocker: blocker.id };
23
+ if (!ticket.signals.includes('ready'))
24
+ return { why: 'not marked ready' };
25
+ return { why: 'not reached' };
26
+ }
27
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAGA,MAAM,SAAS,GAAG,CAAC,MAAc,EAAE,KAA0B,EAAE,EAAE,CAChE,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAE3D,MAAM,SAAS,GAAG,CAAC,MAAc,EAAE,KAA0B,EAAE,EAAE,CAChE,MAAM,CAAC,IAAI;IACX,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;IACrB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;IAChC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE1B,MAAM,UAAU,UAAU,CAAC,OAA0B,EAAE,KAA0B;IAChF,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC;AACzD,CAAC;AAUD,yFAAyF;AACzF,MAAM,UAAU,OAAO,CAAC,OAA0B,EAAE,KAA0B;IAC7E,OAAO,OAAO;SACZ,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SACzC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,GAAG,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,UAAU,CAAC,MAAc,EAAE,KAA0B;IAC7D,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAC;IACvE,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,CAAC;IACvE,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACzE,IAAI,OAAO;QAAE,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC;IAC/D,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,kBAAkB,EAAE,CAAC;IAC1E,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,16 @@
1
+ export declare const FIRST_PORT = 4477;
2
+ export declare const LAST_PORT = 5476;
3
+ export declare const defaultLocks: () => string;
4
+ export declare function portFree(port: number): Promise<boolean>;
5
+ export interface PortClaim {
6
+ readonly port: number;
7
+ release(): void;
8
+ }
9
+ export interface ClaimOptions {
10
+ readonly count: number;
11
+ readonly locks?: string;
12
+ readonly pid?: number;
13
+ readonly free?: (port: number) => Promise<boolean>;
14
+ }
15
+ export declare function portEnv(first: number, names: readonly string[]): Record<string, string>;
16
+ export declare function claimPorts(options: ClaimOptions): Promise<PortClaim | null>;
package/dist/ports.js ADDED
@@ -0,0 +1,104 @@
1
+ // Tests listen on a run's ports only while they run, so a free port is not enough: each port has a lock file holding the owner's pid.
2
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { createServer } from 'node:net';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ // clear of the ports dev servers and test runners commonly default to
7
+ export const FIRST_PORT = 4477;
8
+ export const LAST_PORT = 5476;
9
+ export const defaultLocks = () => join(tmpdir(), 'nightjar-ports');
10
+ const alive = (pid) => {
11
+ try {
12
+ process.kill(pid, 0);
13
+ return true;
14
+ }
15
+ catch {
16
+ return false;
17
+ }
18
+ };
19
+ const holder = (file) => {
20
+ try {
21
+ const pid = Number(readFileSync(file, 'utf8').trim());
22
+ return Number.isInteger(pid) && pid > 0 ? pid : null;
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ };
28
+ export function portFree(port) {
29
+ return new Promise((done) => {
30
+ const server = createServer();
31
+ server.once('error', () => done(false));
32
+ server.listen(port, '127.0.0.1', () => server.close(() => done(true)));
33
+ });
34
+ }
35
+ const pause = (ms) => new Promise((r) => setTimeout(r, ms));
36
+ // The environment variables that hand a run its ports: the first port to the first name, and so on.
37
+ export function portEnv(first, names) {
38
+ return Object.fromEntries(names.map((name, i) => [name, String(first + i)]));
39
+ }
40
+ // The first block of `count` ports that no live run holds and nothing listens on, or null.
41
+ export async function claimPorts(options) {
42
+ const { count } = options;
43
+ const locks = options.locks ?? defaultLocks();
44
+ const pid = options.pid ?? process.pid;
45
+ const free = options.free ?? portFree;
46
+ mkdirSync(locks, { recursive: true });
47
+ // one picker at a time (mkdir is atomic), so two runs never both read a block as free
48
+ const picking = join(locks, '.picking');
49
+ for (let waited = 0;; waited += 1) {
50
+ try {
51
+ mkdirSync(picking);
52
+ break;
53
+ }
54
+ catch {
55
+ const held = holder(join(picking, 'pid'));
56
+ if ((held !== null && !alive(held)) || (held === null && waited >= 50)) {
57
+ rmSync(picking, { recursive: true, force: true });
58
+ continue;
59
+ }
60
+ if (waited >= 1200)
61
+ return null;
62
+ await pause(100);
63
+ }
64
+ }
65
+ writeFileSync(join(picking, 'pid'), String(pid));
66
+ try {
67
+ for (let first = FIRST_PORT; first + count - 1 <= LAST_PORT; first += count) {
68
+ const ports = Array.from({ length: count }, (_, i) => first + i);
69
+ // a lock per port, so blocks of different counts on one machine never overlap
70
+ const taken = ports.some((port) => {
71
+ const held = holder(join(locks, String(port)));
72
+ return held !== null && held !== pid && alive(held);
73
+ });
74
+ if (taken)
75
+ continue;
76
+ let all = true;
77
+ for (const port of ports) {
78
+ all = await free(port);
79
+ if (!all)
80
+ break;
81
+ }
82
+ if (!all)
83
+ continue;
84
+ for (const port of ports)
85
+ writeFileSync(join(locks, String(port)), String(pid));
86
+ return {
87
+ port: first,
88
+ // removes a lock only while it is still this run's
89
+ release: () => {
90
+ for (const port of ports) {
91
+ const lock = join(locks, String(port));
92
+ if (holder(lock) === pid)
93
+ rmSync(lock, { force: true });
94
+ }
95
+ }
96
+ };
97
+ }
98
+ return null;
99
+ }
100
+ finally {
101
+ rmSync(picking, { recursive: true, force: true });
102
+ }
103
+ }
104
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA,sIAAsI;AACtI,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,sEAAsE;AACtE,MAAM,CAAC,MAAM,UAAU,GAAG,IAAI,CAAC;AAC/B,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC;AAE9B,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,CAAC;AAEnE,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,EAAE;IAC7B,IAAI,CAAC;QACJ,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,IAAY,EAAiB,EAAE;IAC9C,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACtD,OAAO,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC,CAAC;AAEF,MAAM,UAAU,QAAQ,CAAC,IAAY;IACpC,OAAO,IAAI,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAepE,oGAAoG;AACpG,MAAM,UAAU,OAAO,CAAC,KAAa,EAAE,KAAwB;IAC9D,OAAO,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9E,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAqB;IACrD,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,YAAY,EAAE,CAAC;IAC9C,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,QAAQ,CAAC;IACtC,SAAS,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,sFAAsF;IACtF,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IACxC,KAAK,IAAI,MAAM,GAAG,CAAC,GAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QACpC,IAAI,CAAC;YACJ,SAAS,CAAC,OAAO,CAAC,CAAC;YACnB,MAAM;QACP,CAAC;QAAC,MAAM,CAAC;YACR,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;YAC1C,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;gBACxE,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBAClD,SAAS;YACV,CAAC;YACD,IAAI,MAAM,IAAI,IAAI;gBAAE,OAAO,IAAI,CAAC;YAChC,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;QAClB,CAAC;IACF,CAAC;IACD,aAAa,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IACjD,IAAI,CAAC;QACJ,KAAK,IAAI,KAAK,GAAG,UAAU,EAAE,KAAK,GAAG,KAAK,GAAG,CAAC,IAAI,SAAS,EAAE,KAAK,IAAI,KAAK,EAAE,CAAC;YAC7E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;YACjE,8EAA8E;YAC9E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;gBACjC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBAC/C,OAAO,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,GAAG,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC;YACrD,CAAC,CAAC,CAAC;YACH,IAAI,KAAK;gBAAE,SAAS;YACpB,IAAI,GAAG,GAAG,IAAI,CAAC;YACf,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBAC1B,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;gBACvB,IAAI,CAAC,GAAG;oBAAE,MAAM;YACjB,CAAC;YACD,IAAI,CAAC,GAAG;gBAAE,SAAS;YACnB,KAAK,MAAM,IAAI,IAAI,KAAK;gBAAE,aAAa,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAChF,OAAO;gBACN,IAAI,EAAE,KAAK;gBACX,mDAAmD;gBACnD,OAAO,EAAE,GAAG,EAAE;oBACb,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;wBAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;wBACvC,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG;4BAAE,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;oBACzD,CAAC;gBACF,CAAC;aACD,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;YAAS,CAAC;QACV,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC;AACF,CAAC"}