layoutbridge-spm 0.0.1 → 1.0.0

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 (79) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/LICENSE +26 -0
  3. package/README.de.md +318 -0
  4. package/README.md +304 -4
  5. package/THIRD-PARTY-NOTICES.md +2878 -0
  6. package/dist/src/mcp/config.js +125 -0
  7. package/dist/src/mcp/dispatch.js +123 -0
  8. package/dist/src/mcp/license/free-tools.js +16 -0
  9. package/dist/src/mcp/license/gate.js +209 -0
  10. package/dist/src/mcp/license/index.js +96 -0
  11. package/dist/src/mcp/license/instance-name.js +56 -0
  12. package/dist/src/mcp/license/key-source.js +58 -0
  13. package/dist/src/mcp/license/lemonsqueezy-verifier.js +207 -0
  14. package/dist/src/mcp/license/placeholder-verifier.js +10 -0
  15. package/dist/src/mcp/license/polar-ids.js +63 -0
  16. package/dist/src/mcp/license/polar-verifier.js +265 -0
  17. package/dist/src/mcp/license/product-ids.js +72 -0
  18. package/dist/src/mcp/license/state-store.js +91 -0
  19. package/dist/src/mcp/license/types.js +11 -0
  20. package/dist/src/mcp/logging.js +270 -0
  21. package/dist/src/mcp/product-info.js +48 -0
  22. package/dist/src/mcp/server.js +250 -0
  23. package/dist/src/mcp/setup.js +608 -0
  24. package/dist/src/mcp/start.cjs +95 -0
  25. package/dist/src/mcp/tool-definition.js +2 -0
  26. package/dist/src/mcp/tools.js +2004 -0
  27. package/dist/src/mcp/typography-rules.js +70 -0
  28. package/dist/src/transport/com.js +250 -0
  29. package/dist/src/transport/osascript.js +171 -0
  30. package/dist/src/transport/queue.js +19 -0
  31. package/dist/src/transport/script-bundle.js +92 -0
  32. package/dist/src/transport/transport.js +30 -0
  33. package/indesign/commands/10-app.js +6 -0
  34. package/indesign/commands/20-documents.js +513 -0
  35. package/indesign/commands/22-document-setup.js +118 -0
  36. package/indesign/commands/23-save-package.js +255 -0
  37. package/indesign/commands/25-export.js +379 -0
  38. package/indesign/commands/26-export-image.js +197 -0
  39. package/indesign/commands/27-textdefaults.js +151 -0
  40. package/indesign/commands/28-selection.js +174 -0
  41. package/indesign/commands/30-pages.js +1032 -0
  42. package/indesign/commands/31-view.js +210 -0
  43. package/indesign/commands/32-sections.js +203 -0
  44. package/indesign/commands/35-parents.js +428 -0
  45. package/indesign/commands/37-layout.js +760 -0
  46. package/indesign/commands/40-frames.js +836 -0
  47. package/indesign/commands/41-shapes.js +505 -0
  48. package/indesign/commands/42-groups.js +117 -0
  49. package/indesign/commands/43-layers.js +291 -0
  50. package/indesign/commands/44-effects.js +274 -0
  51. package/indesign/commands/45-frame-options.js +247 -0
  52. package/indesign/commands/47-anchored.js +287 -0
  53. package/indesign/commands/50-modify.js +781 -0
  54. package/indesign/commands/52-align.js +139 -0
  55. package/indesign/commands/55-fit.js +106 -0
  56. package/indesign/commands/60-images.js +771 -0
  57. package/indesign/commands/62-embed.js +120 -0
  58. package/indesign/commands/69-placeholders.js +127 -0
  59. package/indesign/commands/70-text.js +567 -0
  60. package/indesign/commands/71-outlines.js +92 -0
  61. package/indesign/commands/72-place-text.js +366 -0
  62. package/indesign/commands/74-stories.js +116 -0
  63. package/indesign/commands/75-tables.js +676 -0
  64. package/indesign/commands/76-text-variables.js +260 -0
  65. package/indesign/commands/77-story-options.js +86 -0
  66. package/indesign/commands/78-frame-lines.js +304 -0
  67. package/indesign/commands/79-check-typography.js +303 -0
  68. package/indesign/commands/80-styles.js +1620 -0
  69. package/indesign/commands/81-composition.js +355 -0
  70. package/indesign/commands/82-object-styles.js +263 -0
  71. package/indesign/commands/83-indents-tabs.js +237 -0
  72. package/indesign/commands/84-import-styles.js +142 -0
  73. package/indesign/commands/85-swatches.js +112 -0
  74. package/indesign/commands/86-gradients.js +185 -0
  75. package/indesign/commands/87-replace-swatch.js +180 -0
  76. package/indesign/commands/88-replace-font.js +217 -0
  77. package/indesign/interpreter.js +758 -0
  78. package/package.json +55 -6
  79. package/rules/typography-rules.json +31 -0
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Regelkatalog fuer check_typography (A-7.6): eine Datentabelle im Repo
3
+ * (rules/typography-rules.json), kein Code je Regel. Der Server liest sie
4
+ * einmal, waehlt nach Sprache und Kennungen aus und reicht die Zeilen als
5
+ * Befehlsargument an den Interpreter; der kennt keine Regel von sich aus.
6
+ */
7
+ import { readFileSync } from 'node:fs';
8
+ import { dirname, join } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ export const TYPOGRAPHY_LOCALES = ['de', 'de-CH', 'en'];
11
+ export const SEVERITY_RANK = { info: 0, warning: 1, error: 2 };
12
+ /* Diese Datei laeuft aus dist/src/mcp/ – drei Ebenen ueber ihr liegt das Repo bzw. Paket. */
13
+ export function typographyRulesPath() {
14
+ return join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', 'rules', 'typography-rules.json');
15
+ }
16
+ function isRule(value) {
17
+ if (typeof value !== 'object' || value === null)
18
+ return false;
19
+ const r = value;
20
+ return (typeof r['id'] === 'string' &&
21
+ Array.isArray(r['locales']) &&
22
+ typeof r['category'] === 'string' &&
23
+ typeof r['severity'] === 'string' &&
24
+ (r['kind'] === 'grep' || r['kind'] === 'lines') &&
25
+ (r['kind'] === 'grep' ? typeof r['pattern'] === 'string' : typeof r['check'] === 'string') &&
26
+ typeof r['message'] === 'string' &&
27
+ typeof r['suggestion'] === 'string' &&
28
+ typeof r['source'] === 'string');
29
+ }
30
+ export function parseTypographyRules(text) {
31
+ const raw = JSON.parse(text);
32
+ if (!Array.isArray(raw.rules))
33
+ throw new Error('typography-rules.json: "rules" must be an array');
34
+ const out = [];
35
+ raw.rules.forEach((entry, index) => {
36
+ if (!isRule(entry))
37
+ throw new Error(`typography-rules.json: rule ${index} is malformed`);
38
+ out.push(entry);
39
+ });
40
+ return out;
41
+ }
42
+ let cache = null;
43
+ export function loadTypographyRules(path = typographyRulesPath()) {
44
+ if (cache === null)
45
+ cache = parseTypographyRules(readFileSync(path, 'utf8'));
46
+ return cache;
47
+ }
48
+ /** Alle Kennungen des Katalogs (ohne Doppelte, in Katalogreihenfolge). */
49
+ export function typographyRuleIds(rules = loadTypographyRules()) {
50
+ const out = [];
51
+ for (const rule of rules)
52
+ if (!out.includes(rule.id))
53
+ out.push(rule.id);
54
+ return out;
55
+ }
56
+ /**
57
+ * Zeilen fuer einen Aufruf: Sprache, gewuenschte und ausgeschlossene
58
+ * Kennungen. Unbekannte Kennungen bleiben in unknown[] – der Interpreter
59
+ * meldet sie als invalid_argument, damit der Fehler wie alle anderen im
60
+ * Ergebnisumschlag steht.
61
+ */
62
+ export function selectTypographyRules(locale, wanted, excluded, rules = loadTypographyRules()) {
63
+ const ids = typographyRuleIds(rules);
64
+ const unknown = [...(wanted ?? []), ...(excluded ?? [])].filter((id) => !ids.includes(id));
65
+ const chosen = rules.filter((rule) => rule.locales.includes(locale) &&
66
+ (wanted === undefined || wanted.length === 0 || wanted.includes(rule.id)) &&
67
+ !(excluded ?? []).includes(rule.id));
68
+ return { rules: chosen, unknown };
69
+ }
70
+ //# sourceMappingURL=typography-rules.js.map
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Transport fuer Windows: PowerShell -> COM -> InDesign doScript.
3
+ *
4
+ * Gegenstueck zu osascript.ts. Nur diese Datei weiss, dass hier ein
5
+ * Windows-Rechner steht; der Interpreter und das Befehlsformat {cmd, args}
6
+ * bleiben unveraendert.
7
+ *
8
+ * Ablauf je Aufruf:
9
+ * 1. Interpreter und Handler zu einem Skript zusammensetzen, den Befehl als
10
+ * ASCII-JSON hineinschreiben und in eine temporaere .jsx-Datei legen.
11
+ * 2. Eine .ps1-Datei danebenlegen, die sich die Anwendung ueber COM holt und
12
+ * ihr das Skript per doScript uebergibt. Erst GetActiveObject (Running
13
+ * Object Table), dann CoCreateInstance (New-Object -ComObject): InDesign
14
+ * traegt sich nicht in die Running Object Table ein, CoCreateInstance
15
+ * verbindet aber mit der laufenden Instanz und startet sie sonst - wie
16
+ * `tell application` beim Mac-Transport (Vermerk E-70, 15.09.2026).
17
+ * 3. Das Skript schreibt sein Ergebnis in eine Datei; der Server liest sie.
18
+ * Umweg ueber die Datei wie beim Mac-Transport: die Rueckgabe ueber COM
19
+ * ist in Laenge und Zeichensatz unzuverlaessig.
20
+ *
21
+ * Gebaut ohne Windows-Rechner gegen eine Attrappe des Prozessstarts
22
+ * (Vermerk E-67). Transportweg am 15.09.2026 mit InDesign CS5 (7.0.0.355)
23
+ * auf Windows 11 ARM64 nachgewiesen (Vermerk E-70). Abgenommen am
24
+ * 15.09.2026 (Vermerk E-71): Integrationssuite 108/108 ueber diesen
25
+ * Transport mit InDesign 21.5.1.73 auf Windows 11 x64 (Build 26200,
26
+ * Windows 365 Cloud-PC, Node 24.21.0); scripts/com-test.ps1 Exit 0,
27
+ * scripts/win-smoke.mjs 7/7. Auch das aktuelle InDesign traegt sich nicht
28
+ * in die Running Object Table ein (GetActiveObject: MK_E_UNAVAILABLE),
29
+ * der Rueckfall auf CoCreateInstance traegt.
30
+ */
31
+ import { spawn } from 'node:child_process';
32
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
33
+ import { tmpdir } from 'node:os';
34
+ import { join } from 'node:path';
35
+ import { buildScriptBundle, toAsciiJson } from './script-bundle.js';
36
+ import { transportError, } from './transport.js';
37
+ /** ScriptLanguage.javascript im Objektmodell – als Zahl, weil COM keine Aufzaehlung kennt. */
38
+ export const SCRIPT_LANGUAGE_JAVASCRIPT = 1246973031;
39
+ /** Zeilen, mit denen sich das PowerShell-Skript meldet, wenn es selbst abbricht. */
40
+ export const APP_NOT_AVAILABLE_MARKER = 'IDC-COM-ERROR app_not_available:';
41
+ export const SCRIPT_ERROR_MARKER = 'IDC-COM-ERROR script_error:';
42
+ /** Text in einem PowerShell-Literal mit einfachen Anfuehrungszeichen. */
43
+ export function escapeForPowerShell(text) {
44
+ return text.replace(/'/g, "''");
45
+ }
46
+ /** Text in einem JavaScript-Literal mit doppelten Anfuehrungszeichen. */
47
+ function escapeForJsString(text) {
48
+ return text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
49
+ }
50
+ /**
51
+ * Wertet den Ausgang eines Aufrufs aus. Reine Funktion ohne Dateisystem und
52
+ * ohne Prozess, damit die Einheitstests jede Fehlerart ohne Windows pruefen.
53
+ */
54
+ export function interpretOutcome(outcome, raw, command, timeoutMs) {
55
+ if (outcome.spawnError !== null) {
56
+ return transportError('app_not_available', 'Could not run PowerShell – this transport needs Windows: ' + outcome.spawnError.message, null);
57
+ }
58
+ if (outcome.timedOut) {
59
+ return transportError('timeout', 'InDesign did not answer within ' + timeoutMs +
60
+ ' ms. The script may still be running inside InDesign – check the document before repeating the call.', { command: command.cmd, timeout_ms: timeoutMs });
61
+ }
62
+ const stderr = outcome.stderr.trim();
63
+ const marked = markedMessage(stderr, APP_NOT_AVAILABLE_MARKER);
64
+ if (marked !== null) {
65
+ return transportError('app_not_available', marked, { exit_code: outcome.code });
66
+ }
67
+ const scriptMarked = markedMessage(stderr, SCRIPT_ERROR_MARKER);
68
+ if (scriptMarked !== null) {
69
+ return transportError('script_error', scriptMarked, { exit_code: outcome.code });
70
+ }
71
+ if (raw === null || raw.length === 0) {
72
+ return transportError('script_error', stderr.length > 0
73
+ ? 'InDesign did not return a result: ' + stderr
74
+ : 'InDesign did not return a result and reported nothing.', { exit_code: outcome.code, stdout: outcome.stdout.trim() });
75
+ }
76
+ try {
77
+ const parsed = JSON.parse(raw);
78
+ if (!Array.isArray(parsed.warnings))
79
+ parsed.warnings = [];
80
+ return parsed;
81
+ }
82
+ catch (error) {
83
+ return transportError('script_error', 'The result of the script is not valid JSON: ' + String(error), {
84
+ raw: raw.slice(0, 2000),
85
+ });
86
+ }
87
+ }
88
+ /** Sucht die Markerzeile und gibt den Text dahinter zurueck, sonst null. */
89
+ function markedMessage(stderr, marker) {
90
+ const at = stderr.indexOf(marker);
91
+ if (at < 0)
92
+ return null;
93
+ const rest = stderr.slice(at + marker.length);
94
+ const end = rest.indexOf('\n');
95
+ const line = (end < 0 ? rest : rest.slice(0, end)).trim();
96
+ return line.length > 0 ? line : marker.replace(':', '');
97
+ }
98
+ export class ComTransport {
99
+ name = 'com';
100
+ progIds;
101
+ powershellPath;
102
+ defaultTimeoutMs;
103
+ keepTempFiles;
104
+ runner;
105
+ constructor(options = {}) {
106
+ this.progIds = options.progIds ?? ['InDesign.Application'];
107
+ this.powershellPath = options.powershellPath ?? 'powershell.exe';
108
+ this.defaultTimeoutMs = options.defaultTimeoutMs ?? 120_000;
109
+ this.keepTempFiles = options.keepTempFiles ?? false;
110
+ this.runner = options.runner ?? runProcess;
111
+ }
112
+ /** Das vollstaendige Skript inklusive Rahmen, der das Ergebnis wegschreibt. */
113
+ buildScript(command, resultPath) {
114
+ const bundle = buildScriptBundle();
115
+ const commandLiteral = toAsciiJson(command);
116
+ const target = escapeForJsString(resultPath);
117
+ const frame = [
118
+ '',
119
+ '/* ---- Rahmen des com-Transports (ExtendScript, Windows) ---- */',
120
+ '(function () {',
121
+ ' var __text = IDC.run(' + commandLiteral + ');',
122
+ ' var __file = new File("' + target + '");',
123
+ ' __file.encoding = "UTF-8";',
124
+ ' __file.lineFeed = "Unix";',
125
+ ' __file.open("w");',
126
+ ' __file.write(__text);',
127
+ ' __file.close();',
128
+ ' return "ok";',
129
+ '})();',
130
+ '',
131
+ ].join('\n');
132
+ return bundle + '\n' + frame;
133
+ }
134
+ /**
135
+ * Das PowerShell-Skript: Anwendung ueber COM holen (GetActiveObject, sonst
136
+ * New-Object -ComObject), Skripttext lesen, doScript aufrufen. Bricht es selbst ab, meldet es sich mit einer
137
+ * Markerzeile, damit die Fehlerart nicht aus Fremdtexten geraten wird.
138
+ */
139
+ buildPowerShell(scriptPath) {
140
+ const ids = this.progIds.map((id) => "'" + escapeForPowerShell(id) + "'").join(', ');
141
+ return [
142
+ '$ErrorActionPreference = ' + "'Stop'",
143
+ '$progIds = @(' + ids + ')',
144
+ '$app = $null',
145
+ '$reasons = @()',
146
+ 'foreach ($id in $progIds) {',
147
+ ' try {',
148
+ ' $app = [Runtime.InteropServices.Marshal]::GetActiveObject($id)',
149
+ ' break',
150
+ ' } catch {',
151
+ ' $reasons += ($id + " (GetActiveObject): " + $_.Exception.Message)',
152
+ ' }',
153
+ '}',
154
+ 'if ($null -eq $app) {',
155
+ ' foreach ($id in $progIds) {',
156
+ ' try {',
157
+ ' $app = New-Object -ComObject $id',
158
+ ' break',
159
+ ' } catch {',
160
+ ' $reasons += ($id + " (New-Object): " + $_.Exception.Message)',
161
+ ' }',
162
+ ' }',
163
+ '}',
164
+ 'if ($null -eq $app) {',
165
+ ' [Console]::Error.WriteLine(' + "'" + APP_NOT_AVAILABLE_MARKER +
166
+ " ' + ($reasons -join '; '))",
167
+ ' exit 3',
168
+ '}',
169
+ '$scriptText = [IO.File]::ReadAllText(' + "'" + escapeForPowerShell(scriptPath) +
170
+ "', [Text.Encoding]::UTF8)",
171
+ 'try {',
172
+ ' $null = $app.DoScript($scriptText, ' + SCRIPT_LANGUAGE_JAVASCRIPT + ')',
173
+ '} catch {',
174
+ ' [Console]::Error.WriteLine(' + "'" + SCRIPT_ERROR_MARKER +
175
+ " ' + $_.Exception.Message)",
176
+ ' exit 4',
177
+ '}',
178
+ 'exit 0',
179
+ '',
180
+ ].join('\n');
181
+ }
182
+ async send(command, options = {}) {
183
+ const timeoutMs = options.timeoutMs ?? this.defaultTimeoutMs;
184
+ let directory;
185
+ try {
186
+ directory = mkdtempSync(join(tmpdir(), 'idc-spm-'));
187
+ }
188
+ catch (error) {
189
+ return transportError('io_error', 'Could not create a temporary directory.', String(error));
190
+ }
191
+ const scriptPath = join(directory, 'command.jsx');
192
+ const resultPath = join(directory, 'result.json');
193
+ const powerShellPath = join(directory, 'command.ps1');
194
+ try {
195
+ writeFileSync(scriptPath, this.buildScript(command, resultPath), 'utf8');
196
+ writeFileSync(powerShellPath, this.buildPowerShell(scriptPath), 'utf8');
197
+ const outcome = await this.runner(this.powershellPath, ['-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-File', powerShellPath], timeoutMs);
198
+ let raw = null;
199
+ try {
200
+ raw = readFileSync(resultPath, 'utf8');
201
+ }
202
+ catch {
203
+ raw = null;
204
+ }
205
+ return interpretOutcome(outcome, raw, command, timeoutMs);
206
+ }
207
+ finally {
208
+ if (!this.keepTempFiles) {
209
+ try {
210
+ rmSync(directory, { recursive: true, force: true });
211
+ }
212
+ catch {
213
+ /* Aufraeumen darf keinen Aufruf scheitern lassen. */
214
+ }
215
+ }
216
+ }
217
+ }
218
+ async close() {
219
+ /* Kein dauerhafter Zustand: nichts zu schliessen. */
220
+ }
221
+ }
222
+ function runProcess(command, args, timeoutMs) {
223
+ return new Promise((resolve) => {
224
+ const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'] });
225
+ let stdout = '';
226
+ let stderr = '';
227
+ let timedOut = false;
228
+ let settled = false;
229
+ const timer = setTimeout(() => {
230
+ timedOut = true;
231
+ child.kill('SIGKILL');
232
+ }, timeoutMs);
233
+ child.stdout.on('data', (chunk) => {
234
+ stdout += chunk.toString('utf8');
235
+ });
236
+ child.stderr.on('data', (chunk) => {
237
+ stderr += chunk.toString('utf8');
238
+ });
239
+ const finish = (code, spawnError) => {
240
+ if (settled)
241
+ return;
242
+ settled = true;
243
+ clearTimeout(timer);
244
+ resolve({ code, stdout, stderr, timedOut, spawnError });
245
+ };
246
+ child.on('error', (error) => finish(null, error));
247
+ child.on('close', (code) => finish(code, null));
248
+ });
249
+ }
250
+ //# sourceMappingURL=com.js.map
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Transport fuer den Mac: osascript -> AppleScript -> InDesign do script.
3
+ *
4
+ * Nur diese Datei weiss, dass hier ein Mac steht. Der Interpreter bleibt frei
5
+ * von Plattformannahmen.
6
+ *
7
+ * Ablauf je Aufruf:
8
+ * 1. Interpreter und Handler zu einem Skript zusammensetzen, den Befehl als
9
+ * ASCII-JSON hineinschreiben und in eine temporaere .jsx-Datei legen.
10
+ * 2. AppleScript-Datei danebenlegen, die InDesign dieses Skript ausfuehren
11
+ * laesst.
12
+ * 3. Das Skript schreibt sein Ergebnis in eine Datei; der Server liest sie.
13
+ * Umweg ueber die Datei, weil die Rueckgabe ueber AppleScript in Laenge
14
+ * und Zeichensatz unzuverlaessig ist.
15
+ *
16
+ * Der kleine Rahmen in Schritt 1 benutzt das ExtendScript-Objekt File. Das ist
17
+ * bewusst hier und nicht im Interpreter: Dateizugriff ist Transportsache.
18
+ */
19
+ import { spawn } from 'node:child_process';
20
+ import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
21
+ import { tmpdir } from 'node:os';
22
+ import { join } from 'node:path';
23
+ import { buildScriptBundle, toAsciiJson } from './script-bundle.js';
24
+ import { transportError, } from './transport.js';
25
+ const APP_NOT_AVAILABLE_HINTS = [
26
+ '-1728',
27
+ '-600',
28
+ '-609',
29
+ '-10814',
30
+ "isn't running",
31
+ 'is not running',
32
+ 'Application isn',
33
+ ];
34
+ function escapeForJsString(text) {
35
+ return text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
36
+ }
37
+ export class OsascriptTransport {
38
+ name = 'osascript';
39
+ appBundleId;
40
+ defaultTimeoutMs;
41
+ keepTempFiles;
42
+ constructor(options = {}) {
43
+ this.appBundleId = options.appBundleId ?? 'com.adobe.InDesign';
44
+ this.defaultTimeoutMs = options.defaultTimeoutMs ?? 120_000;
45
+ this.keepTempFiles = options.keepTempFiles ?? false;
46
+ }
47
+ /** Das vollstaendige Skript inklusive Rahmen, der das Ergebnis wegschreibt. */
48
+ buildScript(command, resultPath) {
49
+ const bundle = buildScriptBundle();
50
+ const commandLiteral = toAsciiJson(command);
51
+ const target = escapeForJsString(resultPath);
52
+ const frame = [
53
+ '',
54
+ '/* ---- Rahmen des osascript-Transports (ExtendScript, Mac) ---- */',
55
+ '(function () {',
56
+ ' var __text = IDC.run(' + commandLiteral + ');',
57
+ ' var __file = new File("' + target + '");',
58
+ ' __file.encoding = "UTF-8";',
59
+ ' __file.lineFeed = "Unix";',
60
+ ' __file.open("w");',
61
+ ' __file.write(__text);',
62
+ ' __file.close();',
63
+ ' return "ok";',
64
+ '})();',
65
+ '',
66
+ ].join('\n');
67
+ return bundle + '\n' + frame;
68
+ }
69
+ buildAppleScript(scriptPath, timeoutMs) {
70
+ const seconds = Math.max(5, Math.ceil(timeoutMs / 1000) + 5);
71
+ return [
72
+ 'with timeout of ' + seconds + ' seconds',
73
+ '\ttell application id "' + this.appBundleId + '"',
74
+ '\t\tdo script (POSIX file "' + escapeForJsString(scriptPath) + '") language javascript',
75
+ '\tend tell',
76
+ 'end timeout',
77
+ ].join('\n');
78
+ }
79
+ async send(command, options = {}) {
80
+ const timeoutMs = options.timeoutMs ?? this.defaultTimeoutMs;
81
+ let directory;
82
+ try {
83
+ directory = mkdtempSync(join(tmpdir(), 'idc-spm-'));
84
+ }
85
+ catch (error) {
86
+ return transportError('io_error', 'Could not create a temporary directory.', String(error));
87
+ }
88
+ const scriptPath = join(directory, 'command.jsx');
89
+ const resultPath = join(directory, 'result.json');
90
+ const appleScriptPath = join(directory, 'command.applescript');
91
+ try {
92
+ writeFileSync(scriptPath, this.buildScript(command, resultPath), 'utf8');
93
+ writeFileSync(appleScriptPath, this.buildAppleScript(scriptPath, timeoutMs), 'utf8');
94
+ const outcome = await runProcess('osascript', [appleScriptPath], timeoutMs);
95
+ if (outcome.spawnError !== null) {
96
+ return transportError('app_not_available', 'Could not run osascript – this transport needs macOS: ' + outcome.spawnError.message, null);
97
+ }
98
+ if (outcome.timedOut) {
99
+ return transportError('timeout', 'InDesign did not answer within ' + timeoutMs +
100
+ ' ms. The script may still be running inside InDesign – check the document before repeating the call.', { command: command.cmd, timeout_ms: timeoutMs });
101
+ }
102
+ let raw = null;
103
+ try {
104
+ raw = readFileSync(resultPath, 'utf8');
105
+ }
106
+ catch {
107
+ raw = null;
108
+ }
109
+ if (raw === null || raw.length === 0) {
110
+ const stderr = outcome.stderr.trim();
111
+ const kind = APP_NOT_AVAILABLE_HINTS.some((hint) => stderr.includes(hint))
112
+ ? 'app_not_available'
113
+ : 'script_error';
114
+ return transportError(kind, stderr.length > 0
115
+ ? 'InDesign did not return a result: ' + stderr
116
+ : 'InDesign did not return a result and reported nothing.', { exit_code: outcome.code, stdout: outcome.stdout.trim() });
117
+ }
118
+ try {
119
+ const parsed = JSON.parse(raw);
120
+ if (!Array.isArray(parsed.warnings))
121
+ parsed.warnings = [];
122
+ return parsed;
123
+ }
124
+ catch (error) {
125
+ return transportError('script_error', 'The result of the script is not valid JSON: ' + String(error), { raw: raw.slice(0, 2000) });
126
+ }
127
+ }
128
+ finally {
129
+ if (!this.keepTempFiles) {
130
+ try {
131
+ rmSync(directory, { recursive: true, force: true });
132
+ }
133
+ catch {
134
+ /* Aufraeumen darf keinen Aufruf scheitern lassen. */
135
+ }
136
+ }
137
+ }
138
+ }
139
+ async close() {
140
+ /* Kein dauerhafter Zustand: nichts zu schliessen. */
141
+ }
142
+ }
143
+ function runProcess(command, args, timeoutMs) {
144
+ return new Promise((resolve) => {
145
+ const child = spawn(command, args, { stdio: ['ignore', 'pipe', 'pipe'] });
146
+ let stdout = '';
147
+ let stderr = '';
148
+ let timedOut = false;
149
+ let settled = false;
150
+ const timer = setTimeout(() => {
151
+ timedOut = true;
152
+ child.kill('SIGKILL');
153
+ }, timeoutMs);
154
+ child.stdout.on('data', (chunk) => {
155
+ stdout += chunk.toString('utf8');
156
+ });
157
+ child.stderr.on('data', (chunk) => {
158
+ stderr += chunk.toString('utf8');
159
+ });
160
+ const finish = (code, spawnError) => {
161
+ if (settled)
162
+ return;
163
+ settled = true;
164
+ clearTimeout(timer);
165
+ resolve({ code, stdout, stderr, timedOut, spawnError });
166
+ };
167
+ child.on('error', (error) => finish(null, error));
168
+ child.on('close', (code) => finish(code, null));
169
+ });
170
+ }
171
+ //# sourceMappingURL=osascript.js.map
@@ -0,0 +1,19 @@
1
+ export class SerialTransport {
2
+ inner;
3
+ chain = Promise.resolve();
4
+ constructor(inner) {
5
+ this.inner = inner;
6
+ }
7
+ get name() {
8
+ return this.inner.name;
9
+ }
10
+ send(command, options) {
11
+ const run = this.chain.then(() => this.inner.send(command, options), () => this.inner.send(command, options));
12
+ this.chain = run.then(() => undefined, () => undefined);
13
+ return run;
14
+ }
15
+ close() {
16
+ return this.inner.close();
17
+ }
18
+ }
19
+ //# sourceMappingURL=queue.js.map
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Setzt das Skript zusammen, das in InDesign laeuft: Interpreter-Kern plus
3
+ * alle Handler aus indesign/commands. Der Interpreter darf kein #include
4
+ * benutzen (ExtendScript-eigen, in UXP nicht vorhanden), deshalb haengt der
5
+ * Server die Dateien aneinander.
6
+ */
7
+ import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
8
+ import { dirname, join } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ let cached = null;
11
+ /** Dateiliste mit Aenderungszeiten, mit der das Buendel gebaut wurde. */
12
+ let cachedSignature = '';
13
+ /** Handler-Dateien alphabetisch, wie sie ins Buendel kommen. */
14
+ function commandFiles(root) {
15
+ return readdirSync(join(root, 'commands'))
16
+ .filter((name) => name.endsWith('.js'))
17
+ .sort();
18
+ }
19
+ /*
20
+ * Signatur aus Pfaden und Aenderungszeiten. Aendert sich eine Handler-Datei
21
+ * (Bausitzung mit laufendem Server), wird das Buendel beim naechsten Aufruf
22
+ * neu gelesen – ohne Neustart von Claude Desktop. Ein Stat je Datei und
23
+ * Aufruf ist gegen die Laufzeit eines InDesign-Skripts vernachlaessigbar.
24
+ */
25
+ export function bundleSignature(root = findScriptRoot()) {
26
+ return signature(root, commandFiles(root));
27
+ }
28
+ function signature(root, files) {
29
+ const parts = [root + ':' + statSync(join(root, 'interpreter.js')).mtimeMs];
30
+ for (const name of files) {
31
+ parts.push(name + ':' + statSync(join(root, 'commands', name)).mtimeMs);
32
+ }
33
+ return parts.join('|');
34
+ }
35
+ /** Sucht aufwaerts nach dem Repo-/Paketwurzelordner mit indesign/interpreter.js. */
36
+ export function findScriptRoot(startDir) {
37
+ let dir = startDir ?? dirname(fileURLToPath(import.meta.url));
38
+ for (let step = 0; step < 12; step += 1) {
39
+ if (existsSync(join(dir, 'indesign', 'interpreter.js'))) {
40
+ return join(dir, 'indesign');
41
+ }
42
+ const parent = dirname(dir);
43
+ if (parent === dir)
44
+ break;
45
+ dir = parent;
46
+ }
47
+ throw new Error('indesign/interpreter.js not found – package layout is broken.');
48
+ }
49
+ /** Interpreter und Handler in fester Reihenfolge, Dateinamen alphabetisch. */
50
+ export function buildScriptBundle(force = false) {
51
+ const root = findScriptRoot();
52
+ const files = commandFiles(root);
53
+ let current = '';
54
+ try {
55
+ current = signature(root, files);
56
+ }
57
+ catch {
58
+ current = '';
59
+ }
60
+ if (cached !== null && !force && current !== '' && current === cachedSignature)
61
+ return cached;
62
+ const parts = [];
63
+ parts.push('/* layoutbridge-spm – zusammengesetztes Skript */');
64
+ parts.push(readFileSync(join(root, 'interpreter.js'), 'utf8'));
65
+ const commandDir = join(root, 'commands');
66
+ for (const name of files) {
67
+ parts.push('\n/* ---- commands/' + name + ' ---- */\n');
68
+ parts.push(readFileSync(join(commandDir, name), 'utf8'));
69
+ }
70
+ cached = parts.join('\n');
71
+ cachedSignature = current;
72
+ return cached;
73
+ }
74
+ /**
75
+ * JSON, in dem jedes Zeichen ausserhalb von ASCII als \uXXXX steht. So ist das
76
+ * erzeugte Skript reines ASCII und die Zeichensatzfrage stellt sich nicht.
77
+ */
78
+ export function toAsciiJson(value) {
79
+ const json = JSON.stringify(value ?? null);
80
+ let out = '';
81
+ for (let i = 0; i < json.length; i += 1) {
82
+ const code = json.charCodeAt(i);
83
+ if (code < 0x20 || code > 0x7e) {
84
+ out += '\\u' + code.toString(16).padStart(4, '0');
85
+ }
86
+ else {
87
+ out += json.charAt(i);
88
+ }
89
+ }
90
+ return out;
91
+ }
92
+ //# sourceMappingURL=script-bundle.js.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Transportschnittstelle.
3
+ *
4
+ * Der Transport ist die einzige Stelle mit Plattformwissen. Alles darueber
5
+ * kennt nur diese Schnittstelle: einen Befehl hinschicken, ein Ergebnis
6
+ * zurueckbekommen.
7
+ */
8
+ /**
9
+ * Fehlerarten nach Spezifikation, allgemeine Anforderung 5; dazu "license"
10
+ * fuer die Lizenzschranke (Auftrag A-6, E-60, Spezifikation S11) – sie
11
+ * greift vor dem Transport, InDesign ist nicht beteiligt.
12
+ */
13
+ export const ERROR_KINDS = [
14
+ 'app_not_available',
15
+ 'invalid_argument',
16
+ 'not_found',
17
+ 'invalid_state',
18
+ 'io_error',
19
+ 'timeout',
20
+ 'script_error',
21
+ 'license',
22
+ ];
23
+ /** Baut einen Fehlerumschlag, ohne dass InDesign beteiligt war. */
24
+ export function transportError(kind, message, details = null) {
25
+ return { ok: false, warnings: [], error: { kind, message, details } };
26
+ }
27
+ export function isErrorKind(value) {
28
+ return typeof value === 'string' && ERROR_KINDS.includes(value);
29
+ }
30
+ //# sourceMappingURL=transport.js.map
@@ -0,0 +1,6 @@
1
+ /* Bereich A – Anwendung. */
2
+
3
+ IDC.register('get_app_version', { needs_document: false, read_only: true, ruler: 'none' },
4
+ function () {
5
+ return { version: String(app.version) };
6
+ });