amalgm 0.1.135 → 0.1.136
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/package.json +1 -1
- package/runtime/lib/harnesses.js +61 -162
- package/runtime/scripts/amalgm-mcp/agent-bundles/app-entries.js +124 -0
- package/runtime/scripts/amalgm-mcp/agent-bundles/app-files.js +160 -0
- package/runtime/scripts/amalgm-mcp/agent-bundles/automation-entries.js +186 -0
- package/runtime/scripts/amalgm-mcp/agent-bundles/bundles.js +272 -32
- package/runtime/scripts/amalgm-mcp/agent-bundles/rest.js +25 -8
- package/runtime/scripts/amalgm-mcp/automations/rest.js +21 -11
- package/runtime/scripts/amalgm-mcp/automations/store.js +68 -1
- package/runtime/scripts/amalgm-mcp/automations/tools.js +13 -4
- package/runtime/scripts/amalgm-mcp/events/desktop-release-runner.js +1 -0
- package/runtime/scripts/amalgm-mcp/lib/email-deeplinks.js +117 -0
- package/runtime/scripts/amalgm-mcp/lib/email-embeds.js +42 -19
- package/runtime/scripts/amalgm-mcp/lib/email-md.js +24 -17
- package/runtime/scripts/amalgm-mcp/notify/index.js +6 -2
- package/runtime/scripts/amalgm-mcp/state/snapshot.js +3 -3
- package/runtime/scripts/amalgm-mcp/tests/agent-bundles.test.js +8 -8
- package/runtime/scripts/amalgm-mcp/tests/bundle-entries.test.js +288 -0
- package/runtime/scripts/amalgm-mcp/tests/desktop-release-automation-seed.test.js +37 -1
- package/runtime/scripts/amalgm-mcp/tests/desktop-release-runner.test.js +22 -0
- package/runtime/scripts/amalgm-mcp/tests/desktop-release-server.test.js +15 -5
- package/runtime/scripts/amalgm-mcp/tests/email-embeds.test.js +35 -0
- package/runtime/scripts/amalgm-mcp/workspace/rest.js +1 -0
- package/runtime/scripts/chat-core/adapters/acp-client.js +156 -0
- package/runtime/scripts/chat-core/adapters/cursor.js +254 -0
- package/runtime/scripts/chat-core/adapters/pi.js +302 -0
- package/runtime/scripts/chat-core/auth.js +20 -3
- package/runtime/scripts/chat-core/chat-payload.js +2 -0
- package/runtime/scripts/chat-core/contract.js +56 -1
- package/runtime/scripts/chat-core/normalizers/cursor.js +174 -0
- package/runtime/scripts/chat-core/normalizers/normalizer_spec.md +38 -0
- package/runtime/scripts/chat-core/normalizers/pi.js +143 -0
- package/runtime/scripts/chat-core/server.js +4 -0
- package/runtime/scripts/chat-core/tests/cursor.test.js +178 -0
- package/runtime/scripts/chat-core/tests/pi.test.js +182 -0
- package/runtime/scripts/chat-core/tooling/native-config.js +38 -0
- package/runtime/scripts/chat-core/usage.js +20 -0
package/package.json
CHANGED
package/runtime/lib/harnesses.js
CHANGED
|
@@ -60,7 +60,7 @@ var DEFAULT_MODEL_IDS = {
|
|
|
60
60
|
opencode: "opencode-deepseek-deepseek-v4-pro",
|
|
61
61
|
pi: "pi-anthropic-claude-sonnet-4.6",
|
|
62
62
|
amp: "amp-default",
|
|
63
|
-
cursor: "cursor/composer-2
|
|
63
|
+
cursor: "cursor/composer-2.5"
|
|
64
64
|
};
|
|
65
65
|
var CLAUDE_OPUS_4_8_1M_MODEL_ID = "anthropic/claude-opus-4.8-1m";
|
|
66
66
|
var CLAUDE_LEGACY_ALIASES = {
|
|
@@ -291,14 +291,7 @@ function cursorCanonicalModelIdToCliModel(value) {
|
|
|
291
291
|
return canonical.slice("xai/".length).replace(/^grok-4\.20/, "grok-4-20");
|
|
292
292
|
}
|
|
293
293
|
if (canonical.startsWith("anthropic/")) {
|
|
294
|
-
|
|
295
|
-
const opus47Match = model.match(/^claude-opus-4\.7(?:-(.+))?$/);
|
|
296
|
-
if (opus47Match) return `claude-opus-4-7${opus47Match[1] ? `-${opus47Match[1]}` : ""}`;
|
|
297
|
-
const claudeMatch = model.match(/^claude-(opus|sonnet)-(4(?:\.\d+)?)(?:-(.+))?$/);
|
|
298
|
-
if (claudeMatch) {
|
|
299
|
-
const [, family, version, suffix] = claudeMatch;
|
|
300
|
-
return `claude-${version}-${family}${suffix ? `-${suffix}` : ""}`;
|
|
301
|
-
}
|
|
294
|
+
return canonical.slice("anthropic/".length).replace(/-(\d+)\.(\d+)(?=$|-)/, "-$1-$2");
|
|
302
295
|
}
|
|
303
296
|
return canonical;
|
|
304
297
|
}
|
|
@@ -541,16 +534,30 @@ var OPENCODE_MODELS = [
|
|
|
541
534
|
providerGroup: "mistral"
|
|
542
535
|
}
|
|
543
536
|
];
|
|
544
|
-
var
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
537
|
+
var PI_MODEL_ROWS = [
|
|
538
|
+
["anthropic/claude-opus-4.6", "Claude Opus 4.6", "Most capable Anthropic model", "anthropic"],
|
|
539
|
+
["anthropic/claude-sonnet-4.6", "Claude Sonnet 4.6", "Balanced Anthropic model", "anthropic"],
|
|
540
|
+
["anthropic/claude-haiku-4.5", "Claude Haiku 4.5", "Fast Anthropic model", "anthropic"],
|
|
541
|
+
["openai/gpt-5.4", "GPT-5.4", "Flagship OpenAI model", "openai"],
|
|
542
|
+
["openai/gpt-5.3-codex", "GPT-5.3 Codex", "OpenAI coding model", "openai"],
|
|
543
|
+
["google/gemini-3.1-pro-preview", "Gemini 3.1 Pro", "Latest Google flagship", "google"],
|
|
544
|
+
["google/gemini-3-flash", "Gemini 3 Flash", "Fast Google model", "google"],
|
|
545
|
+
["xai/grok-4", "Grok 4", "xAI flagship model", "xai"],
|
|
546
|
+
["deepseek/deepseek-v4-pro", "DeepSeek V4 Pro", "DeepSeek flagship model", "deepseek"],
|
|
547
|
+
["deepseek/deepseek-v3.2", "DeepSeek V3.2", "Latest DeepSeek model", "deepseek"],
|
|
548
|
+
["mistral/devstral-2", "Devstral 2", "Mistral coding model", "mistral"]
|
|
553
549
|
];
|
|
550
|
+
var PI_MODELS = PI_MODEL_ROWS.map(
|
|
551
|
+
([gatewayId, name, description, providerGroup]) => ({
|
|
552
|
+
id: `pi-${gatewayId.replace(/\//g, "-")}`,
|
|
553
|
+
name,
|
|
554
|
+
description,
|
|
555
|
+
gatewayModelId: gatewayId,
|
|
556
|
+
cliModel: buildGatewayCliModel(gatewayId, "pi"),
|
|
557
|
+
provider: "pi",
|
|
558
|
+
providerGroup
|
|
559
|
+
})
|
|
560
|
+
);
|
|
554
561
|
var AMP_MODELS = [
|
|
555
562
|
{
|
|
556
563
|
id: DEFAULT_MODEL_IDS.amp,
|
|
@@ -563,101 +570,37 @@ var AMP_MODELS = [
|
|
|
563
570
|
];
|
|
564
571
|
var CURSOR_MODEL_ROWS = [
|
|
565
572
|
["auto", "Auto", "cursor"],
|
|
566
|
-
["composer-2
|
|
567
|
-
["
|
|
568
|
-
["
|
|
569
|
-
["
|
|
570
|
-
["
|
|
573
|
+
["composer-2.5", "Composer 2.5", "cursor"],
|
|
574
|
+
["claude-fable-5", "Fable 5 Thinking", "anthropic"],
|
|
575
|
+
["claude-opus-4-8", "Opus 4.8 Thinking", "anthropic"],
|
|
576
|
+
["claude-opus-4-7", "Opus 4.7 Thinking", "anthropic"],
|
|
577
|
+
["claude-opus-4-6", "Opus 4.6 Thinking", "anthropic"],
|
|
578
|
+
["claude-opus-4-5", "Opus 4.5 Thinking", "anthropic"],
|
|
579
|
+
["claude-sonnet-5", "Sonnet 5 Thinking", "anthropic"],
|
|
580
|
+
["claude-sonnet-4-6", "Sonnet 4.6 Thinking", "anthropic"],
|
|
581
|
+
["claude-sonnet-4-5", "Sonnet 4.5 Thinking", "anthropic"],
|
|
582
|
+
["claude-sonnet-4", "Sonnet 4", "anthropic"],
|
|
583
|
+
["claude-haiku-4-5", "Haiku 4.5 Thinking", "anthropic"],
|
|
584
|
+
["gpt-5.5", "GPT-5.5", "openai"],
|
|
585
|
+
["gpt-5.4", "GPT-5.4", "openai"],
|
|
586
|
+
["gpt-5.4-mini", "GPT-5.4 Mini", "openai"],
|
|
587
|
+
["gpt-5.4-nano", "GPT-5.4 Nano", "openai"],
|
|
571
588
|
["gpt-5.3-codex", "Codex 5.3", "openai"],
|
|
572
|
-
["gpt-5.3-codex-fast", "Codex 5.3 Fast", "openai"],
|
|
573
|
-
["gpt-5.3-codex-high", "Codex 5.3 High", "openai"],
|
|
574
|
-
["gpt-5.3-codex-high-fast", "Codex 5.3 High Fast", "openai"],
|
|
575
|
-
["gpt-5.3-codex-xhigh", "Codex 5.3 Extra High", "openai"],
|
|
576
|
-
["gpt-5.3-codex-xhigh-fast", "Codex 5.3 Extra High Fast", "openai"],
|
|
577
|
-
["gpt-5.2", "GPT-5.2", "openai"],
|
|
578
|
-
["gpt-5.3-codex-spark-preview-low", "Codex 5.3 Spark Low", "openai"],
|
|
579
|
-
["gpt-5.3-codex-spark-preview", "Codex 5.3 Spark", "openai"],
|
|
580
|
-
["gpt-5.3-codex-spark-preview-high", "Codex 5.3 Spark High", "openai"],
|
|
581
|
-
["gpt-5.3-codex-spark-preview-xhigh", "Codex 5.3 Spark Extra High", "openai"],
|
|
582
|
-
["gpt-5.2-codex-low", "Codex 5.2 Low", "openai"],
|
|
583
|
-
["gpt-5.2-codex-low-fast", "Codex 5.2 Low Fast", "openai"],
|
|
584
589
|
["gpt-5.2-codex", "Codex 5.2", "openai"],
|
|
585
|
-
["gpt-5.2
|
|
586
|
-
["gpt-5.
|
|
587
|
-
["gpt-5.2-codex-high-fast", "Codex 5.2 High Fast", "openai"],
|
|
588
|
-
["gpt-5.2-codex-xhigh", "Codex 5.2 Extra High", "openai"],
|
|
589
|
-
["gpt-5.2-codex-xhigh-fast", "Codex 5.2 Extra High Fast", "openai"],
|
|
590
|
-
["gpt-5.1-codex-max-low", "Codex 5.1 Max Low", "openai"],
|
|
591
|
-
["gpt-5.1-codex-max-low-fast", "Codex 5.1 Max Low Fast", "openai"],
|
|
592
|
-
["gpt-5.1-codex-max-medium", "Codex 5.1 Max", "openai"],
|
|
593
|
-
["gpt-5.1-codex-max-medium-fast", "Codex 5.1 Max Medium Fast", "openai"],
|
|
594
|
-
["gpt-5.1-codex-max-high", "Codex 5.1 Max High", "openai"],
|
|
595
|
-
["gpt-5.1-codex-max-high-fast", "Codex 5.1 Max High Fast", "openai"],
|
|
596
|
-
["gpt-5.1-codex-max-xhigh", "Codex 5.1 Max Extra High", "openai"],
|
|
597
|
-
["gpt-5.1-codex-max-xhigh-fast", "Codex 5.1 Max Extra High Fast", "openai"],
|
|
598
|
-
["gpt-5.5-medium", "GPT-5.5 1M", "openai"],
|
|
599
|
-
["claude-opus-4-7-thinking-high", "Opus 4.7 1M High Thinking", "anthropic"],
|
|
600
|
-
["gpt-5.4-high", "GPT-5.4 1M High", "openai"],
|
|
601
|
-
["gpt-5.4-high-fast", "GPT-5.4 High Fast", "openai"],
|
|
602
|
-
["claude-4.6-opus-high-thinking-fast", "Opus 4.6 1M Thinking Fast", "anthropic"],
|
|
603
|
-
["gpt-5.5-high", "GPT-5.5 1M High", "openai"],
|
|
604
|
-
["gpt-5.5-extra-high", "GPT-5.5 1M Extra High", "openai"],
|
|
605
|
-
["claude-4.6-sonnet-medium", "Sonnet 4.6 1M", "anthropic"],
|
|
606
|
-
["claude-4.6-sonnet-medium-thinking", "Sonnet 4.6 1M Thinking", "anthropic"],
|
|
607
|
-
["claude-opus-4-7-low", "Opus 4.7 1M Low", "anthropic"],
|
|
608
|
-
["claude-opus-4-7-medium", "Opus 4.7 1M Medium", "anthropic"],
|
|
609
|
-
["claude-opus-4-7-high", "Opus 4.7 1M High", "anthropic"],
|
|
610
|
-
["claude-opus-4-7-xhigh", "Opus 4.7 1M", "anthropic"],
|
|
611
|
-
["claude-opus-4-7-max", "Opus 4.7 1M Max", "anthropic"],
|
|
612
|
-
["claude-opus-4-7-thinking-low", "Opus 4.7 1M Low Thinking", "anthropic"],
|
|
613
|
-
["claude-opus-4-7-thinking-medium", "Opus 4.7 1M Medium Thinking", "anthropic"],
|
|
614
|
-
["claude-opus-4-7-thinking-xhigh", "Opus 4.7 1M Thinking", "anthropic"],
|
|
615
|
-
["claude-opus-4-7-thinking-max", "Opus 4.7 1M Max Thinking", "anthropic"],
|
|
616
|
-
["gpt-5.4-low", "GPT-5.4 1M Low", "openai"],
|
|
617
|
-
["gpt-5.4-medium", "GPT-5.4 1M", "openai"],
|
|
618
|
-
["gpt-5.4-medium-fast", "GPT-5.4 Fast", "openai"],
|
|
619
|
-
["gpt-5.4-xhigh", "GPT-5.4 1M Extra High", "openai"],
|
|
620
|
-
["gpt-5.4-xhigh-fast", "GPT-5.4 Extra High Fast", "openai"],
|
|
621
|
-
["claude-4.6-opus-high", "Opus 4.6 1M", "anthropic"],
|
|
622
|
-
["claude-4.6-opus-max", "Opus 4.6 1M Max", "anthropic"],
|
|
623
|
-
["claude-4.6-opus-high-thinking", "Opus 4.6 1M Thinking", "anthropic"],
|
|
624
|
-
["claude-4.6-opus-max-thinking", "Opus 4.6 1M Max Thinking", "anthropic"],
|
|
625
|
-
["claude-4.6-opus-max-thinking-fast", "Opus 4.6 1M Max Thinking Fast", "anthropic"],
|
|
626
|
-
["claude-4.5-opus-high", "Opus 4.5", "anthropic"],
|
|
627
|
-
["claude-4.5-opus-high-thinking", "Opus 4.5 Thinking", "anthropic"],
|
|
628
|
-
["gpt-5.2-low", "GPT-5.2 Low", "openai"],
|
|
629
|
-
["gpt-5.2-low-fast", "GPT-5.2 Low Fast", "openai"],
|
|
630
|
-
["gpt-5.2-fast", "GPT-5.2 Fast", "openai"],
|
|
631
|
-
["gpt-5.2-high", "GPT-5.2 High", "openai"],
|
|
632
|
-
["gpt-5.2-high-fast", "GPT-5.2 High Fast", "openai"],
|
|
633
|
-
["gpt-5.2-xhigh", "GPT-5.2 Extra High", "openai"],
|
|
634
|
-
["gpt-5.2-xhigh-fast", "GPT-5.2 Extra High Fast", "openai"],
|
|
635
|
-
["gemini-3.1-pro", "Gemini 3.1 Pro", "google"],
|
|
636
|
-
["gpt-5.4-mini-none", "GPT-5.4 Mini None", "openai"],
|
|
637
|
-
["gpt-5.4-mini-low", "GPT-5.4 Mini Low", "openai"],
|
|
638
|
-
["gpt-5.4-mini-medium", "GPT-5.4 Mini", "openai"],
|
|
639
|
-
["gpt-5.4-mini-high", "GPT-5.4 Mini High", "openai"],
|
|
640
|
-
["gpt-5.4-mini-xhigh", "GPT-5.4 Mini Extra High", "openai"],
|
|
641
|
-
["gpt-5.4-nano-none", "GPT-5.4 Nano None", "openai"],
|
|
642
|
-
["gpt-5.4-nano-low", "GPT-5.4 Nano Low", "openai"],
|
|
643
|
-
["gpt-5.4-nano-medium", "GPT-5.4 Nano", "openai"],
|
|
644
|
-
["gpt-5.4-nano-high", "GPT-5.4 Nano High", "openai"],
|
|
645
|
-
["gpt-5.4-nano-xhigh", "GPT-5.4 Nano Extra High", "openai"],
|
|
646
|
-
["grok-4-20", "Grok 4.20", "xai"],
|
|
647
|
-
["grok-4-20-thinking", "Grok 4.20 Thinking", "xai"],
|
|
648
|
-
["claude-4.5-sonnet", "Sonnet 4.5", "anthropic"],
|
|
649
|
-
["claude-4.5-sonnet-thinking", "Sonnet 4.5 Thinking", "anthropic"],
|
|
650
|
-
["gpt-5.1-low", "GPT-5.1 Low", "openai"],
|
|
651
|
-
["gpt-5.1", "GPT-5.1", "openai"],
|
|
652
|
-
["gpt-5.1-high", "GPT-5.1 High", "openai"],
|
|
653
|
-
["gemini-3-flash", "Gemini 3 Flash", "google"],
|
|
654
|
-
["gpt-5.1-codex-mini-low", "Codex 5.1 Mini Low", "openai"],
|
|
590
|
+
["gpt-5.2", "GPT-5.2", "openai"],
|
|
591
|
+
["gpt-5.1-codex-max", "Codex 5.1 Max", "openai"],
|
|
655
592
|
["gpt-5.1-codex-mini", "Codex 5.1 Mini", "openai"],
|
|
656
|
-
["gpt-5.1
|
|
657
|
-
["claude-4-sonnet", "Sonnet 4", "anthropic"],
|
|
658
|
-
["claude-4-sonnet-thinking", "Sonnet 4 Thinking", "anthropic"],
|
|
593
|
+
["gpt-5.1", "GPT-5.1", "openai"],
|
|
659
594
|
["gpt-5-mini", "GPT-5 Mini", "openai"],
|
|
660
|
-
["
|
|
595
|
+
["gemini-3.5-flash", "Gemini 3.5 Flash", "google"],
|
|
596
|
+
["gemini-3.1-pro", "Gemini 3.1 Pro", "google"],
|
|
597
|
+
["gemini-3-flash", "Gemini 3 Flash", "google"],
|
|
598
|
+
["gemini-2.5-flash", "Gemini 2.5 Flash", "google"],
|
|
599
|
+
["grok-4.3", "Grok 4.3", "xai"],
|
|
600
|
+
["grok-build-0.1", "Grok Build 0.1", "xai"],
|
|
601
|
+
["kimi-k2.7-code", "Kimi K2.7 Code", "moonshotai"],
|
|
602
|
+
["kimi-k2.5", "Kimi K2.5", "moonshotai"],
|
|
603
|
+
["glm-5.2", "GLM-5.2", "zai"]
|
|
661
604
|
];
|
|
662
605
|
function cursorModelDescription(providerGroup) {
|
|
663
606
|
switch (providerGroup) {
|
|
@@ -673,47 +616,26 @@ function cursorModelDescription(providerGroup) {
|
|
|
673
616
|
return "xAI model through Cursor Agent";
|
|
674
617
|
case "moonshotai":
|
|
675
618
|
return "Moonshot model through Cursor Agent";
|
|
619
|
+
case "zai":
|
|
620
|
+
return "Z.ai model through Cursor Agent";
|
|
676
621
|
default:
|
|
677
622
|
return "Model through Cursor Agent";
|
|
678
623
|
}
|
|
679
624
|
}
|
|
680
|
-
function cursorModelPricing(cliModel) {
|
|
681
|
-
switch (cliModel) {
|
|
682
|
-
case "auto":
|
|
683
|
-
return { inputPerMTok: 1.25, outputPerMTok: 6, cacheReadPerMTok: 0.25, cacheWritePerMTok: 0 };
|
|
684
|
-
case "composer-2-fast":
|
|
685
|
-
return { inputPerMTok: 1.5, outputPerMTok: 7.5, cacheReadPerMTok: 0.35, cacheWritePerMTok: 0 };
|
|
686
|
-
case "composer-2":
|
|
687
|
-
return { inputPerMTok: 0.5, outputPerMTok: 2.5, cacheReadPerMTok: 0.2, cacheWritePerMTok: 0 };
|
|
688
|
-
case "composer-1.5":
|
|
689
|
-
return { inputPerMTok: 3.5, outputPerMTok: 17.5, cacheReadPerMTok: 0.35, cacheWritePerMTok: 0 };
|
|
690
|
-
default:
|
|
691
|
-
return void 0;
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
625
|
var CURSOR_MODELS = CURSOR_MODEL_ROWS.map(
|
|
695
626
|
([cliModel, name, providerGroup]) => {
|
|
696
627
|
const id = cursorCliModelToModelId(cliModel);
|
|
697
|
-
const pricing = cursorModelPricing(cliModel);
|
|
698
628
|
return {
|
|
699
629
|
id,
|
|
700
630
|
name,
|
|
701
631
|
description: cursorModelDescription(providerGroup),
|
|
702
632
|
gatewayModelId: modelIdToGatewayModelId(id, "cursor") ?? id,
|
|
703
|
-
...pricing ? { pricing } : {},
|
|
704
633
|
cliModel,
|
|
705
634
|
provider: "cursor",
|
|
706
635
|
providerGroup
|
|
707
636
|
};
|
|
708
637
|
}
|
|
709
638
|
);
|
|
710
|
-
var THINKING_LEVELS = ["xhigh", "high", "medium", "low"];
|
|
711
|
-
var THINKING_LEVEL_LABELS = {
|
|
712
|
-
low: "Low",
|
|
713
|
-
medium: "Medium",
|
|
714
|
-
high: "High",
|
|
715
|
-
xhigh: "xHigh"
|
|
716
|
-
};
|
|
717
639
|
function codexModel(id, name, description, reasoningEffort, gatewayModelId = id.replace(/(?::thinking-|-thinking-)(low|medium|high|xhigh)$/i, ""), providerGroup = id.includes("gpt-5.5") ? "gpt-5.5" : "gpt-5.4") {
|
|
718
640
|
return {
|
|
719
641
|
id,
|
|
@@ -726,23 +648,7 @@ function codexModel(id, name, description, reasoningEffort, gatewayModelId = id.
|
|
|
726
648
|
...reasoningEffort ? { reasoningEffort } : {}
|
|
727
649
|
};
|
|
728
650
|
}
|
|
729
|
-
function codexThinkingModels(baseId, baseName, description) {
|
|
730
|
-
return THINKING_LEVELS.map(
|
|
731
|
-
(level) => codexModel(
|
|
732
|
-
`${baseId}-thinking-${level}`,
|
|
733
|
-
`${baseName} Thinking ${THINKING_LEVEL_LABELS[level]}`,
|
|
734
|
-
`${description} with ${THINKING_LEVEL_LABELS[level]} thinking`,
|
|
735
|
-
level,
|
|
736
|
-
baseId
|
|
737
|
-
)
|
|
738
|
-
);
|
|
739
|
-
}
|
|
740
651
|
var CODEX_MODELS = [
|
|
741
|
-
...codexThinkingModels(
|
|
742
|
-
"openai/gpt-5.5",
|
|
743
|
-
"GPT-5.5",
|
|
744
|
-
"Latest frontier model for coding and agentic work"
|
|
745
|
-
),
|
|
746
652
|
codexModel(
|
|
747
653
|
"openai/gpt-5.5",
|
|
748
654
|
"GPT-5.5",
|
|
@@ -753,11 +659,6 @@ var CODEX_MODELS = [
|
|
|
753
659
|
"GPT-5.5 Pro",
|
|
754
660
|
"Highest-accuracy GPT-5.5 variant for difficult work"
|
|
755
661
|
),
|
|
756
|
-
...codexThinkingModels(
|
|
757
|
-
"openai/gpt-5.4",
|
|
758
|
-
"GPT-5.4",
|
|
759
|
-
"Frontier coding model"
|
|
760
|
-
),
|
|
761
662
|
codexModel(
|
|
762
663
|
"openai/gpt-5.4",
|
|
763
664
|
"GPT-5.4",
|
|
@@ -851,9 +752,8 @@ var VISIBLE_HARNESS_IDS = [
|
|
|
851
752
|
"claude_code",
|
|
852
753
|
"codex",
|
|
853
754
|
"opencode",
|
|
854
|
-
"
|
|
855
|
-
"
|
|
856
|
-
"cursor"
|
|
755
|
+
"cursor",
|
|
756
|
+
"pi"
|
|
857
757
|
];
|
|
858
758
|
var DISABLED_HARNESS_IDS = /* @__PURE__ */ new Set([]);
|
|
859
759
|
var dynamicModelCache = /* @__PURE__ */ new Map();
|
|
@@ -896,13 +796,11 @@ function isHarnessId(value) {
|
|
|
896
796
|
function resolvePreferredHarnessModel({
|
|
897
797
|
explicitHarness,
|
|
898
798
|
explicitModel,
|
|
899
|
-
pinnedHarness,
|
|
900
|
-
pinnedModel,
|
|
901
799
|
lastUsedHarness,
|
|
902
800
|
selectedModels
|
|
903
801
|
}) {
|
|
904
|
-
const harness = (isHarnessId(explicitHarness) ? explicitHarness : null) || (isHarnessId(
|
|
905
|
-
const rawModel = explicitModel ||
|
|
802
|
+
const harness = (isHarnessId(explicitHarness) ? explicitHarness : null) || (isHarnessId(lastUsedHarness) ? lastUsedHarness : null) || "claude_code";
|
|
803
|
+
const rawModel = explicitModel || selectedModels?.[harness] || getDefaultModel(harness);
|
|
906
804
|
const model = normalizeModelId(rawModel, harness) ?? rawModel;
|
|
907
805
|
return { harness, model };
|
|
908
806
|
}
|
|
@@ -942,11 +840,12 @@ function coerceAuthMethodForHarness(harnessId, authMethod) {
|
|
|
942
840
|
function resolveHarnessModel(harnessId, modelId) {
|
|
943
841
|
const selection = normalizeModelSelection(modelId ?? getDefaultModel(harnessId), harnessId);
|
|
944
842
|
const normalizedModelId = selection?.modelId ?? getDefaultModel(harnessId);
|
|
945
|
-
const
|
|
843
|
+
const localModel = HARNESSES[harnessId]?.models.find((m) => m.id === normalizedModelId);
|
|
844
|
+
const model = localModel ?? getModelById(normalizedModelId);
|
|
946
845
|
const adaptedModel = harnessId === "claude_code" || harnessId === "codex" || harnessId === "cursor" ? modelIdToHarnessModel(normalizedModelId, harnessId) : null;
|
|
947
846
|
return {
|
|
948
847
|
modelId: normalizedModelId,
|
|
949
|
-
cliModel: model?.cliModel ??
|
|
848
|
+
cliModel: localModel?.cliModel ?? adaptedModel ?? model?.cliModel ?? null,
|
|
950
849
|
reasoningEffort: selection?.reasoningEffort ?? model?.reasoningEffort ?? (harnessId === "codex" ? "xhigh" : void 0)
|
|
951
850
|
};
|
|
952
851
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Portable app bundle entries.
|
|
5
|
+
*
|
|
6
|
+
* An exported entry carries the app's registration record (commands, lifecycle
|
|
7
|
+
* flags) plus its packed file tree. Machine-bound state — cwd, port, appRef,
|
|
8
|
+
* public URL, pid/status — stays behind: install writes the files into a fresh
|
|
9
|
+
* local directory and registers through the normal supervisor path, which
|
|
10
|
+
* allocates a new port and DNS ref, runs the build, and starts the process.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const { getApp } = require('../apps/store');
|
|
17
|
+
const { registerApp } = require('../apps/supervisor');
|
|
18
|
+
const { packAppFiles, unpackAppFiles } = require('./app-files');
|
|
19
|
+
|
|
20
|
+
function isObject(value) {
|
|
21
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function cleanString(value) {
|
|
25
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function slugify(value) {
|
|
29
|
+
return String(value || '')
|
|
30
|
+
.toLowerCase()
|
|
31
|
+
.replace(/[^a-z0-9._-]+/g, '-')
|
|
32
|
+
.replace(/^-+|-+$/g, '')
|
|
33
|
+
.slice(0, 60) || 'app';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function exportAppEntry(appId, options = {}) {
|
|
37
|
+
const app = getApp(appId);
|
|
38
|
+
if (!app) throw new Error(`App not found: ${appId}`);
|
|
39
|
+
if (!cleanString(app.startCommand)) {
|
|
40
|
+
throw new Error(`App has no start command and cannot be shared: ${appId}`);
|
|
41
|
+
}
|
|
42
|
+
if (!app.cwd) throw new Error(`App has no working directory: ${appId}`);
|
|
43
|
+
|
|
44
|
+
const packed = packAppFiles(app.cwd, options);
|
|
45
|
+
if (packed.files.length === 0) {
|
|
46
|
+
throw new Error(`App directory has no shareable files: ${app.cwd}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
entry: {
|
|
51
|
+
sourceAppId: app.id,
|
|
52
|
+
app: {
|
|
53
|
+
name: app.name || 'App',
|
|
54
|
+
description: app.description || '',
|
|
55
|
+
buildCommand: app.buildCommand || null,
|
|
56
|
+
startCommand: app.startCommand,
|
|
57
|
+
autostart: app.autostart !== false,
|
|
58
|
+
keepAlive: app.keepAlive !== false,
|
|
59
|
+
},
|
|
60
|
+
files: packed.files,
|
|
61
|
+
totalBytes: packed.totalBytes,
|
|
62
|
+
skipped: packed.skipped,
|
|
63
|
+
},
|
|
64
|
+
skipped: packed.skipped,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function assertInstallableEntry(entry) {
|
|
69
|
+
if (!isObject(entry)) throw new Error('app entry must be an object');
|
|
70
|
+
if (!isObject(entry.app)) throw new Error('app entry is missing its app record');
|
|
71
|
+
if (!cleanString(entry.app.startCommand)) {
|
|
72
|
+
throw new Error('app entry must include a start command');
|
|
73
|
+
}
|
|
74
|
+
if (!Array.isArray(entry.files) || entry.files.length === 0) {
|
|
75
|
+
throw new Error('app entry must include files');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function allocateInstallDir(rootDir, name) {
|
|
80
|
+
const base = slugify(name);
|
|
81
|
+
fs.mkdirSync(rootDir, { recursive: true });
|
|
82
|
+
for (let index = 0; index < 1000; index += 1) {
|
|
83
|
+
const candidate = path.join(rootDir, index === 0 ? base : `${base}-${index + 1}`);
|
|
84
|
+
if (!fs.existsSync(candidate)) return candidate;
|
|
85
|
+
}
|
|
86
|
+
throw new Error(`Could not allocate an install directory for app: ${name}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Write the entry's files into a fresh directory and return the registration
|
|
91
|
+
* input for the app supervisor. Separated from installAppEntry so the file
|
|
92
|
+
* step can be exercised without spawning processes.
|
|
93
|
+
*/
|
|
94
|
+
function materializeAppEntry(entry, options = {}) {
|
|
95
|
+
assertInstallableEntry(entry);
|
|
96
|
+
if (!cleanString(options.rootDir)) throw new Error('rootDir is required');
|
|
97
|
+
const targetDir = allocateInstallDir(path.resolve(options.rootDir), entry.app.name);
|
|
98
|
+
unpackAppFiles(entry.files, targetDir);
|
|
99
|
+
return {
|
|
100
|
+
name: entry.app.name || 'App',
|
|
101
|
+
description: entry.app.description || '',
|
|
102
|
+
cwd: targetDir,
|
|
103
|
+
build_command: entry.app.buildCommand || null,
|
|
104
|
+
start_command: entry.app.startCommand,
|
|
105
|
+
autostart: entry.app.autostart !== false,
|
|
106
|
+
keep_alive: entry.app.keepAlive !== false,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Install a portable app entry: materialize files, then register — which
|
|
112
|
+
* builds, starts the process, and connects a fresh appRef to app DNS.
|
|
113
|
+
*/
|
|
114
|
+
async function installAppEntry(entry, options = {}) {
|
|
115
|
+
const register = options.register || registerApp;
|
|
116
|
+
const input = materializeAppEntry(entry, options);
|
|
117
|
+
return register(input);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = {
|
|
121
|
+
exportAppEntry,
|
|
122
|
+
installAppEntry,
|
|
123
|
+
materializeAppEntry,
|
|
124
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Pack and unpack an app's file tree for portable bundles.
|
|
5
|
+
*
|
|
6
|
+
* Files travel inline in the bundle JSON (utf8 or base64), so packing is
|
|
7
|
+
* conservative: dependency dirs, VCS state, env files, and build outputs are
|
|
8
|
+
* excluded — the recipient's install re-runs the app's build command — and
|
|
9
|
+
* per-file/total size caps keep the bundle under the storage object limit.
|
|
10
|
+
* Everything skipped is reported so the share UI can surface it.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const DEFAULT_MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
17
|
+
const DEFAULT_MAX_TOTAL_BYTES = 12 * 1024 * 1024;
|
|
18
|
+
|
|
19
|
+
const EXCLUDED_DIR_NAMES = new Set([
|
|
20
|
+
'node_modules',
|
|
21
|
+
'.git',
|
|
22
|
+
'.hg',
|
|
23
|
+
'.svn',
|
|
24
|
+
'.amalgm',
|
|
25
|
+
'.next',
|
|
26
|
+
'.nuxt',
|
|
27
|
+
'.turbo',
|
|
28
|
+
'.vercel',
|
|
29
|
+
'.cache',
|
|
30
|
+
'.parcel-cache',
|
|
31
|
+
'.venv',
|
|
32
|
+
'venv',
|
|
33
|
+
'__pycache__',
|
|
34
|
+
'dist',
|
|
35
|
+
'build',
|
|
36
|
+
'out',
|
|
37
|
+
'coverage',
|
|
38
|
+
'tmp',
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
const EXCLUDED_FILE_NAMES = new Set([
|
|
42
|
+
'.DS_Store',
|
|
43
|
+
'Thumbs.db',
|
|
44
|
+
'npm-debug.log',
|
|
45
|
+
'yarn-error.log',
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
function isExcludedFileName(name) {
|
|
49
|
+
if (EXCLUDED_FILE_NAMES.has(name)) return true;
|
|
50
|
+
// .env, .env.local, .env.production, … — never ship credentials.
|
|
51
|
+
if (name === '.env' || name.startsWith('.env.')) return true;
|
|
52
|
+
if (name.endsWith('.log')) return true;
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isBinary(buffer) {
|
|
57
|
+
const sample = buffer.subarray(0, 8192);
|
|
58
|
+
if (sample.includes(0)) return true;
|
|
59
|
+
return !buffer.equals(Buffer.from(buffer.toString('utf8'), 'utf8'));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function toPosixPath(relativePath) {
|
|
63
|
+
return relativePath.split(path.sep).join('/');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function packAppFiles(rootDir, options = {}) {
|
|
67
|
+
const root = path.resolve(rootDir);
|
|
68
|
+
if (!fs.existsSync(root) || !fs.statSync(root).isDirectory()) {
|
|
69
|
+
throw new Error(`App directory not found: ${root}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const maxFileBytes = options.maxFileBytes || DEFAULT_MAX_FILE_BYTES;
|
|
73
|
+
const maxTotalBytes = options.maxTotalBytes || DEFAULT_MAX_TOTAL_BYTES;
|
|
74
|
+
const files = [];
|
|
75
|
+
const skipped = [];
|
|
76
|
+
let totalBytes = 0;
|
|
77
|
+
|
|
78
|
+
function visit(dir) {
|
|
79
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true })
|
|
80
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
81
|
+
for (const dirent of entries) {
|
|
82
|
+
const absolute = path.join(dir, dirent.name);
|
|
83
|
+
const relative = toPosixPath(path.relative(root, absolute));
|
|
84
|
+
|
|
85
|
+
if (dirent.isSymbolicLink()) {
|
|
86
|
+
skipped.push({ path: relative, reason: 'symlink' });
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (dirent.isDirectory()) {
|
|
90
|
+
if (EXCLUDED_DIR_NAMES.has(dirent.name)) {
|
|
91
|
+
skipped.push({ path: `${relative}/`, reason: 'excluded' });
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
visit(absolute);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (!dirent.isFile()) continue;
|
|
98
|
+
if (isExcludedFileName(dirent.name)) {
|
|
99
|
+
skipped.push({ path: relative, reason: 'excluded' });
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const stat = fs.statSync(absolute);
|
|
104
|
+
if (stat.size > maxFileBytes) {
|
|
105
|
+
skipped.push({ path: relative, reason: 'file-too-large', size: stat.size });
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (totalBytes + stat.size > maxTotalBytes) {
|
|
109
|
+
skipped.push({ path: relative, reason: 'bundle-size-limit', size: stat.size });
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const buffer = fs.readFileSync(absolute);
|
|
114
|
+
const binary = isBinary(buffer);
|
|
115
|
+
totalBytes += stat.size;
|
|
116
|
+
files.push({
|
|
117
|
+
path: relative,
|
|
118
|
+
encoding: binary ? 'base64' : 'utf8',
|
|
119
|
+
content: binary ? buffer.toString('base64') : buffer.toString('utf8'),
|
|
120
|
+
size: stat.size,
|
|
121
|
+
...(stat.mode & 0o111 ? { executable: true } : {}),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
visit(root);
|
|
127
|
+
return { files, totalBytes, skipped };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function assertInsideTarget(targetRoot, relativePath) {
|
|
131
|
+
const normalized = String(relativePath || '');
|
|
132
|
+
const resolved = path.resolve(targetRoot, normalized);
|
|
133
|
+
if (resolved !== targetRoot && !resolved.startsWith(targetRoot + path.sep)) {
|
|
134
|
+
throw new Error(`App file escapes the install directory: ${normalized}`);
|
|
135
|
+
}
|
|
136
|
+
return resolved;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function unpackAppFiles(files, targetDir) {
|
|
140
|
+
const targetRoot = path.resolve(targetDir);
|
|
141
|
+
fs.mkdirSync(targetRoot, { recursive: true });
|
|
142
|
+
|
|
143
|
+
let written = 0;
|
|
144
|
+
for (const file of Array.isArray(files) ? files : []) {
|
|
145
|
+
if (!file || typeof file.path !== 'string' || typeof file.content !== 'string') continue;
|
|
146
|
+
const destination = assertInsideTarget(targetRoot, file.path);
|
|
147
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
148
|
+
const buffer = file.encoding === 'base64'
|
|
149
|
+
? Buffer.from(file.content, 'base64')
|
|
150
|
+
: Buffer.from(file.content, 'utf8');
|
|
151
|
+
fs.writeFileSync(destination, buffer, { mode: file.executable ? 0o755 : 0o644 });
|
|
152
|
+
written += 1;
|
|
153
|
+
}
|
|
154
|
+
return { written, targetDir: targetRoot };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
module.exports = {
|
|
158
|
+
packAppFiles,
|
|
159
|
+
unpackAppFiles,
|
|
160
|
+
};
|