charon-hooks 0.2.0 → 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.
Files changed (2) hide show
  1. package/dist/server/index.js +149 -73
  2. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  // src/server/app.ts
2
2
  import { Hono as Hono8 } from "hono";
3
- import { dirname as dirname2, resolve as resolve3 } from "path";
4
- import { fileURLToPath } from "url";
5
- import { existsSync as existsSync4 } from "fs";
3
+ import { dirname as dirname3, resolve as resolve4 } from "path";
4
+ import { fileURLToPath as fileURLToPath2 } from "url";
5
+ import { existsSync as existsSync5 } from "fs";
6
6
 
7
7
  // src/server/middleware/logger.ts
8
8
  import { createMiddleware } from "hono/factory";
@@ -106,8 +106,8 @@ function serveStatic({ root, path: fallbackPath }) {
106
106
  import { Hono } from "hono";
107
107
 
108
108
  // src/lib/config/loader.ts
109
- import { readFileSync as readFileSync2, writeFileSync, mkdirSync, watch, existsSync as existsSync2 } from "fs";
110
- import { dirname } from "path";
109
+ import { readFileSync as readFileSync2, writeFileSync, mkdirSync as mkdirSync2, watch, existsSync as existsSync3 } from "fs";
110
+ import { dirname as dirname2 } from "path";
111
111
 
112
112
  // src/lib/config/schema.ts
113
113
  import { z } from "zod";
@@ -258,8 +258,8 @@ function initSchema(db2) {
258
258
  var db = null;
259
259
  function getDb() {
260
260
  if (!db) {
261
- const dbPath = process.env.CHARON_DB || "charon.db";
262
- db = createDatabase(dbPath);
261
+ const dbPath2 = process.env.CHARON_DB || "charon.db";
262
+ db = createDatabase(dbPath2);
263
263
  initSchema(db);
264
264
  }
265
265
  return db;
@@ -422,16 +422,92 @@ function listEvents(db2, filter) {
422
422
  }
423
423
 
424
424
  // src/lib/pipeline/sanitizer.ts
425
- import { resolve as resolve2 } from "path";
425
+ import { resolve as resolve3 } from "path";
426
426
  import { pathToFileURL } from "url";
427
+
428
+ // src/lib/data-dir.ts
429
+ import { existsSync as existsSync2, mkdirSync, copyFileSync, readdirSync } from "fs";
430
+ import { resolve as resolve2, dirname } from "path";
431
+ import { homedir } from "os";
432
+ import { fileURLToPath } from "url";
433
+ var __dirname = dirname(fileURLToPath(import.meta.url));
434
+ var cwd = process.cwd();
435
+ var userDataDir = process.env.CHARON_DATA_DIR || resolve2(homedir(), ".charon");
436
+ var isDevMode = existsSync2(resolve2(cwd, "sanitizers")) || existsSync2(resolve2(cwd, "src/lib/data-dir.ts"));
437
+ var dataDir = isDevMode ? process.cwd() : userDataDir;
438
+ var configDir = resolve2(dataDir, "config");
439
+ var triggersPath = resolve2(configDir, "triggers.yaml");
440
+ var configPath = resolve2(configDir, "config.yaml");
441
+ var sanitizersDir = resolve2(dataDir, "sanitizers");
442
+ var dbPath = process.env.CHARON_DB || resolve2(dataDir, "charon.db");
443
+ function getBundledDir() {
444
+ const devPath = resolve2(process.cwd());
445
+ const prodPath = resolve2(__dirname, "../..");
446
+ if (existsSync2(resolve2(devPath, "src/lib/data-dir.ts"))) {
447
+ return devPath;
448
+ }
449
+ return prodPath;
450
+ }
451
+ function initializeDataDir() {
452
+ if (isDevMode) {
453
+ console.log("[data-dir] Dev mode: using", dataDir);
454
+ return;
455
+ }
456
+ console.log("[data-dir] Prod mode: using", dataDir);
457
+ if (!existsSync2(dataDir)) {
458
+ mkdirSync(dataDir, { recursive: true });
459
+ }
460
+ if (!existsSync2(configDir)) {
461
+ mkdirSync(configDir, { recursive: true });
462
+ }
463
+ if (!existsSync2(sanitizersDir)) {
464
+ mkdirSync(sanitizersDir, { recursive: true });
465
+ }
466
+ const bundledDir = getBundledDir();
467
+ copyDefaultFile(
468
+ resolve2(bundledDir, "config/config.yaml.dist"),
469
+ resolve2(configDir, "config.yaml")
470
+ );
471
+ copyDefaultFile(
472
+ resolve2(bundledDir, "config/triggers.yaml.dist"),
473
+ resolve2(configDir, "triggers.yaml")
474
+ );
475
+ const bundledSanitizersDir = resolve2(bundledDir, "sanitizers");
476
+ if (existsSync2(bundledSanitizersDir)) {
477
+ const existingSanitizers = existsSync2(sanitizersDir) ? readdirSync(sanitizersDir).filter((f) => f.endsWith(".ts")) : [];
478
+ if (existingSanitizers.length === 0) {
479
+ const defaultSanitizers = readdirSync(bundledSanitizersDir).filter(
480
+ (f) => f.endsWith(".ts")
481
+ );
482
+ for (const file of defaultSanitizers) {
483
+ copyDefaultFile(
484
+ resolve2(bundledSanitizersDir, file),
485
+ resolve2(sanitizersDir, file)
486
+ );
487
+ }
488
+ if (defaultSanitizers.length > 0) {
489
+ console.log(
490
+ `[data-dir] Installed ${defaultSanitizers.length} default sanitizer(s)`
491
+ );
492
+ }
493
+ }
494
+ }
495
+ }
496
+ function copyDefaultFile(src, dest) {
497
+ if (!existsSync2(dest) && existsSync2(src)) {
498
+ copyFileSync(src, dest);
499
+ console.log(`[data-dir] Created ${dest}`);
500
+ }
501
+ }
502
+
503
+ // src/lib/pipeline/sanitizer.ts
427
504
  var sanitizerCache = /* @__PURE__ */ new Map();
428
- var SANITIZERS_DIR = process.env.CHARON_SANITIZERS_DIR || "sanitizers";
429
505
  async function loadSanitizer(name) {
430
506
  if (sanitizerCache.has(name)) {
431
507
  return sanitizerCache.get(name);
432
508
  }
433
509
  try {
434
- const sanitizerPath = resolve2(SANITIZERS_DIR, `${name}.ts`);
510
+ const sanitizerPath = resolve3(sanitizersDir, `${name}.ts`);
435
511
  const sanitizerUrl = pathToFileURL(sanitizerPath).href;
436
512
  const mod = await import(sanitizerUrl);
437
513
  const fn = mod.default;
@@ -754,7 +830,7 @@ async function startTunnel(config, port = 3e3) {
754
830
  }
755
831
  try {
756
832
  await ngrok.disconnect();
757
- await new Promise((resolve4) => setTimeout(resolve4, 1e3));
833
+ await new Promise((resolve5) => setTimeout(resolve5, 1e3));
758
834
  } catch {
759
835
  }
760
836
  listener = null;
@@ -837,9 +913,9 @@ var DEFAULT_CONFIG = `# Charon trigger configuration
837
913
 
838
914
  triggers: []
839
915
  `;
840
- async function loadConfig(path = "config/triggers.yaml") {
841
- if (!existsSync2(path)) {
842
- mkdirSync(dirname(path), { recursive: true });
916
+ async function loadConfig(path = triggersPath) {
917
+ if (!existsSync3(path)) {
918
+ mkdirSync2(dirname2(path), { recursive: true });
843
919
  writeFileSync(path, DEFAULT_CONFIG, "utf-8");
844
920
  console.log(`[config] Created default config at ${path}`);
845
921
  }
@@ -851,8 +927,8 @@ async function loadConfig(path = "config/triggers.yaml") {
851
927
  cachedConfig = result.data;
852
928
  return result.data;
853
929
  }
854
- async function initializeApp(configPath = "config/triggers.yaml") {
855
- const config = await loadConfig(configPath);
930
+ async function initializeApp(configPath2 = triggersPath) {
931
+ const config = await loadConfig(configPath2);
856
932
  const db2 = getDb();
857
933
  initScheduler(db2, config.triggers);
858
934
  if (config.tunnel) {
@@ -860,7 +936,7 @@ async function initializeApp(configPath = "config/triggers.yaml") {
860
936
  } else {
861
937
  await stopTunnel();
862
938
  }
863
- startConfigWatcher(configPath);
939
+ startConfigWatcher(configPath2);
864
940
  return {
865
941
  config,
866
942
  tunnel: getTunnelStatus()
@@ -876,11 +952,11 @@ function getTrigger(id) {
876
952
  const config = getConfig();
877
953
  return config.triggers.find((t) => t.id === id);
878
954
  }
879
- async function deleteTrigger(id, configPath = "config/triggers.yaml") {
880
- if (!existsSync2(configPath)) {
955
+ async function deleteTrigger(id, configPath2 = triggersPath) {
956
+ if (!existsSync3(configPath2)) {
881
957
  return false;
882
958
  }
883
- const content = readFileSync2(configPath, "utf-8");
959
+ const content = readFileSync2(configPath2, "utf-8");
884
960
  const result = parseConfig(content);
885
961
  if (!result.success) {
886
962
  console.error(`[config] Cannot delete trigger: invalid config`);
@@ -895,22 +971,22 @@ async function deleteTrigger(id, configPath = "config/triggers.yaml") {
895
971
  config.triggers.splice(triggerIndex, 1);
896
972
  const yaml = await import("yaml");
897
973
  const newContent = yaml.stringify(config);
898
- writeFileSync(configPath, newContent, "utf-8");
974
+ writeFileSync(configPath2, newContent, "utf-8");
899
975
  console.log(`[config] Deleted trigger '${id}'`);
900
976
  cachedConfig = config;
901
977
  return true;
902
978
  }
903
- function startConfigWatcher(configPath = "config/triggers.yaml") {
904
- if (configWatcher && watchedConfigPath === configPath) {
979
+ function startConfigWatcher(configPath2 = triggersPath) {
980
+ if (configWatcher && watchedConfigPath === configPath2) {
905
981
  return;
906
982
  }
907
983
  stopConfigWatcher();
908
- if (!existsSync2(configPath)) {
909
- console.warn(`[config] Config file not found: ${configPath}, skipping watcher`);
984
+ if (!existsSync3(configPath2)) {
985
+ console.warn(`[config] Config file not found: ${configPath2}, skipping watcher`);
910
986
  return;
911
987
  }
912
- watchedConfigPath = configPath;
913
- configWatcher = watch(configPath, (eventType) => {
988
+ watchedConfigPath = configPath2;
989
+ configWatcher = watch(configPath2, (eventType) => {
914
990
  if (eventType === "change") {
915
991
  if (reloadTimeout) {
916
992
  clearTimeout(reloadTimeout);
@@ -918,7 +994,7 @@ function startConfigWatcher(configPath = "config/triggers.yaml") {
918
994
  reloadTimeout = setTimeout(async () => {
919
995
  console.log("[config] File changed, reloading...");
920
996
  try {
921
- await reloadConfig(configPath);
997
+ await reloadConfig(configPath2);
922
998
  console.log("[config] Reload complete");
923
999
  } catch (err) {
924
1000
  console.error("[config] Reload failed:", err instanceof Error ? err.message : err);
@@ -926,7 +1002,7 @@ function startConfigWatcher(configPath = "config/triggers.yaml") {
926
1002
  }, 300);
927
1003
  }
928
1004
  });
929
- console.log(`[config] Watching ${configPath} for changes`);
1005
+ console.log(`[config] Watching ${configPath2} for changes`);
930
1006
  }
931
1007
  function stopConfigWatcher() {
932
1008
  if (reloadTimeout) {
@@ -940,8 +1016,8 @@ function stopConfigWatcher() {
940
1016
  console.log("[config] Stopped watching config file");
941
1017
  }
942
1018
  }
943
- async function reloadConfig(configPath) {
944
- const config = await loadConfig(configPath);
1019
+ async function reloadConfig(configPath2) {
1020
+ const config = await loadConfig(configPath2);
945
1021
  const db2 = getDb();
946
1022
  initScheduler(db2, config.triggers);
947
1023
  if (config.tunnel) {
@@ -955,14 +1031,14 @@ async function reloadConfig(configPath) {
955
1031
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
956
1032
  import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
957
1033
  var DEFAULT_CONFIG_PATH = "config/triggers.yaml";
958
- async function writeTrigger(trigger, configPath = DEFAULT_CONFIG_PATH) {
1034
+ async function writeTrigger(trigger, configPath2 = DEFAULT_CONFIG_PATH) {
959
1035
  const validation = validateTrigger(trigger);
960
1036
  if (!validation.success) {
961
1037
  const messages = validation.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
962
1038
  return { success: false, error: `Validation failed: ${messages}` };
963
1039
  }
964
1040
  try {
965
- const content = readFileSync3(configPath, "utf-8");
1041
+ const content = readFileSync3(configPath2, "utf-8");
966
1042
  const config = parseYaml2(content) || {};
967
1043
  if (!config.triggers) {
968
1044
  config.triggers = [];
@@ -996,15 +1072,15 @@ async function writeTrigger(trigger, configPath = DEFAULT_CONFIG_PATH) {
996
1072
  defaultStringType: "PLAIN",
997
1073
  defaultKeyType: "PLAIN"
998
1074
  });
999
- writeFileSync2(configPath, yamlOutput);
1075
+ writeFileSync2(configPath2, yamlOutput);
1000
1076
  return { success: true };
1001
1077
  } catch (err) {
1002
1078
  return { success: false, error: `Write failed: ${err instanceof Error ? err.message : String(err)}` };
1003
1079
  }
1004
1080
  }
1005
- async function deleteTrigger2(id, configPath = DEFAULT_CONFIG_PATH) {
1081
+ async function deleteTrigger2(id, configPath2 = DEFAULT_CONFIG_PATH) {
1006
1082
  try {
1007
- const content = readFileSync3(configPath, "utf-8");
1083
+ const content = readFileSync3(configPath2, "utf-8");
1008
1084
  const config = parseYaml2(content) || {};
1009
1085
  if (!config.triggers || !Array.isArray(config.triggers)) {
1010
1086
  return { success: false, error: `Trigger '${id}' not found` };
@@ -1019,15 +1095,15 @@ async function deleteTrigger2(id, configPath = DEFAULT_CONFIG_PATH) {
1019
1095
  defaultStringType: "PLAIN",
1020
1096
  defaultKeyType: "PLAIN"
1021
1097
  });
1022
- writeFileSync2(configPath, yamlOutput);
1098
+ writeFileSync2(configPath2, yamlOutput);
1023
1099
  return { success: true };
1024
1100
  } catch (err) {
1025
1101
  return { success: false, error: `Delete failed: ${err instanceof Error ? err.message : String(err)}` };
1026
1102
  }
1027
1103
  }
1028
- async function listTriggerIds(configPath = DEFAULT_CONFIG_PATH) {
1104
+ async function listTriggerIds(configPath2 = DEFAULT_CONFIG_PATH) {
1029
1105
  try {
1030
- const content = readFileSync3(configPath, "utf-8");
1106
+ const content = readFileSync3(configPath2, "utf-8");
1031
1107
  const config = parseYaml2(content) || {};
1032
1108
  if (!config.triggers || !Array.isArray(config.triggers)) {
1033
1109
  return [];
@@ -1037,9 +1113,9 @@ async function listTriggerIds(configPath = DEFAULT_CONFIG_PATH) {
1037
1113
  return [];
1038
1114
  }
1039
1115
  }
1040
- async function writeTunnelConfig(tunnel, configPath = DEFAULT_CONFIG_PATH) {
1116
+ async function writeTunnelConfig(tunnel, configPath2 = DEFAULT_CONFIG_PATH) {
1041
1117
  try {
1042
- const content = readFileSync3(configPath, "utf-8");
1118
+ const content = readFileSync3(configPath2, "utf-8");
1043
1119
  const config = parseYaml2(content) || {};
1044
1120
  const tunnelObj = {
1045
1121
  enabled: tunnel.enabled,
@@ -1058,15 +1134,15 @@ async function writeTunnelConfig(tunnel, configPath = DEFAULT_CONFIG_PATH) {
1058
1134
  defaultStringType: "PLAIN",
1059
1135
  defaultKeyType: "PLAIN"
1060
1136
  });
1061
- writeFileSync2(configPath, yamlOutput);
1137
+ writeFileSync2(configPath2, yamlOutput);
1062
1138
  return { success: true };
1063
1139
  } catch (err) {
1064
1140
  return { success: false, error: `Write failed: ${err instanceof Error ? err.message : String(err)}` };
1065
1141
  }
1066
1142
  }
1067
- async function getTunnelConfig(configPath = DEFAULT_CONFIG_PATH) {
1143
+ async function getTunnelConfig(configPath2 = DEFAULT_CONFIG_PATH) {
1068
1144
  try {
1069
- const content = readFileSync3(configPath, "utf-8");
1145
+ const content = readFileSync3(configPath2, "utf-8");
1070
1146
  const config = parseYaml2(content) || {};
1071
1147
  if (!config.tunnel) {
1072
1148
  return null;
@@ -1085,7 +1161,7 @@ async function getTunnelConfig(configPath = DEFAULT_CONFIG_PATH) {
1085
1161
 
1086
1162
  // src/server/routes/triggers.ts
1087
1163
  var triggersRoutes = new Hono();
1088
- async function createTriggerInternal(trigger, configPath) {
1164
+ async function createTriggerInternal(trigger, configPath2) {
1089
1165
  const validation = validateTrigger(trigger);
1090
1166
  if (!validation.success) {
1091
1167
  return {
@@ -1095,7 +1171,7 @@ async function createTriggerInternal(trigger, configPath) {
1095
1171
  status: 400
1096
1172
  };
1097
1173
  }
1098
- const existingIds = await listTriggerIds(configPath);
1174
+ const existingIds = await listTriggerIds(configPath2);
1099
1175
  if (existingIds.includes(trigger.id)) {
1100
1176
  return {
1101
1177
  success: false,
@@ -1103,7 +1179,7 @@ async function createTriggerInternal(trigger, configPath) {
1103
1179
  status: 409
1104
1180
  };
1105
1181
  }
1106
- const result = await writeTrigger(trigger, configPath);
1182
+ const result = await writeTrigger(trigger, configPath2);
1107
1183
  if (!result.success) {
1108
1184
  return {
1109
1185
  success: false,
@@ -1117,7 +1193,7 @@ async function createTriggerInternal(trigger, configPath) {
1117
1193
  status: 201
1118
1194
  };
1119
1195
  }
1120
- async function updateTriggerInternal(id, trigger, configPath) {
1196
+ async function updateTriggerInternal(id, trigger, configPath2) {
1121
1197
  const validation = validateTrigger(trigger);
1122
1198
  if (!validation.success) {
1123
1199
  return {
@@ -1127,7 +1203,7 @@ async function updateTriggerInternal(id, trigger, configPath) {
1127
1203
  status: 400
1128
1204
  };
1129
1205
  }
1130
- const existingIds = await listTriggerIds(configPath);
1206
+ const existingIds = await listTriggerIds(configPath2);
1131
1207
  if (!existingIds.includes(id)) {
1132
1208
  return {
1133
1209
  success: false,
@@ -1144,7 +1220,7 @@ async function updateTriggerInternal(id, trigger, configPath) {
1144
1220
  };
1145
1221
  }
1146
1222
  if (idChanged) {
1147
- const deleteResult = await deleteTrigger2(id, configPath);
1223
+ const deleteResult = await deleteTrigger2(id, configPath2);
1148
1224
  if (!deleteResult.success) {
1149
1225
  return {
1150
1226
  success: false,
@@ -1153,7 +1229,7 @@ async function updateTriggerInternal(id, trigger, configPath) {
1153
1229
  };
1154
1230
  }
1155
1231
  }
1156
- const result = await writeTrigger(trigger, configPath);
1232
+ const result = await writeTrigger(trigger, configPath2);
1157
1233
  if (!result.success) {
1158
1234
  return {
1159
1235
  success: false,
@@ -1169,8 +1245,8 @@ async function updateTriggerInternal(id, trigger, configPath) {
1169
1245
  status: 200
1170
1246
  };
1171
1247
  }
1172
- async function deleteTriggerInternal(id, configPath) {
1173
- const existingIds = await listTriggerIds(configPath);
1248
+ async function deleteTriggerInternal(id, configPath2) {
1249
+ const existingIds = await listTriggerIds(configPath2);
1174
1250
  if (!existingIds.includes(id)) {
1175
1251
  return {
1176
1252
  success: false,
@@ -1178,7 +1254,7 @@ async function deleteTriggerInternal(id, configPath) {
1178
1254
  status: 404
1179
1255
  };
1180
1256
  }
1181
- const result = await deleteTrigger2(id, configPath);
1257
+ const result = await deleteTrigger2(id, configPath2);
1182
1258
  if (!result.success) {
1183
1259
  return {
1184
1260
  success: false,
@@ -1199,9 +1275,9 @@ async function ensureConfig() {
1199
1275
  configLoaded = true;
1200
1276
  }
1201
1277
  }
1202
- async function testTriggerInternal(id, payload, configPath) {
1203
- if (configPath) {
1204
- await loadConfig(configPath);
1278
+ async function testTriggerInternal(id, payload, configPath2) {
1279
+ if (configPath2) {
1280
+ await loadConfig(configPath2);
1205
1281
  } else {
1206
1282
  await ensureConfig();
1207
1283
  }
@@ -1360,10 +1436,9 @@ runsRoutes.get("/:id", async (c) => {
1360
1436
 
1361
1437
  // src/server/routes/sanitizers.ts
1362
1438
  import { Hono as Hono3 } from "hono";
1363
- import { readdirSync, existsSync as existsSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync2 } from "fs";
1439
+ import { readdirSync as readdirSync2, existsSync as existsSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3 } from "fs";
1364
1440
  import { join as join2 } from "path";
1365
1441
  var sanitizersRoutes = new Hono3();
1366
- var DEFAULT_SANITIZERS_DIR = process.env.CHARON_SANITIZERS_DIR || "sanitizers";
1367
1442
  var BOILERPLATE = `/**
1368
1443
  * Sanitizer function for processing webhook payloads.
1369
1444
  *
@@ -1383,17 +1458,17 @@ export default sanitize;
1383
1458
  function sanitizeName(name) {
1384
1459
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").replace(/-+/g, "-");
1385
1460
  }
1386
- function listSanitizersInternal(sanitizersDir = DEFAULT_SANITIZERS_DIR) {
1387
- if (!existsSync3(sanitizersDir)) {
1461
+ function listSanitizersInternal(dir = sanitizersDir) {
1462
+ if (!existsSync4(dir)) {
1388
1463
  return [];
1389
1464
  }
1390
- const files = readdirSync(sanitizersDir);
1465
+ const files = readdirSync2(dir);
1391
1466
  return files.filter((f) => f.endsWith(".ts")).map((f) => ({
1392
1467
  name: f.replace(".ts", ""),
1393
- path: join2(sanitizersDir, f)
1468
+ path: join2(dir, f)
1394
1469
  }));
1395
1470
  }
1396
- function createSanitizerInternal(rawName, sanitizersDir = DEFAULT_SANITIZERS_DIR) {
1471
+ function createSanitizerInternal(rawName, dir = sanitizersDir) {
1397
1472
  if (!rawName || typeof rawName !== "string") {
1398
1473
  return { success: false, error: "Name is required", status: 400 };
1399
1474
  }
@@ -1401,12 +1476,12 @@ function createSanitizerInternal(rawName, sanitizersDir = DEFAULT_SANITIZERS_DIR
1401
1476
  if (!name) {
1402
1477
  return { success: false, error: "Invalid name", status: 400 };
1403
1478
  }
1404
- const filePath = join2(sanitizersDir, `${name}.ts`);
1405
- if (existsSync3(filePath)) {
1479
+ const filePath = join2(dir, `${name}.ts`);
1480
+ if (existsSync4(filePath)) {
1406
1481
  return { success: false, error: `Sanitizer '${name}' already exists`, status: 409 };
1407
1482
  }
1408
- if (!existsSync3(sanitizersDir)) {
1409
- mkdirSync2(sanitizersDir, { recursive: true });
1483
+ if (!existsSync4(dir)) {
1484
+ mkdirSync3(dir, { recursive: true });
1410
1485
  }
1411
1486
  writeFileSync3(filePath, BOILERPLATE);
1412
1487
  return { success: true, name, path: filePath, status: 201 };
@@ -1711,10 +1786,10 @@ async function tunnelProxyMiddleware(c, next) {
1711
1786
  }
1712
1787
 
1713
1788
  // src/server/app.ts
1714
- var __dirname = dirname2(fileURLToPath(import.meta.url));
1715
- var prodClientDir = resolve3(__dirname, "../client");
1716
- var devClientDir = resolve3(process.cwd(), "dist/client");
1717
- var clientDir = existsSync4(devClientDir) ? devClientDir : prodClientDir;
1789
+ var __dirname2 = dirname3(fileURLToPath2(import.meta.url));
1790
+ var prodClientDir = resolve4(__dirname2, "../client");
1791
+ var devClientDir = resolve4(process.cwd(), "dist/client");
1792
+ var clientDir = existsSync5(devClientDir) ? devClientDir : prodClientDir;
1718
1793
  var app = new Hono8();
1719
1794
  app.use("*", quietLogger);
1720
1795
  app.use("*", tunnelProxyMiddleware);
@@ -1726,11 +1801,12 @@ app.route("/api/webhook", webhookRoutes);
1726
1801
  app.route("/api/task", taskRoutes);
1727
1802
  app.route("/api/promise", promiseRoutes);
1728
1803
  app.use("/*", serveStatic({ root: clientDir }));
1729
- app.get("*", serveStatic({ path: resolve3(clientDir, "index.html") }));
1804
+ app.get("*", serveStatic({ path: resolve4(clientDir, "index.html") }));
1730
1805
 
1731
1806
  // src/server/init.ts
1732
1807
  async function initializeServices() {
1733
1808
  try {
1809
+ initializeDataDir();
1734
1810
  const { config, tunnel } = await initializeApp();
1735
1811
  console.log(`[init] Loaded ${config.triggers.length} triggers`);
1736
1812
  if (tunnel.connected) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "charon-hooks",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Autonomous task triggering service - webhooks and cron to AI agents",
5
5
  "type": "module",
6
6
  "bin": {