@pageops/sdk 0.1.4 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +2 -2
- package/dist/index.d.ts +4 -0
- package/dist/pageops-sdk.es.js +110 -93
- package/dist/pageops-sdk.umd.cjs +10 -10
- package/dist/types.d.ts +2 -0
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { ContextSession, ConversationMessage, ConversationThread, PageOpsMountOptions, SourceSnapshot } from './types';
|
|
1
|
+
import type { ContextSession, ConversationMessage, ConversationThread, PageOpsContext, PageOpsMountOptions, SourceSnapshot } from './types';
|
|
2
2
|
export declare class PageOpsClient {
|
|
3
3
|
private options;
|
|
4
4
|
constructor(options: PageOpsMountOptions);
|
|
5
|
-
createContext(): Promise<ContextSession>;
|
|
5
|
+
createContext(context?: PageOpsContext): Promise<ContextSession>;
|
|
6
6
|
listConversations(): Promise<ConversationThread[]>;
|
|
7
7
|
createConversation(contextSessionId: string, title?: string): Promise<ConversationThread>;
|
|
8
8
|
listMessages(threadId: string): Promise<ConversationMessage[]>;
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,8 @@ declare class PageOpsSDKController {
|
|
|
6
6
|
private root;
|
|
7
7
|
private panel;
|
|
8
8
|
private context;
|
|
9
|
+
private contextKey;
|
|
10
|
+
private selectedPageKey;
|
|
9
11
|
private currentThread;
|
|
10
12
|
private conversations;
|
|
11
13
|
private messages;
|
|
@@ -22,6 +24,8 @@ declare class PageOpsSDKController {
|
|
|
22
24
|
updateContext(options: Partial<PageOpsMountOptions>): void;
|
|
23
25
|
private renderClosed;
|
|
24
26
|
private ensureContext;
|
|
27
|
+
private getPageContext;
|
|
28
|
+
private getPageContextKey;
|
|
25
29
|
private loadSources;
|
|
26
30
|
private loadConversations;
|
|
27
31
|
private newConversation;
|
package/dist/pageops-sdk.es.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
var
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
class
|
|
1
|
+
var P = Object.defineProperty;
|
|
2
|
+
var w = (i, t, e) => t in i ? P(i, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : i[t] = e;
|
|
3
|
+
var c = (i, t, e) => w(i, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
class $ {
|
|
5
5
|
constructor(t) {
|
|
6
6
|
this.options = t;
|
|
7
7
|
}
|
|
8
|
-
async createContext() {
|
|
9
|
-
var
|
|
10
|
-
const t = (o = (s = (e = this.options).getPageContext) == null ? void 0 : s.call(e)) != null ? o : {};
|
|
8
|
+
async createContext(t = ((o) => (o = ((e) => ((s) => (s = (e = this.options).getPageContext) == null ? void 0 : s.call(e))())()) != null ? o : {})()) {
|
|
9
|
+
var n, r, l;
|
|
11
10
|
return this.request("/context/session", {
|
|
12
11
|
method: "POST",
|
|
13
12
|
body: JSON.stringify({
|
|
@@ -15,7 +14,8 @@ class S {
|
|
|
15
14
|
env: this.options.env,
|
|
16
15
|
route: t.route,
|
|
17
16
|
url: (n = t.url) != null ? n : location.href,
|
|
18
|
-
title: (
|
|
17
|
+
title: (r = t.title) != null ? r : document.title,
|
|
18
|
+
menu_path: (l = t.menuPath) != null ? l : [],
|
|
19
19
|
tenant_id: t.tenantId,
|
|
20
20
|
user_role: t.userRole,
|
|
21
21
|
build_version: t.buildVersion
|
|
@@ -49,9 +49,9 @@ class S {
|
|
|
49
49
|
});
|
|
50
50
|
}
|
|
51
51
|
async request(t, e) {
|
|
52
|
-
var
|
|
53
|
-
const s = (e == null ? void 0 : e.body) instanceof FormData ? e.headers : { "content-type": "application/json", ...(
|
|
54
|
-
if (!o.ok) throw new Error((
|
|
52
|
+
var r, l;
|
|
53
|
+
const s = (e == null ? void 0 : e.body) instanceof FormData ? e.headers : { "content-type": "application/json", ...(r = e == null ? void 0 : e.headers) != null ? r : {} }, o = await fetch(`${this.base()}${t}`, { ...e, headers: s }), n = await o.json();
|
|
54
|
+
if (!o.ok) throw new Error((l = n.message) != null ? l : "PageOps 请求失败");
|
|
55
55
|
return n;
|
|
56
56
|
}
|
|
57
57
|
base() {
|
|
@@ -59,27 +59,27 @@ class S {
|
|
|
59
59
|
return t.endsWith("/api") ? t : `${t}/api`;
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
-
function
|
|
63
|
-
return
|
|
62
|
+
function x(i) {
|
|
63
|
+
return i.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
64
64
|
}
|
|
65
|
-
function f(
|
|
66
|
-
return
|
|
65
|
+
function f(i) {
|
|
66
|
+
return x(i).replace(/`([^`]+)`/g, "<code>$1</code>").replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
|
|
67
67
|
}
|
|
68
|
-
function
|
|
69
|
-
const e =
|
|
68
|
+
function C(i, t) {
|
|
69
|
+
const e = i[t], s = i[t + 1];
|
|
70
70
|
if (!(e != null && e.includes("|")) || !/^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$/.test(s != null ? s : "")) return null;
|
|
71
71
|
const o = [];
|
|
72
72
|
let n = t;
|
|
73
|
-
for (; n <
|
|
74
|
-
o.push(
|
|
75
|
-
const
|
|
76
|
-
return { html: `<table><thead><tr>${
|
|
73
|
+
for (; n < i.length && i[n].includes("|") && i[n].trim(); )
|
|
74
|
+
o.push(i[n]), n += 1;
|
|
75
|
+
const r = (d) => d.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((g) => f(g.trim())), l = r(o[0]).map((d) => `<th>${d}</th>`).join(""), m = o.slice(2).map((d) => `<tr>${r(d).map((g) => `<td>${g}</td>`).join("")}</tr>`).join("");
|
|
76
|
+
return { html: `<table><thead><tr>${l}</tr></thead><tbody>${m}</tbody></table>`, next: n };
|
|
77
77
|
}
|
|
78
|
-
function
|
|
79
|
-
const t =
|
|
78
|
+
function S(i) {
|
|
79
|
+
const t = i.split(`
|
|
80
80
|
`), e = [];
|
|
81
81
|
for (let s = 0; s < t.length; s += 1) {
|
|
82
|
-
const o =
|
|
82
|
+
const o = C(t, s);
|
|
83
83
|
if (o) {
|
|
84
84
|
e.push(o.html), s = o.next - 1;
|
|
85
85
|
continue;
|
|
@@ -89,48 +89,50 @@ function b(r) {
|
|
|
89
89
|
}
|
|
90
90
|
return e.join("");
|
|
91
91
|
}
|
|
92
|
-
async function
|
|
93
|
-
var n,
|
|
94
|
-
const e =
|
|
92
|
+
async function E(i, t) {
|
|
93
|
+
var n, r, l;
|
|
94
|
+
const e = i.getReader(), s = new TextDecoder();
|
|
95
95
|
let o = "";
|
|
96
96
|
for (; ; ) {
|
|
97
|
-
const { done:
|
|
98
|
-
if (
|
|
97
|
+
const { done: m, value: d } = await e.read();
|
|
98
|
+
if (m) break;
|
|
99
99
|
o += s.decode(d, { stream: !0 });
|
|
100
|
-
const
|
|
100
|
+
const g = o.split(`
|
|
101
101
|
|
|
102
102
|
`);
|
|
103
|
-
o = (n =
|
|
104
|
-
for (const
|
|
105
|
-
const
|
|
106
|
-
|
|
103
|
+
o = (n = g.pop()) != null ? n : "";
|
|
104
|
+
for (const y of g) {
|
|
105
|
+
const h = (r = y.match(/^event: (.+)$/m)) == null ? void 0 : r[1], a = (l = y.match(/^data: (.+)$/m)) == null ? void 0 : l[1];
|
|
106
|
+
h && a && t(h, JSON.parse(a));
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
class
|
|
110
|
+
class T {
|
|
111
111
|
constructor() {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
112
|
+
c(this, "client", null);
|
|
113
|
+
c(this, "options", null);
|
|
114
|
+
c(this, "root", null);
|
|
115
|
+
c(this, "panel", null);
|
|
116
|
+
c(this, "context", null);
|
|
117
|
+
c(this, "contextKey", "");
|
|
118
|
+
c(this, "selectedPageKey", "");
|
|
119
|
+
c(this, "currentThread", null);
|
|
120
|
+
c(this, "conversations", []);
|
|
121
|
+
c(this, "messages", []);
|
|
122
|
+
c(this, "steps", []);
|
|
123
|
+
c(this, "sourceSnapshots", []);
|
|
124
|
+
c(this, "sourceUploadStatus", "");
|
|
125
|
+
c(this, "sourceUploadError", "");
|
|
126
|
+
c(this, "sourceUploading", !1);
|
|
127
|
+
c(this, "streaming", "");
|
|
126
128
|
}
|
|
127
129
|
mount(t) {
|
|
128
130
|
var e;
|
|
129
|
-
this.unmount(), this.options = t, this.client = new
|
|
131
|
+
this.unmount(), this.options = t, this.client = new $(t), this.root = document.createElement("div"), this.root.className = "pageops-sdk", ((e = t.container) != null ? e : document.body).appendChild(this.root), this.renderClosed();
|
|
130
132
|
}
|
|
131
133
|
unmount() {
|
|
132
134
|
var t;
|
|
133
|
-
(t = this.root) == null || t.remove(), this.root = null, this.panel = null, this.context = null, this.currentThread = null, this.conversations = [], this.messages = [], this.steps = [], this.sourceSnapshots = [], this.sourceUploadStatus = "", this.sourceUploadError = "", this.sourceUploading = !1, this.streaming = "";
|
|
135
|
+
(t = this.root) == null || t.remove(), this.root = null, this.panel = null, this.context = null, this.contextKey = "", this.selectedPageKey = "", this.currentThread = null, this.conversations = [], this.messages = [], this.steps = [], this.sourceSnapshots = [], this.sourceUploadStatus = "", this.sourceUploadError = "", this.sourceUploading = !1, this.streaming = "";
|
|
134
136
|
}
|
|
135
137
|
open() {
|
|
136
138
|
this.root && (this.root.innerHTML = "", this.panel = document.createElement("div"), this.panel.className = "po-panel", this.root.appendChild(this.panel), this.loadSources(), this.loadConversations(), this.renderPanel());
|
|
@@ -139,16 +141,29 @@ class P {
|
|
|
139
141
|
this.renderClosed();
|
|
140
142
|
}
|
|
141
143
|
updateContext(t) {
|
|
142
|
-
this.options && (this.options = { ...this.options, ...t }, this.client = new
|
|
144
|
+
this.options && (this.options = { ...this.options, ...t }, this.client = new $(this.options), this.context = null, this.contextKey = "");
|
|
143
145
|
}
|
|
144
146
|
renderClosed() {
|
|
145
147
|
var t;
|
|
146
148
|
this.root && (this.root.innerHTML = '<button class="po-fab" type="button">多轮问答</button>', (t = this.root.querySelector("button")) == null || t.addEventListener("click", () => this.open()));
|
|
147
149
|
}
|
|
148
150
|
async ensureContext() {
|
|
149
|
-
if (this.
|
|
150
|
-
|
|
151
|
-
return this.context = await this.client.createContext(), this.context;
|
|
151
|
+
if (!this.client || !this.options) throw new Error("PageOps SDK 未初始化");
|
|
152
|
+
const t = this.getPageContext(), e = this.getPageContextKey(t);
|
|
153
|
+
return this.context && this.contextKey === e ? this.context : (this.context = await this.client.createContext(t), this.contextKey = e, this.context);
|
|
154
|
+
}
|
|
155
|
+
getPageContext() {
|
|
156
|
+
var t, e, s;
|
|
157
|
+
return (s = (e = (t = this.options) == null ? void 0 : t.getPageContext) == null ? void 0 : e.call(t)) != null ? s : {};
|
|
158
|
+
}
|
|
159
|
+
getPageContextKey(t) {
|
|
160
|
+
var e, s, o, n;
|
|
161
|
+
return JSON.stringify([
|
|
162
|
+
(e = t.route) != null ? e : "",
|
|
163
|
+
(s = t.url) != null ? s : location.href,
|
|
164
|
+
(o = t.title) != null ? o : document.title,
|
|
165
|
+
(n = t.menuPath) != null ? n : []
|
|
166
|
+
]);
|
|
152
167
|
}
|
|
153
168
|
async loadSources() {
|
|
154
169
|
if (this.client) {
|
|
@@ -164,12 +179,13 @@ class P {
|
|
|
164
179
|
this.client && (this.conversations = await this.client.listConversations(), this.renderPanel());
|
|
165
180
|
}
|
|
166
181
|
async newConversation() {
|
|
182
|
+
var s;
|
|
167
183
|
if (!this.client) return;
|
|
168
|
-
const t = await this.ensureContext();
|
|
169
|
-
this.currentThread = await this.client.createConversation(t.id,
|
|
184
|
+
const t = await this.ensureContext(), e = (s = t.menu_path) != null && s.length ? t.menu_path.join(" / ") : t.route || document.title || "新的问答";
|
|
185
|
+
this.currentThread = await this.client.createConversation(t.id, e), this.selectedPageKey = this.contextKey, this.conversations = [this.currentThread, ...this.conversations], this.messages = [], this.steps = [], this.streaming = "", this.renderPanel();
|
|
170
186
|
}
|
|
171
187
|
async selectThread(t) {
|
|
172
|
-
this.client && (this.currentThread = t, this.messages = await this.client.listMessages(t.id), this.steps = [], this.streaming = "", this.renderPanel());
|
|
188
|
+
this.client && (this.currentThread = t, this.selectedPageKey = this.getPageContextKey(this.getPageContext()), this.messages = await this.client.listMessages(t.id), this.steps = [], this.streaming = "", this.renderPanel());
|
|
173
189
|
}
|
|
174
190
|
async uploadSource(t) {
|
|
175
191
|
if (this.client) {
|
|
@@ -189,13 +205,14 @@ class P {
|
|
|
189
205
|
this.sourceUploadError = "请先上传代码包后再提问。", this.renderPanel();
|
|
190
206
|
return;
|
|
191
207
|
}
|
|
192
|
-
|
|
208
|
+
const e = this.getPageContextKey(this.getPageContext());
|
|
209
|
+
if (this.currentThread && this.selectedPageKey && this.selectedPageKey !== e && (this.currentThread = null), this.currentThread || await this.newConversation(), !this.currentThread) return;
|
|
193
210
|
this.messages.push({ id: `local-${Date.now()}`, role: "user", content: t, metadata_json: {} }), this.steps = [], this.streaming = "", this.renderPanel();
|
|
194
|
-
const
|
|
195
|
-
if (!
|
|
196
|
-
await
|
|
197
|
-
var
|
|
198
|
-
|
|
211
|
+
const s = await this.client.streamQuestion(this.currentThread.id, t);
|
|
212
|
+
if (!s.ok || !s.body) throw new Error("PageOps 流式问答失败");
|
|
213
|
+
await E(s.body, (o, n) => {
|
|
214
|
+
var r;
|
|
215
|
+
o === "step" && this.upsertStep(n), o === "delta" && (this.streaming += String((r = n.content) != null ? r : "")), o === "done" && this.selectThread(this.currentThread), o === "error" && this.upsertStep({ id: "error", title: "执行失败", status: "failed", details: n }), this.renderPanel();
|
|
199
216
|
});
|
|
200
217
|
}
|
|
201
218
|
upsertStep(t) {
|
|
@@ -212,15 +229,15 @@ class P {
|
|
|
212
229
|
var e;
|
|
213
230
|
return `
|
|
214
231
|
<button class="po-thread ${t.id === ((e = this.currentThread) == null ? void 0 : e.id) ? "active" : ""}" data-thread="${t.id}">
|
|
215
|
-
<strong>${u(t.title)}</strong><span>${
|
|
232
|
+
<strong>${u(t.title)}</strong><span>${b(t.created_at)}</span>
|
|
216
233
|
</button>`;
|
|
217
234
|
}).join("")}</div>
|
|
218
235
|
</aside>
|
|
219
236
|
<main class="po-chat">
|
|
220
237
|
<header><div><p>问数回答</p><h2>${this.currentThread ? u(this.currentThread.title) : "未选择问答"}</h2></div><span>Markdown · 流式输出</span></header>
|
|
221
238
|
<section class="po-messages">
|
|
222
|
-
${this.messages.map((t) => `<article class="po-message ${t.role}"><b>${t.role === "user" ? "你" : "PageOps 分析"}</b><div>${t.role === "assistant" ?
|
|
223
|
-
${this.streaming ? `<article class="po-message assistant streaming"><b>PageOps 分析</b><div>${
|
|
239
|
+
${this.messages.map((t) => `<article class="po-message ${t.role}"><b>${t.role === "user" ? "你" : "PageOps 分析"}</b><div>${t.role === "assistant" ? S(t.content) : u(t.content)}</div></article>`).join("")}
|
|
240
|
+
${this.streaming ? `<article class="po-message assistant streaming"><b>PageOps 分析</b><div>${S(this.streaming)}</div></article>` : ""}
|
|
224
241
|
</section>
|
|
225
242
|
<form class="po-composer"><textarea ${this.sourceSnapshots.length ? "" : "disabled"} placeholder="${this.sourceSnapshots.length ? "请输入页面分析问题,Enter 发送,Shift + Enter 换行" : "请先上传代码包后再提问"}"></textarea><button type="submit" ${this.sourceSnapshots.length ? "" : "disabled"}>${this.sourceSnapshots.length ? "发送" : "请先上传代码包"}</button></form>
|
|
226
243
|
</main>
|
|
@@ -243,7 +260,7 @@ class P {
|
|
|
243
260
|
`;
|
|
244
261
|
}
|
|
245
262
|
renderSourceItem(t) {
|
|
246
|
-
const e = t.filename || "未命名代码包", s = t.created_at ?
|
|
263
|
+
const e = t.filename || "未命名代码包", s = t.created_at ? b(t.created_at) : "刚刚上传", o = U(t.archive_bytes || t.total_bytes || 0);
|
|
247
264
|
return `<article class="po-source-item"><strong>${u(e)}</strong><span>${u(s)} · ${u(o)}</span></article>`;
|
|
248
265
|
}
|
|
249
266
|
renderSteps() {
|
|
@@ -253,43 +270,43 @@ class P {
|
|
|
253
270
|
}).join("");
|
|
254
271
|
}
|
|
255
272
|
bindPanelEvents() {
|
|
256
|
-
var t, e, s, o, n,
|
|
257
|
-
(e = (t = this.panel) == null ? void 0 : t.querySelector('[data-action="new"]')) == null || e.addEventListener("click", () => void this.newConversation()), (o = (s = this.panel) == null ? void 0 : s.querySelector('[data-action="close"]')) == null || o.addEventListener("click", () => this.close()), (
|
|
273
|
+
var t, e, s, o, n, r, l, m, d, g, y;
|
|
274
|
+
(e = (t = this.panel) == null ? void 0 : t.querySelector('[data-action="new"]')) == null || e.addEventListener("click", () => void this.newConversation()), (o = (s = this.panel) == null ? void 0 : s.querySelector('[data-action="close"]')) == null || o.addEventListener("click", () => this.close()), (r = (n = this.panel) == null ? void 0 : n.querySelector('[data-action="upload-source"]')) == null || r.addEventListener("change", (h) => {
|
|
258
275
|
var p;
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
}), (
|
|
262
|
-
|
|
263
|
-
const
|
|
264
|
-
|
|
276
|
+
const a = (p = h.target.files) == null ? void 0 : p[0];
|
|
277
|
+
a && this.uploadSource(a);
|
|
278
|
+
}), (l = this.panel) == null || l.querySelectorAll("[data-thread]").forEach((h) => {
|
|
279
|
+
h.addEventListener("click", () => {
|
|
280
|
+
const a = this.conversations.find((p) => p.id === h.dataset.thread);
|
|
281
|
+
a && this.selectThread(a);
|
|
265
282
|
});
|
|
266
|
-
}), (d = (
|
|
267
|
-
var
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
p && (
|
|
271
|
-
}), (
|
|
272
|
-
var p,
|
|
273
|
-
const
|
|
274
|
-
|
|
283
|
+
}), (d = (m = this.panel) == null ? void 0 : m.querySelector("form")) == null || d.addEventListener("submit", (h) => {
|
|
284
|
+
var v;
|
|
285
|
+
h.preventDefault();
|
|
286
|
+
const a = (v = this.panel) == null ? void 0 : v.querySelector("textarea"), p = a == null ? void 0 : a.value.trim();
|
|
287
|
+
p && (a.value = "", this.ask(p));
|
|
288
|
+
}), (y = (g = this.panel) == null ? void 0 : g.querySelector("textarea")) == null || y.addEventListener("keydown", (h) => {
|
|
289
|
+
var p, v;
|
|
290
|
+
const a = h;
|
|
291
|
+
a.key === "Enter" && !a.shiftKey && (a.preventDefault(), (v = (p = this.panel) == null ? void 0 : p.querySelector("form")) == null || v.requestSubmit());
|
|
275
292
|
});
|
|
276
293
|
}
|
|
277
294
|
}
|
|
278
|
-
function u(
|
|
279
|
-
return
|
|
295
|
+
function u(i) {
|
|
296
|
+
return i.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
280
297
|
}
|
|
281
|
-
function
|
|
282
|
-
return new Intl.DateTimeFormat("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" }).format(new Date(
|
|
298
|
+
function b(i) {
|
|
299
|
+
return new Intl.DateTimeFormat("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit" }).format(new Date(i));
|
|
283
300
|
}
|
|
284
|
-
function
|
|
285
|
-
if (!
|
|
301
|
+
function U(i) {
|
|
302
|
+
if (!i) return "0 B";
|
|
286
303
|
const t = ["B", "KB", "MB", "GB"];
|
|
287
|
-
let e =
|
|
304
|
+
let e = i, s = 0;
|
|
288
305
|
for (; e >= 1024 && s < t.length - 1; )
|
|
289
306
|
e /= 1024, s += 1;
|
|
290
307
|
return `${e >= 10 || s === 0 ? e.toFixed(0) : e.toFixed(1)} ${t[s]}`;
|
|
291
308
|
}
|
|
292
|
-
const _ = new
|
|
309
|
+
const _ = new T();
|
|
293
310
|
export {
|
|
294
311
|
_ as PageOpsSDK
|
|
295
312
|
};
|
package/dist/pageops-sdk.umd.cjs
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
(function(d,
|
|
2
|
-
`),e=[];for(let s=0;s<t.length;s+=1){const
|
|
1
|
+
(function(d,l){typeof exports=="object"&&typeof module!="undefined"?l(exports):typeof define=="function"&&define.amd?define(["exports"],l):(d=typeof globalThis!="undefined"?globalThis:d||self,l(d.PageOpsSDK={}))})(this,(function(d){"use strict";var K=Object.defineProperty;var O=(d,l,y)=>l in d?K(d,l,{enumerable:!0,configurable:!0,writable:!0,value:y}):d[l]=y;var c=(d,l,y)=>O(d,typeof l!="symbol"?l+"":l,y);class l{constructor(t){this.options=t}async createContext(t=(i=>(i=(e=>(s=>(s=(e=this.options).getPageContext)==null?void 0:s.call(e))())())!=null?i:{})()){var n,r,h;return this.request("/context/session",{method:"POST",body:JSON.stringify({app_id:this.options.appId,env:this.options.env,route:t.route,url:(n=t.url)!=null?n:location.href,title:(r=t.title)!=null?r:document.title,menu_path:(h=t.menuPath)!=null?h:[],tenant_id:t.tenantId,user_role:t.userRole,build_version:t.buildVersion})})}async listConversations(){return(await this.request(`/conversations?app_id=${encodeURIComponent(this.options.appId)}`)).conversations}async createConversation(t,e){return this.request("/conversations",{method:"POST",body:JSON.stringify({app_id:this.options.appId,context_session_id:t,title:e})})}async listMessages(t){return(await this.request(`/conversations/${encodeURIComponent(t)}/messages?app_id=${encodeURIComponent(this.options.appId)}`)).messages}async listSources(){return(await this.request(`/sources?project_id=${encodeURIComponent(this.options.appId)}`)).sources}uploadSource(t){const e=new FormData;return e.append("file",t),this.request(`/sources/upload?project_id=${encodeURIComponent(this.options.appId)}`,{method:"POST",body:e,headers:{}})}streamQuestion(t,e){return fetch(`${this.base()}/question/stream`,{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({app_id:this.options.appId,thread_id:t,question:e})})}async request(t,e){var r,h;const s=(e==null?void 0:e.body)instanceof FormData?e.headers:{"content-type":"application/json",...(r=e==null?void 0:e.headers)!=null?r:{}},i=await fetch(`${this.base()}${t}`,{...e,headers:s}),n=await i.json();if(!i.ok)throw new Error((h=n.message)!=null?h:"PageOps 请求失败");return n}base(){const t=this.options.apiBase.replace(/\/$/,"");return t.endsWith("/api")?t:`${t}/api`}}function y(o){return o.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function v(o){return y(o).replace(/`([^`]+)`/g,"<code>$1</code>").replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>")}function x(o,t){const e=o[t],s=o[t+1];if(!(e!=null&&e.includes("|"))||!/^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$/.test(s!=null?s:""))return null;const i=[];let n=t;for(;n<o.length&&o[n].includes("|")&&o[n].trim();)i.push(o[n]),n+=1;const r=g=>g.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").map(f=>v(f.trim())),h=r(i[0]).map(g=>`<th>${g}</th>`).join(""),S=i.slice(2).map(g=>`<tr>${r(g).map(f=>`<td>${f}</td>`).join("")}</tr>`).join("");return{html:`<table><thead><tr>${h}</tr></thead><tbody>${S}</tbody></table>`,next:n}}function P(o){const t=o.split(`
|
|
2
|
+
`),e=[];for(let s=0;s<t.length;s+=1){const i=x(t,s);if(i){e.push(i.html),s=i.next-1;continue}const n=t[s];n.startsWith("### ")?e.push(`<h4>${v(n.slice(4))}</h4>`):n.startsWith("## ")?e.push(`<h3>${v(n.slice(3))}</h3>`):n.startsWith("# ")?e.push(`<h2>${v(n.slice(2))}</h2>`):n.startsWith("- ")?e.push(`<li>${v(n.slice(2))}</li>`):n.trim()?e.push(`<p>${v(n)}</p>`):e.push("<br>")}return e.join("")}async function C(o,t){var n,r,h;const e=o.getReader(),s=new TextDecoder;let i="";for(;;){const{done:S,value:g}=await e.read();if(S)break;i+=s.decode(g,{stream:!0});const f=i.split(`
|
|
3
3
|
|
|
4
|
-
`);
|
|
4
|
+
`);i=(n=f.pop())!=null?n:"";for(const $ of f){const p=(r=$.match(/^event: (.+)$/m))==null?void 0:r[1],a=(h=$.match(/^data: (.+)$/m))==null?void 0:h[1];p&&a&&t(p,JSON.parse(a))}}}class T{constructor(){c(this,"client",null);c(this,"options",null);c(this,"root",null);c(this,"panel",null);c(this,"context",null);c(this,"contextKey","");c(this,"selectedPageKey","");c(this,"currentThread",null);c(this,"conversations",[]);c(this,"messages",[]);c(this,"steps",[]);c(this,"sourceSnapshots",[]);c(this,"sourceUploadStatus","");c(this,"sourceUploadError","");c(this,"sourceUploading",!1);c(this,"streaming","")}mount(t){var e;this.unmount(),this.options=t,this.client=new l(t),this.root=document.createElement("div"),this.root.className="pageops-sdk",((e=t.container)!=null?e:document.body).appendChild(this.root),this.renderClosed()}unmount(){var t;(t=this.root)==null||t.remove(),this.root=null,this.panel=null,this.context=null,this.contextKey="",this.selectedPageKey="",this.currentThread=null,this.conversations=[],this.messages=[],this.steps=[],this.sourceSnapshots=[],this.sourceUploadStatus="",this.sourceUploadError="",this.sourceUploading=!1,this.streaming=""}open(){this.root&&(this.root.innerHTML="",this.panel=document.createElement("div"),this.panel.className="po-panel",this.root.appendChild(this.panel),this.loadSources(),this.loadConversations(),this.renderPanel())}close(){this.renderClosed()}updateContext(t){this.options&&(this.options={...this.options,...t},this.client=new l(this.options),this.context=null,this.contextKey="")}renderClosed(){var t;this.root&&(this.root.innerHTML='<button class="po-fab" type="button">多轮问答</button>',(t=this.root.querySelector("button"))==null||t.addEventListener("click",()=>this.open()))}async ensureContext(){if(!this.client||!this.options)throw new Error("PageOps SDK 未初始化");const t=this.getPageContext(),e=this.getPageContextKey(t);return this.context&&this.contextKey===e?this.context:(this.context=await this.client.createContext(t),this.contextKey=e,this.context)}getPageContext(){var t,e,s;return(s=(e=(t=this.options)==null?void 0:t.getPageContext)==null?void 0:e.call(t))!=null?s:{}}getPageContextKey(t){var e,s,i,n;return JSON.stringify([(e=t.route)!=null?e:"",(s=t.url)!=null?s:location.href,(i=t.title)!=null?i:document.title,(n=t.menuPath)!=null?n:[]])}async loadSources(){if(this.client){try{this.sourceSnapshots=await this.client.listSources(),this.sourceUploadError=""}catch(t){this.sourceUploadError=t instanceof Error?t.message:"源码包状态加载失败"}this.renderPanel()}}async loadConversations(){this.client&&(this.conversations=await this.client.listConversations(),this.renderPanel())}async newConversation(){var s;if(!this.client)return;const t=await this.ensureContext(),e=(s=t.menu_path)!=null&&s.length?t.menu_path.join(" / "):t.route||document.title||"新的问答";this.currentThread=await this.client.createConversation(t.id,e),this.selectedPageKey=this.contextKey,this.conversations=[this.currentThread,...this.conversations],this.messages=[],this.steps=[],this.streaming="",this.renderPanel()}async selectThread(t){this.client&&(this.currentThread=t,this.selectedPageKey=this.getPageContextKey(this.getPageContext()),this.messages=await this.client.listMessages(t.id),this.steps=[],this.streaming="",this.renderPanel())}async uploadSource(t){if(this.client){this.sourceUploading=!0,this.sourceUploadStatus="上传中...",this.sourceUploadError="",this.renderPanel();try{await this.client.uploadSource(t),this.sourceSnapshots=await this.client.listSources(),this.sourceUploadStatus="上传完成"}catch(e){this.sourceUploadError=e instanceof Error?e.message:"源码包上传失败",this.sourceUploadStatus=""}finally{this.sourceUploading=!1,this.renderPanel()}}}async ask(t){if(!this.client)return;if(!this.sourceSnapshots.length){this.sourceUploadError="请先上传代码包后再提问。",this.renderPanel();return}const e=this.getPageContextKey(this.getPageContext());if(this.currentThread&&this.selectedPageKey&&this.selectedPageKey!==e&&(this.currentThread=null),this.currentThread||await this.newConversation(),!this.currentThread)return;this.messages.push({id:`local-${Date.now()}`,role:"user",content:t,metadata_json:{}}),this.steps=[],this.streaming="",this.renderPanel();const s=await this.client.streamQuestion(this.currentThread.id,t);if(!s.ok||!s.body)throw new Error("PageOps 流式问答失败");await C(s.body,(i,n)=>{var r;i==="step"&&this.upsertStep(n),i==="delta"&&(this.streaming+=String((r=n.content)!=null?r:"")),i==="done"&&this.selectThread(this.currentThread),i==="error"&&this.upsertStep({id:"error",title:"执行失败",status:"failed",details:n}),this.renderPanel()})}upsertStep(t){const e=this.steps.findIndex(s=>s.id===t.id);this.steps=e===-1?[...this.steps,t]:this.steps.map(s=>s.id===t.id?{...s,...t}:s)}renderPanel(){this.panel&&(this.panel.innerHTML=`
|
|
5
5
|
<aside class="po-sidebar">
|
|
6
6
|
<div class="po-brand"><div class="po-logo"></div><div><strong>PageOps 问答</strong><span>页面工程专家</span></div></div>
|
|
7
7
|
<button class="po-new" data-action="new">+ 新建问答</button>
|
|
8
8
|
<input class="po-search" placeholder="搜索问答" />
|
|
9
9
|
<div class="po-thread-list">${this.conversations.map(t=>{var e;return`
|
|
10
10
|
<button class="po-thread ${t.id===((e=this.currentThread)==null?void 0:e.id)?"active":""}" data-thread="${t.id}">
|
|
11
|
-
<strong>${
|
|
11
|
+
<strong>${u(t.title)}</strong><span>${w(t.created_at)}</span>
|
|
12
12
|
</button>`}).join("")}</div>
|
|
13
13
|
</aside>
|
|
14
14
|
<main class="po-chat">
|
|
15
|
-
<header><div><p>问数回答</p><h2>${this.currentThread?
|
|
15
|
+
<header><div><p>问数回答</p><h2>${this.currentThread?u(this.currentThread.title):"未选择问答"}</h2></div><span>Markdown · 流式输出</span></header>
|
|
16
16
|
<section class="po-messages">
|
|
17
|
-
${this.messages.map(t=>`<article class="po-message ${t.role}"><b>${t.role==="user"?"你":"PageOps 分析"}</b><div>${t.role==="assistant"?
|
|
18
|
-
${this.streaming?`<article class="po-message assistant streaming"><b>PageOps 分析</b><div>${
|
|
17
|
+
${this.messages.map(t=>`<article class="po-message ${t.role}"><b>${t.role==="user"?"你":"PageOps 分析"}</b><div>${t.role==="assistant"?P(t.content):u(t.content)}</div></article>`).join("")}
|
|
18
|
+
${this.streaming?`<article class="po-message assistant streaming"><b>PageOps 分析</b><div>${P(this.streaming)}</div></article>`:""}
|
|
19
19
|
</section>
|
|
20
20
|
<form class="po-composer"><textarea ${this.sourceSnapshots.length?"":"disabled"} placeholder="${this.sourceSnapshots.length?"请输入页面分析问题,Enter 发送,Shift + Enter 换行":"请先上传代码包后再提问"}"></textarea><button type="submit" ${this.sourceSnapshots.length?"":"disabled"}>${this.sourceSnapshots.length?"发送":"请先上传代码包"}</button></form>
|
|
21
21
|
</main>
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
<span>${this.sourceUploading?"上传中...":"选择代码包"}</span>
|
|
29
29
|
<input type="file" accept=".zip,.tar,.gz,.tgz" data-action="upload-source" ${this.sourceUploading?"disabled":""} />
|
|
30
30
|
</label>
|
|
31
|
-
${this.sourceUploadStatus?`<p class="po-upload-status">${
|
|
31
|
+
${this.sourceUploadStatus?`<p class="po-upload-status">${u(this.sourceUploadStatus)}</p>`:""}
|
|
32
32
|
${this.sourceSnapshots.length?`<div class="po-source-list">${this.sourceSnapshots.map(t=>this.renderSourceItem(t)).join("")}</div>`:'<p class="po-upload-empty">当前系统暂无代码包,请先上传后再提问。</p>'}
|
|
33
|
-
${this.sourceUploadError?`<p class="po-upload-error">${
|
|
33
|
+
${this.sourceUploadError?`<p class="po-upload-error">${u(this.sourceUploadError)}</p>`:""}
|
|
34
34
|
</div>
|
|
35
|
-
`}renderSourceItem(t){const e=t.filename||"未命名代码包",s=t.created_at?
|
|
35
|
+
`}renderSourceItem(t){const e=t.filename||"未命名代码包",s=t.created_at?w(t.created_at):"刚刚上传",i=E(t.archive_bytes||t.total_bytes||0);return`<article class="po-source-item"><strong>${u(e)}</strong><span>${u(s)} · ${u(i)}</span></article>`}renderSteps(){return(this.steps.length?this.steps:[{id:"idle",title:"等待任务",status:"pending",details:{message:"发送消息后展示执行过程。"}}]).map(e=>{var s;return`<details class="po-step ${e.status}" ${e.status==="running"||e.status==="failed"?"open":""}><summary><strong>${u(e.title)}</strong><span>${e.status}</span></summary><pre>${u(JSON.stringify((s=e.details)!=null?s:{},null,2))}</pre></details>`}).join("")}bindPanelEvents(){var t,e,s,i,n,r,h,S,g,f,$;(e=(t=this.panel)==null?void 0:t.querySelector('[data-action="new"]'))==null||e.addEventListener("click",()=>void this.newConversation()),(i=(s=this.panel)==null?void 0:s.querySelector('[data-action="close"]'))==null||i.addEventListener("click",()=>this.close()),(r=(n=this.panel)==null?void 0:n.querySelector('[data-action="upload-source"]'))==null||r.addEventListener("change",p=>{var m;const a=(m=p.target.files)==null?void 0:m[0];a&&this.uploadSource(a)}),(h=this.panel)==null||h.querySelectorAll("[data-thread]").forEach(p=>{p.addEventListener("click",()=>{const a=this.conversations.find(m=>m.id===p.dataset.thread);a&&this.selectThread(a)})}),(g=(S=this.panel)==null?void 0:S.querySelector("form"))==null||g.addEventListener("submit",p=>{var b;p.preventDefault();const a=(b=this.panel)==null?void 0:b.querySelector("textarea"),m=a==null?void 0:a.value.trim();m&&(a.value="",this.ask(m))}),($=(f=this.panel)==null?void 0:f.querySelector("textarea"))==null||$.addEventListener("keydown",p=>{var m,b;const a=p;a.key==="Enter"&&!a.shiftKey&&(a.preventDefault(),(b=(m=this.panel)==null?void 0:m.querySelector("form"))==null||b.requestSubmit())})}}function u(o){return o.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}function w(o){return new Intl.DateTimeFormat("zh-CN",{month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit"}).format(new Date(o))}function E(o){if(!o)return"0 B";const t=["B","KB","MB","GB"];let e=o,s=0;for(;e>=1024&&s<t.length-1;)e/=1024,s+=1;return`${e>=10||s===0?e.toFixed(0):e.toFixed(1)} ${t[s]}`}const U=new T;d.PageOpsSDK=U,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
|
package/dist/types.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export interface PageOpsContext {
|
|
|
2
2
|
route?: string;
|
|
3
3
|
url?: string;
|
|
4
4
|
title?: string;
|
|
5
|
+
menuPath?: string[];
|
|
5
6
|
tenantId?: string;
|
|
6
7
|
userRole?: string;
|
|
7
8
|
buildVersion?: string;
|
|
@@ -20,6 +21,7 @@ export interface ContextSession {
|
|
|
20
21
|
route?: string;
|
|
21
22
|
url?: string;
|
|
22
23
|
title?: string;
|
|
24
|
+
menu_path?: string[];
|
|
23
25
|
tenant_id?: string;
|
|
24
26
|
user_role?: string;
|
|
25
27
|
build_version?: string;
|