@agentchatme/agent-core 0.0.1 → 0.0.12
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/{chunk-4UOOWYCI.js → chunk-NEGTEBFK.js} +38 -5
- package/dist/{chunk-4UOOWYCI.js.map → chunk-NEGTEBFK.js.map} +1 -1
- package/dist/daemon-entry.d.ts +10 -5
- package/dist/daemon-entry.js +98 -27
- package/dist/daemon-entry.js.map +1 -1
- package/dist/index.d.ts +61 -5
- package/dist/index.js +171 -97
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -4,10 +4,13 @@ import {
|
|
|
4
4
|
WireError,
|
|
5
5
|
acquireLeaderLock,
|
|
6
6
|
alwaysOnHealth,
|
|
7
|
+
alwaysOnOptedOut,
|
|
8
|
+
alwaysOnState,
|
|
7
9
|
alwaysOnWanted,
|
|
8
10
|
atomicWriteFile,
|
|
9
11
|
beat,
|
|
10
12
|
claimReply,
|
|
13
|
+
clearAlwaysOnOptOut,
|
|
11
14
|
clearAlwaysOnWanted,
|
|
12
15
|
clearCredentials,
|
|
13
16
|
clearPending,
|
|
@@ -16,8 +19,10 @@ import {
|
|
|
16
19
|
credentialsPath,
|
|
17
20
|
external_exports,
|
|
18
21
|
getMeLite,
|
|
22
|
+
idle,
|
|
19
23
|
lastDeliveryId,
|
|
20
24
|
log,
|
|
25
|
+
markAlwaysOnOptOut,
|
|
21
26
|
markAlwaysOnWanted,
|
|
22
27
|
markSessionActive,
|
|
23
28
|
pendingPath,
|
|
@@ -30,7 +35,7 @@ import {
|
|
|
30
35
|
syncPeek,
|
|
31
36
|
writeCredentials,
|
|
32
37
|
writePending
|
|
33
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-NEGTEBFK.js";
|
|
34
39
|
|
|
35
40
|
// src/identity/state.ts
|
|
36
41
|
var SESSION_TTL_MS = 48 * 60 * 60 * 1e3;
|
|
@@ -50,7 +55,12 @@ var StateSchema = external_exports.object({
|
|
|
50
55
|
// Machine-wide timestamp of the last registration offer injected by the
|
|
51
56
|
// session-start hook. Keeps the unregistered-plugin nag to once a day
|
|
52
57
|
// instead of once per session.
|
|
53
|
-
last_offer_at: external_exports.string().optional()
|
|
58
|
+
last_offer_at: external_exports.string().optional(),
|
|
59
|
+
// Set when the user has said "not now" to setting up a handle. Unlike the
|
|
60
|
+
// cooldown above this is PERMANENT until they change their mind, because the
|
|
61
|
+
// prompt that needs suppressing lives in the always-loaded instruction file
|
|
62
|
+
// and would otherwise be re-read — and re-acted on — every single session.
|
|
63
|
+
offer_declined_at: external_exports.string().optional()
|
|
54
64
|
});
|
|
55
65
|
function readState(home) {
|
|
56
66
|
const raw = readJsonFile(statePath(home));
|
|
@@ -123,6 +133,122 @@ function recordRegistrationOffer(home, now = /* @__PURE__ */ new Date()) {
|
|
|
123
133
|
state.last_offer_at = now.toISOString();
|
|
124
134
|
writeState(home, state);
|
|
125
135
|
}
|
|
136
|
+
function recordOfferDeclined(home, now = /* @__PURE__ */ new Date()) {
|
|
137
|
+
const state = readState(home);
|
|
138
|
+
state.offer_declined_at = now.toISOString();
|
|
139
|
+
writeState(home, state);
|
|
140
|
+
}
|
|
141
|
+
function offerDeclined(home) {
|
|
142
|
+
return readState(home).offer_declined_at !== void 0;
|
|
143
|
+
}
|
|
144
|
+
function clearOfferDeclined(home) {
|
|
145
|
+
const state = readState(home);
|
|
146
|
+
delete state.offer_declined_at;
|
|
147
|
+
writeState(home, state);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/anchor/block.ts
|
|
151
|
+
import * as fs from "fs";
|
|
152
|
+
import * as path from "path";
|
|
153
|
+
var ANCHOR_START = "<!-- agentchat:start -->";
|
|
154
|
+
var ANCHOR_END = "<!-- agentchat:end -->";
|
|
155
|
+
var LEGACY_ANCHOR_START = "<!-- agentchat-skill:start -->";
|
|
156
|
+
var LEGACY_ANCHOR_END = "<!-- agentchat-skill:end -->";
|
|
157
|
+
function renderAnchorBlock(handle) {
|
|
158
|
+
return [
|
|
159
|
+
ANCHOR_START,
|
|
160
|
+
"## On AgentChat",
|
|
161
|
+
"",
|
|
162
|
+
`You are **@${handle}** on AgentChat \u2014 a peer-to-peer messaging network for AI agents. Your handle is your address here, like a phone number, except the other end is always another agent.`,
|
|
163
|
+
"",
|
|
164
|
+
"- Other agents can DM you. You can DM them \u2014 any time, for anything that fits your context.",
|
|
165
|
+
`- Share \`@${handle}\` freely. Whenever and wherever you think you should share. It's how peers reach you.`,
|
|
166
|
+
"- When you need to send, search, or handle an error, your AgentChat skill has the full reference.",
|
|
167
|
+
"",
|
|
168
|
+
"This is an identity, not a tool you reach for occasionally.",
|
|
169
|
+
ANCHOR_END
|
|
170
|
+
].join("\n");
|
|
171
|
+
}
|
|
172
|
+
function writeAnchor(filePath, block, expectHandle) {
|
|
173
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
174
|
+
const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf-8") : "";
|
|
175
|
+
fs.writeFileSync(filePath, upsertAnchorBlock(existing, block), "utf-8");
|
|
176
|
+
if (expectHandle !== void 0) {
|
|
177
|
+
const verify = fs.readFileSync(filePath, "utf-8");
|
|
178
|
+
if (!verify.includes(`@${expectHandle}`)) {
|
|
179
|
+
throw new Error(
|
|
180
|
+
`writeAnchor: handle @${expectHandle} did not land in ${filePath} \u2014 remove the agentchat block manually and re-run.`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return "written";
|
|
185
|
+
}
|
|
186
|
+
function removeAnchorAt(filePath) {
|
|
187
|
+
if (!fs.existsSync(filePath)) return "noop";
|
|
188
|
+
const existing = fs.readFileSync(filePath, "utf-8");
|
|
189
|
+
const next = stripAnchorBlock(existing);
|
|
190
|
+
if (next === existing) return "noop";
|
|
191
|
+
fs.writeFileSync(filePath, next, "utf-8");
|
|
192
|
+
return "removed";
|
|
193
|
+
}
|
|
194
|
+
function hasAnchorAt(filePath) {
|
|
195
|
+
if (!fs.existsSync(filePath)) return false;
|
|
196
|
+
return findBlock(fs.readFileSync(filePath, "utf-8"), ANCHOR_START, ANCHOR_END) !== null;
|
|
197
|
+
}
|
|
198
|
+
function readAnchorHandleAt(filePath) {
|
|
199
|
+
if (!fs.existsSync(filePath)) return null;
|
|
200
|
+
return readAnchorHandleFrom(fs.readFileSync(filePath, "utf-8"));
|
|
201
|
+
}
|
|
202
|
+
function readAnchorHandleFrom(text) {
|
|
203
|
+
const block = findBlock(text, ANCHOR_START, ANCHOR_END);
|
|
204
|
+
if (block === null) return null;
|
|
205
|
+
const match = /\*\*@([^*\s]+)\*\*/.exec(text.slice(block.from, block.to));
|
|
206
|
+
return match?.[1] ?? null;
|
|
207
|
+
}
|
|
208
|
+
function lineAnchoredIndex(text, marker, fromIndex = 0) {
|
|
209
|
+
let idx = text.indexOf(marker, fromIndex);
|
|
210
|
+
while (idx >= 0) {
|
|
211
|
+
const lineStart = text.lastIndexOf("\n", idx - 1) + 1;
|
|
212
|
+
const lineEndRaw = text.indexOf("\n", idx);
|
|
213
|
+
const lineEnd = lineEndRaw === -1 ? text.length : lineEndRaw;
|
|
214
|
+
if (text.slice(lineStart, lineEnd).trim() === marker) {
|
|
215
|
+
return { start: lineStart, end: lineEnd };
|
|
216
|
+
}
|
|
217
|
+
idx = text.indexOf(marker, idx + marker.length);
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
function findBlock(text, startMarker, endMarker) {
|
|
222
|
+
const start = lineAnchoredIndex(text, startMarker);
|
|
223
|
+
if (start === null) return null;
|
|
224
|
+
const end = lineAnchoredIndex(text, endMarker, start.end);
|
|
225
|
+
if (end === null) return null;
|
|
226
|
+
return { from: start.start, to: end.end };
|
|
227
|
+
}
|
|
228
|
+
function upsertAnchorBlock(existing, block) {
|
|
229
|
+
const cleaned = stripAnchorBlock(existing);
|
|
230
|
+
const trimmed = cleaned.replace(/\n+$/, "");
|
|
231
|
+
if (trimmed.length === 0) return block + "\n";
|
|
232
|
+
return trimmed + "\n\n" + block + "\n";
|
|
233
|
+
}
|
|
234
|
+
function stripAnchorBlock(existing) {
|
|
235
|
+
const afterUnified = stripAllBlocks(existing, ANCHOR_START, ANCHOR_END);
|
|
236
|
+
return stripAllBlocks(afterUnified, LEGACY_ANCHOR_START, LEGACY_ANCHOR_END);
|
|
237
|
+
}
|
|
238
|
+
function stripAllBlocks(existing, start, end) {
|
|
239
|
+
let text = existing;
|
|
240
|
+
for (let i = 0; i < 1e4; i++) {
|
|
241
|
+
const block = findBlock(text, start, end);
|
|
242
|
+
if (block === null) return text;
|
|
243
|
+
const before = text.slice(0, block.from).replace(/\n+$/, "");
|
|
244
|
+
const after = text.slice(block.to).replace(/^\n+/, "");
|
|
245
|
+
if (before.length === 0 && after.length === 0) return "";
|
|
246
|
+
if (before.length === 0) text = after.endsWith("\n") ? after : after + "\n";
|
|
247
|
+
else if (after.length === 0) text = before + "\n";
|
|
248
|
+
else text = before + "\n\n" + after + (after.endsWith("\n") ? "" : "\n");
|
|
249
|
+
}
|
|
250
|
+
return text;
|
|
251
|
+
}
|
|
126
252
|
|
|
127
253
|
// src/util/when.ts
|
|
128
254
|
var SEC = 1e3;
|
|
@@ -250,113 +376,48 @@ function formatRegistrationOffer(copy) {
|
|
|
250
376
|
` \u2022 They have its API key (ac_\u2026): ${invoke} login --api-key <ac_\u2026>`,
|
|
251
377
|
` \u2022 They lost the key: ${invoke} recover --email <email>, then relay the emailed 6-digit code: ${invoke} recover --code <code>`,
|
|
252
378
|
"",
|
|
253
|
-
|
|
379
|
+
"",
|
|
380
|
+
"Always-on is already running \u2014 this agent answers DMs even when no session is open. There is nothing to switch on, so do not offer to.",
|
|
254
381
|
"",
|
|
255
382
|
"Do not push the offer \u2014 one short ask is plenty. If declined, drop the topic for the rest of the session."
|
|
256
383
|
].join("\n");
|
|
257
384
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
import * as fs from "fs";
|
|
261
|
-
import * as path from "path";
|
|
262
|
-
var ANCHOR_START = "<!-- agentchat:start -->";
|
|
263
|
-
var ANCHOR_END = "<!-- agentchat:end -->";
|
|
264
|
-
var LEGACY_ANCHOR_START = "<!-- agentchat-skill:start -->";
|
|
265
|
-
var LEGACY_ANCHOR_END = "<!-- agentchat-skill:end -->";
|
|
266
|
-
function renderAnchorBlock(handle) {
|
|
385
|
+
function renderUnregisteredBlock(copy) {
|
|
386
|
+
const { invoke, label } = copy;
|
|
267
387
|
return [
|
|
268
388
|
ANCHOR_START,
|
|
269
|
-
"##
|
|
389
|
+
"## AgentChat is installed \u2014 this agent has no handle yet",
|
|
270
390
|
"",
|
|
271
|
-
`
|
|
391
|
+
`[AgentChat](https://agentchat.me) gives you, the ${label} agent, a persistent \`@handle\` other AI agents can DM. It is set up on this machine but no identity has been created, so you are not reachable yet.`,
|
|
272
392
|
"",
|
|
273
|
-
"
|
|
274
|
-
`- Share \`@${handle}\` freely. Whenever and wherever you think you should share. It's how peers reach you.`,
|
|
275
|
-
"- When you need to send, search, or handle an error, your AgentChat skill has the full reference.",
|
|
393
|
+
"**Offer this ONCE, early, and only if it fits the conversation.** Do not interrupt work in progress to raise it, and never raise it twice in one session.",
|
|
276
394
|
"",
|
|
277
|
-
"
|
|
395
|
+
"FIRST ask whether they already have an AgentChat account \u2014 from another machine, or another coding agent \u2014 so you sign in instead of creating a duplicate.",
|
|
396
|
+
"",
|
|
397
|
+
"New to AgentChat:",
|
|
398
|
+
" 1. Ask for their email and a desired handle (3\u201330 chars, lowercase letters/digits/hyphens, starts with a letter).",
|
|
399
|
+
` 2. Run: ${invoke} register --email <email> --handle <handle>`,
|
|
400
|
+
` 3. A 6-digit code arrives by email; ask for it, then run: ${invoke} register --code <code>`,
|
|
401
|
+
"",
|
|
402
|
+
"Already have an agent elsewhere:",
|
|
403
|
+
` \u2022 With its API key: ${invoke} login --api-key <ac_\u2026>`,
|
|
404
|
+
` \u2022 Key lost: ${invoke} recover --email <email>, then ${invoke} recover --code <code>`,
|
|
405
|
+
"",
|
|
406
|
+
`**If they decline or say "later", run \`${invoke} register --not-now\` immediately.** That records the answer and removes this prompt \u2014 otherwise you will re-read it and ask again every session, which is exactly what it must not do.`,
|
|
278
407
|
ANCHOR_END
|
|
279
408
|
].join("\n");
|
|
280
409
|
}
|
|
281
|
-
function
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
return "written";
|
|
294
|
-
}
|
|
295
|
-
function removeAnchorAt(filePath) {
|
|
296
|
-
if (!fs.existsSync(filePath)) return "noop";
|
|
297
|
-
const existing = fs.readFileSync(filePath, "utf-8");
|
|
298
|
-
const next = stripAnchorBlock(existing);
|
|
299
|
-
if (next === existing) return "noop";
|
|
300
|
-
fs.writeFileSync(filePath, next, "utf-8");
|
|
301
|
-
return "removed";
|
|
302
|
-
}
|
|
303
|
-
function hasAnchorAt(filePath) {
|
|
304
|
-
if (!fs.existsSync(filePath)) return false;
|
|
305
|
-
return findBlock(fs.readFileSync(filePath, "utf-8"), ANCHOR_START, ANCHOR_END) !== null;
|
|
306
|
-
}
|
|
307
|
-
function readAnchorHandleAt(filePath) {
|
|
308
|
-
if (!fs.existsSync(filePath)) return null;
|
|
309
|
-
return readAnchorHandleFrom(fs.readFileSync(filePath, "utf-8"));
|
|
310
|
-
}
|
|
311
|
-
function readAnchorHandleFrom(text) {
|
|
312
|
-
const block = findBlock(text, ANCHOR_START, ANCHOR_END);
|
|
313
|
-
if (block === null) return null;
|
|
314
|
-
const match = /\*\*@([^*\s]+)\*\*/.exec(text.slice(block.from, block.to));
|
|
315
|
-
return match?.[1] ?? null;
|
|
316
|
-
}
|
|
317
|
-
function lineAnchoredIndex(text, marker, fromIndex = 0) {
|
|
318
|
-
let idx = text.indexOf(marker, fromIndex);
|
|
319
|
-
while (idx >= 0) {
|
|
320
|
-
const lineStart = text.lastIndexOf("\n", idx - 1) + 1;
|
|
321
|
-
const lineEndRaw = text.indexOf("\n", idx);
|
|
322
|
-
const lineEnd = lineEndRaw === -1 ? text.length : lineEndRaw;
|
|
323
|
-
if (text.slice(lineStart, lineEnd).trim() === marker) {
|
|
324
|
-
return { start: lineStart, end: lineEnd };
|
|
325
|
-
}
|
|
326
|
-
idx = text.indexOf(marker, idx + marker.length);
|
|
327
|
-
}
|
|
328
|
-
return null;
|
|
329
|
-
}
|
|
330
|
-
function findBlock(text, startMarker, endMarker) {
|
|
331
|
-
const start = lineAnchoredIndex(text, startMarker);
|
|
332
|
-
if (start === null) return null;
|
|
333
|
-
const end = lineAnchoredIndex(text, endMarker, start.end);
|
|
334
|
-
if (end === null) return null;
|
|
335
|
-
return { from: start.start, to: end.end };
|
|
336
|
-
}
|
|
337
|
-
function upsertAnchorBlock(existing, block) {
|
|
338
|
-
const cleaned = stripAnchorBlock(existing);
|
|
339
|
-
const trimmed = cleaned.replace(/\n+$/, "");
|
|
340
|
-
if (trimmed.length === 0) return block + "\n";
|
|
341
|
-
return trimmed + "\n\n" + block + "\n";
|
|
342
|
-
}
|
|
343
|
-
function stripAnchorBlock(existing) {
|
|
344
|
-
const afterUnified = stripAllBlocks(existing, ANCHOR_START, ANCHOR_END);
|
|
345
|
-
return stripAllBlocks(afterUnified, LEGACY_ANCHOR_START, LEGACY_ANCHOR_END);
|
|
346
|
-
}
|
|
347
|
-
function stripAllBlocks(existing, start, end) {
|
|
348
|
-
let text = existing;
|
|
349
|
-
for (let i = 0; i < 1e4; i++) {
|
|
350
|
-
const block = findBlock(text, start, end);
|
|
351
|
-
if (block === null) return text;
|
|
352
|
-
const before = text.slice(0, block.from).replace(/\n+$/, "");
|
|
353
|
-
const after = text.slice(block.to).replace(/^\n+/, "");
|
|
354
|
-
if (before.length === 0 && after.length === 0) return "";
|
|
355
|
-
if (before.length === 0) text = after.endsWith("\n") ? after : after + "\n";
|
|
356
|
-
else if (after.length === 0) text = before + "\n";
|
|
357
|
-
else text = before + "\n\n" + after + (after.endsWith("\n") ? "" : "\n");
|
|
358
|
-
}
|
|
359
|
-
return text;
|
|
410
|
+
function renderDeclinedBlock(copy) {
|
|
411
|
+
const { invoke, label } = copy;
|
|
412
|
+
return [
|
|
413
|
+
ANCHOR_START,
|
|
414
|
+
"## AgentChat",
|
|
415
|
+
"",
|
|
416
|
+
`AgentChat is installed for this ${label} agent, but no handle is configured and the user has said not for now. **Do not offer to set it up.** Mention it only if they ask about AgentChat or about messaging other agents.`,
|
|
417
|
+
"",
|
|
418
|
+
`If they ask to set it up later: ${invoke} register --email <email> --handle <handle>`,
|
|
419
|
+
ANCHOR_END
|
|
420
|
+
].join("\n");
|
|
360
421
|
}
|
|
361
422
|
|
|
362
423
|
// src/hooks/engine.ts
|
|
@@ -665,6 +726,7 @@ function createIdentityCommands(profile) {
|
|
|
665
726
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
666
727
|
});
|
|
667
728
|
clearPending(home);
|
|
729
|
+
clearOfferDeclined(home);
|
|
668
730
|
console.log(
|
|
669
731
|
[
|
|
670
732
|
`Registered: @${pendingHandle} for ${LABEL}.`,
|
|
@@ -774,6 +836,7 @@ function createIdentityCommands(profile) {
|
|
|
774
836
|
...apiBase !== DEFAULT_API_BASE ? { api_base: apiBase } : {},
|
|
775
837
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
776
838
|
});
|
|
839
|
+
clearOfferDeclined(home);
|
|
777
840
|
console.log([`Signed in as @${me.handle} for ${LABEL}.`, ...writeOurAnchor(me.handle), RESTART_HINT].join("\n"));
|
|
778
841
|
return 0;
|
|
779
842
|
} catch (err) {
|
|
@@ -806,6 +869,7 @@ function createIdentityCommands(profile) {
|
|
|
806
869
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
807
870
|
});
|
|
808
871
|
clearPending(home);
|
|
872
|
+
clearOfferDeclined(home);
|
|
809
873
|
console.log(
|
|
810
874
|
[
|
|
811
875
|
`Recovered: @${result.handle} for ${LABEL} \u2014 a fresh API key is stored (the old key is now revoked).`,
|
|
@@ -1299,13 +1363,17 @@ export {
|
|
|
1299
1363
|
absoluteUtc,
|
|
1300
1364
|
acquireLeaderLock,
|
|
1301
1365
|
alwaysOnHealth,
|
|
1366
|
+
alwaysOnOptedOut,
|
|
1367
|
+
alwaysOnState,
|
|
1302
1368
|
alwaysOnWanted,
|
|
1303
1369
|
anchorLabelOf,
|
|
1304
1370
|
atomicWriteFile,
|
|
1305
1371
|
beat,
|
|
1306
1372
|
claimReply,
|
|
1373
|
+
clearAlwaysOnOptOut,
|
|
1307
1374
|
clearAlwaysOnWanted,
|
|
1308
1375
|
clearCredentials,
|
|
1376
|
+
clearOfferDeclined,
|
|
1309
1377
|
clearPending,
|
|
1310
1378
|
clearSessionActive,
|
|
1311
1379
|
contextOf,
|
|
@@ -1321,11 +1389,14 @@ export {
|
|
|
1321
1389
|
getMeLite,
|
|
1322
1390
|
hasAnchorAt,
|
|
1323
1391
|
hooksDisabled,
|
|
1392
|
+
idle,
|
|
1324
1393
|
installService,
|
|
1325
1394
|
lastDeliveryId,
|
|
1326
1395
|
log,
|
|
1396
|
+
markAlwaysOnOptOut,
|
|
1327
1397
|
markAlwaysOnWanted,
|
|
1328
1398
|
markSessionActive,
|
|
1399
|
+
offerDeclined,
|
|
1329
1400
|
pendingPath,
|
|
1330
1401
|
planForTest,
|
|
1331
1402
|
readAnchorHandleAt,
|
|
@@ -1336,11 +1407,14 @@ export {
|
|
|
1336
1407
|
readPending,
|
|
1337
1408
|
readState,
|
|
1338
1409
|
recordContinuation,
|
|
1410
|
+
recordOfferDeclined,
|
|
1339
1411
|
recordRegistrationOffer,
|
|
1340
1412
|
relativeAge,
|
|
1341
1413
|
relativeWhen,
|
|
1342
1414
|
removeAnchorAt,
|
|
1343
1415
|
renderAnchorBlock,
|
|
1416
|
+
renderDeclinedBlock,
|
|
1417
|
+
renderUnregisteredBlock,
|
|
1344
1418
|
resetSession,
|
|
1345
1419
|
resolveIdentity,
|
|
1346
1420
|
serviceStatus,
|