@kalphq/cli 0.0.0-dev-20260512203604 → 0.0.0-dev-20260513001041
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.
- package/dist/{chunk-6WQW3UVW.js → chunk-YG64BYWB.js} +36 -56
- package/dist/chunk-YG64BYWB.js.map +1 -0
- package/dist/chunk-YMEEYCMI.js +717 -0
- package/dist/chunk-YMEEYCMI.js.map +1 -0
- package/dist/{deploy-ABYREUMX.js → deploy-IKLEF4GU.js} +8 -5
- package/dist/deploy-IKLEF4GU.js.map +1 -0
- package/dist/{dev-Q3UX3HY2.js → dev-7T5MQVJD.js} +2 -2
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/{push-EXOBRO5V.js → push-3ZLFVIPP.js} +4 -3
- package/dist/push-3ZLFVIPP.js.map +1 -0
- package/dist/runtime-template/worker-entry.js +169 -3
- package/package.json +4 -4
- package/dist/chunk-6WQW3UVW.js.map +0 -1
- package/dist/chunk-WMQSBT64.js +0 -423
- package/dist/chunk-WMQSBT64.js.map +0 -1
- package/dist/deploy-ABYREUMX.js.map +0 -1
- package/dist/push-EXOBRO5V.js.map +0 -1
- /package/dist/{dev-Q3UX3HY2.js.map → dev-7T5MQVJD.js.map} +0 -0
|
@@ -0,0 +1,717 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/utils/secret.ts
|
|
4
|
+
import { randomBytes } from "crypto";
|
|
5
|
+
import { readFile, writeFile } from "fs/promises";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
var SECRET_KEY = "KALP_SECRET_KEY";
|
|
8
|
+
var STUDIO_PASSWORD = "KALP_STUDIO_PASSWORD";
|
|
9
|
+
var STUDIO_ADMIN_USER = "KALP_STUDIO_ADMIN_USER";
|
|
10
|
+
var SERVICE_KEY = "KALP_SERVICE_KEY";
|
|
11
|
+
function escapeRegExp(value) {
|
|
12
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
13
|
+
}
|
|
14
|
+
function parseEnv(content) {
|
|
15
|
+
const result = {};
|
|
16
|
+
for (const line of content.split(/\r?\n/g)) {
|
|
17
|
+
const trimmed = line.trim();
|
|
18
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
19
|
+
const eqIndex = trimmed.indexOf("=");
|
|
20
|
+
if (eqIndex <= 0) continue;
|
|
21
|
+
const key = trimmed.slice(0, eqIndex).trim();
|
|
22
|
+
const value = trimmed.slice(eqIndex + 1).trim();
|
|
23
|
+
result[key] = value;
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
function applyEnvUpdates(content, updates) {
|
|
28
|
+
let next = content;
|
|
29
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
30
|
+
const line = `${key}=${value}`;
|
|
31
|
+
const pattern = new RegExp(`^${escapeRegExp(key)}=.*$`, "m");
|
|
32
|
+
if (pattern.test(next)) {
|
|
33
|
+
next = next.replace(pattern, line);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const trimmed = next.trimEnd();
|
|
37
|
+
next = trimmed.length > 0 ? `${trimmed}
|
|
38
|
+
${line}
|
|
39
|
+
` : `${line}
|
|
40
|
+
`;
|
|
41
|
+
}
|
|
42
|
+
return next.trimEnd() + "\n";
|
|
43
|
+
}
|
|
44
|
+
async function readEnvFile(cwd) {
|
|
45
|
+
const envPath = join(cwd, ".env");
|
|
46
|
+
try {
|
|
47
|
+
return await readFile(envPath, "utf-8");
|
|
48
|
+
} catch {
|
|
49
|
+
return "";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function readDevVarsFile(cwd) {
|
|
53
|
+
const devVarsPath = join(cwd, ".dev.vars");
|
|
54
|
+
try {
|
|
55
|
+
return await readFile(devVarsPath, "utf-8");
|
|
56
|
+
} catch {
|
|
57
|
+
return "";
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function generateStudioPassword() {
|
|
61
|
+
return randomBytes(24).toString("base64url");
|
|
62
|
+
}
|
|
63
|
+
function generateServiceKey() {
|
|
64
|
+
return `kalp_sk_live_${randomBytes(32).toString("base64url")}`;
|
|
65
|
+
}
|
|
66
|
+
async function ensureStudioSecrets(cwd) {
|
|
67
|
+
const envPath = join(cwd, ".env");
|
|
68
|
+
const devVarsPath = join(cwd, ".dev.vars");
|
|
69
|
+
const content = await readEnvFile(cwd);
|
|
70
|
+
const parsed = parseEnv(content);
|
|
71
|
+
const key = parsed[SECRET_KEY]?.trim() || randomBytes(32).toString("hex");
|
|
72
|
+
const studioPassword = parsed[STUDIO_PASSWORD]?.trim() || generateStudioPassword();
|
|
73
|
+
const studioAdminUser = parsed[STUDIO_ADMIN_USER]?.trim() || "admin";
|
|
74
|
+
const serviceKey = parsed[SERVICE_KEY]?.trim() || generateServiceKey();
|
|
75
|
+
const isNew = !parsed[SECRET_KEY]?.trim() || !parsed[STUDIO_PASSWORD]?.trim() || !parsed[STUDIO_ADMIN_USER]?.trim() || !parsed[SERVICE_KEY]?.trim();
|
|
76
|
+
const next = applyEnvUpdates(content, {
|
|
77
|
+
[SECRET_KEY]: key,
|
|
78
|
+
[STUDIO_PASSWORD]: studioPassword,
|
|
79
|
+
[STUDIO_ADMIN_USER]: studioAdminUser,
|
|
80
|
+
[SERVICE_KEY]: serviceKey
|
|
81
|
+
});
|
|
82
|
+
await writeFile(envPath, next, "utf-8");
|
|
83
|
+
const devVarsContent = await readDevVarsFile(cwd);
|
|
84
|
+
const nextDevVars = applyEnvUpdates(devVarsContent, {
|
|
85
|
+
[SECRET_KEY]: key,
|
|
86
|
+
[STUDIO_PASSWORD]: studioPassword,
|
|
87
|
+
[STUDIO_ADMIN_USER]: studioAdminUser,
|
|
88
|
+
[SERVICE_KEY]: serviceKey
|
|
89
|
+
});
|
|
90
|
+
await writeFile(devVarsPath, nextDevVars, "utf-8");
|
|
91
|
+
return { key, studioPassword, studioAdminUser, serviceKey, isNew };
|
|
92
|
+
}
|
|
93
|
+
async function ensureSecretKey(cwd) {
|
|
94
|
+
const secrets = await ensureStudioSecrets(cwd);
|
|
95
|
+
return { key: secrets.key, isNew: secrets.isNew };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/utils/project-state.ts
|
|
99
|
+
import { mkdir, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
100
|
+
import { join as join2 } from "path";
|
|
101
|
+
var KALP_DIR = ".kalp";
|
|
102
|
+
var STATE_FILE = "state.json";
|
|
103
|
+
function normalizeProjectState(raw) {
|
|
104
|
+
if (!raw || typeof raw !== "object") return null;
|
|
105
|
+
const value = raw;
|
|
106
|
+
const workerUrl = typeof value.workerUrl === "string" && value.workerUrl.length > 0 ? value.workerUrl : null;
|
|
107
|
+
const deployedAt = typeof value.deployedAt === "string" && value.deployedAt.length > 0 ? value.deployedAt : null;
|
|
108
|
+
const accountId = typeof value.accountId === "string" && value.accountId.length > 0 ? value.accountId : null;
|
|
109
|
+
const studioCredentialsFingerprint = typeof value.studioCredentialsFingerprint === "string" && value.studioCredentialsFingerprint.length > 0 ? value.studioCredentialsFingerprint : null;
|
|
110
|
+
const serviceKeyFingerprint = typeof value.serviceKeyFingerprint === "string" && value.serviceKeyFingerprint.length > 0 ? value.serviceKeyFingerprint : null;
|
|
111
|
+
const agents = {};
|
|
112
|
+
const rawAgents = value.agents && typeof value.agents === "object" ? value.agents : {};
|
|
113
|
+
for (const [name, entry] of Object.entries(rawAgents)) {
|
|
114
|
+
if (!entry || typeof entry !== "object") continue;
|
|
115
|
+
const item = entry;
|
|
116
|
+
if (typeof item.localPath !== "string" || item.localPath.length === 0) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const currentVersion = typeof item.currentVersion === "number" && Number.isFinite(item.currentVersion) ? Math.max(0, Math.floor(item.currentVersion)) : 0;
|
|
120
|
+
agents[name] = {
|
|
121
|
+
currentHash: typeof item.currentHash === "string" ? item.currentHash : null,
|
|
122
|
+
currentVersion,
|
|
123
|
+
lastLocalHash: typeof item.lastLocalHash === "string" ? item.lastLocalHash : null,
|
|
124
|
+
lastRemoteHash: typeof item.lastRemoteHash === "string" ? item.lastRemoteHash : null,
|
|
125
|
+
lastPushedAt: typeof item.lastPushedAt === "string" ? item.lastPushedAt : null,
|
|
126
|
+
localPath: item.localPath,
|
|
127
|
+
workerUrl: typeof item.workerUrl === "string" ? item.workerUrl : null
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
workerUrl,
|
|
132
|
+
deployedAt,
|
|
133
|
+
accountId,
|
|
134
|
+
studioCredentialsFingerprint,
|
|
135
|
+
serviceKeyFingerprint,
|
|
136
|
+
agents
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
async function readProjectState(cwd) {
|
|
140
|
+
try {
|
|
141
|
+
const statePath = join2(cwd, KALP_DIR, STATE_FILE);
|
|
142
|
+
const content = await readFile2(statePath, "utf-8");
|
|
143
|
+
return normalizeProjectState(JSON.parse(content));
|
|
144
|
+
} catch {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
async function writeProjectState(cwd, state) {
|
|
149
|
+
const dir = join2(cwd, KALP_DIR);
|
|
150
|
+
await mkdir(dir, { recursive: true });
|
|
151
|
+
await writeFile2(join2(dir, STATE_FILE), `${JSON.stringify(state, null, 2)}
|
|
152
|
+
`, "utf-8");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/utils/runtime.ts
|
|
156
|
+
import { createHash } from "crypto";
|
|
157
|
+
import {
|
|
158
|
+
access as access3,
|
|
159
|
+
cp,
|
|
160
|
+
mkdir as mkdir2,
|
|
161
|
+
readdir,
|
|
162
|
+
readFile as readFile4,
|
|
163
|
+
rm,
|
|
164
|
+
stat,
|
|
165
|
+
writeFile as writeFile4
|
|
166
|
+
} from "fs/promises";
|
|
167
|
+
import { basename, dirname, join as join6, resolve as resolve2 } from "path";
|
|
168
|
+
import { fileURLToPath } from "url";
|
|
169
|
+
import { deriveLabelFromName } from "@kalphq/project";
|
|
170
|
+
|
|
171
|
+
// src/utils/ai.ts
|
|
172
|
+
import { access, readFile as readFile3 } from "fs/promises";
|
|
173
|
+
import { constants } from "fs";
|
|
174
|
+
import { join as join3 } from "path";
|
|
175
|
+
import { createJiti } from "jiti";
|
|
176
|
+
var PROVIDER_SECRET_MAP = {
|
|
177
|
+
openai: "OPENAI_API_KEY",
|
|
178
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
179
|
+
openrouter: "OPENROUTER_API_KEY",
|
|
180
|
+
custom: "CUSTOM_AI_API_KEY"
|
|
181
|
+
};
|
|
182
|
+
function parseEnv2(content) {
|
|
183
|
+
const env = {};
|
|
184
|
+
for (const raw of content.split(/\r?\n/g)) {
|
|
185
|
+
const line = raw.trim();
|
|
186
|
+
if (!line || line.startsWith("#")) continue;
|
|
187
|
+
const idx = line.indexOf("=");
|
|
188
|
+
if (idx <= 0) continue;
|
|
189
|
+
env[line.slice(0, idx).trim()] = line.slice(idx + 1);
|
|
190
|
+
}
|
|
191
|
+
return env;
|
|
192
|
+
}
|
|
193
|
+
async function resolveProviderFromConfig(cwd) {
|
|
194
|
+
const configPath = join3(cwd, "kalp.config.ts");
|
|
195
|
+
await access(configPath, constants.F_OK);
|
|
196
|
+
const jiti = createJiti(cwd, { interopDefault: true });
|
|
197
|
+
const config = await jiti.import(configPath);
|
|
198
|
+
const provider = config?.default?.ai?.provider ?? config?.ai?.provider ?? "openai";
|
|
199
|
+
return provider;
|
|
200
|
+
}
|
|
201
|
+
async function readDotEnv(cwd) {
|
|
202
|
+
const envPath = join3(cwd, ".env");
|
|
203
|
+
const content = await readFile3(envPath, "utf-8").catch(() => "");
|
|
204
|
+
return parseEnv2(content);
|
|
205
|
+
}
|
|
206
|
+
function getRequiredSecretForProvider(provider) {
|
|
207
|
+
return PROVIDER_SECRET_MAP[provider];
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/utils/project-config.ts
|
|
211
|
+
import { createJiti as createJiti2 } from "jiti";
|
|
212
|
+
import { access as access2 } from "fs/promises";
|
|
213
|
+
import { constants as constants2 } from "fs";
|
|
214
|
+
import { join as join4, resolve } from "path";
|
|
215
|
+
function normalizeStrategy(strategy) {
|
|
216
|
+
if (!strategy) return null;
|
|
217
|
+
if (strategy.type === "jwks") {
|
|
218
|
+
return {
|
|
219
|
+
type: "jwks",
|
|
220
|
+
jwksUrl: strategy.jwksUrl,
|
|
221
|
+
issuer: strategy.issuer,
|
|
222
|
+
audience: strategy.audience
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (strategy.type === "symmetric") {
|
|
226
|
+
return {
|
|
227
|
+
type: "symmetric",
|
|
228
|
+
secretEnvKey: strategy.secretEnvKey
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
type: "apiKey",
|
|
233
|
+
headerName: strategy.headerName,
|
|
234
|
+
envKey: strategy.envKey
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
async function loadProjectConfig(cwd) {
|
|
238
|
+
const configPath = resolve(join4(cwd, "kalp.config.ts"));
|
|
239
|
+
await access2(configPath, constants2.F_OK);
|
|
240
|
+
const jiti = createJiti2(cwd, { interopDefault: true });
|
|
241
|
+
const moduleValue = await jiti.import(configPath);
|
|
242
|
+
const raw = moduleValue && typeof moduleValue === "object" && "default" in moduleValue ? moduleValue.default ?? moduleValue : moduleValue;
|
|
243
|
+
return { path: configPath, raw };
|
|
244
|
+
}
|
|
245
|
+
function resolveRuntimeIdentityConfig(rawConfig) {
|
|
246
|
+
const identity = rawConfig.identity && typeof rawConfig.identity === "object" ? rawConfig.identity : void 0;
|
|
247
|
+
const enforceGlobalAuth = typeof rawConfig.enforceGlobalAuth === "boolean" ? rawConfig.enforceGlobalAuth : true;
|
|
248
|
+
return {
|
|
249
|
+
enforceGlobalAuth,
|
|
250
|
+
identityId: identity && typeof identity.id === "string" && identity.id.length > 0 ? identity.id : null,
|
|
251
|
+
strategy: normalizeStrategy(identity?.strategy)
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
function resolveIdentityAuthRequirements(identity) {
|
|
255
|
+
if (!identity.strategy) return [];
|
|
256
|
+
if (identity.strategy.type === "symmetric") {
|
|
257
|
+
return [
|
|
258
|
+
{
|
|
259
|
+
envKey: identity.strategy.secretEnvKey?.trim() || "JWT_SIGNING_SECRET",
|
|
260
|
+
reason: "symmetric JWT validation"
|
|
261
|
+
}
|
|
262
|
+
];
|
|
263
|
+
}
|
|
264
|
+
if (identity.strategy.type === "apiKey") {
|
|
265
|
+
const envKey = identity.strategy.envKey?.trim();
|
|
266
|
+
if (!envKey) {
|
|
267
|
+
return [
|
|
268
|
+
{
|
|
269
|
+
envKey: "KALP_API_KEY",
|
|
270
|
+
reason: "apiKey strategy (default env key)"
|
|
271
|
+
}
|
|
272
|
+
];
|
|
273
|
+
}
|
|
274
|
+
return [{ envKey, reason: "apiKey strategy" }];
|
|
275
|
+
}
|
|
276
|
+
return [];
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// src/utils/runtime-identity.ts
|
|
280
|
+
import { build } from "esbuild";
|
|
281
|
+
import { writeFile as writeFile3 } from "fs/promises";
|
|
282
|
+
import { join as join5 } from "path";
|
|
283
|
+
import { pathToFileURL } from "url";
|
|
284
|
+
var NODE_BUILTIN_IMPORTS = /* @__PURE__ */ new Set([
|
|
285
|
+
"fs",
|
|
286
|
+
"path",
|
|
287
|
+
"crypto",
|
|
288
|
+
"os",
|
|
289
|
+
"child_process",
|
|
290
|
+
"worker_threads",
|
|
291
|
+
"net",
|
|
292
|
+
"tls",
|
|
293
|
+
"http",
|
|
294
|
+
"https",
|
|
295
|
+
"zlib",
|
|
296
|
+
"stream",
|
|
297
|
+
"url",
|
|
298
|
+
"process",
|
|
299
|
+
"buffer"
|
|
300
|
+
]);
|
|
301
|
+
var DEFAULT_IDENTITY_MAP_SOURCE = `export default function mapIdentity(payload) {
|
|
302
|
+
const sub =
|
|
303
|
+
payload && typeof payload === "object" && typeof payload.sub === "string"
|
|
304
|
+
? payload.sub
|
|
305
|
+
: "anonymous";
|
|
306
|
+
return { userId: sub, claims: {} };
|
|
307
|
+
}
|
|
308
|
+
`;
|
|
309
|
+
function readIdentityMapCandidate(rawConfig) {
|
|
310
|
+
if (!rawConfig.identity || typeof rawConfig.identity !== "object") return null;
|
|
311
|
+
return rawConfig.identity.mapIdentity;
|
|
312
|
+
}
|
|
313
|
+
async function writeDefaultIdentityMap(identityMapPath) {
|
|
314
|
+
await writeFile3(identityMapPath, DEFAULT_IDENTITY_MAP_SOURCE, "utf-8");
|
|
315
|
+
}
|
|
316
|
+
async function bundleIdentityMap(params) {
|
|
317
|
+
const { cwd, configPath, identityMapPath } = params;
|
|
318
|
+
const configSpecifier = pathToFileURL(configPath).href;
|
|
319
|
+
const entrySource = `
|
|
320
|
+
import configModule from ${JSON.stringify(configSpecifier)};
|
|
321
|
+
const config = (configModule && typeof configModule === "object" && "default" in configModule)
|
|
322
|
+
? (configModule.default ?? configModule)
|
|
323
|
+
: configModule;
|
|
324
|
+
|
|
325
|
+
const mapper = config?.identity?.mapIdentity;
|
|
326
|
+
if (typeof mapper !== "function") {
|
|
327
|
+
throw new Error("identity.mapIdentity must be a function.");
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export default mapper;
|
|
331
|
+
`;
|
|
332
|
+
try {
|
|
333
|
+
await build({
|
|
334
|
+
absWorkingDir: cwd,
|
|
335
|
+
bundle: true,
|
|
336
|
+
format: "esm",
|
|
337
|
+
platform: "browser",
|
|
338
|
+
target: ["es2022"],
|
|
339
|
+
write: true,
|
|
340
|
+
outfile: identityMapPath,
|
|
341
|
+
logLevel: "silent",
|
|
342
|
+
plugins: [
|
|
343
|
+
{
|
|
344
|
+
name: "kalp-edge-identity-guard",
|
|
345
|
+
setup(buildContext) {
|
|
346
|
+
buildContext.onResolve({ filter: /.*/ }, (args) => {
|
|
347
|
+
const raw = args.path.startsWith("node:") ? args.path.slice(5) : args.path;
|
|
348
|
+
if (NODE_BUILTIN_IMPORTS.has(raw)) {
|
|
349
|
+
return {
|
|
350
|
+
errors: [
|
|
351
|
+
{
|
|
352
|
+
text: `Node builtin "${args.path}" is not supported in identity.mapIdentity for edge runtime.`
|
|
353
|
+
}
|
|
354
|
+
]
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
return null;
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
],
|
|
362
|
+
stdin: {
|
|
363
|
+
contents: entrySource,
|
|
364
|
+
resolveDir: cwd,
|
|
365
|
+
sourcefile: "kalp-identity-map-entry.mjs",
|
|
366
|
+
loader: "js"
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
} catch (error) {
|
|
370
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
371
|
+
throw new Error(
|
|
372
|
+
`Could not bundle identity.mapIdentity for runtime. Ensure mapIdentity is edge-safe (no Node built-ins). ${message}`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
async function materializeRuntimeIdentity(params) {
|
|
377
|
+
const { cwd, runtimeDir } = params;
|
|
378
|
+
const identityConfigPath = join5(runtimeDir, "identity.config.json");
|
|
379
|
+
const identityMapPath = join5(runtimeDir, "identity.map.mjs");
|
|
380
|
+
let rawConfig = {};
|
|
381
|
+
let configPath = null;
|
|
382
|
+
try {
|
|
383
|
+
const loaded = await loadProjectConfig(cwd);
|
|
384
|
+
rawConfig = loaded.raw;
|
|
385
|
+
configPath = loaded.path;
|
|
386
|
+
} catch {
|
|
387
|
+
rawConfig = {};
|
|
388
|
+
configPath = null;
|
|
389
|
+
}
|
|
390
|
+
const identityConfig = resolveRuntimeIdentityConfig(rawConfig);
|
|
391
|
+
await writeFile3(identityConfigPath, `${JSON.stringify(identityConfig, null, 2)}
|
|
392
|
+
`, "utf-8");
|
|
393
|
+
const mapIdentity = readIdentityMapCandidate(rawConfig);
|
|
394
|
+
if (typeof mapIdentity === "function" && configPath) {
|
|
395
|
+
await bundleIdentityMap({
|
|
396
|
+
cwd,
|
|
397
|
+
configPath,
|
|
398
|
+
identityMapPath
|
|
399
|
+
});
|
|
400
|
+
} else {
|
|
401
|
+
await writeDefaultIdentityMap(identityMapPath);
|
|
402
|
+
}
|
|
403
|
+
return { identityConfig, identityConfigPath, identityMapPath };
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// src/utils/runtime.ts
|
|
407
|
+
var RUNTIME_ROOT = ".kalp";
|
|
408
|
+
var RUNTIME_DIR = "runtime";
|
|
409
|
+
var STUDIO_DIR = "studio";
|
|
410
|
+
var WRANGLER_CONFIG_FILE = "wrangler.jsonc";
|
|
411
|
+
var WORKER_ENTRY_FILE = "worker-entry.js";
|
|
412
|
+
var COMPATIBILITY_DATE = "2026-05-10";
|
|
413
|
+
function sanitizeSegment(input) {
|
|
414
|
+
return input.toLowerCase().replace(/^@/, "").replace(/\//g, "-").replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
415
|
+
}
|
|
416
|
+
async function resolveProjectSlug(cwd) {
|
|
417
|
+
const fallback = sanitizeSegment(basename(cwd)) || "agent";
|
|
418
|
+
const packageJsonPath = join6(cwd, "package.json");
|
|
419
|
+
try {
|
|
420
|
+
const content = await readFile4(packageJsonPath, "utf-8");
|
|
421
|
+
const pkg = JSON.parse(content);
|
|
422
|
+
const name = typeof pkg.name === "string" ? pkg.name : "";
|
|
423
|
+
const sanitized = sanitizeSegment(name);
|
|
424
|
+
return sanitized || fallback;
|
|
425
|
+
} catch {
|
|
426
|
+
return fallback;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
function buildWorkerName(slug, cwd) {
|
|
430
|
+
const cwdHash = createHash("sha1").update(cwd).digest("hex").slice(0, 8);
|
|
431
|
+
const withPrefix = `kalp-${slug}-${cwdHash}`;
|
|
432
|
+
const maxLen = 63;
|
|
433
|
+
if (withPrefix.length <= maxLen) {
|
|
434
|
+
return withPrefix;
|
|
435
|
+
}
|
|
436
|
+
const clipped = withPrefix.slice(0, maxLen).replace(/-+$/g, "");
|
|
437
|
+
return clipped || `kalp-${cwdHash}`;
|
|
438
|
+
}
|
|
439
|
+
function createRuntimeConfig(workerName, mode, requiredSecrets) {
|
|
440
|
+
return {
|
|
441
|
+
$schema: "node_modules/wrangler/config-schema.json",
|
|
442
|
+
name: workerName,
|
|
443
|
+
main: `./${WORKER_ENTRY_FILE}`,
|
|
444
|
+
compatibility_date: COMPATIBILITY_DATE,
|
|
445
|
+
compatibility_flags: ["nodejs_compat"],
|
|
446
|
+
migrations: [
|
|
447
|
+
{
|
|
448
|
+
tag: "v1",
|
|
449
|
+
new_sqlite_classes: ["AgentDurableObject"]
|
|
450
|
+
}
|
|
451
|
+
],
|
|
452
|
+
durable_objects: {
|
|
453
|
+
bindings: [
|
|
454
|
+
{
|
|
455
|
+
name: "KALP_RUNTIME_CLOUDFLARE",
|
|
456
|
+
class_name: "AgentDurableObject"
|
|
457
|
+
}
|
|
458
|
+
]
|
|
459
|
+
},
|
|
460
|
+
kv_namespaces: [
|
|
461
|
+
{
|
|
462
|
+
binding: "KALP_MANIFESTS"
|
|
463
|
+
}
|
|
464
|
+
],
|
|
465
|
+
assets: {
|
|
466
|
+
directory: `./${STUDIO_DIR}`,
|
|
467
|
+
binding: "ASSETS",
|
|
468
|
+
run_worker_first: true
|
|
469
|
+
},
|
|
470
|
+
observability: { enabled: true },
|
|
471
|
+
upload_source_maps: true,
|
|
472
|
+
vars: {
|
|
473
|
+
KALP_ENV: mode
|
|
474
|
+
},
|
|
475
|
+
secrets: {
|
|
476
|
+
required: requiredSecrets
|
|
477
|
+
}
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
function runtimeTemplateCandidates() {
|
|
481
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
482
|
+
const distTemplateRoot = resolve2(here, "runtime-template");
|
|
483
|
+
const packageRootTemplate = resolve2(here, "..", "runtime-template");
|
|
484
|
+
const sourceTemplateRoot = resolve2(here, "..", "..", "runtime-template");
|
|
485
|
+
const monorepoStudioDist = resolve2(
|
|
486
|
+
here,
|
|
487
|
+
"..",
|
|
488
|
+
"..",
|
|
489
|
+
"..",
|
|
490
|
+
"..",
|
|
491
|
+
"apps",
|
|
492
|
+
"studio",
|
|
493
|
+
"dist",
|
|
494
|
+
"client"
|
|
495
|
+
);
|
|
496
|
+
return [
|
|
497
|
+
{
|
|
498
|
+
studioTemplateDir: join6(distTemplateRoot, STUDIO_DIR),
|
|
499
|
+
workerEntryPath: join6(distTemplateRoot, WORKER_ENTRY_FILE)
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
studioTemplateDir: join6(packageRootTemplate, STUDIO_DIR),
|
|
503
|
+
workerEntryPath: join6(packageRootTemplate, WORKER_ENTRY_FILE)
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
studioTemplateDir: join6(sourceTemplateRoot, STUDIO_DIR),
|
|
507
|
+
workerEntryPath: join6(sourceTemplateRoot, WORKER_ENTRY_FILE)
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
studioTemplateDir: monorepoStudioDist,
|
|
511
|
+
workerEntryPath: join6(sourceTemplateRoot, WORKER_ENTRY_FILE)
|
|
512
|
+
}
|
|
513
|
+
];
|
|
514
|
+
}
|
|
515
|
+
async function resolveRuntimeTemplate() {
|
|
516
|
+
for (const candidate of runtimeTemplateCandidates()) {
|
|
517
|
+
try {
|
|
518
|
+
await access3(candidate.studioTemplateDir);
|
|
519
|
+
await access3(candidate.workerEntryPath);
|
|
520
|
+
return candidate;
|
|
521
|
+
} catch {
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
throw new Error(
|
|
525
|
+
"Kalp runtime template not found in CLI package. Reinstall @kalphq/cli."
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
function createStudioShell(entryScript, cssFiles) {
|
|
529
|
+
const cssLinks = cssFiles.map((file) => ` <link rel="stylesheet" href="/studio/assets/${file}" />`).join("\n");
|
|
530
|
+
return `<!doctype html>
|
|
531
|
+
<html lang="en">
|
|
532
|
+
<head>
|
|
533
|
+
<meta charset="UTF-8" />
|
|
534
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
535
|
+
<title>Kalp Studio</title>
|
|
536
|
+
${cssLinks}
|
|
537
|
+
</head>
|
|
538
|
+
<body>
|
|
539
|
+
<div id="root"></div>
|
|
540
|
+
<script type="module" src="/studio/assets/${entryScript}"></script>
|
|
541
|
+
</body>
|
|
542
|
+
</html>
|
|
543
|
+
`;
|
|
544
|
+
}
|
|
545
|
+
async function ensureStudioIndex(studioDir) {
|
|
546
|
+
const indexPath = join6(studioDir, "index.html");
|
|
547
|
+
try {
|
|
548
|
+
await access3(indexPath);
|
|
549
|
+
return;
|
|
550
|
+
} catch {
|
|
551
|
+
}
|
|
552
|
+
const assetsDir = join6(studioDir, "assets");
|
|
553
|
+
const assetFiles = await readdir(assetsDir);
|
|
554
|
+
const entryScript = assetFiles.find((file) => /^index-.*\.js$/i.test(file)) ?? assetFiles.find((file) => file.endsWith(".js"));
|
|
555
|
+
if (!entryScript) {
|
|
556
|
+
throw new Error(
|
|
557
|
+
"Studio runtime template is missing an entry JS bundle in studio/assets."
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
const cssFiles = assetFiles.filter((file) => file.endsWith(".css")).sort();
|
|
561
|
+
const html = createStudioShell(entryScript, cssFiles);
|
|
562
|
+
await writeFile4(indexPath, html, "utf-8");
|
|
563
|
+
}
|
|
564
|
+
async function readLocalAgentNames(cwd) {
|
|
565
|
+
const agentsDir = join6(cwd, "agents");
|
|
566
|
+
try {
|
|
567
|
+
const entries = await readdir(agentsDir, { withFileTypes: true });
|
|
568
|
+
const names = [];
|
|
569
|
+
for (const entry of entries) {
|
|
570
|
+
if (!entry.isDirectory()) continue;
|
|
571
|
+
const indexPath = join6(agentsDir, entry.name, "index.ts");
|
|
572
|
+
const exists = await stat(indexPath).then(() => true).catch(() => false);
|
|
573
|
+
if (exists) names.push(entry.name);
|
|
574
|
+
}
|
|
575
|
+
return names.sort((a, b) => a.localeCompare(b));
|
|
576
|
+
} catch {
|
|
577
|
+
return [];
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
async function createAgentsSnapshot(cwd, mode) {
|
|
581
|
+
const localAgentNames = await readLocalAgentNames(cwd);
|
|
582
|
+
const state = await readProjectState(cwd);
|
|
583
|
+
const byName = /* @__PURE__ */ new Map();
|
|
584
|
+
const stateAgents = state?.agents ?? {};
|
|
585
|
+
for (const name of localAgentNames) {
|
|
586
|
+
const localPath = join6(cwd, "agents", name, "index.ts");
|
|
587
|
+
const saved = stateAgents[name];
|
|
588
|
+
const hasRemoteVersion = !!saved?.lastRemoteHash && (saved?.currentVersion ?? 0) > 0;
|
|
589
|
+
if (mode === "remote" && !hasRemoteVersion) {
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
const resolvedWorkerUrl = saved?.workerUrl ?? (state?.workerUrl ? `${state.workerUrl.replace(/\/$/, "")}/a/${name}` : null);
|
|
593
|
+
const versionNumber = typeof saved?.currentVersion === "number" && saved.currentVersion > 0 ? saved.currentVersion : null;
|
|
594
|
+
byName.set(name, {
|
|
595
|
+
name,
|
|
596
|
+
label: deriveLabelFromName(name),
|
|
597
|
+
tags: [],
|
|
598
|
+
environment: mode === "remote" ? "remote" : hasRemoteVersion ? "both" : "local",
|
|
599
|
+
status: resolvedWorkerUrl ? "online" : "offline",
|
|
600
|
+
hash: saved?.currentHash ?? null,
|
|
601
|
+
version: versionNumber ? `v${versionNumber}` : null,
|
|
602
|
+
versionNumber,
|
|
603
|
+
lastRemoteHash: saved?.lastRemoteHash ?? null,
|
|
604
|
+
lastLocalHash: saved?.lastLocalHash ?? null,
|
|
605
|
+
workerUrl: resolvedWorkerUrl,
|
|
606
|
+
localPath,
|
|
607
|
+
updatedAt: saved?.lastPushedAt ?? state?.deployedAt ?? null
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
if (mode === "remote") {
|
|
611
|
+
for (const [name, saved] of Object.entries(stateAgents)) {
|
|
612
|
+
const hasRemoteVersion = !!saved.lastRemoteHash && (saved.currentVersion ?? 0) > 0;
|
|
613
|
+
if (!hasRemoteVersion || byName.has(name)) continue;
|
|
614
|
+
const localPath = saved.localPath ?? join6(cwd, "agents", name, "index.ts");
|
|
615
|
+
const workerUrl = saved.workerUrl ?? (state?.workerUrl ? `${state.workerUrl.replace(/\/$/, "")}/a/${name}` : null);
|
|
616
|
+
const versionNumber = saved.currentVersion > 0 ? saved.currentVersion : null;
|
|
617
|
+
byName.set(name, {
|
|
618
|
+
name,
|
|
619
|
+
label: deriveLabelFromName(name),
|
|
620
|
+
tags: [],
|
|
621
|
+
environment: "remote",
|
|
622
|
+
status: workerUrl ? "online" : "offline",
|
|
623
|
+
hash: saved.currentHash ?? null,
|
|
624
|
+
version: versionNumber ? `v${versionNumber}` : null,
|
|
625
|
+
versionNumber,
|
|
626
|
+
lastRemoteHash: saved.lastRemoteHash ?? null,
|
|
627
|
+
lastLocalHash: saved.lastLocalHash ?? null,
|
|
628
|
+
workerUrl,
|
|
629
|
+
localPath,
|
|
630
|
+
updatedAt: saved.lastPushedAt ?? state?.deployedAt ?? null
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return {
|
|
635
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
636
|
+
projectPath: cwd,
|
|
637
|
+
workerUrl: state?.workerUrl ?? null,
|
|
638
|
+
mode,
|
|
639
|
+
agents: Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name))
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
async function writeRuntimeAgentsSnapshot(params) {
|
|
643
|
+
const snapshot = await createAgentsSnapshot(params.cwd, params.mode);
|
|
644
|
+
await writeFile4(
|
|
645
|
+
join6(params.runtimeDir, "agents.snapshot.json"),
|
|
646
|
+
`${JSON.stringify(snapshot, null, 2)}
|
|
647
|
+
`,
|
|
648
|
+
"utf-8"
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
async function materializeRuntime(cwd, options = {}) {
|
|
652
|
+
const mode = options.mode ?? "remote";
|
|
653
|
+
const runtimeDir = join6(cwd, RUNTIME_ROOT, RUNTIME_DIR);
|
|
654
|
+
const studioDir = join6(runtimeDir, STUDIO_DIR);
|
|
655
|
+
const workerEntrypointPath = join6(runtimeDir, WORKER_ENTRY_FILE);
|
|
656
|
+
const wranglerConfigPath = join6(runtimeDir, WRANGLER_CONFIG_FILE);
|
|
657
|
+
const template = await resolveRuntimeTemplate();
|
|
658
|
+
await rm(runtimeDir, { recursive: true, force: true });
|
|
659
|
+
await mkdir2(runtimeDir, { recursive: true });
|
|
660
|
+
await cp(template.studioTemplateDir, studioDir, { recursive: true });
|
|
661
|
+
await cp(template.workerEntryPath, workerEntrypointPath);
|
|
662
|
+
await ensureStudioIndex(studioDir);
|
|
663
|
+
await writeRuntimeAgentsSnapshot({ cwd, runtimeDir, mode });
|
|
664
|
+
const identity = await materializeRuntimeIdentity({ cwd, runtimeDir });
|
|
665
|
+
const projectSlug = await resolveProjectSlug(cwd);
|
|
666
|
+
const workerName = buildWorkerName(projectSlug, cwd);
|
|
667
|
+
const requiredSecrets = /* @__PURE__ */ new Set([
|
|
668
|
+
"KALP_SECRET_KEY",
|
|
669
|
+
"KALP_STUDIO_PASSWORD",
|
|
670
|
+
"KALP_STUDIO_ADMIN_USER",
|
|
671
|
+
"KALP_SERVICE_KEY"
|
|
672
|
+
]);
|
|
673
|
+
try {
|
|
674
|
+
const provider = await resolveProviderFromConfig(cwd);
|
|
675
|
+
requiredSecrets.add(getRequiredSecretForProvider(provider));
|
|
676
|
+
} catch {
|
|
677
|
+
}
|
|
678
|
+
const identityRequirements = resolveIdentityAuthRequirements(identity.identityConfig);
|
|
679
|
+
for (const requirement of identityRequirements) {
|
|
680
|
+
requiredSecrets.add(requirement.envKey);
|
|
681
|
+
}
|
|
682
|
+
const wranglerConfig = createRuntimeConfig(
|
|
683
|
+
workerName,
|
|
684
|
+
mode,
|
|
685
|
+
[...requiredSecrets].sort((a, b) => a.localeCompare(b))
|
|
686
|
+
);
|
|
687
|
+
await writeFile4(
|
|
688
|
+
wranglerConfigPath,
|
|
689
|
+
`${JSON.stringify(wranglerConfig, null, 2)}
|
|
690
|
+
`,
|
|
691
|
+
"utf-8"
|
|
692
|
+
);
|
|
693
|
+
return {
|
|
694
|
+
runtimeDir,
|
|
695
|
+
studioDir,
|
|
696
|
+
workerEntrypointPath,
|
|
697
|
+
wranglerConfigPath,
|
|
698
|
+
workerName
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
export {
|
|
703
|
+
ensureStudioSecrets,
|
|
704
|
+
ensureSecretKey,
|
|
705
|
+
readProjectState,
|
|
706
|
+
writeProjectState,
|
|
707
|
+
resolveProviderFromConfig,
|
|
708
|
+
readDotEnv,
|
|
709
|
+
getRequiredSecretForProvider,
|
|
710
|
+
loadProjectConfig,
|
|
711
|
+
resolveRuntimeIdentityConfig,
|
|
712
|
+
resolveIdentityAuthRequirements,
|
|
713
|
+
readLocalAgentNames,
|
|
714
|
+
writeRuntimeAgentsSnapshot,
|
|
715
|
+
materializeRuntime
|
|
716
|
+
};
|
|
717
|
+
//# sourceMappingURL=chunk-YMEEYCMI.js.map
|