@babyclaw/gateway 0.0.1 → 0.2.1

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 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,84 @@ 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
+ hasInstallSpecs: boolean;
285
+ };
286
+ declare function listBundledSkills({ skillsConfig, fullConfig, }: {
287
+ skillsConfig: SkillsConfig;
288
+ fullConfig: Record<string, unknown>;
289
+ }): BundledSkillStatus[];
290
+ declare function getEnabledBundledSkills({ skillsConfig, fullConfig, }: {
291
+ skillsConfig: SkillsConfig;
292
+ fullConfig: Record<string, unknown>;
293
+ }): SkillEntry[];
294
+ declare function getBundledSkillPath({ slug }: {
295
+ slug: string;
296
+ }): string | null;
297
+
207
298
  declare const LOG_LEVELS: readonly ["debug", "info", "warn", "error"];
208
299
  type LogLevel = (typeof LOG_LEVELS)[number];
209
300
  declare const LOG_FORMATS: readonly ["json", "pretty"];
@@ -225,4 +316,4 @@ declare function createChildLogger({ context }: {
225
316
  context: Record<string, unknown>;
226
317
  }): pino.Logger;
227
318
 
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 };
319
+ 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 { resolve as resolve5 } from "path";
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 absolutePath = resolveWorkspacePath({
3805
- workspaceRoot: context.workspaceRoot,
3806
- requestedPath: path
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/onboarding/personality.ts
4221
- import { readFile as readFile7 } from "fs/promises";
4222
- import { join as join9 } from "path";
4223
- function getPersonalityFilePaths({ workspacePath }) {
4224
- return {
4225
- identityPath: join9(workspacePath, "IDENTITY.md"),
4226
- soulPath: join9(workspacePath, "SOUL.md"),
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
- const { identityPath, soulPath, userPath } = getPersonalityFilePaths({ workspacePath });
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) => shouldIncludeSkill({ skill, skillsConfig, fullConfig }));
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 shouldIncludeSkill({
4270
- skill,
4296
+ function checkSkillEligibility({
4297
+ frontmatter,
4271
4298
  skillsConfig,
4272
4299
  fullConfig
4273
4300
  }) {
4274
- const { frontmatter } = skill;
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)) return false;
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) return true;
4317
+ if (!requires) {
4318
+ return { eligible: true, reason: null };
4319
+ }
4286
4320
  if (requires.bins && requires.bins.length > 0) {
4287
- if (!requires.bins.every((bin) => binaryExists({ name: bin }))) return false;
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 }))) return false;
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 })) return false;
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
- return true;
4361
+ exists = true;
4312
4362
  } catch {
4313
- return false;
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,97 @@ 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
+ hasInstallSpecs: false
4397
+ };
4398
+ }
4399
+ const raw = parseFrontmatter({ content });
4400
+ const frontmatter = raw ? buildFrontmatter({ raw }) : null;
4401
+ const skillKey = getSkillKey({ frontmatter, slug });
4402
+ const entry = skillsConfig.entries[skillKey];
4403
+ const enabled = entry?.enabled === true;
4404
+ const { eligible, reason } = frontmatter ? checkSkillEligibility({ frontmatter, skillsConfig, fullConfig }) : { eligible: false, reason: "Invalid frontmatter" };
4405
+ const hasInstallSpecs = (frontmatter?.openclaw?.install?.length ?? 0) > 0;
4406
+ return {
4407
+ slug,
4408
+ frontmatter,
4409
+ enabled,
4410
+ eligible,
4411
+ ineligibilityReason: reason,
4412
+ hasInstallSpecs
4413
+ };
4414
+ });
4415
+ }
4416
+ function getEnabledBundledSkills({
4417
+ skillsConfig,
4418
+ fullConfig
4419
+ }) {
4420
+ const all = listBundledSkills({ skillsConfig, fullConfig });
4421
+ return all.filter((s) => s.enabled && s.eligible && s.frontmatter).map((s) => ({
4422
+ frontmatter: s.frontmatter,
4423
+ slug: s.slug,
4424
+ relativePath: `bundled-skills/${s.slug}/SKILL.md`
4425
+ }));
4426
+ }
4427
+ function getBundledSkillPath({ slug }) {
4428
+ const info = getBundledSkillInfo({ slug });
4429
+ return info?.skillDir ?? null;
4430
+ }
4431
+
4432
+ // src/onboarding/personality.ts
4433
+ import { readFile as readFile7 } from "fs/promises";
4434
+ import { join as join9 } from "path";
4435
+ function getPersonalityFilePaths({ workspacePath }) {
4436
+ return {
4437
+ identityPath: join9(workspacePath, "IDENTITY.md"),
4438
+ soulPath: join9(workspacePath, "SOUL.md"),
4439
+ userPath: join9(workspacePath, "USER.md")
4440
+ };
4441
+ }
4442
+ async function readPersonalityFiles({
4443
+ workspacePath
4444
+ }) {
4445
+ const { identityPath, soulPath, userPath } = getPersonalityFilePaths({ workspacePath });
4446
+ const [identity, soul, user] = await Promise.all([
4447
+ readOptionalFile2({ path: identityPath }),
4448
+ readOptionalFile2({ path: soulPath }),
4449
+ readOptionalFile2({ path: userPath })
4450
+ ]);
4451
+ return {
4452
+ identity,
4453
+ soul,
4454
+ user
4455
+ };
4456
+ }
4457
+ function hasCompletePersonalityFiles(files) {
4458
+ 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;
4459
+ }
4460
+ async function readOptionalFile2({ path }) {
4461
+ try {
4462
+ return await readFile7(path, "utf8");
4463
+ } catch (error) {
4464
+ const maybeErrno = error;
4465
+ if (maybeErrno.code === "ENOENT") {
4466
+ return null;
4467
+ }
4468
+ throw error;
4469
+ }
4470
+ }
4471
+
4329
4472
  // src/agent/context.ts
4330
4473
  async function loadAgentContext({
4331
4474
  workspacePath,
@@ -4338,7 +4481,11 @@ async function loadAgentContext({
4338
4481
  readWorkspaceGuide({ workspacePath }),
4339
4482
  scanWorkspaceSkills({ workspacePath })
4340
4483
  ]);
4341
- const skills = getEligibleSkills({ skills: allSkills, skillsConfig, fullConfig });
4484
+ const workspaceSkills = getEligibleSkills({ skills: allSkills, skillsConfig, fullConfig });
4485
+ const bundledSkills = getEnabledBundledSkills({ skillsConfig, fullConfig });
4486
+ const workspaceSlugs = new Set(workspaceSkills.map((s) => s.slug));
4487
+ const dedupedBundled = bundledSkills.filter((s) => !workspaceSlugs.has(s.slug));
4488
+ const skills = [...workspaceSkills, ...dedupedBundled];
4342
4489
  const personalityFiles = hasCompletePersonalityFiles(rawPersonalityFiles) ? rawPersonalityFiles : void 0;
4343
4490
  return { personalityFiles, toolNotesContent, agentsContent, skills };
4344
4491
  }
@@ -4519,6 +4666,7 @@ var AgentTurnOrchestrator = class {
4519
4666
  if (abortSignal.aborted) return;
4520
4667
  const {
4521
4668
  workspacePath,
4669
+ bundledSkillsDir,
4522
4670
  aiAgent,
4523
4671
  sessionManager,
4524
4672
  schedulerService,
@@ -4618,6 +4766,7 @@ ${description}`;
4618
4766
  toolDeps: this.toolDeps,
4619
4767
  executionContext: {
4620
4768
  workspaceRoot: workspacePath,
4769
+ bundledSkillsDir,
4621
4770
  botTimezone: schedulerService.getTimezone(),
4622
4771
  platform: event.platform,
4623
4772
  chatId: event.chatId,
@@ -5932,11 +6081,11 @@ var ChatRegistry = class {
5932
6081
 
5933
6082
  // src/config/loader.ts
5934
6083
  import { access, mkdir as mkdir5, readFile as readFile8, writeFile as writeFile5 } from "fs/promises";
5935
- import { dirname as dirname5 } from "path";
6084
+ import { dirname as dirname6 } from "path";
5936
6085
 
5937
6086
  // src/config/paths.ts
5938
6087
  import { homedir as homedir2 } from "os";
5939
- import { join as join11 } from "path";
6088
+ import { dirname as dirname5, join as join11, resolve as resolve4, isAbsolute } from "path";
5940
6089
  var CONFIG_PATH_ENV_VAR = "BABYCLAW_CONFIG_PATH";
5941
6090
  function getDefaultConfigPath() {
5942
6091
  return join11(homedir2(), ".babyclaw", "babyclaw.json");
@@ -5948,6 +6097,19 @@ function getConfigPath() {
5948
6097
  }
5949
6098
  return getDefaultConfigPath();
5950
6099
  }
6100
+ function getConfigDir() {
6101
+ return dirname5(getConfigPath());
6102
+ }
6103
+ var DEFAULT_WORKSPACE_ROOT = "~/babyclaw";
6104
+ function resolveWorkspaceRoot({ configRoot }) {
6105
+ if (configRoot === "~" || configRoot.startsWith("~/")) {
6106
+ return join11(homedir2(), configRoot.slice(2));
6107
+ }
6108
+ if (isAbsolute(configRoot)) {
6109
+ return configRoot;
6110
+ }
6111
+ return resolve4(getConfigDir(), configRoot);
6112
+ }
5951
6113
 
5952
6114
  // src/config/schema.ts
5953
6115
  import { z as z11 } from "zod";
@@ -6101,9 +6263,9 @@ var babyclawConfigSchema = z11.object({
6101
6263
  timezone: "UTC"
6102
6264
  }),
6103
6265
  workspace: z11.object({
6104
- root: z11.string().min(1).default(".")
6266
+ root: z11.string().min(1).default("~/babyclaw")
6105
6267
  }).strict().default({
6106
- root: "."
6268
+ root: "~/babyclaw"
6107
6269
  }),
6108
6270
  session: z11.object({
6109
6271
  maxMessagesPerSession: z11.number().int().positive().default(120),
@@ -6163,7 +6325,7 @@ var DEFAULT_CONFIG_TEMPLATE = {
6163
6325
  timezone: "UTC"
6164
6326
  },
6165
6327
  workspace: {
6166
- root: "."
6328
+ root: "~/babyclaw"
6167
6329
  },
6168
6330
  session: {
6169
6331
  maxMessagesPerSession: 120,
@@ -6238,7 +6400,7 @@ async function loadConfigRaw() {
6238
6400
  }
6239
6401
  async function writeConfig({ config }) {
6240
6402
  const configPath = getConfigPath();
6241
- await mkdir5(dirname5(configPath), { recursive: true });
6403
+ await mkdir5(dirname6(configPath), { recursive: true });
6242
6404
  await writeFile5(configPath, JSON.stringify(config, null, 2) + "\n", "utf8");
6243
6405
  }
6244
6406
  async function ensureConfigFileExists({ configPath }) {
@@ -6249,7 +6411,7 @@ async function ensureConfigFileExists({ configPath }) {
6249
6411
  if (error.code !== "ENOENT") {
6250
6412
  throw error;
6251
6413
  }
6252
- await mkdir5(dirname5(configPath), { recursive: true });
6414
+ await mkdir5(dirname6(configPath), { recursive: true });
6253
6415
  await writeFile5(configPath, getDefaultConfigTemplate(), "utf8");
6254
6416
  getLogger().warn({ configPath }, "Created config file. Fill required secrets and restart.");
6255
6417
  }
@@ -6421,6 +6583,7 @@ var HeartbeatExecutor = class {
6421
6583
  toolDeps: this.toolDeps,
6422
6584
  executionContext: {
6423
6585
  workspaceRoot: workspacePath,
6586
+ bundledSkillsDir: this.toolDeps.bundledSkillsDir,
6424
6587
  botTimezone: schedulerService.getTimezone(),
6425
6588
  platform: mainChat.platform,
6426
6589
  chatId: platformChatId,
@@ -6877,6 +7040,7 @@ var SchedulerExecutor = class {
6877
7040
  toolDeps: this.toolDeps,
6878
7041
  executionContext: {
6879
7042
  workspaceRoot: workspacePath,
7043
+ bundledSkillsDir: this.toolDeps.bundledSkillsDir,
6880
7044
  botTimezone: schedulerService.getTimezone(),
6881
7045
  platform: this.channelSender.platform,
6882
7046
  chatId: chatIdStr,
@@ -7623,13 +7787,13 @@ function createDatabase({ workspacePath }) {
7623
7787
  }
7624
7788
 
7625
7789
  // src/database/migrate.ts
7626
- import { dirname as dirname6, resolve as resolve4 } from "path";
7790
+ import { dirname as dirname7, resolve as resolve5 } from "path";
7627
7791
  import { fileURLToPath } from "url";
7628
7792
  import { migrate } from "drizzle-orm/better-sqlite3/migrator";
7629
- var __dirname = dirname6(fileURLToPath(import.meta.url));
7793
+ var __dirname = dirname7(fileURLToPath(import.meta.url));
7630
7794
  function applyMigrations({ workspacePath }) {
7631
7795
  const db = createDatabase({ workspacePath });
7632
- const migrationsFolder = resolve4(__dirname, "..", "drizzle");
7796
+ const migrationsFolder = resolve5(__dirname, "..", "drizzle");
7633
7797
  migrate(db, { migrationsFolder });
7634
7798
  }
7635
7799
 
@@ -7667,7 +7831,8 @@ var GatewayRuntime = class {
7667
7831
  },
7668
7832
  "Gateway configuration summary"
7669
7833
  );
7670
- const workspacePath = resolve5(process.cwd(), config.workspace.root);
7834
+ const workspacePath = resolveWorkspaceRoot({ configRoot: config.workspace.root });
7835
+ mkdirSync3(workspacePath, { recursive: true });
7671
7836
  log.info("Applying database migrations...");
7672
7837
  applyMigrations({ workspacePath });
7673
7838
  log.info("Database migrations applied");
@@ -7751,6 +7916,7 @@ var GatewayRuntime = class {
7751
7916
  const adminSocketPath = getAdminSocketPath();
7752
7917
  const toolDeps = {
7753
7918
  workspacePath,
7919
+ bundledSkillsDir: getBundledSkillsDir(),
7754
7920
  aiAgent,
7755
7921
  sessionManager,
7756
7922
  schedulerService,
@@ -7856,6 +8022,16 @@ var GatewayRuntime = class {
7856
8022
  void this.stop().then(() => process.exit(0));
7857
8023
  });
7858
8024
  return { ok: true };
8025
+ },
8026
+ "/reload-skills": async () => {
8027
+ const freshConfig = await loadConfigRaw();
8028
+ if (!freshConfig) {
8029
+ return { ok: false, error: "config_not_found" };
8030
+ }
8031
+ toolDeps.skillsConfig = freshConfig.skills;
8032
+ toolDeps.fullConfig = freshConfig;
8033
+ log.info("Skills configuration reloaded via admin socket");
8034
+ return { ok: true };
7859
8035
  }
7860
8036
  }
7861
8037
  });
@@ -7956,6 +8132,9 @@ var AdminClient = class {
7956
8132
  async shutdown() {
7957
8133
  return this.get({ path: "/shutdown" });
7958
8134
  }
8135
+ async reloadSkills() {
8136
+ return this.get({ path: "/reload-skills" });
8137
+ }
7959
8138
  get({ path }) {
7960
8139
  return new Promise((resolve6, reject) => {
7961
8140
  const req = request(
@@ -7997,6 +8176,7 @@ export {
7997
8176
  AdminClient,
7998
8177
  CONFIG_PATH_ENV_VAR,
7999
8178
  ClawHubError,
8179
+ DEFAULT_WORKSPACE_ROOT,
8000
8180
  GatewayRuntime,
8001
8181
  SUPPORTED_PROVIDERS,
8002
8182
  SkillAlreadyInstalledError,
@@ -8004,14 +8184,19 @@ export {
8004
8184
  createChildLogger,
8005
8185
  createLogger,
8006
8186
  getAdminSocketPath,
8187
+ getBundledSkillPath,
8007
8188
  getConfigPath,
8008
8189
  getDefaultConfigPath,
8009
8190
  getDefaultConfigTemplate,
8191
+ getEnabledBundledSkills,
8010
8192
  getLogger,
8193
+ getSkillKey,
8011
8194
  installSkillFromClawHub,
8195
+ listBundledSkills,
8012
8196
  loadConfig,
8013
8197
  loadConfigRaw,
8014
8198
  resolveLanguageModel,
8199
+ resolveWorkspaceRoot,
8015
8200
  runSkillSetup,
8016
8201
  writeConfig
8017
8202
  };