@contro1/claude-code 0.1.1 → 0.1.2

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/README.md CHANGED
@@ -23,7 +23,7 @@ Set env vars (or use `.centcom.json` in working directory):
23
23
  - `CENTCOM_SLA_MINUTES` (optional)
24
24
  - `CENTCOM_REQUIRED_ROLE` (optional)
25
25
  - `CENTCOM_FALLBACK` (`deny` default; supported: `deny`, `ask`, `allow`)
26
- - `CENTCOM_CALLBACK_URL` (default internal placeholder, can be overridden)
26
+ - `CENTCOM_CALLBACK_URL` (optional; only set when you also want webhook callbacks)
27
27
 
28
28
  ## Claude Code Hook Setup
29
29
 
@@ -34,7 +34,7 @@ Add this in `.claude/settings.json`:
34
34
  "hooks": {
35
35
  "PreToolUse": [
36
36
  {
37
- "matcher": "",
37
+ "matcher": "Write|Edit|Bash",
38
38
  "command": "centcom-claude-code",
39
39
  "timeout": 310000
40
40
  }
@@ -51,6 +51,18 @@ Add this in `.claude/settings.json`:
51
51
  - Timeout -> request cancel attempt + `deny`.
52
52
  - API errors -> fallback decision (`CENTCOM_FALLBACK`).
53
53
 
54
+ ## Quick Verify
55
+
56
+ ```bash
57
+ npm view @contro1/claude-code version
58
+ ```
59
+
60
+ ## Related Packages
61
+
62
+ - [`centcom`](https://github.com/contro1-hq/centcom) for Python integrations
63
+ - [`@contro1/sdk`](https://github.com/contro1-hq/centcom-sdk) for Node/TypeScript SDK usage
64
+ - [`centcom-langgraph`](https://github.com/contro1-hq/centcom-langgraph) for LangGraph integrations
65
+
54
66
  ## Development
55
67
 
56
68
  ```bash
@@ -58,3 +70,8 @@ npm install
58
70
  npm run build
59
71
  npm pack
60
72
  ```
73
+
74
+ ## Skill
75
+
76
+ This repo includes an integration skill:
77
+ - `skills/centcom-claude-code.md`
package/dist/config.js CHANGED
@@ -1,12 +1,11 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- const DEFAULT_BASE_URL = "https://contro1.com/api/centcom/v1";
3
+ const DEFAULT_BASE_URL = "https://api.contro1.com/api/centcom/v1";
4
4
  const DEFAULT_TOOLS = "Write,Edit,Bash";
5
5
  const DEFAULT_TIMEOUT_MS = 300_000;
6
6
  const DEFAULT_POLL_INTERVAL_MS = 3_000;
7
7
  const DEFAULT_PRIORITY = "urgent";
8
8
  const DEFAULT_FALLBACK = "deny";
9
- const DEFAULT_CALLBACK_URL = "https://centcom.local/claude-code";
10
9
  function readLocalConfig() {
11
10
  const path = join(process.cwd(), ".centcom.json");
12
11
  if (!existsSync(path))
@@ -43,7 +42,7 @@ export function loadConfig() {
43
42
  const slaValue = env.CENTCOM_SLA_MINUTES ?? file.CENTCOM_SLA_MINUTES;
44
43
  const slaMinutes = slaValue !== undefined ? Number(slaValue) : undefined;
45
44
  const requiredRole = env.CENTCOM_REQUIRED_ROLE ?? file.CENTCOM_REQUIRED_ROLE;
46
- const callbackUrl = env.CENTCOM_CALLBACK_URL ?? file.CENTCOM_CALLBACK_URL ?? DEFAULT_CALLBACK_URL;
45
+ const callbackUrl = env.CENTCOM_CALLBACK_URL ?? file.CENTCOM_CALLBACK_URL;
47
46
  return {
48
47
  apiKey,
49
48
  baseUrl,
@@ -54,6 +53,6 @@ export function loadConfig() {
54
53
  slaMinutes: Number.isFinite(slaMinutes) ? slaMinutes : undefined,
55
54
  requiredRole: requiredRole || undefined,
56
55
  fallback,
57
- callbackUrl,
56
+ callbackUrl: callbackUrl || undefined,
58
57
  };
59
58
  }
package/dist/formatter.js CHANGED
@@ -4,7 +4,9 @@ function redactSecrets(text) {
4
4
  return text
5
5
  .replace(/(cc_(?:live|test)_[a-zA-Z0-9_-]{8,})/g, "[REDACTED_API_KEY]")
6
6
  .replace(/(whsec_[a-zA-Z0-9_-]{8,})/g, "[REDACTED_WEBHOOK_SECRET]")
7
- .replace(/(Bearer\s+)[^\s]+/gi, "$1[REDACTED_TOKEN]");
7
+ .replace(/(Bearer\s+)[^\s]+/gi, "$1[REDACTED_TOKEN]")
8
+ .replace(/("?(?:api[_-]?key|token|secret|password)"?\s*:\s*)"[^"]*"/gi, '$1"[REDACTED]"')
9
+ .replace(/((?:api[_-]?key|token|secret|password)\s*=\s*)[^\s]+/gi, "$1[REDACTED]");
8
10
  }
9
11
  function clip(text, size = MAX_PREVIEW) {
10
12
  if (text.length <= size)
@@ -52,6 +54,7 @@ export function formatRequest(input) {
52
54
  source: "claude-code",
53
55
  session_id: input.session_id,
54
56
  tool_name: tool,
57
+ tool_input_hash: createHash("sha256").update(stableStringify(toolInput)).digest("hex").slice(0, 24),
55
58
  cwd: input.cwd ?? "",
56
59
  },
57
60
  };
package/dist/hook.js CHANGED
@@ -76,17 +76,23 @@ async function main() {
76
76
  const formatted = formatRequest(input);
77
77
  const idempotencyKey = buildIdempotencyKey(input);
78
78
  try {
79
- const request = await client.createRequest({
79
+ const requestPayload = {
80
80
  type: "approval",
81
81
  question: formatted.question,
82
82
  context: formatted.context,
83
- callback_url: config.callbackUrl,
84
83
  priority: config.priority,
85
84
  required_role: config.requiredRole,
86
- metadata: formatted.metadata,
85
+ metadata: {
86
+ ...formatted.metadata,
87
+ idempotency_key: idempotencyKey,
88
+ },
87
89
  sla_minutes: config.slaMinutes,
88
90
  idempotency_key: idempotencyKey,
89
- });
91
+ };
92
+ if (config.callbackUrl) {
93
+ requestPayload.callback_url = config.callbackUrl;
94
+ }
95
+ const request = await client.createRequest(requestPayload);
90
96
  activeRequestId = request.id;
91
97
  const result = await client.waitForResponse(request.id, config.pollIntervalMs, config.timeoutMs);
92
98
  const approved = responseApproved(result.response ?? null);
package/dist/types.d.ts CHANGED
@@ -23,5 +23,5 @@ export interface CentcomClaudeConfig {
23
23
  slaMinutes?: number;
24
24
  requiredRole?: string;
25
25
  fallback: PermissionDecision;
26
- callbackUrl: string;
26
+ callbackUrl?: string;
27
27
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@contro1/claude-code",
3
- "version": "0.1.1",
4
- "description": "CENTCOM approval hook for Claude Code PreToolUse",
3
+ "version": "0.1.2",
4
+ "description": "CENTCOM approval hook for Claude Code PermissionRequest",
5
5
  "type": "module",
6
6
  "main": "dist/hook.js",
7
7
  "types": "dist/hook.d.ts",