@westbayberry/dg 1.3.3 → 2.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 (126) hide show
  1. package/LICENSE +1 -201
  2. package/NOTICE +1 -4
  3. package/README.md +293 -0
  4. package/dist/api/analyze.js +210 -0
  5. package/dist/audit/deep.js +180 -0
  6. package/dist/audit/detectors.js +247 -0
  7. package/dist/audit/events.js +41 -0
  8. package/dist/audit/rules.js +426 -0
  9. package/dist/audit-ui/AuditApp.js +39 -0
  10. package/dist/audit-ui/components/AuditHeader.js +24 -0
  11. package/dist/audit-ui/components/AuditResultsView.js +307 -0
  12. package/dist/audit-ui/components/DeepStatusRow.js +11 -0
  13. package/dist/audit-ui/export.js +85 -0
  14. package/dist/audit-ui/format.js +34 -0
  15. package/dist/audit-ui/launch.js +34 -0
  16. package/dist/auth/device-login.js +271 -0
  17. package/dist/auth/env-token.js +6 -0
  18. package/dist/auth/login-app.js +156 -0
  19. package/dist/auth/store.js +147 -0
  20. package/dist/bin/dg.js +71 -0
  21. package/dist/commands/audit.js +357 -0
  22. package/dist/commands/completion.js +116 -0
  23. package/dist/commands/config.js +99 -0
  24. package/dist/commands/doctor.js +39 -0
  25. package/dist/commands/explain.js +100 -0
  26. package/dist/commands/guard-commit.js +158 -0
  27. package/dist/commands/help.js +74 -0
  28. package/dist/commands/licenses.js +435 -0
  29. package/dist/commands/login.js +81 -0
  30. package/dist/commands/logout.js +37 -0
  31. package/dist/commands/router.js +98 -0
  32. package/dist/commands/scan.js +18 -0
  33. package/dist/commands/service.js +475 -0
  34. package/dist/commands/setup.js +302 -0
  35. package/dist/commands/status.js +115 -0
  36. package/dist/commands/suggest.js +35 -0
  37. package/dist/commands/types.js +4 -0
  38. package/dist/commands/unavailable.js +11 -0
  39. package/dist/commands/uninstall.js +111 -0
  40. package/dist/commands/update.js +210 -0
  41. package/dist/commands/verify.js +151 -0
  42. package/dist/commands/version.js +22 -0
  43. package/dist/commands/wrap.js +55 -0
  44. package/dist/config/settings.js +302 -0
  45. package/dist/install-ui/LiveInstall.js +24 -0
  46. package/dist/install-ui/block-render.js +83 -0
  47. package/dist/install-ui/live-install-app.js +48 -0
  48. package/dist/install-ui/prompt.js +24 -0
  49. package/dist/launcher/classify.js +116 -0
  50. package/dist/launcher/env.js +53 -0
  51. package/dist/launcher/live-install.js +50 -0
  52. package/dist/launcher/output-redaction.js +77 -0
  53. package/dist/launcher/preflight-prompt.js +139 -0
  54. package/dist/launcher/resolve-real-binary.js +73 -0
  55. package/dist/launcher/run.js +417 -0
  56. package/dist/policy/evaluate.js +128 -0
  57. package/dist/presentation/mode.js +52 -0
  58. package/dist/presentation/theme.js +29 -0
  59. package/dist/proxy/buffer-budget.js +64 -0
  60. package/dist/proxy/ca.js +126 -0
  61. package/dist/proxy/classify-host.js +26 -0
  62. package/dist/proxy/enforcement.js +102 -0
  63. package/dist/proxy/metadata-map.js +336 -0
  64. package/dist/proxy/server.js +909 -0
  65. package/dist/proxy/upstream-proxy.js +102 -0
  66. package/dist/proxy/worker.js +39 -0
  67. package/dist/publish-set/collect.js +51 -0
  68. package/dist/publish-set/no-exec-shell.js +19 -0
  69. package/dist/publish-set/npm.js +109 -0
  70. package/dist/publish-set/pack.js +36 -0
  71. package/dist/publish-set/pypi.js +59 -0
  72. package/dist/runtime/cli.js +17 -0
  73. package/dist/runtime/first-run.js +60 -0
  74. package/dist/runtime/node-version.js +58 -0
  75. package/dist/runtime/nudges.js +105 -0
  76. package/dist/scan/analyze-worker.js +21 -0
  77. package/dist/scan/collect.js +153 -0
  78. package/dist/scan/command.js +159 -0
  79. package/dist/scan/discovery.js +209 -0
  80. package/dist/scan/render.js +240 -0
  81. package/dist/scan/scanner-report.js +82 -0
  82. package/dist/scan/staged.js +173 -0
  83. package/dist/scan/types.js +1 -0
  84. package/dist/scan-ui/LegacyApp.js +156 -0
  85. package/dist/scan-ui/alt-screen.js +84 -0
  86. package/dist/scan-ui/api-aliases.js +1 -0
  87. package/dist/scan-ui/components/ErrorView.js +23 -0
  88. package/dist/scan-ui/components/InteractiveResultsView.js +1166 -0
  89. package/dist/scan-ui/components/ProgressBar.js +89 -0
  90. package/dist/scan-ui/components/ProjectSelector.js +62 -0
  91. package/dist/scan-ui/components/ScoreHeader.js +20 -0
  92. package/dist/scan-ui/components/SetupBanner.js +13 -0
  93. package/dist/scan-ui/components/Spinner.js +4 -0
  94. package/dist/scan-ui/format-helpers.js +40 -0
  95. package/dist/scan-ui/hooks/useExpandAnimation.js +40 -0
  96. package/dist/scan-ui/hooks/useScan.js +113 -0
  97. package/dist/scan-ui/hooks/useTerminalSize.js +24 -0
  98. package/dist/scan-ui/launch.js +27 -0
  99. package/dist/scan-ui/logo.js +91 -0
  100. package/dist/scan-ui/shims.js +30 -0
  101. package/dist/security/sanitize.js +28 -0
  102. package/dist/service/state.js +837 -0
  103. package/dist/service/trust-store.js +234 -0
  104. package/dist/service/worker.js +88 -0
  105. package/dist/setup/git-hook.js +244 -0
  106. package/dist/setup/optional-support.js +58 -0
  107. package/dist/setup/plan.js +899 -0
  108. package/dist/state/cleanup-registry.js +60 -0
  109. package/dist/state/index.js +5 -0
  110. package/dist/state/locks.js +161 -0
  111. package/dist/state/paths.js +24 -0
  112. package/dist/state/sessions.js +170 -0
  113. package/dist/state/store.js +50 -0
  114. package/dist/telemetry/events.js +40 -0
  115. package/dist/util/git.js +20 -0
  116. package/dist/util/tty-prompt.js +43 -0
  117. package/dist/verify/local.js +400 -0
  118. package/dist/verify/package-check.js +240 -0
  119. package/dist/verify/preflight.js +698 -0
  120. package/dist/verify/render.js +184 -0
  121. package/dist/verify/types.js +1 -0
  122. package/package.json +33 -50
  123. package/dist/index.mjs +0 -54116
  124. package/dist/postinstall.mjs +0 -731
  125. package/dist/python-hook/dg_pip_hook.pth +0 -1
  126. package/dist/python-hook/dg_pip_hook.py +0 -130
@@ -0,0 +1,475 @@
1
+ import { EXIT_USAGE } from "./types.js";
2
+ import { renderCommandHelp } from "./help.js";
3
+ import { buildServiceUninstallPlan, buildTrustInstallPlan, buildTrustUninstallPlan, installServiceTrust, readServiceState, renderServicePlan, restartService, ServiceNotConfiguredError, ServiceProxyError, ServiceTrustStoreError, startService, stopService, uninstallService, uninstallServiceTrust } from "../service/state.js";
4
+ import { LockBusyError } from "../state/locks.js";
5
+ const trustCommand = {
6
+ name: "trust",
7
+ summary: "Manage explicit service-mode trust installation.",
8
+ usage: "dg service trust <install|uninstall>",
9
+ details: ["Trust-store changes require explicit service-mode setup, printed write plans, consent, and full reversal."],
10
+ subcommands: [
11
+ {
12
+ name: "install",
13
+ summary: "Install explicit service-mode trust.",
14
+ usage: "dg service trust install [--print] [--yes]",
15
+ details: ["Managed trust installation is never part of package install or default setup."],
16
+ handler: (context) => trustInstallHandler(context.args)
17
+ },
18
+ {
19
+ name: "uninstall",
20
+ summary: "Remove explicit service-mode trust.",
21
+ usage: "dg service trust uninstall [--print] [--yes]",
22
+ details: ["Managed trust removal reverses dg-owned trust-store writes."],
23
+ handler: (context) => trustUninstallHandler(context.args)
24
+ }
25
+ ],
26
+ handler: routeService
27
+ };
28
+ const serviceSubcommands = [
29
+ {
30
+ name: "start",
31
+ summary: "Start explicit service mode.",
32
+ usage: "dg service start",
33
+ details: ["Service mode is explicit, reversible, documented, and separate from default setup."],
34
+ handler: () => serviceMutation("start", () => startService())
35
+ },
36
+ {
37
+ name: "stop",
38
+ summary: "Stop explicit service mode.",
39
+ usage: "dg service stop",
40
+ details: ["Service mode is explicit, reversible, documented, and separate from default setup."],
41
+ handler: () => serviceMutation("stop", () => stopService())
42
+ },
43
+ {
44
+ name: "restart",
45
+ summary: "Restart explicit service mode.",
46
+ usage: "dg service restart",
47
+ details: ["Service mode is explicit, reversible, documented, and separate from default setup."],
48
+ handler: () => serviceMutation("restart", () => restartService())
49
+ },
50
+ {
51
+ name: "status",
52
+ summary: "Show explicit service mode status.",
53
+ usage: "dg service status [--json]",
54
+ details: ["Service mode status does not mutate service or trust state."],
55
+ handler: (context) => statusHandler(context.args)
56
+ },
57
+ {
58
+ name: "doctor",
59
+ summary: "Diagnose explicit service mode.",
60
+ usage: "dg service doctor [--json]",
61
+ details: ["Service doctor reports service health, trust state, policy sync, and admin guidance."],
62
+ handler: (context) => serviceDoctorHandler(context.args)
63
+ },
64
+ {
65
+ name: "uninstall",
66
+ summary: "Uninstall explicit service mode.",
67
+ usage: "dg service uninstall [--print] [--yes]",
68
+ details: ["Service uninstall reverses dg-owned service and trust writes."],
69
+ handler: (context) => serviceUninstallHandler(context.args)
70
+ }
71
+ ];
72
+ export const serviceCommand = {
73
+ name: "service",
74
+ summary: "Manage explicit service/private-registry mode.",
75
+ usage: "dg service <start|stop|restart|status|doctor|uninstall|trust>",
76
+ examples: ["dg service start", "dg service status --json", "dg service trust install --print"],
77
+ details: [
78
+ "Service mode is never silently enabled by package install or default setup.",
79
+ "status and doctor accept --json; uninstall and trust accept --print / --yes."
80
+ ],
81
+ subcommands: [...serviceSubcommands, trustCommand],
82
+ handler: routeService
83
+ };
84
+ function routeService(context) {
85
+ const [action, trustAction, ...rest] = context.args;
86
+ if (!action || action === "--help" || action === "-h" || action === "help") {
87
+ return {
88
+ exitCode: 0,
89
+ stdout: renderCommandHelp(serviceCommand),
90
+ stderr: ""
91
+ };
92
+ }
93
+ if (action === "trust") {
94
+ if (!trustAction || trustAction === "--help" || trustAction === "-h" || trustAction === "help") {
95
+ return {
96
+ exitCode: 0,
97
+ stdout: renderCommandHelp(trustCommand, ["service", "trust"]),
98
+ stderr: ""
99
+ };
100
+ }
101
+ const trustSubcommand = trustCommand.subcommands?.find((subcommand) => subcommand.name === trustAction);
102
+ if (!trustSubcommand) {
103
+ return {
104
+ exitCode: EXIT_USAGE,
105
+ stdout: "",
106
+ stderr: `dg service: unknown trust subcommand '${trustAction}'. Run 'dg service trust --help'.\n`
107
+ };
108
+ }
109
+ return trustSubcommand.handler({
110
+ commandPath: ["service", "trust", trustAction],
111
+ args: rest
112
+ });
113
+ }
114
+ const subcommand = serviceSubcommands.find((candidate) => candidate.name === action);
115
+ if (!subcommand) {
116
+ return {
117
+ exitCode: EXIT_USAGE,
118
+ stdout: "",
119
+ stderr: `dg service: unknown subcommand '${action}'. Run 'dg service --help'.\n`
120
+ };
121
+ }
122
+ return subcommand.handler({
123
+ commandPath: ["service", action],
124
+ args: [trustAction, ...rest].filter((arg) => arg !== undefined)
125
+ });
126
+ }
127
+ function statusHandler(args) {
128
+ const parsed = parseJsonArgs("dg service status", args);
129
+ if ("error" in parsed) {
130
+ return parsed.error;
131
+ }
132
+ const result = readServiceState();
133
+ if (parsed.json) {
134
+ return {
135
+ exitCode: 0,
136
+ stdout: `${JSON.stringify(serviceStatusJson(result.state), null, 2)}\n`,
137
+ stderr: ""
138
+ };
139
+ }
140
+ return {
141
+ exitCode: 0,
142
+ stdout: renderServiceStatus(result.state),
143
+ stderr: ""
144
+ };
145
+ }
146
+ function serviceDoctorHandler(args) {
147
+ const parsed = parseJsonArgs("dg service doctor", args);
148
+ if ("error" in parsed) {
149
+ return parsed.error;
150
+ }
151
+ const result = readServiceState();
152
+ const checks = serviceDoctorChecks(result.state);
153
+ if (parsed.json) {
154
+ return {
155
+ exitCode: 0,
156
+ stdout: `${JSON.stringify({ checks }, null, 2)}\n`,
157
+ stderr: ""
158
+ };
159
+ }
160
+ return {
161
+ exitCode: 0,
162
+ stdout: `Dependency Guardian service doctor\n\n${checks
163
+ .map((check) => `${check.status.toUpperCase()} ${check.name}: ${check.message}`)
164
+ .join("\n")}\n`,
165
+ stderr: ""
166
+ };
167
+ }
168
+ function serviceMutation(action, run) {
169
+ try {
170
+ const result = run();
171
+ const changed = result.changed ? actionPastTense(action) : `already ${action === "start" ? "running" : "stopped"}`;
172
+ return {
173
+ exitCode: 0,
174
+ stdout: `Dependency Guardian service ${changed}.\n${renderServiceStatus(result.state)}`,
175
+ stderr: ""
176
+ };
177
+ }
178
+ catch (error) {
179
+ if (error instanceof ServiceNotConfiguredError) {
180
+ return serviceNotConfiguredResult();
181
+ }
182
+ if (error instanceof ServiceProxyError || error instanceof ServiceTrustStoreError || error instanceof LockBusyError) {
183
+ return {
184
+ exitCode: 1,
185
+ stdout: "",
186
+ stderr: `dg service ${action}: ${error.message}\n`
187
+ };
188
+ }
189
+ throw error;
190
+ }
191
+ }
192
+ function trustInstallHandler(args) {
193
+ const parsed = parsePlanArgs("dg service trust install", args);
194
+ if ("error" in parsed) {
195
+ return parsed.error;
196
+ }
197
+ const planText = renderServicePlan("Dependency Guardian service trust install plan", buildTrustInstallPlan());
198
+ if (parsed.printOnly) {
199
+ return {
200
+ exitCode: 0,
201
+ stdout: planText,
202
+ stderr: ""
203
+ };
204
+ }
205
+ if (!parsed.yes) {
206
+ return {
207
+ exitCode: EXIT_USAGE,
208
+ stdout: planText,
209
+ stderr: "dg service trust install requires --yes to apply this non-interactive write plan.\n"
210
+ };
211
+ }
212
+ try {
213
+ const result = installServiceTrust();
214
+ return {
215
+ exitCode: 0,
216
+ stdout: `${planText}\nService trust ${result.changed ? "installed" : "already installed"}.\n`,
217
+ stderr: ""
218
+ };
219
+ }
220
+ catch (error) {
221
+ if (error instanceof ServiceNotConfiguredError) {
222
+ return serviceNotConfiguredResult();
223
+ }
224
+ if (error instanceof ServiceTrustStoreError) {
225
+ return trustStoreFailureResult(planText, "install", error.message);
226
+ }
227
+ if (error instanceof LockBusyError) {
228
+ return { exitCode: 1, stdout: "", stderr: `dg service trust install: ${error.message}\n` };
229
+ }
230
+ throw error;
231
+ }
232
+ }
233
+ function trustUninstallHandler(args) {
234
+ const parsed = parsePlanArgs("dg service trust uninstall", args);
235
+ if ("error" in parsed) {
236
+ return parsed.error;
237
+ }
238
+ const planText = renderServicePlan("Dependency Guardian service trust uninstall plan", buildTrustUninstallPlan());
239
+ if (parsed.printOnly) {
240
+ return {
241
+ exitCode: 0,
242
+ stdout: planText,
243
+ stderr: ""
244
+ };
245
+ }
246
+ if (!parsed.yes) {
247
+ return {
248
+ exitCode: EXIT_USAGE,
249
+ stdout: planText,
250
+ stderr: "dg service trust uninstall requires --yes to apply this non-interactive write plan.\n"
251
+ };
252
+ }
253
+ try {
254
+ const result = uninstallServiceTrust();
255
+ return {
256
+ exitCode: 0,
257
+ stdout: `${planText}\nService trust ${result.changed ? "uninstalled" : "was already absent"}.\n`,
258
+ stderr: ""
259
+ };
260
+ }
261
+ catch (error) {
262
+ if (error instanceof ServiceTrustStoreError) {
263
+ return trustStoreFailureResult(planText, "uninstall", error.message);
264
+ }
265
+ if (error instanceof LockBusyError) {
266
+ return { exitCode: 1, stdout: "", stderr: `dg service trust uninstall: ${error.message}\n` };
267
+ }
268
+ throw error;
269
+ }
270
+ }
271
+ export function serviceUninstallHandler(args) {
272
+ const parsed = parsePlanArgs("dg service uninstall", args);
273
+ if ("error" in parsed) {
274
+ return parsed.error;
275
+ }
276
+ const planText = renderServicePlan("Dependency Guardian service uninstall plan", buildServiceUninstallPlan());
277
+ if (parsed.printOnly) {
278
+ return {
279
+ exitCode: 0,
280
+ stdout: planText,
281
+ stderr: ""
282
+ };
283
+ }
284
+ if (!parsed.yes) {
285
+ return {
286
+ exitCode: EXIT_USAGE,
287
+ stdout: planText,
288
+ stderr: "dg service uninstall requires --yes to remove service-mode writes in non-interactive mode.\n"
289
+ };
290
+ }
291
+ try {
292
+ const result = uninstallService();
293
+ return {
294
+ exitCode: 0,
295
+ stdout: `${planText}\n${[
296
+ "Dependency Guardian service uninstall",
297
+ ...result.removed.map((path) => `removed: ${path}`),
298
+ result.removed.length === 0 ? "No dg-owned service writes were present." : "Service uninstall completed."
299
+ ].join("\n")}\n`,
300
+ stderr: ""
301
+ };
302
+ }
303
+ catch (error) {
304
+ if (error instanceof ServiceTrustStoreError) {
305
+ return trustStoreFailureResult(planText, "uninstall", error.message);
306
+ }
307
+ if (error instanceof LockBusyError) {
308
+ return { exitCode: 1, stdout: "", stderr: `dg service uninstall: ${error.message}\n` };
309
+ }
310
+ throw error;
311
+ }
312
+ }
313
+ function parsePlanArgs(command, args) {
314
+ let printOnly = false;
315
+ let yes = false;
316
+ for (const arg of args) {
317
+ if (arg === "--print") {
318
+ printOnly = true;
319
+ }
320
+ else if (arg === "--yes") {
321
+ yes = true;
322
+ }
323
+ else {
324
+ return {
325
+ error: {
326
+ exitCode: EXIT_USAGE,
327
+ stdout: "",
328
+ stderr: `${command}: unknown option '${arg}'. Run '${command} --help'.\n`
329
+ }
330
+ };
331
+ }
332
+ }
333
+ return {
334
+ printOnly,
335
+ yes
336
+ };
337
+ }
338
+ function parseJsonArgs(command, args) {
339
+ let json = false;
340
+ for (const arg of args) {
341
+ if (arg === "--json") {
342
+ json = true;
343
+ }
344
+ else {
345
+ return {
346
+ error: {
347
+ exitCode: EXIT_USAGE,
348
+ stdout: "",
349
+ stderr: `${command}: unknown option '${arg}'. Run '${command} --help'.\n`
350
+ }
351
+ };
352
+ }
353
+ }
354
+ return {
355
+ json
356
+ };
357
+ }
358
+ function renderServiceStatus(state) {
359
+ const lines = [
360
+ "Dependency Guardian service status",
361
+ "",
362
+ `configured: ${state.configured ? "yes" : "no"}`,
363
+ `running: ${state.running ? "yes" : "no"}`,
364
+ `trust installed: ${state.trustInstalled ? "yes" : "no"}`,
365
+ `trust provider: ${state.trust ? state.trust.provider : "not installed"}`,
366
+ `trust fingerprint: ${state.trust ? state.trust.fingerprintSha256 : "not installed"}`,
367
+ `trust drift: ${state.trustDrift ? state.trustDrift.message : "none"}`,
368
+ `service proxy: ${state.proxy ? state.proxy.proxyUrl : "not running"}`,
369
+ `health endpoint: ${state.proxy ? state.proxy.healthUrl : "not running"}`,
370
+ `last error: ${state.lastError ?? "none"}`,
371
+ `policy synced: ${state.policySyncedAt ?? "never"}`
372
+ ];
373
+ return `${lines.join("\n")}\n`;
374
+ }
375
+ function serviceStatusJson(state) {
376
+ return {
377
+ configured: state.configured,
378
+ running: state.running,
379
+ trustInstalled: state.trustInstalled,
380
+ trust: state.trust
381
+ ? {
382
+ provider: state.trust.provider,
383
+ native: state.trust.native,
384
+ adminRequired: state.trust.adminRequired,
385
+ target: state.trust.target,
386
+ fingerprintSha256: state.trust.fingerprintSha256,
387
+ installedAt: state.trust.installedAt
388
+ }
389
+ : null,
390
+ proxy: state.proxy ? {
391
+ pid: state.proxy.pid,
392
+ proxyUrl: state.proxy.proxyUrl,
393
+ healthUrl: state.proxy.healthUrl,
394
+ sessionDir: state.proxy.sessionDir,
395
+ caPath: state.proxy.caPath,
396
+ startedAt: state.proxy.startedAt
397
+ } : null,
398
+ trustDrift: state.trustDrift ? {
399
+ installedFingerprintSha256: state.trustDrift.installedFingerprintSha256,
400
+ activeFingerprintSha256: state.trustDrift.activeFingerprintSha256 ?? null,
401
+ message: state.trustDrift.message
402
+ } : null,
403
+ lastError: state.lastError ?? null,
404
+ policySyncedAt: state.policySyncedAt ?? null
405
+ };
406
+ }
407
+ function serviceDoctorChecks(state) {
408
+ return [
409
+ {
410
+ name: "configured",
411
+ status: state.configured ? "pass" : "warn",
412
+ message: state.configured ? "Explicit service mode is configured" : "Run dg setup --service --yes before service start"
413
+ },
414
+ {
415
+ name: "running",
416
+ status: state.running ? "pass" : state.lastError?.startsWith("stale service runtime state") ? "fail" : "warn",
417
+ message: state.running ? "Service controller state is running" : state.lastError ?? "Service is stopped"
418
+ },
419
+ {
420
+ name: "trust",
421
+ status: state.trustInstalled ? "pass" : "warn",
422
+ message: state.trust
423
+ ? `Managed service trust is installed through ${state.trust.provider} for ${state.trust.fingerprintSha256}`
424
+ : "No managed service trust-store entry is installed"
425
+ },
426
+ {
427
+ name: "trust-drift",
428
+ status: state.trustDrift ? "warn" : "pass",
429
+ message: state.trustDrift?.message ?? "Managed service trust matches the active CA or is not installed"
430
+ },
431
+ {
432
+ name: "policy-sync",
433
+ status: state.policySyncedAt ? "pass" : "warn",
434
+ message: state.policySyncedAt ? `Last policy sync ${state.policySyncedAt}` : "Policy has not been synced for service mode"
435
+ },
436
+ {
437
+ name: "service-proxy",
438
+ status: state.running && state.proxy ? "pass" : state.lastError?.startsWith("stale service runtime state") ? "fail" : "warn",
439
+ message: state.running && state.proxy ? `Persistent service proxy listening at ${state.proxy.proxyUrl}` : state.lastError ?? "Service proxy is not running"
440
+ },
441
+ {
442
+ name: "health-endpoint",
443
+ status: state.running && state.proxy ? "pass" : state.lastError?.startsWith("stale service runtime state") ? "fail" : "warn",
444
+ message: state.running && state.proxy ? `Health endpoint available at ${state.proxy.healthUrl}` : state.lastError ?? "Service health endpoint is not running"
445
+ },
446
+ {
447
+ name: "admin",
448
+ status: state.trust?.adminRequired ? "warn" : "pass",
449
+ message: "Native service trust-store mutation is consent-gated; Linux system trust requires admin/root, macOS user-keychain trust does not, and CI/Docker can use an explicit file trust backend"
450
+ }
451
+ ];
452
+ }
453
+ function actionPastTense(action) {
454
+ if (action === "start") {
455
+ return "started";
456
+ }
457
+ if (action === "stop") {
458
+ return "stopped";
459
+ }
460
+ return "restarted";
461
+ }
462
+ function serviceNotConfiguredResult() {
463
+ return {
464
+ exitCode: EXIT_USAGE,
465
+ stdout: "",
466
+ stderr: "dg service is not configured. Run 'dg setup --service --yes' first; no service or trust state was changed.\n"
467
+ };
468
+ }
469
+ function trustStoreFailureResult(planText, action, message) {
470
+ return {
471
+ exitCode: 1,
472
+ stdout: planText,
473
+ stderr: `dg service trust ${action} failed before recording successful trust state: ${message}\n`
474
+ };
475
+ }