clanka 0.0.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.
Files changed (72) hide show
  1. package/README.md +3 -0
  2. package/dist/Agent.d.ts +119 -0
  3. package/dist/Agent.d.ts.map +1 -0
  4. package/dist/Agent.js +240 -0
  5. package/dist/Agent.js.map +1 -0
  6. package/dist/AgentTools.d.ts +246 -0
  7. package/dist/AgentTools.d.ts.map +1 -0
  8. package/dist/AgentTools.js +374 -0
  9. package/dist/AgentTools.js.map +1 -0
  10. package/dist/AgentTools.test.d.ts +2 -0
  11. package/dist/AgentTools.test.d.ts.map +1 -0
  12. package/dist/AgentTools.test.js +147 -0
  13. package/dist/AgentTools.test.js.map +1 -0
  14. package/dist/ApplyPatch.d.ts +27 -0
  15. package/dist/ApplyPatch.d.ts.map +1 -0
  16. package/dist/ApplyPatch.js +343 -0
  17. package/dist/ApplyPatch.js.map +1 -0
  18. package/dist/ApplyPatch.test.d.ts +2 -0
  19. package/dist/ApplyPatch.test.d.ts.map +1 -0
  20. package/dist/ApplyPatch.test.js +99 -0
  21. package/dist/ApplyPatch.test.js.map +1 -0
  22. package/dist/Codex.d.ts +11 -0
  23. package/dist/Codex.d.ts.map +1 -0
  24. package/dist/Codex.js +14 -0
  25. package/dist/Codex.js.map +1 -0
  26. package/dist/CodexAuth.d.ts +68 -0
  27. package/dist/CodexAuth.d.ts.map +1 -0
  28. package/dist/CodexAuth.js +270 -0
  29. package/dist/CodexAuth.js.map +1 -0
  30. package/dist/CodexAuth.test.d.ts +2 -0
  31. package/dist/CodexAuth.test.d.ts.map +1 -0
  32. package/dist/CodexAuth.test.js +425 -0
  33. package/dist/CodexAuth.test.js.map +1 -0
  34. package/dist/Executor.d.ts +20 -0
  35. package/dist/Executor.d.ts.map +1 -0
  36. package/dist/Executor.js +76 -0
  37. package/dist/Executor.js.map +1 -0
  38. package/dist/OutputFormatter.d.ts +11 -0
  39. package/dist/OutputFormatter.d.ts.map +1 -0
  40. package/dist/OutputFormatter.js +5 -0
  41. package/dist/OutputFormatter.js.map +1 -0
  42. package/dist/ToolkitRenderer.d.ts +17 -0
  43. package/dist/ToolkitRenderer.d.ts.map +1 -0
  44. package/dist/ToolkitRenderer.js +25 -0
  45. package/dist/ToolkitRenderer.js.map +1 -0
  46. package/dist/TypeBuilder.d.ts +11 -0
  47. package/dist/TypeBuilder.d.ts.map +1 -0
  48. package/dist/TypeBuilder.js +383 -0
  49. package/dist/TypeBuilder.js.map +1 -0
  50. package/dist/TypeBuilder.test.d.ts +2 -0
  51. package/dist/TypeBuilder.test.d.ts.map +1 -0
  52. package/dist/TypeBuilder.test.js +243 -0
  53. package/dist/TypeBuilder.test.js.map +1 -0
  54. package/dist/index.d.ts +25 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +25 -0
  57. package/dist/index.js.map +1 -0
  58. package/package.json +72 -0
  59. package/src/Agent.ts +398 -0
  60. package/src/AgentTools.test.ts +215 -0
  61. package/src/AgentTools.ts +507 -0
  62. package/src/ApplyPatch.test.ts +154 -0
  63. package/src/ApplyPatch.ts +473 -0
  64. package/src/Codex.ts +14 -0
  65. package/src/CodexAuth.test.ts +729 -0
  66. package/src/CodexAuth.ts +571 -0
  67. package/src/Executor.ts +129 -0
  68. package/src/OutputFormatter.ts +17 -0
  69. package/src/ToolkitRenderer.ts +39 -0
  70. package/src/TypeBuilder.test.ts +508 -0
  71. package/src/TypeBuilder.ts +670 -0
  72. package/src/index.ts +29 -0
@@ -0,0 +1,473 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+
5
+ export type Chunk = {
6
+ readonly old: ReadonlyArray<string>
7
+ readonly next: ReadonlyArray<string>
8
+ readonly ctx?: string
9
+ readonly eof?: boolean
10
+ }
11
+
12
+ type Wrapped = {
13
+ readonly path: string
14
+ readonly chunks: ReadonlyArray<Chunk>
15
+ }
16
+
17
+ export type FilePatch =
18
+ | {
19
+ readonly type: "add"
20
+ readonly path: string
21
+ readonly content: string
22
+ }
23
+ | {
24
+ readonly type: "delete"
25
+ readonly path: string
26
+ }
27
+ | {
28
+ readonly type: "update"
29
+ readonly path: string
30
+ readonly chunks: ReadonlyArray<Chunk>
31
+ readonly movePath?: string
32
+ }
33
+
34
+ const BEGIN = "*** Begin Patch"
35
+ const END = "*** End Patch"
36
+ const ADD = "*** Add File:"
37
+ const DELETE = "*** Delete File:"
38
+ const MOVE = "*** Move to:"
39
+ const UPDATE = "*** Update File:"
40
+
41
+ const stripHeredoc = (input: string): string => {
42
+ const match = input.match(
43
+ /^(?:cat\s+)?<<['"]?(\w+)['"]?\s*\n([\s\S]*?)\n\1\s*$/,
44
+ )
45
+ return match?.[2] ?? input
46
+ }
47
+
48
+ const normalize = (input: string): string =>
49
+ stripHeredoc(input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim())
50
+
51
+ const fail = (message: string): never => {
52
+ throw new Error(`applyPatch verification failed: ${message}`)
53
+ }
54
+
55
+ const locate = (text: string) => {
56
+ const lines = text.split("\n")
57
+ const begin = lines.findIndex((line) => line === BEGIN)
58
+ const end = lines.findIndex((line) => line === END)
59
+ if (begin === -1 || end === -1 || begin >= end) {
60
+ fail("Invalid patch format: missing Begin/End markers")
61
+ }
62
+ return {
63
+ lines,
64
+ begin,
65
+ end,
66
+ }
67
+ }
68
+
69
+ const parseChunks = (
70
+ lines: ReadonlyArray<string>,
71
+ start: number,
72
+ end = lines.length,
73
+ ) => {
74
+ const chunks = Array<Chunk>()
75
+ let i = start
76
+
77
+ while (i < end) {
78
+ const line = lines[i]!
79
+ if (line.startsWith("***")) {
80
+ break
81
+ }
82
+ if (!line.startsWith("@@")) {
83
+ i++
84
+ continue
85
+ }
86
+
87
+ const ctx = line.slice(2).trim()
88
+ const old = Array<string>()
89
+ const next = Array<string>()
90
+ let eof = false
91
+ i++
92
+
93
+ while (i < end) {
94
+ const line = lines[i]!
95
+ if (line === "*** End of File") {
96
+ eof = true
97
+ i++
98
+ break
99
+ }
100
+ if (line.startsWith("@@") || line.startsWith("***")) {
101
+ break
102
+ }
103
+ if (line.startsWith(" ")) {
104
+ const text = line.slice(1)
105
+ old.push(text)
106
+ next.push(text)
107
+ } else if (line.startsWith("-")) {
108
+ old.push(line.slice(1))
109
+ } else if (line.startsWith("+")) {
110
+ next.push(line.slice(1))
111
+ }
112
+ i++
113
+ }
114
+
115
+ chunks.push({
116
+ old,
117
+ next,
118
+ ...(ctx.length > 0 ? { ctx } : {}),
119
+ ...(eof ? { eof: true } : {}),
120
+ })
121
+ }
122
+
123
+ return {
124
+ chunks,
125
+ next: i,
126
+ }
127
+ }
128
+
129
+ const parseWrapped = (text: string): Wrapped => {
130
+ const { lines, begin, end } = locate(text)
131
+
132
+ let i = begin + 1
133
+ while (i < end && lines[i]!.trim() === "") {
134
+ i++
135
+ }
136
+ if (i === end) {
137
+ throw new Error("patch rejected: empty patch")
138
+ }
139
+ if (!lines[i]!.startsWith(UPDATE)) {
140
+ fail("only single-file update patches are supported")
141
+ }
142
+ const path = lines[i]!.slice(UPDATE.length).trim()
143
+ if (path.length === 0) {
144
+ fail("missing update file path")
145
+ }
146
+
147
+ i++
148
+ if (i < end && lines[i]!.startsWith(MOVE)) {
149
+ fail("move patches are not supported")
150
+ }
151
+
152
+ const parsed = parseChunks(lines, i, end)
153
+ if (parsed.chunks.length === 0) {
154
+ fail("no hunks found")
155
+ }
156
+
157
+ i = parsed.next
158
+ while (i < end && lines[i]!.trim() === "") {
159
+ i++
160
+ }
161
+ if (i !== end) {
162
+ fail("only one update file section is supported")
163
+ }
164
+
165
+ return {
166
+ path,
167
+ chunks: parsed.chunks,
168
+ }
169
+ }
170
+
171
+ const parseAdd = (lines: ReadonlyArray<string>, start: number, end: number) => {
172
+ const out = Array<string>()
173
+ let i = start
174
+
175
+ while (i < end) {
176
+ const line = lines[i]!
177
+ if (line.startsWith("***")) {
178
+ break
179
+ }
180
+ if (line.startsWith("+")) {
181
+ out.push(line.slice(1))
182
+ }
183
+ i++
184
+ }
185
+
186
+ return {
187
+ content: out.join("\n"),
188
+ next: i,
189
+ }
190
+ }
191
+
192
+ export const parsePatch = (input: string): ReadonlyArray<FilePatch> => {
193
+ const text = normalize(input)
194
+ if (text.length === 0) {
195
+ throw new Error("patchText is required")
196
+ }
197
+ if (text === `${BEGIN}\n${END}`) {
198
+ throw new Error("patch rejected: empty patch")
199
+ }
200
+
201
+ const { lines, begin, end } = locate(text)
202
+ const out = Array<FilePatch>()
203
+ let i = begin + 1
204
+
205
+ while (i < end) {
206
+ while (i < end && lines[i]!.trim() === "") {
207
+ i++
208
+ }
209
+ if (i === end) {
210
+ break
211
+ }
212
+
213
+ const line = lines[i]!
214
+ if (line.startsWith(ADD)) {
215
+ const path = line.slice(ADD.length).trim()
216
+ if (path.length === 0) {
217
+ fail("missing add file path")
218
+ }
219
+ const parsed = parseAdd(lines, i + 1, end)
220
+ out.push({
221
+ type: "add",
222
+ path,
223
+ content: parsed.content,
224
+ })
225
+ i = parsed.next
226
+ continue
227
+ }
228
+ if (line.startsWith(DELETE)) {
229
+ const path = line.slice(DELETE.length).trim()
230
+ if (path.length === 0) {
231
+ fail("missing delete file path")
232
+ }
233
+ out.push({
234
+ type: "delete",
235
+ path,
236
+ })
237
+ i++
238
+ continue
239
+ }
240
+ if (line.startsWith(UPDATE)) {
241
+ const path = line.slice(UPDATE.length).trim()
242
+ if (path.length === 0) {
243
+ fail("missing update file path")
244
+ }
245
+
246
+ i++
247
+ let movePath: string | undefined
248
+ if (i < end && lines[i]!.startsWith(MOVE)) {
249
+ movePath = lines[i]!.slice(MOVE.length).trim()
250
+ if (movePath.length === 0) {
251
+ fail("missing move file path")
252
+ }
253
+ i++
254
+ }
255
+
256
+ const parsed = parseChunks(lines, i, end)
257
+ if (parsed.chunks.length === 0) {
258
+ fail("no hunks found")
259
+ }
260
+
261
+ out.push({
262
+ type: "update",
263
+ path,
264
+ chunks: parsed.chunks,
265
+ ...(movePath === undefined ? {} : { movePath }),
266
+ })
267
+ i = parsed.next
268
+ continue
269
+ }
270
+
271
+ fail(`unexpected line in wrapped patch: ${line}`)
272
+ }
273
+
274
+ if (out.length === 0) {
275
+ fail("no hunks found")
276
+ }
277
+
278
+ return out
279
+ }
280
+
281
+ export const wrappedPath = (input: string): string | undefined => {
282
+ const text = normalize(input)
283
+ if (!text.startsWith(BEGIN)) {
284
+ return
285
+ }
286
+ return parseWrapped(text).path
287
+ }
288
+
289
+ const parse = (input: string): ReadonlyArray<Chunk> => {
290
+ const text = normalize(input)
291
+ if (text.length === 0) {
292
+ throw new Error("patchText is required")
293
+ }
294
+ if (text === `${BEGIN}\n${END}`) {
295
+ throw new Error("patch rejected: empty patch")
296
+ }
297
+
298
+ if (text.startsWith(BEGIN)) {
299
+ return parseWrapped(text).chunks
300
+ }
301
+
302
+ const parsed = parseChunks(text.split("\n"), 0)
303
+ if (parsed.chunks.length === 0) {
304
+ fail("no hunks found")
305
+ }
306
+ return parsed.chunks
307
+ }
308
+
309
+ const normalizeUnicode = (line: string): string =>
310
+ line
311
+ .replace(/[\u2018\u2019\u201A\u201B]/g, "'")
312
+ .replace(/[\u201C\u201D\u201E\u201F]/g, '"')
313
+ .replace(/[\u2010\u2011\u2012\u2013\u2014\u2015]/g, "-")
314
+ .replace(/\u2026/g, "...")
315
+ .replace(/\u00A0/g, " ")
316
+
317
+ const match = (
318
+ lines: ReadonlyArray<string>,
319
+ part: ReadonlyArray<string>,
320
+ from: number,
321
+ same: (left: string, right: string) => boolean,
322
+ eof: boolean,
323
+ ): number => {
324
+ if (eof) {
325
+ const last = lines.length - part.length
326
+ if (last >= from) {
327
+ let ok = true
328
+ for (let i = 0; i < part.length; i++) {
329
+ if (!same(lines[last + i]!, part[i]!)) {
330
+ ok = false
331
+ break
332
+ }
333
+ }
334
+ if (ok) {
335
+ return last
336
+ }
337
+ }
338
+ }
339
+
340
+ for (let i = from; i <= lines.length - part.length; i++) {
341
+ let ok = true
342
+ for (let j = 0; j < part.length; j++) {
343
+ if (!same(lines[i + j]!, part[j]!)) {
344
+ ok = false
345
+ break
346
+ }
347
+ }
348
+ if (ok) {
349
+ return i
350
+ }
351
+ }
352
+
353
+ return -1
354
+ }
355
+
356
+ const seek = (
357
+ lines: ReadonlyArray<string>,
358
+ part: ReadonlyArray<string>,
359
+ from: number,
360
+ eof = false,
361
+ ): number => {
362
+ if (part.length === 0) {
363
+ return -1
364
+ }
365
+
366
+ const exact = match(lines, part, from, (left, right) => left === right, eof)
367
+ if (exact !== -1) {
368
+ return exact
369
+ }
370
+
371
+ const rstrip = match(
372
+ lines,
373
+ part,
374
+ from,
375
+ (left, right) => left.trimEnd() === right.trimEnd(),
376
+ eof,
377
+ )
378
+ if (rstrip !== -1) {
379
+ return rstrip
380
+ }
381
+
382
+ const trim = match(
383
+ lines,
384
+ part,
385
+ from,
386
+ (left, right) => left.trim() === right.trim(),
387
+ eof,
388
+ )
389
+ if (trim !== -1) {
390
+ return trim
391
+ }
392
+
393
+ return match(
394
+ lines,
395
+ part,
396
+ from,
397
+ (left, right) =>
398
+ normalizeUnicode(left.trim()) === normalizeUnicode(right.trim()),
399
+ eof,
400
+ )
401
+ }
402
+
403
+ const compute = (
404
+ file: string,
405
+ lines: ReadonlyArray<string>,
406
+ chunks: ReadonlyArray<Chunk>,
407
+ ): Array<readonly [number, number, ReadonlyArray<string>]> => {
408
+ const out = Array<readonly [number, number, ReadonlyArray<string>]>()
409
+ let from = 0
410
+
411
+ for (const chunk of chunks) {
412
+ if (chunk.ctx) {
413
+ const at = seek(lines, [chunk.ctx], from)
414
+ if (at === -1) {
415
+ fail(`Failed to find context '${chunk.ctx}' in ${file}`)
416
+ }
417
+ from = at + 1
418
+ }
419
+
420
+ if (chunk.old.length === 0) {
421
+ out.push([lines.length, 0, chunk.next])
422
+ continue
423
+ }
424
+
425
+ let old = chunk.old
426
+ let next = chunk.next
427
+ let at = seek(lines, old, from, chunk.eof === true)
428
+ if (at === -1 && old.at(-1) === "") {
429
+ old = old.slice(0, -1)
430
+ next = next.at(-1) === "" ? next.slice(0, -1) : next
431
+ at = seek(lines, old, from, chunk.eof === true)
432
+ }
433
+ if (at === -1) {
434
+ fail(`Failed to find expected lines in ${file}:\n${chunk.old.join("\n")}`)
435
+ }
436
+
437
+ out.push([at, old.length, next])
438
+ from = at + old.length
439
+ }
440
+
441
+ out.sort((left, right) => left[0] - right[0])
442
+ return out
443
+ }
444
+
445
+ export const patchChunks = (
446
+ file: string,
447
+ input: string,
448
+ chunks: ReadonlyArray<Chunk>,
449
+ ): string => {
450
+ const eol = input.includes("\r\n") ? "\r\n" : "\n"
451
+ const lines = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n")
452
+ if (lines.at(-1) === "") {
453
+ lines.pop()
454
+ }
455
+
456
+ const out = [...lines]
457
+ for (const [at, size, next] of compute(file, lines, chunks).reverse()) {
458
+ out.splice(at, size, ...next)
459
+ }
460
+
461
+ if (out.at(-1) !== "") {
462
+ out.push("")
463
+ }
464
+
465
+ const text = out.join("\n")
466
+ return eol === "\r\n" ? text.replace(/\n/g, "\r\n") : text
467
+ }
468
+
469
+ export const patchContent = (
470
+ file: string,
471
+ input: string,
472
+ patchText: string,
473
+ ): string => patchChunks(file, input, parse(patchText))
package/src/Codex.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+ import { OpenAiClient } from "@effect/ai-openai"
5
+ import { Layer } from "effect"
6
+ import { CodexAuth } from "./CodexAuth.ts"
7
+
8
+ /**
9
+ * @since 1.0.0
10
+ * @category Layers
11
+ */
12
+ export const CodexAiClient = OpenAiClient.layer({
13
+ apiUrl: "https://chatgpt.com/backend-api/codex",
14
+ }).pipe(Layer.provide(CodexAuth.layerClient))