Files
ac61e5ed32 fix(runtimes): rename to Qoder Cloud Agents and use Qoder's current logo (MUL-8153) (#9418)
* fix(runtimes): rename Qoder Cloud Agent to Qoder Cloud Agents

Qoder's product is called Qoder Cloud Agents, matching how Cursor Cloud
Agents is already spelled in the runtime lists. Update the display name,
default runtime name, error messages, translations, landing copy, docs
and tests.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

* fix(runtimes): use Qoder's current logo

Replace the old green Qoder mark with the current monochrome mark from
qoder.com/favIcon.svg. The background is removed, the paths are
minified, and both paths use currentColor so the mark stays visible in
dark mode. It applies to the Qoder, Qoder CN and Qoder Cloud Agents
runtimes.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

---------

Co-authored-by: 安辰 <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
2026-10-22 13:57:20 +08:00

83 lines
2.5 KiB
Go

// qoder-runtime runs a server-hosted Multica to Qoder Cloud Agents bridge.
package main
import (
"bytes"
"context"
"encoding/json"
"flag"
"fmt"
"io"
"log/slog"
"os"
"os/signal"
"syscall"
"github.com/multica-ai/multica/server/internal/qoderruntime"
)
func main() {
log := slog.New(slog.NewJSONHandler(os.Stderr, nil))
configPath := flag.String("config", "", "JSON configuration file; supplied fields override environment variables")
checkOnly := flag.Bool("check", false, "Verify API access without registering a runtime or running tasks")
flag.Parse()
cfg, err := loadConfig(*configPath, os.Getenv)
if err != nil {
log.Error("read bridge configuration", "error", err)
os.Exit(1)
}
b, err := qoderruntime.New(cfg, log)
if err != nil {
log.Error("invalid bridge configuration", "error", err)
os.Exit(1)
}
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel()
if *checkOnly {
if err := b.Check(ctx); err != nil {
log.ErrorContext(ctx, "bridge check failed", "error", err)
os.Exit(1)
}
log.InfoContext(ctx, "bridge API access verified")
return
}
if err := b.Run(ctx); err != nil {
log.ErrorContext(ctx, "Qoder bridge stopped", "error", err)
os.Exit(1)
}
}
func loadConfig(path string, getenv func(string) string) (qoderruntime.Config, error) {
cfg := qoderruntime.Config{
MulticaURL: getenv("MULTICA_SERVER_URL"), MulticaToken: getenv("MULTICA_TOKEN"), WorkspaceID: getenv("MULTICA_WORKSPACE_ID"),
QoderURL: getenv("QODER_CLOUD_BASE_URL"), QoderToken: getenv("QODER_CLOUD_TOKEN"), EnvironmentID: getenv("QODER_CLOUD_ENVIRONMENT_ID"),
StateDir: getenv("QODER_BRIDGE_STATE_DIR"), Name: getenv("QODER_BRIDGE_NAME"),
}
if path == "" {
return cfg, nil
}
f, err := os.Open(path)
if err != nil {
return cfg, fmt.Errorf("cannot open configuration file")
}
defer f.Close()
raw, err := io.ReadAll(io.LimitReader(f, (1<<20)+1))
if err != nil || len(raw) > 1<<20 {
return cfg, fmt.Errorf("cannot read configuration or file exceeds 1 MiB")
}
raw = bytes.TrimSpace(raw)
if len(raw) == 0 || raw[0] != '{' {
return cfg, fmt.Errorf("configuration must be a JSON object")
}
decoder := json.NewDecoder(bytes.NewReader(raw))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&cfg); err != nil {
return cfg, fmt.Errorf("invalid configuration JSON or unknown field")
}
var extra any
if err := decoder.Decode(&extra); err != io.EOF {
return cfg, fmt.Errorf("configuration must contain one JSON object")
}
return cfg, nil
}