@runeya/runeya 2.0.153 → 2.0.154
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/{chat-Z7SFBEXS.js → chat-COP27XB3.js} +3 -3
- package/{chunk-EUTW2S4P.js → chunk-OLHL5NFP.js} +7 -4
- package/chunk-OLHL5NFP.js.map +1 -0
- package/{chunk-QJSIN26R.js → chunk-VKYSHTYT.js} +443 -78
- package/chunk-VKYSHTYT.js.map +1 -0
- package/{companion-ZDEGNZ2G.js → companion-3MW23ONE.js} +2 -2
- package/index.js +7 -7
- package/{install-gate-TSCXUCYE.js → install-gate-3UVXPA57.js} +2 -2
- package/package.json +1 -1
- package/{src-NWPED6TK.js → src-K4E6GUCZ.js} +37 -25
- package/src-K4E6GUCZ.js.map +1 -0
- package/web/assets/{AgentScalarChatInterface.vue-CEMnvqfq.js → AgentScalarChatInterface.vue-BtA-f77q.js} +3 -3
- package/web/assets/{AgentTerminalPage-qQ96vm5Y.js → AgentTerminalPage-2Ky-vEkw.js} +1 -1
- package/web/assets/AiChatModals-BV3vIIqN.js +4 -0
- package/web/assets/AiChatModals-DS5R3Oz1.css +1 -0
- package/web/assets/AppLayout-BgPr-urw.css +1 -0
- package/web/assets/AppLayout-DsczwAFI.js +1 -0
- package/web/assets/{AuditPage-B-pCRqXH.js → AuditPage-DoKHFRJ7.js} +1 -1
- package/web/assets/{CloudAccountPage-C7aV6M8U.js → CloudAccountPage-CDQNalgx.js} +1 -1
- package/web/assets/{CloudMessagingPage-BTc8xbjF.js → CloudMessagingPage-DduCz3KB.js} +1 -1
- package/web/assets/{DashboardPage-CF7GLFlx.js → DashboardPage-ClJR5DiP.js} +1 -1
- package/web/assets/{DirectoryPickerDialog-bKqIFy8e.js → DirectoryPickerDialog-CVVg3SzI.js} +1 -1
- package/web/assets/{EnvironmentForm-kOyM5xTa.js → EnvironmentForm-eMpLFv_-.js} +1 -1
- package/web/assets/{EnvironmentFormPage-DfNI6meL.js → EnvironmentFormPage-DXH2lgk_.js} +1 -1
- package/web/assets/{EnvironmentListPage-C1VLgeeP.js → EnvironmentListPage-D-g_8lQw.js} +1 -1
- package/web/assets/{KanbanBoardPage-Bl6UnQ6P.js → KanbanBoardPage-BeSkCN6I.js} +3 -3
- package/web/assets/KanbanBoardPage-BrFqn87r.css +1 -0
- package/web/assets/{KanbanListPage-Dly6XiRC.js → KanbanListPage-DnhCC5ET.js} +1 -1
- package/web/assets/{LaunchProjectPage-DCrKSY8v.js → LaunchProjectPage-DyNqhic1.js} +1 -1
- package/web/assets/{LaunchServicesPage-Bl1pjfYH.js → LaunchServicesPage-bI7pVeU7.js} +1 -1
- package/web/assets/{LaunchShell-BcbfPDdc.js → LaunchShell-CokT4E5l.js} +1 -1
- package/web/assets/{LogViewer-C_XcH7Oe.js → LogViewer-Cs3Y8vaZ.js} +1 -1
- package/web/assets/{OrgKeyPrompt-BK6gkvaO.js → OrgKeyPrompt-DtgelhnI.js} +1 -1
- package/web/assets/{ProcessControls-BbRdiZsD.js → ProcessControls-CxlDLGiO.js} +1 -1
- package/web/assets/{ProcessDetailPage-W7S7ogas.js → ProcessDetailPage-D93cKCHb.js} +1 -1
- package/web/assets/{ProjectCreateDialog-q_39qiPd.js → ProjectCreateDialog-Bko_A5wp.js} +1 -1
- package/web/assets/{ProjectLocationGate-BFvdSy-B.js → ProjectLocationGate-jsy8WetY.js} +1 -1
- package/web/assets/{ProjectPublishDialog-DAJAvDTT.js → ProjectPublishDialog-7kR1zZAo.js} +1 -1
- package/web/assets/{RandomJobTestPage-A-I7cImN.js → RandomJobTestPage-Dp9prwa7.js} +1 -1
- package/web/assets/{ScenarioEditorPage-DXw4bCxb.js → ScenarioEditorPage-fv9yLH75.js} +1 -1
- package/web/assets/{SendToChatDialog-CbhE2WYO.js → SendToChatDialog-BVSbXcbf.js} +1 -1
- package/web/assets/{ServiceControlsBar-CudCeQql.js → ServiceControlsBar-DFECGWL8.js} +1 -1
- package/web/assets/{ServiceDetailPage-DQvL-VOx.js → ServiceDetailPage-BQA74240.js} +1 -1
- package/web/assets/{ServiceFormPage-0qXPeqrh.js → ServiceFormPage-ChsRsoLt.js} +1 -1
- package/web/assets/SettingsPage-BXKsN9Py.js +2086 -0
- package/web/assets/{SettingsPage-NpFX7FAy.css → SettingsPage-Dsuzjo8r.css} +1 -1
- package/web/assets/{UnlockPage-CtvaQt45.js → UnlockPage-DhEN16uX.js} +1 -1
- package/web/assets/{VaultMigrationPage-DtoAlng4.js → VaultMigrationPage-BgRNUuTP.js} +1 -1
- package/web/assets/{VersionBadge-CiBz82BB.js → VersionBadge-CXJ-2h5V.js} +1 -1
- package/web/assets/{WikiPage-cuKigw_5.js → WikiPage-BT5a9d5Q.js} +1 -1
- package/web/assets/{WikiView-BW-B83LB.js → WikiView-BNReLsq5.js} +1 -1
- package/web/assets/{add-scalar-classes-BPTxQDQd.js → add-scalar-classes-C_lKfiMa.js} +2 -2
- package/web/assets/{agent.store-DumNiX1X.js → agent.store-B6OM7edC.js} +1 -1
- package/web/assets/{auth-Qa1l14iV.js → auth-5bsz5kSC.js} +2 -2
- package/web/assets/{cloud-chat.store-TYR1_27Z.js → cloud-chat.store-Di38xQRJ.js} +1 -1
- package/web/assets/{cloud-client-CdOqrdP_.js → cloud-client-CEVx-bg6.js} +2 -2
- package/web/assets/{cloud-session-Dd7b9wxV.js → cloud-session-w6eNQX1X.js} +2 -2
- package/web/assets/composables-CCbqJqnd.js +1 -0
- package/web/assets/i18n-DbgjJIf8.js +1 -0
- package/web/assets/index-DB3-fmza.css +1 -0
- package/web/assets/{index-BCU69ZHl.js → index-mvpLwxNT.js} +74 -74
- package/web/assets/modal-Bslt0NZi.js +5 -0
- package/web/assets/{notification.store-D2olEA0V.js → notification.store-CAG1kWYq.js} +1 -1
- package/web/assets/{project-launch.store-MX5LzgIm.js → project-launch.store-Im5SMon9.js} +1 -1
- package/web/assets/{project.store-BFwiDQ1X.js → project.store-CITAykC3.js} +1 -1
- package/web/assets/{scenario.store-C8kW-yTN.js → scenario.store-C9sSoRzb.js} +2 -2
- package/web/assets/{service.store-B0Ass90H.js → service.store-CZJuuJiv.js} +1 -1
- package/web/assets/{settings-scope-D0hzzuyq.js → settings-scope-vcJEMNnD.js} +1 -1
- package/web/assets/{settings.store-B9Kp0l1G.js → settings.store-CydW_2jh.js} +1 -1
- package/web/assets/{src-DnjsM9el.js → src-CIOvFTGI.js} +23 -23
- package/web/assets/{src-DITmhvGS.js → src-COZZWm-m.js} +1 -1
- package/web/assets/src-DrVR8mhy.css +1 -0
- package/web/assets/{trpc-BvrR5qP9.js → trpc-BQ0mLnfI.js} +3 -3
- package/web/assets/{useCloudOrg-2vv5fVV6.js → useCloudOrg-CQvEbrZv.js} +1 -1
- package/web/assets/{useEnvVarAutocomplete-VVX3MC62.js → useEnvVarAutocomplete-hy3rO3SN.js} +1 -1
- package/web/assets/{useProjectCatalog-DsVvtBzo.js → useProjectCatalog-CWHpf3Yj.js} +1 -1
- package/web/assets/{useProjectCloudOrg-wv-RfsmO.js → useProjectCloudOrg-B3rNPes5.js} +1 -1
- package/web/assets/{useServiceUrlHost-C4Evrede.js → useServiceUrlHost-BFfie_6h.js} +1 -1
- package/web/index.html +17 -17
- package/chunk-EUTW2S4P.js.map +0 -1
- package/chunk-QJSIN26R.js.map +0 -1
- package/mcp-server/external.d.ts +0 -44
- package/mcp-server/index.d.ts +0 -1
- package/mcp-server/launch.d.ts +0 -89
- package/src-NWPED6TK.js.map +0 -1
- package/web/assets/AiChatModals-BmWxg-Sr.css +0 -1
- package/web/assets/AiChatModals-BsfKdbEA.js +0 -4
- package/web/assets/AppLayout-Djk3-XbN.css +0 -1
- package/web/assets/AppLayout-f0_BsXSc.js +0 -1
- package/web/assets/KanbanBoardPage-Cmu4Je4N.css +0 -1
- package/web/assets/SettingsPage-Bjn4bKPP.js +0 -2086
- package/web/assets/composables-DguCUR3O.js +0 -1
- package/web/assets/i18n-DipCGVKv.js +0 -1
- package/web/assets/index-Ck_3m_I9.css +0 -1
- package/web/assets/modal-BgrgFhyC.js +0 -5
- package/web/assets/src-CfDz-9Q6.css +0 -1
- /package/{chat-Z7SFBEXS.js.map → chat-COP27XB3.js.map} +0 -0
- /package/{companion-ZDEGNZ2G.js.map → companion-3MW23ONE.js.map} +0 -0
- /package/{install-gate-TSCXUCYE.js.map → install-gate-3UVXPA57.js.map} +0 -0
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
syncConversationFromClaudeTranscript,
|
|
56
56
|
verifyJwt,
|
|
57
57
|
waitForPtyInput
|
|
58
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-OLHL5NFP.js";
|
|
59
59
|
import {
|
|
60
60
|
conversationSectionStore,
|
|
61
61
|
projectStore
|
|
@@ -134,7 +134,7 @@ import { z as z6 } from "zod";
|
|
|
134
134
|
import Anthropic2 from "@anthropic-ai/sdk";
|
|
135
135
|
import OpenAI2 from "openai";
|
|
136
136
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
137
|
-
import { randomUUID as
|
|
137
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
138
138
|
|
|
139
139
|
// ../../packages/mcp-server/src/launch.ts
|
|
140
140
|
import { fileURLToPath } from "url";
|
|
@@ -1557,6 +1557,105 @@ var serviceRouter = router({
|
|
|
1557
1557
|
})
|
|
1558
1558
|
});
|
|
1559
1559
|
|
|
1560
|
+
// ../server/src/services/conversation-secret-env-store.ts
|
|
1561
|
+
import { mkdir as mkdir2, readFile as readFile3, rm } from "fs/promises";
|
|
1562
|
+
import { dirname } from "path";
|
|
1563
|
+
var DIRNAME = "conversation-secret-env";
|
|
1564
|
+
var SECRET_ENV_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
1565
|
+
var SECRET_ENV_MAX_ENTRIES = 50;
|
|
1566
|
+
var SECRET_ENV_MAX_KEY_LENGTH = 128;
|
|
1567
|
+
var SECRET_ENV_MAX_VALUE_LENGTH = 8192;
|
|
1568
|
+
var CONVERSATION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
|
|
1569
|
+
function dict(entries = []) {
|
|
1570
|
+
const out = /* @__PURE__ */ Object.create(null);
|
|
1571
|
+
for (const [k, v] of entries) out[k] = v;
|
|
1572
|
+
return out;
|
|
1573
|
+
}
|
|
1574
|
+
function isSecretFile(value) {
|
|
1575
|
+
return !!value && typeof value === "object" && !Array.isArray(value) && Object.values(value).every((v) => typeof v === "string");
|
|
1576
|
+
}
|
|
1577
|
+
var ConversationSecretEnvStore = class {
|
|
1578
|
+
/** File en mémoire : sérialise les écritures de ce process. */
|
|
1579
|
+
queue = Promise.resolve();
|
|
1580
|
+
pathOf(conversationId) {
|
|
1581
|
+
if (!CONVERSATION_ID_PATTERN.test(conversationId)) {
|
|
1582
|
+
throw new Error("Identifiant de conversation invalide");
|
|
1583
|
+
}
|
|
1584
|
+
return machineFilePath(`${DIRNAME}/${conversationId}.json`);
|
|
1585
|
+
}
|
|
1586
|
+
/**
|
|
1587
|
+
* Seul `ENOENT` veut dire « rien d'enregistré ». Toute autre erreur (lecture
|
|
1588
|
+
* impossible, contenu invalide) remonte : la rendre en `{}` ferait écraser
|
|
1589
|
+
* le fichier à l'écriture suivante.
|
|
1590
|
+
*/
|
|
1591
|
+
async read(conversationId) {
|
|
1592
|
+
let raw;
|
|
1593
|
+
try {
|
|
1594
|
+
raw = await readFile3(this.pathOf(conversationId), "utf-8");
|
|
1595
|
+
} catch (err) {
|
|
1596
|
+
if (err.code === "ENOENT") return {};
|
|
1597
|
+
throw err;
|
|
1598
|
+
}
|
|
1599
|
+
const parsed = JSON.parse(raw);
|
|
1600
|
+
if (!isSecretFile(parsed)) throw new Error(`${DIRNAME}/${conversationId}.json : contenu inattendu, \xE9criture refus\xE9e`);
|
|
1601
|
+
return dict(Object.entries(parsed));
|
|
1602
|
+
}
|
|
1603
|
+
serialize(fn) {
|
|
1604
|
+
const next = this.queue.then(fn, fn);
|
|
1605
|
+
this.queue = next.catch(() => void 0);
|
|
1606
|
+
return next;
|
|
1607
|
+
}
|
|
1608
|
+
/** Les noms enregistrés pour une conversation, jamais les valeurs. */
|
|
1609
|
+
async keys(conversationId) {
|
|
1610
|
+
return Object.keys(await this.read(conversationId));
|
|
1611
|
+
}
|
|
1612
|
+
/** Nom → valeur en clair, prêt à entrer dans l'environnement d'un process. */
|
|
1613
|
+
async resolve(conversationId) {
|
|
1614
|
+
const stored = await this.read(conversationId);
|
|
1615
|
+
const result = dict();
|
|
1616
|
+
for (const [key, encrypted] of Object.entries(stored)) {
|
|
1617
|
+
try {
|
|
1618
|
+
result[key] = decryptValue(encrypted);
|
|
1619
|
+
} catch (err) {
|
|
1620
|
+
console.warn(`[conversation-secret-env] ${key} illisible pour ${conversationId} :`, err.message);
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
return result;
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Remplace l'ensemble des variables de la conversation. Un nom absent de
|
|
1627
|
+
* `entries` est supprimé ; une ligne sans valeur garde celle de la base.
|
|
1628
|
+
*/
|
|
1629
|
+
replace(conversationId, entries) {
|
|
1630
|
+
return this.serialize(async () => {
|
|
1631
|
+
const previous = await this.read(conversationId);
|
|
1632
|
+
const next = dict();
|
|
1633
|
+
for (const { key, value } of entries) {
|
|
1634
|
+
if (value !== void 0 && value !== "") {
|
|
1635
|
+
next[key] = encryptValue(value);
|
|
1636
|
+
} else if (Object.hasOwn(previous, key)) {
|
|
1637
|
+
next[key] = previous[key];
|
|
1638
|
+
} else {
|
|
1639
|
+
throw new Error(`Aucune valeur pour ${key}`);
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
const path = this.pathOf(conversationId);
|
|
1643
|
+
if (Object.keys(next).length === 0) {
|
|
1644
|
+
await rm(path, { force: true });
|
|
1645
|
+
} else {
|
|
1646
|
+
await mkdir2(dirname(path), { recursive: true });
|
|
1647
|
+
await atomicWriteSecure(path, JSON.stringify(next, null, 2));
|
|
1648
|
+
}
|
|
1649
|
+
return Object.keys(next);
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1652
|
+
/** La conversation disparaît : ses secrets partent avec elle. */
|
|
1653
|
+
remove(conversationId) {
|
|
1654
|
+
return this.serialize(() => rm(this.pathOf(conversationId), { force: true }));
|
|
1655
|
+
}
|
|
1656
|
+
};
|
|
1657
|
+
var conversationSecretEnvStore = new ConversationSecretEnvStore();
|
|
1658
|
+
|
|
1560
1659
|
// ../server/src/services/scenario-store.ts
|
|
1561
1660
|
function normalizeNode(raw) {
|
|
1562
1661
|
const aiCli = raw.aiCli ?? "claude-code";
|
|
@@ -1718,7 +1817,7 @@ var ScenarioStore = class {
|
|
|
1718
1817
|
var scenarioStore = new ScenarioStore();
|
|
1719
1818
|
|
|
1720
1819
|
// ../server/src/services/scenario-run-store.ts
|
|
1721
|
-
import { mkdir as
|
|
1820
|
+
import { mkdir as mkdir3, rm as rm2, readFile as readFile4 } from "fs/promises";
|
|
1722
1821
|
import { join as join3 } from "path";
|
|
1723
1822
|
function sanitizeSlug(value) {
|
|
1724
1823
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) || "member";
|
|
@@ -1735,7 +1834,7 @@ var ScenarioRunStore = class _ScenarioRunStore {
|
|
|
1735
1834
|
}
|
|
1736
1835
|
async initializeConversationWorkspace(conversationId, scenario, userInput) {
|
|
1737
1836
|
const dir = this.getRunDir(conversationId);
|
|
1738
|
-
await
|
|
1837
|
+
await mkdir3(dir, { recursive: true });
|
|
1739
1838
|
await this.saveScenarioSnapshot(conversationId, scenario);
|
|
1740
1839
|
const nodesById = new Map(scenario.nodes.map((p) => [p.id, p]));
|
|
1741
1840
|
const teamMap = new Map(scenario.groups.map((t2) => [t2.id, t2.name]));
|
|
@@ -1791,7 +1890,7 @@ ${userInput.trim() || "(empty)"}
|
|
|
1791
1890
|
}
|
|
1792
1891
|
}
|
|
1793
1892
|
async deleteRunDir(conversationId) {
|
|
1794
|
-
await
|
|
1893
|
+
await rm2(this.getRunDir(conversationId), { recursive: true, force: true });
|
|
1795
1894
|
this.activeRuns.delete(conversationId);
|
|
1796
1895
|
}
|
|
1797
1896
|
ensureRunState(conversationId) {
|
|
@@ -1847,7 +1946,7 @@ ${userInput.trim() || "(empty)"}
|
|
|
1847
1946
|
if (!runState) return;
|
|
1848
1947
|
try {
|
|
1849
1948
|
const dir = this.getRunDir(conversationId);
|
|
1850
|
-
await
|
|
1949
|
+
await mkdir3(dir, { recursive: true });
|
|
1851
1950
|
const data = {
|
|
1852
1951
|
nodeStates: { ...runState.nodeStates },
|
|
1853
1952
|
nodeOutputs: { ...runState.nodeOutputs },
|
|
@@ -1866,7 +1965,7 @@ ${userInput.trim() || "(empty)"}
|
|
|
1866
1965
|
async readUserRequest(conversationId) {
|
|
1867
1966
|
try {
|
|
1868
1967
|
const dir = this.getRunDir(conversationId);
|
|
1869
|
-
const content = await
|
|
1968
|
+
const content = await readFile4(join3(dir, "USER_REQUEST.md"), "utf-8");
|
|
1870
1969
|
return content.replace(/^#\s*User request\s*\n+/, "").trim() || null;
|
|
1871
1970
|
} catch {
|
|
1872
1971
|
return null;
|
|
@@ -1875,14 +1974,14 @@ ${userInput.trim() || "(empty)"}
|
|
|
1875
1974
|
async readReadme(conversationId) {
|
|
1876
1975
|
try {
|
|
1877
1976
|
const dir = this.getRunDir(conversationId);
|
|
1878
|
-
return await
|
|
1977
|
+
return await readFile4(join3(dir, "README.md"), "utf-8");
|
|
1879
1978
|
} catch {
|
|
1880
1979
|
return null;
|
|
1881
1980
|
}
|
|
1882
1981
|
}
|
|
1883
1982
|
async saveScenarioSnapshot(conversationId, scenario) {
|
|
1884
1983
|
const dir = this.getRunDir(conversationId);
|
|
1885
|
-
await
|
|
1984
|
+
await mkdir3(dir, { recursive: true });
|
|
1886
1985
|
await atomicWriteJsonSecure(
|
|
1887
1986
|
join3(dir, _ScenarioRunStore.SCENARIO_SNAPSHOT_FILE),
|
|
1888
1987
|
scenario,
|
|
@@ -1899,12 +1998,17 @@ var scenarioRunStore = new ScenarioRunStore();
|
|
|
1899
1998
|
// ../server/src/services/ai-provider-utils.ts
|
|
1900
1999
|
import Anthropic from "@anthropic-ai/sdk";
|
|
1901
2000
|
import OpenAI from "openai";
|
|
2001
|
+
var OLLAMA_BASE_URL = "http://localhost:11434";
|
|
1902
2002
|
var OPENAI_COMPAT_BASE_URLS = {
|
|
1903
2003
|
openai: "https://api.openai.com/v1",
|
|
1904
2004
|
groq: "https://api.groq.com/openai/v1",
|
|
1905
2005
|
mistral: "https://api.mistral.ai/v1",
|
|
1906
|
-
ollama:
|
|
2006
|
+
ollama: `${OLLAMA_BASE_URL}/v1`
|
|
1907
2007
|
};
|
|
2008
|
+
function truncationError(finishReason) {
|
|
2009
|
+
if (finishReason !== "length") return null;
|
|
2010
|
+
return new Error("The response was cut off: the model reached its context or output token limit (finish_reason: length).");
|
|
2011
|
+
}
|
|
1908
2012
|
var DEFAULT_MODELS = {
|
|
1909
2013
|
anthropic: "claude-opus-4-6",
|
|
1910
2014
|
openai: "gpt-4o",
|
|
@@ -1935,6 +2039,159 @@ async function fetchProviderModels(provider, plaintextKey) {
|
|
|
1935
2039
|
return [];
|
|
1936
2040
|
}
|
|
1937
2041
|
|
|
2042
|
+
// ../server/src/services/ollama-chat.ts
|
|
2043
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
2044
|
+
var OLLAMA_RESPONSE_MARGIN = 8192;
|
|
2045
|
+
var CHARS_PER_TOKEN = 4;
|
|
2046
|
+
var SHOW_TIMEOUT_MS = 5e3;
|
|
2047
|
+
var CHAT_TIMEOUT_MS = 10 * 6e4;
|
|
2048
|
+
var contextLimits = /* @__PURE__ */ new Map();
|
|
2049
|
+
async function modelContextLimit(model) {
|
|
2050
|
+
if (contextLimits.has(model)) return contextLimits.get(model);
|
|
2051
|
+
let limit = null;
|
|
2052
|
+
try {
|
|
2053
|
+
const res = await fetch(`${OLLAMA_BASE_URL}/api/show`, {
|
|
2054
|
+
method: "POST",
|
|
2055
|
+
headers: { "Content-Type": "application/json" },
|
|
2056
|
+
body: JSON.stringify({ model }),
|
|
2057
|
+
signal: AbortSignal.timeout(SHOW_TIMEOUT_MS)
|
|
2058
|
+
});
|
|
2059
|
+
if (res.ok) {
|
|
2060
|
+
const body = await res.json();
|
|
2061
|
+
for (const [key, value] of Object.entries(body.model_info ?? {})) {
|
|
2062
|
+
if (key.endsWith(".context_length") && typeof value === "number" && value > 0) limit = value;
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
} catch {
|
|
2066
|
+
}
|
|
2067
|
+
if (limit !== null) contextLimits.set(model, limit);
|
|
2068
|
+
return limit;
|
|
2069
|
+
}
|
|
2070
|
+
function computeNumCtx(promptChars, modelLimit) {
|
|
2071
|
+
const wanted = Math.ceil(promptChars / CHARS_PER_TOKEN) + OLLAMA_RESPONSE_MARGIN;
|
|
2072
|
+
return modelLimit ? Math.min(wanted, modelLimit) : wanted;
|
|
2073
|
+
}
|
|
2074
|
+
function textOf(content) {
|
|
2075
|
+
if (typeof content === "string") return { text: content, images: [] };
|
|
2076
|
+
const images = [];
|
|
2077
|
+
const texts = [];
|
|
2078
|
+
for (const part of content ?? []) {
|
|
2079
|
+
if (part.type === "text") texts.push(part.text);
|
|
2080
|
+
else if (part.type === "image_url") {
|
|
2081
|
+
const base64 = part.image_url.url.split(",", 2)[1];
|
|
2082
|
+
if (base64) images.push(base64);
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
return { text: texts.join("\n"), images };
|
|
2086
|
+
}
|
|
2087
|
+
function toOllamaMessages(messages) {
|
|
2088
|
+
const toolNames = /* @__PURE__ */ new Map();
|
|
2089
|
+
return messages.map((m) => {
|
|
2090
|
+
if (m.role === "tool") {
|
|
2091
|
+
return { role: "tool", content: textOf(m.content).text, tool_name: toolNames.get(m.tool_call_id) };
|
|
2092
|
+
}
|
|
2093
|
+
if (m.role === "assistant") {
|
|
2094
|
+
const toolCalls = (m.tool_calls ?? []).flatMap((tc) => {
|
|
2095
|
+
if (tc.type !== "function") return [];
|
|
2096
|
+
toolNames.set(tc.id, tc.function.name);
|
|
2097
|
+
let args = {};
|
|
2098
|
+
try {
|
|
2099
|
+
args = JSON.parse(tc.function.arguments || "{}");
|
|
2100
|
+
} catch {
|
|
2101
|
+
}
|
|
2102
|
+
return [{ function: { name: tc.function.name, arguments: args } }];
|
|
2103
|
+
});
|
|
2104
|
+
const msg2 = { role: "assistant", content: textOf(m.content ?? "").text };
|
|
2105
|
+
if (toolCalls.length > 0) msg2.tool_calls = toolCalls;
|
|
2106
|
+
return msg2;
|
|
2107
|
+
}
|
|
2108
|
+
const { text, images } = textOf(m.content);
|
|
2109
|
+
const role = m.role === "developer" ? "system" : m.role;
|
|
2110
|
+
const msg = { role, content: text };
|
|
2111
|
+
if (images.length > 0) msg.images = images;
|
|
2112
|
+
return msg;
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
function chunk(delta, finishReason = null) {
|
|
2116
|
+
return {
|
|
2117
|
+
id: "",
|
|
2118
|
+
object: "chat.completion.chunk",
|
|
2119
|
+
created: 0,
|
|
2120
|
+
model: "",
|
|
2121
|
+
choices: [{ index: 0, delta, finish_reason: finishReason }]
|
|
2122
|
+
};
|
|
2123
|
+
}
|
|
2124
|
+
async function* streamOllamaChat(params) {
|
|
2125
|
+
const messages = toOllamaMessages(params.messages);
|
|
2126
|
+
const promptChars = JSON.stringify(messages).length + JSON.stringify(params.tools).length;
|
|
2127
|
+
const numCtx = computeNumCtx(promptChars, await modelContextLimit(params.model));
|
|
2128
|
+
const timeout = AbortSignal.timeout(CHAT_TIMEOUT_MS);
|
|
2129
|
+
const res = await fetch(`${OLLAMA_BASE_URL}/api/chat`, {
|
|
2130
|
+
method: "POST",
|
|
2131
|
+
headers: { "Content-Type": "application/json" },
|
|
2132
|
+
body: JSON.stringify({
|
|
2133
|
+
model: params.model,
|
|
2134
|
+
messages,
|
|
2135
|
+
tools: params.tools,
|
|
2136
|
+
stream: true,
|
|
2137
|
+
options: { num_ctx: numCtx, temperature: params.temperature ?? 0 }
|
|
2138
|
+
}),
|
|
2139
|
+
signal: params.signal ? AbortSignal.any([params.signal, timeout]) : timeout
|
|
2140
|
+
});
|
|
2141
|
+
if (!res.ok || !res.body) {
|
|
2142
|
+
throw new Error(`Ollama error ${res.status}: ${(await res.text().catch(() => "")).slice(0, 500)}`);
|
|
2143
|
+
}
|
|
2144
|
+
const decoder = new TextDecoder();
|
|
2145
|
+
let buffer = "";
|
|
2146
|
+
let toolIndex = 0;
|
|
2147
|
+
const handle = function* (line) {
|
|
2148
|
+
if (!line.trim()) return;
|
|
2149
|
+
const data = JSON.parse(line);
|
|
2150
|
+
if (data.error) throw new Error(`Ollama error: ${data.error}`);
|
|
2151
|
+
const content = data.message?.content;
|
|
2152
|
+
if (content) yield chunk({ content });
|
|
2153
|
+
for (const tc of data.message?.tool_calls ?? []) {
|
|
2154
|
+
yield chunk({
|
|
2155
|
+
tool_calls: [{
|
|
2156
|
+
index: toolIndex++,
|
|
2157
|
+
id: `call_${randomUUID3()}`,
|
|
2158
|
+
type: "function",
|
|
2159
|
+
function: { name: tc.function.name, arguments: JSON.stringify(tc.function.arguments ?? {}) }
|
|
2160
|
+
}]
|
|
2161
|
+
});
|
|
2162
|
+
}
|
|
2163
|
+
if (data.done) yield chunk({}, data.done_reason === "length" ? "length" : "stop");
|
|
2164
|
+
};
|
|
2165
|
+
for await (const bytes of res.body) {
|
|
2166
|
+
buffer += decoder.decode(bytes, { stream: true });
|
|
2167
|
+
let nl;
|
|
2168
|
+
while ((nl = buffer.indexOf("\n")) !== -1) {
|
|
2169
|
+
const line = buffer.slice(0, nl);
|
|
2170
|
+
buffer = buffer.slice(nl + 1);
|
|
2171
|
+
yield* handle(line);
|
|
2172
|
+
}
|
|
2173
|
+
}
|
|
2174
|
+
yield* handle(buffer + decoder.decode());
|
|
2175
|
+
}
|
|
2176
|
+
var OLLAMA_MODEL_PREFIX = "ollama:";
|
|
2177
|
+
async function listOllamaModels() {
|
|
2178
|
+
try {
|
|
2179
|
+
const res = await fetch(`${OLLAMA_BASE_URL}/api/tags`, { signal: AbortSignal.timeout(SHOW_TIMEOUT_MS) });
|
|
2180
|
+
if (!res.ok) return [];
|
|
2181
|
+
const body = await res.json();
|
|
2182
|
+
return (body.models ?? []).flatMap((m) => typeof m.name === "string" ? [m.name] : []);
|
|
2183
|
+
} catch {
|
|
2184
|
+
return [];
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
function claudeOllamaTarget(model) {
|
|
2188
|
+
if (!model?.startsWith(OLLAMA_MODEL_PREFIX)) return null;
|
|
2189
|
+
return {
|
|
2190
|
+
model: model.slice(OLLAMA_MODEL_PREFIX.length),
|
|
2191
|
+
env: { ANTHROPIC_BASE_URL: OLLAMA_BASE_URL, ANTHROPIC_AUTH_TOKEN: "ollama", ANTHROPIC_API_KEY: "" }
|
|
2192
|
+
};
|
|
2193
|
+
}
|
|
2194
|
+
|
|
1938
2195
|
// ../server/src/services/settings-scope.ts
|
|
1939
2196
|
async function vaultOfProject(projectId) {
|
|
1940
2197
|
if (!projectId) return void 0;
|
|
@@ -1945,8 +2202,8 @@ async function vaultOfProject(projectId) {
|
|
|
1945
2202
|
}
|
|
1946
2203
|
|
|
1947
2204
|
// ../server/src/services/image-storage.service.ts
|
|
1948
|
-
import { randomUUID as
|
|
1949
|
-
import { mkdir as
|
|
2205
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
2206
|
+
import { mkdir as mkdir4, writeFile, readFile as readFile5, unlink as unlink2, readdir as readdir3 } from "fs/promises";
|
|
1950
2207
|
import { existsSync as existsSync2 } from "fs";
|
|
1951
2208
|
import { join as join4, extname, resolve, sep } from "path";
|
|
1952
2209
|
var ALLOWED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
@@ -1965,7 +2222,7 @@ var ImageStorageService = class {
|
|
|
1965
2222
|
this.dir = dir;
|
|
1966
2223
|
}
|
|
1967
2224
|
async ensureTmpDir() {
|
|
1968
|
-
await
|
|
2225
|
+
await mkdir4(this.dir, { recursive: true, mode: 448 });
|
|
1969
2226
|
}
|
|
1970
2227
|
async saveImage(buffer, originalName, mimeType, contextId) {
|
|
1971
2228
|
if (!ALLOWED_MIME_TYPES.has(mimeType)) {
|
|
@@ -1982,7 +2239,7 @@ var ImageStorageService = class {
|
|
|
1982
2239
|
throw new Error(`Unsupported extension: ${ext}`);
|
|
1983
2240
|
}
|
|
1984
2241
|
await this.ensureTmpDir();
|
|
1985
|
-
const imageId =
|
|
2242
|
+
const imageId = randomUUID4();
|
|
1986
2243
|
const safeExt = ext.replace(/[^a-z.]/g, "");
|
|
1987
2244
|
const fileName = `${imageId}${safeExt}`;
|
|
1988
2245
|
const filePath = join4(this.dir, fileName);
|
|
@@ -2011,7 +2268,7 @@ var ImageStorageService = class {
|
|
|
2011
2268
|
const files = await readdir3(this.dir);
|
|
2012
2269
|
const metaFile = files.find((f) => f.startsWith(imageId) && f.endsWith(".meta.json"));
|
|
2013
2270
|
if (!metaFile) return null;
|
|
2014
|
-
const raw = JSON.parse(await
|
|
2271
|
+
const raw = JSON.parse(await readFile5(join4(this.dir, metaFile), "utf-8"));
|
|
2015
2272
|
if (expectedContextId !== void 0 && raw.contextId !== expectedContextId) return null;
|
|
2016
2273
|
if (!existsSync2(raw.filePath)) return null;
|
|
2017
2274
|
return { filePath: raw.filePath, mimeType: raw.mimeType, contextId: raw.contextId };
|
|
@@ -2037,7 +2294,7 @@ var ImageStorageService = class {
|
|
|
2037
2294
|
await Promise.all(
|
|
2038
2295
|
files.filter((f) => f.endsWith(".meta.json")).map(async (file) => {
|
|
2039
2296
|
try {
|
|
2040
|
-
const raw = JSON.parse(await
|
|
2297
|
+
const raw = JSON.parse(await readFile5(join4(this.dir, file), "utf-8"));
|
|
2041
2298
|
if (raw.contextId === contextId) {
|
|
2042
2299
|
await unlink2(raw.filePath).catch(() => {
|
|
2043
2300
|
});
|
|
@@ -2052,7 +2309,7 @@ var ImageStorageService = class {
|
|
|
2052
2309
|
async readImageAsBase64(imageId, expectedContextId) {
|
|
2053
2310
|
const result = await this.getImagePath(imageId, expectedContextId);
|
|
2054
2311
|
if (!result) return null;
|
|
2055
|
-
const buffer = await
|
|
2312
|
+
const buffer = await readFile5(result.filePath);
|
|
2056
2313
|
return { base64: buffer.toString("base64"), mimeType: result.mimeType };
|
|
2057
2314
|
}
|
|
2058
2315
|
async listExpiredImages(ttlMs = IMAGE_TTL_MS) {
|
|
@@ -2067,7 +2324,7 @@ var ImageStorageService = class {
|
|
|
2067
2324
|
for (const file of files) {
|
|
2068
2325
|
if (!file.endsWith(".meta.json")) continue;
|
|
2069
2326
|
try {
|
|
2070
|
-
const raw = JSON.parse(await
|
|
2327
|
+
const raw = JSON.parse(await readFile5(join4(this.dir, file), "utf-8"));
|
|
2071
2328
|
if (now - raw.createdAt > ttlMs) expired.push(raw);
|
|
2072
2329
|
} catch {
|
|
2073
2330
|
}
|
|
@@ -2136,8 +2393,8 @@ import { basename } from "path";
|
|
|
2136
2393
|
import { TRPCError as TRPCError3 } from "@trpc/server";
|
|
2137
2394
|
|
|
2138
2395
|
// ../server/src/services/cloud-session-store.ts
|
|
2139
|
-
import { readFile as
|
|
2140
|
-
import { dirname } from "path";
|
|
2396
|
+
import { readFile as readFile6, writeFile as writeFile2, mkdir as mkdir5, chmod, unlink as unlink3 } from "fs/promises";
|
|
2397
|
+
import { dirname as dirname2 } from "path";
|
|
2141
2398
|
import { randomBytes as randomBytes2, createHash, timingSafeEqual as timingSafeEqual2 } from "crypto";
|
|
2142
2399
|
var FILENAME = "cloud-session.json";
|
|
2143
2400
|
var STATE_TTL_MS = 5 * 60 * 1e3;
|
|
@@ -2168,9 +2425,9 @@ var CloudSessionStore = class {
|
|
|
2168
2425
|
*/
|
|
2169
2426
|
async readFromDisk() {
|
|
2170
2427
|
try {
|
|
2171
|
-
return await
|
|
2428
|
+
return await readFile6(await this.getFilePath(), "utf-8");
|
|
2172
2429
|
} catch {
|
|
2173
|
-
return
|
|
2430
|
+
return readFile6(this.getLegacyFilePath(), "utf-8");
|
|
2174
2431
|
}
|
|
2175
2432
|
}
|
|
2176
2433
|
/**
|
|
@@ -2244,7 +2501,7 @@ var CloudSessionStore = class {
|
|
|
2244
2501
|
}
|
|
2245
2502
|
async save() {
|
|
2246
2503
|
const filePath = await this.getFilePath();
|
|
2247
|
-
await
|
|
2504
|
+
await mkdir5(dirname2(filePath), { recursive: true });
|
|
2248
2505
|
const payload = { version: 2, accounts: this.accounts };
|
|
2249
2506
|
const tmpPath = filePath + ".tmp";
|
|
2250
2507
|
await writeFile2(tmpPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
@@ -2385,7 +2642,7 @@ var cloudSessionStore = new CloudSessionStore();
|
|
|
2385
2642
|
|
|
2386
2643
|
// ../server/src/services/lan-mode.ts
|
|
2387
2644
|
import { networkInterfaces } from "os";
|
|
2388
|
-
import { readFile as
|
|
2645
|
+
import { readFile as readFile7, writeFile as writeFile3, mkdir as mkdir6 } from "fs/promises";
|
|
2389
2646
|
import { join as join5 } from "path";
|
|
2390
2647
|
import { z as z4 } from "zod";
|
|
2391
2648
|
var LOOPBACK2 = "127.0.0.1";
|
|
@@ -2403,7 +2660,7 @@ function networkFilePath() {
|
|
|
2403
2660
|
}
|
|
2404
2661
|
async function readFileState() {
|
|
2405
2662
|
try {
|
|
2406
|
-
return NetworkFileSchema.parse(JSON.parse(await
|
|
2663
|
+
return NetworkFileSchema.parse(JSON.parse(await readFile7(networkFilePath(), "utf-8")));
|
|
2407
2664
|
} catch {
|
|
2408
2665
|
return { instances: {} };
|
|
2409
2666
|
}
|
|
@@ -2414,7 +2671,7 @@ async function readPersisted() {
|
|
|
2414
2671
|
}
|
|
2415
2672
|
async function writePersisted(lanEnabled) {
|
|
2416
2673
|
const root = machineRoot();
|
|
2417
|
-
await
|
|
2674
|
+
await mkdir6(root, { recursive: true });
|
|
2418
2675
|
const state = await readFileState();
|
|
2419
2676
|
state.instances[instanceKey()] = { lanEnabled };
|
|
2420
2677
|
const filePath = networkFilePath();
|
|
@@ -2932,8 +3189,17 @@ async function resolveOrganizationId(explicit) {
|
|
|
2932
3189
|
if (explicit) return explicit;
|
|
2933
3190
|
const accounts = await cloudSessionStore.list();
|
|
2934
3191
|
if (accounts.length > 1) {
|
|
3192
|
+
const perAccount = await Promise.all(accounts.map(async (account) => {
|
|
3193
|
+
const res = await callCloud(
|
|
3194
|
+
"/organization/list",
|
|
3195
|
+
{ accountId: account.id }
|
|
3196
|
+
);
|
|
3197
|
+
return { account, orgs: res.ok ? res.data ?? [] : [], failure: res.ok ? null : res.reason };
|
|
3198
|
+
}));
|
|
3199
|
+
const listed2 = perAccount.flatMap((r) => r.orgs).map((o) => `${o.name ?? "(sans nom)"} \u2014 ${o.id}`).join(", ");
|
|
3200
|
+
const failures = perAccount.filter((r) => r.failure).map((r) => `${r.account.email} (${r.failure})`);
|
|
2935
3201
|
throw new KanbanUnavailableError(
|
|
2936
|
-
`Ce poste est li\xE9 \xE0 ${accounts.length} comptes cloud :
|
|
3202
|
+
`Ce poste est li\xE9 \xE0 ${accounts.length} comptes cloud : relancez l'appel avec organizationId` + (listed2 ? `, \xE0 choisir parmi : ${listed2}.` : " (celui de la carte ou du tableau concern\xE9).") + (failures.length ? ` Liste ${listed2 ? "partielle" : "indisponible"}, organisations illisibles pour : ${failures.join(" ; ")}.` : "")
|
|
2937
3203
|
);
|
|
2938
3204
|
}
|
|
2939
3205
|
const accountId = await soleAccountScope();
|
|
@@ -3559,7 +3825,7 @@ Ne jamais omettre ce tag. Il doit \xEAtre la toute derni\xE8re ligne de ta r\xE9
|
|
|
3559
3825
|
`.trim();
|
|
3560
3826
|
|
|
3561
3827
|
// ../server/src/services/ai-runners/claude-pty-runner.ts
|
|
3562
|
-
import { randomUUID as
|
|
3828
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
3563
3829
|
|
|
3564
3830
|
// ../server/src/services/ai-runners/claude-pty-tail.ts
|
|
3565
3831
|
import { open } from "fs/promises";
|
|
@@ -3759,9 +4025,9 @@ function dispatchEntry(entry, state, cbs) {
|
|
|
3759
4025
|
async function runClaudeViaPty(params) {
|
|
3760
4026
|
const { agentId, signal, skipTokenInstructions } = params;
|
|
3761
4027
|
if (!agentId) throw new Error("runClaudeViaPty requires an agentId");
|
|
3762
|
-
const sessionId = params.sessionId && params.sessionId.length > 0 ? params.sessionId :
|
|
4028
|
+
const sessionId = params.sessionId && params.sessionId.length > 0 ? params.sessionId : randomUUID5();
|
|
3763
4029
|
const deferPrompt = !!params.mcpConfig;
|
|
3764
|
-
const mcpReadyId = deferPrompt ?
|
|
4030
|
+
const mcpReadyId = deferPrompt ? randomUUID5() : void 0;
|
|
3765
4031
|
const args = buildPtyArgs(params, sessionId, { excludeMcp: true, deferPrompt });
|
|
3766
4032
|
const agentMgr = await getAgentManager();
|
|
3767
4033
|
const displayArgs = args.map((a) => {
|
|
@@ -4166,14 +4432,14 @@ var ClaudeCodeRunner = class {
|
|
|
4166
4432
|
};
|
|
4167
4433
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
4168
4434
|
let stderrBuffer = "";
|
|
4169
|
-
proc.stdout.on("data", (
|
|
4170
|
-
stdoutBuffer +=
|
|
4435
|
+
proc.stdout.on("data", (chunk2) => {
|
|
4436
|
+
stdoutBuffer += chunk2.toString("utf-8");
|
|
4171
4437
|
const lines = stdoutBuffer.split("\n");
|
|
4172
4438
|
stdoutBuffer = lines.pop() ?? "";
|
|
4173
4439
|
processClaudeStdoutLines(lines, streamState, params, ctx);
|
|
4174
4440
|
});
|
|
4175
|
-
proc.stderr.on("data", (
|
|
4176
|
-
stderrBuffer +=
|
|
4441
|
+
proc.stderr.on("data", (chunk2) => {
|
|
4442
|
+
stderrBuffer += chunk2.toString("utf-8");
|
|
4177
4443
|
});
|
|
4178
4444
|
proc.on("close", (code) => {
|
|
4179
4445
|
if (ctx.earlyResult) {
|
|
@@ -4827,6 +5093,38 @@ var CodexRunner = class {
|
|
|
4827
5093
|
}
|
|
4828
5094
|
};
|
|
4829
5095
|
|
|
5096
|
+
// ../server/src/services/pty-session-stopper.ts
|
|
5097
|
+
var POLL_MS = 50;
|
|
5098
|
+
var EXIT_TIMEOUT_MS = 5e3;
|
|
5099
|
+
async function stopPtysOf(sessionId) {
|
|
5100
|
+
const results = await Promise.allSettled(agentManager.listAgents().map(async (agent) => {
|
|
5101
|
+
const agentId = agent.config.id;
|
|
5102
|
+
const query = () => agentManager.agentQuery(agentId, "ai.findPtyBySession", { sessionId });
|
|
5103
|
+
let pty;
|
|
5104
|
+
try {
|
|
5105
|
+
pty = await query();
|
|
5106
|
+
} catch {
|
|
5107
|
+
return;
|
|
5108
|
+
}
|
|
5109
|
+
if (pty?.status !== "running") return;
|
|
5110
|
+
await agentManager.agentMutation(agentId, "ai.kill", { spawnId: pty.spawnId });
|
|
5111
|
+
const deadline = Date.now() + EXIT_TIMEOUT_MS;
|
|
5112
|
+
while (Date.now() < deadline) {
|
|
5113
|
+
let state;
|
|
5114
|
+
try {
|
|
5115
|
+
state = await query();
|
|
5116
|
+
} catch (err) {
|
|
5117
|
+
throw new Error(`Terminal de la conversation : \xE9tat illisible apr\xE8s l'arr\xEAt (${err.message})`);
|
|
5118
|
+
}
|
|
5119
|
+
if (state?.status !== "running") return;
|
|
5120
|
+
await new Promise((r) => setTimeout(r, POLL_MS));
|
|
5121
|
+
}
|
|
5122
|
+
throw new Error(`Le terminal de la conversation tourne encore ${EXIT_TIMEOUT_MS / 1e3} s apr\xE8s la demande d'arr\xEAt`);
|
|
5123
|
+
}));
|
|
5124
|
+
const failure = results.find((r) => r.status === "rejected");
|
|
5125
|
+
if (failure) throw failure.reason instanceof Error ? failure.reason : new Error(String(failure.reason));
|
|
5126
|
+
}
|
|
5127
|
+
|
|
4830
5128
|
// ../server/src/services/cli-models.ts
|
|
4831
5129
|
var CACHE_MS = 5 * 6e4;
|
|
4832
5130
|
var MAX_PAGES = 10;
|
|
@@ -4980,23 +5278,18 @@ async function* runAnthropic(client, model, system, initialMessages, apiKey, aut
|
|
|
4980
5278
|
}
|
|
4981
5279
|
yield { type: "done", text: "" };
|
|
4982
5280
|
}
|
|
4983
|
-
async function* runOpenAI(
|
|
5281
|
+
async function* runOpenAI(createStream, initialMessages, apiKey, authToken, signal, anchor = {}) {
|
|
4984
5282
|
const messages = [...initialMessages];
|
|
4985
5283
|
const MAX_TOOL_ROUNDS = 10;
|
|
4986
5284
|
for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
|
|
4987
5285
|
const toolCallsAcc = {};
|
|
4988
5286
|
if (signal?.aborted) return;
|
|
4989
|
-
const stream = await
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
stream: true,
|
|
4994
|
-
max_tokens: 16384,
|
|
4995
|
-
temperature: 0
|
|
4996
|
-
}, { signal });
|
|
4997
|
-
for await (const chunk of stream) {
|
|
4998
|
-
const choice = chunk.choices[0];
|
|
5287
|
+
const stream = await createStream(messages, signal);
|
|
5288
|
+
let finishReason = null;
|
|
5289
|
+
for await (const chunk2 of stream) {
|
|
5290
|
+
const choice = chunk2.choices[0];
|
|
4999
5291
|
if (!choice) continue;
|
|
5292
|
+
if (choice.finish_reason) finishReason = choice.finish_reason;
|
|
5000
5293
|
if (choice.delta?.content) {
|
|
5001
5294
|
yield { type: "delta", text: choice.delta.content };
|
|
5002
5295
|
}
|
|
@@ -5008,6 +5301,8 @@ async function* runOpenAI(client, model, initialMessages, apiKey, authToken, sig
|
|
|
5008
5301
|
toolCallsAcc[tc.index] = acc;
|
|
5009
5302
|
}
|
|
5010
5303
|
}
|
|
5304
|
+
const truncated = truncationError(finishReason);
|
|
5305
|
+
if (truncated) throw truncated;
|
|
5011
5306
|
if (Object.keys(toolCallsAcc).length === 0) {
|
|
5012
5307
|
yield { type: "done", text: "" };
|
|
5013
5308
|
return;
|
|
@@ -5142,15 +5437,23 @@ async function buildCliSystemContext(conv, jwt) {
|
|
|
5142
5437
|
});
|
|
5143
5438
|
return systemContext;
|
|
5144
5439
|
}
|
|
5145
|
-
async function resolveInjectedEnv(environmentId) {
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5440
|
+
async function resolveInjectedEnv(environmentId, secretOwnerId) {
|
|
5441
|
+
let injected = {};
|
|
5442
|
+
if (environmentId) {
|
|
5443
|
+
try {
|
|
5444
|
+
injected = await environmentResolver.resolveAiInjectedEnv(environmentId);
|
|
5445
|
+
} catch (err) {
|
|
5446
|
+
console.warn("[chat] resolveInjectedEnv failed:", err.message);
|
|
5447
|
+
}
|
|
5448
|
+
}
|
|
5449
|
+
if (secretOwnerId) {
|
|
5450
|
+
try {
|
|
5451
|
+
injected = { ...injected, ...await conversationSecretEnvStore.resolve(secretOwnerId) };
|
|
5452
|
+
} catch (err) {
|
|
5453
|
+
console.warn("[chat] secret env unreadable, started without it:", err.message);
|
|
5454
|
+
}
|
|
5153
5455
|
}
|
|
5456
|
+
return Object.keys(injected).length > 0 ? injected : void 0;
|
|
5154
5457
|
}
|
|
5155
5458
|
async function startClaudeCodeSession(convId, prompt, opts = {}) {
|
|
5156
5459
|
const conv = await conversationStore.get(convId);
|
|
@@ -5180,7 +5483,7 @@ async function startClaudeCodeSession(convId, prompt, opts = {}) {
|
|
|
5180
5483
|
if (resolvedAgentId) {
|
|
5181
5484
|
session.agentId = resolvedAgentId;
|
|
5182
5485
|
}
|
|
5183
|
-
const injectedEnv = await resolveInjectedEnv(opts.environmentId);
|
|
5486
|
+
const injectedEnv = await resolveInjectedEnv(opts.environmentId, opts.secretEnvOwnerId ?? convId);
|
|
5184
5487
|
const done = new ClaudeCodeRunner().run({
|
|
5185
5488
|
agentId: resolvedAgentId ?? void 0,
|
|
5186
5489
|
// The conversation's project, not the server's launch directory.
|
|
@@ -5247,7 +5550,7 @@ async function startCodexSession(convId, prompt, opts = {}) {
|
|
|
5247
5550
|
if (resolvedAgentId) {
|
|
5248
5551
|
session.agentId = resolvedAgentId;
|
|
5249
5552
|
}
|
|
5250
|
-
const injectedEnv = await resolveInjectedEnv(opts.environmentId);
|
|
5553
|
+
const injectedEnv = await resolveInjectedEnv(opts.environmentId, opts.secretEnvOwnerId ?? convId);
|
|
5251
5554
|
const done = new CodexRunner().run({
|
|
5252
5555
|
agentId: resolvedAgentId ?? void 0,
|
|
5253
5556
|
cwd: await conversationCwd(conv),
|
|
@@ -5423,7 +5726,9 @@ var chatRouter = router({
|
|
|
5423
5726
|
projectId: Id128Schema.optional(),
|
|
5424
5727
|
orgId: z6.string().max(128).nullable().optional(),
|
|
5425
5728
|
// L'agent où la conversation travaille. Absent = agent par défaut.
|
|
5426
|
-
agentId: Id128Schema.optional()
|
|
5729
|
+
agentId: Id128Schema.optional(),
|
|
5730
|
+
// Modèle du terminal Claude Code (`ollama:<nom>` : Claude Code sur Ollama).
|
|
5731
|
+
cliModel: z6.string().max(128).optional()
|
|
5427
5732
|
})).mutation(async ({ input }) => {
|
|
5428
5733
|
let scenario = null;
|
|
5429
5734
|
if (input.scenarioId) {
|
|
@@ -5440,6 +5745,7 @@ var chatRouter = router({
|
|
|
5440
5745
|
);
|
|
5441
5746
|
const extra = {
|
|
5442
5747
|
...input.agentId ? { agentId: input.agentId } : {},
|
|
5748
|
+
...input.cliModel ? { cliModel: input.cliModel } : {},
|
|
5443
5749
|
...companionDefaults
|
|
5444
5750
|
};
|
|
5445
5751
|
const conv = await conversationStore.create(
|
|
@@ -5510,6 +5816,52 @@ var chatRouter = router({
|
|
|
5510
5816
|
forgetCompanionSkip(input.id);
|
|
5511
5817
|
return conv;
|
|
5512
5818
|
}),
|
|
5819
|
+
/**
|
|
5820
|
+
* Les noms des variables secrètes d'une conversation. Jamais les valeurs :
|
|
5821
|
+
* elles entrent dans l'environnement du CLI et n'en ressortent pas.
|
|
5822
|
+
*/
|
|
5823
|
+
secretEnvKeys: protectedProcedure.input(z6.object({ id: Id128Schema })).query(async ({ input }) => {
|
|
5824
|
+
const existing = await conversationStore.get(input.id);
|
|
5825
|
+
if (!existing) throw new TRPCError4({ code: "NOT_FOUND", message: "Conversation not found" });
|
|
5826
|
+
return conversationSecretEnvStore.keys(input.id);
|
|
5827
|
+
}),
|
|
5828
|
+
/**
|
|
5829
|
+
* Remplace les variables secrètes d'une conversation, puis arrête son
|
|
5830
|
+
* process et celui de son compagnon : un process ne reçoit son
|
|
5831
|
+
* environnement qu'à son lancement, la prochaine ouverture repart avec les
|
|
5832
|
+
* nouvelles valeurs. `value` absent = garder la valeur enregistrée.
|
|
5833
|
+
*/
|
|
5834
|
+
setSecretEnv: protectedProcedure.input(z6.object({
|
|
5835
|
+
id: Id128Schema,
|
|
5836
|
+
entries: z6.array(z6.object({
|
|
5837
|
+
key: z6.string().min(1).max(SECRET_ENV_MAX_KEY_LENGTH).regex(SECRET_ENV_KEY_PATTERN),
|
|
5838
|
+
value: z6.string().max(SECRET_ENV_MAX_VALUE_LENGTH).optional()
|
|
5839
|
+
})).max(SECRET_ENV_MAX_ENTRIES).refine((list) => new Set(list.map((e) => e.key)).size === list.length, { message: "Duplicate variable name" })
|
|
5840
|
+
})).mutation(async ({ input }) => {
|
|
5841
|
+
const existing = await conversationStore.get(input.id);
|
|
5842
|
+
if (!existing) throw new TRPCError4({ code: "NOT_FOUND", message: "Conversation not found" });
|
|
5843
|
+
if (existing.isCompanion) {
|
|
5844
|
+
throw new TRPCError4({ code: "BAD_REQUEST", message: "A companion uses the secrets of the conversation it reviews" });
|
|
5845
|
+
}
|
|
5846
|
+
let keys;
|
|
5847
|
+
try {
|
|
5848
|
+
keys = await conversationSecretEnvStore.replace(input.id, input.entries);
|
|
5849
|
+
} catch (err) {
|
|
5850
|
+
throw new TRPCError4({ code: "BAD_REQUEST", message: err.message });
|
|
5851
|
+
}
|
|
5852
|
+
activeSessionRegistry.kill(input.id);
|
|
5853
|
+
const stops = [stopPtysOf(input.id)];
|
|
5854
|
+
if (existing.companionConversationId) {
|
|
5855
|
+
activeSessionRegistry.kill(existing.companionConversationId);
|
|
5856
|
+
stops.push(stopPtysOf(existing.companionConversationId));
|
|
5857
|
+
}
|
|
5858
|
+
try {
|
|
5859
|
+
await Promise.all(stops);
|
|
5860
|
+
} catch (err) {
|
|
5861
|
+
throw new TRPCError4({ code: "INTERNAL_SERVER_ERROR", message: `Variables enregistr\xE9es, mais le process n'a pas pu \xEAtre arr\xEAt\xE9 : ${err.message}` });
|
|
5862
|
+
}
|
|
5863
|
+
return keys;
|
|
5864
|
+
}),
|
|
5513
5865
|
/**
|
|
5514
5866
|
* La dernière livraison du compagnon vers cette conversation.
|
|
5515
5867
|
*
|
|
@@ -5614,19 +5966,9 @@ var chatRouter = router({
|
|
|
5614
5966
|
forgetReviewProgress(input.id);
|
|
5615
5967
|
forgetDelivery(input.id);
|
|
5616
5968
|
forgetCompanionSkip(input.id);
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
agentId,
|
|
5621
|
-
"ai.findPtyBySession",
|
|
5622
|
-
{ sessionId: input.id }
|
|
5623
|
-
).then((pty) => {
|
|
5624
|
-
if (pty?.status === "running") {
|
|
5625
|
-
return agentManager.agentMutation(agentId, "ai.kill", { spawnId: pty.spawnId });
|
|
5626
|
-
}
|
|
5627
|
-
}).catch(() => {
|
|
5628
|
-
});
|
|
5629
|
-
}
|
|
5969
|
+
await stopPtysOf(input.id).catch(() => {
|
|
5970
|
+
});
|
|
5971
|
+
await conversationSecretEnvStore.remove(input.id);
|
|
5630
5972
|
await conversationStore.delete(input.id);
|
|
5631
5973
|
await scenarioRunStore.deleteRunDir(input.id);
|
|
5632
5974
|
imageStorageService.deleteByContextId(input.id).catch(
|
|
@@ -5705,6 +6047,11 @@ var chatRouter = router({
|
|
|
5705
6047
|
return [];
|
|
5706
6048
|
}
|
|
5707
6049
|
}),
|
|
6050
|
+
/**
|
|
6051
|
+
* POC : les modèles Ollama installés, proposés dans « Choisir votre IA » pour
|
|
6052
|
+
* lancer le terminal Claude Code sur Ollama. Liste vide si Ollama ne répond pas.
|
|
6053
|
+
*/
|
|
6054
|
+
listOllamaModels: protectedProcedure.query(async () => ({ models: await listOllamaModels() })),
|
|
5708
6055
|
/**
|
|
5709
6056
|
* Les modèles proposés pour un runner CLI : alias fixes pour Claude Code,
|
|
5710
6057
|
* `model/list` de l'app-server pour Codex, sur l'agent visé (celui par
|
|
@@ -5851,7 +6198,7 @@ ${imagePaths.map((p) => `Image: ${p}`).join("\n")}` : cliPrompt;
|
|
|
5851
6198
|
session.agentId = resolvedAgentId;
|
|
5852
6199
|
}
|
|
5853
6200
|
if (process.env.RUNEYA_DEBUG) console.log("[chat]: claude run...");
|
|
5854
|
-
const injectedEnv = await resolveInjectedEnv(input.environmentId);
|
|
6201
|
+
const injectedEnv = await resolveInjectedEnv(input.environmentId, convId);
|
|
5855
6202
|
new ClaudeCodeRunner().run({
|
|
5856
6203
|
agentId: resolvedAgentId ?? void 0,
|
|
5857
6204
|
cwd: await conversationCwd(conv),
|
|
@@ -5940,7 +6287,7 @@ ${input.prompt}` : input.prompt;
|
|
|
5940
6287
|
if (resolvedAgentId) {
|
|
5941
6288
|
session.agentId = resolvedAgentId;
|
|
5942
6289
|
}
|
|
5943
|
-
const injectedEnv = await resolveInjectedEnv(input.environmentId);
|
|
6290
|
+
const injectedEnv = await resolveInjectedEnv(input.environmentId, convId);
|
|
5944
6291
|
new CodexRunner().run({
|
|
5945
6292
|
agentId: resolvedAgentId ?? void 0,
|
|
5946
6293
|
cwd: await conversationCwd(conv),
|
|
@@ -6048,8 +6395,6 @@ ${input.prompt}` : input.prompt;
|
|
|
6048
6395
|
});
|
|
6049
6396
|
gen = runAnthropic(client, model, system, msgs, apiKey, authToken, apiSignal, anchor);
|
|
6050
6397
|
} else if (provider.provider in OPENAI_COMPAT_BASE_URLS) {
|
|
6051
|
-
const baseURL = OPENAI_COMPAT_BASE_URLS[provider.provider];
|
|
6052
|
-
const client = new OpenAI2({ apiKey: plaintextKey || "ollama", baseURL });
|
|
6053
6398
|
const msgs = [
|
|
6054
6399
|
{ role: "system", content: system },
|
|
6055
6400
|
...input.messages.map((m, i) => {
|
|
@@ -6065,7 +6410,22 @@ ${input.prompt}` : input.prompt;
|
|
|
6065
6410
|
return { role: m.role, content: m.content };
|
|
6066
6411
|
})
|
|
6067
6412
|
];
|
|
6068
|
-
|
|
6413
|
+
let createStream;
|
|
6414
|
+
if (provider.provider === "ollama") {
|
|
6415
|
+
createStream = (messages, signal2) => streamOllamaChat({ model, messages, tools: CHAT_TOOLS_OPENAI, temperature: 0, signal: signal2 });
|
|
6416
|
+
} else {
|
|
6417
|
+
const baseURL = OPENAI_COMPAT_BASE_URLS[provider.provider];
|
|
6418
|
+
const client = new OpenAI2({ apiKey: plaintextKey || "ollama", baseURL });
|
|
6419
|
+
createStream = (messages, signal2) => client.chat.completions.create({
|
|
6420
|
+
model,
|
|
6421
|
+
messages,
|
|
6422
|
+
tools: CHAT_TOOLS_OPENAI,
|
|
6423
|
+
stream: true,
|
|
6424
|
+
max_tokens: 16384,
|
|
6425
|
+
temperature: 0
|
|
6426
|
+
}, { signal: signal2 });
|
|
6427
|
+
}
|
|
6428
|
+
gen = runOpenAI(createStream, msgs, apiKey, authToken, apiSignal, anchor);
|
|
6069
6429
|
} else {
|
|
6070
6430
|
activeSessionRegistry.pushEvent(input.conversationId, { type: "error", message: `Provider "${provider.provider}" is not supported.` });
|
|
6071
6431
|
activeSessionRegistry.pushEvent(input.conversationId, { type: "done", text: "" });
|
|
@@ -6154,6 +6514,7 @@ ${input.prompt}` : input.prompt;
|
|
|
6154
6514
|
lap("findPtyBySession (no reattach)");
|
|
6155
6515
|
const ptyConv = input.sessionId ? await conversationStore.get(input.sessionId).catch(() => null) : null;
|
|
6156
6516
|
const isCodex = input.runner === "codex";
|
|
6517
|
+
let ollamaEnv = {};
|
|
6157
6518
|
const cli = isCodex ? "codex" : "claude";
|
|
6158
6519
|
const args = [];
|
|
6159
6520
|
if (isCodex) {
|
|
@@ -6172,7 +6533,10 @@ ${input.prompt}` : input.prompt;
|
|
|
6172
6533
|
args.push("--session-id", input.sessionId);
|
|
6173
6534
|
}
|
|
6174
6535
|
}
|
|
6175
|
-
|
|
6536
|
+
const ollama = claudeOllamaTarget(input.model ?? ptyConv?.cliModel);
|
|
6537
|
+
if (ollama) ollamaEnv = ollama.env;
|
|
6538
|
+
const model = ollama?.model ?? input.model ?? ptyConv?.cliModel;
|
|
6539
|
+
if (model) args.push("--model", model);
|
|
6176
6540
|
args.push("--dangerously-skip-permissions");
|
|
6177
6541
|
}
|
|
6178
6542
|
const ptyRef = (ptyConv ? await conversationStore.projectRefOf(ptyConv) : null) ?? (input.projectId ? { id: input.projectId, orgId: input.orgId ?? null } : null);
|
|
@@ -6192,9 +6556,9 @@ ${input.prompt}` : input.prompt;
|
|
|
6192
6556
|
}
|
|
6193
6557
|
lap("buildCliSystemContext");
|
|
6194
6558
|
}
|
|
6195
|
-
const injectedEnv = await resolveInjectedEnv(input.environmentId);
|
|
6559
|
+
const injectedEnv = { ...await resolveInjectedEnv(input.environmentId, ptyConv?.id), ...ollamaEnv };
|
|
6196
6560
|
lap("resolveInjectedEnv");
|
|
6197
|
-
const mcpReadyId = input.enableMcp && input.initialPrompt?.trim() ?
|
|
6561
|
+
const mcpReadyId = input.enableMcp && input.initialPrompt?.trim() ? randomUUID6() : void 0;
|
|
6198
6562
|
const res = await agentManager.agentMutation(
|
|
6199
6563
|
agentId,
|
|
6200
6564
|
"ai.spawnPty",
|
|
@@ -6529,6 +6893,7 @@ export {
|
|
|
6529
6893
|
accountForOrgScope,
|
|
6530
6894
|
soleAccountScope,
|
|
6531
6895
|
cloudAuthRouter,
|
|
6896
|
+
KanbanUnavailableError,
|
|
6532
6897
|
kanbanStore,
|
|
6533
6898
|
executeKanbanCommands,
|
|
6534
6899
|
executeKanbanBoardCommands,
|
|
@@ -6543,4 +6908,4 @@ export {
|
|
|
6543
6908
|
startCliSession,
|
|
6544
6909
|
chatRouter
|
|
6545
6910
|
};
|
|
6546
|
-
//# sourceMappingURL=chunk-
|
|
6911
|
+
//# sourceMappingURL=chunk-VKYSHTYT.js.map
|