@getmarrow/install 0.1.17 → 0.1.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/README.md +10 -0
- package/package.json +1 -1
- package/src/installer.js +105 -15
package/README.md
CHANGED
|
@@ -11,6 +11,16 @@ npx @getmarrow/install --repair
|
|
|
11
11
|
npx @getmarrow/install doctor
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
+
## What's New in v0.1.18
|
|
15
|
+
|
|
16
|
+
v0.1.18 makes Marrow key loading more reliable for agents after install.
|
|
17
|
+
|
|
18
|
+
- Installer, SDK passive runtime, and MCP hooks now all recognize the same key locations: process env, MCP/agent secret env, `.marrow/env`, `.marrow/env.local`, `.env`, `.env.local`, `~/.marrow/env`, and `~/.marrow/env.local`.
|
|
19
|
+
- `MARROW_API_KEY` remains the canonical variable. `MARROW_KEY` is accepted as an alias for fleet runners and secret managers.
|
|
20
|
+
- `npx @getmarrow/install doctor` now reports whether a key is loaded, where a likely key file exists, and the exact repair command without printing the key.
|
|
21
|
+
- Generated `.marrow/passive-runtime.mjs` loads `.marrow/env` before deciding Marrow is missing, so agents keep logging even when the shell did not export the key.
|
|
22
|
+
- Full keys are never printed in diagnostics. Keep `.marrow/env` out of git and set file permissions to owner-only when possible.
|
|
23
|
+
|
|
14
24
|
## What's New in v0.1.17
|
|
15
25
|
|
|
16
26
|
v0.1.17 expands the Govern TUI harness addon coverage while preserving adaptive mode recommendations.
|
package/package.json
CHANGED
package/src/installer.js
CHANGED
|
@@ -15,7 +15,7 @@ function parseArgs(argv) {
|
|
|
15
15
|
doctor: false,
|
|
16
16
|
repair: false,
|
|
17
17
|
mode: 'auto',
|
|
18
|
-
apiKey: process.env.MARROW_API_KEY || '',
|
|
18
|
+
apiKey: process.env.MARROW_API_KEY || process.env.MARROW_KEY || '',
|
|
19
19
|
baseUrl: process.env.MARROW_BASE_URL || DEFAULT_BASE_URL,
|
|
20
20
|
agentId: process.env.MARROW_FLEET_AGENT_ID || process.env.MARROW_AGENT_ID || '',
|
|
21
21
|
selfTest: true,
|
|
@@ -57,6 +57,12 @@ function parseArgs(argv) {
|
|
|
57
57
|
throw new Error('--mode must be one of auto, mcp, sdk, both, md');
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
const resolved = resolveMarrowKeyMaterial(options.cwd);
|
|
61
|
+
if (!options.apiKey && resolved.apiKey) options.apiKey = resolved.apiKey;
|
|
62
|
+
if (!options.agentId && resolved.agentId) options.agentId = resolved.agentId;
|
|
63
|
+
if (options.baseUrl === DEFAULT_BASE_URL && resolved.baseUrl) options.baseUrl = resolved.baseUrl;
|
|
64
|
+
if (!options.keySource && resolved.source) options.keySource = resolved.source;
|
|
65
|
+
|
|
60
66
|
return options;
|
|
61
67
|
}
|
|
62
68
|
|
|
@@ -157,8 +163,7 @@ function findLikelyEnvFiles(detection, env = process.env) {
|
|
|
157
163
|
path.join(detection.root, '.marrow', 'env'),
|
|
158
164
|
path.join(detection.root, '.marrow', 'env.local'),
|
|
159
165
|
path.join(home, '.marrow', 'env'),
|
|
160
|
-
path.join(home, '.
|
|
161
|
-
path.join(home, '.openclaw', 'gateway.systemd.env'),
|
|
166
|
+
path.join(home, '.marrow', 'env.local'),
|
|
162
167
|
];
|
|
163
168
|
return candidates.filter((filePath) => {
|
|
164
169
|
if (!exists(filePath)) return false;
|
|
@@ -183,6 +188,36 @@ function readEnvVar(filePath, name) {
|
|
|
183
188
|
return match ? stripQuotes(match[1]) : '';
|
|
184
189
|
}
|
|
185
190
|
|
|
191
|
+
function resolveMarrowKeyMaterial(cwd = process.cwd(), env = process.env) {
|
|
192
|
+
if (env.MARROW_API_KEY || env.MARROW_KEY) {
|
|
193
|
+
return {
|
|
194
|
+
apiKey: env.MARROW_API_KEY || env.MARROW_KEY,
|
|
195
|
+
baseUrl: env.MARROW_BASE_URL || DEFAULT_BASE_URL,
|
|
196
|
+
agentId: env.MARROW_FLEET_AGENT_ID || env.MARROW_AGENT_ID || '',
|
|
197
|
+
source: env.MARROW_API_KEY ? 'MARROW_API_KEY' : 'MARROW_KEY',
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const detection = detectEnvironment(cwd, env);
|
|
202
|
+
for (const filePath of findLikelyEnvFiles(detection, env)) {
|
|
203
|
+
const apiKey = readEnvVar(filePath, 'MARROW_API_KEY') || readEnvVar(filePath, 'MARROW_KEY');
|
|
204
|
+
if (!apiKey) continue;
|
|
205
|
+
return {
|
|
206
|
+
apiKey,
|
|
207
|
+
baseUrl: readEnvVar(filePath, 'MARROW_BASE_URL') || env.MARROW_BASE_URL || DEFAULT_BASE_URL,
|
|
208
|
+
agentId: readEnvVar(filePath, 'MARROW_FLEET_AGENT_ID') || readEnvVar(filePath, 'MARROW_AGENT_ID') || env.MARROW_FLEET_AGENT_ID || env.MARROW_AGENT_ID || '',
|
|
209
|
+
source: filePath,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
apiKey: '',
|
|
215
|
+
baseUrl: env.MARROW_BASE_URL || DEFAULT_BASE_URL,
|
|
216
|
+
agentId: env.MARROW_FLEET_AGENT_ID || env.MARROW_AGENT_ID || '',
|
|
217
|
+
source: null,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
186
221
|
function readFirstLineSecret(filePath) {
|
|
187
222
|
if (!exists(filePath)) return '';
|
|
188
223
|
return safeRead(filePath).split(/\r?\n/).map((line) => line.trim()).find(Boolean) || '';
|
|
@@ -297,27 +332,79 @@ Required environment:
|
|
|
297
332
|
|
|
298
333
|
- \`MARROW_API_KEY\`
|
|
299
334
|
- Optional: \`MARROW_BASE_URL\`, \`MARROW_FLEET_AGENT_ID\`
|
|
335
|
+
|
|
336
|
+
Key loading:
|
|
337
|
+
|
|
338
|
+
- Prefer your shell, MCP secret store, or agent secret manager.
|
|
339
|
+
- Marrow also auto-detects \`.marrow/env\`, \`.marrow/env.local\`, \`.env\`, \`.env.local\`, and \`~/.marrow/env\`.
|
|
340
|
+
- Run \`npx @getmarrow/install doctor\` any time an agent says Marrow is missing or degraded.
|
|
300
341
|
${MARROW_BLOCK_END}`;
|
|
301
342
|
}
|
|
302
343
|
|
|
303
344
|
function passiveRuntimeSource() {
|
|
304
|
-
return `
|
|
345
|
+
return `import fs from 'node:fs';
|
|
346
|
+
import path from 'node:path';
|
|
347
|
+
import os from 'node:os';
|
|
348
|
+
|
|
349
|
+
function stripQuotes(value) {
|
|
350
|
+
const trimmed = String(value || '').trim();
|
|
351
|
+
if ((trimmed.startsWith('"') && trimmed.endsWith('"')) || (trimmed.startsWith("'") && trimmed.endsWith("'"))) return trimmed.slice(1, -1);
|
|
352
|
+
return trimmed;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function readEnvFile(filePath) {
|
|
356
|
+
if (!fs.existsSync(filePath)) return {};
|
|
357
|
+
const values = {};
|
|
358
|
+
const allowed = new Set(['MARROW_API_KEY', 'MARROW_KEY', 'MARROW_BASE_URL', 'MARROW_FLEET_AGENT_ID', 'MARROW_AGENT_ID', 'MARROW_SESSION_ID', 'MARROW_ENFORCEMENT_MODE', 'MARROW_PASSIVE_VALUE_REPORT', 'MARROW_VALUE_REPORT_PERIOD', 'MARROW_AGENT_RUNTIME', 'MARROW_WORKFLOW_GATE', 'MARROW_REQUIRE_OUTCOME_CLOSURE']);
|
|
359
|
+
for (const line of fs.readFileSync(filePath, 'utf8').split(/\\r?\\n/)) {
|
|
360
|
+
const match = line.match(/^\\s*(?:export\\s+)?([A-Z_][A-Z0-9_]*)\\s*=\\s*(.*?)\\s*$/);
|
|
361
|
+
if (!match) continue;
|
|
362
|
+
if (!allowed.has(match[1])) continue;
|
|
363
|
+
let value = match[2] || '';
|
|
364
|
+
const hashIndex = value.search(/\\s+#/);
|
|
365
|
+
if (hashIndex >= 0) value = value.slice(0, hashIndex);
|
|
366
|
+
values[match[1]] = stripQuotes(value);
|
|
367
|
+
}
|
|
368
|
+
return values;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function resolveMarrowEnv() {
|
|
372
|
+
if (process.env.MARROW_API_KEY || process.env.MARROW_KEY) return process.env;
|
|
373
|
+
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
374
|
+
const files = [];
|
|
375
|
+
let dir = process.cwd();
|
|
376
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
377
|
+
files.push(path.join(dir, '.marrow', 'env'), path.join(dir, '.marrow', 'env.local'), path.join(dir, '.env'), path.join(dir, '.env.local'));
|
|
378
|
+
const parent = path.dirname(dir);
|
|
379
|
+
if (parent === dir) break;
|
|
380
|
+
dir = parent;
|
|
381
|
+
}
|
|
382
|
+
files.push(path.join(home, '.marrow', 'env'), path.join(home, '.marrow', 'env.local'));
|
|
383
|
+
for (const filePath of [...new Set(files)]) {
|
|
384
|
+
const values = readEnvFile(filePath);
|
|
385
|
+
if (values.MARROW_API_KEY || values.MARROW_KEY) return { ...process.env, ...values };
|
|
386
|
+
}
|
|
387
|
+
return process.env;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const marrowEnv = resolveMarrowEnv();
|
|
391
|
+
const apiKey = marrowEnv.MARROW_API_KEY || marrowEnv.MARROW_KEY;
|
|
305
392
|
if (apiKey && !globalThis.__MARROW_PASSIVE_RUNTIME__) {
|
|
306
393
|
try {
|
|
307
394
|
const { MarrowClient } = await import('@getmarrow/sdk');
|
|
308
395
|
const marrow = new MarrowClient(apiKey, {
|
|
309
|
-
baseUrl:
|
|
310
|
-
agentId:
|
|
311
|
-
sessionId:
|
|
312
|
-
mode:
|
|
396
|
+
baseUrl: marrowEnv.MARROW_BASE_URL,
|
|
397
|
+
agentId: marrowEnv.MARROW_FLEET_AGENT_ID || marrowEnv.MARROW_AGENT_ID,
|
|
398
|
+
sessionId: marrowEnv.MARROW_SESSION_ID,
|
|
399
|
+
mode: marrowEnv.MARROW_ENFORCEMENT_MODE || 'auto',
|
|
313
400
|
});
|
|
314
401
|
|
|
315
402
|
const runtime = marrow.createPassiveRuntime({
|
|
316
|
-
includeValueReport:
|
|
317
|
-
valueReportPeriod:
|
|
318
|
-
useAgentRuntime:
|
|
319
|
-
useWorkflowGate:
|
|
320
|
-
requireOutcomeClosure:
|
|
403
|
+
includeValueReport: marrowEnv.MARROW_PASSIVE_VALUE_REPORT !== 'false',
|
|
404
|
+
valueReportPeriod: marrowEnv.MARROW_VALUE_REPORT_PERIOD || '7d',
|
|
405
|
+
useAgentRuntime: marrowEnv.MARROW_AGENT_RUNTIME !== 'false',
|
|
406
|
+
useWorkflowGate: marrowEnv.MARROW_WORKFLOW_GATE !== 'false',
|
|
407
|
+
requireOutcomeClosure: marrowEnv.MARROW_REQUIRE_OUTCOME_CLOSURE !== 'false',
|
|
321
408
|
});
|
|
322
409
|
|
|
323
410
|
runtime.install();
|
|
@@ -325,6 +412,8 @@ if (apiKey && !globalThis.__MARROW_PASSIVE_RUNTIME__) {
|
|
|
325
412
|
} catch {
|
|
326
413
|
console.warn('[Marrow] passive runtime skipped: install @getmarrow/sdk or verify SDK initialization. Run npm install @getmarrow/sdk, then rerun npx @getmarrow/install --repair.');
|
|
327
414
|
}
|
|
415
|
+
} else if (!apiKey) {
|
|
416
|
+
console.warn('[Marrow] passive runtime skipped: MARROW_API_KEY missing. Put it in .marrow/env or run npx @getmarrow/install doctor for the exact fix.');
|
|
328
417
|
}
|
|
329
418
|
`;
|
|
330
419
|
}
|
|
@@ -908,8 +997,8 @@ async function install(options) {
|
|
|
908
997
|
missingHooks: changes.filter((change) => change.changed).map((change) => change.label),
|
|
909
998
|
recommendedFix: configDiagnostics.npm_token.recommended_fix || selfTest.recommended_fix || (!options.apiKey
|
|
910
999
|
? envHints.length
|
|
911
|
-
? `MARROW_API_KEY was found in
|
|
912
|
-
: 'Set MARROW_API_KEY, then run npx @getmarrow/install --repair.'
|
|
1000
|
+
? `MARROW_API_KEY was found in ${envHints[0]} and can now be auto-loaded by Marrow SDK/MCP runtimes. Run npx @getmarrow/install --repair to refresh hooks and self-test.`
|
|
1001
|
+
: 'Set MARROW_API_KEY or MARROW_KEY in your shell, MCP secret store, .marrow/env, or ~/.marrow/env, then run npx @getmarrow/install --repair.'
|
|
913
1002
|
: null),
|
|
914
1003
|
},
|
|
915
1004
|
remediation,
|
|
@@ -946,6 +1035,7 @@ module.exports = {
|
|
|
946
1035
|
runSelfTest,
|
|
947
1036
|
runCli,
|
|
948
1037
|
passiveRuntimeSource,
|
|
1038
|
+
resolveMarrowKeyMaterial,
|
|
949
1039
|
inspectNpmTokenConfig,
|
|
950
1040
|
inspectSdkDependency,
|
|
951
1041
|
buildInstallValueMoment,
|