@magpie-community/opencode-trae-auth 0.1.10 → 0.1.11
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/index.mjs +119 -7
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -534,25 +534,79 @@ const callId = () => "call_" + randomBytes(12).toString("hex") + (callSeq++).toS
|
|
|
534
534
|
|
|
535
535
|
// TextTools splits the model's text into what the agent sees and the tool
|
|
536
536
|
// calls it wrote as blocks, holding back the start of a block until it
|
|
537
|
-
// is whole
|
|
537
|
+
// is whole. A block is ours (<tool_call>{…}</tool_call>), its JSON taken
|
|
538
|
+
// once whole even when DeepSeek closes it with its own tags in place of
|
|
539
|
+
// </tool_call>, or DeepSeek's (<|DSML|invoke name="…">, magpie#823); the
|
|
540
|
+
// tags left around a call are dropped.
|
|
538
541
|
class TextTools {
|
|
539
542
|
constructor() {
|
|
540
543
|
this.buf = ""
|
|
544
|
+
this.after = false
|
|
541
545
|
}
|
|
542
546
|
push(s, end = false) {
|
|
543
547
|
this.buf += s
|
|
544
548
|
let text = ""
|
|
545
549
|
const calls = []
|
|
546
550
|
for (;;) {
|
|
551
|
+
if (this.after) {
|
|
552
|
+
const m = this.buf.match(LEFTOVER)
|
|
553
|
+
if (m) this.buf = this.buf.slice(m[0].length)
|
|
554
|
+
if (!this.buf || (!end && MARKS.some((k) => k.startsWith(this.buf)))) break
|
|
555
|
+
this.after = false
|
|
556
|
+
}
|
|
547
557
|
const i = this.buf.indexOf(OPEN)
|
|
548
|
-
|
|
558
|
+
const d = this.buf.search(DSML_AT)
|
|
559
|
+
if (i < 0 && d < 0) break
|
|
560
|
+
if (d >= 0 && (i < 0 || d < i)) {
|
|
561
|
+
text += this.buf.slice(0, d)
|
|
562
|
+
this.buf = this.buf.slice(d)
|
|
563
|
+
const tag = this.buf.match(DSML_TAG)
|
|
564
|
+
if (!tag) {
|
|
565
|
+
// a tag not closed yet; at the end, or with another tag after it,
|
|
566
|
+
// a stray one dropped
|
|
567
|
+
const next = this.buf.indexOf("<", 1)
|
|
568
|
+
if (next < 0 && !end) break
|
|
569
|
+
this.buf = next < 0 ? "" : this.buf.slice(next)
|
|
570
|
+
continue
|
|
571
|
+
}
|
|
572
|
+
const inv = tag[0].match(DSML_INVOKE)
|
|
573
|
+
if (!inv) {
|
|
574
|
+
// function_calls, a parameter's close, any other tag: dropped,
|
|
575
|
+
// with the blanks after it
|
|
576
|
+
this.buf = this.buf.slice(tag[0].length)
|
|
577
|
+
this.after = true
|
|
578
|
+
continue
|
|
579
|
+
}
|
|
580
|
+
const body = this.buf.slice(tag[0].length)
|
|
581
|
+
const c = body.match(DSML_CLOSE)
|
|
582
|
+
const n = body.search(DSML_INVOKE_AT)
|
|
583
|
+
let stop = c ? c.index : n >= 0 ? n : -1
|
|
584
|
+
if (n >= 0 && n < stop) stop = n
|
|
585
|
+
if (stop < 0) {
|
|
586
|
+
if (!end) break
|
|
587
|
+
stop = body.length
|
|
588
|
+
}
|
|
589
|
+
calls.push({ name: inv[1], arguments: dsmlArgs(body.slice(0, stop)) })
|
|
590
|
+
this.buf = body.slice(c && c.index === stop ? stop + c[0].length : stop)
|
|
591
|
+
this.after = true
|
|
592
|
+
continue
|
|
593
|
+
}
|
|
594
|
+
const o = objectEnd(this.buf, i + OPEN.length)
|
|
595
|
+
const v = o > 0 ? looseJSON(this.buf.slice(i + OPEN.length, o)) : null
|
|
596
|
+
if (v?.name) {
|
|
597
|
+
text += this.buf.slice(0, i)
|
|
598
|
+
calls.push({ name: String(v.name), arguments: callArgs(v) })
|
|
599
|
+
this.buf = this.buf.slice(o)
|
|
600
|
+
this.after = true
|
|
601
|
+
continue
|
|
602
|
+
}
|
|
549
603
|
const j = this.buf.indexOf(CLOSE, i + OPEN.length)
|
|
550
604
|
if (j < 0) break
|
|
551
605
|
text += this.buf.slice(0, i)
|
|
552
606
|
const raw = this.buf.slice(i + OPEN.length, j).trim()
|
|
553
607
|
this.buf = this.buf.slice(j + CLOSE.length)
|
|
554
|
-
const
|
|
555
|
-
if (
|
|
608
|
+
const w = looseJSON(raw) ?? {}
|
|
609
|
+
if (w.name) calls.push({ name: String(w.name), arguments: callArgs(w) })
|
|
556
610
|
else text += OPEN + raw + CLOSE
|
|
557
611
|
}
|
|
558
612
|
if (end) {
|
|
@@ -560,9 +614,9 @@ class TextTools {
|
|
|
560
614
|
this.buf = ""
|
|
561
615
|
} else {
|
|
562
616
|
// keep back an opened block, or what may be the start of one
|
|
563
|
-
const
|
|
564
|
-
let keep =
|
|
565
|
-
if (!keep) for (let k = Math.min(
|
|
617
|
+
const at = [this.buf.indexOf(OPEN), this.buf.search(DSML_AT)].filter((x) => x >= 0)
|
|
618
|
+
let keep = at.length ? this.buf.length - Math.min(...at) : 0
|
|
619
|
+
if (!keep) for (let k = Math.min(MARK_MAX - 1, this.buf.length); k > 0; k--) if (MARKS.some((m) => m.startsWith(this.buf.slice(-k)))) { keep = k; break }
|
|
566
620
|
text += this.buf.slice(0, this.buf.length - keep)
|
|
567
621
|
this.buf = this.buf.slice(this.buf.length - keep)
|
|
568
622
|
}
|
|
@@ -570,6 +624,64 @@ class TextTools {
|
|
|
570
624
|
}
|
|
571
625
|
}
|
|
572
626
|
|
|
627
|
+
// DeepSeek's tags, with its full-width bar or a plain one
|
|
628
|
+
const DSML = String.raw`<\s*\/?\s*[||][\s||]*DSML[\s||]*`
|
|
629
|
+
const DSML_AT = new RegExp(DSML)
|
|
630
|
+
const DSML_TAG = new RegExp("^" + DSML + "[^<>]*>")
|
|
631
|
+
const DSML_INVOKE = new RegExp(String.raw`^<\s*[||][\s||]*DSML[\s||]*invoke\s+name\s*=\s*"([^"]+)"\s*>`)
|
|
632
|
+
const DSML_INVOKE_AT = new RegExp(String.raw`<\s*[||][\s||]*DSML[\s||]*invoke\s+name\s*=`)
|
|
633
|
+
const DSML_CLOSE = new RegExp(String.raw`<\s*\/\s*[||][\s||]*DSML[\s||]*invoke\s*>`)
|
|
634
|
+
const DSML_PARAM = new RegExp(String.raw`<\s*[||][\s||]*DSML[\s||]*parameter\s+name\s*=\s*"([^"]+)"((?:\s+\w+\s*=\s*"[^"]*")*)\s*>([\s\S]*?)` + DSML + String.raw`parameter\s*>`, "g")
|
|
635
|
+
// what is left after a call: blanks, our close, DeepSeek's tags but the
|
|
636
|
+
// next call's
|
|
637
|
+
const LEFTOVER = new RegExp(String.raw`^(?:\s+|<\/tool_calls?>|<tool_calls>|` + DSML + String.raw`(?![\s||]*invoke\b)[^<>]*>)+`)
|
|
638
|
+
// what text may be the start of, so held back
|
|
639
|
+
const MARKS = [OPEN, CLOSE, "<|DSML|", "<|DSML|", "</|DSML|", "</|DSML|"]
|
|
640
|
+
const MARK_MAX = Math.max(...MARKS.map((m) => m.length))
|
|
641
|
+
|
|
642
|
+
// dsmlArgs is the arguments of a DSML invoke as JSON: a parameter marked
|
|
643
|
+
// string="true" is its text, any other its JSON (its text when not JSON)
|
|
644
|
+
function dsmlArgs(body) {
|
|
645
|
+
const args = {}
|
|
646
|
+
for (const p of body.matchAll(DSML_PARAM)) {
|
|
647
|
+
if (/string\s*=\s*"true"/.test(p[2])) {
|
|
648
|
+
args[p[1]] = p[3]
|
|
649
|
+
continue
|
|
650
|
+
}
|
|
651
|
+
try {
|
|
652
|
+
args[p[1]] = JSON.parse(p[3].trim())
|
|
653
|
+
} catch {
|
|
654
|
+
args[p[1]] = p[3]
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return JSON.stringify(args)
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// objectEnd is where the JSON object starting at from (after blanks) ends:
|
|
661
|
+
// past its closing brace, -1 when it isn't whole yet, -2 when there's none
|
|
662
|
+
function objectEnd(s, from) {
|
|
663
|
+
let k = from
|
|
664
|
+
while (k < s.length && /\s/.test(s[k])) k++
|
|
665
|
+
if (k >= s.length) return -1
|
|
666
|
+
if (s[k] !== "{") return -2
|
|
667
|
+
let depth = 0
|
|
668
|
+
let str = false
|
|
669
|
+
let esc = false
|
|
670
|
+
for (; k < s.length; k++) {
|
|
671
|
+
const ch = s[k]
|
|
672
|
+
if (str) {
|
|
673
|
+
if (esc) esc = false
|
|
674
|
+
else if (ch === "\\") esc = true
|
|
675
|
+
else if (ch === '"') str = false
|
|
676
|
+
continue
|
|
677
|
+
}
|
|
678
|
+
if (ch === '"') str = true
|
|
679
|
+
else if (ch === "{") depth++
|
|
680
|
+
else if (ch === "}" && --depth === 0) return k + 1
|
|
681
|
+
}
|
|
682
|
+
return -1
|
|
683
|
+
}
|
|
684
|
+
|
|
573
685
|
// looseJSON reads a JSON object as a model writes one: a string holding a
|
|
574
686
|
// raw newline or tab (a file's content written out, #799) is read as if
|
|
575
687
|
// they were escaped. null when it isn't one even so.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magpie-community/opencode-trae-auth",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "OpenCode provider plugin for Trae CN (trae.cn): your Trae CN account's models, free tier included, with Trae's own browser sign-in",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.mjs",
|