@hellolynk-ai/codex-plugin 0.0.0-stage → 0.2.100
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/.agents/plugins/marketplace.json +20 -0
- package/LICENSE +92 -0
- package/README.md +101 -2
- package/bin/hellolynk-client-setup.mjs +2748 -0
- package/bin/hellolynk-mcp-config-shape.mjs +23 -0
- package/hellolynk-client-pack-manifest.json +619 -0
- package/hellolynk-client-pack-pin.json +4 -0
- package/package.json +58 -4
- package/plugins/hellolynk-codex/.app.json +3 -0
- package/plugins/hellolynk-codex/.codex-plugin/plugin.json +38 -0
- package/plugins/hellolynk-codex/.mcp.json +19 -0
- package/plugins/hellolynk-codex/assets/hellolynk-mark.png +0 -0
- package/plugins/hellolynk-codex/package.json +11 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-account.mjs +180 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-core.mjs +770 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-profile.mjs +191 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-host-auth.mjs +347 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-host-protected-store.mjs +2721 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-local-process-lock.mjs +157 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-neutral-host-installation-store.mjs +765 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-platform-credential-store.mjs +773 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-runtime-safety.cjs +471 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/platform/windows/hellolynk-host-protected-store.ps1 +2866 -0
- package/plugins/hellolynk-codex/runtime/connect-activate.mjs +837 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-agent-configurator.mjs +701 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-account.mjs +180 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-core.mjs +770 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-profile.mjs +191 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-entry.mjs +31 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-runtime.mjs +600 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update.mjs +453 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-contact-request-e2ee.mjs +867 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-device-enrollment.mjs +435 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-direct-text-e2ee-client.mjs +1964 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-direct-text-e2ee.mjs +1207 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-host-auth.mjs +347 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-host-protected-store.mjs +2721 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-host-transport-client.mjs +946 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-host.mjs +2374 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-human-confirmation-ledger.mjs +256 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-human-relationship-controller.mjs +1234 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-inbox-read-state.mjs +11 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-indexed-message-store.mjs +1558 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-instance-conversation.mjs +185 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-contact-requests.mjs +1733 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-contacts.mjs +1228 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-data-migration.mjs +766 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-data-protection.mjs +747 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-identity-trust.mjs +274 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-messaging.mjs +3043 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-peer-transport.mjs +103 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-process-lock.mjs +157 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-model-inbox.mjs +230 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-model-notices.cjs +60 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-addressing-tools.cjs +231 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-conversations.mjs +280 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-human-confirmation-broker.mjs +1036 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-inbox-runtime.mjs +553 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-notice.mjs +50 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-reply-routes.mjs +96 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-worker-lease.mjs +84 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-assembly.mjs +955 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-installation-store.mjs +765 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-launcher.mjs +665 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-process.mjs +4374 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-store-adapters.mjs +548 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-workbench-read-model.mjs +1365 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-platform-credential-store.mjs +773 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-relay-client-cli.mjs +232 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-release-retention.mjs +277 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-runtime-safety.cjs +471 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-workbench-action-grants.mjs +457 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-workbench-connection.mjs +234 -0
- package/plugins/hellolynk-codex/runtime/neutral-host-bootstrap.mjs +64 -0
- package/plugins/hellolynk-codex/runtime/neutral-host-release-entry.mjs +70 -0
- package/plugins/hellolynk-codex/runtime/neutral-host-release-loader.mjs +156 -0
- package/plugins/hellolynk-codex/runtime/neutral-host-release-manifest.json +358 -0
- package/plugins/hellolynk-codex/runtime/neutral-host-release-pin.json +4 -0
- package/plugins/hellolynk-codex/runtime/platform/windows/configure-agent.ps1 +281 -0
- package/plugins/hellolynk-codex/runtime/platform/windows/credential-store.ps1 +844 -0
- package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-host-protected-store.ps1 +2866 -0
- package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-human-confirmation-ledger.ps1 +155 -0
- package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-local-data-key.ps1 +300 -0
- package/plugins/hellolynk-codex/runtime/platform/windows/hellolynk-native-human-confirmation-broker.ps1 +216 -0
- package/plugins/hellolynk-codex/runtime/workbench/candidate-bootstrap.mjs +54 -0
- package/plugins/hellolynk-codex/runtime/workbench/contract.mjs +1314 -0
- package/plugins/hellolynk-codex/runtime/workbench/hellolynk-workbench-build.json +215 -0
- package/plugins/hellolynk-codex/runtime/workbench/host-capabilities.mjs +80 -0
- package/plugins/hellolynk-codex/runtime/workbench/i18n.mjs +1127 -0
- package/plugins/hellolynk-codex/runtime/workbench/loopback-surface.mjs +740 -0
- package/plugins/hellolynk-codex/runtime/workbench/mcp-app-bundle.mjs +97 -0
- package/plugins/hellolynk-codex/runtime/workbench/service.mjs +485 -0
- package/plugins/hellolynk-codex/runtime/workbench/state.mjs +421 -0
- package/plugins/hellolynk-codex/runtime/workbench/transports.mjs +171 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/app.mjs +6120 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/connection-form.mjs +155 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/hellolynk-mark.png +0 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/http-transport.mjs +247 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/arrow-left.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/close.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/contacts.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/inbox.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/refresh.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/search.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/security.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/icons/settings.svg +3 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/index.html +139 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/loopback-entry.mjs +26 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/mcp-app-client.mjs +151 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/mcp-app-entry.mjs +452 -0
- package/plugins/hellolynk-codex/runtime/workbench/ui/styles.css +686 -0
- package/plugins/hellolynk-codex/server/codex-neutral-production.mjs +520 -0
- package/plugins/hellolynk-codex/server/mcp-proxy.mjs +306 -0
- package/plugins/hellolynk-codex/server/mcp-server.mjs +1193 -0
- package/plugins/hellolynk-codex/server/native-conversation-directory.mjs +33 -0
- package/plugins/hellolynk-codex/server/native-reply-routes.mjs +32 -0
- package/plugins/hellolynk-codex/server/native-task-client.mjs +382 -0
- package/plugins/hellolynk-codex/server/native-task-runtime.mjs +8 -0
- package/plugins/hellolynk-codex/server/neutral-host-adapter.mjs +649 -0
- package/plugins/hellolynk-codex/server/workbench-runtime.mjs +180 -0
- package/plugins/hellolynk-codex/skills/hellolynk-open/SKILL.md +11 -0
- package/plugins/hellolynk-codex/skills/hellolynk-open/agents/openai.yaml +4 -0
- package/tools/hellolynk-activate-neutral-host-clients.mjs +837 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { lstat, mkdir, open, readFile, rename, rm, stat } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import {
|
|
6
|
+
assertCredentialProfileId,
|
|
7
|
+
runWindowsClientTrustCommand,
|
|
8
|
+
} from "./hellolynk-platform-credential-store.mjs";
|
|
9
|
+
import { normalizeHelloLynkBaseUrl } from "./hellolynk-client-core.mjs";
|
|
10
|
+
import runtimeSafety from "./hellolynk-runtime-safety.cjs";
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
containsHelloLynkCredentialMaterial,
|
|
14
|
+
normalizeHelloLynkClientProfileContainer,
|
|
15
|
+
} = runtimeSafety;
|
|
16
|
+
|
|
17
|
+
const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9_.:-]{2,127}$/;
|
|
18
|
+
const SAFE_SCOPE = /^[a-z][a-z0-9_.:-]{0,127}$/;
|
|
19
|
+
const MAX_PROFILE_BYTES = 64 * 1024;
|
|
20
|
+
|
|
21
|
+
export function defaultHelloLynkClientRoot(platform = process.platform, env = process.env) {
|
|
22
|
+
const home = resolve(String(env.HOME || env.USERPROFILE || homedir()));
|
|
23
|
+
if (platform === "win32") return resolve(String(env.LOCALAPPDATA || join(home, "AppData", "Local")), "HelloLynk");
|
|
24
|
+
if (platform === "darwin") return join(home, "Library", "Application Support", "HelloLynk");
|
|
25
|
+
return resolve(String(env.XDG_DATA_HOME || join(home, ".local", "share")), "hellolynk");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function safeId(value, code) {
|
|
29
|
+
const normalized = String(value || "").trim();
|
|
30
|
+
if (!SAFE_ID.test(normalized)) throw new Error(code);
|
|
31
|
+
return normalized;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function validateProfile(value) {
|
|
35
|
+
const allowedScopes = Array.isArray(value.allowedScopes) ? value.allowedScopes.map((scope) => {
|
|
36
|
+
const normalized = String(scope || "");
|
|
37
|
+
if (!SAFE_SCOPE.test(normalized)) throw new Error("client_profile_scope_invalid");
|
|
38
|
+
return normalized;
|
|
39
|
+
}) : [];
|
|
40
|
+
const schemaVersion = value.schemaVersion == null ? 1 : Number(value.schemaVersion);
|
|
41
|
+
if (![1, 2].includes(schemaVersion)) throw new Error("client_profile_schema_unsupported");
|
|
42
|
+
const delegateId = value.delegateId == null
|
|
43
|
+
? null : safeId(value.delegateId, "client_profile_delegate_id_invalid");
|
|
44
|
+
const hostId = value.hostId == null ? null : safeId(value.hostId, "client_profile_host_id_invalid");
|
|
45
|
+
const authorizationVersion = value.authorizationVersion == null ? null : Number(value.authorizationVersion);
|
|
46
|
+
if (
|
|
47
|
+
(schemaVersion === 1 && (hostId != null || authorizationVersion != null))
|
|
48
|
+
|| (schemaVersion === 2 && (
|
|
49
|
+
!delegateId || !hostId || !Number.isSafeInteger(authorizationVersion) || authorizationVersion < 1
|
|
50
|
+
))
|
|
51
|
+
) throw new Error("client_profile_route_binding_invalid");
|
|
52
|
+
return {
|
|
53
|
+
contract: value.contract,
|
|
54
|
+
schemaVersion,
|
|
55
|
+
profileId: assertCredentialProfileId(value.profileId),
|
|
56
|
+
baseUrl: normalizeHelloLynkBaseUrl(value.baseUrl, { allowLoopbackHttp: value.allowLoopbackHttp === true }),
|
|
57
|
+
allowLoopbackHttp: value.allowLoopbackHttp === true,
|
|
58
|
+
deviceId: safeId(value.deviceId, "client_profile_device_id_invalid"),
|
|
59
|
+
credentialId: safeId(value.credentialId, "client_profile_credential_id_invalid"),
|
|
60
|
+
principalId: safeId(value.principalId, "client_profile_principal_id_invalid"),
|
|
61
|
+
hellolynkId: value.hellolynkId == null ? null : safeId(value.hellolynkId, "client_profile_hellolynk_id_invalid"),
|
|
62
|
+
agentId: safeId(value.agentId, "client_profile_agent_id_invalid"),
|
|
63
|
+
delegateId,
|
|
64
|
+
...(schemaVersion === 2 ? { hostId, authorizationVersion } : {}),
|
|
65
|
+
region: safeId(value.region, "client_profile_region_invalid"),
|
|
66
|
+
allowedScopes,
|
|
67
|
+
platform: String(value.platform || process.platform),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function atomicWriteJson(path, value) {
|
|
72
|
+
await mkdir(dirname(path), { recursive: true });
|
|
73
|
+
const temporary = `${path}.${randomUUID()}.tmp`;
|
|
74
|
+
const body = `${JSON.stringify(value, null, 2)}\n`;
|
|
75
|
+
const handle = await open(temporary, "wx", 0o600);
|
|
76
|
+
try {
|
|
77
|
+
await handle.writeFile(body, "utf8");
|
|
78
|
+
await handle.sync();
|
|
79
|
+
} finally {
|
|
80
|
+
await handle.close();
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
await rename(temporary, path);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
await rm(temporary, { force: true });
|
|
86
|
+
throw error;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function windowsClientFile(path, category) {
|
|
91
|
+
const requested = String(path || "");
|
|
92
|
+
if (!requested || !isAbsolute(requested)) throw new Error("client_root_path_invalid");
|
|
93
|
+
const absolute = resolve(requested);
|
|
94
|
+
const parent = dirname(absolute);
|
|
95
|
+
const profileId = assertCredentialProfileId(basename(absolute, ".json"));
|
|
96
|
+
const clientRoot = dirname(parent);
|
|
97
|
+
if (
|
|
98
|
+
basename(parent).toLowerCase() !== category
|
|
99
|
+
|| absolute.toLowerCase() !== join(clientRoot, category, `${profileId}.json`).toLowerCase()
|
|
100
|
+
) throw new Error("client_root_path_invalid");
|
|
101
|
+
return { clientRoot, profileId };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function writeWindowsClientJson(path, category, command, value) {
|
|
105
|
+
const target = windowsClientFile(path, category);
|
|
106
|
+
await runWindowsClientTrustCommand(command, {
|
|
107
|
+
...target,
|
|
108
|
+
stdin: `${JSON.stringify(value, null, 2)}\n`,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function writeClientProfile(path, value) {
|
|
113
|
+
const original = JSON.stringify(value);
|
|
114
|
+
if (containsHelloLynkCredentialMaterial(original)) {
|
|
115
|
+
throw new Error("client_profile_contains_secret");
|
|
116
|
+
}
|
|
117
|
+
const container = normalizeHelloLynkClientProfileContainer(value);
|
|
118
|
+
const profile = validateProfile(container.value);
|
|
119
|
+
const serialized = JSON.stringify(profile);
|
|
120
|
+
if (containsHelloLynkCredentialMaterial(serialized)) {
|
|
121
|
+
throw new Error("client_profile_contains_secret");
|
|
122
|
+
}
|
|
123
|
+
if (process.platform === "win32") {
|
|
124
|
+
await writeWindowsClientJson(path, "profiles", "StoreProfile", profile);
|
|
125
|
+
} else {
|
|
126
|
+
await atomicWriteJson(path, profile);
|
|
127
|
+
}
|
|
128
|
+
return profile;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function readClientProfile(path) {
|
|
132
|
+
const body = await readClientProfileBody(path);
|
|
133
|
+
const container = normalizeHelloLynkClientProfileContainer(JSON.parse(body));
|
|
134
|
+
return validateProfile(container.value);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// A profile holds no secret, so Windows reads it directly too, avoiding
|
|
138
|
+
// one helper process per profile. Writes keep the helper.
|
|
139
|
+
async function readClientProfileBody(path) {
|
|
140
|
+
const windows = process.platform === "win32";
|
|
141
|
+
if (windows) windowsClientFile(path, "profiles");
|
|
142
|
+
let metadata;
|
|
143
|
+
try { metadata = await (windows ? lstat : stat)(path); }
|
|
144
|
+
catch (error) { throw windows && error?.code === "ENOENT" ? new Error("client_profile_missing") : error; }
|
|
145
|
+
if (!metadata.isFile()) throw new Error("client_profile_not_regular_file");
|
|
146
|
+
if (metadata.size > MAX_PROFILE_BYTES) throw new Error("client_profile_too_large");
|
|
147
|
+
const bytes = await readFile(path);
|
|
148
|
+
if (bytes.length > MAX_PROFILE_BYTES) throw new Error("client_profile_too_large");
|
|
149
|
+
let body;
|
|
150
|
+
try { body = new TextDecoder("utf-8", { fatal: true }).decode(bytes); }
|
|
151
|
+
catch { throw new Error("client_profile_invalid_utf8"); }
|
|
152
|
+
if (containsHelloLynkCredentialMaterial(body)) throw new Error("client_profile_contains_secret");
|
|
153
|
+
return body;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export async function writeSanitizedHostStatus(path, value) {
|
|
157
|
+
const runtimeSessionExpiresAt = value.runtimeSessionExpiresAt == null
|
|
158
|
+
? null
|
|
159
|
+
: new Date(value.runtimeSessionExpiresAt).toISOString();
|
|
160
|
+
const status = {
|
|
161
|
+
schemaVersion: 1,
|
|
162
|
+
checkedAt: new Date(value.checkedAt || Date.now()).toISOString(),
|
|
163
|
+
ok: value.ok === true,
|
|
164
|
+
serviceReachable: value.serviceReachable === true,
|
|
165
|
+
credentialPresent: value.credentialPresent === true,
|
|
166
|
+
runtimeSessionReady: value.runtimeSessionReady === true,
|
|
167
|
+
runtimeSessionExpiresAt,
|
|
168
|
+
messageReceiverReady: value.messageReceiverReady === true,
|
|
169
|
+
lastPollStatus: ["no_messages", "events_projected", "receiver_error"].includes(value.lastPollStatus)
|
|
170
|
+
? value.lastPollStatus
|
|
171
|
+
: null,
|
|
172
|
+
newEventCount: Number.isSafeInteger(Number(value.newEventCount)) && Number(value.newEventCount) >= 0
|
|
173
|
+
? Number(value.newEventCount)
|
|
174
|
+
: 0,
|
|
175
|
+
newNotificationCount: Number.isSafeInteger(Number(value.newNotificationCount)) && Number(value.newNotificationCount) >= 0
|
|
176
|
+
? Number(value.newNotificationCount)
|
|
177
|
+
: 0,
|
|
178
|
+
platform: String(value.platform || process.platform),
|
|
179
|
+
errorCode: value.errorCode == null ? null : String(value.errorCode),
|
|
180
|
+
};
|
|
181
|
+
const serialized = JSON.stringify(status);
|
|
182
|
+
if (containsHelloLynkCredentialMaterial(serialized)) {
|
|
183
|
+
throw new Error("host_status_contains_secret");
|
|
184
|
+
}
|
|
185
|
+
if (process.platform === "win32") {
|
|
186
|
+
await writeWindowsClientJson(path, "status", "StoreStatus", status);
|
|
187
|
+
} else {
|
|
188
|
+
await atomicWriteJson(path, status);
|
|
189
|
+
}
|
|
190
|
+
return status;
|
|
191
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { clientAccountBaseRoot } from "./hellolynk-client-account.mjs";
|
|
4
|
+
import { startClientUpdateRuntime } from "./hellolynk-client-update-runtime.mjs";
|
|
5
|
+
|
|
6
|
+
const running = new Map();
|
|
7
|
+
|
|
8
|
+
// Callers start this outside protocol discovery, then await it before attaching
|
|
9
|
+
// to the Host. A checkout or an uninstalled test fixture never self-updates.
|
|
10
|
+
export function startInstalledClientUpdate(options = {}) {
|
|
11
|
+
const { client, packageRoot, storeRoot } = options;
|
|
12
|
+
if (process.platform !== "win32" || !path.isAbsolute(String(packageRoot || ""))) return Promise.resolve(null);
|
|
13
|
+
let baseRoot;
|
|
14
|
+
try { baseRoot = clientAccountBaseRoot(storeRoot); } catch { return Promise.resolve(null); }
|
|
15
|
+
const key = `${client}\0${path.resolve(packageRoot)}\0${baseRoot}`;
|
|
16
|
+
if (!running.has(key)) running.set(key, start().catch(() => null));
|
|
17
|
+
return running.get(key);
|
|
18
|
+
|
|
19
|
+
async function start() {
|
|
20
|
+
const packageFile = path.join(packageRoot, "package.json");
|
|
21
|
+
const metadata = await fs.lstat(packageFile);
|
|
22
|
+
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.nlink !== 1 || metadata.size > 64 * 1024) return null;
|
|
23
|
+
const manifest = JSON.parse(await fs.readFile(packageFile, "utf8"));
|
|
24
|
+
if (manifest.hellolynkClient?.client !== client
|
|
25
|
+
|| !/^\d+\.\d+\.\d+$/.test(manifest.version || "")) return null;
|
|
26
|
+
return startClientUpdateRuntime({
|
|
27
|
+
root: baseRoot, client, currentVersion: manifest.version,
|
|
28
|
+
installContext: { ...options.installContext, packageRoot: path.resolve(packageRoot) },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|