@awiki/dsh-plugin 0.2.2 → 0.2.3
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 +41 -2
- package/README.zh.md +37 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/lib/index.js +209 -2
- package/lib/provider.js +3 -2
- package/lib/{sdk-adapter-DPPZhs6p.mjs → sdk-adapter-DK6RqV-c.mjs} +39 -0
- package/lib/types/external-http-auth.d.ts +23 -0
- package/lib/types/external-http-auth.d.ts.map +1 -0
- package/lib/types/external-http-auth.js +185 -0
- package/lib/types/external-http-auth.js.map +1 -0
- package/lib/types/index.d.ts +7 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +47 -0
- package/lib/types/index.js.map +1 -1
- package/lib/types/provider-api.d.ts +28 -0
- package/lib/types/provider-api.d.ts.map +1 -1
- package/lib/types/provider.d.ts.map +1 -1
- package/lib/types/provider.js +1 -0
- package/lib/types/provider.js.map +1 -1
- package/lib/types/sdk-adapter.d.ts +2 -1
- package/lib/types/sdk-adapter.d.ts.map +1 -1
- package/lib/types/sdk-adapter.js +31 -0
- package/lib/types/sdk-adapter.js.map +1 -1
- package/package.json +39 -39
package/README.md
CHANGED
|
@@ -36,9 +36,17 @@ group administration, realtime push, or multiple attachments in one message.
|
|
|
36
36
|
Install the official public npm package:
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
|
|
39
|
+
dsh plugin --profile web add @awiki/dsh-plugin@latest
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
The profile installer both adds the package and activates its bundle layer. Do
|
|
43
|
+
not add the plugin to the DSH CLI project root with npm or pnpm: profile-managed
|
|
44
|
+
plugins resolve Host packages from the running Harness installation. This
|
|
45
|
+
release line targets the `0.1.0-rc.6` package family and pins those Host peer
|
|
46
|
+
ranges exactly. The Harness profile sets `autoInstallPeers: false`, so an
|
|
47
|
+
external plugin never installs an `rc.7` Host package into an `rc.6`
|
|
48
|
+
deployment.
|
|
49
|
+
|
|
42
50
|
`@awiki/dsh-plugin` is the canonical package identity starting with
|
|
43
51
|
`0.2.0-rc.4`. The former `@awiki/dsh` registry entry was unpublished and is
|
|
44
52
|
not an installation source for this release line.
|
|
@@ -107,6 +115,37 @@ stale, without another model call, when newer messages arrive. The replaceable
|
|
|
107
115
|
for one direct `ctx.llm.stream` request; it does not create an Agent or write an
|
|
108
116
|
Agent session.
|
|
109
117
|
|
|
118
|
+
## External HTTP ANP authentication
|
|
119
|
+
|
|
120
|
+
Trusted same-process DSH Host plugins can authenticate an externally transported HTTP request
|
|
121
|
+
without handling ANP signatures, access tokens, challenges, or retries themselves:
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
const response = await ctx.awiki.externalHttpAuth.dispatch(
|
|
125
|
+
new Request('https://api.example.com/orders', {
|
|
126
|
+
method: 'POST',
|
|
127
|
+
headers: { 'content-type': 'application/json' },
|
|
128
|
+
body: JSON.stringify({ productId: '123' }),
|
|
129
|
+
}),
|
|
130
|
+
request => fetch(request),
|
|
131
|
+
)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
The callback remains the only network transport owner. AWiki buffers at most 4 MiB of exact body
|
|
135
|
+
bytes, forces manual redirects, asks Rust to select an origin-scoped in-memory Bearer token or a
|
|
136
|
+
fresh HTTP Message Signature, observes only authentication response headers, and invokes the
|
|
137
|
+
transport at most twice for one bounded `401` authentication retry. The final `Response` body is
|
|
138
|
+
untouched. Transport rejections preserve their original error identity.
|
|
139
|
+
|
|
140
|
+
The unsigned input must not contain `Authorization`, `Signature-Input`, `Signature`, or
|
|
141
|
+
`Content-Digest`. Production targets require HTTPS; test-only loopback HTTP uses the existing
|
|
142
|
+
`allowInsecureLoopbackForTesting` deployment gate. Tokens come only from successful
|
|
143
|
+
`Authentication-Info` responses, are scoped to the current identity/signing key/origin, and are
|
|
144
|
+
not persisted across Harness restarts.
|
|
145
|
+
|
|
146
|
+
`externalHttpAuth` is deliberately absent from Browser Remote, Agent tools, Typert Remote, and the
|
|
147
|
+
Web client bundle. Exposing it across an untrusted boundary would create a signing oracle.
|
|
148
|
+
|
|
110
149
|
## Development
|
|
111
150
|
|
|
112
151
|
Requirements: Node.js 22.19+ (or 24+) and pnpm 11.7.
|
|
@@ -117,7 +156,7 @@ pnpm run verify
|
|
|
117
156
|
pnpm pack --dry-run
|
|
118
157
|
```
|
|
119
158
|
|
|
120
|
-
The production Host loads the exact `@awiki/im-core-node@0.1.
|
|
159
|
+
The production Host loads the exact `@awiki/im-core-node@0.1.3` runtime package;
|
|
121
160
|
the platform-specific native addon is selected through its optional dependencies
|
|
122
161
|
and remains external to the JavaScript bundle. Consumers do not need Rust or an
|
|
123
162
|
`awiki-cli-rs2` checkout. See `THIRD_PARTY_NOTICES.md` for provenance and
|
package/README.zh.md
CHANGED
|
@@ -29,9 +29,15 @@ Rust 身份。
|
|
|
29
29
|
安装公开发布的官方 npm 包:
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
|
|
32
|
+
dsh plugin --profile web add @awiki/dsh-plugin@latest
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
Profile 安装器会同时添加包并激活 bundle layer。不要在 DSH CLI 项目根目录用
|
|
36
|
+
npm 或 pnpm 添加插件;Profile 管理的插件会从当前 Harness 安装解析 Host 包。
|
|
37
|
+
本发布线面向 `0.1.0-rc.6` 包族,并精确锁定这些 Host peer。Harness Profile
|
|
38
|
+
设置了 `autoInstallPeers: false`,因此外部插件不会在 `rc.6` 部署中自动混入
|
|
39
|
+
`rc.7` Host 包。
|
|
40
|
+
|
|
35
41
|
从 `0.2.0-rc.4` 起,`@awiki/dsh-plugin` 是唯一规范包名。原
|
|
36
42
|
`@awiki/dsh` registry 条目已被 unpublish,不再作为本发布线的安装来源。
|
|
37
43
|
|
|
@@ -87,6 +93,35 @@ MIME、大小和说明,不发送文件二进制;序列化后的对话内容
|
|
|
87
93
|
调用模型。可替换的 `@awiki/dsh-plugin/summary-provider` 使用 Harness 当前默认 provider/model
|
|
88
94
|
执行一次直接的 `ctx.llm.stream`,不会创建 Agent,也不会写入 Agent session。
|
|
89
95
|
|
|
96
|
+
## 外部 HTTP ANP 身份认证
|
|
97
|
+
|
|
98
|
+
可信的 DSH Host 同进程插件可以认证由外部 transport 发送的 HTTP 请求,而无需自行处理
|
|
99
|
+
ANP 签名、Access Token、challenge 或重试:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const response = await ctx.awiki.externalHttpAuth.dispatch(
|
|
103
|
+
new Request('https://api.example.com/orders', {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: { 'content-type': 'application/json' },
|
|
106
|
+
body: JSON.stringify({ productId: '123' }),
|
|
107
|
+
}),
|
|
108
|
+
request => fetch(request),
|
|
109
|
+
)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
回调函数仍是唯一网络 transport owner。AWiki 最多缓冲 4 MiB 精确 body bytes,强制 manual
|
|
113
|
+
redirect,由 Rust 自动选择当前 origin 的进程内 Bearer Token 或新 HTTP Message Signature,
|
|
114
|
+
只观察认证相关响应头,并且每个逻辑请求最多调用 transport 两次,第二次只能是一次受限的
|
|
115
|
+
`401` 认证重试。最终 `Response` 正文不会被读取;transport rejection 保留原始错误对象。
|
|
116
|
+
|
|
117
|
+
输入请求不得自行携带 `Authorization`、`Signature-Input`、`Signature` 或
|
|
118
|
+
`Content-Digest`。生产目标必须使用 HTTPS;测试用 loopback HTTP 复用现有
|
|
119
|
+
`allowInsecureLoopbackForTesting` 部署开关。Token 只接受成功响应中的
|
|
120
|
+
`Authentication-Info`,并按当前 identity、signing key 和 origin 隔离;Harness 重启后不保留。
|
|
121
|
+
|
|
122
|
+
`externalHttpAuth` 不进入 Browser Remote、Agent tools、Typert Remote 或 Web client bundle,
|
|
123
|
+
避免形成跨不可信边界的签名 oracle。
|
|
124
|
+
|
|
90
125
|
## 开发与验证
|
|
91
126
|
|
|
92
127
|
需要 Node.js 22.19+(或 24+)以及 pnpm 11.7:
|
|
@@ -97,7 +132,7 @@ pnpm run verify
|
|
|
97
132
|
pnpm pack --dry-run
|
|
98
133
|
```
|
|
99
134
|
|
|
100
|
-
生产 Host 加载固定版本 `@awiki/im-core-node@0.1.
|
|
135
|
+
生产 Host 加载固定版本 `@awiki/im-core-node@0.1.3`;平台原生 addon 由它的
|
|
101
136
|
optional dependencies 选择,并保持在 JavaScript bundle 外。使用者无需安装 Rust,
|
|
102
137
|
也无需检出 `awiki-cli-rs2`。来源与许可证见 `THIRD_PARTY_NOTICES.md`。
|
|
103
138
|
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## AWiki Rust IM Core Node SDK
|
|
4
4
|
|
|
5
|
-
`@awiki/im-core-node@0.1.
|
|
5
|
+
`@awiki/im-core-node@0.1.3` and its target-specific optional package provide the
|
|
6
6
|
Rust IM Core runtime used by the Host provider. These packages are distributed
|
|
7
7
|
under AGPL-3.0-only. Each package carries its own `LICENSE`, `NOTICE.md`,
|
|
8
8
|
`SOURCE.md`, CycloneDX SBOM, checksums, and build provenance. The corresponding
|
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as downloadedAttachment } from "./sdk-adapter-
|
|
1
|
+
import { n as downloadedAttachment } from "./sdk-adapter-DK6RqV-c.mjs";
|
|
2
2
|
import "@deepseek-ai/cordis";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
@@ -14,6 +14,178 @@ const AWIKI_LOGOUT_CONFIRMATION = "logout-awiki-session";
|
|
|
14
14
|
/** Exact browser acknowledgement required before destructive local-state removal. */
|
|
15
15
|
const AWIKI_CLEAR_LOCAL_DATA_CONFIRMATION = "clear-awiki-local-data";
|
|
16
16
|
//#endregion
|
|
17
|
+
//#region lib/types/external-http-auth.js
|
|
18
|
+
/** Host-only dispatcher for externally transported ANP-authenticated HTTP. */
|
|
19
|
+
const AWIKI_EXTERNAL_HTTP_MAX_BODY_BYTES = 4194304;
|
|
20
|
+
const MANAGED_HEADERS = [
|
|
21
|
+
"authorization",
|
|
22
|
+
"signature-input",
|
|
23
|
+
"signature",
|
|
24
|
+
"content-digest"
|
|
25
|
+
];
|
|
26
|
+
const ERROR_MESSAGES = {
|
|
27
|
+
"not-registered": "A registered AWiki identity is required.",
|
|
28
|
+
"signed-out": "This installation is signed out of AWiki.",
|
|
29
|
+
"invalid-request": "The external HTTP request is invalid.",
|
|
30
|
+
"unsupported-body": "The external HTTP request body cannot be replayed safely.",
|
|
31
|
+
"body-too-large": "The external HTTP request body exceeds 4 MiB.",
|
|
32
|
+
"auth-state-unavailable": "AWiki external HTTP authentication is unavailable."
|
|
33
|
+
};
|
|
34
|
+
/** Stable Host-only failure without request, response, credential, or path detail. */
|
|
35
|
+
var AwikiExternalHttpAuthError = class extends Error {
|
|
36
|
+
code;
|
|
37
|
+
name = "AwikiExternalHttpAuthError";
|
|
38
|
+
constructor(code) {
|
|
39
|
+
super(ERROR_MESSAGES[code]);
|
|
40
|
+
this.code = code;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
function createAwikiExternalHttpAuth(acquire) {
|
|
44
|
+
return Object.freeze({ async dispatch(request, transport) {
|
|
45
|
+
validateDispatchInput(request, transport);
|
|
46
|
+
const session = await acquire();
|
|
47
|
+
await session.assertActive();
|
|
48
|
+
const body = await readReplayableBody(request);
|
|
49
|
+
await session.assertActive();
|
|
50
|
+
let attempt;
|
|
51
|
+
try {
|
|
52
|
+
attempt = await session.client.prepareExternalHttpRequest({
|
|
53
|
+
url: request.url,
|
|
54
|
+
method: request.method,
|
|
55
|
+
headers: requestHeaders(request),
|
|
56
|
+
...body === void 0 ? {} : { body }
|
|
57
|
+
});
|
|
58
|
+
} catch (error) {
|
|
59
|
+
throw mapProviderError(error);
|
|
60
|
+
}
|
|
61
|
+
await session.assertActive();
|
|
62
|
+
const response = await transport(authenticatedRequest(request, body, attempt));
|
|
63
|
+
const retry = await handleResponseWithoutChangingCompletedRequest(attempt, response);
|
|
64
|
+
if (retry === null) return response;
|
|
65
|
+
try {
|
|
66
|
+
await session.assertActive();
|
|
67
|
+
} catch {
|
|
68
|
+
return response;
|
|
69
|
+
}
|
|
70
|
+
const retriedResponse = await transport(authenticatedRequest(request, body, retry));
|
|
71
|
+
await handleResponseWithoutChangingCompletedRequest(retry, retriedResponse);
|
|
72
|
+
return retriedResponse;
|
|
73
|
+
} });
|
|
74
|
+
}
|
|
75
|
+
function externalHttpAuthError(code) {
|
|
76
|
+
return new AwikiExternalHttpAuthError(code);
|
|
77
|
+
}
|
|
78
|
+
function mapProviderError(error) {
|
|
79
|
+
if (error instanceof AwikiExternalHttpAuthError) return error;
|
|
80
|
+
try {
|
|
81
|
+
if (typeof error === "object" && error !== null) {
|
|
82
|
+
const value = error;
|
|
83
|
+
if (value.name === "AwikiSdkError") {
|
|
84
|
+
if (value.code === "not-registered") return externalHttpAuthError("not-registered");
|
|
85
|
+
if (value.code === "invalid-request") return externalHttpAuthError("invalid-request");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
} catch {}
|
|
89
|
+
return externalHttpAuthError("auth-state-unavailable");
|
|
90
|
+
}
|
|
91
|
+
function validateDispatchInput(request, transport) {
|
|
92
|
+
if (!(request instanceof Request) || typeof transport !== "function" || request.bodyUsed) throw externalHttpAuthError("invalid-request");
|
|
93
|
+
for (const name of MANAGED_HEADERS) if (request.headers.has(name)) throw externalHttpAuthError("invalid-request");
|
|
94
|
+
}
|
|
95
|
+
async function readReplayableBody(request) {
|
|
96
|
+
if (request.body === null) return void 0;
|
|
97
|
+
let clone;
|
|
98
|
+
try {
|
|
99
|
+
clone = request.clone();
|
|
100
|
+
} catch {
|
|
101
|
+
throw externalHttpAuthError("unsupported-body");
|
|
102
|
+
}
|
|
103
|
+
const stream = clone.body;
|
|
104
|
+
if (stream === null) return /* @__PURE__ */ new Uint8Array();
|
|
105
|
+
const reader = stream.getReader();
|
|
106
|
+
const chunks = [];
|
|
107
|
+
let length = 0;
|
|
108
|
+
try {
|
|
109
|
+
while (true) {
|
|
110
|
+
const result = await reader.read();
|
|
111
|
+
if (result.done) break;
|
|
112
|
+
length += result.value.byteLength;
|
|
113
|
+
if (length > 4194304) {
|
|
114
|
+
reader.cancel().catch(() => {});
|
|
115
|
+
request.body?.cancel().catch(() => {});
|
|
116
|
+
throw externalHttpAuthError("body-too-large");
|
|
117
|
+
}
|
|
118
|
+
chunks.push(Uint8Array.from(result.value));
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
if (error instanceof AwikiExternalHttpAuthError) throw error;
|
|
122
|
+
throw externalHttpAuthError("unsupported-body");
|
|
123
|
+
} finally {
|
|
124
|
+
reader.releaseLock();
|
|
125
|
+
}
|
|
126
|
+
const bytes = new Uint8Array(length);
|
|
127
|
+
let offset = 0;
|
|
128
|
+
for (const chunk of chunks) {
|
|
129
|
+
bytes.set(chunk, offset);
|
|
130
|
+
offset += chunk.byteLength;
|
|
131
|
+
}
|
|
132
|
+
return bytes;
|
|
133
|
+
}
|
|
134
|
+
function requestHeaders(request) {
|
|
135
|
+
return [...request.headers].map(([name, value]) => ({
|
|
136
|
+
name,
|
|
137
|
+
value
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
function authenticatedRequest(original, body, attempt) {
|
|
141
|
+
const headers = new Headers(original.headers);
|
|
142
|
+
for (const header of attempt.headerPatch) headers.set(header.name, header.value);
|
|
143
|
+
const init = {
|
|
144
|
+
method: attempt.method,
|
|
145
|
+
headers,
|
|
146
|
+
redirect: "manual",
|
|
147
|
+
signal: original.signal,
|
|
148
|
+
cache: original.cache,
|
|
149
|
+
credentials: original.credentials,
|
|
150
|
+
integrity: original.integrity,
|
|
151
|
+
keepalive: original.keepalive,
|
|
152
|
+
mode: original.mode,
|
|
153
|
+
referrer: original.referrer,
|
|
154
|
+
referrerPolicy: original.referrerPolicy,
|
|
155
|
+
...body === void 0 ? {} : { body: Uint8Array.from(body) }
|
|
156
|
+
};
|
|
157
|
+
try {
|
|
158
|
+
return new Request(attempt.targetUrl, init);
|
|
159
|
+
} catch {
|
|
160
|
+
throw externalHttpAuthError("invalid-request");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async function handleResponseWithoutChangingCompletedRequest(attempt, response) {
|
|
164
|
+
try {
|
|
165
|
+
return await attempt.handleResponse(responseMetadata(response));
|
|
166
|
+
} catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function responseMetadata(response) {
|
|
171
|
+
const headers = [];
|
|
172
|
+
for (const name of [
|
|
173
|
+
"authentication-info",
|
|
174
|
+
"www-authenticate",
|
|
175
|
+
"accept-signature"
|
|
176
|
+
]) {
|
|
177
|
+
const value = response.headers.get(name);
|
|
178
|
+
if (value !== null) headers.push({
|
|
179
|
+
name,
|
|
180
|
+
value
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
statusCode: response.status,
|
|
185
|
+
headers
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
//#endregion
|
|
17
189
|
//#region lib/types/tools.js
|
|
18
190
|
/** Model-facing AWiki read and approved-send tools. */
|
|
19
191
|
/** Model tool that reads the public deployment identity. */
|
|
@@ -1027,6 +1199,8 @@ let AwikiService = (() => {
|
|
|
1027
1199
|
sessionRevision = 0;
|
|
1028
1200
|
activeSummaryRequests = /* @__PURE__ */ new Set();
|
|
1029
1201
|
summaryProvider;
|
|
1202
|
+
/** Trusted same-process external HTTP authentication dispatcher. Never Remote. */
|
|
1203
|
+
externalHttpAuth;
|
|
1030
1204
|
/**
|
|
1031
1205
|
* @param ctx - owning Host context.
|
|
1032
1206
|
* @param config - service endpoints, SDK state path, and public limits.
|
|
@@ -1034,6 +1208,7 @@ let AwikiService = (() => {
|
|
|
1034
1208
|
constructor(ctx, config) {
|
|
1035
1209
|
super(ctx, "awiki");
|
|
1036
1210
|
this.resolved = resolveConfig(config);
|
|
1211
|
+
this.externalHttpAuth = createAwikiExternalHttpAuth(() => this.acquireExternalHttpAuthSession());
|
|
1037
1212
|
this.sessionStore = new AwikiSessionStore(this.resolved.stateRoot);
|
|
1038
1213
|
this.startupUserServiceDomain = this.resolved.userServiceDomain;
|
|
1039
1214
|
ctx.inject(["settings"], (settingsCtx) => {
|
|
@@ -1470,6 +1645,38 @@ let AwikiService = (() => {
|
|
|
1470
1645
|
this.signedOut ??= await this.sessionStore.isSignedOut();
|
|
1471
1646
|
return this.signedOut;
|
|
1472
1647
|
}
|
|
1648
|
+
/** Bind one external-auth dispatch to the current provider and session revision. */
|
|
1649
|
+
async acquireExternalHttpAuthSession() {
|
|
1650
|
+
let signedOut;
|
|
1651
|
+
try {
|
|
1652
|
+
signedOut = await this.isSignedOut();
|
|
1653
|
+
} catch {
|
|
1654
|
+
throw externalHttpAuthError("auth-state-unavailable");
|
|
1655
|
+
}
|
|
1656
|
+
if (signedOut) throw externalHttpAuthError("signed-out");
|
|
1657
|
+
const revision = this.sessionRevision;
|
|
1658
|
+
const provider = this.provider;
|
|
1659
|
+
if (provider === void 0) throw externalHttpAuthError("auth-state-unavailable");
|
|
1660
|
+
let identity;
|
|
1661
|
+
try {
|
|
1662
|
+
identity = await provider.client.getIdentity();
|
|
1663
|
+
} catch (error) {
|
|
1664
|
+
throw mapProviderError(error);
|
|
1665
|
+
}
|
|
1666
|
+
if (identity === null) throw externalHttpAuthError("not-registered");
|
|
1667
|
+
return {
|
|
1668
|
+
client: provider.client,
|
|
1669
|
+
assertActive: async () => {
|
|
1670
|
+
if (this.provider !== provider || this.sessionRevision !== revision) throw externalHttpAuthError("auth-state-unavailable");
|
|
1671
|
+
try {
|
|
1672
|
+
if (await this.isSignedOut()) throw externalHttpAuthError("signed-out");
|
|
1673
|
+
} catch (error) {
|
|
1674
|
+
if (error instanceof AwikiExternalHttpAuthError) throw error;
|
|
1675
|
+
throw externalHttpAuthError("auth-state-unavailable");
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1473
1680
|
/** Serialize sign-in, sign-out, and destructive clear transitions. */
|
|
1474
1681
|
mutateSession(operation) {
|
|
1475
1682
|
const pending = this.sessionMutation.then(operation, operation);
|
|
@@ -1494,4 +1701,4 @@ function containsUnexpectedBinary(value, seen) {
|
|
|
1494
1701
|
return false;
|
|
1495
1702
|
}
|
|
1496
1703
|
//#endregion
|
|
1497
|
-
export { AWIKI_CLEAR_LOCAL_DATA_CONFIRMATION, AWIKI_DOMAIN_FIELD, AWIKI_HISTORY_TOOL, AWIKI_IDENTITY_STATUS_TOOL, AWIKI_LIST_CONVERSATIONS_TOOL, AWIKI_LOGOUT_CONFIRMATION, AWIKI_SEND_ATTACHMENT_TOOL, AWIKI_SEND_MESSAGE_TOOL, AWIKI_SETTINGS_NAMESPACE, AwikiService, AwikiService as default, AwikiSettingsSchema, Config, DEFAULT_ATTACHMENT_MAX_BYTES, DEFAULT_AWIKI_DOMAIN, DEFAULT_AWIKI_MESSAGE_SERVICE_DID, DEFAULT_AWIKI_SERVICE_URL, DEFAULT_POLL_INTERVAL_MS, DEFAULT_SUMMARY_MAX_INPUT_BYTES, MAX_SUMMARY_MESSAGES, normalizeAwikiDomain, validateAwikiSettings };
|
|
1704
|
+
export { AWIKI_CLEAR_LOCAL_DATA_CONFIRMATION, AWIKI_DOMAIN_FIELD, AWIKI_EXTERNAL_HTTP_MAX_BODY_BYTES, AWIKI_HISTORY_TOOL, AWIKI_IDENTITY_STATUS_TOOL, AWIKI_LIST_CONVERSATIONS_TOOL, AWIKI_LOGOUT_CONFIRMATION, AWIKI_SEND_ATTACHMENT_TOOL, AWIKI_SEND_MESSAGE_TOOL, AWIKI_SETTINGS_NAMESPACE, AwikiExternalHttpAuthError, AwikiService, AwikiService as default, AwikiSettingsSchema, Config, DEFAULT_ATTACHMENT_MAX_BYTES, DEFAULT_AWIKI_DOMAIN, DEFAULT_AWIKI_MESSAGE_SERVICE_DID, DEFAULT_AWIKI_SERVICE_URL, DEFAULT_POLL_INTERVAL_MS, DEFAULT_SUMMARY_MAX_INPUT_BYTES, MAX_SUMMARY_MESSAGES, normalizeAwikiDomain, validateAwikiSettings };
|
package/lib/provider.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as RustSdkAdapter } from "./sdk-adapter-
|
|
1
|
+
import { t as RustSdkAdapter } from "./sdk-adapter-DK6RqV-c.mjs";
|
|
2
2
|
import { openImCoreNodeClient } from "@awiki/im-core-node";
|
|
3
3
|
//#region lib/types/provider.js
|
|
4
4
|
/** Production AWiki provider backed by the versioned Rust IM Core Node bridge. */
|
|
@@ -15,7 +15,8 @@ function apply(ctx) {
|
|
|
15
15
|
userServiceEndpoint: options.userServiceUrl,
|
|
16
16
|
messageServiceEndpoint: options.messageServiceUrl,
|
|
17
17
|
anpServiceEndpoint: options.messageServiceUrl,
|
|
18
|
-
anpServiceDid: options.messageServiceDid
|
|
18
|
+
anpServiceDid: options.messageServiceDid,
|
|
19
|
+
externalHttpAllowInsecureLoopbackForTesting: options.allowInsecureLoopbackForTesting
|
|
19
20
|
}))), "awiki Rust SDK client");
|
|
20
21
|
}
|
|
21
22
|
//#endregion
|
|
@@ -111,6 +111,34 @@ function page(value, copy) {
|
|
|
111
111
|
hasMore: value.hasMore
|
|
112
112
|
};
|
|
113
113
|
}
|
|
114
|
+
function httpHeaders(headers) {
|
|
115
|
+
return headers.map((header) => ({
|
|
116
|
+
name: String(header.name),
|
|
117
|
+
value: String(header.value)
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
function externalHttpAttempt(value) {
|
|
121
|
+
return {
|
|
122
|
+
targetUrl: String(value.targetUrl),
|
|
123
|
+
method: String(value.method),
|
|
124
|
+
headerPatch: httpHeaders(value.headerPatch),
|
|
125
|
+
retryCount: value.retryCount,
|
|
126
|
+
async handleResponse(response) {
|
|
127
|
+
try {
|
|
128
|
+
const retry = await value.handleResponse({
|
|
129
|
+
statusCode: response.statusCode,
|
|
130
|
+
headers: response.headers.map((header) => ({
|
|
131
|
+
name: header.name,
|
|
132
|
+
value: header.value
|
|
133
|
+
}))
|
|
134
|
+
});
|
|
135
|
+
return retry === null ? null : externalHttpAttempt(retry);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
mapError(error);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
114
142
|
/** Adapt the Rust Node bridge to the frozen Host provider interface. */
|
|
115
143
|
var RustSdkAdapter = class {
|
|
116
144
|
client;
|
|
@@ -204,6 +232,17 @@ var RustSdkAdapter = class {
|
|
|
204
232
|
}
|
|
205
233
|
fail("not-found");
|
|
206
234
|
}
|
|
235
|
+
prepareExternalHttpRequest(request) {
|
|
236
|
+
return this.run(async (client) => externalHttpAttempt(await client.prepareExternalHttpRequest({
|
|
237
|
+
url: request.url,
|
|
238
|
+
method: request.method,
|
|
239
|
+
headers: request.headers.map((header) => ({
|
|
240
|
+
name: header.name,
|
|
241
|
+
value: header.value
|
|
242
|
+
})),
|
|
243
|
+
...request.body === void 0 ? {} : { body: Uint8Array.from(request.body) }
|
|
244
|
+
})));
|
|
245
|
+
}
|
|
207
246
|
getIdentity() {
|
|
208
247
|
return this.run(async (client) => {
|
|
209
248
|
const value = await client.getDefaultIdentity();
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Host-only dispatcher for externally transported ANP-authenticated HTTP. */
|
|
2
|
+
import type { AwikiSdkClient } from './provider-api.ts';
|
|
3
|
+
export declare const AWIKI_EXTERNAL_HTTP_MAX_BODY_BYTES: number;
|
|
4
|
+
export type AwikiExternalHttpAuthErrorCode = 'not-registered' | 'signed-out' | 'invalid-request' | 'unsupported-body' | 'body-too-large' | 'auth-state-unavailable';
|
|
5
|
+
/** Stable Host-only failure without request, response, credential, or path detail. */
|
|
6
|
+
export declare class AwikiExternalHttpAuthError extends Error {
|
|
7
|
+
readonly code: AwikiExternalHttpAuthErrorCode;
|
|
8
|
+
readonly name = "AwikiExternalHttpAuthError";
|
|
9
|
+
constructor(code: AwikiExternalHttpAuthErrorCode);
|
|
10
|
+
}
|
|
11
|
+
export type AwikiHttpTransport = (request: Request) => Promise<Response>;
|
|
12
|
+
/** Trusted same-process API; never expose this interface through Remote or tools. */
|
|
13
|
+
export interface AwikiExternalHttpAuth {
|
|
14
|
+
dispatch(request: Request, transport: AwikiHttpTransport): Promise<Response>;
|
|
15
|
+
}
|
|
16
|
+
export interface AwikiExternalHttpAuthSession {
|
|
17
|
+
readonly client: AwikiSdkClient;
|
|
18
|
+
assertActive(): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export declare function createAwikiExternalHttpAuth(acquire: () => Promise<AwikiExternalHttpAuthSession>): AwikiExternalHttpAuth;
|
|
21
|
+
export declare function externalHttpAuthError(code: AwikiExternalHttpAuthErrorCode): AwikiExternalHttpAuthError;
|
|
22
|
+
export declare function mapProviderError(error: unknown): AwikiExternalHttpAuthError;
|
|
23
|
+
//# sourceMappingURL=external-http-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-http-auth.d.ts","sourceRoot":"","sources":["../../src/external-http-auth.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAE9E,OAAO,KAAK,EACV,cAAc,EAIf,MAAM,mBAAmB,CAAA;AAE1B,eAAO,MAAM,kCAAkC,QAAkB,CAAA;AASjE,MAAM,MAAM,8BAA8B,GACtC,gBAAgB,GAChB,YAAY,GACZ,iBAAiB,GACjB,kBAAkB,GAClB,gBAAgB,GAChB,wBAAwB,CAAA;AAW5B,sFAAsF;AACtF,qBAAa,0BAA2B,SAAQ,KAAK;aAGhB,IAAI,EAAE,8BAA8B;IAFvE,SAAgB,IAAI,gCAA+B;gBAEhB,IAAI,EAAE,8BAA8B;CAGxE;AAED,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAExE,qFAAqF;AACrF,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CAC7E;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAC9B;AAED,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,MAAM,OAAO,CAAC,4BAA4B,CAAC,GACnD,qBAAqB,CAmCvB;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,8BAA8B,GAAG,0BAA0B,CAEtG;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,0BAA0B,CAY3E"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/** Host-only dispatcher for externally transported ANP-authenticated HTTP. */
|
|
2
|
+
export const AWIKI_EXTERNAL_HTTP_MAX_BODY_BYTES = 4 * 1024 * 1024;
|
|
3
|
+
const MANAGED_HEADERS = [
|
|
4
|
+
'authorization',
|
|
5
|
+
'signature-input',
|
|
6
|
+
'signature',
|
|
7
|
+
'content-digest',
|
|
8
|
+
];
|
|
9
|
+
const ERROR_MESSAGES = {
|
|
10
|
+
'not-registered': 'A registered AWiki identity is required.',
|
|
11
|
+
'signed-out': 'This installation is signed out of AWiki.',
|
|
12
|
+
'invalid-request': 'The external HTTP request is invalid.',
|
|
13
|
+
'unsupported-body': 'The external HTTP request body cannot be replayed safely.',
|
|
14
|
+
'body-too-large': 'The external HTTP request body exceeds 4 MiB.',
|
|
15
|
+
'auth-state-unavailable': 'AWiki external HTTP authentication is unavailable.',
|
|
16
|
+
};
|
|
17
|
+
/** Stable Host-only failure without request, response, credential, or path detail. */
|
|
18
|
+
export class AwikiExternalHttpAuthError extends Error {
|
|
19
|
+
code;
|
|
20
|
+
name = 'AwikiExternalHttpAuthError';
|
|
21
|
+
constructor(code) {
|
|
22
|
+
super(ERROR_MESSAGES[code]);
|
|
23
|
+
this.code = code;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function createAwikiExternalHttpAuth(acquire) {
|
|
27
|
+
return Object.freeze({
|
|
28
|
+
async dispatch(request, transport) {
|
|
29
|
+
validateDispatchInput(request, transport);
|
|
30
|
+
const session = await acquire();
|
|
31
|
+
await session.assertActive();
|
|
32
|
+
const body = await readReplayableBody(request);
|
|
33
|
+
await session.assertActive();
|
|
34
|
+
let attempt;
|
|
35
|
+
try {
|
|
36
|
+
attempt = await session.client.prepareExternalHttpRequest({
|
|
37
|
+
url: request.url,
|
|
38
|
+
method: request.method,
|
|
39
|
+
headers: requestHeaders(request),
|
|
40
|
+
...body === undefined ? {} : { body },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
throw mapProviderError(error);
|
|
45
|
+
}
|
|
46
|
+
await session.assertActive();
|
|
47
|
+
const response = await transport(authenticatedRequest(request, body, attempt));
|
|
48
|
+
const retry = await handleResponseWithoutChangingCompletedRequest(attempt, response);
|
|
49
|
+
if (retry === null)
|
|
50
|
+
return response;
|
|
51
|
+
try {
|
|
52
|
+
await session.assertActive();
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return response;
|
|
56
|
+
}
|
|
57
|
+
const retriedResponse = await transport(authenticatedRequest(request, body, retry));
|
|
58
|
+
await handleResponseWithoutChangingCompletedRequest(retry, retriedResponse);
|
|
59
|
+
return retriedResponse;
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
export function externalHttpAuthError(code) {
|
|
64
|
+
return new AwikiExternalHttpAuthError(code);
|
|
65
|
+
}
|
|
66
|
+
export function mapProviderError(error) {
|
|
67
|
+
if (error instanceof AwikiExternalHttpAuthError)
|
|
68
|
+
return error;
|
|
69
|
+
try {
|
|
70
|
+
if (typeof error === 'object' && error !== null) {
|
|
71
|
+
const value = error;
|
|
72
|
+
if (value.name === 'AwikiSdkError') {
|
|
73
|
+
if (value.code === 'not-registered')
|
|
74
|
+
return externalHttpAuthError('not-registered');
|
|
75
|
+
if (value.code === 'invalid-request')
|
|
76
|
+
return externalHttpAuthError('invalid-request');
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch { }
|
|
81
|
+
return externalHttpAuthError('auth-state-unavailable');
|
|
82
|
+
}
|
|
83
|
+
function validateDispatchInput(request, transport) {
|
|
84
|
+
if (!(request instanceof Request) || typeof transport !== 'function' || request.bodyUsed) {
|
|
85
|
+
throw externalHttpAuthError('invalid-request');
|
|
86
|
+
}
|
|
87
|
+
for (const name of MANAGED_HEADERS) {
|
|
88
|
+
if (request.headers.has(name))
|
|
89
|
+
throw externalHttpAuthError('invalid-request');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function readReplayableBody(request) {
|
|
93
|
+
if (request.body === null)
|
|
94
|
+
return undefined;
|
|
95
|
+
let clone;
|
|
96
|
+
try {
|
|
97
|
+
clone = request.clone();
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
throw externalHttpAuthError('unsupported-body');
|
|
101
|
+
}
|
|
102
|
+
const stream = clone.body;
|
|
103
|
+
if (stream === null)
|
|
104
|
+
return new Uint8Array();
|
|
105
|
+
const reader = stream.getReader();
|
|
106
|
+
const chunks = [];
|
|
107
|
+
let length = 0;
|
|
108
|
+
try {
|
|
109
|
+
while (true) {
|
|
110
|
+
const result = await reader.read();
|
|
111
|
+
if (result.done)
|
|
112
|
+
break;
|
|
113
|
+
length += result.value.byteLength;
|
|
114
|
+
if (length > AWIKI_EXTERNAL_HTTP_MAX_BODY_BYTES) {
|
|
115
|
+
void reader.cancel().catch(() => { });
|
|
116
|
+
void request.body?.cancel().catch(() => { });
|
|
117
|
+
throw externalHttpAuthError('body-too-large');
|
|
118
|
+
}
|
|
119
|
+
chunks.push(Uint8Array.from(result.value));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
if (error instanceof AwikiExternalHttpAuthError)
|
|
124
|
+
throw error;
|
|
125
|
+
throw externalHttpAuthError('unsupported-body');
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
reader.releaseLock();
|
|
129
|
+
}
|
|
130
|
+
const bytes = new Uint8Array(length);
|
|
131
|
+
let offset = 0;
|
|
132
|
+
for (const chunk of chunks) {
|
|
133
|
+
bytes.set(chunk, offset);
|
|
134
|
+
offset += chunk.byteLength;
|
|
135
|
+
}
|
|
136
|
+
return bytes;
|
|
137
|
+
}
|
|
138
|
+
function requestHeaders(request) {
|
|
139
|
+
return [...request.headers].map(([name, value]) => ({ name, value }));
|
|
140
|
+
}
|
|
141
|
+
function authenticatedRequest(original, body, attempt) {
|
|
142
|
+
const headers = new Headers(original.headers);
|
|
143
|
+
for (const header of attempt.headerPatch)
|
|
144
|
+
headers.set(header.name, header.value);
|
|
145
|
+
const init = {
|
|
146
|
+
method: attempt.method,
|
|
147
|
+
headers,
|
|
148
|
+
redirect: 'manual',
|
|
149
|
+
signal: original.signal,
|
|
150
|
+
cache: original.cache,
|
|
151
|
+
credentials: original.credentials,
|
|
152
|
+
integrity: original.integrity,
|
|
153
|
+
keepalive: original.keepalive,
|
|
154
|
+
mode: original.mode,
|
|
155
|
+
referrer: original.referrer,
|
|
156
|
+
referrerPolicy: original.referrerPolicy,
|
|
157
|
+
...body === undefined ? {} : { body: Uint8Array.from(body) },
|
|
158
|
+
};
|
|
159
|
+
try {
|
|
160
|
+
return new Request(attempt.targetUrl, init);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
throw externalHttpAuthError('invalid-request');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function handleResponseWithoutChangingCompletedRequest(attempt, response) {
|
|
167
|
+
try {
|
|
168
|
+
return await attempt.handleResponse(responseMetadata(response));
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// The transport has already completed and may have executed a non-idempotent
|
|
172
|
+
// operation. Never turn post-response auth bookkeeping into a replay signal.
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function responseMetadata(response) {
|
|
177
|
+
const headers = [];
|
|
178
|
+
for (const name of ['authentication-info', 'www-authenticate', 'accept-signature']) {
|
|
179
|
+
const value = response.headers.get(name);
|
|
180
|
+
if (value !== null)
|
|
181
|
+
headers.push({ name, value });
|
|
182
|
+
}
|
|
183
|
+
return { statusCode: response.status, headers };
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=external-http-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-http-auth.js","sourceRoot":"","sources":["../../src/external-http-auth.ts"],"names":[],"mappings":"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"}
|