callman-mcp 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 (244) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE +21 -0
  3. package/README.md +203 -0
  4. package/dist/client/api.d.ts +109 -0
  5. package/dist/client/api.d.ts.map +1 -0
  6. package/dist/client/api.js +180 -0
  7. package/dist/client/api.js.map +1 -0
  8. package/dist/client/errors.d.ts +27 -0
  9. package/dist/client/errors.d.ts.map +1 -0
  10. package/dist/client/errors.js +98 -0
  11. package/dist/client/errors.js.map +1 -0
  12. package/dist/client/http.d.ts +34 -0
  13. package/dist/client/http.d.ts.map +1 -0
  14. package/dist/client/http.js +121 -0
  15. package/dist/client/http.js.map +1 -0
  16. package/dist/client/types.d.ts +327 -0
  17. package/dist/client/types.d.ts.map +1 -0
  18. package/dist/client/types.js +5 -0
  19. package/dist/client/types.js.map +1 -0
  20. package/dist/config.d.ts +20 -0
  21. package/dist/config.d.ts.map +1 -0
  22. package/dist/config.js +69 -0
  23. package/dist/config.js.map +1 -0
  24. package/dist/context.d.ts +35 -0
  25. package/dist/context.d.ts.map +1 -0
  26. package/dist/context.js +91 -0
  27. package/dist/context.js.map +1 -0
  28. package/dist/domain/diagram/build.d.ts +260 -0
  29. package/dist/domain/diagram/build.d.ts.map +1 -0
  30. package/dist/domain/diagram/build.js +160 -0
  31. package/dist/domain/diagram/build.js.map +1 -0
  32. package/dist/domain/diagram/layout.d.ts +17 -0
  33. package/dist/domain/diagram/layout.d.ts.map +1 -0
  34. package/dist/domain/diagram/layout.js +61 -0
  35. package/dist/domain/diagram/layout.js.map +1 -0
  36. package/dist/domain/diagram/patch.d.ts +330 -0
  37. package/dist/domain/diagram/patch.d.ts.map +1 -0
  38. package/dist/domain/diagram/patch.js +176 -0
  39. package/dist/domain/diagram/patch.js.map +1 -0
  40. package/dist/domain/diagram/summarize.d.ts +41 -0
  41. package/dist/domain/diagram/summarize.d.ts.map +1 -0
  42. package/dist/domain/diagram/summarize.js +47 -0
  43. package/dist/domain/diagram/summarize.js.map +1 -0
  44. package/dist/domain/importDetect.d.ts +6 -0
  45. package/dist/domain/importDetect.d.ts.map +1 -0
  46. package/dist/domain/importDetect.js +32 -0
  47. package/dist/domain/importDetect.js.map +1 -0
  48. package/dist/domain/requestShape.d.ts +796 -0
  49. package/dist/domain/requestShape.d.ts.map +1 -0
  50. package/dist/domain/requestShape.js +250 -0
  51. package/dist/domain/requestShape.js.map +1 -0
  52. package/dist/domain/resolve.d.ts +30 -0
  53. package/dist/domain/resolve.d.ts.map +1 -0
  54. package/dist/domain/resolve.js +56 -0
  55. package/dist/domain/resolve.js.map +1 -0
  56. package/dist/domain/scenario/build.d.ts +270 -0
  57. package/dist/domain/scenario/build.d.ts.map +1 -0
  58. package/dist/domain/scenario/build.js +135 -0
  59. package/dist/domain/scenario/build.js.map +1 -0
  60. package/dist/domain/scenario/summarize.d.ts +206 -0
  61. package/dist/domain/scenario/summarize.d.ts.map +1 -0
  62. package/dist/domain/scenario/summarize.js +138 -0
  63. package/dist/domain/scenario/summarize.js.map +1 -0
  64. package/dist/index.d.ts +3 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +142 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/lib/cache.d.ts +12 -0
  69. package/dist/lib/cache.d.ts.map +1 -0
  70. package/dist/lib/cache.js +40 -0
  71. package/dist/lib/cache.js.map +1 -0
  72. package/dist/lib/ids.d.ts +5 -0
  73. package/dist/lib/ids.d.ts.map +1 -0
  74. package/dist/lib/ids.js +19 -0
  75. package/dist/lib/ids.js.map +1 -0
  76. package/dist/lib/log.d.ts +10 -0
  77. package/dist/lib/log.d.ts.map +1 -0
  78. package/dist/lib/log.js +25 -0
  79. package/dist/lib/log.js.map +1 -0
  80. package/dist/lib/mask.d.ts +24 -0
  81. package/dist/lib/mask.d.ts.map +1 -0
  82. package/dist/lib/mask.js +63 -0
  83. package/dist/lib/mask.js.map +1 -0
  84. package/dist/lib/pagination.d.ts +21 -0
  85. package/dist/lib/pagination.d.ts.map +1 -0
  86. package/dist/lib/pagination.js +40 -0
  87. package/dist/lib/pagination.js.map +1 -0
  88. package/dist/lib/truncate.d.ts +25 -0
  89. package/dist/lib/truncate.d.ts.map +1 -0
  90. package/dist/lib/truncate.js +45 -0
  91. package/dist/lib/truncate.js.map +1 -0
  92. package/dist/main.d.ts +9 -0
  93. package/dist/main.d.ts.map +1 -0
  94. package/dist/main.js +12 -0
  95. package/dist/main.js.map +1 -0
  96. package/dist/prompts/index.d.ts +4 -0
  97. package/dist/prompts/index.d.ts.map +1 -0
  98. package/dist/prompts/index.js +72 -0
  99. package/dist/prompts/index.js.map +1 -0
  100. package/dist/resources/docs/diagramSchema.d.ts +2 -0
  101. package/dist/resources/docs/diagramSchema.d.ts.map +1 -0
  102. package/dist/resources/docs/diagramSchema.js +65 -0
  103. package/dist/resources/docs/diagramSchema.js.map +1 -0
  104. package/dist/resources/docs/overview.d.ts +2 -0
  105. package/dist/resources/docs/overview.d.ts.map +1 -0
  106. package/dist/resources/docs/overview.js +46 -0
  107. package/dist/resources/docs/overview.js.map +1 -0
  108. package/dist/resources/docs/requestSchema.d.ts +2 -0
  109. package/dist/resources/docs/requestSchema.d.ts.map +1 -0
  110. package/dist/resources/docs/requestSchema.js +70 -0
  111. package/dist/resources/docs/requestSchema.js.map +1 -0
  112. package/dist/resources/generated/aiPacks.d.ts +3 -0
  113. package/dist/resources/generated/aiPacks.d.ts.map +1 -0
  114. package/dist/resources/generated/aiPacks.js +11 -0
  115. package/dist/resources/generated/aiPacks.js.map +1 -0
  116. package/dist/resources/index.d.ts +4 -0
  117. package/dist/resources/index.d.ts.map +1 -0
  118. package/dist/resources/index.js +114 -0
  119. package/dist/resources/index.js.map +1 -0
  120. package/dist/run/executeCollection.d.ts +19 -0
  121. package/dist/run/executeCollection.d.ts.map +1 -0
  122. package/dist/run/executeCollection.js +89 -0
  123. package/dist/run/executeCollection.js.map +1 -0
  124. package/dist/run/loadBundle.d.ts +9 -0
  125. package/dist/run/loadBundle.d.ts.map +1 -0
  126. package/dist/run/loadBundle.js +242 -0
  127. package/dist/run/loadBundle.js.map +1 -0
  128. package/dist/run/resolveTemplate.d.ts +2 -0
  129. package/dist/run/resolveTemplate.d.ts.map +1 -0
  130. package/dist/run/resolveTemplate.js +12 -0
  131. package/dist/run/resolveTemplate.js.map +1 -0
  132. package/dist/run/summarize.d.ts +73 -0
  133. package/dist/run/summarize.d.ts.map +1 -0
  134. package/dist/run/summarize.js +69 -0
  135. package/dist/run/summarize.js.map +1 -0
  136. package/dist/run/types.d.ts +46 -0
  137. package/dist/run/types.d.ts.map +1 -0
  138. package/dist/run/types.js +2 -0
  139. package/dist/run/types.js.map +1 -0
  140. package/dist/server.d.ts +23 -0
  141. package/dist/server.d.ts.map +1 -0
  142. package/dist/server.js +25 -0
  143. package/dist/server.js.map +1 -0
  144. package/dist/tools/catalog.d.ts +18 -0
  145. package/dist/tools/catalog.d.ts.map +1 -0
  146. package/dist/tools/catalog.js +66 -0
  147. package/dist/tools/catalog.js.map +1 -0
  148. package/dist/tools/collections.d.ts +4 -0
  149. package/dist/tools/collections.d.ts.map +1 -0
  150. package/dist/tools/collections.js +201 -0
  151. package/dist/tools/collections.js.map +1 -0
  152. package/dist/tools/diagrams.d.ts +4 -0
  153. package/dist/tools/diagrams.d.ts.map +1 -0
  154. package/dist/tools/diagrams.js +226 -0
  155. package/dist/tools/diagrams.js.map +1 -0
  156. package/dist/tools/environments.d.ts +4 -0
  157. package/dist/tools/environments.d.ts.map +1 -0
  158. package/dist/tools/environments.js +150 -0
  159. package/dist/tools/environments.js.map +1 -0
  160. package/dist/tools/import.d.ts +6 -0
  161. package/dist/tools/import.d.ts.map +1 -0
  162. package/dist/tools/import.js +144 -0
  163. package/dist/tools/import.js.map +1 -0
  164. package/dist/tools/index.d.ts +5 -0
  165. package/dist/tools/index.d.ts.map +1 -0
  166. package/dist/tools/index.js +26 -0
  167. package/dist/tools/index.js.map +1 -0
  168. package/dist/tools/lookups.d.ts +33 -0
  169. package/dist/tools/lookups.d.ts.map +1 -0
  170. package/dist/tools/lookups.js +132 -0
  171. package/dist/tools/lookups.js.map +1 -0
  172. package/dist/tools/meta.d.ts +4 -0
  173. package/dist/tools/meta.d.ts.map +1 -0
  174. package/dist/tools/meta.js +78 -0
  175. package/dist/tools/meta.js.map +1 -0
  176. package/dist/tools/requests.d.ts +4 -0
  177. package/dist/tools/requests.d.ts.map +1 -0
  178. package/dist/tools/requests.js +247 -0
  179. package/dist/tools/requests.js.map +1 -0
  180. package/dist/tools/run.d.ts +4 -0
  181. package/dist/tools/run.d.ts.map +1 -0
  182. package/dist/tools/run.js +167 -0
  183. package/dist/tools/run.js.map +1 -0
  184. package/dist/tools/scenarios.d.ts +4 -0
  185. package/dist/tools/scenarios.d.ts.map +1 -0
  186. package/dist/tools/scenarios.js +242 -0
  187. package/dist/tools/scenarios.js.map +1 -0
  188. package/dist/tools/shared.d.ts +20 -0
  189. package/dist/tools/shared.d.ts.map +1 -0
  190. package/dist/tools/shared.js +74 -0
  191. package/dist/tools/shared.js.map +1 -0
  192. package/dist/tools/workspaces.d.ts +4 -0
  193. package/dist/tools/workspaces.d.ts.map +1 -0
  194. package/dist/tools/workspaces.js +80 -0
  195. package/dist/tools/workspaces.js.map +1 -0
  196. package/dist-cjs/client/api.js +183 -0
  197. package/dist-cjs/client/errors.js +104 -0
  198. package/dist-cjs/client/http.js +124 -0
  199. package/dist-cjs/client/types.js +5 -0
  200. package/dist-cjs/config.js +73 -0
  201. package/dist-cjs/context.js +94 -0
  202. package/dist-cjs/domain/diagram/build.js +164 -0
  203. package/dist-cjs/domain/diagram/layout.js +98 -0
  204. package/dist-cjs/domain/diagram/patch.js +179 -0
  205. package/dist-cjs/domain/diagram/summarize.js +52 -0
  206. package/dist-cjs/domain/importDetect.js +40 -0
  207. package/dist-cjs/domain/requestShape.js +260 -0
  208. package/dist-cjs/domain/resolve.js +63 -0
  209. package/dist-cjs/domain/scenario/build.js +140 -0
  210. package/dist-cjs/domain/scenario/summarize.js +144 -0
  211. package/dist-cjs/lib/cache.js +43 -0
  212. package/dist-cjs/lib/ids.js +24 -0
  213. package/dist-cjs/lib/log.js +28 -0
  214. package/dist-cjs/lib/mask.js +72 -0
  215. package/dist-cjs/lib/pagination.js +48 -0
  216. package/dist-cjs/lib/truncate.js +52 -0
  217. package/dist-cjs/main.js +31 -0
  218. package/dist-cjs/package.json +3 -0
  219. package/dist-cjs/prompts/index.js +75 -0
  220. package/dist-cjs/resources/docs/diagramSchema.js +67 -0
  221. package/dist-cjs/resources/docs/overview.js +48 -0
  222. package/dist-cjs/resources/docs/requestSchema.js +72 -0
  223. package/dist-cjs/resources/generated/aiPacks.js +13 -0
  224. package/dist-cjs/resources/index.js +117 -0
  225. package/dist-cjs/run/executeCollection.js +93 -0
  226. package/dist-cjs/run/loadBundle.js +246 -0
  227. package/dist-cjs/run/resolveTemplate.js +15 -0
  228. package/dist-cjs/run/summarize.js +73 -0
  229. package/dist-cjs/run/types.js +2 -0
  230. package/dist-cjs/server.js +28 -0
  231. package/dist-cjs/tools/catalog.js +69 -0
  232. package/dist-cjs/tools/collections.js +204 -0
  233. package/dist-cjs/tools/diagrams.js +229 -0
  234. package/dist-cjs/tools/environments.js +153 -0
  235. package/dist-cjs/tools/import.js +147 -0
  236. package/dist-cjs/tools/index.js +31 -0
  237. package/dist-cjs/tools/lookups.js +149 -0
  238. package/dist-cjs/tools/meta.js +81 -0
  239. package/dist-cjs/tools/requests.js +250 -0
  240. package/dist-cjs/tools/run.js +203 -0
  241. package/dist-cjs/tools/scenarios.js +245 -0
  242. package/dist-cjs/tools/shared.js +81 -0
  243. package/dist-cjs/tools/workspaces.js +83 -0
  244. package/package.json +80 -0
@@ -0,0 +1,260 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requestSummaryView = exports.compactRequestView = exports.toUpdateRequestPayload = exports.updateRequestInputSchema = exports.toFlatImportRequest = exports.toCreateRequestPayload = exports.toBodyPayload = exports.toAuthPayload = exports.toRows = exports.requestInputSchema = exports.authInputSchema = exports.bodyInputSchema = exports.kvSchema = exports.HTTP_METHODS = void 0;
4
+ const callman_core_1 = require("callman-core");
5
+ const zod_1 = require("zod");
6
+ const mask_js_1 = require("../lib/mask.js");
7
+ const truncate_js_1 = require("../lib/truncate.js");
8
+ // The COMPACT request form a model writes (name/method/url, key-value lists,
9
+ // a body that may be a JSON value, a string or a mode object, a small auth
10
+ // object) and its translation into the backend's `POST /requests` payload —
11
+ // the same document the desktop editor saves, produced by the same core
12
+ // serialisation (`toPersistedRequestBody`) so an imported request opens in
13
+ // the editor exactly like a hand-made one.
14
+ exports.HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "TRACE"];
15
+ exports.kvSchema = zod_1.z.object({
16
+ key: zod_1.z.string().max(100),
17
+ value: zod_1.z.string().max(5000).default(""),
18
+ enabled: zod_1.z.boolean().optional(),
19
+ description: zod_1.z.string().max(500).optional(),
20
+ });
21
+ exports.bodyInputSchema = zod_1.z.union([
22
+ zod_1.z.object({
23
+ mode: zod_1.z.literal("raw"),
24
+ raw: zod_1.z.string().max(2_000_000),
25
+ language: zod_1.z.enum(["json", "xml", "text", "html"]).optional(),
26
+ }),
27
+ zod_1.z.object({
28
+ mode: zod_1.z.enum(["formdata", "urlencoded"]),
29
+ entries: zod_1.z.array(exports.kvSchema).max(200),
30
+ }),
31
+ zod_1.z.object({ mode: zod_1.z.literal("none") }),
32
+ zod_1.z.unknown(),
33
+ ]);
34
+ exports.authInputSchema = zod_1.z.discriminatedUnion("type", [
35
+ zod_1.z.object({ type: zod_1.z.literal("none") }),
36
+ zod_1.z.object({ type: zod_1.z.literal("bearer"), token: zod_1.z.string().max(10_000) }),
37
+ zod_1.z.object({ type: zod_1.z.literal("basic"), username: zod_1.z.string().max(5000), password: zod_1.z.string().max(5000).default("") }),
38
+ zod_1.z.object({
39
+ type: zod_1.z.literal("apiKey"),
40
+ key: zod_1.z.string().max(5000),
41
+ value: zod_1.z.string().max(10_000),
42
+ location: zod_1.z.enum(["header", "query"]).default("header"),
43
+ }),
44
+ zod_1.z.object({
45
+ type: zod_1.z.literal("oauth2"),
46
+ grantType: zod_1.z.enum(["client_credentials", "password", "refresh_token"]).default("client_credentials"),
47
+ tokenUrl: zod_1.z.string().max(4096),
48
+ clientId: zod_1.z.string().max(5000).default(""),
49
+ clientSecret: zod_1.z.string().max(10_000).default(""),
50
+ scope: zod_1.z.string().max(5000).default(""),
51
+ username: zod_1.z.string().max(5000).default(""),
52
+ password: zod_1.z.string().max(5000).default(""),
53
+ refreshToken: zod_1.z.string().max(10_000).default(""),
54
+ clientAuth: zod_1.z.enum(["basic", "body"]).default("body"),
55
+ audience: zod_1.z.string().max(4096).default(""),
56
+ }),
57
+ ]);
58
+ exports.requestInputSchema = zod_1.z.object({
59
+ name: zod_1.z.string().min(1).max(truncate_js_1.LIMITS.NAME).describe("Display name, e.g. \"GET /users\" or \"Login\""),
60
+ method: zod_1.z.enum(exports.HTTP_METHODS),
61
+ url: zod_1.z
62
+ .string()
63
+ .min(1)
64
+ .max(4096)
65
+ .describe("Full URL; use {{variable}} for hosts/tokens (e.g. {{baseUrl}}/users/:id) and :name for path variables"),
66
+ headers: zod_1.z.array(exports.kvSchema).max(200).optional(),
67
+ query: zod_1.z.array(exports.kvSchema).max(200).optional(),
68
+ pathVariables: zod_1.z.array(exports.kvSchema).max(200).optional().describe("Values for :name segments in the URL"),
69
+ body: exports.bodyInputSchema
70
+ .optional()
71
+ .describe("A JSON value (stored as JSON), a string (JSON is parsed, otherwise raw text/xml/html by sniffing), or { mode: \"raw\" | \"formdata\" | \"urlencoded\" | \"none\", … }"),
72
+ auth: exports.authInputSchema.optional(),
73
+ preRequestScript: zod_1.z.string().max(100_000).optional(),
74
+ postResponseScript: zod_1.z.string().max(100_000).optional().describe("pm.* script run after the response (tests, pm.environment.set …)"),
75
+ testsScript: zod_1.z.string().max(100_000).optional(),
76
+ folder: zod_1.z.string().max(200).optional().describe("Folder id, name or path (\"Auth/Login\") inside the collection"),
77
+ folderKey: zod_1.z.string().max(120).optional().describe("Bulk create only: key of a folder declared in `folders`"),
78
+ isEnabledInRunner: zod_1.z.boolean().optional(),
79
+ });
80
+ const toRows = (entries) => (entries ?? [])
81
+ .filter((entry) => entry.key.trim().length > 0)
82
+ .map((entry) => ({ key: entry.key.trim(), value: entry.value ?? "", enabled: entry.enabled ?? true }))
83
+ .slice(0, 200);
84
+ exports.toRows = toRows;
85
+ const EMPTY_AUTH = {
86
+ type: "none",
87
+ bearerToken: "",
88
+ basicUsername: "",
89
+ basicPassword: "",
90
+ apiKeyKey: "",
91
+ apiKeyValue: "",
92
+ apiKeyLocation: "header",
93
+ };
94
+ const toAuthPayload = (auth) => {
95
+ if (!auth)
96
+ return { ...EMPTY_AUTH };
97
+ switch (auth.type) {
98
+ case "bearer":
99
+ return { ...EMPTY_AUTH, type: "bearer", bearerToken: auth.token };
100
+ case "basic":
101
+ return { ...EMPTY_AUTH, type: "basic", basicUsername: auth.username, basicPassword: auth.password };
102
+ case "apiKey":
103
+ return { ...EMPTY_AUTH, type: "apikey", apiKeyKey: auth.key, apiKeyValue: auth.value, apiKeyLocation: auth.location };
104
+ case "oauth2": {
105
+ const { type: _type, ...oauth2 } = auth;
106
+ return { ...EMPTY_AUTH, type: "oauth2", oauth2 };
107
+ }
108
+ default:
109
+ return { ...EMPTY_AUTH };
110
+ }
111
+ };
112
+ exports.toAuthPayload = toAuthPayload;
113
+ const isModeObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.mode === "string";
114
+ // Persisted body — the exact rules of the desktop editor's serializeBodyState
115
+ // (guarded in the desktop by persistedBody.drift.test.ts): JSON stays a bare
116
+ // JSON value; raw xml/text/html persists as { mode, raw, rawLanguage };
117
+ // form bodies keep their entries; empty → null.
118
+ const toBodyPayload = (body, headers) => {
119
+ if (body === undefined || body === null)
120
+ return null;
121
+ if (typeof body === "string") {
122
+ return (0, callman_core_1.toPersistedRequestBody)((0, callman_core_1.inferBodyStateFromRaw)(body, headers));
123
+ }
124
+ if (isModeObject(body)) {
125
+ const record = body;
126
+ if (record.mode === "none")
127
+ return null;
128
+ if (record.mode === "raw") {
129
+ const raw = typeof record.raw === "string" ? record.raw : "";
130
+ const language = record.language;
131
+ if (language) {
132
+ return (0, callman_core_1.toPersistedRequestBody)({ mode: "raw", raw, rawLanguage: language, formEntries: [] });
133
+ }
134
+ return (0, callman_core_1.toPersistedRequestBody)((0, callman_core_1.inferBodyStateFromRaw)(raw, headers));
135
+ }
136
+ if (record.mode === "formdata" || record.mode === "urlencoded") {
137
+ const entries = Array.isArray(record.entries) ? record.entries : [];
138
+ return (0, callman_core_1.toPersistedRequestBody)({ mode: record.mode, raw: "", formEntries: (0, exports.toRows)(entries) });
139
+ }
140
+ }
141
+ // Any other JSON value is stored as-is (the editor shows it on the JSON tab).
142
+ return body;
143
+ };
144
+ exports.toBodyPayload = toBodyPayload;
145
+ const toCreateRequestPayload = (item, target) => {
146
+ const headers = (0, exports.toRows)(item.headers);
147
+ return {
148
+ name: item.name.trim().slice(0, truncate_js_1.LIMITS.NAME),
149
+ method: item.method,
150
+ url: item.url.trim(),
151
+ headers,
152
+ query: (0, exports.toRows)(item.query),
153
+ pathVariables: (item.pathVariables ?? [])
154
+ .filter((entry) => entry.key.trim())
155
+ .map((entry) => ({ key: entry.key.trim(), value: entry.value ?? "", description: entry.description ?? "" })),
156
+ body: (0, exports.toBodyPayload)(item.body, headers),
157
+ auth: (0, exports.toAuthPayload)(item.auth),
158
+ preRequestScript: item.preRequestScript ?? "",
159
+ postResponseScript: item.postResponseScript ?? "",
160
+ testsScript: item.testsScript ?? "",
161
+ collectionId: target.collectionId,
162
+ folderId: target.folderId,
163
+ };
164
+ };
165
+ exports.toCreateRequestPayload = toCreateRequestPayload;
166
+ // `format: "requests"` bulk import row — same document minus the ids the
167
+ // server assigns, plus the payload-local folderKey.
168
+ const toFlatImportRequest = (item) => {
169
+ const { collectionId: _c, folderId: _f, ...payload } = (0, exports.toCreateRequestPayload)(item, { collectionId: "", folderId: null });
170
+ return { ...payload, ...(item.folderKey ? { folderKey: item.folderKey } : {}) };
171
+ };
172
+ exports.toFlatImportRequest = toFlatImportRequest;
173
+ exports.updateRequestInputSchema = exports.requestInputSchema
174
+ .partial()
175
+ .omit({ folderKey: true })
176
+ .extend({ collection: zod_1.z.string().max(200).optional() });
177
+ const toUpdateRequestPayload = (item, target) => {
178
+ const out = {};
179
+ if (item.name !== undefined)
180
+ out.name = item.name.trim().slice(0, truncate_js_1.LIMITS.NAME);
181
+ if (item.method !== undefined)
182
+ out.method = item.method;
183
+ if (item.url !== undefined)
184
+ out.url = item.url.trim();
185
+ if (item.headers !== undefined)
186
+ out.headers = (0, exports.toRows)(item.headers);
187
+ if (item.query !== undefined)
188
+ out.query = (0, exports.toRows)(item.query);
189
+ if (item.pathVariables !== undefined) {
190
+ out.pathVariables = item.pathVariables
191
+ .filter((entry) => entry.key.trim())
192
+ .map((entry) => ({ key: entry.key.trim(), value: entry.value ?? "", description: entry.description ?? "" }));
193
+ }
194
+ if (item.body !== undefined)
195
+ out.body = (0, exports.toBodyPayload)(item.body, (0, exports.toRows)(item.headers));
196
+ if (item.auth !== undefined)
197
+ out.auth = (0, exports.toAuthPayload)(item.auth);
198
+ if (item.preRequestScript !== undefined)
199
+ out.preRequestScript = item.preRequestScript;
200
+ if (item.postResponseScript !== undefined)
201
+ out.postResponseScript = item.postResponseScript;
202
+ if (item.testsScript !== undefined)
203
+ out.testsScript = item.testsScript;
204
+ if (item.isEnabledInRunner !== undefined)
205
+ out.isEnabledInRunner = item.isEnabledInRunner;
206
+ if (target.collectionId !== undefined)
207
+ out.collectionId = target.collectionId;
208
+ if (target.folderId !== undefined)
209
+ out.folderId = target.folderId;
210
+ return out;
211
+ };
212
+ exports.toUpdateRequestPayload = toUpdateRequestPayload;
213
+ // ── outbound views ─────────────────────────────────────────────────────
214
+ const kvView = (rows, maskHeaders) => (rows ?? []).map((row) => ({
215
+ key: row.key,
216
+ value: maskHeaders ? (0, mask_js_1.maskHeaderValue)(row.key, row.value) : row.value,
217
+ ...(row.enabled === false ? { enabled: false } : {}),
218
+ }));
219
+ const compactRequestView = (request, options = {}) => {
220
+ const bodyText = request.body == null ? null : (0, truncate_js_1.safeStringify)(request.body);
221
+ const bodyTruncated = bodyText !== null && !options.full && bodyText.length > truncate_js_1.LIMITS.BODY_PREVIEW;
222
+ return {
223
+ id: request.id,
224
+ name: request.name,
225
+ method: request.method,
226
+ url: options.full ? request.url : (0, truncate_js_1.clip)(request.url, 2048),
227
+ collectionId: request.collectionId,
228
+ folderId: request.folderId,
229
+ headers: kvView(request.headers, !options.reveal),
230
+ query: kvView(request.query, false),
231
+ ...(request.pathVariables && request.pathVariables.length > 0
232
+ ? { pathVariables: request.pathVariables.map((entry) => ({ key: entry.key, value: entry.value ?? "" })) }
233
+ : {}),
234
+ body: options.full ? request.body : (0, truncate_js_1.jsonPreview)(request.body),
235
+ ...(bodyTruncated ? { bodyTruncated: true } : {}),
236
+ auth: options.reveal ? (request.auth ?? { type: "none" }) : (0, mask_js_1.maskAuth)(request.auth),
237
+ scripts: {
238
+ preRequest: scriptView(request.preRequestScript, options.full),
239
+ postResponse: scriptView(request.postResponseScript, options.full),
240
+ tests: scriptView(request.testsScript, options.full),
241
+ },
242
+ isEnabledInRunner: request.isEnabledInRunner !== false,
243
+ updatedAt: request.updatedAt ?? null,
244
+ };
245
+ };
246
+ exports.compactRequestView = compactRequestView;
247
+ const scriptView = (script, full) => {
248
+ if (!script)
249
+ return null;
250
+ return full ? script : (0, truncate_js_1.clip)(script, truncate_js_1.LIMITS.BODY_PREVIEW);
251
+ };
252
+ const requestSummaryView = (request) => ({
253
+ id: request.id,
254
+ name: request.name,
255
+ method: request.method,
256
+ url: (0, truncate_js_1.clip)(request.url, truncate_js_1.LIMITS.URL),
257
+ folderId: request.folderId,
258
+ ...(request.collectionId ? { collectionId: request.collectionId } : {}),
259
+ });
260
+ exports.requestSummaryView = requestSummaryView;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveFolderRef = exports.flattenFolderTree = exports.resolvedSummary = exports.resolveRefs = exports.resolveRef = void 0;
4
+ const errors_js_1 = require("../client/errors.js");
5
+ const ids_js_1 = require("../lib/ids.js");
6
+ const candidatesOf = (items, options) => items.slice(0, 12).map((item) => ({ id: item.id, name: item.name, ...(options?.describe?.(item) ?? {}) }));
7
+ const resolveRef = (kind, ref, items, options) => {
8
+ const reference = (ref ?? "").trim();
9
+ if (!reference) {
10
+ throw new errors_js_1.ToolInputError("REF_REQUIRED", `A ${kind} reference (id or name) is required.`, {
11
+ kind,
12
+ candidates: candidatesOf(items, options),
13
+ });
14
+ }
15
+ const byId = items.find((item) => item.id === reference);
16
+ if (byId)
17
+ return { item: byId, matched: "id" };
18
+ const needle = (0, ids_js_1.normalizeName)(reference);
19
+ const names = (item) => [item.name, ...(options?.aliases?.(item) ?? [])].map(ids_js_1.normalizeName);
20
+ const exact = items.filter((item) => names(item).some((name) => name === needle));
21
+ if (exact.length === 1) {
22
+ return { item: exact[0], matched: "name", annotation: `matched "${reference}" → ${exact[0].name}` };
23
+ }
24
+ if (exact.length > 1) {
25
+ throw new errors_js_1.ToolInputError("REF_AMBIGUOUS", `${exact.length} ${kind}s are named "${reference}" — use the id.`, { kind, ref: reference, candidates: candidatesOf(exact, options) });
26
+ }
27
+ const partial = items.filter((item) => names(item).some((name) => name.includes(needle)));
28
+ if (partial.length === 1) {
29
+ return { item: partial[0], matched: "partial", annotation: `matched "${reference}" → ${partial[0].name}` };
30
+ }
31
+ throw new errors_js_1.ToolInputError(partial.length > 1 ? "REF_AMBIGUOUS" : "REF_NOT_FOUND", partial.length > 1
32
+ ? `Several ${kind}s match "${reference}" — pick one by id.`
33
+ : `No ${kind} matches "${reference}".`, { kind, ref: reference, candidates: candidatesOf(partial.length > 0 ? partial : items, options) });
34
+ };
35
+ exports.resolveRef = resolveRef;
36
+ const resolveRefs = (kind, refs, items, options) => refs.map((ref) => (0, exports.resolveRef)(kind, ref, items, options));
37
+ exports.resolveRefs = resolveRefs;
38
+ // A resolution summary for tool output (`resolved: [{ input, matched, id }]`).
39
+ const resolvedSummary = (input, resolved) => ({
40
+ input,
41
+ matched: resolved.item.name,
42
+ id: resolved.item.id,
43
+ ...(resolved.annotation ? { note: resolved.annotation } : {}),
44
+ });
45
+ exports.resolvedSummary = resolvedSummary;
46
+ const flattenFolderTree = (tree) => {
47
+ const out = [];
48
+ const walk = (nodes, prefix, depth) => {
49
+ for (const node of nodes) {
50
+ const path = prefix ? `${prefix}/${node.name}` : node.name;
51
+ out.push({ id: node.id, name: node.name, parentId: node.parentId, path, depth });
52
+ walk(node.children ?? [], path, depth + 1);
53
+ }
54
+ };
55
+ walk(tree, "", 0);
56
+ return out;
57
+ };
58
+ exports.flattenFolderTree = flattenFolderTree;
59
+ const resolveFolderRef = (ref, folders) => (0, exports.resolveRef)("folder", ref, folders, {
60
+ describe: (folder) => ({ path: folder.path }),
61
+ aliases: (folder) => [folder.path],
62
+ });
63
+ exports.resolveFolderRef = resolveFolderRef;
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildScenarioCreateBody = exports.buildScenarioGraph = exports.deepMerge = exports.scenarioInputSchema = exports.scenarioEnvironmentInputSchema = exports.scenarioEdgeInputSchema = exports.scenarioNodeInputSchema = exports.SCENARIO_NODE_TYPES = void 0;
4
+ const callman_core_1 = require("callman-core");
5
+ const zod_1 = require("zod");
6
+ const errors_js_1 = require("../../client/errors.js");
7
+ const ids_js_1 = require("../../lib/ids.js");
8
+ const requestShape_js_1 = require("../requestShape.js");
9
+ // From the COMPACT scenario a model writes to the backend's `POST /scenarios`
10
+ // body. Node configs are deep-merged over core's canonical defaults for the
11
+ // node type (the same defaults the builder and the AI assistant use), so a
12
+ // partial config (`{ url, method }`) becomes a complete, runnable node; ids
13
+ // are short and stable; missing positions are laid out in layers so the
14
+ // graph opens tidy in the builder. The backend's zod schema stays the
15
+ // arbiter of node config validity.
16
+ exports.SCENARIO_NODE_TYPES = [...callman_core_1.SCENARIO_AI_REFERENCE_TYPES, "subscenario"];
17
+ exports.scenarioNodeInputSchema = zod_1.z.object({
18
+ id: zod_1.z.string().min(1).max(64).optional(),
19
+ type: zod_1.z.string().min(1).max(40).describe(`Node type: ${exports.SCENARIO_NODE_TYPES.join(", ")} (see callman://docs/scenario-nodes)`),
20
+ label: zod_1.z.string().max(200).optional(),
21
+ config: zod_1.z.record(zod_1.z.unknown()).optional().describe("Type-specific config; merged over the node type's defaults"),
22
+ position: zod_1.z.object({ x: zod_1.z.number(), y: zod_1.z.number() }).optional(),
23
+ });
24
+ exports.scenarioEdgeInputSchema = zod_1.z.object({
25
+ id: zod_1.z.string().min(1).max(64).optional(),
26
+ source: zod_1.z.string().min(1).max(64),
27
+ target: zod_1.z.string().min(1).max(64),
28
+ type: zod_1.z.enum(["default", "yes", "no", "error"]).optional().describe("yes/no for condition branches, error for failure paths"),
29
+ });
30
+ exports.scenarioEnvironmentInputSchema = zod_1.z.object({
31
+ id: zod_1.z.string().min(1).max(64).optional(),
32
+ name: zod_1.z.string().min(1).max(200),
33
+ variables: zod_1.z.array(requestShape_js_1.kvSchema).max(500).default([]),
34
+ });
35
+ exports.scenarioInputSchema = zod_1.z.object({
36
+ name: zod_1.z.string().min(1).max(200),
37
+ description: zod_1.z.string().max(5000).optional(),
38
+ priority: zod_1.z.enum(["critical", "high", "medium", "low"]).optional(),
39
+ nodes: zod_1.z.array(exports.scenarioNodeInputSchema).min(1).max(1000),
40
+ edges: zod_1.z.array(exports.scenarioEdgeInputSchema).max(2000).default([]),
41
+ environments: zod_1.z.array(exports.scenarioEnvironmentInputSchema).max(20).optional(),
42
+ });
43
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
44
+ // Objects merge key by key; arrays and scalars from the override win whole.
45
+ const deepMerge = (base, override) => {
46
+ if (!isRecord(base) || !isRecord(override))
47
+ return override === undefined ? base : override;
48
+ const out = { ...base };
49
+ for (const [key, value] of Object.entries(override)) {
50
+ out[key] = key in base ? (0, exports.deepMerge)(base[key], value) : value;
51
+ }
52
+ return out;
53
+ };
54
+ exports.deepMerge = deepMerge;
55
+ const buildScenarioGraph = (nodesInput, edgesInput) => {
56
+ const nextNodeId = (0, ids_js_1.sequentialIds)("n", nodesInput.map((node) => node.id).filter((id) => Boolean(id)));
57
+ const nextEdgeId = (0, ids_js_1.sequentialIds)("e", edgesInput.map((edge) => edge.id).filter((id) => Boolean(id)));
58
+ const ids = nodesInput.map((node) => node.id ?? nextNodeId());
59
+ const duplicate = ids.find((id, index) => ids.indexOf(id) !== index);
60
+ if (duplicate)
61
+ throw new errors_js_1.ToolInputError("SCENARIO_DUPLICATE_NODE_ID", `Duplicate node id "${duplicate}".`);
62
+ const idSet = new Set(ids);
63
+ const nodes = nodesInput.map((node, index) => {
64
+ const type = node.type.trim();
65
+ if (!exports.SCENARIO_NODE_TYPES.includes(type)) {
66
+ throw new errors_js_1.ToolInputError("SCENARIO_UNKNOWN_NODE_TYPE", `Unknown scenario node type "${type}". Known: ${exports.SCENARIO_NODE_TYPES.join(", ")}.`);
67
+ }
68
+ const defaults = (0, callman_core_1.defaultScenarioNodeConfig)(type);
69
+ const config = (0, exports.deepMerge)(defaults ?? {}, node.config ?? {});
70
+ return {
71
+ id: ids[index],
72
+ type,
73
+ position: node.position ?? { x: 0, y: 0 },
74
+ data: { ...(node.label ? { label: node.label } : {}), config },
75
+ _positioned: Boolean(node.position),
76
+ };
77
+ });
78
+ const edges = edgesInput.map((edge) => {
79
+ for (const endpoint of [edge.source, edge.target]) {
80
+ if (!idSet.has(endpoint))
81
+ throw new errors_js_1.ToolInputError("SCENARIO_EDGE_ENDPOINT_NOT_FOUND", `Edge references "${endpoint}" which is not a node id.`);
82
+ }
83
+ return { id: edge.id ?? nextEdgeId(), source: edge.source, target: edge.target, ...(edge.type ? { type: edge.type } : {}) };
84
+ });
85
+ // Layered positions for the nodes that have none: BFS from the sources.
86
+ const incoming = new Map(ids.map((id) => [id, 0]));
87
+ const outgoing = new Map(ids.map((id) => [id, []]));
88
+ for (const edge of edges) {
89
+ incoming.set(edge.target, (incoming.get(edge.target) ?? 0) + 1);
90
+ outgoing.get(edge.source)?.push(edge.target);
91
+ }
92
+ const level = new Map();
93
+ const queue = ids.filter((id) => (incoming.get(id) ?? 0) === 0);
94
+ for (const id of queue)
95
+ level.set(id, 0);
96
+ while (queue.length > 0) {
97
+ const current = queue.shift();
98
+ for (const next of outgoing.get(current) ?? []) {
99
+ if (!level.has(next)) {
100
+ level.set(next, (level.get(current) ?? 0) + 1);
101
+ queue.push(next);
102
+ }
103
+ }
104
+ }
105
+ const rowCounter = new Map();
106
+ const positioned = nodes.map((node) => {
107
+ if (node._positioned)
108
+ return node;
109
+ const layer = level.get(node.id) ?? 0;
110
+ const row = rowCounter.get(layer) ?? 0;
111
+ rowCounter.set(layer, row + 1);
112
+ return { ...node, position: { x: 80 + layer * 320, y: 80 + row * 170 } };
113
+ });
114
+ return {
115
+ nodes: positioned.map(({ _positioned, ...node }) => node),
116
+ edges,
117
+ };
118
+ };
119
+ exports.buildScenarioGraph = buildScenarioGraph;
120
+ const buildScenarioCreateBody = (input, folderId) => {
121
+ const graph = (0, exports.buildScenarioGraph)(input.nodes, input.edges);
122
+ const nextEnvId = (0, ids_js_1.sequentialIds)("env");
123
+ return {
124
+ name: input.name.trim(),
125
+ ...(input.description ? { description: input.description } : {}),
126
+ ...(input.priority ? { priority: input.priority } : {}),
127
+ folderId,
128
+ nodes: graph.nodes,
129
+ edges: graph.edges,
130
+ environments: (input.environments ?? []).map((environment) => {
131
+ const nextVarId = (0, ids_js_1.sequentialIds)("v");
132
+ return {
133
+ id: environment.id ?? nextEnvId(),
134
+ name: environment.name,
135
+ variables: (0, requestShape_js_1.toRows)(environment.variables).map((row) => ({ id: nextVarId(), key: row.key, value: row.value, enabled: row.enabled ?? true })),
136
+ };
137
+ }),
138
+ };
139
+ };
140
+ exports.buildScenarioCreateBody = buildScenarioCreateBody;
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.compactReportView = exports.scenarioFullView = exports.scenarioGraphView = exports.scenarioSummaryView = void 0;
4
+ const mask_js_1 = require("../../lib/mask.js");
5
+ const truncate_js_1 = require("../../lib/truncate.js");
6
+ const scenarioSummaryView = (scenario, folderName) => ({
7
+ id: scenario.id,
8
+ name: scenario.name,
9
+ description: scenario.description ? (0, truncate_js_1.clip)(scenario.description, 300) : null,
10
+ priority: scenario.priority,
11
+ folderId: scenario.folderId,
12
+ ...(folderName !== undefined ? { folder: folderName } : {}),
13
+ reviewStatus: scenario.reviewStatus,
14
+ version: scenario.version,
15
+ nodeCount: scenario.nodes.length,
16
+ edgeCount: scenario.edges.length,
17
+ environments: (scenario.environments ?? []).map((environment) => ({ id: environment.id, name: environment.name })),
18
+ ...(scenario.groupName ? { group: scenario.groupName } : {}),
19
+ ...(scenario.permissions ? { permissions: scenario.permissions } : {}),
20
+ updatedAt: scenario.updatedAt ?? null,
21
+ });
22
+ exports.scenarioSummaryView = scenarioSummaryView;
23
+ const scenarioGraphView = (scenario) => ({
24
+ ...(0, exports.scenarioSummaryView)(scenario),
25
+ nodes: scenario.nodes.map((node) => ({
26
+ id: node.id,
27
+ type: node.type,
28
+ label: node.data?.label ?? null,
29
+ ...nodeHint(node.type, node.data?.config ?? {}),
30
+ })),
31
+ edges: scenario.edges.map((edge) => ({ id: edge.id, source: edge.source, target: edge.target, ...(edge.type && edge.type !== "default" ? { type: edge.type } : {}) })),
32
+ startNodeIds: scenario.graph?.startNodeIds ?? [],
33
+ warnings: scenario.graph?.warnings ?? [],
34
+ });
35
+ exports.scenarioGraphView = scenarioGraphView;
36
+ const nodeHint = (type, config) => {
37
+ switch (type) {
38
+ case "request":
39
+ return { method: config.method ?? null, url: typeof config.url === "string" ? (0, truncate_js_1.clip)(config.url, truncate_js_1.LIMITS.URL) : null };
40
+ case "db":
41
+ return { query: typeof config.query === "string" ? (0, truncate_js_1.clip)(config.query, 120) : null };
42
+ case "wait":
43
+ return { durationMs: config.durationMs ?? null };
44
+ case "subscenario":
45
+ return { scenarioId: config.scenarioId ?? null };
46
+ default:
47
+ return {};
48
+ }
49
+ };
50
+ const maskConfig = (config, reveal) => {
51
+ if (reveal)
52
+ return config;
53
+ const out = { ...config };
54
+ if (out.auth && typeof out.auth === "object")
55
+ out.auth = (0, mask_js_1.maskAuth)(out.auth);
56
+ for (const key of ["password", "clientSecret", "token", "apiKey", "secret"]) {
57
+ if (typeof out[key] === "string" && out[key])
58
+ out[key] = (0, mask_js_1.maskSecret)(out[key]);
59
+ }
60
+ return out;
61
+ };
62
+ const scenarioFullView = (scenario, options = {}) => {
63
+ const nodes = scenario.nodes.map((node) => {
64
+ const config = maskConfig(node.data?.config ?? {}, options.reveal ?? false);
65
+ const text = (0, truncate_js_1.safeStringify)(config);
66
+ return {
67
+ id: node.id,
68
+ type: node.type,
69
+ label: node.data?.label ?? null,
70
+ position: node.position,
71
+ config: text.length > truncate_js_1.LIMITS.BODY_PREVIEW ? { _truncated: true, preview: (0, truncate_js_1.clip)(text, truncate_js_1.LIMITS.BODY_PREVIEW) } : config,
72
+ };
73
+ });
74
+ const view = {
75
+ ...(0, exports.scenarioSummaryView)(scenario),
76
+ nodes,
77
+ edges: scenario.edges,
78
+ environments: (scenario.environments ?? []).map((environment) => ({
79
+ id: environment.id,
80
+ name: environment.name,
81
+ variables: environment.variables.map((variable) => ({
82
+ key: variable.key,
83
+ value: variable.valueMasked || options.reveal ? variable.value : (0, mask_js_1.maskSecret)(variable.value),
84
+ enabled: variable.enabled,
85
+ })),
86
+ })),
87
+ dataset: scenario.dataset ?? null,
88
+ };
89
+ const bytes = (0, truncate_js_1.byteLength)(view);
90
+ if (bytes > truncate_js_1.LIMITS.SCENARIO_FULL) {
91
+ return { ...(0, exports.scenarioGraphView)(scenario), truncated: true, bytes, note: `Full view is ${bytes} bytes (limit ${truncate_js_1.LIMITS.SCENARIO_FULL}); graph view returned instead.` };
92
+ }
93
+ return view;
94
+ };
95
+ exports.scenarioFullView = scenarioFullView;
96
+ // ── reports ───────────────────────────────────────────────────────────
97
+ const testCounts = (results) => {
98
+ const passed = (results ?? []).filter((result) => result.status === "PASS").length;
99
+ const total = (results ?? []).length;
100
+ return { passed, failed: total - passed };
101
+ };
102
+ const nodeRow = (node) => ({
103
+ nodeId: node.nodeId,
104
+ label: node.label,
105
+ type: node.type,
106
+ status: node.status,
107
+ durationMs: node.durationMs,
108
+ ...(node.statusCode !== undefined && node.statusCode !== null ? { statusCode: node.statusCode } : {}),
109
+ ...(node.retryCount ? { retries: node.retryCount } : {}),
110
+ ...(node.error?.message ? { error: (0, truncate_js_1.clip)(node.error.message, 500) } : {}),
111
+ tests: testCounts(node.scriptResults),
112
+ contracts: testCounts(node.contractResults),
113
+ ...(node.conditionResult !== undefined && node.conditionResult !== null ? { conditionResult: node.conditionResult } : {}),
114
+ });
115
+ const FAILED_STATUSES = new Set(["failed", "error", "timeout"]);
116
+ const compactReportView = (report) => {
117
+ const nodes = report.nodeResults ?? [];
118
+ const failed = nodes.find((node) => FAILED_STATUSES.has(String(node.status)));
119
+ return {
120
+ reportId: report.id,
121
+ scenarioId: report.scenarioId,
122
+ scenarioName: report.scenarioName,
123
+ scenarioVersion: report.scenarioVersion,
124
+ status: report.status,
125
+ triggerType: report.triggerType,
126
+ startedAt: report.startedAt,
127
+ endedAt: report.endedAt,
128
+ durationMs: report.durationMs,
129
+ summary: report.summary,
130
+ nodes: nodes.map(nodeRow),
131
+ ...(failed
132
+ ? {
133
+ failedNodeDetail: {
134
+ ...nodeRow(failed),
135
+ failedTests: (failed.scriptResults ?? []).filter((result) => result.status === "FAIL").map((result) => ({ name: result.name, message: result.message ?? null })),
136
+ response: (0, truncate_js_1.jsonPreview)(failed.response ?? failed.outputSnapshot),
137
+ logs: (failed.logs ?? []).slice(-10).map((entry) => (0, truncate_js_1.clip)(String(entry.message ?? ""), 300)),
138
+ },
139
+ }
140
+ : {}),
141
+ ...(report.iterations && report.iterations.length > 0 ? { iterations: report.iterations.length } : {}),
142
+ };
143
+ };
144
+ exports.compactReportView = compactReportView;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ // Tiny TTL cache for hot reads (workspaces, collection trees) so a chain of
3
+ // tool calls in one turn does not hammer the API. Writes call `clear()`.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.TtlCache = void 0;
6
+ class TtlCache {
7
+ ttlMs;
8
+ now;
9
+ entries = new Map();
10
+ constructor(ttlMs, now = Date.now) {
11
+ this.ttlMs = ttlMs;
12
+ this.now = now;
13
+ }
14
+ get(key) {
15
+ const entry = this.entries.get(key);
16
+ if (!entry)
17
+ return undefined;
18
+ if (entry.expiresAt <= this.now()) {
19
+ this.entries.delete(key);
20
+ return undefined;
21
+ }
22
+ return entry.value;
23
+ }
24
+ set(key, value) {
25
+ this.entries.set(key, { value, expiresAt: this.now() + this.ttlMs });
26
+ return value;
27
+ }
28
+ async getOrSet(key, loader) {
29
+ const hit = this.get(key);
30
+ if (hit !== undefined)
31
+ return hit;
32
+ return this.set(key, await loader());
33
+ }
34
+ delete(prefix) {
35
+ for (const key of [...this.entries.keys()])
36
+ if (key.startsWith(prefix))
37
+ this.entries.delete(key);
38
+ }
39
+ clear() {
40
+ this.entries.clear();
41
+ }
42
+ }
43
+ exports.TtlCache = TtlCache;