@teamvibe/poller 0.8.9-rc.1 → 0.8.9-rc.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 +35 -0
- package/dist/brain-inventory-emitter.d.ts +13 -0
- package/dist/brain-inventory-emitter.js +57 -0
- package/dist/brain-inventory.d.ts +28 -0
- package/dist/brain-inventory.js +209 -0
- package/dist/claude-spawner.js +35 -25
- package/npm-shrinkwrap.json +15 -2
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -100,6 +100,41 @@ The poller authenticates with the TeamVibe API using your token, then continuous
|
|
|
100
100
|
|
|
101
101
|
Passive usage percentages remain usable lower bounds until each window's `resetAt`. Without a reset time, passive windows expire five minutes after observation. Probe windows retain their 60-second TTL. Reset windows are ignored; known accounts still rank before unknown accounts. A window at the account's threshold defers selection until its expiry (reset for passive windows, TTL fallback without a reset).
|
|
102
102
|
|
|
103
|
+
### Brain inventory event (#517)
|
|
104
|
+
|
|
105
|
+
The existing `/events` queue carries `brain.inventory` alongside session, pool,
|
|
106
|
+
maintenance and poller events. Collection runs after pull, before Claude starts,
|
|
107
|
+
for normal **and maintenance** sessions. Per workspace/brain, unchanged snapshots
|
|
108
|
+
are suppressed for 10 minutes; changes to inventory, HEAD, or poller version emit
|
|
109
|
+
immediately. Collection failures log a generic warning and never block the agent.
|
|
110
|
+
|
|
111
|
+
`metadata` is additive and versioned:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
{
|
|
115
|
+
schemaVersion: 1,
|
|
116
|
+
workspaceId, brainId, repo, commit, pollerVersion,
|
|
117
|
+
skills: [{ name, path, discovered, state, description?, source?, ref?, sha?, contentHash? }],
|
|
118
|
+
mcpServers: [{ name, type, requiredEnv: string[], missingEnv: string[] }],
|
|
119
|
+
locks: { skillsLockJson: boolean, skillsLockYaml: boolean },
|
|
120
|
+
inventoryHash
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Only flat `.claude/skills/<name>/SKILL.md` directories are discovered. Nested skills
|
|
125
|
+
are reported as `not-discovered`; missing or invalid YAML frontmatter is `invalid`.
|
|
126
|
+
A lock entry makes a valid flat skill `vendored`; other valid flat skills are `own`.
|
|
127
|
+
Both `skills-lock.json` and `.claude/skills/skills.lock.yaml` are optional; wrapper
|
|
128
|
+
YAML provenance takes precedence over JSON. `contentHash` hashes actual skill files.
|
|
129
|
+
MCP `${VAR}` and `${VAR:-default}` placeholders in `env` and `headers` produce names
|
|
130
|
+
only, compared with defined keys in the resolved spawn environment. No commands,
|
|
131
|
+
URLs, headers, literal credentials, or environment values from `.mcp.json` are sent.
|
|
132
|
+
Symlinked files/directories are not followed. Git repository URL credentials and
|
|
133
|
+
query strings are removed from event attribution.
|
|
134
|
+
|
|
135
|
+
See [#517](https://github.com/teamvibeai/teamvibe.ai/issues/517). Read-only v1;
|
|
136
|
+
updates/additions and scheduled skill maintenance actions are follow-up work.
|
|
137
|
+
|
|
103
138
|
## Channel Git credentials
|
|
104
139
|
|
|
105
140
|
See [Agent Git credentials](docs/git-credentials.md) for multi-host channel bindings,
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare function shouldEmitInventory(previous: {
|
|
2
|
+
hash: string;
|
|
3
|
+
at: number;
|
|
4
|
+
} | undefined, hash: string, now: number): boolean;
|
|
5
|
+
/** Best effort telemetry; a malformed brain or unavailable git must never block Claude. */
|
|
6
|
+
export declare function emitBrainInventory(params: {
|
|
7
|
+
brainPath: string;
|
|
8
|
+
brainId: string;
|
|
9
|
+
workspaceId: string;
|
|
10
|
+
channelId: string;
|
|
11
|
+
repo: string;
|
|
12
|
+
envNames: string[];
|
|
13
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { collectBrainInventory } from './brain-inventory.js';
|
|
4
|
+
import { emitEvent } from './event-queue.js';
|
|
5
|
+
import { POLLER_VERSION } from './config.js';
|
|
6
|
+
import { logger } from './logger.js';
|
|
7
|
+
const exec = promisify(execFile);
|
|
8
|
+
const lastEmitted = new Map();
|
|
9
|
+
const INTERVAL_MS = 10 * 60_000;
|
|
10
|
+
export function shouldEmitInventory(previous, hash, now) {
|
|
11
|
+
return !previous || previous.hash !== hash || now - previous.at >= INTERVAL_MS;
|
|
12
|
+
}
|
|
13
|
+
/** Best effort telemetry; a malformed brain or unavailable git must never block Claude. */
|
|
14
|
+
export async function emitBrainInventory(params) {
|
|
15
|
+
try {
|
|
16
|
+
const inventory = await collectBrainInventory(params.brainPath, params.envNames);
|
|
17
|
+
const { stdout } = await exec('git', [
|
|
18
|
+
'-C',
|
|
19
|
+
params.brainPath,
|
|
20
|
+
'rev-parse',
|
|
21
|
+
'HEAD',
|
|
22
|
+
]);
|
|
23
|
+
const commit = stdout.trim();
|
|
24
|
+
// Include HEAD in throttling so an unrelated commit is still reflected in attribution.
|
|
25
|
+
const hash = `${inventory.inventoryHash}:${commit}:${POLLER_VERSION}`;
|
|
26
|
+
const key = `${params.workspaceId}:${params.brainId}`;
|
|
27
|
+
const now = Date.now();
|
|
28
|
+
if (!shouldEmitInventory(lastEmitted.get(key), hash, now))
|
|
29
|
+
return;
|
|
30
|
+
let repo = params.repo;
|
|
31
|
+
try {
|
|
32
|
+
const url = new URL(repo);
|
|
33
|
+
url.username = '';
|
|
34
|
+
url.password = '';
|
|
35
|
+
url.search = '';
|
|
36
|
+
url.hash = '';
|
|
37
|
+
repo = url.toString();
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
/* SSH remotes carry no HTTP credentials. */
|
|
41
|
+
}
|
|
42
|
+
emitEvent('brain.inventory', params.channelId, {
|
|
43
|
+
schemaVersion: 1,
|
|
44
|
+
workspaceId: params.workspaceId,
|
|
45
|
+
brainId: params.brainId,
|
|
46
|
+
repo,
|
|
47
|
+
commit,
|
|
48
|
+
pollerVersion: POLLER_VERSION,
|
|
49
|
+
...inventory,
|
|
50
|
+
});
|
|
51
|
+
lastEmitted.set(key, { hash, at: now });
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Do not echo parser exceptions: they can contain raw MCP credential values.
|
|
55
|
+
logger.warn('[BrainInventory] Could not collect inventory');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface InventorySkill {
|
|
2
|
+
name: string;
|
|
3
|
+
path: string;
|
|
4
|
+
discovered: boolean;
|
|
5
|
+
state: 'own' | 'vendored' | 'not-discovered' | 'invalid';
|
|
6
|
+
target?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
source?: string;
|
|
9
|
+
ref?: string;
|
|
10
|
+
sha?: string;
|
|
11
|
+
contentHash: string;
|
|
12
|
+
}
|
|
13
|
+
export interface InventoryMcpServer {
|
|
14
|
+
name: string;
|
|
15
|
+
type: 'stdio' | 'http';
|
|
16
|
+
requiredEnv: string[];
|
|
17
|
+
missingEnv: string[];
|
|
18
|
+
}
|
|
19
|
+
/** Read-only, deterministic collection. Receives env NAMES; never accepts env values. */
|
|
20
|
+
export declare function collectBrainInventory(brainPath: string, envNames: readonly string[]): Promise<{
|
|
21
|
+
inventoryHash: string;
|
|
22
|
+
skills: InventorySkill[];
|
|
23
|
+
mcpServers: InventoryMcpServer[];
|
|
24
|
+
locks: {
|
|
25
|
+
skillsLockJson: boolean;
|
|
26
|
+
skillsLockYaml: boolean;
|
|
27
|
+
};
|
|
28
|
+
}>;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readdir, readFile, lstat, realpath } from 'node:fs/promises';
|
|
3
|
+
import { join, posix, relative as relativePath, isAbsolute, sep, } from 'node:path';
|
|
4
|
+
import { parse } from 'yaml';
|
|
5
|
+
const hash = (value) => createHash('sha256').update(value).digest('hex');
|
|
6
|
+
const object = (value) => value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
7
|
+
async function optionalFile(path) {
|
|
8
|
+
try {
|
|
9
|
+
if (!(await lstat(path)).isFile())
|
|
10
|
+
return undefined; // Never follow symlinks out of the brain.
|
|
11
|
+
return await readFile(path, 'utf8');
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
if (['ENOENT', 'ENOTDIR'].includes(error.code ?? ''))
|
|
15
|
+
return undefined;
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function document(text, yaml = false) {
|
|
20
|
+
try {
|
|
21
|
+
return object(text === undefined
|
|
22
|
+
? undefined
|
|
23
|
+
: yaml
|
|
24
|
+
? parse(text, { maxAliasCount: 0 })
|
|
25
|
+
: JSON.parse(text));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return {};
|
|
29
|
+
} // Bad optional locks/config must not prevent the remaining inventory.
|
|
30
|
+
}
|
|
31
|
+
async function entries(path) {
|
|
32
|
+
try {
|
|
33
|
+
return (await readdir(path, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name, 'en'));
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (['ENOENT', 'ENOTDIR'].includes(error.code ?? ''))
|
|
37
|
+
return [];
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function directoryHash(path) {
|
|
42
|
+
const parts = [];
|
|
43
|
+
for (const entry of await entries(path)) {
|
|
44
|
+
if (['.git', 'node_modules'].includes(entry.name))
|
|
45
|
+
continue;
|
|
46
|
+
if (entry.isFile())
|
|
47
|
+
parts.push(`${entry.name}\0${hash(await readFile(join(path, entry.name)))}`);
|
|
48
|
+
if (entry.isDirectory())
|
|
49
|
+
parts.push(`${entry.name}/\0${await directoryHash(join(path, entry.name))}`);
|
|
50
|
+
}
|
|
51
|
+
return hash(parts.join('\n'));
|
|
52
|
+
}
|
|
53
|
+
/** Read-only, deterministic collection. Receives env NAMES; never accepts env values. */
|
|
54
|
+
export async function collectBrainInventory(brainPath, envNames) {
|
|
55
|
+
const brainRoot = await realpath(brainPath);
|
|
56
|
+
const skillsPath = join(brainRoot, '.claude/skills');
|
|
57
|
+
const [jsonText, yamlText, mcpText] = await Promise.all([
|
|
58
|
+
optionalFile(join(brainPath, 'skills-lock.json')),
|
|
59
|
+
optionalFile(join(skillsPath, 'skills.lock.yaml')),
|
|
60
|
+
optionalFile(join(brainPath, '.mcp.json')),
|
|
61
|
+
]);
|
|
62
|
+
const jsonLocks = object(document(jsonText)['skills']);
|
|
63
|
+
const yamlLocks = object(document(yamlText, true)['skills']);
|
|
64
|
+
const skills = [];
|
|
65
|
+
async function walk(path, relative, depth, ancestors) {
|
|
66
|
+
for (const entry of await entries(path)) {
|
|
67
|
+
if ((!entry.isDirectory() && !entry.isSymbolicLink()) ||
|
|
68
|
+
['.git', 'node_modules'].includes(entry.name))
|
|
69
|
+
continue;
|
|
70
|
+
let child = join(path, entry.name);
|
|
71
|
+
const skillPath = posix.join(relative, entry.name);
|
|
72
|
+
let target;
|
|
73
|
+
const invalidLink = () => skills.push({
|
|
74
|
+
name: entry.name,
|
|
75
|
+
path: skillPath,
|
|
76
|
+
discovered: false,
|
|
77
|
+
state: 'invalid',
|
|
78
|
+
contentHash: hash(''), // No target files are read for unsafe or dangling links.
|
|
79
|
+
});
|
|
80
|
+
if (entry.isSymbolicLink()) {
|
|
81
|
+
try {
|
|
82
|
+
child = await realpath(child);
|
|
83
|
+
const resolved = relativePath(brainRoot, child);
|
|
84
|
+
if (resolved === '..' ||
|
|
85
|
+
resolved.startsWith(`..${sep}`) ||
|
|
86
|
+
isAbsolute(resolved) ||
|
|
87
|
+
ancestors.has(child)) {
|
|
88
|
+
invalidLink();
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (!(await lstat(child)).isDirectory()) {
|
|
92
|
+
invalidLink();
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
target = resolved.split(sep).join('/');
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (!['ENOENT', 'ENOTDIR', 'ELOOP'].includes(error.code ?? ''))
|
|
99
|
+
throw error;
|
|
100
|
+
invalidLink();
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const text = await optionalFile(join(child, 'SKILL.md'));
|
|
105
|
+
if (text !== undefined) {
|
|
106
|
+
const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
|
|
107
|
+
let front = {};
|
|
108
|
+
try {
|
|
109
|
+
if (match)
|
|
110
|
+
front = object(parse(match[1], { maxAliasCount: 0 }));
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
/* invalid */
|
|
114
|
+
}
|
|
115
|
+
const valid = (front['name'] === undefined || front['name'] === entry.name) &&
|
|
116
|
+
typeof front['description'] === 'string' &&
|
|
117
|
+
!!front['description'].trim();
|
|
118
|
+
const flat = depth === 0;
|
|
119
|
+
const provenance = flat
|
|
120
|
+
? {
|
|
121
|
+
...object(jsonLocks[entry.name]),
|
|
122
|
+
...object(yamlLocks[entry.name]),
|
|
123
|
+
}
|
|
124
|
+
: {};
|
|
125
|
+
const vendored = flat &&
|
|
126
|
+
(target !== undefined ||
|
|
127
|
+
Object.hasOwn(jsonLocks, entry.name) ||
|
|
128
|
+
Object.hasOwn(yamlLocks, entry.name));
|
|
129
|
+
const skill = {
|
|
130
|
+
name: entry.name,
|
|
131
|
+
path: skillPath,
|
|
132
|
+
discovered: flat && valid,
|
|
133
|
+
state: !valid
|
|
134
|
+
? 'invalid'
|
|
135
|
+
: !flat
|
|
136
|
+
? 'not-discovered'
|
|
137
|
+
: vendored
|
|
138
|
+
? 'vendored'
|
|
139
|
+
: 'own',
|
|
140
|
+
contentHash: await directoryHash(child),
|
|
141
|
+
};
|
|
142
|
+
if (target !== undefined)
|
|
143
|
+
skill.target = target;
|
|
144
|
+
if (typeof front['description'] === 'string')
|
|
145
|
+
skill.description = front['description'];
|
|
146
|
+
for (const key of ['source', 'ref', 'sha']) {
|
|
147
|
+
if (typeof provenance[key] === 'string') {
|
|
148
|
+
let value = provenance[key];
|
|
149
|
+
if (key === 'source') {
|
|
150
|
+
try {
|
|
151
|
+
const url = new URL(value);
|
|
152
|
+
url.username = '';
|
|
153
|
+
url.password = '';
|
|
154
|
+
url.search = '';
|
|
155
|
+
url.hash = '';
|
|
156
|
+
value = url.toString();
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
/* owner/repo or SSH syntax */
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
skill[key] = value;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
skills.push(skill);
|
|
166
|
+
}
|
|
167
|
+
else if (target !== undefined) {
|
|
168
|
+
invalidLink();
|
|
169
|
+
}
|
|
170
|
+
await walk(child, skillPath, depth + 1, new Set([...ancestors, child]));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
await walk(skillsPath, '.claude/skills', 0, new Set([brainRoot, skillsPath]));
|
|
174
|
+
const present = new Set(envNames);
|
|
175
|
+
const mcpServers = Object.entries(object(document(mcpText)['mcpServers']))
|
|
176
|
+
.sort(([a], [b]) => a.localeCompare(b, 'en'))
|
|
177
|
+
.map(([name, raw]) => {
|
|
178
|
+
const server = object(raw);
|
|
179
|
+
const names = new Set();
|
|
180
|
+
// Only env/headers placeholder references are part of the credential contract.
|
|
181
|
+
for (const value of [
|
|
182
|
+
...Object.values(object(server['env'])),
|
|
183
|
+
...Object.values(object(server['headers'])),
|
|
184
|
+
]) {
|
|
185
|
+
if (typeof value !== 'string')
|
|
186
|
+
continue;
|
|
187
|
+
for (const match of value.matchAll(/\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-[^}]*)?\}/g))
|
|
188
|
+
names.add(match[1]);
|
|
189
|
+
}
|
|
190
|
+
const requiredEnv = [...names].sort();
|
|
191
|
+
return {
|
|
192
|
+
name,
|
|
193
|
+
type: server['type'] === 'http' || server['type'] === 'sse' || server['url']
|
|
194
|
+
? 'http'
|
|
195
|
+
: 'stdio',
|
|
196
|
+
requiredEnv,
|
|
197
|
+
missingEnv: requiredEnv.filter((name) => !present.has(name)),
|
|
198
|
+
};
|
|
199
|
+
});
|
|
200
|
+
const inventory = {
|
|
201
|
+
skills,
|
|
202
|
+
mcpServers,
|
|
203
|
+
locks: {
|
|
204
|
+
skillsLockJson: jsonText !== undefined,
|
|
205
|
+
skillsLockYaml: yamlText !== undefined,
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
return { ...inventory, inventoryHash: hash(JSON.stringify(inventory)) };
|
|
209
|
+
}
|
package/dist/claude-spawner.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { emitBrainInventory } from './brain-inventory-emitter.js';
|
|
1
2
|
import { END_OF_TURN_NO_FURTHER_RESPONSE_SENTINEL } from './end-of-turn-sentinel.js';
|
|
2
3
|
import { createSessionInstance, writeSessionOwner } from './session-instance.js';
|
|
3
4
|
import { buildGitCredentialEnv } from './git-credential-env.js';
|
|
@@ -913,6 +914,40 @@ async function runClaudeCode(msg, sessionLog, cwd, directory, sessionId, isFirst
|
|
|
913
914
|
workspaceId: msg.teamvibe.workspaceId,
|
|
914
915
|
channelId: msg.teamvibe.channelId,
|
|
915
916
|
});
|
|
917
|
+
// Prepend persistent bin dir to PATH if configured
|
|
918
|
+
const envPath = config.PERSISTENT_STORAGE_PATH
|
|
919
|
+
? `${config.PERSISTENT_STORAGE_PATH}/bin:${process.env['PATH'] || ''}`
|
|
920
|
+
: process.env['PATH'];
|
|
921
|
+
if (runtime?.account)
|
|
922
|
+
poolInfo(sessionLog, `token target account=${poolLabel(runtime.account)} session=${JSON.stringify(sessionId ?? 'new')} credential=selected account token`);
|
|
923
|
+
const spawnEnv = buildSpawnEnv({
|
|
924
|
+
spawnSecretsEnv: spawnSecrets.env.unwrap(),
|
|
925
|
+
processEnv: process.env,
|
|
926
|
+
envPath,
|
|
927
|
+
claudeConfigDir: runtime?.claudeConfigDir ?? getBaseBrainPath(),
|
|
928
|
+
persistentStoragePath: config.PERSISTENT_STORAGE_PATH,
|
|
929
|
+
slackContext,
|
|
930
|
+
teamvibeApiUrl: config.TEAMVIBE_API_URL,
|
|
931
|
+
teamvibePollerToken: config.TEAMVIBE_POLLER_TOKEN || '',
|
|
932
|
+
teamvibeWorkspaceId: msg.teamvibe.workspaceId,
|
|
933
|
+
teamvibeChannelId: msg.teamvibe.channelId,
|
|
934
|
+
teamvibeBotId: msg.teamvibe.botId,
|
|
935
|
+
teamvibePollerId: getPollerId() || '',
|
|
936
|
+
inboxThreadId: msg.thread_id,
|
|
937
|
+
brainPath: directory.path,
|
|
938
|
+
subscriptionAccount: runtime?.account,
|
|
939
|
+
gitCredential,
|
|
940
|
+
gitTokenFile: gitDelivery.session?.tokenFile,
|
|
941
|
+
});
|
|
942
|
+
// Ordinary and maintenance sessions pass here after pull and all credential overlays.
|
|
943
|
+
// Only defined names from the final spawn environment cross the collector boundary.
|
|
944
|
+
if (directory.brainId && msg.teamvibe.brain?.gitRepoUrl) {
|
|
945
|
+
await emitBrainInventory({
|
|
946
|
+
brainPath: cwd, brainId: directory.brainId, workspaceId: directory.workspaceId,
|
|
947
|
+
channelId: msg.teamvibe.channelId, repo: msg.teamvibe.brain.gitRepoUrl,
|
|
948
|
+
envNames: Object.keys(spawnEnv).filter(name => spawnEnv[name] !== undefined),
|
|
949
|
+
});
|
|
950
|
+
}
|
|
916
951
|
if (runtime?.account)
|
|
917
952
|
await validateAccount(runtime.account, runtime.resolved);
|
|
918
953
|
if (runtime?.account && !runtime.shouldStop()) {
|
|
@@ -1009,31 +1044,6 @@ async function runClaudeCode(msg, sessionLog, cwd, directory, sessionId, isFirst
|
|
|
1009
1044
|
const imageCount = (msg.inlineImages ?? []).filter((img) => img.data || img.url).length;
|
|
1010
1045
|
sessionLog.info(`Inline image content: ${imageCount} image(s) sent via stream-json stdin (not logged)`);
|
|
1011
1046
|
}
|
|
1012
|
-
// Prepend persistent bin dir to PATH if configured
|
|
1013
|
-
const envPath = config.PERSISTENT_STORAGE_PATH
|
|
1014
|
-
? `${config.PERSISTENT_STORAGE_PATH}/bin:${process.env['PATH'] || ''}`
|
|
1015
|
-
: process.env['PATH'];
|
|
1016
|
-
if (runtime?.account)
|
|
1017
|
-
poolInfo(sessionLog, `token target account=${poolLabel(runtime.account)} session=${JSON.stringify(sessionId ?? 'new')} credential=selected account token`);
|
|
1018
|
-
const spawnEnv = buildSpawnEnv({
|
|
1019
|
-
spawnSecretsEnv: spawnSecrets.env.unwrap(),
|
|
1020
|
-
processEnv: process.env,
|
|
1021
|
-
envPath,
|
|
1022
|
-
claudeConfigDir: runtime?.claudeConfigDir ?? getBaseBrainPath(),
|
|
1023
|
-
persistentStoragePath: config.PERSISTENT_STORAGE_PATH,
|
|
1024
|
-
slackContext,
|
|
1025
|
-
teamvibeApiUrl: config.TEAMVIBE_API_URL,
|
|
1026
|
-
teamvibePollerToken: config.TEAMVIBE_POLLER_TOKEN || '',
|
|
1027
|
-
teamvibeWorkspaceId: msg.teamvibe.workspaceId,
|
|
1028
|
-
teamvibeChannelId: msg.teamvibe.channelId,
|
|
1029
|
-
teamvibeBotId: msg.teamvibe.botId,
|
|
1030
|
-
teamvibePollerId: getPollerId() || '',
|
|
1031
|
-
inboxThreadId: msg.thread_id,
|
|
1032
|
-
brainPath: directory.path,
|
|
1033
|
-
subscriptionAccount: runtime?.account,
|
|
1034
|
-
gitCredential,
|
|
1035
|
-
gitTokenFile: gitDelivery.session?.tokenFile,
|
|
1036
|
-
});
|
|
1037
1047
|
// Capture the actual credentials for this attempt, including pool selection
|
|
1038
1048
|
// and secrets overlays, regardless of whether alias exposure is enabled.
|
|
1039
1049
|
const outputTokens = [spawnEnv['CLAUDE_CODE_OAUTH_TOKEN'], spawnEnv['TEAMVIBE_CLAUDE_OAUTH_TOKEN'], spawnEnv['GH_TOKEN'], spawnEnv['GITHUB_TOKEN']]
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamvibe/poller",
|
|
3
|
-
"version": "0.8.9-rc.
|
|
3
|
+
"version": "0.8.9-rc.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@teamvibe/poller",
|
|
9
|
-
"version": "0.8.9-rc.
|
|
9
|
+
"version": "0.8.9-rc.2",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@aws-sdk/client-dynamodb": "3.1108.0",
|
|
12
12
|
"@aws-sdk/client-sqs": "3.1108.0",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"@slack/web-api": "^7.0.0",
|
|
16
16
|
"croner": "^10.0.1",
|
|
17
17
|
"dotenv": "^17.2.3",
|
|
18
|
+
"yaml": "2.8.1",
|
|
18
19
|
"zod": "^3.22.0"
|
|
19
20
|
},
|
|
20
21
|
"bin": {
|
|
@@ -1594,6 +1595,18 @@
|
|
|
1594
1595
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
|
1595
1596
|
"license": "MIT"
|
|
1596
1597
|
},
|
|
1598
|
+
"node_modules/yaml": {
|
|
1599
|
+
"version": "2.8.1",
|
|
1600
|
+
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz",
|
|
1601
|
+
"integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
|
|
1602
|
+
"license": "ISC",
|
|
1603
|
+
"bin": {
|
|
1604
|
+
"yaml": "bin.mjs"
|
|
1605
|
+
},
|
|
1606
|
+
"engines": {
|
|
1607
|
+
"node": ">= 14.6"
|
|
1608
|
+
}
|
|
1609
|
+
},
|
|
1597
1610
|
"node_modules/zod": {
|
|
1598
1611
|
"version": "3.25.76",
|
|
1599
1612
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamvibe/poller",
|
|
3
|
-
"version": "0.8.9-rc.
|
|
3
|
+
"version": "0.8.9-rc.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"@slack/web-api": "^7.0.0",
|
|
35
35
|
"croner": "^10.0.1",
|
|
36
36
|
"dotenv": "^17.2.3",
|
|
37
|
+
"yaml": "2.8.1",
|
|
37
38
|
"zod": "^3.22.0"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|