@tera-system/pro 0.1.3 → 0.2.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/MANIFEST.json +5 -3
- package/package.json +3 -6
- package/scripts/build.mjs +4 -3
- package/scripts/install.js +83 -15
- package/scripts/lib/license-gate.mjs +4 -4
- package/scripts/tera-heartbeat.mjs +4 -4
- package/agents/application-blueprint.md +0 -411
- package/agents/auditor.md +0 -669
- package/agents/design-reviewer.md +0 -396
- package/agents/domain-expert-agent.md +0 -514
- package/agents/domain-research-agent.md +0 -364
- package/agents/engineering-agent-dotnet.md +0 -222
- package/agents/engineering-agent-phaser.md +0 -279
- package/agents/engineering-agent-typescript.md +0 -304
- package/agents/engineering-agent.md +0 -147
- package/agents/monitor.md +0 -399
- package/agents/production-erp-expert.md +0 -510
- package/agents/project-knowledge-agent.md +0 -275
- package/agents/qa-agent.md +0 -502
- package/agents/tera-business-transformation-consultant.md +0 -294
- package/agents/tera-client-engagement.md +0 -895
- package/agents/tera-software-designer.md +0 -241
- package/agents/tera-strategic-advisor.md +0 -329
- package/agents/tera-system-evolution.md +0 -763
- package/agents/tera.md +0 -524
- package/agents/ui-designer.md +0 -430
- package/commands/tera-approve.md +0 -36
- package/commands/tera-diagnose.md +0 -49
- package/commands/tera-gate.md +0 -42
- package/commands/tera-help.md +0 -43
- package/commands/tera-new-project.md +0 -37
- package/commands/tera-plan.md +0 -36
- package/commands/tera-request-build.md +0 -47
- package/commands/tera-resume.md +0 -36
- package/commands/tera-review.md +0 -54
- package/commands/tera-status.md +0 -40
- package/commands/tera-update.md +0 -50
- package/core/tera-system/AGENT_ACTIVATION_MATRIX.md +0 -285
- package/core/tera-system/AGENT_DEPENDENCY_MAP.md +0 -117
- package/core/tera-system/AGENT_GENERATION_TEMPLATE.md +0 -312
- package/core/tera-system/AGENT_PERMISSION_MODEL.md +0 -343
- package/core/tera-system/AIS_PROTOCOL.md +0 -191
- package/core/tera-system/TERA_AGENT_CONDUCT.md +0 -102
- package/core/tera-system/TERA_CONTINUOUS_IMPROVEMENT_POLICY.md +0 -111
- package/core/tera-system/TERA_DISTRIBUTION_POLICY.md +0 -291
- package/core/tera-system/TERA_LICENSE_GUARD.md +0 -144
- package/core/tera-system/TERA_PROJECT_DECISION.md +0 -281
- package/core/tera-system/TERA_USER_GUIDE.md +0 -462
- package/core/tera-system/TOOLING_AND_MCP_POLICY.md +0 -285
- package/core/tera-system/TeraApplicationQuestionBank.md +0 -362
- package/core/tera-system/TeraArchitectureMap.md +0 -91
- package/core/tera-system/TeraClientPolicy.md +0 -366
- package/core/tera-system/TeraHelperAgents.md +0 -970
- package/core/tera-system/TeraPolicyMap.md +0 -131
- package/core/tera-system/TeraPreExecutionGate.md +0 -818
- package/core/tera-system/TeraPreparationDocumentationGovernance.md +0 -370
- package/core/tera-system/TeraPricingPolicy.md +0 -674
- package/core/tera-system/TeraProjectIntakePolicy.md +0 -164
- package/core/tera-system/TeraScenarioStressTests.md +0 -168
- package/core/tera-system/TeraSubAgents.md +0 -854
- package/core/tera-system/TeraSystemMaintenanceChecklist.md +0 -80
- package/core/tera-system/TeraTokenPolicy.md +0 -362
- package/core/tera-system/Tera_Project_Preparation_Files.md +0 -1045
- package/core/tera-system/agent-helpers/application-blueprint-details.md +0 -177
- package/core/tera-system/client-helpers/tera-client-engagement-discovery-domains.md +0 -99
- package/core/tera-system/client-helpers/tera-client-engagement-gates.md +0 -258
- package/core/tera-system/client-helpers/tera-client-engagement-pricing.md +0 -341
- package/core/tera-system/client-helpers/tera-client-engagement-protocols.md +0 -692
- package/core/tera-system/consulting-helpers/BTCA_METHODOLOGY_FRAMEWORK.md +0 -195
- package/core/tera-system/consulting-helpers/BTCA_REPORT_TEMPLATES.md +0 -266
- package/core/tera-system/design-system/ACCESSIBILITY_RULES.md +0 -31
- package/core/tera-system/design-system/COMPONENT_LIBRARY_SCHEMA.md +0 -46
- package/core/tera-system/design-system/DESIGN_MD_INTEGRATION.md +0 -59
- package/core/tera-system/design-system/DESIGN_REVIEW_STANDARDS.md +0 -241
- package/core/tera-system/design-system/DESIGN_SOURCE_PROTOCOL.md +0 -61
- package/core/tera-system/design-system/DESIGN_SYSTEM_OVERVIEW.md +0 -66
- package/core/tera-system/design-system/DESIGN_TOKENS_SCHEMA.md +0 -66
- package/core/tera-system/design-system/EXTERNAL_REFERENCE_ANALYSIS.md +0 -52
- package/core/tera-system/design-system/FIGMA_INTEGRATION.md +0 -138
- package/core/tera-system/design-system/INTERNAL_KITS_INDEX.md +0 -26
- package/core/tera-system/design-system/LAYOUT_PATTERNS.md +0 -52
- package/core/tera-system/design-system/MOBILE_UI_UX_STANDARDS.md +0 -342
- package/core/tera-system/design-system/RTL_LTR_RULES.md +0 -39
- package/core/tera-system/design-system/UI_ACCEPTANCE_GATE.md +0 -80
- package/core/tera-system/design-system/kits/KIT_ADMIN_DASHBOARD.md +0 -102
- package/core/tera-system/engineering-governance/ENGINEERING_AGENT_RESPONSIBILITIES.md +0 -210
- package/core/tera-system/engineering-governance/ENGINEERING_BEST_PRACTICES.md +0 -468
- package/core/tera-system/engineering-governance/ENGINEERING_GOVERNANCE_GATE.md +0 -131
- package/core/tera-system/engineering-governance/ENGINEERING_REVIEW_CHECKLIST.md +0 -129
- package/core/tera-system/engineering-governance/QUALITY_GATE_THRESHOLDS.md +0 -159
- package/core/tera-system/engineering-helpers/engineering-agent-core.md +0 -171
- package/core/tera-system/knowledge-base/OPENHANDS_ARCHITECTURE_REFERENCE.md +0 -243
- package/core/tera-system/knowledge-base/manufacturing/00_INDEX.md +0 -32
- package/core/tera-system/knowledge-base/manufacturing/01_MANUFACTURING_ERP_CORE_CONCEPTS.md +0 -7
- package/core/tera-system/knowledge-base/manufacturing/02_SAP_MANUFACTURING_RESEARCH.md +0 -7
- package/core/tera-system/knowledge-base/manufacturing/03_DYNAMICS_365_MANUFACTURING_RESEARCH.md +0 -7
- package/core/tera-system/knowledge-base/manufacturing/04_ORACLE_MANUFACTURING_RESEARCH.md +0 -7
- package/core/tera-system/knowledge-base/manufacturing/05_ODOO_MANUFACTURING_RESEARCH.md +0 -7
- package/core/tera-system/knowledge-base/manufacturing/06_ERPNEXT_MANUFACTURING_RESEARCH.md +0 -7
- package/core/tera-system/knowledge-base/manufacturing/07_MANUFACTURING_COSTING_GUIDE.md +0 -7
- package/core/tera-system/knowledge-base/manufacturing/08_PRODUCTION_DISCOVERY_QUESTIONS.md +0 -7
- package/core/tera-system/knowledge-base/manufacturing/09_MANUFACTURING_BLUEPRINT_CHECKLIST.md +0 -7
- package/core/tera-system/knowledge-base/manufacturing/10_PRODUCTION_TEST_SCENARIOS.md +0 -7
- package/core/tera-system/knowledge-base/manufacturing/11_QUALITY_REWORK_AND_SCRAP_GUIDE.md +0 -7
- package/core/tera-system/knowledge-base/manufacturing/12_MRP_AND_PLANNING_GUIDE.md +0 -7
- package/core/tera-system/knowledge-base/manufacturing/13_LOCAL_AND_REGIONAL_MANUFACTURING_CONTEXT.md +0 -7
- package/core/tera-system/knowledge-base/manufacturing/14_VENDOR_COMPARISON_MATRIX.md +0 -7
- package/core/tera-system/product-standards/maintenance-apps/BEST_PRACTICES_DOMAIN.md +0 -325
- package/core/tera-system/product-standards/maintenance-apps/STANDARD_DEFINITION.md +0 -142
- package/core/tera-system/profiles/PROFILES_INDEX.md +0 -57
- package/core/tera-system/profiles/TEMPLATE.md +0 -47
- package/core/tera-system/profiles/dotnet-blazor-ef.md +0 -76
- package/core/tera-system/profiles/dotnet-razorpages-adonet.md +0 -137
- package/core/tera-system/profiles/dotnet-wpf-sqlite.md +0 -159
- package/core/tera-system/profiles/effect-bun-opencode.md +0 -109
- package/core/tera-system/profiles/flutter-mobile.md +0 -369
- package/core/tera-system/profiles/nextjs-prisma.md +0 -110
- package/core/tera-system/profiles/phaser-react-node.md +0 -302
- package/core/tera-system/profiles/react-pwa.md +0 -97
- package/core/tera-system/runtime/CLIENT_DISCOVERY_PROTOCOL.md +0 -145
- package/core/tera-system/runtime/DOMAIN_INTELLIGENCE_PROTOCOL.md +0 -124
- package/core/tera-system/runtime/MVP_DEFINITION_PROTOCOL.md +0 -176
- package/core/tera-system/runtime/TERA_RUNTIME_CHECKLISTS.md +0 -646
- package/core/tera-system/runtime/TERA_RUNTIME_PROTOCOLS.md +0 -50
- package/core/tera-system/runtime/TERA_RUNTIME_PROTOCOLS_CLIENT.md +0 -355
- package/core/tera-system/runtime/TERA_RUNTIME_PROTOCOLS_CORE.md +0 -799
- package/core/tera-system/runtime/TERA_RUNTIME_TEMPLATES.md +0 -908
- package/core/tera-system/runtime/TERA_RUNTIME_TEMPLATES_DELIVERY.md +0 -584
- package/core/tera-system/runtime/TERA_RUNTIME_TEMPLATES_PREPARATION.md +0 -376
- package/core/tera-system/runtime/TERA_SOLUTION_PREPARATION_PROTOCOL.md +0 -335
- package/core/tera-system/runtime/TERA_SOLUTION_PREPARATION_TEMPLATES.md +0 -397
- package/core/tera-system/runtime/VERSION_LIFECYCLE_PROTOCOL.md +0 -296
- package/core/tera-system/semgrep-rules/README.md +0 -32
- package/core/tera-system/semgrep-rules/tera-security.yml +0 -66
- package/core/tera-system/semgrep-rules/tera-standards.yml +0 -49
- package/core/tera-system/teranoo-ui/README.md +0 -48
- package/core/tera-system/teranoo-ui/components/button.tsx +0 -51
- package/core/tera-system/teranoo-ui/components/card.tsx +0 -49
- package/core/tera-system/teranoo-ui/components/dashboard-layout.tsx +0 -36
- package/core/tera-system/teranoo-ui/components/data-table.tsx +0 -146
- package/core/tera-system/teranoo-ui/components/empty-state.tsx +0 -31
- package/core/tera-system/teranoo-ui/components/kpi-card.tsx +0 -42
- package/core/tera-system/teranoo-ui/components/page-header.tsx +0 -25
- package/core/tera-system/teranoo-ui/components/search-input.tsx +0 -40
- package/core/tera-system/teranoo-ui/components/sidebar.tsx +0 -66
- package/core/tera-system/teranoo-ui/components/stats-card.tsx +0 -37
- package/core/tera-system/teranoo-ui/registry.json +0 -77
- package/core/tera-system/teranoo-ui/styles/teranoo-theme.css +0 -61
- package/tools/tera-clean.ps1 +0 -97
- package/tools/tera-fetch.ps1 +0 -165
- package/tools/tera-release.ps1 +0 -96
- package/tools/tera-schedule.ps1 +0 -59
- package/tools/tera-update.ps1 +0 -472
- package/tools/tera-watch.ps1 +0 -154
- package/tools/update-client-repositories.ps1 +0 -92
package/MANIFEST.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tera-system/pro",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Build manifest for the Tera System Pro commercial plugin component.",
|
|
3
|
+
"version": "0.2.0",
|
|
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"
|
|
7
7
|
],
|
|
8
8
|
"minOpencodeVersion": "1.18.16",
|
|
9
|
+
"mode": "thin",
|
|
9
10
|
"content": {
|
|
10
11
|
"agents": 20,
|
|
11
12
|
"commands": 11,
|
|
@@ -13,7 +14,8 @@
|
|
|
13
14
|
"tools": 7,
|
|
14
15
|
"projectControlTemplates": 1
|
|
15
16
|
},
|
|
16
|
-
"
|
|
17
|
+
"bundleRef": "core-bundle-v0.1.4.signed.json (SHA256 4C87BAB833AEC0ACE184A790819B51D58427C51F29124CE2B02DA3AAE98C3F13)",
|
|
18
|
+
"builtAt": "2026-08-27",
|
|
17
19
|
"sha256": {
|
|
18
20
|
"agents": "ef634a3bd0a1fb3739b59b74b1e58c150a6beed73c48ce48ac12416b15b6a6a6",
|
|
19
21
|
"commands": "f8c91dab42e41d1f8362edfd816964ceabdbd95e8ebdd02e83ea36f7f805a259",
|
package/package.json
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tera-system/pro",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Tera System Pro
|
|
3
|
+
"version": "0.2.0",
|
|
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",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=18"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"agents",
|
|
12
|
-
"commands",
|
|
13
|
-
"core",
|
|
14
|
-
"tools",
|
|
15
11
|
"scripts",
|
|
12
|
+
"core/project-control",
|
|
16
13
|
"MANIFEST.json",
|
|
17
14
|
"opencode.tera.example.json",
|
|
18
15
|
"WELCOME.md",
|
package/scripts/build.mjs
CHANGED
|
@@ -57,8 +57,9 @@ const groupSha = (dir) => {
|
|
|
57
57
|
return createHash("sha256").update(lines.join("\n")).digest("hex");
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
/* 1. self-check counts vs manifest */
|
|
60
|
+
/* 1. self-check counts vs manifest (thin mode: content dirs absent is OK) */
|
|
61
61
|
console.log("── self-check: content counts vs MANIFEST.json");
|
|
62
|
+
const thinMode = manifest.mode === "thin";
|
|
62
63
|
const checks = [
|
|
63
64
|
["agents", "agents"],
|
|
64
65
|
["commands", "commands"],
|
|
@@ -68,8 +69,8 @@ let fail = false;
|
|
|
68
69
|
for (const [dir, key] of checks) {
|
|
69
70
|
const n = countFiles(dir);
|
|
70
71
|
const expected = manifest.content[key];
|
|
71
|
-
const ok = n === expected;
|
|
72
|
-
console.log(` ${dir.padEnd(16)} ${n} (manifest ${expected}) ${ok ? "OK" : "MISMATCH"}`);
|
|
72
|
+
const ok = n === expected || (thinMode && n === 0);
|
|
73
|
+
console.log(` ${dir.padEnd(16)} ${n} (manifest ${expected}) ${ok ? "OK" : "MISMATCH"}${thinMode && n === 0 ? " [thin — served via bundle]" : ""}`);
|
|
73
74
|
if (!ok) fail = true;
|
|
74
75
|
}
|
|
75
76
|
if (fail) {
|
package/scripts/install.js
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import { createHash } from "node:crypto";
|
|
23
23
|
import fs from "node:fs";
|
|
24
|
+
import os from "node:os";
|
|
24
25
|
import path from "node:path";
|
|
25
26
|
import { fileURLToPath } from "node:url";
|
|
26
27
|
import { assessLicenseState, readLicenseState, writeLicenseState } from "./lib/license.mjs";
|
|
@@ -32,6 +33,12 @@ const PKG_ROOT = path.resolve(__dirname, "..");
|
|
|
32
33
|
const MANIFEST_NAME = "tera-core.manifest.json";
|
|
33
34
|
const CONFIG_EXAMPLE = "opencode.tera.example.json";
|
|
34
35
|
|
|
36
|
+
/** Generate a stable device id from hostname + target path. */
|
|
37
|
+
function createDeviceId(target) {
|
|
38
|
+
const seed = `${os.hostname()}::${target}`;
|
|
39
|
+
return createHash("sha256").update(seed).digest("hex").slice(0, 16);
|
|
40
|
+
}
|
|
41
|
+
|
|
35
42
|
/* ---------------------------------- helpers ---------------------------------- */
|
|
36
43
|
|
|
37
44
|
const sha256 = (buf) => createHash("sha256").update(buf).digest("hex");
|
|
@@ -243,10 +250,12 @@ const LICENSE_TIMEOUT_MS = 5000; // 5 seconds max for API call
|
|
|
243
250
|
|
|
244
251
|
/**
|
|
245
252
|
* Verify license via remote API (online).
|
|
253
|
+
* Requires BOTH licenseKey and accountEmail (the API validates both).
|
|
246
254
|
* Returns: { ok: boolean, state: string, reason: string, expiresAt?: string, source: "api" }
|
|
247
255
|
*/
|
|
248
|
-
async function verifyViaAPI(licenseKey) {
|
|
256
|
+
async function verifyViaAPI(licenseKey, accountEmail) {
|
|
249
257
|
if (!licenseKey) return { ok: false, state: "Unlicensed", reason: "no-key", source: "api" };
|
|
258
|
+
if (!accountEmail) return { ok: false, state: "Unknown", reason: "no-account-email", source: "api-fallback" };
|
|
250
259
|
|
|
251
260
|
try {
|
|
252
261
|
const controller = new AbortController();
|
|
@@ -255,7 +264,7 @@ async function verifyViaAPI(licenseKey) {
|
|
|
255
264
|
const res = await fetch(`${LICENSE_API_URL}/verify`, {
|
|
256
265
|
method: "POST",
|
|
257
266
|
headers: { "Content-Type": "application/json" },
|
|
258
|
-
body: JSON.stringify({ licenseKey }),
|
|
267
|
+
body: JSON.stringify({ licenseKey, accountEmail }),
|
|
259
268
|
signal: controller.signal,
|
|
260
269
|
});
|
|
261
270
|
clearTimeout(timeout);
|
|
@@ -270,7 +279,8 @@ async function verifyViaAPI(licenseKey) {
|
|
|
270
279
|
const data = await res.json();
|
|
271
280
|
return {
|
|
272
281
|
ok: data.valid === true,
|
|
273
|
-
|
|
282
|
+
// The API does not return a `state` field — derive it from `valid` + `reason`.
|
|
283
|
+
state: data.valid === true ? "Active" : data.state || "Invalid",
|
|
274
284
|
reason: data.reason || "api-response",
|
|
275
285
|
expiresAt: data.expiresAt || null,
|
|
276
286
|
source: "api",
|
|
@@ -281,6 +291,29 @@ async function verifyViaAPI(licenseKey) {
|
|
|
281
291
|
}
|
|
282
292
|
}
|
|
283
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Try to ACTIVATE the license on this device (best-effort, never breaks install).
|
|
296
|
+
* Requires licenseKey + accountEmail + deviceId.
|
|
297
|
+
*/
|
|
298
|
+
async function activateViaAPI(licenseKey, accountEmail, deviceId) {
|
|
299
|
+
if (!licenseKey || !accountEmail || !deviceId) return null;
|
|
300
|
+
try {
|
|
301
|
+
const controller = new AbortController();
|
|
302
|
+
const timeout = setTimeout(() => controller.abort(), LICENSE_TIMEOUT_MS);
|
|
303
|
+
const res = await fetch(`${LICENSE_API_URL}/activate`, {
|
|
304
|
+
method: "POST",
|
|
305
|
+
headers: { "Content-Type": "application/json" },
|
|
306
|
+
body: JSON.stringify({ licenseKey, accountEmail, deviceId }),
|
|
307
|
+
signal: controller.signal,
|
|
308
|
+
});
|
|
309
|
+
clearTimeout(timeout);
|
|
310
|
+
if (!res.ok) return null;
|
|
311
|
+
return await res.json();
|
|
312
|
+
} catch {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
284
317
|
/**
|
|
285
318
|
* Verify license offline (Ed25519 signature check).
|
|
286
319
|
* Returns: { ok: boolean, state: string, reason: string, expiresAt?: string, source: "offline" }
|
|
@@ -298,16 +331,16 @@ function verifyOffline(licenseKey) {
|
|
|
298
331
|
}
|
|
299
332
|
|
|
300
333
|
/**
|
|
301
|
-
* Combined verification: API-first, offline fallback.
|
|
334
|
+
* Combined verification: API-first (with email), offline fallback.
|
|
302
335
|
* Always returns a result — never throws.
|
|
303
336
|
*/
|
|
304
|
-
async function verifyLicense(licenseKey) {
|
|
337
|
+
async function verifyLicense(licenseKey, accountEmail) {
|
|
305
338
|
// Step 1: Try API (unless explicitly disabled)
|
|
306
339
|
if (process.env.TERA_OFFLINE_ONLY === "1") {
|
|
307
340
|
return verifyOffline(licenseKey);
|
|
308
341
|
}
|
|
309
342
|
|
|
310
|
-
const apiResult = await verifyViaAPI(licenseKey);
|
|
343
|
+
const apiResult = await verifyViaAPI(licenseKey, accountEmail);
|
|
311
344
|
|
|
312
345
|
// If API succeeded and gave a definitive answer, use it
|
|
313
346
|
if (apiResult.source === "api" && apiResult.ok !== undefined) {
|
|
@@ -337,7 +370,7 @@ const SYNC_URL = process.env.TERA_SYNC_URL || "https://teranoo.com/api/tera-syst
|
|
|
337
370
|
/**
|
|
338
371
|
* Attempt server sync (best-effort, never breaks install).
|
|
339
372
|
* Downloads the signed bundle, verifies signature + hashes, and applies.
|
|
340
|
-
*
|
|
373
|
+
* In THIN mode this is the only source of system files (required).
|
|
341
374
|
*/
|
|
342
375
|
async function tryServerSync() {
|
|
343
376
|
try {
|
|
@@ -346,20 +379,20 @@ async function tryServerSync() {
|
|
|
346
379
|
const res = await fetch(SYNC_URL, { signal: controller.signal });
|
|
347
380
|
clearTimeout(timeout);
|
|
348
381
|
if (!res.ok) {
|
|
349
|
-
console.log(` [sync ] server HTTP ${res.status} —
|
|
382
|
+
console.log(` [sync ] server HTTP ${res.status} — no files applied`);
|
|
350
383
|
return false;
|
|
351
384
|
}
|
|
352
385
|
const bundle = await res.json();
|
|
353
386
|
const v = verifyBundle(bundle);
|
|
354
387
|
if (!v.ok) {
|
|
355
|
-
console.log(` [sync ] bundle rejected (${v.reason}) —
|
|
388
|
+
console.log(` [sync ] bundle rejected (${v.reason}) — no files applied`);
|
|
356
389
|
return false;
|
|
357
390
|
}
|
|
358
391
|
const r = applyBundle(bundle, target);
|
|
359
392
|
console.log(` [sync ] server bundle applied — ${r.applied} files (signed & verified)`);
|
|
360
393
|
return true;
|
|
361
394
|
} catch {
|
|
362
|
-
console.log(` [sync ] server unreachable —
|
|
395
|
+
console.log(` [sync ] server unreachable — no files applied`);
|
|
363
396
|
return false;
|
|
364
397
|
}
|
|
365
398
|
}
|
|
@@ -367,8 +400,28 @@ async function tryServerSync() {
|
|
|
367
400
|
// Run: sync → integrity manifest → license check (async wrapper, non-blocking)
|
|
368
401
|
(async () => {
|
|
369
402
|
try {
|
|
370
|
-
//
|
|
371
|
-
|
|
403
|
+
// Thin-mode detection: no bundled content → the server bundle is REQUIRED.
|
|
404
|
+
const thinMode = !fs.existsSync(path.join(PKG_ROOT, "agents"));
|
|
405
|
+
|
|
406
|
+
// Step 1: server sync (REQUIRED in thin mode; self-healing in thick mode)
|
|
407
|
+
const synced = await tryServerSync();
|
|
408
|
+
if (thinMode && !synced) {
|
|
409
|
+
// No local content and server unreachable → system cannot operate yet.
|
|
410
|
+
// Background retry per design: install completes, sync pending.
|
|
411
|
+
fs.mkdirSync(path.join(target, ".tera"), { recursive: true });
|
|
412
|
+
fs.writeFileSync(
|
|
413
|
+
path.join(target, ".tera", "sync.state.json"),
|
|
414
|
+
JSON.stringify({
|
|
415
|
+
status: "pending",
|
|
416
|
+
thinMode: true,
|
|
417
|
+
lastError: "server-unreachable-at-install",
|
|
418
|
+
lastAttempt: new Date().toISOString(),
|
|
419
|
+
}, null, 2)
|
|
420
|
+
);
|
|
421
|
+
console.log(" ⚠ [sync ] THIN MODE — server unreachable at install.");
|
|
422
|
+
console.log(" No system files were installed. Run later:");
|
|
423
|
+
console.log(" npm run tera-sync (or re-run install when online)");
|
|
424
|
+
}
|
|
372
425
|
|
|
373
426
|
// Step 2: integrity manifest (after sync, covers server files too)
|
|
374
427
|
try {
|
|
@@ -379,17 +432,32 @@ async function tryServerSync() {
|
|
|
379
432
|
console.log(` [integrity] skipped (${err.message}) — install continues`);
|
|
380
433
|
}
|
|
381
434
|
|
|
382
|
-
// Step 3: license verification (API-first, offline fallback)
|
|
435
|
+
// Step 3: license verification (API-first with email, offline fallback)
|
|
383
436
|
const stateFile = path.join(target, ".tera", "license.state.json");
|
|
384
|
-
|
|
437
|
+
const prevState = readLicenseState(stateFile) || {};
|
|
438
|
+
let licenseKey = prevState.licenseKey;
|
|
385
439
|
if (!licenseKey) licenseKey = process.env.TERA_LICENSE_KEY || undefined;
|
|
440
|
+
let accountEmail = prevState.accountEmail;
|
|
441
|
+
if (!accountEmail) accountEmail = process.env.TERA_LICENSE_EMAIL || undefined;
|
|
442
|
+
const deviceId = prevState.deviceId || createDeviceId(target);
|
|
443
|
+
|
|
444
|
+
// 3a. best-effort activation (only when we have key + email and not activated yet)
|
|
445
|
+
if (licenseKey && accountEmail && !prevState.activated) {
|
|
446
|
+
const act = await activateViaAPI(licenseKey, accountEmail, deviceId);
|
|
447
|
+
if (act && act.ok === true) {
|
|
448
|
+
console.log(` [license ] ✔ activated on this device (${act.tier} / ${act.project})`);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
386
451
|
|
|
387
|
-
const result = await verifyLicense(licenseKey);
|
|
452
|
+
const result = await verifyLicense(licenseKey, accountEmail);
|
|
388
453
|
|
|
389
454
|
writeLicenseState(stateFile, {
|
|
390
455
|
state: result.state,
|
|
391
456
|
reason: result.reason,
|
|
392
457
|
licenseKey: licenseKey || null,
|
|
458
|
+
accountEmail: accountEmail || null,
|
|
459
|
+
deviceId,
|
|
460
|
+
activated: true,
|
|
393
461
|
expiresAt: result.expiresAt || null,
|
|
394
462
|
graceUntil: result.graceUntil || null,
|
|
395
463
|
source: result.source,
|
|
@@ -22,15 +22,15 @@ const RECHECK_INTERVAL_MS = RECHECK_INTERVAL_DAYS * 86400000;
|
|
|
22
22
|
* Try to verify license via API (online check).
|
|
23
23
|
* Returns null if API unreachable.
|
|
24
24
|
*/
|
|
25
|
-
async function verifyViaAPI(licenseKey, apiUrl) {
|
|
26
|
-
if (!licenseKey || !apiUrl) return null;
|
|
25
|
+
async function verifyViaAPI(licenseKey, accountEmail, apiUrl) {
|
|
26
|
+
if (!licenseKey || !accountEmail || !apiUrl) return null;
|
|
27
27
|
try {
|
|
28
28
|
const controller = new AbortController();
|
|
29
29
|
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
30
30
|
const res = await fetch(`${apiUrl}/verify`, {
|
|
31
31
|
method: "POST",
|
|
32
32
|
headers: { "Content-Type": "application/json" },
|
|
33
|
-
body: JSON.stringify({ licenseKey }),
|
|
33
|
+
body: JSON.stringify({ licenseKey, accountEmail }),
|
|
34
34
|
signal: controller.signal,
|
|
35
35
|
});
|
|
36
36
|
clearTimeout(timeout);
|
|
@@ -200,7 +200,7 @@ export async function enforceLicenseGate(target, options = {}) {
|
|
|
200
200
|
const recheck = checkRecheckDue(lic);
|
|
201
201
|
if (recheck.due && !offlineOnly) {
|
|
202
202
|
// Try API re-verification
|
|
203
|
-
const apiResult = await verifyViaAPI(lic.licenseKey, apiUrl);
|
|
203
|
+
const apiResult = await verifyViaAPI(lic.licenseKey, lic.accountEmail, apiUrl);
|
|
204
204
|
if (apiResult && apiResult.valid === false) {
|
|
205
205
|
return {
|
|
206
206
|
allow: false,
|
|
@@ -32,15 +32,15 @@ function resolveTarget() {
|
|
|
32
32
|
return path.resolve(process.env.TERA_TARGET_DIR || process.env.INIT_CWD || process.cwd());
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
async function serverVerify(licenseKey) {
|
|
36
|
-
if (!licenseKey) return null;
|
|
35
|
+
async function serverVerify(licenseKey, accountEmail) {
|
|
36
|
+
if (!licenseKey || !accountEmail) return null;
|
|
37
37
|
try {
|
|
38
38
|
const controller = new AbortController();
|
|
39
39
|
const timeout = setTimeout(() => controller.abort(), 8000);
|
|
40
40
|
const res = await fetch(`${API_URL}/verify`, {
|
|
41
41
|
method: "POST",
|
|
42
42
|
headers: { "Content-Type": "application/json" },
|
|
43
|
-
body: JSON.stringify({ licenseKey }),
|
|
43
|
+
body: JSON.stringify({ licenseKey, accountEmail }),
|
|
44
44
|
signal: controller.signal,
|
|
45
45
|
});
|
|
46
46
|
clearTimeout(timeout);
|
|
@@ -81,7 +81,7 @@ const heartbeatFile = path.join(target, ".tera", "heartbeat.state.json");
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
console.log(`heartbeat due (last ${hb.lastHeartbeat || "never"}) — contacting server...`);
|
|
84
|
-
const server = await serverVerify(license.licenseKey);
|
|
84
|
+
const server = await serverVerify(license.licenseKey, license.accountEmail);
|
|
85
85
|
|
|
86
86
|
if (!server) {
|
|
87
87
|
console.log(`⚠ server unreachable — keeping current license state (will retry in ${HEARTBEAT_DAYS}d)`);
|