indesign-cli 0.2.0__py3-none-any.whl
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.
- cli_anything/indesign/README.md +32 -0
- cli_anything/indesign/__init__.py +1 -0
- cli_anything/indesign/__main__.py +5 -0
- cli_anything/indesign/core/artifacts.py +57 -0
- cli_anything/indesign/core/catalog.py +405 -0
- cli_anything/indesign/core/domains.py +178 -0
- cli_anything/indesign/core/envelope.py +65 -0
- cli_anything/indesign/core/errors.py +30 -0
- cli_anything/indesign/core/health.py +46 -0
- cli_anything/indesign/core/hidden_backend.py +116 -0
- cli_anything/indesign/core/hidden_handler_schemas.py +223 -0
- cli_anything/indesign/core/mcp_backend.py +152 -0
- cli_anything/indesign/core/node_setup.py +35 -0
- cli_anything/indesign/core/paths.py +41 -0
- cli_anything/indesign/core/plugins/__init__.py +2 -0
- cli_anything/indesign/core/plugins/backend.py +90 -0
- cli_anything/indesign/core/plugins/discovery.py +69 -0
- cli_anything/indesign/core/plugins/host_actions.py +76 -0
- cli_anything/indesign/core/plugins/install.py +38 -0
- cli_anything/indesign/core/plugins/manifest.py +279 -0
- cli_anything/indesign/core/plugins/validate.py +181 -0
- cli_anything/indesign/core/router.py +217 -0
- cli_anything/indesign/core/runtime.py +59 -0
- cli_anything/indesign/core/scripts.py +44 -0
- cli_anything/indesign/core/session.py +68 -0
- cli_anything/indesign/indesign_cli.py +320 -0
- cli_anything/indesign/node/hidden_handler_bridge.mjs +111 -0
- cli_anything/indesign/server/package-lock.json +168 -0
- cli_anything/indesign/server/package.json +45 -0
- cli_anything/indesign/server/src/advanced/index.js +76 -0
- cli_anything/indesign/server/src/core/InDesignMCPServer.js +273 -0
- cli_anything/indesign/server/src/core/scriptExecutor.js +271 -0
- cli_anything/indesign/server/src/core/sessionManager.js +545 -0
- cli_anything/indesign/server/src/handlers/advancedTemplateHandlers.js +1072 -0
- cli_anything/indesign/server/src/handlers/bookHandlers.js +490 -0
- cli_anything/indesign/server/src/handlers/documentHandlers.js +1472 -0
- cli_anything/indesign/server/src/handlers/exportHandlers.js +208 -0
- cli_anything/indesign/server/src/handlers/graphicsHandlers.js +605 -0
- cli_anything/indesign/server/src/handlers/groupHandlers.js +358 -0
- cli_anything/indesign/server/src/handlers/helpHandlers.js +347 -0
- cli_anything/indesign/server/src/handlers/index.js +77 -0
- cli_anything/indesign/server/src/handlers/layerHandlers.js +75 -0
- cli_anything/indesign/server/src/handlers/masterSpreadHandlers.js +451 -0
- cli_anything/indesign/server/src/handlers/pageHandlers.js +698 -0
- cli_anything/indesign/server/src/handlers/pageItemHandlers.js +704 -0
- cli_anything/indesign/server/src/handlers/presentationHandlers.js +220 -0
- cli_anything/indesign/server/src/handlers/spreadHandlers.js +348 -0
- cli_anything/indesign/server/src/handlers/styleHandlers.js +458 -0
- cli_anything/indesign/server/src/handlers/textHandlers.js +431 -0
- cli_anything/indesign/server/src/handlers/utilityHandlers.js +83 -0
- cli_anything/indesign/server/src/index.js +17 -0
- cli_anything/indesign/server/src/types/index.js +106 -0
- cli_anything/indesign/server/src/types/toolDefinitionsAdvancedTemplates.js +144 -0
- cli_anything/indesign/server/src/types/toolDefinitionsBook.js +224 -0
- cli_anything/indesign/server/src/types/toolDefinitionsContent.js +353 -0
- cli_anything/indesign/server/src/types/toolDefinitionsDocument.js +409 -0
- cli_anything/indesign/server/src/types/toolDefinitionsExport.js +65 -0
- cli_anything/indesign/server/src/types/toolDefinitionsLayer.js +40 -0
- cli_anything/indesign/server/src/types/toolDefinitionsMasterSpread.js +160 -0
- cli_anything/indesign/server/src/types/toolDefinitionsPage.js +271 -0
- cli_anything/indesign/server/src/types/toolDefinitionsPageItemGroup.js +437 -0
- cli_anything/indesign/server/src/types/toolDefinitionsPresentation.js +83 -0
- cli_anything/indesign/server/src/types/toolDefinitionsSpread.js +158 -0
- cli_anything/indesign/server/src/types/toolDefinitionsUtility.js +40 -0
- cli_anything/indesign/server/src/utils/stringUtils.js +107 -0
- cli_anything/indesign/skills/SKILL.md +198 -0
- indesign_cli-0.2.0.dist-info/METADATA +267 -0
- indesign_cli-0.2.0.dist-info/RECORD +72 -0
- indesign_cli-0.2.0.dist-info/WHEEL +5 -0
- indesign_cli-0.2.0.dist-info/entry_points.txt +3 -0
- indesign_cli-0.2.0.dist-info/licenses/LICENSE +21 -0
- indesign_cli-0.2.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from . import __version__
|
|
10
|
+
from .core.artifacts import parse_timestamp, verify_artifact
|
|
11
|
+
from .core.catalog import Catalog
|
|
12
|
+
from .core.catalog import plugin_tool_entries
|
|
13
|
+
from .core.envelope import failure, now_ms, success
|
|
14
|
+
from .core.errors import CliError
|
|
15
|
+
from .core.health import health
|
|
16
|
+
from .core.mcp_backend import McpBackend
|
|
17
|
+
from .core.node_setup import setup_node_dependencies
|
|
18
|
+
from .core.plugins.backend import PluginBackend
|
|
19
|
+
from .core.plugins.discovery import discover_plugins
|
|
20
|
+
from .core.plugins.install import install_plugin, list_plugins, remove_plugin
|
|
21
|
+
from .core.plugins.validate import doctor_plugin, validate_plugin_path
|
|
22
|
+
from .core.router import Router, load_args
|
|
23
|
+
from .core.runtime import install_skill, resolve_server_root
|
|
24
|
+
from .core.scripts import run_script, run_stdin_script
|
|
25
|
+
from .core.session import SessionStore
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
SERVER_ROOT = resolve_server_root()
|
|
29
|
+
REPO_ROOT = SERVER_ROOT
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def emit(payload: dict[str, Any]) -> int:
|
|
33
|
+
print(json.dumps(payload, ensure_ascii=True, indent=2))
|
|
34
|
+
return int(payload.get("exit_code", 0))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def version_payload() -> dict[str, Any]:
|
|
38
|
+
return success(
|
|
39
|
+
command="version",
|
|
40
|
+
data={
|
|
41
|
+
"name": "indesign-cli",
|
|
42
|
+
"version": __version__,
|
|
43
|
+
"aliases": ["cli-anything-indesign"],
|
|
44
|
+
},
|
|
45
|
+
duration_ms=0,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
50
|
+
parser = argparse.ArgumentParser(prog="indesign-cli")
|
|
51
|
+
parser.add_argument("--version", action="store_true")
|
|
52
|
+
parser.add_argument("--json", action="store_true")
|
|
53
|
+
parser.add_argument("--pretty", action="store_true")
|
|
54
|
+
subparsers = parser.add_subparsers(dest="group")
|
|
55
|
+
|
|
56
|
+
tool_parser = subparsers.add_parser("tool")
|
|
57
|
+
tool_sub = tool_parser.add_subparsers(dest="tool_command")
|
|
58
|
+
tool_sub.add_parser("domains")
|
|
59
|
+
list_parser = tool_sub.add_parser("list")
|
|
60
|
+
list_parser.add_argument("--domain")
|
|
61
|
+
list_parser.add_argument("--source")
|
|
62
|
+
list_parser.add_argument("--callable-only", action="store_true")
|
|
63
|
+
search_parser = tool_sub.add_parser("search")
|
|
64
|
+
search_parser.add_argument("--domain")
|
|
65
|
+
search_parser.add_argument("--source")
|
|
66
|
+
search_parser.add_argument("--query", required=True)
|
|
67
|
+
schema_parser = tool_sub.add_parser("schema")
|
|
68
|
+
schema_parser.add_argument("tool_id")
|
|
69
|
+
call_parser = tool_sub.add_parser("call")
|
|
70
|
+
call_parser.add_argument("tool_id")
|
|
71
|
+
call_parser.add_argument("--args", required=True)
|
|
72
|
+
call_parser.add_argument("--timeout", type=int, help="MCP/script backend timeout in seconds")
|
|
73
|
+
|
|
74
|
+
script_parser = subparsers.add_parser("script")
|
|
75
|
+
script_sub = script_parser.add_subparsers(dest="script_command")
|
|
76
|
+
run_parser = script_sub.add_parser("run")
|
|
77
|
+
run_parser.add_argument("file", nargs="?")
|
|
78
|
+
run_parser.add_argument("--stdin", action="store_true")
|
|
79
|
+
run_parser.add_argument("--timeout", type=int, default=300, help="Script backend timeout in seconds")
|
|
80
|
+
|
|
81
|
+
export_parser = subparsers.add_parser("export")
|
|
82
|
+
export_sub = export_parser.add_subparsers(dest="export_command")
|
|
83
|
+
verify_parser = export_sub.add_parser("verify")
|
|
84
|
+
verify_parser.add_argument("path")
|
|
85
|
+
verify_parser.add_argument("--created-after")
|
|
86
|
+
|
|
87
|
+
session_parser = subparsers.add_parser("session")
|
|
88
|
+
session_sub = session_parser.add_subparsers(dest="session_command")
|
|
89
|
+
show_parser = session_sub.add_parser("show")
|
|
90
|
+
show_parser.add_argument("--verbose", action="store_true")
|
|
91
|
+
session_sub.add_parser("clear")
|
|
92
|
+
|
|
93
|
+
server_parser = subparsers.add_parser("server")
|
|
94
|
+
server_sub = server_parser.add_subparsers(dest="server_command")
|
|
95
|
+
health_parser = server_sub.add_parser("health")
|
|
96
|
+
health_parser.add_argument("--deep", action="store_true")
|
|
97
|
+
server_sub.add_parser("setup")
|
|
98
|
+
|
|
99
|
+
skill_parser = subparsers.add_parser("skill")
|
|
100
|
+
skill_sub = skill_parser.add_subparsers(dest="skill_command")
|
|
101
|
+
install_parser = skill_sub.add_parser("install")
|
|
102
|
+
install_parser.add_argument("--target", default=".")
|
|
103
|
+
|
|
104
|
+
plugin_parser = subparsers.add_parser("plugin")
|
|
105
|
+
plugin_sub = plugin_parser.add_subparsers(dest="plugin_command")
|
|
106
|
+
plugin_sub.add_parser("list")
|
|
107
|
+
plugin_install = plugin_sub.add_parser("install")
|
|
108
|
+
plugin_install.add_argument("path")
|
|
109
|
+
plugin_remove = plugin_sub.add_parser("remove")
|
|
110
|
+
plugin_remove.add_argument("id")
|
|
111
|
+
plugin_validate = plugin_sub.add_parser("validate")
|
|
112
|
+
plugin_validate.add_argument("path")
|
|
113
|
+
plugin_doctor = plugin_sub.add_parser("doctor")
|
|
114
|
+
plugin_doctor.add_argument("id")
|
|
115
|
+
plugin_doctor.add_argument("--deep", action="store_true")
|
|
116
|
+
return parser
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def build_catalog_with_backends() -> tuple[Catalog, list[str]]:
|
|
120
|
+
base = Catalog(repo_root=REPO_ROOT)
|
|
121
|
+
warnings: list[str] = []
|
|
122
|
+
advanced_tools: list[dict[str, Any]] = []
|
|
123
|
+
classic_tools: list[dict[str, Any]] = []
|
|
124
|
+
plugin_tools: list[dict[str, Any]] = []
|
|
125
|
+
plugin_domain_summaries: dict[str, str] = {}
|
|
126
|
+
plugin_records = {}
|
|
127
|
+
for source, entry, sink in (
|
|
128
|
+
("advanced", "src/advanced/index.js", advanced_tools),
|
|
129
|
+
("classic", "src/index.js", classic_tools),
|
|
130
|
+
):
|
|
131
|
+
try:
|
|
132
|
+
sink.extend(McpBackend(repo_root=REPO_ROOT, entry=entry).list_tools())
|
|
133
|
+
except CliError as exc:
|
|
134
|
+
warnings.append(f"{source} backend unavailable: {exc.code}")
|
|
135
|
+
try:
|
|
136
|
+
discovered, plugin_warnings = discover_plugins(Path.cwd(), host_version=__version__)
|
|
137
|
+
warnings.extend(plugin_warnings)
|
|
138
|
+
except CliError as exc:
|
|
139
|
+
discovered = []
|
|
140
|
+
warnings.append(f"plugin discovery unavailable: {exc.code}")
|
|
141
|
+
for record in discovered:
|
|
142
|
+
try:
|
|
143
|
+
backend = PluginBackend(record)
|
|
144
|
+
backend.handshake({"name": "indesign-cli", "version": __version__, "protocol": record.manifest["protocol"]})
|
|
145
|
+
plugin_tools.extend(plugin_tool_entries(record, backend.list_tools()))
|
|
146
|
+
plugin_domain_summaries[record.domain] = str(record.manifest.get("description") or f"{record.id} plugin tools")
|
|
147
|
+
plugin_records[record.id] = record
|
|
148
|
+
except CliError as exc:
|
|
149
|
+
warnings.append(f"plugin {record.id} unavailable: {exc.code}")
|
|
150
|
+
return (
|
|
151
|
+
base.with_exposed_tools(
|
|
152
|
+
advanced_tools=advanced_tools,
|
|
153
|
+
classic_tools=classic_tools,
|
|
154
|
+
plugin_tools=plugin_tools,
|
|
155
|
+
plugin_domain_summaries=plugin_domain_summaries,
|
|
156
|
+
plugin_records=plugin_records,
|
|
157
|
+
),
|
|
158
|
+
warnings,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def emit_check(command: str, data: dict[str, Any], *, tool_id: str | None = None) -> int:
|
|
163
|
+
payload = success(command=command, data=data, duration_ms=0, tool_id=tool_id)
|
|
164
|
+
if data.get("ok") is False:
|
|
165
|
+
payload["ok"] = False
|
|
166
|
+
payload["exit_code"] = 1
|
|
167
|
+
payload["tool_success"] = False
|
|
168
|
+
return emit(payload)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def run(argv: list[str] | None = None) -> int:
|
|
172
|
+
parser = build_parser()
|
|
173
|
+
args = parser.parse_args(argv)
|
|
174
|
+
if args.version:
|
|
175
|
+
return emit(version_payload())
|
|
176
|
+
if args.group == "tool":
|
|
177
|
+
catalog, warnings = build_catalog_with_backends()
|
|
178
|
+
if args.tool_command == "domains" or args.tool_command is None:
|
|
179
|
+
return emit(success(command="tool domains", data=catalog.domains(), duration_ms=0, warnings=warnings))
|
|
180
|
+
if args.tool_command == "list":
|
|
181
|
+
if not args.domain and not args.source:
|
|
182
|
+
return emit(success(command="tool list", data=catalog.domains(), duration_ms=0, warnings=warnings))
|
|
183
|
+
data = catalog.list_tools(
|
|
184
|
+
domain=args.domain,
|
|
185
|
+
source=args.source,
|
|
186
|
+
callable_only=args.callable_only,
|
|
187
|
+
)
|
|
188
|
+
return emit(success(command="tool list", data=data, duration_ms=0, warnings=warnings))
|
|
189
|
+
if args.tool_command == "search":
|
|
190
|
+
data = catalog.list_tools(domain=args.domain, source=args.source, query=args.query)
|
|
191
|
+
return emit(success(command="tool search", data=data, duration_ms=0, warnings=warnings))
|
|
192
|
+
router = Router(catalog=catalog, repo_root=REPO_ROOT, backend_timeout_seconds=getattr(args, "timeout", None))
|
|
193
|
+
if args.tool_command == "schema":
|
|
194
|
+
data = router.schema(args.tool_id)
|
|
195
|
+
return emit(success(command="tool schema", data=data, duration_ms=0, tool_id=args.tool_id, warnings=warnings))
|
|
196
|
+
if args.tool_command == "call":
|
|
197
|
+
call_args = load_args(args.args)
|
|
198
|
+
tool = router._find(args.tool_id)
|
|
199
|
+
store = SessionStore(Path.cwd())
|
|
200
|
+
try:
|
|
201
|
+
data = router.call(args.tool_id, call_args)
|
|
202
|
+
except CliError:
|
|
203
|
+
store.record_call(
|
|
204
|
+
tool_id=args.tool_id,
|
|
205
|
+
domain=tool["domain"],
|
|
206
|
+
source=tool["source"],
|
|
207
|
+
ok=False,
|
|
208
|
+
duration_ms=0,
|
|
209
|
+
plugin=tool.get("plugin"),
|
|
210
|
+
)
|
|
211
|
+
raise
|
|
212
|
+
store.record_call(
|
|
213
|
+
tool_id=args.tool_id,
|
|
214
|
+
domain=tool["domain"],
|
|
215
|
+
source=tool["source"],
|
|
216
|
+
ok=True,
|
|
217
|
+
duration_ms=0,
|
|
218
|
+
plugin=tool.get("plugin"),
|
|
219
|
+
artifacts=data.get("artifacts") if isinstance(data, dict) else None,
|
|
220
|
+
)
|
|
221
|
+
return emit(
|
|
222
|
+
success(
|
|
223
|
+
command="tool call",
|
|
224
|
+
data=data,
|
|
225
|
+
duration_ms=0,
|
|
226
|
+
tool_id=args.tool_id,
|
|
227
|
+
domain=tool["domain"],
|
|
228
|
+
source=tool["source"],
|
|
229
|
+
warnings=warnings,
|
|
230
|
+
)
|
|
231
|
+
)
|
|
232
|
+
if args.group == "plugin":
|
|
233
|
+
if args.plugin_command == "list" or args.plugin_command is None:
|
|
234
|
+
data = list_plugins(cwd=Path.cwd(), host_version=__version__)
|
|
235
|
+
return emit(success(command="plugin list", data=data, duration_ms=0, tool_id="plugin.list", domain="plugin", source="cli", warnings=data.get("warnings", [])))
|
|
236
|
+
if args.plugin_command == "install":
|
|
237
|
+
data = install_plugin(args.path, cwd=Path.cwd(), host_version=__version__)
|
|
238
|
+
return emit(success(command="plugin install", data=data, duration_ms=0, tool_id="plugin.install", domain="plugin", source="cli"))
|
|
239
|
+
if args.plugin_command == "remove":
|
|
240
|
+
data = remove_plugin(args.id, cwd=Path.cwd())
|
|
241
|
+
return emit(success(command="plugin remove", data=data, duration_ms=0, tool_id="plugin.remove", domain="plugin", source="cli"))
|
|
242
|
+
if args.plugin_command == "validate":
|
|
243
|
+
data = validate_plugin_path(args.path, host_version=__version__)
|
|
244
|
+
return emit_check("plugin validate", data, tool_id="plugin.validate")
|
|
245
|
+
if args.plugin_command == "doctor":
|
|
246
|
+
data = doctor_plugin(args.id, cwd=Path.cwd(), host_version=__version__, deep=bool(args.deep))
|
|
247
|
+
return emit_check("plugin doctor", data, tool_id="plugin.doctor")
|
|
248
|
+
if args.group == "script" and args.script_command == "run":
|
|
249
|
+
catalog, warnings = build_catalog_with_backends()
|
|
250
|
+
router = Router(catalog=catalog, repo_root=REPO_ROOT, backend_timeout_seconds=args.timeout)
|
|
251
|
+
store = SessionStore(Path.cwd())
|
|
252
|
+
try:
|
|
253
|
+
if args.stdin:
|
|
254
|
+
data = run_stdin_script(router, Path.cwd())
|
|
255
|
+
elif args.file:
|
|
256
|
+
data = run_script(router, Path(args.file))
|
|
257
|
+
else:
|
|
258
|
+
raise CliError("script run requires a file path or --stdin", code="SCRIPT_INPUT_REQUIRED")
|
|
259
|
+
except Exception:
|
|
260
|
+
store.record_call(tool_id="script.run", domain="script", source="script", ok=False, duration_ms=0)
|
|
261
|
+
raise
|
|
262
|
+
store.record_call(tool_id="script.run", domain="script", source="script", ok=True, duration_ms=0)
|
|
263
|
+
return emit(success(command="script run", data=data, duration_ms=0, tool_id="script.run", domain="script", source="script", warnings=warnings))
|
|
264
|
+
if args.group == "export" and args.export_command == "verify":
|
|
265
|
+
try:
|
|
266
|
+
created_after = parse_timestamp(args.created_after) if args.created_after else None
|
|
267
|
+
except ValueError as exc:
|
|
268
|
+
raise CliError("created-after must be an ISO timestamp", code="BAD_TIMESTAMP") from exc
|
|
269
|
+
data = verify_artifact(Path(args.path), created_after=created_after, cwd=Path.cwd())
|
|
270
|
+
SessionStore(Path.cwd()).record_call(tool_id="export.verify", domain="export", source="cli", ok=True, duration_ms=0)
|
|
271
|
+
return emit(success(command="export verify", data=data, duration_ms=0, tool_id="export.verify", domain="export", source="cli"))
|
|
272
|
+
if args.group == "session":
|
|
273
|
+
store = SessionStore(Path.cwd())
|
|
274
|
+
if args.session_command == "show" or args.session_command is None:
|
|
275
|
+
data = store.read(compact=not getattr(args, "verbose", False))
|
|
276
|
+
return emit(success(command="session show", data=data, duration_ms=0, tool_id="session.show"))
|
|
277
|
+
if args.session_command == "clear":
|
|
278
|
+
store.clear()
|
|
279
|
+
return emit(success(command="session clear", data={"cleared": True}, duration_ms=0, tool_id="session.clear"))
|
|
280
|
+
if args.group == "server" and (args.server_command == "health" or args.server_command is None):
|
|
281
|
+
data = health(REPO_ROOT, deep=getattr(args, "deep", False))
|
|
282
|
+
return emit(success(command="server health", data=data, duration_ms=0, tool_id="server.health"))
|
|
283
|
+
if args.group == "server" and args.server_command == "setup":
|
|
284
|
+
data = setup_node_dependencies(REPO_ROOT)
|
|
285
|
+
return emit(success(command="server setup", data=data, duration_ms=0, tool_id="server.setup", domain="server", source="cli"))
|
|
286
|
+
if args.group == "skill" and args.skill_command == "install":
|
|
287
|
+
data = install_skill(Path(args.target))
|
|
288
|
+
return emit(success(command="skill install", data=data, duration_ms=0, tool_id="skill.install", domain="skill", source="cli"))
|
|
289
|
+
raise CliError(
|
|
290
|
+
"Command is required",
|
|
291
|
+
code="COMMAND_REQUIRED",
|
|
292
|
+
details={"groups": ["tool", "script", "export", "session", "server", "skill", "plugin"]},
|
|
293
|
+
hint="先用 tool domains 查看工具域,或用 tool search --query <关键词> 查找工具。",
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def safe_command(argv: list[str] | None) -> str:
|
|
298
|
+
parts = list(argv if argv is not None else sys.argv[1:])
|
|
299
|
+
while parts and parts[0] in {"--json", "--pretty"}:
|
|
300
|
+
parts.pop(0)
|
|
301
|
+
if not parts:
|
|
302
|
+
return "cli"
|
|
303
|
+
if parts[0] == "tool" and len(parts) > 1:
|
|
304
|
+
return f"tool {parts[1]}"
|
|
305
|
+
if parts[0] in {"script", "export", "session", "skill", "plugin"} and len(parts) > 1:
|
|
306
|
+
return f"{parts[0]} {parts[1]}"
|
|
307
|
+
return parts[0]
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def main(argv: list[str] | None = None) -> int:
|
|
311
|
+
start = now_ms()
|
|
312
|
+
try:
|
|
313
|
+
return run(argv)
|
|
314
|
+
except CliError as exc:
|
|
315
|
+
duration_ms = now_ms() - start
|
|
316
|
+
return emit(failure(command=safe_command(argv), error=exc, duration_ms=duration_ms))
|
|
317
|
+
except Exception as exc:
|
|
318
|
+
duration_ms = now_ms() - start
|
|
319
|
+
error = CliError("Unexpected CLI error", code="UNEXPECTED_ERROR", details={"type": exc.__class__.__name__})
|
|
320
|
+
return emit(failure(command=safe_command(argv), error=error, duration_ms=duration_ms))
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { env, stdin, stdout } from 'node:process';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
function resolveServerRoot() {
|
|
10
|
+
const candidates = [
|
|
11
|
+
env.INDESIGN_CLI_SERVER_ROOT,
|
|
12
|
+
path.resolve(__dirname, '../../../../'),
|
|
13
|
+
path.resolve(__dirname, '../server'),
|
|
14
|
+
].filter(Boolean);
|
|
15
|
+
|
|
16
|
+
for (const candidate of candidates) {
|
|
17
|
+
if (
|
|
18
|
+
fs.existsSync(path.join(candidate, 'src', 'handlers', 'bookHandlers.js')) &&
|
|
19
|
+
fs.existsSync(path.join(candidate, 'src', 'handlers', 'presentationHandlers.js'))
|
|
20
|
+
) {
|
|
21
|
+
return candidate;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
throw new Error('Unable to resolve InDesign CLI server root');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const serverRoot = resolveServerRoot();
|
|
29
|
+
const { BookHandlers } = await import(pathToFileURL(path.join(serverRoot, 'src', 'handlers', 'bookHandlers.js')).href);
|
|
30
|
+
const { PresentationHandlers } = await import(
|
|
31
|
+
pathToFileURL(path.join(serverRoot, 'src', 'handlers', 'presentationHandlers.js')).href
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const HANDLERS = {
|
|
35
|
+
book: BookHandlers,
|
|
36
|
+
presentation: PresentationHandlers,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function readStdin() {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
let data = '';
|
|
42
|
+
stdin.setEncoding('utf8');
|
|
43
|
+
stdin.on('data', chunk => {
|
|
44
|
+
data += chunk;
|
|
45
|
+
});
|
|
46
|
+
stdin.on('end', () => resolve(data));
|
|
47
|
+
stdin.on('error', reject);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function snakeToCamel(value) {
|
|
52
|
+
return String(value || '').replace(/_([a-z0-9])/g, (_match, letter) => letter.toUpperCase());
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function camelToSnake(value) {
|
|
56
|
+
return String(value || '')
|
|
57
|
+
.replace(/(.)([A-Z][a-z]+)/g, '$1_$2')
|
|
58
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1_$2')
|
|
59
|
+
.toLowerCase();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function resolveMethodName(handlerClass, name) {
|
|
63
|
+
const normalized = String(name || '');
|
|
64
|
+
for (const candidate of Object.getOwnPropertyNames(handlerClass)) {
|
|
65
|
+
if (typeof handlerClass[candidate] === 'function' && camelToSnake(candidate) === normalized) {
|
|
66
|
+
return candidate;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return snakeToCamel(normalized);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function failure(code, message) {
|
|
73
|
+
return {
|
|
74
|
+
ok: false,
|
|
75
|
+
error: { code, message },
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async function main() {
|
|
80
|
+
const input = await readStdin();
|
|
81
|
+
const request = JSON.parse(input || '{}');
|
|
82
|
+
const domain = request.domain;
|
|
83
|
+
const name = request.name;
|
|
84
|
+
const args = request.args || {};
|
|
85
|
+
|
|
86
|
+
const handlerClass = HANDLERS[domain];
|
|
87
|
+
if (!handlerClass) {
|
|
88
|
+
return failure('HIDDEN_HANDLER_DOMAIN_NOT_FOUND', `Unknown hidden handler domain: ${domain}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const methodName = resolveMethodName(handlerClass, name);
|
|
92
|
+
const method = handlerClass[methodName];
|
|
93
|
+
if (typeof method !== 'function') {
|
|
94
|
+
return failure('HIDDEN_HANDLER_METHOD_NOT_FOUND', `Unknown hidden handler method: ${domain}.${name}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (request.resolveOnly) {
|
|
98
|
+
return { ok: true, methodName };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const result = await method.call(handlerClass, args);
|
|
102
|
+
return { ok: true, result };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
main()
|
|
106
|
+
.then(payload => {
|
|
107
|
+
stdout.write(JSON.stringify(payload));
|
|
108
|
+
})
|
|
109
|
+
.catch(error => {
|
|
110
|
+
stdout.write(JSON.stringify(failure('HIDDEN_HANDLER_EXCEPTION', error?.message || String(error))));
|
|
111
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "indesign-cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "indesign-cli",
|
|
9
|
+
"version": "0.2.0",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@modelcontextprotocol/sdk": "^0.5.0",
|
|
13
|
+
"winax": "^3.6.2"
|
|
14
|
+
},
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=18.0.0"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"node_modules/@modelcontextprotocol/sdk": {
|
|
20
|
+
"version": "0.5.0",
|
|
21
|
+
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.5.0.tgz",
|
|
22
|
+
"integrity": "sha512-RXgulUX6ewvxjAG0kOpLMEdXXWkzWgaoCGaA2CwNW7cQCIphjpJhjpHSiaPdVCnisjRF/0Cm9KWHUuIoeiAblQ==",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"content-type": "^1.0.5",
|
|
26
|
+
"raw-body": "^3.0.0",
|
|
27
|
+
"zod": "^3.23.8"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"node_modules/bytes": {
|
|
31
|
+
"version": "3.1.2",
|
|
32
|
+
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
|
33
|
+
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">= 0.8"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"node_modules/content-type": {
|
|
40
|
+
"version": "1.0.5",
|
|
41
|
+
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
|
42
|
+
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">= 0.6"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"node_modules/depd": {
|
|
49
|
+
"version": "2.0.0",
|
|
50
|
+
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
|
51
|
+
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">= 0.8"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"node_modules/http-errors": {
|
|
58
|
+
"version": "2.0.0",
|
|
59
|
+
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
|
60
|
+
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
|
61
|
+
"license": "MIT",
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"depd": "2.0.0",
|
|
64
|
+
"inherits": "2.0.4",
|
|
65
|
+
"setprototypeof": "1.2.0",
|
|
66
|
+
"statuses": "2.0.1",
|
|
67
|
+
"toidentifier": "1.0.1"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">= 0.8"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"node_modules/iconv-lite": {
|
|
74
|
+
"version": "0.6.3",
|
|
75
|
+
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
|
76
|
+
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
|
77
|
+
"license": "MIT",
|
|
78
|
+
"dependencies": {
|
|
79
|
+
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
|
80
|
+
},
|
|
81
|
+
"engines": {
|
|
82
|
+
"node": ">=0.10.0"
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"node_modules/inherits": {
|
|
86
|
+
"version": "2.0.4",
|
|
87
|
+
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
|
88
|
+
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
|
89
|
+
"license": "ISC"
|
|
90
|
+
},
|
|
91
|
+
"node_modules/raw-body": {
|
|
92
|
+
"version": "3.0.0",
|
|
93
|
+
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
|
|
94
|
+
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
|
|
95
|
+
"license": "MIT",
|
|
96
|
+
"dependencies": {
|
|
97
|
+
"bytes": "3.1.2",
|
|
98
|
+
"http-errors": "2.0.0",
|
|
99
|
+
"iconv-lite": "0.6.3",
|
|
100
|
+
"unpipe": "1.0.0"
|
|
101
|
+
},
|
|
102
|
+
"engines": {
|
|
103
|
+
"node": ">= 0.8"
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"node_modules/safer-buffer": {
|
|
107
|
+
"version": "2.1.2",
|
|
108
|
+
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
|
109
|
+
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
|
110
|
+
"license": "MIT"
|
|
111
|
+
},
|
|
112
|
+
"node_modules/setprototypeof": {
|
|
113
|
+
"version": "1.2.0",
|
|
114
|
+
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
|
115
|
+
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
|
116
|
+
"license": "ISC"
|
|
117
|
+
},
|
|
118
|
+
"node_modules/statuses": {
|
|
119
|
+
"version": "2.0.1",
|
|
120
|
+
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
|
121
|
+
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
|
122
|
+
"license": "MIT",
|
|
123
|
+
"engines": {
|
|
124
|
+
"node": ">= 0.8"
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"node_modules/toidentifier": {
|
|
128
|
+
"version": "1.0.1",
|
|
129
|
+
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
|
130
|
+
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
|
131
|
+
"license": "MIT",
|
|
132
|
+
"engines": {
|
|
133
|
+
"node": ">=0.6"
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
"node_modules/unpipe": {
|
|
137
|
+
"version": "1.0.0",
|
|
138
|
+
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
|
139
|
+
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
|
140
|
+
"license": "MIT",
|
|
141
|
+
"engines": {
|
|
142
|
+
"node": ">= 0.8"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"node_modules/winax": {
|
|
146
|
+
"version": "3.6.2",
|
|
147
|
+
"resolved": "https://registry.npmjs.org/winax/-/winax-3.6.2.tgz",
|
|
148
|
+
"integrity": "sha512-KvNO9lBBW//F4k4kE3QzKWHyGchMswrwSC2TD/rS4BYLaypECPCRXQIyjmQHVteK7cBEqqdF288brwnig/67Ow==",
|
|
149
|
+
"hasInstallScript": true,
|
|
150
|
+
"license": "MIT",
|
|
151
|
+
"bin": {
|
|
152
|
+
"nodewscript": "NodeWScript.js"
|
|
153
|
+
},
|
|
154
|
+
"engines": {
|
|
155
|
+
"node": ">= 10.0.0"
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
"node_modules/zod": {
|
|
159
|
+
"version": "3.25.76",
|
|
160
|
+
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
|
161
|
+
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
|
162
|
+
"license": "MIT",
|
|
163
|
+
"funding": {
|
|
164
|
+
"url": "https://github.com/sponsors/colinhacks"
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "indesign-cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Agent-native CLI and MCP runtime for Adobe InDesign automation",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"start": "node src/index.js",
|
|
9
|
+
"dev": "node --inspect src/index.js",
|
|
10
|
+
"build": "echo 'No build step required for Node.js'",
|
|
11
|
+
"test": "echo 'No tests specified'"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"mcp",
|
|
15
|
+
"model-context-protocol",
|
|
16
|
+
"indesign",
|
|
17
|
+
"adobe",
|
|
18
|
+
"automation",
|
|
19
|
+
"publishing",
|
|
20
|
+
"desktop-publishing",
|
|
21
|
+
"claude",
|
|
22
|
+
"ai",
|
|
23
|
+
"llm",
|
|
24
|
+
"design-automation",
|
|
25
|
+
"document-creation",
|
|
26
|
+
"layout-automation"
|
|
27
|
+
],
|
|
28
|
+
"author": "Sa",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@modelcontextprotocol/sdk": "^0.5.0",
|
|
32
|
+
"winax": "^3.6.2"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=18.0.0"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "git+https://github.com/zhanglongxiao111/indesign-cli.git"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/zhanglongxiao111/indesign-cli/issues"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/zhanglongxiao111/indesign-cli#readme"
|
|
45
|
+
}
|