@plosson/agentio 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/mcp/server.ts +19 -2
- package/src/services/gchat/client.ts +50 -2
package/package.json
CHANGED
package/src/mcp/server.ts
CHANGED
|
@@ -197,6 +197,9 @@ export async function startMcpServer(
|
|
|
197
197
|
process.exit(1);
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
// Track last-checked timestamps per service:space for automatic --since injection
|
|
201
|
+
const lastChecked = new Map<string, Date>();
|
|
202
|
+
|
|
200
203
|
// Create MCP server
|
|
201
204
|
const server = new Server(
|
|
202
205
|
{ name: 'agentio', version: '1.0.0' },
|
|
@@ -230,6 +233,8 @@ export async function startMcpServer(
|
|
|
230
233
|
const service = tool.commandPath[0];
|
|
231
234
|
const profile = profileMap.get(service);
|
|
232
235
|
|
|
236
|
+
const input = (args as Record<string, unknown>) || {};
|
|
237
|
+
|
|
233
238
|
// Build a fresh program for each call to avoid state leaks
|
|
234
239
|
const execProgram = buildProgram(services);
|
|
235
240
|
|
|
@@ -237,11 +242,23 @@ export async function startMcpServer(
|
|
|
237
242
|
const output = await executeCommand(
|
|
238
243
|
execProgram,
|
|
239
244
|
tool,
|
|
240
|
-
|
|
245
|
+
input,
|
|
241
246
|
profile
|
|
242
247
|
);
|
|
248
|
+
|
|
249
|
+
// For list commands, append last-checked info and update timestamp
|
|
250
|
+
let result = output || '(no output)';
|
|
251
|
+
if (name === 'gchat_list' && input.space) {
|
|
252
|
+
const key = `gchat:${input.space}`;
|
|
253
|
+
const last = lastChecked.get(key);
|
|
254
|
+
if (last) {
|
|
255
|
+
result += `\n\nPreviously checked: ${last.toISOString()}`;
|
|
256
|
+
}
|
|
257
|
+
lastChecked.set(key, new Date());
|
|
258
|
+
}
|
|
259
|
+
|
|
243
260
|
return {
|
|
244
|
-
content: [{ type: 'text' as const, text:
|
|
261
|
+
content: [{ type: 'text' as const, text: result }],
|
|
245
262
|
};
|
|
246
263
|
} catch (error: unknown) {
|
|
247
264
|
const message =
|
|
@@ -23,6 +23,7 @@ interface ResolvedUser {
|
|
|
23
23
|
export class GChatClient implements ServiceClient {
|
|
24
24
|
private credentials: GChatCredentials;
|
|
25
25
|
private userCache = new Map<string, ResolvedUser>();
|
|
26
|
+
private spaceIdCache = new Map<string, string>();
|
|
26
27
|
|
|
27
28
|
constructor(credentials: GChatCredentials) {
|
|
28
29
|
this.credentials = credentials;
|
|
@@ -52,9 +53,11 @@ export class GChatClient implements ServiceClient {
|
|
|
52
53
|
async send(options: GChatSendOptions & { spaceId?: string }): Promise<GChatSendResult> {
|
|
53
54
|
if (this.credentials.type === 'webhook') {
|
|
54
55
|
return this.sendViaWebhook(options);
|
|
55
|
-
} else {
|
|
56
|
-
return this.sendViaOAuth(options);
|
|
57
56
|
}
|
|
57
|
+
if (options.spaceId) {
|
|
58
|
+
options.spaceId = await this.resolveSpaceId(options.spaceId);
|
|
59
|
+
}
|
|
60
|
+
return this.sendViaOAuth(options);
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
async list(options: GChatListOptions): Promise<GChatMessage[]> {
|
|
@@ -72,6 +75,7 @@ export class GChatClient implements ServiceClient {
|
|
|
72
75
|
'Use an OAuth profile to read messages'
|
|
73
76
|
);
|
|
74
77
|
}
|
|
78
|
+
options.spaceId = await this.resolveSpaceId(options.spaceId);
|
|
75
79
|
return this.listViaOAuth(options);
|
|
76
80
|
}
|
|
77
81
|
|
|
@@ -90,6 +94,7 @@ export class GChatClient implements ServiceClient {
|
|
|
90
94
|
'Use an OAuth profile to read messages'
|
|
91
95
|
);
|
|
92
96
|
}
|
|
97
|
+
options.spaceId = await this.resolveSpaceId(options.spaceId);
|
|
93
98
|
return this.getViaOAuth(options);
|
|
94
99
|
}
|
|
95
100
|
|
|
@@ -347,6 +352,49 @@ export class GChatClient implements ServiceClient {
|
|
|
347
352
|
}
|
|
348
353
|
}
|
|
349
354
|
|
|
355
|
+
/**
|
|
356
|
+
* Resolve a space identifier that may be an ID or a display name.
|
|
357
|
+
* Tries as ID first (no API call), falls back to name resolution via listSpaces.
|
|
358
|
+
* Results are cached for the lifetime of this client instance.
|
|
359
|
+
*/
|
|
360
|
+
private async resolveSpaceId(spaceIdOrName: string): Promise<string> {
|
|
361
|
+
// Check cache first
|
|
362
|
+
const cached = this.spaceIdCache.get(spaceIdOrName);
|
|
363
|
+
if (cached) return cached;
|
|
364
|
+
|
|
365
|
+
const oauthCreds = this.credentials as GChatOAuthCredentials;
|
|
366
|
+
const auth = this.createOAuthClient(oauthCreds);
|
|
367
|
+
const chat = gchat({ version: 'v1', auth: auth as any });
|
|
368
|
+
|
|
369
|
+
// Try as ID first
|
|
370
|
+
try {
|
|
371
|
+
const resp = await chat.spaces.get({ name: `spaces/${spaceIdOrName}` });
|
|
372
|
+
if (resp.data.name) {
|
|
373
|
+
this.spaceIdCache.set(spaceIdOrName, spaceIdOrName);
|
|
374
|
+
return spaceIdOrName;
|
|
375
|
+
}
|
|
376
|
+
} catch {
|
|
377
|
+
// Not a valid ID, try as display name
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Resolve by display name
|
|
381
|
+
const spaces = await this.listSpacesViaOAuth();
|
|
382
|
+
const nameLower = spaceIdOrName.toLowerCase();
|
|
383
|
+
const match = spaces.find(s => s.displayName.toLowerCase() === nameLower);
|
|
384
|
+
|
|
385
|
+
if (!match) {
|
|
386
|
+
throw new CliError(
|
|
387
|
+
'NOT_FOUND',
|
|
388
|
+
`Space not found: "${spaceIdOrName}"`,
|
|
389
|
+
'Use "agentio gchat spaces" to list available spaces'
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const id = match.name.replace('spaces/', '');
|
|
394
|
+
this.spaceIdCache.set(spaceIdOrName, id);
|
|
395
|
+
return id;
|
|
396
|
+
}
|
|
397
|
+
|
|
350
398
|
private async resolveUsers(userIds: string[], auth: OAuth2Client): Promise<void> {
|
|
351
399
|
const unknown = userIds.filter(id => !this.userCache.has(id));
|
|
352
400
|
if (unknown.length === 0) return;
|