@namzu/cli 11.0.0 → 12.0.0

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.
Files changed (134) hide show
  1. package/README.md +390 -46
  2. package/dist/cli.d.ts +15 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +132 -4
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands/acp.d.ts +29 -0
  7. package/dist/commands/acp.d.ts.map +1 -0
  8. package/dist/commands/acp.js +156 -0
  9. package/dist/commands/acp.js.map +1 -0
  10. package/dist/commands/doctor.d.ts +12 -1
  11. package/dist/commands/doctor.d.ts.map +1 -1
  12. package/dist/commands/doctor.js +18 -2
  13. package/dist/commands/doctor.js.map +1 -1
  14. package/dist/commands/drain.d.ts +2 -2
  15. package/dist/commands/drain.d.ts.map +1 -1
  16. package/dist/commands/drain.js +31 -8
  17. package/dist/commands/drain.js.map +1 -1
  18. package/dist/commands/run-flags.d.ts.map +1 -1
  19. package/dist/commands/run-flags.js +33 -0
  20. package/dist/commands/run-flags.js.map +1 -1
  21. package/dist/commands/run-stream.d.ts +3 -3
  22. package/dist/commands/run-stream.d.ts.map +1 -1
  23. package/dist/commands/run-stream.js +27 -10
  24. package/dist/commands/run-stream.js.map +1 -1
  25. package/dist/commands/run.d.ts.map +1 -1
  26. package/dist/commands/run.js +50 -4
  27. package/dist/commands/run.js.map +1 -1
  28. package/dist/commands/types.d.ts +10 -0
  29. package/dist/commands/types.d.ts.map +1 -1
  30. package/dist/config/load.d.ts +64 -0
  31. package/dist/config/load.d.ts.map +1 -1
  32. package/dist/config/load.js +137 -20
  33. package/dist/config/load.js.map +1 -1
  34. package/dist/config/schema.d.ts +51 -0
  35. package/dist/config/schema.d.ts.map +1 -1
  36. package/dist/config/schema.js.map +1 -1
  37. package/dist/context/capabilities.d.ts +88 -0
  38. package/dist/context/capabilities.d.ts.map +1 -0
  39. package/dist/context/capabilities.js +160 -0
  40. package/dist/context/capabilities.js.map +1 -0
  41. package/dist/context/sandbox.d.ts +13 -0
  42. package/dist/context/sandbox.d.ts.map +1 -1
  43. package/dist/context/sandbox.js +15 -0
  44. package/dist/context/sandbox.js.map +1 -1
  45. package/dist/doctor/checks/index.d.ts +6 -1
  46. package/dist/doctor/checks/index.d.ts.map +1 -1
  47. package/dist/doctor/checks/index.js +45 -2
  48. package/dist/doctor/checks/index.js.map +1 -1
  49. package/dist/doctor/checks/invariants.d.ts +23 -0
  50. package/dist/doctor/checks/invariants.d.ts.map +1 -0
  51. package/dist/doctor/checks/invariants.js +79 -0
  52. package/dist/doctor/checks/invariants.js.map +1 -0
  53. package/dist/doctor/checks/logging.d.ts +10 -0
  54. package/dist/doctor/checks/logging.d.ts.map +1 -0
  55. package/dist/doctor/checks/logging.js +69 -0
  56. package/dist/doctor/checks/logging.js.map +1 -0
  57. package/dist/doctor/checks/session-export.d.ts +21 -0
  58. package/dist/doctor/checks/session-export.d.ts.map +1 -0
  59. package/dist/doctor/checks/session-export.js +62 -0
  60. package/dist/doctor/checks/session-export.js.map +1 -0
  61. package/dist/doctor/checks/telemetry.d.ts +15 -28
  62. package/dist/doctor/checks/telemetry.d.ts.map +1 -1
  63. package/dist/doctor/checks/telemetry.js +34 -54
  64. package/dist/doctor/checks/telemetry.js.map +1 -1
  65. package/dist/doctor/checks/vault.d.ts +4 -20
  66. package/dist/doctor/checks/vault.d.ts.map +1 -1
  67. package/dist/doctor/checks/vault.js +58 -19
  68. package/dist/doctor/checks/vault.js.map +1 -1
  69. package/dist/doctor/registry.d.ts.map +1 -1
  70. package/dist/doctor/registry.js +13 -6
  71. package/dist/doctor/registry.js.map +1 -1
  72. package/dist/index.d.ts +3 -2
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +3 -2
  75. package/dist/index.js.map +1 -1
  76. package/dist/integrations/files/attachment-store.d.ts +44 -0
  77. package/dist/integrations/files/attachment-store.d.ts.map +1 -0
  78. package/dist/integrations/files/attachment-store.js +86 -0
  79. package/dist/integrations/files/attachment-store.js.map +1 -0
  80. package/dist/integrations/providers/credential-provider.d.ts +39 -0
  81. package/dist/integrations/providers/credential-provider.d.ts.map +1 -0
  82. package/dist/integrations/providers/credential-provider.js +74 -0
  83. package/dist/integrations/providers/credential-provider.js.map +1 -0
  84. package/dist/integrations/providers/discover.d.ts.map +1 -1
  85. package/dist/integrations/providers/discover.js +15 -3
  86. package/dist/integrations/providers/discover.js.map +1 -1
  87. package/dist/integrations/providers/oauth.d.ts.map +1 -1
  88. package/dist/integrations/providers/oauth.js +24 -1
  89. package/dist/integrations/providers/oauth.js.map +1 -1
  90. package/dist/integrations/sessions/store.d.ts +2 -2
  91. package/dist/integrations/sessions/store.d.ts.map +1 -1
  92. package/dist/integrations/sessions/store.js +21 -9
  93. package/dist/integrations/sessions/store.js.map +1 -1
  94. package/dist/integrations/subagents/runtime.d.ts +3 -3
  95. package/dist/integrations/subagents/runtime.d.ts.map +1 -1
  96. package/dist/integrations/subagents/runtime.js +11 -11
  97. package/dist/integrations/subagents/runtime.js.map +1 -1
  98. package/dist/integrations/telemetry/session-export.d.ts +99 -0
  99. package/dist/integrations/telemetry/session-export.d.ts.map +1 -0
  100. package/dist/integrations/telemetry/session-export.js +119 -0
  101. package/dist/integrations/telemetry/session-export.js.map +1 -0
  102. package/dist/logging.d.ts +79 -0
  103. package/dist/logging.d.ts.map +1 -0
  104. package/dist/logging.js +67 -0
  105. package/dist/logging.js.map +1 -0
  106. package/dist/permissions/rules.d.ts +3 -3
  107. package/dist/permissions/rules.d.ts.map +1 -1
  108. package/dist/permissions/rules.js +1 -1
  109. package/dist/permissions/rules.js.map +1 -1
  110. package/dist/skills/store.d.ts +1 -1
  111. package/dist/skills/store.js +1 -1
  112. package/dist/tui/App.d.ts.map +1 -1
  113. package/dist/tui/App.js +92 -6
  114. package/dist/tui/App.js.map +1 -1
  115. package/dist/tui/StatusBar.d.ts +1 -1
  116. package/dist/tui/StatusBar.d.ts.map +1 -1
  117. package/dist/tui/agent.d.ts +54 -24
  118. package/dist/tui/agent.d.ts.map +1 -1
  119. package/dist/tui/agent.js +327 -124
  120. package/dist/tui/agent.js.map +1 -1
  121. package/dist/tui/index.d.ts.map +1 -1
  122. package/dist/tui/index.js +17 -6
  123. package/dist/tui/index.js.map +1 -1
  124. package/dist/tui/log-pane.d.ts +48 -0
  125. package/dist/tui/log-pane.d.ts.map +1 -0
  126. package/dist/tui/log-pane.js +106 -0
  127. package/dist/tui/log-pane.js.map +1 -0
  128. package/dist/tui/slashCommands.d.ts +134 -11
  129. package/dist/tui/slashCommands.d.ts.map +1 -1
  130. package/dist/tui/slashCommands.js +169 -15
  131. package/dist/tui/slashCommands.js.map +1 -1
  132. package/dist/tui/types.d.ts +11 -2
  133. package/dist/tui/types.d.ts.map +1 -1
  134. package/package.json +8 -6
@@ -1,11 +1,46 @@
1
+ import { capabilityCheckId } from '../../context/capabilities.js';
1
2
  import { providerChainCheck } from './chain.js';
2
3
  import { credentialSourcesCheck } from './credentials.js';
4
+ import { invariantsCheck } from './invariants.js';
5
+ import { loggingPipelineCheck } from './logging.js';
3
6
  import { providersRegisteredCheck } from './providers.js';
4
7
  import { cwdWritableCheck, tmpdirWritableCheck } from './runtime.js';
5
8
  import { sandboxPlatformCheck } from './sandbox.js';
6
- import { telemetryInstalledCheck } from './telemetry.js';
9
+ import { describeInstalledPackage, telemetryInstalledCheck } from './telemetry.js';
7
10
  import { vaultRegisteredCheck } from './vault.js';
8
- export { credentialSourcesCheck, providerChainCheck, providersRegisteredCheck, cwdWritableCheck, tmpdirWritableCheck, sandboxPlatformCheck, telemetryInstalledCheck, vaultRegisteredCheck, };
11
+ /**
12
+ * Build an `installed` check for one optional package, over the same
13
+ * absent/present/broken mapping `telemetryInstalledCheck` already used.
14
+ *
15
+ * `telemetryInstalledCheck` keeps its own hand-written definition below
16
+ * rather than being rebuilt through this factory — it shipped first, and
17
+ * `doctor/checks/__tests__/telemetry.test.ts` already exercises that exact
18
+ * object with zero edits required by NZ-BOOT-02. A fifth optional package
19
+ * needs one call to this factory, not a fourth hand-written check that can
20
+ * drift from the other three.
21
+ */
22
+ function installedCheck(specifier, category) {
23
+ return {
24
+ id: capabilityCheckId(specifier),
25
+ category,
26
+ run: () => describeInstalledPackage(specifier),
27
+ };
28
+ }
29
+ // `sandboxPlatformCheck` already owns the `sandbox` category for a different
30
+ // question (does THIS PLATFORM enforce isolation); this check asking IS THE
31
+ // PACKAGE HERE fits the same category. `DoctorCategory` has no `files` or
32
+ // `computer-use` member, and widening that union is an SDK type change this
33
+ // task does not make, so those two register under `custom` — the category
34
+ // the registry's own tests already use for exactly this situation.
35
+ export const sandboxInstalledCheck = installedCheck('@namzu/sandbox', 'sandbox');
36
+ export const filesInstalledCheck = installedCheck('@namzu/files', 'custom');
37
+ export const computerUseInstalledCheck = installedCheck('@namzu/computer-use', 'custom');
38
+ // NOTE the fix versus the original submission: sandboxInstalledCheck /
39
+ // filesInstalledCheck / computerUseInstalledCheck are NOT repeated here.
40
+ // They already carry their own `export` keyword above; naming them again in
41
+ // this aggregation block is a duplicate-export declaration and fails
42
+ // `tsc --build` with TS2323/TS2484 (confirmed by building this exact file).
43
+ export { credentialSourcesCheck, providerChainCheck, providersRegisteredCheck, cwdWritableCheck, tmpdirWritableCheck, invariantsCheck, loggingPipelineCheck, sandboxPlatformCheck, telemetryInstalledCheck, vaultRegisteredCheck, };
9
44
  export const builtInDoctorChecks = [
10
45
  sandboxPlatformCheck,
11
46
  cwdWritableCheck,
@@ -17,6 +52,14 @@ export const builtInDoctorChecks = [
17
52
  // the operator has seen what was found.
18
53
  providerChainCheck,
19
54
  vaultRegisteredCheck,
55
+ sandboxInstalledCheck,
56
+ filesInstalledCheck,
57
+ computerUseInstalledCheck,
20
58
  telemetryInstalledCheck,
59
+ // Last, deliberately: it reports what the pipeline did to the records
60
+ // every check above it just produced, so running it first would report a
61
+ // process that had barely logged anything yet.
62
+ loggingPipelineCheck,
63
+ invariantsCheck,
21
64
  ];
22
65
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/doctor/checks/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AACzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AACzD,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAA;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAEjD,OAAO,EACN,sBAAsB,EACtB,kBAAkB,EAClB,wBAAwB,EACxB,gBAAgB,EAChB,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,oBAAoB,GACpB,CAAA;AAED,MAAM,CAAC,MAAM,mBAAmB,GAA2B;IAC1D,oBAAoB;IACpB,gBAAgB;IAChB,mBAAmB;IACnB,wBAAwB;IACxB,sBAAsB;IACtB,4EAA4E;IAC5E,0EAA0E;IAC1E,wCAAwC;IACxC,kBAAkB;IAClB,oBAAoB;IACpB,uBAAuB;CACvB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/doctor/checks/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAA;AACjE,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AACzD,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAA;AAClF,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAEjD;;;;;;;;;;GAUG;AACH,SAAS,cAAc,CAAC,SAAiB,EAAE,QAAiC;IAC3E,OAAO;QACN,EAAE,EAAE,iBAAiB,CAAC,SAAS,CAAC;QAChC,QAAQ;QACR,GAAG,EAAE,GAA+B,EAAE,CAAC,wBAAwB,CAAC,SAAS,CAAC;KAC1E,CAAA;AACF,CAAC;AAED,6EAA6E;AAC7E,4EAA4E;AAC5E,0EAA0E;AAC1E,4EAA4E;AAC5E,0EAA0E;AAC1E,mEAAmE;AACnE,MAAM,CAAC,MAAM,qBAAqB,GAAgB,cAAc,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAA;AAC7F,MAAM,CAAC,MAAM,mBAAmB,GAAgB,cAAc,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAA;AACxF,MAAM,CAAC,MAAM,yBAAyB,GAAgB,cAAc,CACnE,qBAAqB,EACrB,QAAQ,CACR,CAAA;AAED,uEAAuE;AACvE,yEAAyE;AACzE,4EAA4E;AAC5E,qEAAqE;AACrE,4EAA4E;AAC5E,OAAO,EACN,sBAAsB,EACtB,kBAAkB,EAClB,wBAAwB,EACxB,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,uBAAuB,EACvB,oBAAoB,GACpB,CAAA;AAED,MAAM,CAAC,MAAM,mBAAmB,GAA2B;IAC1D,oBAAoB;IACpB,gBAAgB;IAChB,mBAAmB;IACnB,wBAAwB;IACxB,sBAAsB;IACtB,4EAA4E;IAC5E,0EAA0E;IAC1E,wCAAwC;IACxC,kBAAkB;IAClB,oBAAoB;IACpB,qBAAqB;IACrB,mBAAmB;IACnB,yBAAyB;IACzB,uBAAuB;IACvB,sEAAsE;IACtE,yEAAyE;IACzE,+CAA+C;IAC/C,oBAAoB;IACpB,eAAe;CACf,CAAA"}
@@ -0,0 +1,23 @@
1
+ import type { DoctorCheck, DoctorCheckResult, InvariantRegistry } from '@namzu/sdk';
2
+ /**
3
+ * Evaluate every invariant `registry` holds and fold the outcomes, plus each
4
+ * invariant's own violation counter, into one doctor row.
5
+ *
6
+ * Exported separately from the `DoctorCheck` — same reasoning as
7
+ * `describeInstalledPackage` on the telemetry check — so a test can drive a
8
+ * SCOPED registry directly, rather than mutating the process-wide singleton
9
+ * every other test also reads. The check itself is still exercised through
10
+ * `invariantsCheck.run`, because a helper proven in isolation says nothing
11
+ * about whether the check reaches it.
12
+ *
13
+ * Every invariant is evaluated with `ctx: undefined`. `namzu doctor` runs
14
+ * with no live run and no candidate compaction reduction — there is nothing
15
+ * a real invariant's check could read that is specific to one run — so
16
+ * `undefined` is the honest context, not a shortcut: each check registered
17
+ * against it is written to answer `unknown` rather than guess (see
18
+ * `compaction.ts` and `claim-disk.ts` in `@namzu/sdk`), and this row reports
19
+ * that state rather than papering over it as a pass.
20
+ */
21
+ export declare function describeInvariants(registry: InvariantRegistry): Promise<DoctorCheckResult>;
22
+ export declare const invariantsCheck: DoctorCheck;
23
+ //# sourceMappingURL=invariants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariants.d.ts","sourceRoot":"","sources":["../../../src/doctor/checks/invariants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAanF;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAyDhG;AAED,eAAO,MAAM,eAAe,EAAE,WAI7B,CAAA"}
@@ -0,0 +1,79 @@
1
+ import { invariants } from '@namzu/sdk';
2
+ function label(row) {
3
+ return `${row.id} (${row.violations} violation${row.violations === 1 ? '' : 's'})`;
4
+ }
5
+ /**
6
+ * Evaluate every invariant `registry` holds and fold the outcomes, plus each
7
+ * invariant's own violation counter, into one doctor row.
8
+ *
9
+ * Exported separately from the `DoctorCheck` — same reasoning as
10
+ * `describeInstalledPackage` on the telemetry check — so a test can drive a
11
+ * SCOPED registry directly, rather than mutating the process-wide singleton
12
+ * every other test also reads. The check itself is still exercised through
13
+ * `invariantsCheck.run`, because a helper proven in isolation says nothing
14
+ * about whether the check reaches it.
15
+ *
16
+ * Every invariant is evaluated with `ctx: undefined`. `namzu doctor` runs
17
+ * with no live run and no candidate compaction reduction — there is nothing
18
+ * a real invariant's check could read that is specific to one run — so
19
+ * `undefined` is the honest context, not a shortcut: each check registered
20
+ * against it is written to answer `unknown` rather than guess (see
21
+ * `compaction.ts` and `claim-disk.ts` in `@namzu/sdk`), and this row reports
22
+ * that state rather than papering over it as a pass.
23
+ */
24
+ export async function describeInvariants(registry) {
25
+ const ids = registry.listIds();
26
+ if (ids.length === 0) {
27
+ return { status: 'skipped', message: 'no invariants registered' };
28
+ }
29
+ const rows = await Promise.all(ids.map(async (id) => ({
30
+ id,
31
+ outcome: await registry.evaluate(id, undefined),
32
+ violations: registry.violationCount(id),
33
+ })));
34
+ const violated = rows.filter((r) => r.outcome.state === 'violated');
35
+ const unknown = rows.filter((r) => r.outcome.state === 'unknown');
36
+ if (violated.length > 0) {
37
+ const detail = violated
38
+ .map((r) => (r.outcome.state === 'violated' ? `${label(r)}: ${r.outcome.detail}` : label(r)))
39
+ .join('; ');
40
+ return {
41
+ status: 'fail',
42
+ message: `${violated.length}/${ids.length} invariant(s) violated — ${detail}`,
43
+ remediation: 'Check the warn/error logs at the module that registered the violated invariant.',
44
+ };
45
+ }
46
+ if (unknown.length > 0) {
47
+ const names = unknown.map(label).join(', ');
48
+ // `skipped`, not `inconclusive`, and the difference is the whole
49
+ // behaviour of this row. `inconclusive` maps to exit 69 in
50
+ // `doctor/registry.ts` and means "I asked and could not establish an
51
+ // answer" — a real signal that something is wrong with the check
52
+ // itself. A runtime invariant outside a live run is a different thing:
53
+ // the question has no subject yet, every registered check is written
54
+ // to answer `unknown` here on purpose, and so this state is reached on
55
+ // EVERY doctor run. Reporting it as inconclusive made `namzu doctor`
56
+ // exit 69 unconditionally, which retires the one code that was
57
+ // supposed to mean something.
58
+ //
59
+ // `skipped` is not "satisfied" either — the summary counts it
60
+ // separately from `pass`, and the message still names which invariants
61
+ // went unevaluated and how many violations each has accumulated. The
62
+ // violation counters above are what actually carry a live run's
63
+ // findings back to this row.
64
+ return {
65
+ status: 'skipped',
66
+ message: `${unknown.length}/${ids.length} invariant(s) have no subject outside a live run: ${names}`,
67
+ };
68
+ }
69
+ return {
70
+ status: 'pass',
71
+ message: `${ids.length} invariant(s) hold: ${rows.map(label).join(', ')}`,
72
+ };
73
+ }
74
+ export const invariantsCheck = {
75
+ id: 'runtime.invariants',
76
+ category: 'runtime',
77
+ run: () => describeInvariants(invariants),
78
+ };
79
+ //# sourceMappingURL=invariants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"invariants.js","sourceRoot":"","sources":["../../../src/doctor/checks/invariants.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { DoctorCheck, DoctorCheckResult, LogSinkCounters } from '@namzu/sdk';
2
+ /**
3
+ * Exported separately from the check so all three outcomes can be driven
4
+ * directly, the same arrangement `describeInstalledPackage` uses: a check
5
+ * whose only entry point is `run` can be tested for the state the machine
6
+ * happens to be in and nothing else.
7
+ */
8
+ export declare function describeLogPipeline(counters: LogSinkCounters | undefined): DoctorCheckResult;
9
+ export declare const loggingPipelineCheck: DoctorCheck;
10
+ //# sourceMappingURL=logging.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging.d.ts","sourceRoot":"","sources":["../../../src/doctor/checks/logging.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAkCjF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,eAAe,GAAG,SAAS,GAAG,iBAAiB,CA6B5F;AAED,eAAO,MAAM,oBAAoB,EAAE,WAOlC,CAAA"}
@@ -0,0 +1,69 @@
1
+ import { getLogCounters } from '@namzu/sdk';
2
+ /**
3
+ * What the log pipeline actually did to this process's records.
4
+ *
5
+ * `LogSinkCounters` has counted five things on every record since the sink
6
+ * seam landed — dropped, redacted, attributes shed, values truncated,
7
+ * records shrunk — and until this check nothing read any of them. That is
8
+ * `declared-but-undriven` with an unusually direct tell: the comment above
9
+ * `createLogger` promised "a caller that wants to observe the pipeline — a
10
+ * test, `namzu doctor` later — reads `.counters` off the same reference",
11
+ * and `namzu doctor` did not. This file is that comment coming true.
12
+ *
13
+ * The counters only became readable as part of this change. They lived on
14
+ * whatever logger `createLogger` happened to build, and `getRootLogger`
15
+ * builds a new one on every call, so each count died with the expression
16
+ * that produced it. `installProcessSink` now owns one set for the process
17
+ * and every logger routed through it adds to those totals.
18
+ */
19
+ /** Field-by-field, so a new counter cannot be silently left unreported. */
20
+ const FIELDS = [
21
+ 'dropped',
22
+ 'redacted',
23
+ 'attributesDropped',
24
+ 'valuesTruncated',
25
+ 'recordsTruncated',
26
+ ];
27
+ function summarise(counters) {
28
+ return FIELDS.map((f) => `${f}=${counters[f]}`).join(' ');
29
+ }
30
+ /**
31
+ * Exported separately from the check so all three outcomes can be driven
32
+ * directly, the same arrangement `describeInstalledPackage` uses: a check
33
+ * whose only entry point is `run` can be tested for the state the machine
34
+ * happens to be in and nothing else.
35
+ */
36
+ export function describeLogPipeline(counters) {
37
+ if (counters === undefined) {
38
+ // NOT `pass`, and not five zeros dressed up as one. Nothing measured
39
+ // these records, so "nothing was dropped" is a claim this check has no
40
+ // standing to make — and it is the claim a reader would take from a
41
+ // green row. The CLI installs a sink during boot, so reaching here at
42
+ // all means that boot step did not run.
43
+ return {
44
+ status: 'inconclusive',
45
+ message: 'no log sink installed — records reach stderr through the legacy path, where nothing is counted, redacted or size-capped',
46
+ remediation: 'A host owns its log destination: call installProcessSink(sink, level) during boot. The CLI does this itself, so seeing this from `namzu doctor` means that step did not run.',
47
+ };
48
+ }
49
+ if (counters.dropped > 0) {
50
+ return {
51
+ status: 'fail',
52
+ message: `${counters.dropped} log record(s) never reached the sink — ${summarise(counters)}`,
53
+ remediation: 'A record is counted dropped when the installed sink threw, or when the sink is NOOP_SINK. Check the sink passed to installProcessSink: a sink that throws is caught so one bad record cannot fail a run, which is exactly why the count is the only place this shows up.',
54
+ };
55
+ }
56
+ return {
57
+ status: 'pass',
58
+ message: `log pipeline live, nothing dropped — ${summarise(counters)}`,
59
+ };
60
+ }
61
+ export const loggingPipelineCheck = {
62
+ id: 'logging.pipeline',
63
+ // `DoctorCategory` has no `logging` member, and widening that union is an
64
+ // SDK type change this check does not need — the same call the optional
65
+ // package probes made for `files` and `computer-use`.
66
+ category: 'custom',
67
+ run: () => Promise.resolve(describeLogPipeline(getLogCounters())),
68
+ };
69
+ //# sourceMappingURL=logging.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging.js","sourceRoot":"","sources":["../../../src/doctor/checks/logging.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAE3C;;;;;;;;;;;;;;;;GAgBG;AAEH,2EAA2E;AAC3E,MAAM,MAAM,GAAuC;IAClD,SAAS;IACT,UAAU;IACV,mBAAmB;IACnB,iBAAiB;IACjB,kBAAkB;CAClB,CAAA;AAED,SAAS,SAAS,CAAC,QAAyB;IAC3C,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AAC1D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAqC;IACxE,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC5B,qEAAqE;QACrE,uEAAuE;QACvE,oEAAoE;QACpE,sEAAsE;QACtE,wCAAwC;QACxC,OAAO;YACN,MAAM,EAAE,cAAc;YACtB,OAAO,EACN,yHAAyH;YAC1H,WAAW,EACV,8KAA8K;SAC/K,CAAA;IACF,CAAC;IAED,IAAI,QAAQ,CAAC,OAAO,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO;YACN,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,GAAG,QAAQ,CAAC,OAAO,2CAA2C,SAAS,CAAC,QAAQ,CAAC,EAAE;YAC5F,WAAW,EACV,0QAA0Q;SAC3Q,CAAA;IACF,CAAC;IAED,OAAO;QACN,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,wCAAwC,SAAS,CAAC,QAAQ,CAAC,EAAE;KACtE,CAAA;AACF,CAAC;AAED,MAAM,CAAC,MAAM,oBAAoB,GAAgB;IAChD,EAAE,EAAE,kBAAkB;IACtB,0EAA0E;IAC1E,wEAAwE;IACxE,sDAAsD;IACtD,QAAQ,EAAE,QAAQ;IAClB,GAAG,EAAE,GAA+B,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,mBAAmB,CAAC,cAAc,EAAE,CAAC,CAAC;CAC7F,CAAA"}
@@ -0,0 +1,21 @@
1
+ import type { DoctorCheck, DoctorCheckResult } from '@namzu/sdk';
2
+ import type { NamzuCliConfig } from '../../config/schema.js';
3
+ import { type TelemetryLoader } from '../../integrations/telemetry/session-export.js';
4
+ /**
5
+ * Is this machine sending conversation content anywhere?
6
+ *
7
+ * A doctor row rather than only a boot line, because the two answer for
8
+ * different people at different moments: the boot narrative tells whoever
9
+ * started the run, and `namzu doctor` is what somebody runs when they are
10
+ * asked "does this tool send our transcripts off the box" and need an answer
11
+ * they can paste.
12
+ *
13
+ * The row is a `pass` in both directions on purpose. Export being ON is not
14
+ * a fault — it is a configuration somebody chose — so reporting it as a
15
+ * warning would train a reader to ignore the one row that describes their
16
+ * data leaving. What the row owes them is the destination and the redactor
17
+ * count, which is exactly what `describeSessionExport` already builds.
18
+ */
19
+ export declare function describeSessionExportStatus(config: NamzuCliConfig, loader?: TelemetryLoader): Promise<DoctorCheckResult>;
20
+ export declare function sessionExportCheck(config: NamzuCliConfig, loader?: TelemetryLoader): DoctorCheck;
21
+ //# sourceMappingURL=session-export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-export.d.ts","sourceRoot":"","sources":["../../../src/doctor/checks/session-export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEhE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAC5D,OAAO,EACN,KAAK,eAAe,EAEpB,MAAM,gDAAgD,CAAA;AAEvD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,2BAA2B,CAChD,MAAM,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,eAAe,GACtB,OAAO,CAAC,iBAAiB,CAAC,CAyC5B;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,WAAW,CAMhG"}
@@ -0,0 +1,62 @@
1
+ import { describeSessionExportOff, } from '../../integrations/telemetry/session-export.js';
2
+ /**
3
+ * Is this machine sending conversation content anywhere?
4
+ *
5
+ * A doctor row rather than only a boot line, because the two answer for
6
+ * different people at different moments: the boot narrative tells whoever
7
+ * started the run, and `namzu doctor` is what somebody runs when they are
8
+ * asked "does this tool send our transcripts off the box" and need an answer
9
+ * they can paste.
10
+ *
11
+ * The row is a `pass` in both directions on purpose. Export being ON is not
12
+ * a fault — it is a configuration somebody chose — so reporting it as a
13
+ * warning would train a reader to ignore the one row that describes their
14
+ * data leaving. What the row owes them is the destination and the redactor
15
+ * count, which is exactly what `describeSessionExport` already builds.
16
+ */
17
+ export async function describeSessionExportStatus(config, loader) {
18
+ const configured = config.telemetry?.sessionExport;
19
+ if (!configured) {
20
+ const off = await describeSessionExportOff(loader);
21
+ return {
22
+ status: 'pass',
23
+ message: off ?? 'Session export is off: telemetry.sessionExport is not configured.',
24
+ };
25
+ }
26
+ // The disclosure is built from the config, not from a live listener: a
27
+ // doctor run must be able to answer this without starting a session, and
28
+ // starting one to find out what it would export would be the check
29
+ // causing the thing it reports on.
30
+ const off = await describeSessionExportOff(loader);
31
+ if (off === null) {
32
+ // Configured, but the package that would do it is not installed. The
33
+ // run itself refuses in this state (`attachSessionExport`), and saying
34
+ // so here is what stops an operator discovering it at run time.
35
+ return {
36
+ status: 'fail',
37
+ message: `telemetry.sessionExport is configured (destination: ${configured.destination}) but "@namzu/telemetry" is not installed, so nothing would be recorded.`,
38
+ remediation: 'Install @namzu/telemetry, or remove telemetry.sessionExport from your config. namzu refuses to start a session in this state rather than run one whose export silently does not exist.',
39
+ };
40
+ }
41
+ const redactorCount = configured.redactors?.length ?? 1;
42
+ const typePhrase = configured.eventTypes
43
+ ? `${configured.eventTypes.length} event type(s)`
44
+ : 'every run event';
45
+ return {
46
+ status: 'pass',
47
+ message: `Session export is ON: ${typePhrase} is written to ${configured.destination}, with ${redactorCount} redactor(s) installed.`,
48
+ ...(redactorCount === 0
49
+ ? {
50
+ remediation: '`redactors: []` turns redaction off entirely — credential-shaped values in tool results will be written verbatim. Remove the key to install the shipped `secrets` redactor.',
51
+ }
52
+ : {}),
53
+ };
54
+ }
55
+ export function sessionExportCheck(config, loader) {
56
+ return {
57
+ id: 'telemetry.session-export',
58
+ category: 'telemetry',
59
+ run: () => describeSessionExportStatus(config, loader),
60
+ };
61
+ }
62
+ //# sourceMappingURL=session-export.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-export.js","sourceRoot":"","sources":["../../../src/doctor/checks/session-export.ts"],"names":[],"mappings":"AAGA,OAAO,EAEN,wBAAwB,GACxB,MAAM,gDAAgD,CAAA;AAEvD;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAChD,MAAsB,EACtB,MAAwB;IAExB,MAAM,UAAU,GAAG,MAAM,CAAC,SAAS,EAAE,aAAa,CAAA;IAClD,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,MAAM,GAAG,GAAG,MAAM,wBAAwB,CAAC,MAAM,CAAC,CAAA;QAClD,OAAO;YACN,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,GAAG,IAAI,mEAAmE;SACnF,CAAA;IACF,CAAC;IAED,uEAAuE;IACvE,yEAAyE;IACzE,mEAAmE;IACnE,mCAAmC;IACnC,MAAM,GAAG,GAAG,MAAM,wBAAwB,CAAC,MAAM,CAAC,CAAA;IAClD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAClB,qEAAqE;QACrE,uEAAuE;QACvE,gEAAgE;QAChE,OAAO;YACN,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,uDAAuD,UAAU,CAAC,WAAW,0EAA0E;YAChK,WAAW,EACV,wLAAwL;SACzL,CAAA;IACF,CAAC;IAED,MAAM,aAAa,GAAG,UAAU,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,CAAA;IACvD,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU;QACvC,CAAC,CAAC,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,gBAAgB;QACjD,CAAC,CAAC,iBAAiB,CAAA;IACpB,OAAO;QACN,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,yBAAyB,UAAU,kBAAkB,UAAU,CAAC,WAAW,UAAU,aAAa,yBAAyB;QACpI,GAAG,CAAC,aAAa,KAAK,CAAC;YACtB,CAAC,CAAC;gBACA,WAAW,EACV,6KAA6K;aAC9K;YACF,CAAC,CAAC,EAAE,CAAC;KACN,CAAA;AACF,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAsB,EAAE,MAAwB;IAClF,OAAO;QACN,EAAE,EAAE,0BAA0B;QAC9B,QAAQ,EAAE,WAAW;QACrB,GAAG,EAAE,GAA+B,EAAE,CAAC,2BAA2B,CAAC,MAAM,EAAE,MAAM,CAAC;KAClF,CAAA;AACF,CAAC"}
@@ -8,38 +8,25 @@ import type { DoctorCheck, DoctorCheckResult } from '@namzu/sdk';
8
8
  * deferred to a follow-up check that the consumer can register
9
9
  * themselves once their telemetry config is known.
10
10
  *
11
- * ## Resolving and loading are asked separately, on purpose
12
- *
13
- * This used to be one `import()` in a `try`, and every rejection was reported
14
- * as `not installed (optional package)`. A package that IS installed and throws
15
- * on load — a broken build, a native binding that will not open, a dependency
16
- * of its own that cannot resolve — was reported as absent. One word, two facts,
17
- * and the reader is told the reassuring one.
18
- *
19
- * Splitting the question is what makes each answer honest, and a code check on
20
- * the thrown error is not enough to do it: a transitive dependency that cannot
21
- * resolve raises `ERR_MODULE_NOT_FOUND` exactly as a missing `@namzu/telemetry`
22
- * does, so matching on the code would report a broken installation as no
23
- * installation. `resolve` asks only about THIS specifier, so it separates the
24
- * two by construction rather than by reading a message that may be reworded.
25
- *
26
- * - cannot resolve → the package is not here → `skipped`
27
- * - resolves, import throws → it is here and unusable → `fail`
11
+ * The three-state resolve-then-import probe this used to do inline now
12
+ * lives at `context/capabilities.ts` as `probeOptionalPackage` (NZ-BOOT-02),
13
+ * extracted so the boot path can report the SAME answer about
14
+ * `@namzu/sandbox`, `@namzu/files` and `@namzu/computer-use` that this file
15
+ * has always given about telemetry, instead of inventing its own. What
16
+ * stays here is the one thing the doctor still owns: turning that answer
17
+ * into the prose and remediation a `DoctorCheckResult` carries.
28
18
  */
29
19
  /**
30
- * Whether `specifier` is installed, and whether it loads.
20
+ * `specifier`'s `CapabilityProbe`, read as a `DoctorCheckResult`.
31
21
  *
32
22
  * Exported separately from the `DoctorCheck` so all three outcomes can be
33
- * driven with a specifier the test controls — there is no way to uninstall or
34
- * break the real optional package from inside a test run. The check itself is
35
- * still exercised through `telemetryInstalledCheck.run`, because a helper
36
- * proven in isolation says nothing about whether the check reaches it. Same
37
- * arrangement, and the same reason, as `describeProviderChain`.
38
- *
39
- * The import goes through the RESOLVED path rather than the specifier, so one
40
- * code path serves a package name and a file, which is what lets the
41
- * present-but-broken case be reached at all. Resolution has already applied the
42
- * package's export map by that point.
23
+ * asserted directly — there is no way to uninstall or break a real optional
24
+ * package from inside a test run, so `capabilities.test.ts` drives
25
+ * `probeOptionalPackage` with fixtures and this function's own tests only
26
+ * need to check the mapping. The check itself is still exercised through
27
+ * `telemetryInstalledCheck.run`, because a helper proven in isolation says
28
+ * nothing about whether the check reaches it. Same arrangement, and the
29
+ * same reason, as `describeProviderChain`.
43
30
  */
44
31
  export declare function describeInstalledPackage(specifier: string): Promise<DoctorCheckResult>;
45
32
  export declare const telemetryInstalledCheck: DoctorCheck;
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../../src/doctor/checks/telemetry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAIhE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA0B5F;AAED,eAAO,MAAM,uBAAuB,EAAE,WAIrC,CAAA"}
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../../src/doctor/checks/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAMhE;;;;;;;;;;;;;;;;GAgBG;AACH;;;;;;;;;;;GAWG;AACH,wBAAsB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAoB5F;AAED,eAAO,MAAM,uBAAuB,EAAE,WAIrC,CAAA"}
@@ -1,5 +1,4 @@
1
- import { createRequire } from 'node:module';
2
- import { pathToFileURL } from 'node:url';
1
+ import { probeOptionalPackage } from '../../context/capabilities.js';
3
2
  const TELEMETRY = '@namzu/telemetry';
4
3
  /**
5
4
  * Telemetry presence probe.
@@ -10,64 +9,45 @@ const TELEMETRY = '@namzu/telemetry';
10
9
  * deferred to a follow-up check that the consumer can register
11
10
  * themselves once their telemetry config is known.
12
11
  *
13
- * ## Resolving and loading are asked separately, on purpose
14
- *
15
- * This used to be one `import()` in a `try`, and every rejection was reported
16
- * as `not installed (optional package)`. A package that IS installed and throws
17
- * on load — a broken build, a native binding that will not open, a dependency
18
- * of its own that cannot resolve — was reported as absent. One word, two facts,
19
- * and the reader is told the reassuring one.
20
- *
21
- * Splitting the question is what makes each answer honest, and a code check on
22
- * the thrown error is not enough to do it: a transitive dependency that cannot
23
- * resolve raises `ERR_MODULE_NOT_FOUND` exactly as a missing `@namzu/telemetry`
24
- * does, so matching on the code would report a broken installation as no
25
- * installation. `resolve` asks only about THIS specifier, so it separates the
26
- * two by construction rather than by reading a message that may be reworded.
27
- *
28
- * - cannot resolve → the package is not here → `skipped`
29
- * - resolves, import throws → it is here and unusable → `fail`
12
+ * The three-state resolve-then-import probe this used to do inline now
13
+ * lives at `context/capabilities.ts` as `probeOptionalPackage` (NZ-BOOT-02),
14
+ * extracted so the boot path can report the SAME answer about
15
+ * `@namzu/sandbox`, `@namzu/files` and `@namzu/computer-use` that this file
16
+ * has always given about telemetry, instead of inventing its own. What
17
+ * stays here is the one thing the doctor still owns: turning that answer
18
+ * into the prose and remediation a `DoctorCheckResult` carries.
30
19
  */
31
20
  /**
32
- * Whether `specifier` is installed, and whether it loads.
21
+ * `specifier`'s `CapabilityProbe`, read as a `DoctorCheckResult`.
33
22
  *
34
23
  * Exported separately from the `DoctorCheck` so all three outcomes can be
35
- * driven with a specifier the test controls — there is no way to uninstall or
36
- * break the real optional package from inside a test run. The check itself is
37
- * still exercised through `telemetryInstalledCheck.run`, because a helper
38
- * proven in isolation says nothing about whether the check reaches it. Same
39
- * arrangement, and the same reason, as `describeProviderChain`.
40
- *
41
- * The import goes through the RESOLVED path rather than the specifier, so one
42
- * code path serves a package name and a file, which is what lets the
43
- * present-but-broken case be reached at all. Resolution has already applied the
44
- * package's export map by that point.
24
+ * asserted directly — there is no way to uninstall or break a real optional
25
+ * package from inside a test run, so `capabilities.test.ts` drives
26
+ * `probeOptionalPackage` with fixtures and this function's own tests only
27
+ * need to check the mapping. The check itself is still exercised through
28
+ * `telemetryInstalledCheck.run`, because a helper proven in isolation says
29
+ * nothing about whether the check reaches it. Same arrangement, and the
30
+ * same reason, as `describeProviderChain`.
45
31
  */
46
32
  export async function describeInstalledPackage(specifier) {
47
- const require = createRequire(import.meta.url);
48
- let resolved;
49
- try {
50
- resolved = require.resolve(specifier);
51
- }
52
- catch {
53
- return {
54
- status: 'skipped',
55
- message: `${specifier} not installed (optional package)`,
56
- };
57
- }
58
- try {
59
- await import(pathToFileURL(resolved).href);
60
- return { status: 'pass', message: `${specifier} is installed` };
61
- }
62
- catch (err) {
63
- return {
64
- status: 'fail',
65
- // The reason is the whole value of separating these: "installed but
66
- // will not load" sends the reader somewhere, and "not installed" would
67
- // have sent them to install what is already there.
68
- message: `${specifier} is installed but failed to load: ${err instanceof Error ? err.message : String(err)}`,
69
- remediation: `Reinstall ${specifier}, or remove it if you are not using telemetry — it is optional and namzu runs without it.`,
70
- };
33
+ const probe = await probeOptionalPackage(specifier);
34
+ switch (probe.state) {
35
+ case 'absent':
36
+ return {
37
+ status: 'skipped',
38
+ message: `${specifier} not installed (optional package)`,
39
+ };
40
+ case 'present':
41
+ return { status: 'pass', message: `${specifier} is installed` };
42
+ case 'broken':
43
+ return {
44
+ status: 'fail',
45
+ // The reason is the whole value of separating these: "installed but
46
+ // will not load" sends the reader somewhere, and "not installed" would
47
+ // have sent them to install what is already there.
48
+ message: `${specifier} is installed but failed to load: ${probe.error.message}`,
49
+ remediation: `Reinstall ${specifier}, or remove it if you are not using it — it is optional and namzu runs without it.`,
50
+ };
71
51
  }
72
52
  }
73
53
  export const telemetryInstalledCheck = {
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../../src/doctor/checks/telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAIxC,MAAM,SAAS,GAAG,kBAAkB,CAAA;AAEpC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,SAAiB;IAC/D,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC9C,IAAI,QAAgB,CAAA;IACpB,IAAI,CAAC;QACJ,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IACtC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO;YACN,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,GAAG,SAAS,mCAAmC;SACxD,CAAA;IACF,CAAC;IACD,IAAI,CAAC;QACJ,MAAM,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAA;QAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,SAAS,eAAe,EAAE,CAAA;IAChE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,OAAO;YACN,MAAM,EAAE,MAAM;YACd,oEAAoE;YACpE,uEAAuE;YACvE,mDAAmD;YACnD,OAAO,EAAE,GAAG,SAAS,qCACpB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAChD,EAAE;YACF,WAAW,EAAE,aAAa,SAAS,2FAA2F;SAC9H,CAAA;IACF,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAgB;IACnD,EAAE,EAAE,qBAAqB;IACzB,QAAQ,EAAE,WAAW;IACrB,GAAG,EAAE,GAA+B,EAAE,CAAC,wBAAwB,CAAC,SAAS,CAAC;CAC1E,CAAA"}
1
+ {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../../src/doctor/checks/telemetry.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAA;AAEpE,MAAM,SAAS,GAAG,kBAAkB,CAAA;AAEpC;;;;;;;;;;;;;;;;GAgBG;AACH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,SAAiB;IAC/D,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC,SAAS,CAAC,CAAA;IACnD,QAAQ,KAAK,CAAC,KAAK,EAAE,CAAC;QACrB,KAAK,QAAQ;YACZ,OAAO;gBACN,MAAM,EAAE,SAAS;gBACjB,OAAO,EAAE,GAAG,SAAS,mCAAmC;aACxD,CAAA;QACF,KAAK,SAAS;YACb,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,SAAS,eAAe,EAAE,CAAA;QAChE,KAAK,QAAQ;YACZ,OAAO;gBACN,MAAM,EAAE,MAAM;gBACd,oEAAoE;gBACpE,uEAAuE;gBACvE,mDAAmD;gBACnD,OAAO,EAAE,GAAG,SAAS,qCAAqC,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE;gBAC/E,WAAW,EAAE,aAAa,SAAS,oFAAoF;aACvH,CAAA;IACH,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAgB;IACnD,EAAE,EAAE,qBAAqB;IACzB,QAAQ,EAAE,WAAW;IACrB,GAAG,EAAE,GAA+B,EAAE,CAAC,wBAAwB,CAAC,SAAS,CAAC;CAC1E,CAAA"}
@@ -1,22 +1,6 @@
1
- import type { DoctorCheck } from '@namzu/sdk';
2
- /**
3
- * Built-in vault probe is intentionally `skipped`.
4
- *
5
- * `CredentialVault` is an interface, not a globally-discoverable
6
- * registry — consumers instantiate their own (`InMemoryCredentialVault`,
7
- * file-backed, env-backed, KMS-backed, …). The doctor cannot enumerate
8
- * vaults it doesn't know about. To get a real vault healthcheck, the
9
- * consumer registers a custom check via `registerDoctorCheck` that
10
- * exercises their specific vault wiring.
11
- *
12
- * `skipped`, not `inconclusive`, and the difference is the whole point of the
13
- * split: `inconclusive` means a check tried and could not answer, which is a
14
- * gap in the report and now carries a non-zero exit code. This check has no
15
- * subject to answer about — there is no discovery mechanism to fail — and that
16
- * is true on every machine, permanently, by design. Reporting a permanent
17
- * design decision as an unanswered question would make `namzu doctor` non-zero
18
- * on every healthy machine, and a diagnostic that always complains is one
19
- * nobody reads.
20
- */
1
+ import type { CredentialProvider, DoctorCheck } from '@namzu/sdk';
2
+ export declare function registerCredentialProvider(provider: CredentialProvider, label: string, refs: readonly string[]): () => void;
3
+ /** For tests, and for a host rebuilding its wiring. */
4
+ export declare function clearRegisteredCredentialProviders(): void;
21
5
  export declare const vaultRegisteredCheck: DoctorCheck;
22
6
  //# sourceMappingURL=vault.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../../../src/doctor/checks/vault.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAqB,MAAM,YAAY,CAAA;AAEhE;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,oBAAoB,EAAE,WAQlC,CAAA"}
1
+ {"version":3,"file":"vault.d.ts","sourceRoot":"","sources":["../../../src/doctor/checks/vault.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAqB,MAAM,YAAY,CAAA;AAmBpF,wBAAgB,0BAA0B,CACzC,QAAQ,EAAE,kBAAkB,EAC5B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,SAAS,MAAM,EAAE,GACrB,MAAM,IAAI,CAOZ;AAED,uDAAuD;AACvD,wBAAgB,kCAAkC,IAAI,IAAI,CAEzD;AAED,eAAO,MAAM,oBAAoB,EAAE,WA0ClC,CAAA"}