@fancyboi999/open-tag-daemon 0.2.0 → 0.3.0
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/cli.mjs +188 -1
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -5669,6 +5669,190 @@ async function readWorkspaceFile(agentId, rel) {
|
|
|
5669
5669
|
}
|
|
5670
5670
|
}
|
|
5671
5671
|
|
|
5672
|
+
// src/daemon/listModels.ts
|
|
5673
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
5674
|
+
var titleCase = (s) => s ? s[0].toUpperCase() + s.slice(1) : s;
|
|
5675
|
+
function isModelId(s) {
|
|
5676
|
+
return /^[A-Za-z][A-Za-z0-9\-_./]*$/.test(s);
|
|
5677
|
+
}
|
|
5678
|
+
var CLAUDE_MODELS = [
|
|
5679
|
+
{ id: "sonnet", label: "Sonnet" },
|
|
5680
|
+
{ id: "opus", label: "Opus" },
|
|
5681
|
+
{ id: "haiku", label: "Haiku" }
|
|
5682
|
+
];
|
|
5683
|
+
var CLAUDE_EFFORT_LABEL = {
|
|
5684
|
+
low: "Low",
|
|
5685
|
+
medium: "Medium",
|
|
5686
|
+
high: "High",
|
|
5687
|
+
xhigh: "Extra high",
|
|
5688
|
+
max: "Max"
|
|
5689
|
+
};
|
|
5690
|
+
var CLAUDE_MODEL_EFFORT_ALLOW = {
|
|
5691
|
+
opus: /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh", "max"]),
|
|
5692
|
+
sonnet: /* @__PURE__ */ new Set(["low", "medium", "high", "max"]),
|
|
5693
|
+
haiku: /* @__PURE__ */ new Set(["low", "medium", "high"])
|
|
5694
|
+
};
|
|
5695
|
+
function parseClaudeEffortLevels(helpText) {
|
|
5696
|
+
const m = /--effort\s*(?:<[^>]+>)?\s*(?:Effort level[^(]*)?\(([^)]+)\)/.exec(helpText);
|
|
5697
|
+
if (!m) return [];
|
|
5698
|
+
return m[1].split(",").map((s) => s.trim()).filter((s) => /^[a-z]+$/i.test(s));
|
|
5699
|
+
}
|
|
5700
|
+
function claudeThinkingForModel(modelId, superset) {
|
|
5701
|
+
const allow = CLAUDE_MODEL_EFFORT_ALLOW[modelId];
|
|
5702
|
+
const levels = superset.filter((v) => !allow || allow.has(v)).map((v) => ({ value: v, label: CLAUDE_EFFORT_LABEL[v] ?? titleCase(v) }));
|
|
5703
|
+
if (!levels.length) return void 0;
|
|
5704
|
+
return { levels, default: levels.some((l) => l.value === "medium") ? "medium" : levels[0].value };
|
|
5705
|
+
}
|
|
5706
|
+
function parseCodexModels(jsonStr) {
|
|
5707
|
+
let parsed;
|
|
5708
|
+
try {
|
|
5709
|
+
parsed = JSON.parse(jsonStr);
|
|
5710
|
+
} catch {
|
|
5711
|
+
return [];
|
|
5712
|
+
}
|
|
5713
|
+
const models = Array.isArray(parsed?.models) ? parsed.models : [];
|
|
5714
|
+
const out = [];
|
|
5715
|
+
for (const m of models) {
|
|
5716
|
+
if (m?.visibility !== "list") continue;
|
|
5717
|
+
const slug = typeof m?.slug === "string" ? m.slug : "";
|
|
5718
|
+
if (!slug) continue;
|
|
5719
|
+
const raw = Array.isArray(m?.supported_reasoning_levels) ? m.supported_reasoning_levels : [];
|
|
5720
|
+
const levels = raw.map((l) => ({ value: String(l?.effort ?? ""), label: titleCase(String(l?.effort ?? "")), description: typeof l?.description === "string" ? l.description : void 0 })).filter((l) => l.value);
|
|
5721
|
+
const thinking = levels.length ? { levels, default: typeof m?.default_reasoning_level === "string" ? m.default_reasoning_level : void 0 } : void 0;
|
|
5722
|
+
out.push({ id: slug, label: typeof m?.display_name === "string" && m.display_name ? m.display_name : slug, provider: "openai", ...thinking ? { thinking } : {} });
|
|
5723
|
+
}
|
|
5724
|
+
return out;
|
|
5725
|
+
}
|
|
5726
|
+
function parseOpencodeModels(stdout) {
|
|
5727
|
+
const out = [];
|
|
5728
|
+
for (const raw of stdout.split("\n")) {
|
|
5729
|
+
const line = raw.trim();
|
|
5730
|
+
if (!line) continue;
|
|
5731
|
+
if (line.startsWith("{") || line.startsWith('"') || line.startsWith("}")) continue;
|
|
5732
|
+
if (line === line.toUpperCase() && /[A-Z]/.test(line)) continue;
|
|
5733
|
+
const id = line.split(/\s+/)[0];
|
|
5734
|
+
const slash = id.indexOf("/");
|
|
5735
|
+
if (slash <= 0 || slash >= id.length - 1) continue;
|
|
5736
|
+
out.push({ id, label: id, provider: id.slice(0, slash) });
|
|
5737
|
+
}
|
|
5738
|
+
return out;
|
|
5739
|
+
}
|
|
5740
|
+
function parseCursorModels(stdout) {
|
|
5741
|
+
const out = [];
|
|
5742
|
+
for (const raw of stdout.split("\n")) {
|
|
5743
|
+
const line = raw.trim();
|
|
5744
|
+
if (!line) continue;
|
|
5745
|
+
const sep = line.indexOf(" - ");
|
|
5746
|
+
if (sep < 0) continue;
|
|
5747
|
+
const id = line.slice(0, sep).trim();
|
|
5748
|
+
if (!isModelId(id)) continue;
|
|
5749
|
+
let label = line.slice(sep + 3).trim();
|
|
5750
|
+
const isDefault = /default/i.test(label);
|
|
5751
|
+
const paren = label.indexOf("(");
|
|
5752
|
+
if (paren >= 0) label = label.slice(0, paren).trim();
|
|
5753
|
+
out.push({ id, label: label || id, provider: "cursor", ...isDefault ? { default: true } : {} });
|
|
5754
|
+
}
|
|
5755
|
+
return out;
|
|
5756
|
+
}
|
|
5757
|
+
function parsePiModels(out) {
|
|
5758
|
+
const res = [];
|
|
5759
|
+
for (const raw of out.split("\n")) {
|
|
5760
|
+
const line = raw.trim();
|
|
5761
|
+
if (!line) continue;
|
|
5762
|
+
if (isPiNoise(line)) continue;
|
|
5763
|
+
const fields = line.split(/\s+/);
|
|
5764
|
+
const first = fields[0];
|
|
5765
|
+
if (first.toLowerCase() === "provider") continue;
|
|
5766
|
+
let id;
|
|
5767
|
+
if (first.includes(":") || first.includes("/")) id = first.replace(":", "/");
|
|
5768
|
+
else if (fields.length >= 2) id = `${first}/${fields[1]}`;
|
|
5769
|
+
else continue;
|
|
5770
|
+
const slash = id.indexOf("/");
|
|
5771
|
+
if (slash <= 0 || slash >= id.length - 1) continue;
|
|
5772
|
+
res.push({ id, label: id, provider: id.slice(0, slash) });
|
|
5773
|
+
}
|
|
5774
|
+
return res;
|
|
5775
|
+
}
|
|
5776
|
+
function isPiNoise(line) {
|
|
5777
|
+
const l = line.toLowerCase();
|
|
5778
|
+
return l.includes("no models match pattern") || l.startsWith("warning:") || l.startsWith("error:") || l.startsWith("info:");
|
|
5779
|
+
}
|
|
5780
|
+
var LIST_TIMEOUT_MS = 7e3;
|
|
5781
|
+
var OUT_CAP = 256 * 1024;
|
|
5782
|
+
function runList(bin, args2, timeoutMs = LIST_TIMEOUT_MS) {
|
|
5783
|
+
return new Promise((resolve) => {
|
|
5784
|
+
const env = { ...process.env };
|
|
5785
|
+
delete env.NODE_OPTIONS;
|
|
5786
|
+
let proc;
|
|
5787
|
+
try {
|
|
5788
|
+
proc = spawn8(bin, args2, { stdio: ["ignore", "pipe", "pipe"], env });
|
|
5789
|
+
} catch (e) {
|
|
5790
|
+
return resolve({ stdout: "", stderr: String(e?.message ?? e), code: 1 });
|
|
5791
|
+
}
|
|
5792
|
+
let stdout = "";
|
|
5793
|
+
let stderr = "";
|
|
5794
|
+
proc.stdout?.on("data", (c) => {
|
|
5795
|
+
if (stdout.length < OUT_CAP) stdout += c.toString();
|
|
5796
|
+
});
|
|
5797
|
+
proc.stderr?.on("data", (c) => {
|
|
5798
|
+
if (stderr.length < OUT_CAP) stderr += c.toString();
|
|
5799
|
+
});
|
|
5800
|
+
const timer = setTimeout(() => {
|
|
5801
|
+
try {
|
|
5802
|
+
proc.kill("SIGKILL");
|
|
5803
|
+
} catch {
|
|
5804
|
+
}
|
|
5805
|
+
}, timeoutMs);
|
|
5806
|
+
proc.on("error", (e) => {
|
|
5807
|
+
clearTimeout(timer);
|
|
5808
|
+
resolve({ stdout, stderr: stderr || String(e?.message ?? e), code: 1 });
|
|
5809
|
+
});
|
|
5810
|
+
proc.on("exit", (code) => {
|
|
5811
|
+
clearTimeout(timer);
|
|
5812
|
+
resolve({ stdout, stderr, code });
|
|
5813
|
+
});
|
|
5814
|
+
});
|
|
5815
|
+
}
|
|
5816
|
+
async function listModels(runtime) {
|
|
5817
|
+
switch (runtime) {
|
|
5818
|
+
case "opencode": {
|
|
5819
|
+
let r = await runList("opencode", ["models", "--verbose"], 5e3);
|
|
5820
|
+
let models = parseOpencodeModels(r.stdout);
|
|
5821
|
+
if (!models.length) {
|
|
5822
|
+
r = await runList("opencode", ["models"], 2e3);
|
|
5823
|
+
models = parseOpencodeModels(r.stdout);
|
|
5824
|
+
}
|
|
5825
|
+
return models.length ? models : null;
|
|
5826
|
+
}
|
|
5827
|
+
case "cursor": {
|
|
5828
|
+
const r = await runList("cursor-agent", ["--list-models"]);
|
|
5829
|
+
const models = parseCursorModels(r.stdout);
|
|
5830
|
+
return models.length ? models : null;
|
|
5831
|
+
}
|
|
5832
|
+
case "pi": {
|
|
5833
|
+
const r = await runList("pi", ["--list-models"]);
|
|
5834
|
+
const models = parsePiModels(r.stdout || r.stderr);
|
|
5835
|
+
return models.length ? models : null;
|
|
5836
|
+
}
|
|
5837
|
+
case "claude": {
|
|
5838
|
+
const r = await runList("claude", ["--help"]);
|
|
5839
|
+
const superset = parseClaudeEffortLevels(r.stdout || r.stderr);
|
|
5840
|
+
if (!superset.length) return null;
|
|
5841
|
+
return CLAUDE_MODELS.map((m) => {
|
|
5842
|
+
const thinking = claudeThinkingForModel(m.id, superset);
|
|
5843
|
+
return { ...m, provider: "anthropic", ...thinking ? { thinking } : {} };
|
|
5844
|
+
});
|
|
5845
|
+
}
|
|
5846
|
+
case "codex": {
|
|
5847
|
+
const r = await runList("codex", ["debug", "models"]);
|
|
5848
|
+
const models = parseCodexModels(r.stdout);
|
|
5849
|
+
return models.length ? models : null;
|
|
5850
|
+
}
|
|
5851
|
+
default:
|
|
5852
|
+
return null;
|
|
5853
|
+
}
|
|
5854
|
+
}
|
|
5855
|
+
|
|
5672
5856
|
// src/daemon/index.ts
|
|
5673
5857
|
var log = createLogger("daemon");
|
|
5674
5858
|
var args = process.argv.slice(2);
|
|
@@ -5734,6 +5918,9 @@ conn = new Connection(serverUrl, apiKey, (msg) => {
|
|
|
5734
5918
|
case "agent:skills:list":
|
|
5735
5919
|
void listSkills(msg.agentId).then((r) => conn.send({ type: "skills:list", requestId: msg.requestId, agentId: msg.agentId, ...r }));
|
|
5736
5920
|
break;
|
|
5921
|
+
case "probe-models":
|
|
5922
|
+
void listModels(msg.runtime ?? "").then((models) => conn.send({ type: "models", requestId: msg.requestId, runtime: msg.runtime, models })).catch((e) => conn.send({ type: "models", requestId: msg.requestId, runtime: msg.runtime, models: null, error: String(e?.message ?? e) }));
|
|
5923
|
+
break;
|
|
5737
5924
|
case "ping":
|
|
5738
5925
|
conn.send({ type: "pong" });
|
|
5739
5926
|
break;
|
|
@@ -5748,7 +5935,7 @@ conn = new Connection(serverUrl, apiKey, (msg) => {
|
|
|
5748
5935
|
runningAgents: mgr.running(),
|
|
5749
5936
|
hostname: os4.hostname(),
|
|
5750
5937
|
os: `${os4.platform()} ${os4.arch()}`,
|
|
5751
|
-
daemonVersion: "0.
|
|
5938
|
+
daemonVersion: "0.3.0",
|
|
5752
5939
|
machineId: readMachineId()
|
|
5753
5940
|
// Stable identity: empty on first connection; server sends it back via ready:ack for persistence.
|
|
5754
5941
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fancyboi999/open-tag-daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "open-tag compute-plane daemon — connect any machine to an open-tag server so its agents run there. No repo clone needed.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|