@puddle-code/cli 0.2.4 → 0.2.6
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/CHANGELOG.md +2 -13
- package/README.md +33 -23
- package/dist/host-control.mjs +2 -2
- package/dist/index.js +501 -312
- package/dist/install-cli.sh +167 -0
- package/dist/install.sh +7 -10
- package/dist/public/assets/{App-C2IQg_6K.js → App-CzuHShdO.js} +2 -2
- package/dist/public/assets/{Dashboard-DUFOyL1_.js → Dashboard-B5qWtmwb.js} +1 -1
- package/dist/public/assets/{EditorTabClose-C3wpMhYL.js → EditorTabClose-uxuNJdgx.js} +1 -1
- package/dist/public/assets/{FindWidget-Dt1gfA70.js → FindWidget-lUpiYZJG.js} +1 -1
- package/dist/public/assets/{LazyTerminal-B9HzmRBx.js → LazyTerminal-BYbPpGEI.js} +2 -2
- package/dist/public/assets/{ModelRefcount-CxzQ3w05.js → ModelRefcount-N0Xh_Y-_.js} +1 -1
- package/dist/public/assets/{NewProjectDialog-Cq_1po_h.js → NewProjectDialog-D3bNK3u_.js} +1 -1
- package/dist/public/assets/{PaneEditorBody-zS2BI5Fn.js → PaneEditorBody-DrSiMzrp.js} +4 -4
- package/dist/public/assets/{PdfViewer-ckjWE_TR.js → PdfViewer-BhqrWfmb.js} +2 -2
- package/dist/public/assets/{SettingsDialog-Cb2fdV1D.js → SettingsDialog-CamnF91I.js} +1 -1
- package/dist/public/assets/{Terminal-BqbjwcMi.js → Terminal-Ds17sCF_.js} +1 -1
- package/dist/public/assets/{Workspace-BVDgHkML.js → Workspace-BmtA0kYQ.js} +4 -4
- package/dist/public/assets/{abnfDiagram-VCTEODGH-JZdwYDGF.js → abnfDiagram-VCTEODGH-B4ZtDUXi.js} +1 -1
- package/dist/public/assets/architecture-7GRP2DOG-D0iH6Lxl.js +1 -0
- package/dist/public/assets/{architectureDiagram-5GKGNRK7-CYoRlLvs.js → architectureDiagram-5GKGNRK7-Cyzuyzwl.js} +1 -1
- package/dist/public/assets/{blockDiagram-I7D4REHJ-DjPQ4-5b.js → blockDiagram-I7D4REHJ-BN00JnNl.js} +1 -1
- package/dist/public/assets/{buffer-store-GUPJI8Wy.js → buffer-store--4yU4YqM.js} +2 -2
- package/dist/public/assets/{button-CKes3Qwq.js → button-DehrlYw7.js} +1 -1
- package/dist/public/assets/{c4Diagram-7LVT6UL2-BvwPGpk0.js → c4Diagram-7LVT6UL2-DpUKodBy.js} +1 -1
- package/dist/public/assets/channel-DT2izo70.js +1 -0
- package/dist/public/assets/{chunk-2E4U76K2-DcsuTdxj.js → chunk-2E4U76K2-BksaqXZU.js} +1 -1
- package/dist/public/assets/{chunk-4HAMMTFA-BGEaVxkg.js → chunk-4HAMMTFA-D01k6w0A.js} +1 -1
- package/dist/public/assets/{chunk-75Z2AOVW-BOvbyaVc.js → chunk-75Z2AOVW-DJJjslyX.js} +1 -1
- package/dist/public/assets/{chunk-CLGD4ZFX-8fG7spZS.js → chunk-CLGD4ZFX-CX1B9i6H.js} +1 -1
- package/dist/public/assets/{chunk-DU6HZSFF-C94gDWXH.js → chunk-DU6HZSFF-Bx2ELCn-.js} +1 -1
- package/dist/public/assets/{chunk-F27PBJKO-CZD-A2b3.js → chunk-F27PBJKO-CCDhODx8.js} +1 -1
- package/dist/public/assets/{chunk-GMAD6QVW-C7KK3kiY.js → chunk-GMAD6QVW-C1evXXZi.js} +1 -1
- package/dist/public/assets/{chunk-GVQU2GXP-CDFKc0sW.js → chunk-GVQU2GXP-C1iphgLK.js} +1 -1
- package/dist/public/assets/{chunk-IMKFNOWR-BWQiE1Cp.js → chunk-IMKFNOWR-5wMdkmsQ.js} +1 -1
- package/dist/public/assets/{chunk-L3NEJ4N5-DqP3WOXV.js → chunk-L3NEJ4N5-B-aXfMpF.js} +1 -1
- package/dist/public/assets/{chunk-OSK3NFVY-BlmYDJRN.js → chunk-OSK3NFVY-Bm2MaFOc.js} +1 -1
- package/dist/public/assets/{chunk-P2QGCYS3-v7eQ-UQT.js → chunk-P2QGCYS3-H02JaAun.js} +1 -1
- package/dist/public/assets/{chunk-POPQ4Y6H-D_gxItFL.js → chunk-POPQ4Y6H-Dmm46Beg.js} +1 -1
- package/dist/public/assets/{chunk-PWAF6VOD-Ds90M0Eb.js → chunk-PWAF6VOD-CRt8Ete5.js} +1 -1
- package/dist/public/assets/{chunk-SHT3W25Y-B8oepiFw.js → chunk-SHT3W25Y-w4aNK54y.js} +1 -1
- package/dist/public/assets/{chunk-SVP7TREG-DjFxplBp.js → chunk-SVP7TREG-Cl3QpMDs.js} +1 -1
- package/dist/public/assets/{chunk-TICWLB2K-GEeYOgw4.js → chunk-TICWLB2K-BpBbot9h.js} +1 -1
- package/dist/public/assets/{chunk-TLUHSLCS-CEnN9wOB.js → chunk-TLUHSLCS-BOnIB6P6.js} +2 -2
- package/dist/public/assets/classDiagram-ZZMXUADV-BSshJUxJ.js +1 -0
- package/dist/public/assets/classDiagram-v2-VYDZK3BY-BSshJUxJ.js +1 -0
- package/dist/public/assets/{context-menu-CmSSwq3B.js → context-menu-BT88Vgia.js} +1 -1
- package/dist/public/assets/{cssMode-BEaREsxj.js → cssMode-gXT_K4po.js} +1 -1
- package/dist/public/assets/{cynefin-OW5HDTMX-C_sl2wZ-.js → cynefin-OW5HDTMX-CJakxaH8.js} +1 -1
- package/dist/public/assets/{cynefinDiagram-5FMLGOSQ-CPpFUPl3.js → cynefinDiagram-5FMLGOSQ-BJ_fVnC9.js} +1 -1
- package/dist/public/assets/{dagre-GXQ25YYZ-BryapOat.js → dagre-GXQ25YYZ-CjflcbuS.js} +1 -1
- package/dist/public/assets/{diagram-S7CK7UJ4-dIdCar-k.js → diagram-S7CK7UJ4-g8pKIWrY.js} +1 -1
- package/dist/public/assets/{diagram-UQ7AKVKN-EcAWwfjf.js → diagram-UQ7AKVKN-Dm5Pg4rY.js} +1 -1
- package/dist/public/assets/{diagram-VSXAHHWV-Dpl4gvkd.js → diagram-VSXAHHWV-DV5CVAXg.js} +1 -1
- package/dist/public/assets/{diagram-VX7I27RA-CJtemv3e.js → diagram-VX7I27RA-Be9qCk-h.js} +1 -1
- package/dist/public/assets/{diagram-Z3DM3KII-CPSnQEgD.js → diagram-Z3DM3KII-C3-xDrxs.js} +1 -1
- package/dist/public/assets/{dialog-Dam8WdmS.js → dialog-CAMTxigV.js} +1 -1
- package/dist/public/assets/{disclosure-Ex-68BU4.js → disclosure-DvM7-z38.js} +1 -1
- package/dist/public/assets/{dist-CxMnSex-.js → dist-3tmIo7Ja.js} +1 -1
- package/dist/public/assets/{dist-ByY6lqkl.js → dist-sLs_LTiG.js} +1 -1
- package/dist/public/assets/{ebnfDiagram-PWID7BFC-uXsUC0ll.js → ebnfDiagram-PWID7BFC-Bq92n7k-.js} +1 -1
- package/dist/public/assets/{erDiagram-RLTQ6QDP-D5vJ6ZLT.js → erDiagram-RLTQ6QDP-C5I2bKnS.js} +1 -1
- package/dist/public/assets/eventmodeling-NTZA5JFV-CWxfOZuS.js +1 -0
- package/dist/public/assets/flowDiagram-HODETNUW-ddOOBXlS.js +1 -0
- package/dist/public/assets/{ganttDiagram-EL5Y4UJY-C6yTyvpx.js → ganttDiagram-EL5Y4UJY-qUaBgbbK.js} +1 -1
- package/dist/public/assets/{gitGraph-4MIJSDKK-C7nVbcZ1.js → gitGraph-4MIJSDKK-CUfoq3Sa.js} +1 -1
- package/dist/public/assets/{gitGraphDiagram-WWUBYQGX-DByBzF_5.js → gitGraphDiagram-WWUBYQGX-kj34Cchu.js} +1 -1
- package/dist/public/assets/{hotkeys-MSGaeY2m.js → hotkeys-DbIEec2f.js} +1 -1
- package/dist/public/assets/{htmlMode-DirKEo5j.js → htmlMode-C_uC6TDH.js} +1 -1
- package/dist/public/assets/{index-C1a3rCk2.js → index-BLx1AR0-.js} +2 -2
- package/dist/public/assets/index-uKreLbru.css +2 -0
- package/dist/public/assets/{info-A6RAGUB7-BCEGc_J2.js → info-A6RAGUB7-C938EzS-.js} +1 -1
- package/dist/public/assets/{infoDiagram-27XIBGKW-DDVeVGK9.js → infoDiagram-27XIBGKW-BoTxIjdn.js} +1 -1
- package/dist/public/assets/{input-i-kiT55N.js → input-DXYRmAFr.js} +1 -1
- package/dist/public/assets/{ishikawaDiagram-5VMMS53U-CTZvKcdI.js → ishikawaDiagram-5VMMS53U-j98L6THV.js} +1 -1
- package/dist/public/assets/{journeyDiagram-3NMN7TZE-CwFCP_8e.js → journeyDiagram-3NMN7TZE-DI-XCC2F.js} +1 -1
- package/dist/public/assets/{jsonMode-BLMO7T1W.js → jsonMode-BQ3590Ds.js} +1 -1
- package/dist/public/assets/{kanban-definition-UXKFOSKX-BCdCN6ov.js → kanban-definition-UXKFOSKX-BCTvFJTJ.js} +1 -1
- package/dist/public/assets/{line-e7ifLbsv.js → line-hE2Okp1W.js} +1 -1
- package/dist/public/assets/{media-refresh-store-d6Ihz_Yj.js → media-refresh-store-CxqhHl8x.js} +1 -1
- package/dist/public/assets/{mermaid-parser.core-DfmtQ5si.js → mermaid-parser.core-n0Wb6T7Q.js} +3 -3
- package/dist/public/assets/{mermaid.core-B0Ceo892.js → mermaid.core-Cb0bw4D8.js} +3 -3
- package/dist/public/assets/{mindmap-definition-YA3MSWOX-BKWZ0hGZ.js → mindmap-definition-YA3MSWOX-92LM1OuK.js} +1 -1
- package/dist/public/assets/{notification-permission-DThcB8rO.js → notification-permission-CG2r3Oi8.js} +1 -1
- package/dist/public/assets/{packet-AYTQ26CC-C_WSBR7W.js → packet-AYTQ26CC-_HBkwHYp.js} +1 -1
- package/dist/public/assets/{pdf-CW6jyF4i.js → pdf-DABk-fGs.js} +1 -1
- package/dist/public/assets/{pegDiagram-XKGWAZYB-CQw-vxyv.js → pegDiagram-XKGWAZYB-B-fG_WHS.js} +1 -1
- package/dist/public/assets/{pie-WAS4IAKB-CyAolSkk.js → pie-WAS4IAKB-ByxJto7v.js} +1 -1
- package/dist/public/assets/{pieDiagram-E7YTZNPT-fEikc7gQ.js → pieDiagram-E7YTZNPT-Ci8ZSq4L.js} +1 -1
- package/dist/public/assets/{project-abbrev-0K47Phcy.js → project-abbrev-BU2DBpdq.js} +1 -1
- package/dist/public/assets/{quadrantDiagram-AXDQQJYC-BLZF9CN6.js → quadrantDiagram-AXDQQJYC-B97Yoxpb.js} +1 -1
- package/dist/public/assets/{radar-RG4KPBEZ-DZj09Z3T.js → radar-RG4KPBEZ-BT2qdAqW.js} +1 -1
- package/dist/public/assets/{railroad-74A4TZTK-DrrM4RhH.js → railroad-74A4TZTK-Buny6l3B.js} +1 -1
- package/dist/public/assets/railroad-abnf-HS5TGJTU-C_aUf3vj.js +1 -0
- package/dist/public/assets/railroad-ebnf-LZEXJU2U-BGUVD_Qq.js +1 -0
- package/dist/public/assets/railroad-peg-WCYAUIDC-CGdTjBGv.js +1 -0
- package/dist/public/assets/{railroadDiagram-O6MQD6OU-BjnjeLPo.js → railroadDiagram-O6MQD6OU-BktpI6J7.js} +1 -1
- package/dist/public/assets/{requirementDiagram-BXWQKSXE-CJnHoVjp.js → requirementDiagram-BXWQKSXE-DYy_YKmd.js} +1 -1
- package/dist/public/assets/{sankeyDiagram-P5KCCOFB-BFBAycgU.js → sankeyDiagram-P5KCCOFB-BeKH_sFV.js} +1 -1
- package/dist/public/assets/{sequenceDiagram-WJ2MYXX4-IgIUmdp2.js → sequenceDiagram-WJ2MYXX4-DD8okFGM.js} +1 -1
- package/dist/public/assets/{stateDiagram-D77RDMKH-C8ragRoI.js → stateDiagram-D77RDMKH-Bfk3E3Oa.js} +1 -1
- package/dist/public/assets/stateDiagram-v2-MP3YSRHH-DCg8-XDy.js +1 -0
- package/dist/public/assets/{swimlanes-42K2YHIH-mm0BJE0h.js → swimlanes-42K2YHIH-OVNrzhfn.js} +1 -1
- package/dist/public/assets/swimlanesDiagram-VR7AAH4N-DAqvbP32.js +8 -0
- package/dist/public/assets/{switch-TfZl8YQu.js → switch-CN9NW-du.js} +1 -1
- package/dist/public/assets/{timeline-definition-24CTP7MA-CLtACXsf.js → timeline-definition-24CTP7MA-C78a8EdU.js} +1 -1
- package/dist/public/assets/{treeView-Q6P3EWNA-B_s8YnCD.js → treeView-Q6P3EWNA-Cl5tCoga.js} +1 -1
- package/dist/public/assets/{treemap-WGGIJYW6-CiyeynoG.js → treemap-WGGIJYW6-4YVcjSz9.js} +1 -1
- package/dist/public/assets/{tsMode-BJ9DHdAB.js → tsMode-CmGZ7MCT.js} +1 -1
- package/dist/public/assets/{use-ui-state-2bNBLDPU.js → use-ui-state-CzR7YpWb.js} +1 -1
- package/dist/public/assets/{vennDiagram-4TSXK5OY-C9rcT-1u.js → vennDiagram-4TSXK5OY-Lih9MwBs.js} +1 -1
- package/dist/public/assets/{wardley-WFR3VGLG-D_hqUvOM.js → wardley-WFR3VGLG-CcU7I6bt.js} +1 -1
- package/dist/public/assets/{wardleyDiagram-VM6X3IG4-Dfn7mGAc.js → wardleyDiagram-VM6X3IG4-DxHrszj8.js} +1 -1
- package/dist/public/assets/{ws-CDXqi1pN.js → ws-C-kvwpeD.js} +1 -1
- package/dist/public/assets/{xychartDiagram-S5SC5T6Z-vwVJPUe2.js → xychartDiagram-S5SC5T6Z-DpLvvdIG.js} +1 -1
- package/dist/public/index.html +2 -2
- package/package.json +1 -1
- package/dist/public/assets/architecture-7GRP2DOG-RMEcXYv9.js +0 -1
- package/dist/public/assets/channel-BheYsxDc.js +0 -1
- package/dist/public/assets/classDiagram-ZZMXUADV-CDDLBiV5.js +0 -1
- package/dist/public/assets/classDiagram-v2-VYDZK3BY-CDDLBiV5.js +0 -1
- package/dist/public/assets/eventmodeling-NTZA5JFV-WlMCgowE.js +0 -1
- package/dist/public/assets/flowDiagram-HODETNUW-DJ99l88N.js +0 -1
- package/dist/public/assets/index-Ctw2P0rY.css +0 -2
- package/dist/public/assets/railroad-abnf-HS5TGJTU-lXe6u3FO.js +0 -1
- package/dist/public/assets/railroad-ebnf-LZEXJU2U-C7R4jvUV.js +0 -1
- package/dist/public/assets/railroad-peg-WCYAUIDC-B24HGcGJ.js +0 -1
- package/dist/public/assets/stateDiagram-v2-MP3YSRHH-AU0bFC0Z.js +0 -1
- package/dist/public/assets/swimlanesDiagram-VR7AAH4N-cBVuNXB5.js +0 -8
package/dist/index.js
CHANGED
|
@@ -3756,7 +3756,7 @@ var init_wrapper = __esm({
|
|
|
3756
3756
|
|
|
3757
3757
|
// src/lib/ws-client.ts
|
|
3758
3758
|
function connectGateway(port, authority) {
|
|
3759
|
-
return new Promise((
|
|
3759
|
+
return new Promise((resolve6, reject) => {
|
|
3760
3760
|
const ws = new wrapper_default(`ws://127.0.0.1:${port}/ws`, {
|
|
3761
3761
|
headers: { host: `localhost:${port}` }
|
|
3762
3762
|
});
|
|
@@ -3781,7 +3781,7 @@ function connectGateway(port, authority) {
|
|
|
3781
3781
|
}
|
|
3782
3782
|
if (parsed.t === "authenticated") {
|
|
3783
3783
|
clearTimeout(timer);
|
|
3784
|
-
|
|
3784
|
+
resolve6({
|
|
3785
3785
|
send(message) {
|
|
3786
3786
|
if (resource.valid() && ws.readyState === wrapper_default.OPEN)
|
|
3787
3787
|
ws.send(JSON.stringify(message));
|
|
@@ -3855,7 +3855,7 @@ async function attachSession(opts) {
|
|
|
3855
3855
|
rows: streams.stdout.rows ?? 24
|
|
3856
3856
|
});
|
|
3857
3857
|
gateway.send({ t: "attach", session: session.id, term, ...dims2() });
|
|
3858
|
-
return new Promise((
|
|
3858
|
+
return new Promise((resolve6) => {
|
|
3859
3859
|
let settled = false;
|
|
3860
3860
|
const finish = (outcome) => {
|
|
3861
3861
|
if (settled) return;
|
|
@@ -3864,7 +3864,7 @@ async function attachSession(opts) {
|
|
|
3864
3864
|
stdin.removeListener("data", onStdin);
|
|
3865
3865
|
unResize?.();
|
|
3866
3866
|
gateway.close();
|
|
3867
|
-
|
|
3867
|
+
resolve6(outcome);
|
|
3868
3868
|
};
|
|
3869
3869
|
gateway.onMessage((message) => {
|
|
3870
3870
|
switch (message.t) {
|
|
@@ -20039,7 +20039,7 @@ var LocalTransport = class {
|
|
|
20039
20039
|
kind = "local";
|
|
20040
20040
|
label = "this machine";
|
|
20041
20041
|
exec(command, opts = {}) {
|
|
20042
|
-
return new Promise((
|
|
20042
|
+
return new Promise((resolve6) => {
|
|
20043
20043
|
const child = spawn("sh", ["-c", command], { stdio: ["pipe", "pipe", "pipe"] });
|
|
20044
20044
|
let stdout = "";
|
|
20045
20045
|
let stderr = "";
|
|
@@ -20056,11 +20056,11 @@ var LocalTransport = class {
|
|
|
20056
20056
|
child.stderr.on("data", (chunk) => stderr += chunk.toString());
|
|
20057
20057
|
child.on("error", (err) => {
|
|
20058
20058
|
if (timer) clearTimeout(timer);
|
|
20059
|
-
|
|
20059
|
+
resolve6({ code: -1, stdout, stderr: stderr + String(err) });
|
|
20060
20060
|
});
|
|
20061
20061
|
child.on("close", (code) => {
|
|
20062
20062
|
if (timer) clearTimeout(timer);
|
|
20063
|
-
|
|
20063
|
+
resolve6({ code: code ?? -1, stdout, stderr });
|
|
20064
20064
|
});
|
|
20065
20065
|
child.stdin.on("error", () => {
|
|
20066
20066
|
});
|
|
@@ -20154,7 +20154,7 @@ var SshTransport = class {
|
|
|
20154
20154
|
* live control socket behind.
|
|
20155
20155
|
*/
|
|
20156
20156
|
open() {
|
|
20157
|
-
return new Promise((
|
|
20157
|
+
return new Promise((resolve6, reject) => {
|
|
20158
20158
|
const child = spawn2(this.ssh, this.args(this.host, "true"), {
|
|
20159
20159
|
// A GUI has no useful inherited terminal; OpenSSH calls its askpass
|
|
20160
20160
|
// helper instead. The terminal CLI keeps byte-for-byte inheritance.
|
|
@@ -20166,7 +20166,7 @@ var SshTransport = class {
|
|
|
20166
20166
|
(err) => reject(new CliError("ssh_unreachable", `could not run ${this.ssh}: ${err.message}`))
|
|
20167
20167
|
);
|
|
20168
20168
|
child.on("close", (code) => {
|
|
20169
|
-
if (code === 0)
|
|
20169
|
+
if (code === 0) resolve6();
|
|
20170
20170
|
else {
|
|
20171
20171
|
reject(
|
|
20172
20172
|
new CliError(
|
|
@@ -20182,13 +20182,13 @@ var SshTransport = class {
|
|
|
20182
20182
|
/** Whether the master connection is still alive (-O check). */
|
|
20183
20183
|
isAlive() {
|
|
20184
20184
|
if (!this.hasControlMaster) return Promise.resolve(true);
|
|
20185
|
-
return new Promise((
|
|
20185
|
+
return new Promise((resolve6) => {
|
|
20186
20186
|
const child = spawn2(this.ssh, this.args("-O", "check", this.host), {
|
|
20187
20187
|
stdio: "ignore",
|
|
20188
20188
|
env: this.spawnEnv()
|
|
20189
20189
|
});
|
|
20190
|
-
child.on("error", () =>
|
|
20191
|
-
child.on("close", (code) =>
|
|
20190
|
+
child.on("error", () => resolve6(false));
|
|
20191
|
+
child.on("close", (code) => resolve6(code === 0));
|
|
20192
20192
|
});
|
|
20193
20193
|
}
|
|
20194
20194
|
/**
|
|
@@ -20201,18 +20201,18 @@ var SshTransport = class {
|
|
|
20201
20201
|
*/
|
|
20202
20202
|
cancelForward(localPort, remotePort) {
|
|
20203
20203
|
if (!this.hasControlMaster) return Promise.resolve();
|
|
20204
|
-
return new Promise((
|
|
20204
|
+
return new Promise((resolve6) => {
|
|
20205
20205
|
const spec = `${localPort}:127.0.0.1:${remotePort}`;
|
|
20206
20206
|
const child = spawn2(this.ssh, this.args("-O", "cancel", "-L", spec, this.host), {
|
|
20207
20207
|
stdio: "ignore",
|
|
20208
20208
|
env: this.spawnEnv()
|
|
20209
20209
|
});
|
|
20210
|
-
child.on("error", () =>
|
|
20211
|
-
child.on("close", () =>
|
|
20210
|
+
child.on("error", () => resolve6());
|
|
20211
|
+
child.on("close", () => resolve6());
|
|
20212
20212
|
});
|
|
20213
20213
|
}
|
|
20214
20214
|
exec(command, opts = {}) {
|
|
20215
|
-
return new Promise((
|
|
20215
|
+
return new Promise((resolve6) => {
|
|
20216
20216
|
const child = spawn2(this.ssh, this.args(this.host, "--", `sh -c ${shellQuote(command)}`), {
|
|
20217
20217
|
stdio: ["pipe", "pipe", "pipe"],
|
|
20218
20218
|
env: this.spawnEnv()
|
|
@@ -20232,11 +20232,11 @@ var SshTransport = class {
|
|
|
20232
20232
|
child.stderr.on("data", (chunk) => stderr += chunk.toString());
|
|
20233
20233
|
child.on("error", (err) => {
|
|
20234
20234
|
if (timer) clearTimeout(timer);
|
|
20235
|
-
|
|
20235
|
+
resolve6({ code: -1, stdout, stderr: stderr + String(err) });
|
|
20236
20236
|
});
|
|
20237
20237
|
child.on("close", (code) => {
|
|
20238
20238
|
if (timer) clearTimeout(timer);
|
|
20239
|
-
|
|
20239
|
+
resolve6({ code: code ?? -1, stdout, stderr });
|
|
20240
20240
|
});
|
|
20241
20241
|
child.stdin.on("error", () => {
|
|
20242
20242
|
});
|
|
@@ -20256,11 +20256,11 @@ var SshTransport = class {
|
|
|
20256
20256
|
});
|
|
20257
20257
|
let stdout = "";
|
|
20258
20258
|
let stderr = "";
|
|
20259
|
-
const result = new Promise((
|
|
20259
|
+
const result = new Promise((resolve6) => {
|
|
20260
20260
|
child.stdout.on("data", (chunk) => stdout += chunk.toString());
|
|
20261
20261
|
child.stderr.on("data", (chunk) => stderr += chunk.toString());
|
|
20262
|
-
child.on("error", (err) =>
|
|
20263
|
-
child.on("close", (code) =>
|
|
20262
|
+
child.on("error", (err) => resolve6({ code: -1, stdout, stderr: stderr + String(err) }));
|
|
20263
|
+
child.on("close", (code) => resolve6({ code: code ?? -1, stdout, stderr }));
|
|
20264
20264
|
});
|
|
20265
20265
|
return {
|
|
20266
20266
|
result,
|
|
@@ -20275,14 +20275,14 @@ var SshTransport = class {
|
|
|
20275
20275
|
}
|
|
20276
20276
|
async copyTo(localPath, destPath) {
|
|
20277
20277
|
await this.exec(`mkdir -p $(dirname ${destPath})`, { timeoutMs: 15e3 });
|
|
20278
|
-
await new Promise((
|
|
20278
|
+
await new Promise((resolve6, reject) => {
|
|
20279
20279
|
const child = spawn2(this.scp, [...this.controlArgs, localPath, `${this.host}:${destPath}`], {
|
|
20280
20280
|
stdio: ["ignore", "ignore", "inherit"],
|
|
20281
20281
|
env: this.spawnEnv()
|
|
20282
20282
|
});
|
|
20283
20283
|
child.on("error", reject);
|
|
20284
20284
|
child.on("close", (code) => {
|
|
20285
|
-
if (code === 0)
|
|
20285
|
+
if (code === 0) resolve6();
|
|
20286
20286
|
else reject(new CliError("ssh_unreachable", `scp to ${this.host} failed (exit ${code})`));
|
|
20287
20287
|
});
|
|
20288
20288
|
});
|
|
@@ -20383,7 +20383,7 @@ async function registrationCode() {
|
|
|
20383
20383
|
const readline = createInterface({ input: process.stdin, output: silent, terminal: true });
|
|
20384
20384
|
try {
|
|
20385
20385
|
return remoteSecretSchema.parse(
|
|
20386
|
-
await new Promise((
|
|
20386
|
+
await new Promise((resolve6) => readline.question("", resolve6))
|
|
20387
20387
|
);
|
|
20388
20388
|
} finally {
|
|
20389
20389
|
readline.close();
|
|
@@ -20507,6 +20507,8 @@ running in the background once ready, so the terminal may close (--foreground
|
|
|
20507
20507
|
to stay attached; Ctrl-C then stops the cockpit). refresh stops a target's
|
|
20508
20508
|
cockpit (even a wedged one) and runs the full launch flow again \u2014 tunnel,
|
|
20509
20509
|
daemon restart if needed \u2014 keeping the old UI port so open tabs survive.
|
|
20510
|
+
An older host protocol prompts before updating its daemon; --no-upgrade
|
|
20511
|
+
aborts before prompting. Without a terminal, upgrade the daemon explicitly first.
|
|
20510
20512
|
list shows running cockpits; kill stops one. Sessions keep running under a
|
|
20511
20513
|
persistent supervisor; an SSH-attached daemon keeps their state for the next launch.
|
|
20512
20514
|
|
|
@@ -20516,7 +20518,7 @@ AppImage placed where you say (default ~/puddle), then opened in the file
|
|
|
20516
20518
|
manager. Already installed and no @version \u2192 nothing changes.
|
|
20517
20519
|
upgrade moves components to the newest release (or the named @version),
|
|
20518
20520
|
installing any that are missing; with no component it covers everything
|
|
20519
|
-
installed on the target \u2014 the CLI last,
|
|
20521
|
+
installed on the target \u2014 the CLI last, using its installation channel. cli and desktop are
|
|
20520
20522
|
client-machine artefacts: user@host targets the daemon only. remove
|
|
20521
20523
|
uninstalls a component after confirmation; 'daemon' stops it (interrupting
|
|
20522
20524
|
its sessions), unregisters the supervisor, and keeps ~/.puddle's data \u2014
|
|
@@ -20703,7 +20705,7 @@ function parseArgs(argv) {
|
|
|
20703
20705
|
throw new CliError(
|
|
20704
20706
|
"bad_arguments",
|
|
20705
20707
|
"install takes one of: daemon | desktop (with an optional @version)",
|
|
20706
|
-
spec?.what === "cli" ? "the CLI
|
|
20708
|
+
spec?.what === "cli" ? "install the CLI with the public install.sh or npm; update it with puddle upgrade cli" : "e.g. puddle install daemon@v0.0.32 user@host"
|
|
20707
20709
|
);
|
|
20708
20710
|
}
|
|
20709
20711
|
return {
|
|
@@ -20780,6 +20782,182 @@ function argvFor(command) {
|
|
|
20780
20782
|
return argv;
|
|
20781
20783
|
}
|
|
20782
20784
|
|
|
20785
|
+
// src/cli/daemon-upgrade-prompt.ts
|
|
20786
|
+
import { randomUUID } from "node:crypto";
|
|
20787
|
+
|
|
20788
|
+
// src/lib/handshake.ts
|
|
20789
|
+
init_types();
|
|
20790
|
+
var CLI_UPGRADE_COMMAND = "puddle upgrade cli";
|
|
20791
|
+
function decideHandshake(cli, daemon) {
|
|
20792
|
+
if (daemon.major === cli.major) return { kind: "proceed" };
|
|
20793
|
+
if (daemon.major < cli.major) return { kind: "upgrade-daemon" };
|
|
20794
|
+
return { kind: "refuse-cli-outdated", command: CLI_UPGRADE_COMMAND };
|
|
20795
|
+
}
|
|
20796
|
+
function daemonUpgradeMessage(request) {
|
|
20797
|
+
const { host, daemon, clientProtocol, liveSessions } = request;
|
|
20798
|
+
return `${host} runs puddled ${daemon.version} (protocol ${daemon.protocol.major}.${daemon.protocol.minor}). This client uses protocol ${clientProtocol.major}.${clientProtocol.minor} and needs the same protocol major.
|
|
20799
|
+
|
|
20800
|
+
Updating the host daemon will interrupt ${liveSessions} live session(s). Their saved state remains and they can resume after the update.`;
|
|
20801
|
+
}
|
|
20802
|
+
function verifyDaemonVersion(daemon) {
|
|
20803
|
+
const decision = decideHandshake(PROTOCOL_VERSION, daemon.protocol);
|
|
20804
|
+
if (decision.kind === "proceed") return daemon;
|
|
20805
|
+
if (decision.kind === "refuse-cli-outdated") {
|
|
20806
|
+
throw new CliError(
|
|
20807
|
+
"cli_outdated",
|
|
20808
|
+
`the daemon speaks protocol ${daemon.protocol.major}; this client speaks ${PROTOCOL_VERSION.major}`,
|
|
20809
|
+
`update the desktop app, or update the CLI: ${decision.command}`
|
|
20810
|
+
);
|
|
20811
|
+
}
|
|
20812
|
+
throw new CliError(
|
|
20813
|
+
"protocol_mismatch",
|
|
20814
|
+
`the daemon speaks protocol ${daemon.protocol.major}; this client speaks ${PROTOCOL_VERSION.major}`,
|
|
20815
|
+
"reconnect to update the host daemon"
|
|
20816
|
+
);
|
|
20817
|
+
}
|
|
20818
|
+
async function runHandshake(opts) {
|
|
20819
|
+
const { version: daemon, liveSessions } = opts.inspection;
|
|
20820
|
+
if (decideHandshake(PROTOCOL_VERSION, daemon.protocol).kind !== "upgrade-daemon") {
|
|
20821
|
+
return verifyDaemonVersion(daemon);
|
|
20822
|
+
}
|
|
20823
|
+
if (opts.noUpgrade) {
|
|
20824
|
+
throw new CliError(
|
|
20825
|
+
"upgrade_failed",
|
|
20826
|
+
`the daemon speaks protocol ${daemon.protocol.major} (client: ${PROTOCOL_VERSION.major}) and --no-upgrade is set`,
|
|
20827
|
+
`re-run without --no-upgrade to update it (${liveSessions} live session(s) would be interrupted and can be resumed)`
|
|
20828
|
+
);
|
|
20829
|
+
}
|
|
20830
|
+
const request = {
|
|
20831
|
+
host: opts.host,
|
|
20832
|
+
daemon,
|
|
20833
|
+
clientProtocol: PROTOCOL_VERSION,
|
|
20834
|
+
liveSessions
|
|
20835
|
+
};
|
|
20836
|
+
if (!await opts.confirmDaemonUpgrade?.(request)) {
|
|
20837
|
+
throw new CliError(
|
|
20838
|
+
"upgrade_failed",
|
|
20839
|
+
`the daemon update on ${opts.host} was not approved`,
|
|
20840
|
+
"launch again to approve the update, or run puddle upgrade daemon with the host as an optional argument"
|
|
20841
|
+
);
|
|
20842
|
+
}
|
|
20843
|
+
const latest = await opts.reinspect?.();
|
|
20844
|
+
if (latest && decideHandshake(PROTOCOL_VERSION, latest.version.protocol).kind !== "upgrade-daemon") {
|
|
20845
|
+
return verifyDaemonVersion(latest.version);
|
|
20846
|
+
}
|
|
20847
|
+
const logger = opts.logger ?? silentLogger;
|
|
20848
|
+
logger.info(
|
|
20849
|
+
`updating puddled on ${opts.host} (protocol ${daemon.protocol.major} \u2192 ${PROTOCOL_VERSION.major}) \u2014 ${liveSessions} live session(s) will be interrupted and can be resumed`
|
|
20850
|
+
);
|
|
20851
|
+
try {
|
|
20852
|
+
return verifyDaemonVersion(await opts.upgradeDaemon());
|
|
20853
|
+
} catch (err) {
|
|
20854
|
+
if (err instanceof CliError && ["cli_outdated", "protocol_mismatch"].includes(err.code)) {
|
|
20855
|
+
throw new CliError(
|
|
20856
|
+
"upgrade_failed",
|
|
20857
|
+
`the daemon protocol is still incompatible after updating: ${err.message}`,
|
|
20858
|
+
"inspect the daemon logs: puddle logs"
|
|
20859
|
+
);
|
|
20860
|
+
}
|
|
20861
|
+
throw err;
|
|
20862
|
+
}
|
|
20863
|
+
}
|
|
20864
|
+
|
|
20865
|
+
// src/cli/prompt.ts
|
|
20866
|
+
init_types();
|
|
20867
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
20868
|
+
async function ask(question, def) {
|
|
20869
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return def;
|
|
20870
|
+
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
20871
|
+
try {
|
|
20872
|
+
const answer = await new Promise(
|
|
20873
|
+
(resolve6) => rl.question(`${question} [${def}] `, resolve6)
|
|
20874
|
+
);
|
|
20875
|
+
return answer.trim() === "" ? def : answer.trim();
|
|
20876
|
+
} finally {
|
|
20877
|
+
rl.close();
|
|
20878
|
+
}
|
|
20879
|
+
}
|
|
20880
|
+
async function confirm(question, opts = {}) {
|
|
20881
|
+
if (opts.skip === true) return true;
|
|
20882
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
20883
|
+
throw new CliError(
|
|
20884
|
+
"bad_arguments",
|
|
20885
|
+
"refusing to confirm without a terminal",
|
|
20886
|
+
"pass --yes to proceed non-interactively"
|
|
20887
|
+
);
|
|
20888
|
+
}
|
|
20889
|
+
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
20890
|
+
try {
|
|
20891
|
+
const answer = await new Promise(
|
|
20892
|
+
(resolve6) => rl.question(`${question} [y/N] `, resolve6)
|
|
20893
|
+
);
|
|
20894
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
20895
|
+
} finally {
|
|
20896
|
+
rl.close();
|
|
20897
|
+
}
|
|
20898
|
+
}
|
|
20899
|
+
|
|
20900
|
+
// src/cli/daemon-upgrade-prompt.ts
|
|
20901
|
+
var REQUEST = "puddle:confirm-daemon-upgrade";
|
|
20902
|
+
var RESPONSE = "puddle:daemon-upgrade-answer";
|
|
20903
|
+
async function askToUpgrade(message) {
|
|
20904
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return false;
|
|
20905
|
+
return confirm(`${message}
|
|
20906
|
+
|
|
20907
|
+
Update and connect?`);
|
|
20908
|
+
}
|
|
20909
|
+
var confirmDaemonUpgrade = async (request) => {
|
|
20910
|
+
const message = daemonUpgradeMessage(request);
|
|
20911
|
+
if (!process.send || !process.connected) return askToUpgrade(message);
|
|
20912
|
+
return requestDaemonUpgradeApproval(message, process);
|
|
20913
|
+
};
|
|
20914
|
+
function requestDaemonUpgradeApproval(message, channel) {
|
|
20915
|
+
if (!channel.send || !channel.connected) return Promise.resolve(false);
|
|
20916
|
+
const id = randomUUID();
|
|
20917
|
+
return new Promise((resolve6) => {
|
|
20918
|
+
const finish = (approved) => {
|
|
20919
|
+
channel.off("message", answered);
|
|
20920
|
+
channel.off("disconnect", disconnected);
|
|
20921
|
+
resolve6(approved);
|
|
20922
|
+
};
|
|
20923
|
+
const answered = (value) => {
|
|
20924
|
+
if (!value || typeof value !== "object") return;
|
|
20925
|
+
const response = value;
|
|
20926
|
+
if (response.t === RESPONSE && response.id === id && typeof response.approved === "boolean")
|
|
20927
|
+
finish(response.approved);
|
|
20928
|
+
};
|
|
20929
|
+
const disconnected = () => finish(false);
|
|
20930
|
+
channel.on("message", answered);
|
|
20931
|
+
channel.once("disconnect", disconnected);
|
|
20932
|
+
channel.send({ t: REQUEST, id, message }, (err) => {
|
|
20933
|
+
if (err) finish(false);
|
|
20934
|
+
});
|
|
20935
|
+
});
|
|
20936
|
+
}
|
|
20937
|
+
function relayDaemonUpgradePrompt(child, ask2 = askToUpgrade) {
|
|
20938
|
+
let active = true;
|
|
20939
|
+
let pending = false;
|
|
20940
|
+
const receive = (value) => {
|
|
20941
|
+
if (pending || !value || typeof value !== "object") return;
|
|
20942
|
+
const request = value;
|
|
20943
|
+
if (request.t !== REQUEST || typeof request.id !== "string" || typeof request.message !== "string")
|
|
20944
|
+
return;
|
|
20945
|
+
pending = true;
|
|
20946
|
+
void ask2(request.message).catch(() => false).then((approved) => {
|
|
20947
|
+
pending = false;
|
|
20948
|
+
if (active && child.connected)
|
|
20949
|
+
child.send({ t: RESPONSE, id: request.id, approved }, () => {
|
|
20950
|
+
});
|
|
20951
|
+
});
|
|
20952
|
+
};
|
|
20953
|
+
child.on("message", receive);
|
|
20954
|
+
return () => {
|
|
20955
|
+
active = false;
|
|
20956
|
+
child.off("message", receive);
|
|
20957
|
+
if (child.connected) child.disconnect();
|
|
20958
|
+
};
|
|
20959
|
+
}
|
|
20960
|
+
|
|
20783
20961
|
// ../shared/src/node/security.ts
|
|
20784
20962
|
import { createHash, randomBytes } from "node:crypto";
|
|
20785
20963
|
import {
|
|
@@ -20867,14 +21045,14 @@ async function listenPrivate(path, onConnection) {
|
|
|
20867
21045
|
privatePath(path);
|
|
20868
21046
|
const before = lstatSync(path);
|
|
20869
21047
|
if (!before.isSocket()) throw new Error("Puddle control path is not a socket");
|
|
20870
|
-
const stale = await new Promise((
|
|
21048
|
+
const stale = await new Promise((resolve6, reject) => {
|
|
20871
21049
|
const probe = connect(path);
|
|
20872
21050
|
probe.once("connect", () => {
|
|
20873
21051
|
probe.destroy();
|
|
20874
|
-
|
|
21052
|
+
resolve6(false);
|
|
20875
21053
|
});
|
|
20876
21054
|
probe.once("error", (err) => {
|
|
20877
|
-
if (err.code === "ECONNREFUSED" || err.code === "ENOENT")
|
|
21055
|
+
if (err.code === "ECONNREFUSED" || err.code === "ENOENT") resolve6(true);
|
|
20878
21056
|
else reject(err);
|
|
20879
21057
|
});
|
|
20880
21058
|
});
|
|
@@ -20887,11 +21065,11 @@ async function listenPrivate(path, onConnection) {
|
|
|
20887
21065
|
}
|
|
20888
21066
|
}
|
|
20889
21067
|
const server = createServer(onConnection);
|
|
20890
|
-
await new Promise((
|
|
21068
|
+
await new Promise((resolve6, reject) => {
|
|
20891
21069
|
server.once("error", reject);
|
|
20892
21070
|
server.listen(path, () => {
|
|
20893
21071
|
server.off("error", reject);
|
|
20894
|
-
|
|
21072
|
+
resolve6();
|
|
20895
21073
|
});
|
|
20896
21074
|
});
|
|
20897
21075
|
if (process.platform !== "win32") chmodSync(path, 384);
|
|
@@ -21005,7 +21183,7 @@ var HostControlClient = class {
|
|
|
21005
21183
|
}
|
|
21006
21184
|
this.channel = channel;
|
|
21007
21185
|
this.until = performance.now() + CONNECTION_POLICY.leaseMs;
|
|
21008
|
-
await new Promise((
|
|
21186
|
+
await new Promise((resolve6, reject) => {
|
|
21009
21187
|
let pendingAt = null;
|
|
21010
21188
|
let initial = true;
|
|
21011
21189
|
const send = (message, sentAt = performance.now()) => {
|
|
@@ -21036,8 +21214,8 @@ var HostControlClient = class {
|
|
|
21036
21214
|
this.status = "protocol_mismatch";
|
|
21037
21215
|
reject(
|
|
21038
21216
|
this.error(
|
|
21039
|
-
"
|
|
21040
|
-
`Host protocol ${message.version.protocol.major}
|
|
21217
|
+
"protocol_mismatch",
|
|
21218
|
+
`Host protocol ${message.version.protocol.major} does not match client protocol ${PROTOCOL_VERSION.major}`
|
|
21041
21219
|
)
|
|
21042
21220
|
);
|
|
21043
21221
|
channel.destroy();
|
|
@@ -21058,7 +21236,7 @@ var HostControlClient = class {
|
|
|
21058
21236
|
if (this.channel !== channel) return;
|
|
21059
21237
|
this.status = "ready";
|
|
21060
21238
|
this.emit();
|
|
21061
|
-
|
|
21239
|
+
resolve6();
|
|
21062
21240
|
}).catch(() => channel.destroy());
|
|
21063
21241
|
}
|
|
21064
21242
|
},
|
|
@@ -21155,7 +21333,7 @@ async function inspectLegacyHost(home) {
|
|
|
21155
21333
|
};
|
|
21156
21334
|
try {
|
|
21157
21335
|
const version2 = versionResponseSchema.parse(await get("/api/version"));
|
|
21158
|
-
const sessions = sessionSchema.array().parse(await get("/api/sessions"));
|
|
21336
|
+
const sessions = sessionSchema.pick({ status: true }).array().parse(await get("/api/sessions"));
|
|
21159
21337
|
return hostInspectionSchema.parse({
|
|
21160
21338
|
t: "inspection",
|
|
21161
21339
|
port,
|
|
@@ -21179,7 +21357,7 @@ async function inspectHostLocally(home) {
|
|
|
21179
21357
|
}
|
|
21180
21358
|
const socket = connect2(path);
|
|
21181
21359
|
try {
|
|
21182
|
-
const grant = await new Promise((
|
|
21360
|
+
const grant = await new Promise((resolve6, reject) => {
|
|
21183
21361
|
socket.setTimeout(5e3, () => socket.destroy(new Error("Host inspection timed out")));
|
|
21184
21362
|
socket.once("error", reject);
|
|
21185
21363
|
socket.once("close", () => reject(new Error("Host control closed")));
|
|
@@ -21189,7 +21367,7 @@ async function inspectHostLocally(home) {
|
|
|
21189
21367
|
const message = controlResponseSchema.parse(value);
|
|
21190
21368
|
if (message.t !== "authority" || !message.token)
|
|
21191
21369
|
throw new Error("Host authority unavailable");
|
|
21192
|
-
|
|
21370
|
+
resolve6(message);
|
|
21193
21371
|
},
|
|
21194
21372
|
() => socket.destroy(new Error("Invalid host control response"))
|
|
21195
21373
|
);
|
|
@@ -21200,7 +21378,7 @@ async function inspectHostLocally(home) {
|
|
|
21200
21378
|
signal: AbortSignal.timeout(3e3)
|
|
21201
21379
|
});
|
|
21202
21380
|
if (!response.ok) throw new Error("Host inspection rejected");
|
|
21203
|
-
const sessions = sessionSchema.array().parse(await response.json());
|
|
21381
|
+
const sessions = sessionSchema.pick({ status: true }).array().parse(await response.json());
|
|
21204
21382
|
return hostInspectionSchema.parse({
|
|
21205
21383
|
t: "inspection",
|
|
21206
21384
|
port: grant.port,
|
|
@@ -21302,9 +21480,9 @@ async function acquireAuthority(transport) {
|
|
|
21302
21480
|
|
|
21303
21481
|
// src/cli/run.ts
|
|
21304
21482
|
init_attach();
|
|
21305
|
-
import { existsSync as
|
|
21306
|
-
import { dirname as
|
|
21307
|
-
import { fileURLToPath as
|
|
21483
|
+
import { existsSync as existsSync7 } from "node:fs";
|
|
21484
|
+
import { dirname as dirname12, join as join17 } from "node:path";
|
|
21485
|
+
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
21308
21486
|
|
|
21309
21487
|
// src/lib/browser.ts
|
|
21310
21488
|
import { spawn as spawn3 } from "node:child_process";
|
|
@@ -21347,7 +21525,7 @@ import { readFileSync as readFileSync4 } from "node:fs";
|
|
|
21347
21525
|
import { dirname as dirname4, join as join5 } from "node:path";
|
|
21348
21526
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
21349
21527
|
function cliVersion() {
|
|
21350
|
-
if (true) return "0.2.
|
|
21528
|
+
if (true) return "0.2.6";
|
|
21351
21529
|
const here = dirname4(fileURLToPath2(import.meta.url));
|
|
21352
21530
|
for (const candidate of [
|
|
21353
21531
|
join5(here, "..", "..", "package.json"),
|
|
@@ -21590,7 +21768,7 @@ function swapScript(staged2, opts) {
|
|
|
21590
21768
|
}
|
|
21591
21769
|
async function findInstalledDesktopApp(opts = {}) {
|
|
21592
21770
|
if ((opts.platform ?? process.platform) !== "darwin") return null;
|
|
21593
|
-
const { readFile:
|
|
21771
|
+
const { readFile: readFile3 } = await import("node:fs/promises");
|
|
21594
21772
|
const { homedir: homedir3 } = await import("node:os");
|
|
21595
21773
|
const systemApplications = opts.systemApplicationsDir ?? "/Applications";
|
|
21596
21774
|
const home = opts.homeDir ?? homedir3();
|
|
@@ -21599,7 +21777,7 @@ async function findInstalledDesktopApp(opts = {}) {
|
|
|
21599
21777
|
join6(home, "Applications", "Puddle.app")
|
|
21600
21778
|
]) {
|
|
21601
21779
|
try {
|
|
21602
|
-
const plist = await
|
|
21780
|
+
const plist = await readFile3(join6(appPath, "Contents/Info.plist"), "utf8");
|
|
21603
21781
|
const match = /<key>CFBundleShortVersionString<\/key>\s*<string>([^<]+)<\/string>/.exec(
|
|
21604
21782
|
plist
|
|
21605
21783
|
);
|
|
@@ -21627,12 +21805,12 @@ async function desktopAppInstallPath(opts = {}) {
|
|
|
21627
21805
|
return join6(userApplications, "Puddle.app");
|
|
21628
21806
|
}
|
|
21629
21807
|
function isDesktopAppRunning(appPath) {
|
|
21630
|
-
return new Promise((
|
|
21808
|
+
return new Promise((resolve6) => {
|
|
21631
21809
|
const child = spawn4("/usr/bin/pgrep", ["-f", `${appPath}/Contents/MacOS/`], {
|
|
21632
21810
|
stdio: "ignore"
|
|
21633
21811
|
});
|
|
21634
|
-
child.on("error", () =>
|
|
21635
|
-
child.on("exit", (code) =>
|
|
21812
|
+
child.on("error", () => resolve6(false));
|
|
21813
|
+
child.on("exit", (code) => resolve6(code === 0));
|
|
21636
21814
|
});
|
|
21637
21815
|
}
|
|
21638
21816
|
function fileSha256(path) {
|
|
@@ -21640,12 +21818,12 @@ function fileSha256(path) {
|
|
|
21640
21818
|
return pipeline(createReadStream(path), hash2).then(() => hash2.digest("hex"));
|
|
21641
21819
|
}
|
|
21642
21820
|
function run(command, args) {
|
|
21643
|
-
return new Promise((
|
|
21821
|
+
return new Promise((resolve6, reject) => {
|
|
21644
21822
|
const child = spawn4(command, args, { stdio: "ignore" });
|
|
21645
21823
|
child.on("error", reject);
|
|
21646
21824
|
child.on(
|
|
21647
21825
|
"exit",
|
|
21648
|
-
(code) => code === 0 ?
|
|
21826
|
+
(code) => code === 0 ? resolve6() : reject(new CliError("not_installed", `${command} exited with ${code ?? "signal"}`))
|
|
21649
21827
|
);
|
|
21650
21828
|
});
|
|
21651
21829
|
}
|
|
@@ -21724,7 +21902,9 @@ var RELEASE_PROTOCOLS = {
|
|
|
21724
21902
|
"0.2.1": { major: 19, minor: 0 },
|
|
21725
21903
|
"0.2.2": { major: 20, minor: 0 },
|
|
21726
21904
|
"0.2.3": { major: 20, minor: 0 },
|
|
21727
|
-
"0.2.4": { major: 21, minor: 2 }
|
|
21905
|
+
"0.2.4": { major: 21, minor: 2 },
|
|
21906
|
+
"0.2.5": { major: 21, minor: 2 },
|
|
21907
|
+
"0.2.6": { major: 21, minor: 2 }
|
|
21728
21908
|
};
|
|
21729
21909
|
function validProtocol(value) {
|
|
21730
21910
|
if (typeof value !== "object" || value === null) return false;
|
|
@@ -21837,7 +22017,7 @@ function formatComponentVersions(components) {
|
|
|
21837
22017
|
import { join as join12 } from "node:path";
|
|
21838
22018
|
|
|
21839
22019
|
// src/lib/attached-daemon.ts
|
|
21840
|
-
import { randomUUID } from "node:crypto";
|
|
22020
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
21841
22021
|
|
|
21842
22022
|
// src/lib/bootstrap.ts
|
|
21843
22023
|
import { existsSync as existsSync3, readFileSync as readFileSync6 } from "node:fs";
|
|
@@ -21927,22 +22107,22 @@ function readInstallScript() {
|
|
|
21927
22107
|
// src/lib/net.ts
|
|
21928
22108
|
import { connect as connect4, createServer as createServer2 } from "node:net";
|
|
21929
22109
|
function findFreePort() {
|
|
21930
|
-
return new Promise((
|
|
22110
|
+
return new Promise((resolve6, reject) => {
|
|
21931
22111
|
const srv = createServer2();
|
|
21932
22112
|
srv.once("error", reject);
|
|
21933
22113
|
srv.listen(0, "127.0.0.1", () => {
|
|
21934
22114
|
const address = srv.address();
|
|
21935
22115
|
const port = typeof address === "object" && address !== null ? address.port : 0;
|
|
21936
|
-
srv.close(() =>
|
|
22116
|
+
srv.close(() => resolve6(port));
|
|
21937
22117
|
});
|
|
21938
22118
|
});
|
|
21939
22119
|
}
|
|
21940
22120
|
function tcpListening(port, timeoutMs = 500) {
|
|
21941
|
-
return new Promise((
|
|
22121
|
+
return new Promise((resolve6) => {
|
|
21942
22122
|
const sock = connect4({ host: "127.0.0.1", port });
|
|
21943
22123
|
const done = (ok) => {
|
|
21944
22124
|
sock.destroy();
|
|
21945
|
-
|
|
22125
|
+
resolve6(ok);
|
|
21946
22126
|
};
|
|
21947
22127
|
sock.setTimeout(timeoutMs, () => done(false));
|
|
21948
22128
|
sock.once("connect", () => done(true));
|
|
@@ -21978,35 +22158,50 @@ init_types();
|
|
|
21978
22158
|
async function ensureDaemon(transport, opts) {
|
|
21979
22159
|
const logger = opts.logger ?? silentLogger;
|
|
21980
22160
|
const startTimeoutMs = opts.startTimeoutMs ?? (opts.attachedFallback === void 0 ? 2e4 : 5e3);
|
|
22161
|
+
let mismatch;
|
|
21981
22162
|
try {
|
|
21982
22163
|
const authority = await acquireAuthority(transport);
|
|
21983
22164
|
return { port: authority.port, authority, bootstrapped: false, daemonLifetime: "persistent" };
|
|
21984
22165
|
} catch (err) {
|
|
21985
|
-
if (err instanceof CliError && err.code === "
|
|
22166
|
+
if (err instanceof CliError && err.code === "protocol_mismatch") mismatch = err;
|
|
21986
22167
|
}
|
|
21987
22168
|
const inspection = await inspectHost(transport).catch((err) => {
|
|
21988
|
-
if (err instanceof CliError) throw err;
|
|
22169
|
+
if (err instanceof CliError || mismatch) throw err;
|
|
21989
22170
|
return null;
|
|
21990
22171
|
});
|
|
22172
|
+
if (!inspection && mismatch) throw mismatch;
|
|
22173
|
+
const install = async () => {
|
|
22174
|
+
await installDaemon(transport, opts);
|
|
22175
|
+
return waitAfterInstall(transport, opts, logger, true, startTimeoutMs);
|
|
22176
|
+
};
|
|
21991
22177
|
if (inspection) {
|
|
21992
|
-
|
|
21993
|
-
|
|
21994
|
-
|
|
21995
|
-
|
|
21996
|
-
|
|
21997
|
-
|
|
21998
|
-
|
|
21999
|
-
|
|
22000
|
-
|
|
22178
|
+
let replacement;
|
|
22179
|
+
try {
|
|
22180
|
+
await runHandshake({
|
|
22181
|
+
host: transport.label,
|
|
22182
|
+
inspection,
|
|
22183
|
+
noUpgrade: opts.noUpgrade,
|
|
22184
|
+
confirmDaemonUpgrade: opts.confirmDaemonUpgrade,
|
|
22185
|
+
reinspect: () => inspectHost(transport),
|
|
22186
|
+
logger,
|
|
22187
|
+
upgradeDaemon: async () => {
|
|
22188
|
+
replacement = await install();
|
|
22189
|
+
return replacement.authority.version;
|
|
22190
|
+
}
|
|
22191
|
+
});
|
|
22192
|
+
if (replacement) return replacement;
|
|
22193
|
+
const authority = await acquireAuthority(transport);
|
|
22194
|
+
return { port: authority.port, authority, bootstrapped: false, daemonLifetime: "persistent" };
|
|
22195
|
+
} catch (err) {
|
|
22196
|
+
replacement?.authority.close();
|
|
22197
|
+
await replacement?.lease?.stop();
|
|
22198
|
+
throw err;
|
|
22001
22199
|
}
|
|
22002
|
-
|
|
22003
|
-
|
|
22004
|
-
);
|
|
22005
|
-
} else if (await installedVersion(transport) !== null) {
|
|
22200
|
+
}
|
|
22201
|
+
if (await installedVersion(transport) !== null) {
|
|
22006
22202
|
logger.info(`restarting puddled on ${transport.label}`);
|
|
22007
22203
|
}
|
|
22008
|
-
|
|
22009
|
-
return waitAfterInstall(transport, opts, logger, true, startTimeoutMs);
|
|
22204
|
+
return install();
|
|
22010
22205
|
}
|
|
22011
22206
|
async function waitAfterInstall(transport, opts, logger, bootstrapped, timeoutMs) {
|
|
22012
22207
|
try {
|
|
@@ -22042,7 +22237,7 @@ async function waitForStartedDaemon(transport, timeoutMs, signal) {
|
|
|
22042
22237
|
}
|
|
22043
22238
|
return { port: authority.port, authority };
|
|
22044
22239
|
} catch (err) {
|
|
22045
|
-
if (err instanceof CliError && err.code === "
|
|
22240
|
+
if (err instanceof CliError && err.code === "protocol_mismatch") throw err;
|
|
22046
22241
|
}
|
|
22047
22242
|
await sleep(500);
|
|
22048
22243
|
}
|
|
@@ -22055,23 +22250,6 @@ function startTimeout(transport) {
|
|
|
22055
22250
|
transport.kind === "ssh" ? `inspect it with: puddle logs ${transport.label}` : "inspect it with: puddle logs"
|
|
22056
22251
|
);
|
|
22057
22252
|
}
|
|
22058
|
-
function makeUpgrader(transport, client, opts, lease) {
|
|
22059
|
-
return async () => {
|
|
22060
|
-
await installDaemon(transport, opts);
|
|
22061
|
-
await lease?.ensureRunning();
|
|
22062
|
-
const fresh = await waitForStartedDaemon(transport, 3e4);
|
|
22063
|
-
client.setAuthority(fresh.authority);
|
|
22064
|
-
const deadline = Date.now() + 3e4;
|
|
22065
|
-
while (Date.now() < deadline) {
|
|
22066
|
-
if (await client.responds()) return;
|
|
22067
|
-
await sleep(500);
|
|
22068
|
-
}
|
|
22069
|
-
throw new CliError(
|
|
22070
|
-
"daemon_start_timeout",
|
|
22071
|
-
`puddled did not come back after updating on ${transport.label}`
|
|
22072
|
-
);
|
|
22073
|
-
};
|
|
22074
|
-
}
|
|
22075
22253
|
|
|
22076
22254
|
// src/lib/attached-daemon.ts
|
|
22077
22255
|
init_types();
|
|
@@ -22134,7 +22312,7 @@ var AttachedDaemon = class {
|
|
|
22134
22312
|
}
|
|
22135
22313
|
async launchAndWait(preferredPort) {
|
|
22136
22314
|
const portFlag = preferredPort === void 0 ? "" : ` --port ${preferredPort}`;
|
|
22137
|
-
const leasePidPath = `${hostPaths.home}/attached-${
|
|
22315
|
+
const leasePidPath = `${hostPaths.home}/attached-${randomUUID2()}.pid`;
|
|
22138
22316
|
this.leasePidPath = leasePidPath;
|
|
22139
22317
|
const command = this.ssh.runAttached(
|
|
22140
22318
|
`printf '%s\\n' "$$" > ${leasePidPath}; exec ${hostPaths.current}/puddled${portFlag} >> ${hostPaths.logs}/puddled.out.log 2>&1`
|
|
@@ -22230,10 +22408,6 @@ var DaemonClient = class {
|
|
|
22230
22408
|
this.port = port;
|
|
22231
22409
|
this.authority = authority;
|
|
22232
22410
|
}
|
|
22233
|
-
setAuthority(authority) {
|
|
22234
|
-
this.authority.close();
|
|
22235
|
-
this.authority = authority;
|
|
22236
|
-
}
|
|
22237
22411
|
setPort(port) {
|
|
22238
22412
|
this.port = port;
|
|
22239
22413
|
}
|
|
@@ -22275,21 +22449,6 @@ var DaemonClient = class {
|
|
|
22275
22449
|
async liveSessions() {
|
|
22276
22450
|
return (await this.sessions()).filter((s) => LIVE.has(s.status));
|
|
22277
22451
|
}
|
|
22278
|
-
async liveSessionCount() {
|
|
22279
|
-
return (await this.sessions()).filter((s) => LIVE.has(s.status)).length;
|
|
22280
|
-
}
|
|
22281
|
-
/** Readiness requires an authenticated response; rejection never counts as ready. */
|
|
22282
|
-
async responds() {
|
|
22283
|
-
try {
|
|
22284
|
-
const res = await fetch(`http://127.0.0.1:${this.port}/api/version`, {
|
|
22285
|
-
headers: { authorization: `Bearer ${this.authority.credential()}` },
|
|
22286
|
-
signal: AbortSignal.timeout(2e3)
|
|
22287
|
-
});
|
|
22288
|
-
return res.ok;
|
|
22289
|
-
} catch {
|
|
22290
|
-
return false;
|
|
22291
|
-
}
|
|
22292
|
-
}
|
|
22293
22452
|
};
|
|
22294
22453
|
async function readDaemonPort(transport) {
|
|
22295
22454
|
const runtime = await transport.readFile(hostPaths.runtime);
|
|
@@ -22311,51 +22470,8 @@ async function readDaemonPort(transport) {
|
|
|
22311
22470
|
return 7434;
|
|
22312
22471
|
}
|
|
22313
22472
|
|
|
22314
|
-
// src/lib/handshake.ts
|
|
22315
|
-
init_types();
|
|
22316
|
-
var CLI_UPGRADE_COMMAND = "npm install -g @puddle-code/cli";
|
|
22317
|
-
function decideHandshake(cli, daemon) {
|
|
22318
|
-
if (daemon.major === cli.major) return { kind: "proceed" };
|
|
22319
|
-
if (daemon.major < cli.major) return { kind: "upgrade-daemon" };
|
|
22320
|
-
return { kind: "refuse-cli-outdated", command: CLI_UPGRADE_COMMAND };
|
|
22321
|
-
}
|
|
22322
|
-
async function runHandshake(opts) {
|
|
22323
|
-
const logger = opts.logger ?? silentLogger;
|
|
22324
|
-
const daemon = await opts.client.version();
|
|
22325
|
-
const decision = decideHandshake(PROTOCOL_VERSION, daemon.protocol);
|
|
22326
|
-
if (decision.kind === "proceed") return daemon;
|
|
22327
|
-
if (decision.kind === "refuse-cli-outdated") {
|
|
22328
|
-
throw new CliError(
|
|
22329
|
-
"cli_outdated",
|
|
22330
|
-
`the daemon speaks protocol ${daemon.protocol.major}; this CLI speaks ${PROTOCOL_VERSION.major}`,
|
|
22331
|
-
`update the CLI: ${decision.command}`
|
|
22332
|
-
);
|
|
22333
|
-
}
|
|
22334
|
-
const liveSessions = await opts.client.liveSessionCount().catch(() => 0);
|
|
22335
|
-
if (opts.noUpgrade) {
|
|
22336
|
-
throw new CliError(
|
|
22337
|
-
"upgrade_failed",
|
|
22338
|
-
`the daemon speaks protocol ${daemon.protocol.major} (CLI: ${PROTOCOL_VERSION.major}) and --no-upgrade is set`,
|
|
22339
|
-
`re-run without --no-upgrade to update it (${liveSessions} live session(s) would be interrupted and can be resumed)`
|
|
22340
|
-
);
|
|
22341
|
-
}
|
|
22342
|
-
logger.info(
|
|
22343
|
-
`updating puddled (protocol ${daemon.protocol.major} \u2192 ${PROTOCOL_VERSION.major}) \u2014 ${liveSessions} live session(s) will be interrupted and can be resumed`
|
|
22344
|
-
);
|
|
22345
|
-
await opts.upgradeDaemon({ from: daemon.version, liveSessions });
|
|
22346
|
-
const after = await opts.client.version();
|
|
22347
|
-
if (after.protocol.major !== PROTOCOL_VERSION.major) {
|
|
22348
|
-
throw new CliError(
|
|
22349
|
-
"upgrade_failed",
|
|
22350
|
-
`the daemon still speaks protocol ${after.protocol.major} after updating`,
|
|
22351
|
-
"inspect the daemon logs: puddle logs"
|
|
22352
|
-
);
|
|
22353
|
-
}
|
|
22354
|
-
return after;
|
|
22355
|
-
}
|
|
22356
|
-
|
|
22357
22473
|
// src/lib/serve/ui-server.ts
|
|
22358
|
-
import { randomUUID as
|
|
22474
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
22359
22475
|
import { createServer as createServer3 } from "node:http";
|
|
22360
22476
|
|
|
22361
22477
|
// src/lib/auth/launcher.ts
|
|
@@ -22391,7 +22507,7 @@ async function startLauncher(home, identity, invite) {
|
|
|
22391
22507
|
path,
|
|
22392
22508
|
close: async () => {
|
|
22393
22509
|
for (const socket of sockets) socket.destroy();
|
|
22394
|
-
await new Promise((
|
|
22510
|
+
await new Promise((resolve6) => server.close(() => resolve6()));
|
|
22395
22511
|
}
|
|
22396
22512
|
};
|
|
22397
22513
|
}
|
|
@@ -22401,14 +22517,14 @@ async function requestInvitation(home, path) {
|
|
|
22401
22517
|
const stored = readPrivateJson(secretFile(home, path));
|
|
22402
22518
|
pipeSecret = stored.secret;
|
|
22403
22519
|
} else privatePath(path);
|
|
22404
|
-
return new Promise((
|
|
22520
|
+
return new Promise((resolve6, reject) => {
|
|
22405
22521
|
const socket = connect5(path);
|
|
22406
22522
|
socket.setTimeout(5e3, () => socket.destroy(new Error("Launcher did not answer")));
|
|
22407
22523
|
jsonLines(
|
|
22408
22524
|
socket,
|
|
22409
22525
|
(value) => {
|
|
22410
22526
|
const message = launcherResponseSchema.parse(value);
|
|
22411
|
-
|
|
22527
|
+
resolve6(message.url);
|
|
22412
22528
|
socket.end();
|
|
22413
22529
|
},
|
|
22414
22530
|
() => socket.destroy(new Error("Invalid launcher reply"))
|
|
@@ -22628,7 +22744,7 @@ function writeStore(file2, store) {
|
|
|
22628
22744
|
renameSync3(tmp, file2);
|
|
22629
22745
|
}
|
|
22630
22746
|
function readBody(req) {
|
|
22631
|
-
return new Promise((
|
|
22747
|
+
return new Promise((resolve6, reject) => {
|
|
22632
22748
|
let size = 0;
|
|
22633
22749
|
const chunks = [];
|
|
22634
22750
|
req.on("data", (chunk) => {
|
|
@@ -22640,7 +22756,7 @@ function readBody(req) {
|
|
|
22640
22756
|
}
|
|
22641
22757
|
chunks.push(chunk);
|
|
22642
22758
|
});
|
|
22643
|
-
req.on("end", () =>
|
|
22759
|
+
req.on("end", () => resolve6(Buffer.concat(chunks).toString("utf8")));
|
|
22644
22760
|
req.on("error", reject);
|
|
22645
22761
|
});
|
|
22646
22762
|
}
|
|
@@ -23227,7 +23343,7 @@ async function startUiServer(opts) {
|
|
|
23227
23343
|
const tracker = new ProxySocketTracker();
|
|
23228
23344
|
const serveStatic = createStaticHandler(opts.assetsDir);
|
|
23229
23345
|
const handleRemoteAccess = remoteAccessHandler(opts.remoteAccess);
|
|
23230
|
-
const nonce =
|
|
23346
|
+
const nonce = randomUUID3();
|
|
23231
23347
|
let origin = "";
|
|
23232
23348
|
let proxyOrigin = "";
|
|
23233
23349
|
let browsers;
|
|
@@ -23365,8 +23481,8 @@ async function startUiServer(opts) {
|
|
|
23365
23481
|
browsers.close();
|
|
23366
23482
|
bridge.close();
|
|
23367
23483
|
tracker.destroyAll();
|
|
23368
|
-
await new Promise((
|
|
23369
|
-
server.close(() =>
|
|
23484
|
+
await new Promise((resolve6) => {
|
|
23485
|
+
server.close(() => resolve6());
|
|
23370
23486
|
server.closeAllConnections();
|
|
23371
23487
|
});
|
|
23372
23488
|
}
|
|
@@ -23376,15 +23492,15 @@ async function listen(server, startPort, strict, avoidPort) {
|
|
|
23376
23492
|
for (let probe = 0; probe < MAX_PORT_PROBES; probe += 1) {
|
|
23377
23493
|
const port = startPort + probe;
|
|
23378
23494
|
if (!strict && port === avoidPort) continue;
|
|
23379
|
-
const ok = await new Promise((
|
|
23495
|
+
const ok = await new Promise((resolve6, rejectListen) => {
|
|
23380
23496
|
const onError = (err) => {
|
|
23381
23497
|
server.off("listening", onListening);
|
|
23382
|
-
if (err.code === "EADDRINUSE")
|
|
23498
|
+
if (err.code === "EADDRINUSE") resolve6(false);
|
|
23383
23499
|
else rejectListen(err);
|
|
23384
23500
|
};
|
|
23385
23501
|
const onListening = () => {
|
|
23386
23502
|
server.off("error", onError);
|
|
23387
|
-
|
|
23503
|
+
resolve6(true);
|
|
23388
23504
|
};
|
|
23389
23505
|
server.once("error", onError);
|
|
23390
23506
|
server.once("listening", onListening);
|
|
@@ -23665,7 +23781,12 @@ async function connectRemote(opts) {
|
|
|
23665
23781
|
let uiResource;
|
|
23666
23782
|
let endpointResource;
|
|
23667
23783
|
try {
|
|
23668
|
-
const bootstrap = {
|
|
23784
|
+
const bootstrap = {
|
|
23785
|
+
tarball: opts.tarball,
|
|
23786
|
+
logger,
|
|
23787
|
+
noUpgrade: opts.noUpgrade,
|
|
23788
|
+
confirmDaemonUpgrade: opts.confirmDaemonUpgrade
|
|
23789
|
+
};
|
|
23669
23790
|
const attachedDaemon = new AttachedDaemon(ssh, logger);
|
|
23670
23791
|
const endpoint = await ensureDaemon(ssh, {
|
|
23671
23792
|
...bootstrap,
|
|
@@ -23706,12 +23827,7 @@ async function connectRemote(opts) {
|
|
|
23706
23827
|
logger
|
|
23707
23828
|
});
|
|
23708
23829
|
oauthForwardResource = oauthForward;
|
|
23709
|
-
const daemon = await
|
|
23710
|
-
client,
|
|
23711
|
-
noUpgrade: opts.noUpgrade,
|
|
23712
|
-
upgradeDaemon: makeUpgrader(ssh, client, bootstrap, endpoint.lease),
|
|
23713
|
-
logger
|
|
23714
|
-
});
|
|
23830
|
+
const daemon = verifyDaemonVersion(await client.version());
|
|
23715
23831
|
const avoidPort = await readDaemonPort(new LocalTransport());
|
|
23716
23832
|
endpoint.authority.setVerifier(async () => {
|
|
23717
23833
|
const generation = endpoint.authority.generation;
|
|
@@ -23734,7 +23850,7 @@ async function connectRemote(opts) {
|
|
|
23734
23850
|
watchTunnel(replacement);
|
|
23735
23851
|
await previous?.close();
|
|
23736
23852
|
}
|
|
23737
|
-
await client.version();
|
|
23853
|
+
verifyDaemonVersion(await client.version());
|
|
23738
23854
|
});
|
|
23739
23855
|
const ui = await startUiServer({
|
|
23740
23856
|
authority: endpoint.authority,
|
|
@@ -23936,24 +24052,26 @@ init_types();
|
|
|
23936
24052
|
async function startLocal(opts) {
|
|
23937
24053
|
const logger = opts.logger ?? silentLogger;
|
|
23938
24054
|
const transport = new LocalTransport();
|
|
23939
|
-
const bootstrap = {
|
|
23940
|
-
|
|
24055
|
+
const bootstrap = {
|
|
24056
|
+
tarball: opts.tarball,
|
|
24057
|
+
logger,
|
|
24058
|
+
noUpgrade: opts.noUpgrade,
|
|
24059
|
+
confirmDaemonUpgrade: opts.confirmDaemonUpgrade
|
|
24060
|
+
};
|
|
24061
|
+
let endpoint;
|
|
23941
24062
|
try {
|
|
23942
|
-
|
|
23943
|
-
const
|
|
23944
|
-
|
|
23945
|
-
|
|
23946
|
-
upgradeDaemon: makeUpgrader(transport, client, bootstrap),
|
|
23947
|
-
logger
|
|
23948
|
-
});
|
|
24063
|
+
endpoint = await ensureDaemon(transport, bootstrap);
|
|
24064
|
+
const authority = endpoint.authority;
|
|
24065
|
+
const client = new DaemonClient(endpoint.port, authority);
|
|
24066
|
+
const daemon = verifyDaemonVersion(await client.version());
|
|
23949
24067
|
let currentUi = void 0;
|
|
23950
|
-
|
|
23951
|
-
client.setPort(
|
|
23952
|
-
await client.version();
|
|
23953
|
-
currentUi?.setTarget({ host: "127.0.0.1", port:
|
|
24068
|
+
authority.setVerifier(async () => {
|
|
24069
|
+
client.setPort(authority.port);
|
|
24070
|
+
verifyDaemonVersion(await client.version());
|
|
24071
|
+
currentUi?.setTarget({ host: "127.0.0.1", port: authority.port });
|
|
23954
24072
|
});
|
|
23955
24073
|
const ui = await startUiServer({
|
|
23956
|
-
authority
|
|
24074
|
+
authority,
|
|
23957
24075
|
identity: "local",
|
|
23958
24076
|
refreshId: opts.refreshId,
|
|
23959
24077
|
assetsDir: opts.assetsDir,
|
|
@@ -23982,12 +24100,12 @@ async function startLocal(opts) {
|
|
|
23982
24100
|
},
|
|
23983
24101
|
async stop() {
|
|
23984
24102
|
await ui.close();
|
|
23985
|
-
|
|
24103
|
+
authority.close();
|
|
23986
24104
|
transport.dispose();
|
|
23987
24105
|
}
|
|
23988
24106
|
};
|
|
23989
24107
|
} catch (err) {
|
|
23990
|
-
endpoint
|
|
24108
|
+
endpoint?.authority.close();
|
|
23991
24109
|
transport.dispose();
|
|
23992
24110
|
throw err;
|
|
23993
24111
|
}
|
|
@@ -24017,12 +24135,170 @@ async function statusReport(client) {
|
|
|
24017
24135
|
// src/cli/run.ts
|
|
24018
24136
|
init_types();
|
|
24019
24137
|
|
|
24020
|
-
// src/cli/manage.ts
|
|
24138
|
+
// src/cli/manage-cli.ts
|
|
24139
|
+
init_types();
|
|
24021
24140
|
import { spawn as spawn7 } from "node:child_process";
|
|
24022
|
-
import {
|
|
24023
|
-
|
|
24141
|
+
import { dirname as dirname10 } from "node:path";
|
|
24142
|
+
|
|
24143
|
+
// src/lib/standalone-cli.ts
|
|
24144
|
+
init_types();
|
|
24145
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
24146
|
+
import { existsSync as existsSync5, lstatSync as lstatSync2, readFileSync as readFileSync9, realpathSync as realpathSync2 } from "node:fs";
|
|
24147
|
+
import { mkdir as mkdir2, readFile as readFile2, rm as rm2, unlink } from "node:fs/promises";
|
|
24148
|
+
import { dirname as dirname9, isAbsolute, join as join15, resolve as resolve5 } from "node:path";
|
|
24149
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
24150
|
+
function standaloneInstallation(entryFile = fileURLToPath4(import.meta.url), execPath = process.execPath) {
|
|
24151
|
+
const release = resolve5(dirname9(entryFile), "..");
|
|
24152
|
+
if (!existsSync5(join15(release, "STANDALONE"))) return null;
|
|
24153
|
+
try {
|
|
24154
|
+
const root = resolve5(release, "..", "..");
|
|
24155
|
+
const record2 = JSON.parse(readFileSync9(join15(root, "installation.json"), "utf8"));
|
|
24156
|
+
if (readFileSync9(join15(release, "STANDALONE"), "utf8").trim() !== "puddle-cli" || readFileSync9(join15(root, "INSTALLATION"), "utf8").trim() !== "puddle-cli" || dirname9(release) !== join15(root, "versions") || realpathSync2(release) !== release || realpathSync2(execPath) !== join15(release, "bin/node") || record2.kind !== "puddle-cli" || typeof record2.launcher !== "string" || !isAbsolute(record2.launcher))
|
|
24157
|
+
throw new Error("Installation metadata does not match this CLI");
|
|
24158
|
+
return { root, release, launcher: record2.launcher };
|
|
24159
|
+
} catch {
|
|
24160
|
+
throw new CliError(
|
|
24161
|
+
"not_installed",
|
|
24162
|
+
"this standalone archive is not an installer-managed CLI",
|
|
24163
|
+
"run the public install.sh to install the CLI before using upgrade or remove"
|
|
24164
|
+
);
|
|
24165
|
+
}
|
|
24166
|
+
}
|
|
24167
|
+
function upgradeStandaloneCli(installation, version2, repo) {
|
|
24168
|
+
const script = readFileSync9(join15(installation.release, "cli/install-cli.sh"), "utf8");
|
|
24169
|
+
const args = [
|
|
24170
|
+
"-s",
|
|
24171
|
+
"--",
|
|
24172
|
+
"--prefix",
|
|
24173
|
+
installation.root,
|
|
24174
|
+
"--bin-dir",
|
|
24175
|
+
dirname9(installation.launcher)
|
|
24176
|
+
];
|
|
24177
|
+
if (version2 !== void 0) args.push("--version", version2);
|
|
24178
|
+
if (repo !== void 0) args.push("--repo", repo);
|
|
24179
|
+
return new Promise((resolve6, reject) => {
|
|
24180
|
+
const child = spawn6("sh", args, { stdio: ["pipe", "inherit", "inherit"] });
|
|
24181
|
+
child.on("error", reject);
|
|
24182
|
+
child.stdin.on("error", (error51) => {
|
|
24183
|
+
if (error51.code !== "EPIPE") reject(error51);
|
|
24184
|
+
});
|
|
24185
|
+
child.stdin.end(script);
|
|
24186
|
+
child.on("exit", (code) => {
|
|
24187
|
+
if (code === 0) resolve6(0);
|
|
24188
|
+
else reject(new CliError("not_installed", `CLI installer exited with ${code ?? "a signal"}`));
|
|
24189
|
+
});
|
|
24190
|
+
});
|
|
24191
|
+
}
|
|
24192
|
+
function standaloneLauncher(root) {
|
|
24193
|
+
const quoted = `'${join15(root, "current/puddle").replaceAll("'", "'\\''")}'`;
|
|
24194
|
+
return `#!/bin/sh
|
|
24195
|
+
# puddle standalone launcher
|
|
24196
|
+
exec ${quoted} "$@"
|
|
24197
|
+
`;
|
|
24198
|
+
}
|
|
24199
|
+
async function removeStandaloneCli(installation) {
|
|
24200
|
+
const lock = join15(installation.root, ".install-lock");
|
|
24201
|
+
try {
|
|
24202
|
+
await mkdir2(lock);
|
|
24203
|
+
} catch {
|
|
24204
|
+
throw new CliError("already_running", "another CLI installation operation is running");
|
|
24205
|
+
}
|
|
24206
|
+
try {
|
|
24207
|
+
if (lstatSync2(installation.launcher).isSymbolicLink() || await readFile2(installation.launcher, "utf8") !== standaloneLauncher(installation.root))
|
|
24208
|
+
throw new CliError("not_installed", "the CLI launcher was replaced; refusing removal");
|
|
24209
|
+
await unlink(installation.launcher);
|
|
24210
|
+
await rm2(installation.root, { recursive: true, force: true });
|
|
24211
|
+
} finally {
|
|
24212
|
+
await rm2(lock, { recursive: true, force: true });
|
|
24213
|
+
}
|
|
24214
|
+
}
|
|
24215
|
+
|
|
24216
|
+
// src/cli/manage-cli.ts
|
|
24217
|
+
function upgradeCli(version2, logger) {
|
|
24218
|
+
const standalone = standaloneInstallation();
|
|
24219
|
+
if (standalone) {
|
|
24220
|
+
logger.info(`upgrading standalone CLI ${cliVersion()} to ${version2 ?? "the latest release"}`);
|
|
24221
|
+
return upgradeStandaloneCli(standalone, version2, repoSlug());
|
|
24222
|
+
}
|
|
24223
|
+
const spec = `@puddle-code/cli@${version2 ?? "latest"}`;
|
|
24224
|
+
logger.info(`puddle CLI ${cliVersion()} \u2014 asking npm for ${version2 ?? "the latest release"}`);
|
|
24225
|
+
return new Promise((resolve6, reject) => {
|
|
24226
|
+
const child = spawn7("npm", ["install", "-g", spec], { stdio: "inherit" });
|
|
24227
|
+
child.on(
|
|
24228
|
+
"error",
|
|
24229
|
+
() => reject(
|
|
24230
|
+
new CliError(
|
|
24231
|
+
"not_installed",
|
|
24232
|
+
"npm is not on PATH",
|
|
24233
|
+
`the CLI is installed and upgraded via npm: npm install -g ${spec}`
|
|
24234
|
+
)
|
|
24235
|
+
)
|
|
24236
|
+
);
|
|
24237
|
+
child.on("exit", (code) => {
|
|
24238
|
+
if (code === 0) {
|
|
24239
|
+
logger.info("done \u2014 `puddle --version` shows the installed version");
|
|
24240
|
+
resolve6(0);
|
|
24241
|
+
} else {
|
|
24242
|
+
reject(new CliError("not_installed", `npm install exited with ${code ?? "a signal"}`));
|
|
24243
|
+
}
|
|
24244
|
+
});
|
|
24245
|
+
});
|
|
24246
|
+
}
|
|
24247
|
+
async function removeCli(yes, logger) {
|
|
24248
|
+
const standalone = standaloneInstallation();
|
|
24249
|
+
if (standalone) {
|
|
24250
|
+
const proceed2 = await confirm(
|
|
24251
|
+
`Remove the standalone CLI at ${standalone.root} and its launcher in ${dirname10(standalone.launcher)}? Close its cockpits first; daemons and their sessions are untouched.`,
|
|
24252
|
+
{ skip: yes }
|
|
24253
|
+
);
|
|
24254
|
+
if (!proceed2) {
|
|
24255
|
+
logger.info("nothing removed");
|
|
24256
|
+
return 0;
|
|
24257
|
+
}
|
|
24258
|
+
await removeStandaloneCli(standalone);
|
|
24259
|
+
logger.info("removed the standalone CLI \u2014 daemon installations and data are untouched");
|
|
24260
|
+
return 0;
|
|
24261
|
+
}
|
|
24262
|
+
const managed = await new Promise((resolve6) => {
|
|
24263
|
+
const child = spawn7("npm", ["ls", "-g", "@puddle-code/cli", "--depth=0"], { stdio: "ignore" });
|
|
24264
|
+
child.on("error", () => resolve6(false));
|
|
24265
|
+
child.on("exit", (code) => resolve6(code === 0));
|
|
24266
|
+
});
|
|
24267
|
+
if (!managed) {
|
|
24268
|
+
throw new CliError(
|
|
24269
|
+
"not_installed",
|
|
24270
|
+
"the puddle CLI is not an npm global install on this machine",
|
|
24271
|
+
"a repo checkout or linked dev build is removed the way it was installed"
|
|
24272
|
+
);
|
|
24273
|
+
}
|
|
24274
|
+
const proceed = await confirm(
|
|
24275
|
+
`Remove the puddle CLI (npm uninstall -g @puddle-code/cli)? Running cockpits keep running until killed; daemons and their sessions are untouched.`,
|
|
24276
|
+
{ skip: yes }
|
|
24277
|
+
);
|
|
24278
|
+
if (!proceed) {
|
|
24279
|
+
logger.info("nothing removed");
|
|
24280
|
+
return 0;
|
|
24281
|
+
}
|
|
24282
|
+
return new Promise((resolve6, reject) => {
|
|
24283
|
+
const child = spawn7("npm", ["uninstall", "-g", "@puddle-code/cli"], { stdio: "inherit" });
|
|
24284
|
+
child.on("error", () => reject(new CliError("not_installed", "npm is not on PATH")));
|
|
24285
|
+
child.on("exit", (code) => {
|
|
24286
|
+
if (code === 0) {
|
|
24287
|
+
logger.info("removed \u2014 daemons keep running (puddle remove daemon uninstalls one)");
|
|
24288
|
+
resolve6(0);
|
|
24289
|
+
} else {
|
|
24290
|
+
reject(new CliError("not_installed", `npm uninstall exited with ${code ?? "a signal"}`));
|
|
24291
|
+
}
|
|
24292
|
+
});
|
|
24293
|
+
});
|
|
24294
|
+
}
|
|
24295
|
+
|
|
24296
|
+
// src/cli/manage.ts
|
|
24297
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
24298
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
24299
|
+
import { chmod as chmod2, copyFile, mkdir as mkdir3, rename, rm as rm3 } from "node:fs/promises";
|
|
24024
24300
|
import { homedir as homedir2 } from "node:os";
|
|
24025
|
-
import { join as
|
|
24301
|
+
import { join as join16 } from "node:path";
|
|
24026
24302
|
|
|
24027
24303
|
// src/lib/releases.ts
|
|
24028
24304
|
init_types();
|
|
@@ -24155,9 +24431,9 @@ async function upgradeDaemon(transport, opts = {}) {
|
|
|
24155
24431
|
}
|
|
24156
24432
|
|
|
24157
24433
|
// src/cli/detach.ts
|
|
24158
|
-
import { spawn as
|
|
24159
|
-
import { closeSync as closeSync2, mkdirSync as mkdirSync6, openSync as openSync2, readFileSync as
|
|
24160
|
-
import { dirname as
|
|
24434
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
24435
|
+
import { closeSync as closeSync2, mkdirSync as mkdirSync6, openSync as openSync2, readFileSync as readFileSync10 } from "node:fs";
|
|
24436
|
+
import { dirname as dirname11 } from "node:path";
|
|
24161
24437
|
init_types();
|
|
24162
24438
|
var COCKPIT_CHILD_ENV = "PUDDLE_COCKPIT_CHILD";
|
|
24163
24439
|
var READY_TIMEOUT_MS = 3e5;
|
|
@@ -24196,12 +24472,12 @@ async function launchDetached(opts) {
|
|
|
24196
24472
|
}
|
|
24197
24473
|
if (target !== "local") await new SshTransport(target).open();
|
|
24198
24474
|
const logFile = cockpitLogPath(target);
|
|
24199
|
-
mkdirSync6(
|
|
24475
|
+
mkdirSync6(dirname11(logFile), { recursive: true });
|
|
24200
24476
|
const fd = openSync2(logFile, "w");
|
|
24201
24477
|
const argv = opts.argv ?? process.argv.slice(2);
|
|
24202
|
-
const child =
|
|
24478
|
+
const child = spawn8(process.execPath, [process.argv[1] ?? "", ...argv], {
|
|
24203
24479
|
detached: true,
|
|
24204
|
-
stdio: ["ignore", fd, fd],
|
|
24480
|
+
stdio: ["ignore", fd, fd, "ipc"],
|
|
24205
24481
|
env: { ...process.env, [COCKPIT_CHILD_ENV]: "1" }
|
|
24206
24482
|
});
|
|
24207
24483
|
closeSync2(fd);
|
|
@@ -24210,7 +24486,13 @@ async function launchDetached(opts) {
|
|
|
24210
24486
|
throw new CliError("cockpit_launch_failed", "could not spawn the background cockpit process");
|
|
24211
24487
|
}
|
|
24212
24488
|
logger.info(`starting the cockpit for ${target} in the background\u2026`);
|
|
24213
|
-
const
|
|
24489
|
+
const stopPromptRelay = relayDaemonUpgradePrompt(child);
|
|
24490
|
+
let record2;
|
|
24491
|
+
try {
|
|
24492
|
+
record2 = await followStartup(target, child.pid, logFile);
|
|
24493
|
+
} finally {
|
|
24494
|
+
stopPromptRelay();
|
|
24495
|
+
}
|
|
24214
24496
|
if (record2 === null || record2.status !== "ready") {
|
|
24215
24497
|
if (record2?.status === "error") {
|
|
24216
24498
|
logger.warn(record2.message ?? "the cockpit failed to start");
|
|
@@ -24233,7 +24515,7 @@ async function followStartup(target, childPid, logFile) {
|
|
|
24233
24515
|
let offset = 0;
|
|
24234
24516
|
const relay = () => {
|
|
24235
24517
|
try {
|
|
24236
|
-
const buf =
|
|
24518
|
+
const buf = readFileSync10(logFile);
|
|
24237
24519
|
if (buf.length > offset) {
|
|
24238
24520
|
process.stderr.write(buf.subarray(offset));
|
|
24239
24521
|
offset = buf.length;
|
|
@@ -24258,7 +24540,7 @@ async function followStartup(target, childPid, logFile) {
|
|
|
24258
24540
|
}
|
|
24259
24541
|
function spawnDetachedRefresh(target, argv, refreshId) {
|
|
24260
24542
|
const logFile = cockpitLogPath(target);
|
|
24261
|
-
mkdirSync6(
|
|
24543
|
+
mkdirSync6(dirname11(logFile), { recursive: true });
|
|
24262
24544
|
const fd = openSync2(logFile, "a");
|
|
24263
24545
|
const env = {
|
|
24264
24546
|
...process.env,
|
|
@@ -24266,7 +24548,7 @@ function spawnDetachedRefresh(target, argv, refreshId) {
|
|
|
24266
24548
|
PUDDLE_INTERNAL_REFRESH: "1"
|
|
24267
24549
|
};
|
|
24268
24550
|
delete env[COCKPIT_CHILD_ENV];
|
|
24269
|
-
const child =
|
|
24551
|
+
const child = spawn8(process.execPath, [process.argv[1] ?? "", ...argv], {
|
|
24270
24552
|
detached: true,
|
|
24271
24553
|
stdio: ["ignore", fd, fd],
|
|
24272
24554
|
env
|
|
@@ -24299,41 +24581,6 @@ async function presentInvitation(record2, noBrowser, logger) {
|
|
|
24299
24581
|
else openBrowser(url2);
|
|
24300
24582
|
}
|
|
24301
24583
|
|
|
24302
|
-
// src/cli/prompt.ts
|
|
24303
|
-
init_types();
|
|
24304
|
-
import { createInterface as createInterface2 } from "node:readline";
|
|
24305
|
-
async function ask(question, def) {
|
|
24306
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) return def;
|
|
24307
|
-
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
24308
|
-
try {
|
|
24309
|
-
const answer = await new Promise(
|
|
24310
|
-
(resolve5) => rl.question(`${question} [${def}] `, resolve5)
|
|
24311
|
-
);
|
|
24312
|
-
return answer.trim() === "" ? def : answer.trim();
|
|
24313
|
-
} finally {
|
|
24314
|
-
rl.close();
|
|
24315
|
-
}
|
|
24316
|
-
}
|
|
24317
|
-
async function confirm(question, opts = {}) {
|
|
24318
|
-
if (opts.skip === true) return true;
|
|
24319
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
24320
|
-
throw new CliError(
|
|
24321
|
-
"bad_arguments",
|
|
24322
|
-
"refusing to confirm without a terminal",
|
|
24323
|
-
"pass --yes to proceed non-interactively"
|
|
24324
|
-
);
|
|
24325
|
-
}
|
|
24326
|
-
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
24327
|
-
try {
|
|
24328
|
-
const answer = await new Promise(
|
|
24329
|
-
(resolve5) => rl.question(`${question} [y/N] `, resolve5)
|
|
24330
|
-
);
|
|
24331
|
-
return /^y(es)?$/i.test(answer.trim());
|
|
24332
|
-
} finally {
|
|
24333
|
-
rl.close();
|
|
24334
|
-
}
|
|
24335
|
-
}
|
|
24336
|
-
|
|
24337
24584
|
// src/cli/manage.ts
|
|
24338
24585
|
function assertLocal(what, host, verb) {
|
|
24339
24586
|
if (host === void 0 || host === "local") return;
|
|
@@ -24442,31 +24689,6 @@ async function runUpgrade(cmd, logger) {
|
|
|
24442
24689
|
else if (process.platform === "darwin") logger.info("no desktop app installed \u2014 skipping");
|
|
24443
24690
|
return upgradeCli(cmd.version, logger);
|
|
24444
24691
|
}
|
|
24445
|
-
function upgradeCli(version2, logger) {
|
|
24446
|
-
const spec = `@puddle-code/cli@${version2 ?? "latest"}`;
|
|
24447
|
-
logger.info(`puddle CLI ${cliVersion()} \u2014 asking npm for ${version2 ?? "the latest release"}`);
|
|
24448
|
-
return new Promise((resolve5, reject) => {
|
|
24449
|
-
const child = spawn7("npm", ["install", "-g", spec], { stdio: "inherit" });
|
|
24450
|
-
child.on(
|
|
24451
|
-
"error",
|
|
24452
|
-
() => reject(
|
|
24453
|
-
new CliError(
|
|
24454
|
-
"not_installed",
|
|
24455
|
-
"npm is not on PATH",
|
|
24456
|
-
`the CLI is installed and upgraded via npm: npm install -g ${spec}`
|
|
24457
|
-
)
|
|
24458
|
-
)
|
|
24459
|
-
);
|
|
24460
|
-
child.on("exit", (code) => {
|
|
24461
|
-
if (code === 0) {
|
|
24462
|
-
logger.info("done \u2014 `puddle --version` shows the installed version");
|
|
24463
|
-
resolve5(0);
|
|
24464
|
-
} else {
|
|
24465
|
-
reject(new CliError("not_installed", `npm install exited with ${code ?? "a signal"}`));
|
|
24466
|
-
}
|
|
24467
|
-
});
|
|
24468
|
-
});
|
|
24469
|
-
}
|
|
24470
24692
|
async function setDesktop(opts, logger) {
|
|
24471
24693
|
if (process.platform === "linux") {
|
|
24472
24694
|
if (opts.verb !== "install") {
|
|
@@ -24538,22 +24760,22 @@ async function installDesktopAppImage(version2, logger) {
|
|
|
24538
24760
|
);
|
|
24539
24761
|
}
|
|
24540
24762
|
const dir = expandHome2(await ask("Where should the AppImage be stored?", "~/puddle"));
|
|
24541
|
-
const target =
|
|
24542
|
-
if (
|
|
24763
|
+
const target = join16(dir, "Puddle.AppImage");
|
|
24764
|
+
if (existsSync6(target) && version2 === void 0) {
|
|
24543
24765
|
logger.info(`${target} already exists \u2014 the app updates itself in-app (its update toast)`);
|
|
24544
24766
|
return 0;
|
|
24545
24767
|
}
|
|
24546
24768
|
logger.info(`installing Puddle ${update.version} to ${target}`);
|
|
24547
24769
|
const staged2 = await stageDesktopUpdate(update, { logger });
|
|
24548
|
-
await
|
|
24549
|
-
await
|
|
24770
|
+
await mkdir3(dir, { recursive: true });
|
|
24771
|
+
await rm3(target, { force: true });
|
|
24550
24772
|
try {
|
|
24551
24773
|
await rename(staged2.stagedPath, target);
|
|
24552
24774
|
} catch {
|
|
24553
24775
|
await copyFile(staged2.stagedPath, target);
|
|
24554
24776
|
await chmod2(target, 493);
|
|
24555
24777
|
}
|
|
24556
|
-
await
|
|
24778
|
+
await rm3(staged2.dir, { recursive: true, force: true });
|
|
24557
24779
|
const protocol = componentProtocolForVersion(update.version);
|
|
24558
24780
|
recordDesktopInstallation({
|
|
24559
24781
|
path: target,
|
|
@@ -24561,14 +24783,14 @@ async function installDesktopAppImage(version2, logger) {
|
|
|
24561
24783
|
...protocol ? { protocol } : {}
|
|
24562
24784
|
});
|
|
24563
24785
|
logger.info(`installed \u2014 run it directly (${target}) or add it to your launcher`);
|
|
24564
|
-
const opener =
|
|
24786
|
+
const opener = spawn9("xdg-open", [dir], { stdio: "ignore", detached: true });
|
|
24565
24787
|
opener.on("error", () => void 0);
|
|
24566
24788
|
opener.unref();
|
|
24567
24789
|
return 0;
|
|
24568
24790
|
}
|
|
24569
24791
|
function expandHome2(path) {
|
|
24570
24792
|
if (path === "~") return homedir2();
|
|
24571
|
-
return path.startsWith("~/") ?
|
|
24793
|
+
return path.startsWith("~/") ? join16(homedir2(), path.slice(2)) : path;
|
|
24572
24794
|
}
|
|
24573
24795
|
async function runRemove(cmd, logger) {
|
|
24574
24796
|
switch (cmd.what) {
|
|
@@ -24582,40 +24804,6 @@ async function runRemove(cmd, logger) {
|
|
|
24582
24804
|
return removeDaemonCmd(cmd, logger);
|
|
24583
24805
|
}
|
|
24584
24806
|
}
|
|
24585
|
-
async function removeCli(yes, logger) {
|
|
24586
|
-
const managed = await new Promise((resolve5) => {
|
|
24587
|
-
const child = spawn7("npm", ["ls", "-g", "@puddle-code/cli", "--depth=0"], { stdio: "ignore" });
|
|
24588
|
-
child.on("error", () => resolve5(false));
|
|
24589
|
-
child.on("exit", (code) => resolve5(code === 0));
|
|
24590
|
-
});
|
|
24591
|
-
if (!managed) {
|
|
24592
|
-
throw new CliError(
|
|
24593
|
-
"not_installed",
|
|
24594
|
-
"the puddle CLI is not an npm global install on this machine",
|
|
24595
|
-
"a repo checkout or linked dev build is removed the way it was installed"
|
|
24596
|
-
);
|
|
24597
|
-
}
|
|
24598
|
-
const proceed = await confirm(
|
|
24599
|
-
`Remove the puddle CLI (npm uninstall -g @puddle-code/cli)? Running cockpits keep running until killed; daemons and their sessions are untouched.`,
|
|
24600
|
-
{ skip: yes }
|
|
24601
|
-
);
|
|
24602
|
-
if (!proceed) {
|
|
24603
|
-
logger.info("nothing removed");
|
|
24604
|
-
return 0;
|
|
24605
|
-
}
|
|
24606
|
-
return new Promise((resolve5, reject) => {
|
|
24607
|
-
const child = spawn7("npm", ["uninstall", "-g", "@puddle-code/cli"], { stdio: "inherit" });
|
|
24608
|
-
child.on("error", () => reject(new CliError("not_installed", "npm is not on PATH")));
|
|
24609
|
-
child.on("exit", (code) => {
|
|
24610
|
-
if (code === 0) {
|
|
24611
|
-
logger.info("removed \u2014 daemons keep running (puddle remove daemon uninstalls one)");
|
|
24612
|
-
resolve5(0);
|
|
24613
|
-
} else {
|
|
24614
|
-
reject(new CliError("not_installed", `npm uninstall exited with ${code ?? "a signal"}`));
|
|
24615
|
-
}
|
|
24616
|
-
});
|
|
24617
|
-
});
|
|
24618
|
-
}
|
|
24619
24807
|
async function removeDesktop(yes, logger) {
|
|
24620
24808
|
if (process.platform !== "darwin") {
|
|
24621
24809
|
throw new CliError(
|
|
@@ -24640,7 +24828,7 @@ async function removeDesktop(yes, logger) {
|
|
|
24640
24828
|
logger.info("nothing removed");
|
|
24641
24829
|
return 0;
|
|
24642
24830
|
}
|
|
24643
|
-
await
|
|
24831
|
+
await rm3(installed.appPath, { recursive: true, force: true });
|
|
24644
24832
|
await pruneDesktopUpdateCache([]);
|
|
24645
24833
|
logger.info(`deleted ${installed.appPath} (recent hosts kept in ~/.puddle/recent-hosts.json)`);
|
|
24646
24834
|
return 0;
|
|
@@ -24762,9 +24950,9 @@ function timestampedLogger() {
|
|
|
24762
24950
|
|
|
24763
24951
|
// src/cli/run.ts
|
|
24764
24952
|
function assetsDir() {
|
|
24765
|
-
const here =
|
|
24766
|
-
for (const candidate of [
|
|
24767
|
-
if (
|
|
24953
|
+
const here = dirname12(fileURLToPath5(import.meta.url));
|
|
24954
|
+
for (const candidate of [join17(here, "public"), join17(here, "..", "..", "dist", "public")]) {
|
|
24955
|
+
if (existsSync7(join17(candidate, "index.html"))) return candidate;
|
|
24768
24956
|
}
|
|
24769
24957
|
throw new CliError(
|
|
24770
24958
|
"not_installed",
|
|
@@ -24798,7 +24986,7 @@ async function openTarget(host) {
|
|
|
24798
24986
|
};
|
|
24799
24987
|
}
|
|
24800
24988
|
function runUntilInterrupted(stop, farewell) {
|
|
24801
|
-
return new Promise((
|
|
24989
|
+
return new Promise((resolve6) => {
|
|
24802
24990
|
let stopping = false;
|
|
24803
24991
|
process.on("SIGINT", () => {
|
|
24804
24992
|
if (stopping) process.exit(130);
|
|
@@ -24806,10 +24994,10 @@ function runUntilInterrupted(stop, farewell) {
|
|
|
24806
24994
|
process.stderr.write(`
|
|
24807
24995
|
${farewell}
|
|
24808
24996
|
`);
|
|
24809
|
-
void stop().then(
|
|
24997
|
+
void stop().then(resolve6);
|
|
24810
24998
|
});
|
|
24811
24999
|
process.on("SIGTERM", () => {
|
|
24812
|
-
void stop().then(
|
|
25000
|
+
void stop().then(resolve6);
|
|
24813
25001
|
});
|
|
24814
25002
|
});
|
|
24815
25003
|
}
|
|
@@ -25097,6 +25285,7 @@ async function runCockpit(command, target, terminal) {
|
|
|
25097
25285
|
const common = {
|
|
25098
25286
|
...command,
|
|
25099
25287
|
assetsDir: assetsDir(),
|
|
25288
|
+
confirmDaemonUpgrade,
|
|
25100
25289
|
logger,
|
|
25101
25290
|
onRefreshRequest,
|
|
25102
25291
|
refreshId: process.env.PUDDLE_REFRESH_ID
|