@flexkit/studio 0.0.30 → 0.0.32

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 (110) hide show
  1. package/README.md +2 -2
  2. package/dist/asset-ZVPFS73O.js +2 -0
  3. package/dist/{asset-TS7J5GA5.js.map → asset-ZVPFS73O.js.map} +1 -1
  4. package/dist/astro.d.ts +5 -3
  5. package/dist/astro.js +8 -3
  6. package/dist/chunk-5FHCSAK2.js +2 -0
  7. package/dist/chunk-5FHCSAK2.js.map +1 -0
  8. package/dist/{chunk-R7N3MDOP.js → chunk-AEBTHXEP.js} +2 -2
  9. package/dist/{chunk-R7N3MDOP.js.map → chunk-AEBTHXEP.js.map} +1 -1
  10. package/dist/{chunk-W3TUEJJL.js → chunk-CCI3FGPF.js} +2 -2
  11. package/dist/{chunk-W3TUEJJL.js.map → chunk-CCI3FGPF.js.map} +1 -1
  12. package/dist/chunk-D62TAYLI.js +2 -0
  13. package/dist/chunk-D62TAYLI.js.map +1 -0
  14. package/dist/{chunk-UKLE7J3K.js → chunk-E7G46743.js} +139 -16
  15. package/dist/chunk-F2ILIHAU.js +2 -0
  16. package/dist/chunk-F2ILIHAU.js.map +1 -0
  17. package/dist/{chunk-EOCJR5EY.js → chunk-FPH4YETF.js} +2 -2
  18. package/dist/{chunk-EOCJR5EY.js.map → chunk-FPH4YETF.js.map} +1 -1
  19. package/dist/chunk-GFDWGYUR.js +2 -0
  20. package/dist/chunk-GFDWGYUR.js.map +1 -0
  21. package/dist/{chunk-CPUC4QXH.js → chunk-JAARDXQZ.js} +2 -2
  22. package/dist/{chunk-CPUC4QXH.js.map → chunk-JAARDXQZ.js.map} +1 -1
  23. package/dist/{chunk-IXO2LU47.js → chunk-JCHD4EGB.js} +2 -2
  24. package/dist/{chunk-IXO2LU47.js.map → chunk-JCHD4EGB.js.map} +1 -1
  25. package/dist/chunk-KHPTVIYT.js +2 -0
  26. package/dist/chunk-KHPTVIYT.js.map +1 -0
  27. package/dist/{chunk-DWK4NNLB.js → chunk-LKBCPAXQ.js} +2 -2
  28. package/dist/{chunk-DWK4NNLB.js.map → chunk-LKBCPAXQ.js.map} +1 -1
  29. package/dist/chunk-MGXHZDFZ.js +2 -0
  30. package/dist/chunk-MGXHZDFZ.js.map +1 -0
  31. package/dist/chunk-N2T6MKPV.js +2 -0
  32. package/dist/chunk-N2T6MKPV.js.map +1 -0
  33. package/dist/chunk-PXB5QSHN.js +2 -0
  34. package/dist/chunk-PXB5QSHN.js.map +1 -0
  35. package/dist/chunk-QSROLDUU.js +2 -0
  36. package/dist/chunk-QSROLDUU.js.map +1 -0
  37. package/dist/chunk-VHNQDP7Q.js +616 -0
  38. package/dist/chunk-VUAFNQK7.js +2 -0
  39. package/dist/chunk-VUAFNQK7.js.map +1 -0
  40. package/dist/chunk-XFY7MZJN.js +2 -0
  41. package/dist/chunk-XFY7MZJN.js.map +1 -0
  42. package/dist/{core-handler-XAVVKLhV.d.ts → core-handler-C3xC98bN.d.ts} +3 -0
  43. package/dist/datetime-DDEWTNLB.js +2 -0
  44. package/dist/{datetime-NQTYGZIC.js.map → datetime-DDEWTNLB.js.map} +1 -1
  45. package/dist/editor-HAYTKNMY.js +2 -0
  46. package/dist/{editor-YIRZQR3D.js.map → editor-HAYTKNMY.js.map} +1 -1
  47. package/dist/index.css +163 -44
  48. package/dist/index.d.ts +22 -21
  49. package/dist/index.js +157 -210
  50. package/dist/index.js.map +1 -1
  51. package/dist/logo-B6ZKLGM7.js +3 -0
  52. package/dist/logo-B6ZKLGM7.js.map +1 -0
  53. package/dist/nextjs.d.ts +40 -12
  54. package/dist/nextjs.js +8 -3
  55. package/dist/search-X5V3YOTZ.js +2 -0
  56. package/dist/search-X5V3YOTZ.js.map +1 -0
  57. package/dist/select-H3JHOCWR.js +2 -0
  58. package/dist/{select-A6I27JQU.js.map → select-H3JHOCWR.js.map} +1 -1
  59. package/dist/switch-TACKLQJH.js +2 -0
  60. package/dist/{switch-AFZGXSR3.js.map → switch-TACKLQJH.js.map} +1 -1
  61. package/dist/tanstack-start.d.ts +6 -4
  62. package/dist/tanstack-start.js +17 -12
  63. package/dist/text-M3DRK7LU.js +2 -0
  64. package/dist/{text-2RL2L5FT.js.map → text-M3DRK7LU.js.map} +1 -1
  65. package/dist/text-SQXG7XJT.js +2 -0
  66. package/dist/{text-6F4QTCNT.js.map → text-SQXG7XJT.js.map} +1 -1
  67. package/dist/textarea-YF4K2VBM.js +2 -0
  68. package/dist/{textarea-2A4TLWZS.js.map → textarea-YF4K2VBM.js.map} +1 -1
  69. package/dist/tools.d.ts +78 -0
  70. package/dist/tools.js +60 -0
  71. package/dist/types-U28Ppixq.d.ts +56 -0
  72. package/dist/{types.d-CX3kDxFZ.d.ts → types.d-710RgZFw.d.ts} +85 -7
  73. package/dist/ui.d.ts +21 -5
  74. package/dist/ui.js +5 -5
  75. package/dist/ui.js.map +1 -1
  76. package/dist/{user-nav-4JKIFZLB.js → user-nav-XVY7JTX6.js} +2 -2
  77. package/dist/{user-nav-4JKIFZLB.js.map → user-nav-XVY7JTX6.js.map} +1 -1
  78. package/package.json +7 -3
  79. package/dist/asset-TS7J5GA5.js +0 -2
  80. package/dist/chunk-73RMWG53.js +0 -2
  81. package/dist/chunk-73RMWG53.js.map +0 -1
  82. package/dist/chunk-HAQICAOQ.js +0 -2
  83. package/dist/chunk-HAQICAOQ.js.map +0 -1
  84. package/dist/chunk-LL2Z4TD3.js +0 -2
  85. package/dist/chunk-LL2Z4TD3.js.map +0 -1
  86. package/dist/chunk-MC6VZLKC.js +0 -2
  87. package/dist/chunk-MC6VZLKC.js.map +0 -1
  88. package/dist/chunk-N52AF6GE.js +0 -2
  89. package/dist/chunk-N52AF6GE.js.map +0 -1
  90. package/dist/chunk-V6R4AWQS.js +0 -2
  91. package/dist/chunk-V6R4AWQS.js.map +0 -1
  92. package/dist/chunk-WXMCINGZ.js +0 -2
  93. package/dist/chunk-WXMCINGZ.js.map +0 -1
  94. package/dist/chunk-WZ533Y4O.js +0 -2
  95. package/dist/chunk-WZ533Y4O.js.map +0 -1
  96. package/dist/chunk-YA4ACPM5.js +0 -2
  97. package/dist/chunk-YA4ACPM5.js.map +0 -1
  98. package/dist/chunk-ZEZ6TRCO.js +0 -2
  99. package/dist/chunk-ZEZ6TRCO.js.map +0 -1
  100. package/dist/datetime-NQTYGZIC.js +0 -2
  101. package/dist/editor-YIRZQR3D.js +0 -2
  102. package/dist/logo-GCKKZP62.js +0 -3
  103. package/dist/logo-GCKKZP62.js.map +0 -1
  104. package/dist/search-UFFZBRQV.js +0 -2
  105. package/dist/search-UFFZBRQV.js.map +0 -1
  106. package/dist/select-A6I27JQU.js +0 -2
  107. package/dist/switch-AFZGXSR3.js +0 -2
  108. package/dist/text-2RL2L5FT.js +0 -2
  109. package/dist/text-6F4QTCNT.js +0 -2
  110. package/dist/textarea-2A4TLWZS.js +0 -2
@@ -0,0 +1,616 @@
1
+ // src/tools/actor.ts
2
+ function isStringArray(value) {
3
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
4
+ }
5
+ function parseCustomerToolActor(value) {
6
+ if (!value || typeof value !== "object") {
7
+ return null;
8
+ }
9
+ const record = value;
10
+ if (record.kind === "chat") {
11
+ if (typeof record.userId !== "string" || !record.userId) {
12
+ return null;
13
+ }
14
+ if (typeof record.role !== "string" || !record.role) {
15
+ return null;
16
+ }
17
+ if (!isStringArray(record.spaceCodes)) {
18
+ return null;
19
+ }
20
+ return {
21
+ kind: "chat",
22
+ role: record.role,
23
+ spaceCodes: record.spaceCodes,
24
+ userId: record.userId
25
+ };
26
+ }
27
+ if (record.kind === "automation") {
28
+ if (typeof record.automationId !== "string" || !record.automationId) {
29
+ return null;
30
+ }
31
+ const spaceId = record.spaceId ?? null;
32
+ if (spaceId !== null && typeof spaceId !== "string") {
33
+ return null;
34
+ }
35
+ if (!isStringArray(record.spaceCodes)) {
36
+ return null;
37
+ }
38
+ return {
39
+ automationId: record.automationId,
40
+ kind: "automation",
41
+ spaceCodes: record.spaceCodes,
42
+ spaceId
43
+ };
44
+ }
45
+ return null;
46
+ }
47
+
48
+ // src/tools/tool-names.ts
49
+ var CUSTOM_TOOL_NAME_PREFIX = "custom_";
50
+ var TOOL_NAME_PATTERN = /^[a-z][a-zA-Z0-9_]*$/;
51
+ function isValidToolName(name) {
52
+ return TOOL_NAME_PATTERN.test(name) && !name.startsWith(CUSTOM_TOOL_NAME_PREFIX);
53
+ }
54
+ function toAgentToolName(name) {
55
+ return `${CUSTOM_TOOL_NAME_PREFIX}${name}`;
56
+ }
57
+
58
+ // src/tools/define-tool.ts
59
+ import { z } from "zod";
60
+
61
+ // src/tools/json-schema.ts
62
+ function stripJsonSchemaMeta(value) {
63
+ if (Array.isArray(value)) {
64
+ return value.map((entry) => stripJsonSchemaMeta(entry));
65
+ }
66
+ if (!value || typeof value !== "object") {
67
+ return value;
68
+ }
69
+ const result = {};
70
+ for (const [key, nested] of Object.entries(value)) {
71
+ if (key === "$schema") {
72
+ continue;
73
+ }
74
+ result[key] = stripJsonSchemaMeta(nested);
75
+ }
76
+ return result;
77
+ }
78
+
79
+ // src/tools/define-tool.ts
80
+ function defineTool(options) {
81
+ if (!isValidToolName(options.name)) {
82
+ throw new Error(
83
+ `Invalid custom tool name "${options.name}". Use a camelCase identifier that does not start with \`custom_\`.`
84
+ );
85
+ }
86
+ if (!options.description.trim()) {
87
+ throw new Error(`Custom tool "${options.name}" must have a description.`);
88
+ }
89
+ return {
90
+ description: options.description.trim(),
91
+ execute: (input, actor) => options.execute(input, actor),
92
+ input: options.input,
93
+ name: options.name
94
+ };
95
+ }
96
+ function toolToManifest(tool) {
97
+ const inputSchema = stripJsonSchemaMeta(
98
+ z.toJSONSchema(tool.input, { target: "draft-07" })
99
+ );
100
+ return {
101
+ description: tool.description,
102
+ inputSchema,
103
+ name: tool.name
104
+ };
105
+ }
106
+
107
+ // src/tools/hmac.ts
108
+ var FLEXKIT_TIMESTAMP_HEADER = "flexkit-timestamp";
109
+ var FLEXKIT_SIGNATURE_HEADER = "flexkit-signature";
110
+ var FLEXKIT_SIGNATURE_PREVIOUS_HEADER = "flexkit-signature-previous";
111
+ var CUSTOMER_TOOLS_SERVE_PATH = "/api/flexkit/tools";
112
+ var CUSTOMER_TOOLS_HELLO_PATH = "/tools/dev-connect/hello";
113
+ var CUSTOMER_TOOLS_POLL_PATH = "/tools/dev-connect/poll";
114
+ var CUSTOMER_TOOLS_RESPOND_PATH = "/tools/dev-connect/respond";
115
+ var DEFAULT_MAX_SKEW_SECONDS = 300;
116
+ function getSubtleCrypto() {
117
+ const subtle = globalThis.crypto?.subtle;
118
+ if (!subtle) {
119
+ throw new Error("Web Crypto is not available.");
120
+ }
121
+ return subtle;
122
+ }
123
+ function encodeUtf8(value) {
124
+ return new TextEncoder().encode(value);
125
+ }
126
+ function toBase64Url(bytes) {
127
+ const binary = String.fromCharCode(...new Uint8Array(bytes));
128
+ return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
129
+ }
130
+ function timingSafeEqualBytes(left, right) {
131
+ if (left.length !== right.length) {
132
+ return false;
133
+ }
134
+ let diff = 0;
135
+ for (let index = 0; index < left.length; index += 1) {
136
+ diff |= (left[index] ?? 0) ^ (right[index] ?? 0);
137
+ }
138
+ return diff === 0;
139
+ }
140
+ function getUnixTimestampSeconds() {
141
+ return Math.floor(Date.now() / 1e3);
142
+ }
143
+ function buildSignaturePayload({
144
+ body,
145
+ method,
146
+ path,
147
+ timestamp
148
+ }) {
149
+ return `${timestamp.toString()}.${method.toUpperCase()}.${path}.${body}`;
150
+ }
151
+ async function signPayload(secret, payload) {
152
+ const key = await getSubtleCrypto().importKey(
153
+ "raw",
154
+ encodeUtf8(secret),
155
+ { hash: "SHA-256", name: "HMAC" },
156
+ false,
157
+ ["sign"]
158
+ );
159
+ const signature = await getSubtleCrypto().sign("HMAC", key, encodeUtf8(payload));
160
+ return `v1=${toBase64Url(signature)}`;
161
+ }
162
+ async function createSignedHeaders({
163
+ body,
164
+ method,
165
+ path,
166
+ previousSecret,
167
+ secret,
168
+ timestamp = getUnixTimestampSeconds()
169
+ }) {
170
+ const payload = buildSignaturePayload({ body, method, path, timestamp });
171
+ const headers = {
172
+ "Flexkit-Timestamp": timestamp.toString(),
173
+ "Flexkit-Signature": await signPayload(secret, payload)
174
+ };
175
+ if (previousSecret) {
176
+ headers["Flexkit-Signature-Previous"] = await signPayload(previousSecret, payload);
177
+ }
178
+ return headers;
179
+ }
180
+ function uniqueSecrets(secret) {
181
+ const values = Array.isArray(secret) ? secret : [secret];
182
+ const unique = [];
183
+ for (const value of values) {
184
+ const trimmed = value.trim();
185
+ if (trimmed && !unique.includes(trimmed)) {
186
+ unique.push(trimmed);
187
+ }
188
+ }
189
+ return unique;
190
+ }
191
+ function parseSignatureHeader(value) {
192
+ const trimmed = value.trim();
193
+ if (trimmed.startsWith("v1=")) {
194
+ return trimmed.slice(3);
195
+ }
196
+ return null;
197
+ }
198
+ async function verifySignedHeaders({
199
+ body,
200
+ maxSkewSeconds = DEFAULT_MAX_SKEW_SECONDS,
201
+ method,
202
+ path,
203
+ previousSignatureHeader,
204
+ secret,
205
+ signatureHeader,
206
+ timestampHeader
207
+ }) {
208
+ const secrets = uniqueSecrets(secret);
209
+ const signatureHeaders = [signatureHeader, previousSignatureHeader ?? null].filter(
210
+ (value) => Boolean(value)
211
+ );
212
+ if (secrets.length === 0 || signatureHeaders.length === 0 || !timestampHeader) {
213
+ return false;
214
+ }
215
+ const timestamp = Number(timestampHeader);
216
+ if (!Number.isInteger(timestamp)) {
217
+ return false;
218
+ }
219
+ const now = getUnixTimestampSeconds();
220
+ if (Math.abs(now - timestamp) > maxSkewSeconds) {
221
+ return false;
222
+ }
223
+ const payload = buildSignaturePayload({ body, method, path, timestamp });
224
+ for (const candidateSecret of secrets) {
225
+ const expected = await signPayload(candidateSecret, payload);
226
+ const expectedDigest = parseSignatureHeader(expected);
227
+ if (!expectedDigest) {
228
+ continue;
229
+ }
230
+ const expectedBytes = encodeUtf8(expectedDigest);
231
+ for (const header of signatureHeaders) {
232
+ const providedDigest = parseSignatureHeader(header);
233
+ if (!providedDigest) {
234
+ continue;
235
+ }
236
+ const providedBytes = encodeUtf8(providedDigest);
237
+ if (providedBytes.length !== expectedBytes.length) {
238
+ continue;
239
+ }
240
+ if (timingSafeEqualBytes(providedBytes, expectedBytes)) {
241
+ return true;
242
+ }
243
+ }
244
+ }
245
+ return false;
246
+ }
247
+ function getHeader(headers, name) {
248
+ return headers.get(name) ?? headers.get(name.toLowerCase());
249
+ }
250
+
251
+ // src/core/domains.ts
252
+ var defaultFlexkitDomain = "flexkit.io";
253
+ var allowedFlexkitDomains = {
254
+ "flexkit.io": true,
255
+ "flexkit.test": true
256
+ };
257
+ function getConfiguredFlexkitDomain() {
258
+ const configuredDomain = globalThis.process?.env?.FLEXKIT_DOMAIN?.trim();
259
+ if (!configuredDomain) {
260
+ return defaultFlexkitDomain;
261
+ }
262
+ if (allowedFlexkitDomains[configuredDomain]) {
263
+ return configuredDomain;
264
+ }
265
+ return defaultFlexkitDomain;
266
+ }
267
+ var flexkitDomain = getConfiguredFlexkitDomain();
268
+ var flexkitApiDomain = `api.${flexkitDomain}`;
269
+
270
+ // src/tools/dev-connect.ts
271
+ var CUSTOMER_TOOLS_ME_PATH = "/users/me";
272
+ var FLEXKIT_STUDIO_RUNTIME_HEADER = "Flexkit-Studio-Runtime";
273
+ var FLEXKIT_STUDIO_RUNTIME_LOCAL = "local";
274
+ var DEV_CONNECT_ROLES = {
275
+ developer: true,
276
+ owner: true
277
+ };
278
+ function jsonResult(status, body) {
279
+ return {
280
+ body,
281
+ headers: { "Content-Type": "application/json" },
282
+ status,
283
+ type: "json"
284
+ };
285
+ }
286
+ function shouldHandleDevConnectTick() {
287
+ const env = globalThis.process?.env ?? {};
288
+ if (env.FLEXKIT_TOOLS_DEV_CONNECT === "1") {
289
+ return true;
290
+ }
291
+ return env.NODE_ENV !== "production";
292
+ }
293
+ function isDevConnectRole(role) {
294
+ return Boolean(role && DEV_CONNECT_ROLES[role]);
295
+ }
296
+ function getProjectApiOrigin(projectId) {
297
+ return `https://${projectId}.${flexkitApiDomain}`;
298
+ }
299
+ function guessClientLabel(fallbackProjectId) {
300
+ const env = globalThis.process?.env ?? {};
301
+ const port = env.PORT ?? "3000";
302
+ return `${env.npm_package_name ?? fallbackProjectId}@localhost:${port}`;
303
+ }
304
+ function isCustomerToolsTickPath(pathname) {
305
+ return /^\/api\/flexkit\/[^/]+\/tools\/dev-connect\/tick$/.test(pathname);
306
+ }
307
+ function getCustomerToolsTickPath(projectId) {
308
+ return `/api/flexkit/${projectId}/tools/dev-connect/tick`;
309
+ }
310
+ async function sessionFetch({
311
+ body,
312
+ method,
313
+ path,
314
+ projectId,
315
+ sessionToken
316
+ }) {
317
+ const headers = {
318
+ Cookie: `sessionToken=${sessionToken}`
319
+ };
320
+ if (body) {
321
+ headers["Content-Type"] = "application/json";
322
+ }
323
+ return fetch(`${getProjectApiOrigin(projectId)}${path}`, {
324
+ body,
325
+ headers,
326
+ method
327
+ });
328
+ }
329
+ var tickInFlight = false;
330
+ var HELLO_INTERVAL_MS = 3e4;
331
+ var connectDecisions = {};
332
+ var helloStates = {};
333
+ function connectDecisionKey(projectId, sessionToken) {
334
+ return `${sessionToken}\0${projectId}`;
335
+ }
336
+ function forgetConnectDecision({ projectId, sessionToken }) {
337
+ const key = connectDecisionKey(projectId, sessionToken);
338
+ delete connectDecisions[key];
339
+ delete helloStates[key];
340
+ }
341
+ function rememberConnectDecision({
342
+ decision,
343
+ projectId,
344
+ sessionToken
345
+ }) {
346
+ connectDecisions[connectDecisionKey(projectId, sessionToken)] = decision;
347
+ }
348
+ function hashManifest(manifest) {
349
+ let hash = 2166136261;
350
+ for (let index = 0; index < manifest.length; index += 1) {
351
+ hash ^= manifest.charCodeAt(index);
352
+ hash = Math.imul(hash, 16777619);
353
+ }
354
+ return (hash >>> 0).toString(36);
355
+ }
356
+ function shouldSendHello(input) {
357
+ const state = helloStates[connectDecisionKey(input.projectId, input.sessionToken)];
358
+ if (!state || state.manifestHash !== input.manifestHash) {
359
+ return true;
360
+ }
361
+ return Date.now() - state.sentAt >= HELLO_INTERVAL_MS;
362
+ }
363
+ function rememberHello(input) {
364
+ helloStates[connectDecisionKey(input.projectId, input.sessionToken)] = {
365
+ manifestHash: input.manifestHash,
366
+ sentAt: Date.now()
367
+ };
368
+ }
369
+ function deniedDevConnectResult(status) {
370
+ if (status === 401) {
371
+ return jsonResult(401, { error: "Sign in to Studio to connect local tools." });
372
+ }
373
+ return jsonResult(403, { error: "Only owners and developers can connect local tools." });
374
+ }
375
+ async function getDevConnectDenial({
376
+ projectId,
377
+ sessionToken
378
+ }) {
379
+ const cached = connectDecisions[connectDecisionKey(projectId, sessionToken)];
380
+ if (cached === "allow") {
381
+ return null;
382
+ }
383
+ if (cached === 401 || cached === 403) {
384
+ return deniedDevConnectResult(cached);
385
+ }
386
+ let meResponse;
387
+ try {
388
+ meResponse = await sessionFetch({
389
+ method: "GET",
390
+ path: CUSTOMER_TOOLS_ME_PATH,
391
+ projectId,
392
+ sessionToken
393
+ });
394
+ } catch {
395
+ return jsonResult(503, { error: "Unable to verify Studio session for local tools." });
396
+ }
397
+ if (meResponse.status === 401 || meResponse.status === 403) {
398
+ rememberConnectDecision({ decision: meResponse.status, projectId, sessionToken });
399
+ return deniedDevConnectResult(meResponse.status);
400
+ }
401
+ if (!meResponse.ok) {
402
+ return jsonResult(503, { error: "Unable to verify Studio session for local tools." });
403
+ }
404
+ let payload = {};
405
+ try {
406
+ payload = await meResponse.json();
407
+ } catch {
408
+ return jsonResult(503, { error: "Unable to verify Studio session for local tools." });
409
+ }
410
+ const role = typeof payload.role === "string" ? payload.role : "";
411
+ if (isDevConnectRole(role)) {
412
+ rememberConnectDecision({ decision: "allow", projectId, sessionToken });
413
+ return null;
414
+ }
415
+ rememberConnectDecision({ decision: 403, projectId, sessionToken });
416
+ return deniedDevConnectResult(403);
417
+ }
418
+ async function executeJob({
419
+ actor,
420
+ job,
421
+ projectId,
422
+ sessionToken,
423
+ tools
424
+ }) {
425
+ const tool = tools.find((candidate) => candidate.name === job.name);
426
+ let body;
427
+ if (!tool) {
428
+ body = JSON.stringify({ error: `Unknown tool "${job.name}".`, jobId: job.id });
429
+ } else {
430
+ const parsed = tool.input.safeParse(job.arguments ?? {});
431
+ if (!parsed.success) {
432
+ body = JSON.stringify({
433
+ error: parsed.error.issues.map((issue) => issue.message).join(", "),
434
+ jobId: job.id
435
+ });
436
+ } else {
437
+ try {
438
+ const result = await tool.execute(parsed.data, actor);
439
+ body = JSON.stringify({ jobId: job.id, result });
440
+ } catch (error) {
441
+ body = JSON.stringify({
442
+ error: error instanceof Error ? error.message : "Tool execution failed.",
443
+ jobId: job.id
444
+ });
445
+ }
446
+ }
447
+ }
448
+ await sessionFetch({
449
+ body,
450
+ method: "POST",
451
+ path: CUSTOMER_TOOLS_RESPOND_PATH,
452
+ projectId,
453
+ sessionToken
454
+ });
455
+ }
456
+ async function handleDevConnectTick({
457
+ projectId,
458
+ sessionToken,
459
+ skills,
460
+ tools
461
+ }) {
462
+ if (!shouldHandleDevConnectTick()) {
463
+ return jsonResult(204, { ok: true });
464
+ }
465
+ if (!sessionToken) {
466
+ return jsonResult(401, { error: "Sign in to Studio to connect local tools." });
467
+ }
468
+ if (!projectId || tools.length === 0 && skills === void 0) {
469
+ return jsonResult(204, { ok: true });
470
+ }
471
+ const denial = await getDevConnectDenial({ projectId, sessionToken });
472
+ if (denial) {
473
+ return denial;
474
+ }
475
+ if (tickInFlight) {
476
+ return jsonResult(204, { ok: true });
477
+ }
478
+ tickInFlight = true;
479
+ const helloBody = JSON.stringify({
480
+ clientLabel: guessClientLabel(projectId),
481
+ ...skills === void 0 ? {} : { skills },
482
+ tools: tools.map((tool) => toolToManifest(tool))
483
+ });
484
+ const manifestHash = hashManifest(helloBody);
485
+ try {
486
+ if (shouldSendHello({ manifestHash, projectId, sessionToken })) {
487
+ const helloResponse = await sessionFetch({
488
+ body: helloBody,
489
+ method: "POST",
490
+ path: CUSTOMER_TOOLS_HELLO_PATH,
491
+ projectId,
492
+ sessionToken
493
+ });
494
+ if (helloResponse.status === 401 || helloResponse.status === 403) {
495
+ forgetConnectDecision({ projectId, sessionToken });
496
+ return jsonResult(helloResponse.status, { error: "Unable to register the local tools runtime." });
497
+ }
498
+ if (!helloResponse.ok) {
499
+ return jsonResult(helloResponse.status, { error: "Unable to register the local tools runtime." });
500
+ }
501
+ rememberHello({ manifestHash, projectId, sessionToken });
502
+ }
503
+ const pollResponse = await sessionFetch({
504
+ method: "GET",
505
+ path: CUSTOMER_TOOLS_POLL_PATH,
506
+ projectId,
507
+ sessionToken
508
+ });
509
+ if (pollResponse.status === 401 || pollResponse.status === 403) {
510
+ forgetConnectDecision({ projectId, sessionToken });
511
+ return jsonResult(pollResponse.status, { error: "Unable to poll for local tool jobs." });
512
+ }
513
+ if (pollResponse.status !== 200) {
514
+ return jsonResult(pollResponse.status, { error: "Unable to poll for local tool jobs." });
515
+ }
516
+ const { job } = await pollResponse.json();
517
+ if (!job) {
518
+ return jsonResult(200, { job: null });
519
+ }
520
+ const actor = parseCustomerToolActor(job.actor);
521
+ if (!actor) {
522
+ await sessionFetch({
523
+ body: JSON.stringify({ error: "A valid Flexkit actor is required.", jobId: job.id }),
524
+ method: "POST",
525
+ path: CUSTOMER_TOOLS_RESPOND_PATH,
526
+ projectId,
527
+ sessionToken
528
+ });
529
+ return jsonResult(200, { job: job.id });
530
+ }
531
+ await executeJob({ actor, job, projectId, sessionToken, tools });
532
+ return jsonResult(200, { job: job.id });
533
+ } catch {
534
+ return jsonResult(503, { error: "Flexkit is not reachable from this local runtime." });
535
+ } finally {
536
+ tickInFlight = false;
537
+ }
538
+ }
539
+
540
+ // src/tools/define-skill.ts
541
+ var MAX_SKILL_NAME_LENGTH = 120;
542
+ var MAX_SKILL_DESCRIPTION_LENGTH = 500;
543
+ var MAX_SKILL_CONTENT_LENGTH = 5e4;
544
+ function requireValue(value, field) {
545
+ const trimmed = value.trim();
546
+ if (!trimmed) {
547
+ throw new Error(`Custom skill ${field} is required.`);
548
+ }
549
+ return trimmed;
550
+ }
551
+ function defineSkill(options) {
552
+ const name = requireValue(options.name, "name");
553
+ const description = requireValue(options.description, `"${name}" description`);
554
+ const content = requireValue(options.content, `"${name}" content`);
555
+ const space = options.space === void 0 ? void 0 : requireValue(options.space, `"${name}" space`);
556
+ if (name.length > MAX_SKILL_NAME_LENGTH) {
557
+ throw new Error(`Custom skill name must be at most ${String(MAX_SKILL_NAME_LENGTH)} characters.`);
558
+ }
559
+ if (description.length > MAX_SKILL_DESCRIPTION_LENGTH) {
560
+ throw new Error(
561
+ `Custom skill "${name}" description must be at most ${String(MAX_SKILL_DESCRIPTION_LENGTH)} characters.`
562
+ );
563
+ }
564
+ if (content.length > MAX_SKILL_CONTENT_LENGTH) {
565
+ throw new Error(`Custom skill "${name}" content must be at most ${String(MAX_SKILL_CONTENT_LENGTH)} characters.`);
566
+ }
567
+ return {
568
+ content,
569
+ description,
570
+ name,
571
+ ...space === void 0 ? {} : { space }
572
+ };
573
+ }
574
+ function assertUniqueSkillNames(skills) {
575
+ const names = /* @__PURE__ */ new Set();
576
+ for (const skill of skills) {
577
+ if (names.has(skill.name)) {
578
+ throw new Error(`Duplicate custom skill name "${skill.name}".`);
579
+ }
580
+ names.add(skill.name);
581
+ }
582
+ }
583
+
584
+ export {
585
+ flexkitDomain,
586
+ flexkitApiDomain,
587
+ parseCustomerToolActor,
588
+ CUSTOM_TOOL_NAME_PREFIX,
589
+ isValidToolName,
590
+ toAgentToolName,
591
+ defineTool,
592
+ toolToManifest,
593
+ FLEXKIT_TIMESTAMP_HEADER,
594
+ FLEXKIT_SIGNATURE_HEADER,
595
+ FLEXKIT_SIGNATURE_PREVIOUS_HEADER,
596
+ CUSTOMER_TOOLS_SERVE_PATH,
597
+ CUSTOMER_TOOLS_HELLO_PATH,
598
+ CUSTOMER_TOOLS_POLL_PATH,
599
+ CUSTOMER_TOOLS_RESPOND_PATH,
600
+ signPayload,
601
+ createSignedHeaders,
602
+ verifySignedHeaders,
603
+ getHeader,
604
+ FLEXKIT_STUDIO_RUNTIME_HEADER,
605
+ FLEXKIT_STUDIO_RUNTIME_LOCAL,
606
+ shouldHandleDevConnectTick,
607
+ isDevConnectRole,
608
+ isCustomerToolsTickPath,
609
+ getCustomerToolsTickPath,
610
+ handleDevConnectTick,
611
+ MAX_SKILL_NAME_LENGTH,
612
+ MAX_SKILL_DESCRIPTION_LENGTH,
613
+ MAX_SKILL_CONTENT_LENGTH,
614
+ defineSkill,
615
+ assertUniqueSkillNames
616
+ };
@@ -0,0 +1,2 @@
1
+ import'./index.css';import {j}from'./chunk-FVFRTCEB.js';import {useId}from'react';import {jsxs,jsx}from'react/jsx-runtime';function c({scope:e,checked:d,disabled:n,onChange:t}){let l=useId();return !e||e==="default"?null:jsxs("div",{className:"fk:flex fk:items-center fk:space-x-2",children:[jsx(j,{checked:d,disabled:n,id:l,onCheckedChange:t}),jsx("label",{className:"fk:text-xs fk:font-normal fk:leading-none fk:peer-disabled:cursor-not-allowed fk:peer-disabled:opacity-70",htmlFor:l,children:"Use default value"})]})}export{c as a};//# sourceMappingURL=chunk-VUAFNQK7.js.map
2
+ //# sourceMappingURL=chunk-VUAFNQK7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/form/fields/default-value-switch.tsx"],"names":["DefaultValueSwitch","scope","checked","disabled","onChange","elementId","useId","jsxs","jsx","Checkbox"],"mappings":"2HAWO,SAASA,CAAAA,CAAmB,CAAE,KAAA,CAAAC,EAAO,OAAA,CAAAC,CAAAA,CAAS,QAAA,CAAAC,CAAAA,CAAU,QAAA,CAAAC,CAAS,CAAA,CAA8B,CACpG,IAAMC,CAAAA,CAAYC,KAAAA,EAAM,CAExB,OAAI,CAACL,GAASA,CAAAA,GAAU,SAAA,CACf,IAAA,CAIPM,IAAAA,CAAC,KAAA,CAAA,CAAI,SAAA,CAAU,sCAAA,CACb,QAAA,CAAA,CAAAC,GAAAA,CAACC,CAAAA,CAAA,CAAS,OAAA,CAASP,CAAAA,CAAS,QAAA,CAAUC,CAAAA,CAAU,EAAA,CAAIE,CAAAA,CAAW,eAAA,CAAiBD,CAAAA,CAAwB,CAAA,CACxGI,GAAAA,CAAC,OAAA,CAAA,CACC,SAAA,CAAU,2GAAA,CACV,OAAA,CAASH,CAAAA,CACV,QAAA,CAAA,mBAAA,CAED,CAAA,CAAA,CACF,CAEJ","file":"chunk-VUAFNQK7.js","sourcesContent":["import { useId } from 'react';\nimport type { JSX } from 'react';\nimport { Checkbox } from '../../ui/primitives/checkbox';\n\ntype Props = {\n scope: string | undefined;\n checked: boolean;\n disabled?: boolean;\n onChange: (checked: boolean) => void;\n};\n\nexport function DefaultValueSwitch({ scope, checked, disabled, onChange }: Props): JSX.Element | null {\n const elementId = useId();\n\n if (!scope || scope === 'default') {\n return null;\n }\n\n return (\n <div className=\"fk:flex fk:items-center fk:space-x-2\">\n <Checkbox checked={checked} disabled={disabled} id={elementId} onCheckedChange={onChange as () => void} />\n <label\n className=\"fk:text-xs fk:font-normal fk:leading-none fk:peer-disabled:cursor-not-allowed fk:peer-disabled:opacity-70\"\n htmlFor={elementId}\n >\n Use default value\n </label>\n </div>\n );\n}\n"]}
@@ -0,0 +1,2 @@
1
+ import'./index.css';import {c}from'./chunk-KHPTVIYT.js';import {a}from'./chunk-O2QMYUPS.js';import {Slot}from'radix-ui';import {cva}from'class-variance-authority';import {jsx}from'react/jsx-runtime';var l=cva("fk:flex fk:w-fit fk:items-stretch fk:has-[>[data-slot=button-group]]:gap-2 fk:[&>*]:focus-visible:relative fk:[&>*]:focus-visible:z-10 fk:has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md fk:[&>[data-slot=select-trigger]:not([class*=w-])]:w-fit fk:[&>input]:flex-1",{variants:{orientation:{horizontal:"fk:[&>*:not(:first-child)]:rounded-l-none fk:[&>*:not(:first-child)]:border-l-0 fk:[&>*:not(:last-child)]:rounded-r-none",vertical:"fk:flex-col fk:[&>*:not(:first-child)]:rounded-t-none fk:[&>*:not(:first-child)]:border-t-0 fk:[&>*:not(:last-child)]:rounded-b-none"}},defaultVariants:{orientation:"horizontal"}});function k({className:o,orientation:t,...r}){return jsx("div",{role:"group","data-slot":"button-group","data-orientation":t,className:a(l({orientation:t}),o),...r})}function m({className:o,asChild:t=false,...r}){let i=t?Slot.Root:"div";return jsx(i,{className:a("fk:bg-muted fk:shadow-xs fk:flex fk:items-center fk:gap-2 fk:rounded-md fk:border fk:px-4 fk:text-sm fk:font-medium fk:[&_svg:not([class*=size-])]:size-4 fk:[&_svg]:pointer-events-none",o),...r})}function h({className:o,orientation:t="vertical",...r}){return jsx(c,{"data-slot":"button-group-separator",orientation:t,className:a("fk:bg-input fk:relative fk:!m-0 fk:self-stretch fk:data-[orientation=vertical]:h-auto",o),...r})}export{l as a,k as b,m as c,h as d};//# sourceMappingURL=chunk-XFY7MZJN.js.map
2
+ //# sourceMappingURL=chunk-XFY7MZJN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ui/primitives/button-group.tsx"],"names":["buttonGroupVariants","cva","ButtonGroup","className","orientation","props","jsx","cn","ButtonGroupText","asChild","Comp","Slot","ButtonGroupSeparator","Separator"],"mappings":"uMAMA,IAAMA,CAAAA,CAAsBC,GAAAA,CAC1B,2TAAA,CACA,CACE,QAAA,CAAU,CACR,WAAA,CAAa,CACX,UAAA,CACE,0HAAA,CACF,QAAA,CACE,sIACJ,CACF,CAAA,CACA,eAAA,CAAiB,CACf,WAAA,CAAa,YACf,CACF,CACF,EAEA,SAASC,CAAAA,CAAY,CACnB,SAAA,CAAAC,CAAAA,CACA,WAAA,CAAAC,CAAAA,CACA,GAAGC,CACL,CAAA,CAA2E,CACzE,OACEC,GAAAA,CAAC,KAAA,CAAA,CACC,IAAA,CAAK,OAAA,CACL,WAAA,CAAU,cAAA,CACV,kBAAA,CAAkBF,CAAAA,CAClB,SAAA,CAAWG,CAAAA,CAAGP,CAAAA,CAAoB,CAAE,WAAA,CAAAI,CAAY,CAAC,CAAA,CAAGD,CAAS,CAAA,CAC5D,GAAGE,CAAAA,CACN,CAEJ,CAEA,SAASG,CAAAA,CAAgB,CACvB,SAAA,CAAAL,CAAAA,CACA,OAAA,CAAAM,CAAAA,CAAU,KAAA,CACV,GAAGJ,CACL,CAAA,CAEG,CACD,IAAMK,CAAAA,CAAOD,CAAAA,CAAUE,IAAAA,CAAK,IAAA,CAAO,KAAA,CAEnC,OACEL,GAAAA,CAACI,CAAAA,CAAA,CACC,SAAA,CAAWH,CAAAA,CACT,0LAAA,CACAJ,CACF,CAAA,CACC,GAAGE,CAAAA,CACN,CAEJ,CAEA,SAASO,CAAAA,CAAqB,CAC5B,SAAA,CAAAT,CAAAA,CACA,WAAA,CAAAC,CAAAA,CAAc,UAAA,CACd,GAAGC,CACL,CAAA,CAA2C,CACzC,OACEC,GAAAA,CAACO,CAAAA,CAAA,CACC,WAAA,CAAU,wBAAA,CACV,WAAA,CAAaT,CAAAA,CACb,SAAA,CAAWG,CAAAA,CACT,uFAAA,CACAJ,CACF,CAAA,CACC,GAAGE,CAAAA,CACN,CAEJ","file":"chunk-XFY7MZJN.js","sourcesContent":["import * as React from 'react';\nimport { Slot } from 'radix-ui';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '../lib/utils';\nimport { Separator } from './separator';\n\nconst buttonGroupVariants = cva(\n 'fk:flex fk:w-fit fk:items-stretch fk:has-[>[data-slot=button-group]]:gap-2 fk:[&>*]:focus-visible:relative fk:[&>*]:focus-visible:z-10 fk:has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md fk:[&>[data-slot=select-trigger]:not([class*=w-])]:w-fit fk:[&>input]:flex-1',\n {\n variants: {\n orientation: {\n horizontal:\n 'fk:[&>*:not(:first-child)]:rounded-l-none fk:[&>*:not(:first-child)]:border-l-0 fk:[&>*:not(:last-child)]:rounded-r-none',\n vertical:\n 'fk:flex-col fk:[&>*:not(:first-child)]:rounded-t-none fk:[&>*:not(:first-child)]:border-t-0 fk:[&>*:not(:last-child)]:rounded-b-none',\n },\n },\n defaultVariants: {\n orientation: 'horizontal',\n },\n }\n);\n\nfunction ButtonGroup({\n className,\n orientation,\n ...props\n}: React.ComponentProps<'div'> & VariantProps<typeof buttonGroupVariants>) {\n return (\n <div\n role=\"group\"\n data-slot=\"button-group\"\n data-orientation={orientation}\n className={cn(buttonGroupVariants({ orientation }), className)}\n {...props}\n />\n );\n}\n\nfunction ButtonGroupText({\n className,\n asChild = false,\n ...props\n}: React.ComponentProps<'div'> & {\n asChild?: boolean;\n}) {\n const Comp = asChild ? Slot.Root : 'div';\n\n return (\n <Comp\n className={cn(\n 'fk:bg-muted fk:shadow-xs fk:flex fk:items-center fk:gap-2 fk:rounded-md fk:border fk:px-4 fk:text-sm fk:font-medium fk:[&_svg:not([class*=size-])]:size-4 fk:[&_svg]:pointer-events-none',\n className\n )}\n {...props}\n />\n );\n}\n\nfunction ButtonGroupSeparator({\n className,\n orientation = 'vertical',\n ...props\n}: React.ComponentProps<typeof Separator>) {\n return (\n <Separator\n data-slot=\"button-group-separator\"\n orientation={orientation}\n className={cn(\n 'fk:bg-input fk:relative fk:!m-0 fk:self-stretch fk:data-[orientation=vertical]:h-auto',\n className\n )}\n {...props}\n />\n );\n}\n\nexport { ButtonGroup, ButtonGroupSeparator, ButtonGroupText, buttonGroupVariants };\n"]}
@@ -1,9 +1,12 @@
1
+ import { F as FlexkitHandlerOptions } from './types-U28Ppixq.js';
2
+
1
3
  interface FlexkitHandlerContext {
2
4
  request: Request;
3
5
  pathname: string;
4
6
  search: string;
5
7
  sessionToken: string;
6
8
  contentType: string | null;
9
+ options?: FlexkitHandlerOptions;
7
10
  }
8
11
  type FlexkitHandlerHeaders = {
9
12
  [key: string]: string;
@@ -0,0 +1,2 @@
1
+ import'./index.css';export{a as DateTime}from'./chunk-AEBTHXEP.js';import'./chunk-F2ILIHAU.js';import'./chunk-YW2EEXMV.js';import'./chunk-O2QMYUPS.js';//# sourceMappingURL=datetime-DDEWTNLB.js.map
2
+ //# sourceMappingURL=datetime-DDEWTNLB.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"datetime-NQTYGZIC.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"datetime-DDEWTNLB.js"}
@@ -0,0 +1,2 @@
1
+ import'./index.css';export{b as Editor}from'./chunk-FPH4YETF.js';import'./chunk-F2ILIHAU.js';import'./chunk-YW2EEXMV.js';import'./chunk-O2QMYUPS.js';//# sourceMappingURL=editor-HAYTKNMY.js.map
2
+ //# sourceMappingURL=editor-HAYTKNMY.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"editor-YIRZQR3D.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"editor-HAYTKNMY.js"}