myuru 0.1.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.
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Tier enforcement for MyUru.
3
+ * FREE: 2 sequential agents. PRO: 5 concurrent + config packs.
4
+ */
5
+
6
+ const TIERS = {
7
+ FREE: {
8
+ name: "Free",
9
+ maxAgents: 2,
10
+ maxConcurrency: 1,
11
+ configPacks: false,
12
+ price: 0,
13
+ },
14
+ PRO: {
15
+ name: "Pro",
16
+ maxAgents: 5,
17
+ maxConcurrency: 5,
18
+ configPacks: true,
19
+ price: 2900,
20
+ },
21
+ };
22
+
23
+ class TierManager {
24
+ constructor(tier = "FREE") {
25
+ this.tier = TIERS[tier] || TIERS.FREE;
26
+ }
27
+
28
+ canRunAgents(count) {
29
+ if (count > this.tier.maxAgents) {
30
+ return {
31
+ allowed: false,
32
+ reason: `Tier "${this.tier.name}" allows max ${this.tier.maxAgents} agents. You requested ${count}.`,
33
+ upgrade: `Upgrade to Pro ($29) for up to ${TIERS.PRO.maxAgents} agents.`,
34
+ };
35
+ }
36
+ return { allowed: true };
37
+ }
38
+
39
+ canUseConcurrency() {
40
+ if (this.tier.maxConcurrency === 1) {
41
+ return {
42
+ allowed: false,
43
+ reason: "Free tier only supports sequential execution.",
44
+ upgrade: "Upgrade to Pro ($29) for concurrent orchestration.",
45
+ };
46
+ }
47
+ return { allowed: true };
48
+ }
49
+
50
+ enforceLimit(agentCount, concurrent = false) {
51
+ const check = this.canRunAgents(agentCount);
52
+ if (!check.allowed) throw new Error(`${check.reason}\n${check.upgrade}`);
53
+
54
+ if (concurrent) {
55
+ const concCheck = this.canUseConcurrency();
56
+ if (!concCheck.allowed) {
57
+ console.warn(` ${concCheck.reason} Running sequentially.`);
58
+ return false;
59
+ }
60
+ return true;
61
+ }
62
+ return true;
63
+ }
64
+
65
+ info() {
66
+ return {
67
+ tier: this.tier.name,
68
+ price: this.tier.price === 0 ? "Free" : `$${(this.tier.price / 100).toFixed(2)}`,
69
+ maxAgents: this.tier.maxAgents,
70
+ concurrency: this.tier.maxConcurrency > 1 ? "concurrent" : "sequential",
71
+ configPacks: this.tier.configPacks,
72
+ };
73
+ }
74
+ }
75
+
76
+ module.exports = { TierManager, TIERS };
@@ -0,0 +1,49 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ /**
5
+ * Claude CLI provider.
6
+ * Wraps `claude -p` with session persistence, model selection, etc.
7
+ */
8
+ class ClaudeProvider {
9
+ constructor(opts = {}) {
10
+ this.binary = opts.binary || "claude";
11
+ }
12
+
13
+ buildCommand({ model, sessionId, resume, maxTurns, budgetUsd, systemPrompt, promptFile, tmpDir, tempFiles }) {
14
+ const args = ["-p"];
15
+
16
+ if (resume) {
17
+ args.push("--resume", sessionId);
18
+ } else {
19
+ args.push("--session-id", sessionId);
20
+ }
21
+
22
+ args.push("--model", model);
23
+
24
+ if (budgetUsd) {
25
+ args.push("--max-budget-usd", String(budgetUsd));
26
+ }
27
+
28
+ if (systemPrompt) {
29
+ const sysFile = path.join(tmpDir, `sysprompt-${Date.now()}.txt`);
30
+ fs.writeFileSync(sysFile, systemPrompt, "utf-8");
31
+ const sysFileWin = sysFile.replace(/\//g, "\\");
32
+ args.push("--system-prompt-file", `"${sysFileWin}"`);
33
+ tempFiles.push(sysFile);
34
+ }
35
+
36
+ if (maxTurns) {
37
+ args.push("--max-turns", String(maxTurns));
38
+ }
39
+
40
+ args.push("--dangerously-skip-permissions");
41
+
42
+ const promptFileWin = promptFile.replace(/\//g, "\\");
43
+ args.push(`< "${promptFileWin}"`);
44
+
45
+ return { cmd: this.binary, args, env: {} };
46
+ }
47
+ }
48
+
49
+ module.exports = ClaudeProvider;
@@ -0,0 +1,17 @@
1
+ const ClaudeProvider = require("./claude");
2
+ const OpenAIProvider = require("./openai");
3
+
4
+ const PROVIDERS = {
5
+ claude: ClaudeProvider,
6
+ openai: OpenAIProvider,
7
+ };
8
+
9
+ function createProvider(name, opts = {}) {
10
+ const Provider = PROVIDERS[name];
11
+ if (!Provider) {
12
+ throw new Error(`Unknown provider "${name}". Available: ${Object.keys(PROVIDERS).join(", ")}`);
13
+ }
14
+ return new Provider(opts);
15
+ }
16
+
17
+ module.exports = { createProvider, ClaudeProvider, OpenAIProvider, PROVIDERS };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * OpenAI provider — uses the OpenAI API via fetch.
3
+ * Requires OPENAI_API_KEY in environment.
4
+ *
5
+ * For v0.1.0: basic chat completions. No session persistence.
6
+ */
7
+ class OpenAIProvider {
8
+ constructor(opts = {}) {
9
+ this.apiKey = opts.apiKey || process.env.OPENAI_API_KEY;
10
+ this.baseUrl = opts.baseUrl || "https://api.openai.com/v1";
11
+ }
12
+
13
+ static MODELS = {
14
+ "gpt-4o": "gpt-4o",
15
+ "gpt-4o-mini": "gpt-4o-mini",
16
+ "gpt-4-turbo": "gpt-4-turbo",
17
+ "o1": "o1",
18
+ "o1-mini": "o1-mini",
19
+ };
20
+
21
+ async invoke(prompt, { model = "gpt-4o-mini", systemPrompt = "" } = {}) {
22
+ if (!this.apiKey) {
23
+ throw new Error("OPENAI_API_KEY not set. Run: export OPENAI_API_KEY=sk-...");
24
+ }
25
+
26
+ const messages = [];
27
+ if (systemPrompt) messages.push({ role: "system", content: systemPrompt });
28
+ messages.push({ role: "user", content: prompt });
29
+
30
+ const res = await fetch(`${this.baseUrl}/chat/completions`, {
31
+ method: "POST",
32
+ headers: {
33
+ "Content-Type": "application/json",
34
+ "Authorization": `Bearer ${this.apiKey}`,
35
+ },
36
+ body: JSON.stringify({
37
+ model: OpenAIProvider.MODELS[model] || model,
38
+ messages,
39
+ max_tokens: 4096,
40
+ }),
41
+ });
42
+
43
+ if (!res.ok) {
44
+ const text = await res.text();
45
+ throw new Error(`OpenAI API error ${res.status}: ${text.slice(0, 200)}`);
46
+ }
47
+
48
+ const data = await res.json();
49
+ return data.choices[0]?.message?.content || "";
50
+ }
51
+
52
+ buildCommand() {
53
+ throw new Error("OpenAI provider uses invoke() directly, not buildCommand().");
54
+ }
55
+ }
56
+
57
+ module.exports = OpenAIProvider;