@memorax/memorax-code 0.1.5 → 0.1.6
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 +105 -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/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 +311 -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
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
lstatSync,
|
|
7
7
|
mkdirSync,
|
|
8
8
|
readFileSync,
|
|
9
|
+
realpathSync,
|
|
9
10
|
readdirSync,
|
|
10
11
|
renameSync,
|
|
11
12
|
rmSync,
|
|
@@ -22,6 +23,7 @@ import {
|
|
|
22
23
|
parseDshVersion,
|
|
23
24
|
} from "./dsh-version.mjs";
|
|
24
25
|
import {
|
|
26
|
+
buildDshCommand,
|
|
25
27
|
requireDshRuntimeAuthority,
|
|
26
28
|
requireEnabledDshRuntime,
|
|
27
29
|
} from "./runtime-state.mjs";
|
|
@@ -49,6 +51,7 @@ const { resolveWindowsCliInvocation } = await import(
|
|
|
49
51
|
|
|
50
52
|
const STATE_VERSION = 1;
|
|
51
53
|
const RUNTIME = "dsh";
|
|
54
|
+
const DSH_PACKAGE_NAME = "@deepseek-ai/dsh";
|
|
52
55
|
const ADAPTER_PACKAGE_NAME = "@memorax-code/dsh-memorax-code";
|
|
53
56
|
const HEADLESS_PROFILE_NAME = "headless";
|
|
54
57
|
const HEADLESS_BUNDLE_NAME = "@deepseek-ai/dsh-headless";
|
|
@@ -160,14 +163,18 @@ export function collectDshAdapterStatus(options = {}) {
|
|
|
160
163
|
return { ok: true, ...base, skipped: true, reason: "no_existing_profiles" };
|
|
161
164
|
}
|
|
162
165
|
|
|
163
|
-
const dshCommand =
|
|
164
|
-
|
|
166
|
+
const { dshCommand, compatibility } = resolveDshStatusCompatibility(
|
|
167
|
+
options,
|
|
168
|
+
paths,
|
|
169
|
+
state,
|
|
170
|
+
);
|
|
165
171
|
const version = compatibility.dshVersion;
|
|
166
172
|
const versionStatus = {
|
|
167
173
|
dshVersionTested: compatibility.dshVersionTested,
|
|
168
174
|
testedDshVersions: [...DSH_TESTED_VERSIONS],
|
|
169
175
|
};
|
|
170
|
-
if (compatibility.reason === "dsh_version_unavailable"
|
|
176
|
+
if (compatibility.reason === "dsh_version_unavailable"
|
|
177
|
+
|| compatibility.reason === "dsh_profile_runtime_stale") {
|
|
171
178
|
return {
|
|
172
179
|
ok: false,
|
|
173
180
|
...base,
|
|
@@ -327,8 +334,7 @@ function ensureDshPluginInstalledUnlocked(paths, options) {
|
|
|
327
334
|
};
|
|
328
335
|
}
|
|
329
336
|
|
|
330
|
-
const dshCommand =
|
|
331
|
-
const compatibility = inspectDshCompatibility(options, paths, dshCommand);
|
|
337
|
+
const { dshCommand, compatibility } = resolveDshCompatibility(options, paths, state);
|
|
332
338
|
if (compatibility.compatible !== true) {
|
|
333
339
|
const nextState = state?.enabled === true
|
|
334
340
|
? disabledState(state, state.profiles)
|
|
@@ -805,7 +811,13 @@ function disableDshPluginInstallationUnlocked(paths, options, removeState) {
|
|
|
805
811
|
};
|
|
806
812
|
}
|
|
807
813
|
|
|
808
|
-
function removeProfileAdapterPackages(
|
|
814
|
+
function removeProfileAdapterPackages(
|
|
815
|
+
paths,
|
|
816
|
+
options,
|
|
817
|
+
name,
|
|
818
|
+
packageNames,
|
|
819
|
+
dshCommand,
|
|
820
|
+
) {
|
|
809
821
|
const profilePath = join(paths.profilesRoot, name);
|
|
810
822
|
let profile = inspectProfile(name, profilePath);
|
|
811
823
|
let failure;
|
|
@@ -1049,14 +1061,153 @@ function resolveDshCommand(options, paths, state) {
|
|
|
1049
1061
|
?? nonEmpty(paths.env.MEMORAX_CODE_DSH_COMMAND)
|
|
1050
1062
|
?? nonEmpty(state?.dshCommand)
|
|
1051
1063
|
?? "dsh";
|
|
1064
|
+
const normalized = normalizeDshCommand(command);
|
|
1065
|
+
if (normalized) return normalized;
|
|
1066
|
+
const profileRuntime = inspectProfileDshRuntime(paths, state);
|
|
1067
|
+
return profileRuntime?.compatibility.compatible === true
|
|
1068
|
+
? profileRuntime.dshCommand
|
|
1069
|
+
: "dsh";
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
function resolveDshStatusCompatibility(options, paths, state) {
|
|
1073
|
+
if (state?.enabled !== true) return resolveDshCompatibility(options, paths, state);
|
|
1074
|
+
const dshCommand = normalizeDshCommand(state.dshCommand);
|
|
1075
|
+
if (!dshCommand) {
|
|
1076
|
+
return {
|
|
1077
|
+
dshCommand: state.dshCommand,
|
|
1078
|
+
compatibility: unavailableDshCompatibility("dsh_version_unavailable"),
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
const profileRuntime = inspectProfileDshRuntime(paths, state);
|
|
1082
|
+
return profileRuntime?.dshCommand === dshCommand
|
|
1083
|
+
? profileRuntime
|
|
1084
|
+
: { dshCommand, compatibility: inspectDshCompatibility(options, paths, dshCommand) };
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
function resolveDshCompatibility(options, paths, state) {
|
|
1088
|
+
const configured = nonEmpty(options.dshCommand)
|
|
1089
|
+
?? nonEmpty(paths.env.MEMORAX_CODE_DSH_COMMAND);
|
|
1090
|
+
if (configured) {
|
|
1091
|
+
const dshCommand = normalizeDshCommand(configured);
|
|
1092
|
+
return dshCommand
|
|
1093
|
+
? { dshCommand, compatibility: inspectDshCompatibility(options, paths, dshCommand) }
|
|
1094
|
+
: {
|
|
1095
|
+
dshCommand: configured,
|
|
1096
|
+
compatibility: unavailableDshCompatibility("dsh_version_unavailable"),
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
const profileRuntime = inspectProfileDshRuntime(paths, state);
|
|
1101
|
+
const attemptedCommands = new Set();
|
|
1102
|
+
let unavailable;
|
|
1103
|
+
for (const value of [state?.dshCommand, "dsh"]) {
|
|
1104
|
+
const dshCommand = normalizeDshCommand(value);
|
|
1105
|
+
if (!dshCommand || attemptedCommands.has(dshCommand)) continue;
|
|
1106
|
+
attemptedCommands.add(dshCommand);
|
|
1107
|
+
if (dshCommand === profileRuntime?.dshCommand) {
|
|
1108
|
+
if (profileRuntime.compatibility.compatible === true) return profileRuntime;
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
const compatibility = inspectDshCompatibility(options, paths, dshCommand);
|
|
1112
|
+
if (compatibility.compatible === true) return { dshCommand, compatibility };
|
|
1113
|
+
unavailable ??= { dshCommand, compatibility };
|
|
1114
|
+
}
|
|
1115
|
+
if (profileRuntime?.compatibility.compatible === true) return profileRuntime;
|
|
1116
|
+
if (profileRuntime?.compatibility.reason === "dsh_profile_runtime_stale") {
|
|
1117
|
+
return {
|
|
1118
|
+
dshCommand: profileRuntime.dshCommand ?? unavailable?.dshCommand ?? "dsh",
|
|
1119
|
+
compatibility: profileRuntime.compatibility,
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
return unavailable ?? {
|
|
1123
|
+
dshCommand: "dsh",
|
|
1124
|
+
compatibility: unavailableDshCompatibility("dsh_version_unavailable"),
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
function normalizeDshCommand(value) {
|
|
1129
|
+
const command = nonEmpty(value);
|
|
1130
|
+
if (!command) return undefined;
|
|
1131
|
+
const name = command
|
|
1132
|
+
.split(/[\\/]/)
|
|
1133
|
+
.at(-1)
|
|
1134
|
+
.replace(/\.(?:cmd|bat|exe|com)$/i, "")
|
|
1135
|
+
.toLowerCase();
|
|
1136
|
+
if (name === "npx") return undefined;
|
|
1052
1137
|
return command.includes("/") || command.includes("\\") ? resolve(command) : command;
|
|
1053
1138
|
}
|
|
1054
1139
|
|
|
1140
|
+
function inspectProfileDshRuntime(paths, state) {
|
|
1141
|
+
const packageRoot = join(paths.profilesRoot, "node_modules", ...DSH_PACKAGE_NAME.split("/"));
|
|
1142
|
+
const persistedCommand = normalizeDshCommand(state?.dshCommand);
|
|
1143
|
+
const profileCommand = persistedCommand && isPathInside(persistedCommand, packageRoot)
|
|
1144
|
+
? persistedCommand
|
|
1145
|
+
: undefined;
|
|
1146
|
+
try {
|
|
1147
|
+
lstatSync(packageRoot);
|
|
1148
|
+
} catch (error) {
|
|
1149
|
+
return error?.code === "ENOENT" && !profileCommand
|
|
1150
|
+
? undefined
|
|
1151
|
+
: {
|
|
1152
|
+
dshCommand: profileCommand,
|
|
1153
|
+
compatibility: unavailableDshCompatibility("dsh_profile_runtime_stale"),
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
try {
|
|
1158
|
+
const realPackageRoot = realpathSync(packageRoot);
|
|
1159
|
+
if (!lstatSync(realPackageRoot).isDirectory()) throw new Error("DSH package root is not a directory");
|
|
1160
|
+
const manifest = readJsonObject(join(realPackageRoot, "package.json"));
|
|
1161
|
+
const version = manifest?.name === DSH_PACKAGE_NAME
|
|
1162
|
+
? parseDshVersion(manifest.version)
|
|
1163
|
+
: undefined;
|
|
1164
|
+
const bin = manifest?.bin !== null
|
|
1165
|
+
&& typeof manifest?.bin === "object"
|
|
1166
|
+
&& !Array.isArray(manifest.bin)
|
|
1167
|
+
? nonEmpty(manifest.bin.dsh)
|
|
1168
|
+
: undefined;
|
|
1169
|
+
if (!version || !bin || isAbsolute(bin)) throw new Error("DSH package metadata is invalid");
|
|
1170
|
+
|
|
1171
|
+
const dshCommand = resolve(packageRoot, bin);
|
|
1172
|
+
const realCommand = realpathSync(dshCommand);
|
|
1173
|
+
if (!isPathInside(dshCommand, packageRoot)
|
|
1174
|
+
|| !isPathInside(realCommand, realPackageRoot)
|
|
1175
|
+
|| !lstatSync(realCommand).isFile()) {
|
|
1176
|
+
throw new Error("DSH package entrypoint is invalid");
|
|
1177
|
+
}
|
|
1178
|
+
return {
|
|
1179
|
+
dshCommand,
|
|
1180
|
+
compatibility: {
|
|
1181
|
+
compatible: true,
|
|
1182
|
+
dshVersion: version,
|
|
1183
|
+
dshVersionTested: isTestedDshVersion(version),
|
|
1184
|
+
testedDshVersions: [...DSH_TESTED_VERSIONS],
|
|
1185
|
+
},
|
|
1186
|
+
};
|
|
1187
|
+
} catch {
|
|
1188
|
+
return {
|
|
1189
|
+
dshCommand: profileCommand,
|
|
1190
|
+
compatibility: unavailableDshCompatibility("dsh_profile_runtime_stale"),
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
function unavailableDshCompatibility(reason) {
|
|
1196
|
+
return {
|
|
1197
|
+
compatible: false,
|
|
1198
|
+
reason,
|
|
1199
|
+
testedDshVersions: [...DSH_TESTED_VERSIONS],
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1055
1203
|
function runDsh(options, paths, args, command) {
|
|
1056
1204
|
const env = { ...paths.env, DSH_HOME: paths.dshHome };
|
|
1057
1205
|
let executable;
|
|
1058
1206
|
try {
|
|
1059
|
-
|
|
1207
|
+
const [launcher, ...launcherArgs] = buildDshCommand(command, args, {
|
|
1208
|
+
nodePath: options.windowsCliResolution?.nodePath,
|
|
1209
|
+
});
|
|
1210
|
+
executable = resolveWindowsCliInvocation(launcher, launcherArgs, {
|
|
1060
1211
|
...options.windowsCliResolution,
|
|
1061
1212
|
env,
|
|
1062
1213
|
});
|
|
@@ -5,6 +5,17 @@ import { parseDshVersion } from "./dsh-version.mjs";
|
|
|
5
5
|
|
|
6
6
|
const PACKAGE_METADATA_VERSION = 1;
|
|
7
7
|
const STATE_VERSION = 1;
|
|
8
|
+
|
|
9
|
+
export function buildDshCommand(command, args, options = {}) {
|
|
10
|
+
const executable = nonEmptyString(command);
|
|
11
|
+
if (!executable) throw new TypeError("DSH command must be a non-empty string");
|
|
12
|
+
if (!Array.isArray(args)) throw new TypeError("DSH arguments must be an array");
|
|
13
|
+
if (isNpxCommand(executable)) throw disabledError();
|
|
14
|
+
return isAbsolute(executable) && /\.(?:cjs|js|mjs)$/i.test(executable)
|
|
15
|
+
? [nonEmptyString(options.nodePath) ?? process.execPath, executable, ...args]
|
|
16
|
+
: [executable, ...args];
|
|
17
|
+
}
|
|
18
|
+
|
|
8
19
|
/** Read the current durable enablement authority for this installed DSH bundle. */
|
|
9
20
|
export function requireEnabledDshRuntime(pluginRoot) {
|
|
10
21
|
const authority = requireDshRuntimeAuthority(pluginRoot);
|
|
@@ -28,6 +39,7 @@ export function requireDshRuntimeAuthority(pluginRoot) {
|
|
|
28
39
|
|| !nonEmptyString(metadata.memoraxCodeCommand)
|
|
29
40
|
|| !nonEmptyString(metadata.memoraxCodeHome)
|
|
30
41
|
|| !nonEmptyString(metadata.dshCommand)
|
|
42
|
+
|| isNpxCommand(metadata.dshCommand)
|
|
31
43
|
|| !nonEmptyString(metadata.dshHome)
|
|
32
44
|
|| !parseDshVersion(metadata.dshVersion)
|
|
33
45
|
|| !nonEmptyString(metadata.sourceAdapterRoot)
|
|
@@ -102,6 +114,15 @@ function nonEmptyString(value) {
|
|
|
102
114
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
103
115
|
}
|
|
104
116
|
|
|
117
|
+
function isNpxCommand(value) {
|
|
118
|
+
const command = nonEmptyString(value);
|
|
119
|
+
return Boolean(command && command
|
|
120
|
+
.split(/[\\/]/)
|
|
121
|
+
.at(-1)
|
|
122
|
+
.replace(/\.(?:cmd|bat|exe|com)$/i, "")
|
|
123
|
+
.toLowerCase() === "npx");
|
|
124
|
+
}
|
|
125
|
+
|
|
105
126
|
function timestampString(value) {
|
|
106
127
|
const normalized = nonEmptyString(value);
|
|
107
128
|
return normalized && Number.isFinite(Date.parse(normalized)) ? normalized : undefined;
|
|
@@ -83,4 +83,6 @@ Do not add speculation, assistant-only praise, or a current instruction merely b
|
|
|
83
83
|
|
|
84
84
|
## Output
|
|
85
85
|
|
|
86
|
+
If a successful Add returns `quotaNotice`, or prints it as a quota-reminder line in the default CLI output, present the complete reminder once and prominently before the normal result summary. Do not reduce it to only a percentage or omit its account URL or conditional local Mark ID retrieval instructions. Never run `memorax-code account --show-mark-id` for the user, ask for its output, or reproduce a Mark ID in chat. Treat the reminder as user-facing operational output, not coding memory content, and continue the current task.
|
|
87
|
+
|
|
86
88
|
After an add request is accepted, confirm briefly that it was submitted for processing and identify the reusable coding lesson at a high level. If add is disabled or fails, report the issue and continue without bypassing the CLI.
|
|
@@ -6,11 +6,11 @@ Use these instructions only to search reusable coding memory through `memorax-cl
|
|
|
6
6
|
|
|
7
7
|
Run the CLI from the active task workspace. The installed Hook and session binding supply the authoritative workspace root; do not run `git rev-parse`, infer the root from Git metadata, or substitute an unrelated working directory. Do not make `memorax-cli status` a mandatory preflight; use it only to diagnose a configuration or scope failure.
|
|
8
8
|
|
|
9
|
-
Coding memory uses `<MemoraX base
|
|
9
|
+
Coding memory uses `<MemoraX base username>@<normalized repository name>` for Git workspaces and the normalized folder name for genuine non-Git directories. MemoraX Code resolves `.git`, `gitdir`, and `commondir` without executing Git. Linked worktrees share one repository scope; another clone, repository, or non-Git directory retains a different local session key even when its readable name matches.
|
|
10
10
|
|
|
11
11
|
If a successful Search result reports `workspaceScopeFallbackReason: git_metadata_invalid`, malformed or incomplete metadata inside a direct `.git` directory was downgraded to the normalized local folder scope. Search has already run with the reported `effectiveUserId`. Present its `userNotice` once without pausing the current task or asking the user to repair Git first, then continue with the returned memory and live evidence. After the repository or `.git` metadata is repaired, later Search, Add, and automatic writeback in the same client session automatically use the restored Git repository scope.
|
|
12
12
|
|
|
13
|
-
Require a readable active workspace binding. A CLI command from a linked worktree of the bound repository is valid. If `memorax-cli search` reports `workspace_scope_mismatch` or `workspace_scope_unavailable`, do not bypass the scope or fall back to an unscoped
|
|
13
|
+
Require a readable active workspace binding. A CLI command from a linked worktree of the bound repository is valid. If `memorax-cli search` reports `workspace_scope_mismatch` or `workspace_scope_unavailable`, do not bypass the scope or fall back to an unscoped username. Do not change the CLI working directory and retry. Tell the user that memory search was not executed and no request was sent to MemoraX, then present the CLI's `userAction` in natural language. Continue the current task using only live code and documentation.
|
|
14
14
|
|
|
15
15
|
If `memorax-cli` is not on `PATH`, or memory is disabled, unconfigured, or unavailable, report that briefly and continue with live code or documentation. Authenticate through MemoraX Code configuration; never recover credentials from shell history or place tokens in prompts. Treat injected memory as a hypothesis and verify it against the current checkout.
|
|
16
16
|
|
|
@@ -88,4 +88,6 @@ Do not apply this retry to `memorax-cli add`, authentication or configuration fa
|
|
|
88
88
|
|
|
89
89
|
## Output
|
|
90
90
|
|
|
91
|
+
If a successful Search returns `quotaNotice`, or prints it as a quota-reminder line in the default CLI output, present the complete reminder once and prominently before the normal result summary. Do not reduce it to only a percentage or omit its account URL or conditional local Mark ID retrieval instructions. Never run `memorax-code account --show-mark-id` for the user, ask for its output, or reproduce a Mark ID in chat. Treat the reminder as user-facing operational output, not recalled memory, and continue the current task.
|
|
92
|
+
|
|
91
93
|
Mention only an invariant, pitfall, convention, or validation idea that materially affects the answer. Ground claims about current implementation behavior in live code and checks.
|
|
@@ -210,6 +210,7 @@ export function createMemoraxOpenCodePlugin(options = {}) {
|
|
|
210
210
|
}, TURN_START_TIMEOUT_MS);
|
|
211
211
|
turnStartAccepted = true;
|
|
212
212
|
if (!pluginEnabled(options)) return;
|
|
213
|
+
void showUserNotice(client, directory, result?.userNotice, options);
|
|
213
214
|
repositoryWorktree = stringValue(result?.repoMemoryWorktree);
|
|
214
215
|
const repoMemoryEnv = openCodeRepoMemoryEnv(options, openCodeServerUrl, sessionId);
|
|
215
216
|
if (repoMemoryEnv) {
|
|
@@ -384,6 +385,25 @@ function appendSystemContexts(output, ...contexts) {
|
|
|
384
385
|
output.message.system = [existing, ...additions].filter(Boolean).join("\n\n");
|
|
385
386
|
}
|
|
386
387
|
|
|
388
|
+
async function showUserNotice(client, directory, notice, options) {
|
|
389
|
+
const message = stringValue(notice);
|
|
390
|
+
if (!message || typeof client?.tui?.showToast !== "function") return;
|
|
391
|
+
try {
|
|
392
|
+
await client.tui.showToast({
|
|
393
|
+
body: {
|
|
394
|
+
title: "MemoraX Code",
|
|
395
|
+
message,
|
|
396
|
+
variant: "warning",
|
|
397
|
+
duration: 10_000,
|
|
398
|
+
},
|
|
399
|
+
query: { directory },
|
|
400
|
+
throwOnError: true,
|
|
401
|
+
});
|
|
402
|
+
} catch (error) {
|
|
403
|
+
debug(options, "opencode quota reminder failed", error);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
387
407
|
function recordReminder(options, reminder) {
|
|
388
408
|
if (!reminder.turnId) return Promise.resolve();
|
|
389
409
|
return postBackend(options, "/memory/skill-reminder", {
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { existsSync, unlinkSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { withJsonFileLock, withJsonFileLockAsync } from "./memorax-code-adapter-common/src/config-utils.mjs";
|
|
7
|
+
import {
|
|
8
|
+
readJsonRuntimeRecord,
|
|
9
|
+
RuntimeRecordError,
|
|
10
|
+
writePrivateJsonRecord,
|
|
11
|
+
} from "./memorax-code-adapter-common/src/runtime-record.mjs";
|
|
12
|
+
|
|
13
|
+
export const PACKAGE_TRANSITION_RECORD_VERSION = 1;
|
|
14
|
+
export const PACKAGE_TRANSITION_FRESHNESS_MS = 15 * 60 * 1_000;
|
|
15
|
+
export const PACKAGE_TRANSITION_COMMAND_TIMEOUT_MS = 45_000;
|
|
16
|
+
|
|
17
|
+
const RETIRING_KEYS = new Set([
|
|
18
|
+
"version",
|
|
19
|
+
"state",
|
|
20
|
+
"transitionId",
|
|
21
|
+
"startedAt",
|
|
22
|
+
"sourceVersion",
|
|
23
|
+
]);
|
|
24
|
+
const RETIRED_KEYS = new Set([...RETIRING_KEYS, "retiredAt"]);
|
|
25
|
+
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
26
|
+
const ISO_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
27
|
+
|
|
28
|
+
export class PackageTransitionRecordError extends RuntimeRecordError {
|
|
29
|
+
constructor(state, path) {
|
|
30
|
+
super({
|
|
31
|
+
name: "Package transition record",
|
|
32
|
+
path,
|
|
33
|
+
state,
|
|
34
|
+
codePrefix: "PACKAGE_TRANSITION_RECORD",
|
|
35
|
+
recovery: state.status === "unsupported"
|
|
36
|
+
? "upgrade MemoraX Code or restore a supported package transition record"
|
|
37
|
+
: "inspect the package transition record before retrying installation",
|
|
38
|
+
});
|
|
39
|
+
this.name = "PackageTransitionRecordError";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function packageTransitionPath(memoraxCodeHome = defaultMemoraxCodeHome()) {
|
|
44
|
+
return join(memoraxCodeHome, "runtime", "install", "package-transition.json");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function readPackageTransitionRecord(memoraxCodeHome = defaultMemoraxCodeHome()) {
|
|
48
|
+
const path = packageTransitionPath(memoraxCodeHome);
|
|
49
|
+
const state = readJsonRuntimeRecord(path);
|
|
50
|
+
if (state.status !== "present") return state;
|
|
51
|
+
const value = state.value;
|
|
52
|
+
if (value.version !== PACKAGE_TRANSITION_RECORD_VERSION) {
|
|
53
|
+
if (Number.isSafeInteger(value.version) && value.version > 0) {
|
|
54
|
+
return { status: "unsupported", version: value.version };
|
|
55
|
+
}
|
|
56
|
+
return { status: "invalid", reason: "invalid_version" };
|
|
57
|
+
}
|
|
58
|
+
if (value.state !== "retiring" && value.state !== "retired") {
|
|
59
|
+
return { status: "invalid", reason: "invalid_state" };
|
|
60
|
+
}
|
|
61
|
+
const allowedKeys = value.state === "retiring" ? RETIRING_KEYS : RETIRED_KEYS;
|
|
62
|
+
if (Object.keys(value).length !== allowedKeys.size
|
|
63
|
+
|| Object.keys(value).some((key) => !allowedKeys.has(key))) {
|
|
64
|
+
return { status: "invalid", reason: "unknown_or_missing_fields" };
|
|
65
|
+
}
|
|
66
|
+
if (typeof value.transitionId !== "string" || !UUID_PATTERN.test(value.transitionId)) {
|
|
67
|
+
return { status: "invalid", reason: "invalid_transition_id" };
|
|
68
|
+
}
|
|
69
|
+
const startedAt = isoTimestamp(value.startedAt);
|
|
70
|
+
if (!startedAt) return { status: "invalid", reason: "invalid_started_at" };
|
|
71
|
+
const sourceVersion = nonEmptyString(value.sourceVersion);
|
|
72
|
+
if (!sourceVersion) return { status: "invalid", reason: "invalid_source_version" };
|
|
73
|
+
const record = {
|
|
74
|
+
version: PACKAGE_TRANSITION_RECORD_VERSION,
|
|
75
|
+
state: value.state,
|
|
76
|
+
transitionId: value.transitionId,
|
|
77
|
+
startedAt,
|
|
78
|
+
sourceVersion,
|
|
79
|
+
};
|
|
80
|
+
if (value.state === "retired") {
|
|
81
|
+
const retiredAt = isoTimestamp(value.retiredAt);
|
|
82
|
+
if (!retiredAt || Date.parse(retiredAt) < Date.parse(startedAt)) {
|
|
83
|
+
return { status: "invalid", reason: "invalid_retired_at" };
|
|
84
|
+
}
|
|
85
|
+
record.retiredAt = retiredAt;
|
|
86
|
+
}
|
|
87
|
+
return { status: "valid", record };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function runNpmPreinstallPackageTransition(options = {}) {
|
|
91
|
+
const memoraxCodeHome = nonEmptyString(options.memoraxCodeHome) ?? defaultMemoraxCodeHome();
|
|
92
|
+
const memoraxCodeBin = requiredString(options.memoraxCodeBin, "memoraxCodeBin");
|
|
93
|
+
const packageVersion = requiredString(options.packageVersion, "packageVersion");
|
|
94
|
+
const pidPath = backendPidPath(memoraxCodeHome);
|
|
95
|
+
const dshStatePath = join(memoraxCodeHome, "adapters", "dsh", "state.json");
|
|
96
|
+
if (!existsSync(pidPath) && !existsSync(dshStatePath)) return { disposition: "noop" };
|
|
97
|
+
|
|
98
|
+
const transitionPath = packageTransitionPath(memoraxCodeHome);
|
|
99
|
+
let transition;
|
|
100
|
+
let shouldStop = false;
|
|
101
|
+
withJsonFileLock(transitionPath, () => {
|
|
102
|
+
const existing = readPackageTransitionRecord(memoraxCodeHome);
|
|
103
|
+
if (existing.status !== "absent") throw packageTransitionStateError(existing, transitionPath);
|
|
104
|
+
const pidState = readBackendPidState(pidPath);
|
|
105
|
+
const hasDshState = existsSync(dshStatePath);
|
|
106
|
+
if (pidState.status === "absent" && !hasDshState) return;
|
|
107
|
+
shouldStop = true;
|
|
108
|
+
if (pidState.status !== "alive" && !hasDshState) return;
|
|
109
|
+
transition = {
|
|
110
|
+
version: PACKAGE_TRANSITION_RECORD_VERSION,
|
|
111
|
+
state: "retiring",
|
|
112
|
+
transitionId: randomUUID(),
|
|
113
|
+
startedAt: nowIso(options),
|
|
114
|
+
sourceVersion: packageVersion,
|
|
115
|
+
};
|
|
116
|
+
const write = writePrivateJsonRecord(transitionPath, transition, {
|
|
117
|
+
durableBoundary: memoraxCodeHome,
|
|
118
|
+
});
|
|
119
|
+
if (write.durability !== "confirmed") {
|
|
120
|
+
throw transitionError("PACKAGE_TRANSITION_DURABILITY_UNCERTAIN", "durable persistence of the retiring package transition could not be confirmed");
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (!shouldStop) return { disposition: "noop" };
|
|
125
|
+
const stopped = runLifecycleCommand({
|
|
126
|
+
...options,
|
|
127
|
+
memoraxCodeHome,
|
|
128
|
+
memoraxCodeBin,
|
|
129
|
+
args: ["stop", "--home", memoraxCodeHome, "--clients", "none", "--json"],
|
|
130
|
+
env: { ...options.env, MEMORAX_CODE_PACKAGE_REPLACEMENT: "1" },
|
|
131
|
+
label: "memorax-code stop",
|
|
132
|
+
});
|
|
133
|
+
if (existsSync(pidPath)) {
|
|
134
|
+
throw transitionError(
|
|
135
|
+
"PACKAGE_TRANSITION_PID_REMAINS",
|
|
136
|
+
`managed Backend PID authority still exists at ${pidPath}`,
|
|
137
|
+
stopped.result,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
if (!transition) return { disposition: "cleaned" };
|
|
141
|
+
|
|
142
|
+
const retired = withJsonFileLock(transitionPath, () => {
|
|
143
|
+
const current = requireValidTransition(memoraxCodeHome);
|
|
144
|
+
if (current.record.state !== "retiring"
|
|
145
|
+
|| current.record.transitionId !== transition.transitionId) {
|
|
146
|
+
throw transitionError("PACKAGE_TRANSITION_REPLACED", "package transition changed while the Backend was being retired");
|
|
147
|
+
}
|
|
148
|
+
const record = { ...current.record, state: "retired", retiredAt: nowIso(options) };
|
|
149
|
+
const write = writePrivateJsonRecord(transitionPath, record, {
|
|
150
|
+
durableBoundary: memoraxCodeHome,
|
|
151
|
+
});
|
|
152
|
+
if (write.durability !== "confirmed") {
|
|
153
|
+
throw transitionError("PACKAGE_TRANSITION_DURABILITY_UNCERTAIN", "durable persistence of the retired package transition could not be confirmed");
|
|
154
|
+
}
|
|
155
|
+
return record;
|
|
156
|
+
});
|
|
157
|
+
return { disposition: "retired", transition: retired };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export async function runNpmPostinstallPackageTransition(options = {}) {
|
|
161
|
+
const memoraxCodeHome = nonEmptyString(options.memoraxCodeHome) ?? defaultMemoraxCodeHome();
|
|
162
|
+
const memoraxCodeBin = requiredString(options.memoraxCodeBin, "memoraxCodeBin");
|
|
163
|
+
const transitionPath = packageTransitionPath(memoraxCodeHome);
|
|
164
|
+
if (readJsonRuntimeRecord(transitionPath).status === "absent") {
|
|
165
|
+
return { disposition: "noop" };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return await withJsonFileLockAsync(transitionPath, async () => {
|
|
169
|
+
const reloaded = readPackageTransitionRecord(memoraxCodeHome);
|
|
170
|
+
if (reloaded.status === "absent") return { disposition: "noop" };
|
|
171
|
+
if (reloaded.status !== "valid") {
|
|
172
|
+
throw new PackageTransitionRecordError(reloaded, transitionPath);
|
|
173
|
+
}
|
|
174
|
+
const current = reloaded;
|
|
175
|
+
if (current.record.state !== "retired") {
|
|
176
|
+
throw transitionError("PACKAGE_TRANSITION_NOT_RETIRED", "package transition is still retiring");
|
|
177
|
+
}
|
|
178
|
+
const ageMs = nowMs(options) - Date.parse(current.record.retiredAt);
|
|
179
|
+
if (ageMs < 0 || ageMs > PACKAGE_TRANSITION_FRESHNESS_MS) {
|
|
180
|
+
throw transitionError("PACKAGE_TRANSITION_STALE", "retired package transition is stale");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
runLifecycleCommand({
|
|
184
|
+
...options,
|
|
185
|
+
memoraxCodeHome,
|
|
186
|
+
memoraxCodeBin,
|
|
187
|
+
args: ["start", "--home", memoraxCodeHome, "--json"],
|
|
188
|
+
env: { ...options.env, MEMORAX_CODE_PACKAGE_REPLACEMENT: "1" },
|
|
189
|
+
label: "memorax-code start",
|
|
190
|
+
});
|
|
191
|
+
runLifecycleCommand({
|
|
192
|
+
...options,
|
|
193
|
+
memoraxCodeHome,
|
|
194
|
+
memoraxCodeBin,
|
|
195
|
+
args: ["status", "--home", memoraxCodeHome, "--json"],
|
|
196
|
+
label: "memorax-code status",
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const finalState = requireValidTransition(memoraxCodeHome);
|
|
200
|
+
if (finalState.record.state !== "retired"
|
|
201
|
+
|| finalState.record.transitionId !== current.record.transitionId) {
|
|
202
|
+
throw transitionError("PACKAGE_TRANSITION_REPLACED", "package transition changed before it could be consumed");
|
|
203
|
+
}
|
|
204
|
+
unlinkSync(transitionPath);
|
|
205
|
+
return { disposition: "restored", transitionId: current.record.transitionId };
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function requireValidTransition(memoraxCodeHome) {
|
|
210
|
+
const path = packageTransitionPath(memoraxCodeHome);
|
|
211
|
+
const state = readPackageTransitionRecord(memoraxCodeHome);
|
|
212
|
+
if (state.status !== "valid") throw new PackageTransitionRecordError(state, path);
|
|
213
|
+
return state;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function packageTransitionStateError(state, path) {
|
|
217
|
+
if (state.status === "valid") {
|
|
218
|
+
return transitionError("PACKAGE_TRANSITION_PENDING", `package transition is already ${state.record.state}: ${path}`);
|
|
219
|
+
}
|
|
220
|
+
return new PackageTransitionRecordError(state, path);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function readBackendPidState(path) {
|
|
224
|
+
const state = readJsonRuntimeRecord(path);
|
|
225
|
+
if (state.status === "absent") return state;
|
|
226
|
+
if (state.status !== "present" || !Number.isSafeInteger(state.value.pid) || state.value.pid <= 0) {
|
|
227
|
+
return { status: "unknown" };
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
process.kill(state.value.pid, 0);
|
|
231
|
+
return { status: "alive", pid: state.value.pid };
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (error?.code === "ESRCH") return { status: "dead", pid: state.value.pid };
|
|
234
|
+
if (error?.code === "EPERM") return { status: "alive", pid: state.value.pid };
|
|
235
|
+
return { status: "unknown", pid: state.value.pid };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function runLifecycleCommand(options) {
|
|
240
|
+
const spawn = options.spawnSyncImpl ?? spawnSync;
|
|
241
|
+
const result = spawn(process.execPath, [options.memoraxCodeBin, ...options.args], {
|
|
242
|
+
encoding: "utf8",
|
|
243
|
+
env: { ...process.env, ...options.env, MEMORAX_CODE_HOME: options.memoraxCodeHome },
|
|
244
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
245
|
+
timeout: positiveInteger(options.commandTimeoutMs, PACKAGE_TRANSITION_COMMAND_TIMEOUT_MS),
|
|
246
|
+
killSignal: "SIGKILL",
|
|
247
|
+
windowsHide: true,
|
|
248
|
+
});
|
|
249
|
+
if (result.error || result.signal || result.status !== 0) {
|
|
250
|
+
const detail = result.error?.code === "ETIMEDOUT"
|
|
251
|
+
? `${options.label} timed out after ${positiveInteger(options.commandTimeoutMs, PACKAGE_TRANSITION_COMMAND_TIMEOUT_MS)} ms`
|
|
252
|
+
: result.error?.message
|
|
253
|
+
?? (result.signal ? `${options.label} exited from signal ${result.signal}` : `${options.label} exited with status ${result.status ?? "unknown"}`);
|
|
254
|
+
throw transitionError("PACKAGE_TRANSITION_COMMAND_FAILED", detail, result);
|
|
255
|
+
}
|
|
256
|
+
let report;
|
|
257
|
+
try {
|
|
258
|
+
report = JSON.parse(String(result.stdout ?? ""));
|
|
259
|
+
} catch {
|
|
260
|
+
throw transitionError("PACKAGE_TRANSITION_COMMAND_INVALID_JSON", `${options.label} did not return valid JSON`, result);
|
|
261
|
+
}
|
|
262
|
+
if (!report || typeof report !== "object" || Array.isArray(report) || report.ok !== true) {
|
|
263
|
+
throw transitionError("PACKAGE_TRANSITION_COMMAND_NOT_OK", `${options.label} did not report ok=true`, result);
|
|
264
|
+
}
|
|
265
|
+
return { result, report };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function transitionError(code, message, command) {
|
|
269
|
+
const error = new Error(message);
|
|
270
|
+
error.name = "PackageTransitionError";
|
|
271
|
+
error.code = code;
|
|
272
|
+
if (command) error.command = command;
|
|
273
|
+
return error;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function backendPidPath(memoraxCodeHome) {
|
|
277
|
+
return join(memoraxCodeHome, "runtime", "backend", "backend.pid.json");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function defaultMemoraxCodeHome() {
|
|
281
|
+
return join(homedir(), ".memorax-code");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function requiredString(value, name) {
|
|
285
|
+
const result = nonEmptyString(value);
|
|
286
|
+
if (!result) throw new TypeError(`package transition requires ${name}`);
|
|
287
|
+
return result;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function nonEmptyString(value) {
|
|
291
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function isoTimestamp(value) {
|
|
295
|
+
const candidate = nonEmptyString(value);
|
|
296
|
+
return candidate && ISO_TIMESTAMP_PATTERN.test(candidate) && Number.isFinite(Date.parse(candidate))
|
|
297
|
+
? candidate
|
|
298
|
+
: undefined;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function positiveInteger(value, fallback) {
|
|
302
|
+
return Number.isSafeInteger(value) && value > 0 ? value : fallback;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function nowMs(options) {
|
|
306
|
+
return typeof options.now === "function" ? Number(options.now()) : Date.now();
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function nowIso(options) {
|
|
310
|
+
return new Date(nowMs(options)).toISOString();
|
|
311
|
+
}
|