wayfind 2.0.44 → 2.0.45
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/bin/team-context.js +114 -44
- package/package.json +1 -1
- package/templates/deploy/.env.example +33 -69
- package/templates/deploy/docker-compose.yml +1 -44
package/bin/team-context.js
CHANGED
|
@@ -3812,17 +3812,25 @@ async function runDeploy(args) {
|
|
|
3812
3812
|
}
|
|
3813
3813
|
|
|
3814
3814
|
/**
|
|
3815
|
-
* Scaffold a per-team container config
|
|
3815
|
+
* Scaffold a per-team container config in the team's registered repo (deploy/ subdir).
|
|
3816
|
+
* Falls back to ~/.claude/team-context/teams/<teamId>/deploy/ if no repo is registered.
|
|
3816
3817
|
*/
|
|
3817
3818
|
function deployTeamInit(teamId, { port } = {}) {
|
|
3818
|
-
|
|
3819
|
-
if (!teamsBaseDir) {
|
|
3819
|
+
if (!HOME) {
|
|
3820
3820
|
console.error('Cannot resolve home directory.');
|
|
3821
3821
|
process.exit(1);
|
|
3822
3822
|
}
|
|
3823
3823
|
|
|
3824
|
-
|
|
3825
|
-
const
|
|
3824
|
+
// Resolve deploy dir: team repo path first, fallback to store-adjacent
|
|
3825
|
+
const config = readContextConfig();
|
|
3826
|
+
const teamEntry = config.teams && config.teams[teamId];
|
|
3827
|
+
const teamContextPath = teamEntry ? teamEntry.path : null;
|
|
3828
|
+
const deployDir = teamContextPath
|
|
3829
|
+
? path.join(teamContextPath, 'deploy')
|
|
3830
|
+
: path.join(HOME, '.claude', 'team-context', 'teams', teamId, 'deploy');
|
|
3831
|
+
|
|
3832
|
+
// Ensure the per-team store dir exists
|
|
3833
|
+
const storeDir = path.join(HOME, '.claude', 'team-context', 'teams', teamId, 'content-store');
|
|
3826
3834
|
|
|
3827
3835
|
// Check for duplicate running container
|
|
3828
3836
|
const psResult = spawnSync('docker', ['ps', '--filter', `label=com.wayfind.team=${teamId}`, '--format', '{{.Names}}'], { stdio: 'pipe' });
|
|
@@ -3838,22 +3846,42 @@ function deployTeamInit(teamId, { port } = {}) {
|
|
|
3838
3846
|
console.log(`Scaffolding deploy config for team: ${teamId}`);
|
|
3839
3847
|
console.log(`Deploy dir: ${deployDir}`);
|
|
3840
3848
|
|
|
3841
|
-
//
|
|
3842
|
-
const config = readContextConfig();
|
|
3843
|
-
const teamEntry = config.teams && config.teams[teamId];
|
|
3844
|
-
const teamContextPath = teamEntry ? teamEntry.path : null;
|
|
3845
|
-
|
|
3846
|
-
// Assign port (default 3141; if taken, user should pass --port)
|
|
3847
|
-
const assignedPort = port || 3141;
|
|
3849
|
+
// Auto-detect port: find all wayfind containers, pick next available
|
|
3848
3850
|
const containerName = `wayfind-${teamId}`;
|
|
3851
|
+
let assignedPort = port;
|
|
3852
|
+
if (!assignedPort) {
|
|
3853
|
+
const usedPorts = new Set();
|
|
3854
|
+
// Check labeled containers
|
|
3855
|
+
const portsResult = spawnSync('docker', [
|
|
3856
|
+
'ps', '--filter', 'label=com.wayfind.team',
|
|
3857
|
+
'--format', '{{.Ports}}',
|
|
3858
|
+
], { stdio: 'pipe' });
|
|
3859
|
+
// Also check legacy container named "wayfind"
|
|
3860
|
+
const legacyPortsResult = spawnSync('docker', [
|
|
3861
|
+
'ps', '--filter', 'name=^wayfind',
|
|
3862
|
+
'--format', '{{.Ports}}',
|
|
3863
|
+
], { stdio: 'pipe' });
|
|
3864
|
+
const allPortOutput = [
|
|
3865
|
+
(portsResult.stdout || '').toString(),
|
|
3866
|
+
(legacyPortsResult.stdout || '').toString(),
|
|
3867
|
+
].join('\n');
|
|
3868
|
+
// Extract host ports from "0.0.0.0:3141->3141/tcp" patterns
|
|
3869
|
+
for (const match of allPortOutput.matchAll(/:(\d+)->/g)) {
|
|
3870
|
+
usedPorts.add(parseInt(match[1], 10));
|
|
3871
|
+
}
|
|
3872
|
+
assignedPort = 3141;
|
|
3873
|
+
while (usedPorts.has(assignedPort)) assignedPort++;
|
|
3874
|
+
if (assignedPort !== 3141) {
|
|
3875
|
+
console.log(`Port 3141 in use — assigning port ${assignedPort}`);
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3849
3878
|
|
|
3850
3879
|
// Build docker-compose.yml content with per-team overrides
|
|
3851
3880
|
const templatePath = path.join(DEPLOY_TEMPLATES_DIR, 'docker-compose.yml');
|
|
3852
3881
|
let composeContent = fs.readFileSync(templatePath, 'utf8');
|
|
3853
3882
|
composeContent = composeContent
|
|
3854
3883
|
.replace(/container_name: wayfind/, `container_name: ${containerName}`)
|
|
3855
|
-
.replace(/- "3141:3141"/, `- "${assignedPort}:3141"`)
|
|
3856
|
-
.replace(/(TEAM_CONTEXT_TENANT_ID:.*$)/m, `TEAM_CONTEXT_TENANT_ID: \${TEAM_CONTEXT_TENANT_ID:-${teamId}}`);
|
|
3884
|
+
.replace(/- "3141:3141"/, `- "${assignedPort}:3141"`);
|
|
3857
3885
|
|
|
3858
3886
|
// Inject Docker label for discovery
|
|
3859
3887
|
composeContent = composeContent.replace(
|
|
@@ -3869,42 +3897,63 @@ function deployTeamInit(teamId, { port } = {}) {
|
|
|
3869
3897
|
console.log(' docker-compose.yml — already exists, skipping');
|
|
3870
3898
|
}
|
|
3871
3899
|
|
|
3872
|
-
// .env.example
|
|
3900
|
+
// .env.example — full reference with all options
|
|
3873
3901
|
const envExampleSrc = path.join(DEPLOY_TEMPLATES_DIR, '.env.example');
|
|
3874
3902
|
const envExampleDst = path.join(deployDir, '.env.example');
|
|
3875
3903
|
if (!fs.existsSync(envExampleDst) && fs.existsSync(envExampleSrc)) {
|
|
3876
|
-
|
|
3877
|
-
|
|
3878
|
-
envContent += `\nTEAM_CONTEXT_TEAM_CONTEXT_PATH=${teamContextPath}\n`;
|
|
3879
|
-
}
|
|
3880
|
-
fs.writeFileSync(envExampleDst, envContent, 'utf8');
|
|
3881
|
-
console.log(' .env.example — created');
|
|
3904
|
+
fs.copyFileSync(envExampleSrc, envExampleDst);
|
|
3905
|
+
console.log(' .env.example — created (full reference)');
|
|
3882
3906
|
}
|
|
3883
3907
|
|
|
3884
|
-
// .env
|
|
3908
|
+
// .env — minimal seed with only required keys
|
|
3885
3909
|
const envPath = path.join(deployDir, '.env');
|
|
3886
|
-
if (!fs.existsSync(envPath)
|
|
3887
|
-
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
3896
|
-
|
|
3897
|
-
|
|
3910
|
+
if (!fs.existsSync(envPath)) {
|
|
3911
|
+
const ghToken = detectGitHubToken();
|
|
3912
|
+
const lines = [
|
|
3913
|
+
'# Wayfind — required configuration',
|
|
3914
|
+
'# See .env.example for all available options.',
|
|
3915
|
+
'',
|
|
3916
|
+
'# Anthropic API key (for digests and bot answers)',
|
|
3917
|
+
'ANTHROPIC_API_KEY=sk-ant-your-key',
|
|
3918
|
+
'',
|
|
3919
|
+
'# GitHub token (for pulling team journals and signals)',
|
|
3920
|
+
`GITHUB_TOKEN=${ghToken || ''}`,
|
|
3921
|
+
'',
|
|
3922
|
+
`TEAM_CONTEXT_TENANT_ID=${teamId}`,
|
|
3923
|
+
];
|
|
3924
|
+
// Set volume mount path so docker-compose.yml resolves correctly
|
|
3925
|
+
if (teamContextPath) {
|
|
3926
|
+
lines.push(`TEAM_CONTEXT_TEAM_CONTEXT_PATH=${teamContextPath}`);
|
|
3927
|
+
}
|
|
3928
|
+
fs.writeFileSync(envPath, lines.join('\n') + '\n', 'utf8');
|
|
3929
|
+
console.log(' .env — created (fill in ANTHROPIC_API_KEY)');
|
|
3930
|
+
if (ghToken) {
|
|
3898
3931
|
console.log(' GITHUB_TOKEN — auto-detected from gh CLI');
|
|
3899
3932
|
}
|
|
3933
|
+
} else {
|
|
3934
|
+
console.log(' .env — already exists, skipping');
|
|
3935
|
+
}
|
|
3936
|
+
|
|
3937
|
+
// Ensure deploy/.env is gitignored if we're in a repo
|
|
3938
|
+
if (teamContextPath) {
|
|
3939
|
+
const gitignorePath = path.join(teamContextPath, '.gitignore');
|
|
3940
|
+
const gitignoreEntry = 'deploy/.env';
|
|
3941
|
+
if (fs.existsSync(gitignorePath)) {
|
|
3942
|
+
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
3943
|
+
if (!content.includes(gitignoreEntry)) {
|
|
3944
|
+
fs.appendFileSync(gitignorePath, `\n${gitignoreEntry}\n`);
|
|
3945
|
+
console.log(' .gitignore — added deploy/.env');
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3900
3948
|
}
|
|
3901
3949
|
|
|
3902
3950
|
console.log('');
|
|
3903
3951
|
console.log('Next steps:');
|
|
3904
|
-
console.log(` 1.
|
|
3952
|
+
console.log(` 1. Set ANTHROPIC_API_KEY in ${deployDir}/.env`);
|
|
3905
3953
|
console.log(` 2. cd "${deployDir}" && docker compose up -d`);
|
|
3906
3954
|
console.log(` 3. Verify: curl http://localhost:${assignedPort}/healthz`);
|
|
3907
3955
|
console.log('');
|
|
3956
|
+
console.log('See .env.example for optional config (Slack, embeddings, signals, schedules).');
|
|
3908
3957
|
console.log(`Tip: run "wayfind deploy list" to see all running team containers.`);
|
|
3909
3958
|
|
|
3910
3959
|
telemetry.capture('deploy_team_init', { teamId }, CLI_USER);
|
|
@@ -3928,7 +3977,7 @@ function deployList() {
|
|
|
3928
3977
|
const rows = (psResult.stdout || '').toString().trim();
|
|
3929
3978
|
if (!rows) {
|
|
3930
3979
|
console.log('No Wayfind team containers running.');
|
|
3931
|
-
console.log('Start one with: wayfind deploy --team <teamId>
|
|
3980
|
+
console.log('Start one with: wayfind deploy --team <teamId>');
|
|
3932
3981
|
return;
|
|
3933
3982
|
}
|
|
3934
3983
|
|
|
@@ -4170,8 +4219,10 @@ async function runStart() {
|
|
|
4170
4219
|
|
|
4171
4220
|
// Validate required env vars before proceeding
|
|
4172
4221
|
const missing = [];
|
|
4173
|
-
|
|
4174
|
-
|
|
4222
|
+
// Slack tokens are only required for modes that run the bot
|
|
4223
|
+
const needsSlack = ['bot', 'all-in-one'].includes(mode) && !process.env.TEAM_CONTEXT_NO_SLACK;
|
|
4224
|
+
if (needsSlack && !process.env.SLACK_BOT_TOKEN) missing.push('SLACK_BOT_TOKEN');
|
|
4225
|
+
if (needsSlack && !process.env.SLACK_APP_TOKEN) missing.push('SLACK_APP_TOKEN');
|
|
4175
4226
|
if (!process.env.ANTHROPIC_API_KEY) missing.push('ANTHROPIC_API_KEY');
|
|
4176
4227
|
if (missing.length > 0) {
|
|
4177
4228
|
console.error('');
|
|
@@ -4181,6 +4232,8 @@ async function runStart() {
|
|
|
4181
4232
|
console.error(' cp deploy/.env.example deploy/.env');
|
|
4182
4233
|
console.error(' # Fill in your tokens, then: docker compose up -d');
|
|
4183
4234
|
console.error('');
|
|
4235
|
+
console.error('Tip: set TEAM_CONTEXT_NO_SLACK=1 to run without Slack integration.');
|
|
4236
|
+
console.error('');
|
|
4184
4237
|
process.exit(1);
|
|
4185
4238
|
}
|
|
4186
4239
|
|
|
@@ -5181,13 +5234,13 @@ const COMMANDS = {
|
|
|
5181
5234
|
if (labelDir && fs.existsSync(path.join(labelDir, 'docker-compose.yml'))) {
|
|
5182
5235
|
composeDir = labelDir;
|
|
5183
5236
|
} else {
|
|
5184
|
-
//
|
|
5185
|
-
const
|
|
5186
|
-
if (
|
|
5187
|
-
for (const tid of
|
|
5188
|
-
|
|
5237
|
+
// Check team repo paths from context.json first
|
|
5238
|
+
const updateConfig = readContextConfig();
|
|
5239
|
+
if (updateConfig.teams) {
|
|
5240
|
+
for (const [tid, entry] of Object.entries(updateConfig.teams)) {
|
|
5241
|
+
if (!entry.path) continue;
|
|
5242
|
+
const candidate = path.join(entry.path, 'deploy');
|
|
5189
5243
|
if (fs.existsSync(path.join(candidate, 'docker-compose.yml'))) {
|
|
5190
|
-
// Check if this compose file manages this container
|
|
5191
5244
|
const checkResult = spawnSync('docker', ['compose', 'ps', '--format', '{{.Name}}'], { cwd: candidate, stdio: 'pipe' });
|
|
5192
5245
|
const composeContainers = (checkResult.stdout || '').toString();
|
|
5193
5246
|
if (composeContainers.includes(containerName)) {
|
|
@@ -5197,6 +5250,23 @@ const COMMANDS = {
|
|
|
5197
5250
|
}
|
|
5198
5251
|
}
|
|
5199
5252
|
}
|
|
5253
|
+
// Fallback: check store-adjacent deploy dirs
|
|
5254
|
+
if (!composeDir) {
|
|
5255
|
+
const teamsBase = HOME ? path.join(HOME, '.claude', 'team-context', 'teams') : '';
|
|
5256
|
+
if (teamsBase && fs.existsSync(teamsBase)) {
|
|
5257
|
+
for (const tid of fs.readdirSync(teamsBase)) {
|
|
5258
|
+
const candidate = path.join(teamsBase, tid, 'deploy');
|
|
5259
|
+
if (fs.existsSync(path.join(candidate, 'docker-compose.yml'))) {
|
|
5260
|
+
const checkResult = spawnSync('docker', ['compose', 'ps', '--format', '{{.Name}}'], { cwd: candidate, stdio: 'pipe' });
|
|
5261
|
+
const composeContainers = (checkResult.stdout || '').toString();
|
|
5262
|
+
if (composeContainers.includes(containerName)) {
|
|
5263
|
+
composeDir = candidate;
|
|
5264
|
+
break;
|
|
5265
|
+
}
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
}
|
|
5269
|
+
}
|
|
5200
5270
|
// Legacy fallback
|
|
5201
5271
|
if (!composeDir) {
|
|
5202
5272
|
const legacyCandidates = [process.cwd(), path.join(HOME || '', 'team-context', 'deploy')];
|
package/package.json
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
# Wayfind — Docker environment configuration
|
|
2
2
|
# Copy to .env and fill in your values: cp .env.example .env
|
|
3
|
+
#
|
|
4
|
+
# Only ANTHROPIC_API_KEY and GITHUB_TOKEN are required.
|
|
5
|
+
# Everything else is optional — add as needed.
|
|
3
6
|
|
|
4
7
|
# ── Required ──────────────────────────────────────────────────────────────────
|
|
5
8
|
|
|
6
|
-
# Slack bot tokens — from the Slack app you created using slack-app-manifest.json.
|
|
7
|
-
# Go to api.slack.com/apps → your Wayfind app → OAuth & Permissions for the bot token,
|
|
8
|
-
# and Basic Information → App-Level Tokens for the app token.
|
|
9
|
-
SLACK_BOT_TOKEN=xoxb-your-bot-token
|
|
10
|
-
SLACK_APP_TOKEN=xapp-your-app-token
|
|
11
|
-
|
|
12
9
|
# Anthropic API key (for digests and bot answers)
|
|
13
10
|
# Get one at console.anthropic.com
|
|
14
11
|
ANTHROPIC_API_KEY=sk-ant-your-key
|
|
@@ -18,73 +15,33 @@ ANTHROPIC_API_KEY=sk-ant-your-key
|
|
|
18
15
|
# Needs read access to the team-context repo.
|
|
19
16
|
GITHUB_TOKEN=
|
|
20
17
|
|
|
21
|
-
# ──
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# Fallback: webhook. Used only if bot delivery fails.
|
|
18
|
+
# ── Slack (for digest delivery and bot) ──────────────────────────────────────
|
|
19
|
+
# Without these, set TEAM_CONTEXT_NO_SLACK=1 and the container runs in
|
|
20
|
+
# scheduler/worker-only mode (no digest posting, no bot answers).
|
|
25
21
|
|
|
26
|
-
#
|
|
27
|
-
#
|
|
22
|
+
# SLACK_BOT_TOKEN=xoxb-your-bot-token
|
|
23
|
+
# SLACK_APP_TOKEN=xapp-your-app-token
|
|
28
24
|
# SLACK_DIGEST_CHANNEL=C0123456789
|
|
29
|
-
|
|
30
|
-
# Slack webhook — fallback for digest delivery if bot token is unavailable
|
|
31
25
|
# TEAM_CONTEXT_SLACK_WEBHOOK=https://hooks.slack.com/services/T.../B.../...
|
|
32
26
|
|
|
33
|
-
# ──
|
|
34
|
-
|
|
35
|
-
# Tenant identifier (prefixes storage paths)
|
|
36
|
-
# TEAM_CONTEXT_TENANT_ID=my-team
|
|
37
|
-
|
|
38
|
-
# Team repo allowlist — only journals from these repos appear in digests and queries.
|
|
39
|
-
# Supports org/* wildcards. This is the recommended way to scope a container to one team.
|
|
40
|
-
# TEAM_CONTEXT_INCLUDE_REPOS=MyOrg/*,MyOrg-Libs/*
|
|
41
|
-
|
|
42
|
-
# DEPRECATED: Blocklist approach. Use INCLUDE_REPOS instead.
|
|
43
|
-
# TEAM_CONTEXT_EXCLUDE_REPOS=wayfind,personal-project
|
|
44
|
-
|
|
45
|
-
# Encryption key — generate with: openssl rand -base64 32
|
|
46
|
-
# TEAM_CONTEXT_ENCRYPTION_KEY=
|
|
47
|
-
|
|
48
|
-
# Author slug for CLI telemetry attribution
|
|
49
|
-
# TEAM_CONTEXT_AUTHOR=greg
|
|
50
|
-
|
|
51
|
-
# ── LLM models ───────────────────────────────────────────────────────────────
|
|
52
|
-
|
|
53
|
-
# Model for digest generation and onboarding packs (default: claude-sonnet-4-5-20250929)
|
|
54
|
-
# TEAM_CONTEXT_LLM_MODEL=claude-sonnet-4-5-20250929
|
|
55
|
-
|
|
56
|
-
# Model for conversation transcript extraction (default: claude-sonnet-4-5-20250929)
|
|
57
|
-
# TEAM_CONTEXT_EXTRACTION_MODEL=claude-sonnet-4-5-20250929
|
|
58
|
-
|
|
59
|
-
# ── Embeddings (without these, bot uses keyword search only) ─────────────────
|
|
27
|
+
# ── Embeddings (for semantic search — falls back to keyword search without) ──
|
|
60
28
|
# Option A: OpenAI
|
|
61
29
|
# OPENAI_API_KEY=sk-your-openai-key
|
|
62
|
-
# Option B: Azure OpenAI
|
|
30
|
+
# Option B: Azure OpenAI
|
|
63
31
|
# AZURE_OPENAI_EMBEDDING_ENDPOINT=https://your-resource.openai.azure.com/
|
|
64
32
|
# AZURE_OPENAI_EMBEDDING_KEY=your-key
|
|
65
33
|
# AZURE_OPENAI_EMBEDDING_DEPLOYMENT=text-embedding-3-small
|
|
66
34
|
|
|
67
|
-
# ──
|
|
68
|
-
|
|
69
|
-
# Override content store path (default: ~/.claude/team-context/content-store)
|
|
70
|
-
# TEAM_CONTEXT_STORE_PATH=/data/content-store
|
|
71
|
-
|
|
72
|
-
# Override signals directory (default: ~/.claude/team-context/signals)
|
|
73
|
-
# TEAM_CONTEXT_SIGNALS_DIR=/data/signals
|
|
35
|
+
# ── Team scoping ─────────────────────────────────────────────────────────────
|
|
74
36
|
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
|
|
78
|
-
# ── Schedules ────────────────────────────────────────────────────────────────
|
|
79
|
-
|
|
80
|
-
# Digest schedule (cron, default: daily 12pm UTC / 7am ET)
|
|
81
|
-
# TEAM_CONTEXT_DIGEST_SCHEDULE=0 12 * * *
|
|
37
|
+
# Tenant identifier (prefixes storage paths)
|
|
38
|
+
# TEAM_CONTEXT_TENANT_ID=my-team
|
|
82
39
|
|
|
83
|
-
#
|
|
84
|
-
#
|
|
40
|
+
# Team repo allowlist — only journals from these repos appear in digests/queries.
|
|
41
|
+
# TEAM_CONTEXT_INCLUDE_REPOS=MyOrg/*,MyOrg-Libs/*
|
|
85
42
|
|
|
86
|
-
#
|
|
87
|
-
#
|
|
43
|
+
# Author slug for telemetry attribution
|
|
44
|
+
# TEAM_CONTEXT_AUTHOR=greg
|
|
88
45
|
|
|
89
46
|
# ── Signal sources ───────────────────────────────────────────────────────────
|
|
90
47
|
|
|
@@ -93,21 +50,28 @@ GITHUB_TOKEN=
|
|
|
93
50
|
|
|
94
51
|
# Intercom API token for support signal ingestion
|
|
95
52
|
# INTERCOM_TOKEN=your-intercom-token
|
|
96
|
-
|
|
97
|
-
# Filter Intercom conversations by tags (comma-separated)
|
|
98
53
|
# TEAM_CONTEXT_INTERCOM_TAGS=bug,feature-request
|
|
99
54
|
|
|
100
55
|
# Notion integration token for page/database signal ingestion
|
|
101
|
-
# Create at: https://www.notion.so/my-integrations
|
|
102
56
|
# NOTION_TOKEN=ntn_your-integration-token
|
|
103
|
-
|
|
104
|
-
# Specific Notion database IDs to monitor (comma-separated, or blank for all shared pages)
|
|
105
57
|
# TEAM_CONTEXT_NOTION_DATABASES=db_id1,db_id2
|
|
106
58
|
|
|
107
|
-
# ──
|
|
59
|
+
# ── Schedules ────────────────────────────────────────────────────────────────
|
|
108
60
|
|
|
109
|
-
#
|
|
110
|
-
#
|
|
61
|
+
# TEAM_CONTEXT_DIGEST_SCHEDULE=0 12 * * *
|
|
62
|
+
# TEAM_CONTEXT_SIGNAL_SCHEDULE=0 6 * * *
|
|
63
|
+
# TEAM_CONTEXT_REINDEX_SCHEDULE=0 * * * *
|
|
111
64
|
|
|
112
|
-
#
|
|
65
|
+
# ── Advanced ─────────────────────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
# Set to 1 to run without Slack (scheduler + worker only, no bot)
|
|
68
|
+
# TEAM_CONTEXT_NO_SLACK=1
|
|
69
|
+
|
|
70
|
+
# TEAM_CONTEXT_LLM_MODEL=claude-sonnet-4-5-20250929
|
|
71
|
+
# TEAM_CONTEXT_EXTRACTION_MODEL=claude-sonnet-4-5-20250929
|
|
72
|
+
# TEAM_CONTEXT_ENCRYPTION_KEY=
|
|
73
|
+
# TEAM_CONTEXT_HEALTH_PORT=3141
|
|
113
74
|
# TEAM_CONTEXT_TELEMETRY=true
|
|
75
|
+
# TEAM_CONTEXT_STORE_PATH=/data/content-store
|
|
76
|
+
# TEAM_CONTEXT_SIGNALS_DIR=/data/signals
|
|
77
|
+
# TEAM_CONTEXT_CONVERSATIONS_DIR=/data/conversations
|
|
@@ -7,52 +7,9 @@ services:
|
|
|
7
7
|
image: ghcr.io/usewayfind/wayfind:latest
|
|
8
8
|
container_name: wayfind
|
|
9
9
|
restart: unless-stopped
|
|
10
|
+
env_file: .env
|
|
10
11
|
environment:
|
|
11
12
|
TEAM_CONTEXT_MODE: all-in-one
|
|
12
|
-
TEAM_CONTEXT_TENANT_ID: ${TEAM_CONTEXT_TENANT_ID:-my-team}
|
|
13
|
-
TEAM_CONTEXT_AUTHOR: ${TEAM_CONTEXT_AUTHOR:-}
|
|
14
|
-
|
|
15
|
-
# Slack
|
|
16
|
-
SLACK_BOT_TOKEN: ${SLACK_BOT_TOKEN}
|
|
17
|
-
SLACK_APP_TOKEN: ${SLACK_APP_TOKEN}
|
|
18
|
-
SLACK_DIGEST_CHANNEL: ${SLACK_DIGEST_CHANNEL:-}
|
|
19
|
-
TEAM_CONTEXT_SLACK_WEBHOOK: ${TEAM_CONTEXT_SLACK_WEBHOOK:-}
|
|
20
|
-
|
|
21
|
-
# Team repo allowlist (recommended over EXCLUDE_REPOS)
|
|
22
|
-
TEAM_CONTEXT_INCLUDE_REPOS: ${TEAM_CONTEXT_INCLUDE_REPOS:-}
|
|
23
|
-
# DEPRECATED: Blocklist approach — use INCLUDE_REPOS instead
|
|
24
|
-
TEAM_CONTEXT_EXCLUDE_REPOS: ${TEAM_CONTEXT_EXCLUDE_REPOS:-}
|
|
25
|
-
|
|
26
|
-
# Telemetry (opt-in, sends anonymous usage data to improve Wayfind)
|
|
27
|
-
TEAM_CONTEXT_TELEMETRY: ${TEAM_CONTEXT_TELEMETRY:-false}
|
|
28
|
-
|
|
29
|
-
# GitHub signals
|
|
30
|
-
GITHUB_TOKEN: ${GITHUB_TOKEN:-}
|
|
31
|
-
|
|
32
|
-
# Intercom signals
|
|
33
|
-
INTERCOM_TOKEN: ${INTERCOM_TOKEN:-}
|
|
34
|
-
TEAM_CONTEXT_INTERCOM_TAGS: ${TEAM_CONTEXT_INTERCOM_TAGS:-}
|
|
35
|
-
|
|
36
|
-
# LLM
|
|
37
|
-
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
|
|
38
|
-
TEAM_CONTEXT_LLM_MODEL: ${TEAM_CONTEXT_LLM_MODEL:-claude-sonnet-4-5-20250929}
|
|
39
|
-
|
|
40
|
-
# Embeddings (for semantic search — falls back to keyword search without these)
|
|
41
|
-
# Option A: OpenAI
|
|
42
|
-
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
|
|
43
|
-
# Option B: Azure OpenAI
|
|
44
|
-
AZURE_OPENAI_EMBEDDING_ENDPOINT: ${AZURE_OPENAI_EMBEDDING_ENDPOINT:-}
|
|
45
|
-
AZURE_OPENAI_EMBEDDING_KEY: ${AZURE_OPENAI_EMBEDDING_KEY:-}
|
|
46
|
-
AZURE_OPENAI_EMBEDDING_DEPLOYMENT: ${AZURE_OPENAI_EMBEDDING_DEPLOYMENT:-text-embedding-3-small}
|
|
47
|
-
|
|
48
|
-
# Encryption — generate with: openssl rand -base64 32
|
|
49
|
-
TEAM_CONTEXT_ENCRYPTION_KEY: ${TEAM_CONTEXT_ENCRYPTION_KEY:-}
|
|
50
|
-
|
|
51
|
-
# Scheduling
|
|
52
|
-
TEAM_CONTEXT_DIGEST_SCHEDULE: ${TEAM_CONTEXT_DIGEST_SCHEDULE:-0 8 * * 1}
|
|
53
|
-
TEAM_CONTEXT_SIGNAL_SCHEDULE: ${TEAM_CONTEXT_SIGNAL_SCHEDULE:-0 6 * * *}
|
|
54
|
-
|
|
55
|
-
# Team context repo (mounted at /data/team-context for git pull)
|
|
56
13
|
TEAM_CONTEXT_TEAM_CONTEXT_DIR: /data/team-context
|
|
57
14
|
TEAM_CONTEXT_JOURNALS_DIR: /data/team-context/journals
|
|
58
15
|
volumes:
|