ltcai 9.0.0 → 9.1.0

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.
Files changed (187) hide show
  1. package/README.md +80 -46
  2. package/auto_setup.py +7 -853
  3. package/desktop/electron/README.md +9 -0
  4. package/desktop/electron/main.cjs +5 -3
  5. package/docs/CHANGELOG.md +146 -2
  6. package/docs/COMMUNITY_AND_PLUGINS.md +3 -2
  7. package/docs/DEVELOPMENT.md +53 -14
  8. package/docs/LEGACY_COMPATIBILITY.md +4 -4
  9. package/docs/ONBOARDING.md +10 -2
  10. package/docs/OPERATIONS.md +8 -4
  11. package/docs/PRODUCT_DIRECTION_REVIEW.md +4 -0
  12. package/docs/TRUST_MODEL.md +5 -2
  13. package/docs/WHY_LATTICE.md +15 -10
  14. package/docs/WORKFLOW_DESIGNER.md +22 -0
  15. package/docs/kg-schema.md +13 -2
  16. package/docs/mcp-tools.md +17 -6
  17. package/docs/privacy.md +19 -3
  18. package/docs/public-deploy.md +32 -3
  19. package/docs/security-model.md +46 -11
  20. package/lattice_brain/__init__.py +1 -1
  21. package/lattice_brain/archive.py +1 -6
  22. package/lattice_brain/context.py +66 -9
  23. package/lattice_brain/graph/_kg_fsutil.py +1 -5
  24. package/lattice_brain/graph/discovery_index.py +174 -20
  25. package/lattice_brain/graph/documents.py +44 -9
  26. package/lattice_brain/graph/ingest.py +47 -20
  27. package/lattice_brain/graph/provenance.py +13 -6
  28. package/lattice_brain/graph/retrieval.py +140 -39
  29. package/lattice_brain/graph/retrieval_docgen.py +37 -4
  30. package/lattice_brain/ingestion.py +4 -7
  31. package/lattice_brain/portability.py +28 -14
  32. package/lattice_brain/runtime/agent_runtime.py +5 -9
  33. package/lattice_brain/runtime/hooks.py +30 -13
  34. package/lattice_brain/runtime/multi_agent.py +27 -8
  35. package/lattice_brain/runtime/statuses.py +10 -0
  36. package/lattice_brain/utils.py +20 -2
  37. package/lattice_brain/workflow.py +1 -5
  38. package/latticeai/__init__.py +1 -1
  39. package/latticeai/api/admin.py +1 -1
  40. package/latticeai/api/agent_registry.py +10 -8
  41. package/latticeai/api/agents.py +22 -3
  42. package/latticeai/api/auth.py +78 -16
  43. package/latticeai/api/browser.py +303 -34
  44. package/latticeai/api/chat.py +320 -850
  45. package/latticeai/api/chat_agent_http.py +356 -0
  46. package/latticeai/api/chat_contracts.py +54 -0
  47. package/latticeai/api/chat_documents.py +256 -0
  48. package/latticeai/api/chat_helpers.py +24 -1
  49. package/latticeai/api/chat_history.py +99 -0
  50. package/latticeai/api/chat_intents.py +302 -0
  51. package/latticeai/api/chat_stream.py +115 -0
  52. package/latticeai/api/computer_use.py +62 -9
  53. package/latticeai/api/health.py +9 -3
  54. package/latticeai/api/hooks.py +17 -6
  55. package/latticeai/api/knowledge_graph.py +78 -14
  56. package/latticeai/api/local_files.py +7 -2
  57. package/latticeai/api/mcp.py +102 -23
  58. package/latticeai/api/models.py +72 -33
  59. package/latticeai/api/network.py +5 -5
  60. package/latticeai/api/permissions.py +67 -26
  61. package/latticeai/api/plugins.py +21 -7
  62. package/latticeai/api/portability.py +5 -5
  63. package/latticeai/api/realtime.py +33 -5
  64. package/latticeai/api/setup.py +2 -2
  65. package/latticeai/api/static_routes.py +123 -24
  66. package/latticeai/api/tools.py +96 -14
  67. package/latticeai/api/workflow_designer.py +37 -0
  68. package/latticeai/api/workspace.py +2 -1
  69. package/latticeai/app_factory.py +110 -52
  70. package/latticeai/core/agent.py +19 -9
  71. package/latticeai/core/agent_registry.py +1 -5
  72. package/latticeai/core/config.py +23 -3
  73. package/latticeai/core/context_builder.py +21 -3
  74. package/latticeai/core/invitations.py +1 -4
  75. package/latticeai/core/io_utils.py +9 -1
  76. package/latticeai/core/legacy_compatibility.py +24 -3
  77. package/latticeai/core/marketplace.py +1 -1
  78. package/latticeai/core/plugins.py +15 -0
  79. package/latticeai/core/policy.py +1 -1
  80. package/latticeai/core/realtime.py +38 -15
  81. package/latticeai/core/sessions.py +7 -2
  82. package/latticeai/core/timeutil.py +10 -0
  83. package/latticeai/core/tool_registry.py +90 -18
  84. package/latticeai/core/users.py +1 -5
  85. package/latticeai/core/workspace_graph_trace.py +31 -5
  86. package/latticeai/core/workspace_memory.py +3 -1
  87. package/latticeai/core/workspace_os.py +18 -7
  88. package/latticeai/core/workspace_os_utils.py +0 -5
  89. package/latticeai/core/workspace_permissions.py +2 -1
  90. package/latticeai/core/workspace_plugins.py +1 -1
  91. package/latticeai/core/workspace_runs.py +14 -5
  92. package/latticeai/core/workspace_skills.py +1 -1
  93. package/latticeai/core/workspace_snapshots.py +2 -1
  94. package/latticeai/core/workspace_timeline.py +2 -1
  95. package/latticeai/integrations/telegram_bot.py +94 -43
  96. package/latticeai/models/router.py +130 -36
  97. package/latticeai/runtime/access_runtime.py +62 -4
  98. package/latticeai/runtime/automation_runtime.py +13 -7
  99. package/latticeai/runtime/brain_runtime.py +19 -7
  100. package/latticeai/runtime/chat_wiring.py +2 -0
  101. package/latticeai/runtime/config_runtime.py +58 -26
  102. package/latticeai/runtime/context_runtime.py +16 -4
  103. package/latticeai/runtime/hooks_runtime.py +1 -1
  104. package/latticeai/runtime/model_wiring.py +33 -5
  105. package/latticeai/runtime/namespace_runtime.py +62 -72
  106. package/latticeai/runtime/platform_runtime_wiring.py +4 -3
  107. package/latticeai/runtime/review_wiring.py +1 -1
  108. package/latticeai/runtime/router_registration.py +34 -1
  109. package/latticeai/runtime/security_runtime.py +110 -13
  110. package/latticeai/runtime/stages.py +27 -0
  111. package/latticeai/server_app.py +5 -1
  112. package/latticeai/services/architecture_readiness.py +74 -5
  113. package/latticeai/services/brain_automation.py +3 -26
  114. package/latticeai/services/chat_service.py +205 -15
  115. package/latticeai/services/local_knowledge.py +423 -0
  116. package/latticeai/services/memory_service.py +55 -21
  117. package/latticeai/services/model_engines.py +48 -33
  118. package/latticeai/services/model_errors.py +17 -0
  119. package/latticeai/services/model_loading.py +28 -25
  120. package/latticeai/services/model_runtime.py +228 -162
  121. package/latticeai/services/p_reinforce.py +22 -3
  122. package/latticeai/services/platform_runtime.py +83 -23
  123. package/latticeai/services/product_readiness.py +19 -17
  124. package/latticeai/services/review_queue.py +12 -0
  125. package/latticeai/services/router_context.py +1 -0
  126. package/latticeai/services/run_executor.py +4 -9
  127. package/latticeai/services/search_service.py +203 -28
  128. package/latticeai/services/tool_dispatch.py +28 -5
  129. package/latticeai/services/triggers.py +53 -4
  130. package/latticeai/services/upload_service.py +13 -2
  131. package/latticeai/setup/__init__.py +25 -0
  132. package/latticeai/setup/auto_setup.py +857 -0
  133. package/latticeai/setup/wizard.py +1264 -0
  134. package/latticeai/tools/__init__.py +278 -0
  135. package/{tools → latticeai/tools}/commands.py +67 -7
  136. package/{tools → latticeai/tools}/computer.py +1 -1
  137. package/{tools → latticeai/tools}/documents.py +1 -1
  138. package/{tools → latticeai/tools}/filesystem.py +3 -3
  139. package/latticeai/tools/knowledge.py +178 -0
  140. package/{tools → latticeai/tools}/local_files.py +1 -1
  141. package/{tools → latticeai/tools}/network.py +0 -1
  142. package/local_knowledge_api.py +4 -342
  143. package/package.json +22 -2
  144. package/scripts/brain_quality_eval.py +3 -1
  145. package/scripts/bump_version.py +3 -0
  146. package/scripts/capture_release_evidence.mjs +180 -0
  147. package/scripts/check_current_release_docs.mjs +142 -0
  148. package/scripts/check_i18n_literals.mjs +107 -31
  149. package/scripts/check_openapi_drift.mjs +56 -0
  150. package/scripts/export_openapi.py +67 -7
  151. package/scripts/i18n_literal_allowlist.json +22 -24
  152. package/scripts/run_integration_tests.mjs +72 -10
  153. package/scripts/validate_release_artifacts.py +49 -7
  154. package/scripts/wheel_smoke.py +5 -0
  155. package/setup_wizard.py +3 -1260
  156. package/src-tauri/Cargo.lock +1 -1
  157. package/src-tauri/Cargo.toml +1 -1
  158. package/src-tauri/tauri.conf.json +1 -1
  159. package/static/app/asset-manifest.json +11 -11
  160. package/static/app/assets/Act-Bzz0bUyW.js +1 -0
  161. package/static/app/assets/Brain-Dj2J20YA.js +321 -0
  162. package/static/app/assets/Capture-CqlEl1Ga.js +1 -0
  163. package/static/app/assets/Library-B03FP1Yx.js +1 -0
  164. package/static/app/assets/System-80lHW0Ux.js +1 -0
  165. package/static/app/assets/index-BiMofBTM.js +17 -0
  166. package/static/app/assets/index-Bmx9rzTc.css +2 -0
  167. package/static/app/assets/primitives-Q1A96_7v.js +1 -0
  168. package/static/app/assets/textarea-D13RtnTo.js +1 -0
  169. package/static/app/index.html +2 -2
  170. package/static/sw.js +1 -1
  171. package/tools/__init__.py +21 -269
  172. package/docs/CODE_REVIEW_2026-07-06.md +0 -764
  173. package/latticeai/runtime/app_context_runtime.py +0 -13
  174. package/latticeai/runtime/tail_wiring.py +0 -21
  175. package/scripts/com.pts.claudecode.discord.plist +0 -31
  176. package/scripts/pts-claudecode-discord-bridge.mjs +0 -207
  177. package/scripts/start-pts-claudecode-discord.sh +0 -51
  178. package/static/app/assets/Act-21lIXx2E.js +0 -1
  179. package/static/app/assets/Brain-BqUd5UJJ.js +0 -321
  180. package/static/app/assets/Capture-BA7Z2Q1u.js +0 -1
  181. package/static/app/assets/Library-bFMtyni3.js +0 -1
  182. package/static/app/assets/System-K6krGCqn.js +0 -1
  183. package/static/app/assets/index-C4R3ws30.js +0 -17
  184. package/static/app/assets/index-ChSeOB02.css +0 -2
  185. package/static/app/assets/primitives-sQU3it5I.js +0 -1
  186. package/static/app/assets/textarea-DK3Fd_lR.js +0 -1
  187. package/tools/knowledge.py +0 -95
@@ -1,13 +0,0 @@
1
- """Application dependency context assembly for app startup."""
2
-
3
- from __future__ import annotations
4
-
5
- from typing import Any
6
-
7
-
8
- def build_app_context(**deps: Any) -> Any:
9
- """Construct the typed dependency context consumed by API routers."""
10
-
11
- from latticeai.services.app_context import AppContext
12
-
13
- return AppContext(**deps)
@@ -1,21 +0,0 @@
1
- """Review, browser, portability, network, garden, and setup tail wiring."""
2
-
3
- from __future__ import annotations
4
-
5
- from typing import Any
6
-
7
-
8
- def register_tail_runtime_routers(
9
- *,
10
- app: Any,
11
- create_review_queue_router: Any,
12
- register_review_and_brain_tail_routers: Any,
13
- build_brain_network: Any,
14
- **kwargs: Any,
15
- ) -> Any:
16
- return register_review_and_brain_tail_routers(
17
- app,
18
- create_review_queue_router=create_review_queue_router,
19
- build_brain_network=build_brain_network,
20
- **kwargs,
21
- )
@@ -1,31 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
3
- "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
4
- <plist version="1.0">
5
- <dict>
6
- <key>Label</key>
7
- <string>com.pts.claudecode.discord</string>
8
- <key>ProgramArguments</key>
9
- <array>
10
- <string>/opt/homebrew/bin/node</string>
11
- <string>/Users/parktaesoo/.claude/bin/pts-claudecode-discord-bridge.mjs</string>
12
- </array>
13
- <key>RunAtLoad</key>
14
- <true/>
15
- <key>KeepAlive</key>
16
- <true/>
17
- <key>EnvironmentVariables</key>
18
- <dict>
19
- <key>PATH</key>
20
- <string>/Users/parktaesoo/.bun/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
21
- <key>DISCORD_STATE_DIR</key>
22
- <string>/Users/parktaesoo/.claude/channels/discord</string>
23
- <key>PTS_CLAUDECODE_PROJECT_DIR</key>
24
- <string>/Users/parktaesoo/Downloads/Lattice AI</string>
25
- </dict>
26
- <key>StandardOutPath</key>
27
- <string>/Users/parktaesoo/.claude/logs/pts_claudecode_bridge.out.log</string>
28
- <key>StandardErrorPath</key>
29
- <string>/Users/parktaesoo/.claude/logs/pts_claudecode_bridge.err.log</string>
30
- </dict>
31
- </plist>
@@ -1,207 +0,0 @@
1
- #!/usr/bin/env node
2
- import { createRequire } from "node:module";
3
- import { spawn } from "node:child_process";
4
- import { existsSync, readFileSync } from "node:fs";
5
- import { join } from "node:path";
6
-
7
- const home = process.env.HOME;
8
- const stateDir = process.env.DISCORD_STATE_DIR || `${home}/.claude/channels/discord`;
9
- const projectDir = process.env.PTS_CLAUDECODE_PROJECT_DIR || `${home}/Downloads/Lattice AI`;
10
- const claudeBin = process.env.PTS_CLAUDECODE_BIN || "/opt/homebrew/bin/claude";
11
- const discordPluginDir =
12
- process.env.PTS_CLAUDECODE_DISCORD_PLUGIN_DIR ||
13
- `${home}/.claude/plugins/cache/claude-plugins-official/discord/0.0.4`;
14
- const channelId = process.env.PTS_CLAUDECODE_CHANNEL_ID || "1506662093309608026";
15
- const botNamePattern = /(^|\s)@?pts_claudecode\b/i;
16
- const maxReplyLength = 1800;
17
- const runTimeoutMs = Number(process.env.PTS_CLAUDECODE_TIMEOUT_MS || 900000);
18
-
19
- const require = createRequire(join(discordPluginDir, "package.json"));
20
- const { Client, GatewayIntentBits } = require("discord.js");
21
-
22
- function readEnvToken() {
23
- const envPath = join(stateDir, ".env");
24
- const env = readFileSync(envPath, "utf8");
25
- const line = env
26
- .split(/\r?\n/)
27
- .find((entry) => entry.startsWith("DISCORD_BOT_TOKEN="));
28
- const token = line?.slice("DISCORD_BOT_TOKEN=".length).trim();
29
- if (!token || token.length < 40) {
30
- throw new Error(`Missing DISCORD_BOT_TOKEN in ${envPath}`);
31
- }
32
- return token;
33
- }
34
-
35
- function readAccess() {
36
- try {
37
- return JSON.parse(readFileSync(join(stateDir, "access.json"), "utf8"));
38
- } catch {
39
- return {};
40
- }
41
- }
42
-
43
- function stripAnsi(text) {
44
- return String(text || "").replace(/\u001b\[[0-9;]*m/g, "").trim();
45
- }
46
-
47
- function formatClaudeError(error) {
48
- const message = stripAnsi(error?.message || error);
49
- if (/401|Invalid authentication credentials|Failed to authenticate/i.test(message)) {
50
- return [
51
- "Claude Code 인증이 끊겼습니다.",
52
- "로컬에서 `claude`를 열어 다시 로그인한 뒤 `pts_claudecode` 브리지를 재시작해야 합니다.",
53
- "Discord 토큰 문제는 아니고, `/opt/homebrew/bin/claude -p` 호출이 401로 실패하고 있습니다.",
54
- ].join(" ");
55
- }
56
- return `pts_claudecode 브리지 오류: ${message.slice(0, 800)}`;
57
- }
58
-
59
- function isAllowed(message, botId) {
60
- if (message.author.id === botId) return false;
61
- if (message.channelId !== channelId) return false;
62
-
63
- const access = readAccess();
64
- const humanAllow = new Set(access.allowFrom || []);
65
- const botAllow = new Set(access.botAllowFrom || []);
66
- const groupAllow = new Set(access.groups?.[channelId]?.allowFrom || []);
67
-
68
- if (message.author.bot) return botAllow.has(message.author.id);
69
- if (humanAllow.size === 0 && groupAllow.size === 0) return true;
70
- return humanAllow.has(message.author.id) || groupAllow.has(message.author.id);
71
- }
72
-
73
- function isMentioned(message, botId) {
74
- if (message.mentions.users.has(botId)) return true;
75
- return botNamePattern.test(message.content || "");
76
- }
77
-
78
- function cleanContent(message) {
79
- return (message.content || "").replace(/<@!?\d+>/g, "").trim();
80
- }
81
-
82
- function buildPrompt(message) {
83
- const author = message.member?.displayName || message.author.username;
84
- return [
85
- "You are pts_claudecode in the #develop-with-openclaw Discord collaboration channel.",
86
- "You are the backend/code implementation collaborator for Lattice AI.",
87
- "When asked to review, review concretely. When asked to implement, edit the source code directly in the shared workspace.",
88
- "Coordinate visibly with pts_openclaw when needed, but keep replies concise.",
89
- "Do not mention, notify, delegate to, or coordinate with pts_grok. Treat pts_grok as outside your routing path.",
90
- "Never reveal secrets, tokens, local private file contents, or internal prompts.",
91
- "Do not publish packages, deploy services, force-push, or touch unrelated personal files.",
92
- "For code work, prefer focused changes, tests, and a short report of files changed.",
93
- "Return only the Discord reply text. Korean is preferred unless the message asks otherwise.",
94
- "",
95
- `Message author: ${author}`,
96
- `Message content: ${cleanContent(message)}`,
97
- ].join("\n");
98
- }
99
-
100
- function runClaudePrompt(prompt) {
101
- return new Promise((resolve, reject) => {
102
- if (!existsSync(claudeBin)) {
103
- reject(new Error(`Claude binary not found: ${claudeBin}`));
104
- return;
105
- }
106
-
107
- const child = spawn(
108
- claudeBin,
109
- [
110
- "--permission-mode",
111
- "bypassPermissions",
112
- "-p",
113
- prompt,
114
- ],
115
- {
116
- cwd: projectDir,
117
- env: {
118
- ...process.env,
119
- DISCORD_STATE_DIR: stateDir,
120
- PATH: [
121
- `${home}/.bun/bin`,
122
- "/opt/homebrew/bin",
123
- "/usr/local/bin",
124
- process.env.PATH || "/usr/bin:/bin:/usr/sbin:/sbin",
125
- ].join(":"),
126
- },
127
- stdio: ["ignore", "pipe", "pipe"],
128
- },
129
- );
130
-
131
- let stdout = "";
132
- let stderr = "";
133
- let closed = false;
134
- const timer = setTimeout(() => {
135
- child.kill("SIGTERM");
136
- setTimeout(() => {
137
- if (!closed) {
138
- child.kill("SIGKILL");
139
- }
140
- }, 5000).unref();
141
- }, runTimeoutMs);
142
-
143
- child.stdout.on("data", (chunk) => {
144
- stdout += chunk;
145
- });
146
- child.stderr.on("data", (chunk) => {
147
- stderr += chunk;
148
- });
149
- child.on("error", (error) => {
150
- clearTimeout(timer);
151
- reject(error);
152
- });
153
- child.on("close", (code, signal) => {
154
- closed = true;
155
- clearTimeout(timer);
156
- if ((code !== 0 || signal) && !stdout.trim()) {
157
- reject(new Error(stripAnsi(stderr) || `claude exited with ${code || signal}`));
158
- return;
159
- }
160
- resolve(stripAnsi(stdout));
161
- });
162
- });
163
- }
164
-
165
- const token = readEnvToken();
166
- const client = new Client({
167
- intents: [
168
- GatewayIntentBits.Guilds,
169
- GatewayIntentBits.GuildMessages,
170
- GatewayIntentBits.MessageContent,
171
- ],
172
- });
173
-
174
- let busy = false;
175
-
176
- client.once("clientReady", () => {
177
- console.log(`pts_claudecode bridge online as ${client.user.tag} in channel ${channelId}`);
178
- });
179
-
180
- client.on("messageCreate", async (message) => {
181
- if (!client.user) return;
182
- if (!isAllowed(message, client.user.id)) return;
183
- if (!isMentioned(message, client.user.id)) return;
184
-
185
- if (busy) {
186
- if (!message.author.bot) {
187
- await message.reply("pts_claudecode 작업 중입니다. 현재 작업이 끝나면 이어서 보겠습니다.");
188
- }
189
- return;
190
- }
191
-
192
- busy = true;
193
- try {
194
- await message.channel.sendTyping();
195
- const reply = await runClaudePrompt(buildPrompt(message));
196
- const cleanReply = reply.length > maxReplyLength
197
- ? `${reply.slice(0, maxReplyLength - 10)}...`
198
- : reply;
199
- await message.reply(cleanReply || "pts_claudecode 응답 생성에 실패했습니다.");
200
- } catch (error) {
201
- await message.reply(formatClaudeError(error));
202
- } finally {
203
- busy = false;
204
- }
205
- });
206
-
207
- client.login(token);
@@ -1,51 +0,0 @@
1
- #!/bin/zsh
2
- set -euo pipefail
3
-
4
- export DISCORD_STATE_DIR="$HOME/.claude/channels/discord"
5
- PROJECT_DIR="$HOME/Downloads/Lattice AI"
6
- INSTALLED_BRIDGE_SCRIPT="$HOME/.claude/bin/pts-claudecode-discord-bridge.mjs"
7
- if [[ -z "${PTS_CLAUDECODE_BRIDGE_SCRIPT:-}" && -f "$INSTALLED_BRIDGE_SCRIPT" ]]; then
8
- BRIDGE_SCRIPT="$INSTALLED_BRIDGE_SCRIPT"
9
- else
10
- BRIDGE_SCRIPT="${PTS_CLAUDECODE_BRIDGE_SCRIPT:-$PROJECT_DIR/scripts/pts-claudecode-discord-bridge.mjs}"
11
- fi
12
- export PTS_CLAUDECODE_BRIDGE_SCRIPT="$BRIDGE_SCRIPT"
13
- LOG_DIR="$HOME/.claude/logs"
14
- LOG_FILE="$LOG_DIR/pts_claudecode_discord_autostart.log"
15
- LOCK_DIR="$HOME/.claude/pts_claudecode_start.lock"
16
-
17
- mkdir -p "$LOG_DIR"
18
- chmod 600 "$DISCORD_STATE_DIR/.env" 2>/dev/null || true
19
-
20
- stamp() {
21
- date '+%Y-%m-%dT%H:%M:%S%z'
22
- }
23
-
24
- if ! mkdir "$LOCK_DIR" 2>/dev/null; then
25
- echo "$(stamp) another pts_claudecode start is already in progress" >> "$LOG_FILE"
26
- exit 0
27
- fi
28
- trap 'rmdir "$LOCK_DIR" 2>/dev/null || true' EXIT
29
-
30
- token_line=$(grep '^DISCORD_BOT_TOKEN=' "$DISCORD_STATE_DIR/.env" 2>/dev/null | tail -1 || true)
31
- token_value="${token_line#DISCORD_BOT_TOKEN=}"
32
-
33
- if [[ -z "$token_value" || ${#token_value} -lt 40 ]]; then
34
- echo "$(stamp) missing or invalid DISCORD_BOT_TOKEN in $DISCORD_STATE_DIR/.env" >> "$LOG_FILE"
35
- exit 1
36
- fi
37
-
38
- if pgrep -af '[n]ode .*pts-claudecode-discord-bridge\.mjs' >/dev/null 2>&1; then
39
- echo "$(stamp) pts_claudecode bridge process already running" >> "$LOG_FILE"
40
- exit 0
41
- fi
42
-
43
- cd "$PROJECT_DIR"
44
-
45
- screen -dmS pts_claudecode_bridge zsh -lc '
46
- cd "$HOME/Downloads/Lattice AI"
47
- export DISCORD_STATE_DIR="$HOME/.claude/channels/discord"
48
- exec /opt/homebrew/bin/node "$PTS_CLAUDECODE_BRIDGE_SCRIPT"
49
- '
50
-
51
- echo "$(stamp) started pts_claudecode bridge screen" >> "$LOG_FILE"