clinkx 0.1.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 (105) hide show
  1. package/README.md +70 -0
  2. package/conf/adapters/claude.json +33 -0
  3. package/conf/adapters/codex.json +33 -0
  4. package/conf/adapters/gemini.json +33 -0
  5. package/conf/adapters/glm.json +39 -0
  6. package/conf/adapters/hapi/claude.json +36 -0
  7. package/conf/adapters/hapi/codex.json +36 -0
  8. package/conf/adapters/hapi/gemini.json +36 -0
  9. package/conf/adapters/hapi/glm.json +40 -0
  10. package/conf/prompts/codereviewer.txt +8 -0
  11. package/conf/prompts/debug.txt +6 -0
  12. package/conf/prompts/default.txt +8 -0
  13. package/conf/prompts/json.txt +5 -0
  14. package/conf/prompts/planner.txt +8 -0
  15. package/dist/artifacts.d.ts +9 -0
  16. package/dist/artifacts.js +24 -0
  17. package/dist/artifacts.js.map +1 -0
  18. package/dist/concurrency.d.ts +15 -0
  19. package/dist/concurrency.js +39 -0
  20. package/dist/concurrency.js.map +1 -0
  21. package/dist/config.d.ts +103 -0
  22. package/dist/config.js +40 -0
  23. package/dist/config.js.map +1 -0
  24. package/dist/continuation.d.ts +15 -0
  25. package/dist/continuation.js +42 -0
  26. package/dist/continuation.js.map +1 -0
  27. package/dist/env.d.ts +10 -0
  28. package/dist/env.js +52 -0
  29. package/dist/env.js.map +1 -0
  30. package/dist/errors.d.ts +68 -0
  31. package/dist/errors.js +88 -0
  32. package/dist/errors.js.map +1 -0
  33. package/dist/handler.d.ts +21 -0
  34. package/dist/handler.js +45 -0
  35. package/dist/handler.js.map +1 -0
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.js +38 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/logger.d.ts +16 -0
  40. package/dist/logger.js +30 -0
  41. package/dist/logger.js.map +1 -0
  42. package/dist/parsers/claude-json.d.ts +2 -0
  43. package/dist/parsers/claude-json.js +58 -0
  44. package/dist/parsers/claude-json.js.map +1 -0
  45. package/dist/parsers/codex-jsonl.d.ts +2 -0
  46. package/dist/parsers/codex-jsonl.js +75 -0
  47. package/dist/parsers/codex-jsonl.js.map +1 -0
  48. package/dist/parsers/extract.d.ts +25 -0
  49. package/dist/parsers/extract.js +87 -0
  50. package/dist/parsers/extract.js.map +1 -0
  51. package/dist/parsers/gemini-json.d.ts +2 -0
  52. package/dist/parsers/gemini-json.js +72 -0
  53. package/dist/parsers/gemini-json.js.map +1 -0
  54. package/dist/parsers/json-extract.d.ts +2 -0
  55. package/dist/parsers/json-extract.js +19 -0
  56. package/dist/parsers/json-extract.js.map +1 -0
  57. package/dist/parsers/summary.d.ts +7 -0
  58. package/dist/parsers/summary.js +29 -0
  59. package/dist/parsers/summary.js.map +1 -0
  60. package/dist/parsers/text.d.ts +2 -0
  61. package/dist/parsers/text.js +14 -0
  62. package/dist/parsers/text.js.map +1 -0
  63. package/dist/parsers/types.d.ts +25 -0
  64. package/dist/parsers/types.js +2 -0
  65. package/dist/parsers/types.js.map +1 -0
  66. package/dist/parsers/utils.d.ts +11 -0
  67. package/dist/parsers/utils.js +116 -0
  68. package/dist/parsers/utils.js.map +1 -0
  69. package/dist/paths.d.ts +17 -0
  70. package/dist/paths.js +87 -0
  71. package/dist/paths.js.map +1 -0
  72. package/dist/pipeline.d.ts +37 -0
  73. package/dist/pipeline.js +232 -0
  74. package/dist/pipeline.js.map +1 -0
  75. package/dist/progress.d.ts +28 -0
  76. package/dist/progress.js +78 -0
  77. package/dist/progress.js.map +1 -0
  78. package/dist/prompt-mode.d.ts +15 -0
  79. package/dist/prompt-mode.js +23 -0
  80. package/dist/prompt-mode.js.map +1 -0
  81. package/dist/prompt.d.ts +25 -0
  82. package/dist/prompt.js +108 -0
  83. package/dist/prompt.js.map +1 -0
  84. package/dist/registry.d.ts +27 -0
  85. package/dist/registry.js +163 -0
  86. package/dist/registry.js.map +1 -0
  87. package/dist/result-contract.d.ts +13 -0
  88. package/dist/result-contract.js +80 -0
  89. package/dist/result-contract.js.map +1 -0
  90. package/dist/run-dir.d.ts +12 -0
  91. package/dist/run-dir.js +32 -0
  92. package/dist/run-dir.js.map +1 -0
  93. package/dist/runner.d.ts +39 -0
  94. package/dist/runner.js +220 -0
  95. package/dist/runner.js.map +1 -0
  96. package/dist/safety.d.ts +22 -0
  97. package/dist/safety.js +47 -0
  98. package/dist/safety.js.map +1 -0
  99. package/dist/schema.d.ts +69 -0
  100. package/dist/schema.js +91 -0
  101. package/dist/schema.js.map +1 -0
  102. package/dist/server.d.ts +11 -0
  103. package/dist/server.js +109 -0
  104. package/dist/server.js.map +1 -0
  105. package/package.json +34 -0
@@ -0,0 +1,75 @@
1
+ import { preferredText } from "./utils.js";
2
+ function extractCodexMessageText(value) {
3
+ if (value == null || typeof value !== "object")
4
+ return null;
5
+ const rec = value;
6
+ // Common: { type: "message", message: { role, content } }
7
+ const message = rec["message"] ??
8
+ rec["data"];
9
+ const maybeMsg = message ?? rec;
10
+ const content = maybeMsg["content"];
11
+ if (typeof content === "string")
12
+ return content;
13
+ if (Array.isArray(content)) {
14
+ const text = content
15
+ .map((part) => {
16
+ if (typeof part === "string")
17
+ return part;
18
+ if (part && typeof part === "object") {
19
+ const pr = part;
20
+ if (typeof pr["text"] === "string")
21
+ return pr["text"];
22
+ if (typeof pr["content"] === "string")
23
+ return pr["content"];
24
+ }
25
+ return "";
26
+ })
27
+ .filter((t) => t.length > 0)
28
+ .join("");
29
+ return text.length > 0 ? text : null;
30
+ }
31
+ if (typeof rec["output_text"] === "string")
32
+ return rec["output_text"];
33
+ // Codex item.completed events: { type: "item.completed", item: { text: "..." } }
34
+ const item = rec["item"];
35
+ if (item != null && typeof item === "object" && typeof item["text"] === "string") {
36
+ return item["text"];
37
+ }
38
+ return null;
39
+ }
40
+ export const codexJsonlParser = {
41
+ name: "codex_jsonl",
42
+ preferredBuffers: ["stdoutTail", "stdoutHead"],
43
+ parse(input) {
44
+ const parseJsonl = (text) => {
45
+ const lines = text
46
+ .split("\n")
47
+ .map((l) => l.trim())
48
+ .filter((l) => l.length > 0);
49
+ let lastContent = null;
50
+ for (const line of lines) {
51
+ let parsed;
52
+ try {
53
+ parsed = JSON.parse(line);
54
+ }
55
+ catch {
56
+ continue;
57
+ }
58
+ const content = extractCodexMessageText(parsed);
59
+ if (content != null)
60
+ lastContent = content;
61
+ }
62
+ return lastContent;
63
+ };
64
+ const fromTail = parseJsonl(preferredText(input, ["stdoutTail"]));
65
+ const lastContent = fromTail ?? parseJsonl(preferredText(input, ["stdoutHead"]));
66
+ if (lastContent == null)
67
+ return null;
68
+ return {
69
+ content: lastContent,
70
+ source: "parser",
71
+ metadata: { parser: "codex_jsonl" },
72
+ };
73
+ },
74
+ };
75
+ //# sourceMappingURL=codex-jsonl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex-jsonl.js","sourceRoot":"","sources":["../../src/parsers/codex-jsonl.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,SAAS,uBAAuB,CAAC,KAAc;IAC7C,IAAI,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC5D,MAAM,GAAG,GAAG,KAAgC,CAAC;IAE7C,0DAA0D;IAC1D,MAAM,OAAO,GACV,GAAG,CAAC,SAAS,CAAyC;QACtD,GAAG,CAAC,MAAM,CAAyC,CAAC;IAEvD,MAAM,QAAQ,GAAG,OAAO,IAAI,GAAG,CAAC;IAEhC,MAAM,OAAO,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IACpC,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAEhD,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,OAAO;aACjB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;YACZ,IAAI,OAAO,IAAI,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC;YAC1C,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACrC,MAAM,EAAE,GAAG,IAA+B,CAAC;gBAC3C,IAAI,OAAO,EAAE,CAAC,MAAM,CAAC,KAAK,QAAQ;oBAAE,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;gBACtD,IAAI,OAAO,EAAE,CAAC,SAAS,CAAC,KAAK,QAAQ;oBAAE,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC;YAC9D,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;aACD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;aAC3B,IAAI,CAAC,EAAE,CAAC,CAAC;QACZ,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IACvC,CAAC;IAED,IAAI,OAAO,GAAG,CAAC,aAAa,CAAC,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC,aAAa,CAAC,CAAC;IAEtE,iFAAiF;IACjF,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAwC,CAAC;IAChE,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,QAAQ,EAAE,CAAC;QACjF,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAW;IACtC,IAAI,EAAE,aAAa;IACnB,gBAAgB,EAAE,CAAC,YAAY,EAAE,YAAY,CAAC;IAC9C,KAAK,CAAC,KAAK;QACT,MAAM,UAAU,GAAG,CAAC,IAAY,EAAiB,EAAE;YACjD,MAAM,KAAK,GAAG,IAAI;iBACf,KAAK,CAAC,IAAI,CAAC;iBACX,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;iBACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAE/B,IAAI,WAAW,GAAkB,IAAI,CAAC;YACtC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,MAAe,CAAC;gBACpB,IAAI,CAAC;oBACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAY,CAAC;gBACvC,CAAC;gBAAC,MAAM,CAAC;oBACP,SAAS;gBACX,CAAC;gBACD,MAAM,OAAO,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAC;gBAChD,IAAI,OAAO,IAAI,IAAI;oBAAE,WAAW,GAAG,OAAO,CAAC;YAC7C,CAAC;YACD,OAAO,WAAW,CAAC;QACrB,CAAC,CAAC;QAEF,MAAM,QAAQ,GAAG,UAAU,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAClE,MAAM,WAAW,GACf,QAAQ,IAAI,UAAU,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC/D,IAAI,WAAW,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QACrC,OAAO;YACL,OAAO,EAAE,WAAW;YACpB,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE;SACpC,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,25 @@
1
+ import type { ParseInput, ParseResult } from "./types.js";
2
+ export interface ExtractInput extends ParseInput {
3
+ exitCode: number | null;
4
+ /**
5
+ * Default: false (non-zero exit => isError:true).
6
+ * When true, non-zero exits are tolerated if we can parse a valid payload.
7
+ */
8
+ tolerateNonzeroExitForParse?: boolean;
9
+ }
10
+ export interface ExtractOutput {
11
+ result: ParseResult;
12
+ isError: boolean;
13
+ exitCode: number | null;
14
+ }
15
+ /**
16
+ * Deterministically extract the final user-facing content from captured buffers.
17
+ *
18
+ * Order:
19
+ * 1) result contract file (if enabled + present)
20
+ * 2) adapter-specific parser
21
+ * 3) generic json_extract
22
+ * 4) <SUMMARY> extraction
23
+ * 5) raw stdout fallback
24
+ */
25
+ export declare function extractResult(input: ExtractInput): Promise<ExtractOutput>;
@@ -0,0 +1,87 @@
1
+ import { joinBuffers, preferredText } from "./utils.js";
2
+ import { textParser } from "./text.js";
3
+ import { jsonExtractParser } from "./json-extract.js";
4
+ import { geminiJsonParser } from "./gemini-json.js";
5
+ import { codexJsonlParser } from "./codex-jsonl.js";
6
+ import { claudeJsonParser } from "./claude-json.js";
7
+ import { extractSummary } from "./summary.js";
8
+ import { readResultFile } from "../result-contract.js";
9
+ const PARSERS = [
10
+ textParser,
11
+ jsonExtractParser,
12
+ geminiJsonParser,
13
+ codexJsonlParser,
14
+ claudeJsonParser,
15
+ ];
16
+ function parserByName(name) {
17
+ return PARSERS.find((p) => p.name === name) ?? null;
18
+ }
19
+ async function tryReadResultContract(workdir, contract) {
20
+ if (!contract?.enabled)
21
+ return null;
22
+ if (contract.filename.trim().length === 0)
23
+ return null;
24
+ return readResultFile(workdir, contract.filename);
25
+ }
26
+ function rawStdoutFallback(input) {
27
+ // JSONL adapters emit final events late; prefer tail.
28
+ const preferTailFirst = input.adapter.parser === "codex_jsonl" || input.adapter.parser === "jsonl";
29
+ const content = preferTailFirst
30
+ ? input.stdoutTail.length > 0
31
+ ? input.stdoutTail
32
+ : input.stdoutHead
33
+ : joinBuffers([input.stdoutHead, input.stdoutTail]);
34
+ return { content, source: "raw_fallback" };
35
+ }
36
+ function hasValidPayload(result) {
37
+ return result.content.trim().length > 0;
38
+ }
39
+ /**
40
+ * Deterministically extract the final user-facing content from captured buffers.
41
+ *
42
+ * Order:
43
+ * 1) result contract file (if enabled + present)
44
+ * 2) adapter-specific parser
45
+ * 3) generic json_extract
46
+ * 4) <SUMMARY> extraction
47
+ * 5) raw stdout fallback
48
+ */
49
+ export async function extractResult(input) {
50
+ const contractText = await tryReadResultContract(input.workdir, input.adapter.result_contract);
51
+ let result = contractText != null
52
+ ? {
53
+ content: contractText,
54
+ source: "result_contract",
55
+ metadata: { filename: input.adapter.result_contract?.filename },
56
+ }
57
+ : null;
58
+ if (result == null) {
59
+ const adapterParser = parserByName(input.adapter.parser);
60
+ result = adapterParser?.parse(input) ?? null;
61
+ }
62
+ if (result == null && input.adapter.parser !== "json_extract") {
63
+ result = jsonExtractParser.parse(input) ?? null;
64
+ }
65
+ if (result == null) {
66
+ const stdoutCombined = preferredText(input, ["stdoutHead", "stdoutTail"]);
67
+ const summary = extractSummary(stdoutCombined);
68
+ if (summary != null) {
69
+ result = { content: summary, source: "summary" };
70
+ }
71
+ }
72
+ if (result == null) {
73
+ result = rawStdoutFallback(input);
74
+ }
75
+ // Error recovery parity: always attempt extraction, even on non-zero exit.
76
+ const exitCode = input.exitCode;
77
+ const tolerate = input.tolerateNonzeroExitForParse ?? false;
78
+ const isError = exitCode === 0
79
+ ? false
80
+ : exitCode == null
81
+ ? true
82
+ : tolerate && hasValidPayload(result)
83
+ ? false
84
+ : true;
85
+ return { result, isError, exitCode };
86
+ }
87
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../../src/parsers/extract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Parser } from "./types.js";
2
+ export declare const geminiJsonParser: Parser;
@@ -0,0 +1,72 @@
1
+ import { findFirstJsonBlock, preferredText } from "./utils.js";
2
+ function extractGeminiText(value) {
3
+ if (value == null || typeof value !== "object")
4
+ return null;
5
+ const root = value;
6
+ // Simple { "response": "text" } format (PAL parity)
7
+ if (typeof root["response"] === "string") {
8
+ return root["response"].trim() || null;
9
+ }
10
+ const response = root["response"] ?? root;
11
+ const candidates = response["candidates"];
12
+ if (!Array.isArray(candidates))
13
+ return null;
14
+ for (const cand of candidates) {
15
+ if (cand == null || typeof cand !== "object")
16
+ continue;
17
+ const c = cand;
18
+ const content = c["content"];
19
+ if (content && typeof content === "object") {
20
+ const contentObj = content;
21
+ const parts = contentObj["parts"];
22
+ if (Array.isArray(parts)) {
23
+ const texts = parts
24
+ .map((p) => {
25
+ if (p && typeof p === "object") {
26
+ const pr = p;
27
+ if (typeof pr["text"] === "string")
28
+ return pr["text"];
29
+ }
30
+ return "";
31
+ })
32
+ .filter((t) => t.length > 0);
33
+ if (texts.length > 0)
34
+ return texts.join("");
35
+ }
36
+ }
37
+ if (typeof c["text"] === "string")
38
+ return c["text"];
39
+ }
40
+ return null;
41
+ }
42
+ function parseMaybeJson(text) {
43
+ const trimmed = text.trim();
44
+ if (trimmed.length === 0)
45
+ return null;
46
+ try {
47
+ return JSON.parse(trimmed);
48
+ }
49
+ catch {
50
+ const block = findFirstJsonBlock(text);
51
+ return block?.value ?? null;
52
+ }
53
+ }
54
+ export const geminiJsonParser = {
55
+ name: "gemini_json",
56
+ preferredBuffers: ["stdoutHead", "stdoutTail"],
57
+ parse(input) {
58
+ const text = preferredText(input, ["stdoutHead", "stdoutTail"]);
59
+ const parsed = parseMaybeJson(text);
60
+ if (parsed == null)
61
+ return null;
62
+ const extracted = extractGeminiText(parsed);
63
+ if (extracted == null)
64
+ return null;
65
+ return {
66
+ content: extracted,
67
+ source: "parser",
68
+ metadata: { parser: "gemini_json" },
69
+ };
70
+ },
71
+ };
72
+ //# sourceMappingURL=gemini-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini-json.js","sourceRoot":"","sources":["../../src/parsers/gemini-json.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { Parser } from "./types.js";
2
+ export declare const jsonExtractParser: Parser;
@@ -0,0 +1,19 @@
1
+ import { findFirstJsonBlock, extractTextFromContentField, preferredText } from "./utils.js";
2
+ export const jsonExtractParser = {
3
+ name: "json_extract",
4
+ preferredBuffers: ["stdoutHead", "stdoutTail"],
5
+ parse(input) {
6
+ const text = preferredText(input, ["stdoutHead", "stdoutTail"]);
7
+ const block = findFirstJsonBlock(text);
8
+ if (block == null)
9
+ return null;
10
+ const fromContent = extractTextFromContentField(block.value);
11
+ const content = fromContent ?? JSON.stringify(block.value);
12
+ return {
13
+ content,
14
+ source: "json_extract",
15
+ metadata: { parser: "json_extract" },
16
+ };
17
+ },
18
+ };
19
+ //# sourceMappingURL=json-extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-extract.js","sourceRoot":"","sources":["../../src/parsers/json-extract.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5F,MAAM,CAAC,MAAM,iBAAiB,GAAW;IACvC,IAAI,EAAE,cAAc;IACpB,gBAAgB,EAAE,CAAC,YAAY,EAAE,YAAY,CAAC;IAC9C,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,EAAE,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;QAChE,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,KAAK,IAAI,IAAI;YAAE,OAAO,IAAI,CAAC;QAE/B,MAAM,WAAW,GAAG,2BAA2B,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,WAAW,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAE3D,OAAO;YACL,OAAO;YACP,MAAM,EAAE,cAAc;YACtB,QAAQ,EAAE,EAAE,MAAM,EAAE,cAAc,EAAE;SACrC,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Extract a user-facing summary wrapped in <SUMMARY>...</SUMMARY>.
3
+ *
4
+ * Designed as a late-stage fallback: it ignores any tool noise outside the tags
5
+ * and preserves user formatting inside the tags.
6
+ */
7
+ export declare function extractSummary(text: string): string | null;
@@ -0,0 +1,29 @@
1
+ import { normalizeNewlines } from "./utils.js";
2
+ function trimBlankEdges(text) {
3
+ const lines = normalizeNewlines(text).split("\n");
4
+ while (lines.length > 0 && lines[0].trim().length === 0)
5
+ lines.shift();
6
+ while (lines.length > 0 && lines[lines.length - 1].trim().length === 0)
7
+ lines.pop();
8
+ return lines.join("\n");
9
+ }
10
+ /**
11
+ * Extract a user-facing summary wrapped in <SUMMARY>...</SUMMARY>.
12
+ *
13
+ * Designed as a late-stage fallback: it ignores any tool noise outside the tags
14
+ * and preserves user formatting inside the tags.
15
+ */
16
+ export function extractSummary(text) {
17
+ const normalized = normalizeNewlines(text);
18
+ const re = /<SUMMARY>([\s\S]*?)<\/SUMMARY>/gi;
19
+ let match;
20
+ let lastContent = null;
21
+ while ((match = re.exec(normalized)) !== null) {
22
+ lastContent = match[1];
23
+ }
24
+ if (lastContent == null)
25
+ return null;
26
+ const cleaned = trimBlankEdges(lastContent);
27
+ return cleaned.length > 0 ? cleaned : null;
28
+ }
29
+ //# sourceMappingURL=summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.js","sourceRoot":"","sources":["../../src/parsers/summary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,SAAS,cAAc,CAAC,IAAY;IAClC,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,KAAK,CAAC,KAAK,EAAE,CAAC;IACxE,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QACrE,KAAK,CAAC,GAAG,EAAE,CAAC;IACd,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,EAAE,GAAG,kCAAkC,CAAC;IAC9C,IAAI,KAA6B,CAAC;IAClC,IAAI,WAAW,GAAkB,IAAI,CAAC;IACtC,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC9C,WAAW,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IAC1B,CAAC;IACD,IAAI,WAAW,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,OAAO,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;IAC5C,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { Parser } from "./types.js";
2
+ export declare const textParser: Parser;
@@ -0,0 +1,14 @@
1
+ import { preferredText } from "./utils.js";
2
+ export const textParser = {
3
+ name: "text",
4
+ preferredBuffers: ["stdoutHead", "stdoutTail"],
5
+ parse(input) {
6
+ const content = preferredText(input, ["stdoutHead", "stdoutTail"]);
7
+ return {
8
+ content,
9
+ source: "parser",
10
+ metadata: { parser: "text" },
11
+ };
12
+ },
13
+ };
14
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/parsers/text.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,CAAC,MAAM,UAAU,GAAW;IAChC,IAAI,EAAE,MAAM;IACZ,gBAAgB,EAAE,CAAC,YAAY,EAAE,YAAY,CAAC;IAC9C,KAAK,CAAC,KAAK;QACT,MAAM,OAAO,GAAG,aAAa,CAAC,KAAK,EAAE,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC,CAAC;QACnE,OAAO;YACL,OAAO;YACP,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;SAC7B,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,25 @@
1
+ export interface ParseInput {
2
+ stdoutHead: string;
3
+ stdoutTail: string;
4
+ stderrHead: string;
5
+ stderrTail: string;
6
+ workdir: string;
7
+ adapter: {
8
+ name: string;
9
+ parser: string;
10
+ result_contract?: {
11
+ enabled: boolean;
12
+ filename: string;
13
+ };
14
+ };
15
+ }
16
+ export interface ParseResult {
17
+ content: string;
18
+ source: "result_contract" | "parser" | "json_extract" | "summary" | "raw_fallback";
19
+ metadata?: Record<string, unknown>;
20
+ }
21
+ export interface Parser {
22
+ name: string;
23
+ preferredBuffers: ("stdoutHead" | "stdoutTail" | "stderrHead" | "stderrTail")[];
24
+ parse(input: ParseInput): ParseResult | null;
25
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/parsers/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ import type { ParseInput } from "./types.js";
2
+ export declare function joinBuffers(parts: string[]): string;
3
+ export declare function preferredText(input: ParseInput, buffers: Array<keyof Pick<ParseInput, "stdoutHead" | "stdoutTail" | "stderrHead" | "stderrTail">>): string;
4
+ type JsonBlock = {
5
+ raw: string;
6
+ value: unknown;
7
+ };
8
+ export declare function findFirstJsonBlock(text: string): JsonBlock | null;
9
+ export declare function extractTextFromContentField(value: unknown): string | null;
10
+ export declare function normalizeNewlines(text: string): string;
11
+ export {};
@@ -0,0 +1,116 @@
1
+ export function joinBuffers(parts) {
2
+ const nonEmpty = parts.filter((p) => p.length > 0);
3
+ if (nonEmpty.length === 0)
4
+ return "";
5
+ let out = nonEmpty[0];
6
+ for (let i = 1; i < nonEmpty.length; i++) {
7
+ const next = nonEmpty[i];
8
+ if (out.endsWith("\n") || next.startsWith("\n")) {
9
+ out += next;
10
+ }
11
+ else {
12
+ out += "\n" + next;
13
+ }
14
+ }
15
+ return out;
16
+ }
17
+ export function preferredText(input, buffers) {
18
+ return joinBuffers(buffers.map((b) => input[b]));
19
+ }
20
+ function scanJsonBlock(text, startIndex) {
21
+ const open = text[startIndex];
22
+ if (open !== "{" && open !== "[")
23
+ return null;
24
+ const stack = [open === "{" ? "}" : "]"];
25
+ let inString = false;
26
+ let escaped = false;
27
+ for (let i = startIndex + 1; i < text.length; i++) {
28
+ const ch = text[i];
29
+ if (inString) {
30
+ if (escaped) {
31
+ escaped = false;
32
+ continue;
33
+ }
34
+ if (ch === "\\") {
35
+ escaped = true;
36
+ continue;
37
+ }
38
+ if (ch === '"') {
39
+ inString = false;
40
+ }
41
+ continue;
42
+ }
43
+ if (ch === '"') {
44
+ inString = true;
45
+ continue;
46
+ }
47
+ if (ch === "{") {
48
+ stack.push("}");
49
+ continue;
50
+ }
51
+ if (ch === "[") {
52
+ stack.push("]");
53
+ continue;
54
+ }
55
+ if (ch === "}" || ch === "]") {
56
+ const expected = stack[stack.length - 1];
57
+ if (expected !== ch)
58
+ return null;
59
+ stack.pop();
60
+ if (stack.length === 0) {
61
+ return text.slice(startIndex, i + 1);
62
+ }
63
+ }
64
+ }
65
+ return null;
66
+ }
67
+ export function findFirstJsonBlock(text) {
68
+ for (let i = 0; i < text.length; i++) {
69
+ const ch = text[i];
70
+ if (ch !== "{" && ch !== "[")
71
+ continue;
72
+ const raw = scanJsonBlock(text, i);
73
+ if (raw == null)
74
+ continue;
75
+ try {
76
+ const value = JSON.parse(raw);
77
+ return { raw, value };
78
+ }
79
+ catch {
80
+ // Keep scanning.
81
+ }
82
+ }
83
+ return null;
84
+ }
85
+ export function extractTextFromContentField(value) {
86
+ if (value == null || typeof value !== "object")
87
+ return null;
88
+ const record = value;
89
+ if (!("content" in record))
90
+ return null;
91
+ const content = record["content"];
92
+ if (typeof content === "string")
93
+ return content;
94
+ if (Array.isArray(content)) {
95
+ const texts = content
96
+ .map((part) => {
97
+ if (typeof part === "string")
98
+ return part;
99
+ if (part && typeof part === "object") {
100
+ const r = part;
101
+ if (typeof r["text"] === "string")
102
+ return r["text"];
103
+ if (typeof r["content"] === "string")
104
+ return r["content"];
105
+ }
106
+ return "";
107
+ })
108
+ .filter((t) => t.length > 0);
109
+ return texts.length > 0 ? texts.join("\n") : null;
110
+ }
111
+ return null;
112
+ }
113
+ export function normalizeNewlines(text) {
114
+ return text.replaceAll("\r\n", "\n");
115
+ }
116
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/parsers/utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Startup guard: refuse to run if cwd resolves to filesystem root
3
+ * unless CLINKX_ALLOWED_ROOTS is explicitly set.
4
+ *
5
+ * Call this during server startup.
6
+ */
7
+ export declare function assertNotRootDir(): void;
8
+ /**
9
+ * Validate that a given path resolves within one of the allowed roots.
10
+ *
11
+ * - Resolves the real path (following symlinks)
12
+ * - Checks the resolved path starts with an allowed root
13
+ * - Blocks symlink escapes: realpath must stay within an allow-root
14
+ *
15
+ * Throws InvalidParamsError if validation fails.
16
+ */
17
+ export declare function validatePath(inputPath: string): string;
package/dist/paths.js ADDED
@@ -0,0 +1,87 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import { InvalidParamsError } from "./errors.js";
4
+ import { logger } from "./logger.js";
5
+ /**
6
+ * Parse CLINKX_ALLOWED_ROOTS env into an array of resolved absolute paths.
7
+ * Falls back to process.cwd() when not set.
8
+ */
9
+ function getAllowedRoots() {
10
+ const envVal = process.env["CLINKX_ALLOWED_ROOTS"];
11
+ const raw = envVal != null && envVal.length > 0
12
+ ? envVal
13
+ .split(",")
14
+ .map((r) => r.trim())
15
+ .filter((r) => r.length > 0)
16
+ : [process.cwd()];
17
+ // Resolve through realpath so symlinks (e.g. macOS /var → /private/var)
18
+ // are consistent with how we resolve validated paths.
19
+ return raw.map((r) => {
20
+ try {
21
+ return fs.realpathSync(path.resolve(r));
22
+ }
23
+ catch {
24
+ return path.resolve(r);
25
+ }
26
+ });
27
+ }
28
+ /**
29
+ * Check whether a resolved path is a filesystem root.
30
+ * Handles Unix (/) and Windows drive roots (C:\).
31
+ */
32
+ function isFilesystemRoot(resolved) {
33
+ // Unix root
34
+ if (resolved === "/")
35
+ return true;
36
+ // Windows drive root: single letter + colon + backslash (or just colon + backslash)
37
+ if (/^[a-zA-Z]:[/\\]?$/.test(resolved))
38
+ return true;
39
+ return false;
40
+ }
41
+ /**
42
+ * Startup guard: refuse to run if cwd resolves to filesystem root
43
+ * unless CLINKX_ALLOWED_ROOTS is explicitly set.
44
+ *
45
+ * Call this during server startup.
46
+ */
47
+ export function assertNotRootDir() {
48
+ const cwd = path.resolve(process.cwd());
49
+ const hasExplicitRoots = process.env["CLINKX_ALLOWED_ROOTS"] != null &&
50
+ process.env["CLINKX_ALLOWED_ROOTS"].length > 0;
51
+ if (isFilesystemRoot(cwd) && !hasExplicitRoots) {
52
+ throw new Error(`FATAL: cwd resolves to filesystem root (${cwd}). ` +
53
+ "Running ClinkX from the filesystem root without explicit CLINKX_ALLOWED_ROOTS " +
54
+ "is refused for safety. Set CLINKX_ALLOWED_ROOTS to an explicit comma-separated " +
55
+ "list of allowed directories.");
56
+ }
57
+ logger.debug({ cwd, hasExplicitRoots }, "root-dir startup guard passed");
58
+ }
59
+ /**
60
+ * Validate that a given path resolves within one of the allowed roots.
61
+ *
62
+ * - Resolves the real path (following symlinks)
63
+ * - Checks the resolved path starts with an allowed root
64
+ * - Blocks symlink escapes: realpath must stay within an allow-root
65
+ *
66
+ * Throws InvalidParamsError if validation fails.
67
+ */
68
+ export function validatePath(inputPath) {
69
+ const roots = getAllowedRoots();
70
+ let resolved;
71
+ try {
72
+ resolved = fs.realpathSync(inputPath);
73
+ }
74
+ catch {
75
+ throw new InvalidParamsError(`Path does not exist or is not accessible: ${inputPath}`);
76
+ }
77
+ const withinRoot = roots.some((root) => {
78
+ const normalizedRoot = root.endsWith(path.sep) ? root : root + path.sep;
79
+ return resolved === root || resolved.startsWith(normalizedRoot);
80
+ });
81
+ if (!withinRoot) {
82
+ throw new InvalidParamsError(`Path "${inputPath}" (resolved: ${resolved}) is outside allowed roots. ` +
83
+ `Allowed: ${roots.join(", ")}`);
84
+ }
85
+ return resolved;
86
+ }
87
+ //# sourceMappingURL=paths.js.map