@getmarrow/install 0.1.1 → 0.1.3
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 +12 -4
- package/package.json +1 -1
- package/src/installer.js +93 -3
- package/bin/marrow-install.js +0 -9
package/README.md
CHANGED
|
@@ -7,14 +7,16 @@ Use it when you want Marrow to detect the local agent/runtime environment and wi
|
|
|
7
7
|
```bash
|
|
8
8
|
npx @getmarrow/install --dry-run
|
|
9
9
|
npx @getmarrow/install --yes
|
|
10
|
+
npx @getmarrow/install --repair
|
|
10
11
|
npx @getmarrow/install doctor
|
|
11
12
|
```
|
|
12
13
|
|
|
13
|
-
## What's New in v0.1.
|
|
14
|
+
## What's New in v0.1.3
|
|
14
15
|
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
16
|
+
- Generated SDK passive runtime enables one-call agent runtime checks by default with `MARROW_AGENT_RUNTIME=true`.
|
|
17
|
+
- Self-test now verifies the runtime endpoint in addition to creating a harmless decision and committing its outcome.
|
|
18
|
+
- `--repair` keeps dry-run behavior safe when `--dry-run` is present and reports whether one-call runtime was verified.
|
|
19
|
+
- Doctor/repair output still gives exact safe setup and repair commands without printing credential values.
|
|
18
20
|
|
|
19
21
|
## What It Detects
|
|
20
22
|
|
|
@@ -66,6 +68,12 @@ Doctor check:
|
|
|
66
68
|
MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install doctor
|
|
67
69
|
```
|
|
68
70
|
|
|
71
|
+
Repair missing hooks/config:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
MARROW_API_KEY=mrw_live_xxx npx @getmarrow/install --repair
|
|
75
|
+
```
|
|
76
|
+
|
|
69
77
|
## Trust Model
|
|
70
78
|
|
|
71
79
|
This package is intended to be open source and auditable. It prints every file it will touch, requires `--yes` to write, does not store API keys in project files, and supports MCP-only, SDK-only, both, and markdown-only setups.
|
package/package.json
CHANGED
package/src/installer.js
CHANGED
|
@@ -12,6 +12,7 @@ function parseArgs(argv) {
|
|
|
12
12
|
yes: false,
|
|
13
13
|
dryRun: false,
|
|
14
14
|
doctor: false,
|
|
15
|
+
repair: false,
|
|
15
16
|
mode: 'auto',
|
|
16
17
|
apiKey: process.env.MARROW_API_KEY || '',
|
|
17
18
|
baseUrl: process.env.MARROW_BASE_URL || DEFAULT_BASE_URL,
|
|
@@ -23,6 +24,10 @@ function parseArgs(argv) {
|
|
|
23
24
|
for (let i = 0; i < argv.length; i += 1) {
|
|
24
25
|
const arg = argv[i];
|
|
25
26
|
if (arg === '--yes' || arg === '-y') options.yes = true;
|
|
27
|
+
else if (arg === '--repair' || arg === 'repair') {
|
|
28
|
+
options.repair = true;
|
|
29
|
+
options.yes = true;
|
|
30
|
+
}
|
|
26
31
|
else if (arg === '--dry-run') options.dryRun = true;
|
|
27
32
|
else if (arg === '--doctor' || arg === 'doctor' || arg === 'check') options.doctor = true;
|
|
28
33
|
else if (arg === '--json') options.json = true;
|
|
@@ -58,6 +63,7 @@ function usage() {
|
|
|
58
63
|
return `Usage:
|
|
59
64
|
npx @getmarrow/install --dry-run
|
|
60
65
|
npx @getmarrow/install --yes
|
|
66
|
+
npx @getmarrow/install --repair
|
|
61
67
|
npx @getmarrow/install doctor
|
|
62
68
|
npx @getmarrow/install --mcp --yes
|
|
63
69
|
npx @getmarrow/install --sdk --yes
|
|
@@ -65,6 +71,7 @@ function usage() {
|
|
|
65
71
|
Options:
|
|
66
72
|
--dry-run Print planned changes without writing
|
|
67
73
|
--doctor Check install health without writing
|
|
74
|
+
--repair Write missing hooks/config, then run self-test and status check
|
|
68
75
|
--yes, -y Write detected config files
|
|
69
76
|
--mode <mode> auto, mcp, sdk, both, or md
|
|
70
77
|
--key <key> Marrow API key for self-test. Prefer MARROW_API_KEY because CLI args can appear in process listings.
|
|
@@ -135,6 +142,24 @@ function detectEnvironment(cwd = process.cwd(), env = process.env) {
|
|
|
135
142
|
};
|
|
136
143
|
}
|
|
137
144
|
|
|
145
|
+
function findLikelyEnvFiles(detection, env = process.env) {
|
|
146
|
+
const home = env.HOME || env.USERPROFILE || os.homedir();
|
|
147
|
+
const candidates = [
|
|
148
|
+
path.join(detection.root, '.env'),
|
|
149
|
+
path.join(detection.root, '.env.local'),
|
|
150
|
+
path.join(detection.root, '.marrow', 'env'),
|
|
151
|
+
path.join(detection.root, '.marrow', 'env.local'),
|
|
152
|
+
path.join(home, '.marrow', 'env'),
|
|
153
|
+
path.join(home, '.openclaw', 'credentials', 'marrow-mcp.env'),
|
|
154
|
+
path.join(home, '.openclaw', 'gateway.systemd.env'),
|
|
155
|
+
];
|
|
156
|
+
return candidates.filter((filePath) => {
|
|
157
|
+
if (!exists(filePath)) return false;
|
|
158
|
+
const raw = safeRead(filePath);
|
|
159
|
+
return /\bMARROW_API_KEY\s*=/.test(raw) || /\bMARROW_KEY(_[A-Z0-9]+)?\s*=/.test(raw);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
138
163
|
function passiveInstructions() {
|
|
139
164
|
return `${MARROW_BLOCK_START}
|
|
140
165
|
## Marrow Passive Agent Memory
|
|
@@ -169,6 +194,7 @@ if (apiKey && !globalThis.__MARROW_PASSIVE_RUNTIME__) {
|
|
|
169
194
|
const runtime = marrow.createPassiveRuntime({
|
|
170
195
|
includeValueReport: process.env.MARROW_PASSIVE_VALUE_REPORT !== 'false',
|
|
171
196
|
valueReportPeriod: process.env.MARROW_VALUE_REPORT_PERIOD || '7d',
|
|
197
|
+
useAgentRuntime: process.env.MARROW_AGENT_RUNTIME !== 'false',
|
|
172
198
|
useWorkflowGate: process.env.MARROW_WORKFLOW_GATE !== 'false',
|
|
173
199
|
});
|
|
174
200
|
|
|
@@ -185,6 +211,7 @@ MARROW_FLEET_AGENT_ID=agent-or-fleet-id
|
|
|
185
211
|
MARROW_ENFORCEMENT_MODE=auto
|
|
186
212
|
MARROW_PASSIVE_BRIEF=auto
|
|
187
213
|
MARROW_PASSIVE_VALUE_REPORT=true
|
|
214
|
+
MARROW_AGENT_RUNTIME=true
|
|
188
215
|
MARROW_WORKFLOW_GATE=true
|
|
189
216
|
`;
|
|
190
217
|
}
|
|
@@ -370,7 +397,13 @@ async function requestJson(url, options) {
|
|
|
370
397
|
|
|
371
398
|
async function runSelfTest(options) {
|
|
372
399
|
if (!options.selfTest) return { skipped: true, reason: 'disabled' };
|
|
373
|
-
if (!options.apiKey)
|
|
400
|
+
if (!options.apiKey) {
|
|
401
|
+
return {
|
|
402
|
+
skipped: true,
|
|
403
|
+
reason: 'missing MARROW_API_KEY',
|
|
404
|
+
exact_fix: 'export MARROW_API_KEY=mrw_live_... && npx @getmarrow/install --repair',
|
|
405
|
+
};
|
|
406
|
+
}
|
|
374
407
|
|
|
375
408
|
const headers = {
|
|
376
409
|
authorization: `Bearer ${options.apiKey}`,
|
|
@@ -403,6 +436,23 @@ async function runSelfTest(options) {
|
|
|
403
436
|
});
|
|
404
437
|
|
|
405
438
|
const status = await requestJson(`${baseUrl}/v1/agent/status`, { headers });
|
|
439
|
+
const runtime = await requestJson(`${baseUrl}/v1/agent/runtime`, {
|
|
440
|
+
method: 'POST',
|
|
441
|
+
headers,
|
|
442
|
+
body: JSON.stringify({
|
|
443
|
+
action: 'Marrow passive install self-test: verify one-call agent runtime and outcome closure',
|
|
444
|
+
type: 'process',
|
|
445
|
+
role: 'general',
|
|
446
|
+
surfaces: ['workspace'],
|
|
447
|
+
proof: {
|
|
448
|
+
checks: ['installer self-test'],
|
|
449
|
+
outcome: 'self-test outcome committed',
|
|
450
|
+
},
|
|
451
|
+
}),
|
|
452
|
+
}).catch((error) => ({
|
|
453
|
+
ok: false,
|
|
454
|
+
error: error instanceof Error ? error.message : String(error),
|
|
455
|
+
}));
|
|
406
456
|
return {
|
|
407
457
|
skipped: false,
|
|
408
458
|
decision_id: decisionId,
|
|
@@ -410,6 +460,11 @@ async function runSelfTest(options) {
|
|
|
410
460
|
health: status.health || null,
|
|
411
461
|
last_event_at: status.last_event_at || null,
|
|
412
462
|
recommended_fix: status.recommended_fix || null,
|
|
463
|
+
next_action: status.next_action || null,
|
|
464
|
+
auto_outcome_closure: status.auto_outcome_closure || null,
|
|
465
|
+
runtime_active: Boolean(runtime && runtime.ok !== false),
|
|
466
|
+
runtime_exact_next_action: runtime.exact_next_action || null,
|
|
467
|
+
runtime_before_you_act: runtime.before_you_act || null,
|
|
413
468
|
};
|
|
414
469
|
}
|
|
415
470
|
|
|
@@ -433,16 +488,28 @@ function printReport(report) {
|
|
|
433
488
|
process.stdout.write('\nSelf-test:\n');
|
|
434
489
|
if (report.selfTest.skipped) {
|
|
435
490
|
process.stdout.write(`- skipped: ${report.selfTest.reason}\n`);
|
|
491
|
+
if (report.selfTest.exact_fix) process.stdout.write(`- exact fix: ${report.selfTest.exact_fix}\n`);
|
|
436
492
|
} else {
|
|
437
493
|
process.stdout.write(`- active: ${report.selfTest.active ? 'yes' : 'no'}\n`);
|
|
438
494
|
process.stdout.write(`- decision_id: ${report.selfTest.decision_id}\n`);
|
|
439
495
|
process.stdout.write(`- health: ${report.selfTest.health || 'unknown'}\n`);
|
|
496
|
+
process.stdout.write(`- one-call runtime: ${report.selfTest.runtime_active ? 'active' : 'not verified'}\n`);
|
|
497
|
+
if (report.selfTest.next_action) process.stdout.write(`- next action: ${report.selfTest.next_action}\n`);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (report.remediation) {
|
|
501
|
+
process.stdout.write('\nRemediation:\n');
|
|
502
|
+
process.stdout.write(`- attempted: ${report.remediation.attempted ? 'yes' : 'no'}\n`);
|
|
503
|
+
process.stdout.write(`- fixed config: ${report.remediation.fixedConfig ? 'yes' : 'no'}\n`);
|
|
504
|
+
process.stdout.write(`- self-test passed: ${report.remediation.selfTestPassed ? 'yes' : 'no'}\n`);
|
|
505
|
+
if (report.remediation.message) process.stdout.write(`- result: ${report.remediation.message}\n`);
|
|
440
506
|
}
|
|
441
507
|
|
|
442
508
|
if (report.writeMode === 'doctor') {
|
|
443
509
|
process.stdout.write('\nDoctor:\n');
|
|
444
510
|
process.stdout.write(`- Marrow active: ${report.doctor.active ? 'yes' : 'no'}\n`);
|
|
445
511
|
process.stdout.write(`- missing env: ${report.doctor.missingEnv.length ? report.doctor.missingEnv.join(', ') : 'none'}\n`);
|
|
512
|
+
if (report.doctor.envHints.length) process.stdout.write(`- possible env files: ${report.doctor.envHints.join(', ')}\n`);
|
|
446
513
|
process.stdout.write(`- missing hooks/config: ${report.doctor.missingHooks.length ? report.doctor.missingHooks.join('; ') : 'none'}\n`);
|
|
447
514
|
if (report.doctor.recommendedFix) process.stdout.write(`- recommended fix: ${report.doctor.recommendedFix}\n`);
|
|
448
515
|
}
|
|
@@ -462,13 +529,30 @@ function printReport(report) {
|
|
|
462
529
|
async function install(options) {
|
|
463
530
|
const detection = detectEnvironment(options.cwd);
|
|
464
531
|
const plan = buildPlan(detection, options);
|
|
465
|
-
const writeMode = options.doctor ? 'doctor' : options.
|
|
532
|
+
const writeMode = options.doctor ? 'doctor' : options.dryRun ? 'dry-run' : options.repair ? 'repair' : options.yes ? 'write' : 'dry-run';
|
|
466
533
|
const changes = applyPlan(plan, options);
|
|
534
|
+
const envHints = options.apiKey ? [] : findLikelyEnvFiles(detection);
|
|
467
535
|
const selfTest = await runSelfTest(options).catch((error) => ({
|
|
468
536
|
skipped: false,
|
|
469
537
|
active: false,
|
|
470
538
|
error: error instanceof Error ? error.message : String(error),
|
|
471
539
|
}));
|
|
540
|
+
const changedConfig = changes.some((change) => change.changed);
|
|
541
|
+
const selfTestPassed = Boolean(!selfTest.skipped && selfTest.active && !selfTest.error);
|
|
542
|
+
const remediation = options.repair
|
|
543
|
+
? {
|
|
544
|
+
attempted: true,
|
|
545
|
+
fixedConfig: changedConfig,
|
|
546
|
+
selfTestPassed,
|
|
547
|
+
message: selfTestPassed
|
|
548
|
+
? selfTest.health === 'healthy'
|
|
549
|
+
? 'I fixed Marrow passive config, one-call runtime is active, and self-test passed.'
|
|
550
|
+
: `I fixed Marrow passive config and self-test passed; status is ${selfTest.health || 'unknown'}${selfTest.next_action ? `. Next action: ${selfTest.next_action}` : ''}.`
|
|
551
|
+
: selfTest.skipped
|
|
552
|
+
? `Config repair ran, but self-test skipped: ${selfTest.reason}.`
|
|
553
|
+
: `Config repair ran, but self-test failed: ${selfTest.error || 'unknown error'}.`,
|
|
554
|
+
}
|
|
555
|
+
: null;
|
|
472
556
|
|
|
473
557
|
return {
|
|
474
558
|
root: detection.root,
|
|
@@ -487,9 +571,15 @@ async function install(options) {
|
|
|
487
571
|
doctor: {
|
|
488
572
|
active: Boolean(!selfTest.skipped && selfTest.active),
|
|
489
573
|
missingEnv: options.apiKey ? [] : ['MARROW_API_KEY'],
|
|
574
|
+
envHints,
|
|
490
575
|
missingHooks: changes.filter((change) => change.changed).map((change) => change.label),
|
|
491
|
-
recommendedFix: selfTest.recommended_fix || (!options.apiKey
|
|
576
|
+
recommendedFix: selfTest.recommended_fix || (!options.apiKey
|
|
577
|
+
? envHints.length
|
|
578
|
+
? `MARROW_API_KEY was found in a likely env file at ${envHints[0]}. Load that key from trusted secret storage, export only MARROW_API_KEY, then run npx @getmarrow/install --repair.`
|
|
579
|
+
: 'Set MARROW_API_KEY, then run npx @getmarrow/install --repair.'
|
|
580
|
+
: null),
|
|
492
581
|
},
|
|
582
|
+
remediation,
|
|
493
583
|
selfTest,
|
|
494
584
|
warnings: options.keyFromArg
|
|
495
585
|
? ['Avoid --key in shared shells because command-line arguments can be visible in process listings. Prefer MARROW_API_KEY in your environment or secret manager.']
|
package/bin/marrow-install.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const { runCli } = require('../src/installer');
|
|
4
|
-
|
|
5
|
-
runCli(process.argv.slice(2)).catch((error) => {
|
|
6
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
7
|
-
process.stderr.write(`marrow-install failed: ${message}\n`);
|
|
8
|
-
process.exit(1);
|
|
9
|
-
});
|