message-cp 0.1.0 → 0.1.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/dist/dashboard.js +35 -19
- package/dist/db.js +72 -19
- package/dist/init.js +3 -2
- package/dist/server.js +4 -1
- package/package.json +2 -1
package/dist/dashboard.js
CHANGED
|
@@ -702,6 +702,9 @@ let peopleRowsKey = "";
|
|
|
702
702
|
let inviteClosings = 0;
|
|
703
703
|
// The id of the invite whose link the view shows, or undefined, so cancelling that invite takes its link away.
|
|
704
704
|
let shownInvite;
|
|
705
|
+
// How many times showInvite has shown a link, so a write with no answer hides only a link shown before it started, never
|
|
706
|
+
// one another request showed meanwhile.
|
|
707
|
+
let invitesShown = 0;
|
|
705
708
|
// The invite the "People" view asks before cancelling ({ id, person, createdAt }), or undefined.
|
|
706
709
|
let cancelTarget;
|
|
707
710
|
// The list the people view's invites and the "New link" select were last built from, so a poll rebuilds them only when it
|
|
@@ -1545,12 +1548,22 @@ function showInvite(body) {
|
|
|
1545
1548
|
"It works once and lasts " + INVITE_DAYS + " days, until " + when(invite.expiresAt) + ". It shows only here, until you leave this view.";
|
|
1546
1549
|
inviteLink.value = link;
|
|
1547
1550
|
shownInvite = invite.id;
|
|
1551
|
+
invitesShown++;
|
|
1548
1552
|
copyNote.textContent = "";
|
|
1549
1553
|
inviteMade.hidden = false;
|
|
1550
1554
|
inviteLink.focus();
|
|
1551
1555
|
return true;
|
|
1552
1556
|
}
|
|
1553
1557
|
|
|
1558
|
+
// Hides the invite link showInvite showed, with its line and its copy note.
|
|
1559
|
+
function hideInvite() {
|
|
1560
|
+
inviteMade.hidden = true;
|
|
1561
|
+
inviteFor.textContent = "";
|
|
1562
|
+
inviteLink.value = "";
|
|
1563
|
+
copyNote.textContent = "";
|
|
1564
|
+
shownInvite = undefined;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1554
1567
|
// Reads the people and the invites again at once, after a write that may have changed them.
|
|
1555
1568
|
function refresh() {
|
|
1556
1569
|
cancel();
|
|
@@ -1573,6 +1586,7 @@ async function addInvitedPerson() {
|
|
|
1573
1586
|
}
|
|
1574
1587
|
adding = tokenGen;
|
|
1575
1588
|
const closings = inviteClosings;
|
|
1589
|
+
const shown = invitesShown;
|
|
1576
1590
|
personAdd.disabled = true;
|
|
1577
1591
|
personNote.textContent = "Adding…";
|
|
1578
1592
|
const ac = new AbortController();
|
|
@@ -1598,7 +1612,10 @@ async function addInvitedPerson() {
|
|
|
1598
1612
|
return;
|
|
1599
1613
|
}
|
|
1600
1614
|
if (answer.error) {
|
|
1601
|
-
|
|
1615
|
+
// A link may have been made, so the one shown before may be taken for it. A link another request showed meanwhile
|
|
1616
|
+
// stays: it is that request's own result.
|
|
1617
|
+
if (shown === invitesShown) hideInvite();
|
|
1618
|
+
personNote.textContent = answer.error + ", so " + name + " may or may not have been added: the agents panel and the invites below show which once they have been read again.";
|
|
1602
1619
|
refresh();
|
|
1603
1620
|
return;
|
|
1604
1621
|
}
|
|
@@ -1618,6 +1635,7 @@ async function makeInvite() {
|
|
|
1618
1635
|
}
|
|
1619
1636
|
linking = tokenGen;
|
|
1620
1637
|
const closings = inviteClosings;
|
|
1638
|
+
const shown = invitesShown;
|
|
1621
1639
|
linkMake.disabled = true;
|
|
1622
1640
|
linkNote.textContent = "Making a link…";
|
|
1623
1641
|
const ac = new AbortController();
|
|
@@ -1640,7 +1658,10 @@ async function makeInvite() {
|
|
|
1640
1658
|
return;
|
|
1641
1659
|
}
|
|
1642
1660
|
if (answer.error) {
|
|
1643
|
-
|
|
1661
|
+
// A link may have been made, so the one shown before may be taken for it. A link another request showed meanwhile
|
|
1662
|
+
// stays: it is that request's own result.
|
|
1663
|
+
if (shown === invitesShown) hideInvite();
|
|
1664
|
+
linkNote.textContent = answer.error + ", so a link for " + person + " may or may not have been made: the invites below show which once they have been read again.";
|
|
1644
1665
|
refresh();
|
|
1645
1666
|
return;
|
|
1646
1667
|
}
|
|
@@ -1714,17 +1735,12 @@ async function cancelInvite() {
|
|
|
1714
1735
|
if (answer.status === 403) return unmarked();
|
|
1715
1736
|
// A confirmation opened for another invite meanwhile stays.
|
|
1716
1737
|
if (cancelTarget === invite) closeCancelInvite(true);
|
|
1717
|
-
// Once the server says this invite is gone (200, or 404: used, cancelled or expired),
|
|
1718
|
-
if
|
|
1719
|
-
|
|
1720
|
-
inviteFor.textContent = "";
|
|
1721
|
-
inviteLink.value = "";
|
|
1722
|
-
copyNote.textContent = "";
|
|
1723
|
-
shownInvite = undefined;
|
|
1724
|
-
}
|
|
1738
|
+
// Once the server says this invite is gone (200, or 404: used, cancelled or expired), or may be (no answer), its link,
|
|
1739
|
+
// if shown, goes too.
|
|
1740
|
+
if ((answer.status === 200 || answer.status === 404 || answer.error) && shownInvite === invite.id) hideInvite();
|
|
1725
1741
|
if (answer.status === 200) cancelNote.textContent = "Cancelled " + invite.person + "'s invite.";
|
|
1726
1742
|
else if (answer.status === 404) cancelNote.textContent = invite.person + "'s invite was already used, cancelled or expired (404).";
|
|
1727
|
-
else if (answer.error) cancelNote.textContent = answer.error + ", so " + invite.person + "'s invite may have been cancelled: the list below shows
|
|
1743
|
+
else if (answer.error) cancelNote.textContent = answer.error + ", so " + invite.person + "'s invite may or may not have been cancelled: the list below shows which once it has been read again.";
|
|
1728
1744
|
else cancelNote.textContent = refusal(answer);
|
|
1729
1745
|
if (answer.status === 200 || answer.status === 404 || answer.error) refresh();
|
|
1730
1746
|
}
|
|
@@ -1755,7 +1771,7 @@ async function markPerson(name, admin) {
|
|
|
1755
1771
|
if (answer.status === 403) return unmarked();
|
|
1756
1772
|
if (answer.status === 200) peopleNote.textContent = admin ? name + " is now an admin." : name + " is no longer an admin.";
|
|
1757
1773
|
else if (answer.status === 404) peopleNote.textContent = name + " is already gone (404).";
|
|
1758
|
-
else if (answer.error) peopleNote.textContent = answer.error + ", so " + name + " may have been changed: the list below shows
|
|
1774
|
+
else if (answer.error) peopleNote.textContent = answer.error + ", so " + name + " may or may not have been changed: the list below shows which once it has been read again.";
|
|
1759
1775
|
else peopleNote.textContent = refusal(answer);
|
|
1760
1776
|
if (answer.status === 200 || answer.status === 404 || answer.error) refresh();
|
|
1761
1777
|
}
|
|
@@ -1868,7 +1884,7 @@ async function removeAccountPerson() {
|
|
|
1868
1884
|
? "Removed " + name + ": " + counted(body.messages, "message") + " and " + counted(body.mentions, "mention") + " deleted."
|
|
1869
1885
|
: "Removed " + name + ".") + stopped;
|
|
1870
1886
|
} else if (answer.status === 404) peopleNote.textContent = name + " is already gone (404).";
|
|
1871
|
-
else if (answer.error) peopleNote.textContent = answer.error + ", so " + name + " may have been removed: the list below shows
|
|
1887
|
+
else if (answer.error) peopleNote.textContent = answer.error + ", so " + name + " may or may not have been removed: the list below shows which once it has been read again.";
|
|
1872
1888
|
else peopleNote.textContent = refusal(answer);
|
|
1873
1889
|
if (answer.status === 200 || answer.status === 404 || answer.error) refresh();
|
|
1874
1890
|
}
|
|
@@ -1932,7 +1948,7 @@ async function removeWebhook() {
|
|
|
1932
1948
|
? "Removed webhook " + hook.id + " and its " + (body.deliveries === 1 ? "1 delivery" : body.deliveries + " deliveries") + "."
|
|
1933
1949
|
: "Removed webhook " + hook.id + ".";
|
|
1934
1950
|
} else if (answer.status === 404) webhookNote.textContent = "Webhook " + hook.id + " was already gone (404).";
|
|
1935
|
-
else if (answer.error) webhookNote.textContent = answer.error + ", so webhook " + hook.id + " may have been removed: the list below shows
|
|
1951
|
+
else if (answer.error) webhookNote.textContent = answer.error + ", so webhook " + hook.id + " may or may not have been removed: the list below shows which once it has been read again.";
|
|
1936
1952
|
else webhookNote.textContent = refusal(answer);
|
|
1937
1953
|
if (answer.status === 200 || answer.status === 404 || answer.error) refresh();
|
|
1938
1954
|
}
|
|
@@ -2012,8 +2028,8 @@ async function addAccountWebhook() {
|
|
|
2012
2028
|
// Another webhook may have been added, so the secret shown before may no longer be the newest.
|
|
2013
2029
|
hideWebhookSecret();
|
|
2014
2030
|
hookNote.textContent =
|
|
2015
|
-
answer.error + ", so the webhook may or may not have been added: the list below shows
|
|
2016
|
-
"No secret can be shown for it, so if it is
|
|
2031
|
+
answer.error + ", so the webhook may or may not have been added: the list below shows which once it has been read again. " +
|
|
2032
|
+
"No secret can be shown for it, and the list cannot show which webhook this attempt added, so remove one there only if you know it is that one, then add it again.";
|
|
2017
2033
|
refresh();
|
|
2018
2034
|
return;
|
|
2019
2035
|
}
|
|
@@ -2109,7 +2125,7 @@ async function revokeApproval() {
|
|
|
2109
2125
|
const named = "token " + target.id + " (" + target.client + ")";
|
|
2110
2126
|
if (answer.status === 200) approvalNote.textContent = "Revoked " + named + "." + endedAgents(answer.body);
|
|
2111
2127
|
else if (answer.status === 404) approvalNote.textContent = "Token " + target.id + " (" + target.client + ") was already revoked or gone (404).";
|
|
2112
|
-
else if (answer.error) approvalNote.textContent = answer.error + ", so " + named + " may have been revoked: the list below shows
|
|
2128
|
+
else if (answer.error) approvalNote.textContent = answer.error + ", so " + named + " may or may not have been revoked: the list below shows which once it has been read again.";
|
|
2113
2129
|
else approvalNote.textContent = refusal(answer);
|
|
2114
2130
|
if (answer.status === 200 || answer.status === 404 || answer.error) refresh();
|
|
2115
2131
|
}
|
|
@@ -2184,7 +2200,7 @@ async function revokeToken() {
|
|
|
2184
2200
|
tokenNote.textContent = "Revoked token " + target.id + " (" + target.label + ")." + ended + endedAgents(answer.body);
|
|
2185
2201
|
}
|
|
2186
2202
|
else if (answer.status === 404) tokenNote.textContent = "Token " + target.id + " was already revoked or gone (404).";
|
|
2187
|
-
else if (answer.error) tokenNote.textContent = answer.error + ", so token " + target.id + " may or may not have been revoked: the list below shows which.";
|
|
2203
|
+
else if (answer.error) tokenNote.textContent = answer.error + ", so token " + target.id + " may or may not have been revoked: the list below shows which once it has been read again.";
|
|
2188
2204
|
else tokenNote.textContent = refusal(answer);
|
|
2189
2205
|
if (answer.status === 200 || answer.status === 404 || answer.error) refresh();
|
|
2190
2206
|
}
|
|
@@ -2449,7 +2465,7 @@ async function removeAgent() {
|
|
|
2449
2465
|
? "Removed " + name + ": " + counted(body.messages, "message") + " and " + counted(body.mentions, "mention") + " deleted."
|
|
2450
2466
|
: "Removed " + name + ".") + stopped;
|
|
2451
2467
|
} else if (answer.status === 404) agentNote.textContent = name + " is already gone (404).";
|
|
2452
|
-
else if (answer.error) agentNote.textContent = answer.error + ", so " + name + " may have been removed: the list shows
|
|
2468
|
+
else if (answer.error) agentNote.textContent = answer.error + ", so " + name + " may or may not have been removed: the list shows which once it has been read again.";
|
|
2453
2469
|
else agentNote.textContent = refusal(answer);
|
|
2454
2470
|
if (forgot) refresh();
|
|
2455
2471
|
}
|
package/dist/db.js
CHANGED
|
@@ -60,6 +60,37 @@ const MESSAGES_REPLY_INDEXES = `
|
|
|
60
60
|
CREATE INDEX IF NOT EXISTS messages_by_reply ON messages (in_reply_to);
|
|
61
61
|
-- \`ask\` finds the replies to one question sent to the asker through this, in id order, not by walking every reply to it.
|
|
62
62
|
CREATE INDEX IF NOT EXISTS messages_by_question ON messages (in_reply_to, recipient_id);`;
|
|
63
|
+
// Outgoing webhooks, added only with `message-cp webhook`; an admin person token also lists and removes them over
|
|
64
|
+
// /api/webhooks. `event` is 'channel', 'mention' or 'agent' and `name` the channel or agent in the same account,
|
|
65
|
+
// which need not exist yet. The secret is stored as is: deliveries sign with it.
|
|
66
|
+
// So is the value of the one header a webhook may carry (`header_name`, `header_value`), since deliveries send it.
|
|
67
|
+
// `max_per_hour` (null for none) caps its delivery requests in a fixed window: `window_started_at` is when the first
|
|
68
|
+
// request after the last window ended was claimed, and `window_count` how many requests have been claimed since; the
|
|
69
|
+
// window lasts 60 minutes. `fixed_body` is the JSON body the owner set with `webhook add --body`, sent in place of the
|
|
70
|
+
// event body, or null to send the event body. AUTOINCREMENT so a webhook's id, which GET and DELETE /api/webhooks and
|
|
71
|
+
// `webhook list` hand out, never names a later webhook once the highest one is removed. A table from before it is
|
|
72
|
+
// rebuilt by rebuildWebhooks(), which creates the same table; the columns after `created_at` are added to it in
|
|
73
|
+
// migrate first.
|
|
74
|
+
const webhooksTable = (table) => `
|
|
75
|
+
CREATE TABLE IF NOT EXISTS ${table} (
|
|
76
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
77
|
+
account_id INTEGER NOT NULL REFERENCES accounts(id),
|
|
78
|
+
url TEXT NOT NULL,
|
|
79
|
+
event TEXT NOT NULL CHECK (event IN ('channel', 'mention', 'agent')),
|
|
80
|
+
name TEXT NOT NULL,
|
|
81
|
+
include_text INTEGER NOT NULL,
|
|
82
|
+
secret TEXT NOT NULL,
|
|
83
|
+
created_at INTEGER NOT NULL,
|
|
84
|
+
header_name TEXT,
|
|
85
|
+
header_value TEXT,
|
|
86
|
+
max_per_hour INTEGER,
|
|
87
|
+
window_started_at INTEGER,
|
|
88
|
+
window_count INTEGER NOT NULL DEFAULT 0,
|
|
89
|
+
fixed_body TEXT
|
|
90
|
+
);`;
|
|
91
|
+
// Dropping a table drops its indexes, so rebuildWebhooks() creates this again.
|
|
92
|
+
const WEBHOOKS_INDEXES = `
|
|
93
|
+
CREATE INDEX IF NOT EXISTS webhooks_by_name ON webhooks (account_id, event, name COLLATE NOCASE);`;
|
|
63
94
|
// One-time dashboard sign-in codes from `message-cp login-link`, and invites an admin person made (src/login.ts). Only
|
|
64
95
|
// the code's hash is kept. `person_id` is set for a link that signs in as that person; `used_at` once it has given its
|
|
65
96
|
// token or, for an invite, was cancelled. AUTOINCREMENT so an invite's id, which GET and DELETE /api/invites hand out,
|
|
@@ -166,25 +197,8 @@ CREATE INDEX IF NOT EXISTS mentions_unread ON mentions (agent_id, read_at, post_
|
|
|
166
197
|
-- GET /api/channel-posts reads a page of posts' mentions through this, not by scanning every mention.
|
|
167
198
|
CREATE INDEX IF NOT EXISTS mentions_by_post ON mentions (post_id);
|
|
168
199
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
-- which need not exist yet. The secret is stored as is: deliveries sign with it.
|
|
172
|
-
-- So is the value of the one header a webhook may carry (\`header_name\`, \`header_value\`, added in \`migrate\`), since
|
|
173
|
-
-- deliveries send it. \`max_per_hour\` (also added in \`migrate\`, null for none) caps its delivery requests in a fixed
|
|
174
|
-
-- window: \`window_started_at\` is when the first request after the last window ended was claimed, and \`window_count\`
|
|
175
|
-
-- how many requests have been claimed since; the window lasts 60 minutes. \`fixed_body\` (also added in \`migrate\`) is the
|
|
176
|
-
-- JSON body the owner set with \`webhook add --body\`, sent in place of the event body, or null to send the event body.
|
|
177
|
-
CREATE TABLE IF NOT EXISTS webhooks (
|
|
178
|
-
id INTEGER PRIMARY KEY,
|
|
179
|
-
account_id INTEGER NOT NULL REFERENCES accounts(id),
|
|
180
|
-
url TEXT NOT NULL,
|
|
181
|
-
event TEXT NOT NULL CHECK (event IN ('channel', 'mention', 'agent')),
|
|
182
|
-
name TEXT NOT NULL,
|
|
183
|
-
include_text INTEGER NOT NULL,
|
|
184
|
-
secret TEXT NOT NULL,
|
|
185
|
-
created_at INTEGER NOT NULL
|
|
186
|
-
);
|
|
187
|
-
CREATE INDEX IF NOT EXISTS webhooks_by_name ON webhooks (account_id, event, name COLLATE NOCASE);
|
|
200
|
+
${webhooksTable("webhooks")}
|
|
201
|
+
${WEBHOOKS_INDEXES}
|
|
188
202
|
|
|
189
203
|
-- What a webhook is owed: one row per webhook per message or post it is for, queued in the transaction that stores the
|
|
190
204
|
-- message or post. Only ids are kept, so the body is built when it is sent. \`serve\` sends them: \`attempts\` is counted
|
|
@@ -460,6 +474,43 @@ function rebuildDashboardSessions(db) {
|
|
|
460
474
|
db.exec(DASHBOARD_SESSIONS_INDEXES);
|
|
461
475
|
}).immediate();
|
|
462
476
|
}
|
|
477
|
+
/** 1 when rebuildWebhooks() has copied every webhook into its new table. Both tables hold only webhooks, not history. */
|
|
478
|
+
export const COUNT_WEBHOOKS_COPIED = "SELECT (SELECT count(*) FROM webhooks) = (SELECT count(*) FROM webhooks_autoincrement)";
|
|
479
|
+
const webhooksAutoincrement = (db) => /\bAUTOINCREMENT\b/i.test(db.prepare("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'webhooks'").get().sql);
|
|
480
|
+
/**
|
|
481
|
+
* Rebuilds a `webhooks` table from before AUTOINCREMENT as `webhooksTable`, keeping every row, its id and every column,
|
|
482
|
+
* as rebuildLoginCodes() does for `login_codes`, so that no webhook id is handed out again once the highest webhook is
|
|
483
|
+
* removed. Under the write lock, so exactly one process rebuilds. `webhook_deliveries` refers to `webhooks`, so as in
|
|
484
|
+
* rebuildMessages() foreign keys are off around it. Ids are copied as they are, so every delivery still finds its webhook
|
|
485
|
+
* once every row is copied; it checks that by counting webhooks rather than with foreign_key_check, which would read every
|
|
486
|
+
* table's history under the write lock.
|
|
487
|
+
*/
|
|
488
|
+
function rebuildWebhooks(db) {
|
|
489
|
+
if (webhooksAutoincrement(db))
|
|
490
|
+
return;
|
|
491
|
+
// Dropping the old table must not cascade to the deliveries that reference it.
|
|
492
|
+
const foreignKeys = db.pragma("foreign_keys", { simple: true });
|
|
493
|
+
db.pragma("foreign_keys = OFF");
|
|
494
|
+
try {
|
|
495
|
+
db.transaction(() => {
|
|
496
|
+
if (webhooksAutoincrement(db))
|
|
497
|
+
return;
|
|
498
|
+
db.exec(webhooksTable("webhooks_autoincrement"));
|
|
499
|
+
db.exec(`INSERT INTO webhooks_autoincrement (id, account_id, url, event, name, include_text, secret, created_at, header_name, header_value,
|
|
500
|
+
max_per_hour, window_started_at, window_count, fixed_body)
|
|
501
|
+
SELECT id, account_id, url, event, name, include_text, secret, created_at, header_name, header_value,
|
|
502
|
+
max_per_hour, window_started_at, window_count, fixed_body FROM webhooks ORDER BY id`);
|
|
503
|
+
if (db.prepare(COUNT_WEBHOOKS_COPIED).pluck().get() !== 1)
|
|
504
|
+
throw new Error("webhooks migration lost a webhook");
|
|
505
|
+
db.exec("DROP TABLE webhooks");
|
|
506
|
+
db.exec("ALTER TABLE webhooks_autoincrement RENAME TO webhooks");
|
|
507
|
+
db.exec(WEBHOOKS_INDEXES);
|
|
508
|
+
}).immediate();
|
|
509
|
+
}
|
|
510
|
+
finally {
|
|
511
|
+
db.pragma(`foreign_keys = ${foreignKeys ? "ON" : "OFF"}`);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
463
514
|
function migrate(db, path) {
|
|
464
515
|
addColumn(db, "key_hash", "TEXT");
|
|
465
516
|
// A random token per registration, so a session can tell its agent from a later one that reuses the id or name.
|
|
@@ -522,6 +573,8 @@ function migrate(db, path) {
|
|
|
522
573
|
addColumn(db, "window_count", "INTEGER NOT NULL DEFAULT 0", "webhooks");
|
|
523
574
|
// The JSON body a webhook sends in place of the event body (`webhook add --body`); null, so the event body, for one without.
|
|
524
575
|
addColumn(db, "fixed_body", "TEXT", "webhooks");
|
|
576
|
+
// Every webhooks column above is added before rebuildWebhooks() copies it.
|
|
577
|
+
rebuildWebhooks(db);
|
|
525
578
|
addColumn(db, "delivery_uuid", "TEXT", "webhook_deliveries");
|
|
526
579
|
// Set once a DELETE ends an HTTP session, so no server restores it or records it again.
|
|
527
580
|
addColumn(db, "ended", "INTEGER NOT NULL DEFAULT 0", "http_sessions");
|
package/dist/init.js
CHANGED
|
@@ -11,6 +11,7 @@ import { fileURLToPath } from "node:url";
|
|
|
11
11
|
import { NAME_PATTERN, NOT_REGISTERED, PurgeError, purgeAgent } from "./agents.js";
|
|
12
12
|
import { ConnectError, mcpEndpoint } from "./connect.js";
|
|
13
13
|
import { dbPath, openDb } from "./db.js";
|
|
14
|
+
import { VERSION } from "./server.js";
|
|
14
15
|
import { shellQuote } from "./wait.js";
|
|
15
16
|
// `message-cp init`: adds the stdio server `npx -y message-cp` to one coding agent's user-level MCP configuration on
|
|
16
17
|
// this machine, then proves the server works by registering a test agent, sending it a message and reading it back.
|
|
@@ -521,7 +522,7 @@ export async function proveServer(server, env = process.env) {
|
|
|
521
522
|
if (v !== undefined && k !== "MESSAGE_CP_AGENT" && k !== "MESSAGE_CP_AGENT_KEY")
|
|
522
523
|
childEnv[k] = v;
|
|
523
524
|
childEnv.MESSAGE_CP_DB = db;
|
|
524
|
-
const client = new Client({ name: "message-cp-init", version:
|
|
525
|
+
const client = new Client({ name: "message-cp-init", version: VERSION });
|
|
525
526
|
let failure;
|
|
526
527
|
let step = "starting the server";
|
|
527
528
|
let stderr = "";
|
|
@@ -594,7 +595,7 @@ export async function proveServer(server, env = process.env) {
|
|
|
594
595
|
}
|
|
595
596
|
/** A client of the team server `r`, sending `headers` on every request. */
|
|
596
597
|
function remoteClient(r, headers) {
|
|
597
|
-
const client = new Client({ name: "message-cp-init", version:
|
|
598
|
+
const client = new Client({ name: "message-cp-init", version: VERSION });
|
|
598
599
|
const transport = new StreamableHTTPClientTransport(r.endpoint, { requestInit: { headers } });
|
|
599
600
|
return { client, transport };
|
|
600
601
|
}
|
package/dist/server.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
import { z } from "zod";
|
|
3
4
|
import { DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT, MAX_STATUS_LENGTH, NOT_REGISTERED, STATUS_TTL_MS, ToolError, getAgent, listAgents, markWaiting, nameOfIdentity, registerWithIdentity, removeAgent, setStatus, touchAgent, } from "./agents.js";
|
|
4
5
|
import { OWNER_ACCOUNT } from "./db.js";
|
|
@@ -123,13 +124,15 @@ async function poll(deadline, signal, answer) {
|
|
|
123
124
|
return found;
|
|
124
125
|
}
|
|
125
126
|
}
|
|
127
|
+
/** The version in package.json, one directory up from this file whether it runs from src/ (tsx) or dist/ (a build or the npm package). */
|
|
128
|
+
export const VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
126
129
|
/** One server per session. */
|
|
127
130
|
export function createServer(db, session = new Session(OWNER_ACCOUNT), options = {}) {
|
|
128
131
|
const remote = options.remote ?? false;
|
|
129
132
|
const hold = options.hold ?? holdInProcess;
|
|
130
133
|
session.attach(db);
|
|
131
134
|
const account = session.account;
|
|
132
|
-
const server = new McpServer({ name: "message-cp", version:
|
|
135
|
+
const server = new McpServer({ name: "message-cp", version: VERSION }, { instructions: instructions(process.env, remote, options.publicUrl, options.agent, options.approval, session.account) });
|
|
133
136
|
// Aborts when the call is cancelled or, where the transport knows it, the response that would carry its answer closes.
|
|
134
137
|
const ended = (extra) => {
|
|
135
138
|
const closed = options.closed?.(extra.requestId);
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "message-cp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "A messaging service for AI agents: register a name, find each other, send messages and be woken when mail arrives",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"mcpName": "io.github.colinhallett/message-cp",
|
|
6
7
|
"main": "dist/index.js",
|
|
7
8
|
"files": [
|
|
8
9
|
"dist"
|