@tera-system/core 0.1.5 → 0.2.1
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/MANIFEST.json +2 -2
- package/RELEASES.md +56 -0
- package/package.json +2 -2
- package/scripts/install.js +23 -125
package/MANIFEST.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tera-system/core",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Build manifest for the Tera System OpenCode Plugin component.",
|
|
5
5
|
"compatibleEnvironments": [
|
|
6
6
|
"opencode"
|
|
@@ -20,5 +20,5 @@
|
|
|
20
20
|
"tools": "1f96a2de662c50c5c0a4c62d27ec583671c690163a0b35add036bfdd2b3a1f1a",
|
|
21
21
|
"core/project-control": "cb979181d50e54c9b9b9173ffc890ca021bfaefe9678b1cad4a2f67ceac3dfdd"
|
|
22
22
|
},
|
|
23
|
-
"builtAt": "2026-08-
|
|
23
|
+
"builtAt": "2026-08-26"
|
|
24
24
|
}
|
package/RELEASES.md
CHANGED
|
@@ -4,6 +4,62 @@
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## 0.2.0 (2026-08-21)
|
|
8
|
+
|
|
9
|
+
### النسخة الشخصية النظيفة (Personal Edition)
|
|
10
|
+
|
|
11
|
+
**ما تغيّر:**
|
|
12
|
+
- إزالة كل بوابات الترخيص من الوكلاء والأوامر — هذه النسخة للاستخدام الشخصي الحر
|
|
13
|
+
- إزالة ملفات الحماية (license-gate, integrity, preflight)
|
|
14
|
+
- استعادة `install.js` للنسخة البسيطة الأصلية (تسجيل حالة ترخيص غير مُلزم)
|
|
15
|
+
- الفصل عن الحزمة التجارية: `@tera-system/pro`
|
|
16
|
+
|
|
17
|
+
**لماذا هذا التحديث:**
|
|
18
|
+
- `@tera-system/core` = نسخة شخصية للمالك (Majed) تستخدم في مشاريعه بحرية
|
|
19
|
+
- الحماية التجارية (الترخيص، sync من السيرفر، heartbeat) تنتقل إلى `@tera-system/pro`
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 0.1.5 (2026-08-21)
|
|
24
|
+
|
|
25
|
+
### ترقية: التحقق من الترخيص عبر API (أونلاين) مع fallback أوفلاين
|
|
26
|
+
|
|
27
|
+
**ما تم تحسينه:**
|
|
28
|
+
- **التحقق الأونلاين أولاً:** المُثبِّت يحاول الاتصال بـ API التحقق أولاً
|
|
29
|
+
- **Fallback أوفلاين:** إذا فشل الاتصال، يتحقق محلياً بـ Ed25519
|
|
30
|
+
- **لا يُوقف التثبيت:** التحقق لا يُوقف التثبيت أبداً
|
|
31
|
+
- **dıagnostic واضح:** يُظهر مصدر التحقق (☁️ API / 🔒 Offline / 🔄 Fallback)
|
|
32
|
+
|
|
33
|
+
**المتغيرات البيئية الجديدة:**
|
|
34
|
+
| المتغير | الوصف | القيمة الافتراضية |
|
|
35
|
+
|---------|-------|-------------------|
|
|
36
|
+
| `TERA_LICENSE_API` | رابط API التحقق | `https://teranoo.com/api/license` |
|
|
37
|
+
| `TERA_OFFLINE_ONLY` | تعطيل التحقق الأونلاين | `0` (مفعّل) |
|
|
38
|
+
|
|
39
|
+
**لماذا هذا التحديث:**
|
|
40
|
+
- التحقق الأونلاين يمنع التkey lowering
|
|
41
|
+
- Fallback يضمن عمل التثبيت حتى بدون إنترنت
|
|
42
|
+
- استعداد للعميل الذي يُنفّذ API التحقق
|
|
43
|
+
|
|
44
|
+
**ملاحظات:**
|
|
45
|
+
- API التحقق بانتظار تنفيذ العميل
|
|
46
|
+
- التحقق الأوفلاين يعمل بشكل مستقل حتى بدون API
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 0.1.4 (2026-08-21)
|
|
51
|
+
|
|
52
|
+
### إصلاح: تغيير حالة الترخيص عبر npm
|
|
53
|
+
|
|
54
|
+
**ما تم إصلاحه:**
|
|
55
|
+
- تحديث حالة الترخيص من "Active" إلى "Unlicensed" عبر npm
|
|
56
|
+
- توثيق حالة الترخيص في LICENSE.md
|
|
57
|
+
|
|
58
|
+
**ملاحظات:**
|
|
59
|
+
- هذا إصلاح طارئ بعد النشر
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
7
63
|
## 0.1.1 (2026-08-21)
|
|
8
64
|
|
|
9
65
|
### تحديث: توضيح الترخيص والتوثيق
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tera-system/core",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Tera System OpenCode Plugin
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Tera System OpenCode Plugin — governance core, 20 agents, 11 commands, distribution tools (personal edition)",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
package/scripts/install.js
CHANGED
|
@@ -84,7 +84,7 @@ const target = resolveTarget();
|
|
|
84
84
|
const pkg = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, "package.json"), "utf8"));
|
|
85
85
|
const ownership = loadOwnership(target);
|
|
86
86
|
|
|
87
|
-
const stats = { installed: 0, updated: 0, identical: 0, skippedGroups: 0, conflicts: [] };
|
|
87
|
+
const stats = { installed: 0, updated: 0, identical: 0, adopted: 0, skippedGroups: 0, conflicts: [] };
|
|
88
88
|
|
|
89
89
|
/* ------------------------------- group install ------------------------------- */
|
|
90
90
|
|
|
@@ -102,7 +102,15 @@ function installGroup(group) {
|
|
|
102
102
|
if (fs.existsSync(destFile)) {
|
|
103
103
|
const destHash = sha256(fs.readFileSync(destFile));
|
|
104
104
|
if (destHash === srcHash) {
|
|
105
|
-
|
|
105
|
+
// Identical content — adopt ownership if not already owned.
|
|
106
|
+
// (byte-identical file = package content, so it is safe to claim it.)
|
|
107
|
+
if (!Object.prototype.hasOwnProperty.call(ownership.files, destRel)) {
|
|
108
|
+
ownership.files[destRel] = srcHash;
|
|
109
|
+
stats.adopted++;
|
|
110
|
+
console.log(` [adopt ] ${destRel} — identical, now owned by package`);
|
|
111
|
+
} else {
|
|
112
|
+
stats.identical++;
|
|
113
|
+
}
|
|
106
114
|
continue;
|
|
107
115
|
}
|
|
108
116
|
const owned = Object.prototype.hasOwnProperty.call(ownership.files, destRel);
|
|
@@ -203,6 +211,7 @@ const manifest = {
|
|
|
203
211
|
installed: stats.installed,
|
|
204
212
|
updated: stats.updated,
|
|
205
213
|
identical: stats.identical,
|
|
214
|
+
adopted: stats.adopted,
|
|
206
215
|
skippedGroups: stats.skippedGroups,
|
|
207
216
|
conflicts: stats.conflicts.length,
|
|
208
217
|
},
|
|
@@ -213,139 +222,28 @@ fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
|
|
|
213
222
|
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
214
223
|
|
|
215
224
|
/* ------------------- license state (non-blocking, never breaks install) ------------------- */
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
* Flow:
|
|
221
|
-
* 1. Try online API verification (if API URL is configured)
|
|
222
|
-
* 2. On API failure → fallback to offline Ed25519 verification
|
|
223
|
-
* 3. Never blocks install — always continues
|
|
224
|
-
*
|
|
225
|
-
* Environment variables:
|
|
226
|
-
* TERA_LICENSE_API — API base URL (e.g. https://teranoo.com/api/license)
|
|
227
|
-
* TERA_LICENSE_KEY — Manual license key override
|
|
228
|
-
*/
|
|
229
|
-
const LICENSE_API_URL = process.env.TERA_LICENSE_API || "https://teranoo.com/api/license";
|
|
230
|
-
const LICENSE_TIMEOUT_MS = 5000; // 5 seconds max for API call
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Verify license via remote API (online).
|
|
234
|
-
* Returns: { ok: boolean, state: string, reason: string, expiresAt?: string, source: "api" }
|
|
235
|
-
*/
|
|
236
|
-
async function verifyViaAPI(licenseKey) {
|
|
237
|
-
if (!licenseKey) return { ok: false, state: "Unlicensed", reason: "no-key", source: "api" };
|
|
238
|
-
|
|
239
|
-
try {
|
|
240
|
-
const controller = new AbortController();
|
|
241
|
-
const timeout = setTimeout(() => controller.abort(), LICENSE_TIMEOUT_MS);
|
|
242
|
-
|
|
243
|
-
const res = await fetch(`${LICENSE_API_URL}/verify`, {
|
|
244
|
-
method: "POST",
|
|
245
|
-
headers: { "Content-Type": "application/json" },
|
|
246
|
-
body: JSON.stringify({ licenseKey }),
|
|
247
|
-
signal: controller.signal,
|
|
248
|
-
});
|
|
249
|
-
clearTimeout(timeout);
|
|
250
|
-
|
|
251
|
-
if (!res.ok) {
|
|
252
|
-
return { ok: false, state: "Invalid", reason: `api-http-${res.status}`, source: "api" };
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const data = await res.json();
|
|
256
|
-
return {
|
|
257
|
-
ok: data.valid === true,
|
|
258
|
-
state: data.state || "Invalid",
|
|
259
|
-
reason: data.reason || "api-response",
|
|
260
|
-
expiresAt: data.expiresAt || null,
|
|
261
|
-
source: "api",
|
|
262
|
-
};
|
|
263
|
-
} catch (err) {
|
|
264
|
-
// Network error, timeout, abort — fall through to offline
|
|
265
|
-
return { ok: false, state: "Unknown", reason: `api-error: ${err.message}`, source: "api-fallback" };
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Verify license offline (Ed25519 signature check).
|
|
271
|
-
* Returns: { ok: boolean, state: string, reason: string, expiresAt?: string, source: "offline" }
|
|
272
|
-
*/
|
|
273
|
-
function verifyOffline(licenseKey) {
|
|
225
|
+
try {
|
|
226
|
+
const stateFile = path.join(target, ".tera", "license.state.json");
|
|
227
|
+
let licenseKey = (readLicenseState(stateFile) || {}).licenseKey;
|
|
228
|
+
if (!licenseKey) licenseKey = process.env.TERA_LICENSE_KEY || undefined;
|
|
274
229
|
const state = assessLicenseState(licenseKey);
|
|
275
|
-
|
|
276
|
-
ok: state.state === "Active" || state.state === "Grace",
|
|
230
|
+
writeLicenseState(stateFile, {
|
|
277
231
|
state: state.state,
|
|
278
232
|
reason: state.reason,
|
|
233
|
+
licenseKey: licenseKey || null,
|
|
279
234
|
expiresAt: state.expiresAt || null,
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
};
|
|
235
|
+
checkedAt: new Date().toISOString(),
|
|
236
|
+
});
|
|
237
|
+
console.log(` [license ] state=${state.state} (${state.reason})`);
|
|
238
|
+
} catch (err) {
|
|
239
|
+
console.log(` [license ] skipped (${err.message}) — install continues`);
|
|
283
240
|
}
|
|
284
241
|
|
|
285
|
-
/**
|
|
286
|
-
* Combined verification: API-first, offline fallback.
|
|
287
|
-
* Always returns a result — never throws.
|
|
288
|
-
*/
|
|
289
|
-
async function verifyLicense(licenseKey) {
|
|
290
|
-
// Step 1: Try API (unless explicitly disabled)
|
|
291
|
-
if (process.env.TERA_OFFLINE_ONLY === "1") {
|
|
292
|
-
return verifyOffline(licenseKey);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
const apiResult = await verifyViaAPI(licenseKey);
|
|
296
|
-
|
|
297
|
-
// If API succeeded and gave a definitive answer, use it
|
|
298
|
-
if (apiResult.source === "api" && apiResult.ok !== undefined) {
|
|
299
|
-
return apiResult;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Step 2: Fallback to offline
|
|
303
|
-
const offlineResult = verifyOffline(licenseKey);
|
|
304
|
-
|
|
305
|
-
// If offline confirms API's failure, use offline (more trusted)
|
|
306
|
-
if (!apiResult.ok && !offlineResult.ok) {
|
|
307
|
-
return offlineResult;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// If API failed but offline says active, trust offline (API might be down)
|
|
311
|
-
if (!apiResult.ok && offlineResult.ok) {
|
|
312
|
-
return { ...offlineResult, reason: `offline-verified (api-unavailable: ${apiResult.reason})` };
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Default: trust API
|
|
316
|
-
return apiResult;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// Run license check (async wrapper, non-blocking)
|
|
320
|
-
(async () => {
|
|
321
|
-
try {
|
|
322
|
-
const stateFile = path.join(target, ".tera", "license.state.json");
|
|
323
|
-
let licenseKey = (readLicenseState(stateFile) || {}).licenseKey;
|
|
324
|
-
if (!licenseKey) licenseKey = process.env.TERA_LICENSE_KEY || undefined;
|
|
325
|
-
|
|
326
|
-
const result = await verifyLicense(licenseKey);
|
|
327
|
-
|
|
328
|
-
writeLicenseState(stateFile, {
|
|
329
|
-
state: result.state,
|
|
330
|
-
reason: result.reason,
|
|
331
|
-
licenseKey: licenseKey || null,
|
|
332
|
-
expiresAt: result.expiresAt || null,
|
|
333
|
-
graceUntil: result.graceUntil || null,
|
|
334
|
-
source: result.source,
|
|
335
|
-
checkedAt: new Date().toISOString(),
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
const sourceTag = result.source === "api" ? "☁️" : result.source === "offline" ? "🔒" : "🔄";
|
|
339
|
-
console.log(` [license ] ${sourceTag} state=${result.state} (${result.reason})`);
|
|
340
|
-
} catch (err) {
|
|
341
|
-
console.log(` [license ] skipped (${err.message}) — install continues`);
|
|
342
|
-
}
|
|
343
|
-
})();
|
|
344
|
-
|
|
345
242
|
console.log("\n summary:");
|
|
346
243
|
console.log(` installed : ${stats.installed}`);
|
|
347
244
|
console.log(` updated : ${stats.updated}`);
|
|
348
245
|
console.log(` identical : ${stats.identical}`);
|
|
246
|
+
console.log(` adopted : ${stats.adopted} (pre-existing identical files now owned)`);
|
|
349
247
|
console.log(` conflicts : ${stats.conflicts.length} (your files kept untouched)`);
|
|
350
248
|
console.log(` skipped : ${stats.skippedGroups} group(s) — user-owned state`);
|
|
351
249
|
console.log(` ownership log : ${path.join(".opencode", MANIFEST_NAME)}\n`);
|