tabclaw 0.1.0 → 0.1.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/cli/index.js CHANGED
@@ -95,6 +95,16 @@ function getOrCreateHookToken() {
95
95
  function getHookToken() {
96
96
  return loadCredentials()?.hookToken ?? null;
97
97
  }
98
+ function resetCredentials() {
99
+ const newToken = generateToken();
100
+ const credentials = {
101
+ version: CREDENTIALS_VERSION,
102
+ hookToken: newToken,
103
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
104
+ };
105
+ saveCredentials(credentials);
106
+ return newToken;
107
+ }
98
108
 
99
109
  // src/cli/attach.ts
100
110
  import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync } from "fs";
@@ -235,6 +245,7 @@ var en_default = {
235
245
  detected: "Claude Code detected.",
236
246
  hooksInstalled: "Hooks already installed.",
237
247
  hooksNotInstalled: "Hooks not installed.",
248
+ hooksTokenMismatch: "Hooks have outdated token - reconfiguration required.",
238
249
  actionQuestion: "Select action:",
239
250
  injectAction: "Inject hooks configuration",
240
251
  updateAction: "Update hooks configuration",
@@ -603,44 +614,75 @@ function detectClaude() {
603
614
  settingsPath
604
615
  };
605
616
  }
617
+ const currentToken = getHookToken();
606
618
  let hooked = false;
619
+ let tokenMismatch = false;
607
620
  if (existsSync4(settingsPath)) {
608
621
  try {
609
622
  const settings = JSON.parse(readFileSync4(settingsPath, "utf-8"));
610
- hooked = isTabclawHooked(settings);
623
+ const hookStatus = checkTabclawHooks(settings, currentToken);
624
+ hooked = hookStatus.matched;
625
+ tokenMismatch = hookStatus.mismatched;
611
626
  } catch {
612
627
  }
613
628
  }
629
+ let message;
630
+ if (hooked) {
631
+ message = "Claude Code hooks already configured.";
632
+ } else if (tokenMismatch) {
633
+ message = "Claude Code hooks have mismatched token. Reconfiguration required.";
634
+ } else {
635
+ message = "Claude Code detected.";
636
+ }
614
637
  return {
615
638
  supported: true,
616
639
  hooked,
617
- message: hooked ? "Claude Code hooks already configured." : "Claude Code detected.",
640
+ tokenMismatch,
641
+ message,
618
642
  binaryPath,
619
643
  settingsPath
620
644
  };
621
645
  }
622
- function isTabclawHooked(settings) {
646
+ function checkTabclawHooks(settings, expectedToken) {
623
647
  const hooks = settings.hooks;
624
- if (!hooks) return false;
648
+ if (!hooks) return { matched: false, mismatched: false };
649
+ let hasAnyTabclawHook = false;
650
+ let hasMatchingToken = false;
625
651
  for (const key of Object.keys(hooks)) {
626
652
  const entries = hooks[key];
627
653
  for (const entry of entries) {
628
654
  if (!entry || typeof entry !== "object") continue;
629
655
  const e = entry;
630
656
  const inner = e.hooks;
631
- if (Array.isArray(inner) && inner.some((h) => isHttpHookWithTabclawUrl(h))) {
632
- return true;
657
+ if (Array.isArray(inner)) {
658
+ for (const h of inner) {
659
+ const hookInfo = extractTabclawHookInfo(h);
660
+ if (hookInfo.isTabclaw) {
661
+ hasAnyTabclawHook = true;
662
+ if (expectedToken && hookInfo.token === expectedToken) {
663
+ hasMatchingToken = true;
664
+ }
665
+ }
666
+ }
633
667
  }
634
668
  }
635
669
  }
636
- return false;
670
+ return {
671
+ matched: hasMatchingToken,
672
+ mismatched: hasAnyTabclawHook && !hasMatchingToken
673
+ };
637
674
  }
638
- function isHttpHookWithTabclawUrl(entry) {
639
- if (!entry || typeof entry !== "object") return false;
675
+ function extractTabclawHookInfo(entry) {
676
+ if (!entry || typeof entry !== "object") return { isTabclaw: false };
640
677
  const e = entry;
641
- if (e.type !== "http") return false;
678
+ if (e.type !== "http") return { isTabclaw: false };
642
679
  const url = e.url;
643
- return typeof url === "string" && url.includes("localhost") && url.includes("/hook/");
680
+ if (typeof url !== "string") return { isTabclaw: false };
681
+ const match = url.match(/^http:\/\/localhost:\d+\/hook\/([^\/]+)/);
682
+ if (match) {
683
+ return { isTabclaw: true, token: match[1] };
684
+ }
685
+ return { isTabclaw: false };
644
686
  }
645
687
 
646
688
  // src/cli/wizard/prompts.ts
@@ -745,6 +787,8 @@ ${t("wizard.section.cli")}
745
787
  console.log(pc.gray(` Gateway: http://localhost:${port}/hook/${token ? token.substring(0, 8) + "..." : "unknown"}`));
746
788
  if (detection.hooked) {
747
789
  console.log(pc.green(` ${t("wizard.cli.hooksInstalled")}`));
790
+ } else if (detection.tokenMismatch) {
791
+ console.log(pc.red(` ${t("wizard.cli.hooksTokenMismatch")}`));
748
792
  } else {
749
793
  console.log(pc.yellow(` ${t("wizard.cli.hooksNotInstalled")}`));
750
794
  }
@@ -874,7 +918,10 @@ async function stepCliMenu(state) {
874
918
  ${t("wizard.section.cli")}
875
919
  `));
876
920
  const cliStatus = state.itemStatus.get("claude") || "unconfigured";
877
- const label = getChannelStatusLabel(cliStatus);
921
+ let label = getChannelStatusLabel(cliStatus);
922
+ if (state.claudeDetection?.tokenMismatch) {
923
+ label = pc2.yellow("[update needed]");
924
+ }
878
925
  const choice = await prompts.channelMenuSelect([{
879
926
  name: "claude",
880
927
  status: label
@@ -1323,6 +1370,11 @@ var initCommand = new Command2("init").description("Initialize tabclaw configura
1323
1370
  return;
1324
1371
  }
1325
1372
  }
1373
+ if (options.force) {
1374
+ resetCredentials();
1375
+ } else {
1376
+ getOrCreateHookToken();
1377
+ }
1326
1378
  const wizard = new WizardEngine();
1327
1379
  await wizard.run();
1328
1380
  });