@powerhousedao/shared 6.2.3-dev.9 → 6.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analytics/index.d.ts +4 -4
- package/dist/analytics/index.d.ts.map +1 -1
- package/dist/analytics/index.js +25 -24
- package/dist/analytics/index.js.map +1 -1
- package/dist/clis/args/{access-token-To5LaIfL.mjs → access-token-3Hqv_U8I.mjs} +2 -2
- package/dist/clis/args/{access-token-To5LaIfL.mjs.map → access-token-3Hqv_U8I.mjs.map} +1 -1
- package/dist/clis/args/access-token-C5sKsH2O.d.mts +16 -0
- package/dist/clis/args/access-token-C5sKsH2O.d.mts.map +1 -0
- package/dist/clis/args/access-token.d.mts +3 -2
- package/dist/clis/args/access-token.mjs +1 -1
- package/dist/clis/args/{common-CcYbXrBx.mjs → common-BXabdhiO.mjs} +30 -7
- package/dist/clis/args/common-BXabdhiO.mjs.map +1 -0
- package/dist/clis/args/common-Bnm859xU.d.mts +186 -0
- package/dist/clis/args/common-Bnm859xU.d.mts.map +1 -0
- package/dist/clis/args/common.d.mts +3 -2
- package/dist/clis/args/common.mjs +1 -1
- package/dist/clis/args/{connect-BVVvCmL9.mjs → connect-BG3HkYBk.mjs} +8 -3
- package/dist/clis/args/connect-BG3HkYBk.mjs.map +1 -0
- package/dist/clis/args/connect-DjeiqXus.d.mts +450 -0
- package/dist/clis/args/connect-DjeiqXus.d.mts.map +1 -0
- package/dist/clis/args/connect.d.mts +3 -2
- package/dist/clis/args/connect.mjs +1 -1
- package/dist/clis/args/{generate-CA916Fvj.mjs → generate-BOItUNvP.mjs} +40 -2
- package/dist/clis/args/generate-BOItUNvP.mjs.map +1 -0
- package/dist/clis/args/generate-BPBBLeUD.d.mts +103 -0
- package/dist/clis/args/generate-BPBBLeUD.d.mts.map +1 -0
- package/dist/clis/args/generate.d.mts +3 -2
- package/dist/clis/args/generate.mjs +1 -1
- package/dist/clis/args/{help-Bqbgbalu.mjs → help-DS1NlcJQ.mjs} +15 -15
- package/dist/clis/args/{help-Bqbgbalu.mjs.map → help-DS1NlcJQ.mjs.map} +1 -1
- package/dist/clis/args/help-DVPTARGn.d.mts +190 -0
- package/dist/clis/args/help-DVPTARGn.d.mts.map +1 -0
- package/dist/clis/args/help.d.mts +3 -2
- package/dist/clis/args/help.mjs +1 -1
- package/dist/clis/args/index.d.mts +20 -19
- package/dist/clis/args/index.mjs +18 -18
- package/dist/clis/args/init-CFeTq4Cq.d.mts +52 -0
- package/dist/clis/args/init-CFeTq4Cq.d.mts.map +1 -0
- package/dist/clis/args/{init-rz9Ei7nq.mjs → init-Dvb42Y87.mjs} +2 -2
- package/dist/clis/args/{init-rz9Ei7nq.mjs.map → init-Dvb42Y87.mjs.map} +1 -1
- package/dist/clis/args/init.d.mts +3 -2
- package/dist/clis/args/init.mjs +1 -1
- package/dist/clis/args/inspect-BjRmtB86.d.mts +13 -0
- package/dist/clis/args/inspect-BjRmtB86.d.mts.map +1 -0
- package/dist/clis/args/{inspect-tckH8mSw.mjs → inspect-UNQgtuR0.mjs} +2 -2
- package/dist/clis/args/{inspect-tckH8mSw.mjs.map → inspect-UNQgtuR0.mjs.map} +1 -1
- package/dist/clis/args/inspect.d.mts +3 -2
- package/dist/clis/args/inspect.mjs +1 -1
- package/dist/clis/args/install-B0tMyUMw.d.mts +37 -0
- package/dist/clis/args/install-B0tMyUMw.d.mts.map +1 -0
- package/dist/clis/args/{install-Sz2DlCHh.mjs → install-F-27kF6m.mjs} +2 -2
- package/dist/clis/args/{install-Sz2DlCHh.mjs.map → install-F-27kF6m.mjs.map} +1 -1
- package/dist/clis/args/install.d.mts +3 -2
- package/dist/clis/args/install.mjs +1 -1
- package/dist/clis/args/{list-DM2d6f1m.mjs → list-Bkfn8Yuo.mjs} +2 -2
- package/dist/clis/args/{list-DM2d6f1m.mjs.map → list-Bkfn8Yuo.mjs.map} +1 -1
- package/dist/clis/args/list-DrbxA7JJ.d.mts +10 -0
- package/dist/clis/args/list-DrbxA7JJ.d.mts.map +1 -0
- package/dist/clis/args/list.d.mts +3 -2
- package/dist/clis/args/list.mjs +1 -1
- package/dist/clis/args/{login-DiKT5Jfu.mjs → login-CIFkQExi.mjs} +2 -2
- package/dist/clis/args/{login-DiKT5Jfu.mjs.map → login-CIFkQExi.mjs.map} +1 -1
- package/dist/clis/args/login-CXRJXg71.d.mts +25 -0
- package/dist/clis/args/login-CXRJXg71.d.mts.map +1 -0
- package/dist/clis/args/login.d.mts +3 -2
- package/dist/clis/args/login.mjs +1 -1
- package/dist/clis/args/{magic-string.es-h_0HDQBQ.mjs → magic-string.es-BWtG0AyA.mjs} +33 -27
- package/dist/clis/args/magic-string.es-BWtG0AyA.mjs.map +1 -0
- package/dist/clis/args/migrate-DkCQRL2D.d.mts +19 -0
- package/dist/clis/args/migrate-DkCQRL2D.d.mts.map +1 -0
- package/dist/clis/args/{migrate-CG_gWc4k.mjs → migrate-DwJGhc2d.mjs} +2 -2
- package/dist/clis/args/{migrate-CG_gWc4k.mjs.map → migrate-DwJGhc2d.mjs.map} +1 -1
- package/dist/clis/args/migrate.d.mts +3 -2
- package/dist/clis/args/migrate.mjs +1 -1
- package/dist/clis/args/publish-CeW4bOrl.d.mts +16 -0
- package/dist/clis/args/publish-CeW4bOrl.d.mts.map +1 -0
- package/dist/clis/args/{publish-DDZYsUfV.mjs → publish-DHqlGWmL.mjs} +2 -2
- package/dist/clis/args/{publish-DDZYsUfV.mjs.map → publish-DHqlGWmL.mjs.map} +1 -1
- package/dist/clis/args/publish.d.mts +3 -2
- package/dist/clis/args/publish.mjs +1 -1
- package/dist/clis/args/{registry-C_OVFQmI.mjs → registry-DIwES3l2.mjs} +2 -2
- package/dist/clis/args/{registry-C_OVFQmI.mjs.map → registry-DIwES3l2.mjs.map} +1 -1
- package/dist/clis/args/registry-DNH8xuaE.d.mts +17 -0
- package/dist/clis/args/registry-DNH8xuaE.d.mts.map +1 -0
- package/dist/clis/args/registry.d.mts +3 -2
- package/dist/clis/args/registry.mjs +1 -1
- package/dist/clis/args/service-BBx5GDTc.d.mts +27 -0
- package/dist/clis/args/service-BBx5GDTc.d.mts.map +1 -0
- package/dist/clis/args/{service-CHOOr0Gg.mjs → service-Cpjlyeu5.mjs} +2 -2
- package/dist/clis/args/{service-CHOOr0Gg.mjs.map → service-Cpjlyeu5.mjs.map} +1 -1
- package/dist/clis/args/service.d.mts +3 -2
- package/dist/clis/args/service.mjs +1 -1
- package/dist/clis/args/{switchboard-Dsp7nosi.mjs → switchboard-CCaAtU56.mjs} +2 -2
- package/dist/clis/args/{switchboard-Dsp7nosi.mjs.map → switchboard-CCaAtU56.mjs.map} +1 -1
- package/dist/clis/args/switchboard-RrNb1fUH.d.mts +70 -0
- package/dist/clis/args/switchboard-RrNb1fUH.d.mts.map +1 -0
- package/dist/clis/args/switchboard.d.mts +3 -2
- package/dist/clis/args/switchboard.mjs +1 -1
- package/dist/clis/args/{uninstall-CTwTX0u_.mjs → uninstall-3U0B8g5c.mjs} +2 -2
- package/dist/clis/args/{uninstall-CTwTX0u_.mjs.map → uninstall-3U0B8g5c.mjs.map} +1 -1
- package/dist/clis/args/uninstall-CDx7lwxn.d.mts +28 -0
- package/dist/clis/args/uninstall-CDx7lwxn.d.mts.map +1 -0
- package/dist/clis/args/uninstall.d.mts +3 -2
- package/dist/clis/args/uninstall.mjs +1 -1
- package/dist/clis/args/unpublish-CEDsk6uv.d.mts +22 -0
- package/dist/clis/args/unpublish-CEDsk6uv.d.mts.map +1 -0
- package/dist/clis/args/{unpublish-BPMkGEg5.mjs → unpublish-s9zUh6Gw.mjs} +2 -2
- package/dist/clis/args/{unpublish-BPMkGEg5.mjs.map → unpublish-s9zUh6Gw.mjs.map} +1 -1
- package/dist/clis/args/unpublish.d.mts +3 -2
- package/dist/clis/args/unpublish.mjs +1 -1
- package/dist/clis/args/{vetra-b4KUu1Ax.mjs → vetra-BUNlAAPp.mjs} +2 -2
- package/dist/clis/args/{vetra-b4KUu1Ax.mjs.map → vetra-BUNlAAPp.mjs.map} +1 -1
- package/dist/clis/args/vetra-DkDaWFPi.d.mts +111 -0
- package/dist/clis/args/vetra-DkDaWFPi.d.mts.map +1 -0
- package/dist/clis/args/vetra-ports.test.d.mts +2 -1
- package/dist/clis/args/vetra-ports.test.mjs +285 -180
- package/dist/clis/args/vetra-ports.test.mjs.map +1 -1
- package/dist/clis/args/vetra.d.mts +3 -2
- package/dist/clis/args/vetra.mjs +1 -1
- package/dist/clis/build-config-CmdMi_wu.mjs +230 -0
- package/dist/clis/build-config-CmdMi_wu.mjs.map +1 -0
- package/dist/clis/build-config.d.mts +44 -10614
- package/dist/clis/build-config.d.mts.map +1 -1
- package/dist/clis/build-config.mjs +2 -93
- package/dist/clis/build-pieces.d.mts +100 -0
- package/dist/clis/build-pieces.d.mts.map +1 -0
- package/dist/clis/build-pieces.mjs +371 -0
- package/dist/clis/build-pieces.mjs.map +1 -0
- package/dist/clis/command-names.d.mts +2 -2
- package/dist/clis/command-names.d.mts.map +1 -1
- package/dist/clis/command-names.mjs.map +1 -1
- package/dist/clis/constants.d.mts +3 -475
- package/dist/clis/constants.mjs +17 -8
- package/dist/clis/constants.mjs.map +1 -1
- package/dist/clis/index-6CZwrmQi.d.mts +11154 -0
- package/dist/clis/index-6CZwrmQi.d.mts.map +1 -0
- package/dist/clis/index.d.mts +1368 -1221
- package/dist/clis/index.d.mts.map +1 -1
- package/dist/clis/index.mjs +209 -65
- package/dist/clis/index.mjs.map +1 -1
- package/dist/clis/project-env.d.mts +4 -4
- package/dist/clis/project-env.d.mts.map +1 -1
- package/dist/clis/project-env.mjs.map +1 -1
- package/dist/clis/project-ports.d.mts +5 -5
- package/dist/clis/project-ports.d.mts.map +1 -1
- package/dist/clis/project-ports.mjs +1 -1
- package/dist/clis/project-ports.mjs.map +1 -1
- package/dist/clis/services/telemetry.d.mts +7 -7
- package/dist/clis/services/telemetry.d.mts.map +1 -1
- package/dist/clis/services/telemetry.mjs.map +1 -1
- package/dist/clis/types-nzer4WIr.d.mts +541 -0
- package/dist/clis/types-nzer4WIr.d.mts.map +1 -0
- package/dist/clis/utils.d.mts +4 -4
- package/dist/clis/utils.d.mts.map +1 -1
- package/dist/clis/utils.mjs.map +1 -1
- package/dist/{config-loader-B845Hidf.js → config-loader-C6dZB_iX.js} +28 -8
- package/dist/config-loader-C6dZB_iX.js.map +1 -0
- package/dist/connect/config-loader.d.ts +14 -8
- package/dist/connect/config-loader.d.ts.map +1 -1
- package/dist/connect/config-loader.js +2 -2
- package/dist/connect/index.d.ts +190 -56
- package/dist/connect/index.d.ts.map +1 -1
- package/dist/connect/index.js +303 -30
- package/dist/connect/index.js.map +1 -1
- package/dist/connect/json-adapter.d.ts +3 -4
- package/dist/connect/json-adapter.d.ts.map +1 -1
- package/dist/connect/json-adapter.js.map +1 -1
- package/dist/{constants-BP_gjn8g.d.ts → constants-DO4IYxuR.d.ts} +2 -1
- package/dist/constants-DO4IYxuR.d.ts.map +1 -0
- package/dist/constants.d.ts +3 -2
- package/dist/constants.js.map +1 -1
- package/dist/document-drive/index.d.ts +104 -101
- package/dist/document-drive/index.d.ts.map +1 -1
- package/dist/document-drive/index.js +39 -19
- package/dist/document-drive/index.js.map +1 -1
- package/dist/document-model/index.d.ts +5 -4
- package/dist/document-model/index.js +605 -228
- package/dist/document-model/index.js.map +1 -1
- package/dist/document-model/mock.d.ts +2 -3
- package/dist/document-model/mock.d.ts.map +1 -1
- package/dist/document-model/mock.js +2 -1
- package/dist/document-model/mock.js.map +1 -1
- package/dist/document-model/utils.d.ts +3 -2
- package/dist/document-model/utils.js +1 -1
- package/dist/index-5gwOhPG5.d.ts +36 -0
- package/dist/index-5gwOhPG5.d.ts.map +1 -0
- package/dist/{index-DP3QgHTq.d.ts → index-D6833Siv.d.ts} +131 -28
- package/dist/index-D6833Siv.d.ts.map +1 -0
- package/dist/index.d.ts +7 -6
- package/dist/index.js.map +1 -1
- package/dist/processors/index.d.ts +3 -2
- package/dist/processors/index.js +5 -1
- package/dist/processors/index.js.map +1 -1
- package/dist/registry/index.d.ts +15 -15
- package/dist/registry/index.d.ts.map +1 -1
- package/dist/registry/index.js +14 -5
- package/dist/registry/index.js.map +1 -1
- package/dist/registry/manifest-slim.d.ts +4 -3
- package/dist/registry/manifest-slim.d.ts.map +1 -1
- package/dist/registry/manifest-slim.js +22 -2
- package/dist/registry/manifest-slim.js.map +1 -1
- package/dist/registry/updates.d.ts +3 -3
- package/dist/registry/updates.d.ts.map +1 -1
- package/dist/registry/updates.js.map +1 -1
- package/dist/registry/urls.d.ts +2 -2
- package/dist/registry/urls.d.ts.map +1 -1
- package/dist/registry/urls.js.map +1 -1
- package/dist/{types-C1in4fl3.d.ts → types-Bsp8VL2y.d.ts} +2 -2
- package/dist/types-Bsp8VL2y.d.ts.map +1 -0
- package/dist/{types-DDpRvBK_.d.ts → types-CI3ren9F.d.ts} +23 -35
- package/dist/types-CI3ren9F.d.ts.map +1 -0
- package/dist/{types-BIoOWpOT.d.ts → types-CmeoQg_J.d.ts} +602 -437
- package/dist/types-CmeoQg_J.d.ts.map +1 -0
- package/dist/{utils-DP122OSS.d.ts → utils-BrKxCmKx.d.ts} +2 -2
- package/dist/utils-BrKxCmKx.d.ts.map +1 -0
- package/dist/{utils-aaTmfxFW.js → utils-CSF716ux.js} +10 -3
- package/dist/utils-CSF716ux.js.map +1 -0
- package/package.json +10 -5
- package/dist/clis/args/access-token--9H57y8J.d.mts +0 -18
- package/dist/clis/args/access-token--9H57y8J.d.mts.map +0 -1
- package/dist/clis/args/common-CcYbXrBx.mjs.map +0 -1
- package/dist/clis/args/common-Dsf1vpHV.d.mts +0 -183
- package/dist/clis/args/common-Dsf1vpHV.d.mts.map +0 -1
- package/dist/clis/args/connect-BVVvCmL9.mjs.map +0 -1
- package/dist/clis/args/connect-DetTnI8g.d.mts +0 -443
- package/dist/clis/args/connect-DetTnI8g.d.mts.map +0 -1
- package/dist/clis/args/generate-CA916Fvj.mjs.map +0 -1
- package/dist/clis/args/generate-DnV-bfXt.d.mts +0 -87
- package/dist/clis/args/generate-DnV-bfXt.d.mts.map +0 -1
- package/dist/clis/args/help-CjAsudUn.d.mts +0 -193
- package/dist/clis/args/help-CjAsudUn.d.mts.map +0 -1
- package/dist/clis/args/init-CUXyfquF.d.mts +0 -55
- package/dist/clis/args/init-CUXyfquF.d.mts.map +0 -1
- package/dist/clis/args/inspect-DtZeoA5c.d.mts +0 -15
- package/dist/clis/args/inspect-DtZeoA5c.d.mts.map +0 -1
- package/dist/clis/args/install-CkP8zcHb.d.mts +0 -40
- package/dist/clis/args/install-CkP8zcHb.d.mts.map +0 -1
- package/dist/clis/args/list-BGf8s_gj.d.mts +0 -12
- package/dist/clis/args/list-BGf8s_gj.d.mts.map +0 -1
- package/dist/clis/args/login-DNsHwvqk.d.mts +0 -27
- package/dist/clis/args/login-DNsHwvqk.d.mts.map +0 -1
- package/dist/clis/args/magic-string.es-h_0HDQBQ.mjs.map +0 -1
- package/dist/clis/args/migrate-sixkWhxm.d.mts +0 -21
- package/dist/clis/args/migrate-sixkWhxm.d.mts.map +0 -1
- package/dist/clis/args/publish-Dqe5DUst.d.mts +0 -18
- package/dist/clis/args/publish-Dqe5DUst.d.mts.map +0 -1
- package/dist/clis/args/registry-C_RfTsKB.d.mts +0 -19
- package/dist/clis/args/registry-C_RfTsKB.d.mts.map +0 -1
- package/dist/clis/args/service-DhIkf8-O.d.mts +0 -22
- package/dist/clis/args/service-DhIkf8-O.d.mts.map +0 -1
- package/dist/clis/args/switchboard-BlPaq2at.d.mts +0 -72
- package/dist/clis/args/switchboard-BlPaq2at.d.mts.map +0 -1
- package/dist/clis/args/uninstall-BpA3YYbw.d.mts +0 -31
- package/dist/clis/args/uninstall-BpA3YYbw.d.mts.map +0 -1
- package/dist/clis/args/unpublish-CvwaIimG.d.mts +0 -24
- package/dist/clis/args/unpublish-CvwaIimG.d.mts.map +0 -1
- package/dist/clis/args/vetra-DLQ6lhLl.d.mts +0 -113
- package/dist/clis/args/vetra-DLQ6lhLl.d.mts.map +0 -1
- package/dist/clis/build-config.mjs.map +0 -1
- package/dist/clis/constants.d.mts.map +0 -1
- package/dist/config-loader-B845Hidf.js.map +0 -1
- package/dist/constants-BP_gjn8g.d.ts.map +0 -1
- package/dist/index-C_iVZe7f.d.ts +0 -7
- package/dist/index-C_iVZe7f.d.ts.map +0 -1
- package/dist/index-DP3QgHTq.d.ts.map +0 -1
- package/dist/types-BIoOWpOT.d.ts.map +0 -1
- package/dist/types-C1in4fl3.d.ts.map +0 -1
- package/dist/types-DDpRvBK_.d.ts.map +0 -1
- package/dist/utils-DP122OSS.d.ts.map +0 -1
- package/dist/utils-aaTmfxFW.js.map +0 -1
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
* Idempotent per directory — CLI argument defaults call it lazily and may
|
|
12
12
|
* call it more than once per process.
|
|
13
13
|
*/
|
|
14
|
-
declare function loadProjectEnv(cwd?: string): void;
|
|
14
|
+
export declare function loadProjectEnv(cwd?: string): void;
|
|
15
15
|
/** Test seam: forget which directory has already been loaded. */
|
|
16
|
-
declare function resetProjectEnvForTests(): void;
|
|
16
|
+
export declare function resetProjectEnvForTests(): void;
|
|
17
17
|
/**
|
|
18
18
|
* Read an environment variable as a TCP port.
|
|
19
19
|
*
|
|
@@ -21,7 +21,7 @@ declare function resetProjectEnvForTests(): void;
|
|
|
21
21
|
* throwing: a typo in `.env` should fall through to the next source in the
|
|
22
22
|
* precedence chain, not break the CLI.
|
|
23
23
|
*/
|
|
24
|
-
declare function readPortEnv(name: string): number | undefined;
|
|
24
|
+
export declare function readPortEnv(name: string): number | undefined;
|
|
25
25
|
//#endregion
|
|
26
|
-
export {
|
|
26
|
+
export {};
|
|
27
27
|
//# sourceMappingURL=project-env.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-env.d.mts","names":[],"sources":["../../clis/project-env.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"project-env.d.mts","names":[],"sources":["../../clis/project-env.ts"],"mappings":";;;;;;;;;;;;;wBAiBgB,eAAe;;wBAUf;;;;;;;;wBAWA,YAAY"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-env.mjs","names":[],"sources":["../../clis/project-env.ts"],"sourcesContent":["import dotenv from \"dotenv\";\nimport { join } from \"node:path\";\n\nlet loadedFor: string | undefined;\n\n/**\n * Load the project's `.env.local` and `.env` into `process.env`.\n *\n * `dotenv` never overwrites a variable that is already set, so loading\n * `.env.local` first and `.env` second produces the precedence the CLI\n * documents: a real `process.env` value beats `.env.local`, which beats\n * `.env`. One `process.env` lookup afterwards therefore covers all three\n * sources.\n *\n * Idempotent per directory — CLI argument defaults call it lazily and may\n * call it more than once per process.\n */\nexport function loadProjectEnv(cwd: string = process.cwd()): void {\n if (loadedFor === cwd) return;\n loadedFor = cwd;\n // dotenv 16 logs nothing for a missing file, so no suppression option is\n // needed here (and none exists before dotenv 17).\n dotenv.config({ path: join(cwd, \".env.local\") });\n dotenv.config({ path: join(cwd, \".env\") });\n}\n\n/** Test seam: forget which directory has already been loaded. */\nexport function resetProjectEnvForTests(): void {\n loadedFor = undefined;\n}\n\n/**\n * Read an environment variable as a TCP port.\n *\n * Anything that is not a plain integer in 1-65535 is ignored rather than\n * throwing: a typo in `.env` should fall through to the next source in the\n * precedence chain, not break the CLI.\n */\nexport function readPortEnv(name: string): number | undefined {\n const raw = process.env[name]?.trim();\n if (!raw || !/^\\d+$/.test(raw)) return undefined;\n const port = Number(raw);\n if (port < 1 || port > 65535) return undefined;\n return port;\n}\n"],"mappings":";;;AAGA,IAAI;;;;;;;;;;;;;AAcJ,SAAgB,eAAe,MAAc,QAAQ,
|
|
1
|
+
{"version":3,"file":"project-env.mjs","names":[],"sources":["../../clis/project-env.ts"],"sourcesContent":["import dotenv from \"dotenv\";\nimport { join } from \"node:path\";\n\nlet loadedFor: string | undefined;\n\n/**\n * Load the project's `.env.local` and `.env` into `process.env`.\n *\n * `dotenv` never overwrites a variable that is already set, so loading\n * `.env.local` first and `.env` second produces the precedence the CLI\n * documents: a real `process.env` value beats `.env.local`, which beats\n * `.env`. One `process.env` lookup afterwards therefore covers all three\n * sources.\n *\n * Idempotent per directory — CLI argument defaults call it lazily and may\n * call it more than once per process.\n */\nexport function loadProjectEnv(cwd: string = process.cwd()): void {\n if (loadedFor === cwd) return;\n loadedFor = cwd;\n // dotenv 16 logs nothing for a missing file, so no suppression option is\n // needed here (and none exists before dotenv 17).\n dotenv.config({ path: join(cwd, \".env.local\") });\n dotenv.config({ path: join(cwd, \".env\") });\n}\n\n/** Test seam: forget which directory has already been loaded. */\nexport function resetProjectEnvForTests(): void {\n loadedFor = undefined;\n}\n\n/**\n * Read an environment variable as a TCP port.\n *\n * Anything that is not a plain integer in 1-65535 is ignored rather than\n * throwing: a typo in `.env` should fall through to the next source in the\n * precedence chain, not break the CLI.\n */\nexport function readPortEnv(name: string): number | undefined {\n const raw = process.env[name]?.trim();\n if (!raw || !/^\\d+$/.test(raw)) return undefined;\n const port = Number(raw);\n if (port < 1 || port > 65535) return undefined;\n return port;\n}\n"],"mappings":";;;AAGA,IAAI;;;;;;;;;;;;;AAcJ,SAAgB,eAAe,MAAc,QAAQ,IAAI,GAAS;CAChE,IAAI,cAAc,KAAK;CACvB,YAAY;CAGZ,OAAO,OAAO,EAAE,MAAM,KAAK,KAAK,YAAY,EAAE,CAAC;CAC/C,OAAO,OAAO,EAAE,MAAM,KAAK,KAAK,MAAM,EAAE,CAAC;AAC3C;;AAGA,SAAgB,0BAAgC;CAC9C,YAAY,KAAA;AACd;;;;;;;;AASA,SAAgB,YAAY,MAAkC;CAC5D,MAAM,MAAM,QAAQ,IAAI,KAAK,EAAE,KAAK;CACpC,IAAI,CAAC,OAAO,CAAC,QAAQ,KAAK,GAAG,GAAG,OAAO,KAAA;CACvC,MAAM,OAAO,OAAO,GAAG;CACvB,IAAI,OAAO,KAAK,OAAO,OAAO,OAAO,KAAA;CACrC,OAAO;AACT"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region clis/project-ports.d.ts
|
|
2
|
-
type ProjectPorts = {
|
|
2
|
+
export type ProjectPorts = {
|
|
3
3
|
switchboardPort: number;
|
|
4
4
|
studioPort: number;
|
|
5
5
|
vetraConnectPort: number;
|
|
@@ -13,7 +13,7 @@ type ProjectPorts = {
|
|
|
13
13
|
* bands, and well-known services. Being generous here is nearly free — the
|
|
14
14
|
* whole list costs 83 of 1000 offsets.
|
|
15
15
|
*/
|
|
16
|
-
declare const PROJECT_PORT_DENYLIST: ReadonlySet<number>;
|
|
16
|
+
export declare const PROJECT_PORT_DENYLIST: ReadonlySet<number>;
|
|
17
17
|
/**
|
|
18
18
|
* Derive a project's dev-server port triple from its package name.
|
|
19
19
|
*
|
|
@@ -29,12 +29,12 @@ declare const PROJECT_PORT_DENYLIST: ReadonlySet<number>;
|
|
|
29
29
|
* until all three are clear — so the result stays reproducible from the name
|
|
30
30
|
* alone.
|
|
31
31
|
*/
|
|
32
|
-
declare function deriveProjectPorts(projectName: string): ProjectPorts;
|
|
32
|
+
export declare function deriveProjectPorts(projectName: string): ProjectPorts;
|
|
33
33
|
/** Every port this scheme can hand out, for cross-checking against other tools. */
|
|
34
|
-
declare function projectPortBands(): {
|
|
34
|
+
export declare function projectPortBands(): {
|
|
35
35
|
from: number;
|
|
36
36
|
to: number;
|
|
37
37
|
}[];
|
|
38
38
|
//#endregion
|
|
39
|
-
export {
|
|
39
|
+
export {};
|
|
40
40
|
//# sourceMappingURL=project-ports.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-ports.d.mts","names":[],"sources":["../../clis/project-ports.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"project-ports.d.mts","names":[],"sources":["../../clis/project-ports.ts"],"mappings":";YAQY;EACV;EACA;EACA;;;;;;;;;;;qBAsBW,uBAAuB;;;;;;;;;;;;;;;;wBAuDpB,mBAAmB,sBAAsB;;wBAsBzC;EAAsB;EAAc"}
|
|
@@ -18,7 +18,7 @@ function inclusive(from, to) {
|
|
|
18
18
|
* bands, and well-known services. Being generous here is nearly free — the
|
|
19
19
|
* whole list costs 83 of 1000 offsets.
|
|
20
20
|
*/
|
|
21
|
-
const PROJECT_PORT_DENYLIST = new Set([
|
|
21
|
+
const PROJECT_PORT_DENYLIST = /* @__PURE__ */ new Set([
|
|
22
22
|
2e3,
|
|
23
23
|
2049,
|
|
24
24
|
2181,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-ports.mjs","names":[],"sources":["../../clis/project-ports.ts"],"sourcesContent":["import fnv1a from \"@sindresorhus/fnv1a\";\nimport {\n PROJECT_PORT_BAND_SIZE,\n PROJECT_PORT_BAND_STUDIO,\n PROJECT_PORT_BAND_SWITCHBOARD,\n PROJECT_PORT_BAND_VETRA_CONNECT,\n} from \"./constants.js\";\n\nexport type ProjectPorts = {\n switchboardPort: number;\n studioPort: number;\n vetraConnectPort: number;\n};\n\nconst BANDS = [\n PROJECT_PORT_BAND_SWITCHBOARD,\n PROJECT_PORT_BAND_STUDIO,\n PROJECT_PORT_BAND_VETRA_CONNECT,\n] as const;\n\nfunction inclusive(from: number, to: number): number[] {\n return Array.from({ length: to - from + 1 }, (_, i) => from + i);\n}\n\n/**\n * Offsets whose derived port would land on something a developer machine\n * plausibly already holds. Skipped rather than assigned, so a project never\n * starts life needing a port override.\n *\n * Two sources: ports the Powerhouse repos themselves reference inside these\n * bands, and well-known services. Being generous here is nearly free — the\n * whole list costs 83 of 1000 offsets.\n */\nexport const PROJECT_PORT_DENYLIST: ReadonlySet<number> = new Set<number>([\n // 2000 band\n 2000, // Cisco SCCP\n 2049, // NFS\n 2181, // ZooKeeper\n 2222, // alternate SSH\n 2375,\n 2376,\n 2377, // Docker daemon and Swarm\n 2483,\n 2484, // Oracle DB\n // 6000 band\n ...inclusive(6000, 6063), // X11 TCP listeners\n 6006, // Storybook / TensorBoard\n 6060, // Go pprof\n 6100,\n 6200,\n 6300,\n 6500, // referenced elsewhere in the Powerhouse repos\n 6379, // Redis\n 6443, // Kubernetes API server\n 6666,\n 6667, // IRC\n // 7000 band\n 7000,\n 7001, // macOS AirPlay receiver / Cassandra\n 7077, // Spark\n 7474,\n 7687, // Neo4j HTTP and Bolt\n 7777, // referenced elsewhere in the Powerhouse repos\n]);\n\nfunction portsForOffset(offset: number): ProjectPorts {\n return {\n switchboardPort: PROJECT_PORT_BAND_SWITCHBOARD + offset,\n studioPort: PROJECT_PORT_BAND_STUDIO + offset,\n vetraConnectPort: PROJECT_PORT_BAND_VETRA_CONNECT + offset,\n };\n}\n\n/**\n * Derive a project's dev-server port triple from its package name.\n *\n * Name-derived rather than path-derived on purpose: the switchboard port is\n * written literally into the project's committed `.mcp.json`, because an MCP\n * client resolves that file at session start and cannot read the project\n * config or `.env`. A path-derived value would differ on every machine that\n * clones the repo.\n *\n * The three ports share one offset, so a project's numbers end in the same\n * three digits (`my-package` gets 7834 / 6834 / 2834). When any of the three\n * would hit {@link PROJECT_PORT_DENYLIST}, the offset advances deterministically\n * until all three are clear — so the result stays reproducible from the name\n * alone.\n */\nexport function deriveProjectPorts(projectName: string): ProjectPorts {\n if (!projectName.trim()) {\n throw new Error(\"A project name is required to derive project ports.\");\n }\n const start = Number(\n fnv1a(projectName, { size: 32 }) % BigInt(PROJECT_PORT_BAND_SIZE),\n );\n for (let step = 0; step < PROJECT_PORT_BAND_SIZE; step++) {\n const offset = (start + step) % PROJECT_PORT_BAND_SIZE;\n const ports = portsForOffset(offset);\n if (!Object.values(ports).some((port) => PROJECT_PORT_DENYLIST.has(port))) {\n return ports;\n }\n }\n // Unreachable: the denylist covers far fewer offsets than the band holds.\n throw new Error(\n `No free port offset for project \"${projectName}\" — every offset in the ` +\n `${PROJECT_PORT_BAND_SIZE}-wide band is denied.`,\n );\n}\n\n/** Every port this scheme can hand out, for cross-checking against other tools. */\nexport function projectPortBands(): { from: number; to: number }[] {\n return BANDS.map((base) => ({\n from: base,\n to: base + PROJECT_PORT_BAND_SIZE - 1,\n }));\n}\n"],"mappings":";;;AAcA,MAAM,QAAQ;CACZ;CACA;CACA;
|
|
1
|
+
{"version":3,"file":"project-ports.mjs","names":[],"sources":["../../clis/project-ports.ts"],"sourcesContent":["import fnv1a from \"@sindresorhus/fnv1a\";\nimport {\n PROJECT_PORT_BAND_SIZE,\n PROJECT_PORT_BAND_STUDIO,\n PROJECT_PORT_BAND_SWITCHBOARD,\n PROJECT_PORT_BAND_VETRA_CONNECT,\n} from \"./constants.js\";\n\nexport type ProjectPorts = {\n switchboardPort: number;\n studioPort: number;\n vetraConnectPort: number;\n};\n\nconst BANDS = [\n PROJECT_PORT_BAND_SWITCHBOARD,\n PROJECT_PORT_BAND_STUDIO,\n PROJECT_PORT_BAND_VETRA_CONNECT,\n] as const;\n\nfunction inclusive(from: number, to: number): number[] {\n return Array.from({ length: to - from + 1 }, (_, i) => from + i);\n}\n\n/**\n * Offsets whose derived port would land on something a developer machine\n * plausibly already holds. Skipped rather than assigned, so a project never\n * starts life needing a port override.\n *\n * Two sources: ports the Powerhouse repos themselves reference inside these\n * bands, and well-known services. Being generous here is nearly free — the\n * whole list costs 83 of 1000 offsets.\n */\nexport const PROJECT_PORT_DENYLIST: ReadonlySet<number> = new Set<number>([\n // 2000 band\n 2000, // Cisco SCCP\n 2049, // NFS\n 2181, // ZooKeeper\n 2222, // alternate SSH\n 2375,\n 2376,\n 2377, // Docker daemon and Swarm\n 2483,\n 2484, // Oracle DB\n // 6000 band\n ...inclusive(6000, 6063), // X11 TCP listeners\n 6006, // Storybook / TensorBoard\n 6060, // Go pprof\n 6100,\n 6200,\n 6300,\n 6500, // referenced elsewhere in the Powerhouse repos\n 6379, // Redis\n 6443, // Kubernetes API server\n 6666,\n 6667, // IRC\n // 7000 band\n 7000,\n 7001, // macOS AirPlay receiver / Cassandra\n 7077, // Spark\n 7474,\n 7687, // Neo4j HTTP and Bolt\n 7777, // referenced elsewhere in the Powerhouse repos\n]);\n\nfunction portsForOffset(offset: number): ProjectPorts {\n return {\n switchboardPort: PROJECT_PORT_BAND_SWITCHBOARD + offset,\n studioPort: PROJECT_PORT_BAND_STUDIO + offset,\n vetraConnectPort: PROJECT_PORT_BAND_VETRA_CONNECT + offset,\n };\n}\n\n/**\n * Derive a project's dev-server port triple from its package name.\n *\n * Name-derived rather than path-derived on purpose: the switchboard port is\n * written literally into the project's committed `.mcp.json`, because an MCP\n * client resolves that file at session start and cannot read the project\n * config or `.env`. A path-derived value would differ on every machine that\n * clones the repo.\n *\n * The three ports share one offset, so a project's numbers end in the same\n * three digits (`my-package` gets 7834 / 6834 / 2834). When any of the three\n * would hit {@link PROJECT_PORT_DENYLIST}, the offset advances deterministically\n * until all three are clear — so the result stays reproducible from the name\n * alone.\n */\nexport function deriveProjectPorts(projectName: string): ProjectPorts {\n if (!projectName.trim()) {\n throw new Error(\"A project name is required to derive project ports.\");\n }\n const start = Number(\n fnv1a(projectName, { size: 32 }) % BigInt(PROJECT_PORT_BAND_SIZE),\n );\n for (let step = 0; step < PROJECT_PORT_BAND_SIZE; step++) {\n const offset = (start + step) % PROJECT_PORT_BAND_SIZE;\n const ports = portsForOffset(offset);\n if (!Object.values(ports).some((port) => PROJECT_PORT_DENYLIST.has(port))) {\n return ports;\n }\n }\n // Unreachable: the denylist covers far fewer offsets than the band holds.\n throw new Error(\n `No free port offset for project \"${projectName}\" — every offset in the ` +\n `${PROJECT_PORT_BAND_SIZE}-wide band is denied.`,\n );\n}\n\n/** Every port this scheme can hand out, for cross-checking against other tools. */\nexport function projectPortBands(): { from: number; to: number }[] {\n return BANDS.map((base) => ({\n from: base,\n to: base + PROJECT_PORT_BAND_SIZE - 1,\n }));\n}\n"],"mappings":";;;AAcA,MAAM,QAAQ;CACZ;CACA;CACA;AACF;AAEA,SAAS,UAAU,MAAc,IAAsB;CACrD,OAAO,MAAM,KAAK,EAAE,QAAQ,KAAK,OAAO,EAAE,IAAI,GAAG,MAAM,OAAO,CAAC;AACjE;;;;;;;;;;AAWA,MAAa,wCAA6C,IAAI,IAAY;CAExE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,GAAG,UAAU,KAAM,IAAI;CACvB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,eAAe,QAA8B;CACpD,OAAO;EACL,iBAAiB,gCAAgC;EACjD,YAAY,2BAA2B;EACvC,kBAAkB,kCAAkC;CACtD;AACF;;;;;;;;;;;;;;;;AAiBA,SAAgB,mBAAmB,aAAmC;CACpE,IAAI,CAAC,YAAY,KAAK,GACpB,MAAM,IAAI,MAAM,qDAAqD;CAEvE,MAAM,QAAQ,OACZ,MAAM,aAAa,EAAE,MAAM,GAAG,CAAC,IAAI,OAAO,sBAAsB,CAClE;CACA,KAAK,IAAI,OAAO,GAAG,OAAO,wBAAwB,QAAQ;EAExD,MAAM,QAAQ,gBADE,QAAQ,QAAQ,sBACG;EACnC,IAAI,CAAC,OAAO,OAAO,KAAK,CAAC,CAAC,MAAM,SAAS,sBAAsB,IAAI,IAAI,CAAC,GACtE,OAAO;CAEX;CAEA,MAAM,IAAI,MACR,oCAAoC,YAAY,0BAC3C,uBAAuB,sBAC9B;AACF;;AAGA,SAAgB,mBAAmD;CACjE,OAAO,MAAM,KAAK,UAAU;EAC1B,MAAM;EACN,IAAI,OAAO,yBAAyB;CACtC,EAAE;AACJ"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
//#region clis/services/telemetry.d.ts
|
|
2
|
-
type CliInvocationInfo = {
|
|
2
|
+
export type CliInvocationInfo = {
|
|
3
3
|
command?: string;
|
|
4
4
|
subcommand?: string;
|
|
5
5
|
pm?: string;
|
|
6
6
|
argv: string[];
|
|
7
7
|
cwd?: string;
|
|
8
8
|
};
|
|
9
|
-
type TelemetryClient = {
|
|
9
|
+
export type TelemetryClient = {
|
|
10
10
|
/**
|
|
11
11
|
* Attaches per-invocation context (command, sanitized argv, package
|
|
12
12
|
* manager) as Sentry tags + a `invocation` context block, plus a single
|
|
@@ -25,23 +25,23 @@ type TelemetryClient = {
|
|
|
25
25
|
* Prompts the user once, caches the answer. Must be called before init.
|
|
26
26
|
* Returns `true` if telemetry should be enabled, `false` otherwise.
|
|
27
27
|
*/
|
|
28
|
-
declare function resolveTelemetryConsent(): Promise<boolean>;
|
|
28
|
+
export declare function resolveTelemetryConsent(): Promise<boolean>;
|
|
29
29
|
/**
|
|
30
30
|
* Initializes Sentry for CLI error reporting if telemetry is enabled.
|
|
31
31
|
* Safe to call multiple times; only the first call takes effect.
|
|
32
32
|
*/
|
|
33
|
-
declare function initCliTelemetry(opts: {
|
|
33
|
+
export declare function initCliTelemetry(opts: {
|
|
34
34
|
cliName: "ph-cli" | "ph-cmd";
|
|
35
35
|
release?: string;
|
|
36
36
|
}): Promise<TelemetryClient | undefined>;
|
|
37
37
|
/**
|
|
38
38
|
* Explicitly set telemetry consent (used by `ph telemetry on|off`).
|
|
39
39
|
*/
|
|
40
|
-
declare function setTelemetryConsent(enabled: boolean): void;
|
|
40
|
+
export declare function setTelemetryConsent(enabled: boolean): void;
|
|
41
41
|
/**
|
|
42
42
|
* Returns the current telemetry state — useful for `ph telemetry status`.
|
|
43
43
|
*/
|
|
44
|
-
declare function getTelemetryStatus(): {
|
|
44
|
+
export declare function getTelemetryStatus(): {
|
|
45
45
|
source: "env";
|
|
46
46
|
enabled: boolean;
|
|
47
47
|
} | {
|
|
@@ -53,5 +53,5 @@ declare function getTelemetryStatus(): {
|
|
|
53
53
|
enabled: false;
|
|
54
54
|
};
|
|
55
55
|
//#endregion
|
|
56
|
-
export {
|
|
56
|
+
export {};
|
|
57
57
|
//# sourceMappingURL=telemetry.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telemetry.d.mts","names":[],"sources":["../../../clis/services/telemetry.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"telemetry.d.mts","names":[],"sources":["../../../clis/services/telemetry.ts"],"mappings":";YAmCY;EACV;EACA;EACA;EACA;EACA;;YAGU;;;;;;EAMV,0BAA0B,MAAM;;;;;EAKhC,kBACE,cACA;IAAS;QACN;;;;;;wBA8Ce,2BAA2B;;;;;wBAgH3B,iBAAiB;EACrC;EACA;IACE,QAAQ;;;;wBAgGI,oBAAoB;;;;wBAOpB;EACV;EAAe;;EACf;EAAkB;EAAkB;;EACpC;EAAmB"}
|
|
@@ -1 +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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"}
|
|
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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"}
|