@web-ppt/agent-mcp 0.8.0-beta.2 → 0.8.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  本文件记录对使用者可见的变化。版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## 0.8.0-beta.3 - 2026-11-04
6
+
7
+ - MCP 初始化的 `serverInfo.version` 唯一读取自身公开包清单,源码、独立构建和隔离安装均报告实际软件版本。
8
+ - MCP 源码与发布产物回归均验证真实 stdio 握手版本,避免后续发版遗漏手写常量。
9
+
10
+ `@web-ppt/collab` 发布入口为 12.20KB gzip;排除 peer 的完整测试薄包为 14,333B gzip。
11
+
12
+ Windows PowerPoint 真机验收继续延后,未记为通过。
13
+
5
14
  ## 0.8.0-beta.2 - 2026-11-04
6
15
 
7
16
  - 新增仓库 Agent 配对评测工具:复用生产 MCP 比较自动 lint 反馈,保存全部尝试和真实产物;`npm run eval:agent` 可离线重判已提交证据,真实模型调用使用独立显式命令。评测结果不作为发布门禁,不增加 SDK 运行时依赖。
@@ -9,7 +18,7 @@
9
18
  - 模板二进制边界改为审计 npm 实际发布文件清单,覆盖全部发布包;仓库私有评测文稿可保留,SDK 包夹带二进制模板仍会被拒绝。
10
19
  - 官网语言回归隔离后台字体完成回调,保留视图、页码、内容和下载断言;触屏回归使用实际设备能力仿真,并在场景结束恢复原输入状态与原生字体方法。
11
20
 
12
- 九包继续使用同一 beta 版本,SDK 编辑行为与 `0.8.0-beta.1` 保持一致。GitHub 发布链路以 MCP 包的真实 OIDC 新版本发布作为首次激活验收。
21
+ 九包已通过 GitHub OIDC 发布,MCP Trusted Publisher 已完成首次成功发布激活。隔离安装发现此版 MCP 握手仍报告 `beta.1`,修正在 `beta.3`;SDK 编辑行为与 `0.8.0-beta.1` 一致。
13
22
 
14
23
  `@web-ppt/collab` 发布入口为 12.20KB gzip;排除 peer 的完整测试薄包为 14,333B gzip。
15
24
 
package/dist/agent-mcp.js CHANGED
@@ -1,4 +1,4 @@
1
- import { A as a, a as c, c as n } from "./server-IR4Cfdfl.js";
1
+ import { A as a, a as c, c as n } from "./server-B4X8aoUM.js";
2
2
  export {
3
3
  a as AgentMcpError,
4
4
  c as AgentMcpSessionManager,
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import { readFile as c } from "node:fs/promises";
3
3
  import { dirname as a, resolve as p } from "node:path";
4
4
  import { pathToFileURL as f } from "node:url";
5
5
  import { StdioServerTransport as w } from "@modelcontextprotocol/sdk/server/stdio.js";
6
- import { c as l, l as s } from "./server-IR4Cfdfl.js";
6
+ import { c as l, l as s } from "./server-B4X8aoUM.js";
7
7
  async function i(r, e, o) {
8
8
  if (e === void 0) return;
9
9
  if (typeof e != "string" || !e) throw new Error(`${o} 模块路径必须是非空字符串`);
@@ -0,0 +1,225 @@
1
+ import { createRequire as v } from "node:module";
2
+ import { Server as b } from "@modelcontextprotocol/sdk/server/index.js";
3
+ import { ListToolsRequestSchema as S, CallToolRequestSchema as k } from "@modelcontextprotocol/sdk/types.js";
4
+ import { openAgentSession as $, listAgentTools as q } from "@web-ppt/edit-core/agent";
5
+ import { open as E, rename as A, rm as I, readFile as x } from "node:fs/promises";
6
+ import { disposeOpcPackage as P } from "@web-ppt/edit-core/opc";
7
+ import { serializeEditDoc as _ } from "@web-ppt/edit-core/save";
8
+ import { resolve as M, dirname as R, basename as j } from "node:path";
9
+ class c extends Error {
10
+ constructor(e, t) {
11
+ super(t), this.reason = e, this.name = "AgentMcpError";
12
+ }
13
+ }
14
+ let N = 0;
15
+ function w(s) {
16
+ if (typeof s != "string" || !s.trim())
17
+ throw new c("invalid-path", "文件路径必须是非空字符串");
18
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(s))
19
+ throw new c("invalid-path", `只接受本地文件路径,收到 URL:${s}`);
20
+ return M(s);
21
+ }
22
+ async function g(s, e) {
23
+ const t = `${R(s)}/.${j(s)}.web-ppt-${process.pid}-${++N}.tmp`;
24
+ let r;
25
+ try {
26
+ r = await E(t, "wx"), await r.writeFile(e), await r.sync(), await r.close(), r = void 0, await A(t, s);
27
+ } catch (o) {
28
+ throw r && await r.close().catch(() => {
29
+ }), await I(t, { force: !0 }).catch(() => {
30
+ }), new c("save-failed", `写入 ${s} 失败:${o instanceof Error ? o.message : String(o)}`);
31
+ }
32
+ }
33
+ class O {
34
+ constructor(e = {}) {
35
+ this.options = e, this.sessions = /* @__PURE__ */ new Map(), this.sequence = 0, this.active = !0;
36
+ }
37
+ async open(e) {
38
+ this.assertActive();
39
+ const t = w(e);
40
+ let r;
41
+ try {
42
+ r = await x(t);
43
+ } catch (i) {
44
+ throw new c("open-failed", `读取 ${t} 失败:${i instanceof Error ? i.message : String(i)}`);
45
+ }
46
+ const o = `session-${++this.sequence}`, p = new AbortController();
47
+ let n;
48
+ try {
49
+ n = await $(r, {
50
+ ...this.options,
51
+ idPrefix: `mcp-${o}-`,
52
+ lint: { ...this.options.lint, signal: p.signal }
53
+ });
54
+ } catch (i) {
55
+ throw new c("open-failed", `打开 ${t} 失败:${i instanceof Error ? i.message : String(i)}`);
56
+ }
57
+ if (!this.active)
58
+ throw n.dispose(), new c("session-disposed", "会话管理器已经释放");
59
+ return this.sessions.set(o, { session: n, controller: p, queue: Promise.resolve(), closing: !1 }), { sessionId: o, path: t };
60
+ }
61
+ async call(e, t, r) {
62
+ return this.run(e, (o) => o.call(t, r));
63
+ }
64
+ async save(e, t) {
65
+ const r = w(t);
66
+ return this.run(e, async (o) => {
67
+ const { editor: p, doc: n } = o, i = p.captureSavepoint(), l = n.package && !n.package.disposed ? "patch" : "generated", a = await _(n);
68
+ return a.package !== n.package && P(a.package), await g(r, a.bytes), i(), { ok: !0, value: {
69
+ bytes: Buffer.from(a.bytes).toString("base64"),
70
+ path: r,
71
+ saveMode: l,
72
+ packageMode: a.mode,
73
+ fallbackReason: a.fallbackReason,
74
+ preservedEntries: a.preservedEntries,
75
+ rewrittenEntries: a.rewrittenEntries
76
+ } };
77
+ });
78
+ }
79
+ async render(e, t, r) {
80
+ const o = r === void 0 ? void 0 : w(r);
81
+ return this.run(e, async (p) => {
82
+ const n = await p.call("render", t);
83
+ if (!n.ok || o === void 0) return n;
84
+ const i = n.value.format === "svg" ? new TextEncoder().encode(n.value.data) : Buffer.from(n.value.data, "base64");
85
+ return await g(o, i), { ok: !0, value: { ...n.value, path: o } };
86
+ });
87
+ }
88
+ async close(e) {
89
+ const t = this.entry(e);
90
+ t.closing = !0, t.controller.abort();
91
+ const r = t.queue.then(() => (t.session.dispose(), this.sessions.delete(e), { closed: !0 }));
92
+ return t.queue = r.then(() => {
93
+ }, () => {
94
+ }), r;
95
+ }
96
+ async dispose() {
97
+ if (!this.active) return;
98
+ this.active = !1;
99
+ const e = [...this.sessions.values()];
100
+ for (const t of e)
101
+ t.closing = !0, t.controller.abort();
102
+ await Promise.allSettled(e.map((t) => t.queue));
103
+ for (const t of e) t.session.dispose();
104
+ this.sessions.clear();
105
+ }
106
+ entry(e) {
107
+ this.assertActive();
108
+ const t = this.sessions.get(e);
109
+ if (!t || t.closing) throw new c("target-not-found", `找不到打开的会话:${e}`);
110
+ return t;
111
+ }
112
+ run(e, t) {
113
+ const r = this.entry(e), o = r.queue.then(() => {
114
+ if (!this.active || r.closing) throw new c("session-disposed", `会话已经关闭:${e}`);
115
+ return t(r.session);
116
+ });
117
+ return r.queue = o.then(() => {
118
+ }, () => {
119
+ }), o;
120
+ }
121
+ assertActive() {
122
+ if (!this.active) throw new c("session-disposed", "会话管理器已经释放");
123
+ }
124
+ }
125
+ function y(s, e) {
126
+ let t = s;
127
+ for (; typeof t.$ref == "string"; )
128
+ t = t.$ref.split("/").slice(1).reduce((n, i) => n[i], s);
129
+ const r = { ...t.properties, sessionId: { type: "string" } };
130
+ (e === "save" || e === "render") && (r.path = { type: "string" });
131
+ const o = /* @__PURE__ */ new Set([...t.required ?? [], "sessionId"]);
132
+ return e === "save" && o.add("path"), {
133
+ ...t,
134
+ type: "object",
135
+ properties: r,
136
+ required: [...o],
137
+ ...s.definitions ? { definitions: s.definitions } : {}
138
+ };
139
+ }
140
+ function T() {
141
+ const s = [{
142
+ name: "ppt_open",
143
+ description: "打开本地 PPTX / PPT 并创建编辑会话",
144
+ inputSchema: {
145
+ type: "object",
146
+ properties: { path: { type: "string" } },
147
+ required: ["path"],
148
+ additionalProperties: !1
149
+ }
150
+ }];
151
+ for (const e of q()) {
152
+ const t = e.name === "save" ? "save" : e.name === "render" ? "render" : "call";
153
+ s.push({
154
+ name: `ppt_${e.name}`,
155
+ description: e.description,
156
+ inputSchema: y(e.inputSchema, t)
157
+ });
158
+ }
159
+ return s.push({
160
+ name: "ppt_close",
161
+ description: "关闭会话并释放文档资源",
162
+ inputSchema: y({ type: "object", additionalProperties: !1 }, "close")
163
+ }), s;
164
+ }
165
+ function f(s) {
166
+ const e = s;
167
+ return {
168
+ isError: !0,
169
+ content: [{ type: "text", text: JSON.stringify({
170
+ ok: !1,
171
+ error: {
172
+ reason: typeof e?.reason == "string" ? e.reason : "tool-failed",
173
+ message: typeof e?.message == "string" ? e.message : String(s),
174
+ commandIndex: typeof e?.commandIndex == "number" ? e.commandIndex : null,
175
+ elementId: typeof e?.elementId == "string" ? e.elementId : null,
176
+ slideNumber: typeof e?.slideNumber == "number" ? e.slideNumber : null
177
+ }
178
+ }) }]
179
+ };
180
+ }
181
+ function z(s) {
182
+ if (!s || typeof s != "object" || Array.isArray(s)) throw new c("invalid-input", "工具参数必须是对象");
183
+ return s;
184
+ }
185
+ function m(s) {
186
+ if (typeof s.sessionId != "string" || !s.sessionId) throw new c("invalid-input", "sessionId 必须是非空字符串");
187
+ return s.sessionId;
188
+ }
189
+ function U(s = {}) {
190
+ const e = v(import.meta.url)("@web-ppt/agent-mcp/package.json").version;
191
+ if (typeof e != "string" || !e)
192
+ throw new Error(`@web-ppt/agent-mcp/package.json version 必须为非空字符串,实际:${JSON.stringify(e)}`);
193
+ const t = new O(s), r = new b({ name: "web-ppt-agent", version: e }, { capabilities: { tools: {} } }), o = T();
194
+ return r.setRequestHandler(S, async () => ({ tools: o })), r.setRequestHandler(k, async (p) => {
195
+ try {
196
+ const n = p.params.name, i = z(p.params.arguments ?? {});
197
+ let l;
198
+ if (n === "ppt_open") l = await t.open(i.path);
199
+ else if (n === "ppt_close") l = await t.close(m(i));
200
+ else if (n === "ppt_save") {
201
+ const a = await t.save(m(i), i.path);
202
+ if (!a.ok) return f(a.error);
203
+ const { bytes: h, ...d } = a.value;
204
+ l = d;
205
+ } else if (n === "ppt_render") {
206
+ const { sessionId: a, path: h, ...d } = i, u = await t.render(m(i), d, h);
207
+ if (!u.ok) return f(u.error);
208
+ l = u.value;
209
+ } else if (n.startsWith("ppt_")) {
210
+ const a = n.slice(4), { sessionId: h, ...d } = i, u = await t.call(m(i), a, d);
211
+ if (!u.ok) return f(u.error);
212
+ l = u.value;
213
+ } else throw new c("target-not-found", `未知工具:${n}`);
214
+ return { content: [{ type: "text", text: JSON.stringify({ ok: !0, value: l }) }] };
215
+ } catch (n) {
216
+ return f(n);
217
+ }
218
+ }), { server: r, manager: t };
219
+ }
220
+ export {
221
+ c as A,
222
+ O as a,
223
+ U as c,
224
+ w as l
225
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@web-ppt/agent-mcp",
3
- "version": "0.8.0-beta.2",
3
+ "version": "0.8.0-beta.3",
4
4
  "description": "Local-only MCP stdio server and Agent Skill for deterministic PPT reading, editing, rendering and saving.",
5
5
  "keywords": [
6
6
  "pptx",
@@ -48,8 +48,8 @@
48
48
  "prepack": "node ../../tooling/sync-package-docs.mjs"
49
49
  },
50
50
  "peerDependencies": {
51
- "@web-ppt/core": "^0.8.0-beta.2",
52
- "@web-ppt/edit-core": "^0.8.0-beta.2"
51
+ "@web-ppt/core": "^0.8.0-beta.3",
52
+ "@web-ppt/edit-core": "^0.8.0-beta.3"
53
53
  },
54
54
  "dependencies": {
55
55
  "@cfworker/json-schema": "^4.1.1",
@@ -1,221 +0,0 @@
1
- import { Server as g } from "@modelcontextprotocol/sdk/server/index.js";
2
- import { ListToolsRequestSchema as v, CallToolRequestSchema as b } from "@modelcontextprotocol/sdk/types.js";
3
- import { openAgentSession as S, listAgentTools as $ } from "@web-ppt/edit-core/agent";
4
- import { open as k, rename as q, rm as A, readFile as E } from "node:fs/promises";
5
- import { disposeOpcPackage as I } from "@web-ppt/edit-core/opc";
6
- import { serializeEditDoc as x } from "@web-ppt/edit-core/save";
7
- import { resolve as P, dirname as _, basename as M } from "node:path";
8
- class l extends Error {
9
- constructor(e, t) {
10
- super(t), this.reason = e, this.name = "AgentMcpError";
11
- }
12
- }
13
- let R = 0;
14
- function h(s) {
15
- if (typeof s != "string" || !s.trim())
16
- throw new l("invalid-path", "文件路径必须是非空字符串");
17
- if (/^[a-z][a-z0-9+.-]*:\/\//i.test(s))
18
- throw new l("invalid-path", `只接受本地文件路径,收到 URL:${s}`);
19
- return P(s);
20
- }
21
- async function w(s, e) {
22
- const t = `${_(s)}/.${M(s)}.web-ppt-${process.pid}-${++R}.tmp`;
23
- let n;
24
- try {
25
- n = await k(t, "wx"), await n.writeFile(e), await n.sync(), await n.close(), n = void 0, await q(t, s);
26
- } catch (o) {
27
- throw n && await n.close().catch(() => {
28
- }), await A(t, { force: !0 }).catch(() => {
29
- }), new l("save-failed", `写入 ${s} 失败:${o instanceof Error ? o.message : String(o)}`);
30
- }
31
- }
32
- class T {
33
- constructor(e = {}) {
34
- this.options = e, this.sessions = /* @__PURE__ */ new Map(), this.sequence = 0, this.active = !0;
35
- }
36
- async open(e) {
37
- this.assertActive();
38
- const t = h(e);
39
- let n;
40
- try {
41
- n = await E(t);
42
- } catch (i) {
43
- throw new l("open-failed", `读取 ${t} 失败:${i instanceof Error ? i.message : String(i)}`);
44
- }
45
- const o = `session-${++this.sequence}`, a = new AbortController();
46
- let r;
47
- try {
48
- r = await S(n, {
49
- ...this.options,
50
- idPrefix: `mcp-${o}-`,
51
- lint: { ...this.options.lint, signal: a.signal }
52
- });
53
- } catch (i) {
54
- throw new l("open-failed", `打开 ${t} 失败:${i instanceof Error ? i.message : String(i)}`);
55
- }
56
- if (!this.active)
57
- throw r.dispose(), new l("session-disposed", "会话管理器已经释放");
58
- return this.sessions.set(o, { session: r, controller: a, queue: Promise.resolve(), closing: !1 }), { sessionId: o, path: t };
59
- }
60
- async call(e, t, n) {
61
- return this.run(e, (o) => o.call(t, n));
62
- }
63
- async save(e, t) {
64
- const n = h(t);
65
- return this.run(e, async (o) => {
66
- const { editor: a, doc: r } = o, i = a.captureSavepoint(), p = r.package && !r.package.disposed ? "patch" : "generated", c = await x(r);
67
- return c.package !== r.package && I(c.package), await w(n, c.bytes), i(), { ok: !0, value: {
68
- bytes: Buffer.from(c.bytes).toString("base64"),
69
- path: n,
70
- saveMode: p,
71
- packageMode: c.mode,
72
- fallbackReason: c.fallbackReason,
73
- preservedEntries: c.preservedEntries,
74
- rewrittenEntries: c.rewrittenEntries
75
- } };
76
- });
77
- }
78
- async render(e, t, n) {
79
- const o = n === void 0 ? void 0 : h(n);
80
- return this.run(e, async (a) => {
81
- const r = await a.call("render", t);
82
- if (!r.ok || o === void 0) return r;
83
- const i = r.value.format === "svg" ? new TextEncoder().encode(r.value.data) : Buffer.from(r.value.data, "base64");
84
- return await w(o, i), { ok: !0, value: { ...r.value, path: o } };
85
- });
86
- }
87
- async close(e) {
88
- const t = this.entry(e);
89
- t.closing = !0, t.controller.abort();
90
- const n = t.queue.then(() => (t.session.dispose(), this.sessions.delete(e), { closed: !0 }));
91
- return t.queue = n.then(() => {
92
- }, () => {
93
- }), n;
94
- }
95
- async dispose() {
96
- if (!this.active) return;
97
- this.active = !1;
98
- const e = [...this.sessions.values()];
99
- for (const t of e)
100
- t.closing = !0, t.controller.abort();
101
- await Promise.allSettled(e.map((t) => t.queue));
102
- for (const t of e) t.session.dispose();
103
- this.sessions.clear();
104
- }
105
- entry(e) {
106
- this.assertActive();
107
- const t = this.sessions.get(e);
108
- if (!t || t.closing) throw new l("target-not-found", `找不到打开的会话:${e}`);
109
- return t;
110
- }
111
- run(e, t) {
112
- const n = this.entry(e), o = n.queue.then(() => {
113
- if (!this.active || n.closing) throw new l("session-disposed", `会话已经关闭:${e}`);
114
- return t(n.session);
115
- });
116
- return n.queue = o.then(() => {
117
- }, () => {
118
- }), o;
119
- }
120
- assertActive() {
121
- if (!this.active) throw new l("session-disposed", "会话管理器已经释放");
122
- }
123
- }
124
- function y(s, e) {
125
- let t = s;
126
- for (; typeof t.$ref == "string"; )
127
- t = t.$ref.split("/").slice(1).reduce((r, i) => r[i], s);
128
- const n = { ...t.properties, sessionId: { type: "string" } };
129
- (e === "save" || e === "render") && (n.path = { type: "string" });
130
- const o = /* @__PURE__ */ new Set([...t.required ?? [], "sessionId"]);
131
- return e === "save" && o.add("path"), {
132
- ...t,
133
- type: "object",
134
- properties: n,
135
- required: [...o],
136
- ...s.definitions ? { definitions: s.definitions } : {}
137
- };
138
- }
139
- function N() {
140
- const s = [{
141
- name: "ppt_open",
142
- description: "打开本地 PPTX / PPT 并创建编辑会话",
143
- inputSchema: {
144
- type: "object",
145
- properties: { path: { type: "string" } },
146
- required: ["path"],
147
- additionalProperties: !1
148
- }
149
- }];
150
- for (const e of $()) {
151
- const t = e.name === "save" ? "save" : e.name === "render" ? "render" : "call";
152
- s.push({
153
- name: `ppt_${e.name}`,
154
- description: e.description,
155
- inputSchema: y(e.inputSchema, t)
156
- });
157
- }
158
- return s.push({
159
- name: "ppt_close",
160
- description: "关闭会话并释放文档资源",
161
- inputSchema: y({ type: "object", additionalProperties: !1 }, "close")
162
- }), s;
163
- }
164
- function f(s) {
165
- const e = s;
166
- return {
167
- isError: !0,
168
- content: [{ type: "text", text: JSON.stringify({
169
- ok: !1,
170
- error: {
171
- reason: typeof e?.reason == "string" ? e.reason : "tool-failed",
172
- message: typeof e?.message == "string" ? e.message : String(s),
173
- commandIndex: typeof e?.commandIndex == "number" ? e.commandIndex : null,
174
- elementId: typeof e?.elementId == "string" ? e.elementId : null,
175
- slideNumber: typeof e?.slideNumber == "number" ? e.slideNumber : null
176
- }
177
- }) }]
178
- };
179
- }
180
- function O(s) {
181
- if (!s || typeof s != "object" || Array.isArray(s)) throw new l("invalid-input", "工具参数必须是对象");
182
- return s;
183
- }
184
- function m(s) {
185
- if (typeof s.sessionId != "string" || !s.sessionId) throw new l("invalid-input", "sessionId 必须是非空字符串");
186
- return s.sessionId;
187
- }
188
- function J(s = {}) {
189
- const e = new T(s), t = new g({ name: "web-ppt-agent", version: "0.8.0-beta.1" }, { capabilities: { tools: {} } }), n = N();
190
- return t.setRequestHandler(v, async () => ({ tools: n })), t.setRequestHandler(b, async (o) => {
191
- try {
192
- const a = o.params.name, r = O(o.params.arguments ?? {});
193
- let i;
194
- if (a === "ppt_open") i = await e.open(r.path);
195
- else if (a === "ppt_close") i = await e.close(m(r));
196
- else if (a === "ppt_save") {
197
- const p = await e.save(m(r), r.path);
198
- if (!p.ok) return f(p.error);
199
- const { bytes: c, ...d } = p.value;
200
- i = d;
201
- } else if (a === "ppt_render") {
202
- const { sessionId: p, path: c, ...d } = r, u = await e.render(m(r), d, c);
203
- if (!u.ok) return f(u.error);
204
- i = u.value;
205
- } else if (a.startsWith("ppt_")) {
206
- const p = a.slice(4), { sessionId: c, ...d } = r, u = await e.call(m(r), p, d);
207
- if (!u.ok) return f(u.error);
208
- i = u.value;
209
- } else throw new l("target-not-found", `未知工具:${a}`);
210
- return { content: [{ type: "text", text: JSON.stringify({ ok: !0, value: i }) }] };
211
- } catch (a) {
212
- return f(a);
213
- }
214
- }), { server: t, manager: e };
215
- }
216
- export {
217
- l as A,
218
- T as a,
219
- J as c,
220
- h as l
221
- };