@aicqtools/provenance 1.0.0-alpha.2

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.
Files changed (82) hide show
  1. package/LICENSE +21 -0
  2. package/dist/ai-bom/cyclonedx.d.ts +31 -0
  3. package/dist/ai-bom/cyclonedx.d.ts.map +1 -0
  4. package/dist/ai-bom/cyclonedx.js +29 -0
  5. package/dist/ai-bom/cyclonedx.js.map +1 -0
  6. package/dist/ai-bom/index.d.ts +2 -0
  7. package/dist/ai-bom/index.d.ts.map +1 -0
  8. package/dist/ai-bom/index.js +2 -0
  9. package/dist/ai-bom/index.js.map +1 -0
  10. package/dist/git-hook/capture.d.ts +14 -0
  11. package/dist/git-hook/capture.d.ts.map +1 -0
  12. package/dist/git-hook/capture.js +20 -0
  13. package/dist/git-hook/capture.js.map +1 -0
  14. package/dist/git-hook/git.d.ts +9 -0
  15. package/dist/git-hook/git.d.ts.map +1 -0
  16. package/dist/git-hook/git.js +54 -0
  17. package/dist/git-hook/git.js.map +1 -0
  18. package/dist/git-hook/index.d.ts +7 -0
  19. package/dist/git-hook/index.d.ts.map +1 -0
  20. package/dist/git-hook/index.js +5 -0
  21. package/dist/git-hook/index.js.map +1 -0
  22. package/dist/git-hook/persist.d.ts +4 -0
  23. package/dist/git-hook/persist.d.ts.map +1 -0
  24. package/dist/git-hook/persist.js +16 -0
  25. package/dist/git-hook/persist.js.map +1 -0
  26. package/dist/git-hook/session-source.d.ts +7 -0
  27. package/dist/git-hook/session-source.d.ts.map +1 -0
  28. package/dist/git-hook/session-source.js +39 -0
  29. package/dist/git-hook/session-source.js.map +1 -0
  30. package/dist/index.d.ts +9 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +5 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/reporter/article-50-html.d.ts +6 -0
  35. package/dist/reporter/article-50-html.d.ts.map +1 -0
  36. package/dist/reporter/article-50-html.js +143 -0
  37. package/dist/reporter/article-50-html.js.map +1 -0
  38. package/dist/reporter/article-50-pdf.d.ts +28 -0
  39. package/dist/reporter/article-50-pdf.d.ts.map +1 -0
  40. package/dist/reporter/article-50-pdf.js +36 -0
  41. package/dist/reporter/article-50-pdf.js.map +1 -0
  42. package/dist/reporter/article-50.d.ts +25 -0
  43. package/dist/reporter/article-50.d.ts.map +1 -0
  44. package/dist/reporter/article-50.js +29 -0
  45. package/dist/reporter/article-50.js.map +1 -0
  46. package/dist/reporter/index.d.ts +7 -0
  47. package/dist/reporter/index.d.ts.map +1 -0
  48. package/dist/reporter/index.js +4 -0
  49. package/dist/reporter/index.js.map +1 -0
  50. package/dist/session-readers/claude-code.d.ts +14 -0
  51. package/dist/session-readers/claude-code.d.ts.map +1 -0
  52. package/dist/session-readers/claude-code.js +105 -0
  53. package/dist/session-readers/claude-code.js.map +1 -0
  54. package/dist/session-readers/composite.d.ts +8 -0
  55. package/dist/session-readers/composite.d.ts.map +1 -0
  56. package/dist/session-readers/composite.js +16 -0
  57. package/dist/session-readers/composite.js.map +1 -0
  58. package/dist/session-readers/cursor.d.ts +27 -0
  59. package/dist/session-readers/cursor.d.ts.map +1 -0
  60. package/dist/session-readers/cursor.js +74 -0
  61. package/dist/session-readers/cursor.js.map +1 -0
  62. package/dist/session-readers/factory.d.ts +4 -0
  63. package/dist/session-readers/factory.d.ts.map +1 -0
  64. package/dist/session-readers/factory.js +17 -0
  65. package/dist/session-readers/factory.js.map +1 -0
  66. package/dist/session-readers/index.d.ts +10 -0
  67. package/dist/session-readers/index.d.ts.map +1 -0
  68. package/dist/session-readers/index.js +6 -0
  69. package/dist/session-readers/index.js.map +1 -0
  70. package/dist/session-readers/manual.d.ts +6 -0
  71. package/dist/session-readers/manual.d.ts.map +1 -0
  72. package/dist/session-readers/manual.js +8 -0
  73. package/dist/session-readers/manual.js.map +1 -0
  74. package/dist/session-readers/types.d.ts +10 -0
  75. package/dist/session-readers/types.d.ts.map +1 -0
  76. package/dist/session-readers/types.js +2 -0
  77. package/dist/session-readers/types.js.map +1 -0
  78. package/dist/types.d.ts +39 -0
  79. package/dist/types.d.ts.map +1 -0
  80. package/dist/types.js +9 -0
  81. package/dist/types.js.map +1 -0
  82. package/package.json +61 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Eom Sik (aicqtools)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,31 @@
1
+ import type { ProvenanceRecord } from '../types.js';
2
+ /**
3
+ * CycloneDX AI-BOM emitter (skeleton).
4
+ *
5
+ * Phase 0 emits a minimal but spec-shaped document. Phase 1a fills in
6
+ * model cards, training data references, and risk classifications per
7
+ * EU AI Act Annex IV.
8
+ */
9
+ interface CycloneDxAiBom {
10
+ bomFormat: 'CycloneDX';
11
+ specVersion: '1.6';
12
+ version: 1;
13
+ serialNumber: string;
14
+ metadata: {
15
+ timestamp: string;
16
+ tools: Array<{
17
+ vendor: string;
18
+ name: string;
19
+ version: string;
20
+ }>;
21
+ };
22
+ components: Array<{
23
+ type: 'machine-learning-model';
24
+ name: string;
25
+ version?: string;
26
+ 'bom-ref': string;
27
+ }>;
28
+ }
29
+ export declare function emitAiBom(record: ProvenanceRecord, toolVersion?: string): CycloneDxAiBom;
30
+ export {};
31
+ //# sourceMappingURL=cyclonedx.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cyclonedx.d.ts","sourceRoot":"","sources":["../../src/ai-bom/cyclonedx.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD;;;;;;GAMG;AAEH,UAAU,cAAc;IACtB,SAAS,EAAE,WAAW,CAAC;IACvB,WAAW,EAAE,KAAK,CAAC;IACnB,OAAO,EAAE,CAAC,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE;QACR,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,KAAK,CAAC;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACjE,CAAC;IACF,UAAU,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,wBAAwB,CAAC;QAC/B,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,CAAC;CACJ;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,gBAAgB,EAAE,WAAW,SAAU,GAAG,cAAc,CA2BzF"}
@@ -0,0 +1,29 @@
1
+ export function emitAiBom(record, toolVersion = '0.0.0') {
2
+ const models = new Map();
3
+ for (const session of record.sessions) {
4
+ const key = `${session.model}@${session.modelVersion ?? 'unspecified'}`;
5
+ if (!models.has(key)) {
6
+ models.set(key, {
7
+ name: session.model,
8
+ ...(session.modelVersion !== undefined ? { version: session.modelVersion } : {}),
9
+ });
10
+ }
11
+ }
12
+ return {
13
+ bomFormat: 'CycloneDX',
14
+ specVersion: '1.6',
15
+ version: 1,
16
+ serialNumber: `urn:uuid:${crypto.randomUUID()}`,
17
+ metadata: {
18
+ timestamp: record.capturedAt,
19
+ tools: [{ vendor: 'aicq', name: 'provenance', version: toolVersion }],
20
+ },
21
+ components: [...models.entries()].map(([ref, m]) => ({
22
+ type: 'machine-learning-model',
23
+ name: m.name,
24
+ ...(m.version !== undefined ? { version: m.version } : {}),
25
+ 'bom-ref': ref,
26
+ })),
27
+ };
28
+ }
29
+ //# sourceMappingURL=cyclonedx.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cyclonedx.js","sourceRoot":"","sources":["../../src/ai-bom/cyclonedx.ts"],"names":[],"mappings":"AA2BA,MAAM,UAAU,SAAS,CAAC,MAAwB,EAAE,WAAW,GAAG,OAAO;IACvE,MAAM,MAAM,GAAG,IAAI,GAAG,EAA8C,CAAC;IACrE,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,KAAK,IAAI,OAAO,CAAC,YAAY,IAAI,aAAa,EAAE,CAAC;QACxE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE;gBACd,IAAI,EAAE,OAAO,CAAC,KAAK;gBACnB,GAAG,CAAC,OAAO,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACjF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO;QACL,SAAS,EAAE,WAAW;QACtB,WAAW,EAAE,KAAK;QAClB,OAAO,EAAE,CAAC;QACV,YAAY,EAAE,YAAY,MAAM,CAAC,UAAU,EAAE,EAAE;QAC/C,QAAQ,EAAE;YACR,SAAS,EAAE,MAAM,CAAC,UAAU;YAC5B,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC;SACtE;QACD,UAAU,EAAE,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACnD,IAAI,EAAE,wBAAwB;YAC9B,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,GAAG,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1D,SAAS,EAAE,GAAG;SACf,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { emitAiBom } from './cyclonedx.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ai-bom/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { emitAiBom } from './cyclonedx.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ai-bom/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { SessionReader } from '../session-readers/types.js';
2
+ import type { AiSession, AiPromptRecord, CodeAttribution } from '../types.js';
3
+ export interface CaptureContext {
4
+ readonly cwd: string;
5
+ readonly commitTimestamp: string;
6
+ readonly reader?: SessionReader;
7
+ }
8
+ export interface CaptureResult {
9
+ readonly sessions: readonly AiSession[];
10
+ readonly prompts: readonly AiPromptRecord[];
11
+ readonly attributions: readonly CodeAttribution[];
12
+ }
13
+ export declare function capture(ctx: CaptureContext): Promise<CaptureResult>;
14
+ //# sourceMappingURL=capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../../src/git-hook/capture.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAI9E,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,SAAS,EAAE,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;IAC5C,QAAQ,CAAC,YAAY,EAAE,SAAS,eAAe,EAAE,CAAC;CACnD;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAkBzE"}
@@ -0,0 +1,20 @@
1
+ import { ManualSessionReader } from '../session-readers/manual.js';
2
+ import { getStagedHunks } from './git.js';
3
+ import { findClosestSession } from './session-source.js';
4
+ export async function capture(ctx) {
5
+ const reader = ctx.reader ?? new ManualSessionReader();
6
+ const [{ sessions, prompts }, hunks] = await Promise.all([
7
+ reader.read(ctx.cwd),
8
+ getStagedHunks(ctx.cwd),
9
+ ]);
10
+ const closest = findClosestSession(sessions, ctx.commitTimestamp);
11
+ const attributions = hunks.map((h) => ({
12
+ filePath: h.filePath,
13
+ startLine: h.startLine,
14
+ endLine: h.endLine,
15
+ sessionId: closest?.sessionId ?? 'unknown',
16
+ humanEdited: closest === null,
17
+ }));
18
+ return { sessions, prompts, attributions };
19
+ }
20
+ //# sourceMappingURL=capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/git-hook/capture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAGnE,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAczD,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,GAAmB;IAC/C,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,IAAI,mBAAmB,EAAE,CAAC;IACvD,MAAM,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACvD,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;QACpB,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC;KACxB,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,kBAAkB,CAAC,QAAQ,EAAE,GAAG,CAAC,eAAe,CAAC,CAAC;IAElE,MAAM,YAAY,GAAsB,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACxD,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,OAAO,EAAE,CAAC,CAAC,OAAO;QAClB,SAAS,EAAE,OAAO,EAAE,SAAS,IAAI,SAAS;QAC1C,WAAW,EAAE,OAAO,KAAK,IAAI;KAC9B,CAAC,CAAC,CAAC;IAEJ,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,9 @@
1
+ export declare function getStagedFiles(cwd: string): Promise<string[]>;
2
+ export interface StagedHunk {
3
+ readonly filePath: string;
4
+ readonly startLine: number;
5
+ readonly endLine: number;
6
+ }
7
+ export declare function getStagedHunks(cwd: string): Promise<StagedHunk[]>;
8
+ export declare function getCurrentCommitSha(cwd: string): Promise<string | null>;
9
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/git-hook/git.ts"],"names":[],"mappings":"AAKA,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAWnE;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAKD,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CA+BvE;AAED,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAO7E"}
@@ -0,0 +1,54 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ const execFileAsync = promisify(execFile);
4
+ export async function getStagedFiles(cwd) {
5
+ try {
6
+ const { stdout } = await execFileAsync('git', ['diff', '--cached', '--name-only', '--diff-filter=AM'], { cwd, maxBuffer: 16 * 1024 * 1024 });
7
+ return stdout.split('\n').filter((line) => line.length > 0);
8
+ }
9
+ catch {
10
+ return [];
11
+ }
12
+ }
13
+ const HUNK_HEADER = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/;
14
+ const FILE_HEADER = /^\+\+\+ b\/(.+)$/;
15
+ export async function getStagedHunks(cwd) {
16
+ try {
17
+ const { stdout } = await execFileAsync('git', ['diff', '--cached', '--unified=0', '--no-color'], { cwd, maxBuffer: 32 * 1024 * 1024 });
18
+ const hunks = [];
19
+ let currentFile = null;
20
+ for (const line of stdout.split('\n')) {
21
+ const fileMatch = FILE_HEADER.exec(line);
22
+ if (fileMatch) {
23
+ currentFile = fileMatch[1] ?? null;
24
+ continue;
25
+ }
26
+ const hunkMatch = HUNK_HEADER.exec(line);
27
+ if (hunkMatch && currentFile) {
28
+ const start = parseInt(hunkMatch[1] ?? '0', 10);
29
+ const count = hunkMatch[2] !== undefined ? parseInt(hunkMatch[2], 10) : 1;
30
+ if (count === 0)
31
+ continue;
32
+ hunks.push({
33
+ filePath: currentFile,
34
+ startLine: start,
35
+ endLine: start + count - 1,
36
+ });
37
+ }
38
+ }
39
+ return hunks;
40
+ }
41
+ catch {
42
+ return [];
43
+ }
44
+ }
45
+ export async function getCurrentCommitSha(cwd) {
46
+ try {
47
+ const { stdout } = await execFileAsync('git', ['rev-parse', 'HEAD'], { cwd });
48
+ return stdout.trim();
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
54
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/git-hook/git.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CACpC,KAAK,EACL,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,kBAAkB,CAAC,EACvD,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CACrC,CAAC;QACF,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAQD,MAAM,WAAW,GAAG,yCAAyC,CAAC;AAC9D,MAAM,WAAW,GAAG,kBAAkB,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CACpC,KAAK,EACL,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,CAAC,EACjD,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,CACrC,CAAC;QACF,MAAM,KAAK,GAAiB,EAAE,CAAC;QAC/B,IAAI,WAAW,GAAkB,IAAI,CAAC;QACtC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,SAAS,EAAE,CAAC;gBACd,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;gBACnC,SAAS;YACX,CAAC;YACD,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,SAAS,IAAI,WAAW,EAAE,CAAC;gBAC7B,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;gBAChD,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC1E,IAAI,KAAK,KAAK,CAAC;oBAAE,SAAS;gBAC1B,KAAK,CAAC,IAAI,CAAC;oBACT,QAAQ,EAAE,WAAW;oBACrB,SAAS,EAAE,KAAK;oBAChB,OAAO,EAAE,KAAK,GAAG,KAAK,GAAG,CAAC;iBAC3B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,GAAW;IACnD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QAC9E,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { capture } from './capture.js';
2
+ export type { CaptureContext, CaptureResult } from './capture.js';
3
+ export { getStagedFiles, getStagedHunks, getCurrentCommitSha } from './git.js';
4
+ export type { StagedHunk } from './git.js';
5
+ export { readActiveSessions, findClosestSession } from './session-source.js';
6
+ export { writeProvenanceRecord, buildRecord } from './persist.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/git-hook/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC/E,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { capture } from './capture.js';
2
+ export { getStagedFiles, getStagedHunks, getCurrentCommitSha } from './git.js';
3
+ export { readActiveSessions, findClosestSession } from './session-source.js';
4
+ export { writeProvenanceRecord, buildRecord } from './persist.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/git-hook/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAE/E,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { ProvenanceRecord } from '../types.js';
2
+ export declare function writeProvenanceRecord(filePath: string, record: ProvenanceRecord): Promise<void>;
3
+ export declare function buildRecord(partial: Pick<ProvenanceRecord, 'sessions' | 'prompts' | 'attributions'>): ProvenanceRecord;
4
+ //# sourceMappingURL=persist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/git-hook/persist.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAgB,WAAW,CACzB,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,UAAU,GAAG,SAAS,GAAG,cAAc,CAAC,GACvE,gBAAgB,CAQlB"}
@@ -0,0 +1,16 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { dirname } from 'node:path';
3
+ export async function writeProvenanceRecord(filePath, record) {
4
+ await mkdir(dirname(filePath), { recursive: true });
5
+ await writeFile(filePath, JSON.stringify(record, null, 2), 'utf-8');
6
+ }
7
+ export function buildRecord(partial) {
8
+ return {
9
+ version: '0.1',
10
+ sessions: partial.sessions,
11
+ prompts: partial.prompts,
12
+ attributions: partial.attributions,
13
+ capturedAt: new Date().toISOString(),
14
+ };
15
+ }
16
+ //# sourceMappingURL=persist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"persist.js","sourceRoot":"","sources":["../../src/git-hook/persist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,QAAgB,EAChB,MAAwB;IAExB,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,MAAM,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,OAAwE;IAExE,OAAO;QACL,OAAO,EAAE,KAAK;QACd,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACrC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { AiSession, AiPromptRecord } from '../types.js';
2
+ export declare function readActiveSessions(cwd: string): Promise<{
3
+ sessions: AiSession[];
4
+ prompts: AiPromptRecord[];
5
+ }>;
6
+ export declare function findClosestSession(sessions: readonly AiSession[], timestamp: string): AiSession | null;
7
+ //# sourceMappingURL=session-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-source.d.ts","sourceRoot":"","sources":["../../src/git-hook/session-source.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAgB7D,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7D,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B,CAAC,CAaD;AAED,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,SAAS,SAAS,EAAE,EAC9B,SAAS,EAAE,MAAM,GAChB,SAAS,GAAG,IAAI,CAgBlB"}
@@ -0,0 +1,39 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ export async function readActiveSessions(cwd) {
5
+ const path = resolve(cwd, '.aicq/sessions.json');
6
+ if (!existsSync(path))
7
+ return { sessions: [], prompts: [] };
8
+ try {
9
+ const raw = await readFile(path, 'utf-8');
10
+ const parsed = JSON.parse(raw);
11
+ return {
12
+ sessions: parsed.sessions ?? [],
13
+ prompts: parsed.prompts ?? [],
14
+ };
15
+ }
16
+ catch {
17
+ return { sessions: [], prompts: [] };
18
+ }
19
+ }
20
+ export function findClosestSession(sessions, timestamp) {
21
+ if (sessions.length === 0)
22
+ return null;
23
+ const target = Date.parse(timestamp);
24
+ let best = null;
25
+ let bestDelta = Infinity;
26
+ for (const s of sessions) {
27
+ const start = Date.parse(s.startedAt);
28
+ if (Number.isNaN(start))
29
+ continue;
30
+ const end = s.endedAt ? Date.parse(s.endedAt) : Date.now();
31
+ const delta = target >= start && target <= end ? 0 : Math.min(Math.abs(target - start), Math.abs(target - end));
32
+ if (delta < bestDelta) {
33
+ bestDelta = delta;
34
+ best = s;
35
+ }
36
+ }
37
+ return best;
38
+ }
39
+ //# sourceMappingURL=session-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-source.js","sourceRoot":"","sources":["../../src/git-hook/session-source.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiBpC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAW;IAIlD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAE,qBAAqB,CAAC,CAAC;IACjD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IAC5D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAC;QAC/C,OAAO;YACL,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,EAAE;YAC/B,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE;SAC9B,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;IACvC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,QAA8B,EAC9B,SAAiB;IAEjB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACrC,IAAI,IAAI,GAAqB,IAAI,CAAC;IAClC,IAAI,SAAS,GAAG,QAAQ,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,SAAS;QAClC,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3D,MAAM,KAAK,GAAG,MAAM,IAAI,KAAK,IAAI,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,KAAK,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC;QAChH,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;YACtB,SAAS,GAAG,KAAK,CAAC;YAClB,IAAI,GAAG,CAAC,CAAC;QACX,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,9 @@
1
+ export type { AiTool, AiSession, AiPromptRecord, CodeAttribution, ProvenanceRecord, } from './types.js';
2
+ export { emitAiBom } from './ai-bom/index.js';
3
+ export { capture, getStagedFiles, getStagedHunks, getCurrentCommitSha, readActiveSessions, findClosestSession, writeProvenanceRecord, buildRecord, } from './git-hook/index.js';
4
+ export type { CaptureContext, CaptureResult, StagedHunk, } from './git-hook/index.js';
5
+ export { buildArticle50Report, renderArticle50Html, renderArticle50Pdf, } from './reporter/index.js';
6
+ export type { Article50Report, RenderHtmlOptions, RenderPdfOptions, } from './reporter/index.js';
7
+ export { ManualSessionReader, ClaudeCodeSessionReader, CursorSessionReader, CompositeSessionReader, createReader, encodeClaudeProjectId, getCursorWorkspaceStorageDir, } from './session-readers/index.js';
8
+ export type { SessionReader, SessionReaderResult, ClaudeCodeReaderOptions, CursorReaderOptions, ReaderName, } from './session-readers/index.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,MAAM,EACN,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EACL,OAAO,EACP,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,cAAc,EACd,aAAa,EACb,UAAU,GACX,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,EACtB,YAAY,EACZ,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,aAAa,EACb,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,UAAU,GACX,MAAM,4BAA4B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { emitAiBom } from './ai-bom/index.js';
2
+ export { capture, getStagedFiles, getStagedHunks, getCurrentCommitSha, readActiveSessions, findClosestSession, writeProvenanceRecord, buildRecord, } from './git-hook/index.js';
3
+ export { buildArticle50Report, renderArticle50Html, renderArticle50Pdf, } from './reporter/index.js';
4
+ export { ManualSessionReader, ClaudeCodeSessionReader, CursorSessionReader, CompositeSessionReader, createReader, encodeClaudeProjectId, getCursorWorkspaceStorageDir, } from './session-readers/index.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EACL,OAAO,EACP,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,qBAAqB,EACrB,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAM7B,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,qBAAqB,CAAC;AAM7B,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,sBAAsB,EACtB,YAAY,EACZ,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Article50Report } from './article-50.js';
2
+ export interface RenderHtmlOptions {
3
+ readonly locale?: 'ko' | 'en';
4
+ }
5
+ export declare function renderArticle50Html(report: Article50Report, opts?: RenderHtmlOptions): string;
6
+ //# sourceMappingURL=article-50-html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article-50-html.d.ts","sourceRoot":"","sources":["../../src/reporter/article-50-html.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEvD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC/B;AA4CD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,GAAE,iBAAsB,GAAG,MAAM,CA4GjG"}
@@ -0,0 +1,143 @@
1
+ const messages = {
2
+ ko: {
3
+ title: 'EU AI Act Article 50 컴플라이언스 리포트',
4
+ subtitle: 'AI 코드 출처 투명성 명세',
5
+ generatedAt: '생성 일시',
6
+ formatId: '포맷 식별자',
7
+ aiSystems: 'AI 시스템',
8
+ tool: '도구',
9
+ model: '모델',
10
+ modelVersion: '모델 버전',
11
+ sessionCount: '세션 수',
12
+ attributedFiles: '출처 명시 파일',
13
+ none: '(없음)',
14
+ notAvailable: '미상',
15
+ footer: 'aicqtools provenance · MIT License · https://github.com/aicqtools/aicqtools',
16
+ },
17
+ en: {
18
+ title: 'EU AI Act Article 50 Compliance Report',
19
+ subtitle: 'AI code provenance transparency disclosure',
20
+ generatedAt: 'Generated at',
21
+ formatId: 'Format ID',
22
+ aiSystems: 'AI Systems',
23
+ tool: 'Tool',
24
+ model: 'Model',
25
+ modelVersion: 'Model Version',
26
+ sessionCount: 'Session Count',
27
+ attributedFiles: 'Attributed Files',
28
+ none: '(none)',
29
+ notAvailable: 'n/a',
30
+ footer: 'aicqtools provenance · MIT License · https://github.com/aicqtools/aicqtools',
31
+ },
32
+ };
33
+ function escapeHtml(s) {
34
+ return s
35
+ .replace(/&/g, '&amp;')
36
+ .replace(/</g, '&lt;')
37
+ .replace(/>/g, '&gt;')
38
+ .replace(/"/g, '&quot;')
39
+ .replace(/'/g, '&#39;');
40
+ }
41
+ export function renderArticle50Html(report, opts = {}) {
42
+ const locale = opts.locale ?? 'en';
43
+ const m = messages[locale];
44
+ const systemsRows = report.aiSystems.length === 0
45
+ ? `<tr><td colspan="4" class="empty">${m.none}</td></tr>`
46
+ : report.aiSystems
47
+ .map((s) => `<tr>
48
+ <td>${escapeHtml(s.tool)}</td>
49
+ <td>${escapeHtml(s.model)}</td>
50
+ <td>${s.modelVersion ? escapeHtml(s.modelVersion) : `<span class="muted">${m.notAvailable}</span>`}</td>
51
+ <td class="num">${s.sessionCount}</td>
52
+ </tr>`)
53
+ .join('\n');
54
+ const fileItems = report.attributedFiles.length === 0
55
+ ? `<li class="empty">${m.none}</li>`
56
+ : report.attributedFiles.map((f) => `<li><code>${escapeHtml(f)}</code></li>`).join('\n');
57
+ return `<!DOCTYPE html>
58
+ <html lang="${locale}">
59
+ <head>
60
+ <meta charset="utf-8">
61
+ <meta name="viewport" content="width=device-width,initial-scale=1">
62
+ <title>${escapeHtml(m.title)}</title>
63
+ <style>
64
+ :root {
65
+ --fg: #1a1a1a;
66
+ --muted: #6b6b6b;
67
+ --border: #e1e4e8;
68
+ --bg-alt: #f6f8fa;
69
+ --accent: #0366d6;
70
+ }
71
+ * { box-sizing: border-box; }
72
+ body {
73
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Malgun Gothic", "Apple SD Gothic Neo", "Noto Sans CJK KR", sans-serif;
74
+ color: var(--fg);
75
+ line-height: 1.6;
76
+ max-width: 880px;
77
+ margin: 2rem auto;
78
+ padding: 0 1.5rem;
79
+ }
80
+ header { border-bottom: 2px solid var(--fg); padding-bottom: 1rem; margin-bottom: 2rem; }
81
+ h1 { margin: 0 0 0.25rem; font-size: 1.75rem; }
82
+ .subtitle { color: var(--muted); margin: 0; }
83
+ .meta { display: grid; grid-template-columns: max-content 1fr; gap: 0.25rem 1rem; margin-top: 1rem; font-size: 0.9rem; }
84
+ .meta dt { color: var(--muted); }
85
+ .meta dd { margin: 0; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }
86
+ h2 { font-size: 1.25rem; margin-top: 2rem; padding-bottom: 0.25rem; border-bottom: 1px solid var(--border); }
87
+ table { width: 100%; border-collapse: collapse; margin-top: 0.5rem; }
88
+ th, td { padding: 0.5rem 0.75rem; text-align: left; border-bottom: 1px solid var(--border); }
89
+ th { background: var(--bg-alt); font-weight: 600; }
90
+ td.num { text-align: right; font-variant-numeric: tabular-nums; }
91
+ .muted { color: var(--muted); }
92
+ .empty { color: var(--muted); font-style: italic; text-align: center; }
93
+ ul { padding-left: 1.25rem; }
94
+ code { background: var(--bg-alt); padding: 0.1rem 0.4rem; border-radius: 3px; font-size: 0.875rem; }
95
+ footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--border); color: var(--muted); font-size: 0.8rem; text-align: center; }
96
+ @media print {
97
+ body { margin: 0; max-width: none; }
98
+ header { page-break-after: avoid; }
99
+ h2 { page-break-after: avoid; }
100
+ table { page-break-inside: avoid; }
101
+ }
102
+ </style>
103
+ </head>
104
+ <body>
105
+ <header>
106
+ <h1>${escapeHtml(m.title)}</h1>
107
+ <p class="subtitle">${escapeHtml(m.subtitle)}</p>
108
+ <dl class="meta">
109
+ <dt>${m.generatedAt}</dt><dd>${escapeHtml(report.generatedAt)}</dd>
110
+ <dt>${m.formatId}</dt><dd>${escapeHtml(report.format)}</dd>
111
+ </dl>
112
+ </header>
113
+
114
+ <section>
115
+ <h2>${m.aiSystems}</h2>
116
+ <table>
117
+ <thead>
118
+ <tr>
119
+ <th>${m.tool}</th>
120
+ <th>${m.model}</th>
121
+ <th>${m.modelVersion}</th>
122
+ <th class="num">${m.sessionCount}</th>
123
+ </tr>
124
+ </thead>
125
+ <tbody>
126
+ ${systemsRows}
127
+ </tbody>
128
+ </table>
129
+ </section>
130
+
131
+ <section>
132
+ <h2>${m.attributedFiles}</h2>
133
+ <ul>
134
+ ${fileItems}
135
+ </ul>
136
+ </section>
137
+
138
+ <footer>${m.footer}</footer>
139
+ </body>
140
+ </html>
141
+ `;
142
+ }
143
+ //# sourceMappingURL=article-50-html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article-50-html.js","sourceRoot":"","sources":["../../src/reporter/article-50-html.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import type { Article50Report } from './article-50.js';
2
+ import { type RenderHtmlOptions } from './article-50-html.js';
3
+ export interface RenderPdfOptions extends RenderHtmlOptions {
4
+ readonly format?: 'A4' | 'Letter';
5
+ readonly margin?: {
6
+ readonly top?: string;
7
+ readonly right?: string;
8
+ readonly bottom?: string;
9
+ readonly left?: string;
10
+ };
11
+ }
12
+ interface PuppeteerPage {
13
+ setContent(html: string, opts?: {
14
+ waitUntil?: string;
15
+ }): Promise<void>;
16
+ pdf(opts?: unknown): Promise<Uint8Array>;
17
+ }
18
+ interface PuppeteerBrowser {
19
+ newPage(): Promise<PuppeteerPage>;
20
+ close(): Promise<void>;
21
+ }
22
+ interface PuppeteerModule {
23
+ launch(opts?: unknown): Promise<PuppeteerBrowser>;
24
+ }
25
+ export declare function loadPuppeteer(): Promise<PuppeteerModule>;
26
+ export declare function renderArticle50Pdf(report: Article50Report, opts?: RenderPdfOptions): Promise<Uint8Array>;
27
+ export {};
28
+ //# sourceMappingURL=article-50-pdf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article-50-pdf.d.ts","sourceRoot":"","sources":["../../src/reporter/article-50-pdf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAuB,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEnF,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB;IACzD,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC;IAClC,QAAQ,CAAC,MAAM,CAAC,EAAE;QAChB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAED,UAAU,aAAa;IACrB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAC1C;AACD,UAAU,gBAAgB;IACxB,OAAO,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAClC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AACD,UAAU,eAAe;IACvB,MAAM,CAAC,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACnD;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,eAAe,CAAC,CAY9D;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,eAAe,EACvB,IAAI,GAAE,gBAAqB,GAC1B,OAAO,CAAC,UAAU,CAAC,CAoBrB"}
@@ -0,0 +1,36 @@
1
+ import { renderArticle50Html } from './article-50-html.js';
2
+ export async function loadPuppeteer() {
3
+ try {
4
+ // @ts-expect-error — puppeteer is an optional peer dependency, may be unresolved at type-check time
5
+ const mod = await import('puppeteer');
6
+ return (mod.default ?? mod);
7
+ }
8
+ catch {
9
+ throw new Error('puppeteer is required for PDF rendering but is not installed.\n' +
10
+ 'Install it as a peer dependency: `pnpm add puppeteer` (or `npm install puppeteer`).\n' +
11
+ 'See docs/pdf-rendering.md for details.');
12
+ }
13
+ }
14
+ export async function renderArticle50Pdf(report, opts = {}) {
15
+ const html = renderArticle50Html(report, opts);
16
+ const puppeteer = await loadPuppeteer();
17
+ const browser = await puppeteer.launch({ headless: 'new' });
18
+ try {
19
+ const page = await browser.newPage();
20
+ await page.setContent(html, { waitUntil: 'networkidle0' });
21
+ return await page.pdf({
22
+ format: opts.format ?? 'A4',
23
+ printBackground: true,
24
+ margin: opts.margin ?? {
25
+ top: '20mm',
26
+ right: '20mm',
27
+ bottom: '20mm',
28
+ left: '20mm',
29
+ },
30
+ });
31
+ }
32
+ finally {
33
+ await browser.close();
34
+ }
35
+ }
36
+ //# sourceMappingURL=article-50-pdf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"article-50-pdf.js","sourceRoot":"","sources":["../../src/reporter/article-50-pdf.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAA0B,MAAM,sBAAsB,CAAC;AAwBnF,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,IAAI,CAAC;QACH,oGAAoG;QACpG,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAoB,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,iEAAiE;YAC/D,uFAAuF;YACvF,wCAAwC,CAC3C,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAuB,EACvB,OAAyB,EAAE;IAE3B,MAAM,IAAI,GAAG,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC/C,MAAM,SAAS,GAAG,MAAM,aAAa,EAAE,CAAC;IACxC,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IAC5D,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;QACrC,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,CAAC,CAAC;QAC3D,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC;YACpB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI;YAC3B,eAAe,EAAE,IAAI;YACrB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI;gBACrB,GAAG,EAAE,MAAM;gBACX,KAAK,EAAE,MAAM;gBACb,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,MAAM;aACb;SACF,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC"}