@memorax/memorax-code 0.1.5 → 0.1.7
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/README.md +7 -18
- package/bin/memorax-code-npm-preinstall.mjs +28 -39
- package/bin/memorax-code-plugin-postinstall.mjs +23 -1537
- package/bin/memorax-code-setup.mjs +1615 -0
- package/bin/memorax-code.mjs +289 -5
- package/docs/configuration.md +66 -17
- package/docs/troubleshooting.md +123 -7
- package/lib/memorax-code-adapter-common/src/config-utils.d.mts +5 -1
- package/lib/memorax-code-adapter-common/src/config-utils.mjs +36 -3
- package/lib/memorax-code-adapter-common/src/credentials/linux-secret-service.mjs +110 -0
- package/lib/memorax-code-adapter-common/src/credentials/macos-keychain.mjs +224 -0
- package/lib/memorax-code-adapter-common/src/credentials/secure-command.mjs +340 -0
- package/lib/memorax-code-adapter-common/src/credentials/trial-credential-record.d.mts +82 -0
- package/lib/memorax-code-adapter-common/src/credentials/trial-credential-record.mjs +183 -0
- package/lib/memorax-code-adapter-common/src/credentials/trial-credential-store.d.mts +82 -0
- package/lib/memorax-code-adapter-common/src/credentials/trial-credential-store.mjs +271 -0
- package/lib/memorax-code-adapter-common/src/credentials/windows-dpapi.mjs +440 -0
- package/lib/memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs +7 -4
- package/lib/memorax-code-adapter-common/src/setup-completion.d.mts +81 -0
- package/lib/memorax-code-adapter-common/src/setup-completion.mjs +154 -0
- package/lib/memorax-code-backend/dist/clients/claude/memory-hook-runtime.js +21 -2
- package/lib/memorax-code-backend/dist/clients/codex/memory-hook-runtime.js +23 -3
- package/lib/memorax-code-backend/dist/clients/opencode/memory-hook-runtime.js +19 -1
- package/lib/memorax-code-backend/dist/entrypoints/backend-cli.js +17 -16
- package/lib/memorax-code-backend/dist/lifecycle/backend/service.js +1 -0
- package/lib/memorax-code-backend/dist/lifecycle/orchestrator.js +81 -22
- package/lib/memorax-code-backend/dist/memorax-cli.js +3 -0
- package/lib/memorax-code-backend/dist/memory/automatic-retrieval.js +8 -2
- package/lib/memorax-code-backend/dist/memory/automatic-writeback.js +13 -1
- package/lib/memorax-code-backend/dist/memory/cli.js +10 -0
- package/lib/memorax-code-backend/dist/memory/quota-notice.js +208 -0
- package/lib/memorax-code-backend/dist/memory/service.js +11 -0
- package/lib/memorax-code-backend/dist/provider/memorax/adapter.js +4 -2
- package/lib/memorax-code-backend/dist/provider/memorax/http.js +10 -3
- package/lib/memorax-code-backend/dist/provider/memorax/quota.js +26 -0
- package/lib/memorax-code-backend/package.json +1 -1
- package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
- package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-claude-adapter/package.json +1 -1
- package/lib/memorax-code-claude-adapter/runtime-hooks/memory-turn.mjs +5 -3
- package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +2 -0
- package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +4 -2
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.d.mts +5 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.mjs +36 -3
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/linux-secret-service.mjs +110 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/macos-keychain.mjs +224 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/secure-command.mjs +340 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/trial-credential-record.d.mts +82 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/trial-credential-record.mjs +183 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/trial-credential-store.d.mts +82 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/trial-credential-store.mjs +271 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/credentials/windows-dpapi.mjs +440 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/memory-skill-reminder-hook.mjs +7 -4
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/setup-completion.d.mts +81 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/setup-completion.mjs +154 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/runtime-hooks/memory-turn.mjs +5 -3
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +2 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +4 -2
- package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
- package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-codex-adapter/package.json +1 -1
- package/lib/memorax-code-codex-adapter/runtime-hooks/memory-skill-reminder.mjs +2 -0
- package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-add.md +2 -0
- package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-search.md +4 -2
- package/lib/memorax-code-dsh-adapter/hooks/repo-memory-job.mjs +5 -2
- package/lib/memorax-code-dsh-adapter/package.json +1 -1
- package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-add.md +2 -0
- package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-search.md +4 -2
- package/lib/memorax-code-dsh-adapter/src/profile-lifecycle.mjs +158 -7
- package/lib/memorax-code-dsh-adapter/src/runtime-state.mjs +21 -0
- package/lib/memorax-code-opencode-adapter/package.json +1 -1
- package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-add.md +2 -0
- package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-search.md +4 -2
- package/lib/memorax-code-opencode-adapter/src/plugin.mjs +20 -0
- package/lib/package-transition.mjs +312 -0
- package/lib/resolve-codex-command.mjs +32 -6
- package/lib/setup-memory-preferences.mjs +113 -0
- package/lib/setup-reconcile.mjs +128 -0
- package/lib/trial-plugin-mark.mjs +171 -0
- package/lib/trial-provision-client.mjs +307 -0
- package/lib/trial-provision-flow.mjs +215 -0
- package/lib/trial-setup.mjs +53 -0
- package/package.json +1 -1
|
@@ -8,9 +8,10 @@ import {
|
|
|
8
8
|
findVsCodeExtensionCommand,
|
|
9
9
|
isExecutableCommand,
|
|
10
10
|
} from "./vscode-extension-command.mjs";
|
|
11
|
+
import { resolveWindowsCliInvocation } from "./windows-cli-invocation.mjs";
|
|
11
12
|
|
|
12
13
|
const APP_BUNDLE_NAMES = ["ChatGPT.app", "Codex.app"];
|
|
13
|
-
const
|
|
14
|
+
const WINDOWS_CODEX_DISCOVERY_PROBE_TIMEOUT_MS = 2_000;
|
|
14
15
|
const WINDOWS_CODEX_APP_RUNTIME_SEGMENTS = ["plugins", ".plugin-appserver", "codex.exe"];
|
|
15
16
|
|
|
16
17
|
export function resolveCodexCommand({
|
|
@@ -19,10 +20,12 @@ export function resolveCodexCommand({
|
|
|
19
20
|
platform = process.platform,
|
|
20
21
|
arch = process.arch,
|
|
21
22
|
applicationRoots = [join(homeDir, "Applications"), "/Applications"],
|
|
23
|
+
pathCommandAvailable = commandOnPath,
|
|
22
24
|
vscodeExtensionRoots = defaultVsCodeExtensionRoots(homeDir),
|
|
23
25
|
windowsAppProbe = spawnSync,
|
|
24
26
|
windowsAppRuntimePaths,
|
|
25
27
|
windowsPathExists = isExecutableCommand,
|
|
28
|
+
windowsPathProbe = spawnSync,
|
|
26
29
|
} = {}) {
|
|
27
30
|
const npmOverride = nonEmpty(env.MEMORAX_CODE_CODEX_COMMAND);
|
|
28
31
|
if (npmOverride) return { command: npmOverride, source: "npm-override" };
|
|
@@ -30,7 +33,13 @@ export function resolveCodexCommand({
|
|
|
30
33
|
const configured = nonEmpty(env.CODEX_CLI_PATH);
|
|
31
34
|
if (configured) return { command: configured, source: "configured" };
|
|
32
35
|
|
|
33
|
-
if (
|
|
36
|
+
if (
|
|
37
|
+
pathCommandAvailable("codex", env.PATH, platform, env.PATHEXT)
|
|
38
|
+
&& (
|
|
39
|
+
platform !== "win32"
|
|
40
|
+
|| windowsCodexPathCommandIsRunnable("codex", env, windowsPathProbe)
|
|
41
|
+
)
|
|
42
|
+
) {
|
|
34
43
|
return { command: "codex", source: "path" };
|
|
35
44
|
}
|
|
36
45
|
|
|
@@ -77,7 +86,10 @@ export function resolveWindowsCodexAppCommand({
|
|
|
77
86
|
const codexHome = nonEmpty(env.CODEX_HOME) ?? win32.join(homeDir, ".codex");
|
|
78
87
|
const candidates = runtimePaths ?? [win32.join(codexHome, ...WINDOWS_CODEX_APP_RUNTIME_SEGMENTS)];
|
|
79
88
|
for (const command of candidates) {
|
|
80
|
-
if (
|
|
89
|
+
if (
|
|
90
|
+
pathExists(command, platform)
|
|
91
|
+
&& windowsCodexCommandIsRunnable(command, ["--version"], env, spawnSyncImpl)
|
|
92
|
+
) {
|
|
81
93
|
return command;
|
|
82
94
|
}
|
|
83
95
|
}
|
|
@@ -123,17 +135,31 @@ function codexPlatformDirectories(platform, arch) {
|
|
|
123
135
|
return [];
|
|
124
136
|
}
|
|
125
137
|
|
|
126
|
-
function
|
|
138
|
+
function windowsCodexPathCommandIsRunnable(command, env, spawnSyncImpl) {
|
|
139
|
+
let invocation;
|
|
140
|
+
try {
|
|
141
|
+
invocation = resolveWindowsCliInvocation(command, ["--version"], {
|
|
142
|
+
env,
|
|
143
|
+
platform: "win32",
|
|
144
|
+
spawnSync: spawnSyncImpl,
|
|
145
|
+
});
|
|
146
|
+
} catch {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
return windowsCodexCommandIsRunnable(invocation.command, invocation.args, env, spawnSyncImpl);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function windowsCodexCommandIsRunnable(command, args, env, spawnSyncImpl) {
|
|
127
153
|
let result;
|
|
128
154
|
try {
|
|
129
155
|
result = spawnSyncImpl(
|
|
130
156
|
command,
|
|
131
|
-
|
|
157
|
+
args,
|
|
132
158
|
{
|
|
133
159
|
encoding: "utf8",
|
|
134
160
|
env,
|
|
135
161
|
stdio: ["ignore", "pipe", "pipe"],
|
|
136
|
-
timeout:
|
|
162
|
+
timeout: WINDOWS_CODEX_DISCOVERY_PROBE_TIMEOUT_MS,
|
|
137
163
|
windowsHide: true,
|
|
138
164
|
},
|
|
139
165
|
);
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { userInfo } from "node:os";
|
|
3
|
+
import { win32 } from "node:path";
|
|
4
|
+
|
|
5
|
+
const COMMAND_TIMEOUT_MS = 2_000;
|
|
6
|
+
const MAX_OUTPUT_BYTES = 16 * 1024;
|
|
7
|
+
|
|
8
|
+
export function detectSetupMemoryPreferences(options = {}) {
|
|
9
|
+
const platform = options.platform ?? process.platform;
|
|
10
|
+
const env = options.env ?? process.env;
|
|
11
|
+
const account = safely(options.readUserInfo ?? userInfo);
|
|
12
|
+
const locale = systemLocale({
|
|
13
|
+
platform,
|
|
14
|
+
env,
|
|
15
|
+
runCommand: options.runCommand ?? spawnSync,
|
|
16
|
+
readIntlLocale: options.readIntlLocale ?? defaultIntlLocale,
|
|
17
|
+
});
|
|
18
|
+
return Object.freeze({
|
|
19
|
+
userId: detectedUserId(account, platform),
|
|
20
|
+
outputLanguage: memoryLanguage(locale),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function detectedUserId(account, platform) {
|
|
25
|
+
if (platform !== "win32" && account?.uid === 0) return undefined;
|
|
26
|
+
const username = String(account?.username ?? "").normalize("NFKC").trim();
|
|
27
|
+
if (!username || /[\0\r\n]/.test(username)) return undefined;
|
|
28
|
+
return [...username].slice(0, 120).join("");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function systemLocale({ platform, env, runCommand, readIntlLocale }) {
|
|
32
|
+
if (platform === "darwin") {
|
|
33
|
+
return macosPreferredLanguage(runCommand, env)
|
|
34
|
+
?? environmentLocale(env)
|
|
35
|
+
?? safely(readIntlLocale);
|
|
36
|
+
}
|
|
37
|
+
if (platform === "win32") {
|
|
38
|
+
return windowsUiLanguage(runCommand, env)
|
|
39
|
+
?? safely(readIntlLocale);
|
|
40
|
+
}
|
|
41
|
+
return environmentLocale(env) ?? safely(readIntlLocale);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function macosPreferredLanguage(runCommand, env) {
|
|
45
|
+
const output = commandOutput(runCommand, "/usr/bin/defaults", [
|
|
46
|
+
"read",
|
|
47
|
+
"-g",
|
|
48
|
+
"AppleLanguages",
|
|
49
|
+
], env);
|
|
50
|
+
return /"([^"]+)"/.exec(output ?? "")?.[1];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function windowsUiLanguage(runCommand, env) {
|
|
54
|
+
const systemRoot = nonEmpty(env.SystemRoot) ?? nonEmpty(env.SYSTEMROOT);
|
|
55
|
+
const powershell = systemRoot
|
|
56
|
+
? win32.join(systemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
|
|
57
|
+
: "powershell.exe";
|
|
58
|
+
return commandOutput(runCommand, powershell, [
|
|
59
|
+
"-NoLogo",
|
|
60
|
+
"-NoProfile",
|
|
61
|
+
"-NonInteractive",
|
|
62
|
+
"-Command",
|
|
63
|
+
"[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false); [System.Globalization.CultureInfo]::CurrentUICulture.Name",
|
|
64
|
+
], env);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function commandOutput(runCommand, command, args, env) {
|
|
68
|
+
let result;
|
|
69
|
+
try {
|
|
70
|
+
result = runCommand(command, args, {
|
|
71
|
+
encoding: "utf8",
|
|
72
|
+
env,
|
|
73
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
74
|
+
timeout: COMMAND_TIMEOUT_MS,
|
|
75
|
+
maxBuffer: MAX_OUTPUT_BYTES,
|
|
76
|
+
windowsHide: true,
|
|
77
|
+
});
|
|
78
|
+
} catch {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
if (result?.status !== 0 || result.error || result.signal) return undefined;
|
|
82
|
+
return nonEmpty(String(result.stdout ?? "").replace(/^\uFEFF/, ""));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function environmentLocale(env) {
|
|
86
|
+
return nonEmpty(env.LC_ALL)
|
|
87
|
+
?? nonEmpty(env.LC_MESSAGES)
|
|
88
|
+
?? nonEmpty(env.LANGUAGE)?.split(":", 1)[0]
|
|
89
|
+
?? nonEmpty(env.LANG);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function defaultIntlLocale() {
|
|
93
|
+
return Intl.DateTimeFormat().resolvedOptions().locale;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function memoryLanguage(value) {
|
|
97
|
+
const locale = nonEmpty(value)?.toLowerCase().replaceAll("_", "-");
|
|
98
|
+
if (!locale) return undefined;
|
|
99
|
+
return /^zh(?:-|$)/.test(locale) ? "zh" : "en";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function nonEmpty(value) {
|
|
103
|
+
const normalized = String(value ?? "").trim();
|
|
104
|
+
return normalized || undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function safely(operation) {
|
|
108
|
+
try {
|
|
109
|
+
return operation();
|
|
110
|
+
} catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
export async function reconcileSetup({
|
|
2
|
+
start,
|
|
3
|
+
stop,
|
|
4
|
+
status,
|
|
5
|
+
isReady,
|
|
6
|
+
onEvent = () => {},
|
|
7
|
+
}) {
|
|
8
|
+
await onEvent({ type: "start", attempt: 1 });
|
|
9
|
+
let started = await start();
|
|
10
|
+
let recovered = false;
|
|
11
|
+
|
|
12
|
+
if (!commandSucceeded(started)) {
|
|
13
|
+
await onEvent({ type: "start-failed", attempt: 1 });
|
|
14
|
+
const deterministicFailure = classifyDeterministicStartFailure(started);
|
|
15
|
+
if (deterministicFailure) {
|
|
16
|
+
return await complete(onEvent, deterministicFailure);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
await onEvent({ type: "stop", reason: "start-failed" });
|
|
20
|
+
await stop();
|
|
21
|
+
await onEvent({ type: "start", attempt: 2 });
|
|
22
|
+
started = await start();
|
|
23
|
+
if (!commandSucceeded(started)) {
|
|
24
|
+
await onEvent({ type: "start-failed", attempt: 2 });
|
|
25
|
+
await onEvent({ type: "diagnostic-status" });
|
|
26
|
+
await status();
|
|
27
|
+
return await complete(onEvent, compactResult({
|
|
28
|
+
status: "not-verified",
|
|
29
|
+
reason: "start-failed-after-recovery",
|
|
30
|
+
code: commandFailureCode(started),
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
recovered = true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
await onEvent({ type: "start-succeeded", recovered });
|
|
37
|
+
await onEvent({ type: "status", recovered });
|
|
38
|
+
const checked = await status();
|
|
39
|
+
if (!commandSucceeded(checked)) {
|
|
40
|
+
await onEvent({ type: "status-failed" });
|
|
41
|
+
return await complete(onEvent, compactResult({
|
|
42
|
+
status: "not-verified",
|
|
43
|
+
reason: "status-failed",
|
|
44
|
+
code: commandFailureCode(checked),
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
await onEvent({ type: "status-succeeded" });
|
|
48
|
+
|
|
49
|
+
const ready = await isReady(checked);
|
|
50
|
+
if (!ready) {
|
|
51
|
+
return await complete(onEvent, {
|
|
52
|
+
status: "unavailable",
|
|
53
|
+
reason: "not-ready",
|
|
54
|
+
recovered,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return await complete(onEvent, {
|
|
59
|
+
status: "enabled",
|
|
60
|
+
reason: "ready",
|
|
61
|
+
recovered,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function runtimeAuthorityFailureCode(result) {
|
|
66
|
+
const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
|
|
67
|
+
return output.match(/\b(BACKEND_(?:(?:CONNECTION_AUTHORITY|TOKEN_RECORD|SERVICE_STATE)_(?:ABSENT|INVALID|UNSUPPORTED)|SERVICE_STATE_CLEANUP_FAILED))\b/)?.[1];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function lifecycleLockFailureCode(result) {
|
|
71
|
+
const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
|
|
72
|
+
return output.match(/\bBACKEND_LIFECYCLE_LOCK_TIMEOUT\b/)?.[0];
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function clientHookRuntimeActivationFailed(result) {
|
|
76
|
+
const output = `${result.stdout ?? ""}\n${result.stderr ?? ""}`;
|
|
77
|
+
return /client Hook runtime activation failed:/i.test(output);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function classifyDeterministicStartFailure(result) {
|
|
81
|
+
if (clientHookRuntimeActivationFailed(result)) {
|
|
82
|
+
return {
|
|
83
|
+
status: "not-verified",
|
|
84
|
+
reason: "hook-runtime-activation-failed",
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const lifecycleCode = lifecycleLockFailureCode(result);
|
|
89
|
+
if (lifecycleCode) {
|
|
90
|
+
return {
|
|
91
|
+
status: "not-verified",
|
|
92
|
+
reason: "lifecycle-lock-timeout",
|
|
93
|
+
code: lifecycleCode,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const authorityCode = runtimeAuthorityFailureCode(result);
|
|
98
|
+
if (authorityCode) {
|
|
99
|
+
return {
|
|
100
|
+
status: "not-verified",
|
|
101
|
+
reason: "runtime-authority-failed",
|
|
102
|
+
code: authorityCode,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function commandSucceeded(result) {
|
|
110
|
+
return result?.status === 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function commandFailureCode(result) {
|
|
114
|
+
return result?.code ?? result?.error?.code ?? result?.status;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function compactResult(result) {
|
|
118
|
+
if (result.code === undefined) {
|
|
119
|
+
const { code: _code, ...withoutCode } = result;
|
|
120
|
+
return withoutCode;
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function complete(onEvent, result) {
|
|
126
|
+
await onEvent({ type: "complete", ...result });
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import {
|
|
4
|
+
hostname as readHostname,
|
|
5
|
+
networkInterfaces,
|
|
6
|
+
platform as readPlatform,
|
|
7
|
+
} from "node:os";
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
|
|
10
|
+
const COMMAND_TIMEOUT_MS = 1_000;
|
|
11
|
+
export const TRIAL_MARK_VERSION = 1;
|
|
12
|
+
export const TRIAL_APP_SALT = "memorax-plugin-v1";
|
|
13
|
+
|
|
14
|
+
export function generateTrialPluginIdentity() {
|
|
15
|
+
const platform = normalizePlatform(safely(readPlatform));
|
|
16
|
+
return deriveTrialPluginIdentity({
|
|
17
|
+
markVersion: TRIAL_MARK_VERSION,
|
|
18
|
+
appSalt: TRIAL_APP_SALT,
|
|
19
|
+
machineId: readMachineId(platform),
|
|
20
|
+
hostname: safely(readHostname),
|
|
21
|
+
platform,
|
|
22
|
+
arch: process.arch,
|
|
23
|
+
macHash: createTrialMacHash(flattenNetworkInterfaces()),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function generateTrialMarkId() {
|
|
28
|
+
return generateTrialPluginIdentity().markId;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function deriveTrialPluginIdentity(fields = {}) {
|
|
32
|
+
const identity = Object.freeze({
|
|
33
|
+
markVersion: fields.markVersion ?? TRIAL_MARK_VERSION,
|
|
34
|
+
appSalt: normalizeAppSalt(fields.appSalt),
|
|
35
|
+
machineId: trimmed(fields.machineId),
|
|
36
|
+
hostname: trimmed(fields.hostname),
|
|
37
|
+
platform: normalizePlatform(fields.platform),
|
|
38
|
+
arch: normalizeArch(fields.arch),
|
|
39
|
+
macHash: normalize(fields.macHash),
|
|
40
|
+
});
|
|
41
|
+
const material = [
|
|
42
|
+
identity.appSalt,
|
|
43
|
+
hashMachineId(identity.machineId),
|
|
44
|
+
identity.hostname,
|
|
45
|
+
identity.platform,
|
|
46
|
+
identity.arch,
|
|
47
|
+
identity.macHash,
|
|
48
|
+
].join("");
|
|
49
|
+
return Object.freeze({
|
|
50
|
+
...identity,
|
|
51
|
+
markId: `mk_${sha256Hex(material)}`,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function deriveTrialMarkId(fields = {}) {
|
|
56
|
+
return deriveTrialPluginIdentity(fields).markId;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function createTrialMacHash(entries = []) {
|
|
60
|
+
const macs = [...new Set(entries
|
|
61
|
+
.filter((entry) => entry?.internal === false)
|
|
62
|
+
.map((entry) => normalizeMac(entry?.mac))
|
|
63
|
+
.filter(Boolean))]
|
|
64
|
+
.sort();
|
|
65
|
+
return sha256Hex(macs.join(""));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function readMachineId(platform) {
|
|
69
|
+
if (platform === "linux") {
|
|
70
|
+
return readFirstFile(["/etc/machine-id", "/var/lib/dbus/machine-id"]);
|
|
71
|
+
}
|
|
72
|
+
if (platform === "macos") {
|
|
73
|
+
const output = runCommand("/usr/sbin/ioreg", [
|
|
74
|
+
"-rd1",
|
|
75
|
+
"-c",
|
|
76
|
+
"IOPlatformExpertDevice",
|
|
77
|
+
]);
|
|
78
|
+
return output.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/i)?.[1] ?? "";
|
|
79
|
+
}
|
|
80
|
+
if (platform === "windows") {
|
|
81
|
+
const output = runCommand("reg.exe", [
|
|
82
|
+
"query",
|
|
83
|
+
"HKLM\\SOFTWARE\\Microsoft\\Cryptography",
|
|
84
|
+
"/v",
|
|
85
|
+
"MachineGuid",
|
|
86
|
+
]);
|
|
87
|
+
return output.match(/^\s*MachineGuid\s+REG_\w+\s+(.+?)\s*$/im)?.[1] ?? "";
|
|
88
|
+
}
|
|
89
|
+
return "";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function readFirstFile(paths) {
|
|
93
|
+
for (const path of paths) {
|
|
94
|
+
try {
|
|
95
|
+
const value = readFileSync(path, "utf8").trim();
|
|
96
|
+
if (value) return value;
|
|
97
|
+
} catch {
|
|
98
|
+
// Try the next standard machine ID location.
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return "";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function runCommand(command, args) {
|
|
105
|
+
try {
|
|
106
|
+
const result = spawnSync(command, args, {
|
|
107
|
+
encoding: "utf8",
|
|
108
|
+
maxBuffer: 64 * 1024,
|
|
109
|
+
shell: false,
|
|
110
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
111
|
+
timeout: COMMAND_TIMEOUT_MS,
|
|
112
|
+
windowsHide: true,
|
|
113
|
+
});
|
|
114
|
+
return result.status === 0 && typeof result.stdout === "string" ? result.stdout : "";
|
|
115
|
+
} catch {
|
|
116
|
+
return "";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function flattenNetworkInterfaces() {
|
|
121
|
+
const groups = safely(networkInterfaces);
|
|
122
|
+
if (!groups || typeof groups !== "object") return [];
|
|
123
|
+
return Object.values(groups).flatMap((entries) => Array.isArray(entries) ? entries : []);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function normalizeMac(value) {
|
|
127
|
+
const normalized = normalize(value);
|
|
128
|
+
if (!/^(?:[0-9a-f]{2}[:-]){5}[0-9a-f]{2}$/.test(normalized)) return "";
|
|
129
|
+
const compact = normalized.replaceAll(":", "").replaceAll("-", "");
|
|
130
|
+
return compact === "000000000000" ? "" : compact;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function normalize(value) {
|
|
134
|
+
return typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function trimmed(value) {
|
|
138
|
+
return typeof value === "string" ? value.trim() : "";
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function normalizeAppSalt(value) {
|
|
142
|
+
return typeof value === "string" ? value.trim() : "";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function normalizePlatform(value) {
|
|
146
|
+
const platform = normalize(value);
|
|
147
|
+
if (platform === "darwin" || platform === "macos") return "macos";
|
|
148
|
+
if (platform === "win32" || platform === "windows") return "windows";
|
|
149
|
+
return platform;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function normalizeArch(value) {
|
|
153
|
+
const arch = normalize(value);
|
|
154
|
+
return arch === "x64" ? "x86_64" : arch;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function hashMachineId(value) {
|
|
158
|
+
return sha256Hex(normalize(value));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function sha256Hex(value) {
|
|
162
|
+
return createHash("sha256").update(value, "utf8").digest("hex");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function safely(operation) {
|
|
166
|
+
try {
|
|
167
|
+
return operation();
|
|
168
|
+
} catch {
|
|
169
|
+
return "";
|
|
170
|
+
}
|
|
171
|
+
}
|