@clipboard-health/groundcrew 4.2.0 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/cleaner.d.ts +1 -1
- package/dist/commands/cleaner.d.ts.map +1 -1
- package/dist/commands/cleaner.js +4 -2
- package/dist/commands/dispatcher.d.ts +6 -6
- package/dist/commands/dispatcher.d.ts.map +1 -1
- package/dist/commands/dispatcher.js +43 -27
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +18 -22
- package/dist/commands/eligibility.d.ts +1 -1
- package/dist/commands/eligibility.d.ts.map +1 -1
- package/dist/commands/eligibility.js +7 -6
- package/dist/commands/orchestrator.d.ts.map +1 -1
- package/dist/commands/orchestrator.js +18 -14
- package/dist/commands/resumeWorkspace.d.ts.map +1 -1
- package/dist/commands/resumeWorkspace.js +3 -2
- package/dist/commands/setupWorkspace.d.ts +2 -4
- package/dist/commands/setupWorkspace.d.ts.map +1 -1
- package/dist/commands/setupWorkspace.js +27 -27
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +6 -3
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/lib/adapters/linear/client.d.ts +22 -0
- package/dist/lib/adapters/linear/client.d.ts.map +1 -0
- package/dist/lib/adapters/linear/client.js +36 -0
- package/dist/lib/adapters/linear/factory.d.ts +24 -14
- package/dist/lib/adapters/linear/factory.d.ts.map +1 -1
- package/dist/lib/adapters/linear/factory.js +113 -46
- package/dist/lib/{boardSource.d.ts → adapters/linear/fetch.d.ts} +19 -71
- package/dist/lib/adapters/linear/fetch.d.ts.map +1 -0
- package/dist/lib/{boardSource.js → adapters/linear/fetch.js} +21 -133
- package/dist/lib/adapters/linear/index.d.ts +1 -0
- package/dist/lib/adapters/linear/index.d.ts.map +1 -1
- package/dist/lib/adapters/linear/parsing.d.ts +44 -0
- package/dist/lib/adapters/linear/parsing.d.ts.map +1 -0
- package/dist/lib/adapters/linear/parsing.js +144 -0
- package/dist/lib/{linearIssueStatus.d.ts → adapters/linear/writeback.d.ts} +1 -2
- package/dist/lib/adapters/linear/writeback.d.ts.map +1 -0
- package/dist/lib/{linearIssueStatus.js → adapters/linear/writeback.js} +16 -17
- package/dist/lib/adapters/shell/factory.d.ts +1 -1
- package/dist/lib/adapters/shell/factory.d.ts.map +1 -1
- package/dist/lib/adapters/shell/factory.js +8 -4
- package/dist/lib/adapters/shell/invoke.d.ts +4 -7
- package/dist/lib/adapters/shell/invoke.d.ts.map +1 -1
- package/dist/lib/adapters/shell/invoke.js +46 -75
- package/dist/lib/adapters/shell/schema.d.ts +10 -0
- package/dist/lib/adapters/shell/schema.d.ts.map +1 -1
- package/dist/lib/adapters/shell/schema.js +9 -5
- package/dist/lib/board.d.ts.map +1 -1
- package/dist/lib/board.js +43 -4
- package/dist/lib/buildSources.d.ts +11 -0
- package/dist/lib/buildSources.d.ts.map +1 -1
- package/dist/lib/buildSources.js +41 -0
- package/dist/lib/repositoryValidation.d.ts +13 -0
- package/dist/lib/repositoryValidation.d.ts.map +1 -0
- package/dist/lib/repositoryValidation.js +20 -0
- package/dist/lib/testing/canonicalFixtures.d.ts +19 -0
- package/dist/lib/testing/canonicalFixtures.d.ts.map +1 -0
- package/dist/lib/testing/canonicalFixtures.js +62 -0
- package/dist/lib/ticketSource.d.ts +71 -1
- package/dist/lib/ticketSource.d.ts.map +1 -1
- package/dist/lib/ticketSource.js +31 -0
- package/dist/lib/util.d.ts +0 -20
- package/dist/lib/util.d.ts.map +1 -1
- package/dist/lib/util.js +0 -35
- package/package.json +1 -1
- package/dist/lib/boardSource.d.ts.map +0 -1
- package/dist/lib/linearIssueStatus.d.ts.map +0 -1
|
@@ -13,31 +13,22 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { spawn } from "node:child_process";
|
|
15
15
|
import { log } from "../../util.js";
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Hard cap on captured stdout/stderr per stream. Misbehaving scripts that
|
|
18
|
+
* `yes | head -c <huge>` would otherwise exhaust memory. 10 MB is enough for
|
|
19
|
+
* any realistic JSON ticket payload; tests can override via InvokeArgs.maxOutputBytes
|
|
20
|
+
* to exercise the truncation path with a smaller fixture.
|
|
21
|
+
*/
|
|
22
|
+
const DEFAULT_MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
|
|
18
23
|
export class ShellAdapterTimeoutError extends Error {
|
|
19
24
|
constructor(arguments_) {
|
|
20
25
|
super(`Shell command timed out after ${arguments_.timeoutMs}ms: ${arguments_.command}`);
|
|
21
26
|
this.name = "ShellAdapterTimeoutError";
|
|
22
27
|
}
|
|
23
28
|
}
|
|
24
|
-
export class ShellAdapterOutputLimitError extends Error {
|
|
25
|
-
constructor(arguments_) {
|
|
26
|
-
super(`Shell command exceeded combined stdout/stderr maxBuffer of ${arguments_.maxBytes} bytes: ${arguments_.command}`);
|
|
27
|
-
this.name = "ShellAdapterOutputLimitError";
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
29
|
function shellQuote(value) {
|
|
31
30
|
return `'${value.replaceAll("'", String.raw `'\''`)}'`;
|
|
32
31
|
}
|
|
33
|
-
function killChildProcess(child, signal, shouldUseProcessGroup) {
|
|
34
|
-
/* v8 ignore next 4 @preserve -- fallback path is for Windows or a spawn failure before pid assignment */
|
|
35
|
-
if (!shouldUseProcessGroup || child.pid === undefined) {
|
|
36
|
-
child.kill(signal);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
process.kill(-child.pid, signal);
|
|
40
|
-
}
|
|
41
32
|
export function applySubstitutions(command, subs) {
|
|
42
33
|
let result = command;
|
|
43
34
|
for (const [key, value] of Object.entries(subs)) {
|
|
@@ -49,92 +40,72 @@ export async function invokeShellCommand(args) {
|
|
|
49
40
|
const command = args.substitutions === undefined
|
|
50
41
|
? args.command
|
|
51
42
|
: applySubstitutions(args.command, args.substitutions);
|
|
43
|
+
const maxBytes = args.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
52
44
|
return await new Promise((resolve, reject) => {
|
|
53
|
-
const shouldUseProcessGroup = process.platform !== "win32";
|
|
54
45
|
const child = spawn("sh", ["-c", command], {
|
|
55
46
|
cwd: args.cwd,
|
|
56
|
-
detached: shouldUseProcessGroup,
|
|
57
47
|
// oxlint-disable-next-line node/no-process-env -- subprocess inherits the parent's full env by design; user-supplied vars layer on top
|
|
58
48
|
env: { ...process.env, ...args.env },
|
|
59
49
|
stdio: ["pipe", "pipe", "pipe"],
|
|
60
50
|
});
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
let
|
|
64
|
-
let stderrLength = 0;
|
|
51
|
+
let stdout = Buffer.alloc(0);
|
|
52
|
+
let stderr = Buffer.alloc(0);
|
|
53
|
+
let truncated = false;
|
|
65
54
|
let settled = false;
|
|
66
|
-
|
|
67
|
-
clearTimeout
|
|
68
|
-
}
|
|
69
|
-
function killChild(signal) {
|
|
70
|
-
try {
|
|
71
|
-
killChildProcess(child, signal, shouldUseProcessGroup);
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
// The child may have exited between timeout/output-limit handling and the kill request.
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
function failAndKill(error) {
|
|
78
|
-
/* v8 ignore next 3 @preserve -- timeout/output-limit races can call this after another terminal event; deterministic tests cover the first terminal path */
|
|
55
|
+
const timer = setTimeout(() => {
|
|
56
|
+
/* v8 ignore next 3 @preserve -- timer/close race: clearTimeout in the close handler should prevent this branch, but the guard is kept as defense-in-depth */
|
|
79
57
|
if (settled) {
|
|
80
58
|
return;
|
|
81
59
|
}
|
|
82
60
|
settled = true;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
61
|
+
child.kill("SIGKILL");
|
|
62
|
+
reject(new ShellAdapterTimeoutError({ command, timeoutMs: args.timeoutMs }));
|
|
63
|
+
}, args.timeoutMs);
|
|
64
|
+
// Buffer accumulators so the byte cap matches its name. A string-based
|
|
65
|
+
// comparison would measure UTF-16 code units against a byte budget,
|
|
66
|
+
// letting multibyte UTF-8 sneak past the cap and risking a mid-
|
|
67
|
+
// surrogate-pair slice on truncation.
|
|
68
|
+
const appendCapped = (current, chunk) => {
|
|
69
|
+
if (current.byteLength >= maxBytes) {
|
|
70
|
+
truncated = true;
|
|
71
|
+
return current;
|
|
91
72
|
}
|
|
92
|
-
const
|
|
93
|
-
if (
|
|
94
|
-
|
|
95
|
-
command,
|
|
96
|
-
maxBytes: SHELL_COMMAND_MAX_BUFFER_BYTES,
|
|
97
|
-
}));
|
|
98
|
-
return input.currentLength;
|
|
73
|
+
const next = Buffer.concat([current, chunk]);
|
|
74
|
+
if (next.byteLength <= maxBytes) {
|
|
75
|
+
return next;
|
|
99
76
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
77
|
+
truncated = true;
|
|
78
|
+
const clipped = next.subarray(0, maxBytes);
|
|
79
|
+
return Buffer.concat([
|
|
80
|
+
clipped,
|
|
81
|
+
Buffer.from(`\n[truncated: stream exceeded ${maxBytes} bytes]`),
|
|
82
|
+
]);
|
|
83
|
+
};
|
|
106
84
|
child.stdout.on("data", (chunk) => {
|
|
107
|
-
|
|
108
|
-
chunks: stdoutChunks,
|
|
109
|
-
currentLength: stdoutLength,
|
|
110
|
-
chunk,
|
|
111
|
-
});
|
|
85
|
+
stdout = appendCapped(stdout, chunk);
|
|
112
86
|
});
|
|
113
87
|
child.stderr.on("data", (chunk) => {
|
|
114
|
-
|
|
115
|
-
chunks: stderrChunks,
|
|
116
|
-
currentLength: stderrLength,
|
|
117
|
-
chunk,
|
|
118
|
-
});
|
|
88
|
+
stderr = appendCapped(stderr, chunk);
|
|
119
89
|
});
|
|
120
90
|
child.on("close", (code) => {
|
|
91
|
+
/* v8 ignore next 3 @preserve -- timer/close race: when the timeout fires first it SIGKILLs and sets settled=true; the 'close' event still arrives and must no-op. No deterministic test exists — an orphaned grandchild (`sh -c "sleep N; ..."`) keeps the stdout pipe open, so 'close' doesn't arrive until the real timeout elapses; mirrors the ignored timer/error settle guards above. */
|
|
121
92
|
if (settled) {
|
|
122
93
|
return;
|
|
123
94
|
}
|
|
124
95
|
settled = true;
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
log(`[shell:${args.sourceName}] ${command}\n${stderr.trimEnd()}`);
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
const stderrText = stderr.toString("utf8");
|
|
98
|
+
if (stderrText.length > 0) {
|
|
99
|
+
log(`[shell:${args.sourceName}] ${command}\n${stderrText.trimEnd()}`);
|
|
130
100
|
}
|
|
131
|
-
/* v8 ignore next @preserve -- `code` is null only when the process was killed by signal; timeout
|
|
101
|
+
/* v8 ignore next @preserve -- `code` is null only when the process was killed by signal; the timeout path SIGKILLs but settles via the timer rather than 'close' */
|
|
132
102
|
const exitCode = code ?? 1;
|
|
103
|
+
const stdoutText = stdout.toString("utf8");
|
|
133
104
|
if (exitCode === 0 || exitCode === 3) {
|
|
134
|
-
resolve({ stdout, stderr, exitCode });
|
|
105
|
+
resolve({ stdout: stdoutText, stderr: stderrText, exitCode, truncated });
|
|
135
106
|
return;
|
|
136
107
|
}
|
|
137
|
-
reject(new Error(`Shell command for source "${args.sourceName}" failed with exit ${exitCode}: ${
|
|
108
|
+
reject(new Error(`Shell command for source "${args.sourceName}" failed with exit ${exitCode}: ${stderrText.trim().length > 0 ? stderrText.trim() : command}`));
|
|
138
109
|
});
|
|
139
110
|
/* v8 ignore next 8 @preserve -- spawn 'error' event fires only on exec failures (PATH miss, EACCES) which are hard to simulate in tests without polluting host PATH */
|
|
140
111
|
child.on("error", (error) => {
|
|
@@ -142,7 +113,7 @@ export async function invokeShellCommand(args) {
|
|
|
142
113
|
return;
|
|
143
114
|
}
|
|
144
115
|
settled = true;
|
|
145
|
-
|
|
116
|
+
clearTimeout(timer);
|
|
146
117
|
reject(error);
|
|
147
118
|
});
|
|
148
119
|
if (args.stdin !== undefined) {
|
|
@@ -34,6 +34,11 @@ export declare const shellIssueSchema: z.ZodObject<{
|
|
|
34
34
|
other: "other";
|
|
35
35
|
todo: "todo";
|
|
36
36
|
}>;
|
|
37
|
+
statusReason: z.ZodOptional<z.ZodEnum<{
|
|
38
|
+
missing: "missing";
|
|
39
|
+
unmapped: "unmapped";
|
|
40
|
+
}>>;
|
|
41
|
+
nativeStatus: z.ZodOptional<z.ZodString>;
|
|
37
42
|
}, z.core.$strip>>;
|
|
38
43
|
hasMoreBlockers: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
39
44
|
sourceRef: z.ZodUnknown;
|
|
@@ -64,6 +69,11 @@ export declare const shellFetchOutputSchema: z.ZodArray<z.ZodObject<{
|
|
|
64
69
|
other: "other";
|
|
65
70
|
todo: "todo";
|
|
66
71
|
}>;
|
|
72
|
+
statusReason: z.ZodOptional<z.ZodEnum<{
|
|
73
|
+
missing: "missing";
|
|
74
|
+
unmapped: "unmapped";
|
|
75
|
+
}>>;
|
|
76
|
+
nativeStatus: z.ZodOptional<z.ZodString>;
|
|
67
77
|
}, z.core.$strip>>;
|
|
68
78
|
hasMoreBlockers: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
69
79
|
sourceRef: z.ZodUnknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/shell/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../src/lib/adapters/shell/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAY3B,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBAA4B,CAAC;AAEhE,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;iBAwBnC,CAAC;AAEH,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
|
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
const canonicalStatusSchema = z.enum(["todo", "in-progress", "in-review", "done", "other"]);
|
|
13
|
-
const timeoutSchema = z.number().int().positive();
|
|
14
13
|
const shellBlockerSchema = z.object({
|
|
15
14
|
id: z.string(),
|
|
16
15
|
title: z.string(),
|
|
17
16
|
status: canonicalStatusSchema,
|
|
17
|
+
statusReason: z.enum(["missing", "unmapped"]).optional(),
|
|
18
|
+
nativeStatus: z.string().optional(),
|
|
18
19
|
});
|
|
19
20
|
export const shellIssueSchema = z.object({
|
|
20
21
|
id: z.string(),
|
|
@@ -44,10 +45,13 @@ export const shellAdapterConfigSchema = z.object({
|
|
|
44
45
|
cwd: z.string().optional(),
|
|
45
46
|
timeouts: z
|
|
46
47
|
.object({
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
// Per-method timeout in milliseconds. Must be a positive integer —
|
|
49
|
+
// zero, negative, and fractional values would either deadlock or
|
|
50
|
+
// misbehave inside setTimeout.
|
|
51
|
+
verify: z.number().int().positive().optional(),
|
|
52
|
+
fetch: z.number().int().positive().optional(),
|
|
53
|
+
resolveOne: z.number().int().positive().optional(),
|
|
54
|
+
markInProgress: z.number().int().positive().optional(),
|
|
51
55
|
})
|
|
52
56
|
.optional(),
|
|
53
57
|
env: z.record(z.string(), z.string()).optional(),
|
package/dist/lib/board.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"board.d.ts","sourceRoot":"","sources":["../../src/lib/board.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,KAAK,
|
|
1
|
+
{"version":3,"file":"board.d.ts","sourceRoot":"","sources":["../../src/lib/board.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,KAAK,EAEV,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,KAAK;IACpB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IAC7B;;;OAGG;IACH,UAAU,CAAC,oBAAoB,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IACrE,wFAAwF;IACxF,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7C;AAqBD,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,KAAK,CAwGnE"}
|
package/dist/lib/board.js
CHANGED
|
@@ -11,6 +11,12 @@ async function callVerify(source) {
|
|
|
11
11
|
async function callFetch(source) {
|
|
12
12
|
return await source.fetch();
|
|
13
13
|
}
|
|
14
|
+
async function callFetchParentSkips(source) {
|
|
15
|
+
if (source.fetchParentSkips !== undefined) {
|
|
16
|
+
return await source.fetchParentSkips();
|
|
17
|
+
}
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
14
20
|
async function callResolveOne(source, naturalId) {
|
|
15
21
|
return await source.resolveOne(naturalId);
|
|
16
22
|
}
|
|
@@ -38,8 +44,21 @@ export function createBoard(sources) {
|
|
|
38
44
|
}
|
|
39
45
|
},
|
|
40
46
|
async fetch() {
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
// Per-source serialization: each source's callFetch must complete
|
|
48
|
+
// before its callFetchParentSkips so adapters that cache parent skips
|
|
49
|
+
// as a side effect of fetch() (e.g. Linear, which stores them on
|
|
50
|
+
// `lastParentSkips`) don't serve stale or empty data. Outer Promise.all
|
|
51
|
+
// keeps cross-source fan-out concurrent.
|
|
52
|
+
const perSource = await Promise.all(sources.map(async (source) => {
|
|
53
|
+
const issues = await callFetch(source);
|
|
54
|
+
const parentSkips = await callFetchParentSkips(source);
|
|
55
|
+
return { issues, parentSkips };
|
|
56
|
+
}));
|
|
57
|
+
return {
|
|
58
|
+
timestamp: new Date().toISOString(),
|
|
59
|
+
issues: perSource.flatMap((entry) => entry.issues),
|
|
60
|
+
parentSkips: perSource.flatMap((entry) => entry.parentSkips),
|
|
61
|
+
};
|
|
43
62
|
},
|
|
44
63
|
async resolveOne(idArgument) {
|
|
45
64
|
const colonIndex = idArgument.indexOf(":");
|
|
@@ -52,9 +71,29 @@ export function createBoard(sources) {
|
|
|
52
71
|
}
|
|
53
72
|
return await callResolveOne(source, naturalId);
|
|
54
73
|
}
|
|
55
|
-
|
|
56
|
-
|
|
74
|
+
// Per-source resolveOne errors must not poison sibling resolutions.
|
|
75
|
+
// A source that rejects on a natural-id lookup is treated as "I don't
|
|
76
|
+
// have this ticket" (or "I can't say"). If any source resolved we use
|
|
77
|
+
// it; only when none resolved AND at least one rejected do we surface
|
|
78
|
+
// the rejection — so the user sees a real Linear/network error when
|
|
79
|
+
// there's no fallback, but a stray "not found" from one source doesn't
|
|
80
|
+
// mask a successful match from another.
|
|
81
|
+
const results = await Promise.allSettled(sources.map(async (s) => await callResolveOne(s, idArgument)));
|
|
82
|
+
const matches = [];
|
|
83
|
+
const rejections = [];
|
|
84
|
+
for (const result of results) {
|
|
85
|
+
if (result.status === "rejected") {
|
|
86
|
+
rejections.push(result.reason);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (result.value !== undefined) {
|
|
90
|
+
matches.push(result.value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
57
93
|
if (matches.length === 0) {
|
|
94
|
+
if (rejections.length > 0) {
|
|
95
|
+
throw rejections[0];
|
|
96
|
+
}
|
|
58
97
|
return undefined;
|
|
59
98
|
}
|
|
60
99
|
if (matches.length === 1) {
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* directory-scanned `adapterRegistry`.
|
|
7
7
|
*/
|
|
8
8
|
import type { AdapterContext, AdapterDefinition } from "./adapterDefinition.ts";
|
|
9
|
+
import type { ResolvedConfig } from "./config.ts";
|
|
9
10
|
import type { TicketSource } from "./ticketSource.ts";
|
|
10
11
|
/**
|
|
11
12
|
* Production entry point. Awaits the directory-scanned registry, then dispatches.
|
|
@@ -16,4 +17,14 @@ export declare function buildSources(rawConfigs: readonly unknown[], context: Ad
|
|
|
16
17
|
* import side effects.
|
|
17
18
|
*/
|
|
18
19
|
export declare function buildSourcesWith(registry: Record<string, AdapterDefinition>, rawConfigs: readonly unknown[], context: AdapterContext): TicketSource[];
|
|
20
|
+
/**
|
|
21
|
+
* Build the runtime source list from a ResolvedConfig: synthesizes the
|
|
22
|
+
* implicit Linear source (Linear is always active under the post-#110
|
|
23
|
+
* model — viewer + agent-* label filtering happens at the GraphQL layer)
|
|
24
|
+
* and appends any user-declared `sources`. The implicit source is omitted
|
|
25
|
+
* when the user already declared a Linear source (by `kind` or by runtime
|
|
26
|
+
* name "linear") so they can override its `name` / construction without
|
|
27
|
+
* spawning a duplicate adapter.
|
|
28
|
+
*/
|
|
29
|
+
export declare function sourcesFromConfig(config: ResolvedConfig): readonly unknown[];
|
|
19
30
|
//# sourceMappingURL=buildSources.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildSources.d.ts","sourceRoot":"","sources":["../../src/lib/buildSources.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAItD;;GAEG;AACH,wBAAsB,YAAY,CAChC,UAAU,EAAE,SAAS,OAAO,EAAE,EAC9B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,YAAY,EAAE,CAAC,CAGzB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAC3C,UAAU,EAAE,SAAS,OAAO,EAAE,EAC9B,OAAO,EAAE,cAAc,GACtB,YAAY,EAAE,CAchB"}
|
|
1
|
+
{"version":3,"file":"buildSources.d.ts","sourceRoot":"","sources":["../../src/lib/buildSources.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAItD;;GAEG;AACH,wBAAsB,YAAY,CAChC,UAAU,EAAE,SAAS,OAAO,EAAE,EAC9B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,YAAY,EAAE,CAAC,CAGzB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAC3C,UAAU,EAAE,SAAS,OAAO,EAAE,EAC9B,OAAO,EAAE,cAAc,GACtB,YAAY,EAAE,CAchB;AA6BD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,OAAO,EAAE,CAM5E"}
|
package/dist/lib/buildSources.js
CHANGED
|
@@ -32,3 +32,44 @@ export function buildSourcesWith(registry, rawConfigs, context) {
|
|
|
32
32
|
return adapter.create(config, context);
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
|
+
const sourceShape = z.looseObject({
|
|
36
|
+
name: z.string().optional(),
|
|
37
|
+
kind: z.string().optional(),
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* True when `raw` is an explicitly-declared Linear source. Matches either a
|
|
41
|
+
* `kind: "linear"` entry — regardless of any `name` override — or any entry
|
|
42
|
+
* whose resolved runtime name (explicit `name`, else `kind`) is "linear".
|
|
43
|
+
* The latter catches a non-Linear adapter the user named "linear", which
|
|
44
|
+
* would otherwise collide with the implicit Linear source.
|
|
45
|
+
*
|
|
46
|
+
* Used to suppress the synthesized implicit Linear source so a renamed Linear
|
|
47
|
+
* entry like `{ kind: "linear", name: "custom" }` doesn't spawn a duplicate
|
|
48
|
+
* adapter pointed at the same viewer. Returns false for malformed entries
|
|
49
|
+
* (no `kind`/`name`) — those get rejected by the per-adapter Zod schema
|
|
50
|
+
* downstream.
|
|
51
|
+
*/
|
|
52
|
+
function isExplicitLinearSource(raw) {
|
|
53
|
+
const parsed = sourceShape.safeParse(raw);
|
|
54
|
+
/* v8 ignore next 3 @preserve -- looseObject() with all-optional fields only fails to parse non-object inputs (null, primitives); the same input would be rejected by the per-adapter Zod schema in buildSourcesWith, so this guard never fires in practice. */
|
|
55
|
+
if (!parsed.success) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
return parsed.data.kind === "linear" || (parsed.data.name ?? parsed.data.kind) === "linear";
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Build the runtime source list from a ResolvedConfig: synthesizes the
|
|
62
|
+
* implicit Linear source (Linear is always active under the post-#110
|
|
63
|
+
* model — viewer + agent-* label filtering happens at the GraphQL layer)
|
|
64
|
+
* and appends any user-declared `sources`. The implicit source is omitted
|
|
65
|
+
* when the user already declared a Linear source (by `kind` or by runtime
|
|
66
|
+
* name "linear") so they can override its `name` / construction without
|
|
67
|
+
* spawning a duplicate adapter.
|
|
68
|
+
*/
|
|
69
|
+
export function sourcesFromConfig(config) {
|
|
70
|
+
const hasExplicitLinear = config.sources.some(isExplicitLinearSource);
|
|
71
|
+
if (hasExplicitLinear) {
|
|
72
|
+
return [...config.sources];
|
|
73
|
+
}
|
|
74
|
+
return [{ kind: "linear" }, ...config.sources];
|
|
75
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-level repository validation for canonical Issues. Adapters produce
|
|
3
|
+
* Issue.repository based on their own signal (Linear: agent-* label parse;
|
|
4
|
+
* shell: script JSON output). The host (dispatcher) decides whether that
|
|
5
|
+
* repository is configured for this crew via workspace.knownRepositories.
|
|
6
|
+
*
|
|
7
|
+
* WARN+skip on unknown repo is a deliberate behavior choice (P-refined in
|
|
8
|
+
* the MVP-2 plan): one badly-labelled ticket should not throw and abort
|
|
9
|
+
* the tick across N sources.
|
|
10
|
+
*/
|
|
11
|
+
import type { Issue } from "./ticketSource.ts";
|
|
12
|
+
export declare function dispatchableRepository(issue: Issue, knownRepositories: readonly string[], log: (message: string) => void): string | undefined;
|
|
13
|
+
//# sourceMappingURL=repositoryValidation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repositoryValidation.d.ts","sourceRoot":"","sources":["../../src/lib/repositoryValidation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,KAAK,EACZ,iBAAiB,EAAE,SAAS,MAAM,EAAE,EACpC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAC7B,MAAM,GAAG,SAAS,CAWpB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host-level repository validation for canonical Issues. Adapters produce
|
|
3
|
+
* Issue.repository based on their own signal (Linear: agent-* label parse;
|
|
4
|
+
* shell: script JSON output). The host (dispatcher) decides whether that
|
|
5
|
+
* repository is configured for this crew via workspace.knownRepositories.
|
|
6
|
+
*
|
|
7
|
+
* WARN+skip on unknown repo is a deliberate behavior choice (P-refined in
|
|
8
|
+
* the MVP-2 plan): one badly-labelled ticket should not throw and abort
|
|
9
|
+
* the tick across N sources.
|
|
10
|
+
*/
|
|
11
|
+
export function dispatchableRepository(issue, knownRepositories, log) {
|
|
12
|
+
if (issue.repository === undefined) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
if (!knownRepositories.includes(issue.repository)) {
|
|
16
|
+
log(`issue ${issue.id} references unknown repository ${issue.repository}; configured workspace.knownRepositories: ${knownRepositories.join(", ") || "(none)"}`);
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
return issue.repository;
|
|
20
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Blocker, type Issue } from "../ticketSource.ts";
|
|
2
|
+
export declare function canonicalLinearIssue(overrides: Partial<Issue> & {
|
|
3
|
+
naturalId: string;
|
|
4
|
+
}): Issue;
|
|
5
|
+
export declare function canonicalBlocker(overrides: Partial<Blocker> & {
|
|
6
|
+
naturalId: string;
|
|
7
|
+
}): Blocker;
|
|
8
|
+
/**
|
|
9
|
+
* Canonical Issue fixture for a non-Linear source. Default source name is
|
|
10
|
+
* "shell-test"; override via `sourceName`. Mirrors `canonicalLinearIssue`'s
|
|
11
|
+
* defaults except `sourceRef` is an empty opaque object (no LinearSourceRef
|
|
12
|
+
* shape, since this is meant to stand in for any non-Linear adapter — the
|
|
13
|
+
* shell adapter, future Jira adapter, etc.).
|
|
14
|
+
*/
|
|
15
|
+
export declare function canonicalShellIssue(overrides: Partial<Issue> & {
|
|
16
|
+
naturalId: string;
|
|
17
|
+
sourceName?: string;
|
|
18
|
+
}): Issue;
|
|
19
|
+
//# sourceMappingURL=canonicalFixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonicalFixtures.d.ts","sourceRoot":"","sources":["../../../src/lib/testing/canonicalFixtures.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,EAAiB,MAAM,oBAAoB,CAAC;AAE7E,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CA0B7F;AAED,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAQ7F;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,OAAO,CAAC,KAAK,CAAC,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,GACrE,KAAK,CAiBP"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { toCanonicalId } from "../ticketSource.js";
|
|
2
|
+
export function canonicalLinearIssue(overrides) {
|
|
3
|
+
const { naturalId, sourceRef: refOverride, ...rest } = overrides;
|
|
4
|
+
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- test fixture; sourceRef is opaque unknown in the Issue contract; we reinterpret it here only for fixture construction
|
|
5
|
+
const refPartial = refOverride ?? {};
|
|
6
|
+
const sourceRef = {
|
|
7
|
+
uuid: refPartial.uuid ?? `uuid-${naturalId}`,
|
|
8
|
+
statusId: refPartial.statusId ?? "statusId-default",
|
|
9
|
+
teamId: refPartial.teamId ?? "team-default",
|
|
10
|
+
stateType: refPartial.stateType ?? "unstarted",
|
|
11
|
+
nativeStatus: refPartial.nativeStatus ?? "Todo",
|
|
12
|
+
};
|
|
13
|
+
return {
|
|
14
|
+
id: toCanonicalId("linear", naturalId),
|
|
15
|
+
source: "linear",
|
|
16
|
+
title: `Title for ${naturalId}`,
|
|
17
|
+
description: "",
|
|
18
|
+
status: "todo",
|
|
19
|
+
repository: undefined,
|
|
20
|
+
model: undefined,
|
|
21
|
+
assignee: "Unassigned",
|
|
22
|
+
updatedAt: "2026-01-01T00:00:00.000Z",
|
|
23
|
+
blockers: [],
|
|
24
|
+
hasMoreBlockers: false,
|
|
25
|
+
...rest,
|
|
26
|
+
sourceRef,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function canonicalBlocker(overrides) {
|
|
30
|
+
const { naturalId, ...rest } = overrides;
|
|
31
|
+
return {
|
|
32
|
+
id: toCanonicalId("linear", naturalId),
|
|
33
|
+
title: `Title for ${naturalId}`,
|
|
34
|
+
status: "todo",
|
|
35
|
+
...rest,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Canonical Issue fixture for a non-Linear source. Default source name is
|
|
40
|
+
* "shell-test"; override via `sourceName`. Mirrors `canonicalLinearIssue`'s
|
|
41
|
+
* defaults except `sourceRef` is an empty opaque object (no LinearSourceRef
|
|
42
|
+
* shape, since this is meant to stand in for any non-Linear adapter — the
|
|
43
|
+
* shell adapter, future Jira adapter, etc.).
|
|
44
|
+
*/
|
|
45
|
+
export function canonicalShellIssue(overrides) {
|
|
46
|
+
const { naturalId, sourceName = "shell-test", sourceRef, ...rest } = overrides;
|
|
47
|
+
return {
|
|
48
|
+
id: toCanonicalId(sourceName, naturalId),
|
|
49
|
+
source: sourceName,
|
|
50
|
+
title: `Title for ${naturalId}`,
|
|
51
|
+
description: "",
|
|
52
|
+
status: "todo",
|
|
53
|
+
repository: undefined,
|
|
54
|
+
model: undefined,
|
|
55
|
+
assignee: "Unassigned",
|
|
56
|
+
updatedAt: "2026-01-01T00:00:00.000Z",
|
|
57
|
+
blockers: [],
|
|
58
|
+
hasMoreBlockers: false,
|
|
59
|
+
sourceRef: sourceRef ?? {},
|
|
60
|
+
...rest,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
@@ -26,9 +26,33 @@ export interface Blocker {
|
|
|
26
26
|
id: string;
|
|
27
27
|
title: string;
|
|
28
28
|
status: CanonicalStatus;
|
|
29
|
+
/**
|
|
30
|
+
* When `status === "other"`, adapters MUST set this to explain why
|
|
31
|
+
* they couldn't classify. Consumers (specifically `ticketDoctor`) render
|
|
32
|
+
* this verbatim to give users an actionable next step.
|
|
33
|
+
*
|
|
34
|
+
* - `"missing"`: the source returned no status for this blocker
|
|
35
|
+
* (e.g., Linear had no state on the blocker; shell script omitted
|
|
36
|
+
* the field).
|
|
37
|
+
* - `"unmapped"`: the source returned a status that isn't in the
|
|
38
|
+
* source's known mapping (e.g., a Linear column not in
|
|
39
|
+
* `linear.projects[*].statuses`, or an unrecognized shell value).
|
|
40
|
+
*
|
|
41
|
+
* MUST be undefined when `status !== "other"`.
|
|
42
|
+
*/
|
|
43
|
+
statusReason?: "missing" | "unmapped";
|
|
44
|
+
/**
|
|
45
|
+
* Human-readable native status from the source, when available.
|
|
46
|
+
* Used for diagnostic display only — never branched on. Adapters SHOULD
|
|
47
|
+
* populate this when `statusReason === "unmapped"` so users can see
|
|
48
|
+
* which status name to add to their config; MAY populate for mapped
|
|
49
|
+
* statuses too if the source's native vocabulary differs usefully
|
|
50
|
+
* from `CanonicalStatus`.
|
|
51
|
+
*/
|
|
52
|
+
nativeStatus?: string;
|
|
29
53
|
}
|
|
30
54
|
export interface Issue {
|
|
31
|
-
/** Canonical, source-prefixed id, e.g. "linear:eng-220" or "shell-jira:
|
|
55
|
+
/** Canonical, source-prefixed id, e.g. "linear:eng-220" or "shell-jira:hrd-1". */
|
|
32
56
|
id: string;
|
|
33
57
|
/** Source name (the adapter's `name`, defaulting to its `kind`). */
|
|
34
58
|
source: string;
|
|
@@ -52,9 +76,26 @@ export type GroundcrewIssue = Issue & {
|
|
|
52
76
|
repository: string;
|
|
53
77
|
};
|
|
54
78
|
export declare function isGroundcrewIssue(issue: Issue): issue is GroundcrewIssue;
|
|
79
|
+
/**
|
|
80
|
+
* A parent ticket that was dropped from the fetch result because it has
|
|
81
|
+
* sub-issues. Surfaced separately so the dispatcher can log WHY a
|
|
82
|
+
* Todo+labelled ticket wasn't picked up (PR #80 behavior).
|
|
83
|
+
*/
|
|
84
|
+
export interface ParentSkip {
|
|
85
|
+
/**
|
|
86
|
+
* Canonical, source-prefixed id, e.g. "linear:eng-220". Matches the form
|
|
87
|
+
* used by `Issue.id` so consumers can treat all ids uniformly and strip
|
|
88
|
+
* the prefix with `naturalIdFromCanonical` when displaying to operators.
|
|
89
|
+
*/
|
|
90
|
+
id: string;
|
|
91
|
+
title: string;
|
|
92
|
+
childCount: number;
|
|
93
|
+
}
|
|
55
94
|
export interface BoardState {
|
|
56
95
|
timestamp: string;
|
|
57
96
|
issues: Issue[];
|
|
97
|
+
/** Parent tickets skipped because they have sub-issues. */
|
|
98
|
+
parentSkips: readonly ParentSkip[];
|
|
58
99
|
}
|
|
59
100
|
export interface TicketSource {
|
|
60
101
|
/** Stable identifier used as the id prefix and in log lines. Equal to the source's config `name`. */
|
|
@@ -67,6 +108,13 @@ export interface TicketSource {
|
|
|
67
108
|
resolveOne(naturalId: string): Promise<Issue | undefined>;
|
|
68
109
|
/** Writeback. The adapter downcasts `issue.sourceRef` internally. */
|
|
69
110
|
markInProgress(issue: Issue): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* Optional: return parent tickets that were excluded from `fetch()` because
|
|
113
|
+
* they have sub-issues. Board surfaces these so the dispatcher can log WHY
|
|
114
|
+
* a Todo+labelled ticket was skipped (PR #80 behavior). Adapters that
|
|
115
|
+
* don't distinguish parents simply omit this method; Board returns [].
|
|
116
|
+
*/
|
|
117
|
+
fetchParentSkips?(): Promise<readonly ParentSkip[]>;
|
|
70
118
|
}
|
|
71
119
|
export declare class RepositoryResolutionError extends Error {
|
|
72
120
|
constructor(arguments_: {
|
|
@@ -80,4 +128,26 @@ export declare class AmbiguousTicketError extends Error {
|
|
|
80
128
|
matches: readonly string[];
|
|
81
129
|
});
|
|
82
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Build a canonical source-prefixed id from a source name and a natural
|
|
133
|
+
* (possibly mixed-case) id. Lower-cases the natural part so the same
|
|
134
|
+
* ticket always produces the same canonical id regardless of which code
|
|
135
|
+
* path or adapter constructed it.
|
|
136
|
+
*
|
|
137
|
+
* All adapters MUST use this helper when constructing canonical ids
|
|
138
|
+
* (rather than concatenating `${sourceName}:${naturalId}` inline) so
|
|
139
|
+
* that `Board.resolveOne` lookups against lower-cased natural-id input
|
|
140
|
+
* find the issue regardless of the casing the source emitted.
|
|
141
|
+
*/
|
|
142
|
+
export declare function toCanonicalId(sourceName: string, naturalId: string): string;
|
|
143
|
+
/**
|
|
144
|
+
* Strip the source prefix from a canonical id, yielding the natural id
|
|
145
|
+
* the producing adapter exposed. Use at consumer boundaries where you
|
|
146
|
+
* need to compare a canonical id against natural-id artifacts like
|
|
147
|
+
* `WorktreeEntry.ticket` or filesystem directory names.
|
|
148
|
+
*
|
|
149
|
+
* Canonical ids always carry a `<source>:` prefix; the no-colon branch
|
|
150
|
+
* is a defensive fallback that's unreachable in normal operation.
|
|
151
|
+
*/
|
|
152
|
+
export declare function naturalIdFromCanonical(id: string): string;
|
|
83
153
|
//# sourceMappingURL=ticketSource.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ticketSource.d.ts","sourceRoot":"","sources":["../../src/lib/ticketSource.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,aAAa,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,CAAC;AAEtF,MAAM,WAAW,OAAO;IACtB,6DAA6D;IAC7D,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"ticketSource.d.ts","sourceRoot":"","sources":["../../src/lib/ticketSource.ts"],"names":[],"mappings":"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"}
|