@kindgi/cli 0.1.8-rc.0 → 0.1.9-rc.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 (265) hide show
  1. package/README.md +59 -2
  2. package/dist/api-address.d.ts +104 -12
  3. package/dist/api-address.d.ts.map +1 -1
  4. package/dist/api-address.js +169 -30
  5. package/dist/api-address.js.map +1 -1
  6. package/dist/cli.js +12 -2
  7. package/dist/cli.js.map +1 -1
  8. package/dist/commands/approvals.d.ts.map +1 -1
  9. package/dist/commands/approvals.js +15 -3
  10. package/dist/commands/approvals.js.map +1 -1
  11. package/dist/commands/auth.d.ts.map +1 -1
  12. package/dist/commands/auth.js +27 -0
  13. package/dist/commands/auth.js.map +1 -1
  14. package/dist/commands/build.d.ts +18 -0
  15. package/dist/commands/build.d.ts.map +1 -1
  16. package/dist/commands/build.js +118 -13
  17. package/dist/commands/build.js.map +1 -1
  18. package/dist/commands/conversations.d.ts.map +1 -1
  19. package/dist/commands/conversations.js +13 -3
  20. package/dist/commands/conversations.js.map +1 -1
  21. package/dist/commands/deploy.d.ts.map +1 -1
  22. package/dist/commands/deploy.js +235 -53
  23. package/dist/commands/deploy.js.map +1 -1
  24. package/dist/commands/deployments.d.ts +3 -0
  25. package/dist/commands/deployments.d.ts.map +1 -0
  26. package/dist/commands/deployments.js +196 -0
  27. package/dist/commands/deployments.js.map +1 -0
  28. package/dist/commands/doctor.d.ts +1 -5
  29. package/dist/commands/doctor.d.ts.map +1 -1
  30. package/dist/commands/doctor.js +42 -22
  31. package/dist/commands/doctor.js.map +1 -1
  32. package/dist/commands/egress.d.ts +9 -0
  33. package/dist/commands/egress.d.ts.map +1 -0
  34. package/dist/commands/egress.js +40 -0
  35. package/dist/commands/egress.js.map +1 -0
  36. package/dist/commands/env-scoped.d.ts +5 -0
  37. package/dist/commands/env-scoped.d.ts.map +1 -1
  38. package/dist/commands/env-scoped.js +64 -19
  39. package/dist/commands/env-scoped.js.map +1 -1
  40. package/dist/commands/env.d.ts.map +1 -1
  41. package/dist/commands/env.js +91 -30
  42. package/dist/commands/env.js.map +1 -1
  43. package/dist/commands/environments.d.ts +6 -0
  44. package/dist/commands/environments.d.ts.map +1 -0
  45. package/dist/commands/environments.js +166 -0
  46. package/dist/commands/environments.js.map +1 -0
  47. package/dist/commands/exports.d.ts.map +1 -1
  48. package/dist/commands/exports.js +118 -3
  49. package/dist/commands/exports.js.map +1 -1
  50. package/dist/commands/feedback.d.ts +1 -2
  51. package/dist/commands/feedback.d.ts.map +1 -1
  52. package/dist/commands/feedback.js +55 -24
  53. package/dist/commands/feedback.js.map +1 -1
  54. package/dist/commands/helpers.d.ts +6 -0
  55. package/dist/commands/helpers.d.ts.map +1 -1
  56. package/dist/commands/helpers.js +8 -0
  57. package/dist/commands/helpers.js.map +1 -1
  58. package/dist/commands/index.d.ts.map +1 -1
  59. package/dist/commands/index.js +12 -0
  60. package/dist/commands/index.js.map +1 -1
  61. package/dist/commands/license.d.ts +3 -0
  62. package/dist/commands/license.d.ts.map +1 -1
  63. package/dist/commands/license.js +1 -1
  64. package/dist/commands/license.js.map +1 -1
  65. package/dist/commands/people.d.ts.map +1 -1
  66. package/dist/commands/people.js +105 -10
  67. package/dist/commands/people.js.map +1 -1
  68. package/dist/commands/projects.d.ts.map +1 -1
  69. package/dist/commands/projects.js +133 -11
  70. package/dist/commands/projects.js.map +1 -1
  71. package/dist/commands/promote.d.ts +38 -0
  72. package/dist/commands/promote.d.ts.map +1 -0
  73. package/dist/commands/promote.js +371 -0
  74. package/dist/commands/promote.js.map +1 -0
  75. package/dist/commands/proposals.d.ts.map +1 -1
  76. package/dist/commands/proposals.js +2 -1
  77. package/dist/commands/proposals.js.map +1 -1
  78. package/dist/commands/providers.d.ts.map +1 -1
  79. package/dist/commands/providers.js +29 -5
  80. package/dist/commands/providers.js.map +1 -1
  81. package/dist/commands/schedules.d.ts.map +1 -1
  82. package/dist/commands/schedules.js +30 -2
  83. package/dist/commands/schedules.js.map +1 -1
  84. package/dist/commands/secrets.d.ts.map +1 -1
  85. package/dist/commands/secrets.js +65 -40
  86. package/dist/commands/secrets.js.map +1 -1
  87. package/dist/commands/sign-in.d.ts +3 -0
  88. package/dist/commands/sign-in.d.ts.map +1 -0
  89. package/dist/commands/sign-in.js +61 -0
  90. package/dist/commands/sign-in.js.map +1 -0
  91. package/dist/commands/sites.d.ts +3 -0
  92. package/dist/commands/sites.d.ts.map +1 -0
  93. package/dist/commands/sites.js +244 -0
  94. package/dist/commands/sites.js.map +1 -0
  95. package/dist/commands/tools.d.ts.map +1 -1
  96. package/dist/commands/tools.js +2 -2
  97. package/dist/commands/tools.js.map +1 -1
  98. package/dist/commands/types.d.ts +11 -0
  99. package/dist/commands/types.d.ts.map +1 -1
  100. package/dist/commands/webhooks.d.ts.map +1 -1
  101. package/dist/commands/webhooks.js +38 -2
  102. package/dist/commands/webhooks.js.map +1 -1
  103. package/dist/config.d.ts +13 -0
  104. package/dist/config.d.ts.map +1 -1
  105. package/dist/config.js +20 -7
  106. package/dist/config.js.map +1 -1
  107. package/dist/context.d.ts +38 -2
  108. package/dist/context.d.ts.map +1 -1
  109. package/dist/context.js +12 -1
  110. package/dist/context.js.map +1 -1
  111. package/dist/deploy/defaults.d.ts.map +1 -1
  112. package/dist/deploy/defaults.js +7 -1
  113. package/dist/deploy/defaults.js.map +1 -1
  114. package/dist/deploy/pack-service-text.d.ts +29 -0
  115. package/dist/deploy/pack-service-text.d.ts.map +1 -0
  116. package/dist/deploy/pack-service-text.js +72 -0
  117. package/dist/deploy/pack-service-text.js.map +1 -0
  118. package/dist/deploy/post.d.ts +17 -4
  119. package/dist/deploy/post.d.ts.map +1 -1
  120. package/dist/deploy/post.js +177 -60
  121. package/dist/deploy/post.js.map +1 -1
  122. package/dist/deploy/promotion-path.d.ts +44 -0
  123. package/dist/deploy/promotion-path.d.ts.map +1 -0
  124. package/dist/deploy/promotion-path.js +86 -0
  125. package/dist/deploy/promotion-path.js.map +1 -0
  126. package/dist/deploy/response.d.ts +3 -0
  127. package/dist/deploy/response.d.ts.map +1 -1
  128. package/dist/deploy/response.js +66 -12
  129. package/dist/deploy/response.js.map +1 -1
  130. package/dist/deploy/runners.d.ts +99 -5
  131. package/dist/deploy/runners.d.ts.map +1 -1
  132. package/dist/dev/java-builder.d.ts.map +1 -1
  133. package/dist/dev/java-builder.js +19 -3
  134. package/dist/dev/java-builder.js.map +1 -1
  135. package/dist/dev/python-builder.d.ts.map +1 -1
  136. package/dist/dev/python-builder.js +18 -3
  137. package/dist/dev/python-builder.js.map +1 -1
  138. package/dist/dev/runtime-container.d.ts +28 -3
  139. package/dist/dev/runtime-container.d.ts.map +1 -1
  140. package/dist/dev/runtime-container.js +103 -36
  141. package/dist/dev/runtime-container.js.map +1 -1
  142. package/dist/dev/runtime-image.d.ts +1 -1
  143. package/dist/dev/runtime-image.js +1 -1
  144. package/dist/dev/scala-builder.d.ts.map +1 -1
  145. package/dist/dev/scala-builder.js +55 -21
  146. package/dist/dev/scala-builder.js.map +1 -1
  147. package/dist/dev/shared-watch.d.ts +30 -0
  148. package/dist/dev/shared-watch.d.ts.map +1 -1
  149. package/dist/dev/shared-watch.js +46 -1
  150. package/dist/dev/shared-watch.js.map +1 -1
  151. package/dist/env/pack-env-hint.d.ts +52 -0
  152. package/dist/env/pack-env-hint.d.ts.map +1 -0
  153. package/dist/env/pack-env-hint.js +57 -0
  154. package/dist/env/pack-env-hint.js.map +1 -0
  155. package/dist/env/pack-env-plan.d.ts +2 -0
  156. package/dist/env/pack-env-plan.d.ts.map +1 -1
  157. package/dist/env/pack-env-plan.js +2 -1
  158. package/dist/env/pack-env-plan.js.map +1 -1
  159. package/dist/env-projects.d.ts +23 -0
  160. package/dist/env-projects.d.ts.map +1 -0
  161. package/dist/env-projects.js +28 -0
  162. package/dist/env-projects.js.map +1 -0
  163. package/dist/exports/create.d.ts +71 -0
  164. package/dist/exports/create.d.ts.map +1 -0
  165. package/dist/exports/create.js +576 -0
  166. package/dist/exports/create.js.map +1 -0
  167. package/dist/exports/plan.d.ts +69 -0
  168. package/dist/exports/plan.d.ts.map +1 -0
  169. package/dist/exports/plan.js +496 -0
  170. package/dist/exports/plan.js.map +1 -0
  171. package/dist/exports/reader.d.ts +48 -0
  172. package/dist/exports/reader.d.ts.map +1 -0
  173. package/dist/exports/reader.js +181 -0
  174. package/dist/exports/reader.js.map +1 -0
  175. package/dist/feedback/entries.d.ts +53 -0
  176. package/dist/feedback/entries.d.ts.map +1 -0
  177. package/dist/feedback/entries.js +173 -0
  178. package/dist/feedback/entries.js.map +1 -0
  179. package/dist/feedback/intake.d.ts +37 -0
  180. package/dist/feedback/intake.d.ts.map +1 -0
  181. package/dist/feedback/intake.js +101 -0
  182. package/dist/feedback/intake.js.map +1 -0
  183. package/dist/feedback/protocol.d.ts +111 -0
  184. package/dist/feedback/protocol.d.ts.map +1 -0
  185. package/dist/feedback/protocol.js +143 -0
  186. package/dist/feedback/protocol.js.map +1 -0
  187. package/dist/feedback/redact.d.ts +34 -0
  188. package/dist/feedback/redact.d.ts.map +1 -0
  189. package/dist/feedback/redact.js +258 -0
  190. package/dist/feedback/redact.js.map +1 -0
  191. package/dist/feedback/report.d.ts +51 -0
  192. package/dist/feedback/report.d.ts.map +1 -0
  193. package/dist/feedback/report.js +122 -0
  194. package/dist/feedback/report.js.map +1 -0
  195. package/dist/feedback/run.d.ts +21 -0
  196. package/dist/feedback/run.d.ts.map +1 -0
  197. package/dist/feedback/run.js +578 -0
  198. package/dist/feedback/run.js.map +1 -0
  199. package/dist/feedback/store.d.ts +24 -0
  200. package/dist/feedback/store.d.ts.map +1 -0
  201. package/dist/feedback/store.js +117 -0
  202. package/dist/feedback/store.js.map +1 -0
  203. package/dist/main.d.ts +6 -0
  204. package/dist/main.d.ts.map +1 -1
  205. package/dist/main.js +86 -5
  206. package/dist/main.js.map +1 -1
  207. package/dist/node-version.d.ts +13 -0
  208. package/dist/node-version.d.ts.map +1 -0
  209. package/dist/node-version.js +37 -0
  210. package/dist/node-version.js.map +1 -0
  211. package/dist/parse.d.ts +8 -2
  212. package/dist/parse.d.ts.map +1 -1
  213. package/dist/parse.js +7 -2
  214. package/dist/parse.js.map +1 -1
  215. package/dist/project-ref.d.ts +44 -0
  216. package/dist/project-ref.d.ts.map +1 -0
  217. package/dist/project-ref.js +69 -0
  218. package/dist/project-ref.js.map +1 -0
  219. package/dist/sdk-skills/kindgi-authoring-agents/SKILL.md +15 -2
  220. package/dist/sdk-skills/kindgi-authoring-flows/SKILL.md +1 -1
  221. package/dist/sdk-skills/kindgi-authoring-guardrails/SKILL.md +44 -7
  222. package/dist/sdk-skills/kindgi-authoring-mcp-servers/SKILL.md +11 -2
  223. package/dist/sdk-skills/kindgi-authoring-providers/SKILL.md +60 -13
  224. package/dist/sdk-skills/kindgi-authoring-tools/SKILL.md +19 -2
  225. package/dist/sdk-skills/kindgi-framework-feedback/SKILL.md +82 -3
  226. package/dist/sdk-skills/kindgi-getting-started/SKILL.md +1 -1
  227. package/dist/sdk-skills/kindgi-java-authoring-agents/SKILL.md +17 -3
  228. package/dist/sdk-skills/kindgi-java-authoring-flows/SKILL.md +1 -1
  229. package/dist/sdk-skills/kindgi-java-authoring-guardrails/SKILL.md +43 -6
  230. package/dist/sdk-skills/kindgi-java-authoring-tools/SKILL.md +19 -2
  231. package/dist/sdk-skills/kindgi-java-getting-started/SKILL.md +1 -1
  232. package/dist/sdk-skills/kindgi-python-authoring-agents/SKILL.md +17 -3
  233. package/dist/sdk-skills/kindgi-python-authoring-flows/SKILL.md +1 -1
  234. package/dist/sdk-skills/kindgi-python-authoring-guardrails/SKILL.md +49 -13
  235. package/dist/sdk-skills/kindgi-python-authoring-tools/SKILL.md +27 -3
  236. package/dist/sdk-skills/kindgi-python-getting-started/SKILL.md +1 -1
  237. package/dist/sdk-skills/kindgi-scala-authoring-agents/SKILL.md +17 -3
  238. package/dist/sdk-skills/kindgi-scala-authoring-flows/SKILL.md +1 -1
  239. package/dist/sdk-skills/kindgi-scala-authoring-guardrails/SKILL.md +43 -6
  240. package/dist/sdk-skills/kindgi-scala-authoring-tools/SKILL.md +19 -2
  241. package/dist/sdk-skills/kindgi-scala-getting-started/SKILL.md +1 -1
  242. package/dist/sdk-skills/kindgi-using/SKILL.md +1 -1
  243. package/dist/slug.d.ts +18 -0
  244. package/dist/slug.d.ts.map +1 -0
  245. package/dist/slug.js +29 -0
  246. package/dist/slug.js.map +1 -0
  247. package/dist/templates/drupal/AGENTS.md +2 -0
  248. package/dist/templates/java/AGENTS.md +2 -0
  249. package/dist/templates/minimal/AGENTS.md +2 -0
  250. package/dist/templates/python/AGENTS.md +2 -0
  251. package/dist/templates/sample/AGENTS.md +2 -0
  252. package/dist/templates/scala/AGENTS.md +2 -0
  253. package/dist/templates/woocommerce/AGENTS.md +11 -4
  254. package/dist/templates/woocommerce/README.md.tmpl +47 -4
  255. package/dist/templates/woocommerce/agents/store-agent/index.test.ts.tmpl +13 -0
  256. package/dist/templates/woocommerce/agents/store-agent/index.ts.tmpl +11 -1
  257. package/dist/templates/woocommerce/tools/publish-product/index.ts.tmpl +7 -1
  258. package/dist/templates/woocommerce/tools/refund/index.ts.tmpl +7 -2
  259. package/dist/templates/woocommerce/tools/refund-large/index.ts.tmpl +8 -2
  260. package/dist/templates/woocommerce/tools/set-price/index.ts.tmpl +6 -0
  261. package/dist/value-target.d.ts +139 -0
  262. package/dist/value-target.d.ts.map +1 -0
  263. package/dist/value-target.js +231 -0
  264. package/dist/value-target.js.map +1 -0
  265. package/package.json +13 -13
@@ -0,0 +1,181 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+ /**
4
+ * The reads `kindgi exports create` makes, straight over HTTP: each answer
5
+ * is written as the route gives it, so there's nothing for a typed client
6
+ * to add (as `deploy/post.ts` sends its `POST` itself).
7
+ *
8
+ * - A `429` with a `Retry-After` is waited out and sent again, as the
9
+ * commands that publish do (`rate-limit-wait.ts`).
10
+ * - A `5xx`, or no answer at all, is sent again `SERVER_ERROR_RETRIES`
11
+ * times, after 1s, 2s, then 4s.
12
+ * - Anything else that isn't a `200` is a `ReadError`, with the status and
13
+ * the code the server named.
14
+ */
15
+ import { Readable } from 'node:stream';
16
+ import { sendWaitingOutRateLimits } from '../rate-limit-wait.js';
17
+ /** How many records a page asks for. */
18
+ export const PAGE_SIZE = 100;
19
+ /** How many times a `5xx`, or no answer, is sent again. */
20
+ export const SERVER_ERROR_RETRIES = 3;
21
+ /** A read the server refused, or that never got an answer (`status` absent). */
22
+ export class ReadError extends Error {
23
+ status;
24
+ code;
25
+ constructor(message, status, code) {
26
+ super(message);
27
+ this.status = status;
28
+ this.code = code;
29
+ this.name = 'ReadError';
30
+ }
31
+ }
32
+ /** A route with its path parameters filled in, each one encoded. */
33
+ export function fillRoute(route, values) {
34
+ return route.replace(/\{([^}]+)\}/g, (_m, name) => {
35
+ const value = values[name];
36
+ if (value === undefined)
37
+ throw new Error(`${route}: no value for {${name}}`);
38
+ return encodeURIComponent(value);
39
+ });
40
+ }
41
+ export function createReader(options) {
42
+ const base = options.apiUrl.replace(/\/+$/, '');
43
+ const pause = options.pause ?? pauseFor;
44
+ const url = (path, query) => {
45
+ const search = new URLSearchParams(query ?? {}).toString();
46
+ return `${base}${path}${search === '' ? '' : `?${search}`}`;
47
+ };
48
+ /** Send, waiting out rate limits and sending a `5xx` again; the answer, or what went wrong. */
49
+ async function send(method, target, extra = {}) {
50
+ const once = () => fetchOnce(options, method, target, extra);
51
+ for (let retry = 0;; retry++) {
52
+ const answer = await sendWaitingOutRateLimits(options, once, rateLimitRefusal);
53
+ if (!(answer instanceof Error) && answer.status < 500)
54
+ return answer;
55
+ if (retry >= SERVER_ERROR_RETRIES || options.stopSignal?.aborted === true) {
56
+ throw await unanswered(method, target, answer);
57
+ }
58
+ if (!(answer instanceof Error))
59
+ await answer.body?.cancel().catch(() => undefined);
60
+ await pause(1000 * 2 ** retry, options.stopSignal);
61
+ }
62
+ }
63
+ async function json(method, target, init) {
64
+ const res = await send(method, target, init);
65
+ if (res.status !== 200)
66
+ throw await refusal(method, target, res);
67
+ try {
68
+ return await res.json();
69
+ }
70
+ catch (err) {
71
+ throw new ReadError(`${method} ${target} answered 200 with a body that isn't JSON: ${err.message}`, 200);
72
+ }
73
+ }
74
+ return {
75
+ one: (path, query) => json('GET', url(path, query)),
76
+ async whole(path, query) {
77
+ const target = url(path, query);
78
+ const page = (await json('GET', target));
79
+ if (!Array.isArray(page.data)) {
80
+ throw new ReadError(`GET ${target} answered no \`data\` list`, 200);
81
+ }
82
+ if (page.hasMore === true) {
83
+ throw new ReadError(`GET ${target} says it has more, and the route takes no cursor to read it by`, 200);
84
+ }
85
+ return page.data;
86
+ },
87
+ async *pages(path, query) {
88
+ let cursor;
89
+ for (;;) {
90
+ const target = url(path, {
91
+ ...query,
92
+ limit: String(PAGE_SIZE),
93
+ ...(cursor !== undefined && { cursor }),
94
+ });
95
+ const page = (await json('GET', target));
96
+ if (!Array.isArray(page.data)) {
97
+ throw new ReadError(`GET ${target} answered no \`data\` list`, 200);
98
+ }
99
+ yield* page.data;
100
+ if (page.hasMore !== true)
101
+ return;
102
+ if (typeof page.nextCursor !== 'string' || page.nextCursor === cursor) {
103
+ throw new ReadError(`GET ${target} says it has more, with no new cursor to read it by`, 200);
104
+ }
105
+ cursor = page.nextCursor;
106
+ }
107
+ },
108
+ async bytes(path) {
109
+ const target = url(path);
110
+ const res = await send('GET', target);
111
+ if (res.status !== 200)
112
+ throw await refusal('GET', target, res);
113
+ if (res.body === null)
114
+ return Readable.from([]);
115
+ return Readable.fromWeb(res.body);
116
+ },
117
+ post: (path, body, idempotencyKey) => json('POST', url(path), {
118
+ body: JSON.stringify(body),
119
+ headers: { 'Content-Type': 'application/json', 'Idempotency-Key': idempotencyKey },
120
+ }),
121
+ };
122
+ }
123
+ /** One send: the answer, or the error that stopped it (no answer, or Ctrl+C). */
124
+ async function fetchOnce(options, method, target, extra) {
125
+ try {
126
+ return await options.fetch(target, {
127
+ method,
128
+ headers: { Authorization: `Bearer ${options.token}`, ...extra.headers },
129
+ ...(extra.body !== undefined && { body: extra.body }),
130
+ ...(options.stopSignal !== undefined && { signal: options.stopSignal }),
131
+ });
132
+ }
133
+ catch (err) {
134
+ return err instanceof Error ? err : new Error(String(err));
135
+ }
136
+ }
137
+ /** The last `5xx`, or no answer, as a `ReadError`. */
138
+ async function unanswered(method, target, answer) {
139
+ if (answer instanceof Error)
140
+ return new ReadError(`${method} ${target} got no answer: ${answer.message}`);
141
+ return refusal(method, target, answer);
142
+ }
143
+ /** A `429` the server says to wait out. */
144
+ function rateLimitRefusal(answer) {
145
+ if (answer instanceof Error || answer.status !== 429)
146
+ return undefined;
147
+ const after = answer.headers.get('retry-after')?.trim();
148
+ // The server sends whole seconds.
149
+ if (after === undefined || !/^\d+$/.test(after))
150
+ return undefined;
151
+ return { retryAfterSeconds: Number(after) };
152
+ }
153
+ /** A refusal as a `ReadError`: the status, and the server's code and message when it sent them. */
154
+ async function refusal(method, target, res) {
155
+ let code;
156
+ let message;
157
+ try {
158
+ const body = (await res.json());
159
+ if (typeof body?.error?.code === 'string')
160
+ code = body.error.code;
161
+ if (typeof body?.error?.message === 'string')
162
+ message = body.error.message;
163
+ }
164
+ catch {
165
+ // Not JSON: the status says it.
166
+ }
167
+ const said = code === undefined ? '' : ` ${code}${message === undefined ? '' : `: ${message}`}`;
168
+ return new ReadError(`${method} ${target} answered ${res.status}${said}`, res.status, code);
169
+ }
170
+ function pauseFor(ms, signal) {
171
+ return new Promise((resolve) => {
172
+ const done = () => {
173
+ clearTimeout(timer);
174
+ signal?.removeEventListener('abort', done);
175
+ resolve();
176
+ };
177
+ const timer = setTimeout(done, ms);
178
+ signal?.addEventListener('abort', done, { once: true });
179
+ });
180
+ }
181
+ //# sourceMappingURL=reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reader.js","sourceRoot":"","sources":["../../src/exports/reader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * `FEEDBACK.md`'s entries, read back: what `kindgi feedback write` appended,
3
+ * plus what a person or an agent edited since. An entry is a `## <title>`
4
+ * heading followed by its metadata list (`- **id:** …`, `- **hash:** …`,
5
+ * …); a `## ` heading without one is part of the entry above it, so a body
6
+ * can have headings of its own. Fenced code blocks are skipped.
7
+ *
8
+ * `submit`, `status` and `withdraw` edit the file through here, one line at
9
+ * a time, and leave every other byte as it was:
10
+ * - `- **submitted:** <date> · **report:** <reportId>` after the metadata,
11
+ * one per submission;
12
+ * - ` · **withdrawn:** <date>` at the end of that line;
13
+ * - `> **Fixed:** <note>` under the heading, as a person marks it by hand.
14
+ */
15
+ export type FeedbackKind = 'bug' | 'friction' | 'question' | 'design';
16
+ export type FeedbackSeverity = 'blocker' | 'high' | 'medium' | 'low';
17
+ export declare const KINDS: readonly FeedbackKind[];
18
+ export declare const SEVERITIES: readonly FeedbackSeverity[];
19
+ export declare const AUTHORS: readonly ["claude-code", "human", "mixed"];
20
+ export declare const FEEDBACK_FILENAME = "FEEDBACK.md";
21
+ /** One submission of an entry, from its `submitted` line. */
22
+ export interface Submission {
23
+ readonly reportId: string;
24
+ readonly submittedAt: string;
25
+ readonly withdrawnAt?: string;
26
+ }
27
+ export interface FeedbackEntry {
28
+ readonly title: string;
29
+ /** The metadata as written (`id`, `hash`, `kind`, …), each value trimmed. */
30
+ readonly fields: Readonly<Record<string, string>>;
31
+ readonly body: string;
32
+ /** A `> **Fixed:** …` line under the heading. */
33
+ readonly fixed: boolean;
34
+ readonly submissions: readonly Submission[];
35
+ }
36
+ /** Every entry in the file, in order. */
37
+ export declare function parseFeedback(text: string): FeedbackEntry[];
38
+ /** Add one `submitted` line per report, after its entry's metadata. */
39
+ export declare function addSubmissions(text: string, reports: readonly {
40
+ readonly entry: string;
41
+ readonly reportId: string;
42
+ }[], submittedAt: string): {
43
+ readonly text: string;
44
+ readonly missing: readonly string[];
45
+ };
46
+ /** End each report's `submitted` line with ` · **withdrawn:** <date>`, once. */
47
+ export declare function markWithdrawn(text: string, reportIds: readonly string[], withdrawnAt: string): string;
48
+ /** Put `> **Fixed:** <note>` under the heading of each entry not marked fixed yet. */
49
+ export declare function markFixed(text: string, fixes: readonly {
50
+ readonly entry: string;
51
+ readonly note: string;
52
+ }[]): string;
53
+ //# sourceMappingURL=entries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entries.d.ts","sourceRoot":"","sources":["../../src/feedback/entries.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;AACtE,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAErE,eAAO,MAAM,KAAK,EAAE,SAAS,YAAY,EAA8C,CAAC;AACxF,eAAO,MAAM,UAAU,EAAE,SAAS,gBAAgB,EAAyC,CAAC;AAC5F,eAAO,MAAM,OAAO,4CAA6C,CAAC;AAElE,eAAO,MAAM,iBAAiB,gBAAgB,CAAC;AAE/C,6DAA6D;AAC7D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,SAAS,UAAU,EAAE,CAAC;CAC7C;AAiBD,yCAAyC;AACzC,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE,CAE3D;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,SAAS;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,EAAE,EACzE,WAAW,EAAE,MAAM,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAsBhE;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,WAAW,EAAE,MAAM,GAClB,MAAM,CAYR;AAED,sFAAsF;AACtF,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,SAAS;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,GAClE,MAAM,CAiBR"}
@@ -0,0 +1,173 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+ export const KINDS = ['bug', 'friction', 'question', 'design'];
4
+ export const SEVERITIES = ['blocker', 'high', 'medium', 'low'];
5
+ export const AUTHORS = ['claude-code', 'human', 'mixed'];
6
+ export const FEEDBACK_FILENAME = 'FEEDBACK.md';
7
+ const HEADING = /^## (?!#)(.*)$/;
8
+ const META = /^- \*\*[a-z][a-z-]*:\*\*/;
9
+ const SEGMENT = /^\*\*([a-z][a-z-]*):\*\*\s*(.*)$/;
10
+ const FIXED = /^>\s*\*\*Fixed:\*\*/;
11
+ const FENCE = /^ {0,3}(`{3,}|~{3,})/;
12
+ /** Every entry in the file, in order. */
13
+ export function parseFeedback(text) {
14
+ return locate(splitLines(text).lines).map((l) => l.entry);
15
+ }
16
+ /** Add one `submitted` line per report, after its entry's metadata. */
17
+ export function addSubmissions(text, reports, submittedAt) {
18
+ const { lines, eol, trailing } = splitLines(text);
19
+ const missing = [];
20
+ const located = locate(lines);
21
+ // Each entry's new lines, in the order the reports came in.
22
+ const byLine = new Map();
23
+ for (const report of reports) {
24
+ const at = located.find((l) => l.entry.fields.id === report.entry);
25
+ if (at === undefined) {
26
+ missing.push(report.entry);
27
+ continue;
28
+ }
29
+ const added = byLine.get(at.lastMeta) ?? [];
30
+ added.push(`- **submitted:** ${submittedAt} · **report:** ${report.reportId}`);
31
+ byLine.set(at.lastMeta, added);
32
+ }
33
+ const out = [...lines];
34
+ // Bottom up, so an insertion never moves a line still to be edited.
35
+ for (const [after, added] of [...byLine].sort((a, b) => b[0] - a[0])) {
36
+ out.splice(after + 1, 0, ...added);
37
+ }
38
+ return { text: joinLines(out, eol, trailing), missing };
39
+ }
40
+ /** End each report's `submitted` line with ` · **withdrawn:** <date>`, once. */
41
+ export function markWithdrawn(text, reportIds, withdrawnAt) {
42
+ const { lines, eol, trailing } = splitLines(text);
43
+ const out = [...lines];
44
+ for (const at of locate(lines)) {
45
+ for (const reportId of reportIds) {
46
+ const line = at.submissionLines.get(reportId);
47
+ const sub = at.entry.submissions.find((s) => s.reportId === reportId);
48
+ if (line === undefined || sub === undefined || sub.withdrawnAt !== undefined)
49
+ continue;
50
+ out[line] = `${out[line].replace(/\s+$/, '')} · **withdrawn:** ${withdrawnAt}`;
51
+ }
52
+ }
53
+ return joinLines(out, eol, trailing);
54
+ }
55
+ /** Put `> **Fixed:** <note>` under the heading of each entry not marked fixed yet. */
56
+ export function markFixed(text, fixes) {
57
+ const { lines, eol, trailing } = splitLines(text);
58
+ const out = [...lines];
59
+ const located = locate(lines);
60
+ // One note per entry (the first given), bottom up.
61
+ const notes = new Map();
62
+ for (const fix of fixes) {
63
+ const at = located.find((l) => l.entry.fields.id === fix.entry);
64
+ if (at !== undefined && !at.entry.fixed && !notes.has(at.heading)) {
65
+ notes.set(at.heading, fix.note);
66
+ }
67
+ }
68
+ for (const [heading, note] of [...notes].sort((a, b) => b[0] - a[0])) {
69
+ const spaced = (out[heading + 1] ?? '').trim() === '';
70
+ out.splice(heading + 1, 0, '', `> **Fixed:** ${note}`, ...(spaced ? [] : ['']));
71
+ }
72
+ return joinLines(out, eol, trailing);
73
+ }
74
+ function locate(lines) {
75
+ const headings = [];
76
+ let fence;
77
+ for (const [i, line] of lines.entries()) {
78
+ const f = FENCE.exec(line);
79
+ if (f !== null) {
80
+ const mark = f[1][0];
81
+ if (fence === undefined)
82
+ fence = mark;
83
+ else if (fence === mark)
84
+ fence = undefined;
85
+ continue;
86
+ }
87
+ if (fence === undefined && HEADING.test(line) && startsEntry(lines, i))
88
+ headings.push(i);
89
+ }
90
+ return headings.map((heading, n) => readEntry(lines, heading, headings[n + 1] ?? lines.length));
91
+ }
92
+ /** A heading starts an entry when its metadata list follows it (blank and `>` lines between). */
93
+ function startsEntry(lines, heading) {
94
+ for (let i = heading + 1; i < lines.length; i++) {
95
+ const line = lines[i];
96
+ if (line.trim() === '' || line.startsWith('>'))
97
+ continue;
98
+ return /^- \*\*id:\*\*/.test(line);
99
+ }
100
+ return false;
101
+ }
102
+ function readEntry(lines, heading, end) {
103
+ const title = (HEADING.exec(lines[heading])?.[1] ?? '').trim();
104
+ let i = heading + 1;
105
+ let fixed = false;
106
+ for (; i < end; i++) {
107
+ const line = lines[i];
108
+ if (line.trim() === '')
109
+ continue;
110
+ if (line.startsWith('>')) {
111
+ if (FIXED.test(line))
112
+ fixed = true;
113
+ continue;
114
+ }
115
+ break;
116
+ }
117
+ const fields = {};
118
+ const submissions = [];
119
+ const submissionLines = new Map();
120
+ let lastMeta = i;
121
+ for (; i < end && META.test(lines[i]); i++) {
122
+ lastMeta = i;
123
+ const segments = readSegments(lines[i]);
124
+ const reportId = segments.report;
125
+ if (segments.submitted !== undefined && reportId !== undefined) {
126
+ submissions.push({
127
+ reportId,
128
+ submittedAt: segments.submitted,
129
+ ...(segments.withdrawn !== undefined && { withdrawnAt: segments.withdrawn }),
130
+ });
131
+ submissionLines.set(reportId, i);
132
+ continue;
133
+ }
134
+ for (const [key, value] of Object.entries(segments))
135
+ fields[key] ??= value;
136
+ }
137
+ let last = end - 1;
138
+ while (last >= i && lines[last].trim() === '')
139
+ last--;
140
+ if (last >= i && lines[last].trim() === '---')
141
+ last--;
142
+ const body = lines
143
+ .slice(i, last + 1)
144
+ .join('\n')
145
+ .trim();
146
+ return {
147
+ entry: { title, fields, body, fixed, submissions },
148
+ heading,
149
+ lastMeta,
150
+ submissionLines,
151
+ };
152
+ }
153
+ /** `- **sdk:** 0.1.8 · **cli:** 0.1.8 · **by:** human` as `{ sdk, cli, by }`. */
154
+ function readSegments(line) {
155
+ const out = {};
156
+ for (const part of line.slice(2).split(' · ')) {
157
+ const m = SEGMENT.exec(part.trim());
158
+ if (m !== null)
159
+ out[m[1]] ??= m[2].trim();
160
+ }
161
+ return out;
162
+ }
163
+ /** The lines without their `\r`, and how to put them back. */
164
+ function splitLines(text) {
165
+ const eol = text.includes('\r\n') ? '\r\n' : '\n';
166
+ const trailing = text.endsWith('\n');
167
+ const body = trailing ? text.replace(/\r?\n$/, '') : text;
168
+ return { lines: body === '' ? [] : body.split(/\r?\n/), eol, trailing };
169
+ }
170
+ function joinLines(lines, eol, trailing) {
171
+ return lines.join(eol) + (trailing ? eol : '');
172
+ }
173
+ //# sourceMappingURL=entries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entries.js","sourceRoot":"","sources":["../../src/feedback/entries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import { type EntryPayload, type ReportStatus, type SubmittedReport, type WithdrawAnswer } from './protocol.js';
2
+ export interface IntakeDeps {
3
+ readonly fetch: typeof fetch;
4
+ readonly timeoutMs?: number;
5
+ }
6
+ export type Asked<T> = {
7
+ readonly kind: 'ok';
8
+ readonly value: T;
9
+ }
10
+ /** The intake said no, in its own words (`invalid-request`, `rate-limited`, …): it did nothing. */
11
+ | {
12
+ readonly kind: 'refused';
13
+ readonly status: number;
14
+ readonly error: string;
15
+ readonly message: string;
16
+ /** With a 429: how long to wait, in seconds. */
17
+ readonly retryAfter?: number;
18
+ }
19
+ /**
20
+ * No answer this CLI can use. `maybeReceived` is false only when the
21
+ * request surely never reached the intake: no connection was made, or
22
+ * something in front of it answered 4xx. A timeout, a dropped connection,
23
+ * a 5xx, or a success this CLI can't read may follow a request that did
24
+ * its work.
25
+ */
26
+ | {
27
+ readonly kind: 'failed';
28
+ readonly message: string;
29
+ readonly maybeReceived: boolean;
30
+ };
31
+ export declare function sendReports(origin: string, bytes: string, sent: readonly EntryPayload[], deps: IntakeDeps): Promise<Asked<readonly SubmittedReport[]>>;
32
+ export declare function askStatus(origin: string, reportIds: readonly string[], deps: IntakeDeps): Promise<Asked<readonly ReportStatus[]>>;
33
+ export declare function withdrawReports(origin: string, reports: readonly {
34
+ readonly reportId: string;
35
+ readonly withdrawKey: string;
36
+ }[], deps: IntakeDeps): Promise<Asked<WithdrawAnswer>>;
37
+ //# sourceMappingURL=intake.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intake.d.ts","sourceRoot":"","sources":["../../src/feedback/intake.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,KAAK,YAAY,EAIjB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,cAAc,EAIpB,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,KAAK,CAAC,CAAC,IACf;IAAE,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;CAAE;AAC5C,mGAAmG;GACjG;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,gDAAgD;IAChD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AACH;;;;;;GAMG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAA;CAAE,CAAC;AAE3F,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,SAAS,YAAY,EAAE,EAC7B,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,KAAK,CAAC,SAAS,eAAe,EAAE,CAAC,CAAC,CAS5C;AAED,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,KAAK,CAAC,SAAS,YAAY,EAAE,CAAC,CAAC,CASzC;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,SAAS;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EAAE,EAC/E,IAAI,EAAE,UAAU,GACf,OAAO,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAahC"}
@@ -0,0 +1,101 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+ /**
4
+ * One request to the feedback intake, and what came of it. A network
5
+ * failure, or an answer that isn't the intake's, is said, never thrown,
6
+ * so the command can say what was and wasn't sent, and what may have
7
+ * arrived anyway.
8
+ */
9
+ import { networkReason } from '../license/renew.js';
10
+ import { FEEDBACK_REPORTS_PATH, FEEDBACK_STATUS_PATH, FEEDBACK_WITHDRAW_PATH, readStatusAnswer, readSubmitAnswer, readWithdrawAnswer, } from './protocol.js';
11
+ export function sendReports(origin, bytes, sent, deps) {
12
+ return ask(origin, FEEDBACK_REPORTS_PATH, bytes, 201, (body) => readSubmitAnswer(body, sent), deps);
13
+ }
14
+ export function askStatus(origin, reportIds, deps) {
15
+ return ask(origin, FEEDBACK_STATUS_PATH, JSON.stringify({ reportIds }), 200, (body) => readStatusAnswer(body, reportIds), deps);
16
+ }
17
+ export function withdrawReports(origin, reports, deps) {
18
+ return ask(origin, FEEDBACK_WITHDRAW_PATH, JSON.stringify({ reports }), 200, (body) => readWithdrawAnswer(body, reports.map((r) => r.reportId)), deps);
19
+ }
20
+ async function ask(origin, path, bytes, expected, read, deps) {
21
+ let res;
22
+ let body;
23
+ try {
24
+ res = await deps.fetch(`${origin}${path}`, {
25
+ method: 'POST',
26
+ headers: { 'content-type': 'application/json' },
27
+ body: bytes,
28
+ signal: AbortSignal.timeout(deps.timeoutMs ?? 30_000),
29
+ });
30
+ body = await res.json().catch(() => undefined);
31
+ }
32
+ catch (cause) {
33
+ return {
34
+ kind: 'failed',
35
+ message: `${origin} couldn't be reached (${networkReason(cause)})`,
36
+ maybeReceived: !neverConnected(cause),
37
+ };
38
+ }
39
+ if (res.status === expected) {
40
+ const value = read(body);
41
+ return value === undefined
42
+ ? {
43
+ kind: 'failed',
44
+ message: `${origin}'s answer wasn't one this CLI can read`,
45
+ maybeReceived: true,
46
+ }
47
+ : { kind: 'ok', value };
48
+ }
49
+ const said = typeof body === 'object' && body !== null ? body : {};
50
+ if (res.status >= 500 || res.ok || typeof said.error !== 'string') {
51
+ // A 5xx, a proxy's page, or a success that isn't this route's: the next try may work.
52
+ return {
53
+ kind: 'failed',
54
+ message: `${origin} answered ${res.status}${res.status >= 500 && typeof said.error === 'string' ? ` (${said.error})` : ''}`,
55
+ maybeReceived: res.status < 400 || res.status >= 500,
56
+ };
57
+ }
58
+ const retryAfter = res.status === 429 ? retryAfterSeconds(res.headers.get('retry-after')) : undefined;
59
+ return {
60
+ kind: 'refused',
61
+ status: res.status,
62
+ error: said.error,
63
+ message: typeof said.message === 'string' ? said.message : `${origin} answered ${res.status}`,
64
+ ...(retryAfter !== undefined && { retryAfter }),
65
+ };
66
+ }
67
+ /**
68
+ * The error codes that mean no connection was made, so no byte of the
69
+ * request left: the name didn't resolve, nothing listened, no route, or the
70
+ * TLS handshake failed on the certificate. Anything else (a timeout, a
71
+ * reset) may have come after the request went.
72
+ */
73
+ const NOT_CONNECTED = new Set([
74
+ 'ENOTFOUND',
75
+ 'EAI_AGAIN',
76
+ 'ECONNREFUSED',
77
+ 'EHOSTUNREACH',
78
+ 'ENETUNREACH',
79
+ 'UND_ERR_CONNECT_TIMEOUT',
80
+ ]);
81
+ const CERTIFICATE_CODE = /^(?:CERT_|ERR_TLS_CERT_|UNABLE_TO_|DEPTH_ZERO_|SELF_SIGNED_)/;
82
+ function neverConnected(err) {
83
+ let at = err;
84
+ for (let depth = 0; at instanceof Error && depth < 4; depth++) {
85
+ const code = at.code;
86
+ if (typeof code === 'string' && (NOT_CONNECTED.has(code) || CERTIFICATE_CODE.test(code)))
87
+ return true;
88
+ at = at.cause;
89
+ }
90
+ return false;
91
+ }
92
+ /** `Retry-After` as whole seconds: a number of seconds, or an HTTP date. */
93
+ function retryAfterSeconds(header) {
94
+ if (header === null)
95
+ return undefined;
96
+ if (/^\d+$/.test(header.trim()))
97
+ return Number.parseInt(header.trim(), 10);
98
+ const at = Date.parse(header);
99
+ return Number.isNaN(at) ? undefined : Math.max(0, Math.ceil((at - Date.now()) / 1000));
100
+ }
101
+ //# sourceMappingURL=intake.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"intake.js","sourceRoot":"","sources":["../../src/feedback/intake.ts"],"names":[],"mappings":"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"}