@tier0/sdk-v2 0.4.0 → 0.6.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/README.md +6 -2
- package/dist/cjs/capabilities.d.ts +1 -2
- package/dist/cjs/capabilities.js +5 -6
- package/dist/cjs/capabilities.js.map +1 -1
- package/dist/cjs/client.d.ts +0 -1
- package/dist/cjs/openapi/api.generated.d.ts +0 -1
- package/dist/cjs/openapi/codecs.generated.d.ts +0 -4
- package/dist/cjs/openapi/codecs.generated.js +1 -1
- package/dist/cjs/openapi/codecs.generated.js.map +1 -1
- package/dist/cjs/openapi/types.generated.d.ts +0 -2
- package/dist/cjs/openapi/types.generated.js.map +1 -1
- package/dist/cjs/uns-v2/generated.d.ts +398 -54
- package/dist/cjs/uns-v2/generated.js +8 -8
- package/dist/cjs/uns-v2/generated.js.map +1 -1
- package/dist/esm/capabilities.d.ts +1 -2
- package/dist/esm/capabilities.js +5 -6
- package/dist/esm/capabilities.js.map +1 -1
- package/dist/esm/client.d.ts +0 -1
- package/dist/esm/openapi/api.generated.d.ts +0 -1
- package/dist/esm/openapi/codecs.generated.d.ts +0 -4
- package/dist/esm/openapi/codecs.generated.js +1 -1
- package/dist/esm/openapi/codecs.generated.js.map +1 -1
- package/dist/esm/openapi/types.generated.d.ts +0 -2
- package/dist/esm/openapi/types.generated.js.map +1 -1
- package/dist/esm/uns-v2/generated.d.ts +398 -54
- package/dist/esm/uns-v2/generated.js +8 -8
- package/dist/esm/uns-v2/generated.js.map +1 -1
- package/package.json +2 -2
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/SKILL.md +2 -2
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/create.md +67 -9
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/protocols.md +13 -7
- package/skills/tier0-sdk-v2/tier0-sdk-v2-source/references/read.md +1 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/SKILL.md +6 -4
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/create.md +9 -7
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/http-server.md +89 -0
- package/skills/tier0-sdk-v2/tier0-sdk-v2-stream/references/update.md +1 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-system/SKILL.md +1 -1
- package/skills/tier0-sdk-v2/tier0-sdk-v2-uns/references/create.md +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tier0/sdk-v2",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Tier0 Cloud new-architecture SDK for TypeScript and JavaScript",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
"scripts": {
|
|
157
157
|
"sync:contracts": "node scripts/sync-contracts.mjs",
|
|
158
158
|
"generate": "node scripts/generate-openapi.mjs && node scripts/generate-uns-v2.mjs",
|
|
159
|
-
"check:contracts": "node scripts/check-contracts.mjs && node --test scripts/retired-stream-probe.test.mjs && node scripts/generate-openapi.mjs --check && node scripts/generate-uns-v2.mjs --check",
|
|
159
|
+
"check:contracts": "node scripts/check-contracts.mjs && node --test scripts/retired-stream-probe.test.mjs scripts/retired-http-push.test.mjs scripts/sync-contracts.test.mjs && node scripts/generate-openapi.mjs --check && node scripts/generate-uns-v2.mjs --check",
|
|
160
160
|
"check:skills": "node scripts/check-skills.mjs && node scripts/check-skill-examples.mjs",
|
|
161
161
|
"typecheck": "tsc --noEmit && tsc -p tsconfig.tests.json",
|
|
162
162
|
"build": "node scripts/build.mjs",
|
|
@@ -23,10 +23,10 @@ Source 维护连接配置、认证引用与源状态。脚本处理用 [Stream](
|
|
|
23
23
|
|
|
24
24
|
## 核心约束
|
|
25
25
|
|
|
26
|
-
1. Source 配置统一使用 documents.*,原生文档为 `{name,protocol,config}`。
|
|
26
|
+
1. Source 配置统一使用 documents.*,原生文档为 `{name,protocol,config}`,HTTP API 显式指定 `config.role`。HTTP 统一为 `protocol: http-api` + `config.role: client/server`;Client 主动请求,Server 接收入站请求,不能互换。HTTPServer 统一接收入站请求,固定前缀后的路由和响应时机属于 Stream;旧 http_push 不再用于新建。
|
|
27
27
|
2. 已有 ID 和 revision 时优先用 documents.update 的 patch,避免额外 get;完整替换 document 前读取完整文档,安全列表摘要不能无损回写。原生 ID 是十进制字符串,revision 原样传递,不转 Number。
|
|
28
28
|
3. 现有 Source 修改、启停、删除都要 expectedRevision。没有任何 Stream 引用时可直接提交;有草稿、活动、历史或不可见引用时仍需 confirmedImpactRevision。先形成精确意图,再读取全部影响分页;回执不可合成、跨意图复用或静默刷新。提供的回执始终严格校验,不自动获取并同意影响。
|
|
29
|
-
4. 配置中保留认证/TLS 引用,不写明文秘密;HTTP 新凭据走创建专用 credentials
|
|
29
|
+
4. 配置中保留认证/TLS 引用,不写明文秘密;HTTP Client 新凭据走创建专用 credentials;Server 不携带 Client auth/TLS 配置。
|
|
30
30
|
5. desiredState 不代表实际健康,保存配置不代表 Stream 已切换至新 configRevision。运行状态另行读取。
|
|
31
31
|
|
|
32
32
|
## 返回值与异常
|
|
@@ -6,15 +6,16 @@
|
|
|
6
6
|
| --- | --- | --- | --- |
|
|
7
7
|
| dryRun | boolean | 是 | true 校验;false 保存 |
|
|
8
8
|
| document.name | string | 是 | 1–128 字符 |
|
|
9
|
-
| document.protocol | string | 是 | 原生协议,如 http、mqtt、modbus |
|
|
10
|
-
| document.config |
|
|
9
|
+
| document.protocol | string | 是 | 原生协议,如 http-api、mqtt、modbus |
|
|
10
|
+
| document.config.role | client / server | HTTP API 显式指定 | client 主动请求;server 接收入站请求 |
|
|
11
|
+
| document.config | object | 是 | endpoint、options;见[协议配置](protocols.md) |
|
|
11
12
|
| document.description | string | 否 | 最多 512 字符 |
|
|
12
|
-
| credentials | object | 否 |
|
|
13
|
+
| credentials | object | 否 | 仅创建 HTTP Client 时提供新凭据,与已有 auth/TLS 引用互斥 |
|
|
13
14
|
| idempotencyKey | string | 否 | 建议提供,最多 128 字符,同一业务意图保持不变 |
|
|
14
15
|
|
|
15
|
-
## 完整示例:HTTP 数据源
|
|
16
|
+
## 完整示例:HTTP Client 数据源
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
传入实际端点、名称和意图键。此函数创建 `http-api` + `config.role: client` 配置,不启用源。client 从服务端环境读取 API Host/Key,也可显式注入。
|
|
18
19
|
|
|
19
20
|
```ts
|
|
20
21
|
import { createClient, type Tier0Client, type CloudSourceDocument } from '@tier0/sdk-v2';
|
|
@@ -23,8 +24,8 @@ export async function createHttpSource(
|
|
|
23
24
|
endpoint: string, name: string, intentKey: string, client: Tier0Client = createClient(),
|
|
24
25
|
) {
|
|
25
26
|
const document: CloudSourceDocument = {
|
|
26
|
-
name, protocol: 'http', description: '订单系统 HTTP 接入',
|
|
27
|
-
config: {
|
|
27
|
+
name, protocol: 'http-api', description: '订单系统 HTTP 接入',
|
|
28
|
+
config: { role: 'client', endpoint, options: {} },
|
|
28
29
|
};
|
|
29
30
|
const saved = await client.source.documents.create({
|
|
30
31
|
dryRun: false, document, idempotencyKey: intentKey,
|
|
@@ -36,7 +37,9 @@ export async function createHttpSource(
|
|
|
36
37
|
|
|
37
38
|
一次提交即可返回完整 data:id、revision、document、desiredState、runtimeState、health、createdAt、updatedAt,无需另行 get。需要单独预检时可先传 `dryRun: true`,返回 `{ validated: true, sideEffects: false }`,没有 ID。预检不保留提交条件;实际保存仍会校验。ID 原样保留十进制字符串。
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
HTTP Server 同样使用 `protocol: http-api`,角色为 `config.role: server`;其配置以 `source.protocols.list({})` 返回的正式合同为准,不能复用下方 Client 的主动请求示例。入站业务统一绑定 HTTPServer Source,路由和响应时机在 Stream 定义;202 状态不能据此报告 Stream 执行完成。
|
|
41
|
+
|
|
42
|
+
## HTTP Client Bearer 凭据
|
|
40
43
|
|
|
41
44
|
token 从服务端密钥提供者取得,不写入脚本、文档、日志或仓库。
|
|
42
45
|
|
|
@@ -49,7 +52,7 @@ export async function createAuthenticatedHttpSource(
|
|
|
49
52
|
) {
|
|
50
53
|
const saved = await client.source.documents.create({
|
|
51
54
|
dryRun: false, idempotencyKey: intentKey,
|
|
52
|
-
document: { name, protocol: 'http', config: {
|
|
55
|
+
document: { name, protocol: 'http-api', config: { role: 'client', endpoint, options: {} } },
|
|
53
56
|
credentials: { method: 'GET', kind: 'bearer', material: { token } },
|
|
54
57
|
});
|
|
55
58
|
if (!('id' in saved.data)) throw new Error('未返回 Source ID');
|
|
@@ -58,3 +61,58 @@ export async function createAuthenticatedHttpSource(
|
|
|
58
61
|
```
|
|
59
62
|
|
|
60
63
|
配置保存不代表连接健康;见[查询](read.md)。仅在任务包含启用时执行[启停](state.md)。结果未知时保留原请求、requestId、意图键,先核对列表/历史,不自动重复创建。
|
|
64
|
+
|
|
65
|
+
## HTTP Server:管理配置
|
|
66
|
+
|
|
67
|
+
正式协议目录规定 Server 的 `config.endpoint` 为 `http://managed`、`options` 为 `{}`,不携带 Client 的 `auth` / `tls` 或创建专用 `credentials`。该函数只保存 Source,不启用、不创建 Stream,也不发起入站请求。
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { createClient, type Tier0Client, type CloudSourceDocument } from '@tier0/sdk-v2';
|
|
71
|
+
|
|
72
|
+
export async function createHttpServerSource(
|
|
73
|
+
name: string, intentKey: string, client: Tier0Client = createClient(),
|
|
74
|
+
) {
|
|
75
|
+
const document: CloudSourceDocument = {
|
|
76
|
+
name, protocol: 'http-api',
|
|
77
|
+
config: { role: 'server', endpoint: 'http://managed', options: {} },
|
|
78
|
+
};
|
|
79
|
+
const saved = await client.source.documents.create({
|
|
80
|
+
dryRun: false, document, idempotencyKey: intentKey,
|
|
81
|
+
});
|
|
82
|
+
if (!('id' in saved.data)) throw new Error('未返回已保存的 Source');
|
|
83
|
+
return { receipt: saved, current: saved.data };
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Server 的固定前缀 `/ingest/v1/{workspaceId}/{sourceId}` 和 Bearer 要求由协议目录的 `x-tier0-http-in` 描述。Source 不接受业务路径配置;在 [Stream HTTPServer 示例](../../tier0-sdk-v2-stream/references/http-server.md)中注册 `onRequest` 的 method/path。响应时机属于 Stream。
|
|
88
|
+
|
|
89
|
+
## 四类 inline 认证与 REQUEST 绑定
|
|
90
|
+
|
|
91
|
+
单条创建可通过 `credentials` 提供以下只写材料;与已有 auth/TLS 引用互斥,并要求本次创建的 `idempotencyKey`。材料从调用方已授权的密钥提供者注入,不在示例中读取文件或环境中的秘密,不记录或返回材料。
|
|
92
|
+
|
|
93
|
+
| kind | material | method |
|
|
94
|
+
| --- | --- | --- |
|
|
95
|
+
| basic | username、password | 省略时 REQUEST;可显式绑定受限 HTTP 方法 |
|
|
96
|
+
| bearer | token | 省略时 REQUEST;上方 GET 示例保持明确的方法限制 |
|
|
97
|
+
| apiKey | headerName、value | 省略或 REQUEST;不接受旧 name/key 形态 |
|
|
98
|
+
| oauth2ClientCredentials | tokenUrl、clientId、clientSecret;可选 scope、clientAuthentication | 省略、REQUEST 或 GET |
|
|
99
|
+
|
|
100
|
+
OAuth 的 `clientAuthentication` 支持 `client_secret_post`(默认)和 `client_secret_basic`。REQUEST 表示绑定 Source 基址,实际路径和方法仍由执行宿主逐次授权;显式方法的已有绑定不能解释成 REQUEST 的基址范围。vendor SESSION 合同独立保留,不是此 inline 创建的 method,也不新增独立公开凭据 API。
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { createClient, type Tier0Client, type CloudSourceCreateCredentials } from '@tier0/sdk-v2';
|
|
104
|
+
|
|
105
|
+
export async function createHttpSourceWithCredentials(
|
|
106
|
+
endpoint: string, name: string, credentials: CloudSourceCreateCredentials, intentKey: string,
|
|
107
|
+
client: Tier0Client = createClient(),
|
|
108
|
+
) {
|
|
109
|
+
const saved = await client.source.documents.create({
|
|
110
|
+
dryRun: false, idempotencyKey: intentKey,
|
|
111
|
+
document: { name, protocol: 'http-api',
|
|
112
|
+
config: { role: 'client', endpoint, options: {} } },
|
|
113
|
+
credentials,
|
|
114
|
+
});
|
|
115
|
+
if (!('id' in saved.data)) throw new Error('未返回已保存的 Source');
|
|
116
|
+
return { receipt: saved, current: saved.data };
|
|
117
|
+
}
|
|
118
|
+
```
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# 协议与连接配置
|
|
2
2
|
|
|
3
|
-
先调用 `source.protocols.list({})
|
|
3
|
+
先调用 `source.protocols.list({})`。协议枚举存在不等于目标环境驱动或凭据解析器已就绪。HTTP Source 统一使用 `protocol: 'http-api'`,通过文档的 `config.role: 'client' | 'server'` 区分主动请求和入站服务,与 Enterprise 的一协议两角色模型一致。角色和协议能力以目标环境目录为准;旧 `http` 不作为新建示例。
|
|
4
4
|
|
|
5
5
|
| 配置字段 | 类型 | 必填 | 说明 |
|
|
6
6
|
| --- | --- | --- | --- |
|
|
7
|
-
|
|
|
7
|
+
| role | client / server | HTTP 示例显式指定 | 省略时为 client;Server 使用托管地址和空 options |
|
|
8
8
|
| endpoint | string | 是 | 地址,不内嵌凭据 |
|
|
9
9
|
| options | object | 是 | 协议专用选项 |
|
|
10
10
|
| auth.kind | string | 有认证时 | basic/bearer/apiKey/usernamePassword/oauth2ClientCredentials |
|
|
@@ -19,18 +19,24 @@ import { createClient, type CloudSourceDocument } from '@tier0/sdk-v2';
|
|
|
19
19
|
export async function inspectProtocolExamples() {
|
|
20
20
|
const capabilities = await createClient().source.protocols.list({});
|
|
21
21
|
const examples: CloudSourceDocument[] = [
|
|
22
|
-
{ name: 'orders-http', protocol: 'http',
|
|
23
|
-
config: {
|
|
22
|
+
{ name: 'orders-http', protocol: 'http-api',
|
|
23
|
+
config: { role: 'client', endpoint: 'https://orders.example.com', options: {} } },
|
|
24
24
|
{ name: 'line-plc', protocol: 'modbus',
|
|
25
|
-
config: {
|
|
25
|
+
config: { endpoint: 'tcp://plc.example.com:502', options: { unitId: 1 } } },
|
|
26
26
|
{ name: 'line-mqtt', protocol: 'mqtt',
|
|
27
|
-
config: {
|
|
27
|
+
config: { endpoint: 'mqtt://broker.example.com:1883',
|
|
28
28
|
options: { clientId: 'tier0-line-reader', subscriptions: { 'factory/line1/status': 1 } } } },
|
|
29
29
|
];
|
|
30
30
|
return { capabilities: capabilities.data, examples };
|
|
31
31
|
}
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
HTTP
|
|
34
|
+
HTTP Client 的 options 支持 headers 和 parameters;headers 可包含目录允许的非秘密自定义头,Accept/Content-Type 保留 MIME 校验,认证及传输头保留给服务端,不能塞 Authorization/Cookie。Modbus 寄存器范围和轮询频率在 Stream acquisition 中定义;MQTT QoS 为 0/1/2。认证创建见[创建](create.md),修改时保留原 auth/tls 引用。
|
|
35
35
|
|
|
36
36
|
SQL options 包含 tlsMode(verify-full/disable)和 queries(资源名→固定 SQL);脚本不能替换任意 SQL。OPC UA、SAP、Teamcenter 等按能力和服务端校验配置,不从 HTTP 模板生造字段。不要为通过校验自动移除认证或关闭 TLS。
|
|
37
|
+
|
|
38
|
+
HTTP Client 示例不能用于 Server。HTTPServer 通过 `source.documents.*` 配置监听源,固定前缀为 `/ingest/v1/{workspaceId}/{sourceId}`;Stream 注册相对业务路由和处理函数,见 [HTTPServer Stream](../../tier0-sdk-v2-stream/references/http-server.md)。旧 `http_push` 不再作为独立模式创建;响应时机在 Stream 层处理。
|
|
39
|
+
|
|
40
|
+
Cloud 的角色位于 `config.role`;Enterprise 投影的顶层 `role` 不能直接抄入 Cloud 管理文档。Server 配置见[创建](create.md),以 `http://managed` 和空 options 保存,不从 Client 模板推导。
|
|
41
|
+
|
|
42
|
+
HTTP Client 的单条 inline 认证支持 basic / bearer / apiKey / oauth2ClientCredentials,method 省略时由服务端按 REQUEST 绑定 Source 基址;显式方法限制保持不变。APIKey 材料为 headerName/value,OAuth 可提供 clientAuthentication。见[创建](create.md)。
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
| source.runtime.get | /openapi/v2/uns/sources/runtime/get | id(必填) | 运行状态 |
|
|
8
8
|
| source.observations.get | /openapi/v2/uns/sources/observations/get | items:id、expectedRevision | 指定源和修订的观测 |
|
|
9
9
|
|
|
10
|
-
page 从 1 开始,size 1–200。列表默认安全摘要。get 省略 view 保持旧的 full 行为;summary 需要 uns.source.view 或 uns.source.manage,full 需要 uns.source.manage;API Key 读取完整文档还必须是 full_access(includeDocument:true 同样适用)。未知 revision 时可读取 summary;要完整替换 document 或查看配置时读取 full。已持有写回执中的 ID/revision 时,可直接提交 patch,无需为了编辑再 get;服务端仍会检查版本冲突。摘要不能用于重建完整配置。
|
|
10
|
+
page 从 1 开始,size 1–200。列表默认安全摘要。get 省略 view 保持旧的 full 行为;summary 需要 uns.source.view 或 uns.source.manage,full 需要 uns.source.manage;API Key 读取完整文档还必须是 full_access(includeDocument:true 同样适用)。未知 revision 时可读取 summary;要完整替换 document 或查看配置时读取 full。已持有写回执中的 ID/revision 时,可直接提交 patch,无需为了编辑再 get;服务端仍会检查版本冲突。摘要不能用于重建完整配置。 对已知旧版配置,安全读取投影可能移除退休的 configVersion 标记;这不代表历史已经迁移或可以运行。写入仍使用当前无版本配置,不向请求补回旧标记或协议别名。
|
|
11
11
|
|
|
12
12
|
```ts
|
|
13
13
|
import { createClient, type Tier0Client } from '@tier0/sdk-v2';
|
|
@@ -11,7 +11,8 @@ description: 使用 @tier0/sdk-v2 的 client.stream 创建、查询、修改和
|
|
|
11
11
|
|
|
12
12
|
| 用户任务 | 参考 | 方法 |
|
|
13
13
|
| --- | --- | --- |
|
|
14
|
-
| 新建并绑定 HTTP Source | [创建](references/create.md) | documents.create({program}) |
|
|
14
|
+
| 新建并绑定 HTTP Client Source | [创建](references/create.md) | documents.create({program}) |
|
|
15
|
+
| HTTPServer 固定前缀与业务路由 | [HTTPServer](references/http-server.md) | documents.create({document})、api.stream.onRequest、ctx.reply |
|
|
15
16
|
| 列表、完整文档、运行记录 | [查询](references/read.md) | programs.list、documents.get、observations.get、runs.list |
|
|
16
17
|
| 修改草稿、脚本和绑定 | [修改](references/update.md) | documents.validate/update |
|
|
17
18
|
| 发布、停用、版本回滚 | [部署](references/deploy.md) | programs.deploy/rollback、documents.disable |
|
|
@@ -22,10 +23,10 @@ description: 使用 @tier0/sdk-v2 的 client.stream 创建、查询、修改和
|
|
|
22
23
|
|
|
23
24
|
| 形态 | 完整输入 | 适用入口 |
|
|
24
25
|
| --- | --- | --- |
|
|
25
|
-
| Runtime program | name、
|
|
26
|
+
| Runtime program | name、script 字符串、sourceBindings、timeoutMs | documents.create/update/validate({program}) |
|
|
26
27
|
| 云端原生文档 | name、model、script 对象、sources、entries | documents.create/get/update/validate |
|
|
27
28
|
|
|
28
|
-
程序必须是完整 `module.exports = api => { ... }`,执行器 API 是 api.sources/api.uns/api.stream,与 npm 包的 client.source 不同。Runtime sourceBindings 为 sourceId/configRevision/acquisition;原生 sources 还使用 alias/resources。连接凭据不进脚本或 acquisition
|
|
29
|
+
程序必须是完整 `module.exports = api => { ... }`,执行器 API 是 api.sources/api.uns/api.stream,与 npm 包的 client.source 不同。Runtime sourceBindings 为 sourceId/configRevision/acquisition;原生 sources 还使用 alias/resources。连接凭据不进脚本或 acquisition,秘密材料由 Source 执行宿主管理,不通过脚本读取、记录或返回。定时入口使用 api.stream.every,不传播旧注册别名;npm 的 client.controls 管理入口不作为 Goja 的 Stream 注册参数。
|
|
29
30
|
|
|
30
31
|
## 核心约束
|
|
31
32
|
|
|
@@ -34,7 +35,8 @@ description: 使用 @tier0/sdk-v2 的 client.stream 创建、查询、修改和
|
|
|
34
35
|
3. 所有更新/部署/删除携带当前 expectedRevision。原生停用另需 expectedActiveRelease,避免停掉他人刚发布的版本。
|
|
35
36
|
4. versionId 从 versions.list 取得,不把 release 序号当 ID。expectedSourceRevision 仅用于恰好一个依赖源,不代表多个源的修订。
|
|
36
37
|
5. resources=[] 是空冻结资源集,不是通配符。写入本身由服务端校验,需要预览时单独 validate;当前云端程序 timeoutMs 上限 30000。旧 runtime.capabilities 查询已退役,不作为管理或执行前置检查,不为此增加 Control scope 或逐 Key 控制码授权。
|
|
37
|
-
6.
|
|
38
|
+
6. HTTPServer 响应时机在 Stream:普通返回等处理/提交完成;HTTP onRequest 内 await ctx.reply 可先回复一次再继续,仍受超时/并发/权限约束,断线不取消但 shutdown 可取消,不保证崩溃重放。
|
|
39
|
+
7. 仅执行用户要求的生命周期步骤;修改脚本不自动授予发布、停用或删除权限。
|
|
38
40
|
|
|
39
41
|
## 成功与异常
|
|
40
42
|
|
|
@@ -6,15 +6,14 @@
|
|
|
6
6
|
| --- | --- | --- | --- |
|
|
7
7
|
| dryRun | boolean | 是 | true 预检,false 保存草稿 |
|
|
8
8
|
| program.name | string | 是 | 1–63 字符 |
|
|
9
|
-
| program.sdkVersion | tier0-stream/v2 | 是 | 运行时脚本版本 |
|
|
10
9
|
| program.script | string | 是 | 完整 module.exports 程序 |
|
|
11
10
|
| program.timeoutMs | integer | 否 | 当前云端上限 30000 ms |
|
|
12
11
|
| program.sourceBindings | array | 否 | sourceId、configRevision、acquisition |
|
|
13
12
|
| idempotencyKey | string | 否 | 最多 128 字符,本次创建的固定意图键 |
|
|
14
13
|
|
|
15
|
-
## HTTP Source 绑定示例
|
|
14
|
+
## HTTP Client Source 绑定示例
|
|
16
15
|
|
|
17
|
-
先按[Source 查询](../../tier0-sdk-v2-source/references/read.md)选定真实 HTTP Source。脚本的 api.sources 属于平台执行器,与应用中的 client.source 不同。sourceId 保持字符串,configRevision
|
|
16
|
+
先按[Source 查询](../../tier0-sdk-v2-source/references/read.md)选定真实 HTTP Client Source。脚本的 api.sources 属于平台执行器,与应用中的 client.source 不同。sourceId 保持字符串,configRevision 从完整源读取。省略 `config.role` 时按正式协议目录默认为 client;Server 走入站语义,不能创建为主动请求程序。
|
|
18
17
|
|
|
19
18
|
```ts
|
|
20
19
|
import { createClient, type Tier0Client, type RuntimeStreamDefinition } from '@tier0/sdk-v2';
|
|
@@ -25,14 +24,17 @@ export async function createHttpStream(
|
|
|
25
24
|
if (!/^[1-9][0-9]*$/.test(sourceId)) throw new Error('Source ID 必须是正十进制字符串');
|
|
26
25
|
const source = await client.source.documents.get({ id: sourceId, view: 'full' });
|
|
27
26
|
if (!('document' in source.data)) throw new Error('需要完整 Source 文档');
|
|
28
|
-
if (source.data.document.protocol !== 'http'
|
|
27
|
+
if (source.data.document.protocol !== 'http-api' || 'sourceType' in source.data.document.config ||
|
|
28
|
+
(source.data.document.config.role ?? 'client') !== 'client') {
|
|
29
|
+
throw new Error('此主动请求示例需要 HTTP API Client Source');
|
|
30
|
+
}
|
|
29
31
|
const document: RuntimeStreamDefinition = {
|
|
30
|
-
name,
|
|
32
|
+
name, timeoutMs: 5000,
|
|
31
33
|
sourceBindings: [{ sourceId, configRevision: source.data.revision, acquisition: {} }],
|
|
32
34
|
script: [
|
|
33
35
|
'module.exports = api => {',
|
|
34
36
|
' const upstream = api.sources.use({ id: ' + JSON.stringify(sourceId) + ', resources: [] });',
|
|
35
|
-
' api.stream.
|
|
37
|
+
' api.stream.every({ id: "tick", intervalMs: 60000 }, async () => {',
|
|
36
38
|
' const response = await upstream.request({ method: "GET", path: "orders" });',
|
|
37
39
|
' return response.data;',
|
|
38
40
|
' });',
|
|
@@ -53,4 +55,4 @@ export async function createHttpStream(
|
|
|
53
55
|
|
|
54
56
|
programs.create 已退役,创建统一使用 documents.create。历史创建结局未知时先按已知 ID 或业务标识核对状态,不能将旧请求或幂等键改投 documents.create 重做。
|
|
55
57
|
|
|
56
|
-
原生编辑器形态 `documents.create({dryRun, document})` 的 document 为 name/model/script对象/sources/entries,script 为 `{
|
|
58
|
+
原生编辑器形态 `documents.create({dryRun, document})` 的 document 为 name/model/script对象/sources/entries,script 为 `{code,timeoutMs}`,sources 用 alias/sourceId/configRevision/resources/acquisition。不要把上面的 script 字符串直接塞入原生文档。
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# HTTPServer 固定前缀与 Stream 路由
|
|
2
|
+
|
|
3
|
+
先用 [Source 创建](../../tier0-sdk-v2-source/references/create.md)保存 `http-api` + `config.role: server`。Source 使用 `http://managed` 和空 options;业务路径在 Stream 注册。
|
|
4
|
+
|
|
5
|
+
固定前缀为 `/ingest/v1/{workspaceId}/{sourceId}`。下例路由为 `POST /orders/:id`,完整请求路径为 `POST /ingest/v1/{workspaceId}/{sourceId}/orders/A-001`。前缀由部署环境和已认证的工作空间、Source 身份确定,不从请求 body 推断工作空间。Bearer 凭据由 Source 的既有管理合同取得并在调用时注入,不写入脚本。
|
|
6
|
+
|
|
7
|
+
## 保存原生程序草稿
|
|
8
|
+
|
|
9
|
+
使用完整原生 `document`:script 是 `{code,timeoutMs}`,sources 使用 alias。这与 `program` 的 script 字符串形态不同;不能把旧 Flow 脚本直接改名为新字段。
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { createClient, type Tier0Client, type CloudStreamDocument } from '@tier0/sdk-v2';
|
|
13
|
+
|
|
14
|
+
/** Save an HTTPServer route draft using the existing native Stream document contract. */
|
|
15
|
+
export async function createHttpServerStream(
|
|
16
|
+
sourceId: string, name: string, intentKey: string, client: Tier0Client = createClient(),
|
|
17
|
+
) {
|
|
18
|
+
if (!/^[1-9][0-9]*$/.test(sourceId)) throw new Error('Source ID 必须是正十进制字符串');
|
|
19
|
+
const source = await client.source.documents.get({ id: sourceId, view: 'full' });
|
|
20
|
+
if (!('document' in source.data)) throw new Error('需要完整 Source 文档');
|
|
21
|
+
const config = source.data.document.config;
|
|
22
|
+
if (source.data.document.protocol !== 'http-api' || config.role !== 'server') {
|
|
23
|
+
throw new Error('此入站路由示例需要 HTTP API Server Source');
|
|
24
|
+
}
|
|
25
|
+
const document: CloudStreamDocument = {
|
|
26
|
+
name, model: 'program',
|
|
27
|
+
sources: [{ alias: 'incoming', sourceId, configRevision: source.data.revision, resources: [], acquisition: {} }],
|
|
28
|
+
script: { timeoutMs: 5000, code: [
|
|
29
|
+
'module.exports = api => {',
|
|
30
|
+
' const server = api.sources.use("incoming");',
|
|
31
|
+
' api.stream.onRequest({ id: "orders", source: server, method: "POST", path: "/orders/:id" }, ctx => {',
|
|
32
|
+
' return { status: 200, body: { orderId: ctx.request.params.id, payload: ctx.request.body } };',
|
|
33
|
+
' });',
|
|
34
|
+
'};',
|
|
35
|
+
].join('\n') },
|
|
36
|
+
};
|
|
37
|
+
const saved = await client.stream.documents.create({ dryRun: false, document, idempotencyKey: intentKey });
|
|
38
|
+
if (!('id' in saved.data)) throw new Error('创建未返回已保存的草稿');
|
|
39
|
+
return { receipt: saved, current: saved.data };
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
此函数只读取 Source 并保存草稿;发布按[部署](deploy.md)使用回执的 revision,只有实际发布运行后才可调用路由。示例返回请求的参数和 body,不写入 UNS。`ctx.request` 由执行宿主构造;`source` 引用限定 HTTPServer,不能换成 Client 主动请求。
|
|
44
|
+
|
|
45
|
+
普通 handler 返回 `{status,headers?,body?}` 时,宿主等待处理和提交完成才发送响应。需要先回复再处理时,在 HTTP `onRequest` handler 中调用一次 `await ctx.reply({status,headers?,body?})`,随后继续执行;见下例。
|
|
46
|
+
|
|
47
|
+
## 先回复,再写入集合
|
|
48
|
+
|
|
49
|
+
目标环境必须部署支持 `ctx.reply` 的新 Stream 执行器。传入已有且允许当前 Stream 写入的 Collection ID;请求 body 必须符合其 Record schema。Source 仍是同一种 HTTPServer,不增加 Source 模式。
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { createClient, type Tier0Client, type CloudStreamDocument } from '@tier0/sdk-v2';
|
|
53
|
+
|
|
54
|
+
/** Save an HTTPServer draft that acknowledges before its bounded Stream work. */
|
|
55
|
+
export async function createHttpServerEarlyReplyStream(
|
|
56
|
+
sourceId: string, collectionId: string, name: string, intentKey: string,
|
|
57
|
+
client: Tier0Client = createClient(),
|
|
58
|
+
) {
|
|
59
|
+
if (!/^[1-9][0-9]*$/.test(sourceId)) throw new Error('Source ID 必须是正十进制字符串');
|
|
60
|
+
if (!collectionId) throw new Error('需要实际 Collection ID');
|
|
61
|
+
const source = await client.source.documents.get({ id: sourceId, view: 'full' });
|
|
62
|
+
if (!('document' in source.data)) throw new Error('需要完整 Source 文档');
|
|
63
|
+
const config = source.data.document.config;
|
|
64
|
+
if (source.data.document.protocol !== 'http-api' || config.role !== 'server') {
|
|
65
|
+
throw new Error('此入站路由示例需要 HTTP API Server Source');
|
|
66
|
+
}
|
|
67
|
+
const document: CloudStreamDocument = {
|
|
68
|
+
name, model: 'program',
|
|
69
|
+
sources: [{ alias: 'incoming', sourceId, configRevision: source.data.revision, resources: [], acquisition: {} }],
|
|
70
|
+
script: { timeoutMs: 5000, code: [
|
|
71
|
+
'module.exports = api => {',
|
|
72
|
+
' const server = api.sources.use("incoming");',
|
|
73
|
+
' const output = api.uns.use({ collectionId: ' + JSON.stringify(collectionId) + ', access: "write" });',
|
|
74
|
+
' api.stream.onRequest({ id: "orders", source: server, method: "POST", path: "/orders/:id" }, async ctx => {',
|
|
75
|
+
' await ctx.reply({ status: 202, body: { accepted: true, orderId: ctx.request.params.id } });',
|
|
76
|
+
' await output.write({ recordId: ctx.request.params.id, record: ctx.request.body });',
|
|
77
|
+
' });',
|
|
78
|
+
'};',
|
|
79
|
+
].join('\n') },
|
|
80
|
+
};
|
|
81
|
+
const saved = await client.stream.documents.create({ dryRun: false, document, idempotencyKey: intentKey });
|
|
82
|
+
if (!('id' in saved.data)) throw new Error('创建未返回已保存的草稿');
|
|
83
|
+
return { receipt: saved, current: saved.data };
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`ctx.reply` 只用于 HTTP `api.stream.onRequest`,一次执行只回复一次;不要在 `every`、Control 或普通事件 handler 中调用,也不增加 npm App API 或公开 HTTP endpoint。示例先发 202 再写 Record,202 表示已回复接受,不能报告写入或提交成功;失败通过 Stream 运行记录检查,不能再发送第二份响应。
|
|
88
|
+
|
|
89
|
+
回复后的处理仍受 Stream timeout(本例 5000 ms)、并发上限和实时权限约束。客户端断线不会取消已回复后的执行,服务 shutdown 仍可取消。进程崩溃不保证重放;业务若需持久投递、去重或恢复,必须另行设计,不能把 `ctx.reply` 当作持久队列。凭据和权限均沿用现有 Source/Stream 合同。
|
|
@@ -29,6 +29,6 @@ export async function updateStreamDescription(
|
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
代码应用可直接用 documents.update({id, expectedRevision, program, dryRun:false, idempotencyKey}),由服务端编译入口和绑定。编辑完整文档时保留
|
|
32
|
+
代码应用可直接用 documents.update({id, expectedRevision, program, dryRun:false, idempotencyKey}),由服务端编译入口和绑定。编辑完整文档时保留 script.code/timeoutMs,并同步 entries/sources;写入本身执行校验,只有需要提前展示问题时才单独 validate。不要对documents.get 的 summary 做展开更新。
|
|
33
33
|
|
|
34
34
|
响应是完整 CloudStreamDocumentView 或预检结果;把 saved.data 直接传给下一步[发布示例](deploy.md),无需为了 revision 再读取。已有当前完整文档时可省去示例最初的 get。保存草稿不会自动部署,activeDocument 可能仍是旧发布版本。VERSION_CONFLICT 后重新读取、比较并决策;不无条件覆盖。
|
|
@@ -51,7 +51,7 @@ info 返回 name/version/mqttBroker/capabilities,字段可能省略。MQTT bro
|
|
|
51
51
|
| disabled / not_supported / unavailable | 展示具体不可用原因,不强制调用 |
|
|
52
52
|
| readiness_unknown | 保留未知状态,不能按成功处理 |
|
|
53
53
|
|
|
54
|
-
能力目录当前识别 namespace.v2、source
|
|
54
|
+
能力目录当前识别 namespace.v2、source、stream、control.v1、control.invoke.v1。Source/Stream 的 contractVersions 使用 openapi/v2,不要求或返回解释器版本。contractVersions、apiContractVersions、sdkContractVersions 是契约标签,不是 npm 包版本;productVersion 未配置时可缺省。
|
|
55
55
|
|
|
56
56
|
## 定位问题
|
|
57
57
|
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
集合 data 必须包含 name、recordSchema,可选 description、parentId、sampleSchema、indexes、semantics。parentId=null 表示根目录。索引创建后仍需确认状态为 ready。
|
|
6
6
|
|
|
7
|
+
目录、集合和主题使用现有 name 填写易读的真实业务名称,语言遵循用户偏好和已有术语;不要用程序 alias、随机串或 ID 代替名称,也不新增展示名称字段。工业目录优先复用已有 ISA-95 设备层级,只创建业务已知的层级;修改既有名称须有用户授权,不借整理名称改写 ID、alias、权限或移动数据。
|
|
8
|
+
|
|
7
9
|
```ts
|
|
8
10
|
import { createClient, type Tier0Client, type UnsV2Request } from '@tier0/sdk-v2';
|
|
9
11
|
|