@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 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.1
14
+ ## What's New in v0.1.3
14
15
 
15
- - `doctor` / `--doctor` checks whether Marrow is active without writing files.
16
- - Generated SDK passive runtime enables workflow gates and value summaries by default.
17
- - Doctor output reports missing env, missing hooks/config, self-test state, and recommended fix.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getmarrow/install",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Universal installer for Marrow passive agent setup.",
5
5
  "bin": {
6
6
  "marrow-install": "bin/marrow-install.js"
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) return { skipped: true, reason: 'missing MARROW_API_KEY' };
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.yes && !options.dryRun ? 'write' : 'dry-run';
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 ? 'Set MARROW_API_KEY, then run npx @getmarrow/install doctor.' : null),
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.']
@@ -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
- });