@optimuslabs/harness-map-staging 1.5.16 → 1.5.18

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.
@@ -1,10 +1,20 @@
1
1
  import { existsSync, readdirSync, statSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { readJSONFile, readMarkdownFile } from '../readers/file_readers.js';
4
+ import { collectSkillMdRecord, isSkillDirEntry } from './skill_symlink.js';
4
5
  function collectSubdirMdFiles(dirPath, fileType, mdFilename, source) {
5
6
  const results = [];
7
+ const isSkillMd = mdFilename.toLowerCase() === 'skill.md';
6
8
  try {
7
9
  for (const entry of readdirSync(dirPath, { withFileTypes: true })) {
10
+ if (isSkillMd) {
11
+ if (!isSkillDirEntry(entry))
12
+ continue;
13
+ const record = collectSkillMdRecord(join(dirPath, entry.name), fileType, source, mdFilename);
14
+ if (record)
15
+ results.push(record);
16
+ continue;
17
+ }
8
18
  if (!entry.isDirectory())
9
19
  continue;
10
20
  const mdPath = join(dirPath, entry.name, mdFilename);
@@ -5,6 +5,7 @@ import { createHash } from 'node:crypto';
5
5
  import { readJSONFile, readMarkdownFile } from '../readers/file_readers.js';
6
6
  import { getInstalledPluginsPath, getPluginHooksPath, getPluginMcpPaths, getPluginSkillFilename, getPluginSkillsDir } from '../paths/path_constants_helpers.js';
7
7
  import { versionFromPluginCachePath, parseInstalledPluginKey } from './plugin_version_helpers.js';
8
+ import { collectSkillMdRecord, isSkillDirEntry, symlinkInfoAllowsExtraFiles } from './skill_symlink.js';
8
9
  /** Directories never worth shipping: git history is huge and already-compressed, deps are not the skill. */
9
10
  const SKILL_SKIP_DIRS = new Set(['.git', 'node_modules']);
10
11
  /** Fonts are the single largest thing in skill folders (canvas-design ships 54) and carry no signal. */
@@ -53,7 +54,9 @@ function collectSkillDirFiles(dir, results, constants, skillFilename, budget, de
53
54
  collectSkillDirFiles(full, results, constants, skillFilename, budget, depth + 1);
54
55
  continue;
55
56
  }
56
- // Symlinks report isSymbolicLink(), not isFile(), so this also keeps the walk inside the skill dir.
57
+ if (ent.name === skillFilename)
58
+ continue;
59
+ // Extra skill files: skip nested symlinks so the walk stays inside this skill dir.
57
60
  if (!ent.isFile())
58
61
  continue;
59
62
  const ext = extname(ent.name).toLowerCase();
@@ -100,11 +103,16 @@ function collectSkillDirFiles(dir, results, constants, skillFilename, budget, de
100
103
  function collectSkillFiles(skillsDir, results, constants) {
101
104
  const skillFilename = getPluginSkillFilename(constants);
102
105
  try {
103
- for (const d of readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory())) {
106
+ for (const d of readdirSync(skillsDir, { withFileTypes: true }).filter((d) => isSkillDirEntry(d))) {
104
107
  const skillRoot = join(skillsDir, d.name);
105
- if (!existsSync(join(skillRoot, skillFilename)))
108
+ const ver = versionFromPluginCachePath(join(skillRoot, skillFilename), constants);
109
+ const record = collectSkillMdRecord(skillRoot, 'claude_skill', 'file', skillFilename, ver ? { version: ver } : {});
110
+ if (!record)
111
+ continue;
112
+ results.push(record);
113
+ if (!symlinkInfoAllowsExtraFiles(record.symlink_info))
106
114
  continue;
107
- const budget = { files: 0, bytes: 0 };
115
+ const budget = { files: 1, bytes: 0 };
108
116
  collectSkillDirFiles(skillRoot, results, constants, skillFilename, budget, 0);
109
117
  if (budget.files >= SKILL_MAX_FILES || budget.bytes >= SKILL_MAX_TOTAL_BYTES) {
110
118
  console.warn(`Skill ${skillRoot} hit collection cap (${budget.files} files, ${budget.bytes} bytes)`);
@@ -0,0 +1,191 @@
1
+ import { accessSync, constants, lstatSync, readlinkSync, realpathSync } from 'node:fs';
2
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
3
+ import { readMarkdownFile } from '../readers/file_readers.js';
4
+ function isMissingPathError(err) {
5
+ const code = err?.code;
6
+ return code === 'ENOENT' || code === 'ENOTDIR';
7
+ }
8
+ /**
9
+ * Resolve readlink's raw target the way the OS does when parent dirs include
10
+ * symlinks. A lexical join against the logical parent is wrong when that parent
11
+ * is itself a link (e.g. ~/.config → a dotfiles repo).
12
+ */
13
+ export function resolveRawSymlinkTarget(linkPath, target) {
14
+ if (isAbsolute(target))
15
+ return resolve(target);
16
+ const physicalParent = realpathSync(dirname(linkPath));
17
+ return resolve(physicalParent, target);
18
+ }
19
+ function probeAccess(path) {
20
+ try {
21
+ accessSync(path, constants.F_OK);
22
+ return 'valid';
23
+ }
24
+ catch (err) {
25
+ return isMissingPathError(err) ? 'broken' : 'inaccessible';
26
+ }
27
+ }
28
+ function readResolvedSymlinkTarget(linkPath) {
29
+ try {
30
+ const raw = readlinkSync(linkPath);
31
+ const target = resolveRawSymlinkTarget(linkPath, raw);
32
+ return { target, status: probeAccess(target) };
33
+ }
34
+ catch (err) {
35
+ if (err?.code === 'EINVAL') {
36
+ return { target: null, status: 'valid' };
37
+ }
38
+ return { target: null, status: isMissingPathError(err) ? 'broken' : 'inaccessible' };
39
+ }
40
+ }
41
+ function asOverall(status) {
42
+ return status === 'missing' ? 'broken' : status;
43
+ }
44
+ function worseStatus(current, next) {
45
+ const rank = { valid: 0, broken: 1, inaccessible: 2 };
46
+ return rank[next] > rank[current] ? next : current;
47
+ }
48
+ function emptyPath(path, status = 'valid') {
49
+ return { path, is_symlink: false, points_to: null, status };
50
+ }
51
+ /** True for a skills-dir child that is a real folder or a symlink slot. */
52
+ export function isSkillDirEntry(entry) {
53
+ return entry.isDirectory() || Boolean(entry.isSymbolicLink?.());
54
+ }
55
+ /**
56
+ * Inspect one agent skill slot: the folder (local vs symlink) and SKILL.md inside it.
57
+ */
58
+ export function inspectSkillLink(skillDir, skillFilename = 'SKILL.md') {
59
+ const skillMdPath = join(skillDir, skillFilename);
60
+ const skill_folder = emptyPath(skillDir);
61
+ const skill_md = emptyPath(skillMdPath);
62
+ try {
63
+ const dirStat = lstatSync(skillDir);
64
+ if (dirStat.isSymbolicLink()) {
65
+ skill_folder.is_symlink = true;
66
+ const resolved = readResolvedSymlinkTarget(skillDir);
67
+ skill_folder.points_to = resolved.target;
68
+ skill_folder.status = resolved.status;
69
+ }
70
+ else if (!dirStat.isDirectory()) {
71
+ skill_folder.status = 'broken';
72
+ }
73
+ }
74
+ catch (err) {
75
+ skill_folder.status = isMissingPathError(err) ? 'broken' : 'inaccessible';
76
+ skill_md.status = 'missing';
77
+ return {
78
+ status: asOverall(skill_folder.status),
79
+ skill_folder,
80
+ skill_md,
81
+ };
82
+ }
83
+ try {
84
+ const mdStat = lstatSync(skillMdPath);
85
+ if (mdStat.isSymbolicLink()) {
86
+ skill_md.is_symlink = true;
87
+ const resolved = readResolvedSymlinkTarget(skillMdPath);
88
+ skill_md.points_to = resolved.target;
89
+ skill_md.status = resolved.status;
90
+ }
91
+ }
92
+ catch (err) {
93
+ skill_md.status = isMissingPathError(err) ? 'missing' : 'inaccessible';
94
+ }
95
+ return {
96
+ status: worseStatus(asOverall(skill_folder.status), asOverall(skill_md.status)),
97
+ skill_folder,
98
+ skill_md,
99
+ };
100
+ }
101
+ function symlinkEntry(info) {
102
+ return {
103
+ path: info.path,
104
+ points_to: info.points_to,
105
+ status: info.status === 'missing' ? 'broken' : info.status,
106
+ };
107
+ }
108
+ /** Only include the path that is actually a symlink. No symlink → `{ symlink_enabled: false }`. */
109
+ export function skillLinkPayload(info) {
110
+ const symlink_info = { symlink_enabled: false };
111
+ if (info.skill_folder.is_symlink) {
112
+ symlink_info.symlink_enabled = true;
113
+ symlink_info.skill_folder = symlinkEntry(info.skill_folder);
114
+ }
115
+ if (info.skill_md.is_symlink) {
116
+ symlink_info.symlink_enabled = true;
117
+ symlink_info.skill_md = symlinkEntry(info.skill_md);
118
+ }
119
+ return symlink_info;
120
+ }
121
+ export function symlinkInfoAllowsExtraFiles(info) {
122
+ if (!info || typeof info !== 'object')
123
+ return true;
124
+ const payload = info;
125
+ if (!payload.symlink_enabled)
126
+ return true;
127
+ for (const entry of [payload.skill_folder, payload.skill_md]) {
128
+ if (entry?.status && entry.status !== 'valid')
129
+ return false;
130
+ }
131
+ return true;
132
+ }
133
+ function shouldEmitSkillSlot(info, skillFilename) {
134
+ if (info.skill_folder.is_symlink)
135
+ return true;
136
+ if (info.status === 'inaccessible')
137
+ return true;
138
+ if (info.skill_md.is_symlink)
139
+ return true;
140
+ try {
141
+ accessSync(join(info.skill_folder.path, skillFilename), constants.F_OK);
142
+ return true;
143
+ }
144
+ catch {
145
+ return false;
146
+ }
147
+ }
148
+ function unknownInspectFailure(skillDir, skillFilename, err) {
149
+ const status = isMissingPathError(err) ? 'broken' : 'inaccessible';
150
+ return {
151
+ status,
152
+ skill_folder: emptyPath(skillDir, status),
153
+ skill_md: emptyPath(join(skillDir, skillFilename), 'missing'),
154
+ };
155
+ }
156
+ /**
157
+ * Collect one SKILL.md inventory row, including symlink metadata even when the
158
+ * file is missing, dangling, or unreadable.
159
+ */
160
+ export function collectSkillMdRecord(skillDir, fileType, source, skillFilename = 'SKILL.md', extraRaw = {}) {
161
+ let info;
162
+ try {
163
+ info = inspectSkillLink(skillDir, skillFilename);
164
+ }
165
+ catch (err) {
166
+ info = unknownInspectFailure(skillDir, skillFilename, err);
167
+ }
168
+ if (!shouldEmitSkillSlot(info, skillFilename))
169
+ return null;
170
+ const mdPath = join(skillDir, skillFilename);
171
+ const raw = {
172
+ source,
173
+ ...extraRaw,
174
+ };
175
+ const symlink_info = skillLinkPayload(info);
176
+ if (info.status === 'valid') {
177
+ const content = readMarkdownFile(mdPath);
178
+ if (content !== null) {
179
+ raw.content = content;
180
+ }
181
+ else if (symlink_info.symlink_enabled) {
182
+ const skill_md = symlink_info.skill_md;
183
+ const skill_folder = symlink_info.skill_folder;
184
+ if (skill_md)
185
+ skill_md.status = 'inaccessible';
186
+ else if (skill_folder)
187
+ skill_folder.status = 'inaccessible';
188
+ }
189
+ }
190
+ return { file_type: fileType, file_path: mdPath, raw_content: raw, symlink_info };
191
+ }
@@ -0,0 +1,57 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ /** Org-pushed endpoint config. Prompt collection and later org options share this file. */
5
+ const ORG_CONFIG_RELATIVE_PATH = path.join(".optimuslabs", "management", "org", "config.json");
6
+ export function orgConfigPath() {
7
+ return path.join(os.homedir(), ORG_CONFIG_RELATIVE_PATH);
8
+ }
9
+ function readOrgConfig() {
10
+ try {
11
+ const parsed = JSON.parse(fs.readFileSync(orgConfigPath(), "utf8"));
12
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
13
+ return parsed;
14
+ }
15
+ }
16
+ catch {
17
+ // Missing or invalid — treat as empty.
18
+ }
19
+ return {};
20
+ }
21
+ function collectPromptsFromConfig(cfg) {
22
+ if (typeof cfg.collect_prompts === "boolean")
23
+ return cfg.collect_prompts;
24
+ // Previous on-disk key was inverted (true = hash / don't collect).
25
+ if (typeof cfg.hash_tool_call_prompts === "boolean")
26
+ return cfg.hash_tool_call_prompts !== true;
27
+ // No org config yet: match server default (collection off).
28
+ return false;
29
+ }
30
+ export function readCollectPrompts() {
31
+ return collectPromptsFromConfig(readOrgConfig());
32
+ }
33
+ export function persistCollectPrompts(value) {
34
+ if (typeof value !== "boolean")
35
+ return;
36
+ const filePath = orgConfigPath();
37
+ try {
38
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
39
+ const next = { ...readOrgConfig(), collect_prompts: value };
40
+ delete next.hash_tool_call_prompts;
41
+ fs.writeFileSync(filePath, `${JSON.stringify(next)}\n`, { encoding: "utf8", mode: 0o600 });
42
+ }
43
+ catch {
44
+ // Best-effort: next heartbeat can retry.
45
+ }
46
+ }
47
+ export function persistCollectPromptsFromResponse(body) {
48
+ if (!body || typeof body !== "object")
49
+ return;
50
+ if (typeof body.collect_prompts === "boolean") {
51
+ persistCollectPrompts(body.collect_prompts);
52
+ return;
53
+ }
54
+ if (typeof body.hash_tool_call_prompts === "boolean") {
55
+ persistCollectPrompts(body.hash_tool_call_prompts !== true);
56
+ }
57
+ }
@@ -551,7 +551,7 @@ function assertSafeSqliteIdentifiersForItemTable(table, keyColumn, valueColumn)
551
551
  const TRUSTED_CURSOR_SQLITE_DEFERRED_RESTART_COMMAND = 'REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) && export REPO_ROOT && ' +
552
552
  'CURSOR_PROJECT="${CURSOR_PROJECT_DIR:-$REPO_ROOT}" && export CURSOR_PROJECT && ' +
553
553
  'OPTIMUS_DEFERRED_LOG="${HOME}/.optimuslabs/management/vscdb/deferred_vscdb_restart.log" && mkdir -p "$(dirname "$OPTIMUS_DEFERRED_LOG")" && export OPTIMUS_DEFERRED_LOG && ' +
554
- "nohup bash -c 'exec >>\"\$OPTIMUS_DEFERRED_LOG\" 2>&1; echo deferred_restart:begin ts=\$(date -u +%Y-%m-%dT%H:%M:%SZ) REPO_ROOT=\"\$REPO_ROOT\" CURSOR_PROJECT=\"\$CURSOR_PROJECT\"; sleep 2; STATIC_DEV_ENV=\"\$HOME/.optimuslabs/management/optimus_dev.env\"; ENV_LABEL=\"\"; if [ -f \"\$STATIC_DEV_ENV\" ]; then ENV_LABEL=\$(grep -E \"^(environment|ENVIRONMENT|OPTIMUS_ENVIRONMENT)=\" \"\$STATIC_DEV_ENV\" 2>/dev/null | head -1 | cut -d\"=\" -f2- | tr \"[:upper:]\" \"[:lower:]\" | xargs); fi; if [ -z \"\$ENV_LABEL\" ] && [ -n \"\${OPTIMUS_ENVIRONMENT:-}\" ]; then ENV_LABEL=\$(printf \"%s\" \"\$OPTIMUS_ENVIRONMENT\" | tr \"[:upper:]\" \"[:lower:]\" | xargs); fi; [ -z \"\$ENV_LABEL\" ] && ENV_LABEL=\"production\"; NPX_BASE=\"\"; if [ -f \"\$STATIC_DEV_ENV\" ]; then _nb=\$(grep -E \"^npx=\" \"\$STATIC_DEV_ENV\" 2>/dev/null | head -1 | cut -d\"=\" -f2- | xargs); [ -n \"\$_nb\" ] && [ -d \"\$_nb\" ] && NPX_BASE=\"\$_nb\"; fi; p=\"\${npm_config_prefix:-\${NPM_CONFIG_PREFIX:-}}\"; gp=\"\${npm_config_global_prefix:-}\"; gc=\"\${npm_config_globalconfig:-}\"; if [[ \"\$p\" == *\"/Applications/Cursor.app/\"* ]] || [[ \"\$gp\" == *\"/Applications/Cursor.app/\"* ]] || [[ \"\$gc\" == *\"/Applications/Cursor.app/\"* ]]; then unset npm_config_prefix NPM_CONFIG_PREFIX npm_config_global_prefix npm_config_globalconfig npm_node_execpath 2>/dev/null; fi; APPLY_EC=0; if [ -f \"\$REPO_ROOT/dev_npx_packages/log-llm-config/dist/apply_deferred_vscdb.js\" ]; then echo deferred_restart:apply_via_dev_npx_packages; node \"\$REPO_ROOT/dev_npx_packages/log-llm-config/dist/apply_deferred_vscdb.js\"; APPLY_EC=\$?; elif [ \"\$ENV_LABEL\" = \"development\" ] && [ -n \"\$NPX_BASE\" ] && [ -f \"\$NPX_BASE/optimuslabs/log-llm-config/dist/apply_deferred_vscdb.js\" ]; then echo deferred_restart:apply_via_optimus_npx_base; node \"\$NPX_BASE/optimuslabs/log-llm-config/dist/apply_deferred_vscdb.js\"; APPLY_EC=\$?; elif command -v npx >/dev/null 2>&1; then echo deferred_restart:apply_via_npx env=\"\$ENV_LABEL\"; cd \"\$REPO_ROOT\" || true; if [ \"\$ENV_LABEL\" = \"staging\" ]; then npx --yes --package=@optimuslabs/harness-map-staging@latest apply-deferred-vscdb-staging; APPLY_EC=\$?; else npx --yes --package=@optimuslabs/harness-map@latest apply-deferred-vscdb; APPLY_EC=\$?; fi; else echo deferred_restart:no_npx; APPLY_EC=127; fi; echo deferred_restart:apply_exit=\$APPLY_EC; if [ \$APPLY_EC -ne 0 ]; then echo deferred_restart:APPLY_FAILED_see_messages_above; fi; echo deferred_restart:open_cursor; env -u npm_config_package -u npm_lifecycle_event -u npm_lifecycle_script -u npm_config_local_prefix open -a Cursor \"\$CURSOR_PROJECT\"; echo deferred_restart:open_exit=\$?; echo deferred_restart:end ts=\$(date -u +%Y-%m-%dT%H:%M:%SZ)' >/dev/null 2>&1 & killall -9 Cursor";
554
+ "nohup bash -c 'exec >>\"\$OPTIMUS_DEFERRED_LOG\" 2>&1; echo deferred_restart:begin ts=\$(date -u +%Y-%m-%dT%H:%M:%SZ) REPO_ROOT=\"\$REPO_ROOT\" CURSOR_PROJECT=\"\$CURSOR_PROJECT\"; sleep 2; STATIC_DEV_ENV=\"\$HOME/.optimuslabs/management/optimus_dev.env\"; ENV_LABEL=\"\"; if [ -f \"\$STATIC_DEV_ENV\" ]; then ENV_LABEL=\$(grep -E \"^(environment|ENVIRONMENT|OPTIMUS_ENVIRONMENT)=\" \"\$STATIC_DEV_ENV\" 2>/dev/null | head -1 | cut -d\"=\" -f2- | tr \"[:upper:]\" \"[:lower:]\" | xargs); fi; if [ -z \"\$ENV_LABEL\" ] && [ -n \"\${OPTIMUS_ENVIRONMENT:-}\" ]; then ENV_LABEL=\$(printf \"%s\" \"\$OPTIMUS_ENVIRONMENT\" | tr \"[:upper:]\" \"[:lower:]\" | xargs); fi; [ -z \"\$ENV_LABEL\" ] && ENV_LABEL=\"production\"; NPX_BASE=\"\"; if [ -f \"\$STATIC_DEV_ENV\" ]; then _nb=\$(grep -E \"^npx=\" \"\$STATIC_DEV_ENV\" 2>/dev/null | head -1 | cut -d\"=\" -f2- | xargs); [ -n \"\$_nb\" ] && [ -d \"\$_nb\" ] && NPX_BASE=\"\$_nb\"; fi; p=\"\${npm_config_prefix:-\${NPM_CONFIG_PREFIX:-}}\"; gp=\"\${npm_config_global_prefix:-}\"; gc=\"\${npm_config_globalconfig:-}\"; if [[ \"\$p\" == *\"/Applications/Cursor.app/\"* ]] || [[ \"\$gp\" == *\"/Applications/Cursor.app/\"* ]] || [[ \"\$gc\" == *\"/Applications/Cursor.app/\"* ]]; then unset npm_config_prefix NPM_CONFIG_PREFIX npm_config_global_prefix npm_config_globalconfig npm_node_execpath 2>/dev/null; fi; APPLY_EC=0; if [ -f \"\$REPO_ROOT/dev_npx_packages/log-llm-config/dist/apply_deferred_vscdb.js\" ]; then echo deferred_restart:apply_via_dev_npx_packages; node \"\$REPO_ROOT/dev_npx_packages/log-llm-config/dist/apply_deferred_vscdb.js\"; APPLY_EC=\$?; elif [ \"\$ENV_LABEL\" = \"development\" ] && [ -n \"\$NPX_BASE\" ] && [ -f \"\$NPX_BASE/optimuslabs/harness-map/dist/apply_deferred_vscdb.js\" ]; then echo deferred_restart:apply_via_optimus_npx_base; node \"\$NPX_BASE/optimuslabs/harness-map/dist/apply_deferred_vscdb.js\"; APPLY_EC=\$?; elif command -v npx >/dev/null 2>&1; then echo deferred_restart:apply_via_npx env=\"\$ENV_LABEL\"; cd \"\$REPO_ROOT\" || true; if [ \"\$ENV_LABEL\" = \"staging\" ]; then npx --yes --package=@optimuslabs/harness-map-staging@latest apply-deferred-vscdb-staging; APPLY_EC=\$?; else npx --yes --package=@optimuslabs/harness-map@latest apply-deferred-vscdb; APPLY_EC=\$?; fi; else echo deferred_restart:no_npx; APPLY_EC=127; fi; echo deferred_restart:apply_exit=\$APPLY_EC; if [ \$APPLY_EC -ne 0 ]; then echo deferred_restart:APPLY_FAILED_see_messages_above; fi; echo deferred_restart:open_cursor; env -u npm_config_package -u npm_lifecycle_event -u npm_lifecycle_script -u npm_config_local_prefix open -a Cursor \"\$CURSOR_PROJECT\"; echo deferred_restart:open_exit=\$?; echo deferred_restart:end ts=\$(date -u +%Y-%m-%dT%H:%M:%SZ)' >/dev/null 2>&1 & killall -9 Cursor";
555
555
  /** Legacy manifests; hooks may run with cwd under `.cursor` where `pwd` is wrong. */
556
556
  const TRUSTED_CURSOR_JSON_SETTINGS_RESTART_COMMAND_LEGACY = 'CURSOR_PROJECT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) && export CURSOR_PROJECT && nohup bash -c \'sleep 2 && open -a Cursor "$CURSOR_PROJECT"\' >/dev/null 2>&1 & killall -9 Cursor';
557
557
  const TRUSTED_CURSOR_JSON_SETTINGS_RESTART_COMMAND = 'REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) && export REPO_ROOT && ' +
@@ -4,6 +4,7 @@ import { loadEndpointBase } from './endpoint_config.js';
4
4
  import { hookRunLog } from '../runtime/hook_logger.js';
5
5
  import { canonicalCursorUserStateVscdbPath } from '../runtime/remediation_config_path.js';
6
6
  import { resolveWorkspaceRepoFromEnv } from '../runtime/workspace_repo.js';
7
+ import { persistCollectPromptsFromResponse } from '../runtime/prompt-collection-policy.js';
7
8
  import fs from 'node:fs';
8
9
  import os from 'node:os';
9
10
  import path from 'node:path';
@@ -79,11 +80,16 @@ function buildBatchChunks(configFiles, basePayloadSize) {
79
80
  return chunks;
80
81
  }
81
82
  function buildChunkBody(chunk, hardwareUuid, authKey, hookRequestId, metadata) {
82
- const config_files = chunk.map((c) => ({
83
- file_type: c.file_type,
84
- file_path: canonicalCursorUserStateVscdbPath(c.file_path),
85
- raw_content: c.raw_content,
86
- }));
83
+ const config_files = chunk.map((c) => {
84
+ const item = {
85
+ file_type: c.file_type,
86
+ file_path: canonicalCursorUserStateVscdbPath(c.file_path),
87
+ raw_content: c.raw_content,
88
+ };
89
+ if (c.symlink_info !== undefined)
90
+ item.symlink_info = c.symlink_info;
91
+ return item;
92
+ });
87
93
  const payload = { hardware_uuid: hardwareUuid, metadata, config_files };
88
94
  if (hookRequestId != null)
89
95
  payload.hook_request_id = hookRequestId;
@@ -127,6 +133,7 @@ async function sendConfigFilesBatch(configFiles, hardwareUuid, authKey, hookRequ
127
133
  const timeoutMs = Math.min(20000 + Math.ceil(bodySize / (1024 * 1024)) * 15000, 90000);
128
134
  try {
129
135
  const response = (await postStartupPayload(apiUrl, body, timeoutMs));
136
+ persistCollectPromptsFromResponse(response);
130
137
  if (response.status === 'accepted') {
131
138
  totals.accepted += typeof response.accepted === 'number' ? response.accepted : chunk.length;
132
139
  const failedList = Array.isArray(response.failed) ? response.failed : [];
@@ -221,6 +228,8 @@ async function sendConfigFile(configFile, hardwareUuid, authKey, repoIdentifier)
221
228
  const apiUrl = `${resolveApiBase(endpoint)}/endpoint_security/log-config-file/`;
222
229
  const uploadPath = canonicalCursorUserStateVscdbPath(configFile.file_path);
223
230
  const payload = { hardware_uuid: hardwareUuid, file_type: configFile.file_type, file_path: uploadPath, raw_content: configFile.raw_content };
231
+ if (configFile.symlink_info !== undefined)
232
+ payload.symlink_info = configFile.symlink_info;
224
233
  const signature = createSignature(payload, authKey.key);
225
234
  const body = { ...payload, signature, key_id: authKey.key_id || '', metadata: { org_identifier: process.env.GITHUB_ORG || process.env.GH_ORG || '', organization_uuid: readOrganizationUuid(), repo_identifier: repoIdentifier ?? resolveWorkspaceRepoFromEnv() } };
226
235
  try {
@@ -5,6 +5,7 @@ import { resolveHardwareUuid } from './hardware_uuid.js';
5
5
  import { writeAuthKey, readStoredAuthKey, loadEndpointBase, buildStartupEndpointUrl } from './auth_key_store.js';
6
6
  import { resolveUserProfile } from './user_profile.js';
7
7
  import { resolveWorkspaceRepo } from '../log_config_files/runtime/workspace_repo.js';
8
+ import { persistCollectPromptsFromResponse } from '../log_config_files/runtime/prompt-collection-policy.js';
8
9
  import fs from 'node:fs';
9
10
  import os from 'node:os';
10
11
  import path from 'node:path';
@@ -83,6 +84,7 @@ const maybeSendToEndpoint = async (hardwareUuid, timestamp, options = {}) => {
83
84
  const requestBody = buildRequestBody(hardwareUuid, timestamp);
84
85
  try {
85
86
  const response = await postStartupPayload(startupEndpointUrl, requestBody);
87
+ persistCollectPromptsFromResponse(response);
86
88
  const result = classifyEndpointResponse(response);
87
89
  console.log(result.message);
88
90
  if (result.branch === 'key_issued' && result.key) {
package/dist/tofu.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Reads ~/.optimuslabs/management/optimus_dev.env at startup:
5
5
  * - No file / unreadable → staging (ignores OPTIMUS_ENVIRONMENT).
6
6
  * - File present → environment= line in file, then OPTIMUS_ENVIRONMENT, then staging.
7
- * - environment=development + local dist present → loads sibling optimus-tofu dist
7
+ * - environment=development + local dist present → loads sibling tofu dist
8
8
  * - staging / production / npx installs → published @optimuslabs/tofu-staging npm package
9
9
  *
10
10
  * All other source files import from this module, never directly from
@@ -14,7 +14,7 @@ import path from "path";
14
14
  import { fileURLToPath } from "url";
15
15
  import { shouldUseLocalTofuDist } from "./tofu_environment.js";
16
16
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
- const localTofuPath = path.join(__dirname, "../../optimus-tofu/dist/index.js");
17
+ const localTofuPath = path.join(__dirname, "../../tofu/dist/index.js");
18
18
  const useLocalTofu = shouldUseLocalTofuDist(localTofuPath);
19
19
  const tofu = (useLocalTofu
20
20
  ? await import(localTofuPath)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optimuslabs/harness-map-staging",
3
- "version": "1.5.16",
3
+ "version": "1.5.18",
4
4
  "description": "CLI helpers for logging hardware UUIDs and posting startup payloads to Optimus Security.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,12 +34,12 @@
34
34
  "repository": {
35
35
  "type": "git",
36
36
  "url": "git+https://github.com/optimuslabs-io/optimus-secure-fdn.git",
37
- "directory": "npx_packages/staging/optimuslabs/log-llm-config"
37
+ "directory": "npx_packages/staging/optimuslabs/harness-map"
38
38
  },
39
39
  "bugs": {
40
40
  "url": "https://github.com/optimuslabs-io/optimus-secure-fdn/issues"
41
41
  },
42
- "homepage": "https://github.com/optimuslabs-io/optimus-secure-fdn/tree/main/npx_packages/staging/optimuslabs/log-llm-config#readme",
42
+ "homepage": "https://github.com/optimuslabs-io/optimus-secure-fdn/tree/main/npx_packages/staging/optimuslabs/harness-map#readme",
43
43
  "files": [
44
44
  "dist/**/*",
45
45
  "README.md"