llmgw's own extension for VS Code, plain JavaScript with no dependencies, packed into a .vsix by the binary at /setup/vscode/llmgw.vsix with the serving gateway's address in it, so it needs no build, no Node and no marketplace. It registers llmgw as a language-model provider: the chat's model picker, agent mode and any extension that asks VS Code for a model get every chat model the key may use, sized and with tool and vision capabilities as the catalog says, streamed, tool calls both ways. VS Code is a tool on the account page's Connect your editor: a key, the install command, and an Open in VS Code link that hands the key to the extension's secret storage. The extension's Sign In opens the page with a state of its own; a link without it is confirmed, the gateway named, since it decides where code is sent. Tested in VS Code 1.138 against the PoC: 27 models listed, an answer from qwen2.5-0.5b-instruct, and qwen3-coder-30b calling a tool and using its result (internal/vscodeext/e2e). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
61 lines
1.5 KiB
Go
61 lines
1.5 KiB
Go
// Command pack writes the VS Code extension as a gateway would serve it: the
|
|
// .vsix, and the same files unpacked, for VS Code's --extensionDevelopmentPath.
|
|
package main
|
|
|
|
import (
|
|
"archive/zip"
|
|
"bytes"
|
|
"flag"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"gitea.mynodes.xyz/ilker/llmgw/internal/vscodeext"
|
|
)
|
|
|
|
func main() {
|
|
out := flag.String("out", "llmgw-vsix", "directory to write llmgw.vsix and its unpacked files into")
|
|
version := flag.String("version", "v0.0.1", "llmgw version the extension says it is")
|
|
gateway := flag.String("gateway", "", "gateway address the extension's setting starts at")
|
|
flag.Parse()
|
|
|
|
b, err := vscodeext.Build(*version, *gateway)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := os.MkdirAll(*out, 0o755); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(*out, vscodeext.FileName), b, 0o644); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
zr, err := zip.NewReader(bytes.NewReader(b), int64(len(b)))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
for _, f := range zr.File {
|
|
if strings.Contains(f.Name, "..") {
|
|
log.Fatalf("unsafe name %q", f.Name)
|
|
}
|
|
dst := filepath.Join(*out, filepath.FromSlash(f.Name))
|
|
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
rc, err := f.Open()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
var body bytes.Buffer
|
|
_, err = body.ReadFrom(rc)
|
|
rc.Close()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(dst, body.Bytes(), 0o644); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
}
|
|
log.Printf("wrote %s and its files under %s", vscodeext.FileName, *out)
|
|
}
|