@piecemaker-legal/piecemaker 2.0.10 → 2.0.12
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/{FindReplaceDialog-KU5TEIBJ-CQ76sPaJ.js → FindReplaceDialog-KU5TEIBJ-BBxf1he7.js} +1 -1
- package/dist/assets/{FootnotePropertiesDialog-W3YHM4BV-BpgYQvvd.js → FootnotePropertiesDialog-W3YHM4BV-BYqp6U4x.js} +1 -1
- package/dist/assets/{HyperlinkDialog-ZYH7ZHRF-z08UbYbL.js → HyperlinkDialog-ZYH7ZHRF-BVC9zOMs.js} +4 -4
- package/dist/assets/{ImagePositionDialog-HEWYR6Q3-CFzOnzAt.js → ImagePositionDialog-HEWYR6Q3-Xn2mC64y.js} +1 -1
- package/dist/assets/{ImagePropertiesDialog-RAB47C6M-D-ThGU-b.js → ImagePropertiesDialog-RAB47C6M-CFcdqEif.js} +1 -1
- package/dist/assets/{PageSetupDialog-AIGIXO3E-_PYIO8eb.js → PageSetupDialog-AIGIXO3E-DECaeRS9.js} +1 -1
- package/dist/assets/{SplitCellDialog-IFBKYCD6-Cb9uQOB8.js → SplitCellDialog-IFBKYCD6-ByYa04H_.js} +1 -1
- package/dist/assets/{TablePropertiesDialog-TMLKGOOW-12vmjb6B.js → TablePropertiesDialog-TMLKGOOW-BuyqZB5A.js} +1 -1
- package/dist/assets/{WatermarkDialog-KRL7WMIS-C-d8rzt2.js → WatermarkDialog-KRL7WMIS-ByZ1Acr2.js} +1 -1
- package/dist/assets/{abnfDiagram-VCTEODGH-B4OsX7eF.js → abnfDiagram-VCTEODGH-BtttzdZA.js} +1 -1
- package/dist/assets/{arc-BK1oUIk7.js → arc-DN4m1tLs.js} +1 -1
- package/dist/assets/{architectureDiagram-5GKGNRK7-CfaFI6L7.js → architectureDiagram-5GKGNRK7-Bt-zFumG.js} +1 -1
- package/dist/assets/{blockDiagram-I7D4REHJ-DQEI2azY.js → blockDiagram-I7D4REHJ-D14V8v4j.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-DbravpVi.js → c4Diagram-7LVT6UL2-BynDo1-W.js} +1 -1
- package/dist/assets/channel-C4gJXzYa.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-BLpZ9huC.js → chunk-2Q5K7J3B-B8gKhSj9.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-BLlEdmX9.js → chunk-5VM5RSS4-GvOpglRR.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-D-ZNhI6q.js → chunk-F27PBJKO-CAgZy0qc.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-vzSiKnhh.js → chunk-IMKFNOWR-gf8Ux8Zd.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-DfF0k4nb.js → chunk-JWPE2WC7-D4zFFRMp.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-BnwCYsPP.js → chunk-POPQ4Y6H-Ct8X_LaT.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-DRxc8fhq.js → chunk-SVP7TREG-g5iH1bs5.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-Dd2Q-md-.js → chunk-TICWLB2K-89-VFKTn.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-DBjkEUp_.js → chunk-XXDRQBXY-CaQbdObb.js} +1 -1
- package/dist/assets/classDiagram-ZZMXUADV-Cm08FeYk.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-Cm08FeYk.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-By6PS4oe.js → cose-bilkent-JH36ORCC-BiYOGJAU.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-YxWpjc5W.js → cynefin-OW5HDTMX-F6mJ-4tj.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-DrtXzz3p.js → cynefinDiagram-5FMLGOSQ-q-Rggn6r.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-BDpTLKqZ.js → dagre-GXQ25YYZ-COqImSUH.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-C4LvqeCs.js → diagram-S7CK7UJ4-BAg1dyEc.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-BTpnMeHp.js → diagram-UQ7AKVKN-DFJS8qCV.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV-DucHHWev.js → diagram-VSXAHHWV-BfldL35r.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-DYHZNmmH.js → diagram-VX7I27RA-kLjovpYe.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-BHIH2BGN.js → diagram-Z3DM3KII-Dhwvi3Iu.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-CGiEkwj0.js → ebnfDiagram-PWID7BFC-Bp4YE1i7.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-AJMHi2Us.js → erDiagram-RLTQ6QDP-BKDdaSWz.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-BqzANBh2.js → flowDiagram-HODETNUW-zHProVxL.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-B0xPDY2_.js → ganttDiagram-EL5Y4UJY-B-B-BI6j.js} +1 -1
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-CzPX5CNO.js → gitGraphDiagram-WWUBYQGX-BTHMg5CH.js} +1 -1
- package/dist/assets/index-C5slQNvE.css +1 -0
- package/dist/assets/{index-Bk2iXKnJ.js → index-CJP4dZ-c.js} +146 -146
- package/dist/assets/{infoDiagram-27XIBGKW-Cjc44H0O.js → infoDiagram-27XIBGKW-mabp4H9I.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-BWhr2v7r.js → ishikawaDiagram-5VMMS53U-CLNhcCrM.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-Bl4r6RrP.js → journeyDiagram-3NMN7TZE-CGEi2RB6.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-FV-T0Vy1.js → kanban-definition-UXKFOSKX-B2fLX1fK.js} +1 -1
- package/dist/assets/{layout-CTfb1sAF.js → layout-CloKWsFq.js} +1 -1
- package/dist/assets/{linear-Dfgi6vlo.js → linear-BFyydI0c.js} +1 -1
- package/dist/assets/{mermaid.core-DOCGsdlo.js → mermaid.core-Dxl3LVAh.js} +6 -6
- package/dist/assets/{mindmap-definition-YA3MSWOX-CymQ86ir.js → mindmap-definition-YA3MSWOX-FT4pm55s.js} +1 -1
- package/dist/assets/{pegDiagram-XKGWAZYB-1VMq_vi0.js → pegDiagram-XKGWAZYB-okyIfJWM.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-C5GgXtJU.js → pieDiagram-E7YTZNPT-NF61CQz8.js} +1 -1
- package/dist/assets/{quadrantDiagram-AXDQQJYC-C6zpryYf.js → quadrantDiagram-AXDQQJYC-uVfvZzzt.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-D-sv4SWi.js → railroadDiagram-O6MQD6OU-Dp8IpeoH.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-CkjY-z4B.js → requirementDiagram-BXWQKSXE-D8VV0oQe.js} +1 -1
- package/dist/assets/{sankeyDiagram-P5KCCOFB-De-d8Q6L.js → sankeyDiagram-P5KCCOFB-D-xhxjob.js} +1 -1
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-DCsGSJa9.js → sequenceDiagram-WJ2MYXX4-BAQBK95h.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-xINZhgER.js → sizeCapture-INFHLROL-C6rk9SVx.js} +1 -1
- package/dist/assets/{stateDiagram-D77RDMKH-CuLUTWSL.js → stateDiagram-D77RDMKH-DshqaEOj.js} +1 -1
- package/dist/assets/{stateDiagram-v2-MP3YSRHH-Cnbs-M92.js → stateDiagram-v2-MP3YSRHH-BohZIrk5.js} +1 -1
- package/dist/assets/{swimlanes-42K2YHIH-BwxARD7N.js → swimlanes-42K2YHIH-DgcPDy4l.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-CuM3Su0K.js +8 -0
- package/dist/assets/{timeline-definition-24CTP7MA-BIOvnn-l.js → timeline-definition-24CTP7MA-OgpIe-gQ.js} +1 -1
- package/dist/assets/{vennDiagram-4TSXK5OY-BSD6Pfmt.js → vennDiagram-4TSXK5OY-BZzGd6jt.js} +1 -1
- package/dist/assets/{wardleyDiagram-VM6X3IG4-7Zz2iRkF.js → wardleyDiagram-VM6X3IG4-1tg-H7l_.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-Ctx17Y1D.js → xychartDiagram-S5SC5T6Z-ByTsX8g0.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/sw.js +1 -1
- package/dist-server/plugins/piecemaker-dossier/src/knowledge.js +61 -1
- package/dist-server/plugins/piecemaker-dossier/src/knowledge.js.map +1 -1
- package/dist-server/plugins/piecemaker-dossier/src/scan-result.js +0 -48
- package/dist-server/plugins/piecemaker-dossier/src/scan-result.js.map +1 -1
- package/dist-server/server/piecemaker/agent-instructions.js +148 -0
- package/dist-server/server/piecemaker/agent-instructions.js.map +1 -0
- package/dist-server/server/piecemaker/index.js +4 -0
- package/dist-server/server/piecemaker/index.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/pipeline.js +25 -29
- package/dist-server/server/piecemaker/knowledge/pipeline.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/service.js +1 -16
- package/dist-server/server/piecemaker/knowledge/service.js.map +1 -1
- package/dist-server/server/piecemaker/project-registry.js +5 -5
- package/dist-server/server/piecemaker/project-registry.js.map +1 -1
- package/package.json +1 -1
- package/scripts/piecemaker/cli/lib/plugins.mjs +1 -0
- package/server/piecemaker/agent-instructions.ts +148 -0
- package/server/piecemaker/anonymizer/dictionary.cjs +34 -3
- package/server/piecemaker/anonymizer/hudsucker-fixture.cjs +1 -1
- package/server/piecemaker/anonymizer/hudsucker-proxy/src/main.rs +2 -1
- package/server/piecemaker/anonymizer/hudsucker-smoke.cjs +343 -0
- package/server/piecemaker/anonymizer/rewrite.cjs +2 -2
- package/server/piecemaker/anonymizer/rewriter-bridge.cjs +7 -4
- package/server/piecemaker/anonymizer/rewriter-worker.cjs +85 -0
- package/server/piecemaker/anonymizer/service.cjs +10 -8
- package/server/piecemaker/anonymizer/sqlite-dictionary.cjs +15 -32
- package/server/piecemaker/index.ts +5 -1
- package/server/piecemaker/knowledge/pipeline.ts +24 -31
- package/server/piecemaker/knowledge/service.ts +1 -14
- package/server/piecemaker/project-registry.ts +6 -6
- package/server/piecemaker/router.cjs +0 -1
- package/server/piecemaker/vendor/installer/bin/piecemaker.mjs +9 -162
- package/server/piecemaker/vendor/installer/lib/commandes.mjs +1 -2
- package/server/piecemaker/vendor/installer/lib/conversion-client.mjs +62 -3
- package/server/piecemaker/vendor/installer/steps/02-dependances-node.mjs +5 -5
- package/server/piecemaker/vendor/installer/steps/06-hooks.mjs +23 -69
- package/server/piecemaker/vendor/installer/steps/12-mcp-piecemaker.mjs +3 -3
- package/server/piecemaker/vendor/mcp/README.md +1 -1
- package/server/piecemaker/vendor/mcp/piecemaker/server.mjs +12 -75
- package/server/piecemaker/vendor/piecemaker-plugin/hooks/hooks.json +0 -40
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/compile-recherche.mjs +2 -2
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folder-structure.cjs +2 -90
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/case-folders.cjs +26 -7
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/commits.cjs +0 -28
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/hook-io.mjs +0 -13
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/institutional-terms.cjs +3 -3
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/mapping.cjs +10 -143
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/protection.cjs +1 -1
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/session-timing.cjs +0 -65
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/lib/substitution.cjs +189 -41
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/protect-originals.mjs +11 -2
- package/server/piecemaker/vendor/piecemaker-plugin/skills/ajout-de-fonctionnalites/SKILL.md +4 -18
- package/server/piecemaker/vendor/piecemaker-plugin/skills/conversion-md/SKILL.md +9 -5
- package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +3 -179
- package/server/piecemaker/vendor/websocket-server/case-registry.cjs +0 -10
- package/server/piecemaker/vendor/websocket-server/claude-hooks.cjs +3 -0
- package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +23 -372
- package/server/piecemaker/vendor/websocket-server/renamed-originals.cjs +136 -0
- package/server/piecemaker/vendor/websocket-server/scripts/convert_and_scan_pipeline.py +115 -96
- package/dist/assets/channel-DVyNr2Yg.js +0 -1
- package/dist/assets/classDiagram-ZZMXUADV-2pGPHl5H.js +0 -1
- package/dist/assets/classDiagram-v2-VYDZK3BY-2pGPHl5H.js +0 -1
- package/dist/assets/index-Dc9wNuS4.css +0 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-D7p8qZI2.js +0 -8
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/billing-track.mjs +0 -194
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/commit-track.mjs +0 -71
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/session-commit-worker.mjs +0 -260
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/session-commit.mjs +0 -83
|
@@ -1,31 +1,10 @@
|
|
|
1
|
+
const fs = require('node:fs');
|
|
1
2
|
const path = require('node:path');
|
|
2
3
|
|
|
3
4
|
const Database = require('better-sqlite3');
|
|
4
5
|
|
|
5
6
|
const { buildDictionary, EMPTY_DICTIONARY } = require('./dictionary.cjs');
|
|
6
7
|
|
|
7
|
-
function codePrefix(code) {
|
|
8
|
-
const match = /^(.*?)_(\d+)$/.exec(code);
|
|
9
|
-
return match ? match[1] : code || 'ENTITE';
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function codeNumber(code) {
|
|
13
|
-
const match = /_(\d+)$/.exec(code);
|
|
14
|
-
return match ? Number(match[1]) : 0;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function allocateCode(localCode, counters, used) {
|
|
18
|
-
const prefix = codePrefix(localCode);
|
|
19
|
-
let index = counters.get(prefix) || 0;
|
|
20
|
-
let candidate;
|
|
21
|
-
do {
|
|
22
|
-
index += 1;
|
|
23
|
-
candidate = `${prefix}_${String(index).padStart(2, '0')}`;
|
|
24
|
-
} while (used.has(candidate));
|
|
25
|
-
counters.set(prefix, index);
|
|
26
|
-
return candidate;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
8
|
function buildMappingDocument(rows) {
|
|
30
9
|
const grouped = new Map();
|
|
31
10
|
let updatedAt = null;
|
|
@@ -37,20 +16,14 @@ function buildMappingDocument(rows) {
|
|
|
37
16
|
if (!updatedAt || row.updated_at > updatedAt) updatedAt = row.updated_at;
|
|
38
17
|
}
|
|
39
18
|
|
|
40
|
-
const used = new Set();
|
|
41
|
-
const counters = new Map();
|
|
42
19
|
const mapping = {};
|
|
43
20
|
const reverse_mapping = {};
|
|
44
21
|
for (const group of grouped.values()) {
|
|
45
|
-
const
|
|
46
|
-
const globalCode = used.has(desired) ? allocateCode(desired, counters, used) : desired;
|
|
47
|
-
used.add(globalCode);
|
|
48
|
-
const prefix = codePrefix(globalCode);
|
|
49
|
-
counters.set(prefix, Math.max(counters.get(prefix) || 0, codeNumber(globalCode)));
|
|
22
|
+
const code = group.localCode;
|
|
50
23
|
const canonical = group.variants.includes(group.label) ? group.label : group.variants[0];
|
|
51
24
|
const variants = [canonical, ...group.variants.filter((value) => value !== canonical)];
|
|
52
|
-
reverse_mapping[
|
|
53
|
-
for (const variant of variants) if (!mapping[variant]) mapping[variant] =
|
|
25
|
+
reverse_mapping[code] = variants;
|
|
26
|
+
for (const variant of variants) if (!mapping[variant]) mapping[variant] = code;
|
|
54
27
|
}
|
|
55
28
|
return { mapping, reverse_mapping, updated_at: updatedAt };
|
|
56
29
|
}
|
|
@@ -62,6 +35,7 @@ function createSqliteDictionaryLoader({ databasePath }) {
|
|
|
62
35
|
let mappingSignature = null;
|
|
63
36
|
let generation = 0;
|
|
64
37
|
let current = EMPTY_DICTIONARY;
|
|
38
|
+
let failure = null;
|
|
65
39
|
|
|
66
40
|
function connection() {
|
|
67
41
|
if (!database) {
|
|
@@ -97,15 +71,24 @@ function createSqliteDictionaryLoader({ databasePath }) {
|
|
|
97
71
|
generation += 1;
|
|
98
72
|
current = buildDictionary(buildMappingDocument(rows), generation);
|
|
99
73
|
dataVersion = nextDataVersion;
|
|
74
|
+
failure = null;
|
|
100
75
|
return current;
|
|
101
|
-
} catch {
|
|
76
|
+
} catch (error) {
|
|
77
|
+
failure = fs.existsSync(file) ? error : null;
|
|
102
78
|
return current;
|
|
103
79
|
}
|
|
104
80
|
}
|
|
105
81
|
|
|
82
|
+
function loadForOutbound() {
|
|
83
|
+
const dictionary = load(false);
|
|
84
|
+
if (failure) throw new Error(`dictionnaire illisible : ${failure.message}`);
|
|
85
|
+
return dictionary;
|
|
86
|
+
}
|
|
87
|
+
|
|
106
88
|
return {
|
|
107
89
|
file,
|
|
108
90
|
get: () => load(false),
|
|
91
|
+
getForOutbound: loadForOutbound,
|
|
109
92
|
refresh: () => load(true),
|
|
110
93
|
exists: () => !load(false).empty,
|
|
111
94
|
close: () => {
|
|
@@ -3,11 +3,13 @@ import path from 'path';
|
|
|
3
3
|
|
|
4
4
|
import type { Router } from 'express';
|
|
5
5
|
|
|
6
|
+
import { projectsDb } from '@/modules/database/index.js';
|
|
6
7
|
import { providerRuntimeService, sessionsService } from '@/modules/providers/index.js';
|
|
7
8
|
import { providerRegistry } from '@/modules/providers/provider.registry.js';
|
|
8
9
|
import { findApplicationRoot, getModuleDirectory } from '@/shared/utils.js';
|
|
9
10
|
|
|
10
11
|
|
|
12
|
+
import { createAgentInstructionsRouter, startAgentInstructionsMirror } from './agent-instructions.js';
|
|
11
13
|
import { startRequiredAnonymizer } from './anonymizer/lifecycle.js';
|
|
12
14
|
import { createCitationStore } from './harness/citation-store.js';
|
|
13
15
|
import { installChatCitationHarness } from './harness/chat-harness.js';
|
|
@@ -43,7 +45,7 @@ type PieceMakerRuntimeStatus = {
|
|
|
43
45
|
type PieceMakerVendorModule = {
|
|
44
46
|
createPieceMakerRouter(options?: { getRuntimeStatus?: () => PieceMakerRuntimeStatus; anonymizer?: unknown; publishProjects?: () => void }): Router;
|
|
45
47
|
piecemakerHome(): string;
|
|
46
|
-
publishProjectSource(sourceId: string, folders: string[] | null): void;
|
|
48
|
+
publishProjectSource(sourceId: string, folders: string[] | null, anonymizedFolders?: string[]): void;
|
|
47
49
|
stopOriginalsJobs(): Promise<void>;
|
|
48
50
|
};
|
|
49
51
|
|
|
@@ -62,6 +64,7 @@ installLibraryRuntime(providerRuntimeService, sessionsService, library.store);
|
|
|
62
64
|
installModelDiscovery(providerRegistry);
|
|
63
65
|
const timesheet = createTimesheetBackend(piecemakerHome());
|
|
64
66
|
const knowledge = createKnowledgeBackend(applicationRoot);
|
|
67
|
+
startAgentInstructionsMirror(() => projectsDb.getProjectPaths().map((row) => row.project_path));
|
|
65
68
|
|
|
66
69
|
export function createPieceMakerLocalRouter() {
|
|
67
70
|
return knowledge.localRouter;
|
|
@@ -78,6 +81,7 @@ export function createPieceMakerRouter(options: { getRuntimeStatus?: () => Piece
|
|
|
78
81
|
router.use(createCompanySearchRouter());
|
|
79
82
|
router.use(createBodaccSearchRouter());
|
|
80
83
|
router.use(createDocxDocumentRouter());
|
|
84
|
+
router.use(createAgentInstructionsRouter(path.join(piecemakerHome(), 'CLAUDE.md')));
|
|
81
85
|
router.use(createShellEnvironmentRouter(shellEnvironment));
|
|
82
86
|
return router;
|
|
83
87
|
}
|
|
@@ -5,6 +5,7 @@ import os from 'node:os';
|
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
|
|
7
7
|
import { findApplicationRoot, getModuleDirectory } from '../../shared/utils.js';
|
|
8
|
+
import { resolveKnowledgeDatabasePath } from '../../../plugins/piecemaker-dossier/src/knowledge.js';
|
|
8
9
|
import type { KnowledgeStore } from '../../../plugins/piecemaker-dossier/src/knowledge.js';
|
|
9
10
|
import { persistScanResult, scanResultOperations } from '../../../plugins/piecemaker-dossier/src/scan-result.js';
|
|
10
11
|
import type { GlinerDocument, GlinerMappingDocument, JsonData } from '../../../plugins/piecemaker-dossier/src/types.js';
|
|
@@ -25,7 +26,6 @@ type PipelineOptions = {
|
|
|
25
26
|
type OriginalFile = { path: string; resource?: boolean };
|
|
26
27
|
|
|
27
28
|
type OriginalsPipeline = {
|
|
28
|
-
caseMappingFile(caseRoot: string): string;
|
|
29
29
|
listOriginals(caseRoot: string): Promise<OriginalFile[]>;
|
|
30
30
|
runManagedPythonJob(options: {
|
|
31
31
|
action: 'convert' | 'anonymize';
|
|
@@ -33,16 +33,15 @@ type OriginalsPipeline = {
|
|
|
33
33
|
args: string[];
|
|
34
34
|
onProgress?: (progress: KnowledgeScanProgress) => void;
|
|
35
35
|
signal?: AbortSignal;
|
|
36
|
+
input?: string;
|
|
37
|
+
onMapping?: (mapping: unknown) => void;
|
|
36
38
|
}): Promise<unknown>;
|
|
37
|
-
writeCaseMapping(caseRoot: string, document: GlinerMappingDocument): unknown;
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
const require = createRequire(import.meta.url);
|
|
41
42
|
const {
|
|
42
|
-
caseMappingFile,
|
|
43
43
|
listOriginals,
|
|
44
44
|
runManagedPythonJob,
|
|
45
|
-
writeCaseMapping,
|
|
46
45
|
} = require(path.join(
|
|
47
46
|
findApplicationRoot(getModuleDirectory(import.meta.url)),
|
|
48
47
|
'server',
|
|
@@ -117,18 +116,7 @@ function pythonExecutable(explicit: string | undefined): string {
|
|
|
117
116
|
return fs.existsSync(candidate) ? candidate : 'python3';
|
|
118
117
|
}
|
|
119
118
|
|
|
120
|
-
|
|
121
|
-
const candidate = path.join(projectPath, 'Fichiers convertis PieceMaker', 'mapping_default.json');
|
|
122
|
-
if (!fs.existsSync(candidate)) return [];
|
|
123
|
-
try {
|
|
124
|
-
const document = parseObject(candidate);
|
|
125
|
-
return Array.isArray(document.ignored) ? document.ignored.map(textValue).filter(Boolean) : [];
|
|
126
|
-
} catch {
|
|
127
|
-
return [];
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function mappingSeed(projectId: string, projectPath: string, store: KnowledgeStore): GlinerMappingDocument {
|
|
119
|
+
function mappingSeed(projectId: string, store: KnowledgeStore): GlinerMappingDocument {
|
|
132
120
|
const snapshot = store.snapshot(projectId);
|
|
133
121
|
const mapping: Record<string, string> = {};
|
|
134
122
|
const reverseMapping: Record<string, string[]> = {};
|
|
@@ -145,7 +133,7 @@ function mappingSeed(projectId: string, projectPath: string, store: KnowledgeSto
|
|
|
145
133
|
extractedData[category] ||= {};
|
|
146
134
|
extractedData[category][code] = node.data;
|
|
147
135
|
}
|
|
148
|
-
const ignored = snapshot.
|
|
136
|
+
const ignored = snapshot.exclusions || [];
|
|
149
137
|
return { mapping, reverse_mapping: reverseMapping, extracted_data: extractedData, ignored };
|
|
150
138
|
}
|
|
151
139
|
|
|
@@ -174,34 +162,39 @@ export function createKnowledgePipeline(options: PipelineOptions) {
|
|
|
174
162
|
const explicitFiles = Array.isArray(requestedFiles) && requestedFiles.length > 0;
|
|
175
163
|
const files = await safeFiles(projectPath, requestedFiles);
|
|
176
164
|
if (!files.length) throw new Error('No supported file to scan.');
|
|
177
|
-
const mappingFile = caseMappingFile(projectPath);
|
|
178
165
|
const stateFile = path.join(projectPath, '.piecemaker', 'anonymization-state.json');
|
|
179
166
|
const outputDirectory = path.join(projectPath, 'Fichiers convertis PieceMaker');
|
|
180
|
-
fs.mkdirSync(path.dirname(mappingFile), { recursive: true });
|
|
181
167
|
fs.mkdirSync(path.dirname(stateFile), { recursive: true });
|
|
182
168
|
fs.mkdirSync(outputDirectory, { recursive: true });
|
|
183
|
-
|
|
169
|
+
const seed = mappingSeed(projectId, options.store);
|
|
184
170
|
const args = [
|
|
185
171
|
...files,
|
|
186
172
|
'-o',
|
|
187
173
|
outputDirectory,
|
|
188
|
-
'--
|
|
189
|
-
|
|
174
|
+
'--database',
|
|
175
|
+
resolveKnowledgeDatabasePath(),
|
|
190
176
|
'--case-root',
|
|
191
177
|
projectPath,
|
|
192
178
|
'--state-file',
|
|
193
179
|
stateFile,
|
|
194
180
|
];
|
|
195
181
|
if (!explicitFiles) args.push('--skip-existing');
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
182
|
+
let received: GlinerMappingDocument | null = null;
|
|
183
|
+
try {
|
|
184
|
+
await withPythonPath(pythonExecutable(options.pythonPath), () => runManagedPythonJob({
|
|
185
|
+
action: 'anonymize',
|
|
186
|
+
script,
|
|
187
|
+
args,
|
|
188
|
+
onProgress,
|
|
189
|
+
signal,
|
|
190
|
+
input: JSON.stringify(seed),
|
|
191
|
+
onMapping: (value) => { received = value as GlinerMappingDocument; },
|
|
192
|
+
}));
|
|
193
|
+
} catch (error) {
|
|
194
|
+
if (received) options.store.update({ projectId, operations: scanResultOperations({ projectId, mapping: received, documents: [] }) });
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
const mapping: GlinerMappingDocument = received || seed;
|
|
205
198
|
const documents = documentsFromIndex(projectPath, files, readDocumentIndex(projectPath));
|
|
206
199
|
const scanResult = { projectId, mapping, documents };
|
|
207
200
|
const result = explicitFiles
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import type { KnowledgeStore } from '../../../plugins/piecemaker-dossier/src/knowledge.js';
|
|
2
|
-
import { exclusionNodeOperation } from '../../../plugins/piecemaker-dossier/src/scan-result.js';
|
|
3
2
|
import type { KnowledgeQueryInput, KnowledgeUpdateInput } from '../../../plugins/piecemaker-dossier/src/types.js';
|
|
4
3
|
|
|
5
|
-
import { legacyExclusions } from './pipeline.js';
|
|
6
4
|
import type { createKnowledgePipeline } from './pipeline.js';
|
|
7
5
|
import { createKnowledgeScanJobs } from './scan-jobs.js';
|
|
8
6
|
|
|
@@ -22,16 +20,6 @@ export function createKnowledgeService(store: KnowledgeStore, projects: ProjectL
|
|
|
22
20
|
if (!projects.getProjectById(id)) throw new Error('Project not found.');
|
|
23
21
|
return id;
|
|
24
22
|
};
|
|
25
|
-
const snapshot = (id: string) => {
|
|
26
|
-
const current = store.snapshot(id);
|
|
27
|
-
if (current.exclusionsInitialized) return current;
|
|
28
|
-
const project = projects.getProjectById(id);
|
|
29
|
-
const values = project ? legacyExclusions(project.project_path) : [];
|
|
30
|
-
if (!values.length) return current;
|
|
31
|
-
const operation = exclusionNodeOperation(values);
|
|
32
|
-
store.update({ projectId: id, operations: [operation] });
|
|
33
|
-
return store.snapshot(id);
|
|
34
|
-
};
|
|
35
23
|
return {
|
|
36
24
|
query(input: KnowledgeQueryInput) {
|
|
37
25
|
return store.query({ ...input, projectId: ensureProject(input.projectId), projectPath: undefined });
|
|
@@ -40,8 +28,7 @@ export function createKnowledgeService(store: KnowledgeStore, projects: ProjectL
|
|
|
40
28
|
return store.update({ ...input, projectId: ensureProject(input.projectId) });
|
|
41
29
|
},
|
|
42
30
|
graph(value: unknown) {
|
|
43
|
-
|
|
44
|
-
return snapshot(id);
|
|
31
|
+
return store.snapshot(ensureProject(value));
|
|
45
32
|
},
|
|
46
33
|
scan(value: unknown, files?: unknown) {
|
|
47
34
|
const id = ensureProject(value);
|
|
@@ -4,19 +4,19 @@ import { projectsDb } from '@/modules/database/index.js';
|
|
|
4
4
|
|
|
5
5
|
const PUBLISH_INTERVAL_MS = 5000;
|
|
6
6
|
|
|
7
|
-
type PublishProjectSource = (sourceId: string, folders: string[] | null) => void;
|
|
7
|
+
type PublishProjectSource = (sourceId: string, folders: string[] | null, anonymizedFolders?: string[]) => void;
|
|
8
8
|
|
|
9
9
|
export function startProjectRegistry(publishProjectSource: PublishProjectSource): () => void {
|
|
10
10
|
const sourceId = `database:${path.resolve(process.env.DATABASE_PATH || 'auth.db')}`;
|
|
11
11
|
let publishedSignature: string | null = null;
|
|
12
12
|
|
|
13
13
|
const publishProjects = () => {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const signature = folders.join('\n')
|
|
14
|
+
const rows = [...projectsDb.getProjectPaths(), ...projectsDb.getArchivedProjectPaths()];
|
|
15
|
+
const folders = rows.map((row) => row.project_path).sort();
|
|
16
|
+
const anonymizedFolders = rows.filter((row) => row.anonymization_complete).map((row) => row.project_path).sort();
|
|
17
|
+
const signature = `${folders.join('\n')}\n--\n${anonymizedFolders.join('\n')}`;
|
|
18
18
|
if (signature === publishedSignature) return;
|
|
19
|
-
publishProjectSource(sourceId, folders);
|
|
19
|
+
publishProjectSource(sourceId, folders, anonymizedFolders);
|
|
20
20
|
publishedSignature = signature;
|
|
21
21
|
};
|
|
22
22
|
|
|
@@ -67,7 +67,6 @@ function createPieceMakerRouter({ getRuntimeStatus = defaultRuntimeStatus, anony
|
|
|
67
67
|
publishProjects();
|
|
68
68
|
const result = await registerLegalCase({
|
|
69
69
|
folder: request.body?.folder,
|
|
70
|
-
configFile: path.join(homeDir, 'config.json'),
|
|
71
70
|
homeDir,
|
|
72
71
|
});
|
|
73
72
|
response.status(201).json({ ok: true, ...result });
|
|
@@ -8,11 +8,6 @@
|
|
|
8
8
|
*
|
|
9
9
|
* Usage:
|
|
10
10
|
* piecemaker menu interactif
|
|
11
|
-
* piecemaker chronology affiche la chronologie du dossier courant
|
|
12
|
-
* piecemaker chronology write --path <pièce> --correction-json <json>
|
|
13
|
-
* crée une correction de chronologie pour une pièce
|
|
14
|
-
* piecemaker chronology edit --path <pièce> --correction-json <json>
|
|
15
|
-
* modifie une correction de chronologie existante
|
|
16
11
|
* piecemaker conversion convertit et pseudonymise les pièces manquantes
|
|
17
12
|
* piecemaker install ouvre le menu des composants
|
|
18
13
|
* piecemaker doctor diagnostic seul
|
|
@@ -33,16 +28,15 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
33
28
|
import { banner, title, log, write, blank, summary, spinner, badge, c } from '../lib/ui.mjs';
|
|
34
29
|
import { select, confirm, multiSelect, pause, nonInteractive } from '../lib/prompt.mjs';
|
|
35
30
|
import { GIT_REPO_ROOT, HOME_DIR, REPO_ROOT, commandExists, findPython } from '../lib/platform.mjs';
|
|
36
|
-
import { COMMANDS
|
|
31
|
+
import { COMMANDS } from '../lib/commandes.mjs';
|
|
37
32
|
import { loadConfig, readEnv, markStep, loadState, CONFIG_FILE } from '../lib/state.mjs';
|
|
38
33
|
import { scheduleStepResume, selectStepsToResume } from '../lib/resume-steps.mjs';
|
|
39
|
-
import { appServerPort, readLocalScanJob, startLocalScan } from '../lib/conversion-client.mjs';
|
|
34
|
+
import { adoptRunningServerPort, appServerPort, localServerReachable, readLocalScanJob, startLocalScan } from '../lib/conversion-client.mjs';
|
|
40
35
|
import { checkForUpdate, updateRepository } from '../lib/service.mjs';
|
|
41
36
|
|
|
42
37
|
const require = createRequire(import.meta.url);
|
|
43
38
|
const CLAUDE_ASSETS_MODULE = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../websocket-server/claude-assets.cjs');
|
|
44
39
|
const CLAUDE_HOOKS_MODULE = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../websocket-server/claude-hooks.cjs');
|
|
45
|
-
const DOCUMENT_INDEX_MODULE = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../websocket-server/document-index.cjs');
|
|
46
40
|
const ORIGINALS_PIPELINE_MODULE = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../websocket-server/originals-pipeline.cjs');
|
|
47
41
|
const APP_CLI_LIB = path.join(GIT_REPO_ROOT, 'scripts', 'piecemaker', 'cli', 'lib');
|
|
48
42
|
|
|
@@ -129,11 +123,8 @@ function parseArgs(argv) {
|
|
|
129
123
|
check: false,
|
|
130
124
|
dryRun: false,
|
|
131
125
|
conversionDocuments: [],
|
|
132
|
-
correctionJson: null,
|
|
133
126
|
force: false,
|
|
134
|
-
chronologyAction: 'read',
|
|
135
127
|
json: false,
|
|
136
|
-
piecePath: null,
|
|
137
128
|
resumeSteps: null,
|
|
138
129
|
step: null,
|
|
139
130
|
yes: false,
|
|
@@ -142,18 +133,13 @@ function parseArgs(argv) {
|
|
|
142
133
|
for (let i = 0; i < argv.length; i++) {
|
|
143
134
|
const arg = argv[i];
|
|
144
135
|
if (!arg.startsWith('-') && !flags.command && COMMANDS.has(arg)) flags.command = arg;
|
|
145
|
-
else if (!arg.startsWith('-') && flags.command === 'chronology' && flags.chronologyAction === 'read' && CHRONOLOGY_ACTIONS.has(arg)) flags.chronologyAction = arg;
|
|
146
|
-
else if (!arg.startsWith('-') && flags.command === 'chronology' && !flags.caseTarget) flags.caseTarget = arg;
|
|
147
136
|
else if (!arg.startsWith('-') && flags.command === 'conversion') flags.conversionDocuments.push(arg);
|
|
148
137
|
else if (arg === '--all') flags.all = true;
|
|
149
|
-
else if (arg === '--action') flags.chronologyAction = argv[++i];
|
|
150
138
|
else if (arg === '--case') flags.caseTarget = argv[++i];
|
|
151
139
|
else if (arg === '--check') flags.check = true;
|
|
152
|
-
else if (arg === '--correction-json') flags.correctionJson = argv[++i];
|
|
153
140
|
else if (arg === '--dry-run') flags.dryRun = true;
|
|
154
141
|
else if (arg === '--force') flags.force = true;
|
|
155
142
|
else if (arg === '--json') flags.json = true;
|
|
156
|
-
else if (arg === '--path') flags.piecePath = argv[++i];
|
|
157
143
|
else if (arg === '--yes' || arg === '-y') flags.yes = true;
|
|
158
144
|
else if (arg === '--step') flags.step = argv[++i];
|
|
159
145
|
else if (arg === '--resume-steps') {
|
|
@@ -313,20 +299,15 @@ async function runCheck(steps, ctx) {
|
|
|
313
299
|
function printHelp() {
|
|
314
300
|
write(` ${c.bold('piecemaker')} — PieceMaker local`);
|
|
315
301
|
blank();
|
|
316
|
-
write(' chronology [read] affiche la chronologie pseudonymisée du dossier courant');
|
|
317
|
-
write(' chronology write --path <pièce> --correction-json <json> crée une correction de chronologie');
|
|
318
|
-
write(' chronology edit --path <pièce> --correction-json <json> modifie une correction existante');
|
|
319
302
|
write(' conversion [pièce…] convertit et pseudonymise les pièces manquantes ou indiquées');
|
|
320
303
|
write(' install ouvre le menu d’installation/réparation');
|
|
321
304
|
write(' doctor, check diagnostic seul, n’installe rien');
|
|
322
305
|
write(' update met à jour PieceMaker');
|
|
323
306
|
blank();
|
|
324
307
|
write(' --all installe tout sans menu');
|
|
325
|
-
write(' --case <chemin> cible un dossier enregistré (
|
|
326
|
-
write(' --path <pièce> chemin relatif de la pièce (chronology write/edit)');
|
|
327
|
-
write(' --correction-json <json> correction à appliquer (chronology write/edit)');
|
|
308
|
+
write(' --case <chemin> cible un dossier enregistré (conversion)');
|
|
328
309
|
write(' --force retraite les pièces');
|
|
329
|
-
write(' --json produit une sortie JSON sans décor (
|
|
310
|
+
write(' --json produit une sortie JSON sans décor (conversion)');
|
|
330
311
|
write(' --check diagnostic seul, n\'installe rien');
|
|
331
312
|
write(' --step <id> rejoue une seule étape');
|
|
332
313
|
write(' --resume-steps <ids> rejoue les étapes indiquées sans interaction (usage interne)');
|
|
@@ -375,135 +356,6 @@ async function installerMenu(steps, ctx, { allowBack = false } = {}) {
|
|
|
375
356
|
}
|
|
376
357
|
}
|
|
377
358
|
|
|
378
|
-
function formatChronologyText(chronology) {
|
|
379
|
-
const lines = [];
|
|
380
|
-
lines.push(`Chronologie : ${chronology.stats.documents} pièce(s), ${chronology.stats.dated} datée(s), ${chronology.stats.entities} entité(s).`);
|
|
381
|
-
lines.push('');
|
|
382
|
-
for (const doc of chronology.documents) {
|
|
383
|
-
lines.push(`${doc.dateIso || '(date manquante)'} ${doc.nature || '(nature inconnue)'} ${doc.name}`);
|
|
384
|
-
}
|
|
385
|
-
const missing = chronology.documents.filter((doc) => !doc.dateIso);
|
|
386
|
-
if (missing.length) {
|
|
387
|
-
lines.push('');
|
|
388
|
-
lines.push('Dates manquantes :');
|
|
389
|
-
for (const doc of missing) lines.push(` - ${doc.name}`);
|
|
390
|
-
}
|
|
391
|
-
return `${lines.join('\n')}\n`;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
function locateChronologyCase(flags) {
|
|
395
|
-
const { locateProjectCase } = require('../../piecemaker-plugin/scripts/lib/case-folders.cjs');
|
|
396
|
-
const located = locateProjectCase(flags.caseTarget || process.cwd());
|
|
397
|
-
if (!located) {
|
|
398
|
-
throw new Error('Lancez la commande depuis un dossier juridique enregistré ou passez --case <chemin>.');
|
|
399
|
-
}
|
|
400
|
-
return located;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
async function runChronologyReadCommand(flags) {
|
|
404
|
-
if (!fs.existsSync(DOCUMENT_INDEX_MODULE)) {
|
|
405
|
-
throw new Error('Le module de chronologie PieceMaker est introuvable.');
|
|
406
|
-
}
|
|
407
|
-
const located = locateChronologyCase(flags);
|
|
408
|
-
const { buildChronology } = require(DOCUMENT_INDEX_MODULE);
|
|
409
|
-
const chronology = await buildChronology(located.caseRoot, { deanonymizeLabels: false, includeManualDecisions: true });
|
|
410
|
-
const output = flags.json
|
|
411
|
-
? `${JSON.stringify(chronology, null, 2)}\n`
|
|
412
|
-
: formatChronologyText(chronology);
|
|
413
|
-
process.stdout.write(output);
|
|
414
|
-
return 0;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
function resolveChronologyPiecePath(caseRoot, relativePath) {
|
|
418
|
-
const relative = String(relativePath || '').replaceAll('\\', '/').replace(/^\.\//, '');
|
|
419
|
-
if (!relative) throw new Error('Chemin de pièce manquant.');
|
|
420
|
-
const absolute = path.resolve(caseRoot, ...relative.split('/'));
|
|
421
|
-
if (absolute !== caseRoot && !absolute.startsWith(`${caseRoot}${path.sep}`)) {
|
|
422
|
-
throw new Error('Pièce hors du dossier juridique.');
|
|
423
|
-
}
|
|
424
|
-
if (!fs.existsSync(absolute)) throw new Error('Pièce introuvable.');
|
|
425
|
-
return relative;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
async function runChronologyCorrectionCommand(flags) {
|
|
429
|
-
if (!fs.existsSync(DOCUMENT_INDEX_MODULE)) {
|
|
430
|
-
throw new Error('Le module de chronologie PieceMaker est introuvable.');
|
|
431
|
-
}
|
|
432
|
-
const located = locateChronologyCase(flags);
|
|
433
|
-
const relative = resolveChronologyPiecePath(located.caseRoot, flags.piecePath);
|
|
434
|
-
|
|
435
|
-
let correction;
|
|
436
|
-
try {
|
|
437
|
-
correction = JSON.parse(flags.correctionJson || '{}');
|
|
438
|
-
} catch (error) {
|
|
439
|
-
throw new Error(`Correction JSON invalide : ${error.message}`);
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
const { stateKey } = require('../../piecemaker-plugin/scripts/lib/anonymization-state.cjs');
|
|
443
|
-
const { applyDocumentIndexCorrection, readDocumentIndex, documentIndexFile } = require(DOCUMENT_INDEX_MODULE);
|
|
444
|
-
const key = stateKey(relative);
|
|
445
|
-
const existing = readDocumentIndex(located.caseRoot).overrides[key] || null;
|
|
446
|
-
|
|
447
|
-
if (flags.chronologyAction === 'write' && existing) {
|
|
448
|
-
throw new Error('Une correction existe déjà pour cette pièce ; utilisez edit_chronology pour la modifier.');
|
|
449
|
-
}
|
|
450
|
-
if (flags.chronologyAction === 'edit' && !existing) {
|
|
451
|
-
throw new Error('Aucune correction existante pour cette pièce ; utilisez write_chronology pour en créer une.');
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
const mutation = applyDocumentIndexCorrection(located.caseRoot, relative, correction);
|
|
455
|
-
|
|
456
|
-
let history;
|
|
457
|
-
try {
|
|
458
|
-
const { createCommit } = require('../../piecemaker-plugin/scripts/lib/commits.cjs');
|
|
459
|
-
history = await createCommit({
|
|
460
|
-
casesRoot: located.casesRoot,
|
|
461
|
-
caseName: located.caseName,
|
|
462
|
-
homeDir: HOME_DIR,
|
|
463
|
-
envFile: path.join(REPO_ROOT, '.env'),
|
|
464
|
-
label: flags.chronologyAction === 'write'
|
|
465
|
-
? 'Création d’une correction de chronologie'
|
|
466
|
-
: 'Modification d’une correction de chronologie',
|
|
467
|
-
description: `Mise à jour déterministe de la pièce ${mutation.documentKey.slice(0, 12).toUpperCase()}.`,
|
|
468
|
-
event: 'assistant-chronology-correction',
|
|
469
|
-
paths: [path.relative(located.caseRoot, documentIndexFile(located.caseRoot)).split(path.sep).join('/')],
|
|
470
|
-
waitForLockMs: 10_000,
|
|
471
|
-
});
|
|
472
|
-
} catch (error) {
|
|
473
|
-
history = { created: false, error: error.message };
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
const result = {
|
|
477
|
-
ok: true,
|
|
478
|
-
action: flags.chronologyAction,
|
|
479
|
-
path: relative,
|
|
480
|
-
documentKey: mutation.documentKey,
|
|
481
|
-
override: mutation.override,
|
|
482
|
-
entityDecisions: mutation.entityDecisions,
|
|
483
|
-
editRevision: mutation.editRevision,
|
|
484
|
-
history: {
|
|
485
|
-
created: Boolean(history?.created),
|
|
486
|
-
hash: history?.commit || null,
|
|
487
|
-
error: history?.error || null,
|
|
488
|
-
},
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
if (flags.json) {
|
|
492
|
-
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
493
|
-
} else {
|
|
494
|
-
const verb = flags.chronologyAction === 'write' ? 'Correction créée' : 'Correction modifiée';
|
|
495
|
-
log.ok(`${verb} pour ${relative}.`);
|
|
496
|
-
}
|
|
497
|
-
return 0;
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
async function runChronologyCommand(flags) {
|
|
501
|
-
if (flags.chronologyAction === 'write' || flags.chronologyAction === 'edit') {
|
|
502
|
-
return runChronologyCorrectionCommand(flags);
|
|
503
|
-
}
|
|
504
|
-
return runChronologyReadCommand(flags);
|
|
505
|
-
}
|
|
506
|
-
|
|
507
359
|
function normalizedConversionRequest(value, caseRoot) {
|
|
508
360
|
const raw = String(value || '').trim();
|
|
509
361
|
if (!raw) throw new Error('Nom de pièce vide.');
|
|
@@ -570,6 +422,8 @@ async function waitForConversionJob({ folder, id }, { json = false } = {}) {
|
|
|
570
422
|
}
|
|
571
423
|
|
|
572
424
|
async function ensureServerForConversion({ json = false } = {}) {
|
|
425
|
+
await adoptRunningServerPort();
|
|
426
|
+
if (await localServerReachable()) return;
|
|
573
427
|
const servicesModule = path.join(APP_CLI_LIB, 'services.mjs');
|
|
574
428
|
if (!fs.existsSync(servicesModule)) {
|
|
575
429
|
throw new Error(`Le serveur applicatif doit être démarré par la commande « piecemaker » avant la conversion (port ${appServerPort()}).`);
|
|
@@ -625,7 +479,6 @@ async function runConversionCommand(flags) {
|
|
|
625
479
|
}
|
|
626
480
|
|
|
627
481
|
async function runOperationalCommand(command, knownUpdate = null, flags = {}) {
|
|
628
|
-
if (command === 'chronology') return runChronologyCommand(flags);
|
|
629
482
|
if (command === 'conversion') return runConversionCommand(flags);
|
|
630
483
|
if (command === 'update') {
|
|
631
484
|
const pending = knownUpdate ?? checkForUpdate();
|
|
@@ -749,20 +602,14 @@ async function main() {
|
|
|
749
602
|
return 1;
|
|
750
603
|
}
|
|
751
604
|
|
|
752
|
-
if (
|
|
605
|
+
if (flags.command !== 'conversion' && (flags.caseTarget || flags.json)) {
|
|
753
606
|
banner();
|
|
754
|
-
log.error('Les options --case et --json sont réservées
|
|
755
|
-
return 1;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
if (flags.command === 'chronology' && !CHRONOLOGY_ACTIONS.has(flags.chronologyAction)) {
|
|
759
|
-
log.error('Action inconnue : utilisez « piecemaker chronology read|write|edit ».');
|
|
607
|
+
log.error('Les options --case et --json sont réservées à la commande conversion.');
|
|
760
608
|
return 1;
|
|
761
609
|
}
|
|
762
610
|
|
|
763
611
|
// Les sorties JSON sont directement consommées par les assistants.
|
|
764
|
-
if (
|
|
765
|
-
|| (flags.command === 'conversion' && flags.json)) {
|
|
612
|
+
if (flags.command === 'conversion' && flags.json) {
|
|
766
613
|
return runOperationalCommand(flags.command, null, flags);
|
|
767
614
|
}
|
|
768
615
|
|
|
@@ -7,5 +7,4 @@
|
|
|
7
7
|
* est importable sans effet de bord.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
export const COMMANDS = new Set(['
|
|
11
|
-
export const CHRONOLOGY_ACTIONS = new Set(['read', 'write', 'edit']);
|
|
10
|
+
export const COMMANDS = new Set(['conversion', 'install', 'doctor', 'check', 'update']);
|
|
@@ -4,20 +4,79 @@
|
|
|
4
4
|
* La conversion et l'analyse PII n'ont qu'un seul point d'entrée : la route
|
|
5
5
|
* `knowledge/scan` du serveur applicatif. Le CLI en est un client, sur la
|
|
6
6
|
* boucle locale, via le montage `/api/piecemaker/local` placé avant
|
|
7
|
-
* l'authentification. Le serveur applicatif écoute en clair sur
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* l'authentification. Le serveur applicatif écoute en clair sur la boucle
|
|
8
|
+
* locale : celui qui tourne déjà (application de bureau comprise, annoncé par
|
|
9
|
+
* `local-server.json` dans le dossier de données), sinon le port du CLI
|
|
10
|
+
* (`PIECEMAKER_APP_PORT`, 3003 par défaut). Aucun certificat n'entre en jeu et
|
|
11
|
+
* la requête ne sort jamais de 127.0.0.1.
|
|
10
12
|
*/
|
|
11
13
|
|
|
14
|
+
import fs from 'node:fs';
|
|
12
15
|
import http from 'node:http';
|
|
16
|
+
import os from 'node:os';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
|
|
19
|
+
import { GIT_REPO_ROOT } from './platform.mjs';
|
|
13
20
|
|
|
14
21
|
const LOCAL_BASE = '/api/piecemaker/local';
|
|
22
|
+
const LOOPBACK_HOSTS = new Set(['127.0.0.1', 'localhost', '::1', '0.0.0.0', '::']);
|
|
15
23
|
|
|
16
24
|
export function appServerPort() {
|
|
17
25
|
const parsed = Number.parseInt(process.env.PIECEMAKER_APP_PORT || '', 10);
|
|
18
26
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : 3003;
|
|
19
27
|
}
|
|
20
28
|
|
|
29
|
+
function runningServerMarkerPath() {
|
|
30
|
+
if (process.env.CLOUDCLI_HOME) return join(process.env.CLOUDCLI_HOME, 'local-server.json');
|
|
31
|
+
try {
|
|
32
|
+
const config = JSON.parse(fs.readFileSync(join(GIT_REPO_ROOT, 'product.config.json'), 'utf8'));
|
|
33
|
+
return join(os.homedir(), config.dataDirectoryName, 'local-server.json');
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function processAlive(pid) {
|
|
40
|
+
try {
|
|
41
|
+
process.kill(pid, 0);
|
|
42
|
+
return true;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return error.code === 'EPERM';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function runningServerPort(markerPath = runningServerMarkerPath()) {
|
|
49
|
+
if (!markerPath) return null;
|
|
50
|
+
try {
|
|
51
|
+
const marker = JSON.parse(fs.readFileSync(markerPath, 'utf8'));
|
|
52
|
+
const port = Number(marker?.port);
|
|
53
|
+
const pid = Number(marker?.pid);
|
|
54
|
+
if (!Number.isInteger(port) || port <= 0 || !LOOPBACK_HOSTS.has(marker?.host)) return null;
|
|
55
|
+
if (!Number.isInteger(pid) || pid <= 0 || !processAlive(pid)) return null;
|
|
56
|
+
return port;
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function localServerReachable(port = appServerPort()) {
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
const request = http.get({ hostname: '127.0.0.1', port, path: '/api/auth/status', timeout: 2_000 }, (response) => {
|
|
65
|
+
response.resume();
|
|
66
|
+
resolve(response.statusCode < 500);
|
|
67
|
+
});
|
|
68
|
+
request.on('timeout', () => request.destroy());
|
|
69
|
+
request.on('error', () => resolve(false));
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function adoptRunningServerPort({ markerPath, reachable = localServerReachable } = {}) {
|
|
74
|
+
if (process.env.PIECEMAKER_APP_PORT) return appServerPort();
|
|
75
|
+
const port = runningServerPort(markerPath);
|
|
76
|
+
if (port && await reachable(port)) process.env.PIECEMAKER_APP_PORT = String(port);
|
|
77
|
+
return appServerPort();
|
|
78
|
+
}
|
|
79
|
+
|
|
21
80
|
function requestJson({ method, path, body }) {
|
|
22
81
|
const payload = body === undefined ? null : Buffer.from(JSON.stringify(body));
|
|
23
82
|
return new Promise((resolve, reject) => {
|