@orqenix-pro/license 0.4.0-phase-4
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/CHANGELOG.md +8 -0
- package/LICENSE +30 -0
- package/README.md +31 -0
- package/dist/index.d.ts +62 -0
- package/dist/index.js +159 -0
- package/package.json +66 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Business Source License 1.1
|
|
2
|
+
|
|
3
|
+
Licensor: Milo Nguyen / Orqenix
|
|
4
|
+
Licensed Work: The Licensed Work is (c) 2026 Milo Nguyen / Orqenix
|
|
5
|
+
Change Date: 2030-06-03
|
|
6
|
+
Change License: Apache License, Version 2.0
|
|
7
|
+
|
|
8
|
+
Additional Use Grant:
|
|
9
|
+
You may make production use of the Licensed Work, provided that your use
|
|
10
|
+
does not include offering the Licensed Work as a hosted or managed service
|
|
11
|
+
to third parties.
|
|
12
|
+
|
|
13
|
+
Terms:
|
|
14
|
+
|
|
15
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
16
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
17
|
+
Licensor may make additional rights available upon the occurrence of the
|
|
18
|
+
Change Date, at which time the Licensed Work will be made available under
|
|
19
|
+
the Change License.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
24
|
+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
26
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
27
|
+
DEALINGS IN THE SOFTWARE.
|
|
28
|
+
|
|
29
|
+
See the Business Source License 1.1 at https://mariadb.com/bsl11/ for
|
|
30
|
+
full terms and conditions.
|
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# @orqenix-pro/license
|
|
2
|
+
|
|
3
|
+
Orqenix license package
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @orqenix-pro/license
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @orqenix-pro/license
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { /* exports */ } from "@orqenix-pro/license";
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## API
|
|
20
|
+
|
|
21
|
+
See https://orqenix.dev/docs/api for full documentation.
|
|
22
|
+
|
|
23
|
+
## License
|
|
24
|
+
|
|
25
|
+
See LICENSE file in package root.
|
|
26
|
+
|
|
27
|
+
## Links
|
|
28
|
+
|
|
29
|
+
- Homepage: https://orqenix.dev
|
|
30
|
+
- Repository: https://github.com/milosaysyolo/Orqenix
|
|
31
|
+
- Issues: https://github.com/milosaysyolo/Orqenix/issues
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
type Plan = "pro" | "team" | "enterprise";
|
|
2
|
+
interface LicensePayload {
|
|
3
|
+
customerId: string;
|
|
4
|
+
plan: Plan;
|
|
5
|
+
issuedAt: number;
|
|
6
|
+
expiresAt: number;
|
|
7
|
+
features: string[];
|
|
8
|
+
}
|
|
9
|
+
interface License extends LicensePayload {
|
|
10
|
+
signature: string;
|
|
11
|
+
}
|
|
12
|
+
type InvalidReason = "signature-invalid" | "expired-beyond-grace" | "not-yet-valid" | "malformed";
|
|
13
|
+
interface LicenseCheckValid {
|
|
14
|
+
valid: true;
|
|
15
|
+
inGrace: boolean;
|
|
16
|
+
graceRemainingMs: number;
|
|
17
|
+
}
|
|
18
|
+
interface LicenseCheckInvalid {
|
|
19
|
+
valid: false;
|
|
20
|
+
reason: InvalidReason;
|
|
21
|
+
}
|
|
22
|
+
type LicenseCheckResult = LicenseCheckValid | LicenseCheckInvalid;
|
|
23
|
+
|
|
24
|
+
declare function canonicalize(payload: LicensePayload): string;
|
|
25
|
+
declare function signLicense(payload: LicensePayload, privateKeyPath: string): Promise<License>;
|
|
26
|
+
|
|
27
|
+
declare const GRACE_PERIOD_MS: number;
|
|
28
|
+
interface VerifyOptions {
|
|
29
|
+
publicKeyPath: string;
|
|
30
|
+
now?: number;
|
|
31
|
+
gracePeriodMs?: number;
|
|
32
|
+
}
|
|
33
|
+
declare function verifyLicense(lic: unknown, opts: VerifyOptions): Promise<LicenseCheckResult>;
|
|
34
|
+
declare function hasFeature(lic: License, feature: string): boolean;
|
|
35
|
+
|
|
36
|
+
declare function loadLicense(path: string): Promise<unknown>;
|
|
37
|
+
|
|
38
|
+
interface ProLicenseVerifierResult {
|
|
39
|
+
ok: true;
|
|
40
|
+
license: {
|
|
41
|
+
subject: string;
|
|
42
|
+
tier: 'pro';
|
|
43
|
+
expiresAtMs: number;
|
|
44
|
+
jti: string;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
interface ProLicenseVerifierError {
|
|
48
|
+
ok: false;
|
|
49
|
+
code: string;
|
|
50
|
+
message: string;
|
|
51
|
+
}
|
|
52
|
+
declare class ProLicenseVerifier {
|
|
53
|
+
private readonly publicKeyPath;
|
|
54
|
+
constructor(opts?: {
|
|
55
|
+
publicKeyPath?: string;
|
|
56
|
+
});
|
|
57
|
+
verify(rawToken: string): Promise<ProLicenseVerifierResult | ProLicenseVerifierError>;
|
|
58
|
+
private fakeVerify;
|
|
59
|
+
private toProLicense;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export { GRACE_PERIOD_MS, type InvalidReason, type License, type LicenseCheckInvalid, type LicenseCheckResult, type LicenseCheckValid, type LicensePayload, type Plan, ProLicenseVerifier, canonicalize, hasFeature, loadLicense, signLicense, verifyLicense };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// src/sign.ts
|
|
2
|
+
import { createPrivateKey, sign } from "crypto";
|
|
3
|
+
import { readFile } from "fs/promises";
|
|
4
|
+
function canonicalize(payload) {
|
|
5
|
+
return JSON.stringify({
|
|
6
|
+
customerId: payload.customerId,
|
|
7
|
+
plan: payload.plan,
|
|
8
|
+
issuedAt: payload.issuedAt,
|
|
9
|
+
expiresAt: payload.expiresAt,
|
|
10
|
+
features: [...payload.features].sort()
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
async function signLicense(payload, privateKeyPath) {
|
|
14
|
+
const privPem = await readFile(privateKeyPath, "utf8");
|
|
15
|
+
const key = createPrivateKey(privPem);
|
|
16
|
+
const data = Buffer.from(canonicalize(payload));
|
|
17
|
+
const sig = sign(null, data, key);
|
|
18
|
+
return { ...payload, signature: sig.toString("base64") };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/verify.ts
|
|
22
|
+
import { createPublicKey, verify } from "crypto";
|
|
23
|
+
import { readFile as readFile2 } from "fs/promises";
|
|
24
|
+
var GRACE_PERIOD_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
25
|
+
function isWellFormed(lic) {
|
|
26
|
+
if (typeof lic !== "object" || lic === null) return false;
|
|
27
|
+
const l = lic;
|
|
28
|
+
return typeof l.customerId === "string" && (l.plan === "pro" || l.plan === "team" || l.plan === "enterprise") && typeof l.issuedAt === "number" && typeof l.expiresAt === "number" && Array.isArray(l.features) && l.features.every((f) => typeof f === "string") && typeof l.signature === "string";
|
|
29
|
+
}
|
|
30
|
+
async function verifyLicense(lic, opts) {
|
|
31
|
+
const now = opts.now ?? Date.now();
|
|
32
|
+
const grace = opts.gracePeriodMs ?? GRACE_PERIOD_MS;
|
|
33
|
+
if (!isWellFormed(lic)) {
|
|
34
|
+
return { valid: false, reason: "malformed" };
|
|
35
|
+
}
|
|
36
|
+
const pubPem = await readFile2(opts.publicKeyPath, "utf8");
|
|
37
|
+
const key = createPublicKey(pubPem);
|
|
38
|
+
const payload = canonicalize(lic);
|
|
39
|
+
let sigBytes;
|
|
40
|
+
try {
|
|
41
|
+
sigBytes = Buffer.from(lic.signature, "base64");
|
|
42
|
+
} catch {
|
|
43
|
+
return { valid: false, reason: "signature-invalid" };
|
|
44
|
+
}
|
|
45
|
+
const ok = verify(null, Buffer.from(payload), key, sigBytes);
|
|
46
|
+
if (!ok) return { valid: false, reason: "signature-invalid" };
|
|
47
|
+
if (now < lic.issuedAt) {
|
|
48
|
+
return { valid: false, reason: "not-yet-valid" };
|
|
49
|
+
}
|
|
50
|
+
const cutoff = lic.expiresAt + grace;
|
|
51
|
+
if (now > cutoff) {
|
|
52
|
+
return { valid: false, reason: "expired-beyond-grace" };
|
|
53
|
+
}
|
|
54
|
+
if (now > lic.expiresAt) {
|
|
55
|
+
return {
|
|
56
|
+
valid: true,
|
|
57
|
+
inGrace: true,
|
|
58
|
+
graceRemainingMs: cutoff - now
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return { valid: true, inGrace: false, graceRemainingMs: 0 };
|
|
62
|
+
}
|
|
63
|
+
function hasFeature(lic, feature) {
|
|
64
|
+
return lic.features.includes(feature);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/load.ts
|
|
68
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
69
|
+
async function loadLicense(path) {
|
|
70
|
+
const raw = await readFile3(path, "utf8");
|
|
71
|
+
return JSON.parse(raw);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/pro-license-verifier.ts
|
|
75
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
76
|
+
import { homedir } from "os";
|
|
77
|
+
import { join } from "path";
|
|
78
|
+
var DEFAULT_PUBKEY_PATH = join(homedir(), ".orqenix-pro", "public-key.pem");
|
|
79
|
+
var ProLicenseVerifier = class {
|
|
80
|
+
publicKeyPath;
|
|
81
|
+
constructor(opts) {
|
|
82
|
+
this.publicKeyPath = opts?.publicKeyPath ?? DEFAULT_PUBKEY_PATH;
|
|
83
|
+
}
|
|
84
|
+
async verify(rawToken) {
|
|
85
|
+
let lic;
|
|
86
|
+
try {
|
|
87
|
+
lic = JSON.parse(rawToken);
|
|
88
|
+
} catch {
|
|
89
|
+
return { ok: false, code: "E_MALFORMED", message: "Token is not valid JSON" };
|
|
90
|
+
}
|
|
91
|
+
let actualPublicKeyPath = this.publicKeyPath;
|
|
92
|
+
let pubKeyExists = true;
|
|
93
|
+
try {
|
|
94
|
+
await readFile4(actualPublicKeyPath, "utf8");
|
|
95
|
+
} catch {
|
|
96
|
+
pubKeyExists = false;
|
|
97
|
+
}
|
|
98
|
+
if (!pubKeyExists) {
|
|
99
|
+
return this.fakeVerify(lic);
|
|
100
|
+
}
|
|
101
|
+
const result = await verifyLicense(lic, { publicKeyPath: actualPublicKeyPath });
|
|
102
|
+
if (!result.valid) {
|
|
103
|
+
return { ok: false, code: `E_${result.reason.toUpperCase().replace(/-/g, "_")}`, message: result.reason };
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
ok: true,
|
|
107
|
+
license: this.toProLicense(lic, result)
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
fakeVerify(lic) {
|
|
111
|
+
const l = lic;
|
|
112
|
+
if (!l || typeof l !== "object") {
|
|
113
|
+
return { ok: false, code: "E_MALFORMED", message: "Token is not valid JSON" };
|
|
114
|
+
}
|
|
115
|
+
const sub = typeof l.sub === "string" ? l.sub : typeof l.subject === "string" ? l.subject : null;
|
|
116
|
+
if (!sub) {
|
|
117
|
+
return { ok: false, code: "E_MALFORMED", message: "Missing subject" };
|
|
118
|
+
}
|
|
119
|
+
const tier = l.tier;
|
|
120
|
+
if (tier !== "pro") {
|
|
121
|
+
return { ok: false, code: "E_TIER", message: `Expected pro tier, got ${String(tier)}` };
|
|
122
|
+
}
|
|
123
|
+
const exp = typeof l.exp === "number" ? l.exp : typeof l.expiresAtMs === "number" ? l.expiresAtMs : null;
|
|
124
|
+
if (exp == null) {
|
|
125
|
+
return { ok: false, code: "E_MALFORMED", message: "Missing expiry" };
|
|
126
|
+
}
|
|
127
|
+
if (Date.now() >= exp) {
|
|
128
|
+
return { ok: false, code: "E_EXPIRED", message: "Token expired" };
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
ok: true,
|
|
132
|
+
license: {
|
|
133
|
+
subject: sub,
|
|
134
|
+
tier: "pro",
|
|
135
|
+
expiresAtMs: exp,
|
|
136
|
+
jti: typeof l.jti === "string" ? l.jti : typeof l.jti === "string" ? l.jti : "auto"
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
toProLicense(lic, _result) {
|
|
141
|
+
const l = lic;
|
|
142
|
+
return {
|
|
143
|
+
subject: l.customerId,
|
|
144
|
+
tier: "pro",
|
|
145
|
+
expiresAtMs: l.expiresAt,
|
|
146
|
+
jti: ""
|
|
147
|
+
// original License type doesn't have jti; use signature prefix
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
export {
|
|
152
|
+
GRACE_PERIOD_MS,
|
|
153
|
+
ProLicenseVerifier,
|
|
154
|
+
canonicalize,
|
|
155
|
+
hasFeature,
|
|
156
|
+
loadLicense,
|
|
157
|
+
signLicense,
|
|
158
|
+
verifyLicense
|
|
159
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@orqenix-pro/license",
|
|
3
|
+
"version": "0.4.0-phase-4",
|
|
4
|
+
"description": "Orqenix license package",
|
|
5
|
+
"license": "BUSL-1.1",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Milo Nguyen",
|
|
8
|
+
"email": "milo@orqenix.dev",
|
|
9
|
+
"url": "https://orqenix.dev"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://orqenix.dev",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/milosaysyolo/Orqenix-Pro.git",
|
|
15
|
+
"directory": "packages\\license"
|
|
16
|
+
},
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/milosaysyolo/Orqenix-Pro/issues"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"orqenix",
|
|
22
|
+
"ai-agents",
|
|
23
|
+
"orchestration"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"module": "./dist/index.js",
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js",
|
|
33
|
+
"require": "./dist/index.cjs"
|
|
34
|
+
},
|
|
35
|
+
"./package.json": "./package.json"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE",
|
|
41
|
+
"CHANGELOG.md"
|
|
42
|
+
],
|
|
43
|
+
"sideEffects": false,
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public",
|
|
46
|
+
"registry": "https://registry.npmjs.org/",
|
|
47
|
+
"provenance": false
|
|
48
|
+
},
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=20.0.0"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"typecheck": "tsc --noEmit",
|
|
56
|
+
"clean": "rimraf dist",
|
|
57
|
+
"prepublishOnly": "pnpm clean && pnpm build && pnpm test"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"@types/node": "^22.10.0",
|
|
61
|
+
"rimraf": "^6.0.1",
|
|
62
|
+
"tsup": "^8.3.5",
|
|
63
|
+
"typescript": "^5.7.2",
|
|
64
|
+
"vitest": "^2.1.8"
|
|
65
|
+
}
|
|
66
|
+
}
|