log-llm-config 1.5.8 → 1.5.9

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 CHANGED
@@ -11,7 +11,7 @@ CLI helpers for Optimus Security startup workflows.
11
11
 
12
12
  #### `log_uuid`
13
13
 
14
- Collects the machine hardware UUID (or uses the `OPTIMUS_HARDWARE_UUID` env variable), prints it, and POSTs a startup payload to `OPTIMUS_ENDPOINT/startup/`. If the server responds with a key, it stores it at `~/opt-ai-sec/management/auth_key.txt`.
14
+ Collects the machine hardware UUID (or uses the `OPTIMUS_HARDWARE_UUID` env variable), prints it, and POSTs a startup payload to `OPTIMUS_ENDPOINT/startup/`. If the server responds with a key, it stores it at `~/.optimuslabs/management/auth_key.txt`.
15
15
 
16
16
  ```bash
17
17
  OPTIMUS_ENDPOINT=http://localhost:8080/endpoint_security/ \
@@ -2,14 +2,14 @@
2
2
  * Bootstrap-level path constants — known before the API response is available.
3
3
  * Used by auth, hook logging, and audit output. Do not add agent-specific paths here.
4
4
  */
5
- export const OPT_AI_SEC_MANAGEMENT_REL = 'opt-ai-sec/management';
5
+ export const OPTIMUSLABS_MANAGEMENT_REL = '.optimuslabs/management';
6
6
  /**
7
7
  * All hook-written log files live under this subdirectory, grouped by type
8
8
  * (see LOG_GROUP_* below). Keeping logs in one subtree leaves the management
9
9
  * root limited to secrets/identity/state (auth_key, organization-uuid, contracts).
10
10
  */
11
- export const OPT_AI_SEC_LOGS_REL = `${OPT_AI_SEC_MANAGEMENT_REL}/logs`;
12
- /** Log group subdirectories under {@link OPT_AI_SEC_LOGS_REL}. */
11
+ export const OPTIMUSLABS_LOGS_REL = `${OPTIMUSLABS_MANAGEMENT_REL}/logs`;
12
+ /** Log group subdirectories under {@link OPTIMUSLABS_LOGS_REL}. */
13
13
  export const LOG_GROUP_HOOK = 'hook';
14
14
  export const LOG_GROUP_COMPLIANCE = 'compliance';
15
15
  export const LOG_GROUP_AUDIT = 'audit';
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * CLI for macOS non-restart autofix dialogs and dialog preferences in ~/opt-ai-sec/management.
3
+ * CLI for macOS non-restart autofix dialogs and dialog preferences in ~/.optimuslabs/management.
4
4
  * Invoked from optimus-compliance-check.sh hooks.
5
5
  */
6
6
  import { execFileSync } from 'node:child_process';
@@ -2,9 +2,9 @@ import path from 'node:path';
2
2
  import { postStartupPayload } from '../../endpoint_client/index.js';
3
3
  import { ensureAuthentication as ensureTofuAuthentication } from '../../tofu.js';
4
4
  import { loadEndpointBase } from '../sender/endpoint_config.js';
5
- import { OPT_AI_SEC_MANAGEMENT_REL } from '../../bootstrap_constants.js';
5
+ import { OPTIMUSLABS_MANAGEMENT_REL } from '../../bootstrap_constants.js';
6
6
  import { hookRunLog } from '../runtime/hook_logger.js';
7
- const AUTH_KEY_RELATIVE_PATH = path.join(OPT_AI_SEC_MANAGEMENT_REL, 'auth', 'auth_key.txt');
7
+ const AUTH_KEY_RELATIVE_PATH = path.join(OPTIMUSLABS_MANAGEMENT_REL, 'auth', 'auth_key.txt');
8
8
  /** Ensure authentication — verify stored key or request new one via handshake. */
9
9
  async function ensureAuthentication(hardwareUuid, endpointBase) {
10
10
  const key = await ensureTofuAuthentication({
@@ -1,7 +1,7 @@
1
1
  import path from 'node:path';
2
2
  import { getAuthKeyPath as getSharedAuthKeyPath, readStoredAuthKey as readSharedStoredAuthKey, } from '../../tofu.js';
3
- import { OPT_AI_SEC_MANAGEMENT_REL } from '../../bootstrap_constants.js';
4
- const AUTH_KEY_RELATIVE_PATH = path.join(OPT_AI_SEC_MANAGEMENT_REL, 'auth', 'auth_key.txt');
3
+ import { OPTIMUSLABS_MANAGEMENT_REL } from '../../bootstrap_constants.js';
4
+ const AUTH_KEY_RELATIVE_PATH = path.join(OPTIMUSLABS_MANAGEMENT_REL, 'auth', 'auth_key.txt');
5
5
  const AUTH_KEY_OPTIONS = { authRelativePath: AUTH_KEY_RELATIVE_PATH };
6
6
  /** Return absolute path to auth_key.txt, or null if home dir is unavailable. */
7
7
  function getAuthKeyPath() {
@@ -235,6 +235,7 @@ export { collectCursorTokenUsage } from './cursor_token_usage_collector.js';
235
235
  export { collectHermesTokenUsage } from './hermes_token_usage_collector.js';
236
236
  export { collectOpenclawTokenUsage } from './openclaw_token_usage_collector.js';
237
237
  export { collectPiTokenUsage } from './pi_token_usage_collector.js';
238
+ export { collectCoworkDesktopVersion } from './cowork_desktop_version_collector.js';
238
239
  export { collectClaudeDesktopExtensionManifests, collectClaudeDesktopExtensionSettingsFiles, enrichClaudeDesktopExtensionsInstallationsUpload, mergeClaudeDesktopExtensionEnableSettings, } from './claude_desktop_extensions_collector.js';
239
240
  export { collectCursorProjectWorkspaceMcpConfigs } from './cursor_project_mcp_collector.js';
240
241
  export { determineFileTypeFromPath } from './file_type_rules.js';
@@ -0,0 +1,60 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { homedir } from 'node:os';
5
+ /**
6
+ * Claude Desktop app version, surfaced as Cowork's Agent Profile `agent_version`.
7
+ *
8
+ * Cowork has no per-session version signal — its `local_<sessionId>.json` session files
9
+ * (see cowork_session_whitelist.ts) carry no version field, confirmed against a live install.
10
+ * Cowork runs inside the Claude Desktop app rather than as a standalone CLI, so the app's own
11
+ * bundle version is the only real "version" available for it: `CFBundleShortVersionString` from
12
+ * Claude.app's Info.plist, read via `plutil` (a stock macOS binary — no new dependency, same
13
+ * shell-out pattern as the sqlite3-based collectors).
14
+ *
15
+ * Gated on the Cowork enablement marker (~/Library/Application Support/Claude/
16
+ * cowork-enabled-cli-ops.json) so a machine with Claude Desktop installed but Cowork never
17
+ * enabled doesn't get an agent=cowork version row. macOS only — Claude Desktop/Cowork ships on
18
+ * macOS today.
19
+ */
20
+ const FILE_TYPE = 'cowork_desktop_version';
21
+ const BUNDLE_VERSION_KEY = 'CFBundleShortVersionString';
22
+ const CLAUDE_APP_INFO_PLIST = '/Applications/Claude.app/Contents/Info.plist';
23
+ function coworkEnabledMarkerPath(home) {
24
+ return join(home, 'Library', 'Application Support', 'Claude', 'cowork-enabled-cli-ops.json');
25
+ }
26
+ /** `plutil -extract CFBundleShortVersionString raw <path>`, or '' when plutil/the plist is unavailable. */
27
+ function readBundleVersion(plistPath) {
28
+ try {
29
+ return execFileSync('/usr/bin/plutil', ['-extract', BUNDLE_VERSION_KEY, 'raw', plistPath], {
30
+ encoding: 'utf8',
31
+ stdio: ['ignore', 'pipe', 'ignore'],
32
+ }).trim();
33
+ }
34
+ catch {
35
+ return '';
36
+ }
37
+ }
38
+ /**
39
+ * Emit the Claude Desktop app version as a single-element aggregate, or [] when this isn't
40
+ * macOS, Cowork isn't enabled on this machine, or Claude.app/plutil is unavailable.
41
+ */
42
+ function collectCoworkDesktopVersion(home = homedir(), plistPath = CLAUDE_APP_INFO_PLIST) {
43
+ if (process.platform !== 'darwin')
44
+ return [];
45
+ if (!existsSync(coworkEnabledMarkerPath(home)))
46
+ return [];
47
+ if (!existsSync(plistPath))
48
+ return [];
49
+ const version = readBundleVersion(plistPath);
50
+ if (!version)
51
+ return [];
52
+ return [
53
+ {
54
+ file_type: FILE_TYPE,
55
+ file_path: plistPath,
56
+ raw_content: { version },
57
+ },
58
+ ];
59
+ }
60
+ export { collectCoworkDesktopVersion, FILE_TYPE as COWORK_DESKTOP_VERSION_FILE_TYPE };
@@ -21,6 +21,12 @@ import { homedir } from 'node:os';
21
21
  * only — never any other key (openclaw.json's other sections can carry messaging/provider
22
22
  * settings).
23
23
  *
24
+ * Auth method comes from a scoped read of openclaw.json's `auth.profiles.*` entries — each
25
+ * profile is `{provider, mode}` (e.g. `{"provider": "anthropic", "mode": "api_key"}`); we read
26
+ * only those two keys, never any credential material (profiles carry no token/key fields to
27
+ * begin with). Exposed per-provider so the extractor can match it against the provider actually
28
+ * observed in session usage.
29
+ *
24
30
  * Only checks ~/.openclaw (the canonical root); the moltbot/clawdbot compatible-fork roots
25
31
  * tracked by the presence/log file types are not covered here.
26
32
  */
@@ -113,6 +119,34 @@ function readVersion(openclawDir) {
113
119
  return '';
114
120
  }
115
121
  }
122
+ /**
123
+ * Scoped read of openclaw.json's `auth.profiles` — provider + mode only, never credentials
124
+ * (the profiles carry no token/key fields). Returns a provider -> mode map (e.g.
125
+ * `{"anthropic": "api_key"}`); a provider with multiple profiles keeps the last one read.
126
+ */
127
+ function readAuthModesByProvider(openclawDir) {
128
+ try {
129
+ const raw = JSON.parse(readFileSync(join(openclawDir, 'openclaw.json'), 'utf8'));
130
+ const auth = raw.auth;
131
+ const profiles = auth && typeof auth === 'object' ? auth.profiles : undefined;
132
+ const out = {};
133
+ if (profiles && typeof profiles === 'object') {
134
+ for (const value of Object.values(profiles)) {
135
+ if (!value || typeof value !== 'object')
136
+ continue;
137
+ const entry = value;
138
+ const provider = str(entry.provider);
139
+ const mode = str(entry.mode);
140
+ if (provider && mode)
141
+ out[provider] = mode;
142
+ }
143
+ }
144
+ return out;
145
+ }
146
+ catch {
147
+ return {};
148
+ }
149
+ }
116
150
  /**
117
151
  * Aggregate OpenClaw session count / token usage / models / providers / version across every
118
152
  * agent dir's sessions.json store. Returns a single-element array (one per-machine aggregate)
@@ -148,6 +182,7 @@ function collectOpenclawTokenUsage(home = homedir()) {
148
182
  model_token_split: acc.modelTokenSplit,
149
183
  window_days: TOKEN_USAGE_RECENCY_DAYS,
150
184
  totals: acc.totals,
185
+ auth_modes_by_provider: readAuthModesByProvider(openclawDir),
151
186
  },
152
187
  },
153
188
  ];
@@ -9,9 +9,11 @@ import { resolveSqlite3Binary } from '../runtime/sqlite_binary.js';
9
9
  * Unlike Claude/Copilot (JSONL transcripts), OpenCode stores everything in a SQLite DB at
10
10
  * ~/.local/share/opencode/opencode.db. Each assistant row in the `message` table has a JSON
11
11
  * `data` blob carrying `modelID`, `providerID`, `tokens` ({input,output,reasoning,cache:{read,write}})
12
- * and `cost`; the `account` table carries the login `email`/`url`. We read those via the bundled
13
- * `sqlite3` binary (same resolver the remediation path uses) and emit only the aggregated numbers
14
- * (plus the login email) never message content. The DB is opened read-only.
12
+ * and `cost`; the `account` table carries the login `email`/`url`; the singleton `account_state`
13
+ * row carries `active_org_id` (an org identifier no org *name* exists anywhere in the schema).
14
+ * We read those via the bundled `sqlite3` binary (same resolver the remediation path uses) and
15
+ * emit only the aggregated numbers (plus the login email / org id) — never message content. The
16
+ * DB is opened read-only.
15
17
  *
16
18
  * Bounded by DB-file mtime recency so a stale install isn't re-read every hook run.
17
19
  */
@@ -151,6 +153,10 @@ function collectOpencodeTokenUsage(home = homedir()) {
151
153
  .replace(/^https?:\/\//, '')
152
154
  .replace(/\/$/, '');
153
155
  }
156
+ // Singleton row; absent on older installs/schemas without account_state — querySqlite
157
+ // returns null on "no such table" and orgIdRaw stays empty in that case.
158
+ const orgIdRaw = querySqlite(sqlite3, dbPath, "SELECT COALESCE(active_org_id, '') FROM account_state LIMIT 1;");
159
+ const orgId = orgIdRaw ? orgIdRaw.trim() : '';
154
160
  if (!sawData && !email)
155
161
  return [];
156
162
  return [
@@ -166,6 +172,7 @@ function collectOpencodeTokenUsage(home = homedir()) {
166
172
  cost,
167
173
  email,
168
174
  host,
175
+ org_id: orgId,
169
176
  window_days: TOKEN_USAGE_RECENCY_DAYS,
170
177
  totals,
171
178
  },
@@ -14,10 +14,11 @@ import { homedir } from 'node:os';
14
14
  * session count, and models/providers used without ingesting transcript content.
15
15
  *
16
16
  * Configured defaults come from a scoped read of settings.json's `defaultProvider` /
17
- * `defaultModel` / `lastChangelogVersion` keys only — never any other key. auth.json is never
18
- * touched at all: it holds literal per-provider `{type, key}` credential blobs
19
- * (`{"anthropic": {"type": ..., "key": ...}}`), the same risk class as Hermes's/Codex's
20
- * auth.json.
17
+ * `defaultModel` / `lastChangelogVersion` keys only — never any other key.
18
+ *
19
+ * auth.json holds literal per-provider `{type, key}` blobs (`{"anthropic": {"type": ...,
20
+ * "key": ...}}`), the same risk class as Hermes's/Codex's auth.json. We read only the `type`
21
+ * field per provider (e.g. `"api_key"`) — never `key`, the actual credential.
21
22
  *
22
23
  * `lastChangelogVersion` (confirmed against a real install: "0.80.3", matching `pi --version`
23
24
  * exactly) is a best-effort CLI version signal, not a guaranteed-current one — it records the
@@ -83,6 +84,27 @@ function readDefaults(home) {
83
84
  return { provider: '', model: '', version: '' };
84
85
  }
85
86
  }
87
+ /**
88
+ * Scoped read of auth.json's per-provider `type` field only — never the sibling `key`
89
+ * credential. Returns a provider -> type map (e.g. `{"anthropic": "api_key"}`).
90
+ */
91
+ function readAuthTypesByProvider(home) {
92
+ try {
93
+ const raw = JSON.parse(readFileSync(join(home, '.pi', 'agent', 'auth.json'), 'utf8'));
94
+ const out = {};
95
+ for (const [provider, value] of Object.entries(raw)) {
96
+ if (!value || typeof value !== 'object')
97
+ continue;
98
+ const type = str(value.type);
99
+ if (type)
100
+ out[provider] = type;
101
+ }
102
+ return out;
103
+ }
104
+ catch {
105
+ return {};
106
+ }
107
+ }
86
108
  /**
87
109
  * Aggregate Pi session count / token usage / models / providers across recent session
88
110
  * transcripts. Returns a single-element array (one per-machine aggregate) or [] when there is
@@ -173,6 +195,7 @@ function collectPiTokenUsage(home = homedir()) {
173
195
  configured_provider: defaults.provider,
174
196
  window_days: TOKEN_USAGE_RECENCY_DAYS,
175
197
  totals,
198
+ auth_types_by_provider: readAuthTypesByProvider(home),
176
199
  },
177
200
  },
178
201
  ];
@@ -27,7 +27,7 @@ export function getInstalledPluginsPath(home, constants) {
27
27
  return join(home, rel.replace(/^~\//, ''));
28
28
  }
29
29
  export function getOptAiSecManagementPath(home, constants) {
30
- const rel = requireKey(constants, 'opt_ai_sec_management_under_home', 'opt-ai-sec management path');
30
+ const rel = requireKey(constants, 'optimuslabs_management_under_home', '.optimuslabs management path');
31
31
  return join(home, rel.replace(/^~\//, ''));
32
32
  }
33
33
  export function getAuthKeyPathFromConstants(home, constants) {
@@ -13,7 +13,7 @@
13
13
  import { existsSync, mkdirSync, appendFileSync, readFileSync, renameSync, unlinkSync, statSync } from 'node:fs';
14
14
  import path from 'node:path';
15
15
  import { randomUUID } from 'node:crypto';
16
- import { OPT_AI_SEC_MANAGEMENT_REL } from '../../bootstrap_constants.js';
16
+ import { OPTIMUSLABS_MANAGEMENT_REL } from '../../bootstrap_constants.js';
17
17
  import { executeBody } from '../../endpoint_client/http_transport.js';
18
18
  import { loadEndpointBase } from '../sender/endpoint_config.js';
19
19
  import { createSignature } from '../sender/signing.js';
@@ -29,7 +29,7 @@ function bufferPath() {
29
29
  const home = process.env.HOME || process.env.USERPROFILE;
30
30
  if (!home)
31
31
  return null;
32
- return path.join(home, OPT_AI_SEC_MANAGEMENT_REL, 'telemetry', BUFFER_BASENAME);
32
+ return path.join(home, OPTIMUSLABS_MANAGEMENT_REL, 'telemetry', BUFFER_BASENAME);
33
33
  }
34
34
  /** Keep filename-safe chars only — must match optimus_sanitize_id in optimus-hook-common.sh. */
35
35
  function sanitizeId(v) {
@@ -40,7 +40,7 @@ function storyFilePath() {
40
40
  const home = process.env.HOME || process.env.USERPROFILE;
41
41
  if (!home)
42
42
  return null;
43
- const telemetryDir = path.join(home, OPT_AI_SEC_MANAGEMENT_REL, 'telemetry');
43
+ const telemetryDir = path.join(home, OPTIMUSLABS_MANAGEMENT_REL, 'telemetry');
44
44
  const session = sanitizeId((process.env.OPTIMUS_SESSION_ID || '').trim());
45
45
  if (session) {
46
46
  const agent = sanitizeId((process.env.OPTIMUS_HOOK_TYPE || process.env.OPTIMUS_AGENT || 'claude').trim()) || 'claude';
@@ -107,7 +107,7 @@ function hookRunFilePath() {
107
107
  const home = process.env.HOME || process.env.USERPROFILE;
108
108
  if (!home)
109
109
  return null;
110
- return path.join(home, OPT_AI_SEC_MANAGEMENT_REL, 'telemetry', 'current_hook_run.id');
110
+ return path.join(home, OPTIMUSLABS_MANAGEMENT_REL, 'telemetry', 'current_hook_run.id');
111
111
  }
112
112
  /**
113
113
  * hook_run_id: one uuid per shell hook invocation.
@@ -1,7 +1,7 @@
1
1
  import { existsSync, mkdirSync, readdirSync, renameSync, statSync, unlinkSync, writeFileSync, appendFileSync, readFileSync, } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { homedir } from 'node:os';
4
- import { OPT_AI_SEC_LOGS_REL, LOG_GROUP_COMPLIANCE } from '../../bootstrap_constants.js';
4
+ import { OPTIMUSLABS_LOGS_REL, LOG_GROUP_COMPLIANCE } from '../../bootstrap_constants.js';
5
5
  import { buildLogMetadataBlock } from './log_metadata.js';
6
6
  const COMPLIANCE_SUBDIR = LOG_GROUP_COMPLIANCE;
7
7
  const TIER_NOOP = 'noop';
@@ -35,7 +35,7 @@ export function isFinalizeComplianceSessionArgv(argv) {
35
35
  return argv.includes('--finalize-compliance-session');
36
36
  }
37
37
  export function getComplianceLogDir() {
38
- return path.join(homedir(), OPT_AI_SEC_LOGS_REL, COMPLIANCE_SUBDIR);
38
+ return path.join(homedir(), OPTIMUSLABS_LOGS_REL, COMPLIANCE_SUBDIR);
39
39
  }
40
40
  export function getActiveComplianceLogPath() {
41
41
  return activeComplianceLogPath;
@@ -1,6 +1,6 @@
1
1
  import { existsSync, mkdirSync, appendFileSync, writeFileSync, statSync, readFileSync } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { OPT_AI_SEC_LOGS_REL, LOG_GROUP_HOOK, LOG_GROUP_COMPLIANCE, } from '../../bootstrap_constants.js';
3
+ import { OPTIMUSLABS_LOGS_REL, LOG_GROUP_HOOK, LOG_GROUP_COMPLIANCE, } from '../../bootstrap_constants.js';
4
4
  import { getActiveComplianceLogPath } from './compliance_session_log.js';
5
5
  import { buildLogMetadataBlock, formatErrorBlock } from './log_metadata.js';
6
6
  const HOOK_LOG_FILENAME = 'hook_log.txt';
@@ -12,13 +12,13 @@ function getHookLogPath() {
12
12
  const homeDir = process.env.HOME || process.env.USERPROFILE;
13
13
  if (!homeDir)
14
14
  return null;
15
- return path.join(homeDir, OPT_AI_SEC_LOGS_REL, LOG_GROUP_HOOK, HOOK_LOG_FILENAME);
15
+ return path.join(homeDir, OPTIMUSLABS_LOGS_REL, LOG_GROUP_HOOK, HOOK_LOG_FILENAME);
16
16
  }
17
17
  function getComplianceRunnerLogPath() {
18
18
  const homeDir = process.env.HOME || process.env.USERPROFILE;
19
19
  if (!homeDir)
20
20
  return null;
21
- return path.join(homeDir, OPT_AI_SEC_LOGS_REL, LOG_GROUP_COMPLIANCE, COMPLIANCE_FALLBACK_TIER, COMPLIANCE_RUNNER_LOG_FILENAME);
21
+ return path.join(homeDir, OPTIMUSLABS_LOGS_REL, LOG_GROUP_COMPLIANCE, COMPLIANCE_FALLBACK_TIER, COMPLIANCE_RUNNER_LOG_FILENAME);
22
22
  }
23
23
  /**
24
24
  * Append one ISO-timestamped line to the active compliance session log.
@@ -7,7 +7,7 @@ import os from 'node:os';
7
7
  import { existsSync, readFileSync } from 'node:fs';
8
8
  import path from 'node:path';
9
9
  import { fileURLToPath } from 'node:url';
10
- import { OPT_AI_SEC_MANAGEMENT_REL } from '../../bootstrap_constants.js';
10
+ import { OPTIMUSLABS_MANAGEMENT_REL } from '../../bootstrap_constants.js';
11
11
  /** This package's name + version as "<name>: <version>", read from its own package.json. */
12
12
  function readPackageVersion() {
13
13
  try {
@@ -28,7 +28,7 @@ function readBundleVersion() {
28
28
  const home = process.env.HOME || process.env.USERPROFILE;
29
29
  if (!home)
30
30
  return 'unknown';
31
- const p = path.join(home, OPT_AI_SEC_MANAGEMENT_REL, 'release', 'bundle-version.json');
31
+ const p = path.join(home, OPTIMUSLABS_MANAGEMENT_REL, 'release', 'bundle-version.json');
32
32
  try {
33
33
  if (!existsSync(p))
34
34
  return 'unknown';
@@ -3,7 +3,7 @@ import { join } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
4
  const HOME_DIR = homedir();
5
5
  import { getFileCollectionPatterns, FILE_PATH_REGISTRY_FILE_PATTERNS_PATH } from '../../endpoint_client/index.js';
6
- import { OPT_AI_SEC_LOGS_REL, LOG_GROUP_AUDIT } from '../../bootstrap_constants.js';
6
+ import { OPTIMUSLABS_LOGS_REL, LOG_GROUP_AUDIT } from '../../bootstrap_constants.js';
7
7
  import { runSensitivePathsAudit } from '../../log_sensitive_paths_audit.js';
8
8
  import { loadEndpointBase, getEndpointSource } from '../sender/endpoint_config.js';
9
9
  import { hookLogReplace, hookRunLog, hookLogError, readHookLog } from './hook_logger.js';
@@ -14,7 +14,7 @@ import { ensureAuthentication } from '../auth/auth_flow.js';
14
14
  import { readJSONFile, readMarkdownFile } from '../readers/file_readers.js';
15
15
  import { isVscdbVirtualPath, tryReadVscdbVirtualFile, summarizeComposerPayloadForDiagnostics, } from '../readers/vscdb_config_builder.js';
16
16
  import { persistVscdbComposerContractFromPatternsResponse } from '../readers/vscdb_reader.js';
17
- import { collectConfigFilesFromPatterns, collectMcpToolFiles, collectConfigFilesFromInstalledPlugins, collectPluginCacheMcpFiles, collectMcpFromClaudeJsonProjects, collectClaudeTokenUsage, collectCopilotTokenUsage, collectOpencodeTokenUsage, collectCodexTokenUsage, collectCursorTokenUsage, collectHermesTokenUsage, collectOpenclawTokenUsage, collectPiTokenUsage, collectClaudeDesktopExtensionManifests, collectClaudeDesktopExtensionSettingsFiles, enrichClaudeDesktopExtensionsInstallationsUpload, determineFileTypeFromPath, } from '../collection/config_collector.js';
17
+ import { collectConfigFilesFromPatterns, collectMcpToolFiles, collectConfigFilesFromInstalledPlugins, collectPluginCacheMcpFiles, collectMcpFromClaudeJsonProjects, collectClaudeTokenUsage, collectCopilotTokenUsage, collectOpencodeTokenUsage, collectCodexTokenUsage, collectCursorTokenUsage, collectHermesTokenUsage, collectOpenclawTokenUsage, collectPiTokenUsage, collectCoworkDesktopVersion, collectClaudeDesktopExtensionManifests, collectClaudeDesktopExtensionSettingsFiles, enrichClaudeDesktopExtensionsInstallationsUpload, determineFileTypeFromPath, } from '../collection/config_collector.js';
18
18
  import { ensureCursorUserSettingsSnapshotInBatch } from '../collection/ensure_cursor_user_settings_snapshot.js';
19
19
  import { collectSkillsCliInstalled } from '../collection/skills_cli_collector.js';
20
20
  import { collectWorkspaceVscdbs } from '../collection/mcp_tool_collector.js';
@@ -162,6 +162,14 @@ async function collectAllConfigFiles(endpointBase) {
162
162
  configFiles.push(entry);
163
163
  }
164
164
  }
165
+ hookRunLog(`reading Claude Desktop app version for Cowork's Agent Profile`);
166
+ for (const entry of collectCoworkDesktopVersion(HOME_DIR)) {
167
+ const key = `${entry.file_type}\t${entry.file_path}`;
168
+ if (!existingPaths.has(key)) {
169
+ existingPaths.add(key);
170
+ configFiles.push(entry);
171
+ }
172
+ }
165
173
  hookRunLog(`merging disk-only Claude Desktop extensions into extensions-installations.json`);
166
174
  const diskExtMerge = enrichClaudeDesktopExtensionsInstallationsUpload(configFiles, HOME_DIR);
167
175
  hookRunLog(`claude desktop extensions: merged=${diskExtMerge.mergedCount} had_registry_upload=${diskExtMerge.hadRegistryUpload}`);
@@ -199,7 +207,7 @@ async function collectAllConfigFiles(endpointBase) {
199
207
  }
200
208
  async function addSensitivePathsAudit(endpointBase, configFiles) {
201
209
  hookRunLog(`running sensitive paths audit`);
202
- const auditOutputDir = join(homedir(), OPT_AI_SEC_LOGS_REL, LOG_GROUP_AUDIT);
210
+ const auditOutputDir = join(homedir(), OPTIMUSLABS_LOGS_REL, LOG_GROUP_AUDIT);
203
211
  try {
204
212
  const auditResult = await runSensitivePathsAudit(endpointBase, auditOutputDir, PROJECT_ROOT);
205
213
  hookRunLog(`sensitive_paths_audit: ${auditResult.paths.length} path(s)`);
@@ -1,11 +1,11 @@
1
1
  /**
2
- * Paths and JSON persistence for ~/opt-ai-sec/management:
2
+ * Paths and JSON persistence for ~/.optimuslabs/management:
3
3
  * - remediation_instructions.json (remediations[])
4
4
  */
5
5
  import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
6
6
  import { dirname, join } from 'node:path';
7
7
  import { homedir } from 'node:os';
8
- import { OPT_AI_SEC_MANAGEMENT_REL, OPT_AI_SEC_LOGS_REL } from '../../bootstrap_constants.js';
8
+ import { OPTIMUSLABS_MANAGEMENT_REL, OPTIMUSLABS_LOGS_REL } from '../../bootstrap_constants.js';
9
9
  export const REMEDIATION_INSTRUCTIONS_BASENAME = 'remediation_instructions.json';
10
10
  /** Pending state.vscdb writes applied after Cursor exits (IDE holds the DB locked). */
11
11
  export const DEFERRED_VSCDB_APPLY_BASENAME = 'optimus_deferred_vscdb_apply.json';
@@ -51,14 +51,14 @@ export function sanitizeFileCollectionVscdbContract(contract) {
51
51
  };
52
52
  }
53
53
  export function getManagementDir() {
54
- return join(homedir(), OPT_AI_SEC_MANAGEMENT_REL);
54
+ return join(homedir(), OPTIMUSLABS_MANAGEMENT_REL);
55
55
  }
56
- /** Root of the grouped hook log subtree (~/opt-ai-sec/management/logs). */
56
+ /** Root of the grouped hook log subtree (~/.optimuslabs/management/logs). */
57
57
  export function getLogsDir() {
58
- return join(homedir(), OPT_AI_SEC_LOGS_REL);
58
+ return join(homedir(), OPTIMUSLABS_LOGS_REL);
59
59
  }
60
60
  /**
61
- * Directory for vscdb state/contract files (~/opt-ai-sec/management/vscdb), keeping the
61
+ * Directory for vscdb state/contract files (~/.optimuslabs/management/vscdb), keeping the
62
62
  * several generated vscdb artifacts grouped instead of dangling at the management root.
63
63
  * Includes deferred_vscdb_restart.log (detached restart pipeline output).
64
64
  */
@@ -66,7 +66,7 @@ export function getVscdbStateDir() {
66
66
  return join(getManagementDir(), 'vscdb');
67
67
  }
68
68
  /**
69
- * Directory for non-log remediation state (~/opt-ai-sec/management/remediation):
69
+ * Directory for non-log remediation state (~/.optimuslabs/management/remediation):
70
70
  * remediation_instructions.json (what to fix) and remediation_apply_tracking.json
71
71
  * (quarantine + verify-failure history that escapes the autofix restart loop).
72
72
  * Remediation session logs live under logs/compliance/.
@@ -86,7 +86,7 @@ export function getDeferredVscdbRestartLogPath() {
86
86
  export function getFileCollectionVscdbContractPath() {
87
87
  return join(getVscdbStateDir(), FILE_COLLECTION_VSCDB_CONTRACT_BASENAME);
88
88
  }
89
- /** Directory for macOS dialog assets/state (~/opt-ai-sec/management/dialog): prefs + cached icon. */
89
+ /** Directory for macOS dialog assets/state (~/.optimuslabs/management/dialog): prefs + cached icon. */
90
90
  export function getDialogStateDir() {
91
91
  return join(getManagementDir(), 'dialog');
92
92
  }
@@ -541,7 +541,7 @@ function assertSafeSqliteIdentifiersForItemTable(table, keyColumn, valueColumn)
541
541
  * Canonical Cursor restart_command strings — single source for buildDeferredCursorRestartCommand + allowlist.
542
542
  *
543
543
  * - **SQLite / state.vscdb (deferred):** autofix queued ItemTable writes; restart runs `apply_deferred_vscdb` then reopens Cursor.
544
- * Routing matches shell hooks (`optimus-compliance-check.sh`): read `~/opt-ai-sec/management/optimus_dev.env`
544
+ * Routing matches shell hooks (`optimus-compliance-check.sh`): read `~/.optimuslabs/management/optimus_dev.env`
545
545
  * (then `OPTIMUS_ENVIRONMENT`), use `npx=` base for local `dist` when `environment=development`,
546
546
  * `log-llm-config-staging@latest` when `environment=staging`, else production package.
547
547
  * - **JSON settings files:** autofix wrote normal config JSON; restart is kill + reopen only (no deferred apply).
@@ -551,8 +551,8 @@ function assertSafeSqliteIdentifiersForItemTable(table, keyColumn, valueColumn)
551
551
  */
552
552
  const TRUSTED_CURSOR_SQLITE_DEFERRED_RESTART_COMMAND = 'REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) && export REPO_ROOT && ' +
553
553
  'CURSOR_PROJECT="${CURSOR_PROJECT_DIR:-$REPO_ROOT}" && export CURSOR_PROJECT && ' +
554
- 'OPTIMUS_DEFERRED_LOG="${HOME}/opt-ai-sec/management/vscdb/deferred_vscdb_restart.log" && mkdir -p "$(dirname "$OPTIMUS_DEFERRED_LOG")" && export OPTIMUS_DEFERRED_LOG && ' +
555
- "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/opt-ai-sec/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/log-llm-config/dist/apply_deferred_vscdb.js\" ]; then echo deferred_restart:apply_via_optimus_npx_base; node \"\$NPX_BASE/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=log-llm-config-staging@latest apply-deferred-vscdb-staging; APPLY_EC=\$?; else npx --yes --package=log-llm-config@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
+ 'OPTIMUS_DEFERRED_LOG="${HOME}/.optimuslabs/management/vscdb/deferred_vscdb_restart.log" && mkdir -p "$(dirname "$OPTIMUS_DEFERRED_LOG")" && export OPTIMUS_DEFERRED_LOG && ' +
555
+ "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/log-llm-config/dist/apply_deferred_vscdb.js\" ]; then echo deferred_restart:apply_via_optimus_npx_base; node \"\$NPX_BASE/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=log-llm-config-staging@latest apply-deferred-vscdb-staging; APPLY_EC=\$?; else npx --yes --package=log-llm-config@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";
556
556
  /** Legacy manifests; hooks may run with cwd under `.cursor` where `pwd` is wrong. */
557
557
  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';
558
558
  const TRUSTED_CURSOR_JSON_SETTINGS_RESTART_COMMAND = 'REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) && export REPO_ROOT && ' +
@@ -43,7 +43,7 @@ function readOrganizationUuid() {
43
43
  if (envValue)
44
44
  return envValue;
45
45
  try {
46
- return fs.readFileSync(path.join(os.homedir(), 'opt-ai-sec', 'management', 'org', 'organization-uuid.txt'), 'utf8').trim();
46
+ return fs.readFileSync(path.join(os.homedir(), '.optimuslabs', 'management', 'org', 'organization-uuid.txt'), 'utf8').trim();
47
47
  }
48
48
  catch {
49
49
  return '';
@@ -10,7 +10,7 @@ import { existsSync, writeFileSync, mkdirSync, statSync } from 'node:fs';
10
10
  import { join } from 'node:path';
11
11
  import { homedir } from 'node:os';
12
12
  import { getSensitivePathsAuditCandidates } from './endpoint_client/index.js';
13
- import { OPT_AI_SEC_LOGS_REL, LOG_GROUP_AUDIT } from './bootstrap_constants.js';
13
+ import { OPTIMUSLABS_LOGS_REL, LOG_GROUP_AUDIT } from './bootstrap_constants.js';
14
14
  import { loadEndpointBase } from './log_config_files/sender/endpoint_config.js';
15
15
  const AUDIT_FILENAME = 'sensitive_paths_audit.txt';
16
16
  function expandPath(template, cwd) {
@@ -63,7 +63,7 @@ export function writeSensitivePathsAudit(outputDir, pathTemplates, cwd = process
63
63
  * Run audit: fetch candidates from backend, check existence, write file. Returns paths for sending.
64
64
  * Used by log_config_files to include the audit in the same batch (same auth).
65
65
  */
66
- export async function runSensitivePathsAudit(endpointBase, outputDir = join(homedir(), OPT_AI_SEC_LOGS_REL, LOG_GROUP_AUDIT), cwd = process.cwd()) {
66
+ export async function runSensitivePathsAudit(endpointBase, outputDir = join(homedir(), OPTIMUSLABS_LOGS_REL, LOG_GROUP_AUDIT), cwd = process.cwd()) {
67
67
  const response = await getSensitivePathsAuditCandidates(endpointBase);
68
68
  const pathTemplates = response?.paths ?? [];
69
69
  if (pathTemplates.length === 0 && !response) {
@@ -75,7 +75,7 @@ export async function runSensitivePathsAudit(endpointBase, outputDir = join(home
75
75
  return { paths };
76
76
  }
77
77
  export async function main() {
78
- const outputDir = join(homedir(), OPT_AI_SEC_LOGS_REL, LOG_GROUP_AUDIT);
78
+ const outputDir = join(homedir(), OPTIMUSLABS_LOGS_REL, LOG_GROUP_AUDIT);
79
79
  const endpointBase = loadEndpointBase();
80
80
  await runSensitivePathsAudit(endpointBase, outputDir, process.cwd());
81
81
  process.exit(0);
@@ -1,9 +1,9 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { OPT_AI_SEC_MANAGEMENT_REL } from '../bootstrap_constants.js';
3
+ import { OPTIMUSLABS_MANAGEMENT_REL } from '../bootstrap_constants.js';
4
4
  import { loadEndpointBase } from '../log_config_files/sender/endpoint_config.js';
5
5
  import { buildStartupEndpointUrl, normalizeBaseUrl } from '../tofu.js';
6
- const AUTH_KEY_RELATIVE_PATH = path.join(OPT_AI_SEC_MANAGEMENT_REL, 'auth', 'auth_key.txt');
6
+ const AUTH_KEY_RELATIVE_PATH = path.join(OPTIMUSLABS_MANAGEMENT_REL, 'auth', 'auth_key.txt');
7
7
  const getAuthKeyPath = () => {
8
8
  const homeDir = process.env.HOME || process.env.USERPROFILE;
9
9
  if (!homeDir)
@@ -29,7 +29,7 @@ const readOrganizationUuid = () => {
29
29
  if (envValue)
30
30
  return envValue;
31
31
  try {
32
- return fs.readFileSync(path.join(os.homedir(), 'opt-ai-sec', 'management', 'org', 'organization-uuid.txt'), 'utf8').trim();
32
+ return fs.readFileSync(path.join(os.homedir(), '.optimuslabs', 'management', 'org', 'organization-uuid.txt'), 'utf8').trim();
33
33
  }
34
34
  catch {
35
35
  return '';
package/dist/tofu.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Tofu shim — centralises all imports from optimus-tofu.
3
3
  *
4
- * Reads ~/opt-ai-sec/management/optimus_dev.env at startup:
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
7
  * - environment=development + local dist present → loads sibling optimus-tofu dist
@@ -5,7 +5,7 @@ export function managementEnvPath() {
5
5
  const home = homedir();
6
6
  if (!home)
7
7
  return null;
8
- return path.join(home, 'opt-ai-sec', 'management', 'optimus_dev.env');
8
+ return path.join(home, '.optimuslabs', 'management', 'optimus_dev.env');
9
9
  }
10
10
  /**
11
11
  * Resolve optimus environment label (matches shell hooks / optimus-tool-call):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "log-llm-config",
3
- "version": "1.5.8",
3
+ "version": "1.5.9",
4
4
  "description": "CLI helpers for logging hardware UUIDs and posting startup payloads to Optimus Security.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -58,6 +58,6 @@
58
58
  "dependencies": {
59
59
  "axios": "^1.15.2",
60
60
  "canonicalize": "^2.1.0",
61
- "optimus-tofu": "^0.1.17"
61
+ "optimus-tofu": "^0.1.18"
62
62
  }
63
63
  }