@77-zhike/app-sdk 0.2.0-d7b1.2 → 0.2.0-d7b1.20

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.
Files changed (96) hide show
  1. package/README.md +281 -0
  2. package/package.json +17 -3
  3. package/src/cli/api-client.js +4 -0
  4. package/src/cli/browser-auth.js +64 -18
  5. package/src/cli/credential-store.js +21 -0
  6. package/src/cli/development-logs.js +5 -2
  7. package/src/cli/index.js +59 -42
  8. package/src/cli/local-commands.js +6 -3
  9. package/src/cli/logs.js +56 -0
  10. package/src/cli/project-init.js +111 -179
  11. package/src/cli/windows-credential-bridge.ps1 +131 -0
  12. package/src/cli/windows-credential-store.js +112 -0
  13. package/src/client/analysis.d.ts +4 -0
  14. package/src/client/analysis.js +12 -0
  15. package/src/client/destinations.d.ts +25 -0
  16. package/src/client/destinations.js +70 -0
  17. package/src/client/documents.d.ts +21 -0
  18. package/src/client/documents.js +39 -0
  19. package/src/client/external-url.js +9 -0
  20. package/src/client/index.d.ts +225 -3
  21. package/src/client/index.js +75 -3
  22. package/src/client/interactions.d.ts +23 -3
  23. package/src/client/interactions.js +53 -3
  24. package/src/client/points.generated.js +56 -0
  25. package/src/config.js +11 -7
  26. package/src/forms/array-contract.d.ts +32 -0
  27. package/src/forms/array-contract.js +138 -0
  28. package/src/forms/components.generated.js +2 -2
  29. package/src/forms/factory.js +21 -7
  30. package/src/forms/field-array.d.ts +16 -0
  31. package/src/forms/field-array.js +52 -0
  32. package/src/forms/index.d.ts +74 -3
  33. package/src/forms/index.js +2 -0
  34. package/src/forms/options.d.ts +7 -2
  35. package/src/forms/options.js +8 -1
  36. package/src/forms/record-options.d.ts +28 -0
  37. package/src/forms/record-options.js +92 -0
  38. package/src/forms/schema.d.ts +73 -9
  39. package/src/forms/schema.js +128 -0
  40. package/src/forms/state.d.ts +30 -0
  41. package/src/forms/state.js +82 -0
  42. package/src/forms/validation.d.ts +9 -0
  43. package/src/forms/validation.js +81 -0
  44. package/src/forms/values.d.ts +5 -0
  45. package/src/forms/values.js +38 -0
  46. package/src/jsx-dev-runtime.d.ts +2 -0
  47. package/src/jsx-dev-runtime.js +1 -0
  48. package/src/jsx-runtime.d.ts +2 -0
  49. package/src/jsx-runtime.js +1 -0
  50. package/src/server/agents/index.d.ts +62 -0
  51. package/src/server/agents/index.js +40 -0
  52. package/src/server/index.d.ts +60 -5
  53. package/src/server/index.js +43 -0
  54. package/src/server/steps/index.d.ts +71 -0
  55. package/src/server/steps/index.js +15 -0
  56. package/src/server/triggers/index.d.ts +68 -0
  57. package/src/server/triggers/index.js +27 -0
  58. package/src/server/workflow-blocks/index.d.ts +72 -0
  59. package/src/server/workflow-blocks/index.js +32 -0
  60. package/src/toolchain/agents/discovery.js +126 -0
  61. package/src/toolchain/agents/exposure.js +31 -0
  62. package/src/toolchain/agents/manifest.js +84 -0
  63. package/src/toolchain/ai-capabilities.js +69 -0
  64. package/src/toolchain/allowed-packages.json +7 -0
  65. package/src/toolchain/dependency-policy.js +181 -0
  66. package/src/toolchain/index.js +0 -88
  67. package/src/toolchain/real-build.js +135 -27
  68. package/src/toolchain/source-discovery.js +330 -81
  69. package/src/toolchain/workflow-blocks/discovery.js +133 -0
  70. package/src/toolchain/workflow-steps/manifest.js +99 -0
  71. package/src/toolchain/workflow-triggers/manifest.js +47 -0
  72. package/src/ui/components.generated.js +4 -1
  73. package/src/ui/conversation.d.ts +81 -0
  74. package/src/ui/conversation.js +82 -0
  75. package/src/ui/data-table-model.d.ts +14 -0
  76. package/src/ui/data-table-model.js +98 -0
  77. package/src/ui/data-table.d.ts +70 -0
  78. package/src/ui/data-table.js +74 -0
  79. package/src/ui/events.d.ts +10 -0
  80. package/src/ui/factory.js +87 -3
  81. package/src/ui/index.d.ts +351 -8
  82. package/src/ui/index.js +1 -1
  83. package/src/ui/jsx.generated.d.ts +359 -0
  84. package/src/ui/native-dom.d.ts +49 -0
  85. package/src/ui/native-dom.js +44 -0
  86. package/src/ui/scroll-viewport.d.ts +22 -0
  87. package/src/ui/scroll-viewport.js +23 -0
  88. package/src/ui/slider.d.ts +27 -0
  89. package/src/ui/slider.js +26 -0
  90. package/src/ui/style.d.ts +5 -0
  91. package/src/ui/style.generated.js +2 -0
  92. package/src/ui/style.js +15 -0
  93. package/src/ui/text-selection.d.ts +22 -0
  94. package/src/ui/text-selection.js +23 -0
  95. package/src/ui/validation.js +86 -0
  96. package/src/cli/validate-bundle.js +0 -36
@@ -1,17 +1,20 @@
1
+ import { publicComponentNames } from '../ui/components.generated.js';
1
2
  import { createRequire } from "node:module";
2
3
  import { createHash } from "node:crypto";
3
- import { mkdir, rm, writeFile } from "node:fs/promises";
4
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
4
5
  import { dirname, relative, resolve, sep } from "node:path";
5
6
  import { build } from "esbuild";
6
7
  import ts from "typescript";
7
8
 
9
+ import { agentManifest, stripPath } from "./agents/discovery.js";
8
10
  import { serializeWebhookResponse } from "./webhook-response.js";
9
11
 
10
12
  import { TOOLING_VERSION } from "../config.js";
11
13
  import { ZhikeCliError } from "../errors.js";
12
14
  import { readProjectBinding } from "../project-binding.js";
13
15
  import { canonicalJsonBytes } from "./canonical-json.js";
14
- import { discoverAppSources } from "./source-discovery.js";
16
+ import { discoverAppSources, validateImports } from "./source-discovery.js";
17
+ import { createDependencyPolicyPlugin } from "./dependency-policy.js";
15
18
  import { createTransportTar } from "./transport-tar.js";
16
19
 
17
20
  export const APP_BUNDLE_PACKAGE_FORMAT = "zhike.app-bundle.v1";
@@ -32,18 +35,21 @@ export async function buildAppBundle({
32
35
  } = {}) {
33
36
  await readProjectBinding(projectDirectory, expectedEnvironment);
34
37
  const sources = await discoverAppSources(projectDirectory);
38
+ let clientBytes, serverBytes;
39
+ try {
40
+ clientBytes = sources.frontendExtensions.length > 0
41
+ ? await buildRemoteReactClient(projectDirectory, sources, development) : null;
42
+ serverBytes = sources.lifecycleEvents.length > 0 ||
43
+ sources.serverFunctions.length > 0 ||
44
+ sources.webhookHandlers.length > 0 ||
45
+ sources.workflowSteps.length > 0 ||
46
+ sources.workflowTriggers.length > 0 || sources.agents.length > 0 || sources.actions.length > 0
47
+ ? await buildServerArtifact(projectDirectory, sources) : null;
48
+ } catch (error) {
49
+ const policyError = error.errors?.find(item => item.detail instanceof ZhikeCliError)?.detail;
50
+ throw policyError ?? error;
51
+ }
35
52
  await assertTypeScriptProgram(projectDirectory, sources);
36
-
37
- const clientBytes =
38
- sources.frontendExtensions.length > 0
39
- ? await buildRemoteReactClient(projectDirectory, sources, development)
40
- : null;
41
- const serverBytes =
42
- sources.lifecycleEvents.length > 0 ||
43
- sources.serverFunctions.length > 0 ||
44
- sources.webhookHandlers.length > 0
45
- ? await buildServerArtifact(projectDirectory, sources)
46
- : null;
47
53
  const manifest = createManifest({
48
54
  clientBytes,
49
55
  serverBytes,
@@ -90,13 +96,15 @@ export async function buildAppBundle({
90
96
  });
91
97
  }
92
98
 
93
- async function assertTypeScriptProgram(projectDirectory, { sourceFiles, serverFunctions }) {
99
+ async function assertTypeScriptProgram(projectDirectory, { sourceFiles, serverFunctions, agents, actions }) {
94
100
  const paths = {
95
101
  "@77-zhike/app-sdk": [resolve(SDK_SOURCE_ROOT, "index.d.ts")],
96
102
  "@77-zhike/app-sdk/client": [resolve(SDK_SOURCE_ROOT, "client", "index.d.ts")],
97
103
  "@77-zhike/app-sdk/server": [resolve(SDK_SOURCE_ROOT, "server", "index.d.ts")],
98
104
  "@77-zhike/app-sdk/ui": [resolve(SDK_SOURCE_ROOT, "ui", "index.d.ts")],
99
105
  "@77-zhike/app-sdk/forms": [resolve(SDK_SOURCE_ROOT, "forms", "index.d.ts")],
106
+ "@77-zhike/app-sdk/jsx-runtime": [resolve(SDK_SOURCE_ROOT, "jsx-runtime.d.ts")],
107
+ "@77-zhike/app-sdk/jsx-dev-runtime": [resolve(SDK_SOURCE_ROOT, "jsx-dev-runtime.d.ts")],
100
108
  "react": [resolve(dirname(createRequire(import.meta.url).resolve("@types/react/package.json")), "index.d.ts")],
101
109
  "react/jsx-runtime": [resolve(dirname(createRequire(import.meta.url).resolve("@types/react/package.json")), "jsx-runtime.d.ts")],
102
110
  "react/jsx-dev-runtime": [resolve(dirname(createRequire(import.meta.url).resolve("@types/react/package.json")), "jsx-dev-runtime.d.ts")],
@@ -104,6 +112,7 @@ async function assertTypeScriptProgram(projectDirectory, { sourceFiles, serverFu
104
112
  const options = {
105
113
  baseUrl: projectDirectory,
106
114
  jsx: ts.JsxEmit.ReactJSX,
115
+ jsxImportSource: "@77-zhike/app-sdk",
107
116
  lib: ["lib.es2022.d.ts", "lib.dom.d.ts"],
108
117
  module: ts.ModuleKind.ESNext,
109
118
  moduleResolution: ts.ModuleResolutionKind.Bundler,
@@ -117,11 +126,16 @@ async function assertTypeScriptProgram(projectDirectory, { sourceFiles, serverFu
117
126
  const rootNames = sourceFiles.map((path) => resolve(projectDirectory, "src", path));
118
127
  // 仅供 TypeScript 检查,不落盘、不进 Artifact;复用原包装的参数/结果合同。
119
128
  const contractPath = resolve(projectDirectory, ".zhike/server-function-contracts.ts");
129
+ const handlers = [
130
+ ...serverFunctions.map(item => ({ ...item, member: '' })),
131
+ ...agents.flatMap(agent => agent.tools.map(item => ({ ...item, member: '.execute' }))),
132
+ ...actions.map(item => ({...item, member: '.execute'})),
133
+ ];
120
134
  const contractSource = [
121
135
  'import type { ServerFunction } from "@77-zhike/app-sdk/server";',
122
- ...serverFunctions.flatMap(({ relativePath }, index) => [
136
+ ...handlers.flatMap(({ relativePath, member }, index) => [
123
137
  `import handler${index} from ${JSON.stringify(`../src/${relativePath.slice(0, -3)}`)};`,
124
- `type Contract${index} = ServerFunction<Parameters<typeof handler${index}>, Awaited<ReturnType<typeof handler${index}>>>;`,
138
+ `type Contract${index} = ServerFunction<Parameters<typeof handler${index}${member}>, Awaited<ReturnType<typeof handler${index}${member}>>>;`,
125
139
  ]),
126
140
  ].join("\n");
127
141
  const host = ts.createCompilerHost(options);
@@ -130,14 +144,14 @@ async function assertTypeScriptProgram(projectDirectory, { sourceFiles, serverFu
130
144
  resolve(fileName) === contractPath
131
145
  ? ts.createSourceFile(fileName, contractSource, languageVersion, true)
132
146
  : getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile);
133
- if (serverFunctions.length) rootNames.push(contractPath);
147
+ if (handlers.length) rootNames.push(contractPath);
134
148
  const program = ts.createProgram({ rootNames, options, host });
135
149
  const diagnostics = ts.getPreEmitDiagnostics(program);
136
150
  if (diagnostics.length === 0) return;
137
151
  const diagnostic = diagnostics[0];
138
- const contractLine = diagnostic.file?.fileName === contractPath
152
+ const contractLine = diagnostic.file && resolve(diagnostic.file.fileName) === contractPath
139
153
  ? diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start || 0).line : -1;
140
- const contractOwner = contractLine > 0 ? serverFunctions[Math.floor((contractLine - 1) / 2)] : null;
154
+ const contractOwner = contractLine > 0 ? handlers[Math.floor((contractLine - 1) / 2)] : null;
141
155
  const location = contractOwner ? `src/${contractOwner.relativePath}(参数/返回类型)` : diagnostic.file
142
156
  ? `${relative(projectDirectory, diagnostic.file.fileName).split(sep).join("/")}:${
143
157
  diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start || 0).line + 1
@@ -156,13 +170,16 @@ async function buildRemoteReactClient(projectDirectory, sources, development) {
156
170
  `${JSON.stringify(item.key)}: extension${i}`).join(",");
157
171
  const result = await build({
158
172
  absWorkingDir: projectDirectory, bundle: true, charset: "utf8", format: "cjs",
159
- legalComments: "none", logLevel: "silent", minify: !development, platform: "browser",
173
+ legalComments: "none", logLevel: "silent", minify: true, platform: "browser",
160
174
  external: ["@77-zhike/app-sdk/runtime", "react", "react/jsx-runtime", "react/jsx-dev-runtime", "@77-zhike/app-sdk/client", "@77-zhike/app-sdk/ui", "@77-zhike/app-sdk/forms"],
161
- plugins: [createSdkBuildPlugin(projectDirectory, sources, "remote-client")],
175
+ plugins: [createSdkBuildPlugin(projectDirectory, sources, "remote-client"),
176
+ await createDependencyPolicyPlugin(projectDirectory, "client", validateImports)],
162
177
  sourcemap: development ? "inline" : false,
163
178
  banner: { js: 'export function createClientApp(platformModules) {\nconst module = { exports: {} };\nconst exports = module.exports;\nconst require = name => { if (!Object.hasOwn(platformModules, name)) throw new Error("runtime_module_unavailable"); return platformModules[name]; };\nconst Promise = require("@77-zhike/app-sdk/runtime").Promise;' },
164
179
  footer: { js: 'return module.exports;\n}' },
165
- jsx: "automatic", jsxDev: development,
180
+ // dev 只增加源码映射;编译语义与正式发布一致,不能依赖宿主的 React 开发构建。
181
+ jsx: "automatic", jsxDev: false, jsxImportSource: "@77-zhike/app-sdk",
182
+ define: { "process.env.NODE_ENV": '"production"' },
166
183
  stdin: { contents: `${imports}\nexport const extensions = Object.freeze({${entries}});`, loader: "tsx", resolveDir: projectDirectory, sourcefile: "zhike-client-entry.tsx" },
167
184
  // 原生 await 不经过 Promise.then;降级后的 helper 必须使用工厂内受管 Promise。
168
185
  target: "es2022", supported: { "async-await": false, "async-generator": false }, treeShaking: true, write: false,
@@ -178,7 +195,12 @@ async function buildServerArtifact(projectDirectory, sources) {
178
195
  const hasConnectionLifecycle = sources.lifecycleEvents.some(
179
196
  ({ event }) => event === "connection-added" || event === "connection-removed",
180
197
  );
198
+ const tools = [...sources.agents.flatMap(agent => agent.tools), ...sources.actions];
199
+ const toolImports = tools.map((tool, index) => `import agentTool${index} from "./src/${tool.relativePath}";`);
200
+ const agentToolDeclarations = `const agentTools = new Map([${tools.map((tool, index) => `[${JSON.stringify(tool.entry)}, agentTool${index}]`).join(",")}]);`;
201
+ const agentToolDispatch = ` if (request.kind === "agent_tool" || request.kind === "app_action") { const prefix = request.kind === "app_action" ? "action/" : "agent-tool/"; const tool = typeof request.entryKey === "string" && request.entryKey.startsWith(prefix) ? agentTools.get(request.entryKey) : null; if (!tool) throw new Error("runtime_agent_tool_not_found"); return tool.execute(Object.freeze({ ...request.input })); }\n`;
181
202
  const imports = [
203
+ ...toolImports,
182
204
  ...(hasConnectionLifecycle
183
205
  ? ['import { getWorkspaceConnection as getLifecycleConnection } from "@77-zhike/app-sdk/server";']
184
206
  : []),
@@ -194,6 +216,14 @@ async function buildServerArtifact(projectDirectory, sources) {
194
216
  (definition, index) =>
195
217
  `import webhookHandler${index} from "./src/${definition.relativePath}";`,
196
218
  ),
219
+ ...sources.workflowSteps.map(
220
+ (definition, index) =>
221
+ `import workflowStep${index} from "./src/${definition.handlerPaths.execute}";`,
222
+ ),
223
+ ...sources.workflowTriggers.map(
224
+ (definition, index) => Object.entries(definition.handlerPaths).map(([name, path]) =>
225
+ `import workflowTrigger${index}_${name} from "./src/${path}";`).join("\n"),
226
+ ),
197
227
  ].join("\n");
198
228
  const lifecycleEntries = sources.lifecycleEvents
199
229
  .map(
@@ -213,7 +243,19 @@ async function buildServerArtifact(projectDirectory, sources) {
213
243
  `[${JSON.stringify(definition.key)}, webhookHandler${index}]`,
214
244
  )
215
245
  .join(",\n");
216
- const stdin = `${imports}\n${serializeWebhookResponse.toString()}\nconst lifecycleEvents = new Map([${lifecycleEntries}]);\nconst serverFunctions = new Map([${serverEntries}]);\nconst webhookHandlers = new Map([${webhookEntries}]);\nexport async function dispatchServerInvocation(request) {\n if (!request || typeof request !== "object") throw new Error("runtime_invocation_invalid");\n if (request.kind === "lifecycle_event") {\n const definition = lifecycleEvents.get(request.entryKey);\n if (!definition) throw new Error("runtime_lifecycle_event_not_found");\n const params = definition.event === "app-installed" || definition.event === "app-uninstalled" ? request.context : Object.freeze({ connection: getLifecycleConnection() });\n await definition.handler(params);\n return null;\n }\n if (request.kind === "server_function") {\n const handler = serverFunctions.get(request.entryKey);\n if (!handler) throw new Error("runtime_server_function_not_found");\n return handler(...request.args);\n }\n if (request.kind === "webhook_handler") {\n const handler = webhookHandlers.get(request.entryKey);\n if (!handler) throw new Error("runtime_webhook_handler_not_found");\n const input = request.webRequest;\n const webRequest = new Request(input.url, { method: input.method, headers: input.headers, body: ["GET", "HEAD"].includes(input.method) ? undefined : input.body });\n const response = await handler(webRequest, Object.freeze({ ...request.context }));\n if (!(response instanceof Response)) throw new Error("runtime_webhook_response_invalid");\n return serializeWebhookResponse(response);\n }\n throw new Error("runtime_invocation_kind_invalid");\n}\n`;
246
+ const workflowEntries = sources.workflowSteps
247
+ .map((definition, index) => `[${JSON.stringify(definition.entry)}, workflowStep${index}]`)
248
+ .join(",\n");
249
+ const triggerEntries = sources.workflowTriggers.flatMap((definition, index) =>
250
+ Object.entries(definition.entries).map(([name, entry]) =>
251
+ `[${JSON.stringify(entry)}, workflowTrigger${index}_${name}]`)).join(",\n");
252
+ const triggerDeclarations = sources.workflowTriggers.length > 0
253
+ ? `const workflowTriggers = new Map([${triggerEntries}]);\n`
254
+ : "";
255
+ const triggerDispatch = sources.workflowTriggers.length > 0
256
+ ? ` if (request.kind === "workflow_trigger") {\n const handler = workflowTriggers.get(request.entryKey);\n if (!handler) throw new Error("runtime_workflow_trigger_not_found");\n const result = await handler(Object.freeze({ ...request.params }));\n return request.entryKey.endsWith("/deactivate") ? null : result;\n }\n`
257
+ : "";
258
+ const stdin = `${imports}\n${serializeWebhookResponse.toString()}\nconst lifecycleEvents = new Map([${lifecycleEntries}]);\nconst serverFunctions = new Map([${serverEntries}]);\nconst webhookHandlers = new Map([${webhookEntries}]);\nconst workflowSteps = new Map([${workflowEntries}]);\n${triggerDeclarations}${agentToolDeclarations}\nexport async function dispatchServerInvocation(request) {\n if (!request || typeof request !== "object") throw new Error("runtime_invocation_invalid");\n if (request.kind === "lifecycle_event") {\n const definition = lifecycleEvents.get(request.entryKey);\n if (!definition) throw new Error("runtime_lifecycle_event_not_found");\n const params = definition.event === "app-installed" || definition.event === "app-uninstalled" ? request.context : Object.freeze({ connection: getLifecycleConnection() });\n await definition.handler(params);\n return null;\n }\n if (request.kind === "server_function") {\n const handler = serverFunctions.get(request.entryKey);\n if (!handler) throw new Error("runtime_server_function_not_found");\n return handler(...request.args);\n }\n if (request.kind === "workflow_step") {\n const handler = workflowSteps.get(request.entryKey);\n if (!handler) throw new Error("runtime_workflow_step_not_found");\n return handler(Object.freeze({ config: request.config }));\n }\n${triggerDispatch}${agentToolDispatch} if (request.kind === "webhook_handler") {\n const handler = webhookHandlers.get(request.entryKey);\n if (!handler) throw new Error("runtime_webhook_handler_not_found");\n const input = request.webRequest;\n const webRequest = new Request(input.url, { method: input.method, headers: input.headers, body: ["GET", "HEAD"].includes(input.method) ? undefined : input.body });\n const response = await handler(webRequest, Object.freeze({ ...request.context }));\n if (!(response instanceof Response)) throw new Error("runtime_webhook_response_invalid");\n return serializeWebhookResponse(response);\n }\n throw new Error("runtime_invocation_kind_invalid");\n}\n`;
217
259
  const result = await build({
218
260
  absWorkingDir: projectDirectory,
219
261
  bundle: true,
@@ -223,7 +265,8 @@ async function buildServerArtifact(projectDirectory, sources) {
223
265
  logLevel: "silent",
224
266
  minify: true,
225
267
  platform: "browser",
226
- plugins: [createSdkBuildPlugin(projectDirectory, sources, "server")],
268
+ plugins: [createSdkBuildPlugin(projectDirectory, sources, "server"),
269
+ await createDependencyPolicyPlugin(projectDirectory, "server", validateImports)],
227
270
  sourcemap: false,
228
271
  stdin: { contents: stdin, loader: "ts", resolveDir: projectDirectory, sourcefile: "zhike-server-entry.ts" },
229
272
  target: "es2022",
@@ -243,6 +286,38 @@ function createSdkBuildPlugin(projectDirectory, sources, target) {
243
286
  return {
244
287
  name: "zhike-app-sdk-runtime",
245
288
  setup(buildApi) {
289
+ buildApi.onResolve({ filter: /^@77-zhike\/app-sdk\/jsx(?:-dev)?-runtime$/ }, args => ({ path: args.path, namespace: "zhike-jsx" }));
290
+ buildApi.onLoad({ filter: /.*/, namespace: "zhike-jsx" }, args => ({
291
+ contents: args.path.endsWith("jsx-dev-runtime")
292
+ ? 'export { jsxDEV, Fragment } from "react/jsx-dev-runtime";'
293
+ : 'export { jsx, jsxs, Fragment } from "react/jsx-runtime";', loader: "js",
294
+ }));
295
+ if (target === 'remote-client') {
296
+ buildApi.onResolve({ filter: /\.css\?inline$/ }, args => args.path.startsWith('.')
297
+ ? { path: resolve(args.resolveDir, args.path.slice(0, -7)), namespace: 'zhike-native-css' }
298
+ : { errors: [{ text: 'NativeDOM 样式必须使用本地相对路径' }] });
299
+ buildApi.onLoad({ filter: /.*/, namespace: 'zhike-native-css' }, async args => ({
300
+ contents: await readFile(args.path, 'utf8'), loader: 'text', watchFiles: [args.path],
301
+ }));
302
+ }
303
+ if (target === "remote-client") {
304
+ // 新构建固化 authoring 默认值;Guest 仍为不可变旧 Bundle 提供兼容模块。
305
+ buildApi.onResolve({ filter: /^@77-zhike\/app-sdk\/ui$/ }, args => args.namespace === "zhike-ui-authoring"
306
+ ? { path: args.path, external: true }
307
+ : { path: args.path, namespace: "zhike-ui-authoring" });
308
+ buildApi.onLoad({ filter: /.*/, namespace: "zhike-ui-authoring" }, () => ({ loader: "js", contents: `
309
+ import * as ui from "@77-zhike/app-sdk/ui";
310
+ import { createElement } from "react";
311
+ export const TextBlock = props => createElement(ui.TextBlock, { ...props, align: props.align ?? "left" });
312
+ export const Badge = props => {
313
+ if (props.tone !== undefined) throw new Error("Badge.tone 已移除,请使用 StatusBadge");
314
+ return createElement(ui.Badge, props);
315
+ };
316
+ export const TableCell = Object.defineProperties(props => createElement(ui.TableCell, props), { Avatar: { get: () => ui.Avatar }, Badge: { value: Badge } });
317
+ export const Table = Object.defineProperties(props => createElement(ui.Table, props), { Header: { get: () => ui.Table.Header }, HeaderCell: { get: () => ui.Table.HeaderCell }, Body: { get: () => ui.Table.Body }, Row: { get: () => ui.Table.Row }, Cell: { value: TableCell } });
318
+ export const { ${[...publicComponentNames.filter(name => !["TextBlock", "Badge", "Table", "TableCell"].includes(name)), "Typography", "Widget", "useConversation"].join(", ")} } = ui;
319
+ ` }));
320
+ }
246
321
  if (target !== "remote-client") buildApi.onResolve({ filter: /^@77-zhike\/app-sdk\/(client|server)$/ }, (args) => ({
247
322
  external: target === "server" && args.path.endsWith("/server"),
248
323
  path: args.path,
@@ -252,7 +327,10 @@ function createSdkBuildPlugin(projectDirectory, sources, target) {
252
327
  }));
253
328
  buildApi.onLoad({ filter: /.*/, namespace: "zhike-sdk-runtime" }, (args) => {
254
329
  if (args.path.endsWith("/client")) {
255
- return { contents: "export const defineFrontendExtension = (definition) => definition;", loader: "js" };
330
+ return {
331
+ contents: "export const defineFrontendExtension = (definition) => definition; export const definePage = (definition) => ({ ...definition, type: 'page' }); export const defineToolRenderer = (definition) => ({ ...definition, type: 'tool-renderer' });",
332
+ loader: "js",
333
+ };
256
334
  }
257
335
  return { contents: "export const defineLifecycleEvent = (definition) => definition; export const defineServerFunction = (handler) => handler; export const defineWebhookHandler = (definition) => definition; export const createWebhookHandler = async () => { throw new Error('webhook_handler_runtime_unavailable'); }; export const listWebhookHandlers = async () => { throw new Error('webhook_handler_runtime_unavailable'); }; export const deleteWebhookHandler = async () => { throw new Error('webhook_handler_runtime_unavailable'); };", loader: "js" };
258
336
  });
@@ -291,7 +369,7 @@ function createManifest({ clientBytes, serverBytes, sources }) {
291
369
  sdkAbiVersion: "2",
292
370
  toolchainVersion: TOOLING_VERSION,
293
371
  ...(sources.frontendExtensions.length > 0
294
- ? { clientRuntime: { runtimeVersion: "remote-react.v2" } } : {}),
372
+ ? { clientRuntime: { runtimeVersion: "remote-react.v3" } } : {}),
295
373
  artifacts: {
296
374
  ...(clientBytes
297
375
  ? {
@@ -314,7 +392,7 @@ function createManifest({ clientBytes, serverBytes, sources }) {
314
392
  }
315
393
  : {}),
316
394
  },
317
- frontendExtensions: sources.frontendExtensions.map(({ key, type, entry, label, icon, objects }) => ({
395
+ frontendExtensions: sources.frontendExtensions.map(({ key, type, entry, label, icon, objects, name, order, variant, showInNavigation, showHeader, fillHeight }) => ({
318
396
  artifact: "client",
319
397
  entry,
320
398
  key,
@@ -322,12 +400,22 @@ function createManifest({ clientBytes, serverBytes, sources }) {
322
400
  ...(label === undefined ? {} : { label }),
323
401
  ...(icon === undefined ? {} : { icon }),
324
402
  ...(objects === undefined ? {} : { objects }),
403
+ ...(name === undefined ? {} : { name }),
404
+ ...(order === undefined ? {} : { order }),
405
+ ...(variant === undefined ? {} : { variant }),
406
+ ...(showInNavigation === undefined ? {} : { showInNavigation }),
407
+ ...(showHeader === undefined ? {} : { showHeader }),
408
+ ...(fillHeight === undefined ? {} : { fillHeight }),
325
409
  })),
326
410
  lifecycleEvents: sources.lifecycleEvents.map(({ event, key }) => ({
327
411
  artifact: "server",
328
412
  event,
329
413
  key,
330
414
  })),
415
+ ...(sources.agents.length ? { agents: sources.agents.map(agentManifest) } : {}),
416
+ ...((sources.actions.length || sources.dataCapabilities.length || sources.agents.some(agent => agent.aiExposure || agent.actionRefs || agent.tools.some(tool => tool.aiExposure))) ? {aiCapabilitiesVersion:'1'} : {}),
417
+ ...(sources.actions.length ? {actions:sources.actions.map(stripPath)} : {}),
418
+ ...(sources.dataCapabilities.length ? {dataCapabilities:sources.dataCapabilities.map(stripPath)} : {}),
331
419
  serverFunctions: sources.serverFunctions.map(({ key }) => ({
332
420
  artifact: "server",
333
421
  key,
@@ -337,6 +425,20 @@ function createManifest({ clientBytes, serverBytes, sources }) {
337
425
  key,
338
426
  methods,
339
427
  })),
428
+ ...(sources.workflowSteps.length > 0 ? { workflowSteps: sources.workflowSteps.map(
429
+ ({ key, title, description, connection, configSchema, outcomes, entry }) => ({
430
+ artifact: "server", key, entry, title,
431
+ ...(description === undefined ? {} : { description }),
432
+ connection, configSchema, outcomes,
433
+ }),
434
+ ) } : {}),
435
+ ...(sources.workflowTriggers.length > 0 ? { workflowTriggers: sources.workflowTriggers.map(
436
+ ({ key, title, description, connection, requestHeaders, configSchema, outcomes, entries }) => ({
437
+ artifact: "server", key, entries, title,
438
+ ...(description === undefined ? {} : { description }),
439
+ connection, requestHeaders, configSchema, outcomes,
440
+ }),
441
+ ) } : {}),
340
442
  };
341
443
  }
342
444
 
@@ -344,8 +446,14 @@ function createServerDefinitionsDigest(sources) {
344
446
  return sha256(
345
447
  canonicalJsonBytes({
346
448
  lifecycleEvents: sources.lifecycleEvents.map(({ event, key }) => ({ event, key })),
449
+ ...(sources.agents.length ? { agents: sources.agents.map(({ key, tools }) => ({
450
+ key, tools: tools.map(({ key: toolKey, entry }) => ({ key: toolKey, entry })),
451
+ })) } : {}),
452
+ ...(sources.actions.length ? {actions:sources.actions.map(({key,entry})=>({key,entry}))} : {}),
347
453
  serverFunctions: sources.serverFunctions.map(({ key }) => ({ key })),
348
454
  webhookHandlers: sources.webhookHandlers.map(({ key, methods }) => ({ key, methods })),
455
+ ...(sources.workflowSteps.length > 0 ? { workflowSteps: sources.workflowSteps.map(({ key, entry }) => ({ key, entry })) } : {}),
456
+ ...(sources.workflowTriggers.length > 0 ? { workflowTriggers: sources.workflowTriggers.map(({ key, entries }) => ({ key, entries })) } : {}),
349
457
  }),
350
458
  );
351
459
  }