@piecemaker-legal/piecemaker 1.0.6 → 1.0.8
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/assets/{abnfDiagram-VCTEODGH-DF7LFtT-.js → abnfDiagram-VCTEODGH-K8VucbQ5.js} +1 -1
- package/dist/assets/{arc-xOmjWeVH.js → arc-DUmCdRoC.js} +1 -1
- package/dist/assets/{architectureDiagram-5GKGNRK7-CESzNPwa.js → architectureDiagram-5GKGNRK7-DYhs5vqD.js} +1 -1
- package/dist/assets/{blockDiagram-I7D4REHJ-BjdN78Ad.js → blockDiagram-I7D4REHJ-DaXJ9nve.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-BcK9EDWT.js → c4Diagram-7LVT6UL2-BLbi-zPT.js} +1 -1
- package/dist/assets/channel-CJIq1vqf.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-DGUN1JFP.js → chunk-2Q5K7J3B-Bzs3gxaq.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-BG3BBcFr.js → chunk-5VM5RSS4-CQNzml7k.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-CLIOfrF5.js → chunk-F27PBJKO-B8xedWWH.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-D6lGkqij.js → chunk-IMKFNOWR-CjBGlNOu.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-BmF9vLBT.js → chunk-JWPE2WC7-vfnYEpgA.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-C_0SR3ak.js → chunk-POPQ4Y6H-Btl98tE8.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-rT20Wfpp.js → chunk-SVP7TREG-Bp6-pLjR.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-Cn2hkmcf.js → chunk-TICWLB2K-DvmznW2q.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-Dm_eP1_v.js → chunk-XXDRQBXY-QSHJTsI3.js} +1 -1
- package/dist/assets/classDiagram-ZZMXUADV-BYkEWggn.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-BYkEWggn.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-cJeeJsep.js → cose-bilkent-JH36ORCC-DxpyZoe2.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-DX2ePIXe.js → cynefin-OW5HDTMX-B9GRp2Gn.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-BvWXN-RR.js → cynefinDiagram-5FMLGOSQ-Q-2tz5GW.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-CkwPRhGR.js → dagre-GXQ25YYZ-3Rl3NqNe.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-CBGNzYyv.js → diagram-S7CK7UJ4-CreeWCO1.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-CiwCU0SJ.js → diagram-UQ7AKVKN-DNBoIyDJ.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV-Aekg4nTk.js → diagram-VSXAHHWV-ZkcE_cRk.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-DPFP5K7r.js → diagram-VX7I27RA-Dzdf7N2Q.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-BHJkxoey.js → diagram-Z3DM3KII-M6b-nr1u.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-2rV1P32T.js → ebnfDiagram-PWID7BFC-DunIw64n.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-C4BjyS_H.js → erDiagram-RLTQ6QDP-kN0Se5uc.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-DL1TBlAG.js → flowDiagram-HODETNUW-24YCovut.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-MKjvzCWz.js → ganttDiagram-EL5Y4UJY-DTUUEEy6.js} +1 -1
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-CS_oTGK5.js → gitGraphDiagram-WWUBYQGX-BitrqhMr.js} +1 -1
- package/dist/assets/{index-XOGQDqU3.js → index-CG9hW1GU.js} +120 -120
- package/dist/assets/index-D_Dc_nQv.css +1 -0
- package/dist/assets/{infoDiagram-27XIBGKW-ihUr2GjN.js → infoDiagram-27XIBGKW-BNHN5Mby.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-D2YAnuir.js → ishikawaDiagram-5VMMS53U-De3IQKso.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-Bc2hSZGD.js → journeyDiagram-3NMN7TZE-CWlkZ25T.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-LRMGNonX.js → kanban-definition-UXKFOSKX-2o_ax-Nk.js} +1 -1
- package/dist/assets/{layout-CcfWOlPV.js → layout-Btga9ZYP.js} +1 -1
- package/dist/assets/{linear-7JoS2gQO.js → linear-BoGGmqPG.js} +1 -1
- package/dist/assets/{mermaid.core-ijh_SsuN.js → mermaid.core-M308jnLQ.js} +6 -6
- package/dist/assets/{mindmap-definition-YA3MSWOX-DCE4nzfF.js → mindmap-definition-YA3MSWOX-BJ6C_grh.js} +1 -1
- package/dist/assets/{pegDiagram-XKGWAZYB-SjaGa7lE.js → pegDiagram-XKGWAZYB-qcdYX0Xe.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-1xcRidSZ.js → pieDiagram-E7YTZNPT-XPrSlt1M.js} +1 -1
- package/dist/assets/{quadrantDiagram-AXDQQJYC-obHAHnYV.js → quadrantDiagram-AXDQQJYC-COhHhra5.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-BsCVcRZb.js → railroadDiagram-O6MQD6OU-Mw_KF6gb.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-CUNkhvqb.js → requirementDiagram-BXWQKSXE-DPbt7jUM.js} +1 -1
- package/dist/assets/{sankeyDiagram-P5KCCOFB-DhezXMKm.js → sankeyDiagram-P5KCCOFB-B6HzL6Gh.js} +1 -1
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-CL9VswXH.js → sequenceDiagram-WJ2MYXX4-B_dXXCC0.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-t0g9jiEz.js → sizeCapture-INFHLROL-DwODubWN.js} +1 -1
- package/dist/assets/{stateDiagram-D77RDMKH-BW-g-IOz.js → stateDiagram-D77RDMKH-5AUnOL3S.js} +1 -1
- package/dist/assets/stateDiagram-v2-MP3YSRHH-CFM_nDL9.js +1 -0
- package/dist/assets/{swimlanes-42K2YHIH-LsuyeBte.js → swimlanes-42K2YHIH-BLnIwbnd.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-BRQJYnDN.js +8 -0
- package/dist/assets/{timeline-definition-24CTP7MA-B0cFka1R.js → timeline-definition-24CTP7MA-B1Yc7-na.js} +1 -1
- package/dist/assets/{vendor-codemirror-C5qqu4R7.js → vendor-codemirror-CjW0lHR5.js} +5 -5
- package/dist/assets/{vennDiagram-4TSXK5OY-D3HNCDR3.js → vennDiagram-4TSXK5OY-BFRtl-NI.js} +1 -1
- package/dist/assets/{wardleyDiagram-VM6X3IG4-DJ3Co8nb.js → wardleyDiagram-VM6X3IG4-BsR8iaD_.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-Cz5E9New.js → xychartDiagram-S5SC5T6Z-Crh1SwrA.js} +1 -1
- package/dist/favicon.png +0 -0
- package/dist/favicon.svg +1 -9
- package/dist/generate-icons.js +47 -46
- package/dist/icons/icon-128x128.png +0 -0
- package/dist/icons/icon-128x128.svg +1 -12
- package/dist/icons/icon-144x144.png +0 -0
- package/dist/icons/icon-144x144.svg +1 -12
- package/dist/icons/icon-152x152.png +0 -0
- package/dist/icons/icon-152x152.svg +1 -12
- package/dist/icons/icon-192x192.png +0 -0
- package/dist/icons/icon-192x192.svg +1 -12
- package/dist/icons/icon-384x384.png +0 -0
- package/dist/icons/icon-384x384.svg +1 -12
- package/dist/icons/icon-512x512.png +0 -0
- package/dist/icons/icon-512x512.svg +1 -12
- package/dist/icons/icon-72x72.png +0 -0
- package/dist/icons/icon-72x72.svg +1 -12
- package/dist/icons/icon-96x96.png +0 -0
- package/dist/icons/icon-96x96.svg +1 -12
- package/dist/index.html +3 -3
- package/dist/logo-128.png +0 -0
- package/dist/logo-256.png +0 -0
- package/dist/logo-32.png +0 -0
- package/dist/logo-512.png +0 -0
- package/dist/logo-64.png +0 -0
- package/dist/logo-sources/logo-black.png +0 -0
- package/dist/logo-sources/logo-white.png +0 -0
- package/dist/logo.svg +1 -17
- package/dist/sw.js +1 -1
- package/dist-server/plugins/piecemaker-dossier/src/institutional-terms.js +91 -0
- package/dist-server/plugins/piecemaker-dossier/src/institutional-terms.js.map +1 -0
- package/dist-server/plugins/piecemaker-dossier/src/knowledge.js +112 -10
- package/dist-server/plugins/piecemaker-dossier/src/knowledge.js.map +1 -1
- package/dist-server/plugins/piecemaker-dossier/src/scan-result.js +5 -1
- package/dist-server/plugins/piecemaker-dossier/src/scan-result.js.map +1 -1
- package/dist-server/server/index.js +60 -26
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/cli/desktop-shortcut.js +80 -0
- package/dist-server/server/modules/cli/desktop-shortcut.js.map +1 -0
- package/dist-server/server/modules/cli/pwa-launcher.js +27 -4
- package/dist-server/server/modules/cli/pwa-launcher.js.map +1 -1
- package/dist-server/server/modules/database/repositories/projects.db.js +28 -6
- package/dist-server/server/modules/database/repositories/projects.db.js.map +1 -1
- package/dist-server/server/modules/database/tests/projects.db.integration.test.js +58 -1
- package/dist-server/server/modules/database/tests/projects.db.integration.test.js.map +1 -1
- package/dist-server/server/modules/plugins/plugins.routes.js +8 -1
- package/dist-server/server/modules/plugins/plugins.routes.js.map +1 -1
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js +2 -0
- package/dist-server/server/modules/projects/services/projects-with-sessions-fetch.service.js.map +1 -1
- package/dist-server/server/piecemaker/bodacc-search.js +191 -0
- package/dist-server/server/piecemaker/bodacc-search.js.map +1 -0
- package/dist-server/server/piecemaker/company-search.js +145 -0
- package/dist-server/server/piecemaker/company-search.js.map +1 -0
- package/dist-server/server/piecemaker/company-search.test.js +18 -0
- package/dist-server/server/piecemaker/company-search.test.js.map +1 -0
- package/dist-server/server/piecemaker/index.js +6 -2
- package/dist-server/server/piecemaker/index.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/index.js +9 -4
- package/dist-server/server/piecemaker/knowledge/index.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/pipeline.js +68 -58
- package/dist-server/server/piecemaker/knowledge/pipeline.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/routes.js +2 -4
- package/dist-server/server/piecemaker/knowledge/routes.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/scan-jobs.js +91 -0
- package/dist-server/server/piecemaker/knowledge/scan-jobs.js.map +1 -0
- package/dist-server/server/piecemaker/knowledge/service.js +18 -36
- package/dist-server/server/piecemaker/knowledge/service.js.map +1 -1
- package/dist-server/server/piecemaker/library/connector-installation.js +224 -0
- package/dist-server/server/piecemaker/library/connector-installation.js.map +1 -0
- package/dist-server/server/piecemaker/library/index.js +11 -1
- package/dist-server/server/piecemaker/library/index.js.map +1 -1
- package/dist-server/server/piecemaker/library/marketplace.js +102 -43
- package/dist-server/server/piecemaker/library/marketplace.js.map +1 -1
- package/dist-server/server/piecemaker/library/migrate.js +8 -1
- package/dist-server/server/piecemaker/library/migrate.js.map +1 -1
- package/dist-server/server/piecemaker/library/provider-agents.js +50 -0
- package/dist-server/server/piecemaker/library/provider-agents.js.map +1 -0
- package/dist-server/server/piecemaker/library/provider-connectors.js +80 -0
- package/dist-server/server/piecemaker/library/provider-connectors.js.map +1 -0
- package/dist-server/server/piecemaker/library/provider-skills.js +11 -2
- package/dist-server/server/piecemaker/library/provider-skills.js.map +1 -1
- package/dist-server/server/piecemaker/library/store.js +125 -35
- package/dist-server/server/piecemaker/library/store.js.map +1 -1
- package/dist-server/server/piecemaker/library/workspace-installation.js +53 -29
- package/dist-server/server/piecemaker/library/workspace-installation.js.map +1 -1
- package/dist-server/server/piecemaker/timesheet/service.js +4 -21
- package/dist-server/server/piecemaker/timesheet/service.js.map +1 -1
- package/package.json +3 -2
- package/product.config.json +1 -0
- package/scripts/piecemaker/cli/install-command.mjs +35 -9
- package/scripts/piecemaker/cli/lib/app-window.mjs +47 -0
- package/scripts/piecemaker/cli/lib/config.mjs +1 -0
- package/scripts/piecemaker/cli/lib/piecemaker-anonymizer.mjs +78 -0
- package/scripts/piecemaker/cli/lib/plugins.mjs +2 -0
- package/scripts/piecemaker/cli/lib/pwa.mjs +49 -13
- package/scripts/piecemaker/cli/lib/services.mjs +65 -4
- package/scripts/piecemaker/cli/piecemaker-command.test.mjs +25 -0
- package/scripts/piecemaker/cli/piecemaker.mjs +60 -45
- package/scripts/piecemaker/cli/piecemaker.ps1 +8 -0
- package/scripts/piecemaker/cli/piecemaker.sh +6 -1
- package/server/index.ts +52 -23
- package/server/modules/cli/desktop-shortcut.ts +89 -0
- package/server/modules/cli/pwa-launcher.ts +29 -4
- package/server/modules/database/repositories/projects.db.ts +28 -6
- package/server/modules/database/tests/projects.db.integration.test.ts +63 -1
- package/server/modules/plugins/plugins.routes.ts +8 -1
- package/server/modules/projects/services/projects-with-sessions-fetch.service.ts +5 -0
- package/server/piecemaker/anonymizer/client-config.cjs +10 -2
- package/server/piecemaker/anonymizer/dictionary.cjs +6 -72
- package/server/piecemaker/bodacc-search.ts +205 -0
- package/server/piecemaker/company-search.test.ts +26 -0
- package/server/piecemaker/company-search.ts +183 -0
- package/server/piecemaker/index.ts +8 -2
- package/server/piecemaker/knowledge/index.ts +9 -4
- package/server/piecemaker/knowledge/pipeline.ts +89 -57
- package/server/piecemaker/knowledge/routes.ts +2 -4
- package/server/piecemaker/knowledge/scan-jobs.ts +111 -0
- package/server/piecemaker/knowledge/service.ts +18 -33
- package/server/piecemaker/library/connector-installation.ts +248 -0
- package/server/piecemaker/library/index.ts +5 -1
- package/server/piecemaker/library/marketplace.ts +104 -39
- package/server/piecemaker/library/migrate.ts +8 -1
- package/server/piecemaker/library/provider-agents.ts +49 -0
- package/server/piecemaker/library/provider-connectors.ts +89 -0
- package/server/piecemaker/library/provider-skills.ts +10 -1
- package/server/piecemaker/library/store.ts +129 -36
- package/server/piecemaker/library/workspace-installation.ts +54 -29
- package/server/piecemaker/router.cjs +2 -1
- package/server/piecemaker/timesheet/service.ts +4 -16
- package/server/piecemaker/vendor/installer/steps/03-python-gliner.mjs +0 -27
- package/server/piecemaker/vendor/installer/steps/14-mxc-sandbox.mjs +1 -1
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/substitution.cjs +2 -3
- package/server/piecemaker/vendor/piecemaker-plugin/skills/conversion-md/SKILL.md +3 -3
- package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +3 -10
- package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +135 -4
- package/server/piecemaker/vendor/websocket-server/process-group.cjs +95 -0
- package/server/piecemaker/vendor/websocket-server/scripts/__pycache__/scan_utils.cpython-312.pyc +0 -0
- package/server/piecemaker/vendor/websocket-server/scripts/convert_and_scan_pipeline.py +280 -149
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/model_config.cpython-312.pyc +0 -0
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/scanner_worker.cpython-312.pyc +0 -0
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/scanner_worker.py +35 -29
- package/server/piecemaker/vendor/websocket-server/scripts/requirements.txt +0 -5
- package/server/shared/types.ts +1 -0
- package/dist/assets/channel-Dz3eE5vU.js +0 -1
- package/dist/assets/classDiagram-ZZMXUADV-BC1eKV2j.js +0 -1
- package/dist/assets/classDiagram-v2-VYDZK3BY-BC1eKV2j.js +0 -1
- package/dist/assets/index-DhwJpHCq.css +0 -1
- package/dist/assets/stateDiagram-v2-MP3YSRHH-BXuT4zDG.js +0 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-B0eYrB4U.js +0 -8
- package/server/piecemaker/vendor/websocket-server/case-instructions.cjs +0 -111
- package/server/piecemaker/vendor/websocket-server/scripts/ADDRESS_ANONYMIZATION.md +0 -216
- package/server/piecemaker/vendor/websocket-server/scripts/DEDUPLICATION_CHANGES.md +0 -134
- package/server/piecemaker/vendor/websocket-server/scripts/GLINER2_README.md +0 -138
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/AUDIT_PERF_QUALITE.md +0 -246
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/MODELE_COREML.md +0 -62
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/__pycache__/coreml_runtime.cpython-312.pyc +0 -0
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/build_coreml.py +0 -118
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/coreml_runtime.py +0 -164
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/presidio-gliner.py +0 -479
|
@@ -6,6 +6,7 @@ import { spawn } from 'node:child_process';
|
|
|
6
6
|
import { APP, LOG_DIR, PIECEMAKER_HOME, PORTS } from './config.mjs';
|
|
7
7
|
import { waitUntil } from './exec.mjs';
|
|
8
8
|
import { npmPath, runtimeEnv } from './node-runtime.mjs';
|
|
9
|
+
import { freePort } from './ports.mjs';
|
|
9
10
|
|
|
10
11
|
export const APP_LOG = path.join(LOG_DIR, 'application.log');
|
|
11
12
|
export const APP_PID_FILE = path.join(PIECEMAKER_HOME, 'application.pid');
|
|
@@ -32,6 +33,53 @@ export function appClientReachable() {
|
|
|
32
33
|
return httpReachable(`http://127.0.0.1:${PORTS.appClient}/`);
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
function anonymizerPort() {
|
|
37
|
+
try {
|
|
38
|
+
const port = Number.parseInt(JSON.parse(fs.readFileSync(path.join(PIECEMAKER_HOME, 'config.json'), 'utf8')).mikePiiPort, 10);
|
|
39
|
+
if (Number.isInteger(port) && port > 0) return port;
|
|
40
|
+
} catch {
|
|
41
|
+
}
|
|
42
|
+
return PORTS.anonymizer;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function stopPid(pid) {
|
|
46
|
+
if (!Number.isInteger(pid) || pid <= 0 || pid === process.pid) return false;
|
|
47
|
+
if (process.platform === 'win32') {
|
|
48
|
+
try { process.kill(pid, 'SIGTERM'); return true; } catch { return false; }
|
|
49
|
+
}
|
|
50
|
+
try { process.kill(-pid, 'SIGTERM'); return true; } catch {
|
|
51
|
+
try { process.kill(pid, 'SIGTERM'); return true; } catch { return false; }
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function stopApplication() {
|
|
56
|
+
const killed = [];
|
|
57
|
+
const occupied = [];
|
|
58
|
+
try {
|
|
59
|
+
const pid = Number.parseInt(fs.readFileSync(APP_PID_FILE, 'utf8'), 10);
|
|
60
|
+
if (stopPid(pid)) killed.push(pid);
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const port of [PORTS.appClient, PORTS.appServer, anonymizerPort()]) {
|
|
65
|
+
const result = await freePort(port);
|
|
66
|
+
killed.push(...result.killed);
|
|
67
|
+
if (!result.released) occupied.push(port);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try { fs.rmSync(APP_PID_FILE, { force: true }); } catch { }
|
|
71
|
+
return { killed: [...new Set(killed)], occupied };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function readLogTail(file, maxLines = 40) {
|
|
75
|
+
try {
|
|
76
|
+
const lines = fs.readFileSync(file, 'utf8').trimEnd().split(/\n/);
|
|
77
|
+
return lines.slice(-maxLines).join('\n');
|
|
78
|
+
} catch {
|
|
79
|
+
return '';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
35
83
|
export async function startApplication(runtime, report) {
|
|
36
84
|
const alreadyRunning = await appClientReachable() && await appServerReachable();
|
|
37
85
|
if (alreadyRunning) {
|
|
@@ -61,11 +109,24 @@ export async function startApplication(runtime, report) {
|
|
|
61
109
|
fs.closeSync(logHandle);
|
|
62
110
|
}
|
|
63
111
|
|
|
64
|
-
child.unref();
|
|
65
112
|
fs.writeFileSync(APP_PID_FILE, String(child.pid), 'utf8');
|
|
66
113
|
|
|
67
|
-
const
|
|
68
|
-
|
|
114
|
+
const death = new Promise((resolve) => {
|
|
115
|
+
child.once('error', () => resolve('died'));
|
|
116
|
+
child.once('exit', () => resolve('died'));
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
await Promise.race([
|
|
120
|
+
waitUntil(appServerReachable, { timeoutMs: 120_000 }),
|
|
121
|
+
death,
|
|
122
|
+
]);
|
|
123
|
+
const serverUp = await appServerReachable();
|
|
124
|
+
if (!serverUp) {
|
|
125
|
+
child.unref();
|
|
126
|
+
return { started: true, pid: child.pid, serverUp: false, clientUp: false, logTail: readLogTail(APP_LOG) };
|
|
127
|
+
}
|
|
69
128
|
|
|
70
|
-
|
|
129
|
+
const clientUp = await waitUntil(appClientReachable, { timeoutMs: 120_000 });
|
|
130
|
+
child.unref();
|
|
131
|
+
return { started: true, pid: child.pid, serverUp: true, clientUp, logTail: clientUp ? '' : readLogTail(APP_LOG) };
|
|
71
132
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import test from 'node:test';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
import { BIN_DIR } from './lib/config.mjs';
|
|
8
|
+
import { piecemakerExecutable } from './install-command.mjs';
|
|
9
|
+
|
|
10
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
test('the curl bootstrap installs the piecemaker command before launching', () => {
|
|
13
|
+
const posix = fs.readFileSync(path.join(here, 'piecemaker.sh'), 'utf8');
|
|
14
|
+
const windows = fs.readFileSync(path.join(here, 'piecemaker.ps1'), 'utf8');
|
|
15
|
+
assert.match(posix, /install-command\.mjs/);
|
|
16
|
+
assert.match(windows, /install-command\.mjs/);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('the desktop launcher binds to the installed piecemaker binary', () => {
|
|
20
|
+
const executable = piecemakerExecutable();
|
|
21
|
+
assert.equal(path.basename(executable).startsWith('piecemaker'), true);
|
|
22
|
+
if (executable !== 'piecemaker') {
|
|
23
|
+
assert.equal(executable.startsWith(BIN_DIR) || executable.includes(`${path.sep}node${path.sep}` ) || executable.includes('versions/node'), true);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
@@ -1,50 +1,36 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { installPiecemakerCommand, piecemakerExecutable } from './install-command.mjs';
|
|
3
4
|
import { APP, APP_URL, PORTS } from './lib/config.mjs';
|
|
5
|
+
import { repairClientProxies } from './lib/piecemaker-anonymizer.mjs';
|
|
4
6
|
import { installComponents } from './lib/composants.mjs';
|
|
5
7
|
import { installPlugins } from './lib/plugins.mjs';
|
|
6
8
|
import { gitAvailable, resolveNodeRuntime } from './lib/node-runtime.mjs';
|
|
7
|
-
import { freePort } from './lib/ports.mjs';
|
|
8
9
|
import { ensureDependencies, ensureRepository, rebuildNativeModules } from './lib/repos.mjs';
|
|
9
|
-
import { APP_LOG, appClientReachable, appServerReachable, startApplication } from './lib/services.mjs';
|
|
10
|
-
import { installApplicationEntry, openApplication, verifyPwaAssets } from './lib/pwa.mjs';
|
|
10
|
+
import { APP_LOG, appClientReachable, appServerReachable, startApplication, stopApplication } from './lib/services.mjs';
|
|
11
|
+
import { applicationWindowIsOpen, installApplicationEntry, openApplication, verifyPwaAssets } from './lib/pwa.mjs';
|
|
12
|
+
import { runApplicationWindow } from './lib/app-window.mjs';
|
|
11
13
|
import { banner, blank, c, detail, fail, ok, step, warn } from './lib/ui.mjs';
|
|
12
14
|
|
|
13
15
|
const report = { step, ok, warn, detail };
|
|
14
16
|
|
|
15
|
-
const HELP = `${c.bold('piecemaker')} — installe, met à jour et lance toute la plateforme.
|
|
17
|
+
const HELP = `${c.bold('piecemaker')} — installe, met à jour, répare et lance toute la plateforme.
|
|
16
18
|
|
|
17
|
-
piecemaker
|
|
18
|
-
piecemaker --launch-only
|
|
19
|
-
piecemaker --
|
|
19
|
+
piecemaker reset propre, puis installation et lancement
|
|
20
|
+
piecemaker --launch-only répare et relance seulement si l'application est arrêtée
|
|
21
|
+
piecemaker --window relance si besoin, ouvre la fenêtre et arrête tout à sa fermeture
|
|
20
22
|
piecemaker --help cette aide
|
|
21
23
|
`;
|
|
22
24
|
|
|
23
25
|
function parseArguments(argv) {
|
|
26
|
+
const window = argv.includes('--window');
|
|
24
27
|
return {
|
|
25
28
|
help: argv.includes('--help') || argv.includes('-h'),
|
|
26
|
-
launchOnly: argv.includes('--launch-only'),
|
|
27
|
-
|
|
29
|
+
launchOnly: argv.includes('--launch-only') || window,
|
|
30
|
+
window,
|
|
28
31
|
};
|
|
29
32
|
}
|
|
30
33
|
|
|
31
|
-
async function cleanPorts(launchOnly) {
|
|
32
|
-
step('Libération des ports');
|
|
33
|
-
const targets = launchOnly
|
|
34
|
-
? [PORTS.appServer, PORTS.appClient]
|
|
35
|
-
: [PORTS.appClient, PORTS.appServer];
|
|
36
|
-
|
|
37
|
-
for (const port of targets) {
|
|
38
|
-
const result = await freePort(port);
|
|
39
|
-
if (!result.released) {
|
|
40
|
-
warn(`port ${port} toujours occupé`);
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
if (result.killed.length > 0) detail(`port ${port} libéré (${result.killed.join(', ')})`);
|
|
44
|
-
}
|
|
45
|
-
ok('Ports disponibles');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
34
|
async function synchroniseRepositories(runtime) {
|
|
49
35
|
if (!gitAvailable()) {
|
|
50
36
|
throw new Error('git est requis et introuvable.');
|
|
@@ -63,32 +49,43 @@ async function synchroniseRepositories(runtime) {
|
|
|
63
49
|
rebuildNativeModules(APP, runtime, report, ['better-sqlite3', 'node-pty']);
|
|
64
50
|
}
|
|
65
51
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
52
|
+
function printLogTail(logTail) {
|
|
53
|
+
if (!logTail) {
|
|
54
|
+
detail(`journal : ${APP_LOG}`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
detail(`journal : ${APP_LOG}`);
|
|
58
|
+
for (const line of logTail.split('\n')) {
|
|
59
|
+
if (line.trim()) detail(line);
|
|
73
60
|
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function applicationIsRunning() {
|
|
64
|
+
return await appServerReachable() && await appClientReachable();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function resetAndLaunch(runtime) {
|
|
68
|
+
step('Reset de l application');
|
|
69
|
+
const stopped = await stopApplication();
|
|
70
|
+
if (stopped.killed.length) detail(`arrêtés : ${stopped.killed.join(', ')}`);
|
|
71
|
+
for (const port of stopped.occupied) warn(`port ${port} toujours occupé`);
|
|
72
|
+
ok('Processus précédents arrêtés');
|
|
74
73
|
|
|
75
74
|
const application = await startApplication(runtime, report);
|
|
76
75
|
if (application.alreadyRunning) {
|
|
77
76
|
ok(`Application déjà active — ${APP_URL}`);
|
|
78
77
|
return true;
|
|
79
78
|
}
|
|
80
|
-
|
|
81
79
|
if (!application.serverUp) {
|
|
82
80
|
fail(`Serveur applicatif injoignable sur le port ${PORTS.appServer}`);
|
|
83
|
-
|
|
81
|
+
printLogTail(application.logTail);
|
|
84
82
|
return false;
|
|
85
83
|
}
|
|
86
84
|
if (!application.clientUp) {
|
|
87
85
|
fail(`Client applicatif injoignable sur le port ${PORTS.appClient}`);
|
|
88
|
-
|
|
86
|
+
printLogTail(application.logTail);
|
|
89
87
|
return false;
|
|
90
88
|
}
|
|
91
|
-
|
|
92
89
|
ok(`Application active — serveur :${PORTS.appServer}, client ${APP_URL}`);
|
|
93
90
|
return true;
|
|
94
91
|
}
|
|
@@ -111,6 +108,7 @@ async function installPwa() {
|
|
|
111
108
|
for (const location of entry.locations || []) {
|
|
112
109
|
if (location !== entry.location) detail(`également posé : ${location}`);
|
|
113
110
|
}
|
|
111
|
+
detail(`icône liée à ${piecemakerExecutable()}`);
|
|
114
112
|
if (!entry.standalone) detail('navigateur Chromium absent : ouverture en onglet classique');
|
|
115
113
|
if (!entry.verified) warn('installation incomplète : un emplacement attendu est introuvable après coup');
|
|
116
114
|
}
|
|
@@ -127,7 +125,19 @@ async function main() {
|
|
|
127
125
|
const runtime = resolveNodeRuntime();
|
|
128
126
|
detail(`Node ${runtime.version}`);
|
|
129
127
|
|
|
130
|
-
|
|
128
|
+
step('Commande piecemaker');
|
|
129
|
+
const command = installPiecemakerCommand();
|
|
130
|
+
if (command.shims.length) ok(`installée : ${command.executable}`);
|
|
131
|
+
else warn('commande absente du PATH — relancez node scripts/piecemaker/cli/install-command.mjs');
|
|
132
|
+
|
|
133
|
+
await repairClientProxies({ appDir: APP.directory, report });
|
|
134
|
+
|
|
135
|
+
if (!options.launchOnly || !(await applicationIsRunning())) {
|
|
136
|
+
const running = await resetAndLaunch(runtime);
|
|
137
|
+
if (!running) return 1;
|
|
138
|
+
} else {
|
|
139
|
+
ok(`Application déjà active — ${APP_URL}`);
|
|
140
|
+
}
|
|
131
141
|
|
|
132
142
|
if (!options.launchOnly) {
|
|
133
143
|
await synchroniseRepositories(runtime);
|
|
@@ -135,19 +145,24 @@ async function main() {
|
|
|
135
145
|
await installPlugins(runtime, report);
|
|
136
146
|
}
|
|
137
147
|
|
|
138
|
-
|
|
139
|
-
if (!running) return 1;
|
|
140
|
-
|
|
141
|
-
if (!options.launchOnly) {
|
|
142
|
-
await installPwa();
|
|
143
|
-
}
|
|
148
|
+
await installPwa();
|
|
144
149
|
|
|
145
150
|
blank();
|
|
146
151
|
ok(`Application ${c.bold(APP_URL)}`);
|
|
147
152
|
detail('Configuration du socle : onglet Dossier › Configuration.');
|
|
148
153
|
blank();
|
|
149
154
|
|
|
150
|
-
if (options.
|
|
155
|
+
if (options.window) {
|
|
156
|
+
const session = await runApplicationWindow();
|
|
157
|
+
if (!session.supervised) {
|
|
158
|
+
warn(`Fenêtre non supervisée : ${session.reason}`);
|
|
159
|
+
return 0;
|
|
160
|
+
}
|
|
161
|
+
ok('Fenêtre fermée — application arrêtée');
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (!options.launchOnly && !applicationWindowIsOpen()) openApplication();
|
|
151
166
|
return 0;
|
|
152
167
|
}
|
|
153
168
|
|
|
@@ -59,5 +59,13 @@ if (-not (Test-Path $EntryPath)) {
|
|
|
59
59
|
exit 1
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
$Installer = Join-Path $AppDir 'scripts\piecemaker\cli\install-command.mjs'
|
|
63
|
+
if (Test-Path $Installer) {
|
|
64
|
+
& $NodeBin $Installer
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
$BinDir = Join-Path $HOME '.piecemaker\bin'
|
|
68
|
+
$env:PATH = "$(Split-Path $NodeBin -Parent);$BinDir;$env:PATH"
|
|
69
|
+
|
|
62
70
|
& $NodeBin $EntryPath @args
|
|
63
71
|
exit $LASTEXITCODE
|
|
@@ -54,7 +54,12 @@ if [ ! -f "$APP_DIR/$ENTRY" ]; then
|
|
|
54
54
|
exit 1
|
|
55
55
|
fi
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
INSTALLER="$APP_DIR/scripts/piecemaker/cli/install-command.mjs"
|
|
58
|
+
if [ -f "$INSTALLER" ]; then
|
|
59
|
+
"$NODE_BIN" "$INSTALLER" || true
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
PATH="$(dirname "$NODE_BIN"):${HOME}/.piecemaker/bin:${PATH:-}"
|
|
58
63
|
export PATH
|
|
59
64
|
|
|
60
65
|
exec "$NODE_BIN" "$APP_DIR/$ENTRY" "$@"
|
package/server/index.ts
CHANGED
|
@@ -30,7 +30,7 @@ import { commandsRoutes } from './modules/commands/index.js';
|
|
|
30
30
|
import { settingsRoutes } from './modules/settings/index.js';
|
|
31
31
|
import { createSystemModule } from './modules/system/index.js';
|
|
32
32
|
import { createAgentModule } from './modules/agent/index.js';
|
|
33
|
-
import { createPieceMakerRouter } from './piecemaker/index.js';
|
|
33
|
+
import { createPieceMakerRouter, stopOriginalsJobs } from './piecemaker/index.js';
|
|
34
34
|
import projectModuleRoutes from './modules/projects/projects.routes.js';
|
|
35
35
|
import notificationRoutes from './modules/notifications/notifications.routes.js';
|
|
36
36
|
import { userRoutes } from './modules/user/index.js';
|
|
@@ -378,29 +378,58 @@ async function startServer() {
|
|
|
378
378
|
});
|
|
379
379
|
});
|
|
380
380
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
381
|
+
let shutdownPromise: Promise<void> | null = null;
|
|
382
|
+
const shutdownRuntimeServices = () => {
|
|
383
|
+
if (shutdownPromise) return shutdownPromise;
|
|
384
|
+
shutdownPromise = (async () => {
|
|
385
|
+
// Stop accepting HTTP work first. Existing requests may finish while
|
|
386
|
+
// the process pipelines and background services are being drained.
|
|
387
|
+
const serverClosed = new Promise<void>((resolve, reject) => {
|
|
388
|
+
server.close((error) => {
|
|
389
|
+
if (error && getErrorCode(error) !== 'ERR_SERVER_NOT_RUNNING') reject(error);
|
|
390
|
+
else resolve();
|
|
391
|
+
});
|
|
392
|
+
}).catch((err) => {
|
|
393
|
+
console.error('[Server] Error closing HTTP server during shutdown:', getErrorMessage(err));
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
try {
|
|
397
|
+
await closeSessionsWatcher();
|
|
398
|
+
} catch (err) {
|
|
399
|
+
console.error('[Sessions] Error stopping watchers during shutdown:', getErrorMessage(err));
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
closeScheduledMessageDispatcher();
|
|
403
|
+
} catch (err) {
|
|
404
|
+
console.error('[Scheduled messages] Error stopping dispatcher during shutdown:', getErrorMessage(err));
|
|
405
|
+
}
|
|
406
|
+
try {
|
|
407
|
+
await stopOriginalsJobs();
|
|
408
|
+
} catch (err) {
|
|
409
|
+
console.error('[PieceMaker] Error stopping pipeline jobs during shutdown:', getErrorMessage(err));
|
|
410
|
+
}
|
|
411
|
+
try {
|
|
412
|
+
await browserUseService.stopAllSessions();
|
|
413
|
+
} catch (err) {
|
|
414
|
+
console.error('[Browser] Error stopping sessions during shutdown:', getErrorMessage(err));
|
|
415
|
+
}
|
|
416
|
+
try {
|
|
417
|
+
await stopAllPlugins();
|
|
418
|
+
} catch (err) {
|
|
419
|
+
console.error('[Plugins] Error stopping plugins during shutdown:', getErrorMessage(err));
|
|
420
|
+
}
|
|
421
|
+
await serverClosed;
|
|
422
|
+
try {
|
|
423
|
+
await removeLocalServerMarker();
|
|
424
|
+
} catch (err) {
|
|
425
|
+
console.error('[Local Server] Error removing server marker during shutdown:', getErrorMessage(err));
|
|
426
|
+
}
|
|
427
|
+
process.exit(0);
|
|
428
|
+
})();
|
|
429
|
+
return shutdownPromise;
|
|
401
430
|
};
|
|
402
|
-
process.on('SIGTERM', () => void shutdownRuntimeServices());
|
|
403
|
-
process.on('SIGINT', () => void shutdownRuntimeServices());
|
|
431
|
+
process.on('SIGTERM', () => { void shutdownRuntimeServices(); });
|
|
432
|
+
process.on('SIGINT', () => { void shutdownRuntimeServices(); });
|
|
404
433
|
} catch (error) {
|
|
405
434
|
console.error('[ERROR] Failed to start server:', error);
|
|
406
435
|
process.exit(1);
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
type ShortcutOutput = {
|
|
7
|
+
log(message: string): void;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const SHORTCUT_NAME = 'PieceMaker';
|
|
11
|
+
|
|
12
|
+
function desktopDirectory(): string {
|
|
13
|
+
return process.env.PIECEMAKER_DESKTOP_DIR || path.join(os.homedir(), 'Desktop');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function launchCommand(): { executable: string; argument: string } {
|
|
17
|
+
return { executable: process.execPath, argument: process.argv[1] || '' };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function createMacShortcut(desktop: string): string | null {
|
|
21
|
+
const { executable, argument } = launchCommand();
|
|
22
|
+
if (!argument) return null;
|
|
23
|
+
const bundle = path.join(desktop, `${SHORTCUT_NAME}.app`);
|
|
24
|
+
if (fs.existsSync(bundle)) return null;
|
|
25
|
+
|
|
26
|
+
const macOsDirectory = path.join(bundle, 'Contents', 'MacOS');
|
|
27
|
+
fs.mkdirSync(macOsDirectory, { recursive: true });
|
|
28
|
+
fs.writeFileSync(
|
|
29
|
+
path.join(bundle, 'Contents', 'Info.plist'),
|
|
30
|
+
`<?xml version="1.0" encoding="UTF-8"?>
|
|
31
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
32
|
+
<plist version="1.0">
|
|
33
|
+
<dict>
|
|
34
|
+
<key>CFBundleName</key><string>${SHORTCUT_NAME}</string>
|
|
35
|
+
<key>CFBundleIdentifier</key><string>legal.piecemaker.launcher</string>
|
|
36
|
+
<key>CFBundleExecutable</key><string>${SHORTCUT_NAME}</string>
|
|
37
|
+
<key>CFBundlePackageType</key><string>APPL</string>
|
|
38
|
+
<key>LSUIElement</key><true/>
|
|
39
|
+
</dict>
|
|
40
|
+
</plist>
|
|
41
|
+
`,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const launcher = path.join(macOsDirectory, SHORTCUT_NAME);
|
|
45
|
+
fs.writeFileSync(launcher, `#!/bin/sh\nexec ${JSON.stringify(executable)} ${JSON.stringify(argument)} start\n`);
|
|
46
|
+
fs.chmodSync(launcher, 0o755);
|
|
47
|
+
return bundle;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function createWindowsShortcut(desktop: string): string | null {
|
|
51
|
+
const { executable, argument } = launchCommand();
|
|
52
|
+
if (!argument) return null;
|
|
53
|
+
const shortcut = path.join(desktop, `${SHORTCUT_NAME}.lnk`);
|
|
54
|
+
if (fs.existsSync(shortcut)) return null;
|
|
55
|
+
|
|
56
|
+
const script = [
|
|
57
|
+
'$shell = New-Object -ComObject WScript.Shell',
|
|
58
|
+
`$shortcut = $shell.CreateShortcut(${JSON.stringify(shortcut)})`,
|
|
59
|
+
`$shortcut.TargetPath = ${JSON.stringify(executable)}`,
|
|
60
|
+
`$shortcut.Arguments = ${JSON.stringify(`${JSON.stringify(argument)} start`)}`,
|
|
61
|
+
`$shortcut.WorkingDirectory = ${JSON.stringify(path.dirname(executable))}`,
|
|
62
|
+
'$shortcut.WindowStyle = 7',
|
|
63
|
+
`$shortcut.Description = ${JSON.stringify('Ouvre PieceMaker')}`,
|
|
64
|
+
'$shortcut.Save()',
|
|
65
|
+
].join('; ');
|
|
66
|
+
execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', script], { stdio: 'ignore' });
|
|
67
|
+
return shortcut;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Creates the PieceMaker desktop icon once, right after a fresh install, so the
|
|
72
|
+
* application can be reopened without a terminal. Existing shortcuts are left
|
|
73
|
+
* untouched and any failure is reported without interrupting the launch.
|
|
74
|
+
*/
|
|
75
|
+
export function ensureDesktopShortcut(output: ShortcutOutput): void {
|
|
76
|
+
if (process.platform !== 'darwin' && process.platform !== 'win32') return;
|
|
77
|
+
|
|
78
|
+
const desktop = desktopDirectory();
|
|
79
|
+
if (!fs.existsSync(desktop)) return;
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const created = process.platform === 'darwin'
|
|
83
|
+
? createMacShortcut(desktop)
|
|
84
|
+
: createWindowsShortcut(desktop);
|
|
85
|
+
if (created) output.log(`[OK] PieceMaker desktop icon created at ${created}`);
|
|
86
|
+
} catch {
|
|
87
|
+
output.log('[WARN] Could not create the PieceMaker desktop icon.');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -4,6 +4,8 @@ import fs from 'node:fs';
|
|
|
4
4
|
import os from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
|
|
7
|
+
import { ensureDesktopShortcut } from './desktop-shortcut.js';
|
|
8
|
+
|
|
7
9
|
type PwaOutput = {
|
|
8
10
|
log(message: string): void;
|
|
9
11
|
};
|
|
@@ -102,7 +104,22 @@ async function waitForServer(url: string): Promise<boolean> {
|
|
|
102
104
|
return false;
|
|
103
105
|
}
|
|
104
106
|
|
|
105
|
-
|
|
107
|
+
const SHUTDOWN_GRACE_MS = 10_000;
|
|
108
|
+
const DELEGATED_LAUNCH_MS = 3_000;
|
|
109
|
+
|
|
110
|
+
function shutdownAfterWindowClosed(output: PwaOutput): void {
|
|
111
|
+
output.log('[OK] PieceMaker window closed; stopping the local server.');
|
|
112
|
+
const forced = setTimeout(() => process.exit(0), SHUTDOWN_GRACE_MS);
|
|
113
|
+
forced.unref();
|
|
114
|
+
if (process.listenerCount('SIGTERM') > 0) process.emit('SIGTERM', 'SIGTERM');
|
|
115
|
+
else process.exit(0);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Starts PieceMaker in an isolated Chromium app window after its backend is
|
|
120
|
+
* ready, and ties the server's lifetime to that window: closing it shuts the
|
|
121
|
+
* local server down through the same graceful path as SIGTERM.
|
|
122
|
+
*/
|
|
106
123
|
export async function launchPwa(output: PwaOutput = DEFAULT_OUTPUT): Promise<void> {
|
|
107
124
|
const url = pwaUrl();
|
|
108
125
|
if (!(await waitForServer(url))) {
|
|
@@ -116,16 +133,24 @@ export async function launchPwa(output: PwaOutput = DEFAULT_OUTPUT): Promise<voi
|
|
|
116
133
|
return;
|
|
117
134
|
}
|
|
118
135
|
|
|
136
|
+
ensureDesktopShortcut(output);
|
|
137
|
+
|
|
119
138
|
const profileDirectory = pwaProfileDirectory();
|
|
120
139
|
fs.mkdirSync(profileDirectory, { recursive: true });
|
|
140
|
+
const openedAt = Date.now();
|
|
121
141
|
const child = spawn(browser, [`--app=${url}`, `--user-data-dir=${profileDirectory}`], {
|
|
122
|
-
detached: true,
|
|
123
142
|
stdio: 'ignore',
|
|
124
143
|
windowsHide: true,
|
|
125
144
|
});
|
|
126
145
|
child.on('error', () => {
|
|
127
146
|
output.log(`[WARN] Could not open the PieceMaker PWA; open ${url} manually.`);
|
|
128
147
|
});
|
|
129
|
-
child.
|
|
130
|
-
|
|
148
|
+
child.on('exit', () => {
|
|
149
|
+
if (Date.now() - openedAt < DELEGATED_LAUNCH_MS) {
|
|
150
|
+
output.log(`[WARN] The PieceMaker window is owned by another browser process; the server keeps running.`);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
shutdownAfterWindowClosed(output);
|
|
154
|
+
});
|
|
155
|
+
output.log(`[OK] PieceMaker PWA opened at ${url} — closing the window stops the server.`);
|
|
131
156
|
}
|
|
@@ -15,6 +15,22 @@ function normalizeProjectDisplayName(projectPath: string, customProjectName: str
|
|
|
15
15
|
return directoryName || projectPath;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function ensureAnonymizationStatusTable(db: ReturnType<typeof getConnection>): void {
|
|
19
|
+
db.exec(`
|
|
20
|
+
CREATE TABLE IF NOT EXISTS piecemaker_anonymization_status (
|
|
21
|
+
project_id TEXT PRIMARY KEY NOT NULL,
|
|
22
|
+
completed_at TEXT NOT NULL,
|
|
23
|
+
FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE
|
|
24
|
+
)
|
|
25
|
+
`);
|
|
26
|
+
const mappingsExist = db.prepare(`SELECT 1 AS ok FROM sqlite_master WHERE type='table' AND name='piecemaker_mappings'`).get() as { ok: number } | undefined;
|
|
27
|
+
if (!mappingsExist) return;
|
|
28
|
+
db.prepare(`
|
|
29
|
+
INSERT OR IGNORE INTO piecemaker_anonymization_status(project_id, completed_at)
|
|
30
|
+
SELECT DISTINCT project_id, ? FROM piecemaker_mappings
|
|
31
|
+
`).run(new Date().toISOString());
|
|
32
|
+
}
|
|
33
|
+
|
|
18
34
|
export const projectsDb = {
|
|
19
35
|
createProjectPath(projectPath: string, customProjectName: string | null = null): CreateProjectPathResult {
|
|
20
36
|
const db = getConnection();
|
|
@@ -88,10 +104,13 @@ export const projectsDb = {
|
|
|
88
104
|
|
|
89
105
|
getProjectPaths(): ProjectRepositoryRow[] {
|
|
90
106
|
const db = getConnection();
|
|
107
|
+
ensureAnonymizationStatusTable(db);
|
|
91
108
|
return db.prepare(`
|
|
92
|
-
SELECT project_id, project_path, custom_project_name, isStarred, isArchived
|
|
93
|
-
|
|
94
|
-
|
|
109
|
+
SELECT p.project_id, p.project_path, p.custom_project_name, p.isStarred, p.isArchived,
|
|
110
|
+
CASE WHEN s.project_id IS NULL THEN 0 ELSE 1 END AS anonymization_complete
|
|
111
|
+
FROM projects p
|
|
112
|
+
LEFT JOIN piecemaker_anonymization_status s ON s.project_id = p.project_id
|
|
113
|
+
WHERE p.isArchived = 0
|
|
95
114
|
`).all() as ProjectRepositoryRow[];
|
|
96
115
|
},
|
|
97
116
|
|
|
@@ -101,10 +120,13 @@ export const projectsDb = {
|
|
|
101
120
|
*/
|
|
102
121
|
getArchivedProjectPaths(): ProjectRepositoryRow[] {
|
|
103
122
|
const db = getConnection();
|
|
123
|
+
ensureAnonymizationStatusTable(db);
|
|
104
124
|
return db.prepare(`
|
|
105
|
-
SELECT project_id, project_path, custom_project_name, isStarred, isArchived
|
|
106
|
-
|
|
107
|
-
|
|
125
|
+
SELECT p.project_id, p.project_path, p.custom_project_name, p.isStarred, p.isArchived,
|
|
126
|
+
CASE WHEN s.project_id IS NULL THEN 0 ELSE 1 END AS anonymization_complete
|
|
127
|
+
FROM projects p
|
|
128
|
+
LEFT JOIN piecemaker_anonymization_status s ON s.project_id = p.project_id
|
|
129
|
+
WHERE p.isArchived = 1
|
|
108
130
|
`).all() as ProjectRepositoryRow[];
|
|
109
131
|
},
|
|
110
132
|
|