@powerhousedao/shared 6.0.2-staging.5 → 6.0.2-staging.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 (127) hide show
  1. package/dist/{actions-CAH-hbfc.d.ts → actions-_5ZcthYq.d.ts} +5 -5
  2. package/dist/{actions-CAH-hbfc.d.ts.map → actions-_5ZcthYq.d.ts.map} +1 -1
  3. package/dist/clis/args/access-token.d.mts +18 -0
  4. package/dist/clis/args/access-token.d.mts.map +1 -0
  5. package/dist/clis/args/access-token.mjs +22 -0
  6. package/dist/clis/args/access-token.mjs.map +1 -0
  7. package/dist/clis/args/common-BQ2pvhCr.d.mts +177 -0
  8. package/dist/clis/args/common-BQ2pvhCr.d.mts.map +1 -0
  9. package/dist/clis/args/common-Bc8EcW9w.mjs +269 -0
  10. package/dist/clis/args/common-Bc8EcW9w.mjs.map +1 -0
  11. package/dist/clis/args/common.d.mts +2 -0
  12. package/dist/clis/args/common.mjs +2 -0
  13. package/dist/clis/args/connect.d.mts +204 -0
  14. package/dist/clis/args/connect.d.mts.map +1 -0
  15. package/dist/clis/args/connect.mjs +51 -0
  16. package/dist/clis/args/connect.mjs.map +1 -0
  17. package/dist/clis/args/generate-DNGRYRdp.mjs +153 -0
  18. package/dist/clis/args/generate-DNGRYRdp.mjs.map +1 -0
  19. package/dist/clis/args/generate.d.mts +87 -0
  20. package/dist/clis/args/generate.d.mts.map +1 -0
  21. package/dist/clis/args/generate.mjs +2 -0
  22. package/dist/clis/args/help-CFAVJzhI.mjs +152 -0
  23. package/dist/clis/args/help-CFAVJzhI.mjs.map +1 -0
  24. package/dist/clis/args/help-CYjFrzIJ.d.mts +193 -0
  25. package/dist/clis/args/help-CYjFrzIJ.d.mts.map +1 -0
  26. package/dist/clis/args/help.d.mts +2 -0
  27. package/dist/clis/args/help.mjs +14 -0
  28. package/dist/clis/args/index.d.mts +19 -0
  29. package/dist/clis/args/index.mjs +19 -0
  30. package/dist/clis/args/init.d.mts +49 -0
  31. package/dist/clis/args/init.d.mts.map +1 -0
  32. package/dist/clis/args/init.mjs +56 -0
  33. package/dist/clis/args/init.mjs.map +1 -0
  34. package/dist/clis/args/inspect.d.mts +15 -0
  35. package/dist/clis/args/inspect.d.mts.map +1 -0
  36. package/dist/clis/args/inspect.mjs +15 -0
  37. package/dist/clis/args/inspect.mjs.map +1 -0
  38. package/dist/clis/args/install.d.mts +40 -0
  39. package/dist/clis/args/install.d.mts.map +1 -0
  40. package/dist/clis/args/install.mjs +32 -0
  41. package/dist/clis/args/install.mjs.map +1 -0
  42. package/dist/clis/args/list.d.mts +12 -0
  43. package/dist/clis/args/list.d.mts.map +1 -0
  44. package/dist/clis/args/list.mjs +7 -0
  45. package/dist/clis/args/list.mjs.map +1 -0
  46. package/dist/clis/args/login.d.mts +27 -0
  47. package/dist/clis/args/login.d.mts.map +1 -0
  48. package/dist/clis/args/login.mjs +40 -0
  49. package/dist/clis/args/login.mjs.map +1 -0
  50. package/dist/clis/args/migrate.d.mts +21 -0
  51. package/dist/clis/args/migrate.d.mts.map +1 -0
  52. package/dist/clis/args/migrate.mjs +29 -0
  53. package/dist/clis/args/migrate.mjs.map +1 -0
  54. package/dist/clis/args/publish.d.mts +18 -0
  55. package/dist/clis/args/publish.d.mts.map +1 -0
  56. package/dist/clis/args/publish.mjs +19 -0
  57. package/dist/clis/args/publish.mjs.map +1 -0
  58. package/dist/clis/args/registry.d.mts +19 -0
  59. package/dist/clis/args/registry.d.mts.map +1 -0
  60. package/dist/clis/args/registry.mjs +23 -0
  61. package/dist/clis/args/registry.mjs.map +1 -0
  62. package/dist/clis/args/service-C88bN_g_.d.mts +22 -0
  63. package/dist/clis/args/service-C88bN_g_.d.mts.map +1 -0
  64. package/dist/clis/args/service.d.mts +2 -0
  65. package/dist/clis/args/service.mjs +11 -0
  66. package/dist/clis/args/service.mjs.map +1 -0
  67. package/dist/clis/args/switchboard.d.mts +66 -0
  68. package/dist/clis/args/switchboard.d.mts.map +1 -0
  69. package/dist/clis/args/switchboard.mjs +74 -0
  70. package/dist/clis/args/switchboard.mjs.map +1 -0
  71. package/dist/clis/args/uninstall.d.mts +31 -0
  72. package/dist/clis/args/uninstall.d.mts.map +1 -0
  73. package/dist/clis/args/uninstall.mjs +16 -0
  74. package/dist/clis/args/uninstall.mjs.map +1 -0
  75. package/dist/clis/args/unpublish.d.mts +24 -0
  76. package/dist/clis/args/unpublish.d.mts.map +1 -0
  77. package/dist/clis/args/unpublish.mjs +30 -0
  78. package/dist/clis/args/unpublish.mjs.map +1 -0
  79. package/dist/clis/args/vetra.d.mts +93 -0
  80. package/dist/clis/args/vetra.d.mts.map +1 -0
  81. package/dist/clis/args/vetra.mjs +64 -0
  82. package/dist/clis/args/vetra.mjs.map +1 -0
  83. package/dist/clis/build-config.d.mts +10623 -0
  84. package/dist/clis/build-config.d.mts.map +1 -0
  85. package/dist/clis/build-config.mjs +86 -0
  86. package/dist/clis/build-config.mjs.map +1 -0
  87. package/dist/clis/command-names.d.mts +5 -0
  88. package/dist/clis/command-names.d.mts.map +1 -0
  89. package/dist/clis/command-names.mjs +28 -0
  90. package/dist/clis/command-names.mjs.map +1 -0
  91. package/dist/clis/constants.d.mts +86 -0
  92. package/dist/clis/constants.d.mts.map +1 -0
  93. package/dist/clis/constants.mjs +64 -0
  94. package/dist/clis/constants.mjs.map +1 -0
  95. package/dist/clis/index.d.mts +328 -11101
  96. package/dist/clis/index.d.mts.map +1 -1
  97. package/dist/clis/index.mjs +155 -137
  98. package/dist/clis/index.mjs.map +1 -1
  99. package/dist/clis/services/telemetry.d.mts +57 -0
  100. package/dist/clis/services/telemetry.d.mts.map +1 -0
  101. package/dist/clis/services/telemetry.mjs +221 -0
  102. package/dist/clis/services/telemetry.mjs.map +1 -0
  103. package/dist/clis/utils.d.mts +9 -0
  104. package/dist/clis/utils.d.mts.map +1 -0
  105. package/dist/clis/utils.mjs +373 -0
  106. package/dist/clis/utils.mjs.map +1 -0
  107. package/dist/document-drive/index.d.ts +70 -3
  108. package/dist/document-drive/index.d.ts.map +1 -1
  109. package/dist/document-drive/index.js +31 -31
  110. package/dist/document-drive/index.js.map +1 -1
  111. package/dist/document-model/index.d.ts +2 -2
  112. package/dist/index-C_iVZe7f.d.ts +7 -0
  113. package/dist/index-C_iVZe7f.d.ts.map +1 -0
  114. package/dist/{index-DPTdhtLL.d.ts → index-dcU9nj_u.d.ts} +2 -2
  115. package/dist/{index-DPTdhtLL.d.ts.map → index-dcU9nj_u.d.ts.map} +1 -1
  116. package/dist/index.d.ts +4 -3
  117. package/dist/index.js +13 -1
  118. package/dist/index.js.map +1 -0
  119. package/dist/processors/index.d.ts +1 -1
  120. package/dist/registry/index.d.ts +22 -2
  121. package/dist/registry/index.d.ts.map +1 -1
  122. package/dist/registry/index.js +40 -5
  123. package/dist/registry/index.js.map +1 -1
  124. package/dist/{types-1E8sqdB9.d.ts → types-DyMP31mD.d.ts} +7 -7
  125. package/dist/{types-1E8sqdB9.d.ts.map → types-DyMP31mD.d.ts.map} +1 -1
  126. package/package.json +31 -3
  127. package/dist/index-D0E78WnU.d.ts +0 -1
@@ -0,0 +1,57 @@
1
+ //#region clis/services/telemetry.d.ts
2
+ type CliInvocationInfo = {
3
+ command?: string;
4
+ subcommand?: string;
5
+ pm?: string;
6
+ argv: string[];
7
+ cwd?: string;
8
+ };
9
+ type TelemetryClient = {
10
+ /**
11
+ * Attaches per-invocation context (command, sanitized argv, package
12
+ * manager) as Sentry tags + a `invocation` context block, plus a single
13
+ * "cli invoked" breadcrumb. Safe to call once per process.
14
+ */
15
+ attachInvocationContext: (info: CliInvocationInfo) => void;
16
+ /**
17
+ * Captures an error (if telemetry is initialized) and flushes before the
18
+ * caller calls process.exit(). Safe no-op when telemetry is disabled.
19
+ */
20
+ captureCliError: (err: unknown, opts?: {
21
+ kind?: "crash" | "user";
22
+ }) => Promise<void>;
23
+ };
24
+ /**
25
+ * Prompts the user once, caches the answer. Must be called before init.
26
+ * Returns `true` if telemetry should be enabled, `false` otherwise.
27
+ */
28
+ declare function resolveTelemetryConsent(): Promise<boolean>;
29
+ /**
30
+ * Initializes Sentry for CLI error reporting if telemetry is enabled.
31
+ * Safe to call multiple times; only the first call takes effect.
32
+ */
33
+ declare function initCliTelemetry(opts: {
34
+ cliName: "ph-cli" | "ph-cmd";
35
+ release?: string;
36
+ }): Promise<TelemetryClient | undefined>;
37
+ /**
38
+ * Explicitly set telemetry consent (used by `ph telemetry on|off`).
39
+ */
40
+ declare function setTelemetryConsent(enabled: boolean): void;
41
+ /**
42
+ * Returns the current telemetry state — useful for `ph telemetry status`.
43
+ */
44
+ declare function getTelemetryStatus(): {
45
+ source: "env";
46
+ enabled: boolean;
47
+ } | {
48
+ source: "config";
49
+ enabled: boolean;
50
+ askedAt: string;
51
+ } | {
52
+ source: "default";
53
+ enabled: false;
54
+ };
55
+ //#endregion
56
+ export { CliInvocationInfo, TelemetryClient, getTelemetryStatus, initCliTelemetry, resolveTelemetryConsent, setTelemetryConsent };
57
+ //# sourceMappingURL=telemetry.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.mts","names":[],"sources":["../../../clis/services/telemetry.ts"],"mappings":";KAmCY,iBAAA;EACV,OAAA;EACA,UAAA;EACA,EAAA;EACA,IAAA;EACA,GAAA;AAAA;AAAA,KAGU,eAAA;EAJV;;;;AAIF;EAME,uBAAA,GAA0B,IAAA,EAAM,iBAAA;;;;;EAKhC,eAAA,GACE,GAAA,WACA,IAAA;IAAS,IAAA;EAAA,MACN,OAAA;AAAA;;;;;iBA8Ce,uBAAA,CAAA,GAA2B,OAAA;;;;;iBAgH3B,gBAAA,CAAiB,IAAA;EACrC,OAAA;EACA,OAAA;AAAA,IACE,OAAA,CAAQ,eAAA;;;;iBAgGI,mBAAA,CAAoB,OAAA;;;;iBAOpB,kBAAA,CAAA;EACV,MAAA;EAAe,OAAA;AAAA;EACf,MAAA;EAAkB,OAAA;EAAkB,OAAA;AAAA;EACpC,MAAA;EAAmB,OAAA;AAAA"}
@@ -0,0 +1,221 @@
1
+ import { homedir } from "node:os";
2
+ import { join } from "node:path";
3
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
+ //#region clis/services/telemetry.ts
5
+ /**
6
+ * CLI telemetry (error reporting via Sentry).
7
+ *
8
+ * Design:
9
+ * - Opt-out by default, asked once on first interactive run.
10
+ * - Stores consent in ~/.ph/telemetry.json so we never ask twice.
11
+ * - Respects PH_NO_TELEMETRY=1 and DO_NOT_TRACK=1 as immediate kill switches.
12
+ * - Non-interactive (TTY missing, CI, piped) defaults to DISABLED — we don't
13
+ * want to hang a CI pipeline on an unanswered prompt, and we don't want to
14
+ * capture errors without informed consent.
15
+ * - DSN is published in the CLI binary; Sentry DSNs accept events but grant
16
+ * no read access, so this is safe. Hardcoded so users can't accidentally
17
+ * misroute events.
18
+ *
19
+ * PII scrubbing in beforeSend hook:
20
+ * - Home-directory paths collapsed to ~
21
+ * - Flag/arg values that look like secrets (tokens, keys) stripped
22
+ * - No source-context from user files
23
+ */
24
+ const SENTRY_DSN = "https://0e7793802288589b4923896118374462@sentry.monitoring.vetra.io/3";
25
+ const TELEMETRY_FILE = join(homedir(), ".ph", "telemetry.json");
26
+ function isExplicitlyDisabled() {
27
+ return process.env.PH_NO_TELEMETRY === "1" || process.env.PH_NO_TELEMETRY === "true" || process.env.DO_NOT_TRACK === "1" || process.env.DO_NOT_TRACK === "true";
28
+ }
29
+ function isExplicitlyEnabled() {
30
+ return process.env.PH_TELEMETRY === "1" || process.env.PH_TELEMETRY === "true";
31
+ }
32
+ function isInteractive() {
33
+ return Boolean(process.stdin.isTTY) && !process.env.CI;
34
+ }
35
+ function readConfig() {
36
+ try {
37
+ if (!existsSync(TELEMETRY_FILE)) return null;
38
+ return JSON.parse(readFileSync(TELEMETRY_FILE, "utf8"));
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+ function writeConfig(cfg) {
44
+ try {
45
+ mkdirSync(join(homedir(), ".ph"), { recursive: true });
46
+ writeFileSync(TELEMETRY_FILE, JSON.stringify(cfg, null, 2));
47
+ } catch {}
48
+ }
49
+ /**
50
+ * Prompts the user once, caches the answer. Must be called before init.
51
+ * Returns `true` if telemetry should be enabled, `false` otherwise.
52
+ */
53
+ async function resolveTelemetryConsent() {
54
+ if (isExplicitlyDisabled()) return false;
55
+ if (isExplicitlyEnabled()) return true;
56
+ const cached = readConfig();
57
+ if (cached) return cached.enabled;
58
+ if (!isInteractive()) return false;
59
+ const enquirer = await import("enquirer");
60
+ try {
61
+ const { enabled } = await enquirer.default.prompt({
62
+ type: "confirm",
63
+ name: "enabled",
64
+ message: "Help improve Powerhouse by sending anonymous error reports? (stack traces only, paths and secrets are scrubbed)",
65
+ initial: true
66
+ });
67
+ writeConfig({
68
+ enabled,
69
+ askedAt: (/* @__PURE__ */ new Date()).toISOString()
70
+ });
71
+ return enabled;
72
+ } catch {
73
+ return false;
74
+ }
75
+ }
76
+ function scrubString(input) {
77
+ if (!input) return input;
78
+ const home = homedir();
79
+ let out = input;
80
+ if (home && out.includes(home)) out = out.split(home).join("~");
81
+ out = out.replace(/(--?(?:token|api[-_]?key|password|secret|auth)[=\s])([^\s]+)/gi, "$1<redacted>");
82
+ return out;
83
+ }
84
+ function scrubEvent(event) {
85
+ const e = event;
86
+ try {
87
+ if (e.message) e.message = scrubString(e.message);
88
+ if (e.logentry?.message) e.logentry.message = scrubString(e.logentry.message);
89
+ const values = e.exception?.values;
90
+ if (Array.isArray(values)) for (const ex of values) {
91
+ if (ex.value) ex.value = scrubString(ex.value);
92
+ const frames = ex.stacktrace?.frames;
93
+ if (Array.isArray(frames)) for (const f of frames) {
94
+ if (f.filename) f.filename = scrubString(f.filename);
95
+ if (f.abs_path) f.abs_path = scrubString(f.abs_path);
96
+ delete f.pre_context;
97
+ delete f.context_line;
98
+ delete f.post_context;
99
+ delete f.vars;
100
+ }
101
+ }
102
+ delete e.server_name;
103
+ if (e.extra) {
104
+ delete e.extra.argv;
105
+ delete e.extra.env;
106
+ }
107
+ } catch {}
108
+ return event;
109
+ }
110
+ /**
111
+ * Initializes Sentry for CLI error reporting if telemetry is enabled.
112
+ * Safe to call multiple times; only the first call takes effect.
113
+ */
114
+ async function initCliTelemetry(opts) {
115
+ if (!await resolveTelemetryConsent()) return;
116
+ const Sentry = await import("@sentry/node-core/light");
117
+ const os = await import("node:os");
118
+ const sentryClient = Sentry.init({
119
+ dsn: SENTRY_DSN,
120
+ release: opts.release,
121
+ environment: process.env.NODE_ENV || "production",
122
+ sendDefaultPii: false,
123
+ defaultIntegrations: false,
124
+ integrations: [],
125
+ beforeSend(event) {
126
+ return scrubEvent(event);
127
+ },
128
+ beforeBreadcrumb(breadcrumb) {
129
+ if (breadcrumb.message) breadcrumb.message = scrubString(breadcrumb.message);
130
+ return breadcrumb;
131
+ }
132
+ });
133
+ Sentry.setTag("cli_name", opts.cliName);
134
+ if (opts.release) Sentry.setTag("cli_version", opts.release);
135
+ Sentry.setTag("os", process.platform);
136
+ Sentry.setTag("arch", process.arch);
137
+ Sentry.setTag("node_version", process.version);
138
+ Sentry.setTag("ci", String(Boolean(process.env.CI)));
139
+ Sentry.setTag("tty", String(Boolean(process.stdin.isTTY)));
140
+ Sentry.setContext("runtime", {
141
+ name: "node",
142
+ version: process.version
143
+ });
144
+ Sentry.setContext("os", {
145
+ name: process.platform,
146
+ version: os.release()
147
+ });
148
+ Sentry.setContext("device", { arch: process.arch });
149
+ if (!sentryClient) return;
150
+ return {
151
+ attachInvocationContext(info) {
152
+ const argv = info.argv.map(scrubString);
153
+ const flagNames = info.argv.filter((a) => a.startsWith("-")).map((a) => a.split("=")[0]);
154
+ if (info.command) Sentry.setTag("command", info.command);
155
+ if (info.subcommand) Sentry.setTag("subcommand", info.subcommand);
156
+ if (info.pm) Sentry.setTag("pm", info.pm);
157
+ Sentry.setContext("invocation", {
158
+ command: info.command,
159
+ subcommand: info.subcommand,
160
+ argv,
161
+ flag_names: flagNames,
162
+ argv_count: argv.length,
163
+ cwd: info.cwd ? scrubString(info.cwd) : void 0
164
+ });
165
+ Sentry.addBreadcrumb({
166
+ category: "cli",
167
+ level: "info",
168
+ message: "cli invoked",
169
+ data: {
170
+ command: info.command,
171
+ pm: info.pm,
172
+ tty: Boolean(process.stdin.isTTY),
173
+ ci: Boolean(process.env.CI)
174
+ }
175
+ });
176
+ },
177
+ captureCliError: async (err, captureOpts) => {
178
+ try {
179
+ Sentry.setTag("error_kind", captureOpts?.kind ?? "crash");
180
+ sentryClient.captureException(err);
181
+ await sentryClient.flush(2e3);
182
+ } catch {}
183
+ }
184
+ };
185
+ }
186
+ /**
187
+ * Explicitly set telemetry consent (used by `ph telemetry on|off`).
188
+ */
189
+ function setTelemetryConsent(enabled) {
190
+ writeConfig({
191
+ enabled,
192
+ askedAt: (/* @__PURE__ */ new Date()).toISOString()
193
+ });
194
+ }
195
+ /**
196
+ * Returns the current telemetry state — useful for `ph telemetry status`.
197
+ */
198
+ function getTelemetryStatus() {
199
+ if (isExplicitlyDisabled()) return {
200
+ source: "env",
201
+ enabled: false
202
+ };
203
+ if (isExplicitlyEnabled()) return {
204
+ source: "env",
205
+ enabled: true
206
+ };
207
+ const cached = readConfig();
208
+ if (cached) return {
209
+ source: "config",
210
+ enabled: cached.enabled,
211
+ askedAt: cached.askedAt
212
+ };
213
+ return {
214
+ source: "default",
215
+ enabled: false
216
+ };
217
+ }
218
+ //#endregion
219
+ export { getTelemetryStatus, initCliTelemetry, resolveTelemetryConsent, setTelemetryConsent };
220
+
221
+ //# sourceMappingURL=telemetry.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.mjs","names":[],"sources":["../../../clis/services/telemetry.ts"],"sourcesContent":["/**\n * CLI telemetry (error reporting via Sentry).\n *\n * Design:\n * - Opt-out by default, asked once on first interactive run.\n * - Stores consent in ~/.ph/telemetry.json so we never ask twice.\n * - Respects PH_NO_TELEMETRY=1 and DO_NOT_TRACK=1 as immediate kill switches.\n * - Non-interactive (TTY missing, CI, piped) defaults to DISABLED — we don't\n * want to hang a CI pipeline on an unanswered prompt, and we don't want to\n * capture errors without informed consent.\n * - DSN is published in the CLI binary; Sentry DSNs accept events but grant\n * no read access, so this is safe. Hardcoded so users can't accidentally\n * misroute events.\n *\n * PII scrubbing in beforeSend hook:\n * - Home-directory paths collapsed to ~\n * - Flag/arg values that look like secrets (tokens, keys) stripped\n * - No source-context from user files\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n// Sentry project \"ph-cli\" on the powerhouse-hosted Sentry instance.\n// Public DSNs are safe to ship — they grant write-only ingest access.\nconst SENTRY_DSN =\n \"https://0e7793802288589b4923896118374462@sentry.monitoring.vetra.io/3\";\n\nconst TELEMETRY_FILE = join(homedir(), \".ph\", \"telemetry.json\");\n\ntype TelemetryConfig = {\n enabled: boolean;\n askedAt: string;\n};\n\nexport type CliInvocationInfo = {\n command?: string;\n subcommand?: string;\n pm?: string;\n argv: string[];\n cwd?: string;\n};\n\nexport type TelemetryClient = {\n /**\n * Attaches per-invocation context (command, sanitized argv, package\n * manager) as Sentry tags + a `invocation` context block, plus a single\n * \"cli invoked\" breadcrumb. Safe to call once per process.\n */\n attachInvocationContext: (info: CliInvocationInfo) => void;\n /**\n * Captures an error (if telemetry is initialized) and flushes before the\n * caller calls process.exit(). Safe no-op when telemetry is disabled.\n */\n captureCliError: (\n err: unknown,\n opts?: { kind?: \"crash\" | \"user\" },\n ) => Promise<void>;\n};\n\nfunction isExplicitlyDisabled(): boolean {\n // Standard opt-out signals respected by most OSS CLIs.\n return (\n process.env.PH_NO_TELEMETRY === \"1\" ||\n process.env.PH_NO_TELEMETRY === \"true\" ||\n process.env.DO_NOT_TRACK === \"1\" ||\n process.env.DO_NOT_TRACK === \"true\"\n );\n}\n\nfunction isExplicitlyEnabled(): boolean {\n return (\n process.env.PH_TELEMETRY === \"1\" || process.env.PH_TELEMETRY === \"true\"\n );\n}\n\nfunction isInteractive(): boolean {\n // Only ask if stdin is a TTY and CI env isn't set.\n return Boolean(process.stdin.isTTY) && !process.env.CI;\n}\n\nfunction readConfig(): TelemetryConfig | null {\n try {\n if (!existsSync(TELEMETRY_FILE)) return null;\n return JSON.parse(readFileSync(TELEMETRY_FILE, \"utf8\")) as TelemetryConfig;\n } catch {\n return null;\n }\n}\n\nfunction writeConfig(cfg: TelemetryConfig): void {\n try {\n mkdirSync(join(homedir(), \".ph\"), { recursive: true });\n writeFileSync(TELEMETRY_FILE, JSON.stringify(cfg, null, 2));\n } catch {\n // non-fatal; we'll just ask again next time\n }\n}\n\n/**\n * Prompts the user once, caches the answer. Must be called before init.\n * Returns `true` if telemetry should be enabled, `false` otherwise.\n */\nexport async function resolveTelemetryConsent(): Promise<boolean> {\n if (isExplicitlyDisabled()) return false;\n if (isExplicitlyEnabled()) return true;\n\n const cached = readConfig();\n if (cached) return cached.enabled;\n\n if (!isInteractive()) {\n // Non-interactive first run: stay silent, don't ask, don't send. User can\n // opt in later with `ph telemetry on` or PH_TELEMETRY=1.\n return false;\n }\n\n const enquirer = await import(\"enquirer\");\n try {\n const { enabled } = await enquirer.default.prompt<{ enabled: boolean }>({\n type: \"confirm\",\n name: \"enabled\",\n message:\n \"Help improve Powerhouse by sending anonymous error reports? \" +\n \"(stack traces only, paths and secrets are scrubbed)\",\n initial: true,\n });\n writeConfig({ enabled, askedAt: new Date().toISOString() });\n return enabled;\n } catch {\n // user hit Ctrl-C during prompt — treat as no, but don't persist so we\n // ask again next time\n return false;\n }\n}\n\nfunction scrubString(input: string): string {\n if (!input) return input;\n const home = homedir();\n let out = input;\n // Collapse home dir to ~\n if (home && out.includes(home)) {\n out = out.split(home).join(\"~\");\n }\n // Strip common secret-shaped flag values: --token=XYZ, --api-key XYZ, etc.\n out = out.replace(\n /(--?(?:token|api[-_]?key|password|secret|auth)[=\\s])([^\\s]+)/gi,\n \"$1<redacted>\",\n );\n return out;\n}\n\ntype ScrubbableFrame = {\n filename?: string;\n abs_path?: string;\n pre_context?: unknown;\n context_line?: unknown;\n post_context?: unknown;\n vars?: unknown;\n};\n\ntype ScrubbableException = {\n value?: string;\n stacktrace?: { frames?: ScrubbableFrame[] };\n};\n\ntype ScrubbableEvent = {\n message?: string;\n logentry?: { message?: string };\n exception?: { values?: ScrubbableException[] };\n server_name?: string;\n extra?: Record<string, unknown>;\n};\n\nfunction scrubEvent<T>(event: T): T {\n const e = event as ScrubbableEvent;\n try {\n if (e.message) e.message = scrubString(e.message);\n if (e.logentry?.message) {\n e.logentry.message = scrubString(e.logentry.message);\n }\n const values = e.exception?.values;\n if (Array.isArray(values)) {\n for (const ex of values) {\n if (ex.value) ex.value = scrubString(ex.value);\n const frames = ex.stacktrace?.frames;\n if (Array.isArray(frames)) {\n for (const f of frames) {\n if (f.filename) f.filename = scrubString(f.filename);\n if (f.abs_path) f.abs_path = scrubString(f.abs_path);\n // Drop captured source context from user machines — privacy.\n delete f.pre_context;\n delete f.context_line;\n delete f.post_context;\n delete f.vars;\n }\n }\n }\n }\n // Server name can leak the user's machine hostname; drop it.\n delete e.server_name;\n // Strip raw argv from extra context.\n if (e.extra) {\n delete e.extra.argv;\n delete e.extra.env;\n }\n } catch {\n // Never let scrubbing throw — worst case we drop the event below.\n }\n return event;\n}\n\n/**\n * Initializes Sentry for CLI error reporting if telemetry is enabled.\n * Safe to call multiple times; only the first call takes effect.\n */\nexport async function initCliTelemetry(opts: {\n cliName: \"ph-cli\" | \"ph-cmd\";\n release?: string;\n}): Promise<TelemetryClient | undefined> {\n const enabled = await resolveTelemetryConsent();\n if (!enabled) return;\n\n const Sentry = await import(\"@sentry/node-core/light\");\n const os = await import(\"node:os\");\n const sentryClient = Sentry.init({\n dsn: SENTRY_DSN,\n release: opts.release,\n environment: process.env.NODE_ENV || \"production\",\n sendDefaultPii: false,\n defaultIntegrations: false,\n integrations: [\n // Only the bare minimum — no automatic HTTP/FS instrumentation.\n ],\n beforeSend(event) {\n return scrubEvent(event);\n },\n beforeBreadcrumb(breadcrumb) {\n if (breadcrumb.message) {\n breadcrumb.message = scrubString(breadcrumb.message);\n }\n return breadcrumb;\n },\n });\n Sentry.setTag(\"cli_name\", opts.cliName);\n if (opts.release) Sentry.setTag(\"cli_version\", opts.release);\n // Environment tags — `defaultIntegrations: false` strips Sentry's auto\n // node-context, so attach the bits we care about manually (and keep\n // hostname out of it).\n Sentry.setTag(\"os\", process.platform);\n Sentry.setTag(\"arch\", process.arch);\n Sentry.setTag(\"node_version\", process.version);\n Sentry.setTag(\"ci\", String(Boolean(process.env.CI)));\n Sentry.setTag(\"tty\", String(Boolean(process.stdin.isTTY)));\n Sentry.setContext(\"runtime\", {\n name: \"node\",\n version: process.version,\n });\n Sentry.setContext(\"os\", {\n name: process.platform,\n version: os.release(),\n });\n Sentry.setContext(\"device\", {\n arch: process.arch,\n });\n if (!sentryClient) {\n return;\n }\n return {\n attachInvocationContext(info: CliInvocationInfo) {\n const argv = info.argv.map(scrubString);\n const flagNames = info.argv\n .filter((a) => a.startsWith(\"-\"))\n .map((a) => a.split(\"=\")[0]);\n if (info.command) Sentry.setTag(\"command\", info.command);\n if (info.subcommand) Sentry.setTag(\"subcommand\", info.subcommand);\n if (info.pm) Sentry.setTag(\"pm\", info.pm);\n Sentry.setContext(\"invocation\", {\n command: info.command,\n subcommand: info.subcommand,\n argv,\n flag_names: flagNames,\n argv_count: argv.length,\n cwd: info.cwd ? scrubString(info.cwd) : undefined,\n });\n Sentry.addBreadcrumb({\n category: \"cli\",\n level: \"info\",\n message: \"cli invoked\",\n data: {\n command: info.command,\n pm: info.pm,\n tty: Boolean(process.stdin.isTTY),\n ci: Boolean(process.env.CI),\n },\n });\n },\n captureCliError: async (\n err: unknown,\n captureOpts?: { kind?: \"crash\" | \"user\" },\n ) => {\n try {\n Sentry.setTag(\"error_kind\", captureOpts?.kind ?? \"crash\");\n sentryClient.captureException(err);\n await sentryClient.flush(2000);\n } catch {\n // Reporting must never mask the real error.\n }\n },\n };\n}\n\n/**\n * Explicitly set telemetry consent (used by `ph telemetry on|off`).\n */\nexport function setTelemetryConsent(enabled: boolean): void {\n writeConfig({ enabled, askedAt: new Date().toISOString() });\n}\n\n/**\n * Returns the current telemetry state — useful for `ph telemetry status`.\n */\nexport function getTelemetryStatus():\n | { source: \"env\"; enabled: boolean }\n | { source: \"config\"; enabled: boolean; askedAt: string }\n | { source: \"default\"; enabled: false } {\n if (isExplicitlyDisabled()) return { source: \"env\", enabled: false };\n if (isExplicitlyEnabled()) return { source: \"env\", enabled: true };\n const cached = readConfig();\n if (cached)\n return {\n source: \"config\",\n enabled: cached.enabled,\n askedAt: cached.askedAt,\n };\n return { source: \"default\", enabled: false };\n}\n"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ //#region clis/utils.d.ts
2
+ declare class NodeVersionError extends Error {
3
+ constructor(currentVersion: string, minimumVersion: string);
4
+ static isError(error: unknown): error is NodeVersionError;
5
+ }
6
+ declare function assertNodeVersion(nodeVersion?: string): void;
7
+ //#endregion
8
+ export { NodeVersionError, assertNodeVersion };
9
+ //# sourceMappingURL=utils.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.mts","names":[],"sources":["../../clis/utils.ts"],"mappings":";cAIa,gBAAA,SAAyB,KAAA;cACxB,cAAA,UAAwB,cAAA;EAAA,OAO7B,OAAA,CAAQ,KAAA,YAAiB,KAAA,IAAS,gBAAA;AAAA;AAAA,iBAK3B,iBAAA,CAAkB,WAAA"}