@piecemaker-legal/piecemaker 1.1.1 → 2.0.1
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-DEXWh3LH.js → abnfDiagram-VCTEODGH-C2JDmlZx.js} +1 -1
- package/dist/assets/{arc-CoTRLXKW.js → arc-XhSg5Mrj.js} +1 -1
- package/dist/assets/{architectureDiagram-5GKGNRK7-Cg-jO9oR.js → architectureDiagram-5GKGNRK7-DKREuXxk.js} +1 -1
- package/dist/assets/{blockDiagram-I7D4REHJ-ChXwQSIq.js → blockDiagram-I7D4REHJ-CNK0Tp5d.js} +1 -1
- package/dist/assets/{c4Diagram-7LVT6UL2-Bw2VGggd.js → c4Diagram-7LVT6UL2-C8fJzWVp.js} +1 -1
- package/dist/assets/channel-DO5hoFdU.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-C1WdqrLP.js → chunk-2Q5K7J3B-DlE8nLJo.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-BerUZBa1.js → chunk-5VM5RSS4-CSm-2TZ4.js} +1 -1
- package/dist/assets/{chunk-F27PBJKO-5W7ax2gB.js → chunk-F27PBJKO-CSgnXT2c.js} +1 -1
- package/dist/assets/{chunk-IMKFNOWR-JgDN1h4T.js → chunk-IMKFNOWR-C6bkt6C1.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-ulNHnEdQ.js → chunk-JWPE2WC7-jUR52MBO.js} +1 -1
- package/dist/assets/{chunk-POPQ4Y6H-D4aTDC2T.js → chunk-POPQ4Y6H-D0UAL7O7.js} +1 -1
- package/dist/assets/{chunk-SVP7TREG-BA5zX4XF.js → chunk-SVP7TREG-C9dtOEii.js} +1 -1
- package/dist/assets/{chunk-TICWLB2K-DhOULVhX.js → chunk-TICWLB2K-BEzWKT39.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-CkpCgrVg.js → chunk-XXDRQBXY-uD1oklVY.js} +1 -1
- package/dist/assets/classDiagram-ZZMXUADV-BsQtwznI.js +1 -0
- package/dist/assets/classDiagram-v2-VYDZK3BY-BsQtwznI.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-0xUSfjfV.js → cose-bilkent-JH36ORCC-aGjlSdMe.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-BE6fNu2_.js → cynefin-OW5HDTMX-D3G4OYOn.js} +1 -1
- package/dist/assets/{cynefinDiagram-5FMLGOSQ-rFMnZ9b3.js → cynefinDiagram-5FMLGOSQ-9swRDUnu.js} +1 -1
- package/dist/assets/{dagre-GXQ25YYZ-C1p4ffd7.js → dagre-GXQ25YYZ-lvaAGdqY.js} +1 -1
- package/dist/assets/{diagram-S7CK7UJ4-BV_3Xj4O.js → diagram-S7CK7UJ4-BaZ6YIkR.js} +1 -1
- package/dist/assets/{diagram-UQ7AKVKN-Bb_Evkrm.js → diagram-UQ7AKVKN-COJ2Gb38.js} +1 -1
- package/dist/assets/{diagram-VSXAHHWV-BzgupjxD.js → diagram-VSXAHHWV-CYZEkbzP.js} +1 -1
- package/dist/assets/{diagram-VX7I27RA-D1c67z76.js → diagram-VX7I27RA-fFeQJ4ae.js} +1 -1
- package/dist/assets/{diagram-Z3DM3KII-DsJAJGKE.js → diagram-Z3DM3KII-B7knszDV.js} +1 -1
- package/dist/assets/{ebnfDiagram-PWID7BFC-B1ge_obq.js → ebnfDiagram-PWID7BFC-Dk0l329Y.js} +1 -1
- package/dist/assets/{erDiagram-RLTQ6QDP-DrV-tggw.js → erDiagram-RLTQ6QDP-CtpYxui8.js} +1 -1
- package/dist/assets/{flowDiagram-HODETNUW-CVzDiSQ3.js → flowDiagram-HODETNUW-aVWueRJD.js} +1 -1
- package/dist/assets/{ganttDiagram-EL5Y4UJY-CGRdSN9D.js → ganttDiagram-EL5Y4UJY-DfrX-B7p.js} +1 -1
- package/dist/assets/{gitGraphDiagram-WWUBYQGX-CAfvFXzL.js → gitGraphDiagram-WWUBYQGX-BFB1ZPF3.js} +1 -1
- package/dist/assets/{index-DaxH81ZJ.js → index-DqRygn9E.js} +170 -169
- package/dist/assets/index-Ds4HaAjz.css +1 -0
- package/dist/assets/{infoDiagram-27XIBGKW-DTshEkGK.js → infoDiagram-27XIBGKW-CuYkzm43.js} +1 -1
- package/dist/assets/{ishikawaDiagram-5VMMS53U-DkbshVxp.js → ishikawaDiagram-5VMMS53U-EMiw8Akh.js} +1 -1
- package/dist/assets/{journeyDiagram-3NMN7TZE-v5qE8xpn.js → journeyDiagram-3NMN7TZE-DCoLV1f8.js} +1 -1
- package/dist/assets/{kanban-definition-UXKFOSKX-a9CNMFLt.js → kanban-definition-UXKFOSKX-CyA4Yinu.js} +1 -1
- package/dist/assets/{layout-DrW4R1TH.js → layout-BlaB0grA.js} +1 -1
- package/dist/assets/{linear-Cs3qpXT_.js → linear-DLX3dJDH.js} +1 -1
- package/dist/assets/{mermaid.core-C7Ti-Fty.js → mermaid.core-Cy51CT89.js} +6 -6
- package/dist/assets/{mindmap-definition-YA3MSWOX-B0VDdIcG.js → mindmap-definition-YA3MSWOX-OBXjfL7I.js} +1 -1
- package/dist/assets/{pegDiagram-XKGWAZYB-CV1L7MjP.js → pegDiagram-XKGWAZYB-C_BJjUh1.js} +1 -1
- package/dist/assets/{pieDiagram-E7YTZNPT-DSCuzVTV.js → pieDiagram-E7YTZNPT-jIpbr3Fi.js} +1 -1
- package/dist/assets/{quadrantDiagram-AXDQQJYC-hzX-9AKo.js → quadrantDiagram-AXDQQJYC-C1A15xI5.js} +1 -1
- package/dist/assets/{railroadDiagram-O6MQD6OU-BbmzIqcp.js → railroadDiagram-O6MQD6OU-Rslzn1pI.js} +1 -1
- package/dist/assets/{requirementDiagram-BXWQKSXE-CQCRRBSL.js → requirementDiagram-BXWQKSXE-LlfURO5-.js} +1 -1
- package/dist/assets/{sankeyDiagram-P5KCCOFB-kiaLcW6X.js → sankeyDiagram-P5KCCOFB-B5BPF4-O.js} +1 -1
- package/dist/assets/{sequenceDiagram-WJ2MYXX4-Bgx8ST7Q.js → sequenceDiagram-WJ2MYXX4-orYZe9zX.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-BTjJ-ygN.js → sizeCapture-INFHLROL-Co-K5Cfb.js} +1 -1
- package/dist/assets/{stateDiagram-D77RDMKH-D48RR1FL.js → stateDiagram-D77RDMKH-COEx1aH2.js} +1 -1
- package/dist/assets/{stateDiagram-v2-MP3YSRHH-B-XwSR-z.js → stateDiagram-v2-MP3YSRHH-DaeTZvmy.js} +1 -1
- package/dist/assets/{swimlanes-42K2YHIH-BhE-QuEZ.js → swimlanes-42K2YHIH-BHQaDhfZ.js} +1 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-BHW8q5M5.js +8 -0
- package/dist/assets/{timeline-definition-24CTP7MA-Cm7EUWWt.js → timeline-definition-24CTP7MA-COqCRwGX.js} +1 -1
- package/dist/assets/{vennDiagram-4TSXK5OY-X-nC9wZ0.js → vennDiagram-4TSXK5OY-nym5XSnr.js} +1 -1
- package/dist/assets/{wardleyDiagram-VM6X3IG4-D33YvEQZ.js → wardleyDiagram-VM6X3IG4-DEPPey_W.js} +1 -1
- package/dist/assets/{xychartDiagram-S5SC5T6Z-lziwBKsM.js → xychartDiagram-S5SC5T6Z-uSJ404kL.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/sw.js +1 -1
- package/dist-server/server/index.js +3 -1
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/piecemaker/index.js +7 -3
- package/dist-server/server/piecemaker/index.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/index.js +13 -6
- package/dist-server/server/piecemaker/knowledge/index.js.map +1 -1
- package/dist-server/server/piecemaker/knowledge/local-routes.js +55 -0
- package/dist-server/server/piecemaker/knowledge/local-routes.js.map +1 -0
- package/dist-server/server/piecemaker/knowledge/scan-jobs.js +3 -1
- package/dist-server/server/piecemaker/knowledge/scan-jobs.js.map +1 -1
- package/dist-server/server/piecemaker/library/index.js +14 -41
- package/dist-server/server/piecemaker/library/index.js.map +1 -1
- package/dist-server/server/piecemaker/library/store.js +11 -3
- package/dist-server/server/piecemaker/library/store.js.map +1 -1
- package/dist-server/server/piecemaker/library/workspace-installation.js +26 -3
- package/dist-server/server/piecemaker/library/workspace-installation.js.map +1 -1
- package/dist-server/server/piecemaker/timesheet/service.js +8 -7
- package/dist-server/server/piecemaker/timesheet/service.js.map +1 -1
- package/dist-server/server/piecemaker/timesheet/store.js +5 -3
- package/dist-server/server/piecemaker/timesheet/store.js.map +1 -1
- package/package.json +1 -1
- package/product.config.json +2 -0
- package/scripts/piecemaker/build-app-icon.py +88 -0
- package/scripts/piecemaker/cli/lib/piecemaker-anonymizer.mjs +2 -2
- package/scripts/piecemaker/hooks/proxy-guard.mjs +19 -54
- package/scripts/release/assert-piecemaker-version.mjs +25 -0
- package/server/index.ts +4 -1
- package/server/piecemaker/anonymizer/client-config.cjs +45 -162
- package/server/piecemaker/anonymizer/hudsucker-fixture.cjs +127 -0
- package/server/piecemaker/anonymizer/hudsucker-proxy/Cargo.lock +2396 -0
- package/server/piecemaker/anonymizer/hudsucker-proxy/Cargo.toml +14 -0
- package/server/piecemaker/anonymizer/hudsucker-proxy/src/main.rs +501 -0
- package/server/piecemaker/anonymizer/providers.cjs +14 -135
- package/server/piecemaker/anonymizer/rewriter-bridge.cjs +221 -0
- package/server/piecemaker/anonymizer/service.cjs +144 -28
- package/server/piecemaker/index.ts +8 -3
- package/server/piecemaker/knowledge/index.ts +14 -6
- package/server/piecemaker/knowledge/local-routes.ts +72 -0
- package/server/piecemaker/knowledge/scan-jobs.ts +4 -1
- package/server/piecemaker/library/index.ts +14 -34
- package/server/piecemaker/library/store.ts +11 -3
- package/server/piecemaker/library/workspace-installation.ts +20 -3
- package/server/piecemaker/timesheet/service.ts +9 -7
- package/server/piecemaker/timesheet/store.ts +5 -3
- package/server/piecemaker/vendor/installer/assets/claude-hooks/piecemaker-guard-secrets.mjs +0 -1
- package/server/piecemaker/vendor/installer/assets/piecemaker.icns +0 -0
- package/server/piecemaker/vendor/installer/assets/piecemaker.ico +0 -0
- package/server/piecemaker/vendor/installer/bin/piecemaker.mjs +56 -206
- package/server/piecemaker/vendor/installer/lib/commandes.mjs +2 -4
- package/server/piecemaker/vendor/installer/lib/conversion-client.mjs +74 -0
- package/server/piecemaker/vendor/installer/lib/service.mjs +4 -358
- package/server/piecemaker/vendor/installer/lib/state.mjs +0 -6
- package/server/piecemaker/vendor/installer/steps/03-python-gliner.mjs +4 -4
- package/server/piecemaker/vendor/installer/steps/09-claude-assets.mjs +0 -31
- package/server/piecemaker/vendor/piecemaker-plugin/scripts/statusline.mjs +3 -4
- package/server/piecemaker/vendor/websocket-server/admin-routes.cjs +1 -49
- package/server/piecemaker/vendor/websocket-server/claude-hooks.cjs +14 -12
- package/server/piecemaker/vendor/websocket-server/originals-pipeline.cjs +17 -476
- package/server/piecemaker/vendor/websocket-server/scripts/convert_and_scan_pipeline.py +2 -2
- package/server/piecemaker/vendor/websocket-server/scripts/mineru_improved_1.py +11 -2
- package/server/piecemaker/vendor/websocket-server/scripts/presidio-gliner/scanner_worker.py +13 -37
- package/server/piecemaker/vendor/websocket-server/scripts/requirements.txt +1 -7
- package/server/piecemaker/vendor/websocket-server/scripts/warmup.py +14 -85
- package/dist/assets/channel-DdzrPZ6Y.js +0 -1
- package/dist/assets/classDiagram-ZZMXUADV-Dim2o8E5.js +0 -1
- package/dist/assets/classDiagram-v2-VYDZK3BY-Dim2o8E5.js +0 -1
- package/dist/assets/index-D_Dc_nQv.css +0 -1
- package/dist/assets/swimlanesDiagram-VR7AAH4N-DR525d5V.js +0 -8
- package/server/piecemaker/anonymizer/proxy.cjs +0 -365
- package/server/piecemaker/vendor/installer/assets/PieceMakerApp.swift +0 -189
- package/server/piecemaker/vendor/installer/lib/desktop-launcher.mjs +0 -305
- package/server/piecemaker/vendor/installer/steps/05-certificats.mjs +0 -171
- package/server/piecemaker/vendor/installer/steps/14-mxc-sandbox.mjs +0 -271
- package/server/piecemaker/vendor/installer/steps/15-pwa-desktop.mjs +0 -64
- package/server/piecemaker/vendor/installer/templates/root-CLAUDE.md +0 -56
- package/server/piecemaker/vendor/installer/templates/workspace-CLAUDE.md +0 -52
|
@@ -11,7 +11,7 @@ import { startRequiredAnonymizer } from './anonymizer/lifecycle.js';
|
|
|
11
11
|
import { createCitationStore } from './harness/citation-store.js';
|
|
12
12
|
import { installChatCitationHarness } from './harness/chat-harness.js';
|
|
13
13
|
import { createCitationsRouter } from './harness/citations.routes.js';
|
|
14
|
-
import {
|
|
14
|
+
import { openLibrary, installLibraryRuntime } from './library/index.js';
|
|
15
15
|
import { createKnowledgeBackend } from './knowledge/index.js';
|
|
16
16
|
import { createTimesheetBackend } from './timesheet/index.js';
|
|
17
17
|
import { createCompanySearchRouter } from './company-search.js';
|
|
@@ -49,17 +49,22 @@ const anonymizer = createAnonymizerService({ homeDir: piecemakerHome(), required
|
|
|
49
49
|
const ensureProxy = await startRequiredAnonymizer(anonymizer);
|
|
50
50
|
const citations = createCitationStore(piecemakerHome());
|
|
51
51
|
installChatCitationHarness({ runtime: providerRuntimeService, sessions: sessionsService, store: citations, ensureProxy });
|
|
52
|
-
const library = await
|
|
53
|
-
installLibraryRuntime(providerRuntimeService, sessionsService, library);
|
|
52
|
+
const library = await openLibrary(piecemakerHome(), applicationRoot);
|
|
53
|
+
installLibraryRuntime(providerRuntimeService, sessionsService, library.store);
|
|
54
54
|
const timesheet = createTimesheetBackend(piecemakerHome());
|
|
55
55
|
const knowledge = createKnowledgeBackend(applicationRoot);
|
|
56
56
|
|
|
57
|
+
export function createPieceMakerLocalRouter() {
|
|
58
|
+
return knowledge.localRouter;
|
|
59
|
+
}
|
|
60
|
+
|
|
57
61
|
export function createPieceMakerRouter(options: { getRuntimeStatus?: () => PieceMakerRuntimeStatus } = {}) {
|
|
58
62
|
const router = vendor.createPieceMakerRouter({ ...options, anonymizer });
|
|
59
63
|
router.use(createCitationsRouter(citations, (id) => {
|
|
60
64
|
try { sessionsService.getSessionDetailsById(id); return true; } catch { return false; }
|
|
61
65
|
}));
|
|
62
66
|
router.use(timesheet);
|
|
67
|
+
router.use('/library', library.router);
|
|
63
68
|
router.use(knowledge.router);
|
|
64
69
|
router.use(createCompanySearchRouter());
|
|
65
70
|
router.use(createBodaccSearchRouter());
|
|
@@ -4,24 +4,32 @@ import { getConnection, projectsDb } from '@/modules/database/index.js';
|
|
|
4
4
|
|
|
5
5
|
import { KnowledgeStore } from '../../../plugins/piecemaker-dossier/src/knowledge.js';
|
|
6
6
|
|
|
7
|
+
import { createKnowledgeLocalRouter } from './local-routes.js';
|
|
7
8
|
import { createKnowledgePipeline } from './pipeline.js';
|
|
8
9
|
import { createKnowledgeRouter } from './routes.js';
|
|
9
10
|
import { createKnowledgeService } from './service.js';
|
|
10
11
|
|
|
11
12
|
export function createKnowledgeBackend(applicationRoot: string) {
|
|
12
|
-
const router = express.Router();
|
|
13
|
-
let knowledgeRouter: express.Router | null = null;
|
|
14
13
|
let store: KnowledgeStore | null = null;
|
|
14
|
+
let service: ReturnType<typeof createKnowledgeService> | null = null;
|
|
15
15
|
const getStore = () => {
|
|
16
16
|
if (!store) store = new KnowledgeStore(getConnection());
|
|
17
17
|
return store;
|
|
18
18
|
};
|
|
19
|
-
|
|
20
|
-
if (!
|
|
19
|
+
const getService = () => {
|
|
20
|
+
if (!service) {
|
|
21
21
|
const pipeline = createKnowledgePipeline({ applicationRoot, projects: projectsDb, store: getStore() });
|
|
22
|
-
|
|
22
|
+
service = createKnowledgeService(getStore(), projectsDb, pipeline);
|
|
23
23
|
}
|
|
24
|
+
return service;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const router = express.Router();
|
|
28
|
+
let knowledgeRouter: express.Router | null = null;
|
|
29
|
+
router.use((request, response, next) => {
|
|
30
|
+
if (!knowledgeRouter) knowledgeRouter = createKnowledgeRouter(getService());
|
|
24
31
|
knowledgeRouter(request, response, next);
|
|
25
32
|
});
|
|
26
|
-
|
|
33
|
+
|
|
34
|
+
return { router, localRouter: createKnowledgeLocalRouter(getService, projectsDb) };
|
|
27
35
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
|
|
3
|
+
import type { createKnowledgeService } from './service.js';
|
|
4
|
+
|
|
5
|
+
type KnowledgeService = ReturnType<typeof createKnowledgeService>;
|
|
6
|
+
|
|
7
|
+
type ProjectPathLookup = {
|
|
8
|
+
getProjectPath(projectPath: string): { project_id: string; project_path: string } | null;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const LOOPBACK_ADDRESSES = new Set(['127.0.0.1', '::1', '::ffff:127.0.0.1']);
|
|
12
|
+
|
|
13
|
+
function isLoopback(request: express.Request): boolean {
|
|
14
|
+
return LOOPBACK_ADDRESSES.has(request.socket.remoteAddress || '');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function statusFor(error: unknown): number {
|
|
18
|
+
if (error instanceof TypeError) return 400;
|
|
19
|
+
if (error instanceof Error && error.message === 'Project not found.') return 404;
|
|
20
|
+
return 500;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function createKnowledgeLocalRouter(
|
|
24
|
+
getService: () => KnowledgeService,
|
|
25
|
+
projects: ProjectPathLookup,
|
|
26
|
+
) {
|
|
27
|
+
const router = express.Router();
|
|
28
|
+
|
|
29
|
+
router.use((request, response, next) => {
|
|
30
|
+
if (!isLoopback(request)) {
|
|
31
|
+
response.status(403).json({ error: 'Local scan is restricted to loopback callers.' });
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
next();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const resolveProjectId = (body: unknown): string => {
|
|
38
|
+
const source = (body ?? {}) as { projectId?: unknown; folder?: unknown };
|
|
39
|
+
if (typeof source.projectId === 'string' && source.projectId.trim()) return source.projectId.trim();
|
|
40
|
+
if (typeof source.folder !== 'string' || !source.folder.trim()) {
|
|
41
|
+
throw new TypeError('folder or projectId is required.');
|
|
42
|
+
}
|
|
43
|
+
const project = projects.getProjectPath(source.folder.trim());
|
|
44
|
+
if (!project) throw new Error('Project not found.');
|
|
45
|
+
return project.project_id;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const respond = (operation: () => unknown | Promise<unknown>, response: express.Response) => {
|
|
49
|
+
Promise.resolve().then(operation).then((result) => response.json(result)).catch((error: unknown) => {
|
|
50
|
+
response.status(statusFor(error)).json({ error: error instanceof Error ? error.message : 'Local scan failed.' });
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
router.post('/scan', (request, response) => respond(() => {
|
|
55
|
+
const id = resolveProjectId(request.body);
|
|
56
|
+
return { projectId: id, ...getService().scan(id, request.body?.files) };
|
|
57
|
+
}, response));
|
|
58
|
+
|
|
59
|
+
router.get('/scan/job', (request, response) => respond(() => {
|
|
60
|
+
const id = typeof request.query.projectId === 'string' && request.query.projectId.trim()
|
|
61
|
+
? request.query.projectId.trim()
|
|
62
|
+
: resolveProjectId({ folder: request.query.folder });
|
|
63
|
+
return { projectId: id, ...getService().scanJob(request.query.id, id) };
|
|
64
|
+
}, response));
|
|
65
|
+
|
|
66
|
+
router.post('/scan/cancel', (request, response) => respond(() => {
|
|
67
|
+
const id = resolveProjectId(request.body);
|
|
68
|
+
return { projectId: id, ...getService().cancelScan(request.body?.id, id) };
|
|
69
|
+
}, response));
|
|
70
|
+
|
|
71
|
+
return router;
|
|
72
|
+
}
|
|
@@ -20,6 +20,7 @@ export type KnowledgeScanJob = {
|
|
|
20
20
|
processed: number;
|
|
21
21
|
total: number;
|
|
22
22
|
error: string | null;
|
|
23
|
+
result: unknown;
|
|
23
24
|
startedAt: string;
|
|
24
25
|
finishedAt: string | null;
|
|
25
26
|
};
|
|
@@ -63,6 +64,7 @@ export function createKnowledgeScanJobs() {
|
|
|
63
64
|
processed: 0,
|
|
64
65
|
total: 0,
|
|
65
66
|
error: null,
|
|
67
|
+
result: null,
|
|
66
68
|
startedAt: new Date().toISOString(),
|
|
67
69
|
finishedAt: null,
|
|
68
70
|
};
|
|
@@ -76,11 +78,12 @@ export function createKnowledgeScanJobs() {
|
|
|
76
78
|
};
|
|
77
79
|
const controller = new AbortController();
|
|
78
80
|
controllers.set(job.id, controller);
|
|
79
|
-
run(report, controller.signal).then(() => {
|
|
81
|
+
run(report, controller.signal).then((result: unknown) => {
|
|
80
82
|
if (job.state !== 'running') return;
|
|
81
83
|
job.state = 'done';
|
|
82
84
|
job.phase = 'commit';
|
|
83
85
|
job.percent = 100;
|
|
86
|
+
job.result = result ?? null;
|
|
84
87
|
}, (error: unknown) => {
|
|
85
88
|
if (job.state !== 'running') return;
|
|
86
89
|
job.state = 'error';
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { randomBytes } from 'node:crypto';
|
|
5
|
-
import { createRequire } from 'node:module';
|
|
6
4
|
|
|
7
5
|
import express from 'express';
|
|
8
6
|
|
|
@@ -15,44 +13,26 @@ import { scanAndPersistLibraryProviderSkills } from './provider-skills.js';
|
|
|
15
13
|
|
|
16
14
|
export { installLibraryRuntime } from './runtime.js';
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
const LIBRARY_BODY_LIMIT = 1024 * 1024;
|
|
17
|
+
|
|
18
|
+
export async function openLibrary(home: string, applicationRoot: string) {
|
|
19
19
|
const store = createLibraryStore(home);
|
|
20
20
|
try { await scanAndPersistLibraryProviderSkills(store, undefined, undefined, os.homedir()); } catch {}
|
|
21
21
|
try { scanAndPersistLibraryProviderAgents(store, os.homedir()); } catch {}
|
|
22
22
|
try { scanAndPersistLibraryProviderConnectors(store, os.homedir()); } catch {}
|
|
23
23
|
try { scanInstalledLibraryCollections(store, os.homedir()); } catch {}
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
try { fs.unlinkSync(path.join(store.directory, 'connection.json')); } catch {}
|
|
25
|
+
const router = express.Router();
|
|
26
|
+
router.use((req, res, next) => {
|
|
27
|
+
const length = Number(req.headers['content-length'] || 0);
|
|
28
|
+
if (Number.isFinite(length) && length > LIBRARY_BODY_LIMIT) {
|
|
29
|
+
res.status(413).json({ error: 'Requête trop volumineuse.' });
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
31
32
|
res.setHeader('Cache-Control', 'no-store');
|
|
32
33
|
next();
|
|
33
34
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
app.use((req, res, next) => {
|
|
38
|
-
if ((req.method === 'GET' && req.path === '/activation')
|
|
39
|
-
|| (req.method === 'POST' && req.path === '/activation/toggle')) { next(); return; }
|
|
40
|
-
res.sendStatus(404);
|
|
41
|
-
});
|
|
42
|
-
app.use(createActivationRouter(options));
|
|
43
|
-
const server = app.listen(0, '127.0.0.1');
|
|
44
|
-
await new Promise<void>((resolve, reject) => { server.once('listening', resolve); server.once('error', reject); });
|
|
45
|
-
const address = server.address();
|
|
46
|
-
if (!address || typeof address === 'string') throw new Error('Bibliothèque indisponible.');
|
|
47
|
-
const connection = path.join(store.directory, 'connection.json');
|
|
48
|
-
const temporary = `${connection}.${process.pid}.tmp`;
|
|
49
|
-
fs.writeFileSync(temporary, JSON.stringify({ port: address.port, token }), { mode: 0o600 });
|
|
50
|
-
fs.renameSync(temporary, connection);
|
|
51
|
-
server.unref();
|
|
52
|
-
process.once('exit', () => {
|
|
53
|
-
try {
|
|
54
|
-
if (JSON.parse(fs.readFileSync(connection, 'utf8')).token === token) fs.unlinkSync(connection);
|
|
55
|
-
} catch {}
|
|
56
|
-
});
|
|
57
|
-
return store;
|
|
35
|
+
router.use(createLibraryRouter(store));
|
|
36
|
+
router.use(createLibraryMarketplaceRouter(store, applicationRoot, os.homedir()));
|
|
37
|
+
return { store, router };
|
|
58
38
|
}
|
|
@@ -7,7 +7,7 @@ import Database from 'better-sqlite3';
|
|
|
7
7
|
import { parseFrontMatter } from '@/shared/frontmatter.js';
|
|
8
8
|
|
|
9
9
|
import { parseConnectorConfig, prepareConnectorInstallation, type LibraryConnectorConfig } from './connector-installation.js';
|
|
10
|
-
import { prepareWorkspaceInstallation } from './workspace-installation.js';
|
|
10
|
+
import { componentFolderName, prepareWorkspaceInstallation } from './workspace-installation.js';
|
|
11
11
|
|
|
12
12
|
type StoredLibraryEntry = {
|
|
13
13
|
id: string;
|
|
@@ -64,6 +64,11 @@ function decodeEditableFile(bytes: Buffer) {
|
|
|
64
64
|
return decodeUtf8File(bytes);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
function installationFolder(content: string, fallback = '') {
|
|
68
|
+
const { data } = parseFrontMatter(content);
|
|
69
|
+
return componentFolderName(String(data.name || data.metadata?.title || fallback));
|
|
70
|
+
}
|
|
71
|
+
|
|
67
72
|
function collectionMainPath(entry: LibraryEntry, rootPath: string) {
|
|
68
73
|
return entry.kind === 'skill' && !rootPath.toLowerCase().endsWith('.md') ? `${rootPath}/SKILL.md` : rootPath;
|
|
69
74
|
}
|
|
@@ -219,6 +224,10 @@ export function createLibraryStore(home: string) {
|
|
|
219
224
|
return db.transaction(() => {
|
|
220
225
|
const entry = document(id);
|
|
221
226
|
if (entry.content !== previousContent) throw new Error('Le document a été modifié ailleurs. Rouvrez-le avant d’enregistrer.');
|
|
227
|
+
const active = db.prepare('SELECT workspace FROM activation WHERE entry_id = ?').all(id) as Array<{ workspace: string }>;
|
|
228
|
+
if (entry.kind !== 'connector' && installationFolder(content) !== installationFolder(entry.content)) {
|
|
229
|
+
for (const { workspace: selected } of active) setEnabled(selected, id, false);
|
|
230
|
+
}
|
|
222
231
|
if (entry.kind === 'connector') {
|
|
223
232
|
const config = parseConnectorConfig(content);
|
|
224
233
|
const description = config.url || config.command || entry.name;
|
|
@@ -229,7 +238,6 @@ export function createLibraryStore(home: string) {
|
|
|
229
238
|
const description = typeof data.description === 'string' ? data.description : '';
|
|
230
239
|
db.prepare('UPDATE entries SET name = ?, description = ?, content = ? WHERE id = ?').run(name, description, content, id);
|
|
231
240
|
}
|
|
232
|
-
const active = db.prepare('SELECT workspace FROM activation WHERE entry_id = ?').all(id) as Array<{ workspace: string }>;
|
|
233
241
|
for (const { workspace: selected } of active) setEnabled(selected, id, true);
|
|
234
242
|
return document(id);
|
|
235
243
|
})();
|
|
@@ -465,7 +473,7 @@ export function createLibraryStore(home: string) {
|
|
|
465
473
|
return { ok: true, enabled };
|
|
466
474
|
}
|
|
467
475
|
const packageRoot = path.join(directory, 'active', createHash('sha256').update(selected).digest('hex'), id);
|
|
468
|
-
const install = prepareWorkspaceInstallation(selected, id, packageRoot, entry.kind, enabled);
|
|
476
|
+
const install = prepareWorkspaceInstallation(selected, id, installationFolder(entry.content, entry.name), packageRoot, entry.kind, enabled);
|
|
469
477
|
if (enabled) {
|
|
470
478
|
fs.mkdirSync(path.dirname(packageRoot), { recursive: true, mode: 0o700 });
|
|
471
479
|
if (!fs.realpathSync(path.dirname(packageRoot)).startsWith(fs.realpathSync(directory) + path.sep)) throw new Error('Répertoire d’activation non autorisé.');
|
|
@@ -10,6 +10,12 @@ const AGENT_TARGETS = [
|
|
|
10
10
|
{ provider: '.grok', folder: 'agents', extension: '.md', sourceFile: 'agent.md' },
|
|
11
11
|
] as const;
|
|
12
12
|
|
|
13
|
+
export function componentFolderName(name: string) {
|
|
14
|
+
const slug = String(name).normalize('NFKD').replace(/[^\p{Letter}\p{Number}._-]+/gu, '-').replace(/^[-._]+|[-._]+$/g, '');
|
|
15
|
+
if (!slug) throw new Error(`Nom de composant inutilisable : ${name}`);
|
|
16
|
+
return slug;
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
function assertWorkspaceDirectory(workspace: string, directory: string, enabled: boolean) {
|
|
14
20
|
if (!fs.existsSync(directory)) {
|
|
15
21
|
if (enabled) fs.mkdirSync(directory);
|
|
@@ -20,14 +26,16 @@ function assertWorkspaceDirectory(workspace: string, directory: string, enabled:
|
|
|
20
26
|
}
|
|
21
27
|
}
|
|
22
28
|
|
|
23
|
-
export function prepareWorkspaceInstallation(workspace: string, id: string, packageRoot: string, kind: 'skill' | 'agent', enabled: boolean) {
|
|
29
|
+
export function prepareWorkspaceInstallation(workspace: string, id: string, name: string, packageRoot: string, kind: 'skill' | 'agent', enabled: boolean) {
|
|
30
|
+
const slug = componentFolderName(name);
|
|
24
31
|
const targets = kind === 'skill'
|
|
25
32
|
? SKILL_PROVIDERS.map((provider) => {
|
|
26
33
|
const parent = path.join(workspace, provider, 'skills');
|
|
27
34
|
for (const directory of [path.dirname(parent), parent]) assertWorkspaceDirectory(workspace, directory, enabled);
|
|
28
35
|
return {
|
|
29
36
|
parent,
|
|
30
|
-
target: path.join(parent,
|
|
37
|
+
target: path.join(parent, slug),
|
|
38
|
+
legacy: path.join(parent, `piecemaker-${id}`),
|
|
31
39
|
source: packageRoot,
|
|
32
40
|
kind: 'skill' as const,
|
|
33
41
|
};
|
|
@@ -37,7 +45,8 @@ export function prepareWorkspaceInstallation(workspace: string, id: string, pack
|
|
|
37
45
|
for (const directory of [path.dirname(parent), parent]) assertWorkspaceDirectory(workspace, directory, enabled);
|
|
38
46
|
return {
|
|
39
47
|
parent,
|
|
40
|
-
target: path.join(parent,
|
|
48
|
+
target: path.join(parent, `${slug}${entry.extension}`),
|
|
49
|
+
legacy: path.join(parent, `piecemaker-${id}${entry.extension}`),
|
|
41
50
|
source: path.join(packageRoot, entry.sourceFile),
|
|
42
51
|
kind: 'agent' as const,
|
|
43
52
|
};
|
|
@@ -82,5 +91,13 @@ export function prepareWorkspaceInstallation(workspace: string, id: string, pack
|
|
|
82
91
|
}
|
|
83
92
|
throw error;
|
|
84
93
|
}
|
|
94
|
+
for (const { legacy, source, kind: targetKind } of prepared) {
|
|
95
|
+
let existing: fs.Stats;
|
|
96
|
+
try { existing = fs.lstatSync(legacy); } catch { continue; }
|
|
97
|
+
const owned = targetKind === 'skill'
|
|
98
|
+
? existing.isSymbolicLink() && fs.existsSync(source) && fs.realpathSync(legacy) === fs.realpathSync(source)
|
|
99
|
+
: existing.isFile() && fs.existsSync(source) && fs.readFileSync(legacy).equals(fs.readFileSync(source));
|
|
100
|
+
if (owned) fs.unlinkSync(legacy);
|
|
101
|
+
}
|
|
85
102
|
};
|
|
86
103
|
}
|
|
@@ -36,7 +36,7 @@ type SessionRecord = ReturnType<typeof sessionsDb.getAllSessions>[number];
|
|
|
36
36
|
type TimesheetStore = {
|
|
37
37
|
upsert(entry: TimesheetEntry): void;
|
|
38
38
|
findBySessionId(sessionId: string): TimesheetEntry | null;
|
|
39
|
-
list(
|
|
39
|
+
list(projectPath?: string): TimesheetEntry[];
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
type TimesheetDependencies = {
|
|
@@ -154,10 +154,13 @@ export function createTimesheetService(
|
|
|
154
154
|
return project;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
function projectPathFor(query: TimesheetQuery): string | undefined {
|
|
158
|
+
if (query.scope !== 'project') return undefined;
|
|
159
|
+
return resolveProject(query.projectId as string).project_path;
|
|
160
|
+
}
|
|
161
|
+
|
|
157
162
|
function sessionsFor(query: TimesheetQuery): SessionRecord[] {
|
|
158
|
-
const projectPath = query
|
|
159
|
-
? resolveProject(query.projectId as string).project_path
|
|
160
|
-
: null;
|
|
163
|
+
const projectPath = projectPathFor(query) ?? null;
|
|
161
164
|
const activeSessions = projectPath
|
|
162
165
|
? dependencies.sessions.getSessionsByProjectPath(projectPath)
|
|
163
166
|
: dependencies.sessions.getAllSessions();
|
|
@@ -197,7 +200,7 @@ export function createTimesheetService(
|
|
|
197
200
|
}
|
|
198
201
|
|
|
199
202
|
return {
|
|
200
|
-
entries: store.list(query
|
|
203
|
+
entries: store.list(projectPathFor(query)).map(toPublicEntry),
|
|
201
204
|
refreshedAt: new Date().toISOString(),
|
|
202
205
|
refreshedCount,
|
|
203
206
|
skippedCount,
|
|
@@ -221,8 +224,7 @@ export function createTimesheetService(
|
|
|
221
224
|
|
|
222
225
|
return {
|
|
223
226
|
list(query: TimesheetQuery): TimesheetPublicEntry[] {
|
|
224
|
-
|
|
225
|
-
return store.list(query.scope === 'project' ? query.projectId : undefined).map(toPublicEntry);
|
|
227
|
+
return store.list(projectPathFor(query)).map(toPublicEntry);
|
|
226
228
|
},
|
|
227
229
|
refresh,
|
|
228
230
|
};
|
|
@@ -60,6 +60,8 @@ function createSchema(database: TimesheetDatabase): void {
|
|
|
60
60
|
);
|
|
61
61
|
CREATE INDEX IF NOT EXISTS timesheet_project_date
|
|
62
62
|
ON timesheet_sessions(project_id, started_at);
|
|
63
|
+
CREATE INDEX IF NOT EXISTS timesheet_project_path_date
|
|
64
|
+
ON timesheet_sessions(project_path, started_at);
|
|
63
65
|
CREATE INDEX IF NOT EXISTS timesheet_date
|
|
64
66
|
ON timesheet_sessions(started_at);
|
|
65
67
|
`);
|
|
@@ -144,7 +146,7 @@ export function createTimesheetStore(homeDir: string) {
|
|
|
144
146
|
`);
|
|
145
147
|
const findBySessionId = database.prepare('SELECT * FROM timesheet_sessions WHERE session_id = ?');
|
|
146
148
|
const listAll = database.prepare('SELECT * FROM timesheet_sessions ORDER BY datetime(started_at) DESC, session_id DESC');
|
|
147
|
-
const
|
|
149
|
+
const listByProjectPath = database.prepare('SELECT * FROM timesheet_sessions WHERE project_path = ? ORDER BY datetime(started_at) DESC, session_id DESC');
|
|
148
150
|
|
|
149
151
|
return {
|
|
150
152
|
databasePath,
|
|
@@ -156,8 +158,8 @@ export function createTimesheetStore(homeDir: string) {
|
|
|
156
158
|
const row = findBySessionId.get(sessionId) as TimesheetEntryRow | undefined;
|
|
157
159
|
return row ? toEntry(row) : null;
|
|
158
160
|
},
|
|
159
|
-
list(
|
|
160
|
-
const rows = (
|
|
161
|
+
list(projectPath?: string): TimesheetEntry[] {
|
|
162
|
+
const rows = (projectPath ? listByProjectPath.all(projectPath) : listAll.all()) as TimesheetEntryRow[];
|
|
161
163
|
return rows.map(toEntry);
|
|
162
164
|
},
|
|
163
165
|
close(): void {
|
|
@@ -42,7 +42,6 @@ const CONFIG_FILE = path.join(os.homedir(), '.claude', 'piecemaker-secret-paths.
|
|
|
42
42
|
/** Never empty: at minimum, the general PieceMaker runtime .env. */
|
|
43
43
|
const DEFAULT_BLOCKLIST = [
|
|
44
44
|
path.join(os.homedir(), 'PieceMaker', '.env'),
|
|
45
|
-
path.join(os.homedir(), 'Sites', 'PieceMaker-Installer', '.env'),
|
|
46
45
|
];
|
|
47
46
|
|
|
48
47
|
const PATH_TOOLS = new Set(['Read', 'Grep', 'Glob', 'Edit', 'Write', 'NotebookEdit', 'MultiEdit']);
|
|
Binary file
|
|
Binary file
|