@aiwg/cli 2026.10.5 → 2026.10.7

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 (263) hide show
  1. package/agentic/code/addons/composition-engine/lib/runtime.mjs +1401 -0
  2. package/agentic/code/addons/composition-engine/lib/validator.mjs +513 -0
  3. package/agentic/code/addons/composition-engine/schemas/flow-graph.schema.json +372 -0
  4. package/agentic/code/providers/model-capabilities.v1.json +74 -50
  5. package/bin/aiwg.mjs +7 -3
  6. package/dist/src/a2a/agent-card.js +11 -0
  7. package/dist/src/api/index.d.ts +1 -0
  8. package/dist/src/api/index.js +1 -0
  9. package/dist/src/artifacts/browser-export.js +1 -0
  10. package/dist/src/artifacts/citation-parser.js +8 -1
  11. package/dist/src/artifacts/cli.js +57 -6
  12. package/dist/src/artifacts/fortemi-core-query-adapter.js +2 -0
  13. package/dist/src/artifacts/fortemi-core-sync.js +26 -8
  14. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/contract.json +103 -0
  15. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-config.schema.json +212 -0
  16. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-set.schema.json +240 -0
  17. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-manifest.schema.json +451 -0
  18. package/dist/src/artifacts/fortemi-shard-export.js +284 -0
  19. package/dist/src/artifacts/graph-query.js +18 -8
  20. package/dist/src/artifacts/index-adapters.js +339 -0
  21. package/dist/src/artifacts/index-builder.js +143 -14
  22. package/dist/src/artifacts/index-files.js +15 -4
  23. package/dist/src/artifacts/index-preparation.js +2 -0
  24. package/dist/src/artifacts/index-reader.js +44 -0
  25. package/dist/src/artifacts/index-refresh.js +114 -55
  26. package/dist/src/artifacts/index-status.js +20 -5
  27. package/dist/src/artifacts/memory-maintenance.js +503 -0
  28. package/dist/src/artifacts/prebuilt-relations.js +3 -1
  29. package/dist/src/artifacts/query-engine.js +263 -108
  30. package/dist/src/artifacts/relations-builder.js +2 -2
  31. package/dist/src/artifacts/source-graph.js +2 -2
  32. package/dist/src/artifacts/types.js +9 -1
  33. package/dist/src/auth/client.js +5 -1
  34. package/dist/src/auth/credential-store.js +5 -1
  35. package/dist/src/auth/sensitive-input.js +151 -0
  36. package/dist/src/channel/manager.mjs +3 -0
  37. package/dist/src/cli/handlers/agentcard.js +47 -25
  38. package/dist/src/cli/handlers/auth.js +22 -2
  39. package/dist/src/cli/handlers/decision.js +22 -3
  40. package/dist/src/cli/handlers/effect.js +4 -84
  41. package/dist/src/cli/handlers/guided-setup.js +470 -0
  42. package/dist/src/cli/handlers/help.js +2 -2
  43. package/dist/src/cli/handlers/housekeeper.js +1110 -0
  44. package/dist/src/cli/handlers/index.js +4 -2
  45. package/dist/src/cli/handlers/init.js +41 -6
  46. package/dist/src/cli/handlers/install.js +6 -0
  47. package/dist/src/cli/handlers/local-executor.js +5 -3
  48. package/dist/src/cli/handlers/marketplace.js +15 -4
  49. package/dist/src/cli/handlers/mc.js +509 -53
  50. package/dist/src/cli/handlers/models.js +32 -2
  51. package/dist/src/cli/handlers/ralph-launcher.js +76 -4
  52. package/dist/src/cli/handlers/rules.js +1 -1
  53. package/dist/src/cli/handlers/script-runner.js +73 -2
  54. package/dist/src/cli/handlers/serve.js +5 -3
  55. package/dist/src/cli/handlers/sessions.js +1 -0
  56. package/dist/src/cli/handlers/setup.js +432 -1
  57. package/dist/src/cli/handlers/skill-lint.js +1 -1
  58. package/dist/src/cli/handlers/subcommands.js +118 -8
  59. package/dist/src/cli/handlers/use.js +335 -107
  60. package/dist/src/cli/handlers/workspace.js +3 -1
  61. package/dist/src/cli/prompt-utils.js +81 -0
  62. package/dist/src/cli/router.js +16 -1
  63. package/dist/src/config/aiwg-config.js +42 -0
  64. package/dist/src/config/cli.js +7 -3
  65. package/dist/src/config/decision-config.js +14 -1
  66. package/dist/src/config/gitignore.js +4 -0
  67. package/dist/src/config/native-sandbox-warnings.js +25 -0
  68. package/dist/src/config/workspace.js +2 -1
  69. package/dist/src/contributors/validation.js +6 -5
  70. package/dist/src/decision/adapters/jev.js +20 -31
  71. package/dist/src/decision/admission.js +7 -3
  72. package/dist/src/decision/ask.js +6 -1
  73. package/dist/src/decision/benchmark.js +482 -0
  74. package/dist/src/decision/ledger.js +22 -3
  75. package/dist/src/decision/pricing.js +147 -0
  76. package/dist/src/decision/qualification/gate-evidence.js +23 -18
  77. package/dist/src/decision/qualification/load-staged.js +576 -0
  78. package/dist/src/decision/qualification/load.js +109 -33
  79. package/dist/src/decision/qualification/runner.js +12 -5
  80. package/dist/src/decision/review/service.js +6 -4
  81. package/dist/src/decision/review/validate.js +23 -2
  82. package/dist/src/decision/savings.js +8 -3
  83. package/dist/src/discovery/index.js +492 -0
  84. package/dist/src/discovery/interview.js +238 -0
  85. package/dist/src/discovery/plan-apply.js +567 -0
  86. package/dist/src/discovery/plan-store.js +171 -0
  87. package/dist/src/discovery/plan-types.js +2 -0
  88. package/dist/src/discovery/plan-validation.js +139 -0
  89. package/dist/src/discovery/types.js +2 -0
  90. package/dist/src/effects/host.js +91 -0
  91. package/dist/src/extensions/commands/definitions.js +49 -9
  92. package/dist/src/extensions/manifest.js +71 -44
  93. package/dist/src/extensions/project-local-discovery.js +1 -1
  94. package/dist/src/extensions/project-local-gitignore.js +2 -0
  95. package/dist/src/extensions/project-quickref.js +18 -17
  96. package/dist/src/extensions/validation.js +32 -29
  97. package/dist/src/housekeeper/daemon-config.js +148 -0
  98. package/dist/src/housekeeper/ipc.js +84 -0
  99. package/dist/src/housekeeper/lifecycle.js +148 -0
  100. package/dist/src/housekeeper/platform.js +35 -0
  101. package/dist/src/housekeeper/status.js +70 -0
  102. package/dist/src/housekeeper/units.js +114 -0
  103. package/dist/src/issues/cli.js +16 -5
  104. package/dist/src/issues/live.js +44 -12
  105. package/dist/src/jobs/flow.js +22 -21
  106. package/dist/src/lint/rule-activation.js +20 -5
  107. package/dist/src/lint/runner.js +1 -1
  108. package/dist/src/marketplace/exchange.js +18 -6
  109. package/dist/src/marketplace/provenance.js +3 -2
  110. package/dist/src/mcp/adapters/codex-runtime.js +46 -76
  111. package/dist/src/mcp/cli.mjs +22 -96
  112. package/dist/src/mcp/config-file.mjs +6 -2
  113. package/dist/src/mcp/config-layers.mjs +23 -7
  114. package/dist/src/mcp/grok-build-config.mjs +2 -1
  115. package/dist/src/mcp/muse-native-config.mjs +2 -1
  116. package/dist/src/mcp/profiles.js +18 -31
  117. package/dist/src/mcp/profiles.mjs +18 -32
  118. package/dist/src/mcp/registry.js +7 -27
  119. package/dist/src/mcp/registry.mjs +7 -28
  120. package/dist/src/mcp/server.mjs +34 -1
  121. package/dist/src/mcp/tools/agentic-sandbox.mjs +8 -7
  122. package/dist/src/mcp/tools/command-run.mjs +3 -2
  123. package/dist/src/mcp/tools/decision.mjs +49 -48
  124. package/dist/src/mcp/tools/discovery.mjs +46 -21
  125. package/dist/src/mcp/tools/orchestration.mjs +22 -22
  126. package/dist/src/mcp/tools/subsystems.mjs +9 -8
  127. package/dist/src/mcp/tools/worker-invoke.mjs +30 -6
  128. package/dist/src/mcp/tools/writer-profiles.mjs +1 -1
  129. package/dist/src/memory/audit.js +719 -0
  130. package/dist/src/memory/canonical-context.js +392 -13
  131. package/dist/src/memory/canonical-maintenance.js +95 -0
  132. package/dist/src/memory/cli-result.js +22 -0
  133. package/dist/src/memory/cli.js +176 -0
  134. package/dist/src/memory/context-pack.js +57 -6
  135. package/dist/src/memory/cost.js +512 -0
  136. package/dist/src/memory/firewall-classification.d.mts +7 -0
  137. package/dist/src/memory/firewall-classification.mjs +32 -0
  138. package/dist/src/memory/index.js +8 -0
  139. package/dist/src/memory/line-maintenance.js +68 -0
  140. package/dist/src/memory/maintenance-cli.js +286 -0
  141. package/dist/src/memory/maintenance-contract.js +136 -0
  142. package/dist/src/memory/maintenance-index.js +18 -0
  143. package/dist/src/memory/maintenance-owner.js +2 -0
  144. package/dist/src/memory/maintenance-record.js +138 -0
  145. package/dist/src/memory/maintenance.js +254 -0
  146. package/dist/src/memory/project-registry.js +2 -1
  147. package/dist/src/memory/provider-memory-adapter.js +154 -0
  148. package/dist/src/memory/recommend-cli.js +83 -0
  149. package/dist/src/memory/recommend.js +269 -0
  150. package/dist/src/memory/scheduled.js +81 -0
  151. package/dist/src/memory/taxonomy.js +44 -0
  152. package/dist/src/mission-protocol/codecs.js +19 -9
  153. package/dist/src/mission-protocol/worker-status.js +91 -1
  154. package/dist/src/models/config-loader.js +11 -1
  155. package/dist/src/models/model-capabilities.v1.json +74 -50
  156. package/dist/src/models/provider-policy.js +125 -62
  157. package/dist/src/models/resolver.js +45 -8
  158. package/dist/src/models/worker-installation.d.mts +8 -1
  159. package/dist/src/models/worker-installation.mjs +231 -38
  160. package/dist/src/models/wrapper-route.js +1 -1
  161. package/dist/src/output-modes/registry.js +8 -7
  162. package/dist/src/packages/adapters/git.js +99 -23
  163. package/dist/src/packages/adapters/gitea.js +8 -27
  164. package/dist/src/packages/adapters/github.js +7 -25
  165. package/dist/src/packages/registry.js +4 -4
  166. package/dist/src/plugin/metadata-validator.js +1 -1
  167. package/dist/src/process/cli-command.d.mts +5 -0
  168. package/dist/src/process/cli-command.mjs +42 -0
  169. package/dist/src/providers/deploy-version.d.mts +1 -0
  170. package/dist/src/providers/deploy-version.mjs +17 -0
  171. package/dist/src/providers/provider-definitions.js +19 -16
  172. package/dist/src/providers/skill-ownership.d.mts +11 -2
  173. package/dist/src/providers/skill-ownership.mjs +35 -17
  174. package/dist/src/rules/activation.d.mts +11 -2
  175. package/dist/src/rules/activation.mjs +82 -6
  176. package/dist/src/rules/inventory.js +1 -1
  177. package/dist/src/rules/rule-frontmatter.schema.json +2 -0
  178. package/dist/src/schema/zod-validation.d.mts +6 -0
  179. package/dist/src/schema/zod-validation.mjs +50 -0
  180. package/dist/src/serve/executor-registry.js +297 -20
  181. package/dist/src/serve/executor-token.d.mts +1 -0
  182. package/dist/src/serve/executor-token.mjs +8 -0
  183. package/dist/src/serve/shared-host-scheduler.js +1102 -31
  184. package/dist/src/serve/worker-invocation.js +124 -15
  185. package/dist/src/sessions/adapters/claude.js +19 -18
  186. package/dist/src/sessions/adapters/codex.js +10 -9
  187. package/dist/src/sessions/adapters/copilot.js +14 -13
  188. package/dist/src/sessions/adapters/cursor.js +19 -18
  189. package/dist/src/sessions/adapters/deepseek-harness.js +9 -8
  190. package/dist/src/sessions/adapters/factory.js +11 -10
  191. package/dist/src/sessions/adapters/generic.js +32 -23
  192. package/dist/src/sessions/adapters/grokbot.js +35 -35
  193. package/dist/src/sessions/adapters/hermes.js +20 -19
  194. package/dist/src/sessions/adapters/muse.js +50 -49
  195. package/dist/src/sessions/adapters/omp.js +5 -4
  196. package/dist/src/sessions/adapters/openclaw.js +30 -29
  197. package/dist/src/sessions/adapters/opencode.js +26 -26
  198. package/dist/src/sessions/adapters/openhuman.js +24 -23
  199. package/dist/src/sessions/adapters/pi.js +5 -4
  200. package/dist/src/sessions/adapters/windsurf.js +8 -8
  201. package/dist/src/sessions/analytics.js +10 -9
  202. package/dist/src/sessions/candidates.js +7 -6
  203. package/dist/src/sessions/contracts.js +63 -53
  204. package/dist/src/sessions/output-registration.js +11 -10
  205. package/dist/src/sessions/workspace-discovery.js +3 -4
  206. package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
  207. package/dist/src/smiths/context-pipeline/claude-hook.js +46 -10
  208. package/dist/src/smiths/context-pipeline/context-file-safety.js +330 -9
  209. package/dist/src/smiths/context-pipeline/finalization.js +3 -2
  210. package/dist/src/smiths/context-pipeline/generator.js +7 -19
  211. package/dist/src/smiths/context-pipeline/legacy-inject.js +3 -10
  212. package/dist/src/smiths/context-pipeline/managed-hook.js +5 -5
  213. package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
  214. package/dist/src/smiths/context-pipeline/workspace-context.js +110 -22
  215. package/dist/src/tracker/project-inputs.js +2 -0
  216. package/dist/src/update/checker.d.mts +4 -0
  217. package/dist/src/update/checker.mjs +14 -0
  218. package/dist/src/update/notifier.mjs +2 -4
  219. package/dist/src/writing/exemplar-selection.js +6 -6
  220. package/dist/src/writing/voice-evaluation.js +24 -23
  221. package/dist/src/writing/writer-migration.js +14 -13
  222. package/dist/src/writing/writer-profile.js +19 -18
  223. package/dist/src/writing/writing-brief.js +18 -17
  224. package/dist/src/writing/writing-receipt.js +47 -46
  225. package/package.json +5 -2
  226. package/schemas/decision/DecisionReview.schema.json +78 -1
  227. package/schemas/mission-v1.schema.json +3 -0
  228. package/tools/agents/deploy-agents.mjs +67 -39
  229. package/tools/agents/providers/antigravity.mjs +6 -1
  230. package/tools/agents/providers/base.mjs +24 -14
  231. package/tools/agents/providers/claude.mjs +6 -3
  232. package/tools/agents/providers/codex.mjs +54 -57
  233. package/tools/agents/providers/context-write.mjs +28 -0
  234. package/tools/agents/providers/copilot.mjs +13 -4
  235. package/tools/agents/providers/cursor.mjs +1 -1
  236. package/tools/agents/providers/deepseek-harness.mjs +1 -1
  237. package/tools/agents/providers/factory.mjs +7 -1
  238. package/tools/agents/providers/grok-build.mjs +18 -2
  239. package/tools/agents/providers/grokbot.mjs +1 -1
  240. package/tools/agents/providers/hermes.mjs +12 -15
  241. package/tools/agents/providers/muse-hooks.mjs +21 -14
  242. package/tools/agents/providers/muse.mjs +7 -2
  243. package/tools/agents/providers/omp.mjs +31 -5
  244. package/tools/agents/providers/opencode.mjs +14 -5
  245. package/tools/agents/providers/openhuman.mjs +1 -1
  246. package/tools/agents/providers/pi.mjs +1 -1
  247. package/tools/agents/providers/provider-prepare-coordinator.mjs +29 -9
  248. package/tools/agents/providers/rule-delivery.mjs +289 -80
  249. package/tools/agents/providers/warp.mjs +4 -1
  250. package/tools/agents/providers/windsurf.mjs +13 -3
  251. package/tools/cli/workspace-status.mjs +5 -0
  252. package/tools/commands/deploy-prompts-codex.mjs +11 -8
  253. package/tools/ralph-external/lib/codex-adapter.mjs +6 -2
  254. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +26 -0
  255. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +7 -1
  256. package/tools/ralph-external/lib/muse-adapter.mjs +12 -2
  257. package/tools/ralph-external/lib/recovery-probe.mjs +14 -4
  258. package/tools/ralph-external/lib/worker-health.mjs +1 -1
  259. package/tools/ralph-external/lib/worker-isolation.mjs +65 -9
  260. package/tools/ralph-external/process-monitor.mjs +49 -8
  261. package/tools/ralph-external/session-launcher.mjs +405 -35
  262. package/tools/skills/deploy-skills-codex.mjs +11 -7
  263. package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Daemon config access for `aiwg housekeeper` (epic #3353 slice B5).
3
+ *
4
+ * Mirrors the file precedence of `tools/daemon/config.mjs` (YAML, then YML,
5
+ * then JSON) without importing `tools/` — `src/` must never import `tools/`
6
+ * (tools import compiled src via `importImpl`). Only the `schedule.jobs`
7
+ * and `watch.paths` sections plus `daemon.profile` are read or written;
8
+ * every other key is preserved byte-for-byte through a YAML round-trip.
9
+ */
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import YAML from 'yaml';
13
+ import { parseMemoryAuditOptions } from '../memory/cli.js';
14
+ export const HOUSEKEEPER_PROFILE = 'housekeeper';
15
+ /**
16
+ * Deterministic maintenance kinds. Mirrors `MAINTENANCE_KINDS` in
17
+ * `tools/daemon/scheduled-task-runner.mjs` — that module owns execution;
18
+ * this list only validates `job register` input on the CLI side.
19
+ */
20
+ export const MAINTENANCE_KINDS = Object.freeze([
21
+ 'index.verify',
22
+ 'index.reconcile',
23
+ 'cache.retention',
24
+ 'update.check',
25
+ 'update.install',
26
+ 'index.freshness',
27
+ 'index.refresh',
28
+ 'cache.prune',
29
+ 'memory.audit',
30
+ 'memory.recommend',
31
+ 'memory.apply',
32
+ ]);
33
+ /** Same argument contract for registration, daemon configuration and dispatch. */
34
+ export function validateMemoryJobArgs(kind, args = {}) {
35
+ if (!kind.startsWith('memory.'))
36
+ return;
37
+ if (!['memory.audit', 'memory.recommend', 'memory.apply'].includes(kind))
38
+ throw new Error(`Unsupported memory kind: ${kind}`);
39
+ if (!args || typeof args !== 'object' || Array.isArray(args))
40
+ throw new Error('Memory job args must be an object');
41
+ const values = args;
42
+ const allowed = kind === 'memory.apply' ? ['plan', 'confirmedDigest', 'windows']
43
+ : ['scope', 'provider', ...(kind === 'memory.recommend' ? ['evidence', 'usage'] : [])];
44
+ for (const [key, value] of Object.entries(values)) {
45
+ if (!allowed.includes(key))
46
+ throw new Error(`Unsupported ${kind} argument: ${key}`);
47
+ if (key !== 'windows' && (typeof value !== 'string' || !value.trim()))
48
+ throw new Error(`${key} must be a non-empty string`);
49
+ }
50
+ if (kind === 'memory.apply') {
51
+ if (!values.plan || !values.confirmedDigest)
52
+ throw new Error('memory.apply requires args.plan and args.confirmedDigest for the named reviewed plan');
53
+ }
54
+ else {
55
+ parseMemoryAuditOptions(['scope', 'provider'].flatMap(key => values[key] === undefined ? [] : [`--${key}`, String(values[key])]));
56
+ }
57
+ }
58
+ /** Resolve the daemon config file with the same precedence as Config.load. */
59
+ export function resolveDaemonConfigPath(cwd) {
60
+ const candidates = [
61
+ path.join(cwd, '.aiwg', 'daemon.yaml'),
62
+ path.join(cwd, '.aiwg', 'daemon.yml'),
63
+ path.join(cwd, '.aiwg', 'daemon.json'),
64
+ ];
65
+ for (const candidate of candidates) {
66
+ if (fs.existsSync(candidate))
67
+ return { path: candidate, existed: true };
68
+ }
69
+ return { path: candidates[0], existed: false };
70
+ }
71
+ function parseConfigFile(file) {
72
+ const raw = fs.readFileSync(file, 'utf8');
73
+ if (file.endsWith('.json')) {
74
+ const parsed = JSON.parse(raw);
75
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
76
+ throw new Error(`Daemon config ${file} must contain a JSON object`);
77
+ }
78
+ return parsed;
79
+ }
80
+ const parsed = YAML.parse(raw);
81
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
82
+ throw new Error(`Daemon config ${file} must contain a mapping`);
83
+ }
84
+ return parsed;
85
+ }
86
+ export function loadDaemonConfig(cwd) {
87
+ const { path: file, existed } = resolveDaemonConfigPath(cwd);
88
+ if (!existed)
89
+ return { path: file, existed, config: {} };
90
+ return { path: file, existed, config: parseConfigFile(file) };
91
+ }
92
+ export function saveDaemonConfig(file, config) {
93
+ fs.mkdirSync(path.dirname(file), { recursive: true });
94
+ const body = file.endsWith('.json')
95
+ ? `${JSON.stringify(config, null, 2)}\n`
96
+ : YAML.stringify(config);
97
+ const tmp = `${file}.tmp.${process.pid}`;
98
+ fs.writeFileSync(tmp, body);
99
+ fs.renameSync(tmp, file);
100
+ }
101
+ export function getDaemonProfile(config) {
102
+ const daemon = config['daemon'];
103
+ if (!daemon || typeof daemon !== 'object' || Array.isArray(daemon))
104
+ return undefined;
105
+ const profile = daemon['profile'];
106
+ return typeof profile === 'string' ? profile : undefined;
107
+ }
108
+ export function setDaemonProfile(config, profile) {
109
+ const daemon = (config['daemon'] ??= {});
110
+ daemon['profile'] = profile;
111
+ }
112
+ export function getScheduleJobs(config) {
113
+ const schedule = config['schedule'];
114
+ if (!schedule || typeof schedule !== 'object' || Array.isArray(schedule))
115
+ return [];
116
+ const jobs = schedule['jobs'];
117
+ return Array.isArray(jobs) ? jobs : [];
118
+ }
119
+ export function setScheduleJobs(config, jobs) {
120
+ const schedule = (config['schedule'] ??= {});
121
+ schedule['jobs'] = jobs;
122
+ }
123
+ export function scheduleEnabled(config) {
124
+ const schedule = config['schedule'];
125
+ if (!schedule || typeof schedule !== 'object' || Array.isArray(schedule))
126
+ return false;
127
+ return schedule['enabled'] !== false;
128
+ }
129
+ export function getWatchPaths(config) {
130
+ const watch = config['watch'];
131
+ if (!watch || typeof watch !== 'object' || Array.isArray(watch))
132
+ return [];
133
+ const paths = watch['paths'];
134
+ return Array.isArray(paths) ? paths : [];
135
+ }
136
+ export function setWatchPaths(config, entries) {
137
+ const watch = (config['watch'] ??= {});
138
+ watch['paths'] = entries;
139
+ }
140
+ /** Minimal housekeeper seed written when no daemon config exists yet. */
141
+ export function seedHousekeeperConfig() {
142
+ return {
143
+ daemon: { profile: HOUSEKEEPER_PROFILE, heartbeat_interval_seconds: 30 },
144
+ watch: { enabled: false, paths: [] },
145
+ schedule: { enabled: true, jobs: [] },
146
+ };
147
+ }
148
+ //# sourceMappingURL=daemon-config.js.map
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Minimal JSON-RPC 2.0 client for the housekeeper daemon socket
3
+ * (epic #3353 slice B5).
4
+ *
5
+ * Speaks the same newline-delimited JSON framing as
6
+ * `tools/daemon/ipc-server.mjs` without importing `tools/` — `src/` must
7
+ * never import `tools/` (tools import compiled src via `importImpl`).
8
+ */
9
+ import net from 'node:net';
10
+ export async function requestDaemon(socketPath, method, options = {}) {
11
+ const timeoutMs = options.timeoutMs ?? 10_000;
12
+ return new Promise((resolve, reject) => {
13
+ let settled = false;
14
+ const fail = (error) => {
15
+ if (settled)
16
+ return;
17
+ settled = true;
18
+ try {
19
+ socket.destroy();
20
+ }
21
+ catch { /* already closed */ }
22
+ reject(error);
23
+ };
24
+ const succeed = (value) => {
25
+ if (settled)
26
+ return;
27
+ settled = true;
28
+ try {
29
+ socket.end();
30
+ }
31
+ catch { /* already closed */ }
32
+ resolve(value);
33
+ };
34
+ const timer = setTimeout(() => {
35
+ fail(new Error(`Daemon request timed out: ${method} (${timeoutMs}ms)`));
36
+ }, timeoutMs);
37
+ timer.unref?.();
38
+ let buffer = '';
39
+ const socket = net.createConnection(socketPath);
40
+ socket.once('connect', () => {
41
+ socket.write(`${JSON.stringify({
42
+ jsonrpc: '2.0', id: 1, method, params: options.params ?? {},
43
+ })}\n`);
44
+ });
45
+ socket.on('data', (chunk) => {
46
+ buffer += chunk.toString('utf8');
47
+ const newline = buffer.indexOf('\n');
48
+ if (newline === -1)
49
+ return;
50
+ const line = buffer.slice(0, newline).trim();
51
+ buffer = buffer.slice(newline + 1);
52
+ if (!line)
53
+ return;
54
+ clearTimeout(timer);
55
+ let response;
56
+ try {
57
+ response = JSON.parse(line);
58
+ }
59
+ catch {
60
+ fail(new Error(`Daemon returned invalid JSON for ${method}`));
61
+ return;
62
+ }
63
+ if (response.error) {
64
+ fail(new Error(`Daemon error for ${method}: ${response.error.message ?? 'unknown error'}`));
65
+ return;
66
+ }
67
+ succeed(response.result);
68
+ });
69
+ socket.once('error', (error) => {
70
+ clearTimeout(timer);
71
+ const code = error?.code;
72
+ if (code === 'ENOENT' || code === 'ECONNREFUSED') {
73
+ fail(new Error('Daemon is not running (socket unavailable)'));
74
+ return;
75
+ }
76
+ fail(error);
77
+ });
78
+ socket.once('close', () => {
79
+ clearTimeout(timer);
80
+ fail(new Error(`Daemon connection closed during ${method}`));
81
+ });
82
+ });
83
+ }
84
+ //# sourceMappingURL=ipc.js.map
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Housekeeper daemon lifecycle helpers (epic #3353 slice B5).
3
+ *
4
+ * Mirrors the pid/lock/heartbeat/state/socket paths of
5
+ * `tools/daemon/daemon-main.mjs` so `aiwg housekeeper start|stop|status`
6
+ * serializes against the same single-instance lock: a second `start`
7
+ * fails clearly, and `stop` cleans up exactly the files the daemon owns.
8
+ * All process signalling of owned ChildProcess handles goes through
9
+ * `signalChild`; pid-based stop uses `process.kill` (no ChildProcess).
10
+ */
11
+ import fs from 'node:fs';
12
+ import path from 'node:path';
13
+ import { signalChild } from '../process/signal-child.mjs';
14
+ /** Same relative layout as DaemonMain; resolved against the caller cwd. */
15
+ export function daemonPaths(cwd, frameworkRoot) {
16
+ return {
17
+ pidFile: path.join(cwd, '.aiwg', 'daemon.pid'),
18
+ lockFile: path.join(cwd, '.aiwg', 'daemon', '.lock'),
19
+ heartbeatFile: path.join(cwd, '.aiwg', 'daemon', 'heartbeat'),
20
+ stateFile: path.join(cwd, '.aiwg', 'daemon', 'state.json'),
21
+ socketPath: path.join(cwd, '.aiwg', 'daemon', 'daemon.sock'),
22
+ logFile: path.join(cwd, '.aiwg', 'daemon', 'daemon.log'),
23
+ taskStorePath: path.join(cwd, '.aiwg', 'daemon', 'tasks.json'),
24
+ daemonScript: path.join(frameworkRoot, 'tools', 'daemon', 'daemon-main.mjs'),
25
+ };
26
+ }
27
+ export function readPidFile(pidFile) {
28
+ let raw;
29
+ try {
30
+ raw = fs.readFileSync(pidFile, 'utf8').trim();
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ const pid = Number.parseInt(raw, 10);
36
+ return Number.isSafeInteger(pid) && pid > 0 && String(pid) === raw ? pid : null;
37
+ }
38
+ export function defaultKill(pid, signal) {
39
+ process.kill(pid, signal);
40
+ }
41
+ export function isPidAlive(pid) {
42
+ try {
43
+ process.kill(pid, 0);
44
+ return true;
45
+ }
46
+ catch (error) {
47
+ // ESRCH = no such process. Any other error (e.g. EPERM) means a
48
+ // process exists that we may not signal — treat as alive.
49
+ if (error?.code === 'ESRCH')
50
+ return false;
51
+ return true;
52
+ }
53
+ }
54
+ /**
55
+ * Report whether a daemon owns the pid file. A pid file pointing at a
56
+ * dead process is stale (safe to overwrite after cleanup); a live one
57
+ * blocks `start`.
58
+ */
59
+ export function describeRunning(pidFile) {
60
+ const pid = readPidFile(pidFile);
61
+ if (pid === null)
62
+ return { running: false, pid: null, stale: false };
63
+ if (isPidAlive(pid))
64
+ return { running: true, pid, stale: false };
65
+ return { running: false, pid, stale: true };
66
+ }
67
+ export function removeLifecycleFiles(paths) {
68
+ for (const file of [paths.pidFile, paths.lockFile]) {
69
+ try {
70
+ fs.unlinkSync(file);
71
+ }
72
+ catch (error) {
73
+ if (error?.code !== 'ENOENT')
74
+ throw error;
75
+ }
76
+ }
77
+ }
78
+ const defaultSleep = (ms) => new Promise((resolve) => { setTimeout(resolve, ms); });
79
+ /**
80
+ * Stop the daemon gracefully (SIGTERM, poll, then SIGKILL on timeout),
81
+ * then release the pid/lock files. Never throws for absent files.
82
+ */
83
+ export async function stopDaemon(options) {
84
+ const { paths, kill = defaultKill, timeoutMs = 10_000, pollMs = 200 } = options;
85
+ const sleep = options.sleep ?? defaultSleep;
86
+ const state = describeRunning(paths.pidFile);
87
+ if (state.pid === null)
88
+ return { outcome: 'not-running' };
89
+ if (!state.running) {
90
+ removeLifecycleFiles(paths);
91
+ return { outcome: 'stale-cleaned', pid: state.pid };
92
+ }
93
+ const pid = state.pid;
94
+ try {
95
+ kill(pid, 'SIGTERM');
96
+ }
97
+ catch (error) {
98
+ if (error?.code === 'ESRCH') {
99
+ removeLifecycleFiles(paths);
100
+ return { outcome: 'stale-cleaned', pid };
101
+ }
102
+ throw error;
103
+ }
104
+ const deadline = Date.now() + timeoutMs;
105
+ while (isPidAlive(pid)) {
106
+ if (Date.now() >= deadline)
107
+ break;
108
+ await sleep(pollMs);
109
+ }
110
+ let forced = false;
111
+ if (isPidAlive(pid)) {
112
+ try {
113
+ kill(pid, 'SIGKILL');
114
+ }
115
+ catch (error) {
116
+ if (error?.code !== 'ESRCH')
117
+ throw error;
118
+ }
119
+ forced = true;
120
+ const killDeadline = Date.now() + Math.min(timeoutMs, 5000);
121
+ while (isPidAlive(pid) && Date.now() < killDeadline) {
122
+ await sleep(pollMs);
123
+ }
124
+ }
125
+ removeLifecycleFiles(paths);
126
+ return { outcome: 'stopped', pid, forced };
127
+ }
128
+ /** Forward a terminal signal to an owned foreground child. */
129
+ export function forwardSignalToChild(child, signal) {
130
+ signalChild(child, signal);
131
+ }
132
+ /**
133
+ * Poll until `predicate` is true or the deadline passes. Returns true on
134
+ * success; used by `start` to wait for the pid file without fixed sleeps.
135
+ */
136
+ export async function pollUntil(predicate, options = {}) {
137
+ const { timeoutMs = 10_000, pollMs = 200 } = options;
138
+ const sleep = options.sleep ?? defaultSleep;
139
+ const deadline = Date.now() + timeoutMs;
140
+ for (;;) {
141
+ if (predicate())
142
+ return true;
143
+ if (Date.now() >= deadline)
144
+ return false;
145
+ await sleep(pollMs);
146
+ }
147
+ }
148
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Platform honesty for `aiwg housekeeper` (epic #3353 slice B5).
3
+ *
4
+ * Design-note §3 matrix in one place, with the platform injectable so
5
+ * tests can cover every row without running on that OS: Windows is
6
+ * unsupported (WSL2 follows the Linux rows); macOS install is a
7
+ * best-effort launchd plist (documented, not CI-tested); Linux install
8
+ * targets systemd user units.
9
+ */
10
+ export function normalizePlatform(platform = process.platform) {
11
+ if (platform === 'win32')
12
+ return 'windows';
13
+ if (platform === 'darwin')
14
+ return 'darwin';
15
+ if (platform === 'linux')
16
+ return 'linux';
17
+ return 'other';
18
+ }
19
+ /** run-once / daemon processes: everything except Windows. */
20
+ export function runSupported(platform = process.platform) {
21
+ return normalizePlatform(platform) !== 'windows';
22
+ }
23
+ export function runUnsupportedMessage() {
24
+ return 'aiwg housekeeper is not supported on Windows (no recursive fs.watch, POSIX paths assumed). Use WSL2 and follow the Linux rows.';
25
+ }
26
+ /** Install backends offered on a platform, in preference order. */
27
+ export function installBackends(platform = process.platform) {
28
+ const normalized = normalizePlatform(platform);
29
+ if (normalized === 'windows')
30
+ return [];
31
+ if (normalized === 'darwin')
32
+ return ['launchd', 'systemd-user'];
33
+ return ['systemd-user', 'launchd'];
34
+ }
35
+ //# sourceMappingURL=platform.js.map
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Housekeeper status envelope (epic #3353 slice B5).
3
+ *
4
+ * `aiwg housekeeper status --json` emits this shape: the B1 §6 contract
5
+ * fields (`contractVersion`/`command`/`status`/`mode`/`jobs`/`totals`) plus
6
+ * per-index freshness measured by the B4 adapters. The CLI builds it from
7
+ * live daemon IPC when a daemon answers, otherwise from local config and
8
+ * persisted receipts (offline mode), so ordinary `aiwg` commands keep
9
+ * working with no daemon running.
10
+ */
11
+ export const HOUSEKEEPER_STATUS_CONTRACT_VERSION = 1;
12
+ const RECEIPT_STATES = new Set(['ok', 'failed', 'skipped', 'deferred']);
13
+ export function countJobTotals(jobs) {
14
+ const totals = {
15
+ jobs: jobs.length, ok: 0, failed: 0, skipped: 0, deferred: 0, never: 0,
16
+ };
17
+ for (const job of jobs) {
18
+ if (job.lastState === 'ok')
19
+ totals.ok += 1;
20
+ else if (job.lastState === 'failed')
21
+ totals.failed += 1;
22
+ else if (job.lastState === 'skipped')
23
+ totals.skipped += 1;
24
+ else if (job.lastState === 'deferred')
25
+ totals.deferred += 1;
26
+ else
27
+ totals.never += 1;
28
+ }
29
+ return totals;
30
+ }
31
+ export function normalizeJobState(state) {
32
+ return typeof state === 'string' && RECEIPT_STATES.has(state)
33
+ ? state
34
+ : 'never';
35
+ }
36
+ /** Assemble the envelope; every field is always present. */
37
+ export function buildHousekeeperStatus(input) {
38
+ const jobs = input.jobs ?? [];
39
+ return {
40
+ contractVersion: HOUSEKEEPER_STATUS_CONTRACT_VERSION,
41
+ command: 'housekeeper.status',
42
+ status: 'ok',
43
+ mode: input.mode,
44
+ pid: input.pid ?? null,
45
+ uptimeSeconds: input.uptimeSeconds ?? 0,
46
+ jobs,
47
+ totals: countJobTotals(jobs),
48
+ freshness: input.freshness ?? [],
49
+ update: {
50
+ channel: 'stable',
51
+ checkOnly: true,
52
+ lastCheck: input.lastCheck ?? null,
53
+ available: input.available ?? null,
54
+ lastInstall: input.lastInstall ?? null,
55
+ },
56
+ };
57
+ }
58
+ /**
59
+ * Map a B4 freshness snapshot to the status entry. `unavailable` (no local
60
+ * deterministic probe) surfaces as `unsupported` per the B1 §6 state set.
61
+ */
62
+ export function toFreshnessEntry(snapshot) {
63
+ return {
64
+ index: snapshot.name,
65
+ state: snapshot.status === 'unavailable' ? 'unsupported' : snapshot.status,
66
+ targetMs: snapshot.policy?.targetStaleAfterMs ?? null,
67
+ converged: snapshot.convergence?.converged ?? null,
68
+ };
69
+ }
70
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Autostart unit renderers for `aiwg housekeeper install` (epic #3353 B5).
3
+ *
4
+ * Platform matrix (design note §3): Linux + systemd is documented;
5
+ * Linux without systemd and macOS fall back to foreground `start` or an
6
+ * external timer calling `run-once`; Windows is unsupported (WSL2 follows
7
+ * the Linux rows). Units are pure strings here so `--dry-run` can print
8
+ * them without writing and tests can assert content with an injected
9
+ * platform. This module never enables anything: enabling is an explicit
10
+ * operator step printed by the handler, never performed implicitly.
11
+ */
12
+ import os from 'node:os';
13
+ import path from 'node:path';
14
+ export const HOUSEKEEPER_SERVICE_NAME = 'aiwg-housekeeper.service';
15
+ export const HOUSEKEEPER_TIMER_NAME = 'aiwg-housekeeper.timer';
16
+ export const HOUSEKEEPER_LAUNCHD_LABEL = 'dev.aiwg.housekeeper';
17
+ export function renderSystemdService(ctx) {
18
+ const node = ctx.nodeBin ?? process.execPath;
19
+ const runOnce = ctx.jobId
20
+ ? `${quote(ctx.aiwgBin)} housekeeper run-once --job ${quote(ctx.jobId)}`
21
+ : `${quote(ctx.aiwgBin)} housekeeper run-once`;
22
+ return [
23
+ '[Unit]',
24
+ 'Description=AIWG housekeeper (deterministic maintenance, oneshot for timer)',
25
+ 'Documentation=https://github.com/jmagly/aiwg',
26
+ '',
27
+ '[Service]',
28
+ 'Type=oneshot',
29
+ `WorkingDirectory=${ctx.cwd}`,
30
+ `ExecStart=${node} ${runOnce}`,
31
+ 'NoNewPrivileges=true',
32
+ '',
33
+ '[Install]',
34
+ 'WantedBy=default.target',
35
+ '',
36
+ ].join('\n');
37
+ }
38
+ export function renderSystemdTimer(options = {}) {
39
+ return [
40
+ '[Unit]',
41
+ 'Description=AIWG housekeeper periodic run (calls aiwg housekeeper run-once)',
42
+ '',
43
+ '[Timer]',
44
+ `OnCalendar=${options.calendar ?? 'hourly'}`,
45
+ 'Persistent=true',
46
+ `Unit=${HOUSEKEEPER_SERVICE_NAME}`,
47
+ '',
48
+ '[Install]',
49
+ 'WantedBy=timers.target',
50
+ '',
51
+ ].join('\n');
52
+ }
53
+ /**
54
+ * Best-effort macOS plist (design note §3: documented, not CI-tested —
55
+ * there is no macOS runner). Runs `run-once` on a calendar interval.
56
+ */
57
+ export function renderLaunchdPlist(ctx, options = {}) {
58
+ const node = ctx.nodeBin ?? process.execPath;
59
+ const args = ctx.jobId
60
+ ? [ctx.aiwgBin, 'housekeeper', 'run-once', '--job', ctx.jobId]
61
+ : [ctx.aiwgBin, 'housekeeper', 'run-once'];
62
+ const argsXml = [node, ...args].map((arg) => ` <string>${xmlEscape(arg)}</string>`).join('\n');
63
+ return [
64
+ '<?xml version="1.0" encoding="UTF-8"?>',
65
+ '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
66
+ '<plist version="1.0">',
67
+ '<dict>',
68
+ ' <key>Label</key>',
69
+ ` <string>${HOUSEKEEPER_LAUNCHD_LABEL}</string>`,
70
+ ' <key>ProgramArguments</key>',
71
+ ' <array>',
72
+ argsXml,
73
+ ' </array>',
74
+ ' <key>WorkingDirectory</key>',
75
+ ` <string>${xmlEscape(ctx.cwd)}</string>`,
76
+ ' <key>StartInterval</key>',
77
+ ` <integer>${options.intervalSeconds ?? 3600}</integer>`,
78
+ ' <key>RunAtLoad</key>',
79
+ ' <false/>',
80
+ '</dict>',
81
+ '</plist>',
82
+ '',
83
+ ].join('\n');
84
+ }
85
+ /** Default `aiwg` entry point under a framework root (overridable in tests). */
86
+ export function defaultAiwgBin(frameworkRoot) {
87
+ return path.join(frameworkRoot, 'bin', 'aiwg.mjs');
88
+ }
89
+ /** XDG user systemd directory (`~/.config/systemd/user` by default). */
90
+ export function systemdUserDir(home = os.homedir()) {
91
+ const base = process.env['XDG_CONFIG_HOME'] || path.join(home, '.config');
92
+ return path.join(base, 'systemd', 'user');
93
+ }
94
+ /** macOS user agent directory. */
95
+ export function launchdAgentDir(home = os.homedir()) {
96
+ return path.join(home, 'Library', 'LaunchAgents');
97
+ }
98
+ export function backendForPlatform(platform) {
99
+ if (platform === 'win32')
100
+ return 'unsupported';
101
+ if (platform === 'darwin')
102
+ return 'launchd';
103
+ return 'systemd-user';
104
+ }
105
+ function quote(value) {
106
+ return /[\s"']/.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value;
107
+ }
108
+ function xmlEscape(value) {
109
+ return value
110
+ .replace(/&/g, '&amp;')
111
+ .replace(/</g, '&lt;')
112
+ .replace(/>/g, '&gt;');
113
+ }
114
+ //# sourceMappingURL=units.js.map
@@ -12,7 +12,8 @@ import path from 'node:path';
12
12
  import { pathToFileURL } from 'node:url';
13
13
  import { getFrameworkRoot } from '../channel/manager.mjs';
14
14
  import { readAiwgConfig } from '../config/aiwg-config.js';
15
- import { LocalIssueProviderCore, buildExternalIssueSnapshotFromLocal, buildLocalIssueConflictReport, createLiveIssueClient, localIssueRoot, parseCommentIdMappings, parseExternalIssueSnapshot, } from './index.js';
15
+ import { sensitiveInputOptions } from '../auth/sensitive-input.js';
16
+ import { LocalIssueProviderCore, buildExternalIssueSnapshotFromLocal, buildLocalIssueConflictReport, createLiveIssueClient, resolveLiveIssueToken, localIssueRoot, parseCommentIdMappings, parseExternalIssueSnapshot, } from './index.js';
16
17
  export async function main(args, cwd = process.cwd()) {
17
18
  const parsed = parseArgs(args);
18
19
  const subcommand = parsed.positional[0];
@@ -218,7 +219,7 @@ async function handleImport(issues, args) {
218
219
  if (from !== "gitea" && from !== "github")
219
220
  throw new Error("Usage: aiwg issue import --from gitea|github (--snapshot-file path | --live --repo owner/name --external-id N)");
220
221
  const snapshot = args.flags.has("live")
221
- ? await createLiveIssueClient({ provider: from, repo: requiredStringFlag(args, "repo"), apiUrl: stringFlag(args, "api-url") }).importIssue(requiredStringFlag(args, "external-id"))
222
+ ? await (await liveClient(args, from)).importIssue(requiredStringFlag(args, "external-id"))
222
223
  : parseExternalIssueSnapshot(JSON.parse(await readRequiredFile(args, "snapshot-file")));
223
224
  if (from !== snapshot.provider)
224
225
  throw new Error(`snapshot provider ${snapshot.provider} does not match --from ${from}`);
@@ -249,7 +250,7 @@ async function handleExport(issues, rest, args) {
249
250
  await writeOptionalOutput(args, snapshot);
250
251
  return;
251
252
  }
252
- const client = createLiveIssueClient({ provider, repo: requiredStringFlag(args, "repo"), apiUrl: stringFlag(args, "api-url") });
253
+ const client = await liveClient(args, provider);
253
254
  if (snapshot.external_id && !args.flags.has("force")) {
254
255
  const remote = await client.fetchIssue(snapshot.external_id);
255
256
  const report = buildLocalIssueConflictReport(issue, remote);
@@ -314,6 +315,11 @@ async function handleIndex(issues, rest, args) {
314
315
  const index = await issues.rebuildIssueIndex({ force: args.flags.has('force') });
315
316
  printJsonOrText(args, index, `Local issue index ready (${index.issues.length} issues)`);
316
317
  }
318
+ async function liveClient(args, provider) {
319
+ const repo = requiredStringFlag(args, 'repo');
320
+ const token = await resolveLiveIssueToken(provider, sensitiveInputOptions(args.raw, 'token'));
321
+ return createLiveIssueClient({ provider, repo, apiUrl: stringFlag(args, 'api-url'), token });
322
+ }
317
323
  function requiredStringFlag(args, flag) {
318
324
  const value = stringFlag(args, flag);
319
325
  if (!value)
@@ -381,7 +387,7 @@ function parseArgs(args) {
381
387
  flags.set(rawKey, true);
382
388
  }
383
389
  }
384
- return { positional, flags };
390
+ return { raw: args, positional, flags };
385
391
  }
386
392
  function stringFlag(args, name) {
387
393
  const value = args.flags.get(name);
@@ -402,10 +408,15 @@ function printUsage() {
402
408
  aiwg issue show <KEY> [--comments last:10|all]
403
409
  aiwg issue import --from gitea|github --snapshot-file path [--json]
404
410
  aiwg issue export <KEY> --to gitea|github [--out path]
411
+ aiwg issue import --from gitea|github --live --repo owner/name --external-id N [token source]
412
+ aiwg issue export <KEY> --to gitea|github --live --repo owner/name [token source]
405
413
  aiwg issue sync conflicts <KEY> --snapshot-file path [--out path]
406
414
  aiwg issue sync map-comments <KEY> --map-file path [--json]
407
415
  aiwg issue comment <KEY> --body "..." [--author name]
408
416
  aiwg issue close <KEY> [--reason "..."]
409
- aiwg issue index rebuild [--force]`);
417
+ aiwg issue index rebuild [--force]
418
+
419
+ Live token sources: --token-interactive, --token-stdin, --token-file <path>,
420
+ --token-store <name>, --token-env <NAME> (see docs/cli/sensitive-values.md).`);
410
421
  }
411
422
  //# sourceMappingURL=cli.js.map