@chibitek/chibi 0.1.157 → 0.1.158
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/assets/{abnfDiagram-O67JEVCF-BkYbog6D.js → abnfDiagram-O67JEVCF-B8-8zzMS.js} +1 -1
- package/dist/assets/{arc-QwTuMsxp.js → arc-CSHQ9Dx3.js} +1 -1
- package/dist/assets/{architectureDiagram-NJMV4G6O-GdicO2PQ.js → architectureDiagram-NJMV4G6O-BCHM9rXW.js} +1 -1
- package/dist/assets/{blockDiagram-BEXU5L5S-Dkr29TFF.js → blockDiagram-BEXU5L5S-B-5uxPFN.js} +1 -1
- package/dist/assets/{c4Diagram-YGBWAQC7-Dz_3dstx.js → c4Diagram-YGBWAQC7-CuKNM_MR.js} +1 -1
- package/dist/assets/channel-bPN03jIt.js +1 -0
- package/dist/assets/{chunk-2Q5K7J3B-CGsfu-PI.js → chunk-2Q5K7J3B-BNF02GM4.js} +1 -1
- package/dist/assets/{chunk-3FUC2YCW-CDqI37_6.js → chunk-3FUC2YCW-uEWgBd4M.js} +2 -2
- package/dist/assets/{chunk-5VM5RSS4-DnmSuyTb.js → chunk-5VM5RSS4-eSZuXctY.js} +1 -1
- package/dist/assets/{chunk-GTNCS2PH-Dh7tXcJT.js → chunk-GTNCS2PH-CslS8nRS.js} +1 -1
- package/dist/assets/{chunk-GWA4HPMP-D_SM1ZrY.js → chunk-GWA4HPMP-w71-MeKj.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-DmeMuTsI.js → chunk-JWPE2WC7-Bjju7WGh.js} +1 -1
- package/dist/assets/{chunk-NETBCI7D-R1UT9b5f.js → chunk-NETBCI7D-HxrroD4M.js} +1 -1
- package/dist/assets/{chunk-WEXAMYUT-DpPfU1yO.js → chunk-WEXAMYUT-D7Rz470x.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-DlihfTkM.js → chunk-XXDRQBXY-BWKnAeyM.js} +1 -1
- package/dist/assets/{classDiagram-v2-NBCMYWYE-BlTV6lSC.js → classDiagram-v2-NBCMYWYE-CeBDRHmP.js} +1 -1
- package/dist/assets/{cose-bilkent-JH36ORCC-Br1kPFWw.js → cose-bilkent-JH36ORCC-BBbRKlvr.js} +1 -1
- package/dist/assets/{cynefin-OW5HDTMX-BqPp3ZMd.js → cynefin-OW5HDTMX-W02cnJ0B.js} +1 -1
- package/dist/assets/{cynefinDiagram-VND7K2PF-DFfs_k8-.js → cynefinDiagram-VND7K2PF-Cs47eaxd.js} +1 -1
- package/dist/assets/{dagre-6A5THRUB-Ch3xIJA7.js → dagre-6A5THRUB-8mG18hzT.js} +1 -1
- package/dist/assets/{diagram-22UHCM2B-DIHUD87N.js → diagram-22UHCM2B-DF4Qrw67.js} +1 -1
- package/dist/assets/{diagram-3UASUU5V-CAgQQdeF.js → diagram-3UASUU5V-3BAVoed1.js} +1 -1
- package/dist/assets/{diagram-ATOU4E4O-9HVKodrI.js → diagram-ATOU4E4O-CKO3pECE.js} +1 -1
- package/dist/assets/{diagram-CDSNMT55-DU2kDq_h.js → diagram-CDSNMT55-CZhrGvur.js} +1 -1
- package/dist/assets/{diagram-MLGK6HIB-D-qTAhHD.js → diagram-MLGK6HIB-C07l9pdQ.js} +1 -1
- package/dist/assets/{diagram-MPIPVDR6-C31SnI9P.js → diagram-MPIPVDR6-D3or4lkZ.js} +1 -1
- package/dist/assets/{ebnfDiagram-ZINNZB2B-BHrdiBVd.js → ebnfDiagram-ZINNZB2B-DV2epXp_.js} +1 -1
- package/dist/assets/{elk-276RUBZZ-DWyljRo5.js → elk-276RUBZZ-DlvEn4-1.js} +1 -1
- package/dist/assets/{erDiagram-OPXOYQCR-DyY59XPp.js → erDiagram-OPXOYQCR-CB6X7Wt5.js} +1 -1
- package/dist/assets/{flowDiagram-KWPJA3E3-BevMIeAy.js → flowDiagram-KWPJA3E3-C3bqz4lC.js} +1 -1
- package/dist/assets/{ganttDiagram-FUAMR5RP-Ct18WHf4.js → ganttDiagram-FUAMR5RP-yo_Evhab.js} +1 -1
- package/dist/assets/{gitGraphDiagram-X574FWY7-yhixO1N3.js → gitGraphDiagram-X574FWY7-Cq2oUlap.js} +1 -1
- package/dist/assets/{index-BEJ8EKQt.js → index-CG4AFPb4.js} +1 -1
- package/dist/assets/{index-ziyH_uer.js → index-CMcr5k3Z.js} +199 -196
- package/dist/assets/index-CZjRcUdn.css +1 -0
- package/dist/assets/{infoDiagram-VRGFBTTK-CYa-r_S5.js → infoDiagram-VRGFBTTK-Y3ZrY7qL.js} +1 -1
- package/dist/assets/{ishikawaDiagram-OU5B5YK6-CnxwM7gY.js → ishikawaDiagram-OU5B5YK6-BpOtSfyi.js} +1 -1
- package/dist/assets/{journeyDiagram-ZHPQQLJL-RKQfXBrp.js → journeyDiagram-ZHPQQLJL-UQM727le.js} +1 -1
- package/dist/assets/{kanban-definition-PNTS6WVX-CSlRDIF6.js → kanban-definition-PNTS6WVX-C3s5oHpY.js} +1 -1
- package/dist/assets/{linear-Ch19KVyt.js → linear-hX6nFOQr.js} +1 -1
- package/dist/assets/{mermaid.core-Bek7I_t6.js → mermaid.core-DZObGLN7.js} +5 -5
- package/dist/assets/{mindmap-definition-NLK3R4M7-B_LiP8su.js → mindmap-definition-NLK3R4M7-Br-i2E3P.js} +1 -1
- package/dist/assets/{pegDiagram-GJSIUBJH-DhzQeiZf.js → pegDiagram-GJSIUBJH-D5SERB-N.js} +1 -1
- package/dist/assets/{pieDiagram-5QR66LMP-D-BYNKHS.js → pieDiagram-5QR66LMP-BPNf4J3D.js} +1 -1
- package/dist/assets/{quadrantDiagram-O4NWA36T-DnCm0heP.js → quadrantDiagram-O4NWA36T-Bm_Wh7Jr.js} +1 -1
- package/dist/assets/{railroadDiagram-XR7U4H2S-COqgYnhn.js → railroadDiagram-XR7U4H2S-D6bo0abD.js} +1 -1
- package/dist/assets/{requirementDiagram-PLB6GJNP-Dvt72ajf.js → requirementDiagram-PLB6GJNP--1n5rkV7.js} +1 -1
- package/dist/assets/{sankeyDiagram-IPEJSGJF-CisC9nbp.js → sankeyDiagram-IPEJSGJF-Ct4tP2kK.js} +1 -1
- package/dist/assets/{sequenceDiagram-PO4LG4MO-BO8xqvxa.js → sequenceDiagram-PO4LG4MO-CCG7O5fR.js} +1 -1
- package/dist/assets/{sizeCapture-INFHLROL-DLk1jBzf.js → sizeCapture-INFHLROL-CJJJra9c.js} +1 -1
- package/dist/assets/{stateDiagram-v2-GCMORJYK-C_wutSE7.js → stateDiagram-v2-GCMORJYK-BMdRvVEX.js} +1 -1
- package/dist/assets/{swimlanes-2SLR337P-hqo1UlEi.js → swimlanes-2SLR337P-B1wnogip.js} +1 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-DWyjdGGR.js +8 -0
- package/dist/assets/{timeline-definition-EJHVYXUP-C_DJldPg.js → timeline-definition-EJHVYXUP-l-3yhXUP.js} +1 -1
- package/dist/assets/{usecaseDiagram-POWQR4AR-CsidVXMA.js → usecaseDiagram-POWQR4AR-DtwtCOoJ.js} +1 -1
- package/dist/assets/{vennDiagram-UO4OBE2U-Bs-IIqn_.js → vennDiagram-UO4OBE2U-BZAEce1B.js} +1 -1
- package/dist/assets/{wardleyDiagram-VNRHLVJA-CXEUyxaj.js → wardleyDiagram-VNRHLVJA-Na_BCMMO.js} +1 -1
- package/dist/assets/{xychartDiagram-PMCCYNJV-B9qvmm4P.js → xychartDiagram-PMCCYNJV-BW9dlUfE.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/connector-proxy.js +205 -0
- package/dist-server/container-mcp.js +149 -95
- package/dist-server/drivers/agents-proxy.js +81 -81
- package/dist-server/index.js +4459 -4013
- package/dist-server/openmausbot.js +596 -570
- package/dist-server/pair-cli.js +596 -570
- package/dist-server/server/avatar-subscription.js +51 -0
- package/dist-server/server/cli.js +5 -4
- package/dist-server/server/colima-docker.js +136 -0
- package/dist-server/server/composio.js +1051 -0
- package/dist-server/server/connector-proxy.js +249 -0
- package/dist-server/server/container-computer.js +31 -63
- package/dist-server/server/container-mcp.js +5 -0
- package/dist-server/server/engine-failover.js +22 -12
- package/dist-server/server/index.js +40 -16
- package/dist-server/server/mcp-public-registry.js +325 -0
- package/dist-server/server/port-in-use.js +21 -0
- package/dist-server/server/session-fallback.js +76 -0
- package/dist-server/server/vault/routes.js +43 -0
- package/dist-server/server/vault/runtime.js +48 -0
- package/dist-server/server/vault/store.js +177 -0
- package/dist-server/shared/connector-availability.js +12 -0
- package/dist-server/shared/desktop-private-port.js +1 -0
- package/dist-server/shared/subscription-bots.js +3 -0
- package/dist-server/shared/vault.js +59 -0
- package/dist-server/vps-container-mcp.js +85 -83
- package/package.json +1 -1
- package/dist/assets/channel-D2y0i2ps.js +0 -1
- package/dist/assets/index-DGWeJcck.css +0 -1
- package/dist/assets/swimlanesDiagram-TC7HE7FX-D15JqTL9.js +0 -8
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { VaultStore } from "./store.js";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { ROUTES } from "../routes/table.js";
|
|
6
|
+
import { createVaultRoutes } from "./routes.js";
|
|
7
|
+
const keyMessage = z.object({ type: z.literal("atomic:native-vault-key"), ownerId: z.string().uuid(), key: z.string().regex(/^[0-9a-f]{64}$/) }).strict();
|
|
8
|
+
/** Constructed by the server, bootstrapped only on Electron's private port.
|
|
9
|
+
* No HTTP endpoint can unlock the vault or replace its owner. */
|
|
10
|
+
export class NativeVaultRuntime {
|
|
11
|
+
#directory;
|
|
12
|
+
#store = null;
|
|
13
|
+
#ownerId = null;
|
|
14
|
+
constructor(directory) { this.#directory = directory; }
|
|
15
|
+
receive(raw) {
|
|
16
|
+
if (!raw || typeof raw !== "object" || !("type" in raw) || raw.type !== "atomic:native-vault-key")
|
|
17
|
+
return false;
|
|
18
|
+
const parsed = keyMessage.safeParse(raw);
|
|
19
|
+
if (!parsed.success || this.#store)
|
|
20
|
+
return true;
|
|
21
|
+
const key = Buffer.from(parsed.data.key, "hex");
|
|
22
|
+
try {
|
|
23
|
+
this.#store = new VaultStore(this.#directory, key);
|
|
24
|
+
this.#ownerId = parsed.data.ownerId;
|
|
25
|
+
}
|
|
26
|
+
finally {
|
|
27
|
+
key.fill(0);
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
store() { return this.#store; }
|
|
32
|
+
/** Until paired owner identity is established, remote clients fail closed.
|
|
33
|
+
* GET needs the desktop capability too; loopback alone is not authority. */
|
|
34
|
+
owner(req, auth, desktopToken) {
|
|
35
|
+
if (!desktopToken || auth.kind !== "loopback" || auth.trust === "service" || !auth.scopes.includes("admin"))
|
|
36
|
+
return null;
|
|
37
|
+
const supplied = req.headers["x-chibi-desktop-owner"];
|
|
38
|
+
if (typeof supplied !== "string" || !/^[A-Za-z0-9_-]{43}$/.test(supplied) || supplied.length !== desktopToken.length || !timingSafeEqual(Buffer.from(supplied), Buffer.from(desktopToken)))
|
|
39
|
+
return null;
|
|
40
|
+
return this.#ownerId ?? "pending";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Keep bootstrapping and route registration together, outside index.ts. */
|
|
44
|
+
export function installNativeVault(dataDir, port, desktopToken, fromBot) {
|
|
45
|
+
const runtime = new NativeVaultRuntime(join(dataDir, "providers", "native-vault"));
|
|
46
|
+
port?.on("message", (event) => { runtime.receive(event?.data); });
|
|
47
|
+
ROUTES.push(createVaultRoutes({ store: () => runtime.store(), owner: (req, auth) => fromBot(req) ? null : runtime.owner(req, auth, desktopToken()) }));
|
|
48
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Native Vault persistence. No API/OAuth credentials are stored here.
|
|
2
|
+
// The caller supplies a key from the trusted runtime; this module never
|
|
3
|
+
// creates a plaintext key file or returns stored secrets to a model.
|
|
4
|
+
import { createCipheriv, createDecipheriv, randomBytes, randomUUID } from "node:crypto";
|
|
5
|
+
import { lstatSync, mkdirSync, readFileSync, rmdirSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { vaultInputSchema, vaultMetadata } from "../../shared/vault.js";
|
|
9
|
+
import { writeFileAtomic } from "../atomic.js";
|
|
10
|
+
const MAX_BYTES = 16 * 1024 * 1024;
|
|
11
|
+
const MAX_ITEMS = 1000;
|
|
12
|
+
const AAD = Buffer.from("atomic-native-vault:v1");
|
|
13
|
+
const identity = z.string().uuid();
|
|
14
|
+
const metadataSchema = z.object({
|
|
15
|
+
id: identity, ownerId: identity, createdAt: z.number().int().nonnegative(), updatedAt: z.number().int().nonnegative(),
|
|
16
|
+
});
|
|
17
|
+
const envelopeSchema = z.object({
|
|
18
|
+
version: z.literal(1), iv: z.string().regex(/^[0-9a-f]{24}$/),
|
|
19
|
+
tag: z.string().regex(/^[0-9a-f]{32}$/), data: z.string().regex(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/),
|
|
20
|
+
}).strict();
|
|
21
|
+
export class VaultError extends Error {
|
|
22
|
+
}
|
|
23
|
+
function owner(value) {
|
|
24
|
+
if (!identity.safeParse(value).success)
|
|
25
|
+
throw new VaultError("Invalid Vault owner.");
|
|
26
|
+
}
|
|
27
|
+
export class VaultStore {
|
|
28
|
+
#directory;
|
|
29
|
+
#file;
|
|
30
|
+
#key;
|
|
31
|
+
constructor(directory, key) {
|
|
32
|
+
if (key.length !== 32)
|
|
33
|
+
throw new VaultError("Vault encryption is unavailable.");
|
|
34
|
+
this.#directory = directory;
|
|
35
|
+
this.#file = join(directory, "vault.enc");
|
|
36
|
+
this.#key = Buffer.from(key);
|
|
37
|
+
}
|
|
38
|
+
read() {
|
|
39
|
+
try {
|
|
40
|
+
let stat;
|
|
41
|
+
try {
|
|
42
|
+
stat = lstatSync(this.#file);
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
if (error.code === "ENOENT")
|
|
46
|
+
return [];
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_BYTES)
|
|
50
|
+
throw new Error();
|
|
51
|
+
const envelope = envelopeSchema.parse(JSON.parse(readFileSync(this.#file, "utf8")));
|
|
52
|
+
const decipher = createDecipheriv("aes-256-gcm", this.#key, Buffer.from(envelope.iv, "hex"));
|
|
53
|
+
decipher.setAAD(AAD);
|
|
54
|
+
decipher.setAuthTag(Buffer.from(envelope.tag, "hex"));
|
|
55
|
+
const plain = Buffer.concat([decipher.update(Buffer.from(envelope.data, "base64")), decipher.final()]);
|
|
56
|
+
try {
|
|
57
|
+
// Separate metadata before the strict input schema validates each item.
|
|
58
|
+
const raw = JSON.parse(plain.toString("utf8"));
|
|
59
|
+
if (!Array.isArray(raw) || raw.length > MAX_ITEMS)
|
|
60
|
+
throw new Error();
|
|
61
|
+
const items = raw.map((value) => {
|
|
62
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
63
|
+
throw new Error();
|
|
64
|
+
const { id, ownerId, createdAt, updatedAt, ...input } = value;
|
|
65
|
+
const metadata = metadataSchema.parse({ id, ownerId, createdAt, updatedAt });
|
|
66
|
+
return { ...vaultInputSchema.parse(input), ...metadata };
|
|
67
|
+
});
|
|
68
|
+
if (new Set(items.map((item) => item.id)).size !== items.length)
|
|
69
|
+
throw new Error();
|
|
70
|
+
return items;
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
plain.fill(0);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
throw new VaultError("Vault is locked or unreadable.");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
write(items) {
|
|
81
|
+
const iv = randomBytes(12);
|
|
82
|
+
const cipher = createCipheriv("aes-256-gcm", this.#key, iv);
|
|
83
|
+
cipher.setAAD(AAD);
|
|
84
|
+
const plain = Buffer.from(JSON.stringify(items));
|
|
85
|
+
try {
|
|
86
|
+
const encrypted = Buffer.concat([cipher.update(plain), cipher.final()]);
|
|
87
|
+
const envelope = JSON.stringify({ version: 1, iv: iv.toString("hex"), tag: cipher.getAuthTag().toString("hex"), data: encrypted.toString("base64") });
|
|
88
|
+
if (Buffer.byteLength(envelope) > MAX_BYTES)
|
|
89
|
+
throw new VaultError("Vault has reached its size limit.");
|
|
90
|
+
writeFileAtomic(this.#file, envelope, { mode: 0o600 });
|
|
91
|
+
}
|
|
92
|
+
finally {
|
|
93
|
+
plain.fill(0);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** Cross-process writes refuse contention instead of losing an update.
|
|
97
|
+
* A crash leaves the lock in place for explicit recovery, never guessing
|
|
98
|
+
* that an older process has stopped writing. */
|
|
99
|
+
mutate(fn) {
|
|
100
|
+
const lock = join(this.#directory, "write.lock");
|
|
101
|
+
try {
|
|
102
|
+
mkdirSync(this.#directory, { recursive: true, mode: 0o700 });
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
throw new VaultError("Vault could not be saved.");
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
mkdirSync(lock, { mode: 0o700 });
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
throw new VaultError("Vault is busy or needs recovery.");
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const items = this.read();
|
|
115
|
+
const result = fn(items);
|
|
116
|
+
this.write(items);
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (error instanceof VaultError)
|
|
121
|
+
throw error;
|
|
122
|
+
throw new VaultError("Vault could not be saved.");
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
rmdirSync(lock);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
list(ownerId) {
|
|
129
|
+
owner(ownerId);
|
|
130
|
+
return this.read().filter((item) => item.ownerId === ownerId).map(vaultMetadata);
|
|
131
|
+
}
|
|
132
|
+
create(ownerId, input) {
|
|
133
|
+
owner(ownerId);
|
|
134
|
+
const parsed = vaultInputSchema.safeParse(input);
|
|
135
|
+
if (!parsed.success)
|
|
136
|
+
throw new VaultError("Invalid Vault item.");
|
|
137
|
+
return this.mutate((items) => {
|
|
138
|
+
if (items.length >= MAX_ITEMS)
|
|
139
|
+
throw new VaultError("Vault has reached its item limit.");
|
|
140
|
+
const now = Date.now();
|
|
141
|
+
const item = { ...parsed.data, id: randomUUID(), ownerId, createdAt: now, updatedAt: now };
|
|
142
|
+
items.push(item);
|
|
143
|
+
return vaultMetadata(item);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
update(ownerId, id, input) {
|
|
147
|
+
owner(ownerId);
|
|
148
|
+
if (!input || typeof input !== "object" || Array.isArray(input))
|
|
149
|
+
throw new VaultError("Invalid Vault item.");
|
|
150
|
+
return this.mutate((items) => {
|
|
151
|
+
const index = items.findIndex((item) => item.id === id && item.ownerId === ownerId);
|
|
152
|
+
const current = items[index];
|
|
153
|
+
if (!current)
|
|
154
|
+
throw new VaultError("Vault item is unavailable.");
|
|
155
|
+
const { id: _id, ownerId: _owner, createdAt: _created, updatedAt: _updated, ...previous } = current;
|
|
156
|
+
// A blank password is invalid; omission preserves it, so metadata can
|
|
157
|
+
// be edited without ever reading the saved secret back to the UI.
|
|
158
|
+
const parsed = vaultInputSchema.safeParse({ ...previous, ...input });
|
|
159
|
+
if (!parsed.success)
|
|
160
|
+
throw new VaultError("Invalid Vault item.");
|
|
161
|
+
if (current.kind !== parsed.data.kind)
|
|
162
|
+
throw new VaultError("A Vault item's category cannot change.");
|
|
163
|
+
const item = { ...parsed.data, id, ownerId, createdAt: current.createdAt, updatedAt: Date.now() };
|
|
164
|
+
items[index] = item;
|
|
165
|
+
return vaultMetadata(item);
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
remove(ownerId, id) {
|
|
169
|
+
owner(ownerId);
|
|
170
|
+
this.mutate((items) => {
|
|
171
|
+
const index = items.findIndex((item) => item.id === id && item.ownerId === ownerId);
|
|
172
|
+
if (index < 0)
|
|
173
|
+
throw new VaultError("Vault item is unavailable.");
|
|
174
|
+
items.splice(index, 1);
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const MANAGED_CUSTOM_AUTH_TOOLKITS = new Set(["twitter", "x"]);
|
|
2
|
+
/**
|
|
3
|
+
* Some Composio toolkits no longer include provider-managed credentials.
|
|
4
|
+
* The official OpenMausBot broker cannot offer those connections until its
|
|
5
|
+
* project owns the corresponding OAuth app. Self-hosted Composio projects can
|
|
6
|
+
* still supply their own auth config, so keep this restriction mode-specific.
|
|
7
|
+
*/
|
|
8
|
+
export function managedConnectorUnavailableReason(mode, slug) {
|
|
9
|
+
if (mode !== "managed" || !MANAGED_CUSTOM_AUTH_TOOLKITS.has(slug.trim().toLowerCase()))
|
|
10
|
+
return null;
|
|
11
|
+
return "Twitter/X needs your own X Developer app and Composio auth config. Use self-hosted connected apps for now.";
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -88,6 +88,9 @@ export function subscriptionProviderForEngine(engine) {
|
|
|
88
88
|
export function isProviderModel(provider, model) {
|
|
89
89
|
return provider.ownModels ? provider.ownModels.test(model) : true;
|
|
90
90
|
}
|
|
91
|
+
/** Turns one conversation of a company subscription bot may run on another
|
|
92
|
+
* provider's subscription when its own has no account free (server/session-fallback.ts). */
|
|
93
|
+
export const SESSION_FALLBACK_TURNS = 50;
|
|
91
94
|
export function isSubscriptionBot(bot) {
|
|
92
95
|
const value = bot?.subscription;
|
|
93
96
|
return Boolean(value && typeof value.instanceId === "string" && subscriptionProvider(value.provider));
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Only absolute HTTPS origins can receive a saved item. Paths never broaden
|
|
3
|
+
* authority to sibling domains, frames, ports or subdomains. */
|
|
4
|
+
export function vaultOrigin(raw) {
|
|
5
|
+
if (raw.length > 2048 || [...raw].some((c) => c.charCodeAt(0) <= 32 || c.charCodeAt(0) === 127) || raw.includes("\\"))
|
|
6
|
+
return null;
|
|
7
|
+
try {
|
|
8
|
+
const url = new URL(raw);
|
|
9
|
+
if (url.protocol !== "https:" || !url.hostname || url.username || url.password || url.hostname.endsWith("."))
|
|
10
|
+
return null;
|
|
11
|
+
return url.origin;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const text = (max) => z.string().max(max);
|
|
18
|
+
const origin = z.string().refine((value) => vaultOrigin(value) !== null).transform((value) => vaultOrigin(value));
|
|
19
|
+
const address = z.object({
|
|
20
|
+
name: text(200), line1: text(300), line2: text(300), city: text(200),
|
|
21
|
+
region: text(200), postalCode: text(40), country: text(100),
|
|
22
|
+
}).strict();
|
|
23
|
+
const base = {
|
|
24
|
+
label: z.string().trim().min(1).max(200), hints: text(1000),
|
|
25
|
+
botIds: z.array(z.string().uuid()).max(100).refine((ids) => new Set(ids).size === ids.length),
|
|
26
|
+
expiresAt: z.number().int().positive().max(Number.MAX_SAFE_INTEGER).nullable(),
|
|
27
|
+
};
|
|
28
|
+
/** Reject unknown fields rather than accidentally persisting secrets in
|
|
29
|
+
* metadata. CVC is deliberately absent: it belongs to a single secure use. */
|
|
30
|
+
export const vaultInputSchema = z.discriminatedUnion("kind", [
|
|
31
|
+
z.object({ ...base, kind: z.literal("login"), website: origin,
|
|
32
|
+
username: z.string().min(1).max(320), password: z.string().min(1).max(4096),
|
|
33
|
+
}).strict(),
|
|
34
|
+
z.object({ ...base, kind: z.literal("card"), origins: z.array(origin).min(1).max(50),
|
|
35
|
+
number: z.string().regex(/^\d{12,19}$/), expiryMonth: z.number().int().min(1).max(12),
|
|
36
|
+
expiryYear: z.number().int().min(2000).max(9999), billing: address,
|
|
37
|
+
}).strict(),
|
|
38
|
+
z.object({ ...base, kind: z.literal("contact"), origins: z.array(origin).min(1).max(50),
|
|
39
|
+
address, email: z.union([z.literal(""), z.string().email().max(320)]), phone: text(80),
|
|
40
|
+
}).strict(),
|
|
41
|
+
]);
|
|
42
|
+
/** Explicit allowlist. Never spread a stored item into a response. */
|
|
43
|
+
export function vaultMetadata(item) {
|
|
44
|
+
return {
|
|
45
|
+
id: item.id, kind: item.kind, label: item.label, hints: item.hints,
|
|
46
|
+
botIds: [...item.botIds], expiresAt: item.expiresAt,
|
|
47
|
+
origins: item.kind === "login" ? [item.website] : [...item.origins],
|
|
48
|
+
...(item.kind === "login" ? { username: item.username } : {}),
|
|
49
|
+
...(item.kind === "card" ? { lastFour: item.number.slice(-4), expiryMonth: item.expiryMonth, expiryYear: item.expiryYear, billing: { ...item.billing } } : {}),
|
|
50
|
+
...(item.kind === "contact" ? { address: { ...item.address }, email: item.email, phone: item.phone } : {}),
|
|
51
|
+
createdAt: item.createdAt, updatedAt: item.updatedAt,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function vaultUseAllowed(item, actor, now) {
|
|
55
|
+
const destination = vaultOrigin(actor.destination);
|
|
56
|
+
return Number.isSafeInteger(now) && now >= 0 && item.ownerId === actor.ownerId && item.botIds.includes(actor.botId)
|
|
57
|
+
&& (item.expiresAt === null || now < item.expiresAt) && destination !== null
|
|
58
|
+
&& (item.kind === "login" ? item.website === destination : item.origins.includes(destination));
|
|
59
|
+
}
|