@teamvibe/poller 0.1.18 → 0.1.20

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.
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Log git auth diagnostics to help debug credential issues.
3
+ */
4
+ export declare function logGitDiagnostics(): Promise<void>;
1
5
  interface BrainConfig {
2
6
  brainId: string;
3
7
  gitRepoUrl: string;
@@ -5,6 +5,36 @@ import { join } from 'path';
5
5
  import { config } from './config.js';
6
6
  import { logger } from './logger.js';
7
7
  const execAsync = promisify(exec);
8
+ // Clean env for git commands — npm/npx sets GIT_ASKPASS which
9
+ // prevents git credential helpers (like gh) from working.
10
+ const gitEnv = { ...process.env };
11
+ delete gitEnv['GIT_ASKPASS'];
12
+ function gitExec(command, options) {
13
+ return execAsync(command, { ...options, env: gitEnv });
14
+ }
15
+ /**
16
+ * Log git auth diagnostics to help debug credential issues.
17
+ */
18
+ export async function logGitDiagnostics() {
19
+ const diag = async (label, cmd) => {
20
+ try {
21
+ const { stdout } = await execAsync(cmd);
22
+ logger.info(`[git-diag] ${label}: ${stdout.trim()}`);
23
+ }
24
+ catch (e) {
25
+ logger.warn(`[git-diag] ${label}: FAILED - ${e instanceof Error ? e.message : e}`);
26
+ }
27
+ };
28
+ logger.info(`[git-diag] GIT_ASKPASS=${process.env['GIT_ASKPASS'] ?? '(unset)'}`);
29
+ logger.info(`[git-diag] GIT_TERMINAL_PROMPT=${process.env['GIT_TERMINAL_PROMPT'] ?? '(unset)'}`);
30
+ logger.info(`[git-diag] GITHUB_TOKEN=${process.env['GITHUB_TOKEN'] ? '***set***' : '(unset)'}`);
31
+ logger.info(`[git-diag] HOME=${process.env['HOME'] ?? '(unset)'}`);
32
+ await diag('git version', 'git --version');
33
+ await diag('gh version', 'gh --version | head -1');
34
+ await diag('credential.helper', 'git config --global credential.helper || echo "(not set)"');
35
+ await diag('credential.github', 'git config --global --get-regexp "credential.*github" || echo "(not set)"');
36
+ await diag('gh auth status', 'gh auth status 2>&1 || true');
37
+ }
8
38
  // Cooldown tracker per brain
9
39
  const lastUpdateTimes = new Map();
10
40
  /**
@@ -48,17 +78,17 @@ export async function getBrainPath(brain, channelId, workspaceId) {
48
78
  const brainDir = join(config.BRAINS_PATH, brain.brainId);
49
79
  if (!existsSync(brainDir)) {
50
80
  logger.info(`Cloning brain ${brain.brainId} from ${brain.gitRepoUrl} (branch: ${brain.branch})...`);
51
- await execAsync(`git clone --recurse-submodules ${brain.gitRepoUrl} ${brainDir}`);
81
+ await gitExec(`git clone --recurse-submodules ${brain.gitRepoUrl} ${brainDir}`);
52
82
  // Ensure the desired branch exists (handles empty repos and missing branches)
53
- const { stdout: currentBranch } = await execAsync('git branch --show-current', { cwd: brainDir });
83
+ const { stdout: currentBranch } = await gitExec('git branch --show-current', { cwd: brainDir });
54
84
  if (currentBranch.trim() !== brain.branch) {
55
85
  // Check if the branch exists on remote
56
- const { stdout: remoteBranches } = await execAsync('git branch -r', { cwd: brainDir });
86
+ const { stdout: remoteBranches } = await gitExec('git branch -r', { cwd: brainDir });
57
87
  if (remoteBranches.includes(`origin/${brain.branch}`)) {
58
- await execAsync(`git checkout ${brain.branch}`, { cwd: brainDir });
88
+ await gitExec(`git checkout ${brain.branch}`, { cwd: brainDir });
59
89
  }
60
90
  else {
61
- await execAsync(`git checkout -b ${brain.branch}`, { cwd: brainDir });
91
+ await gitExec(`git checkout -b ${brain.branch}`, { cwd: brainDir });
62
92
  }
63
93
  }
64
94
  logger.info(`Brain ${brain.brainId} cloned successfully`);
@@ -84,7 +114,7 @@ async function updateBrain(brainDir, brainId, branch) {
84
114
  }
85
115
  try {
86
116
  logger.info(`Updating brain ${brainId} at ${brainDir}...`);
87
- await execAsync(`git fetch origin ${branch} && git reset --hard origin/${branch} && git submodule update --init --recursive`, {
117
+ await gitExec(`git fetch origin ${branch} && git reset --hard origin/${branch} && git submodule update --init --recursive`, {
88
118
  cwd: brainDir,
89
119
  });
90
120
  lastUpdateTimes.set(brainId, Date.now());
@@ -103,7 +133,7 @@ export async function ensureBaseBrain() {
103
133
  const brainDir = config.BASE_BRAIN_PATH;
104
134
  if (!existsSync(brainDir)) {
105
135
  logger.info(`Cloning base brain from ${config.BASE_BRAIN_REPO} (branch: ${config.BASE_BRAIN_BRANCH})...`);
106
- await execAsync(`git clone --recurse-submodules --branch ${config.BASE_BRAIN_BRANCH} ${config.BASE_BRAIN_REPO} ${brainDir}`);
136
+ await gitExec(`git clone --recurse-submodules --branch ${config.BASE_BRAIN_BRANCH} ${config.BASE_BRAIN_REPO} ${brainDir}`);
107
137
  lastUpdateTimes.set('__base_brain__', Date.now());
108
138
  logger.info('Base brain cloned successfully');
109
139
  }
@@ -125,17 +155,17 @@ export async function pushBrainChanges(brainDir, brainId, workspaceId) {
125
155
  try {
126
156
  // Check if this is a git repo with a remote
127
157
  try {
128
- await execAsync('git remote get-url origin', { cwd: brainDir });
158
+ await gitExec('git remote get-url origin', { cwd: brainDir });
129
159
  }
130
160
  catch {
131
161
  logger.debug(`Brain ${brainId} has no git remote, skipping push`);
132
162
  return;
133
163
  }
134
164
  // Stage all changes
135
- await execAsync('git add -A', { cwd: brainDir });
165
+ await gitExec('git add -A', { cwd: brainDir });
136
166
  // Check if there are staged changes
137
167
  try {
138
- await execAsync('git diff --cached --quiet', { cwd: brainDir });
168
+ await gitExec('git diff --cached --quiet', { cwd: brainDir });
139
169
  // If the command succeeds (exit 0), there are no changes
140
170
  logger.debug(`Brain ${brainId} has no changes to push`);
141
171
  return;
@@ -143,8 +173,8 @@ export async function pushBrainChanges(brainDir, brainId, workspaceId) {
143
173
  catch {
144
174
  // Exit code 1 means there are changes — continue
145
175
  }
146
- await execAsync('git commit -m "auto: session update"', { cwd: brainDir });
147
- await execAsync('git push', { cwd: brainDir });
176
+ await gitExec('git commit -m "auto: session update"', { cwd: brainDir });
177
+ await gitExec('git push', { cwd: brainDir });
148
178
  logger.info(`Brain ${brainId} changes pushed successfully`);
149
179
  if (workspaceId)
150
180
  reportBrainSync(brainId, workspaceId, 'pushed');
package/dist/poller.js CHANGED
@@ -4,7 +4,7 @@ import { pollMessages, deleteMessage, extendVisibility, } from './sqs-poller.js'
4
4
  import { spawnClaudeCode, isAtCapacity, getActiveProcessCount } from './claude-spawner.js';
5
5
  import { sendSlackError, addReaction, getUserInfo, startTypingIndicator } from './slack-client.js';
6
6
  import { acquireSessionLock, releaseSessionLock, updateSessionId } from './session-store.js';
7
- import { getBrainPath, ensureDirectories, ensureBaseBrain, pushBrainChanges } from './brain-manager.js';
7
+ import { getBrainPath, ensureDirectories, ensureBaseBrain, pushBrainChanges, logGitDiagnostics } from './brain-manager.js';
8
8
  import { initAuth, stopRefresh } from './auth-provider.js';
9
9
  // Track active message processing
10
10
  const processingMessages = new Set();
@@ -254,6 +254,7 @@ export async function startPoller() {
254
254
  logger.info(` Queue: ${config.SQS_QUEUE_URL}`);
255
255
  logger.info(` Sessions table: ${config.SESSIONS_TABLE}`);
256
256
  }
257
+ await logGitDiagnostics();
257
258
  await ensureDirectories();
258
259
  await ensureBaseBrain();
259
260
  await pollLoop();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamvibe/poller",
3
- "version": "0.1.18",
3
+ "version": "0.1.20",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "bin": {