@somacheck/vibecheck 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +85 -0
- package/dist/api.js.map +1 -0
- package/dist/cli.js +42 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +42 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.js +4 -0
- package/dist/constants.js.map +1 -0
- package/dist/link.js +11 -0
- package/dist/link.js.map +1 -0
- package/dist/server.js +59 -0
- package/dist/server.js.map +1 -0
- package/dist/vibecheck.js +50 -0
- package/dist/vibecheck.js.map +1 -0
- package/package.json +37 -0
package/dist/api.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const STATUSES = new Set(["pending", "answered", "expired", "cancelled"]);
|
|
2
|
+
const VERDICTS = new Set(["aligned", "unaligned", "inconclusive"]);
|
|
3
|
+
function firstRow(value) {
|
|
4
|
+
const row = Array.isArray(value) ? value[0] : value;
|
|
5
|
+
if (row === null || typeof row !== "object" || Array.isArray(row)) {
|
|
6
|
+
throw new Error("SomaCheck returned an unexpected response.");
|
|
7
|
+
}
|
|
8
|
+
return row;
|
|
9
|
+
}
|
|
10
|
+
function requiredString(row, key) {
|
|
11
|
+
const value = row[key];
|
|
12
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
13
|
+
throw new Error("SomaCheck returned an incomplete response.");
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
export class SupabaseAgentApi {
|
|
18
|
+
#endpoint;
|
|
19
|
+
#apiKey;
|
|
20
|
+
#fetch;
|
|
21
|
+
constructor(endpoint, apiKey, fetchImplementation = fetch) {
|
|
22
|
+
this.#endpoint = endpoint.replace(/\/$/, "");
|
|
23
|
+
this.#apiKey = apiKey;
|
|
24
|
+
this.#fetch = fetchImplementation;
|
|
25
|
+
}
|
|
26
|
+
async redeemLink(code) {
|
|
27
|
+
const row = await this.#rpc("agent_link_redeem", { code });
|
|
28
|
+
return requiredString(row, "token");
|
|
29
|
+
}
|
|
30
|
+
async createRequest(token, question) {
|
|
31
|
+
const row = await this.#rpc("agent_request_create", { token, question });
|
|
32
|
+
return requiredString(row, "request_id");
|
|
33
|
+
}
|
|
34
|
+
async pollRequest(token, requestId) {
|
|
35
|
+
const row = await this.#rpc("agent_request_poll", { token, request_id: requestId });
|
|
36
|
+
const status = row.status;
|
|
37
|
+
if (typeof status !== "string" || !STATUSES.has(status)) {
|
|
38
|
+
throw new Error("SomaCheck returned an invalid request status.");
|
|
39
|
+
}
|
|
40
|
+
const verdict = row.verdict;
|
|
41
|
+
const confidence = row.confidence;
|
|
42
|
+
const latency = row.latency_s;
|
|
43
|
+
if (verdict !== null && (typeof verdict !== "string" || !VERDICTS.has(verdict))) {
|
|
44
|
+
throw new Error("SomaCheck returned an invalid verdict.");
|
|
45
|
+
}
|
|
46
|
+
if (confidence !== null && typeof confidence !== "number") {
|
|
47
|
+
throw new Error("SomaCheck returned an invalid confidence.");
|
|
48
|
+
}
|
|
49
|
+
if (latency !== null && typeof latency !== "number") {
|
|
50
|
+
throw new Error("SomaCheck returned an invalid latency.");
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
status: status,
|
|
54
|
+
verdict: verdict,
|
|
55
|
+
confidence: confidence,
|
|
56
|
+
latency_s: latency,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
async #rpc(name, body) {
|
|
60
|
+
const response = await this.#fetch(`${this.#endpoint}/rest/v1/rpc/${name}`, {
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: {
|
|
63
|
+
apikey: this.#apiKey,
|
|
64
|
+
authorization: `Bearer ${this.#apiKey}`,
|
|
65
|
+
"content-type": "application/json",
|
|
66
|
+
"content-profile": "somacheck_engine",
|
|
67
|
+
"accept-profile": "somacheck_engine",
|
|
68
|
+
},
|
|
69
|
+
body: JSON.stringify(body),
|
|
70
|
+
});
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
throw new Error(`SomaCheck request failed (HTTP ${response.status}).`);
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
return firstRow(await response.json());
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (error instanceof Error && error.message.startsWith("SomaCheck returned")) {
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
throw new Error("SomaCheck returned an unreadable response.");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { SupabaseAgentApi } from "./api.js";
|
|
5
|
+
import { readConfig } from "./config.js";
|
|
6
|
+
import { SUPABASE_PUBLISHABLE_KEY, SUPABASE_URL } from "./constants.js";
|
|
7
|
+
import { linkAgent } from "./link.js";
|
|
8
|
+
import { createVibecheckServer } from "./server.js";
|
|
9
|
+
const api = new SupabaseAgentApi(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY);
|
|
10
|
+
async function main() {
|
|
11
|
+
const args = process.argv.slice(2);
|
|
12
|
+
if (args[0] === "link") {
|
|
13
|
+
if (args.length !== 2) {
|
|
14
|
+
throw new Error("Usage: npx @somacheck/vibecheck link <CODE>");
|
|
15
|
+
}
|
|
16
|
+
await linkAgent(args[1] ?? "", {
|
|
17
|
+
home: homedir(),
|
|
18
|
+
redeem: (code) => api.redeemLink(code),
|
|
19
|
+
output: (line) => process.stdout.write(`${line}\n`),
|
|
20
|
+
});
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (args.length !== 0) {
|
|
24
|
+
throw new Error("Usage: npx @somacheck/vibecheck [link <CODE>]");
|
|
25
|
+
}
|
|
26
|
+
const server = createVibecheckServer({
|
|
27
|
+
api,
|
|
28
|
+
loadToken: async () => (await readConfig(homedir())).token,
|
|
29
|
+
});
|
|
30
|
+
await server.connect(new StdioServerTransport());
|
|
31
|
+
}
|
|
32
|
+
main().catch((error) => {
|
|
33
|
+
const isUsageError = error instanceof Error && error.message.startsWith("Usage:");
|
|
34
|
+
const message = isUsageError
|
|
35
|
+
? error.message
|
|
36
|
+
: process.argv[2] === "link"
|
|
37
|
+
? "Could not link SomaCheck. Check the pairing code and try again."
|
|
38
|
+
: "Vibecheck server could not start.";
|
|
39
|
+
process.stderr.write(`${message}\n`);
|
|
40
|
+
process.exitCode = 1;
|
|
41
|
+
});
|
|
42
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,wBAAwB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,GAAG,GAAG,IAAI,gBAAgB,CAAC,YAAY,EAAE,wBAAwB,CAAC,CAAC;AAEzE,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;QACvB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QACD,MAAM,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE;YAC7B,IAAI,EAAE,OAAO,EAAE;YACf,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;YACtC,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,IAAI,CAAC;SACpD,CAAC,CAAC;QACH,OAAO;IACT,CAAC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IAED,MAAM,MAAM,GAAG,qBAAqB,CAAC;QACnC,GAAG;QACH,SAAS,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,MAAM,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK;KAC3D,CAAC,CAAC;IACH,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClF,MAAM,OAAO,GAAG,YAAY;QAC1B,CAAC,CAAC,KAAK,CAAC,OAAO;QACf,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM;YAC1B,CAAC,CAAC,iEAAiE;YACnE,CAAC,CAAC,mCAAmC,CAAC;IAC1C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;IACrC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export function configPath(home) {
|
|
4
|
+
return join(home, ".sensie", "config.json");
|
|
5
|
+
}
|
|
6
|
+
export async function readConfig(home) {
|
|
7
|
+
let value;
|
|
8
|
+
try {
|
|
9
|
+
value = JSON.parse(await readFile(configPath(home), "utf8"));
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
throw new Error("SomaCheck is not linked. Run: npx @somacheck/vibecheck link <CODE>");
|
|
13
|
+
}
|
|
14
|
+
if (value === null || typeof value !== "object" || !("token" in value)) {
|
|
15
|
+
throw new Error("SomaCheck link configuration is invalid.");
|
|
16
|
+
}
|
|
17
|
+
const token = value.token;
|
|
18
|
+
if (typeof token !== "string" || token.length === 0) {
|
|
19
|
+
throw new Error("SomaCheck link configuration is invalid.");
|
|
20
|
+
}
|
|
21
|
+
return { token };
|
|
22
|
+
}
|
|
23
|
+
export async function writeConfig(home, config) {
|
|
24
|
+
const directory = join(home, ".sensie");
|
|
25
|
+
const destination = configPath(home);
|
|
26
|
+
const temporary = join(directory, `.config.json.${process.pid}.${Date.now()}.tmp`);
|
|
27
|
+
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
28
|
+
await chmod(directory, 0o700);
|
|
29
|
+
try {
|
|
30
|
+
await writeFile(temporary, `${JSON.stringify(config, null, 2)}\n`, {
|
|
31
|
+
encoding: "utf8",
|
|
32
|
+
flag: "wx",
|
|
33
|
+
mode: 0o600,
|
|
34
|
+
});
|
|
35
|
+
await rename(temporary, destination);
|
|
36
|
+
await chmod(destination, 0o600);
|
|
37
|
+
}
|
|
38
|
+
finally {
|
|
39
|
+
await rm(temporary, { force: true });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACjF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAMjC,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC3C,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;IACxF,CAAC;IAED,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC,EAAE,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,MAAM,KAAK,GAAI,KAA6B,CAAC,KAAK,CAAC;IACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,MAAuB;IACrE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACxC,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,gBAAgB,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAEnF,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzD,MAAM,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC9B,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;YACjE,QAAQ,EAAE,MAAM;YAChB,IAAI,EAAE,IAAI;YACV,IAAI,EAAE,KAAK;SACZ,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QACrC,MAAM,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Supabase publishable keys are public client identifiers; authorization remains enforced by RLS/RPCs.
|
|
2
|
+
export const SUPABASE_URL = "https://pbldcmniommltbdwuykk.supabase.co";
|
|
3
|
+
export const SUPABASE_PUBLISHABLE_KEY = "sb_publishable_af-lUNI2FqEcb-oGy-4uxQ_cnm6kY85";
|
|
4
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,uGAAuG;AACvG,MAAM,CAAC,MAAM,YAAY,GAAG,0CAA0C,CAAC;AACvE,MAAM,CAAC,MAAM,wBAAwB,GAAG,gDAAgD,CAAC"}
|
package/dist/link.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { writeConfig } from "./config.js";
|
|
2
|
+
export async function linkAgent(code, dependencies) {
|
|
3
|
+
const normalizedCode = code.trim();
|
|
4
|
+
if (!normalizedCode) {
|
|
5
|
+
throw new Error("Pairing code is required.");
|
|
6
|
+
}
|
|
7
|
+
const token = await dependencies.redeem(normalizedCode);
|
|
8
|
+
await writeConfig(dependencies.home, { token });
|
|
9
|
+
dependencies.output("SomaCheck is linked. Your agent can now call vibecheck.");
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=link.js.map
|
package/dist/link.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.js","sourceRoot":"","sources":["../src/link.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAQ1C,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAY,EAAE,YAA8B;IAC1E,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IACnC,IAAI,CAAC,cAAc,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IACxD,MAAM,WAAW,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IAChD,YAAY,CAAC,MAAM,CAAC,yDAAyD,CAAC,CAAC;AACjF,CAAC"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { performVibecheck } from "./vibecheck.js";
|
|
4
|
+
const outcomeSchema = {
|
|
5
|
+
verdict: z.enum(["aligned", "unaligned", "inconclusive"]),
|
|
6
|
+
confidence: z.number(),
|
|
7
|
+
latency_s: z.number(),
|
|
8
|
+
};
|
|
9
|
+
export function createVibecheckServer(dependencies) {
|
|
10
|
+
const server = new McpServer({ name: "@somacheck/vibecheck", version: "0.1.0" });
|
|
11
|
+
server.registerTool("vibecheck", {
|
|
12
|
+
title: "Vibecheck",
|
|
13
|
+
description: "Ask the person for an embodied answer right now. Blocks while SomaCheck waits for their next wrist gesture.",
|
|
14
|
+
inputSchema: {
|
|
15
|
+
question: z.string().optional().describe("The question to ask the person."),
|
|
16
|
+
timeout_s: z.number().positive().default(120).describe("How long to wait for a gesture, in seconds."),
|
|
17
|
+
},
|
|
18
|
+
outputSchema: outcomeSchema,
|
|
19
|
+
}, async ({ question, timeout_s }) => {
|
|
20
|
+
const startedAt = dependencies.now?.() ?? Date.now();
|
|
21
|
+
try {
|
|
22
|
+
const token = await dependencies.loadToken();
|
|
23
|
+
const run = await performVibecheck({
|
|
24
|
+
api: dependencies.api,
|
|
25
|
+
token,
|
|
26
|
+
...(question === undefined ? {} : { question }),
|
|
27
|
+
timeoutSeconds: timeout_s,
|
|
28
|
+
...(dependencies.now === undefined ? {} : { now: dependencies.now }),
|
|
29
|
+
...(dependencies.sleep === undefined ? {} : { sleep: dependencies.sleep }),
|
|
30
|
+
});
|
|
31
|
+
const structuredContent = { ...run.outcome };
|
|
32
|
+
return {
|
|
33
|
+
content: [{ type: "text", text: run.message }],
|
|
34
|
+
structuredContent,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
const endedAt = dependencies.now?.() ?? Date.now();
|
|
39
|
+
const latencySeconds = Math.max(0, (endedAt - startedAt) / 1_000);
|
|
40
|
+
const outcome = {
|
|
41
|
+
verdict: "inconclusive",
|
|
42
|
+
confidence: 0,
|
|
43
|
+
latency_s: latencySeconds,
|
|
44
|
+
};
|
|
45
|
+
return {
|
|
46
|
+
isError: true,
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: "text",
|
|
50
|
+
text: "Vibecheck could not complete. Check your link and try again.",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
structuredContent: outcome,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return server;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const POLL_INTERVAL_MS = 2_000;
|
|
2
|
+
const defaultSleep = async (milliseconds) => {
|
|
3
|
+
await new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
4
|
+
};
|
|
5
|
+
export async function performVibecheck(options) {
|
|
6
|
+
const timeoutSeconds = options.timeoutSeconds ?? 120;
|
|
7
|
+
if (!Number.isFinite(timeoutSeconds) || timeoutSeconds <= 0) {
|
|
8
|
+
throw new Error("timeout_s must be a positive number.");
|
|
9
|
+
}
|
|
10
|
+
const now = options.now ?? Date.now;
|
|
11
|
+
const sleep = options.sleep ?? defaultSleep;
|
|
12
|
+
const calledAt = now();
|
|
13
|
+
const deadline = calledAt + timeoutSeconds * 1_000;
|
|
14
|
+
const requestId = await options.api.createRequest(options.token, options.question?.trim() || null);
|
|
15
|
+
while (now() < deadline) {
|
|
16
|
+
const waitMilliseconds = Math.min(POLL_INTERVAL_MS, deadline - now());
|
|
17
|
+
await sleep(waitMilliseconds);
|
|
18
|
+
if (now() >= deadline) {
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
const response = await options.api.pollRequest(options.token, requestId);
|
|
22
|
+
if (response.status === "pending") {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (response.status === "answered") {
|
|
26
|
+
if (response.verdict === null || response.confidence === null || response.latency_s === null) {
|
|
27
|
+
throw new Error("SomaCheck returned an incomplete answer.");
|
|
28
|
+
}
|
|
29
|
+
const outcome = {
|
|
30
|
+
verdict: response.verdict,
|
|
31
|
+
confidence: response.confidence,
|
|
32
|
+
latency_s: response.latency_s,
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
outcome,
|
|
36
|
+
message: `Your embodied answer is ${outcome.verdict} (${Math.round(outcome.confidence * 100)}% confidence, ${outcome.latency_s}s).`,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const elapsedSeconds = (now() - calledAt) / 1_000;
|
|
40
|
+
return {
|
|
41
|
+
outcome: { verdict: "inconclusive", confidence: 0, latency_s: elapsedSeconds },
|
|
42
|
+
message: `The vibecheck request ${response.status}. Open SomaCheck and try again.`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
outcome: { verdict: "inconclusive", confidence: 0, latency_s: timeoutSeconds },
|
|
47
|
+
message: `No gesture arrived within ${timeoutSeconds} seconds. Open SomaCheck and try again.`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=vibecheck.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vibecheck.js","sourceRoot":"","sources":["../src/vibecheck.ts"],"names":[],"mappings":"AAmCA,MAAM,gBAAgB,GAAG,KAAK,CAAC;AAE/B,MAAM,YAAY,GAAG,KAAK,EAAE,YAAoB,EAAiB,EAAE;IACjE,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;AAC1E,CAAC,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAyB;IAC9D,MAAM,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,GAAG,CAAC;IACrD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,cAAc,IAAI,CAAC,EAAE,CAAC;QAC5D,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,YAAY,CAAC;IAC5C,MAAM,QAAQ,GAAG,GAAG,EAAE,CAAC;IACvB,MAAM,QAAQ,GAAG,QAAQ,GAAG,cAAc,GAAG,KAAK,CAAC;IACnD,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,CAAC;IAEnG,OAAO,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;QACxB,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,QAAQ,GAAG,GAAG,EAAE,CAAC,CAAC;QACtE,MAAM,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAC9B,IAAI,GAAG,EAAE,IAAI,QAAQ,EAAE,CAAC;YACtB,MAAM;QACR,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QACzE,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAClC,SAAS;QACX,CAAC;QAED,IAAI,QAAQ,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YACnC,IAAI,QAAQ,CAAC,OAAO,KAAK,IAAI,IAAI,QAAQ,CAAC,UAAU,KAAK,IAAI,IAAI,QAAQ,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;gBAC7F,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;YAC9D,CAAC;YACD,MAAM,OAAO,GAAG;gBACd,OAAO,EAAE,QAAQ,CAAC,OAAO;gBACzB,UAAU,EAAE,QAAQ,CAAC,UAAU;gBAC/B,SAAS,EAAE,QAAQ,CAAC,SAAS;aACH,CAAC;YAC7B,OAAO;gBACL,OAAO;gBACP,OAAO,EAAE,2BAA2B,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,GAAG,GAAG,CAAC,iBAAiB,OAAO,CAAC,SAAS,KAAK;aACpI,CAAC;QACJ,CAAC;QAED,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,GAAG,KAAK,CAAC;QAClD,OAAO;YACL,OAAO,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE;YAC9E,OAAO,EAAE,yBAAyB,QAAQ,CAAC,MAAM,iCAAiC;SACnF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,cAAc,EAAE;QAC9E,OAAO,EAAE,6BAA6B,cAAc,yCAAyC;KAC9F,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@somacheck/vibecheck",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Ask your embodied sense for an answer through SomaCheck.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"vibecheck": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/server.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc -p tsconfig.json",
|
|
17
|
+
"test": "npm run build && node --import tsx --test test/api.test.ts && node --import tsx --test test/link.test.ts && node --import tsx --test test/vibecheck.test.ts && node --test test/server.test.mjs",
|
|
18
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
19
|
+
"prepack": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=20"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
26
|
+
"zod": "^3.25.76"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^24.0.0",
|
|
30
|
+
"tsx": "^4.23.1",
|
|
31
|
+
"typescript": "^5.9.3"
|
|
32
|
+
},
|
|
33
|
+
"license": "UNLICENSED",
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
}
|
|
37
|
+
}
|