downcity 1.0.509 → 1.0.519
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/bin/services/contact/runtime/InboxStore.d.ts.map +1 -1
- package/bin/services/contact/runtime/InboxStore.js +36 -18
- package/bin/services/contact/runtime/InboxStore.js.map +1 -1
- package/bin/services/contact/runtime/LinkApproval.d.ts.map +1 -1
- package/bin/services/contact/runtime/LinkApproval.js +152 -33
- package/bin/services/contact/runtime/LinkApproval.js.map +1 -1
- package/package.json +1 -1
- package/test/contact/contact-inbox-store.test.mjs +65 -0
- package/test/contact/contact-link-approval.test.mjs +798 -0
- package/test/contact/contact-runtime.test.mjs +48 -269
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InboxStore.d.ts","sourceRoot":"","sources":["../../../../src/services/contact/runtime/InboxStore.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EACV,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC3B,MAAM,iCAAiC,CAAC;AAoCzC;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,qBAAqB,CAAC,
|
|
1
|
+
{"version":3,"file":"InboxStore.d.ts","sourceRoot":"","sources":["../../../../src/services/contact/runtime/InboxStore.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EACV,qBAAqB,EACrB,wBAAwB,EACxB,0BAA0B,EAC3B,MAAM,iCAAiC,CAAC;AAoCzC;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,qBAAqB,CAAC,CA2ChC;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAKvC;AAED;;GAEG;AACH,wBAAsB,4BAA4B,CAChD,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAO1C;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAWlC;AAED;;GAEG;AACH,wBAAsB,6BAA6B,CACjD,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,qBAAqB,CAAC,CAiBhC;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnF"}
|
|
@@ -34,22 +34,39 @@ function isMeta(input) {
|
|
|
34
34
|
* 保存 inbox share。
|
|
35
35
|
*/
|
|
36
36
|
export async function saveContactInboxShare(projectRoot, input) {
|
|
37
|
+
const inboxRoot = getContactInboxRootPath(projectRoot);
|
|
37
38
|
const sharePath = getContactInboxSharePath(projectRoot, input.meta.id);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
39
|
+
const tempRoot = path.join(path.dirname(inboxRoot), ".inbox-tmp");
|
|
40
|
+
const tempPath = path.join(tempRoot, `${path.basename(sharePath)}.${process.pid}.${Date.now()}`);
|
|
41
|
+
await fs.ensureDir(inboxRoot);
|
|
42
|
+
await fs.remove(tempPath);
|
|
43
|
+
try {
|
|
44
|
+
await fs.ensureDir(tempPath);
|
|
45
|
+
await fs.writeJson(path.join(tempPath, "payload.json"), input.payload, {
|
|
46
|
+
spaces: 2,
|
|
47
|
+
});
|
|
48
|
+
const filesRoot = path.join(tempPath, "files");
|
|
49
|
+
for (const file of input.files) {
|
|
50
|
+
const relativePath = assertSafeRelativePath(file.relativePath);
|
|
51
|
+
const outputPath = path.join(filesRoot, relativePath);
|
|
52
|
+
const encoding = file.encoding === "base64" ? "base64" : "utf8";
|
|
53
|
+
const content = encoding === "base64"
|
|
54
|
+
? Buffer.from(file.content, "base64")
|
|
55
|
+
: Buffer.from(file.content, "utf-8");
|
|
56
|
+
await fs.ensureDir(path.dirname(outputPath));
|
|
57
|
+
await fs.writeFile(outputPath, content);
|
|
58
|
+
}
|
|
59
|
+
// meta 最后写入,正式目录只在整条 share 完整后出现,避免 inbox 暴露半成品。
|
|
60
|
+
await fs.writeJson(path.join(tempPath, "meta.json"), input.meta, {
|
|
61
|
+
spaces: 2,
|
|
62
|
+
});
|
|
63
|
+
await fs.move(tempPath, sharePath, {
|
|
64
|
+
overwrite: false,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
await fs.remove(tempPath).catch(() => undefined);
|
|
69
|
+
throw error;
|
|
53
70
|
}
|
|
54
71
|
return input.meta;
|
|
55
72
|
}
|
|
@@ -105,14 +122,15 @@ export async function markContactInboxShareReceived(projectRoot, shareId) {
|
|
|
105
122
|
...meta,
|
|
106
123
|
status: "received",
|
|
107
124
|
};
|
|
108
|
-
await fs.writeJson(getContactInboxShareMetaPath(projectRoot, shareId), next, {
|
|
109
|
-
spaces: 2,
|
|
110
|
-
});
|
|
111
125
|
const receivedPath = getContactReceivedSharePath(projectRoot, shareId);
|
|
112
126
|
await fs.ensureDir(receivedPath);
|
|
113
127
|
await fs.writeJson(path.join(receivedPath, "meta.json"), next, {
|
|
114
128
|
spaces: 2,
|
|
115
129
|
});
|
|
130
|
+
// received 区先完整写入,最后再更新 inbox meta,避免列表状态提前变成 received。
|
|
131
|
+
await fs.writeJson(getContactInboxShareMetaPath(projectRoot, shareId), next, {
|
|
132
|
+
spaces: 2,
|
|
133
|
+
});
|
|
116
134
|
return next;
|
|
117
135
|
}
|
|
118
136
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InboxStore.js","sourceRoot":"","sources":["../../../../src/services/contact/runtime/InboxStore.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,IAAI,MAAM,WAAW,CAAC;AAM7B,OAAO,EACL,uBAAuB,EACvB,6BAA6B,EAC7B,4BAA4B,EAC5B,wBAAwB,EACxB,+BAA+B,EAC/B,2BAA2B,GAC5B,MAAM,YAAY,CAAC;AAEpB,SAAS,sBAAsB,CAAC,YAAoB;IAClD,MAAM,KAAK,GAAG,MAAM,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAChD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IACxD,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,KAAK,EAAE,CAAC,CAAC;IACtF,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACzC,IACE,UAAU,KAAK,GAAG;QAClB,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC;QAC3B,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;QAC/C,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,EACpC,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,MAAM,CAAC,KAAc;IAC5B,MAAM,IAAI,GAAG,KAA8C,CAAC;IAC5D,OAAO,OAAO,CACZ,IAAI;QACF,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ;QAC3B,OAAO,IAAI,CAAC,aAAa,KAAK,QAAQ;QACtC,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAC5D,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,WAAmB,EACnB,KAAiC;IAEjC,MAAM,SAAS,GAAG,wBAAwB,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvE,MAAM,
|
|
1
|
+
{"version":3,"file":"InboxStore.js","sourceRoot":"","sources":["../../../../src/services/contact/runtime/InboxStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LinkApproval.d.ts","sourceRoot":"","sources":["../../../../src/services/contact/runtime/LinkApproval.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AACjF,OAAO,KAAK,EACV,8BAA8B,EAC9B,8BAA8B,EAC9B,sBAAsB,EACvB,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"LinkApproval.d.ts","sourceRoot":"","sources":["../../../../src/services/contact/runtime/LinkApproval.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AACjF,OAAO,KAAK,EACV,8BAA8B,EAC9B,8BAA8B,EAC9B,sBAAsB,EACvB,MAAM,oCAAoC,CAAC;AAiB5C,eAAO,MAAM,4BAA4B,yTAC+Q,CAAC;AA0GzT;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,0BAA0B,CAAC,CA4FrC;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,sBAAsB,CAAC,CAgJjC"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - 同一个 agent 在 link 有效期内可重试,用于恢复“远端已保存、本地保存失败”的半建联。
|
|
7
7
|
* - contact token 由协议自动交换,不依赖用户手动配置环境变量。
|
|
8
8
|
*/
|
|
9
|
-
import { createStableContactId,
|
|
9
|
+
import { createStableContactId, listContacts, normalizeContactEndpoint, readContact, saveContact, } from "./ContactStore.js";
|
|
10
10
|
import { readContactLinkRecord, saveContactLinkRecord, } from "./LinkStore.js";
|
|
11
11
|
import { createContactToken, hashContactToken, } from "./Token.js";
|
|
12
12
|
export const CONTACT_LINK_NOT_FOUND_ERROR = "Contact link not found. This runtime does not have that link record. This usually means the approve request reached the wrong endpoint or port, the link was generated by another agent/project, or the local link store was cleared. If the code is expired, the remote agent returns Contact link expired instead.";
|
|
@@ -28,6 +28,61 @@ function confirmFailure(params) {
|
|
|
28
28
|
error: params.error,
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
+
function readCallbackCandidate(request) {
|
|
32
|
+
if (request.callbackOffered !== true) {
|
|
33
|
+
return {
|
|
34
|
+
endpoint: null,
|
|
35
|
+
token: null,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const token = String(request.tokenForRequester || "").trim();
|
|
39
|
+
if (!request.endpoint || !token) {
|
|
40
|
+
return {
|
|
41
|
+
endpoint: null,
|
|
42
|
+
token: null,
|
|
43
|
+
error: "Contact approve request is missing the peer callback token",
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
return {
|
|
48
|
+
endpoint: normalizeContactEndpoint(request.endpoint),
|
|
49
|
+
token,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return {
|
|
54
|
+
endpoint: null,
|
|
55
|
+
token: null,
|
|
56
|
+
error: "Contact approve request has an invalid peer callback endpoint",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function findApprovedContact(params) {
|
|
61
|
+
const stable = await readContact(params.projectRoot, createStableContactId(params.agentName));
|
|
62
|
+
if (stable)
|
|
63
|
+
return stable;
|
|
64
|
+
const contacts = await listContacts(params.projectRoot);
|
|
65
|
+
const lower = params.agentName.trim().toLowerCase();
|
|
66
|
+
return contacts.find((item) => item.name.toLowerCase() === lower) || null;
|
|
67
|
+
}
|
|
68
|
+
async function saveApprovedInboundContact(params) {
|
|
69
|
+
const existing = await findApprovedContact({
|
|
70
|
+
projectRoot: params.projectRoot,
|
|
71
|
+
agentName: params.agentName,
|
|
72
|
+
});
|
|
73
|
+
const hasOutboundSide = Boolean(existing?.endpoint && existing?.outboundToken);
|
|
74
|
+
await saveContact(params.projectRoot, {
|
|
75
|
+
id: existing?.id || createStableContactId(params.agentName),
|
|
76
|
+
name: existing?.name || params.agentName,
|
|
77
|
+
endpoint: existing?.endpoint || null,
|
|
78
|
+
reachability: hasOutboundSide ? "bidirectional" : "inbound",
|
|
79
|
+
status: "trusted",
|
|
80
|
+
outboundToken: existing?.outboundToken || null,
|
|
81
|
+
inboundTokenHash: hashContactToken(params.tokenForOwner),
|
|
82
|
+
createdAt: existing?.createdAt || params.nowMs,
|
|
83
|
+
lastSeenAt: params.nowMs,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
31
86
|
/**
|
|
32
87
|
* 执行远端 link approve 状态转换。
|
|
33
88
|
*/
|
|
@@ -55,9 +110,29 @@ export async function approveContactLinkRequest(input) {
|
|
|
55
110
|
error: "Invalid contact link secret",
|
|
56
111
|
});
|
|
57
112
|
}
|
|
113
|
+
const callbackCandidate = readCallbackCandidate(input.request);
|
|
58
114
|
if (link.usedAt) {
|
|
59
115
|
if (link.approvedAgentName === input.request.agentName &&
|
|
60
116
|
link.tokenForOwner) {
|
|
117
|
+
let nextLink = link;
|
|
118
|
+
if (!link.confirmedAt &&
|
|
119
|
+
!callbackCandidate.error &&
|
|
120
|
+
callbackCandidate.endpoint &&
|
|
121
|
+
callbackCandidate.token) {
|
|
122
|
+
nextLink = {
|
|
123
|
+
...link,
|
|
124
|
+
approvedEndpoint: callbackCandidate.endpoint,
|
|
125
|
+
tokenForRequester: callbackCandidate.token,
|
|
126
|
+
callbackReason: input.request.callbackReason || link.callbackReason || null,
|
|
127
|
+
};
|
|
128
|
+
await saveContactLinkRecord(input.projectRoot, nextLink);
|
|
129
|
+
}
|
|
130
|
+
await saveApprovedInboundContact({
|
|
131
|
+
projectRoot: input.projectRoot,
|
|
132
|
+
agentName: input.request.agentName,
|
|
133
|
+
tokenForOwner: link.tokenForOwner,
|
|
134
|
+
nowMs,
|
|
135
|
+
});
|
|
61
136
|
return {
|
|
62
137
|
success: true,
|
|
63
138
|
agentName: input.ownerAgentName,
|
|
@@ -72,39 +147,22 @@ export async function approveContactLinkRequest(input) {
|
|
|
72
147
|
});
|
|
73
148
|
}
|
|
74
149
|
const tokenForOwner = createContactToken();
|
|
75
|
-
const
|
|
76
|
-
const requesterEndpoint = callbackOffered && input.request.endpoint
|
|
77
|
-
? normalizeContactEndpoint(input.request.endpoint)
|
|
78
|
-
: null;
|
|
79
|
-
const tokenForRequester = requesterEndpoint
|
|
80
|
-
? String(input.request.tokenForRequester || "").trim()
|
|
81
|
-
: null;
|
|
82
|
-
if (callbackOffered && (!requesterEndpoint || !tokenForRequester)) {
|
|
83
|
-
return failure({
|
|
84
|
-
ownerAgentName: input.ownerAgentName,
|
|
85
|
-
endpoint: link.endpoint,
|
|
86
|
-
error: "Contact approve request is missing the peer callback token",
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
await saveContact(input.projectRoot, {
|
|
90
|
-
id: createStableContactId(input.request.agentName),
|
|
91
|
-
name: input.request.agentName,
|
|
92
|
-
endpoint: null,
|
|
93
|
-
reachability: "inbound",
|
|
94
|
-
status: "trusted",
|
|
95
|
-
outboundToken: null,
|
|
96
|
-
inboundTokenHash: hashContactToken(tokenForOwner),
|
|
97
|
-
createdAt: nowMs,
|
|
98
|
-
lastSeenAt: nowMs,
|
|
99
|
-
});
|
|
100
|
-
await saveContactLinkRecord(input.projectRoot, {
|
|
150
|
+
const approvedLink = {
|
|
101
151
|
...link,
|
|
102
152
|
usedAt: nowMs,
|
|
103
153
|
approvedAgentName: input.request.agentName,
|
|
104
|
-
approvedEndpoint:
|
|
154
|
+
approvedEndpoint: callbackCandidate.endpoint,
|
|
155
|
+
tokenForOwner,
|
|
156
|
+
tokenForRequester: callbackCandidate.token,
|
|
157
|
+
callbackReason: callbackCandidate.error ? null : input.request.callbackReason || null,
|
|
158
|
+
};
|
|
159
|
+
// 先消费 link,再派生 contact;重试时以 link 记录为来源修复 contact,避免孤儿 trusted contact。
|
|
160
|
+
await saveContactLinkRecord(input.projectRoot, approvedLink);
|
|
161
|
+
await saveApprovedInboundContact({
|
|
162
|
+
projectRoot: input.projectRoot,
|
|
163
|
+
agentName: input.request.agentName,
|
|
105
164
|
tokenForOwner,
|
|
106
|
-
|
|
107
|
-
callbackReason: input.request.callbackReason || null,
|
|
165
|
+
nowMs,
|
|
108
166
|
});
|
|
109
167
|
return {
|
|
110
168
|
success: true,
|
|
@@ -125,7 +183,7 @@ export async function confirmContactLinkRequest(input) {
|
|
|
125
183
|
error: CONTACT_LINK_NOT_FOUND_ERROR,
|
|
126
184
|
});
|
|
127
185
|
}
|
|
128
|
-
if (link.expiresAt <= nowMs) {
|
|
186
|
+
if (!link.usedAt && link.expiresAt <= nowMs) {
|
|
129
187
|
return confirmFailure({
|
|
130
188
|
ownerAgentName: input.ownerAgentName,
|
|
131
189
|
error: "Contact link expired",
|
|
@@ -143,7 +201,16 @@ export async function confirmContactLinkRequest(input) {
|
|
|
143
201
|
error: "Contact link is not approved by this agent",
|
|
144
202
|
});
|
|
145
203
|
}
|
|
146
|
-
|
|
204
|
+
let endpoint;
|
|
205
|
+
try {
|
|
206
|
+
endpoint = normalizeContactEndpoint(input.request.endpoint);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
return confirmFailure({
|
|
210
|
+
ownerAgentName: input.ownerAgentName,
|
|
211
|
+
error: "Contact confirm request has an invalid peer callback endpoint",
|
|
212
|
+
});
|
|
213
|
+
}
|
|
147
214
|
const token = String(input.request.tokenForRequester || "").trim();
|
|
148
215
|
if (!token) {
|
|
149
216
|
return confirmFailure({
|
|
@@ -151,13 +218,65 @@ export async function confirmContactLinkRequest(input) {
|
|
|
151
218
|
error: "Contact confirm request is missing the peer callback token",
|
|
152
219
|
});
|
|
153
220
|
}
|
|
154
|
-
|
|
221
|
+
if (!link.tokenForRequester) {
|
|
222
|
+
return confirmFailure({
|
|
223
|
+
ownerAgentName: input.ownerAgentName,
|
|
224
|
+
error: "Contact link has no callback token to confirm",
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
if (link.tokenForRequester !== token) {
|
|
228
|
+
return confirmFailure({
|
|
229
|
+
ownerAgentName: input.ownerAgentName,
|
|
230
|
+
error: "Contact confirm callback token does not match the approved token",
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
if (!link.approvedEndpoint) {
|
|
234
|
+
return confirmFailure({
|
|
235
|
+
ownerAgentName: input.ownerAgentName,
|
|
236
|
+
error: "Contact link has no callback endpoint to confirm",
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
if (normalizeContactEndpoint(link.approvedEndpoint) !== endpoint) {
|
|
240
|
+
return confirmFailure({
|
|
241
|
+
ownerAgentName: input.ownerAgentName,
|
|
242
|
+
error: "Contact confirm callback endpoint does not match the approved endpoint",
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
const contact = await findApprovedContact({
|
|
246
|
+
projectRoot: input.projectRoot,
|
|
247
|
+
agentName: input.request.agentName,
|
|
248
|
+
});
|
|
155
249
|
if (!contact || contact.status !== "trusted") {
|
|
156
250
|
return confirmFailure({
|
|
157
251
|
ownerAgentName: input.ownerAgentName,
|
|
158
252
|
error: "Contact not found for approved link",
|
|
159
253
|
});
|
|
160
254
|
}
|
|
255
|
+
if (!link.tokenForOwner || contact.inboundTokenHash !== hashContactToken(link.tokenForOwner)) {
|
|
256
|
+
return confirmFailure({
|
|
257
|
+
ownerAgentName: input.ownerAgentName,
|
|
258
|
+
error: "Contact token state does not match the approved link",
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
if (contact.reachability === "bidirectional" &&
|
|
262
|
+
contact.endpoint === endpoint &&
|
|
263
|
+
contact.outboundToken === token) {
|
|
264
|
+
if (!link.confirmedAt) {
|
|
265
|
+
// contact 已经完成双向升级时,重试 confirm 需要补齐 link 状态,避免最终状态分裂。
|
|
266
|
+
await saveContactLinkRecord(input.projectRoot, {
|
|
267
|
+
...link,
|
|
268
|
+
approvedEndpoint: endpoint,
|
|
269
|
+
tokenForRequester: token,
|
|
270
|
+
confirmedAt: nowMs,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
success: true,
|
|
275
|
+
agentName: input.ownerAgentName,
|
|
276
|
+
confirmed: true,
|
|
277
|
+
reachability: "bidirectional",
|
|
278
|
+
};
|
|
279
|
+
}
|
|
161
280
|
let callbackOk = false;
|
|
162
281
|
try {
|
|
163
282
|
callbackOk = await input.verifyCallback({ endpoint, token });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LinkApproval.js","sourceRoot":"","sources":["../../../../src/services/contact/runtime/LinkApproval.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH,OAAO,EACL,qBAAqB,EACrB,
|
|
1
|
+
{"version":3,"file":"LinkApproval.js","sourceRoot":"","sources":["../../../../src/services/contact/runtime/LinkApproval.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* contact inbox 状态一致性测试。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - inbox 列表状态只能在 received 区完整写入后更新。
|
|
6
|
+
* - 失败路径不能把 pending share 标成 received。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import fs from "node:fs/promises";
|
|
11
|
+
import os from "node:os";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import test from "node:test";
|
|
14
|
+
import {
|
|
15
|
+
markContactInboxShareReceived,
|
|
16
|
+
readContactInboxShareMeta,
|
|
17
|
+
saveContactInboxShare,
|
|
18
|
+
} from "../../bin/services/contact/runtime/InboxStore.js";
|
|
19
|
+
import {
|
|
20
|
+
getContactReceivedSharePath,
|
|
21
|
+
} from "../../bin/services/contact/runtime/Paths.js";
|
|
22
|
+
|
|
23
|
+
test("inbox receive keeps share pending when received meta cannot be written", async () => {
|
|
24
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), "downcity-contact-receive-partial-"));
|
|
25
|
+
try {
|
|
26
|
+
await saveContactInboxShare(root, {
|
|
27
|
+
meta: {
|
|
28
|
+
id: "share_receive_bad",
|
|
29
|
+
fromContactId: "contact_alice",
|
|
30
|
+
fromAgentName: "alice-agent",
|
|
31
|
+
title: "receive-bad",
|
|
32
|
+
status: "pending",
|
|
33
|
+
receivedAt: 1776173400000,
|
|
34
|
+
sizeBytes: 0,
|
|
35
|
+
itemCount: 1,
|
|
36
|
+
},
|
|
37
|
+
payload: {
|
|
38
|
+
kind: "share",
|
|
39
|
+
items: [
|
|
40
|
+
{
|
|
41
|
+
id: "item_text",
|
|
42
|
+
type: "text",
|
|
43
|
+
title: "Text",
|
|
44
|
+
text: "hello",
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
files: [],
|
|
49
|
+
});
|
|
50
|
+
await fs.mkdir(
|
|
51
|
+
path.join(getContactReceivedSharePath(root, "share_receive_bad"), "meta.json"),
|
|
52
|
+
{ recursive: true },
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
await assert.rejects(
|
|
56
|
+
markContactInboxShareReceived(root, "share_receive_bad"),
|
|
57
|
+
/EISDIR|illegal operation|is a directory/i,
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const meta = await readContactInboxShareMeta(root, "share_receive_bad");
|
|
61
|
+
assert.equal(meta.status, "pending");
|
|
62
|
+
} finally {
|
|
63
|
+
await fs.rm(root, { recursive: true, force: true });
|
|
64
|
+
}
|
|
65
|
+
});
|