@hellolynk-ai/codex-plugin 0.2.101 → 0.2.102-macos.1
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 +17 -17
- package/bin/hellolynk-client-setup.mjs +72 -11
- package/hellolynk-client-pack-manifest.json +109 -69
- package/hellolynk-client-pack-pin.json +1 -1
- package/package.json +8 -4
- package/plugins/hellolynk-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/hellolynk-codex/.mcp.json +20 -19
- package/plugins/hellolynk-codex/package.json +1 -1
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-account.mjs +1 -1
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-client-profile.mjs +25 -2
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-darwin-host.mjs +256 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-darwin-vault.mjs +153 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-host-protected-store.mjs +20 -8
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-neutral-host-installation-store.mjs +3 -7
- package/plugins/hellolynk-codex/runtime/bootstrap/core/hellolynk-platform-credential-store.mjs +68 -14
- package/plugins/hellolynk-codex/runtime/bootstrap/platform/darwin/hellolynk-keychain-helper.bin +0 -0
- package/plugins/hellolynk-codex/runtime/bootstrap/platform/darwin/hellolynk-keychain-helper.swift +149 -0
- package/plugins/hellolynk-codex/runtime/connect-activate.mjs +5 -1
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-account.mjs +1 -1
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-profile.mjs +25 -2
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-entry.mjs +7 -3
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update-runtime.mjs +24 -8
- package/plugins/hellolynk-codex/runtime/core/hellolynk-client-update.mjs +23 -9
- package/plugins/hellolynk-codex/runtime/core/hellolynk-darwin-host.mjs +256 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-darwin-vault.mjs +153 -0
- package/plugins/hellolynk-codex/runtime/core/hellolynk-host-protected-store.mjs +20 -8
- package/plugins/hellolynk-codex/runtime/core/hellolynk-human-confirmation-ledger.mjs +53 -6
- package/plugins/hellolynk-codex/runtime/core/hellolynk-local-data-protection.mjs +22 -7
- package/plugins/hellolynk-codex/runtime/core/hellolynk-native-human-confirmation-broker.mjs +7 -5
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-installation-store.mjs +3 -7
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-launcher.mjs +9 -3
- package/plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-process.mjs +1 -1
- package/plugins/hellolynk-codex/runtime/core/hellolynk-platform-credential-store.mjs +68 -14
- package/plugins/hellolynk-codex/runtime/core/hellolynk-release-retention.mjs +78 -2
- package/plugins/hellolynk-codex/runtime/core/hellolynk-workbench-connection.mjs +5 -3
- package/plugins/hellolynk-codex/runtime/neutral-host-release-entry.mjs +1 -1
- package/plugins/hellolynk-codex/runtime/neutral-host-release-loader.mjs +1 -1
- package/plugins/hellolynk-codex/runtime/neutral-host-release-manifest.json +58 -26
- package/plugins/hellolynk-codex/runtime/neutral-host-release-pin.json +1 -1
- package/plugins/hellolynk-codex/runtime/platform/darwin/hellolynk-keychain-helper.bin +0 -0
- package/plugins/hellolynk-codex/runtime/platform/darwin/hellolynk-keychain-helper.swift +149 -0
- package/plugins/hellolynk-codex/server/codex-neutral-production.mjs +15 -9
- package/plugins/hellolynk-codex/server/mcp-server.mjs +1193 -1193
- package/plugins/hellolynk-codex/server/native-conversation-directory.mjs +33 -33
- package/plugins/hellolynk-codex/server/workbench-runtime.mjs +42 -42
- package/tools/hellolynk-activate-neutral-host-clients.mjs +5 -1
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import os from "node:os";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
|
|
5
|
-
// The desktop's metadata index, read-only. Never read rollout bodies, previews,
|
|
6
|
-
// first_user_message or model summaries. Native read_thread still decides
|
|
7
|
-
// whether a selected conversation can actually receive a reminder.
|
|
8
|
-
export async function readCodexConversationDirectory({ home = process.env.CODEX_HOME || path.join(os.homedir(), ".codex"),
|
|
9
|
-
callerThreadId, projects = [], openDatabase } = {}) {
|
|
10
|
-
const names = (await fs.readdir(home)).filter(name => /^state_\d+\.sqlite$/.test(name))
|
|
11
|
-
.sort((a, b) => Number(b.match(/\d+/)[0]) - Number(a.match(/\d+/)[0]));
|
|
12
|
-
if (!names.length) throw new Error("codex_native_directory_unavailable");
|
|
13
|
-
const Database = openDatabase || ((await import("node:sqlite")).DatabaseSync);
|
|
14
|
-
const db = new Database(path.join(home, names[0]), { readOnly: true });
|
|
15
|
-
try {
|
|
16
|
-
const columns = new Set(db.prepare("PRAGMA table_info(threads)").all().map(row => row.name));
|
|
17
|
-
if (!["id", "title", "cwd", "source", "archived", "creator_account_id"].every(key => columns.has(key)))
|
|
18
|
-
throw new Error("codex_native_directory_schema_unavailable");
|
|
19
|
-
const owner = db.prepare("SELECT creator_account_id FROM threads WHERE id = ?").get(callerThreadId);
|
|
20
|
-
if (!owner) throw new Error("codex_native_directory_owner_unavailable");
|
|
21
|
-
const optional = ["name", "project_id"].filter(key => columns.has(key));
|
|
22
|
-
const rows = db.prepare(`SELECT id, cwd, archived${optional.length ? ", " + optional.join(", ") : ""}
|
|
23
|
-
FROM threads WHERE creator_account_id IS ? AND source IN ('vscode','cli','exec','appServer')`).all(owner.creator_account_id);
|
|
24
|
-
const byProject = new Map(projects.map(project => [project.id || project.projectId, project.name || project.title]));
|
|
25
|
-
return { coverage: "complete", exactTargetSupported: true, conversations: rows.map(row => ({
|
|
26
|
-
// In the native index, title can be the complete first user message.
|
|
27
|
-
// Only the explicit conversation name is metadata safe to export.
|
|
28
|
-
targetConversationId: row.id, title: row.name || row.id,
|
|
29
|
-
project: byProject.get(row.project_id) || path.win32.basename(row.cwd || "") || path.posix.basename(row.cwd || ""),
|
|
30
|
-
state: row.archived ? "archived" : "notLoaded",
|
|
31
|
-
})) };
|
|
32
|
-
} finally { db.close(); }
|
|
33
|
-
}
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
// The desktop's metadata index, read-only. Never read rollout bodies, previews,
|
|
6
|
+
// first_user_message or model summaries. Native read_thread still decides
|
|
7
|
+
// whether a selected conversation can actually receive a reminder.
|
|
8
|
+
export async function readCodexConversationDirectory({ home = process.env.CODEX_HOME || path.join(os.homedir(), ".codex"),
|
|
9
|
+
callerThreadId, projects = [], openDatabase } = {}) {
|
|
10
|
+
const names = (await fs.readdir(home)).filter(name => /^state_\d+\.sqlite$/.test(name))
|
|
11
|
+
.sort((a, b) => Number(b.match(/\d+/)[0]) - Number(a.match(/\d+/)[0]));
|
|
12
|
+
if (!names.length) throw new Error("codex_native_directory_unavailable");
|
|
13
|
+
const Database = openDatabase || ((await import("node:sqlite")).DatabaseSync);
|
|
14
|
+
const db = new Database(path.join(home, names[0]), { readOnly: true });
|
|
15
|
+
try {
|
|
16
|
+
const columns = new Set(db.prepare("PRAGMA table_info(threads)").all().map(row => row.name));
|
|
17
|
+
if (!["id", "title", "cwd", "source", "archived", "creator_account_id"].every(key => columns.has(key)))
|
|
18
|
+
throw new Error("codex_native_directory_schema_unavailable");
|
|
19
|
+
const owner = db.prepare("SELECT creator_account_id FROM threads WHERE id = ?").get(callerThreadId);
|
|
20
|
+
if (!owner) throw new Error("codex_native_directory_owner_unavailable");
|
|
21
|
+
const optional = ["name", "project_id"].filter(key => columns.has(key));
|
|
22
|
+
const rows = db.prepare(`SELECT id, cwd, archived${optional.length ? ", " + optional.join(", ") : ""}
|
|
23
|
+
FROM threads WHERE creator_account_id IS ? AND source IN ('vscode','cli','exec','appServer')`).all(owner.creator_account_id);
|
|
24
|
+
const byProject = new Map(projects.map(project => [project.id || project.projectId, project.name || project.title]));
|
|
25
|
+
return { coverage: "complete", exactTargetSupported: true, conversations: rows.map(row => ({
|
|
26
|
+
// In the native index, title can be the complete first user message.
|
|
27
|
+
// Only the explicit conversation name is metadata safe to export.
|
|
28
|
+
targetConversationId: row.id, title: row.name || row.id,
|
|
29
|
+
project: byProject.get(row.project_id) || path.win32.basename(row.cwd || "") || path.posix.basename(row.cwd || ""),
|
|
30
|
+
state: row.archived ? "archived" : "notLoaded",
|
|
31
|
+
})) };
|
|
32
|
+
} finally { db.close(); }
|
|
33
|
+
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
|
|
5
|
-
// Codex uses resource metadata before the app can report its intrinsic size.
|
|
6
|
-
// Keep a usable frame when its initial container limit is echoed by the app.
|
|
7
|
-
export const CODEX_WORKBENCH_RESOURCE_META = Object.freeze({
|
|
8
|
-
ui: Object.freeze({ prefersBorder: true, domain: "hellolynk.local" }),
|
|
9
|
-
"openai/widgetPrefersBorder": true,
|
|
10
|
-
"openai/widgetHeightHint": 680,
|
|
11
|
-
"openai/widgetMinFrameHeight": 680,
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
const CODEX_RESOURCE_URI_SEED = "ui://hellolynk/workbench-codex-pending.html";
|
|
15
|
-
const OUTPUT_SHA256_RE = /^[a-f0-9]{64}$/;
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
// Codex uses resource metadata before the app can report its intrinsic size.
|
|
6
|
+
// Keep a usable frame when its initial container limit is echoed by the app.
|
|
7
|
+
export const CODEX_WORKBENCH_RESOURCE_META = Object.freeze({
|
|
8
|
+
ui: Object.freeze({ prefersBorder: true, domain: "hellolynk.local" }),
|
|
9
|
+
"openai/widgetPrefersBorder": true,
|
|
10
|
+
"openai/widgetHeightHint": 680,
|
|
11
|
+
"openai/widgetMinFrameHeight": 680,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const CODEX_RESOURCE_URI_SEED = "ui://hellolynk/workbench-codex-pending.html";
|
|
15
|
+
const OUTPUT_SHA256_RE = /^[a-f0-9]{64}$/;
|
|
16
16
|
const SHARED_ROOTS = Object.freeze([
|
|
17
17
|
new URL("../runtime/workbench/", import.meta.url),
|
|
18
18
|
new URL("../../../clients/workbench/", import.meta.url),
|
|
@@ -38,14 +38,14 @@ export async function bootstrapCodexWorkbenchRuntime(options = {}) {
|
|
|
38
38
|
|| typeof shared.validateWorkbenchCommandResult !== "function") {
|
|
39
39
|
throw codeError("workbench_codex_shared_bootstrap_invalid");
|
|
40
40
|
}
|
|
41
|
-
const explicitResourceUri = options.resourceUri == null ? null : options.resourceUri;
|
|
42
|
-
const bootstrapped = await shared.bootstrapWorkbenchCandidate("codex", {
|
|
43
|
-
resourceUri: explicitResourceUri || CODEX_RESOURCE_URI_SEED,
|
|
44
|
-
});
|
|
45
|
-
const resourceUri = explicitResourceUri || codexWorkbenchResourceUri(bootstrapped);
|
|
46
|
-
const candidate = resourceUri === bootstrapped.resourceUri
|
|
47
|
-
? bootstrapped
|
|
48
|
-
: Object.freeze({ ...bootstrapped, resourceUri });
|
|
41
|
+
const explicitResourceUri = options.resourceUri == null ? null : options.resourceUri;
|
|
42
|
+
const bootstrapped = await shared.bootstrapWorkbenchCandidate("codex", {
|
|
43
|
+
resourceUri: explicitResourceUri || CODEX_RESOURCE_URI_SEED,
|
|
44
|
+
});
|
|
45
|
+
const resourceUri = explicitResourceUri || codexWorkbenchResourceUri(bootstrapped);
|
|
46
|
+
const candidate = resourceUri === bootstrapped.resourceUri
|
|
47
|
+
? bootstrapped
|
|
48
|
+
: Object.freeze({ ...bootstrapped, resourceUri });
|
|
49
49
|
if (options.adapter === undefined) return candidate;
|
|
50
50
|
if (typeof options.adapter?.readWorkbenchSnapshot !== "function") {
|
|
51
51
|
throw codeError("workbench_codex_rpc_adapter_invalid");
|
|
@@ -58,9 +58,9 @@ export async function bootstrapCodexWorkbenchRuntime(options = {}) {
|
|
|
58
58
|
const readOnlyCandidate = {
|
|
59
59
|
resourceUri: candidate.resourceUri,
|
|
60
60
|
html: candidate.html,
|
|
61
|
-
readSnapshot: (input = {}) => options.adapter.readWorkbenchSnapshot(input),
|
|
62
|
-
...(typeof options.adapter.readWorkbenchChanges === "function"
|
|
63
|
-
? { readChanges: (input) => options.adapter.readWorkbenchChanges(input) } : {}),
|
|
61
|
+
readSnapshot: (input = {}) => options.adapter.readWorkbenchSnapshot(input),
|
|
62
|
+
...(typeof options.adapter.readWorkbenchChanges === "function"
|
|
63
|
+
? { readChanges: (input) => options.adapter.readWorkbenchChanges(input) } : {}),
|
|
64
64
|
...(typeof options.connectAccount === "function" ? { connectAccount: options.connectAccount } : {}),
|
|
65
65
|
validateReadRequest: shared.validateWorkbenchReadRequest,
|
|
66
66
|
validateSnapshot: shared.validateWorkbenchSnapshot,
|
|
@@ -82,24 +82,24 @@ export async function bootstrapCodexWorkbenchRuntime(options = {}) {
|
|
|
82
82
|
validateCommand: shared.validateWorkbenchCommand,
|
|
83
83
|
validateCommandResult: shared.validateWorkbenchCommandResult,
|
|
84
84
|
});
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function codexWorkbenchResourceUri(candidate) {
|
|
88
|
-
const outputSha256 = String(candidate?.manifest?.outputSha256 || "");
|
|
89
|
-
if (!OUTPUT_SHA256_RE.test(outputSha256)) throw codeError("workbench_codex_output_sha256_invalid");
|
|
90
|
-
// Metadata changes must also invalidate Codex's cached resource.
|
|
91
|
-
const resourceSha256 = createHash("sha256")
|
|
92
|
-
.update(JSON.stringify({ outputSha256, meta: CODEX_WORKBENCH_RESOURCE_META }))
|
|
93
|
-
.digest("hex");
|
|
94
|
-
return `ui://hellolynk/workbench-codex-${resourceSha256}.html`;
|
|
95
|
-
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function codexWorkbenchResourceUri(candidate) {
|
|
88
|
+
const outputSha256 = String(candidate?.manifest?.outputSha256 || "");
|
|
89
|
+
if (!OUTPUT_SHA256_RE.test(outputSha256)) throw codeError("workbench_codex_output_sha256_invalid");
|
|
90
|
+
// Metadata changes must also invalidate Codex's cached resource.
|
|
91
|
+
const resourceSha256 = createHash("sha256")
|
|
92
|
+
.update(JSON.stringify({ outputSha256, meta: CODEX_WORKBENCH_RESOURCE_META }))
|
|
93
|
+
.digest("hex");
|
|
94
|
+
return `ui://hellolynk/workbench-codex-${resourceSha256}.html`;
|
|
95
|
+
}
|
|
96
96
|
|
|
97
97
|
function supportsF3ActionGrantTransport(shared, candidate, adapter) {
|
|
98
|
-
return shared.HELLOLYNK_WORKBENCH_SNAPSHOT_CONTRACT === "hellolynk-workbench-snapshot-v12"
|
|
99
|
-
&& shared.HELLOLYNK_WORKBENCH_SNAPSHOT_SCHEMA_VERSION === 12
|
|
100
|
-
&& shared.HELLOLYNK_WORKBENCH_COMMAND_CONTRACT === "hellolynk-workbench-command-v6"
|
|
101
|
-
&& shared.HELLOLYNK_WORKBENCH_RESULT_CONTRACT === "hellolynk-workbench-command-result-v6"
|
|
102
|
-
&& shared.HELLOLYNK_WORKBENCH_SCHEMA_VERSION === 6
|
|
98
|
+
return shared.HELLOLYNK_WORKBENCH_SNAPSHOT_CONTRACT === "hellolynk-workbench-snapshot-v12"
|
|
99
|
+
&& shared.HELLOLYNK_WORKBENCH_SNAPSHOT_SCHEMA_VERSION === 12
|
|
100
|
+
&& shared.HELLOLYNK_WORKBENCH_COMMAND_CONTRACT === "hellolynk-workbench-command-v6"
|
|
101
|
+
&& shared.HELLOLYNK_WORKBENCH_RESULT_CONTRACT === "hellolynk-workbench-command-result-v6"
|
|
102
|
+
&& shared.HELLOLYNK_WORKBENCH_SCHEMA_VERSION === 6
|
|
103
103
|
&& candidate?.manifest?.sourceContract === shared.HELLOLYNK_WORKBENCH_SNAPSHOT_CONTRACT
|
|
104
104
|
&& typeof candidate.html === "string"
|
|
105
105
|
&& candidate.html.includes("hellolynk_workbench_action_grant")
|
|
@@ -5,6 +5,7 @@ import crypto from "node:crypto";
|
|
|
5
5
|
import fs from "node:fs/promises";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
|
|
8
|
+
import { verifyDarwinUnclaimedAuthorization } from "../plugins/hellolynk-codex/runtime/core/hellolynk-darwin-host.mjs";
|
|
8
9
|
import { readClientProfile } from "../plugins/hellolynk-codex/runtime/core/hellolynk-client-profile.mjs";
|
|
9
10
|
import { createNeutralHostInstallationStore } from "../plugins/hellolynk-codex/runtime/core/hellolynk-neutral-host-installation-store.mjs";
|
|
10
11
|
import { clientAccountBaseRoot } from "../plugins/hellolynk-codex/runtime/core/hellolynk-client-account.mjs";
|
|
@@ -164,7 +165,7 @@ assert.equal(releaseVerification.verifiedFileCount >= 8, true);
|
|
|
164
165
|
let transport = null;
|
|
165
166
|
let bootstrapTransportRequired = false;
|
|
166
167
|
let protectedStorePresent = true;
|
|
167
|
-
try { await fs.lstat(path.join(root, "capabilities-v1.dpapi.clixml")); }
|
|
168
|
+
try { await fs.lstat(path.join(root, process.platform === "darwin" ? "protected-v1.sqlite" : "capabilities-v1.dpapi.clixml")); }
|
|
168
169
|
catch (error) {
|
|
169
170
|
if (error?.code !== "ENOENT") throw error;
|
|
170
171
|
protectedStorePresent = false;
|
|
@@ -173,6 +174,9 @@ if (!protectedStorePresent) {
|
|
|
173
174
|
if (!(freshPreBind || recoveryPreBind)) await store.status();
|
|
174
175
|
await verifyHostProtectedStorePath({ root, path: root, directory: true });
|
|
175
176
|
bootstrapTransportRequired = true;
|
|
177
|
+
} else if (process.platform === "darwin" && freshPreBind) {
|
|
178
|
+
await verifyDarwinUnclaimedAuthorization(root);
|
|
179
|
+
bootstrapTransportRequired = true;
|
|
176
180
|
} else {
|
|
177
181
|
assert.equal((await store.status()).protected, true);
|
|
178
182
|
try { transport = preflightOnly ? await store.verifyTransport() : await store.resolveTransport(); }
|