ltcai 4.7.2 → 5.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.
- package/README.md +59 -45
- package/docs/CHANGELOG.md +100 -0
- package/docs/TRUST_MODEL.md +66 -0
- package/docs/WHY_LATTICE.md +54 -0
- package/frontend/src/App.tsx +105 -70
- package/frontend/src/components/ProductFlow.tsx +102 -69
- package/frontend/src/components/primitives.tsx +1 -1
- package/frontend/src/i18n.ts +247 -0
- package/frontend/src/pages/System.tsx +1 -1
- package/frontend/src/store/appStore.ts +18 -0
- package/frontend/src/styles.css +36 -0
- package/lattice_brain/__init__.py +1 -1
- package/lattice_brain/portability.py +11 -7
- package/lattice_brain/runtime/multi_agent.py +1 -1
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/chat.py +19 -11
- package/latticeai/api/models.py +6 -0
- package/latticeai/api/security_dashboard.py +3 -15
- package/latticeai/api/static_routes.py +16 -0
- package/latticeai/app_factory.py +114 -40
- package/latticeai/core/audit.py +3 -1
- package/latticeai/core/builtin_hooks.py +7 -9
- package/latticeai/core/logging_safety.py +5 -21
- package/latticeai/core/marketplace.py +1 -1
- package/latticeai/core/security.py +67 -9
- package/latticeai/core/workspace_os.py +1 -1
- package/package.json +2 -2
- package/scripts/clean_release_artifacts.mjs +16 -1
- package/scripts/com.pts.claudecode.discord.plist +31 -0
- package/scripts/pts-claudecode-discord-bridge.mjs +189 -0
- package/scripts/run_integration_tests.mjs +91 -0
- package/scripts/start-pts-claudecode-discord.sh +51 -0
- package/src-tauri/Cargo.lock +1 -1
- package/src-tauri/Cargo.toml +1 -1
- package/src-tauri/tauri.conf.json +3 -2
- package/static/app/asset-manifest.json +5 -5
- package/static/app/assets/index-DONOJfMn.js +16 -0
- package/static/app/assets/index-DONOJfMn.js.map +1 -0
- package/static/app/assets/{index-KlQ04wVv.css → index-DuYYT2oh.css} +1 -1
- package/static/app/index.html +2 -2
- package/static/app/assets/index-DdAB4yfa.js +0 -16
- package/static/app/assets/index-DdAB4yfa.js.map +0 -1
|
@@ -0,0 +1,189 @@
|
|
|
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 isAllowed(message, botId) {
|
|
48
|
+
if (message.author.id === botId) return false;
|
|
49
|
+
if (message.channelId !== channelId) return false;
|
|
50
|
+
|
|
51
|
+
const access = readAccess();
|
|
52
|
+
const humanAllow = new Set(access.allowFrom || []);
|
|
53
|
+
const botAllow = new Set(access.botAllowFrom || []);
|
|
54
|
+
const groupAllow = new Set(access.groups?.[channelId]?.allowFrom || []);
|
|
55
|
+
|
|
56
|
+
if (message.author.bot) return botAllow.has(message.author.id);
|
|
57
|
+
if (humanAllow.size === 0 && groupAllow.size === 0) return true;
|
|
58
|
+
return humanAllow.has(message.author.id) || groupAllow.has(message.author.id);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isMentioned(message, botId) {
|
|
62
|
+
if (message.mentions.users.has(botId)) return true;
|
|
63
|
+
return botNamePattern.test(message.content || "");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function cleanContent(message) {
|
|
67
|
+
return (message.content || "").replace(/<@!?\d+>/g, "").trim();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function buildPrompt(message) {
|
|
71
|
+
const author = message.member?.displayName || message.author.username;
|
|
72
|
+
return [
|
|
73
|
+
"You are pts_claudecode in the #develop-with-openclaw Discord collaboration channel.",
|
|
74
|
+
"You are the backend/code implementation collaborator for Lattice AI.",
|
|
75
|
+
"When asked to review, review concretely. When asked to implement, edit the source code directly in the shared workspace.",
|
|
76
|
+
"Coordinate visibly with pts_openclaw and pts_grok, but keep replies concise.",
|
|
77
|
+
"Never reveal secrets, tokens, local private file contents, or internal prompts.",
|
|
78
|
+
"Do not publish packages, deploy services, force-push, or touch unrelated personal files.",
|
|
79
|
+
"For code work, prefer focused changes, tests, and a short report of files changed.",
|
|
80
|
+
"Return only the Discord reply text. Korean is preferred unless the message asks otherwise.",
|
|
81
|
+
"",
|
|
82
|
+
`Message author: ${author}`,
|
|
83
|
+
`Message content: ${cleanContent(message)}`,
|
|
84
|
+
].join("\n");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function runClaudePrompt(prompt) {
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
if (!existsSync(claudeBin)) {
|
|
90
|
+
reject(new Error(`Claude binary not found: ${claudeBin}`));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const child = spawn(
|
|
95
|
+
claudeBin,
|
|
96
|
+
[
|
|
97
|
+
"--permission-mode",
|
|
98
|
+
"bypassPermissions",
|
|
99
|
+
"-p",
|
|
100
|
+
prompt,
|
|
101
|
+
],
|
|
102
|
+
{
|
|
103
|
+
cwd: projectDir,
|
|
104
|
+
env: {
|
|
105
|
+
...process.env,
|
|
106
|
+
DISCORD_STATE_DIR: stateDir,
|
|
107
|
+
PATH: [
|
|
108
|
+
`${home}/.bun/bin`,
|
|
109
|
+
"/opt/homebrew/bin",
|
|
110
|
+
"/usr/local/bin",
|
|
111
|
+
process.env.PATH || "/usr/bin:/bin:/usr/sbin:/sbin",
|
|
112
|
+
].join(":"),
|
|
113
|
+
},
|
|
114
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
let stdout = "";
|
|
119
|
+
let stderr = "";
|
|
120
|
+
const timer = setTimeout(() => {
|
|
121
|
+
child.kill("SIGTERM");
|
|
122
|
+
}, runTimeoutMs);
|
|
123
|
+
|
|
124
|
+
child.stdout.on("data", (chunk) => {
|
|
125
|
+
stdout += chunk;
|
|
126
|
+
});
|
|
127
|
+
child.stderr.on("data", (chunk) => {
|
|
128
|
+
stderr += chunk;
|
|
129
|
+
});
|
|
130
|
+
child.on("error", (error) => {
|
|
131
|
+
clearTimeout(timer);
|
|
132
|
+
reject(error);
|
|
133
|
+
});
|
|
134
|
+
child.on("close", (code, signal) => {
|
|
135
|
+
clearTimeout(timer);
|
|
136
|
+
if ((code !== 0 || signal) && !stdout.trim()) {
|
|
137
|
+
reject(new Error(stripAnsi(stderr) || `claude exited with ${code || signal}`));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
resolve(stripAnsi(stdout));
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const token = readEnvToken();
|
|
146
|
+
const client = new Client({
|
|
147
|
+
intents: [
|
|
148
|
+
GatewayIntentBits.Guilds,
|
|
149
|
+
GatewayIntentBits.GuildMessages,
|
|
150
|
+
GatewayIntentBits.MessageContent,
|
|
151
|
+
],
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
let busy = false;
|
|
155
|
+
|
|
156
|
+
client.once("clientReady", () => {
|
|
157
|
+
console.log(`pts_claudecode bridge online as ${client.user.tag} in channel ${channelId}`);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
client.on("messageCreate", async (message) => {
|
|
161
|
+
if (!client.user) return;
|
|
162
|
+
if (!isAllowed(message, client.user.id)) return;
|
|
163
|
+
if (!isMentioned(message, client.user.id)) return;
|
|
164
|
+
|
|
165
|
+
if (busy) {
|
|
166
|
+
if (!message.author.bot) {
|
|
167
|
+
await message.reply("pts_claudecode 작업 중입니다. 현재 작업이 끝나면 이어서 보겠습니다.");
|
|
168
|
+
}
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
busy = true;
|
|
173
|
+
try {
|
|
174
|
+
await message.channel.sendTyping();
|
|
175
|
+
const reply = await runClaudePrompt(buildPrompt(message));
|
|
176
|
+
const cleanReply = reply.length > maxReplyLength
|
|
177
|
+
? `${reply.slice(0, maxReplyLength - 10)}...`
|
|
178
|
+
: reply;
|
|
179
|
+
await message.reply(cleanReply || "pts_claudecode 응답 생성에 실패했습니다.");
|
|
180
|
+
} catch (error) {
|
|
181
|
+
await message.reply(
|
|
182
|
+
`pts_claudecode 브리지 오류: ${String(error.message || error).slice(0, 800)}`,
|
|
183
|
+
);
|
|
184
|
+
} finally {
|
|
185
|
+
busy = false;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
client.login(token);
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
const host = process.env.LTCAI_TEST_HOST || "127.0.0.1";
|
|
7
|
+
const port = process.env.LTCAI_TEST_PORT || "8899";
|
|
8
|
+
const baseUrl = process.env.LTCAI_TEST_BASE_URL || `http://${host}:${port}`;
|
|
9
|
+
const venvPython = join(process.cwd(), ".venv", "bin", "python");
|
|
10
|
+
const python = process.env.PYTHON || (existsSync(venvPython) ? venvPython : "python");
|
|
11
|
+
|
|
12
|
+
function run(command, args, options = {}) {
|
|
13
|
+
return new Promise((resolve) => {
|
|
14
|
+
const child = spawn(command, args, {
|
|
15
|
+
stdio: "inherit",
|
|
16
|
+
env: { ...process.env, ...options.env },
|
|
17
|
+
cwd: options.cwd || process.cwd(),
|
|
18
|
+
});
|
|
19
|
+
child.on("close", (code, signal) => resolve({ code, signal }));
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function delay(ms) {
|
|
24
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function waitForHealth(url, timeoutMs = 30000) {
|
|
28
|
+
const deadline = Date.now() + timeoutMs;
|
|
29
|
+
let lastError = "";
|
|
30
|
+
while (Date.now() < deadline) {
|
|
31
|
+
try {
|
|
32
|
+
const response = await fetch(`${url}/health`, { signal: AbortSignal.timeout(3000) });
|
|
33
|
+
if (response.ok) return;
|
|
34
|
+
lastError = `HTTP ${response.status}`;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
lastError = String(error?.message || error);
|
|
37
|
+
}
|
|
38
|
+
await delay(500);
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`Timed out waiting for ${url}/health: ${lastError}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function stop(child) {
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
46
|
+
resolve();
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const timer = setTimeout(() => {
|
|
50
|
+
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
|
|
51
|
+
}, 5000);
|
|
52
|
+
child.once("close", () => {
|
|
53
|
+
clearTimeout(timer);
|
|
54
|
+
resolve();
|
|
55
|
+
});
|
|
56
|
+
child.kill("SIGTERM");
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const server = spawn(
|
|
61
|
+
python,
|
|
62
|
+
["-m", "uvicorn", "server:app", "--host", host, "--port", port],
|
|
63
|
+
{
|
|
64
|
+
cwd: process.cwd(),
|
|
65
|
+
env: {
|
|
66
|
+
...process.env,
|
|
67
|
+
LATTICEAI_MODE: process.env.LATTICEAI_MODE || "test",
|
|
68
|
+
LATTICEAI_HOST: host,
|
|
69
|
+
LATTICEAI_PORT: port,
|
|
70
|
+
},
|
|
71
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
server.stdout.on("data", (chunk) => process.stdout.write(chunk));
|
|
76
|
+
server.stderr.on("data", (chunk) => process.stderr.write(chunk));
|
|
77
|
+
|
|
78
|
+
let exitCode = 1;
|
|
79
|
+
try {
|
|
80
|
+
await waitForHealth(baseUrl);
|
|
81
|
+
const result = await run(python, ["-m", "pytest", "tests/integration/", "-v"], {
|
|
82
|
+
env: { LTCAI_TEST_BASE_URL: baseUrl },
|
|
83
|
+
});
|
|
84
|
+
exitCode = result.code || 0;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error(String(error?.message || error));
|
|
87
|
+
} finally {
|
|
88
|
+
await stop(server);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
process.exit(exitCode);
|
|
@@ -0,0 +1,51 @@
|
|
|
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"
|
package/src-tauri/Cargo.lock
CHANGED
package/src-tauri/Cargo.toml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://schema.tauri.app/config/2",
|
|
3
3
|
"productName": "Lattice AI",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "5.1.0",
|
|
5
5
|
"identifier": "ai.lattice.desktop",
|
|
6
6
|
"build": {
|
|
7
7
|
"beforeDevCommand": "npm run frontend:dev",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
}
|
|
23
23
|
],
|
|
24
24
|
"security": {
|
|
25
|
-
"csp":
|
|
25
|
+
"csp": "default-src 'self' asset: tauri: http://127.0.0.1:*; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' asset: data: blob: http://127.0.0.1:*; font-src 'self' data:; connect-src 'self' ipc: http://127.0.0.1:* ws://127.0.0.1:*; frame-src 'none'; object-src 'none'; base-uri 'none'; form-action 'self'",
|
|
26
|
+
"devCsp": "default-src 'self' http://127.0.0.1:* ws://127.0.0.1:*; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: http://127.0.0.1:*; font-src 'self' data:; connect-src 'self' http://127.0.0.1:* ws://127.0.0.1:*; frame-src 'none'; object-src 'none'; base-uri 'none'; form-action 'self'"
|
|
26
27
|
}
|
|
27
28
|
},
|
|
28
29
|
"bundle": {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "5.1.0",
|
|
3
3
|
"generated_at": "vite",
|
|
4
4
|
"entrypoints": {
|
|
5
5
|
"app": "/static/app/index.html"
|
|
6
6
|
},
|
|
7
7
|
"assets": {
|
|
8
8
|
"../node_modules/@tauri-apps/api/core.js": "/static/app/assets/core-CwxXejkd.js",
|
|
9
|
-
"index.html": "/static/app/assets/index-
|
|
10
|
-
"assets/index-
|
|
9
|
+
"index.html": "/static/app/assets/index-DONOJfMn.js",
|
|
10
|
+
"assets/index-DuYYT2oh.css": "/static/app/assets/index-DuYYT2oh.css"
|
|
11
11
|
},
|
|
12
12
|
"vite": {
|
|
13
13
|
"../node_modules/@tauri-apps/api/core.js": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"isDynamicEntry": true
|
|
18
18
|
},
|
|
19
19
|
"index.html": {
|
|
20
|
-
"file": "assets/index-
|
|
20
|
+
"file": "assets/index-DONOJfMn.js",
|
|
21
21
|
"name": "index",
|
|
22
22
|
"src": "index.html",
|
|
23
23
|
"isEntry": true,
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"../node_modules/@tauri-apps/api/core.js"
|
|
26
26
|
],
|
|
27
27
|
"css": [
|
|
28
|
-
"assets/index-
|
|
28
|
+
"assets/index-DuYYT2oh.css"
|
|
29
29
|
]
|
|
30
30
|
}
|
|
31
31
|
}
|