@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.
- package/dist/log_config_files/collection/directory_collector.js +10 -0
- package/dist/log_config_files/collection/plugin_collector.js +12 -4
- package/dist/log_config_files/collection/skill_symlink.js +191 -0
- package/dist/log_config_files/runtime/prompt-collection-policy.js +57 -0
- package/dist/log_config_files/runtime/remediation_sync.js +1 -1
- package/dist/log_config_files/sender/batch_sender.js +14 -5
- package/dist/log_uuid/startup_sender.js +2 -0
- package/dist/tofu.js +2 -2
- package/package.json +3 -3
|
@@ -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
|
-
|
|
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
|
|
106
|
+
for (const d of readdirSync(skillsDir, { withFileTypes: true }).filter((d) => isSkillDirEntry(d))) {
|
|
104
107
|
const skillRoot = join(skillsDir, d.name);
|
|
105
|
-
|
|
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:
|
|
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/
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
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, "../../
|
|
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.
|
|
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/
|
|
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/
|
|
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"
|