@tera-system/pro 0.2.2 → 0.2.11
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/LICENSE.md +1 -1
- package/MANIFEST.json +5 -4
- package/README.md +5 -5
- package/RELEASES.md +29 -2
- package/WELCOME.md +1 -1
- package/opencode.tera.example.json +3 -3
- package/package.json +1 -1
- package/scripts/build-bundle.mjs +16 -9
- package/scripts/install.js +26 -4
- package/scripts/lib/bundle.mjs +3 -1
- package/scripts/lib/license-gate.mjs +1 -1
package/LICENSE.md
CHANGED
package/MANIFEST.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tera-system/pro",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "Build manifest for the Tera System Pro commercial plugin component (THIN mode — content served from teranoo.com signed bundle).",
|
|
5
5
|
"compatibleEnvironments": [
|
|
6
6
|
"opencode"
|
|
@@ -11,10 +11,11 @@
|
|
|
11
11
|
"agents": 20,
|
|
12
12
|
"commands": 11,
|
|
13
13
|
"coreFiles": 112,
|
|
14
|
-
"tools":
|
|
15
|
-
"projectControlTemplates": 1
|
|
14
|
+
"tools": 8,
|
|
15
|
+
"projectControlTemplates": 1,
|
|
16
|
+
"assets": 49
|
|
16
17
|
},
|
|
17
|
-
"bundleRef": "core-bundle-v0.
|
|
18
|
+
"bundleRef": "core-bundle-v0.2.11.signed.json (SHA256 B05E0A99811958BE44DFB162812366723D3080145122A3C3E0C345E37E2E51B2)",
|
|
18
19
|
"builtAt": "2026-08-28",
|
|
19
20
|
"sha256": {
|
|
20
21
|
"agents": "9769b3acc15e44e8ae56e4d585f7b965f6c30eafceea509edd0dfe3ce1d83270",
|
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ npm install @tera-system/pro
|
|
|
20
20
|
> ```
|
|
21
21
|
> أو مباشرة بدون موافقة:
|
|
22
22
|
> ```bash
|
|
23
|
-
> npx tera-restore
|
|
23
|
+
> npx tera-pro-restore
|
|
24
24
|
> # أو
|
|
25
25
|
> node node_modules/@tera-system/pro/scripts/install.js
|
|
26
26
|
> ```
|
|
@@ -54,21 +54,21 @@ npm run tera-sync
|
|
|
54
54
|
| ط§ظ„ظ†ظˆط§ط© ط§ظ„طظˆظƒظ…ظٹط© (110 ظ…ظ„ظپط§ظ‹) | `tera-system/` |
|
|
55
55
|
| ظ‡ظٹظƒظ„ `project-control/` (ظ‚ظˆط§ظ„ط¨) | `project-control/` *(ظپظ‚ط· ط¥ظ† ظ„ظ… ظٹظˆط¬ط¯ — طط§ظ„ط© ط®ط§طµط© ط¨ظƒظ„ ط¹ظ…ظٹظ„)* |
|
|
56
56
|
| ط£ط¯ظˆط§طھ ط§ظ„طھظˆط²ظٹط¹ (7 ط³ظƒط±ط¨طھط§طھ) | `tools/` |
|
|
57
|
-
| ط³ط¬ظ„ ط§ظ„ظ…ظ„ظƒظٹط© | `.opencode/tera
|
|
57
|
+
| ط³ط¬ظ„ ط§ظ„ظ…ظ„ظƒظٹط© | `.opencode/tera.manifest.json` |
|
|
58
58
|
|
|
59
59
|
## ط³ظ„ظˆظƒ ط§ظ„ظ…ط«ط¨ظ‘طھ (ط؛ظٹط± ظ…ط¯ظ…ط± + ظˆط§ط¹ظچ ط¨ط§ظ„ظ…ظ„ظƒظٹط©)
|
|
60
60
|
|
|
61
61
|
- **ظ„ط§ ظٹطط°ظپ ط£ظٹ ط´ظٹط، ط£ط¨ط¯ط§ظ‹.**
|
|
62
62
|
- **ظ„ط§ ظٹظƒطھط¨ ظپظˆظ‚ ط£ظٹ ظ…ظ„ظپ ط؛ظٹط± ظ…ظ…ظ„ظˆظƒ ظ„ظ‡:** ط£ظٹ ظ…ظ„ظپ ظ…ظˆط¬ظˆط¯ ط¨ط§ط³ظ… ظٹطھط¹ط§ط±ط¶ ظ…ط¹ طط²ظ…ط© `@tera-system/pro` (ظˆظ„ظ… طھط«ط¨ظ‘طھظ‡ ط§ظ„طط²ظ…ط© ط³ط§ط¨ظ‚ط§ظ‹) ظٹظڈطھط±ظƒ ظƒظ…ط§ ظ‡ظˆ ظˆظٹظڈط³ط¬ظژظ‘ظ„ ظƒظ€ `CONFLICT` ظپظٹ طھظ‚ط±ظٹط± ط§ظ„طھط±ظƒظٹط¨.
|
|
63
|
-
- **ظٹظپط±ظ‘ظ‚ ط¨ظٹظ† ظ…ظ„ظپط§طھظ‡ ظˆظ…ظ„ظپط§طھ ط§ظ„ظ…ط³طھط®ط¯ظ…** ط¹ط¨ط± ط³ط¬ظ„ ط§ظ„ظ…ظ„ظƒظٹط© `.opencode/tera
|
|
63
|
+
- **ظٹظپط±ظ‘ظ‚ ط¨ظٹظ† ظ…ظ„ظپط§طھظ‡ ظˆظ…ظ„ظپط§طھ ط§ظ„ظ…ط³طھط®ط¯ظ…** ط¹ط¨ط± ط³ط¬ظ„ ط§ظ„ظ…ظ„ظƒظٹط© `.opencode/tera.manifest.json`.
|
|
64
64
|
- **ط§ظ„ظ…ظ„ظپط§طھ ط§ظ„ظ…ظ…ظ„ظˆظƒط© ظ„ظ„طط²ظ…ط© ظپظ‚ط·** طھظڈطط¯ظژظ‘ط« ط¹ظ†ط¯ ط§ط®طھظ„ط§ظپظ‡ط§طŒ ظ…ط¹ **ظ†ط³ط®ط© ط§ططھظٹط§ط·ظٹط©** (`*.tera-bak-<ts>`) ظ‚ط¨ظ„ ط£ظٹ طھطط¯ظٹط«.
|
|
65
65
|
- **ط¥ط¹ط§ط¯ط© ط§ظ„طھط´ط؛ظٹظ„ ط¢ظ…ظ†ط© (idempotent):** ط§ظ„ظ…ظ„ظپط§طھ ط§ظ„ظ…طھط·ط§ط¨ظ‚ط© طھظڈطھط®ط·ظ‘ظ‰.
|
|
66
66
|
- `opencode.json` ط§ظ„ظ…ظˆط¬ظˆط¯ ظ„ط¯ظ‰ ط§ظ„ظ…ط³طھط®ط¯ظ… **ظ„ط§ ظٹظڈظ„ظ…ط³ ط£ط¨ط¯ط§ظ‹** — طھظڈظˆط¶ط¹ ظ†ط³ط®ط© ظ…ط±ط¬ط¹ظٹط© ظپظ‚ط· ظˆطھط¸ظ‡ط± ط§ظ„طھط¹ظ„ظٹظ…ط§طھ ظپظٹ ط§ظ„طھظ‚ط±ظٹط±.
|
|
67
67
|
|
|
68
68
|
## ط¥ظ„ط؛ط§ط، ط§ظ„طھط«ط¨ظٹطھ (ظٹط¯ظˆظٹ — ط؛ظٹط± ظ…ط¯ظ…ط±)
|
|
69
69
|
|
|
70
|
-
1. ط§طط°ظپ ط§ظ„ظ…ظ„ظپط§طھ ط§ظ„ظ…ط³ط¬ظ‘ظ„ط© ظپظٹ `.opencode/tera
|
|
71
|
-
2. ط§طط°ظپ `.opencode/tera
|
|
70
|
+
1. ط§طط°ظپ ط§ظ„ظ…ظ„ظپط§طھ ط§ظ„ظ…ط³ط¬ظ‘ظ„ط© ظپظٹ `.opencode/tera.manifest.json` (`files`).
|
|
71
|
+
2. ط§طط°ظپ `.opencode/tera.manifest.json` ظ†ظپط³ظ‡.
|
|
72
72
|
3. ط§ططھظپط¸ ط¨ط£ظٹ `*.tera-bak-*` ط£ظˆ ظ…ظ„ظپط§طھظƒ ط§ظ„ط´ط®طµظٹط© ظƒظ…ط§ ظ‡ظٹ.
|
|
73
73
|
|
|
74
74
|
## ط§ظ„طھطظ‚ظ‚ ظ…ظ† ط§ظ„ظ†ط²ط§ظ‡ط©
|
package/RELEASES.md
CHANGED
|
@@ -4,17 +4,44 @@
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## 0.2.11 (2026-08-30)
|
|
10
|
+
|
|
11
|
+
### Security hardening: license-gate message fix + zero core traces
|
|
12
|
+
|
|
13
|
+
**What changed:**
|
|
14
|
+
- Runtime license-gate tamper message now points to @tera-system/pro (was @tera-system/core)
|
|
15
|
+
- Removed ALL references to any internal package from user-visible files (LICENSE, README, WELCOME, RELEASES)
|
|
16
|
+
- Manifest renamed .opencode/tera-core.manifest.json -> .opencode/tera.manifest.json (auto-migrated on install)
|
|
17
|
+
- Bundle type renamed tera-core-bundle -> tera-bundle (signed, verified)
|
|
18
|
+
|
|
19
|
+
**Why:**
|
|
20
|
+
- Commercial isolation: the licensed product is @tera-system/pro; no trace of other packages remains for clients
|
|
21
|
+
|
|
22
|
+
## 0.2.10 (2026-08-29)
|
|
23
|
+
|
|
24
|
+
### First-sale readiness: full content sync with core 0.2.10 + license gates
|
|
25
|
+
|
|
26
|
+
**What changed:**
|
|
27
|
+
- Synced all content from core 0.2.10: 20 agents, 11 commands, tera-system (112 files), tools (8), opencode.tera.example.json (structurizr enabled)
|
|
28
|
+
- License gates preserved/re-injected: every agent has its LICENSE GATE section, every command its license-gate line (verified 1-per-file)
|
|
29
|
+
- Bundle rebuilt with production key (core-bundle-v0.2.10.signed.json) - 200 files incl. assets (pricing calculator v4.3, 35 client templates, structurizr-mcp, architecture) - binary-safe
|
|
30
|
+
|
|
31
|
+
**Why:**
|
|
32
|
+
- First commercial sale prep - client must receive the current system (source ownership SCP-004, calculator v4.3, 5-tool standard SCP-012, voucher templates, etc.)
|
|
33
|
+
|
|
7
34
|
## 0.2.2 (2026-08-28)
|
|
8
35
|
|
|
9
36
|
### حل الاستعادة عند الحذف بالخطأ + توافق npm 11
|
|
10
37
|
|
|
11
38
|
**ما تغيّر:**
|
|
12
|
-
- إضافة `bin` جديد: `tera-restore` و `tera-pro-restore` —
|
|
39
|
+
- إضافة `bin` جديد: `tera-restore` و `tera-pro-restore` — أمرا استعادة للحزمة التجارية
|
|
13
40
|
- توثيق `npm approve-scripts @tera-system/pro` كحل لأمان npm 11
|
|
14
41
|
- قسم "الاستعادة عند الحذف بالخطأ" في README
|
|
15
42
|
|
|
16
43
|
**لماذا هذا التحديث:**
|
|
17
|
-
-
|
|
44
|
+
- حذف `.opencode/agents` بالخطأ كان يحتاج أمراً صريحاً لا يعتمد على `postinstall`
|
|
18
45
|
|
|
19
46
|
---
|
|
20
47
|
|
package/WELCOME.md
CHANGED
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"structurizr-mcp",
|
|
21
21
|
"structurizr-mcp"
|
|
22
22
|
],
|
|
23
|
-
"enabled":
|
|
24
|
-
"
|
|
25
|
-
"STRUCTURIZR_WORKSPACE_DIR": "
|
|
23
|
+
"enabled": true,
|
|
24
|
+
"environment": {
|
|
25
|
+
"STRUCTURIZR_WORKSPACE_DIR": "architecture",
|
|
26
26
|
"STRUCTURIZR_URL": "http://localhost:8081"
|
|
27
27
|
}
|
|
28
28
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tera-system/pro",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"description": "Tera System Pro — commercial edition (thin): system files are fetched from teranoo.com as a signed bundle; enforced license gate + heartbeat",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"type": "module",
|
package/scripts/build-bundle.mjs
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* @tera-system/pro — Signed Core Bundle Builder
|
|
3
|
+
* @tera-system/pro — Signed Core Bundle Builder (SCP-2026-08-29-013)
|
|
4
4
|
* --------------------------------------------------------------
|
|
5
5
|
* Generates a signed bundle of all core content (agents, commands,
|
|
6
|
-
* tera-system, tools) that the remote server
|
|
6
|
+
* tera-system, tools, assets) that the remote server hosts as a static
|
|
7
7
|
* artifact. The installer/sync module downloads it, verifies the
|
|
8
8
|
* Ed25519 signature, validates hashes, and applies files.
|
|
9
9
|
*
|
|
10
|
+
* Binary-safe: every file is read as raw bytes and embedded as
|
|
11
|
+
* base64url — the pricing calculator (.xlsx) and images survive
|
|
12
|
+
* byte-for-byte.
|
|
13
|
+
*
|
|
10
14
|
* Usage:
|
|
11
15
|
* node scripts/build-bundle.mjs [--out artifacts/core-bundle.json]
|
|
12
16
|
*
|
|
@@ -52,26 +56,29 @@ function loadPrivateKey() {
|
|
|
52
56
|
throw new Error("Private key not found. Set TERA_PRIVATE_KEY_PATH.");
|
|
53
57
|
}
|
|
54
58
|
|
|
59
|
+
// Bundle groups. `assets` is binary-safe and installs to the repo root
|
|
60
|
+
// (project-control/, tera-workshop/, structurizr-mcp/, architecture/).
|
|
55
61
|
const groups = [
|
|
56
62
|
{ name: "agents", relDir: "agents" },
|
|
57
63
|
{ name: "commands", relDir: "commands" },
|
|
58
64
|
{ name: "tera-system", relDir: "core/tera-system" },
|
|
59
65
|
{ name: "tools", relDir: "tools" },
|
|
66
|
+
{ name: "assets", relDir: "assets" },
|
|
60
67
|
];
|
|
61
68
|
|
|
62
|
-
// Collect all bundle files
|
|
69
|
+
// Collect all bundle files — keep raw Buffer (binary-safe)
|
|
63
70
|
const files = [];
|
|
64
71
|
for (const g of groups) {
|
|
65
72
|
const srcDir = path.join(ROOT, g.relDir);
|
|
66
73
|
for (const f of walkFiles(srcDir)) {
|
|
67
|
-
files.push({ path: `${g.name}/${f.rel}`,
|
|
74
|
+
files.push({ path: `${g.name}/${f.rel}`, buf: fs.readFileSync(f.abs) });
|
|
68
75
|
}
|
|
69
76
|
}
|
|
70
77
|
|
|
71
|
-
// Build manifest of hashes
|
|
78
|
+
// Build manifest of hashes (over raw bytes)
|
|
72
79
|
const manifest = {};
|
|
73
80
|
for (const f of files) {
|
|
74
|
-
manifest[f.path] = createHash("sha256").update(f.
|
|
81
|
+
manifest[f.path] = createHash("sha256").update(f.buf).digest("hex");
|
|
75
82
|
}
|
|
76
83
|
|
|
77
84
|
// Canonical payload to sign: sorted manifest lines
|
|
@@ -85,13 +92,13 @@ const privateKey = loadPrivateKey();
|
|
|
85
92
|
const signature = crypto.sign(null, Buffer.from(canonicalHash), privateKey).toString("base64url");
|
|
86
93
|
|
|
87
94
|
const bundle = {
|
|
88
|
-
type: "tera-
|
|
95
|
+
type: "tera-bundle",
|
|
89
96
|
schema: 1,
|
|
90
97
|
packageName: pkg.name,
|
|
91
98
|
packageVersion: pkg.version,
|
|
92
99
|
createdAt: new Date().toISOString(),
|
|
93
100
|
contentSha256: canonicalHash,
|
|
94
|
-
files: files.map((f) => ({ path: f.path, data:
|
|
101
|
+
files: files.map((f) => ({ path: f.path, data: f.buf.toString("base64url") })),
|
|
95
102
|
manifest,
|
|
96
103
|
signature,
|
|
97
104
|
};
|
|
@@ -115,4 +122,4 @@ console.log(` contentSha256 : ${canonicalHash}`);
|
|
|
115
122
|
console.log(` signature : ${signature.slice(0, 40)}...`);
|
|
116
123
|
console.log(` full : ${outPath}`);
|
|
117
124
|
console.log(` compact : ${compactPath}`);
|
|
118
|
-
console.log(` size(full) : ${(fs.statSync(outPath).size / 1024).toFixed(1)} KB`);
|
|
125
|
+
console.log(` size(full) : ${(fs.statSync(outPath).size / 1024).toFixed(1)} KB`);
|
package/scripts/install.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* Rules:
|
|
9
9
|
* - Never deletes anything.
|
|
10
10
|
* - Never overwrites a file it does not own.
|
|
11
|
-
* - Tracks ownership via `.opencode/tera
|
|
11
|
+
* - Tracks ownership via `.opencode/tera.manifest.json`.
|
|
12
12
|
* - Only updates files it owns, always keeping a backup first.
|
|
13
13
|
* - Idempotent: re-running is safe; identical files are skipped.
|
|
14
14
|
* - Never touches an existing user `opencode.json`.
|
|
@@ -30,7 +30,8 @@ import { verifyBundle, applyBundle } from "./lib/bundle.mjs";
|
|
|
30
30
|
|
|
31
31
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
32
32
|
const PKG_ROOT = path.resolve(__dirname, "..");
|
|
33
|
-
const MANIFEST_NAME = "tera
|
|
33
|
+
const MANIFEST_NAME = "tera.manifest.json";
|
|
34
|
+
const LEGACY_MANIFEST_NAME = "tera-core.manifest.json";
|
|
34
35
|
const CONFIG_EXAMPLE = "opencode.tera.example.json";
|
|
35
36
|
|
|
36
37
|
/** Generate a stable device id from hostname + target path. */
|
|
@@ -78,7 +79,19 @@ function copyFile(src, dest) {
|
|
|
78
79
|
|
|
79
80
|
function loadOwnership(target) {
|
|
80
81
|
const p = path.join(target, ".opencode", MANIFEST_NAME);
|
|
81
|
-
if (!fs.existsSync(p))
|
|
82
|
+
if (!fs.existsSync(p)) {
|
|
83
|
+
// Legacy name (pre-0.2.11) — migrate silently
|
|
84
|
+
const legacy = path.join(target, ".opencode", LEGACY_MANIFEST_NAME);
|
|
85
|
+
if (fs.existsSync(legacy)) {
|
|
86
|
+
try {
|
|
87
|
+
const parsed = JSON.parse(fs.readFileSync(legacy, "utf8"));
|
|
88
|
+
return { files: parsed.files && typeof parsed.files === "object" ? parsed.files : {} };
|
|
89
|
+
} catch {
|
|
90
|
+
return { files: {} };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return { files: {} };
|
|
94
|
+
}
|
|
82
95
|
try {
|
|
83
96
|
const parsed = JSON.parse(fs.readFileSync(p, "utf8"));
|
|
84
97
|
return { files: parsed.files && typeof parsed.files === "object" ? parsed.files : {} };
|
|
@@ -197,7 +210,7 @@ function installProjectControl() {
|
|
|
197
210
|
|
|
198
211
|
/* ----------------------------------- run ----------------------------------- */
|
|
199
212
|
|
|
200
|
-
console.log(`\
|
|
213
|
+
console.log(`\nTera System Pro v${pkg.version} — install`);
|
|
201
214
|
console.log(` target workspace : ${target}\n`);
|
|
202
215
|
|
|
203
216
|
installGroup({ name: "agents", src: path.join(PKG_ROOT, "agents"), dest: path.join(target, ".opencode", "agents") });
|
|
@@ -231,6 +244,15 @@ const manifestPath = path.join(target, ".opencode", MANIFEST_NAME);
|
|
|
231
244
|
fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
232
245
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
233
246
|
|
|
247
|
+
// Migrate away from the legacy manifest name (pre-0.2.11) — removes any "core" trace
|
|
248
|
+
const legacyManifestPath = path.join(target, ".opencode", LEGACY_MANIFEST_NAME);
|
|
249
|
+
if (fs.existsSync(legacyManifestPath)) {
|
|
250
|
+
try {
|
|
251
|
+
fs.unlinkSync(legacyManifestPath);
|
|
252
|
+
console.log(` [manifest] migrated: ${LEGACY_MANIFEST_NAME} → ${MANIFEST_NAME}`);
|
|
253
|
+
} catch { /* non-destructive best-effort */ }
|
|
254
|
+
}
|
|
255
|
+
|
|
234
256
|
/* ------------------- license state (non-blocking, never breaks install) ------------------- */
|
|
235
257
|
|
|
236
258
|
/**
|
package/scripts/lib/bundle.mjs
CHANGED
|
@@ -18,7 +18,7 @@ export function loadPublicKey() {
|
|
|
18
18
|
* Returns { ok, reason, manifest?, contentSha256? }
|
|
19
19
|
*/
|
|
20
20
|
export function verifyBundle(bundle) {
|
|
21
|
-
if (!bundle || bundle.type !== "tera-
|
|
21
|
+
if (!bundle || bundle.type !== "tera-bundle") {
|
|
22
22
|
return { ok: false, reason: "not-a-bundle" };
|
|
23
23
|
}
|
|
24
24
|
if (typeof bundle.contentSha256 !== "string" || !bundle.signature) {
|
|
@@ -80,6 +80,7 @@ export function verifyBundle(bundle) {
|
|
|
80
80
|
* - .opencode/commands/
|
|
81
81
|
* - tera-system/
|
|
82
82
|
* - tools/
|
|
83
|
+
* - project-control/, tera-workshop/, structurizr-mcp/, architecture/ (assets group)
|
|
83
84
|
*/
|
|
84
85
|
export function applyBundle(bundle, target, { dryRun = false } = {}) {
|
|
85
86
|
if (!Array.isArray(bundle.files)) return { applied: 0, errors: [] };
|
|
@@ -89,6 +90,7 @@ export function applyBundle(bundle, target, { dryRun = false } = {}) {
|
|
|
89
90
|
commands: path.join(target, ".opencode", "commands"),
|
|
90
91
|
"tera-system": path.join(target, "tera-system"),
|
|
91
92
|
tools: path.join(target, "tools"),
|
|
93
|
+
assets: path.join(target, "."), // calculator, client templates, structurizr-mcp, architecture
|
|
92
94
|
};
|
|
93
95
|
|
|
94
96
|
let applied = 0;
|
|
@@ -169,7 +169,7 @@ export async function enforceLicenseGate(target, options = {}) {
|
|
|
169
169
|
message:
|
|
170
170
|
"❌ Tera System files have been tampered with.\n" +
|
|
171
171
|
`Tampered files: ${criticalCheck.tampered.map((t) => t.file).join(", ")}\n\n` +
|
|
172
|
-
"Reinstall required: npm install @tera-system/
|
|
172
|
+
"Reinstall required: npm install @tera-system/pro@latest (or npx tera-pro-restore)",
|
|
173
173
|
};
|
|
174
174
|
}
|
|
175
175
|
|