log-llm-config-staging 1.3.72 → 1.3.74

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/cli.js CHANGED
@@ -94,7 +94,7 @@ function parseAndSetLogUuidEnvVars() {
94
94
  if (userArg)
95
95
  process.env.GITHUB_USER = userArg;
96
96
  if (repoArg)
97
- process.env.GITHUB_REPOSITORY = repoArg;
97
+ process.env.OPTIMUS_WORKSPACE_REPO = repoArg;
98
98
  if (branchArg)
99
99
  process.env.GITHUB_REF_NAME = branchArg;
100
100
  if (agentArg)
@@ -8,12 +8,14 @@
8
8
  */
9
9
  import { applyAutofixViolations, normalizeAgentToken, pruneSatisfiedOneTimeRemediations, runLocalRemediationComplianceCheck, } from './log_config_files/runtime/compliance_check.js';
10
10
  import { existsSync, readFileSync, statSync } from 'node:fs';
11
- import { getRemediationInstructionsPath } from './log_config_files/runtime/management_storage.js';
11
+ import { getRemediationInstructionsPath, readRemediationInstructionsFile } from './log_config_files/runtime/management_storage.js';
12
12
  import { hookLogSessionBanner, hookRunLog, logRemediationApplyFailure } from './log_config_files/runtime/hook_logger.js';
13
13
  import { isThisCliModule } from './cli_invocation_match.js';
14
14
  import { ensureAuthentication } from './log_config_files/auth/auth_flow.js';
15
15
  import { sendConfigFile } from './log_config_files/sender/batch_sender.js';
16
16
  import { tryResolveHardwareUuid } from './log_config_files/runtime/hardware_uuid.js';
17
+ import { syncRemediations } from './log_config_files/runtime/remediation_sync.js';
18
+ import { loadEndpointBase } from './log_config_files/sender/endpoint_config.js';
17
19
  const MANIFEST_STALE_MS = 7 * 24 * 60 * 60 * 1000;
18
20
  function parseIde() {
19
21
  const eq = process.argv.find((a) => a.startsWith('--ide='));
@@ -90,6 +92,19 @@ function autofixDialogLine(v) {
90
92
  const short = d.length > 160 ? `${d.slice(0, 157)}…` : d;
91
93
  return `• [${v.finding_formatted_id}] ${short}`;
92
94
  }
95
+ /** Cursor restart dialog after enforced/preventive remediation is applied locally. */
96
+ export function formatCursorRestartAutofixDialog(appliedViolations) {
97
+ const policyNames = [
98
+ ...new Set(appliedViolations
99
+ .map((v) => v.policy_name?.trim() || v.finding_title?.trim() || '')
100
+ .filter(Boolean)),
101
+ ];
102
+ const policyLabel = policyNames.length > 0 ? policyNames.join('\n\n') : 'Agent security policy';
103
+ return ('Optimus Labs enforced a preventive agent security policy as determined by your security team:\n\n' +
104
+ `${policyLabel}\n\n` +
105
+ 'Cursor will now restart to apply this policy, and your context will be retained.\n\n' +
106
+ 'Click OK to continue');
107
+ }
93
108
  /**
94
109
  * Upload a secondary compliance file to the backend so the server can resolve the finding.
95
110
  * Fire-and-forget: upload runs in background; any failure is logged but does not block the gate.
@@ -135,6 +150,24 @@ export async function runCompliancePromptGate() {
135
150
  const ide = parseIde();
136
151
  const agent = parseAgent(ide);
137
152
  hookLogSessionBanner('compliance_prompt_gate (before submit)');
153
+ // Sync before checking so server-side changes (enforce/unenforce) propagate on every prompt
154
+ // without waiting for the background runner. Race against a 3 s timeout so a slow or
155
+ // unavailable server never delays the gate significantly.
156
+ const hw = tryResolveHardwareUuid();
157
+ if (hw) {
158
+ const { remediations } = readRemediationInstructionsFile();
159
+ if (Array.isArray(remediations) && remediations.length > 0) {
160
+ try {
161
+ await Promise.race([
162
+ syncRemediations(loadEndpointBase(), hw),
163
+ new Promise((resolve) => setTimeout(resolve, 3000)),
164
+ ]);
165
+ }
166
+ catch {
167
+ // Network or auth failure — fall back to local file state.
168
+ }
169
+ }
170
+ }
138
171
  const status = runLocalRemediationComplianceCheck(agent);
139
172
  // Secondary-satisfied: primary checks failed but settings.json (or equiv) has the fix.
140
173
  // Upload those files fire-and-forget so the backend can resolve the finding immediately.
@@ -189,9 +222,11 @@ export async function runCompliancePromptGate() {
189
222
  }
190
223
  if (deferredSqlitePending || recheckOk || claudeRecheckStaleAfterImmediateApply) {
191
224
  const violationLabel = fixed === 1 ? 'policy violation' : 'policy violations';
192
- const autofixMessage = `Optimus Labs auto-fixed ${fixed} ${violationLabel}:\n\n${appliedViolations
193
- .map((v) => autofixDialogLine(v))
194
- .join('\n')}`;
225
+ const autofixMessage = ide === 'cursor' && restartCommands.length > 0
226
+ ? formatCursorRestartAutofixDialog(appliedViolations)
227
+ : `Optimus Labs auto-fixed ${fixed} ${violationLabel}:\n\n${appliedViolations
228
+ .map((v) => autofixDialogLine(v))
229
+ .join('\n')}`;
195
230
  const payload = { __optimus_autofix: true, autofix_message: autofixMessage };
196
231
  if (restartCommands.length > 0)
197
232
  payload.restart_commands = restartCommands;
@@ -20,7 +20,7 @@ import { complianceRunnerDiag, hookRunLog, logRemediationApplyFailure } from './
20
20
  import { loadEndpointBase } from '../sender/endpoint_config.js';
21
21
  import { resolveHardwareUuid, tryResolveHardwareUuid } from './hardware_uuid.js';
22
22
  import { buildDeferredCursorRestartCommand, discoverAllWorkspaceVscdbs, enforceRemediation, fetchSync, isTrustedRestartCommandForAutofix, remediationFixSpec, reportAutofixApplied, syncRemediations, } from './remediation_sync.js';
23
- import { sendConfigFile } from '../sender/batch_sender.js';
23
+ import { sendConfigFile, resolveRepoFromPath } from '../sender/batch_sender.js';
24
24
  import { ensureAuthentication } from '../auth/auth_flow.js';
25
25
  /** Normalize manifest/env/CLI agent tokens to a known Agent, or '' if unrecognized. */
26
26
  export function normalizeAgentToken(raw) {
@@ -325,6 +325,7 @@ export function runLocalRemediationComplianceCheck(agent = 'cursor') {
325
325
  description: check.description,
326
326
  finding_title: entry.finding_title,
327
327
  finding_description: entry.finding_description,
328
+ policy_name: entry.policy_name,
328
329
  severity: compliance.severity,
329
330
  autofix_allowed: compliance.autofix_allowed,
330
331
  config_file_path: entry.config_file_path,
@@ -347,6 +348,7 @@ export function runLocalRemediationComplianceCheck(agent = 'cursor') {
347
348
  description: check.description,
348
349
  finding_title: entry.finding_title,
349
350
  finding_description: entry.finding_description,
351
+ policy_name: entry.policy_name,
350
352
  severity: compliance.severity,
351
353
  autofix_allowed: compliance.autofix_allowed,
352
354
  config_file_path: entry.config_file_path,
@@ -371,6 +373,7 @@ export function runLocalRemediationComplianceCheck(agent = 'cursor') {
371
373
  description: check.description,
372
374
  finding_title: entry.finding_title,
373
375
  finding_description: entry.finding_description,
376
+ policy_name: entry.policy_name,
374
377
  severity: compliance.severity,
375
378
  autofix_allowed: compliance.autofix_allowed,
376
379
  config_file_path: entry.config_file_path,
@@ -525,10 +528,11 @@ export function applyAutofixViolations(violations, agent = 'cursor') {
525
528
  if (fileType) {
526
529
  const hw = tryResolveHardwareUuid();
527
530
  if (hw) {
531
+ const repoIdentifier = resolveRepoFromPath(configPathForDisk);
528
532
  // Do not rely on the bulk uploader (start-every-prompt throttles at 1800s).
529
533
  // Always attempt to upload the single remediated file immediately.
530
534
  reportPromises.push(ensureAuthentication(hw)
531
- .then((authKey) => sendConfigFile({ file_type: fileType, file_path: configPathForDisk, raw_content: updatedContent }, hw, authKey))
535
+ .then((authKey) => sendConfigFile({ file_type: fileType, file_path: configPathForDisk, raw_content: updatedContent }, hw, authKey, repoIdentifier))
532
536
  .then((sentOk) => {
533
537
  hookRunLog(`autofix: uploaded remediated file uuid=${inst.uuid} path=${configPathForDisk} ok=${sentOk}`);
534
538
  })
@@ -84,7 +84,7 @@ async function addSensitivePathsAudit(endpointBase, configFiles) {
84
84
  async function sendAllConfigFiles(configFiles, hardwareUuid, authKey) {
85
85
  const hookTypeRaw = (process.env.OPTIMUS_HOOK_TYPE || 'claude').toLowerCase();
86
86
  const hookType = hookTypeRaw === 'cursor' ? 'cursor' : 'claude';
87
- const workspaceRepo = process.env.OPTIMUS_WORKSPACE_REPO || process.env.GITHUB_REPOSITORY || '';
87
+ const workspaceRepo = process.env.OPTIMUS_WORKSPACE_REPO || '';
88
88
  const manifest = configFiles.map((c) => canonicalCursorUserStateVscdbPath(c.file_path));
89
89
  const hookRequestId = await sendHookRequestCreate(hardwareUuid, authKey, hookType, workspaceRepo);
90
90
  hookRunLog(`hook-request id=${hookRequestId ?? 'none'}`);
@@ -118,7 +118,7 @@ async function main() {
118
118
  const endpointBase = loadEndpointBase();
119
119
  hookRunLog(`start endpoint=${endpointBase} hardware_uuid=${hardwareUuid}`);
120
120
  hookRunLog(`endpoint_source=${getEndpointSource()} cwd=${process.cwd()}`);
121
- hookRunLog(`env: OPTIMUS_HOOK_TYPE=${process.env.OPTIMUS_HOOK_TYPE ? 'set' : 'unset'} GITHUB_REPOSITORY=${process.env.GITHUB_REPOSITORY ? 'set' : 'unset'} OPTIMUS_ENDPOINT=${process.env.OPTIMUS_ENDPOINT ? 'set' : 'unset'}`);
121
+ hookRunLog(`env: OPTIMUS_HOOK_TYPE=${process.env.OPTIMUS_HOOK_TYPE ? 'set' : 'unset'} OPTIMUS_WORKSPACE_REPO=${process.env.OPTIMUS_WORKSPACE_REPO ? 'set' : 'unset'} OPTIMUS_ENDPOINT=${process.env.OPTIMUS_ENDPOINT ? 'set' : 'unset'}`);
122
122
  let authKey;
123
123
  try {
124
124
  authKey = await ensureAuthentication(hardwareUuid);
@@ -6,9 +6,24 @@ import { canonicalCursorUserStateVscdbPath } from '../runtime/remediation_config
6
6
  import fs from 'node:fs';
7
7
  import os from 'node:os';
8
8
  import path from 'node:path';
9
+ import { execFileSync } from 'node:child_process';
9
10
  /** Chunk size per batch request. */
10
11
  export const BATCH_CHUNK_SIZE = 20;
11
12
  const MAX_BATCH_SIZE_BYTES = 500 * 1024; // 500KB
13
+ function resolveWorkspaceRepo() {
14
+ return process.env.OPTIMUS_WORKSPACE_REPO || '';
15
+ }
16
+ export function resolveRepoFromPath(filePath) {
17
+ try {
18
+ const dir = path.dirname(filePath);
19
+ return execFileSync('git', ['remote', '-v'], { cwd: dir, timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'] })
20
+ .toString()
21
+ .trim();
22
+ }
23
+ catch {
24
+ return resolveWorkspaceRepo();
25
+ }
26
+ }
12
27
  function resolveApiBase(endpoint) {
13
28
  try {
14
29
  return new URL(endpoint).origin;
@@ -86,7 +101,7 @@ function splitChunk(chunks, chunkIndex) {
86
101
  async function sendConfigFilesBatch(configFiles, hardwareUuid, authKey, hookRequestId, ingestSessionId) {
87
102
  const endpoint = loadEndpointBase();
88
103
  const apiUrl = `${resolveApiBase(endpoint)}/endpoint_security/log-config-files/`;
89
- const metadata = { org_identifier: process.env.GITHUB_ORG || process.env.GH_ORG || '', organization_uuid: readOrganizationUuid(), repo_identifier: process.env.GITHUB_REPOSITORY || process.env.GH_REPOSITORY || '' };
104
+ const metadata = { org_identifier: process.env.GITHUB_ORG || process.env.GH_ORG || '', organization_uuid: readOrganizationUuid(), repo_identifier: resolveWorkspaceRepo() };
90
105
  const basePayloadSize = JSON.stringify({ hardware_uuid: hardwareUuid, metadata }).length + 800;
91
106
  const chunks = buildBatchChunks(configFiles, basePayloadSize);
92
107
  const totals = { accepted: 0, failed: 0 };
@@ -169,13 +184,13 @@ async function sendIngestSessionFinish(hardwareUuid, authKey, ingestSessionId) {
169
184
  return false;
170
185
  }
171
186
  }
172
- async function sendConfigFile(configFile, hardwareUuid, authKey) {
187
+ async function sendConfigFile(configFile, hardwareUuid, authKey, repoIdentifier) {
173
188
  const endpoint = loadEndpointBase();
174
189
  const apiUrl = `${resolveApiBase(endpoint)}/endpoint_security/log-config-file/`;
175
190
  const uploadPath = canonicalCursorUserStateVscdbPath(configFile.file_path);
176
191
  const payload = { hardware_uuid: hardwareUuid, file_type: configFile.file_type, file_path: uploadPath, raw_content: configFile.raw_content };
177
192
  const signature = createSignature(payload, authKey.key);
178
- 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: process.env.GITHUB_REPOSITORY || process.env.GH_REPOSITORY || '' } };
193
+ 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 ?? resolveWorkspaceRepo() } };
179
194
  try {
180
195
  const response = await postStartupPayload(apiUrl, body);
181
196
  if (response.status !== 'accepted') {
@@ -51,7 +51,7 @@ const getMetadata = () => ({
51
51
  github_username: process.env.GITHUB_USER || process.env.GH_USER || '',
52
52
  org_identifier: process.env.GITHUB_ORG || '',
53
53
  organization_uuid: readOrganizationUuid(),
54
- repo_identifier: process.env.GITHUB_REPOSITORY || '',
54
+ repo_identifier: process.env.OPTIMUS_WORKSPACE_REPO || process.env.GITHUB_REPOSITORY || '',
55
55
  branch: process.env.GITHUB_REF_NAME || process.env.BRANCH_NAME || '',
56
56
  commit_sha: process.env.GITHUB_SHA || '',
57
57
  agent_name: process.env.OPTIMUS_AGENT || '',
package/dist/tofu.js CHANGED
@@ -10,39 +10,12 @@
10
10
  * All other source files import from this module, never directly from
11
11
  * "optimus-tofu-staging", so the conditional lives in exactly one place.
12
12
  */
13
- import { existsSync, readFileSync } from "fs";
14
- import { homedir } from "os";
15
13
  import path from "path";
16
14
  import { fileURLToPath } from "url";
17
- function managementEnvPath() {
18
- const home = homedir();
19
- if (!home)
20
- return null;
21
- return path.join(home, "opt-ai-sec", "management", "optimus_dev.env");
22
- }
23
- function readEnvironment() {
24
- const envPath = managementEnvPath();
25
- if (!envPath) {
26
- return "staging";
27
- }
28
- try {
29
- const content = readFileSync(envPath, "utf8");
30
- const match = content.match(/^(?:environment|ENVIRONMENT|OPTIMUS_ENVIRONMENT)\s*=\s*(.+)/m);
31
- if (match)
32
- return match[1].trim().toLowerCase();
33
- }
34
- catch {
35
- // No management file: this staging package defaults to staging (promotion rewrites to production).
36
- return "staging";
37
- }
38
- const fromEnv = process.env.OPTIMUS_ENVIRONMENT?.trim().toLowerCase();
39
- if (fromEnv)
40
- return fromEnv;
41
- return "staging";
42
- }
15
+ import { shouldUseLocalTofuDist } from "./tofu_environment.js";
43
16
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
44
17
  const localTofuPath = path.join(__dirname, "../../optimus-tofu/dist/index.js");
45
- const useLocalTofu = readEnvironment() === "development" && existsSync(localTofuPath);
18
+ const useLocalTofu = shouldUseLocalTofuDist(localTofuPath);
46
19
  const tofu = (useLocalTofu
47
20
  ? await import(localTofuPath)
48
21
  : await import("optimus-tofu-staging"));
@@ -0,0 +1,32 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import path from 'path';
4
+ export function managementEnvPath() {
5
+ const home = homedir();
6
+ if (!home)
7
+ return null;
8
+ return path.join(home, 'opt-ai-sec', 'management', 'optimus_dev.env');
9
+ }
10
+ /** Resolve optimus environment label (file wins over shell OPTIMUS_ENVIRONMENT). */
11
+ export function readEnvironment() {
12
+ const envPath = managementEnvPath();
13
+ if (!envPath) {
14
+ return 'staging';
15
+ }
16
+ try {
17
+ const content = readFileSync(envPath, 'utf8');
18
+ const match = content.match(/^(?:environment|ENVIRONMENT|OPTIMUS_ENVIRONMENT)\s*=\s*(.+)/m);
19
+ if (match)
20
+ return match[1].trim().toLowerCase();
21
+ }
22
+ catch {
23
+ return 'staging';
24
+ }
25
+ const fromEnv = process.env.OPTIMUS_ENVIRONMENT?.trim().toLowerCase();
26
+ if (fromEnv)
27
+ return fromEnv;
28
+ return 'staging';
29
+ }
30
+ export function shouldUseLocalTofuDist(localTofuPath) {
31
+ return readEnvironment() === 'development' && existsSync(localTofuPath);
32
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "log-llm-config-staging",
3
- "version": "1.3.72",
3
+ "version": "1.3.74",
4
4
  "description": "CLI helpers for logging hardware UUIDs and posting startup payloads to Optimus Security.",
5
5
  "type": "module",
6
6
  "bin": {