@iamem/amem 0.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.
Files changed (104) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +534 -0
  3. package/dist/activity.d.ts +27 -0
  4. package/dist/activity.js +202 -0
  5. package/dist/api/routes.d.ts +31 -0
  6. package/dist/api/routes.js +1345 -0
  7. package/dist/attest.d.ts +52 -0
  8. package/dist/attest.js +192 -0
  9. package/dist/backup-schedule.d.ts +25 -0
  10. package/dist/backup-schedule.js +216 -0
  11. package/dist/capture.d.ts +34 -0
  12. package/dist/capture.js +257 -0
  13. package/dist/cli.d.ts +2 -0
  14. package/dist/cli.js +1007 -0
  15. package/dist/context.d.ts +46 -0
  16. package/dist/context.js +334 -0
  17. package/dist/crypto.d.ts +39 -0
  18. package/dist/crypto.js +166 -0
  19. package/dist/db.d.ts +192 -0
  20. package/dist/db.js +666 -0
  21. package/dist/draft-quality.d.ts +19 -0
  22. package/dist/draft-quality.js +85 -0
  23. package/dist/embed.d.ts +76 -0
  24. package/dist/embed.js +331 -0
  25. package/dist/estimate.d.ts +32 -0
  26. package/dist/estimate.js +69 -0
  27. package/dist/freshness.d.ts +15 -0
  28. package/dist/freshness.js +93 -0
  29. package/dist/hook.d.ts +16 -0
  30. package/dist/hook.js +177 -0
  31. package/dist/hygiene-schedule.d.ts +28 -0
  32. package/dist/hygiene-schedule.js +221 -0
  33. package/dist/hygiene.d.ts +61 -0
  34. package/dist/hygiene.js +196 -0
  35. package/dist/install/claude.d.ts +6 -0
  36. package/dist/install/claude.js +69 -0
  37. package/dist/install/cursor.d.ts +7 -0
  38. package/dist/install/cursor.js +80 -0
  39. package/dist/install/hosts.d.ts +21 -0
  40. package/dist/install/hosts.js +186 -0
  41. package/dist/install/skills.d.ts +8 -0
  42. package/dist/install/skills.js +67 -0
  43. package/dist/it-pack.d.ts +20 -0
  44. package/dist/it-pack.js +84 -0
  45. package/dist/kinds.d.ts +18 -0
  46. package/dist/kinds.js +106 -0
  47. package/dist/license.d.ts +48 -0
  48. package/dist/license.js +172 -0
  49. package/dist/mcp.d.ts +40 -0
  50. package/dist/mcp.js +435 -0
  51. package/dist/paths.d.ts +11 -0
  52. package/dist/paths.js +55 -0
  53. package/dist/personal.d.ts +7 -0
  54. package/dist/personal.js +44 -0
  55. package/dist/platforms.d.ts +11 -0
  56. package/dist/platforms.js +32 -0
  57. package/dist/policy.d.ts +46 -0
  58. package/dist/policy.js +254 -0
  59. package/dist/prefs.d.ts +6 -0
  60. package/dist/prefs.js +11 -0
  61. package/dist/proposal.d.ts +90 -0
  62. package/dist/proposal.js +376 -0
  63. package/dist/publish.d.ts +28 -0
  64. package/dist/publish.js +57 -0
  65. package/dist/remember-contract.d.ts +23 -0
  66. package/dist/remember-contract.js +117 -0
  67. package/dist/repo-identity.d.ts +15 -0
  68. package/dist/repo-identity.js +82 -0
  69. package/dist/rules-sync.d.ts +7 -0
  70. package/dist/rules-sync.js +47 -0
  71. package/dist/savings-export.d.ts +49 -0
  72. package/dist/savings-export.js +141 -0
  73. package/dist/scan.d.ts +16 -0
  74. package/dist/scan.js +109 -0
  75. package/dist/search.d.ts +25 -0
  76. package/dist/search.js +150 -0
  77. package/dist/service.d.ts +20 -0
  78. package/dist/service.js +254 -0
  79. package/dist/shop.d.ts +9 -0
  80. package/dist/shop.js +15 -0
  81. package/dist/ui/server.d.ts +21 -0
  82. package/dist/ui/server.js +268 -0
  83. package/dist/vault.d.ts +25 -0
  84. package/dist/vault.js +42 -0
  85. package/dist/workspace-setup.d.ts +8 -0
  86. package/dist/workspace-setup.js +55 -0
  87. package/docs/agent-install-prompt.md +41 -0
  88. package/docs/backlog.md +59 -0
  89. package/docs/enterprise-endpoint.md +98 -0
  90. package/docs/license.md +54 -0
  91. package/docs/npm-release.md +38 -0
  92. package/docs/remember-contract.md +42 -0
  93. package/package.json +64 -0
  94. package/scripts/mdm-offboard.sh +14 -0
  95. package/skills/amem-bootstrap/SKILL.md +83 -0
  96. package/skills/amem-update-working-memory/SKILL.md +54 -0
  97. package/templates/cursor-rule.mdc +23 -0
  98. package/templates/mdm/co.amem.managed.plist +33 -0
  99. package/templates/policy.deny-default.toml +20 -0
  100. package/templates/policy.example.toml +22 -0
  101. package/ui-static/app.js +3966 -0
  102. package/ui-static/index.html +178 -0
  103. package/ui-static/orbit.js +389 -0
  104. package/ui-static/styles.css +2411 -0
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Local IT pack: deny-default policy, MDM snippet, SBOM, offboard pointer.
3
+ * No cloud, no telemetry. IT tier recommended; generating the folder is allowed on any machine.
4
+ */
5
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync } from "node:fs";
6
+ import { join } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { createHash } from "node:crypto";
9
+ import { amemHome } from "./paths.js";
10
+ import { licenseStatus } from "./license.js";
11
+ import { embedStatus } from "./embed.js";
12
+ import { vaultStatus } from "./vault.js";
13
+ function packageRoot() {
14
+ return join(fileURLToPath(new URL(".", import.meta.url)), "..");
15
+ }
16
+ export function buildSbom() {
17
+ const pkgPath = join(packageRoot(), "package.json");
18
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
19
+ const components = Object.entries(pkg.dependencies ?? {}).map(([name, version]) => ({
20
+ name,
21
+ version: String(version).replace(/^[\^~]/, ""),
22
+ type: "library",
23
+ }));
24
+ return {
25
+ bomFormat: "CycloneDX",
26
+ specVersion: "1.5",
27
+ serialNumber: `urn:uuid:${createHash("sha256").update(`${pkg.name}@${pkg.version}`).digest("hex").slice(0, 32)}`,
28
+ metadata: {
29
+ timestamp: new Date().toISOString(),
30
+ component: {
31
+ name: pkg.name || "amem",
32
+ version: pkg.version || "0.0.0",
33
+ type: "application",
34
+ },
35
+ },
36
+ components,
37
+ };
38
+ }
39
+ export function writeItPack(outDir) {
40
+ mkdirSync(outDir, { recursive: true, mode: 0o700 });
41
+ const files = [];
42
+ const root = packageRoot();
43
+ const denySrc = join(root, "templates", "policy.deny-default.toml");
44
+ const denyOut = join(outDir, "policy.toml");
45
+ copyFileSync(existsSync(denySrc) ? denySrc : join(root, "templates", "policy.example.toml"), denyOut);
46
+ files.push(denyOut);
47
+ const mdmSrc = join(root, "templates", "mdm", "co.amem.managed.plist");
48
+ if (existsSync(mdmSrc)) {
49
+ const mdmOut = join(outDir, "co.amem.managed.plist");
50
+ copyFileSync(mdmSrc, mdmOut);
51
+ files.push(mdmOut);
52
+ }
53
+ const offboardSrc = join(root, "scripts", "mdm-offboard.sh");
54
+ if (existsSync(offboardSrc)) {
55
+ const offOut = join(outDir, "mdm-offboard.sh");
56
+ copyFileSync(offboardSrc, offOut);
57
+ files.push(offOut);
58
+ }
59
+ const sbomOut = join(outDir, "sbom.json");
60
+ writeFileSync(sbomOut, `${JSON.stringify(buildSbom(), null, 2)}\n`, "utf8");
61
+ files.push(sbomOut);
62
+ const notes = `# amem IT pack (local)
63
+
64
+ Memory stays on each laptop under ${amemHome()}.
65
+ There is no license server and no cloud sync.
66
+
67
+ 1. Deploy policy.toml as /etc/amem/policy.toml (root-owned).
68
+ 2. Install a pinned amem build (internal npm or mirrored clone).
69
+ 3. Optional: wrap co.amem.managed.plist in your MDM profile.
70
+ 4. Offboard with mdm-offboard.sh (wipes ~/.amem on that machine).
71
+ 5. Attach sbom.json + \`amem doctor --attest --json\` to the security ticket.
72
+ IT tier adds a richer vault/host attest packet; the pack folder itself is available on Free.
73
+
74
+ Signed macOS/Windows installers need your org’s code-signing cert — amem does not upload binaries.
75
+
76
+ License: ${licenseStatus().tier}
77
+ Embed: ${embedStatus().backend}
78
+ Vault backups: ${vaultStatus().backup.dir}
79
+ `;
80
+ const notesOut = join(outDir, "README.txt");
81
+ writeFileSync(notesOut, notes, "utf8");
82
+ files.push(notesOut);
83
+ return { dir: outDir, files };
84
+ }
@@ -0,0 +1,18 @@
1
+ /** Claim kind helpers: inference + retrieval priority. */
2
+ export declare function kindRankBoost(kind: string | null | undefined): number;
3
+ /**
4
+ * Infer a durable kind from prompt/answer text.
5
+ * Prefers constraint/gotcha when the language looks like a lasting rule.
6
+ */
7
+ export declare function inferClaimKind(prompt: string, answer?: string): string;
8
+ /** Compact durable claim text from a Q/A turn (prefer outcome over raw chat). */
9
+ export declare function compactClaimText(prompt: string, answer?: string): string;
10
+ /** Multi-turn: fold several notes into one compact fact string. */
11
+ export declare function compactFromNotes(notes: Array<{
12
+ role: string;
13
+ text: string;
14
+ }>): {
15
+ prompt: string;
16
+ answer: string;
17
+ } | null;
18
+ export declare function isDurableCapture(prompt: string, answer: string | undefined, anchorCount: number): boolean;
package/dist/kinds.js ADDED
@@ -0,0 +1,106 @@
1
+ /** Claim kind helpers: inference + retrieval priority. */
2
+ const KIND_WEIGHT = {
3
+ constraint: 12,
4
+ gotcha: 12,
5
+ owner: 8,
6
+ howto: 6,
7
+ structure: 5,
8
+ flow: 4,
9
+ session: 1,
10
+ };
11
+ export function kindRankBoost(kind) {
12
+ if (!kind)
13
+ return 3;
14
+ return KIND_WEIGHT[kind.toLowerCase()] ?? 3;
15
+ }
16
+ /**
17
+ * Infer a durable kind from prompt/answer text.
18
+ * Prefers constraint/gotcha when the language looks like a lasting rule.
19
+ */
20
+ export function inferClaimKind(prompt, answer = "") {
21
+ const hay = `${prompt}\n${answer}`.toLowerCase();
22
+ if (/\b(gotcha|pitfall|trap|don'?t|never|always|must not|broken if|fails if|watch out)\b/.test(hay)) {
23
+ return "gotcha";
24
+ }
25
+ if (/\b(must|should|required|constraint|invariant|idempotent|only after)\b/.test(hay)) {
26
+ return "constraint";
27
+ }
28
+ if (/\b(owned by|owner|maintained by|responsible)\b/.test(hay)) {
29
+ return "owner";
30
+ }
31
+ if (/\b(how to|steps? to|run this|workflow)\b/.test(hay)) {
32
+ return "howto";
33
+ }
34
+ if (/\b(lives in|entrypoint|located in|module|component)\b/.test(hay)) {
35
+ return "structure";
36
+ }
37
+ return "session";
38
+ }
39
+ /** Compact durable claim text from a Q/A turn (prefer outcome over raw chat). */
40
+ export function compactClaimText(prompt, answer) {
41
+ const takeaway = scrubCodeNoise((answer ?? "").replace(/\s+/g, " ").trim());
42
+ if (takeaway.length >= 40) {
43
+ const preferred = pickFactSentences(takeaway);
44
+ if (preferred)
45
+ return preferred.slice(0, 400);
46
+ const sentences = takeaway.split(/(?<=[.!?])\s+/).filter(Boolean);
47
+ const head = sentences.slice(0, 2).join(" ").slice(0, 400);
48
+ return head || takeaway.slice(0, 400);
49
+ }
50
+ const q = prompt.replace(/\s+/g, " ").trim().slice(0, 280);
51
+ return takeaway ? `${q}\n\nPrior outcome: ${takeaway.slice(0, 200)}` : q.slice(0, 400);
52
+ }
53
+ /** Multi-turn: fold several notes into one compact fact string. */
54
+ export function compactFromNotes(notes) {
55
+ const users = notes.filter((n) => n.role === "user").map((n) => n.text.trim());
56
+ const assistants = notes.filter((n) => n.role === "assistant").map((n) => n.text.trim());
57
+ if (users.length === 0 && assistants.length === 0)
58
+ return null;
59
+ const prompt = users.slice(0, 2).join(" — ").slice(0, 400) || "Session takeaways";
60
+ // Prefer assistant sentences that mention paths or durable language
61
+ const pool = assistants.join(" ");
62
+ const answer = pickFactSentences(scrubCodeNoise(pool)) ||
63
+ assistants[0]?.replace(/\s+/g, " ").trim().slice(0, 400) ||
64
+ "";
65
+ if (!answer && !isUsefulish(prompt))
66
+ return null;
67
+ return { prompt, answer };
68
+ }
69
+ function isUsefulish(text) {
70
+ return text.trim().length >= 16;
71
+ }
72
+ function scrubCodeNoise(text) {
73
+ return text
74
+ .replace(/```[\s\S]*?```/g, " ")
75
+ .replace(/`[^`]+`/g, " ")
76
+ .replace(/\s+/g, " ")
77
+ .trim();
78
+ }
79
+ function pickFactSentences(text) {
80
+ const sentences = text.split(/(?<=[.!?])\s+/).filter((s) => s.length > 25);
81
+ const scored = sentences.map((s) => {
82
+ let score = 0;
83
+ if (/\b(src\/|[\w.-]+\.(ts|tsx|js|py|go|rs))\b/i.test(s))
84
+ score += 3;
85
+ if (/\b(must|should|never|always|gotcha|idempotent|before|after)\b/i.test(s))
86
+ score += 2;
87
+ if (/\b(lives in|entrypoint|owned by|constraint)\b/i.test(s))
88
+ score += 2;
89
+ if (/^[A-Z]/.test(s))
90
+ score += 1;
91
+ return { s, score };
92
+ });
93
+ scored.sort((a, b) => b.score - a.score);
94
+ const top = scored.filter((x) => x.score > 0).slice(0, 2).map((x) => x.s);
95
+ if (top.length === 0)
96
+ return null;
97
+ return top.join(" ");
98
+ }
99
+ export function isDurableCapture(prompt, answer, anchorCount) {
100
+ if (anchorCount <= 0)
101
+ return false;
102
+ const kind = inferClaimKind(prompt, answer ?? "");
103
+ if (kind === "session" && (answer ?? "").trim().length < 80)
104
+ return false;
105
+ return true;
106
+ }
@@ -0,0 +1,48 @@
1
+ export type LicenseTier = "free" | "pro" | "it";
2
+ export type LicenseKind = "none" | "signed";
3
+ export type LicensePayload = {
4
+ tier: LicenseTier;
5
+ subject?: string;
6
+ issued_at: string;
7
+ expires_at?: string;
8
+ features?: string[];
9
+ };
10
+ export type LicenseFile = {
11
+ kind: "signed";
12
+ payload: LicensePayload;
13
+ signature: string;
14
+ };
15
+ export type LicenseStatus = {
16
+ tier: LicenseTier;
17
+ kind: LicenseKind;
18
+ subject?: string;
19
+ expires_at?: string;
20
+ features: string[];
21
+ valid: boolean;
22
+ transferable: boolean;
23
+ path: string;
24
+ issues: string[];
25
+ };
26
+ export declare const FEATURE_LOCAL_EMBED = "local_embed_model";
27
+ export declare const FEATURE_ATTEST_SKU = "attest_sku";
28
+ export declare const FEATURE_HYGIENE = "hygiene";
29
+ export declare const FEATURE_RULES_SYNC = "rules_sync";
30
+ /** Vendor verify key (SPKI DER hex). Issue signed files with AMEM_LICENSE_PRIVKEY. */
31
+ export declare const DEFAULT_LICENSE_PUBKEY_HEX = "302a300506032b6570032100b1e01cdb2d1ec60b372bb4307fa48ed743caba09a67633e4689aaa22221080fa";
32
+ export declare function licensePath(): string;
33
+ export declare function featuresForTier(tier: LicenseTier): string[];
34
+ export declare function generateLicenseKeys(): {
35
+ publicKeyHex: string;
36
+ privateKeyHex: string;
37
+ };
38
+ export declare function signLicense(privateKeyHex: string, payload: LicensePayload): LicenseFile;
39
+ export declare function verifySignedLicense(file: LicenseFile, publicKeyHex?: string): string[];
40
+ export declare function parseLicenseFile(raw: unknown): LicenseFile;
41
+ export declare function readLicenseFile(path?: string): LicenseFile | null;
42
+ export declare function licenseStatus(now?: Date): LicenseStatus;
43
+ export declare function hasFeature(feature: string): boolean;
44
+ export declare function writeLicense(file: LicenseFile, path?: string): string;
45
+ export declare function applyLicenseJson(raw: unknown): LicenseStatus;
46
+ export declare function applyLicenseFile(sourcePath: string): LicenseStatus;
47
+ export declare function clearLicense(): void;
48
+ export declare function requireFeature(feature: string, label?: string): void;
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Local license SKU. No license server, no telemetry.
3
+ * Paid tiers require a vendor-signed file (Ed25519). No self-serve unlock from source.
4
+ */
5
+ import { generateKeyPairSync, sign as signBytes, verify as verifyBytes } from "node:crypto";
6
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
7
+ import { join } from "node:path";
8
+ import { amemHome } from "./paths.js";
9
+ export const FEATURE_LOCAL_EMBED = "local_embed_model";
10
+ export const FEATURE_ATTEST_SKU = "attest_sku";
11
+ export const FEATURE_HYGIENE = "hygiene";
12
+ export const FEATURE_RULES_SYNC = "rules_sync";
13
+ /** Vendor verify key (SPKI DER hex). Issue signed files with AMEM_LICENSE_PRIVKEY. */
14
+ export const DEFAULT_LICENSE_PUBKEY_HEX = "302a300506032b6570032100b1e01cdb2d1ec60b372bb4307fa48ed743caba09a67633e4689aaa22221080fa";
15
+ const BUY_HINT = "Buy at https://getamem.com then: amem license apply --file ~/Downloads/amem-license.json";
16
+ const TIER_FEATURES = {
17
+ free: [],
18
+ pro: [FEATURE_LOCAL_EMBED, FEATURE_HYGIENE, FEATURE_RULES_SYNC],
19
+ it: [FEATURE_LOCAL_EMBED, FEATURE_HYGIENE, FEATURE_RULES_SYNC, FEATURE_ATTEST_SKU],
20
+ };
21
+ export function licensePath() {
22
+ return join(amemHome(), "license.json");
23
+ }
24
+ export function featuresForTier(tier) {
25
+ return [...TIER_FEATURES[tier]];
26
+ }
27
+ export function generateLicenseKeys() {
28
+ const pair = generateKeyPairSync("ed25519");
29
+ return {
30
+ publicKeyHex: pair.publicKey.export({ type: "spki", format: "der" }).toString("hex"),
31
+ privateKeyHex: pair.privateKey.export({ type: "pkcs8", format: "der" }).toString("hex"),
32
+ };
33
+ }
34
+ function canonical(payload) {
35
+ return Buffer.from(JSON.stringify({
36
+ expires_at: payload.expires_at ?? "",
37
+ features: [...(payload.features ?? [])].sort(),
38
+ issued_at: payload.issued_at,
39
+ subject: payload.subject ?? "",
40
+ tier: payload.tier,
41
+ }));
42
+ }
43
+ export function signLicense(privateKeyHex, payload) {
44
+ const key = Buffer.from(privateKeyHex, "hex");
45
+ const signature = signBytes(null, canonical(payload), {
46
+ key,
47
+ format: "der",
48
+ type: "pkcs8",
49
+ }).toString("hex");
50
+ return { kind: "signed", payload, signature };
51
+ }
52
+ export function verifySignedLicense(file, publicKeyHex = process.env.AMEM_LICENSE_PUBKEY || DEFAULT_LICENSE_PUBKEY_HEX) {
53
+ const issues = [];
54
+ if (file.kind !== "signed" || !file.signature) {
55
+ issues.push("not a signed license");
56
+ return issues;
57
+ }
58
+ try {
59
+ const ok = verifyBytes(null, canonical(file.payload), { key: Buffer.from(publicKeyHex, "hex"), format: "der", type: "spki" }, Buffer.from(file.signature, "hex"));
60
+ if (!ok)
61
+ issues.push("license signature is invalid");
62
+ }
63
+ catch (error) {
64
+ issues.push(error instanceof Error ? error.message : String(error));
65
+ }
66
+ return issues;
67
+ }
68
+ export function parseLicenseFile(raw) {
69
+ if (!raw || typeof raw !== "object")
70
+ throw new Error("License must be a JSON object");
71
+ const rec = raw;
72
+ if (rec.kind === "dev") {
73
+ throw new Error(`Self-issued /dev licenses are not accepted. ${BUY_HINT}`);
74
+ }
75
+ if (rec.kind !== "signed")
76
+ throw new Error("License kind must be signed");
77
+ if (!rec.signature || typeof rec.signature !== "string") {
78
+ throw new Error("Signed license requires a signature");
79
+ }
80
+ if (!rec.payload || !["free", "pro", "it"].includes(rec.payload.tier)) {
81
+ throw new Error("License payload.tier must be free, pro, or it");
82
+ }
83
+ return { kind: "signed", payload: rec.payload, signature: rec.signature };
84
+ }
85
+ function expired(payload, now = new Date()) {
86
+ if (!payload.expires_at)
87
+ return false;
88
+ const exp = new Date(payload.expires_at);
89
+ return Number.isFinite(exp.getTime()) && exp.getTime() < now.getTime();
90
+ }
91
+ export function readLicenseFile(path = licensePath()) {
92
+ if (!existsSync(path))
93
+ return null;
94
+ return parseLicenseFile(JSON.parse(readFileSync(path, "utf8")));
95
+ }
96
+ export function licenseStatus(now = new Date()) {
97
+ const path = licensePath();
98
+ const issues = [];
99
+ try {
100
+ const file = readLicenseFile(path);
101
+ if (!file) {
102
+ return {
103
+ tier: "free",
104
+ kind: "none",
105
+ features: [],
106
+ valid: true,
107
+ transferable: false,
108
+ path,
109
+ issues: [],
110
+ };
111
+ }
112
+ if (expired(file.payload, now))
113
+ issues.push("license expired");
114
+ issues.push(...verifySignedLicense(file));
115
+ const tier = issues.length ? "free" : file.payload.tier;
116
+ const features = [...new Set([...(file.payload.features ?? []), ...featuresForTier(tier)])];
117
+ return {
118
+ tier,
119
+ kind: "signed",
120
+ subject: file.payload.subject,
121
+ expires_at: file.payload.expires_at,
122
+ features,
123
+ valid: issues.length === 0,
124
+ transferable: true,
125
+ path,
126
+ issues,
127
+ };
128
+ }
129
+ catch (error) {
130
+ issues.push(error instanceof Error ? error.message : String(error));
131
+ return {
132
+ tier: "free",
133
+ kind: "none",
134
+ features: [],
135
+ valid: false,
136
+ transferable: false,
137
+ path,
138
+ issues,
139
+ };
140
+ }
141
+ }
142
+ export function hasFeature(feature) {
143
+ const status = licenseStatus();
144
+ return status.valid && status.features.includes(feature);
145
+ }
146
+ export function writeLicense(file, path = licensePath()) {
147
+ mkdirSync(amemHome(), { recursive: true, mode: 0o700 });
148
+ writeFileSync(path, `${JSON.stringify(file, null, 2)}\n`, { mode: 0o600 });
149
+ return path;
150
+ }
151
+ export function applyLicenseJson(raw) {
152
+ const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
153
+ const file = parseLicenseFile(parsed);
154
+ const bad = verifySignedLicense(file);
155
+ if (bad.length)
156
+ throw new Error(bad.join("; "));
157
+ writeLicense(file);
158
+ return licenseStatus();
159
+ }
160
+ export function applyLicenseFile(sourcePath) {
161
+ return applyLicenseJson(JSON.parse(readFileSync(sourcePath, "utf8")));
162
+ }
163
+ export function clearLicense() {
164
+ const path = licensePath();
165
+ if (existsSync(path))
166
+ unlinkSync(path);
167
+ }
168
+ export function requireFeature(feature, label = feature) {
169
+ if (hasFeature(feature))
170
+ return;
171
+ throw new Error(`${label} needs an amem Pro or IT license. ${BUY_HINT}`);
172
+ }
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,40 @@
1
+ export type JsonRpc = {
2
+ jsonrpc?: string;
3
+ id?: number | string | null;
4
+ method?: string;
5
+ params?: Record<string, unknown>;
6
+ };
7
+ export type JsonRpcResponse = {
8
+ jsonrpc: "2.0";
9
+ id: number | string;
10
+ result?: unknown;
11
+ error?: {
12
+ code: number;
13
+ message: string;
14
+ };
15
+ };
16
+ export declare function isJsonRpcMessage(body: unknown): body is JsonRpc;
17
+ /** Handle one JSON-RPC message. Notifications (no id) return null. */
18
+ export declare function dispatchMcp(msg: JsonRpc, fallbackWorkspace?: string): JsonRpcResponse | null;
19
+ export declare function handleMcpHttpBody(body: unknown, fallbackWorkspace?: string): {
20
+ status: number;
21
+ body: unknown;
22
+ };
23
+ export declare function stdioMcpLaunch(workspace?: string): {
24
+ command: string;
25
+ args: string[];
26
+ env: Record<string, string>;
27
+ };
28
+ export declare function mcpClientConfig(workspace?: string, port?: number): {
29
+ mcpServers: {
30
+ amem: {
31
+ command: string;
32
+ args: string[];
33
+ env: Record<string, string>;
34
+ };
35
+ };
36
+ http: {
37
+ url: string;
38
+ };
39
+ };
40
+ export declare function runMcpServer(): Promise<void>;