@babyclaw/gateway 0.0.1 → 0.2.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.
- package/dist/index.d.ts +91 -1
- package/dist/index.js +254 -72
- package/dist/index.js.map +1 -1
- package/dist/main.js +255 -71
- package/dist/main.js.map +1 -1
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -134,6 +134,9 @@ declare class AdminClient {
|
|
|
134
134
|
shutdown(): Promise<{
|
|
135
135
|
ok: boolean;
|
|
136
136
|
}>;
|
|
137
|
+
reloadSkills(): Promise<{
|
|
138
|
+
ok: boolean;
|
|
139
|
+
}>;
|
|
137
140
|
private get;
|
|
138
141
|
}
|
|
139
142
|
|
|
@@ -148,6 +151,16 @@ declare function writeConfig({ config }: {
|
|
|
148
151
|
declare const CONFIG_PATH_ENV_VAR = "BABYCLAW_CONFIG_PATH";
|
|
149
152
|
declare function getDefaultConfigPath(): string;
|
|
150
153
|
declare function getConfigPath(): string;
|
|
154
|
+
declare const DEFAULT_WORKSPACE_ROOT = "~/babyclaw";
|
|
155
|
+
/**
|
|
156
|
+
* Resolves the workspace path from the config's workspace.root value.
|
|
157
|
+
* Supports ~ expansion for home-relative paths.
|
|
158
|
+
* Plain relative paths are resolved against the config file's directory
|
|
159
|
+
* so the result is deterministic regardless of where the process starts.
|
|
160
|
+
*/
|
|
161
|
+
declare function resolveWorkspaceRoot({ configRoot }: {
|
|
162
|
+
configRoot: string;
|
|
163
|
+
}): string;
|
|
151
164
|
|
|
152
165
|
declare function getDefaultConfigTemplate(): string;
|
|
153
166
|
|
|
@@ -204,6 +217,83 @@ declare function resolveLanguageModel({ config }: {
|
|
|
204
217
|
config: BabyclawConfig;
|
|
205
218
|
}): LanguageModel;
|
|
206
219
|
|
|
220
|
+
type SkillInstallSpec = {
|
|
221
|
+
id?: string;
|
|
222
|
+
kind: "brew" | "node" | "go" | "uv" | "download";
|
|
223
|
+
label?: string;
|
|
224
|
+
bins?: string[];
|
|
225
|
+
os?: string[];
|
|
226
|
+
formula?: string;
|
|
227
|
+
package?: string;
|
|
228
|
+
module?: string;
|
|
229
|
+
url?: string;
|
|
230
|
+
archive?: string;
|
|
231
|
+
extract?: boolean;
|
|
232
|
+
stripComponents?: number;
|
|
233
|
+
targetDir?: string;
|
|
234
|
+
};
|
|
235
|
+
type OpenClawSkillMetadata = {
|
|
236
|
+
always?: boolean;
|
|
237
|
+
skillKey?: string;
|
|
238
|
+
primaryEnv?: string;
|
|
239
|
+
emoji?: string;
|
|
240
|
+
homepage?: string;
|
|
241
|
+
os?: string[];
|
|
242
|
+
requires?: {
|
|
243
|
+
bins?: string[];
|
|
244
|
+
anyBins?: string[];
|
|
245
|
+
env?: string[];
|
|
246
|
+
config?: string[];
|
|
247
|
+
};
|
|
248
|
+
install?: SkillInstallSpec[];
|
|
249
|
+
};
|
|
250
|
+
type SkillFrontmatter = {
|
|
251
|
+
name: string;
|
|
252
|
+
description: string;
|
|
253
|
+
homepage?: string;
|
|
254
|
+
userInvocable: boolean;
|
|
255
|
+
disableModelInvocation: boolean;
|
|
256
|
+
commandDispatch?: string;
|
|
257
|
+
commandTool?: string;
|
|
258
|
+
commandArgMode?: string;
|
|
259
|
+
openclaw?: OpenClawSkillMetadata;
|
|
260
|
+
};
|
|
261
|
+
type SkillEntry = {
|
|
262
|
+
frontmatter: SkillFrontmatter;
|
|
263
|
+
slug: string;
|
|
264
|
+
relativePath: string;
|
|
265
|
+
};
|
|
266
|
+
declare function getSkillKey({ frontmatter, slug, }: {
|
|
267
|
+
frontmatter: SkillFrontmatter | null;
|
|
268
|
+
slug: string;
|
|
269
|
+
}): string;
|
|
270
|
+
type SkillsConfig = {
|
|
271
|
+
entries: Record<string, {
|
|
272
|
+
enabled: boolean;
|
|
273
|
+
apiKey?: string;
|
|
274
|
+
env?: Record<string, string>;
|
|
275
|
+
}>;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
type BundledSkillStatus = {
|
|
279
|
+
slug: string;
|
|
280
|
+
frontmatter: SkillFrontmatter | null;
|
|
281
|
+
enabled: boolean;
|
|
282
|
+
eligible: boolean;
|
|
283
|
+
ineligibilityReason: string | null;
|
|
284
|
+
};
|
|
285
|
+
declare function listBundledSkills({ skillsConfig, fullConfig, }: {
|
|
286
|
+
skillsConfig: SkillsConfig;
|
|
287
|
+
fullConfig: Record<string, unknown>;
|
|
288
|
+
}): BundledSkillStatus[];
|
|
289
|
+
declare function getEnabledBundledSkills({ skillsConfig, fullConfig, }: {
|
|
290
|
+
skillsConfig: SkillsConfig;
|
|
291
|
+
fullConfig: Record<string, unknown>;
|
|
292
|
+
}): SkillEntry[];
|
|
293
|
+
declare function getBundledSkillPath({ slug }: {
|
|
294
|
+
slug: string;
|
|
295
|
+
}): string | null;
|
|
296
|
+
|
|
207
297
|
declare const LOG_LEVELS: readonly ["debug", "info", "warn", "error"];
|
|
208
298
|
type LogLevel = (typeof LOG_LEVELS)[number];
|
|
209
299
|
declare const LOG_FORMATS: readonly ["json", "pretty"];
|
|
@@ -225,4 +315,4 @@ declare function createChildLogger({ context }: {
|
|
|
225
315
|
context: Record<string, unknown>;
|
|
226
316
|
}): pino.Logger;
|
|
227
317
|
|
|
228
|
-
export { AdminClient, type BabyclawConfig, CONFIG_PATH_ENV_VAR, ClawHubError, GatewayRuntime, type GatewayStatus, type InstallSkillResult, type LogFormat, type LogLevel, type LoggingConfig, SUPPORTED_PROVIDERS, SkillAlreadyInstalledError, type SkillSetupResult, babyclawConfigSchema, createChildLogger, createLogger, getAdminSocketPath, getConfigPath, getDefaultConfigPath, getDefaultConfigTemplate, getLogger, installSkillFromClawHub, loadConfig, loadConfigRaw, resolveLanguageModel, runSkillSetup, writeConfig };
|
|
318
|
+
export { AdminClient, type BabyclawConfig, type BundledSkillStatus, CONFIG_PATH_ENV_VAR, ClawHubError, DEFAULT_WORKSPACE_ROOT, GatewayRuntime, type GatewayStatus, type InstallSkillResult, type LogFormat, type LogLevel, type LoggingConfig, SUPPORTED_PROVIDERS, SkillAlreadyInstalledError, type SkillSetupResult, babyclawConfigSchema, createChildLogger, createLogger, getAdminSocketPath, getBundledSkillPath, getConfigPath, getDefaultConfigPath, getDefaultConfigTemplate, getEnabledBundledSkills, getLogger, getSkillKey, installSkillFromClawHub, listBundledSkills, loadConfig, loadConfigRaw, resolveLanguageModel, resolveWorkspaceRoot, runSkillSetup, writeConfig };
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,8 @@ var __export = (target, all) => {
|
|
|
5
5
|
};
|
|
6
6
|
|
|
7
7
|
// src/runtime.ts
|
|
8
|
-
import {
|
|
8
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
9
|
+
import { getBundledSkillsDir } from "@babyclaw/skills";
|
|
9
10
|
|
|
10
11
|
// src/ai/agent.ts
|
|
11
12
|
import {
|
|
@@ -2042,9 +2043,26 @@ import { z } from "zod";
|
|
|
2042
2043
|
// src/utils/path.ts
|
|
2043
2044
|
import { resolve, sep } from "path";
|
|
2044
2045
|
import { stat } from "fs/promises";
|
|
2046
|
+
var BUNDLED_SKILLS_PREFIX = "bundled-skills/";
|
|
2045
2047
|
function normalizeSeparators({ path }) {
|
|
2046
2048
|
return path.replaceAll("\\", "/");
|
|
2047
2049
|
}
|
|
2050
|
+
function resolveBundledSkillPath({
|
|
2051
|
+
bundledSkillsDir,
|
|
2052
|
+
requestedPath
|
|
2053
|
+
}) {
|
|
2054
|
+
const relative3 = requestedPath.slice(BUNDLED_SKILLS_PREFIX.length);
|
|
2055
|
+
const normalized = normalizeSeparators({ path: relative3 }).trim();
|
|
2056
|
+
if (normalized.length === 0) {
|
|
2057
|
+
throw new Error("Path is required");
|
|
2058
|
+
}
|
|
2059
|
+
const absoluteRoot = resolve(bundledSkillsDir);
|
|
2060
|
+
const absoluteTarget = resolve(absoluteRoot, normalized);
|
|
2061
|
+
if (!isSubPath({ parent: absoluteRoot, child: absoluteTarget })) {
|
|
2062
|
+
throw new Error("Path escapes bundled skills root");
|
|
2063
|
+
}
|
|
2064
|
+
return absoluteTarget;
|
|
2065
|
+
}
|
|
2048
2066
|
function resolveWorkspacePath({
|
|
2049
2067
|
workspaceRoot,
|
|
2050
2068
|
requestedPath
|
|
@@ -3801,10 +3819,25 @@ function createWorkspaceTools({ context }) {
|
|
|
3801
3819
|
defaultCode: "WORKSPACE_READ_FAILED",
|
|
3802
3820
|
input: { path, format },
|
|
3803
3821
|
action: async () => {
|
|
3804
|
-
const
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3822
|
+
const isBundled = path.startsWith(BUNDLED_SKILLS_PREFIX);
|
|
3823
|
+
let absolutePath;
|
|
3824
|
+
if (isBundled) {
|
|
3825
|
+
if (!context.bundledSkillsDir) {
|
|
3826
|
+
throw new ToolExecutionError({
|
|
3827
|
+
code: "BUNDLED_SKILLS_UNAVAILABLE",
|
|
3828
|
+
message: "Bundled skills directory is not configured"
|
|
3829
|
+
});
|
|
3830
|
+
}
|
|
3831
|
+
absolutePath = resolveBundledSkillPath({
|
|
3832
|
+
bundledSkillsDir: context.bundledSkillsDir,
|
|
3833
|
+
requestedPath: path
|
|
3834
|
+
});
|
|
3835
|
+
} else {
|
|
3836
|
+
absolutePath = resolveWorkspacePath({
|
|
3837
|
+
workspaceRoot: context.workspaceRoot,
|
|
3838
|
+
requestedPath: path
|
|
3839
|
+
});
|
|
3840
|
+
}
|
|
3808
3841
|
const raw = await readFile6(absolutePath, "utf8");
|
|
3809
3842
|
ensurePayloadWithinLimit({
|
|
3810
3843
|
value: raw,
|
|
@@ -3862,6 +3895,12 @@ function createWorkspaceTools({ context }) {
|
|
|
3862
3895
|
hasValue: value !== void 0
|
|
3863
3896
|
},
|
|
3864
3897
|
action: async () => {
|
|
3898
|
+
if (path.startsWith(BUNDLED_SKILLS_PREFIX)) {
|
|
3899
|
+
throw new ToolExecutionError({
|
|
3900
|
+
code: "BUNDLED_SKILLS_READONLY",
|
|
3901
|
+
message: "Bundled skills are read-only and cannot be written to"
|
|
3902
|
+
});
|
|
3903
|
+
}
|
|
3865
3904
|
const absolutePath = resolveWorkspacePath({
|
|
3866
3905
|
workspaceRoot: context.workspaceRoot,
|
|
3867
3906
|
requestedPath: path
|
|
@@ -4001,6 +4040,12 @@ function createWorkspaceTools({ context }) {
|
|
|
4001
4040
|
defaultCode: "WORKSPACE_DELETE_FAILED",
|
|
4002
4041
|
input: { path, recursive },
|
|
4003
4042
|
action: async () => {
|
|
4043
|
+
if (path.startsWith(BUNDLED_SKILLS_PREFIX)) {
|
|
4044
|
+
throw new ToolExecutionError({
|
|
4045
|
+
code: "BUNDLED_SKILLS_READONLY",
|
|
4046
|
+
message: "Bundled skills are read-only and cannot be deleted"
|
|
4047
|
+
});
|
|
4048
|
+
}
|
|
4004
4049
|
const absolutePath = resolveWorkspacePath({
|
|
4005
4050
|
workspaceRoot: context.workspaceRoot,
|
|
4006
4051
|
requestedPath: path
|
|
@@ -4037,6 +4082,12 @@ function createWorkspaceTools({ context }) {
|
|
|
4037
4082
|
defaultCode: "WORKSPACE_MOVE_FAILED",
|
|
4038
4083
|
input: { from_path, to_path, overwrite },
|
|
4039
4084
|
action: async () => {
|
|
4085
|
+
if (from_path.startsWith(BUNDLED_SKILLS_PREFIX) || to_path.startsWith(BUNDLED_SKILLS_PREFIX)) {
|
|
4086
|
+
throw new ToolExecutionError({
|
|
4087
|
+
code: "BUNDLED_SKILLS_READONLY",
|
|
4088
|
+
message: "Bundled skills are read-only and cannot be moved"
|
|
4089
|
+
});
|
|
4090
|
+
}
|
|
4040
4091
|
const fromAbsolute = resolveWorkspacePath({
|
|
4041
4092
|
workspaceRoot: context.workspaceRoot,
|
|
4042
4093
|
requestedPath: from_path
|
|
@@ -4217,44 +4268,15 @@ function createUnifiedTools({
|
|
|
4217
4268
|
};
|
|
4218
4269
|
}
|
|
4219
4270
|
|
|
4220
|
-
// src/
|
|
4221
|
-
import {
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
userPath: join9(workspacePath, "USER.md")
|
|
4228
|
-
};
|
|
4229
|
-
}
|
|
4230
|
-
async function readPersonalityFiles({
|
|
4231
|
-
workspacePath
|
|
4271
|
+
// src/bundled-skills/index.ts
|
|
4272
|
+
import { listBundledSlugs, readBundledSkillContent, getBundledSkillInfo } from "@babyclaw/skills";
|
|
4273
|
+
|
|
4274
|
+
// src/workspace/skills/types.ts
|
|
4275
|
+
function getSkillKey({
|
|
4276
|
+
frontmatter,
|
|
4277
|
+
slug
|
|
4232
4278
|
}) {
|
|
4233
|
-
|
|
4234
|
-
const [identity, soul, user] = await Promise.all([
|
|
4235
|
-
readOptionalFile2({ path: identityPath }),
|
|
4236
|
-
readOptionalFile2({ path: soulPath }),
|
|
4237
|
-
readOptionalFile2({ path: userPath })
|
|
4238
|
-
]);
|
|
4239
|
-
return {
|
|
4240
|
-
identity,
|
|
4241
|
-
soul,
|
|
4242
|
-
user
|
|
4243
|
-
};
|
|
4244
|
-
}
|
|
4245
|
-
function hasCompletePersonalityFiles(files) {
|
|
4246
|
-
return typeof files.identity === "string" && files.identity.trim().length > 0 && typeof files.soul === "string" && files.soul.trim().length > 0 && typeof files.user === "string" && files.user.trim().length > 0;
|
|
4247
|
-
}
|
|
4248
|
-
async function readOptionalFile2({ path }) {
|
|
4249
|
-
try {
|
|
4250
|
-
return await readFile7(path, "utf8");
|
|
4251
|
-
} catch (error) {
|
|
4252
|
-
const maybeErrno = error;
|
|
4253
|
-
if (maybeErrno.code === "ENOENT") {
|
|
4254
|
-
return null;
|
|
4255
|
-
}
|
|
4256
|
-
throw error;
|
|
4257
|
-
}
|
|
4279
|
+
return frontmatter?.openclaw?.skillKey ?? frontmatter?.name ?? slug;
|
|
4258
4280
|
}
|
|
4259
4281
|
|
|
4260
4282
|
// src/workspace/skills/eligibility.ts
|
|
@@ -4264,54 +4286,84 @@ function getEligibleSkills({
|
|
|
4264
4286
|
skillsConfig,
|
|
4265
4287
|
fullConfig
|
|
4266
4288
|
}) {
|
|
4267
|
-
return skills.filter((skill) =>
|
|
4289
|
+
return skills.filter((skill) => {
|
|
4290
|
+
const { frontmatter, slug } = skill;
|
|
4291
|
+
const entry = skillsConfig.entries[getSkillKey({ frontmatter, slug })];
|
|
4292
|
+
if (entry?.enabled === false) return false;
|
|
4293
|
+
return checkSkillEligibility({ frontmatter, skillsConfig, fullConfig }).eligible;
|
|
4294
|
+
});
|
|
4268
4295
|
}
|
|
4269
|
-
function
|
|
4270
|
-
|
|
4296
|
+
function checkSkillEligibility({
|
|
4297
|
+
frontmatter,
|
|
4271
4298
|
skillsConfig,
|
|
4272
4299
|
fullConfig
|
|
4273
4300
|
}) {
|
|
4274
|
-
|
|
4301
|
+
if (frontmatter.disableModelInvocation) {
|
|
4302
|
+
return { eligible: false, reason: "Model invocation disabled" };
|
|
4303
|
+
}
|
|
4275
4304
|
const openclaw = frontmatter.openclaw;
|
|
4276
|
-
const skillKey = openclaw?.skillKey ?? frontmatter.name;
|
|
4277
|
-
const entry = skillsConfig.entries[skillKey];
|
|
4278
|
-
if (entry?.enabled === false) return false;
|
|
4279
|
-
if (frontmatter.disableModelInvocation) return false;
|
|
4280
4305
|
if (openclaw?.os && openclaw.os.length > 0) {
|
|
4281
|
-
if (!openclaw.os.includes(process.platform))
|
|
4306
|
+
if (!openclaw.os.includes(process.platform)) {
|
|
4307
|
+
return {
|
|
4308
|
+
eligible: false,
|
|
4309
|
+
reason: `Requires OS: ${openclaw.os.join(", ")} (current: ${process.platform})`
|
|
4310
|
+
};
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
if (openclaw?.always) {
|
|
4314
|
+
return { eligible: true, reason: null };
|
|
4282
4315
|
}
|
|
4283
|
-
if (openclaw?.always) return true;
|
|
4284
4316
|
const requires = openclaw?.requires;
|
|
4285
|
-
if (!requires)
|
|
4317
|
+
if (!requires) {
|
|
4318
|
+
return { eligible: true, reason: null };
|
|
4319
|
+
}
|
|
4286
4320
|
if (requires.bins && requires.bins.length > 0) {
|
|
4287
|
-
|
|
4321
|
+
const missing = requires.bins.filter((bin) => !binaryExists({ name: bin }));
|
|
4322
|
+
if (missing.length > 0) {
|
|
4323
|
+
return { eligible: false, reason: `Missing binaries: ${missing.join(", ")}` };
|
|
4324
|
+
}
|
|
4288
4325
|
}
|
|
4289
4326
|
if (requires.anyBins && requires.anyBins.length > 0) {
|
|
4290
|
-
if (!requires.anyBins.some((bin) => binaryExists({ name: bin })))
|
|
4327
|
+
if (!requires.anyBins.some((bin) => binaryExists({ name: bin }))) {
|
|
4328
|
+
return {
|
|
4329
|
+
eligible: false,
|
|
4330
|
+
reason: `Requires at least one of: ${requires.anyBins.join(", ")}`
|
|
4331
|
+
};
|
|
4332
|
+
}
|
|
4291
4333
|
}
|
|
4292
4334
|
if (requires.env && requires.env.length > 0) {
|
|
4335
|
+
const skillKey = openclaw?.skillKey ?? frontmatter.name;
|
|
4336
|
+
const entry = skillsConfig.entries[skillKey];
|
|
4293
4337
|
const primaryEnv = openclaw?.primaryEnv;
|
|
4294
4338
|
const hasApiKeyInConfig = Boolean(entry?.apiKey);
|
|
4295
4339
|
for (const envVar of requires.env) {
|
|
4296
4340
|
if (process.env[envVar]) continue;
|
|
4297
4341
|
if (primaryEnv === envVar && hasApiKeyInConfig) continue;
|
|
4298
|
-
return false;
|
|
4342
|
+
return { eligible: false, reason: `Missing environment variable: ${envVar}` };
|
|
4299
4343
|
}
|
|
4300
4344
|
}
|
|
4301
4345
|
if (requires.config && requires.config.length > 0) {
|
|
4302
4346
|
for (const configPath of requires.config) {
|
|
4303
|
-
if (!getConfigValue({ config: fullConfig, path: configPath }))
|
|
4347
|
+
if (!getConfigValue({ config: fullConfig, path: configPath })) {
|
|
4348
|
+
return { eligible: false, reason: `Missing config value: ${configPath}` };
|
|
4349
|
+
}
|
|
4304
4350
|
}
|
|
4305
4351
|
}
|
|
4306
|
-
return true;
|
|
4352
|
+
return { eligible: true, reason: null };
|
|
4307
4353
|
}
|
|
4354
|
+
var binaryExistsCache = /* @__PURE__ */ new Map();
|
|
4308
4355
|
function binaryExists({ name }) {
|
|
4356
|
+
const cached = binaryExistsCache.get(name);
|
|
4357
|
+
if (cached !== void 0) return cached;
|
|
4358
|
+
let exists;
|
|
4309
4359
|
try {
|
|
4310
4360
|
execFileSync("which", [name], { stdio: "ignore" });
|
|
4311
|
-
|
|
4361
|
+
exists = true;
|
|
4312
4362
|
} catch {
|
|
4313
|
-
|
|
4363
|
+
exists = false;
|
|
4314
4364
|
}
|
|
4365
|
+
binaryExistsCache.set(name, exists);
|
|
4366
|
+
return exists;
|
|
4315
4367
|
}
|
|
4316
4368
|
function getConfigValue({
|
|
4317
4369
|
config,
|
|
@@ -4326,6 +4378,94 @@ function getConfigValue({
|
|
|
4326
4378
|
return current;
|
|
4327
4379
|
}
|
|
4328
4380
|
|
|
4381
|
+
// src/bundled-skills/index.ts
|
|
4382
|
+
function listBundledSkills({
|
|
4383
|
+
skillsConfig,
|
|
4384
|
+
fullConfig
|
|
4385
|
+
}) {
|
|
4386
|
+
const slugs = listBundledSlugs();
|
|
4387
|
+
return slugs.map((slug) => {
|
|
4388
|
+
const content = readBundledSkillContent({ slug });
|
|
4389
|
+
if (!content) {
|
|
4390
|
+
return {
|
|
4391
|
+
slug,
|
|
4392
|
+
frontmatter: null,
|
|
4393
|
+
enabled: false,
|
|
4394
|
+
eligible: false,
|
|
4395
|
+
ineligibilityReason: "Could not read SKILL.md"
|
|
4396
|
+
};
|
|
4397
|
+
}
|
|
4398
|
+
const raw = parseFrontmatter({ content });
|
|
4399
|
+
const frontmatter = raw ? buildFrontmatter({ raw }) : null;
|
|
4400
|
+
const skillKey = getSkillKey({ frontmatter, slug });
|
|
4401
|
+
const entry = skillsConfig.entries[skillKey];
|
|
4402
|
+
const enabled = entry?.enabled === true;
|
|
4403
|
+
const { eligible, reason } = frontmatter ? checkSkillEligibility({ frontmatter, skillsConfig, fullConfig }) : { eligible: false, reason: "Invalid frontmatter" };
|
|
4404
|
+
return {
|
|
4405
|
+
slug,
|
|
4406
|
+
frontmatter,
|
|
4407
|
+
enabled,
|
|
4408
|
+
eligible,
|
|
4409
|
+
ineligibilityReason: reason
|
|
4410
|
+
};
|
|
4411
|
+
});
|
|
4412
|
+
}
|
|
4413
|
+
function getEnabledBundledSkills({
|
|
4414
|
+
skillsConfig,
|
|
4415
|
+
fullConfig
|
|
4416
|
+
}) {
|
|
4417
|
+
const all = listBundledSkills({ skillsConfig, fullConfig });
|
|
4418
|
+
return all.filter((s) => s.enabled && s.eligible && s.frontmatter).map((s) => ({
|
|
4419
|
+
frontmatter: s.frontmatter,
|
|
4420
|
+
slug: s.slug,
|
|
4421
|
+
relativePath: `bundled-skills/${s.slug}/SKILL.md`
|
|
4422
|
+
}));
|
|
4423
|
+
}
|
|
4424
|
+
function getBundledSkillPath({ slug }) {
|
|
4425
|
+
const info = getBundledSkillInfo({ slug });
|
|
4426
|
+
return info?.skillDir ?? null;
|
|
4427
|
+
}
|
|
4428
|
+
|
|
4429
|
+
// src/onboarding/personality.ts
|
|
4430
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
4431
|
+
import { join as join9 } from "path";
|
|
4432
|
+
function getPersonalityFilePaths({ workspacePath }) {
|
|
4433
|
+
return {
|
|
4434
|
+
identityPath: join9(workspacePath, "IDENTITY.md"),
|
|
4435
|
+
soulPath: join9(workspacePath, "SOUL.md"),
|
|
4436
|
+
userPath: join9(workspacePath, "USER.md")
|
|
4437
|
+
};
|
|
4438
|
+
}
|
|
4439
|
+
async function readPersonalityFiles({
|
|
4440
|
+
workspacePath
|
|
4441
|
+
}) {
|
|
4442
|
+
const { identityPath, soulPath, userPath } = getPersonalityFilePaths({ workspacePath });
|
|
4443
|
+
const [identity, soul, user] = await Promise.all([
|
|
4444
|
+
readOptionalFile2({ path: identityPath }),
|
|
4445
|
+
readOptionalFile2({ path: soulPath }),
|
|
4446
|
+
readOptionalFile2({ path: userPath })
|
|
4447
|
+
]);
|
|
4448
|
+
return {
|
|
4449
|
+
identity,
|
|
4450
|
+
soul,
|
|
4451
|
+
user
|
|
4452
|
+
};
|
|
4453
|
+
}
|
|
4454
|
+
function hasCompletePersonalityFiles(files) {
|
|
4455
|
+
return typeof files.identity === "string" && files.identity.trim().length > 0 && typeof files.soul === "string" && files.soul.trim().length > 0 && typeof files.user === "string" && files.user.trim().length > 0;
|
|
4456
|
+
}
|
|
4457
|
+
async function readOptionalFile2({ path }) {
|
|
4458
|
+
try {
|
|
4459
|
+
return await readFile7(path, "utf8");
|
|
4460
|
+
} catch (error) {
|
|
4461
|
+
const maybeErrno = error;
|
|
4462
|
+
if (maybeErrno.code === "ENOENT") {
|
|
4463
|
+
return null;
|
|
4464
|
+
}
|
|
4465
|
+
throw error;
|
|
4466
|
+
}
|
|
4467
|
+
}
|
|
4468
|
+
|
|
4329
4469
|
// src/agent/context.ts
|
|
4330
4470
|
async function loadAgentContext({
|
|
4331
4471
|
workspacePath,
|
|
@@ -4338,7 +4478,11 @@ async function loadAgentContext({
|
|
|
4338
4478
|
readWorkspaceGuide({ workspacePath }),
|
|
4339
4479
|
scanWorkspaceSkills({ workspacePath })
|
|
4340
4480
|
]);
|
|
4341
|
-
const
|
|
4481
|
+
const workspaceSkills = getEligibleSkills({ skills: allSkills, skillsConfig, fullConfig });
|
|
4482
|
+
const bundledSkills = getEnabledBundledSkills({ skillsConfig, fullConfig });
|
|
4483
|
+
const workspaceSlugs = new Set(workspaceSkills.map((s) => s.slug));
|
|
4484
|
+
const dedupedBundled = bundledSkills.filter((s) => !workspaceSlugs.has(s.slug));
|
|
4485
|
+
const skills = [...workspaceSkills, ...dedupedBundled];
|
|
4342
4486
|
const personalityFiles = hasCompletePersonalityFiles(rawPersonalityFiles) ? rawPersonalityFiles : void 0;
|
|
4343
4487
|
return { personalityFiles, toolNotesContent, agentsContent, skills };
|
|
4344
4488
|
}
|
|
@@ -4519,6 +4663,7 @@ var AgentTurnOrchestrator = class {
|
|
|
4519
4663
|
if (abortSignal.aborted) return;
|
|
4520
4664
|
const {
|
|
4521
4665
|
workspacePath,
|
|
4666
|
+
bundledSkillsDir,
|
|
4522
4667
|
aiAgent,
|
|
4523
4668
|
sessionManager,
|
|
4524
4669
|
schedulerService,
|
|
@@ -4618,6 +4763,7 @@ ${description}`;
|
|
|
4618
4763
|
toolDeps: this.toolDeps,
|
|
4619
4764
|
executionContext: {
|
|
4620
4765
|
workspaceRoot: workspacePath,
|
|
4766
|
+
bundledSkillsDir,
|
|
4621
4767
|
botTimezone: schedulerService.getTimezone(),
|
|
4622
4768
|
platform: event.platform,
|
|
4623
4769
|
chatId: event.chatId,
|
|
@@ -5932,11 +6078,11 @@ var ChatRegistry = class {
|
|
|
5932
6078
|
|
|
5933
6079
|
// src/config/loader.ts
|
|
5934
6080
|
import { access, mkdir as mkdir5, readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
|
|
5935
|
-
import { dirname as
|
|
6081
|
+
import { dirname as dirname6 } from "path";
|
|
5936
6082
|
|
|
5937
6083
|
// src/config/paths.ts
|
|
5938
6084
|
import { homedir as homedir2 } from "os";
|
|
5939
|
-
import { join as join11 } from "path";
|
|
6085
|
+
import { dirname as dirname5, join as join11, resolve as resolve4, isAbsolute } from "path";
|
|
5940
6086
|
var CONFIG_PATH_ENV_VAR = "BABYCLAW_CONFIG_PATH";
|
|
5941
6087
|
function getDefaultConfigPath() {
|
|
5942
6088
|
return join11(homedir2(), ".babyclaw", "babyclaw.json");
|
|
@@ -5948,6 +6094,19 @@ function getConfigPath() {
|
|
|
5948
6094
|
}
|
|
5949
6095
|
return getDefaultConfigPath();
|
|
5950
6096
|
}
|
|
6097
|
+
function getConfigDir() {
|
|
6098
|
+
return dirname5(getConfigPath());
|
|
6099
|
+
}
|
|
6100
|
+
var DEFAULT_WORKSPACE_ROOT = "~/babyclaw";
|
|
6101
|
+
function resolveWorkspaceRoot({ configRoot }) {
|
|
6102
|
+
if (configRoot === "~" || configRoot.startsWith("~/")) {
|
|
6103
|
+
return join11(homedir2(), configRoot.slice(2));
|
|
6104
|
+
}
|
|
6105
|
+
if (isAbsolute(configRoot)) {
|
|
6106
|
+
return configRoot;
|
|
6107
|
+
}
|
|
6108
|
+
return resolve4(getConfigDir(), configRoot);
|
|
6109
|
+
}
|
|
5951
6110
|
|
|
5952
6111
|
// src/config/schema.ts
|
|
5953
6112
|
import { z as z11 } from "zod";
|
|
@@ -6101,9 +6260,9 @@ var babyclawConfigSchema = z11.object({
|
|
|
6101
6260
|
timezone: "UTC"
|
|
6102
6261
|
}),
|
|
6103
6262
|
workspace: z11.object({
|
|
6104
|
-
root: z11.string().min(1).default("
|
|
6263
|
+
root: z11.string().min(1).default("~/babyclaw")
|
|
6105
6264
|
}).strict().default({
|
|
6106
|
-
root: "
|
|
6265
|
+
root: "~/babyclaw"
|
|
6107
6266
|
}),
|
|
6108
6267
|
session: z11.object({
|
|
6109
6268
|
maxMessagesPerSession: z11.number().int().positive().default(120),
|
|
@@ -6163,7 +6322,7 @@ var DEFAULT_CONFIG_TEMPLATE = {
|
|
|
6163
6322
|
timezone: "UTC"
|
|
6164
6323
|
},
|
|
6165
6324
|
workspace: {
|
|
6166
|
-
root: "
|
|
6325
|
+
root: "~/babyclaw"
|
|
6167
6326
|
},
|
|
6168
6327
|
session: {
|
|
6169
6328
|
maxMessagesPerSession: 120,
|
|
@@ -6238,7 +6397,7 @@ async function loadConfigRaw() {
|
|
|
6238
6397
|
}
|
|
6239
6398
|
async function writeConfig({ config }) {
|
|
6240
6399
|
const configPath = getConfigPath();
|
|
6241
|
-
await mkdir5(
|
|
6400
|
+
await mkdir5(dirname6(configPath), { recursive: true });
|
|
6242
6401
|
await writeFile5(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
6243
6402
|
}
|
|
6244
6403
|
async function ensureConfigFileExists({ configPath }) {
|
|
@@ -6249,7 +6408,7 @@ async function ensureConfigFileExists({ configPath }) {
|
|
|
6249
6408
|
if (error.code !== "ENOENT") {
|
|
6250
6409
|
throw error;
|
|
6251
6410
|
}
|
|
6252
|
-
await mkdir5(
|
|
6411
|
+
await mkdir5(dirname6(configPath), { recursive: true });
|
|
6253
6412
|
await writeFile5(configPath, getDefaultConfigTemplate(), "utf8");
|
|
6254
6413
|
getLogger().warn({ configPath }, "Created config file. Fill required secrets and restart.");
|
|
6255
6414
|
}
|
|
@@ -6421,6 +6580,7 @@ var HeartbeatExecutor = class {
|
|
|
6421
6580
|
toolDeps: this.toolDeps,
|
|
6422
6581
|
executionContext: {
|
|
6423
6582
|
workspaceRoot: workspacePath,
|
|
6583
|
+
bundledSkillsDir: this.toolDeps.bundledSkillsDir,
|
|
6424
6584
|
botTimezone: schedulerService.getTimezone(),
|
|
6425
6585
|
platform: mainChat.platform,
|
|
6426
6586
|
chatId: platformChatId,
|
|
@@ -6877,6 +7037,7 @@ var SchedulerExecutor = class {
|
|
|
6877
7037
|
toolDeps: this.toolDeps,
|
|
6878
7038
|
executionContext: {
|
|
6879
7039
|
workspaceRoot: workspacePath,
|
|
7040
|
+
bundledSkillsDir: this.toolDeps.bundledSkillsDir,
|
|
6880
7041
|
botTimezone: schedulerService.getTimezone(),
|
|
6881
7042
|
platform: this.channelSender.platform,
|
|
6882
7043
|
chatId: chatIdStr,
|
|
@@ -7623,13 +7784,13 @@ function createDatabase({ workspacePath }) {
|
|
|
7623
7784
|
}
|
|
7624
7785
|
|
|
7625
7786
|
// src/database/migrate.ts
|
|
7626
|
-
import { dirname as
|
|
7787
|
+
import { dirname as dirname7, resolve as resolve5 } from "path";
|
|
7627
7788
|
import { fileURLToPath } from "url";
|
|
7628
7789
|
import { migrate } from "drizzle-orm/better-sqlite3/migrator";
|
|
7629
|
-
var __dirname =
|
|
7790
|
+
var __dirname = dirname7(fileURLToPath(import.meta.url));
|
|
7630
7791
|
function applyMigrations({ workspacePath }) {
|
|
7631
7792
|
const db = createDatabase({ workspacePath });
|
|
7632
|
-
const migrationsFolder =
|
|
7793
|
+
const migrationsFolder = resolve5(__dirname, "..", "drizzle");
|
|
7633
7794
|
migrate(db, { migrationsFolder });
|
|
7634
7795
|
}
|
|
7635
7796
|
|
|
@@ -7667,7 +7828,8 @@ var GatewayRuntime = class {
|
|
|
7667
7828
|
},
|
|
7668
7829
|
"Gateway configuration summary"
|
|
7669
7830
|
);
|
|
7670
|
-
const workspacePath =
|
|
7831
|
+
const workspacePath = resolveWorkspaceRoot({ configRoot: config.workspace.root });
|
|
7832
|
+
mkdirSync3(workspacePath, { recursive: true });
|
|
7671
7833
|
log.info("Applying database migrations...");
|
|
7672
7834
|
applyMigrations({ workspacePath });
|
|
7673
7835
|
log.info("Database migrations applied");
|
|
@@ -7751,6 +7913,7 @@ var GatewayRuntime = class {
|
|
|
7751
7913
|
const adminSocketPath = getAdminSocketPath();
|
|
7752
7914
|
const toolDeps = {
|
|
7753
7915
|
workspacePath,
|
|
7916
|
+
bundledSkillsDir: getBundledSkillsDir(),
|
|
7754
7917
|
aiAgent,
|
|
7755
7918
|
sessionManager,
|
|
7756
7919
|
schedulerService,
|
|
@@ -7856,6 +8019,16 @@ var GatewayRuntime = class {
|
|
|
7856
8019
|
void this.stop().then(() => process.exit(0));
|
|
7857
8020
|
});
|
|
7858
8021
|
return { ok: true };
|
|
8022
|
+
},
|
|
8023
|
+
"/reload-skills": async () => {
|
|
8024
|
+
const freshConfig = await loadConfigRaw();
|
|
8025
|
+
if (!freshConfig) {
|
|
8026
|
+
return { ok: false, error: "config_not_found" };
|
|
8027
|
+
}
|
|
8028
|
+
toolDeps.skillsConfig = freshConfig.skills;
|
|
8029
|
+
toolDeps.fullConfig = freshConfig;
|
|
8030
|
+
log.info("Skills configuration reloaded via admin socket");
|
|
8031
|
+
return { ok: true };
|
|
7859
8032
|
}
|
|
7860
8033
|
}
|
|
7861
8034
|
});
|
|
@@ -7956,6 +8129,9 @@ var AdminClient = class {
|
|
|
7956
8129
|
async shutdown() {
|
|
7957
8130
|
return this.get({ path: "/shutdown" });
|
|
7958
8131
|
}
|
|
8132
|
+
async reloadSkills() {
|
|
8133
|
+
return this.get({ path: "/reload-skills" });
|
|
8134
|
+
}
|
|
7959
8135
|
get({ path }) {
|
|
7960
8136
|
return new Promise((resolve6, reject) => {
|
|
7961
8137
|
const req = request(
|
|
@@ -7997,6 +8173,7 @@ export {
|
|
|
7997
8173
|
AdminClient,
|
|
7998
8174
|
CONFIG_PATH_ENV_VAR,
|
|
7999
8175
|
ClawHubError,
|
|
8176
|
+
DEFAULT_WORKSPACE_ROOT,
|
|
8000
8177
|
GatewayRuntime,
|
|
8001
8178
|
SUPPORTED_PROVIDERS,
|
|
8002
8179
|
SkillAlreadyInstalledError,
|
|
@@ -8004,14 +8181,19 @@ export {
|
|
|
8004
8181
|
createChildLogger,
|
|
8005
8182
|
createLogger,
|
|
8006
8183
|
getAdminSocketPath,
|
|
8184
|
+
getBundledSkillPath,
|
|
8007
8185
|
getConfigPath,
|
|
8008
8186
|
getDefaultConfigPath,
|
|
8009
8187
|
getDefaultConfigTemplate,
|
|
8188
|
+
getEnabledBundledSkills,
|
|
8010
8189
|
getLogger,
|
|
8190
|
+
getSkillKey,
|
|
8011
8191
|
installSkillFromClawHub,
|
|
8192
|
+
listBundledSkills,
|
|
8012
8193
|
loadConfig,
|
|
8013
8194
|
loadConfigRaw,
|
|
8014
8195
|
resolveLanguageModel,
|
|
8196
|
+
resolveWorkspaceRoot,
|
|
8015
8197
|
runSkillSetup,
|
|
8016
8198
|
writeConfig
|
|
8017
8199
|
};
|