@seer-ai/seer-vue 0.1.0-beta.1 → 0.1.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/README.md CHANGED
@@ -17,7 +17,8 @@ import App from './App.vue'
17
17
 
18
18
  createApp(App)
19
19
  .use(SeerChatPlugin, {
20
- endpoint: '/api/seer-chat',
20
+ sessionEndpoint: 'https://seer.example.com/v1/browser/session',
21
+ endpoint: 'https://seer.example.com/v1/browser/chat/completions',
21
22
  model: 'seer',
22
23
  stream: false,
23
24
  })
@@ -55,4 +56,4 @@ async function submit() {
55
56
 
56
57
  ## Security
57
58
 
58
- Do not put long-lived seer API keys in browser code or `VITE_*` variables. Browser code should call an application-owned proxy such as `/api/seer-chat`; that server-side proxy forwards to seer `/v1/chat/completions` and injects the Bearer token.
59
+ Do not put long-lived seer API keys in browser code or `VITE_*` variables. Browser code should call seer's browser session endpoint, receive a short-lived token, and use that token for `/v1/browser/chat/completions`.
package/dist/client.d.ts CHANGED
@@ -6,6 +6,7 @@ export interface SeerChatMessage {
6
6
  }
7
7
  export interface SeerClientOptions {
8
8
  endpoint: string;
9
+ sessionEndpoint?: string;
9
10
  model?: string;
10
11
  fetcher?: typeof fetch;
11
12
  }
package/dist/index.css CHANGED
@@ -1 +1 @@
1
- .seer-chat[data-v-51cf8455]{display:grid;gap:16px;max-width:720px;padding:20px;color:#172033;background:#fff;border:1px solid #d9e2ef;border-radius:16px}.seer-chat__header[data-v-51cf8455]{display:flex;gap:16px;align-items:flex-start;justify-content:space-between}.seer-chat__header h2[data-v-51cf8455]{margin:0;font-size:20px}.seer-chat__header p[data-v-51cf8455],.seer-chat__empty[data-v-51cf8455],.seer-chat__status[data-v-51cf8455],.seer-chat__citations[data-v-51cf8455]{margin:4px 0 0;color:#5f6f89;font-size:14px}.seer-chat__messages[data-v-51cf8455]{display:grid;gap:12px;min-height:180px}.seer-chat__message[data-v-51cf8455]{display:grid;gap:4px;max-width:86%;padding:12px 14px;border-radius:14px;background:#f3f6fb}.seer-chat__message--user[data-v-51cf8455]{justify-self:end;color:#fff;background:#1f5eff}.seer-chat__role[data-v-51cf8455]{font-size:12px;font-weight:700;opacity:.75}.seer-chat__message p[data-v-51cf8455]{margin:0;white-space:pre-wrap}.seer-chat__error[data-v-51cf8455],.seer-chat__handoff[data-v-51cf8455]{margin:0;padding:10px 12px;border-radius:10px}.seer-chat__error[data-v-51cf8455]{color:#8a1f11;background:#fff0ed}.seer-chat__handoff[data-v-51cf8455]{color:#704600;background:#fff7db}.seer-chat__citations ul[data-v-51cf8455]{margin:6px 0 0;padding-left:18px}.seer-chat__form[data-v-51cf8455]{display:grid;gap:10px}.seer-chat__label[data-v-51cf8455]{font-size:13px;font-weight:700}.seer-chat textarea[data-v-51cf8455]{width:100%;box-sizing:border-box;padding:12px;color:inherit;border:1px solid #c8d3e2;border-radius:12px;resize:vertical}.seer-chat button[data-v-51cf8455]{padding:9px 14px;color:#fff;background:#172033;border:0;border-radius:999px;cursor:pointer}.seer-chat button[data-v-51cf8455]:disabled{cursor:not-allowed;opacity:.5}.seer-chat button[data-v-51cf8455]:focus-visible,.seer-chat textarea[data-v-51cf8455]:focus-visible{outline:3px solid #8fb4ff;outline-offset:2px}.seer-chat__ghost-button[data-v-51cf8455]{color:#172033!important;background:#eef3fa!important}.seer-chat__actions[data-v-51cf8455]{display:flex;gap:8px;justify-content:flex-end}
1
+ .seer-chat[data-v-163bc07c]{display:grid;gap:16px;max-width:720px;padding:20px;color:#172033;background:#fff;border:1px solid #d9e2ef;border-radius:16px}.seer-chat__header[data-v-163bc07c]{display:flex;gap:16px;align-items:flex-start;justify-content:space-between}.seer-chat__header h2[data-v-163bc07c]{margin:0;font-size:20px}.seer-chat__header p[data-v-163bc07c],.seer-chat__empty[data-v-163bc07c],.seer-chat__status[data-v-163bc07c]{margin:4px 0 0;color:#5f6f89;font-size:14px}.seer-chat__messages[data-v-163bc07c]{display:grid;gap:12px;min-height:180px}.seer-chat__message[data-v-163bc07c]{display:grid;gap:4px;max-width:86%;padding:12px 14px;border-radius:14px;background:#f3f6fb}.seer-chat__message--user[data-v-163bc07c]{justify-self:end;color:#fff;background:#1f5eff}.seer-chat__role[data-v-163bc07c]{font-size:12px;font-weight:700;opacity:.75}.seer-chat__message p[data-v-163bc07c]{margin:0;white-space:pre-wrap}.seer-chat__error[data-v-163bc07c],.seer-chat__handoff[data-v-163bc07c]{margin:0;padding:10px 12px;border-radius:10px}.seer-chat__error[data-v-163bc07c]{color:#8a1f11;background:#fff0ed}.seer-chat__handoff[data-v-163bc07c]{color:#704600;background:#fff7db}.seer-chat__form[data-v-163bc07c]{display:grid;gap:10px}.seer-chat__label[data-v-163bc07c]{font-size:13px;font-weight:700}.seer-chat textarea[data-v-163bc07c]{width:100%;box-sizing:border-box;padding:12px;color:inherit;border:1px solid #c8d3e2;border-radius:12px;resize:vertical}.seer-chat button[data-v-163bc07c]{padding:9px 14px;color:#fff;background:#172033;border:0;border-radius:999px;cursor:pointer}.seer-chat button[data-v-163bc07c]:disabled{cursor:not-allowed;opacity:.5}.seer-chat button[data-v-163bc07c]:focus-visible,.seer-chat textarea[data-v-163bc07c]:focus-visible{outline:3px solid #8fb4ff;outline-offset:2px}.seer-chat__ghost-button[data-v-163bc07c]{color:#172033!important;background:#eef3fa!important}.seer-chat__actions[data-v-163bc07c]{display:flex;gap:8px;justify-content:flex-end}
package/dist/index.js CHANGED
@@ -1,71 +1,88 @@
1
- import { hasInjectionContext as q, inject as A, ref as g, defineComponent as N, computed as T, openBlock as f, createElementBlock as h, createElementVNode as u, toDisplayString as S, unref as d, createCommentVNode as b, Fragment as R, renderList as j, normalizeClass as M, withModifiers as B, withDirectives as F, vModelText as P } from "vue";
1
+ import { hasInjectionContext as T, inject as $, ref as w, defineComponent as A, computed as q, openBlock as v, createElementBlock as p, createElementVNode as u, toDisplayString as E, unref as h, createCommentVNode as k, Fragment as M, renderList as N, normalizeClass as B, withModifiers as D, withDirectives as F, vModelText as P } from "vue";
2
2
  function V(n) {
3
3
  const t = n.fetcher ?? fetch, e = n.model ?? "seer";
4
+ let s, c = 0;
4
5
  return {
5
- async chat(s, i = {}) {
6
- const r = s.stream ?? !1, c = await t(n.endpoint, {
6
+ async chat(r, a = {}) {
7
+ const o = r.stream ?? !1, f = await l(), m = {
8
+ "Content-Type": "application/json"
9
+ };
10
+ f && (m.Authorization = `Bearer ${f}`);
11
+ const d = await t(n.endpoint, {
7
12
  method: "POST",
8
- headers: {
9
- "Content-Type": "application/json"
10
- },
11
- signal: i.signal,
13
+ headers: m,
14
+ signal: a.signal,
12
15
  body: JSON.stringify({
13
- model: s.model ?? e,
14
- messages: s.messages,
15
- stream: r,
16
- conversation_id: s.conversationId,
17
- metadata: s.metadata
16
+ model: r.model ?? e,
17
+ messages: r.messages,
18
+ stream: o,
19
+ conversation_id: r.conversationId,
20
+ metadata: r.metadata
18
21
  })
19
22
  });
20
- if (!c.ok)
21
- throw new Error(await L(c));
22
- if (r)
23
- return D(c, i.onDelta);
24
- const a = await c.json();
25
- return E(a);
23
+ if (!d.ok)
24
+ throw new Error(await j(d));
25
+ if (o)
26
+ return z(d, a.onDelta);
27
+ const y = await d.json();
28
+ return O(y);
26
29
  }
27
30
  };
31
+ async function l() {
32
+ if (!n.sessionEndpoint)
33
+ return;
34
+ const r = Math.floor(Date.now() / 1e3);
35
+ if (s && c - 30 > r)
36
+ return s;
37
+ const a = await t(n.sessionEndpoint, {
38
+ method: "GET"
39
+ });
40
+ if (!a.ok)
41
+ throw new Error(await j(a));
42
+ const o = await a.json();
43
+ return s = o.token, c = o.expires_at, s;
44
+ }
28
45
  }
29
- function E(n) {
30
- var e, s, i;
46
+ function O(n) {
47
+ var e, s, c;
31
48
  const t = (e = n.choices) == null ? void 0 : e[0];
32
49
  return {
33
- content: ((s = t == null ? void 0 : t.message) == null ? void 0 : s.content) ?? ((i = t == null ? void 0 : t.delta) == null ? void 0 : i.content) ?? "",
50
+ content: ((s = t == null ? void 0 : t.message) == null ? void 0 : s.content) ?? ((c = t == null ? void 0 : t.delta) == null ? void 0 : c.content) ?? "",
34
51
  conversationId: n.conversation_id,
35
52
  citations: n.citations ?? [],
36
53
  handoffRequested: n.handoff_requested ?? !1,
37
54
  finishReason: t == null ? void 0 : t.finish_reason
38
55
  };
39
56
  }
40
- async function D(n, t) {
41
- var y, v, C;
57
+ async function z(n, t) {
58
+ var m, d, y;
42
59
  if (!n.body)
43
- return E({});
60
+ return O({});
44
61
  const e = n.body.getReader(), s = new TextDecoder();
45
- let i = "", r = "", c, a = [], o = !1, m;
62
+ let c = "", l = "", r, a = [], o = !1, f;
46
63
  for (; ; ) {
47
- const { done: x, value: O } = await e.read();
64
+ const { done: x, value: I } = await e.read();
48
65
  if (x)
49
66
  break;
50
- i += s.decode(O, { stream: !0 });
51
- const k = i.split(`
67
+ c += s.decode(I, { stream: !0 });
68
+ const S = c.split(`
52
69
 
53
70
  `);
54
- i = k.pop() ?? "";
55
- for (const I of k) {
56
- const w = J(I);
57
- if (!w || w === "[DONE]")
71
+ c = S.pop() ?? "";
72
+ for (const C of S) {
73
+ const b = J(C);
74
+ if (!b || b === "[DONE]")
58
75
  continue;
59
- const p = JSON.parse(w), l = (y = p.choices) == null ? void 0 : y[0], _ = ((v = l == null ? void 0 : l.delta) == null ? void 0 : v.content) ?? ((C = l == null ? void 0 : l.message) == null ? void 0 : C.content) ?? "";
60
- _ && (r += _, t == null || t(_)), c = p.conversation_id ?? c, a = p.citations ?? a, o = p.handoff_requested ?? o, m = (l == null ? void 0 : l.finish_reason) ?? m;
76
+ const _ = JSON.parse(b), i = (m = _.choices) == null ? void 0 : m[0], g = ((d = i == null ? void 0 : i.delta) == null ? void 0 : d.content) ?? ((y = i == null ? void 0 : i.message) == null ? void 0 : y.content) ?? "";
77
+ g && (l += g, t == null || t(g)), r = _.conversation_id ?? r, a = _.citations ?? a, o = _.handoff_requested ?? o, f = (i == null ? void 0 : i.finish_reason) ?? f;
61
78
  }
62
79
  }
63
80
  return {
64
- content: r,
65
- conversationId: c,
81
+ content: l,
82
+ conversationId: r,
66
83
  citations: a,
67
84
  handoffRequested: o,
68
- finishReason: m
85
+ finishReason: f
69
86
  };
70
87
  }
71
88
  function J(n) {
@@ -74,7 +91,7 @@ function J(n) {
74
91
  return t.length === 0 ? null : t.join(`
75
92
  `);
76
93
  }
77
- async function L(n) {
94
+ async function j(n) {
78
95
  let t = n.statusText || "unknown error";
79
96
  try {
80
97
  const e = await n.json();
@@ -85,75 +102,76 @@ async function L(n) {
85
102
  }
86
103
  return `seer chat request failed with status ${n.status}: ${t}`;
87
104
  }
88
- const $ = Symbol("seer-chat-options"), re = {
105
+ const R = Symbol("seer-chat-options"), oe = {
89
106
  install(n, t) {
90
- n.provide($, t);
107
+ n.provide(R, t);
91
108
  }
92
109
  };
93
- function z(n = {}) {
94
- const e = { ...q() ? A($, void 0) : void 0, ...n };
110
+ function L(n = {}) {
111
+ const e = { ...T() ? $(R, void 0) : void 0, ...n };
95
112
  if (!e.endpoint)
96
113
  throw new Error("Seer chat endpoint is required. Pass it to app.use() or useSeerChat().");
97
114
  const s = {
98
115
  endpoint: e.endpoint,
116
+ sessionEndpoint: e.sessionEndpoint,
99
117
  model: e.model,
100
118
  fetcher: e.fetcher
101
- }, i = V(s), r = g([]), c = g(!1), a = g(null), o = g(), m = g([]), y = g(!1);
102
- let v = null;
103
- async function C(k) {
104
- const I = k.trim();
105
- if (!I || c.value)
119
+ }, c = V(s), l = w([]), r = w(!1), a = w(null), o = w(), f = w([]), m = w(!1);
120
+ let d = null;
121
+ async function y(S) {
122
+ const C = S.trim();
123
+ if (!C || r.value)
106
124
  return;
107
- const w = { role: "user", content: I };
108
- r.value = [...r.value, w], c.value = !0, a.value = null, v = new AbortController();
109
- const p = { role: "assistant", content: "" };
110
- e.stream && (r.value = [...r.value, p]);
125
+ const b = { role: "user", content: C };
126
+ l.value = [...l.value, b], r.value = !0, a.value = null, d = new AbortController();
127
+ const _ = { role: "assistant", content: "" };
128
+ e.stream && (l.value = [...l.value, _]);
111
129
  try {
112
- const l = await i.chat(
130
+ const i = await c.chat(
113
131
  {
114
- messages: r.value.filter((_) => _.content),
132
+ messages: l.value.filter((g) => g.content),
115
133
  stream: e.stream ?? !1,
116
134
  conversationId: o.value
117
135
  },
118
136
  {
119
- signal: v.signal,
120
- onDelta(_) {
121
- p.content += _, r.value = [...r.value];
137
+ signal: d.signal,
138
+ onDelta(g) {
139
+ _.content += g, l.value = [...l.value];
122
140
  }
123
141
  }
124
142
  );
125
- e.stream || (r.value = [...r.value, { role: "assistant", content: l.content }]), o.value = l.conversationId, m.value = l.citations, y.value = l.handoffRequested;
126
- } catch (l) {
127
- l.name !== "AbortError" && (a.value = l instanceof Error ? l.message : String(l));
143
+ e.stream || (l.value = [...l.value, { role: "assistant", content: i.content }]), o.value = i.conversationId, f.value = i.citations, m.value = i.handoffRequested;
144
+ } catch (i) {
145
+ i.name !== "AbortError" && (a.value = i instanceof Error ? i.message : String(i));
128
146
  } finally {
129
- c.value = !1, v = null;
147
+ r.value = !1, d = null;
130
148
  }
131
149
  }
132
150
  function x() {
133
- r.value = [], a.value = null, o.value = void 0, m.value = [], y.value = !1;
151
+ l.value = [], a.value = null, o.value = void 0, f.value = [], m.value = !1;
134
152
  }
135
- function O() {
136
- v == null || v.abort();
153
+ function I() {
154
+ d == null || d.abort();
137
155
  }
138
156
  return {
139
- messages: r,
140
- loading: c,
157
+ messages: l,
158
+ loading: r,
141
159
  error: a,
142
160
  conversationId: o,
143
- citations: m,
144
- handoffRequested: y,
145
- send: C,
161
+ citations: f,
162
+ handoffRequested: m,
163
+ send: y,
146
164
  reset: x,
147
- abort: O
165
+ abort: I
148
166
  };
149
167
  }
150
- const K = {
168
+ const G = {
151
169
  class: "seer-chat",
152
170
  "aria-label": "多发货 AI 客服聊天"
153
- }, U = { class: "seer-chat__header" }, W = {
171
+ }, K = { class: "seer-chat__header" }, U = {
154
172
  class: "seer-chat__messages",
155
173
  "aria-live": "polite"
156
- }, G = {
174
+ }, W = {
157
175
  key: 0,
158
176
  class: "seer-chat__empty"
159
177
  }, H = { class: "seer-chat__role" }, Q = {
@@ -166,10 +184,7 @@ const K = {
166
184
  }, Y = {
167
185
  key: 2,
168
186
  class: "seer-chat__handoff"
169
- }, Z = {
170
- key: 3,
171
- class: "seer-chat__citations"
172
- }, ee = ["placeholder", "disabled"], te = { class: "seer-chat__actions" }, ne = ["disabled"], se = /* @__PURE__ */ N({
187
+ }, Z = ["placeholder", "disabled"], ee = { class: "seer-chat__actions" }, te = ["disabled"], ne = /* @__PURE__ */ A({
173
188
  __name: "SeerChat",
174
189
  props: {
175
190
  endpoint: {},
@@ -180,54 +195,48 @@ const K = {
180
195
  placeholder: { default: "输入你的问题,例如:美国海派怎么报价?" }
181
196
  },
182
197
  setup(n) {
183
- const t = n, e = g(""), s = z({
198
+ const t = n, e = w(""), s = L({
184
199
  endpoint: t.endpoint,
185
200
  model: t.model,
186
201
  stream: t.stream,
187
202
  fetcher: t.fetcher
188
- }), i = T(() => e.value.trim().length > 0 && !s.loading.value);
189
- async function r() {
190
- if (!i.value)
203
+ }), c = q(() => e.value.trim().length > 0 && !s.loading.value);
204
+ async function l() {
205
+ if (!c.value)
191
206
  return;
192
- const c = e.value;
193
- e.value = "", await s.send(c);
207
+ const r = e.value;
208
+ e.value = "", await s.send(r);
194
209
  }
195
- return (c, a) => (f(), h("section", K, [
196
- u("header", U, [
210
+ return (r, a) => (v(), p("section", G, [
211
+ u("header", K, [
197
212
  u("div", null, [
198
- u("h2", null, S(n.title), 1),
213
+ u("h2", null, E(n.title), 1),
199
214
  a[3] || (a[3] = u("p", null, "可咨询渠道报价、时效、下单资料、入仓和售后问题。", -1))
200
215
  ]),
201
216
  u("button", {
202
217
  type: "button",
203
218
  class: "seer-chat__ghost-button",
204
- onClick: a[0] || (a[0] = (o) => d(s).reset())
219
+ onClick: a[0] || (a[0] = (o) => h(s).reset())
205
220
  }, "清空")
206
221
  ]),
207
- u("div", W, [
208
- d(s).messages.value.length === 0 ? (f(), h("p", G, " 直接输入问题即可,复杂查单或赔付问题会提示转人工。 ")) : b("", !0),
209
- (f(!0), h(R, null, j(d(s).messages.value, (o, m) => (f(), h("article", {
210
- key: `${o.role}-${m}`,
211
- class: M(["seer-chat__message", `seer-chat__message--${o.role}`])
222
+ u("div", U, [
223
+ h(s).messages.value.length === 0 ? (v(), p("p", W, " 直接输入问题即可,复杂查单或赔付问题会提示转人工。 ")) : k("", !0),
224
+ (v(!0), p(M, null, N(h(s).messages.value, (o, f) => (v(), p("article", {
225
+ key: `${o.role}-${f}`,
226
+ class: B(["seer-chat__message", `seer-chat__message--${o.role}`])
212
227
  }, [
213
- u("span", H, S(o.role === "user" ? "你" : "AI 客服"), 1),
214
- u("p", null, S(o.content), 1)
228
+ u("span", H, E(o.role === "user" ? "你" : "AI 客服"), 1),
229
+ u("p", null, E(o.content), 1)
215
230
  ], 2))), 128))
216
231
  ]),
217
- d(s).loading.value ? (f(), h("p", Q, "正在回复...")) : b("", !0),
218
- d(s).error.value ? (f(), h("p", X, S(d(s).error.value), 1)) : b("", !0),
219
- d(s).handoffRequested.value ? (f(), h("p", Y, " 建议转人工:请留下联系方式或等待客服接入。 ")) : b("", !0),
220
- d(s).citations.value.length > 0 ? (f(), h("footer", Z, [
221
- a[4] || (a[4] = u("span", null, "参考资料", -1)),
222
- u("ul", null, [
223
- (f(!0), h(R, null, j(d(s).citations.value, (o) => (f(), h("li", { key: o }, S(o), 1))), 128))
224
- ])
225
- ])) : b("", !0),
232
+ h(s).loading.value ? (v(), p("p", Q, "正在回复...")) : k("", !0),
233
+ h(s).error.value ? (v(), p("p", X, E(h(s).error.value), 1)) : k("", !0),
234
+ h(s).handoffRequested.value ? (v(), p("p", Y, " 建议转人工:请留下联系方式或等待客服接入。 ")) : k("", !0),
226
235
  u("form", {
227
236
  class: "seer-chat__form",
228
- onSubmit: B(r, ["prevent"])
237
+ onSubmit: D(l, ["prevent"])
229
238
  }, [
230
- a[5] || (a[5] = u("label", {
239
+ a[4] || (a[4] = u("label", {
231
240
  class: "seer-chat__label",
232
241
  for: "seer-chat-input"
233
242
  }, "问题", -1)),
@@ -235,35 +244,35 @@ const K = {
235
244
  id: "seer-chat-input",
236
245
  "onUpdate:modelValue": a[1] || (a[1] = (o) => e.value = o),
237
246
  placeholder: n.placeholder,
238
- disabled: d(s).loading.value,
247
+ disabled: h(s).loading.value,
239
248
  rows: "3"
240
- }, null, 8, ee), [
249
+ }, null, 8, Z), [
241
250
  [P, e.value]
242
251
  ]),
243
- u("div", te, [
244
- d(s).loading.value ? (f(), h("button", {
252
+ u("div", ee, [
253
+ h(s).loading.value ? (v(), p("button", {
245
254
  key: 0,
246
255
  type: "button",
247
256
  class: "seer-chat__ghost-button",
248
- onClick: a[2] || (a[2] = (o) => d(s).abort())
249
- }, " 停止 ")) : b("", !0),
257
+ onClick: a[2] || (a[2] = (o) => h(s).abort())
258
+ }, " 停止 ")) : k("", !0),
250
259
  u("button", {
251
260
  type: "submit",
252
- disabled: !i.value
253
- }, "发送", 8, ne)
261
+ disabled: !c.value
262
+ }, "发送", 8, te)
254
263
  ])
255
264
  ], 32)
256
265
  ]));
257
266
  }
258
- }), ae = (n, t) => {
267
+ }), se = (n, t) => {
259
268
  const e = n.__vccOpts || n;
260
- for (const [s, i] of t)
261
- e[s] = i;
269
+ for (const [s, c] of t)
270
+ e[s] = c;
262
271
  return e;
263
- }, le = /* @__PURE__ */ ae(se, [["__scopeId", "data-v-51cf8455"]]);
272
+ }, re = /* @__PURE__ */ se(ne, [["__scopeId", "data-v-163bc07c"]]);
264
273
  export {
265
- le as SeerChat,
266
- re as SeerChatPlugin,
274
+ re as SeerChat,
275
+ oe as SeerChatPlugin,
267
276
  V as createSeerClient,
268
- z as useSeerChat
277
+ L as useSeerChat
269
278
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seer-ai/seer-vue",
3
- "version": "0.1.0-beta.1",
3
+ "version": "0.1.0-beta.3",
4
4
  "description": "Vue chat plugin for the seer OpenAI-compatible API",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",