symposium-mcp 0.2.0 → 0.3.1
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/README.md +26 -7
- package/dist/mcp.js +870 -131
- package/package.json +1 -1
package/dist/mcp.js
CHANGED
|
@@ -3231,8 +3231,8 @@ var require_utils = __commonJS({
|
|
|
3231
3231
|
}
|
|
3232
3232
|
return ind;
|
|
3233
3233
|
}
|
|
3234
|
-
function removeDotSegments(
|
|
3235
|
-
let input2 =
|
|
3234
|
+
function removeDotSegments(path5) {
|
|
3235
|
+
let input2 = path5;
|
|
3236
3236
|
const output2 = [];
|
|
3237
3237
|
let nextSlash = -1;
|
|
3238
3238
|
let len = 0;
|
|
@@ -3324,9 +3324,9 @@ var require_utils = __commonJS({
|
|
|
3324
3324
|
const hex3 = input2.slice(i + 1, i + 3);
|
|
3325
3325
|
if (isHexPair(hex3)) {
|
|
3326
3326
|
const normalizedHex = hex3.toUpperCase();
|
|
3327
|
-
const
|
|
3328
|
-
if (decodeUnreserved && isUnreserved(
|
|
3329
|
-
output2 +=
|
|
3327
|
+
const decoded2 = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3328
|
+
if (decodeUnreserved && isUnreserved(decoded2)) {
|
|
3329
|
+
output2 += decoded2;
|
|
3330
3330
|
} else {
|
|
3331
3331
|
output2 += "%" + normalizedHex;
|
|
3332
3332
|
}
|
|
@@ -3345,9 +3345,9 @@ var require_utils = __commonJS({
|
|
|
3345
3345
|
const hex3 = input2.slice(i + 1, i + 3);
|
|
3346
3346
|
if (isHexPair(hex3)) {
|
|
3347
3347
|
const normalizedHex = hex3.toUpperCase();
|
|
3348
|
-
const
|
|
3349
|
-
if (
|
|
3350
|
-
output2 +=
|
|
3348
|
+
const decoded2 = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
3349
|
+
if (decoded2 !== "." && isUnreserved(decoded2)) {
|
|
3350
|
+
output2 += decoded2;
|
|
3351
3351
|
} else {
|
|
3352
3352
|
output2 += "%" + normalizedHex;
|
|
3353
3353
|
}
|
|
@@ -3484,8 +3484,8 @@ var require_schemes = __commonJS({
|
|
|
3484
3484
|
wsComponent.secure = void 0;
|
|
3485
3485
|
}
|
|
3486
3486
|
if (wsComponent.resourceName) {
|
|
3487
|
-
const [
|
|
3488
|
-
wsComponent.path =
|
|
3487
|
+
const [path5, query] = wsComponent.resourceName.split("?");
|
|
3488
|
+
wsComponent.path = path5 && path5 !== "/" ? path5 : void 0;
|
|
3489
3489
|
wsComponent.query = query;
|
|
3490
3490
|
wsComponent.resourceName = void 0;
|
|
3491
3491
|
}
|
|
@@ -6269,7 +6269,7 @@ var require_discriminator = __commonJS({
|
|
|
6269
6269
|
error: error62,
|
|
6270
6270
|
code(cxt) {
|
|
6271
6271
|
const { gen, data, schema, parentSchema, it } = cxt;
|
|
6272
|
-
const { oneOf } = parentSchema;
|
|
6272
|
+
const { oneOf: oneOf2 } = parentSchema;
|
|
6273
6273
|
if (!it.opts.discriminator) {
|
|
6274
6274
|
throw new Error("discriminator: requires discriminator option");
|
|
6275
6275
|
}
|
|
@@ -6278,7 +6278,7 @@ var require_discriminator = __commonJS({
|
|
|
6278
6278
|
throw new Error("discriminator: requires propertyName");
|
|
6279
6279
|
if (schema.mapping)
|
|
6280
6280
|
throw new Error("discriminator: mapping is not supported");
|
|
6281
|
-
if (!
|
|
6281
|
+
if (!oneOf2)
|
|
6282
6282
|
throw new Error("discriminator: requires oneOf keyword");
|
|
6283
6283
|
const valid = gen.let("valid", false);
|
|
6284
6284
|
const tag = gen.const("tag", (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(tagName)}`);
|
|
@@ -6306,8 +6306,8 @@ var require_discriminator = __commonJS({
|
|
|
6306
6306
|
const oneOfMapping = {};
|
|
6307
6307
|
const topRequired = hasRequired(parentSchema);
|
|
6308
6308
|
let tagRequired = true;
|
|
6309
|
-
for (let i = 0; i <
|
|
6310
|
-
let sch =
|
|
6309
|
+
for (let i = 0; i < oneOf2.length; i++) {
|
|
6310
|
+
let sch = oneOf2[i];
|
|
6311
6311
|
if ((sch === null || sch === void 0 ? void 0 : sch.$ref) && !(0, util_1.schemaHasRulesButRef)(sch, it.self.RULES)) {
|
|
6312
6312
|
const ref = sch.$ref;
|
|
6313
6313
|
sch = compile_1.resolveRef.call(it.self, it.schemaEnv.root, it.baseId, ref);
|
|
@@ -6898,7 +6898,7 @@ var require_dist = __commonJS({
|
|
|
6898
6898
|
});
|
|
6899
6899
|
|
|
6900
6900
|
// src/mcp.ts
|
|
6901
|
-
import { hostname as
|
|
6901
|
+
import { hostname as hostname4 } from "node:os";
|
|
6902
6902
|
|
|
6903
6903
|
// ../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/helpers/util.js
|
|
6904
6904
|
var util;
|
|
@@ -7274,8 +7274,8 @@ function getErrorMap() {
|
|
|
7274
7274
|
|
|
7275
7275
|
// ../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/helpers/parseUtil.js
|
|
7276
7276
|
var makeIssue = (params) => {
|
|
7277
|
-
const { data, path:
|
|
7278
|
-
const fullPath = [...
|
|
7277
|
+
const { data, path: path5, errorMaps, issueData } = params;
|
|
7278
|
+
const fullPath = [...path5, ...issueData.path || []];
|
|
7279
7279
|
const fullIssue = {
|
|
7280
7280
|
...issueData,
|
|
7281
7281
|
path: fullPath
|
|
@@ -7390,11 +7390,11 @@ var errorUtil;
|
|
|
7390
7390
|
|
|
7391
7391
|
// ../node_modules/.pnpm/zod@4.6.7/node_modules/zod/v3/types.js
|
|
7392
7392
|
var ParseInputLazyPath = class {
|
|
7393
|
-
constructor(parent, value,
|
|
7393
|
+
constructor(parent, value, path5, key) {
|
|
7394
7394
|
this._cachedPath = [];
|
|
7395
7395
|
this.parent = parent;
|
|
7396
7396
|
this.data = value;
|
|
7397
|
-
this._path =
|
|
7397
|
+
this._path = path5;
|
|
7398
7398
|
this._key = key;
|
|
7399
7399
|
}
|
|
7400
7400
|
get path() {
|
|
@@ -7790,14 +7790,14 @@ function isValidJWT(jwt2, alg) {
|
|
|
7790
7790
|
if (!header)
|
|
7791
7791
|
return false;
|
|
7792
7792
|
const base643 = header.replace(/-/g, "+").replace(/_/g, "/").padEnd(header.length + (4 - header.length % 4) % 4, "=");
|
|
7793
|
-
const
|
|
7794
|
-
if (typeof
|
|
7793
|
+
const decoded2 = JSON.parse(atob(base643));
|
|
7794
|
+
if (typeof decoded2 !== "object" || decoded2 === null)
|
|
7795
7795
|
return false;
|
|
7796
|
-
if ("typ" in
|
|
7796
|
+
if ("typ" in decoded2 && decoded2?.typ !== "JWT")
|
|
7797
7797
|
return false;
|
|
7798
|
-
if (!
|
|
7798
|
+
if (!decoded2.alg)
|
|
7799
7799
|
return false;
|
|
7800
|
-
if (alg &&
|
|
7800
|
+
if (alg && decoded2.alg !== alg)
|
|
7801
7801
|
return false;
|
|
7802
7802
|
return true;
|
|
7803
7803
|
} catch {
|
|
@@ -11349,10 +11349,10 @@ function mergeDefs(...defs) {
|
|
|
11349
11349
|
function cloneDef(schema) {
|
|
11350
11350
|
return mergeDefs(schema._zod.def);
|
|
11351
11351
|
}
|
|
11352
|
-
function getElementAtPath(obj,
|
|
11353
|
-
if (!
|
|
11352
|
+
function getElementAtPath(obj, path5) {
|
|
11353
|
+
if (!path5)
|
|
11354
11354
|
return obj;
|
|
11355
|
-
return
|
|
11355
|
+
return path5.reduce((acc, key) => acc?.[key], obj);
|
|
11356
11356
|
}
|
|
11357
11357
|
function promiseAllObject(promisesObj) {
|
|
11358
11358
|
const keys = Object.keys(promisesObj);
|
|
@@ -11692,11 +11692,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
11692
11692
|
}
|
|
11693
11693
|
return false;
|
|
11694
11694
|
}
|
|
11695
|
-
function prefixIssues(
|
|
11695
|
+
function prefixIssues(path5, issues) {
|
|
11696
11696
|
return issues.map((iss) => {
|
|
11697
11697
|
var _a3;
|
|
11698
11698
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
11699
|
-
iss.path.unshift(
|
|
11699
|
+
iss.path.unshift(path5);
|
|
11700
11700
|
return iss;
|
|
11701
11701
|
});
|
|
11702
11702
|
}
|
|
@@ -12158,16 +12158,16 @@ function flattenError(error62, mapper = (issue2) => issue2.message) {
|
|
|
12158
12158
|
}
|
|
12159
12159
|
function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
12160
12160
|
const fieldErrors = { _errors: [] };
|
|
12161
|
-
const processError = (error63,
|
|
12161
|
+
const processError = (error63, path5 = []) => {
|
|
12162
12162
|
for (const issue2 of error63.issues) {
|
|
12163
12163
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
12164
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
12164
|
+
issue2.errors.map((issues) => processError({ issues }, [...path5, ...issue2.path]));
|
|
12165
12165
|
} else if (issue2.code === "invalid_key") {
|
|
12166
|
-
processError({ issues: issue2.issues }, [...
|
|
12166
|
+
processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
|
|
12167
12167
|
} else if (issue2.code === "invalid_element") {
|
|
12168
|
-
processError({ issues: issue2.issues }, [...
|
|
12168
|
+
processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
|
|
12169
12169
|
} else {
|
|
12170
|
-
const fullpath = [...
|
|
12170
|
+
const fullpath = [...path5, ...issue2.path];
|
|
12171
12171
|
if (fullpath.length === 0) {
|
|
12172
12172
|
fieldErrors._errors.push(mapper(issue2));
|
|
12173
12173
|
} else {
|
|
@@ -12206,17 +12206,17 @@ function formatError(error62, mapper = (issue2) => issue2.message) {
|
|
|
12206
12206
|
}
|
|
12207
12207
|
function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
12208
12208
|
const result = { errors: [] };
|
|
12209
|
-
const processError = (error63,
|
|
12209
|
+
const processError = (error63, path5 = []) => {
|
|
12210
12210
|
var _a3;
|
|
12211
12211
|
for (const issue2 of error63.issues) {
|
|
12212
12212
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
12213
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
12213
|
+
issue2.errors.map((issues) => processError({ issues }, [...path5, ...issue2.path]));
|
|
12214
12214
|
} else if (issue2.code === "invalid_key") {
|
|
12215
|
-
processError({ issues: issue2.issues }, [...
|
|
12215
|
+
processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
|
|
12216
12216
|
} else if (issue2.code === "invalid_element") {
|
|
12217
|
-
processError({ issues: issue2.issues }, [...
|
|
12217
|
+
processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
|
|
12218
12218
|
} else {
|
|
12219
|
-
const fullpath = [...
|
|
12219
|
+
const fullpath = [...path5, ...issue2.path];
|
|
12220
12220
|
if (fullpath.length === 0) {
|
|
12221
12221
|
result.errors.push(mapper(issue2));
|
|
12222
12222
|
continue;
|
|
@@ -12255,8 +12255,8 @@ function treeifyError(error62, mapper = (issue2) => issue2.message) {
|
|
|
12255
12255
|
}
|
|
12256
12256
|
function toDotPath(_path) {
|
|
12257
12257
|
const segs = [];
|
|
12258
|
-
const
|
|
12259
|
-
for (const seg of
|
|
12258
|
+
const path5 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
12259
|
+
for (const seg of path5) {
|
|
12260
12260
|
if (typeof seg === "number")
|
|
12261
12261
|
segs.push(`[${seg}]`);
|
|
12262
12262
|
else if (typeof seg === "symbol")
|
|
@@ -13358,9 +13358,9 @@ var asciiTabOrNewline = /[\t\n\r]/g;
|
|
|
13358
13358
|
function stripTabAndNewline(value) {
|
|
13359
13359
|
return value.replace(asciiTabOrNewline, "");
|
|
13360
13360
|
}
|
|
13361
|
-
function urlHostnameOk(url2,
|
|
13362
|
-
|
|
13363
|
-
return
|
|
13361
|
+
function urlHostnameOk(url2, hostname5) {
|
|
13362
|
+
hostname5.lastIndex = 0;
|
|
13363
|
+
return hostname5.test(url2.hostname);
|
|
13364
13364
|
}
|
|
13365
13365
|
function urlProtocolOk(url2, protocol) {
|
|
13366
13366
|
protocol.lastIndex = 0;
|
|
@@ -27807,11 +27807,11 @@ function normalizeObjectSchema(schema) {
|
|
|
27807
27807
|
}
|
|
27808
27808
|
return void 0;
|
|
27809
27809
|
}
|
|
27810
|
-
function getDotPath(
|
|
27811
|
-
if (
|
|
27810
|
+
function getDotPath(path5) {
|
|
27811
|
+
if (path5.length === 0) {
|
|
27812
27812
|
return "object root";
|
|
27813
27813
|
}
|
|
27814
|
-
return
|
|
27814
|
+
return path5.reduce((acc, seg, index) => {
|
|
27815
27815
|
if (index === 0) {
|
|
27816
27816
|
return String(seg);
|
|
27817
27817
|
}
|
|
@@ -30038,13 +30038,13 @@ function resolveRef(ref, ctx) {
|
|
|
30038
30038
|
if (!ref.startsWith("#")) {
|
|
30039
30039
|
throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
|
|
30040
30040
|
}
|
|
30041
|
-
const
|
|
30042
|
-
if (
|
|
30041
|
+
const path5 = ref.slice(1).split("/").filter(Boolean);
|
|
30042
|
+
if (path5.length === 0) {
|
|
30043
30043
|
return ctx.rootSchema;
|
|
30044
30044
|
}
|
|
30045
30045
|
const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
|
|
30046
|
-
if (
|
|
30047
|
-
const key =
|
|
30046
|
+
if (path5[0] === defsKey) {
|
|
30047
|
+
const key = path5[1] === void 0 ? void 0 : decodeJSONPointerSegment(path5[1]);
|
|
30048
30048
|
if (!key || !ctx.defs[key]) {
|
|
30049
30049
|
throw new Error(`Reference not found: ${ref}`);
|
|
30050
30050
|
}
|
|
@@ -33507,13 +33507,13 @@ var zodToJsonSchema = (schema, options) => {
|
|
|
33507
33507
|
}, true) ?? parseAnyDef(refs)
|
|
33508
33508
|
}), {}) : void 0;
|
|
33509
33509
|
const name = typeof options === "string" ? options : options?.nameStrategy === "title" ? void 0 : options?.name;
|
|
33510
|
-
const
|
|
33510
|
+
const main2 = parseDef(schema._def, name === void 0 ? refs : {
|
|
33511
33511
|
...refs,
|
|
33512
33512
|
currentPath: [...refs.basePath, refs.definitionPath, name]
|
|
33513
33513
|
}, false) ?? parseAnyDef(refs);
|
|
33514
33514
|
const title = typeof options === "object" && options.name !== void 0 && options.nameStrategy === "title" ? options.name : void 0;
|
|
33515
33515
|
if (title !== void 0) {
|
|
33516
|
-
|
|
33516
|
+
main2.title = title;
|
|
33517
33517
|
}
|
|
33518
33518
|
if (refs.flags.hasReferencedOpenAiAnyType) {
|
|
33519
33519
|
if (!definitions) {
|
|
@@ -33534,9 +33534,9 @@ var zodToJsonSchema = (schema, options) => {
|
|
|
33534
33534
|
}
|
|
33535
33535
|
}
|
|
33536
33536
|
const combined = name === void 0 ? definitions ? {
|
|
33537
|
-
...
|
|
33537
|
+
...main2,
|
|
33538
33538
|
[refs.definitionPath]: definitions
|
|
33539
|
-
} :
|
|
33539
|
+
} : main2 : {
|
|
33540
33540
|
$ref: [
|
|
33541
33541
|
...refs.$refStrategy === "relative" ? [] : refs.basePath,
|
|
33542
33542
|
refs.definitionPath,
|
|
@@ -33544,7 +33544,7 @@ var zodToJsonSchema = (schema, options) => {
|
|
|
33544
33544
|
].join("/"),
|
|
33545
33545
|
[refs.definitionPath]: {
|
|
33546
33546
|
...definitions,
|
|
33547
|
-
[name]:
|
|
33547
|
+
[name]: main2
|
|
33548
33548
|
}
|
|
33549
33549
|
};
|
|
33550
33550
|
if (refs.target === "jsonSchema7") {
|
|
@@ -34190,7 +34190,7 @@ var Protocol = class {
|
|
|
34190
34190
|
}
|
|
34191
34191
|
};
|
|
34192
34192
|
}
|
|
34193
|
-
const cancel = (
|
|
34193
|
+
const cancel = (reason2) => {
|
|
34194
34194
|
this._responseHandlers.delete(messageId);
|
|
34195
34195
|
this._progressHandlers.delete(messageId);
|
|
34196
34196
|
this._cleanupTimeout(messageId);
|
|
@@ -34199,10 +34199,10 @@ var Protocol = class {
|
|
|
34199
34199
|
method: "notifications/cancelled",
|
|
34200
34200
|
params: {
|
|
34201
34201
|
requestId: messageId,
|
|
34202
|
-
reason: String(
|
|
34202
|
+
reason: String(reason2)
|
|
34203
34203
|
}
|
|
34204
34204
|
}, { relatedRequestId, resumptionToken, onresumptiontoken }).catch((error63) => this._onerror(new Error(`Failed to send cancellation: ${error63}`)));
|
|
34205
|
-
const error62 =
|
|
34205
|
+
const error62 = reason2 instanceof McpError ? reason2 : new McpError(ErrorCode.RequestTimeout, String(reason2));
|
|
34206
34206
|
reject(error62);
|
|
34207
34207
|
};
|
|
34208
34208
|
this._responseHandlers.set(messageId, (response) => {
|
|
@@ -36301,6 +36301,7 @@ var StdioServerTransport = class {
|
|
|
36301
36301
|
|
|
36302
36302
|
// ../lib/meeting/protocol.ts
|
|
36303
36303
|
var MAX_DOC_PACKET_BYTES = 12 * 1024;
|
|
36304
|
+
var MAIN_DOC_ID = "main";
|
|
36304
36305
|
var DOC_EDIT_OPS = ["append", "replace_section", "replace_all"];
|
|
36305
36306
|
var MAX_WAIT_SECONDS = 50;
|
|
36306
36307
|
var MAX_SURFACE_HTML_BYTES = 512 * 1024;
|
|
@@ -36315,6 +36316,15 @@ function agentIdOfKey(key) {
|
|
|
36315
36316
|
}
|
|
36316
36317
|
var PROPOSAL_PROGRESS = ["working", "done", "failed"];
|
|
36317
36318
|
var REQUEST_STATUSES = ["acknowledged", "working", "done", "failed"];
|
|
36319
|
+
var WORK_KINDS = [
|
|
36320
|
+
"thinking",
|
|
36321
|
+
"searching",
|
|
36322
|
+
"writing",
|
|
36323
|
+
"drawing",
|
|
36324
|
+
"charting",
|
|
36325
|
+
"building"
|
|
36326
|
+
];
|
|
36327
|
+
var AGENT_WORK_TTL_MS = 3 * 6e4;
|
|
36318
36328
|
var PRESENCE_IDLE_AFTER_MS = 5 * 6e4;
|
|
36319
36329
|
var SPACE_EVENT_TYPES = [
|
|
36320
36330
|
"speech",
|
|
@@ -36324,6 +36334,7 @@ var SPACE_EVENT_TYPES = [
|
|
|
36324
36334
|
"proposal",
|
|
36325
36335
|
"proposal_decided",
|
|
36326
36336
|
"doc_changed",
|
|
36337
|
+
"doc_removed",
|
|
36327
36338
|
"ui_event",
|
|
36328
36339
|
"state_changed",
|
|
36329
36340
|
"surface_changed",
|
|
@@ -36349,13 +36360,13 @@ function fromBase64Url(code) {
|
|
|
36349
36360
|
function findConnect(pasted) {
|
|
36350
36361
|
for (const [, body] of pasted.matchAll(/symc_([A-Za-z0-9_-]{20,})/g)) {
|
|
36351
36362
|
try {
|
|
36352
|
-
const
|
|
36353
|
-
if (typeof
|
|
36363
|
+
const connect3 = JSON.parse(fromBase64Url(body));
|
|
36364
|
+
if (typeof connect3?.api === "string" && typeof connect3?.id === "string" && typeof connect3?.secret === "string") {
|
|
36354
36365
|
return {
|
|
36355
|
-
api:
|
|
36356
|
-
id:
|
|
36357
|
-
secret:
|
|
36358
|
-
...typeof
|
|
36366
|
+
api: connect3.api,
|
|
36367
|
+
id: connect3.id,
|
|
36368
|
+
secret: connect3.secret,
|
|
36369
|
+
...typeof connect3.space === "string" ? { space: connect3.space } : {}
|
|
36359
36370
|
};
|
|
36360
36371
|
}
|
|
36361
36372
|
} catch {
|
|
@@ -36545,19 +36556,36 @@ async function call(url2, init) {
|
|
|
36545
36556
|
}
|
|
36546
36557
|
return resp.status === 204 ? void 0 : await resp.json();
|
|
36547
36558
|
}
|
|
36548
|
-
function redeemConnect(
|
|
36549
|
-
return call(`${
|
|
36559
|
+
function redeemConnect(connect3, about) {
|
|
36560
|
+
return call(`${connect3.api}/agents/claim`, {
|
|
36561
|
+
method: "POST",
|
|
36562
|
+
body: { id: connect3.id, secret: connect3.secret, ...about }
|
|
36563
|
+
});
|
|
36564
|
+
}
|
|
36565
|
+
async function redeemConnectFor(connect3, agents) {
|
|
36566
|
+
const connected = await call(`${connect3.api}/agents/claim`, {
|
|
36550
36567
|
method: "POST",
|
|
36551
|
-
body: { id:
|
|
36568
|
+
body: { id: connect3.id, secret: connect3.secret, agents }
|
|
36552
36569
|
});
|
|
36570
|
+
if (connected.all?.length !== agents.length) {
|
|
36571
|
+
throw new Error("This Symposium site cannot connect several agents with one code yet.");
|
|
36572
|
+
}
|
|
36573
|
+
return { person: connected.person, all: connected.all };
|
|
36553
36574
|
}
|
|
36554
|
-
var AccountClient = class {
|
|
36575
|
+
var AccountClient = class _AccountClient {
|
|
36555
36576
|
constructor(api, token) {
|
|
36556
36577
|
this.api = api;
|
|
36557
36578
|
this.token = token;
|
|
36558
36579
|
}
|
|
36559
36580
|
api;
|
|
36560
36581
|
token;
|
|
36582
|
+
/**
|
|
36583
|
+
* The same credential at another site. Only for a site that shares this one's
|
|
36584
|
+
* accounts, which is what a summons naming it (`Summons.api`) says.
|
|
36585
|
+
*/
|
|
36586
|
+
at(api) {
|
|
36587
|
+
return api === this.api ? this : new _AccountClient(api, this.token);
|
|
36588
|
+
}
|
|
36561
36589
|
/** The spaces this agent's person owns or is a member of, most recently active first. */
|
|
36562
36590
|
async spaces() {
|
|
36563
36591
|
return (await call(`${this.api}/agents/me/spaces`, {
|
|
@@ -36606,6 +36634,18 @@ var AccountClient = class {
|
|
|
36606
36634
|
}
|
|
36607
36635
|
}
|
|
36608
36636
|
};
|
|
36637
|
+
function summonsApi(summons, fallback) {
|
|
36638
|
+
if (!summons.api) return fallback;
|
|
36639
|
+
try {
|
|
36640
|
+
const url2 = new URL(summons.api);
|
|
36641
|
+
const local = /^(localhost|127\.0\.0\.1|\[::1\]|.+\.localhost)$/.test(url2.hostname);
|
|
36642
|
+
if (url2.protocol !== "https:" && !(url2.protocol === "http:" && local)) return fallback;
|
|
36643
|
+
if (url2.username || url2.password || url2.search || url2.hash) return fallback;
|
|
36644
|
+
return url2.href.replace(/\/$/, "");
|
|
36645
|
+
} catch {
|
|
36646
|
+
return fallback;
|
|
36647
|
+
}
|
|
36648
|
+
}
|
|
36609
36649
|
var SUMMONS_FRESH_MS = 10 * 60 * 1e3;
|
|
36610
36650
|
function chooseSpace(spaces, wanted, summoned) {
|
|
36611
36651
|
const query = wanted?.trim().toLowerCase();
|
|
@@ -36633,6 +36673,8 @@ var SpaceClient = class {
|
|
|
36633
36673
|
space;
|
|
36634
36674
|
/** Origin of the Symposium app this space lives on. */
|
|
36635
36675
|
appOrigin;
|
|
36676
|
+
/** Base URL of that app's REST API, as the invite gave it (no trailing slash). */
|
|
36677
|
+
api;
|
|
36636
36678
|
base;
|
|
36637
36679
|
key;
|
|
36638
36680
|
cursor;
|
|
@@ -36641,10 +36683,11 @@ var SpaceClient = class {
|
|
|
36641
36683
|
this.space = space2;
|
|
36642
36684
|
this.key = key;
|
|
36643
36685
|
this.appOrigin = new URL(api).origin;
|
|
36644
|
-
this.
|
|
36686
|
+
this.api = api.replace(/\/$/, "");
|
|
36687
|
+
this.base = `${this.api}/spaces/${encodeURIComponent(space2)}`;
|
|
36645
36688
|
}
|
|
36646
|
-
async request(method,
|
|
36647
|
-
const resp = await fetch(this.base +
|
|
36689
|
+
async request(method, path5, body, timeoutMs = 3e4) {
|
|
36690
|
+
const resp = await fetch(this.base + path5, {
|
|
36648
36691
|
method,
|
|
36649
36692
|
headers: {
|
|
36650
36693
|
Authorization: `Bearer ${this.key}`,
|
|
@@ -36652,11 +36695,16 @@ var SpaceClient = class {
|
|
|
36652
36695
|
},
|
|
36653
36696
|
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
36654
36697
|
signal: AbortSignal.timeout(timeoutMs)
|
|
36698
|
+
}).catch((e) => {
|
|
36699
|
+
const why = e instanceof Error ? e.message : String(e);
|
|
36700
|
+
throw new Error(
|
|
36701
|
+
`Could not reach ${this.appOrigin} (${why}). If that site is no longer running, call join_space to join again.`
|
|
36702
|
+
);
|
|
36655
36703
|
});
|
|
36656
36704
|
if (!resp.ok) {
|
|
36657
36705
|
const detail = await resp.json().catch(() => null);
|
|
36658
36706
|
throw new Error(
|
|
36659
|
-
`${method} ${
|
|
36707
|
+
`${method} ${path5} failed (${resp.status}): ${detail?.error ?? resp.statusText}`
|
|
36660
36708
|
);
|
|
36661
36709
|
}
|
|
36662
36710
|
return resp.status === 204 ? void 0 : await resp.json();
|
|
@@ -36705,23 +36753,46 @@ var SpaceClient = class {
|
|
|
36705
36753
|
postMessage(text2) {
|
|
36706
36754
|
return this.request("POST", "/timeline", { text: text2 });
|
|
36707
36755
|
}
|
|
36708
|
-
// --- shared
|
|
36709
|
-
/** The space's shared
|
|
36710
|
-
async
|
|
36711
|
-
return (await this.request("GET", "/
|
|
36756
|
+
// --- shared documents ------------------------------------------------------------
|
|
36757
|
+
/** The space's shared documents, the one it started with (`main`) first. */
|
|
36758
|
+
async listDocuments() {
|
|
36759
|
+
return (await this.request("GET", "/docs")).docs;
|
|
36760
|
+
}
|
|
36761
|
+
/** Add a document to the space, optionally with its first text in Markdown. */
|
|
36762
|
+
createDocument(input2) {
|
|
36763
|
+
return this.request("POST", "/docs", input2);
|
|
36712
36764
|
}
|
|
36713
36765
|
/**
|
|
36714
|
-
*
|
|
36766
|
+
* One of the shared documents, as Markdown (`main` unless you name another). People see
|
|
36767
|
+
* this agent on the document's tab for a little while afterwards.
|
|
36768
|
+
*/
|
|
36769
|
+
async readDocument(id = MAIN_DOC_ID) {
|
|
36770
|
+
return (await this.request("GET", `/docs/${encodeURIComponent(id)}`)).markdown;
|
|
36771
|
+
}
|
|
36772
|
+
/**
|
|
36773
|
+
* Edit a shared document. People may be typing in it at the same time, so prefer
|
|
36715
36774
|
* `append` or `replace_section` (which only touch part of it) to `replace_all`.
|
|
36716
|
-
*
|
|
36775
|
+
* What the edit adds or changes is shown to everyone as written by this agent, until a
|
|
36776
|
+
* person edits it. Returns the document after the edit, as Markdown.
|
|
36717
36777
|
*/
|
|
36718
|
-
async editDocument(edit) {
|
|
36719
|
-
return (await this.request(
|
|
36778
|
+
async editDocument(edit, id = MAIN_DOC_ID) {
|
|
36779
|
+
return (await this.request(
|
|
36780
|
+
"POST",
|
|
36781
|
+
`/docs/${encodeURIComponent(id)}/edits`,
|
|
36782
|
+
edit
|
|
36783
|
+
)).markdown;
|
|
36784
|
+
}
|
|
36785
|
+
renameDocument(id, title) {
|
|
36786
|
+
return this.request("PATCH", `/docs/${encodeURIComponent(id)}`, { title });
|
|
36787
|
+
}
|
|
36788
|
+
/** Remove a document this agent made. The first document (`main`) cannot be removed. */
|
|
36789
|
+
removeDocument(id) {
|
|
36790
|
+
return this.request("DELETE", `/docs/${encodeURIComponent(id)}`);
|
|
36720
36791
|
}
|
|
36721
36792
|
// --- proposals -------------------------------------------------------------------
|
|
36722
36793
|
/**
|
|
36723
36794
|
* Offer to do something and wait for a person to approve. Shows as a card in the
|
|
36724
|
-
*
|
|
36795
|
+
* conversation. Do not act until a `proposal_decided` event arrives with this
|
|
36725
36796
|
* proposal's `seq` and decision `approved`.
|
|
36726
36797
|
*/
|
|
36727
36798
|
propose(text2, options = {}) {
|
|
@@ -36733,12 +36804,16 @@ var SpaceClient = class {
|
|
|
36733
36804
|
}
|
|
36734
36805
|
// --- attention -------------------------------------------------------------------
|
|
36735
36806
|
/**
|
|
36736
|
-
* Bring everyone's stage to one tab: a surface (yours or anyone's), or
|
|
36737
|
-
*
|
|
36738
|
-
*
|
|
36807
|
+
* Bring everyone's stage to one tab: a surface (yours or anyone's), a document or
|
|
36808
|
+
* Agents. People are switched to it, with a note saying you did it and a way to go
|
|
36809
|
+
* back. Use it when you have put up something everyone should look at now. The
|
|
36810
|
+
* conversation is a column beside the stage, not a tab: that target opens it for
|
|
36811
|
+
* anyone who has closed it. `size` asks for it large: `expanded` gives the stage the
|
|
36812
|
+
* screen, `alone` opens a surface on its own instead of as a tile among the others.
|
|
36813
|
+
* Nobody's stage is made smaller.
|
|
36739
36814
|
*/
|
|
36740
|
-
bringEveryoneTo(target) {
|
|
36741
|
-
return this.request("POST", "/focus", { target });
|
|
36815
|
+
bringEveryoneTo(target, size) {
|
|
36816
|
+
return this.request("POST", "/focus", { target, size });
|
|
36742
36817
|
}
|
|
36743
36818
|
// --- requests --------------------------------------------------------------------
|
|
36744
36819
|
/** This agent's id, as it appears in `request` events' `to` list. */
|
|
@@ -36749,10 +36824,23 @@ var SpaceClient = class {
|
|
|
36749
36824
|
* Tell everyone in the space how you are getting on with a request addressed to you
|
|
36750
36825
|
* (a `request` event; its `seq` is the request id). People see this next to the
|
|
36751
36826
|
* request: `acknowledged` as soon as you have seen it, `working` (with a short note
|
|
36752
|
-
* on what you are doing) while you act, then `done` or `failed`.
|
|
36827
|
+
* on what you are doing) while you act, then `done` or `failed`. `surfaceId` says
|
|
36828
|
+
* which surface the work is on: people see this agent on that tab meanwhile. `doing`
|
|
36829
|
+
* says what kind of work it is, which the agent's figure shows. `doc` says which shared
|
|
36830
|
+
* document the work is in, and under which heading: people see this agent on that
|
|
36831
|
+
* document's tab, and beside that section in the text.
|
|
36832
|
+
*/
|
|
36833
|
+
updateRequest(id, status, note, surfaceId, doing, doc) {
|
|
36834
|
+
return this.request("PATCH", `/requests/${id}`, { status, note, surfaceId, doing, ...doc });
|
|
36835
|
+
}
|
|
36836
|
+
/**
|
|
36837
|
+
* Show the space what this agent is working on when it is not an answer to a request.
|
|
36838
|
+
* Its figure plays the matching animation for everyone, on the surface's or the
|
|
36839
|
+
* document's tab if one is named. Say it again to renew or change it (it lapses after a few minutes), and
|
|
36840
|
+
* pass `done` when finished. `key` tells several pieces of work apart.
|
|
36753
36841
|
*/
|
|
36754
|
-
|
|
36755
|
-
return this.request("
|
|
36842
|
+
setWork(work) {
|
|
36843
|
+
return this.request("PUT", "/work", work);
|
|
36756
36844
|
}
|
|
36757
36845
|
// --- events ----------------------------------------------------------------------
|
|
36758
36846
|
/** How far through the event log this client has read: save it to carry on later. */
|
|
@@ -36852,16 +36940,73 @@ function agentName(client2, configured = process.env.SYMPOSIUM_AGENT_NAME) {
|
|
|
36852
36940
|
return words.map((w) => w[0].toUpperCase() + w.slice(1)).join(" ") || "Agent";
|
|
36853
36941
|
}
|
|
36854
36942
|
|
|
36943
|
+
// src/setup.ts
|
|
36944
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
36945
|
+
import { copyFileSync, existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
36946
|
+
import { homedir as homedir2, hostname as hostname3 } from "node:os";
|
|
36947
|
+
import path3 from "node:path";
|
|
36948
|
+
import { createInterface } from "node:readline/promises";
|
|
36949
|
+
import { fileURLToPath } from "node:url";
|
|
36950
|
+
|
|
36951
|
+
// src/command.ts
|
|
36952
|
+
import { existsSync } from "node:fs";
|
|
36953
|
+
import path from "node:path";
|
|
36954
|
+
function whereIs(command, env = process.env, exists = existsSync) {
|
|
36955
|
+
const extensions = (env.PATHEXT || ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean);
|
|
36956
|
+
for (const dir of (env.PATH ?? env.Path ?? "").split(";")) {
|
|
36957
|
+
if (!dir) continue;
|
|
36958
|
+
for (const extension of extensions) {
|
|
36959
|
+
const file3 = path.win32.join(dir, command + extension.toLowerCase());
|
|
36960
|
+
if (exists(file3)) return file3;
|
|
36961
|
+
}
|
|
36962
|
+
}
|
|
36963
|
+
return void 0;
|
|
36964
|
+
}
|
|
36965
|
+
var quoted = (arg) => /^[\w\-./:\\=@]+$/.test(arg) ? arg : `"${arg.replace(/["%]/g, "")}"`;
|
|
36966
|
+
function starting(command, args, platform = process.platform, find = whereIs) {
|
|
36967
|
+
if (platform !== "win32") return { file: command, args, shell: false };
|
|
36968
|
+
const file3 = find(command);
|
|
36969
|
+
if (!file3) return void 0;
|
|
36970
|
+
if (!/\.(cmd|bat)$/i.test(file3)) return { file: file3, args, shell: false };
|
|
36971
|
+
return { file: [file3, ...args].map(quoted).join(" "), args: [], shell: true };
|
|
36972
|
+
}
|
|
36973
|
+
function relaxed(text2) {
|
|
36974
|
+
let out = "";
|
|
36975
|
+
for (let i = 0; i < text2.length; i++) {
|
|
36976
|
+
const c = text2[i];
|
|
36977
|
+
if (c === '"') {
|
|
36978
|
+
const start2 = i;
|
|
36979
|
+
for (i++; i < text2.length && text2[i] !== '"'; i++) if (text2[i] === "\\") i++;
|
|
36980
|
+
out += text2.slice(start2, i + 1);
|
|
36981
|
+
} else if (c === "/" && text2[i + 1] === "/") {
|
|
36982
|
+
while (i < text2.length && text2[i] !== "\n") i++;
|
|
36983
|
+
out += "\n";
|
|
36984
|
+
} else if (c === "/" && text2[i + 1] === "*") {
|
|
36985
|
+
const end = text2.indexOf("*/", i + 2);
|
|
36986
|
+
i = end < 0 ? text2.length : end + 1;
|
|
36987
|
+
} else if (c === "}" || c === "]") {
|
|
36988
|
+
out = out.replace(/,\s*$/, "") + c;
|
|
36989
|
+
} else {
|
|
36990
|
+
out += c;
|
|
36991
|
+
}
|
|
36992
|
+
}
|
|
36993
|
+
return out;
|
|
36994
|
+
}
|
|
36995
|
+
function decoded(bytes) {
|
|
36996
|
+
if (bytes[0] === 255 && bytes[1] === 254) return bytes.subarray(2).toString("utf16le");
|
|
36997
|
+
return bytes.toString("utf8").replace(/^/, "");
|
|
36998
|
+
}
|
|
36999
|
+
|
|
36855
37000
|
// src/remembered.ts
|
|
36856
37001
|
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
36857
37002
|
import { homedir } from "node:os";
|
|
36858
|
-
import
|
|
37003
|
+
import path2 from "node:path";
|
|
36859
37004
|
var FIRST_AGENT = "Claude";
|
|
36860
37005
|
function stateFile(agent2, home = homedir(), override = process.env.SYMPOSIUM_STATE_FILE) {
|
|
36861
37006
|
if (override) return override;
|
|
36862
37007
|
const slug = agent2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
36863
37008
|
const name = slug === FIRST_AGENT.toLowerCase() ? "mcp.json" : `mcp.${slug || "agent"}.json`;
|
|
36864
|
-
return
|
|
37009
|
+
return path2.join(home, ".symposium", name);
|
|
36865
37010
|
}
|
|
36866
37011
|
var agent = FIRST_AGENT;
|
|
36867
37012
|
function rememberAs(name) {
|
|
@@ -36879,7 +37024,7 @@ function load() {
|
|
|
36879
37024
|
function save(next) {
|
|
36880
37025
|
try {
|
|
36881
37026
|
const target = file2();
|
|
36882
|
-
mkdirSync(
|
|
37027
|
+
mkdirSync(path2.dirname(target), { recursive: true, mode: 448 });
|
|
36883
37028
|
const temp = `${target}.${process.pid}.tmp`;
|
|
36884
37029
|
writeFileSync(temp, JSON.stringify(next), { mode: 384 });
|
|
36885
37030
|
renameSync(temp, target);
|
|
@@ -36897,12 +37042,408 @@ function startingInvite(configured = process.env.SYMPOSIUM_INVITE) {
|
|
|
36897
37042
|
function rememberInvite(invite, configured = process.env.SYMPOSIUM_INVITE) {
|
|
36898
37043
|
return save({ ...load(), invite, configured: configured ?? "" });
|
|
36899
37044
|
}
|
|
36900
|
-
|
|
36901
|
-
const account2 =
|
|
36902
|
-
return typeof account2?.api === "string" && typeof account2?.token === "string"
|
|
37045
|
+
var isAccount = (value) => {
|
|
37046
|
+
const account2 = value;
|
|
37047
|
+
return typeof account2?.api === "string" && typeof account2?.token === "string";
|
|
37048
|
+
};
|
|
37049
|
+
var accounts = (saved) => [saved.account, ...Array.isArray(saved.others) ? saved.others : []].filter(isAccount);
|
|
37050
|
+
function savedAccount(api) {
|
|
37051
|
+
const all = accounts(load());
|
|
37052
|
+
return api && all.find((a) => a.api === api) || all[0];
|
|
36903
37053
|
}
|
|
36904
37054
|
function rememberAccount(account2) {
|
|
36905
|
-
|
|
37055
|
+
const saved = load();
|
|
37056
|
+
if (!account2) return save({ ...saved, account: void 0, others: void 0 });
|
|
37057
|
+
const [first, ...rest] = accounts(saved);
|
|
37058
|
+
if (!first || first.api === account2.api) return save({ ...saved, account: account2, others: rest });
|
|
37059
|
+
return save({
|
|
37060
|
+
...saved,
|
|
37061
|
+
account: first,
|
|
37062
|
+
others: [...rest.filter((a) => a.api !== account2.api), account2]
|
|
37063
|
+
});
|
|
37064
|
+
}
|
|
37065
|
+
|
|
37066
|
+
// src/setup.ts
|
|
37067
|
+
var SERVER = "symposium";
|
|
37068
|
+
var realHost = {
|
|
37069
|
+
run(command, args) {
|
|
37070
|
+
const how = starting(command, args);
|
|
37071
|
+
if (!how) return void 0;
|
|
37072
|
+
const done = spawnSync(how.file, how.args, {
|
|
37073
|
+
stdio: ["ignore", "ignore", "pipe"],
|
|
37074
|
+
encoding: "utf8",
|
|
37075
|
+
shell: how.shell,
|
|
37076
|
+
timeout: 6e4
|
|
37077
|
+
});
|
|
37078
|
+
if (done.error) {
|
|
37079
|
+
const missing = done.error.code === "ENOENT";
|
|
37080
|
+
return missing ? void 0 : { status: 1, said: done.error.message };
|
|
37081
|
+
}
|
|
37082
|
+
return { status: done.status ?? 1, said: lastLine(done.stderr ?? "") };
|
|
37083
|
+
},
|
|
37084
|
+
redeem: redeemConnectFor,
|
|
37085
|
+
join: (account2, space2) => new AccountClient(account2.api, account2.token).join(space2),
|
|
37086
|
+
say: (line) => void process.stdout.write(`${line}
|
|
37087
|
+
`),
|
|
37088
|
+
async ask(question) {
|
|
37089
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return void 0;
|
|
37090
|
+
const prompt = createInterface({ input: process.stdin, output: process.stdout });
|
|
37091
|
+
const gone = new Promise(
|
|
37092
|
+
(resolve) => prompt.once("close", () => resolve(void 0))
|
|
37093
|
+
);
|
|
37094
|
+
try {
|
|
37095
|
+
const answer = await Promise.race([prompt.question(question), gone]).catch(() => void 0);
|
|
37096
|
+
if (answer === void 0) process.stdout.write("\n");
|
|
37097
|
+
return answer;
|
|
37098
|
+
} finally {
|
|
37099
|
+
prompt.close();
|
|
37100
|
+
}
|
|
37101
|
+
},
|
|
37102
|
+
open(command, args) {
|
|
37103
|
+
return new Promise((resolve) => {
|
|
37104
|
+
const how = starting(command, args);
|
|
37105
|
+
if (!how) return resolve();
|
|
37106
|
+
const ignore = () => {
|
|
37107
|
+
};
|
|
37108
|
+
process.on("SIGINT", ignore);
|
|
37109
|
+
const child = spawn(how.file, how.args, { stdio: "inherit", shell: how.shell });
|
|
37110
|
+
const done = () => {
|
|
37111
|
+
process.off("SIGINT", ignore);
|
|
37112
|
+
resolve();
|
|
37113
|
+
};
|
|
37114
|
+
child.on("error", done);
|
|
37115
|
+
child.on("exit", done);
|
|
37116
|
+
});
|
|
37117
|
+
}
|
|
37118
|
+
};
|
|
37119
|
+
function lastLine(output2) {
|
|
37120
|
+
const lines = output2.replace(/\x1b\[[0-9;]*m/g, "").split("\n").map((line) => line.trim()).filter(Boolean);
|
|
37121
|
+
return (lines.at(-1) ?? "").slice(0, 200);
|
|
37122
|
+
}
|
|
37123
|
+
function serverCommand(self = fileURLToPath(import.meta.url), platform = process.platform) {
|
|
37124
|
+
const npx = self.endsWith(".ts") ? ["npx", "-y", "tsx", path3.join(path3.dirname(self), "mcp.ts")] : ["npx", "-y", "symposium-mcp"];
|
|
37125
|
+
return platform === "win32" ? ["cmd", "/c", ...npx] : npx;
|
|
37126
|
+
}
|
|
37127
|
+
var withCommand = (label, agent2, bin, add) => ({
|
|
37128
|
+
label,
|
|
37129
|
+
agent: agent2,
|
|
37130
|
+
// Installed and working: a command that cannot say its version cannot be set up either.
|
|
37131
|
+
found: (host) => host.run(bin, ["--version"])?.status === 0,
|
|
37132
|
+
register(host, command) {
|
|
37133
|
+
if (host.run(bin, ["mcp", "get", SERVER])?.status === 0) return { tools: "present" };
|
|
37134
|
+
const added = host.run(bin, add(command));
|
|
37135
|
+
if (added?.status !== 0) {
|
|
37136
|
+
throw new Error(`\`${bin} mcp add\` failed${added?.said ? ` (${added.said})` : ""}`);
|
|
37137
|
+
}
|
|
37138
|
+
return { tools: "added" };
|
|
37139
|
+
},
|
|
37140
|
+
start: (prompt) => [bin, prompt]
|
|
37141
|
+
});
|
|
37142
|
+
var withConfigFile = (label, agent2, dir, file3) => {
|
|
37143
|
+
const entry = (command) => ({
|
|
37144
|
+
command: command[0],
|
|
37145
|
+
args: command.slice(1),
|
|
37146
|
+
env: { SYMPOSIUM_AGENT_NAME: agent2 }
|
|
37147
|
+
});
|
|
37148
|
+
const target = () => path3.join(homedir2(), ...dir, file3);
|
|
37149
|
+
return {
|
|
37150
|
+
label,
|
|
37151
|
+
agent: agent2,
|
|
37152
|
+
found: () => existsSync2(path3.join(homedir2(), ...dir)),
|
|
37153
|
+
register(_host, command) {
|
|
37154
|
+
let settings = {};
|
|
37155
|
+
let loose = false;
|
|
37156
|
+
const text2 = existsSync2(target()) ? decoded(readFileSync2(target())) : "";
|
|
37157
|
+
if (text2.trim()) {
|
|
37158
|
+
let parsed;
|
|
37159
|
+
try {
|
|
37160
|
+
parsed = JSON.parse(text2);
|
|
37161
|
+
} catch {
|
|
37162
|
+
loose = true;
|
|
37163
|
+
try {
|
|
37164
|
+
parsed = JSON.parse(relaxed(text2));
|
|
37165
|
+
} catch {
|
|
37166
|
+
parsed = null;
|
|
37167
|
+
}
|
|
37168
|
+
}
|
|
37169
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
37170
|
+
throw new Error(`${target()} could not be read as JSON`);
|
|
37171
|
+
}
|
|
37172
|
+
settings = parsed;
|
|
37173
|
+
}
|
|
37174
|
+
const servers = settings.mcpServers;
|
|
37175
|
+
const existing = typeof servers === "object" && servers !== null && !Array.isArray(servers) ? servers : {};
|
|
37176
|
+
if (SERVER in existing) return { tools: "present" };
|
|
37177
|
+
const kept = `${target()}.before-symposium`;
|
|
37178
|
+
if (loose) copyFileSync(target(), kept);
|
|
37179
|
+
settings.mcpServers = { ...existing, [SERVER]: entry(command) };
|
|
37180
|
+
writeFileSync2(target(), `${JSON.stringify(settings, null, 2)}
|
|
37181
|
+
`);
|
|
37182
|
+
return {
|
|
37183
|
+
tools: "added",
|
|
37184
|
+
note: loose ? `the file's comments are kept in ${path3.basename(kept)}` : void 0
|
|
37185
|
+
};
|
|
37186
|
+
},
|
|
37187
|
+
byHand: (command) => `${label}: add this inside "mcpServers" in ${target()}
|
|
37188
|
+
"${SERVER}": ${JSON.stringify(entry(command))}`
|
|
37189
|
+
};
|
|
37190
|
+
};
|
|
37191
|
+
var TARGETS = [
|
|
37192
|
+
withCommand("Claude Code", "Claude", "claude", (command) => [
|
|
37193
|
+
"mcp",
|
|
37194
|
+
"add",
|
|
37195
|
+
"--scope",
|
|
37196
|
+
"user",
|
|
37197
|
+
SERVER,
|
|
37198
|
+
"--",
|
|
37199
|
+
...command
|
|
37200
|
+
]),
|
|
37201
|
+
withCommand("Codex", "Codex", "codex", (command) => ["mcp", "add", SERVER, "--", ...command]),
|
|
37202
|
+
withConfigFile("Cursor", "Cursor", [".cursor"], "mcp.json"),
|
|
37203
|
+
withConfigFile("Gemini CLI", "Gemini", [".gemini"], "settings.json"),
|
|
37204
|
+
withConfigFile("Windsurf", "Windsurf", [".codeium", "windsurf"], "mcp_config.json")
|
|
37205
|
+
];
|
|
37206
|
+
var reason = (e) => e instanceof Error ? e.message : String(e);
|
|
37207
|
+
async function setup(argv, host = realHost) {
|
|
37208
|
+
const pasted = argv.join(" ").trim();
|
|
37209
|
+
const code = pasted ? findConnect(pasted) : void 0;
|
|
37210
|
+
if (pasted && !code) {
|
|
37211
|
+
host.say(
|
|
37212
|
+
'That is not a Symposium connect code. Press "Connect your agent" in Symposium and copy the line again.'
|
|
37213
|
+
);
|
|
37214
|
+
return 1;
|
|
37215
|
+
}
|
|
37216
|
+
const command = serverCommand();
|
|
37217
|
+
const found = TARGETS.filter((target) => target.found(host));
|
|
37218
|
+
if (found.length === 0) {
|
|
37219
|
+
host.say("No agent that setup knows was found on this computer.");
|
|
37220
|
+
host.say(`It looks for: ${TARGETS.map((t) => t.label).join(", ")}.`);
|
|
37221
|
+
host.say(`For any other MCP client, add this as a stdio server: ${command.join(" ")}`);
|
|
37222
|
+
return 1;
|
|
37223
|
+
}
|
|
37224
|
+
const rows = found.map((target) => {
|
|
37225
|
+
try {
|
|
37226
|
+
return { target, note: "", ...target.register(host, command) };
|
|
37227
|
+
} catch (e) {
|
|
37228
|
+
return { target, tools: "failed", note: reason(e) };
|
|
37229
|
+
}
|
|
37230
|
+
});
|
|
37231
|
+
let failed = rows.some((row) => row.tools === "failed");
|
|
37232
|
+
const connection = /* @__PURE__ */ new Map();
|
|
37233
|
+
const paired = (name) => {
|
|
37234
|
+
rememberAs(name);
|
|
37235
|
+
const account2 = savedAccount(code?.api);
|
|
37236
|
+
return code && account2?.api === code.api ? account2 : void 0;
|
|
37237
|
+
};
|
|
37238
|
+
if (code) {
|
|
37239
|
+
const names = [...new Set(rows.filter((r) => r.tools !== "failed").map((r) => r.target.agent))];
|
|
37240
|
+
const fresh = names.filter((name) => !paired(name));
|
|
37241
|
+
for (const name of names) if (!fresh.includes(name)) connection.set(name, "already connected");
|
|
37242
|
+
if (fresh.length) {
|
|
37243
|
+
try {
|
|
37244
|
+
const { person, all } = await host.redeem(
|
|
37245
|
+
code,
|
|
37246
|
+
fresh.map((name) => ({ name, device: hostname3() }))
|
|
37247
|
+
);
|
|
37248
|
+
fresh.forEach((name, i) => {
|
|
37249
|
+
rememberAs(name);
|
|
37250
|
+
const saved = rememberAccount({ api: code.api, token: all[i].token, person });
|
|
37251
|
+
connection.set(
|
|
37252
|
+
name,
|
|
37253
|
+
saved ? `connected for ${person}` : "connected, but the connection could not be saved"
|
|
37254
|
+
);
|
|
37255
|
+
failed ||= !saved;
|
|
37256
|
+
});
|
|
37257
|
+
} catch (e) {
|
|
37258
|
+
failed = true;
|
|
37259
|
+
for (const name of fresh) connection.set(name, `not connected: ${reason(e)}`);
|
|
37260
|
+
}
|
|
37261
|
+
}
|
|
37262
|
+
if (code.space) {
|
|
37263
|
+
for (const name of names) {
|
|
37264
|
+
const account2 = paired(name);
|
|
37265
|
+
if (!account2) continue;
|
|
37266
|
+
try {
|
|
37267
|
+
rememberInvite(encodeInvite(await host.join(account2, code.space)));
|
|
37268
|
+
} catch (e) {
|
|
37269
|
+
connection.set(name, `${connection.get(name)}; could not join the space: ${reason(e)}`);
|
|
37270
|
+
}
|
|
37271
|
+
}
|
|
37272
|
+
}
|
|
37273
|
+
}
|
|
37274
|
+
const width = Math.max(...rows.map((row) => row.target.label.length));
|
|
37275
|
+
host.say("Symposium setup");
|
|
37276
|
+
host.say("");
|
|
37277
|
+
for (const row of rows) {
|
|
37278
|
+
const tools = row.tools === "added" ? "tools added" : row.tools === "present" ? "tools already there" : `could not add the tools: ${row.note}`;
|
|
37279
|
+
const link = row.tools === "failed" ? void 0 : connection.get(row.target.agent);
|
|
37280
|
+
const note = row.tools !== "failed" && row.note ? `(${row.note})` : void 0;
|
|
37281
|
+
host.say(
|
|
37282
|
+
` ${row.target.label.padEnd(width)} ${[tools, link].filter(Boolean).join(", ")}${note ? ` ${note}` : ""}`
|
|
37283
|
+
);
|
|
37284
|
+
}
|
|
37285
|
+
host.say("");
|
|
37286
|
+
const byHand = rows.filter((row) => row.tools === "failed" && row.target.byHand);
|
|
37287
|
+
for (const row of byHand) host.say(row.target.byHand(command));
|
|
37288
|
+
if (byHand.length) host.say("");
|
|
37289
|
+
const working = rows.filter((row) => row.tools !== "failed");
|
|
37290
|
+
if (!code) {
|
|
37291
|
+
if (working.length) {
|
|
37292
|
+
host.say(
|
|
37293
|
+
'To connect them: press "Connect your agent" in Symposium and run the line it shows.'
|
|
37294
|
+
);
|
|
37295
|
+
}
|
|
37296
|
+
return failed ? 1 : 0;
|
|
37297
|
+
}
|
|
37298
|
+
const connected = working.filter((row) => paired(row.target.agent));
|
|
37299
|
+
if (connected.length === 0) {
|
|
37300
|
+
host.say(
|
|
37301
|
+
'No agent was connected. Once the above is sorted out, press "Connect your agent" in Symposium again and run the new line.'
|
|
37302
|
+
);
|
|
37303
|
+
return 1;
|
|
37304
|
+
}
|
|
37305
|
+
const ready = connected.filter((row) => row.target.start);
|
|
37306
|
+
const chosen = ready.length ? await choose(
|
|
37307
|
+
host,
|
|
37308
|
+
ready.map((row) => row.target)
|
|
37309
|
+
) : void 0;
|
|
37310
|
+
if (!chosen) {
|
|
37311
|
+
host.say(
|
|
37312
|
+
`Start a new session in ${oneOf(connected.map((row) => row.target.label))} and say "join my meeting".`
|
|
37313
|
+
);
|
|
37314
|
+
return failed ? 1 : 0;
|
|
37315
|
+
}
|
|
37316
|
+
const [bin, ...args] = chosen.start(
|
|
37317
|
+
code.space && /^[\w .-]+$/.test(code.space) ? `Join my Symposium meeting in the space ${code.space}.` : "Join my Symposium meeting."
|
|
37318
|
+
);
|
|
37319
|
+
await host.open(bin, args);
|
|
37320
|
+
return failed ? 1 : 0;
|
|
37321
|
+
}
|
|
37322
|
+
var oneOf = (names) => names.length < 2 ? names.join("") : `${names.slice(0, -1).join(", ")} or ${names.at(-1)}`;
|
|
37323
|
+
async function choose(host, targets) {
|
|
37324
|
+
const question = targets.length === 1 ? `Open ${targets[0].label} in the meeting now? [Y/n] ` : `Open one in the meeting now? ${targets.map((t, i) => `${i + 1}) ${t.label}`).join(" ")} [1, or n for none] `;
|
|
37325
|
+
const answer = (await host.ask(question))?.trim().toLowerCase();
|
|
37326
|
+
if (answer === void 0 || answer.startsWith("n")) return void 0;
|
|
37327
|
+
if (answer === "" || answer.startsWith("y")) return targets[0];
|
|
37328
|
+
return targets[Number(answer) - 1];
|
|
37329
|
+
}
|
|
37330
|
+
|
|
37331
|
+
// src/wake.ts
|
|
37332
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
37333
|
+
import path4 from "node:path";
|
|
37334
|
+
import { pathToFileURL } from "node:url";
|
|
37335
|
+
function wakeEvents(events, agentId, now, maxAgeMs) {
|
|
37336
|
+
return events.filter(
|
|
37337
|
+
(e) => now - e.ts <= maxAgeMs && (e.type !== "request" || e.to.includes(agentId))
|
|
37338
|
+
);
|
|
37339
|
+
}
|
|
37340
|
+
var flag = (name, fallback) => {
|
|
37341
|
+
const hit = process.argv.find((a) => a.startsWith(`--${name}=`));
|
|
37342
|
+
return hit ? hit.slice(name.length + 3) : fallback;
|
|
37343
|
+
};
|
|
37344
|
+
var minutes = (name, fallback) => {
|
|
37345
|
+
const n = Number(flag(name, String(fallback)));
|
|
37346
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
37347
|
+
};
|
|
37348
|
+
function positionFile(client2) {
|
|
37349
|
+
const safe = (s) => s.replace(/[^\w-]/g, "_");
|
|
37350
|
+
return path4.join(
|
|
37351
|
+
path4.dirname(stateFile("")),
|
|
37352
|
+
`wake-${safe(client2.space)}-${safe(client2.agentId)}`
|
|
37353
|
+
);
|
|
37354
|
+
}
|
|
37355
|
+
function readPosition(file3) {
|
|
37356
|
+
try {
|
|
37357
|
+
const n = Number(readFileSync3(file3, "utf8"));
|
|
37358
|
+
return Number.isInteger(n) && n >= 0 ? n : void 0;
|
|
37359
|
+
} catch {
|
|
37360
|
+
return void 0;
|
|
37361
|
+
}
|
|
37362
|
+
}
|
|
37363
|
+
function savePosition(file3, position) {
|
|
37364
|
+
if (position === void 0) return;
|
|
37365
|
+
try {
|
|
37366
|
+
mkdirSync2(path4.dirname(file3), { recursive: true, mode: 448 });
|
|
37367
|
+
const temp = `${file3}.${process.pid}.tmp`;
|
|
37368
|
+
writeFileSync3(temp, String(position), { mode: 384 });
|
|
37369
|
+
renameSync2(temp, file3);
|
|
37370
|
+
} catch {
|
|
37371
|
+
}
|
|
37372
|
+
}
|
|
37373
|
+
function rememberPosition(client2) {
|
|
37374
|
+
const position = client2.position;
|
|
37375
|
+
if (position === void 0) return;
|
|
37376
|
+
const file3 = positionFile(client2);
|
|
37377
|
+
if ((readPosition(file3) ?? -1) < position) savePosition(file3, position);
|
|
37378
|
+
}
|
|
37379
|
+
function withoutHeard(client2, events) {
|
|
37380
|
+
const heard = readPosition(positionFile(client2)) ?? -1;
|
|
37381
|
+
return events.filter(
|
|
37382
|
+
(e) => !(e.type === "request" && e.seq <= heard && e.to.includes(client2.agentId))
|
|
37383
|
+
);
|
|
37384
|
+
}
|
|
37385
|
+
async function connect() {
|
|
37386
|
+
const wanted = flag("space", "");
|
|
37387
|
+
if (!wanted) {
|
|
37388
|
+
const invite = startingInvite();
|
|
37389
|
+
return invite ? new SpaceClient(invite) : "No space to join: pass --space=<name or id>.";
|
|
37390
|
+
}
|
|
37391
|
+
const api = flag("api", "").replace(/\/$/, "");
|
|
37392
|
+
const saved = savedAccount(api || void 0);
|
|
37393
|
+
if (!saved) return "--space needs this agent connected to an account (use connect first).";
|
|
37394
|
+
const paired = new AccountClient(api || saved.api, saved.token);
|
|
37395
|
+
const choice = chooseSpace(await paired.spaces(), wanted);
|
|
37396
|
+
if (!("space" in choice)) {
|
|
37397
|
+
return `${choice.why} Try one of: ${choice.candidates.map((s) => s.name || s.id).join(", ")}`;
|
|
37398
|
+
}
|
|
37399
|
+
return new SpaceClient(await paired.join(choice.space.id));
|
|
37400
|
+
}
|
|
37401
|
+
async function main() {
|
|
37402
|
+
const agent2 = flag("agent", "");
|
|
37403
|
+
if (agent2) rememberAs(agent2);
|
|
37404
|
+
const client2 = await connect();
|
|
37405
|
+
if (typeof client2 === "string") {
|
|
37406
|
+
console.error(client2);
|
|
37407
|
+
return 2;
|
|
37408
|
+
}
|
|
37409
|
+
const types = flag("types", "request").split(",").filter(Boolean);
|
|
37410
|
+
const maxAgeMs = minutes("max-age-minutes", 10) * 6e4;
|
|
37411
|
+
const deadline = Date.now() + minutes("max-minutes", 30) * 6e4;
|
|
37412
|
+
const file3 = positionFile(client2);
|
|
37413
|
+
const saved = readPosition(file3);
|
|
37414
|
+
if (saved !== void 0) client2.resumeFrom(saved);
|
|
37415
|
+
console.error(`Listening for ${types.join(", ")} in space ${client2.space} as ${client2.agentId}`);
|
|
37416
|
+
let failures = 0;
|
|
37417
|
+
while (Date.now() < deadline) {
|
|
37418
|
+
try {
|
|
37419
|
+
const events = await client2.waitForEvents(deadline - Date.now(), types);
|
|
37420
|
+
failures = 0;
|
|
37421
|
+
savePosition(file3, client2.position);
|
|
37422
|
+
const wake = wakeEvents(events, client2.agentId, Date.now(), maxAgeMs);
|
|
37423
|
+
if (wake.length) {
|
|
37424
|
+
for (const event of wake) process.stdout.write(`${JSON.stringify(event)}
|
|
37425
|
+
`);
|
|
37426
|
+
return 0;
|
|
37427
|
+
}
|
|
37428
|
+
} catch (e) {
|
|
37429
|
+
if (++failures >= 5) {
|
|
37430
|
+
console.error(e instanceof Error ? e.message : String(e));
|
|
37431
|
+
return 1;
|
|
37432
|
+
}
|
|
37433
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3 * failures));
|
|
37434
|
+
}
|
|
37435
|
+
}
|
|
37436
|
+
console.error("Nothing arrived in time; start it again.");
|
|
37437
|
+
return 3;
|
|
37438
|
+
}
|
|
37439
|
+
if (process.argv[1] && path4.basename(process.argv[1]).startsWith("wake.") && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
37440
|
+
main().then(
|
|
37441
|
+
(code) => process.exit(code),
|
|
37442
|
+
(e) => {
|
|
37443
|
+
console.error(e instanceof Error ? e.message : String(e));
|
|
37444
|
+
process.exit(1);
|
|
37445
|
+
}
|
|
37446
|
+
);
|
|
36906
37447
|
}
|
|
36907
37448
|
|
|
36908
37449
|
// src/mcp.ts
|
|
@@ -36973,6 +37514,18 @@ away. Do not run it as a shell command, and do not change the MCP configuration:
|
|
|
36973
37514
|
switches spaces and remembers the choice for later sessions by itself. Then tell them in a
|
|
36974
37515
|
line that you are in, and stay engaged by calling wait_for_events in a loop.
|
|
36975
37516
|
|
|
37517
|
+
People in the space can only reach you while you are waiting on it: the room shows you as
|
|
37518
|
+
"not listening" otherwise, and what they ask goes unheard. join_space tells you two things
|
|
37519
|
+
about that; act on both. \`unansweredRequests\` are requests sent to you while nobody was
|
|
37520
|
+
waiting. \`listen.command\` is a shell command that waits for you in the background and
|
|
37521
|
+
exits when a request arrives: if you can run background commands, start it whenever you
|
|
37522
|
+
stop calling wait_for_events (before ending your turn, or before a long piece of other
|
|
37523
|
+
work), and start it again after handling what it printed.
|
|
37524
|
+
|
|
37525
|
+
Everyone in the space sees you as a small figure that shows what you are doing. Keep it
|
|
37526
|
+
true: pass \`doing\` (and \`surfaceId\` once there is one) to update_request while you
|
|
37527
|
+
work on a request, and call working_on for anything else you do in the space.
|
|
37528
|
+
|
|
36976
37529
|
When the user pastes a connect code \u2014 a line like "Connect to my Symposium: symc_\u2026" \u2014
|
|
36977
37530
|
call connect with what they pasted, straight away. It links you to their account for
|
|
36978
37531
|
good (and joins the space it was made in, if any).
|
|
@@ -36995,7 +37548,7 @@ function start() {
|
|
|
36995
37548
|
var client;
|
|
36996
37549
|
function account() {
|
|
36997
37550
|
begin();
|
|
36998
|
-
const saved = savedAccount();
|
|
37551
|
+
const saved = savedAccount(client?.api);
|
|
36999
37552
|
return saved ? new AccountClient(saved.api, saved.token) : void 0;
|
|
37000
37553
|
}
|
|
37001
37554
|
var NOT_PAIRED = `You are not connected to the user's Symposium account. Ask them to press "Connect your agent" in Symposium (on the home page, or Stage \u2192 Agents in a space) and paste you the line it shows; then call connect with it. It is needed once.`;
|
|
@@ -37018,23 +37571,61 @@ var markSeen = (at) => {
|
|
|
37018
37571
|
summonsSeen = Math.max(summonsSeen ?? 0, at);
|
|
37019
37572
|
};
|
|
37020
37573
|
var summonsFollowed = 0;
|
|
37574
|
+
var lastSummons;
|
|
37575
|
+
async function joinWhereCalled(paired, space2, called) {
|
|
37576
|
+
const site = called?.space === space2 ? summonsApi(called, paired.api) : paired.api;
|
|
37577
|
+
if (site === paired.api) return paired.join(space2);
|
|
37578
|
+
return paired.at(site).join(space2).catch(() => paired.join(space2));
|
|
37579
|
+
}
|
|
37580
|
+
var quoteArg = (arg) => /^[\w@%+=:,./-]+$/.test(arg) ? arg : `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
37581
|
+
function wakeCommand(target) {
|
|
37582
|
+
const script = process.argv[1];
|
|
37583
|
+
if (!script) return void 0;
|
|
37584
|
+
const where = savedAccount(target.api)?.token.startsWith(PAIRED_KEY_PREFIX) ? [`--space=${target.space}`, `--api=${target.api}`, `--agent=${whoAmI()}`] : [];
|
|
37585
|
+
return [process.execPath, ...process.execArgv, script, "wake", ...where].map(quoteArg).join(" ");
|
|
37586
|
+
}
|
|
37587
|
+
var UNANSWERED_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
37021
37588
|
var unfinishedWait;
|
|
37022
37589
|
async function enter(next, invite) {
|
|
37023
37590
|
begin();
|
|
37024
|
-
const
|
|
37591
|
+
const same = client?.space === next.space && client.agentId === next.agentId;
|
|
37592
|
+
const target = same && client.api === next.api ? client : next;
|
|
37593
|
+
if (same && target === next && client.position !== void 0) {
|
|
37594
|
+
next.resumeFrom(client.position);
|
|
37595
|
+
}
|
|
37025
37596
|
const snap = await target.snapshot();
|
|
37026
37597
|
client = target;
|
|
37598
|
+
rememberPosition(target);
|
|
37027
37599
|
const remembered = rememberInvite(invite);
|
|
37600
|
+
const now = Date.now();
|
|
37601
|
+
const unanswered = snap.timeline.filter(
|
|
37602
|
+
(e) => e.kind === "request" && now - e.ts < UNANSWERED_MAX_AGE_MS && e.request?.to.some((t) => t.agentId === target.agentId && t.status === "sent")
|
|
37603
|
+
).map((e) => ({
|
|
37604
|
+
id: e.seq,
|
|
37605
|
+
from: e.name,
|
|
37606
|
+
text: e.text,
|
|
37607
|
+
asked: `${Math.max(0, Math.round((now - e.ts) / 6e4))} min ago`
|
|
37608
|
+
}));
|
|
37609
|
+
const command = wakeCommand(target);
|
|
37028
37610
|
return {
|
|
37029
37611
|
space: snap.id,
|
|
37030
37612
|
...snap.name ? { name: snap.name } : {},
|
|
37031
37613
|
you: snap.agents.find((a) => a.id === target.agentId)?.name ?? target.agentId,
|
|
37032
|
-
liveParticipants: snap.participants,
|
|
37033
|
-
presence: snap.presence,
|
|
37614
|
+
liveParticipants: withoutPhotos(snap.participants),
|
|
37615
|
+
presence: withoutPhotos(snap.presence),
|
|
37034
37616
|
surfaces: snap.surfaces.map((s) => ({ id: s.id, title: s.title })),
|
|
37617
|
+
...unanswered.length ? {
|
|
37618
|
+
unansweredRequests: unanswered,
|
|
37619
|
+
aboutUnanswered: 'Sent to you while you were not listening, and still showing as unanswered in the room. For a recent one, call update_request with "acknowledged" and do it. For one that is hours old or plainly overtaken, ask the user whether they still want it, and otherwise close it with update_request "failed" and a note saying it was not picked up in time.'
|
|
37620
|
+
} : {},
|
|
37621
|
+
listen: {
|
|
37622
|
+
how: "People can reach you only while you are waiting: call wait_for_events in a loop. When you stop (before ending your turn, or before long work elsewhere) and you can run a shell command in the background, start `command` there: it exits as soon as a request for you arrives and prints it. Handle it, then start the command again.",
|
|
37623
|
+
...command ? { command } : {}
|
|
37624
|
+
},
|
|
37035
37625
|
remembered: remembered ? "Later sessions start in this space. No configuration change is needed." : "Could not save this space; later sessions will need to join again."
|
|
37036
37626
|
};
|
|
37037
37627
|
}
|
|
37628
|
+
var withoutPhotos = (people) => people.map((person) => ({ ...person, picture: void 0 }));
|
|
37038
37629
|
var text = (value) => ({
|
|
37039
37630
|
content: [
|
|
37040
37631
|
{
|
|
@@ -37050,6 +37641,13 @@ var LINE_LABELS = {
|
|
|
37050
37641
|
request: " (request to agents)",
|
|
37051
37642
|
proposal: " (agent proposal)"
|
|
37052
37643
|
};
|
|
37644
|
+
var DOING = 'The kind of work, which your figure in the room shows: "thinking" (working something out), "searching" (looking things up, reading), "writing" (text, the shared document), "drawing" (making an image or illustration), "charting" (charts, numbers, dashboards), "building" (putting a surface or other UI together).';
|
|
37645
|
+
var WORK_DOCUMENT = external_exports.string().optional().describe(
|
|
37646
|
+
`The shared document this work is in, if any: its id from list_documents ("main" for the first). You are shown on that document's tab while you work.`
|
|
37647
|
+
);
|
|
37648
|
+
var WORK_SECTION = external_exports.string().max(200).optional().describe(
|
|
37649
|
+
"With documentId: the text of the heading you are writing under. People see you beside that section in the document, which tells them not to type there while you work."
|
|
37650
|
+
);
|
|
37053
37651
|
var fmtTime = (ts) => new Date(ts).toISOString().slice(0, 19).replace("T", " ");
|
|
37054
37652
|
var server = new McpServer(
|
|
37055
37653
|
{ name: "symposium", version: "0.2.0" },
|
|
@@ -37064,9 +37662,35 @@ function begin() {
|
|
|
37064
37662
|
client = start();
|
|
37065
37663
|
}
|
|
37066
37664
|
server.server.oninitialized = begin;
|
|
37067
|
-
async function
|
|
37665
|
+
async function connect2(code) {
|
|
37068
37666
|
begin();
|
|
37069
|
-
const
|
|
37667
|
+
const known = savedAccount(code.api);
|
|
37668
|
+
if (known && known.api !== code.api) {
|
|
37669
|
+
const there = new AccountClient(code.api, known.token);
|
|
37670
|
+
const shared = await there.spaces().then(
|
|
37671
|
+
() => true,
|
|
37672
|
+
() => false
|
|
37673
|
+
);
|
|
37674
|
+
if (shared) {
|
|
37675
|
+
const result2 = {
|
|
37676
|
+
connected: true,
|
|
37677
|
+
with: known.person,
|
|
37678
|
+
note: `This site shares its accounts with ${new URL(known.api).origin}, where you were already connected. That connection is used here too; the code was not needed.`
|
|
37679
|
+
};
|
|
37680
|
+
if (!code.space) {
|
|
37681
|
+
return text({
|
|
37682
|
+
...result2,
|
|
37683
|
+
next: "Call join_space to join the meeting the user is in, or list_spaces to see their spaces."
|
|
37684
|
+
});
|
|
37685
|
+
}
|
|
37686
|
+
const joined2 = await there.join(code.space);
|
|
37687
|
+
return text({
|
|
37688
|
+
...result2,
|
|
37689
|
+
joined: await enter(new SpaceClient(joined2), encodeInvite(joined2))
|
|
37690
|
+
});
|
|
37691
|
+
}
|
|
37692
|
+
}
|
|
37693
|
+
const connected = await redeemConnect(code, { name: whoAmI(), device: hostname4() });
|
|
37070
37694
|
const saved = rememberAccount({
|
|
37071
37695
|
api: code.api,
|
|
37072
37696
|
token: connected.token,
|
|
@@ -37105,7 +37729,7 @@ server.registerTool(
|
|
|
37105
37729
|
'There is no Symposium connect code in that text. Ask the user to press "Connect your agent" in Symposium and copy the line again.'
|
|
37106
37730
|
);
|
|
37107
37731
|
}
|
|
37108
|
-
return
|
|
37732
|
+
return connect2(code);
|
|
37109
37733
|
}
|
|
37110
37734
|
);
|
|
37111
37735
|
server.registerTool(
|
|
@@ -37142,7 +37766,7 @@ server.registerTool(
|
|
|
37142
37766
|
const invite = pasted ? findInvite(pasted) : void 0;
|
|
37143
37767
|
if (invite) return text(await enter(new SpaceClient(invite), invite));
|
|
37144
37768
|
const code = pasted ? findConnect(pasted) : void 0;
|
|
37145
|
-
if (code) return
|
|
37769
|
+
if (code) return connect2(code);
|
|
37146
37770
|
const paired = account();
|
|
37147
37771
|
if (!paired) {
|
|
37148
37772
|
throw new Error(
|
|
@@ -37151,11 +37775,14 @@ server.registerTool(
|
|
|
37151
37775
|
}
|
|
37152
37776
|
const named = wanted ?? pasted;
|
|
37153
37777
|
const recentSummons = async () => {
|
|
37154
|
-
const
|
|
37155
|
-
return paired.summons(Math.max(
|
|
37778
|
+
const fresh2 = await paired.serverNow() - SUMMONS_FRESH_MS;
|
|
37779
|
+
return paired.summons(Math.max(fresh2, summonsFollowed));
|
|
37156
37780
|
};
|
|
37157
37781
|
const [spaces, called] = await Promise.all([paired.spaces(), named ? null : recentSummons()]);
|
|
37158
|
-
if (called)
|
|
37782
|
+
if (called) {
|
|
37783
|
+
markSeen(called.at);
|
|
37784
|
+
lastSummons = called;
|
|
37785
|
+
}
|
|
37159
37786
|
const choice = chooseSpace(spaces, named, called?.space);
|
|
37160
37787
|
if ("candidates" in choice) {
|
|
37161
37788
|
return text({
|
|
@@ -37171,7 +37798,8 @@ server.registerTool(
|
|
|
37171
37798
|
}
|
|
37172
37799
|
const target = choice.space.id;
|
|
37173
37800
|
if (called?.space === target) summonsFollowed = called.at;
|
|
37174
|
-
const
|
|
37801
|
+
const fresh = !choice.space.here && lastSummons?.space === target && await paired.serverNow() - lastSummons.at < SUMMONS_FRESH_MS;
|
|
37802
|
+
const joined = await joinWhereCalled(paired, target, fresh ? lastSummons : null);
|
|
37175
37803
|
return text(await enter(new SpaceClient(joined), encodeInvite(joined)));
|
|
37176
37804
|
}
|
|
37177
37805
|
);
|
|
@@ -37184,14 +37812,15 @@ server.registerTool(
|
|
|
37184
37812
|
const snap = await space().snapshot();
|
|
37185
37813
|
return text({
|
|
37186
37814
|
space: snap.id,
|
|
37187
|
-
liveParticipants: snap.participants,
|
|
37188
|
-
presence: snap.presence,
|
|
37815
|
+
liveParticipants: withoutPhotos(snap.participants),
|
|
37816
|
+
presence: withoutPhotos(snap.presence),
|
|
37189
37817
|
surfaces: snap.surfaces.map((s) => ({
|
|
37190
37818
|
id: s.id,
|
|
37191
37819
|
title: s.title,
|
|
37192
37820
|
by: s.agentName,
|
|
37193
37821
|
state: s.state
|
|
37194
37822
|
})),
|
|
37823
|
+
documents: (snap.docs ?? []).map((d) => ({ id: d.id, title: d.title })),
|
|
37195
37824
|
you: space().agentId,
|
|
37196
37825
|
agents: snap.agents.map((a) => ({ id: a.id, name: a.name, owner: a.owner })),
|
|
37197
37826
|
latestSeq: snap.seq
|
|
@@ -37353,7 +37982,7 @@ server.registerTool(
|
|
|
37353
37982
|
"- request: someone asked agents to do something. `to` lists the agent ids it is for and `seq` is its id; if yours is in `to`, call update_request right away.",
|
|
37354
37983
|
'- proposal_decided: a person approved or dismissed a proposal. If `agentId` is yours and `decision` is "approved", go ahead and report with update_proposal.',
|
|
37355
37984
|
"- proposal: another agent offered to do something.",
|
|
37356
|
-
"- doc_changed:
|
|
37985
|
+
"- doc_changed: a shared document (`docId`, `title`) was edited (at most every 30 seconds per document), renamed or, with `created`, made; call read_document with that id to see it. doc_removed: one was removed.",
|
|
37357
37986
|
"- speech, chat, message: what people said, what they typed, what other agents posted.",
|
|
37358
37987
|
"- ui_event (meeting.emit from a surface), state_changed (surface state), surface_changed / surface_removed (another agent edited the stage).",
|
|
37359
37988
|
'When you are connected to their account, it can instead return a `summons`: the user pressed "Bring my agent" in a space; join_space there.'
|
|
@@ -37368,9 +37997,11 @@ server.registerTool(
|
|
|
37368
37997
|
const paired = account();
|
|
37369
37998
|
const summoned = (called2) => {
|
|
37370
37999
|
markSeen(called2.at);
|
|
38000
|
+
lastSummons = called2;
|
|
38001
|
+
const here = called2.space === client?.space && summonsApi(called2, client.api) === client.api;
|
|
37371
38002
|
return text({
|
|
37372
38003
|
summons: { space: called2.space, ...called2.name ? { name: called2.name } : {} },
|
|
37373
|
-
do:
|
|
38004
|
+
do: here ? 'The user pressed "Bring my agent" in the space you are already in. Tell them you are here.' : `The user pressed "Bring my agent" in ${called2.space === client?.space ? "this space on another site" : "another space"}. Call join_space with space "${called2.space}" now.`
|
|
37374
38005
|
});
|
|
37375
38006
|
};
|
|
37376
38007
|
if (!client && paired) {
|
|
@@ -37378,7 +38009,11 @@ server.registerTool(
|
|
|
37378
38009
|
return called2 ? summoned(called2) : text("(no events)");
|
|
37379
38010
|
}
|
|
37380
38011
|
const current = space();
|
|
37381
|
-
const events = unfinishedWait?.client === current ? unfinishedWait.events : current.waitForEvents(timeoutMs, types)
|
|
38012
|
+
const events = unfinishedWait?.client === current ? unfinishedWait.events : current.waitForEvents(timeoutMs, types).then((list) => {
|
|
38013
|
+
const unheard = withoutHeard(current, list);
|
|
38014
|
+
rememberPosition(current);
|
|
38015
|
+
return unheard;
|
|
38016
|
+
});
|
|
37382
38017
|
unfinishedWait = void 0;
|
|
37383
38018
|
if (!paired) {
|
|
37384
38019
|
const list = await events;
|
|
@@ -37412,38 +38047,73 @@ server.registerTool(
|
|
|
37412
38047
|
server.registerTool(
|
|
37413
38048
|
"bring_everyone_to",
|
|
37414
38049
|
{
|
|
37415
|
-
description: "Switch everyone's stage to one tab so they are all looking at the same thing: one surface (pass its id; it can be yours or another agent's)
|
|
38050
|
+
description: "Switch everyone's stage to one tab so they are all looking at the same thing: one surface (pass its id; it can be yours or another agent's) or one shared document. Each person sees a note saying you brought them there and can go back. With neither, it opens the conversation column for anyone who has closed it; the conversation is beside the stage, not a tab on it, so this is rarely needed. Use it sparingly: when you have just put up or updated something the whole room should look at now, or someone asked you to show something. Do not use it for every small update.",
|
|
37416
38051
|
inputSchema: {
|
|
37417
|
-
surfaceId: external_exports.string().optional().describe("Surface to show; omit
|
|
37418
|
-
|
|
38052
|
+
surfaceId: external_exports.string().optional().describe("Surface to show; omit to open the conversation"),
|
|
38053
|
+
documentId: external_exports.string().optional().describe("A shared document to show instead: its id from list_documents"),
|
|
38054
|
+
document: external_exports.boolean().optional().describe('Set true to show the first shared document ("main") instead')
|
|
37419
38055
|
}
|
|
37420
38056
|
},
|
|
37421
|
-
async ({ surfaceId, document }) => {
|
|
38057
|
+
async ({ surfaceId, documentId, document }) => {
|
|
37422
38058
|
const result = await space().bringEveryoneTo(
|
|
37423
|
-
document ? { kind: "document" } : surfaceId ? { kind: "surface", id: surfaceId } : { kind: "conversation" }
|
|
38059
|
+
documentId || document ? { kind: "document", id: documentId || MAIN_DOC_ID } : surfaceId ? { kind: "surface", id: surfaceId } : { kind: "conversation" }
|
|
37424
38060
|
);
|
|
37425
38061
|
return text({ showing: result.target });
|
|
37426
38062
|
}
|
|
37427
38063
|
);
|
|
38064
|
+
var DOCUMENT = external_exports.string().optional().describe(
|
|
38065
|
+
`Which document: its id from list_documents. Omit for the first one ("main"), the space's notes.`
|
|
38066
|
+
);
|
|
37428
38067
|
server.registerTool(
|
|
37429
|
-
"
|
|
38068
|
+
"list_documents",
|
|
37430
38069
|
{
|
|
37431
|
-
description:
|
|
38070
|
+
description: `List the shared documents in the space: each one's id and title, who made it, when it last changed, and which agent last wrote in it. Every space has the document "main" (its notes); people and agents add others beside it, each on its own tab of the stage.`,
|
|
37432
38071
|
inputSchema: {}
|
|
37433
38072
|
},
|
|
37434
|
-
async () => text(
|
|
38073
|
+
async () => text(
|
|
38074
|
+
(await space().listDocuments()).map((d) => ({
|
|
38075
|
+
id: d.id,
|
|
38076
|
+
title: d.title,
|
|
38077
|
+
...d.createdBy ? { madeBy: d.createdBy.name } : {},
|
|
38078
|
+
...d.updatedAt ? { changed: fmtTime(d.updatedAt) } : {},
|
|
38079
|
+
...d.agentEdit ? { lastAgentEdit: d.agentEdit.name } : {}
|
|
38080
|
+
}))
|
|
38081
|
+
)
|
|
38082
|
+
);
|
|
38083
|
+
server.registerTool(
|
|
38084
|
+
"create_document",
|
|
38085
|
+
{
|
|
38086
|
+
description: 'Add a shared document to the space, on a tab of its own, optionally with its first text in Markdown. Use it for a piece of writing that stands by itself (a draft, a brief, a write-up, minutes of one meeting) instead of putting it into the notes people are keeping in "main". Returns its id, which read_document, edit_document and bring_everyone_to take.',
|
|
38087
|
+
inputSchema: {
|
|
38088
|
+
title: external_exports.string().max(80).describe('Short tab title, e.g. "Launch brief"'),
|
|
38089
|
+
markdown: external_exports.string().optional().describe("Its first text")
|
|
38090
|
+
}
|
|
38091
|
+
},
|
|
38092
|
+
async ({ title, markdown }) => {
|
|
38093
|
+
const doc = await space().createDocument({ title, markdown });
|
|
38094
|
+
return text({ id: doc.id, title: doc.title });
|
|
38095
|
+
}
|
|
38096
|
+
);
|
|
38097
|
+
server.registerTool(
|
|
38098
|
+
"read_document",
|
|
38099
|
+
{
|
|
38100
|
+
description: "Read one of the space's shared documents as Markdown. Everyone in the space can edit them together, in a meeting or between meetings; they are where notes, agendas, decisions and drafts live. Read a document before editing it so you know its headings. People see you on the document's tab for a little while after you read it.",
|
|
38101
|
+
inputSchema: { document: DOCUMENT }
|
|
38102
|
+
},
|
|
38103
|
+
async ({ document }) => text(await space().readDocument(document) || "(the document is empty)")
|
|
37435
38104
|
);
|
|
37436
38105
|
server.registerTool(
|
|
37437
38106
|
"edit_document",
|
|
37438
38107
|
{
|
|
37439
|
-
description: 'Edit
|
|
38108
|
+
description: 'Edit a shared document, in Markdown (headings, lists, bold, italic, links, quotes, code blocks). People may be typing in it at the same moment, so make the narrowest edit that does the job: "append" adds to the end; "replace_section" replaces everything under one heading, keeping the heading, and creates the section at the end if the heading does not exist yet; "replace_all" rewrites the whole document and should be used only when asked to. Everyone sees what you added or changed marked as written by you until a person edits it; text you send back unchanged is not marked, so send the rest of a section back exactly as it was. Before you start composing anything that takes more than a moment, say where you are about to write with update_request (or working_on): doing "writing", `documentId` and, for replace_section, `section`. People then see you beside that section and know not to type in it. Returns the document after your edit.',
|
|
37440
38109
|
inputSchema: {
|
|
38110
|
+
document: DOCUMENT,
|
|
37441
38111
|
op: external_exports.enum(DOC_EDIT_OPS),
|
|
37442
38112
|
heading: external_exports.string().optional().describe("For replace_section: the text of the heading whose section to replace"),
|
|
37443
38113
|
markdown: external_exports.string().describe("The new content")
|
|
37444
38114
|
}
|
|
37445
38115
|
},
|
|
37446
|
-
async ({ op, heading, markdown }) => {
|
|
38116
|
+
async ({ document, op, heading, markdown }) => {
|
|
37447
38117
|
if (op === "replace_section" && !heading) {
|
|
37448
38118
|
throw new Error(
|
|
37449
38119
|
"replace_section needs `heading`: the text of the heading whose section to replace."
|
|
@@ -37451,15 +38121,27 @@ server.registerTool(
|
|
|
37451
38121
|
}
|
|
37452
38122
|
return text(
|
|
37453
38123
|
await space().editDocument(
|
|
37454
|
-
op === "replace_section" ? { op, heading, markdown } : { op, markdown }
|
|
38124
|
+
op === "replace_section" ? { op, heading, markdown } : { op, markdown },
|
|
38125
|
+
document
|
|
37455
38126
|
)
|
|
37456
38127
|
);
|
|
37457
38128
|
}
|
|
37458
38129
|
);
|
|
38130
|
+
server.registerTool(
|
|
38131
|
+
"remove_document",
|
|
38132
|
+
{
|
|
38133
|
+
description: 'Remove a shared document you made, for good. You cannot remove documents other people or agents made, nor the first one ("main").',
|
|
38134
|
+
inputSchema: { document: external_exports.string().describe("Its id from list_documents") }
|
|
38135
|
+
},
|
|
38136
|
+
async ({ document }) => {
|
|
38137
|
+
await space().removeDocument(document);
|
|
38138
|
+
return text({ removed: document });
|
|
38139
|
+
}
|
|
38140
|
+
);
|
|
37459
38141
|
server.registerTool(
|
|
37460
38142
|
"propose",
|
|
37461
38143
|
{
|
|
37462
|
-
description: 'Offer to do something and wait for a person to approve it. Everyone sees it as a card in the
|
|
38144
|
+
description: 'Offer to do something and wait for a person to approve it. Everyone sees it as a card in the conversation with an approve button and Dismiss. Use it before anything that reaches outside the meeting or is hard to undo (sending a message, filing a ticket, changing files, spending money), and when you have a suggestion the room may not want ("Add this to the decisions list?"). Do NOT act until you receive a `proposal_decided` event from wait_for_events with this proposal\'s id and decision "approved"; if it is "dismissed", drop it. After approval, report progress with update_proposal.',
|
|
37463
38145
|
inputSchema: {
|
|
37464
38146
|
text: external_exports.string().describe("What you are offering to do, as a question or a plain statement"),
|
|
37465
38147
|
action: external_exports.string().max(40).optional().describe('Label for the approve button (default "Do it")'),
|
|
@@ -37491,16 +38173,73 @@ server.registerTool(
|
|
|
37491
38173
|
server.registerTool(
|
|
37492
38174
|
"update_request",
|
|
37493
38175
|
{
|
|
37494
|
-
description: 'Report progress on a request that was addressed to you (a `request` event from wait_for_events whose `to` includes your agent id; its `seq` is the id). Everyone in the meeting sees the status next to the request, so keep it current: call with "acknowledged" the moment you receive the request, "working" with a short note on what you are doing before each slow step, and finish with "done" (note: what you did or where to look) or "failed" (note: why). A request left without a final status keeps showing as in progress.',
|
|
38176
|
+
description: 'Report progress on a request that was addressed to you (a `request` event from wait_for_events whose `to` includes your agent id; its `seq` is the id). Everyone in the meeting sees the status next to the request, and sees your figure in the room busy with it (several at once, if you are working on several), so keep it current: call with "acknowledged" the moment you receive the request, "working" with a short note on what you are doing before each slow step, and finish with "done" (note: what you did or where to look) or "failed" (note: why). A request left without a final status keeps showing as in progress.',
|
|
37495
38177
|
inputSchema: {
|
|
37496
38178
|
id: external_exports.number().int().describe("The request id: `seq` of the request event"),
|
|
37497
38179
|
status: external_exports.enum(REQUEST_STATUSES),
|
|
37498
|
-
note: external_exports.string().max(200).optional().describe("One short line shown next to the status")
|
|
38180
|
+
note: external_exports.string().max(200).optional().describe("One short line shown next to the status"),
|
|
38181
|
+
surfaceId: external_exports.string().optional().describe(
|
|
38182
|
+
"The surface this work is on, if any. You are shown on that tab while you work, so people see where to look. Pass it once you have created or picked the surface."
|
|
38183
|
+
),
|
|
38184
|
+
doing: external_exports.enum(WORK_KINDS).optional().describe(DOING),
|
|
38185
|
+
documentId: WORK_DOCUMENT,
|
|
38186
|
+
section: WORK_SECTION
|
|
37499
38187
|
}
|
|
37500
38188
|
},
|
|
37501
|
-
async ({ id, status, note }) => {
|
|
37502
|
-
const entry = await space().updateRequest(
|
|
38189
|
+
async ({ id, status, note, surfaceId, doing, documentId, section }) => {
|
|
38190
|
+
const entry = await space().updateRequest(
|
|
38191
|
+
id,
|
|
38192
|
+
status,
|
|
38193
|
+
note,
|
|
38194
|
+
surfaceId,
|
|
38195
|
+
doing,
|
|
38196
|
+
documentId ? { docId: documentId, section } : void 0
|
|
38197
|
+
);
|
|
37503
38198
|
return text({ request: entry.seq, status, to: entry.request?.to });
|
|
37504
38199
|
}
|
|
37505
38200
|
);
|
|
37506
|
-
|
|
38201
|
+
server.registerTool(
|
|
38202
|
+
"working_on",
|
|
38203
|
+
{
|
|
38204
|
+
description: "Show everyone in the space what you are doing right now, when it is not an answer to a request (for a request, update_request takes the same `doing`). Your figure in the room plays the matching animation, on the surface's tab if you name one. Call it before any piece of work that takes more than a few seconds (your person asked you for something in your own conversation, say), again when what you are doing changes, and with `done: true` when you finish; it lapses by itself after three minutes. To show several things at once, give each its own `key`.",
|
|
38205
|
+
inputSchema: {
|
|
38206
|
+
doing: external_exports.enum(WORK_KINDS).optional().describe(DOING),
|
|
38207
|
+
note: external_exports.string().max(200).optional().describe("A few words on what it is"),
|
|
38208
|
+
surfaceId: external_exports.string().optional().describe("The surface it is happening on, if any"),
|
|
38209
|
+
documentId: WORK_DOCUMENT,
|
|
38210
|
+
section: WORK_SECTION,
|
|
38211
|
+
key: external_exports.string().max(40).optional().describe('Names this piece of work, when you have several at once (default "main")'),
|
|
38212
|
+
done: external_exports.boolean().optional().describe("Set true when this piece of work is finished")
|
|
38213
|
+
}
|
|
38214
|
+
},
|
|
38215
|
+
async ({ doing, note, surfaceId, documentId, section, key, done }) => {
|
|
38216
|
+
if (done) return text(await space().setWork({ key, done: true }));
|
|
38217
|
+
if (!doing) throw new Error(`working_on needs \`doing\`: one of ${WORK_KINDS.join(", ")}.`);
|
|
38218
|
+
return text(
|
|
38219
|
+
await space().setWork({
|
|
38220
|
+
key,
|
|
38221
|
+
doing,
|
|
38222
|
+
note,
|
|
38223
|
+
surfaceId,
|
|
38224
|
+
...documentId ? { docId: documentId, section } : {}
|
|
38225
|
+
})
|
|
38226
|
+
);
|
|
38227
|
+
}
|
|
38228
|
+
);
|
|
38229
|
+
if (process.argv[2] === "setup") {
|
|
38230
|
+
process.exit(
|
|
38231
|
+
await setup(process.argv.slice(3)).catch((e) => {
|
|
38232
|
+
console.error(e instanceof Error ? e.message : String(e));
|
|
38233
|
+
return 1;
|
|
38234
|
+
})
|
|
38235
|
+
);
|
|
38236
|
+
} else if (process.argv[2] === "wake") {
|
|
38237
|
+
process.exit(
|
|
38238
|
+
await main().catch((e) => {
|
|
38239
|
+
console.error(e instanceof Error ? e.message : String(e));
|
|
38240
|
+
return 1;
|
|
38241
|
+
})
|
|
38242
|
+
);
|
|
38243
|
+
} else {
|
|
38244
|
+
await server.connect(new StdioServerTransport());
|
|
38245
|
+
}
|