@lark-base-open/base-database-cli 1.3.9 → 1.4.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/CHANGELOG.md +75 -0
- package/README.md +119 -15
- package/THIRD_PARTY_LICENSES.json +1662 -0
- package/config-schema.json +64 -3
- package/data/template-hmr-scripts.json +9 -0
- package/data/template-index-html/17e698eef687ece8f1628e3e2c4801da5e7233faa87076b3424acde8df57b825.html +24 -0
- package/dist/agent-tools-guide.d.ts +29 -0
- package/dist/agent-tools-guide.d.ts.map +1 -0
- package/dist/agent-tools-guide.js +169 -0
- package/dist/agent-tools-guide.js.map +1 -0
- package/dist/agent-tools-skill.d.ts +16 -0
- package/dist/agent-tools-skill.d.ts.map +1 -0
- package/dist/agent-tools-skill.js +183 -0
- package/dist/agent-tools-skill.js.map +1 -0
- package/dist/agent-tools.d.ts +20 -0
- package/dist/agent-tools.d.ts.map +1 -0
- package/dist/agent-tools.js +137 -0
- package/dist/agent-tools.js.map +1 -0
- package/dist/ai-functions.d.ts +10 -0
- package/dist/ai-functions.d.ts.map +1 -1
- package/dist/ai-functions.js +264 -212
- package/dist/ai-functions.js.map +1 -1
- package/dist/app-builder/core.d.mts +37 -0
- package/dist/app-builder/core.d.mts.map +1 -0
- package/dist/app-builder/core.mjs +673 -0
- package/dist/app-builder/core.mjs.map +1 -0
- package/dist/app-builder/package.json +1 -0
- package/dist/app-builder/schema.d.ts +4 -0
- package/dist/app-builder/schema.d.ts.map +1 -0
- package/dist/app-builder/schema.js +72 -0
- package/dist/app-builder/schema.js.map +1 -0
- package/dist/archive.d.ts +22 -2
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +311 -74
- package/dist/archive.js.map +1 -1
- package/dist/auth.d.ts +2 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +13 -8
- package/dist/auth.js.map +1 -1
- package/dist/build-server.d.ts +25 -0
- package/dist/build-server.d.ts.map +1 -0
- package/dist/build-server.js +32 -0
- package/dist/build-server.js.map +1 -0
- package/dist/business-update-notice.d.ts +37 -0
- package/dist/business-update-notice.d.ts.map +1 -0
- package/dist/business-update-notice.js +24 -0
- package/dist/business-update-notice.js.map +1 -0
- package/dist/check.d.ts +11 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +85 -15
- package/dist/check.js.map +1 -1
- package/dist/cloud-build-diagnostic.d.ts +9 -0
- package/dist/cloud-build-diagnostic.d.ts.map +1 -0
- package/dist/cloud-build-diagnostic.js +65 -0
- package/dist/cloud-build-diagnostic.js.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +41 -11
- package/dist/config.js.map +1 -1
- package/dist/contract-extractor.d.ts +21 -0
- package/dist/contract-extractor.d.ts.map +1 -0
- package/dist/contract-extractor.js +260 -0
- package/dist/contract-extractor.js.map +1 -0
- package/dist/contract-worker.d.ts +12 -0
- package/dist/contract-worker.d.ts.map +1 -0
- package/dist/contract-worker.js +90 -0
- package/dist/contract-worker.js.map +1 -0
- package/dist/dependency-policy.d.ts.map +1 -1
- package/dist/dependency-policy.js +6 -1
- package/dist/dependency-policy.js.map +1 -1
- package/dist/deploy.d.ts +20 -1
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +181 -52
- package/dist/deploy.js.map +1 -1
- package/dist/dev-bootstrap.d.ts +5 -0
- package/dist/dev-bootstrap.d.ts.map +1 -0
- package/dist/dev-bootstrap.js +185 -0
- package/dist/dev-bootstrap.js.map +1 -0
- package/dist/dev-events.d.ts +24 -0
- package/dist/dev-events.d.ts.map +1 -0
- package/dist/dev-events.js +67 -0
- package/dist/dev-events.js.map +1 -0
- package/dist/dev-pairing.d.ts +4 -0
- package/dist/dev-pairing.d.ts.map +1 -0
- package/dist/dev-pairing.js +54 -0
- package/dist/dev-pairing.js.map +1 -0
- package/dist/dev-url.d.ts +4 -0
- package/dist/dev-url.d.ts.map +1 -0
- package/dist/dev-url.js +29 -0
- package/dist/dev-url.js.map +1 -0
- package/dist/dev-vite.d.ts +9 -0
- package/dist/dev-vite.d.ts.map +1 -0
- package/dist/dev-vite.js +237 -0
- package/dist/dev-vite.js.map +1 -0
- package/dist/dev-watch.d.ts +33 -0
- package/dist/dev-watch.d.ts.map +1 -0
- package/dist/dev-watch.js +246 -0
- package/dist/dev-watch.js.map +1 -0
- package/dist/dev.d.ts +24 -0
- package/dist/dev.d.ts.map +1 -0
- package/dist/dev.js +244 -0
- package/dist/dev.js.map +1 -0
- package/dist/development-pairing.generated.d.ts +12 -0
- package/dist/development-pairing.generated.d.ts.map +1 -0
- package/dist/development-pairing.generated.js +13 -0
- package/dist/development-pairing.generated.js.map +1 -0
- package/dist/diagnostic-platforms.d.ts +3 -0
- package/dist/diagnostic-platforms.d.ts.map +1 -0
- package/dist/diagnostic-platforms.js +3 -0
- package/dist/diagnostic-platforms.js.map +1 -0
- package/dist/diagnostic-schema.d.ts +151 -0
- package/dist/diagnostic-schema.d.ts.map +1 -0
- package/dist/diagnostic-schema.js +129 -0
- package/dist/diagnostic-schema.js.map +1 -0
- package/dist/diagnostics.d.ts +78 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +333 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/distribution-commands.d.ts.map +1 -1
- package/dist/distribution-commands.js +6 -2
- package/dist/distribution-commands.js.map +1 -1
- package/dist/distribution-npm.d.ts +2 -2
- package/dist/distribution-npm.d.ts.map +1 -1
- package/dist/distribution-npm.js +14 -1
- package/dist/distribution-npm.js.map +1 -1
- package/dist/environment-variables.d.ts.map +1 -1
- package/dist/environment-variables.js +2 -1
- package/dist/environment-variables.js.map +1 -1
- package/dist/function-contracts.d.ts +40 -0
- package/dist/function-contracts.d.ts.map +1 -0
- package/dist/function-contracts.js +357 -0
- package/dist/function-contracts.js.map +1 -0
- package/dist/function-description.d.ts +11 -4
- package/dist/function-description.d.ts.map +1 -1
- package/dist/function-description.js +22 -12
- package/dist/function-description.js.map +1 -1
- package/dist/function-input-types.d.ts +6 -4
- package/dist/function-input-types.d.ts.map +1 -1
- package/dist/function-input-types.js +97 -135
- package/dist/function-input-types.js.map +1 -1
- package/dist/function-params.d.ts +53 -0
- package/dist/function-params.d.ts.map +1 -0
- package/dist/function-params.js +184 -0
- package/dist/function-params.js.map +1 -0
- package/dist/function-schema.d.ts +14 -0
- package/dist/function-schema.d.ts.map +1 -1
- package/dist/function-schema.js +99 -9
- package/dist/function-schema.js.map +1 -1
- package/dist/init-journal.d.ts +1 -0
- package/dist/init-journal.d.ts.map +1 -1
- package/dist/init-journal.js +21 -7
- package/dist/init-journal.js.map +1 -1
- package/dist/init.d.ts +1 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +55 -5
- package/dist/init.js.map +1 -1
- package/dist/lark-cli-error.d.ts +13 -2
- package/dist/lark-cli-error.d.ts.map +1 -1
- package/dist/lark-cli-error.js +54 -13
- package/dist/lark-cli-error.js.map +1 -1
- package/dist/lark-cli-passthrough.d.ts +9 -0
- package/dist/lark-cli-passthrough.d.ts.map +1 -0
- package/dist/lark-cli-passthrough.js +101 -0
- package/dist/lark-cli-passthrough.js.map +1 -0
- package/dist/lark-cli.d.ts +5 -2
- package/dist/lark-cli.d.ts.map +1 -1
- package/dist/lark-cli.js +96 -29
- package/dist/lark-cli.js.map +1 -1
- package/dist/legacy-function-input-renderer.d.ts +6 -0
- package/dist/legacy-function-input-renderer.d.ts.map +1 -0
- package/dist/legacy-function-input-renderer.js +175 -0
- package/dist/legacy-function-input-renderer.js.map +1 -0
- package/dist/link-guard-check.d.ts +12 -0
- package/dist/link-guard-check.d.ts.map +1 -0
- package/dist/link-guard-check.js +138 -0
- package/dist/link-guard-check.js.map +1 -0
- package/dist/managed-agent-docs.d.ts +14 -0
- package/dist/managed-agent-docs.d.ts.map +1 -0
- package/dist/managed-agent-docs.js +172 -0
- package/dist/managed-agent-docs.js.map +1 -0
- package/dist/mutation-schema.d.ts +6 -0
- package/dist/mutation-schema.d.ts.map +1 -1
- package/dist/mutation-schema.js +6 -0
- package/dist/mutation-schema.js.map +1 -1
- package/dist/mutation.d.ts +4 -1
- package/dist/mutation.d.ts.map +1 -1
- package/dist/mutation.js +17 -2
- package/dist/mutation.js.map +1 -1
- package/dist/npm.d.ts +1 -0
- package/dist/npm.d.ts.map +1 -1
- package/dist/npm.js +28 -0
- package/dist/npm.js.map +1 -1
- package/dist/preview-receipt.d.ts +0 -2
- package/dist/preview-receipt.d.ts.map +1 -1
- package/dist/preview-receipt.js +0 -28
- package/dist/preview-receipt.js.map +1 -1
- package/dist/process.d.ts +1 -0
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +25 -0
- package/dist/process.js.map +1 -1
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +239 -26
- package/dist/program.js.map +1 -1
- package/dist/project-build.d.ts +2 -1
- package/dist/project-build.d.ts.map +1 -1
- package/dist/project-build.js +53 -16
- package/dist/project-build.js.map +1 -1
- package/dist/project-capabilities.d.ts +8 -0
- package/dist/project-capabilities.d.ts.map +1 -0
- package/dist/project-capabilities.js +47 -0
- package/dist/project-capabilities.js.map +1 -0
- package/dist/project-context.d.ts +66 -0
- package/dist/project-context.d.ts.map +1 -1
- package/dist/project-context.js +68 -6
- package/dist/project-context.js.map +1 -1
- package/dist/project-layout.d.ts +11 -0
- package/dist/project-layout.d.ts.map +1 -1
- package/dist/project-layout.js +63 -1
- package/dist/project-layout.js.map +1 -1
- package/dist/public-exports.d.ts +5 -0
- package/dist/public-exports.d.ts.map +1 -0
- package/dist/public-exports.js +76 -0
- package/dist/public-exports.js.map +1 -0
- package/dist/public-sql-error.d.ts +3 -1
- package/dist/public-sql-error.d.ts.map +1 -1
- package/dist/public-sql-error.js +5 -2
- package/dist/public-sql-error.js.map +1 -1
- package/dist/pull.d.ts +1 -0
- package/dist/pull.d.ts.map +1 -1
- package/dist/pull.js +103 -20
- package/dist/pull.js.map +1 -1
- package/dist/sdk-codegen.d.ts +16 -0
- package/dist/sdk-codegen.d.ts.map +1 -0
- package/dist/sdk-codegen.js +181 -0
- package/dist/sdk-codegen.js.map +1 -0
- package/dist/sdk-install.d.ts +4 -0
- package/dist/sdk-install.d.ts.map +1 -0
- package/dist/sdk-install.js +33 -0
- package/dist/sdk-install.js.map +1 -0
- package/dist/skill-frontmatter.d.ts +5 -0
- package/dist/skill-frontmatter.d.ts.map +1 -0
- package/dist/skill-frontmatter.js +28 -0
- package/dist/skill-frontmatter.js.map +1 -0
- package/dist/skill.d.ts +2 -1
- package/dist/skill.d.ts.map +1 -1
- package/dist/skill.js +18 -12
- package/dist/skill.js.map +1 -1
- package/dist/source-format.d.ts +10 -0
- package/dist/source-format.d.ts.map +1 -0
- package/dist/source-format.js +49 -0
- package/dist/source-format.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/unified-functions.d.ts +22 -0
- package/dist/unified-functions.d.ts.map +1 -0
- package/dist/unified-functions.js +114 -0
- package/dist/unified-functions.js.map +1 -0
- package/dist/updates.d.ts +13 -1
- package/dist/updates.d.ts.map +1 -1
- package/dist/updates.js +65 -29
- package/dist/updates.js.map +1 -1
- package/dist/workspace-fingerprint.d.ts.map +1 -1
- package/dist/workspace-fingerprint.js +5 -0
- package/dist/workspace-fingerprint.js.map +1 -1
- package/package.json +17 -5
- package/skills/lark-base-database/SKILL.md +21 -55
- package/skills/lark-base-database/references/agent-tool-design.md +181 -0
- package/skills/lark-base-database/references/agent-tools.md +101 -0
- package/skills/lark-base-database/references/ai-function.md +80 -186
- package/skills/lark-base-database/references/database-function-limitations.md +12 -4
- package/skills/lark-base-database/references/database-function.md +49 -21
- package/skills/lark-base-database/references/development.md +81 -109
- package/skills/lark-base-database/references/environment-variables.md +2 -2
- package/skills/lark-base-database/references/faas.md +43 -16
- package/skills/lark-base-database/references/file-transfer.md +10 -33
- package/skills/lark-base-database/references/frontend-design-guide.md +1 -1
- package/skills/lark-base-database/references/legacy-ai-function.md +120 -0
- package/skills/lark-base-database/references/legacy-development.md +156 -0
- package/skills/lark-base-database/references/legacy-file-transfer.md +68 -0
- package/skills/lark-base-database/references/mutations.md +38 -29
- package/skills/lark-base-database/references/notifications.md +1 -1
- package/skills/lark-base-database/references/permission-design.md +11 -5
- package/skills/lark-base-database/references/release.md +22 -7
- package/skills/lark-base-database/references/verification.md +55 -16
- package/skills/lark-base-database/references/workspace-migration.md +86 -0
- package/skills/lark-base-database/references/workspace.md +55 -44
- package/templates/database-starter/README.md +2 -0
- package/templates/database-starter/database-functions/README.md +2 -2
- package/templates/database-starter/mutations/README.md +2 -2
- package/templates/database-starter/page/README.md +5 -4
- package/templates/database-starter/page/src/examples/ai-function/TextStreamOperation.tsx +6 -6
- package/templates/database-starter/page/src/lib/attachment-image.tsx +19 -5
- package/templates/database-starter/page/src/lib/database-runtime-attachment.test.ts +124 -5
- package/templates/database-starter/page/src/lib/database-runtime.test.ts +2 -2
- package/templates/database-starter/page/src/lib/database-runtime.ts +77 -13
- package/templates/database-starter/server-functions/scripts/run-tsc.mjs +3 -2
- package/templates/database-starter-v2/README.md +25 -0
- package/templates/database-starter-v2/base-app.config.ts +10 -0
- package/templates/database-starter-v2/database-functions/README.md +1 -0
- package/templates/database-starter-v2/gitignore +6 -0
- package/templates/database-starter-v2/mutations/README.md +1 -0
- package/templates/database-starter-v2/package-lock.json +2239 -0
- package/templates/database-starter-v2/package.json +18 -0
- package/templates/database-starter-v2/page/index.html +2 -0
- package/templates/database-starter-v2/page/package.json +25 -0
- package/templates/database-starter-v2/page/src/App.tsx +12 -0
- package/templates/database-starter-v2/page/src/generated/README.md +1 -0
- package/templates/database-starter-v2/page/src/index.css +1 -0
- package/templates/database-starter-v2/page/src/lib/date-time.ts +61 -0
- package/templates/database-starter-v2/page/src/main.tsx +6 -0
- package/templates/database-starter-v2/page/tsconfig.json +19 -0
- package/templates/database-starter-v2/page/vite.config.ts +3 -0
- package/templates/database-starter-v2/prettierignore +3 -0
- package/templates/database-starter-v2/server/functions/README.md +1 -0
- package/templates/database-starter-v2/server/functions/hello/contract.ts +5 -0
- package/templates/database-starter-v2/server/functions/hello/index.ts +7 -0
- package/templates/database-starter-v2/server/functions/hello/meta.json +1 -0
- package/templates/database-starter-v2/server/generated/runtime-profile.ts +5 -0
- package/templates/database-starter-v2/server/package.json +17 -0
- package/templates/database-starter-v2/server/tsconfig.json +14 -0
package/dist/ai-functions.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync, } from 'node:fs';
|
|
2
2
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { assertFunctionResults, functionEnv, getFunction, listFunctions, putFunctions, } from './unified-functions.js';
|
|
4
|
+
import { responseFunctionHints, AGENT_TOOL_DESIGN_REFERENCE, } from './function-params.js';
|
|
5
|
+
import { normalizeFunctionInputSchema } from './function-schema.js';
|
|
3
6
|
import { readConfig } from './config.js';
|
|
4
7
|
import { assertProjectRuntimeProfile, ONLINE_RUNTIME_PROFILE, } from './environment.js';
|
|
5
8
|
import { LarkCliError } from './lark-cli-error.js';
|
|
6
|
-
import { responseData, runLarkCli } from './lark-cli.js';
|
|
9
|
+
import { responseData, runLarkCli, resolveLarkProfileEnvironment, } from './lark-cli.js';
|
|
7
10
|
// 服务端对 AI Function 管理接口(list/get/upsert/delete/validate/update/usage)做用户级 feature gate,
|
|
8
11
|
// 未开通时返回 800004011 "ai function feature is not enabled"。这不是 scope 缺失,重新登录无法解决;
|
|
9
12
|
// 这里把它翻译成可执行的降级路径,避免 Agent 把它当作权限错误循环授权。
|
|
@@ -11,11 +14,13 @@ export const AI_FUNCTION_FEATURE_NOT_ENABLED_CODE = 800004011;
|
|
|
11
14
|
export const AI_FUNCTION_FEATURE_NOT_ENABLED_MESSAGE = 'AI Function management is not enabled for the current user (800004011). This is a feature gate, not a missing scope, so `auth login` does not help. Publish without AI Functions via `deploy --skip-ai-functions` (or remove ai/) and report the AI Function part as blocked.';
|
|
12
15
|
export function isAIFunctionFeatureNotEnabled(error) {
|
|
13
16
|
return (error instanceof LarkCliError &&
|
|
14
|
-
Number(error.details.code) === AI_FUNCTION_FEATURE_NOT_ENABLED_CODE
|
|
17
|
+
Number(error.details.code) === AI_FUNCTION_FEATURE_NOT_ENABLED_CODE &&
|
|
18
|
+
/ai function feature is not enabled|AI Function management is not enabled/i.test(error.details.message));
|
|
15
19
|
}
|
|
16
|
-
function aiFunctionAccessError(error) {
|
|
20
|
+
export function aiFunctionAccessError(error) {
|
|
17
21
|
if (error instanceof LarkCliError &&
|
|
18
|
-
Number(error.details.code) === AI_FUNCTION_FEATURE_NOT_ENABLED_CODE
|
|
22
|
+
Number(error.details.code) === AI_FUNCTION_FEATURE_NOT_ENABLED_CODE &&
|
|
23
|
+
/ai function feature is not enabled|AI Function management is not enabled/i.test(error.details.message)) {
|
|
19
24
|
return new LarkCliError({
|
|
20
25
|
...error.details,
|
|
21
26
|
message: AI_FUNCTION_FEATURE_NOT_ENABLED_MESSAGE,
|
|
@@ -32,14 +37,89 @@ function runAIFunctionApi(args, env, cwd) {
|
|
|
32
37
|
}
|
|
33
38
|
}
|
|
34
39
|
// AI Function 是「功能清单」里的一类声明式资源(function_type=4):model/System Prompt/能力配置都在
|
|
35
|
-
// ai/<functionKey>/meta.json 里维护,随 deploy 走 OpenAPI
|
|
40
|
+
// ai/<functionKey>/meta.json 里维护,随 deploy 走 OpenAPI 全量覆盖同步。随 site.zip 保存源声明,pull 以远端草稿详情恢复配置。
|
|
36
41
|
const FUNCTION_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
37
|
-
const MAX_AI_FUNCTIONS = 200;
|
|
38
42
|
const AI_FUNCTIONS_DIR = 'ai';
|
|
39
43
|
const CAPABILITY_TYPES = new Set(['text', 'vision', 'image', 'video']);
|
|
44
|
+
function parseAIJSON(source) {
|
|
45
|
+
const json = JSON;
|
|
46
|
+
return JSON.parse(source, (_key, value, context) => {
|
|
47
|
+
if (typeof value !== 'number')
|
|
48
|
+
return value;
|
|
49
|
+
if (json.rawJSON && context?.source)
|
|
50
|
+
return json.rawJSON(context.source);
|
|
51
|
+
if (!Number.isFinite(value) ||
|
|
52
|
+
(Number.isInteger(value) && !Number.isSafeInteger(value)))
|
|
53
|
+
throw new RangeError('Exact AI numeric defaults require Node.js 22');
|
|
54
|
+
return value;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
40
57
|
function stripPlatformManagedFields(config) {
|
|
41
58
|
const normalized = { ...config };
|
|
42
59
|
delete normalized.limits;
|
|
60
|
+
delete normalized.output_mode;
|
|
61
|
+
if (normalized.variables !== undefined) {
|
|
62
|
+
process.stderr.write('Warning: AI config.variables 已弃用,请将 meta.json 迁移为 config.input_schema。\n');
|
|
63
|
+
}
|
|
64
|
+
if (normalized.input_schema === undefined) {
|
|
65
|
+
const properties = Object.create(null);
|
|
66
|
+
const required = [];
|
|
67
|
+
const variables = normalized.variables ?? [];
|
|
68
|
+
if (!Array.isArray(variables))
|
|
69
|
+
throw new Error('AI variables must be an array');
|
|
70
|
+
for (const variable of [...variables].sort((a, b) => String(a?.name) < String(b?.name)
|
|
71
|
+
? -1
|
|
72
|
+
: String(a?.name) > String(b?.name)
|
|
73
|
+
? 1
|
|
74
|
+
: 0)) {
|
|
75
|
+
if (!variable ||
|
|
76
|
+
typeof variable.name !== 'string' ||
|
|
77
|
+
!['string', 'number', 'boolean', 'object'].includes(variable.type))
|
|
78
|
+
throw new Error('Invalid legacy AI variable declaration');
|
|
79
|
+
if (Object.hasOwn(properties, variable.name))
|
|
80
|
+
throw new Error(`Duplicate AI variable: ${variable.name}`);
|
|
81
|
+
const property = variable.type === 'object'
|
|
82
|
+
? { oneOf: [{ type: 'object' }, { type: 'array' }] }
|
|
83
|
+
: { type: variable.type };
|
|
84
|
+
if (typeof variable.description === 'string')
|
|
85
|
+
property.description = variable.description;
|
|
86
|
+
if (variable.default !== undefined && variable.default !== '') {
|
|
87
|
+
try {
|
|
88
|
+
const value = variable.type === 'string'
|
|
89
|
+
? variable.default
|
|
90
|
+
: JSON.parse(variable.default);
|
|
91
|
+
if (variable.type === 'object'
|
|
92
|
+
? value === null || typeof value !== 'object'
|
|
93
|
+
: typeof value !== variable.type)
|
|
94
|
+
throw new Error('type');
|
|
95
|
+
property.default =
|
|
96
|
+
variable.type === 'string' ? value : parseAIJSON(variable.default);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
if (error instanceof RangeError)
|
|
100
|
+
throw error;
|
|
101
|
+
process.stderr.write(`Warning: AI variable ${variable.name}: invalid default omitted.\n`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
properties[variable.name] = property;
|
|
105
|
+
if (variable.required === true && !Object.hasOwn(property, 'default'))
|
|
106
|
+
required.push(variable.name);
|
|
107
|
+
}
|
|
108
|
+
normalized.input_schema = {
|
|
109
|
+
type: 'object',
|
|
110
|
+
properties,
|
|
111
|
+
...(required.length ? { required } : {}),
|
|
112
|
+
additionalProperties: true,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
normalizeFunctionInputSchema(normalized.input_schema, 'AI config.input_schema');
|
|
117
|
+
}
|
|
118
|
+
if (!normalized.input_schema ||
|
|
119
|
+
typeof normalized.input_schema !== 'object' ||
|
|
120
|
+
normalized.input_schema.type !== 'object')
|
|
121
|
+
throw new Error('AI input_schema must have type object');
|
|
122
|
+
delete normalized.variables;
|
|
43
123
|
const text = config.text;
|
|
44
124
|
if (text && typeof text === 'object' && !Array.isArray(text)) {
|
|
45
125
|
const rest = { ...text };
|
|
@@ -58,7 +138,7 @@ function readAIFunctionMeta(path) {
|
|
|
58
138
|
!FUNCTION_ID_PATTERN.test(functionKey)) {
|
|
59
139
|
throw new Error(`Invalid AI Function key: ${functionKey}`);
|
|
60
140
|
}
|
|
61
|
-
const manifest =
|
|
141
|
+
const manifest = parseAIJSON(readFileSync(path, 'utf8'));
|
|
62
142
|
if (typeof manifest.name !== 'string' || manifest.name === '') {
|
|
63
143
|
throw new Error(`AI Function name must be a non-empty string: ${path}`);
|
|
64
144
|
}
|
|
@@ -66,7 +146,9 @@ function readAIFunctionMeta(path) {
|
|
|
66
146
|
typeof manifest.description !== 'string') {
|
|
67
147
|
throw new Error(`AI Function description must be a string: ${path}`);
|
|
68
148
|
}
|
|
69
|
-
if (!manifest.config ||
|
|
149
|
+
if (!manifest.config ||
|
|
150
|
+
typeof manifest.config !== 'object' ||
|
|
151
|
+
Array.isArray(manifest.config)) {
|
|
70
152
|
throw new Error(`AI Function config must be an object: ${path}`);
|
|
71
153
|
}
|
|
72
154
|
const config = stripPlatformManagedFields(manifest.config);
|
|
@@ -81,114 +163,116 @@ function readAIFunctionMeta(path) {
|
|
|
81
163
|
name: manifest.name,
|
|
82
164
|
};
|
|
83
165
|
}
|
|
84
|
-
|
|
85
|
-
export function
|
|
166
|
+
/** Inventory does not parse AI configuration, so skip can still bypass invalid AI metadata. */
|
|
167
|
+
export function localAIFunctionKeys(workspace) {
|
|
86
168
|
const root = aiFunctionsRoot(workspace);
|
|
87
|
-
if (!existsSync(root))
|
|
169
|
+
if (!existsSync(root))
|
|
88
170
|
return [];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const seen = new Set();
|
|
92
|
-
for (const entry of readdirSync(root, { withFileTypes: true })) {
|
|
93
|
-
if (!entry.isDirectory())
|
|
94
|
-
continue;
|
|
171
|
+
return readdirSync(root, { withFileTypes: true })
|
|
172
|
+
.filter((entry) => {
|
|
95
173
|
const path = join(root, entry.name, 'meta.json');
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
throw new Error(`Duplicate AI Function key: ${item.functionKey}`);
|
|
101
|
-
}
|
|
102
|
-
seen.add(item.functionKey);
|
|
103
|
-
items.push(item);
|
|
104
|
-
}
|
|
105
|
-
if (items.length > MAX_AI_FUNCTIONS) {
|
|
106
|
-
throw new Error(`AI Functions cannot exceed ${MAX_AI_FUNCTIONS}`);
|
|
107
|
-
}
|
|
108
|
-
return items;
|
|
174
|
+
return entry.isDirectory() && existsSync(path) && statSync(path).isFile();
|
|
175
|
+
})
|
|
176
|
+
.map((entry) => entry.name)
|
|
177
|
+
.sort();
|
|
109
178
|
}
|
|
110
|
-
//
|
|
111
|
-
function
|
|
112
|
-
return
|
|
113
|
-
ai_functions: items.map((item) => ({
|
|
114
|
-
config_json: item.configJson,
|
|
115
|
-
description: item.description,
|
|
116
|
-
function_key: item.functionKey,
|
|
117
|
-
name: item.name,
|
|
118
|
-
})),
|
|
119
|
-
});
|
|
179
|
+
// Full synchronization also validates each declared AI configuration.
|
|
180
|
+
export function collectLocalAIFunctions(workspace) {
|
|
181
|
+
return localAIFunctionKeys(workspace).map((key) => readAIFunctionMeta(join(aiFunctionsRoot(workspace), key, 'meta.json')));
|
|
120
182
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
if (!Array.isArray(results)) {
|
|
130
|
-
throw new Error('AI Function sync returned an invalid results payload');
|
|
131
|
-
}
|
|
132
|
-
const failures = [];
|
|
133
|
-
for (const entry of results) {
|
|
134
|
-
if (!entry || typeof entry !== 'object')
|
|
135
|
-
continue;
|
|
136
|
-
const record = entry;
|
|
137
|
-
const code = record.code ?? record.Code;
|
|
138
|
-
if (typeof code === 'number' && code !== 0) {
|
|
139
|
-
const key = record.function_key ?? record.functionKey ?? record.FunctionKey;
|
|
140
|
-
const message = record.msg ?? record.Msg ?? record.message ?? '';
|
|
141
|
-
failures.push(`${String(key ?? '(unknown)')}: code=${code}${message ? ` ${String(message)}` : ''}`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
if (failures.length > 0) {
|
|
145
|
-
throw new Error(`AI Function sync failed for ${failures.length} function(s): ${failures.join('; ')}`);
|
|
146
|
-
}
|
|
183
|
+
export function aiFunctionWrite(item) {
|
|
184
|
+
return {
|
|
185
|
+
functionId: item.functionKey,
|
|
186
|
+
name: item.name,
|
|
187
|
+
description: item.description,
|
|
188
|
+
functionType: 'ai',
|
|
189
|
+
spec: item.configJson,
|
|
190
|
+
};
|
|
147
191
|
}
|
|
148
|
-
// syncAIFunctionsFullCover 走 OpenAPI 全量覆盖同步(对齐 update_functions 心智):传本地全集,服务端 diff 出
|
|
149
|
-
// add/update/delete + 逐条校验码。deploy 与 `ai sync` 命令共用。空集合仍会清空远端 AI Function(全量覆盖语义)。
|
|
150
|
-
// 任一条 results[].code != 0 立即抛错,避免 deploy 在 AI 同步失败后继续 artifact/publish。
|
|
151
192
|
export function syncAIFunctionsFullCover(appToken, items, env, cwd) {
|
|
152
|
-
const token = encodeURIComponent(appToken);
|
|
153
|
-
let response;
|
|
154
193
|
try {
|
|
155
|
-
|
|
156
|
-
'api',
|
|
157
|
-
'POST',
|
|
158
|
-
`/open-apis/base/v3/sites/${token}/ai_functions/update`,
|
|
159
|
-
'--data',
|
|
160
|
-
buildUpdatePayload(items),
|
|
161
|
-
], env, cwd);
|
|
194
|
+
return putFunctions(appToken, ['ai'], items.map(aiFunctionWrite), env, cwd);
|
|
162
195
|
}
|
|
163
196
|
catch (error) {
|
|
164
|
-
// 顶层失败不能覆盖服务端保留的逐条诊断。
|
|
165
|
-
if (error instanceof LarkCliError && error.details.results) {
|
|
166
|
-
assertAIFunctionResultsOk({ results: error.details.results });
|
|
167
|
-
}
|
|
168
197
|
throw aiFunctionAccessError(error);
|
|
169
198
|
}
|
|
170
|
-
const data = responseData(response);
|
|
171
|
-
assertAIFunctionResultsOk(data);
|
|
172
|
-
return data;
|
|
173
199
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
200
|
+
// 专用管理接口返回完整草稿列表,不支持 Prod,也不要求 discovery 的 skillHash。
|
|
201
|
+
// 当前服务端不分页;如响应声明 hasMore,则必须读完游标,不能把首屏当全集。
|
|
202
|
+
function listDraftAIFunctions(appToken, env, cwd) {
|
|
203
|
+
const functions = [];
|
|
204
|
+
const keys = new Set();
|
|
205
|
+
const cursors = new Set();
|
|
206
|
+
let pageToken = '';
|
|
207
|
+
for (let page = 0; page < 1000; page++) {
|
|
208
|
+
const data = responseData(runAIFunctionApi([
|
|
209
|
+
'api',
|
|
210
|
+
'GET',
|
|
211
|
+
`/open-apis/base/v3/sites/${encodeURIComponent(appToken)}/ai_functions`,
|
|
212
|
+
...(pageToken ? ['--params', JSON.stringify({ pageToken })] : []),
|
|
213
|
+
], env, cwd));
|
|
214
|
+
const hasMore = data.hasMore ?? data.has_more;
|
|
215
|
+
if (!Array.isArray(data.functions) ||
|
|
216
|
+
(hasMore !== undefined && typeof hasMore !== 'boolean'))
|
|
217
|
+
throw new Error('AI_FUNCTION_RESPONSE_UNKNOWN: invalid AI Function list');
|
|
218
|
+
for (const item of data.functions) {
|
|
219
|
+
const key = item?.functionKey ?? item?.function_key;
|
|
220
|
+
if (typeof key !== 'string' ||
|
|
221
|
+
!/^[A-Za-z0-9_-]{1,64}$/.test(key) ||
|
|
222
|
+
keys.has(key))
|
|
223
|
+
throw new Error('AI_FUNCTION_RESPONSE_UNKNOWN: invalid or duplicate functionKey');
|
|
224
|
+
keys.add(key);
|
|
225
|
+
// 保持 1.4.1 的列表字段;专用接口缺失的发现元数据明确置空,不推断可调用性。
|
|
226
|
+
functions.push({
|
|
227
|
+
functionId: key,
|
|
228
|
+
name: item.name ?? null,
|
|
229
|
+
description: item.description ?? null,
|
|
230
|
+
functionType: 'ai',
|
|
231
|
+
capabilityType: item.capabilityType ?? item.capability_type ?? null,
|
|
232
|
+
mode: null,
|
|
233
|
+
result: null,
|
|
234
|
+
schemaStatus: null,
|
|
235
|
+
inputSchema: null,
|
|
236
|
+
agentTool: null,
|
|
237
|
+
statusReason: null,
|
|
238
|
+
statusHint: null,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
if (!hasMore)
|
|
242
|
+
return functions;
|
|
243
|
+
const next = data.pageToken ?? data.page_token;
|
|
244
|
+
if (typeof next !== 'string' || !next || cursors.has(next))
|
|
245
|
+
throw new Error('AI Function list returned an empty or repeated pageToken');
|
|
246
|
+
cursors.add(next);
|
|
247
|
+
pageToken = next;
|
|
248
|
+
}
|
|
249
|
+
throw new Error('AI_FUNCTION_RESPONSE_UNKNOWN: unbounded AI Function list');
|
|
250
|
+
}
|
|
251
|
+
export function remoteAIFunctionKeys(appToken, env, cwd) {
|
|
252
|
+
return listDraftAIFunctions(appToken, env, cwd).map((item) => item.functionId);
|
|
253
|
+
}
|
|
254
|
+
function aiDetail(appToken, key, version, env, cwd) {
|
|
255
|
+
const view = getFunction(appToken, key, version, env, cwd);
|
|
256
|
+
if (view.functionType !== 'ai')
|
|
257
|
+
throw new Error(`Function ${key} is not an AI Function`);
|
|
258
|
+
return view;
|
|
259
|
+
}
|
|
260
|
+
function restoreAI(view, functionKey, workspace) {
|
|
261
|
+
if (!FUNCTION_ID_PATTERN.test(functionKey) || functionKey.length > 64)
|
|
262
|
+
throw new Error('Invalid remote AI Function key');
|
|
263
|
+
if (typeof view.name !== 'string' || typeof view.spec !== 'string')
|
|
264
|
+
throw new Error(`Remote AI Function ${functionKey} did not return name and spec`);
|
|
265
|
+
const parsed = parseAIJSON(view.spec);
|
|
266
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
267
|
+
throw new Error('AI spec must be an object');
|
|
268
|
+
const config = stripPlatformManagedFields(parsed);
|
|
269
|
+
const directory = join(aiFunctionsRoot(workspace), functionKey);
|
|
270
|
+
mkdirSync(directory, { recursive: true });
|
|
271
|
+
writeFileSync(join(directory, 'meta.json'), `${JSON.stringify({ name: view.name, description: view.description ?? '', config }, null, 2)}\n`);
|
|
188
272
|
}
|
|
189
273
|
// restoreRemoteAIFunctions 把远端 AI Function 草稿配置写回 <workspace>/ai/<key>/meta.json。
|
|
190
274
|
// pull 调用它,让"修改已有应用前先恢复远端 AI Function"由 CLI 完成,而不是交给 Agent 判断。
|
|
191
|
-
//
|
|
275
|
+
// 仅明确的 feature-not-enabled 消息可分类跳过,普通权限错误必须失败。
|
|
192
276
|
export function restoreRemoteAIFunctions(appToken, workspace, env, cwd) {
|
|
193
277
|
let keys;
|
|
194
278
|
try {
|
|
@@ -199,38 +283,8 @@ export function restoreRemoteAIFunctions(appToken, workspace, env, cwd) {
|
|
|
199
283
|
return { status: 'not_enabled' };
|
|
200
284
|
throw error;
|
|
201
285
|
}
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
if (functionKey.length > 64 || !FUNCTION_ID_PATTERN.test(functionKey)) {
|
|
205
|
-
throw new Error(`Remote AI Function key is not restorable: ${functionKey}`);
|
|
206
|
-
}
|
|
207
|
-
const detail = responseData(runAIFunctionApi([
|
|
208
|
-
'api',
|
|
209
|
-
'GET',
|
|
210
|
-
`/open-apis/base/v3/sites/${encodeURIComponent(appToken)}/ai_functions/${encodeURIComponent(functionKey)}`,
|
|
211
|
-
], env, cwd));
|
|
212
|
-
const view = (detail.function ?? detail);
|
|
213
|
-
const configJson = view.config_json ?? view.configJson;
|
|
214
|
-
if (typeof view.name !== 'string' || typeof configJson !== 'string') {
|
|
215
|
-
throw new Error(`Remote AI Function ${functionKey} did not return name and config_json`);
|
|
216
|
-
}
|
|
217
|
-
let config;
|
|
218
|
-
try {
|
|
219
|
-
config = JSON.parse(configJson);
|
|
220
|
-
}
|
|
221
|
-
catch {
|
|
222
|
-
throw new Error(`Remote AI Function ${functionKey} config_json is not valid JSON`);
|
|
223
|
-
}
|
|
224
|
-
const directory = join(root, functionKey);
|
|
225
|
-
mkdirSync(directory, { recursive: true });
|
|
226
|
-
writeFileSync(join(directory, 'meta.json'), `${JSON.stringify({
|
|
227
|
-
name: view.name,
|
|
228
|
-
...(typeof view.description === 'string' && view.description !== ''
|
|
229
|
-
? { description: view.description }
|
|
230
|
-
: {}),
|
|
231
|
-
config,
|
|
232
|
-
}, null, 2)}\n`);
|
|
233
|
-
}
|
|
286
|
+
const details = keys.map((key) => aiDetail(appToken, key, 'preview', env, cwd));
|
|
287
|
+
keys.forEach((key, index) => restoreAI(details[index], key, workspace));
|
|
234
288
|
return { status: 'restored', functionKeys: keys };
|
|
235
289
|
}
|
|
236
290
|
// assertNoRemoteAIFunctionLoss 在全量覆盖前核对:远端已有的 key 必须都在本地全集中,否则拒绝,
|
|
@@ -262,127 +316,124 @@ export function syncAIFunctionsForDeploy(appToken, items, env, cwd) {
|
|
|
262
316
|
assertNoRemoteAIFunctionLoss(appToken, items, env, cwd);
|
|
263
317
|
return syncAIFunctionsFullCover(appToken, items, env, cwd);
|
|
264
318
|
}
|
|
265
|
-
async function resolveEnv(workspace, runtimeProfile) {
|
|
319
|
+
async function resolveEnv(workspace, runtimeProfile, explicitProfile) {
|
|
266
320
|
const config = readConfig(workspace);
|
|
267
321
|
assertProjectRuntimeProfile(config.runtimeProfile, runtimeProfile);
|
|
268
|
-
const env = await runtimeProfile.commandEnvironment();
|
|
322
|
+
const { env } = resolveLarkProfileEnvironment(await runtimeProfile.commandEnvironment(), explicitProfile, config.larkProfile, runtimeProfile);
|
|
269
323
|
return { config, env };
|
|
270
324
|
}
|
|
271
325
|
// listAIFunctions 列出 Site 下 AI Function。
|
|
272
326
|
export async function listAIFunctions(options) {
|
|
273
327
|
const workspace = resolve(options.directory);
|
|
274
328
|
const runtimeProfile = options.runtimeProfile ?? ONLINE_RUNTIME_PROFILE;
|
|
275
|
-
const { config, env } = await resolveEnv(workspace, runtimeProfile);
|
|
276
|
-
const
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
329
|
+
const { config, env } = await resolveEnv(workspace, runtimeProfile, options.larkProfile);
|
|
330
|
+
const version = functionEnv(options.env);
|
|
331
|
+
return {
|
|
332
|
+
env: version,
|
|
333
|
+
functions: version === 'preview'
|
|
334
|
+
? listDraftAIFunctions(config.appToken, env, workspace)
|
|
335
|
+
: listFunctions(config.appToken, version, env, workspace).filter((item) => item.functionType === 'ai'),
|
|
336
|
+
};
|
|
282
337
|
}
|
|
283
|
-
// getAIFunction 读取单个 AI Function 配置(用于本地编辑 / diff)。
|
|
284
338
|
export async function getAIFunction(functionKey, options) {
|
|
285
339
|
const workspace = resolve(options.directory);
|
|
286
|
-
const
|
|
287
|
-
const
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
return
|
|
340
|
+
const { config, env } = await resolveEnv(workspace, options.runtimeProfile ?? ONLINE_RUNTIME_PROFILE, options.larkProfile);
|
|
341
|
+
const version = functionEnv(options.env);
|
|
342
|
+
const view = aiDetail(config.appToken, functionKey, version, env, workspace);
|
|
343
|
+
if (version === 'prod')
|
|
344
|
+
return { env: version, function: view };
|
|
345
|
+
if (typeof view.spec !== 'string')
|
|
346
|
+
throw new Error('AI Function detail is missing spec');
|
|
347
|
+
return {
|
|
348
|
+
env: version,
|
|
349
|
+
function: view,
|
|
350
|
+
config: stripPlatformManagedFields(parseAIJSON(view.spec)),
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
export async function pullAIFunction(functionKey, options) {
|
|
354
|
+
if (functionEnv(options.env) !== 'preview')
|
|
355
|
+
throw new Error('AI pull requires env=preview');
|
|
356
|
+
const workspace = resolve(options.directory);
|
|
357
|
+
const { config, env } = await resolveEnv(workspace, options.runtimeProfile ?? ONLINE_RUNTIME_PROFILE, options.larkProfile);
|
|
358
|
+
restoreAI(aiDetail(config.appToken, functionKey, 'preview', env, workspace), functionKey, workspace);
|
|
359
|
+
return { ok: true, functionKey, env: 'preview' };
|
|
294
360
|
}
|
|
295
361
|
// syncAIFunctions 把本地 ai/ 全集全量覆盖同步到 Site。
|
|
296
362
|
export async function syncAIFunctions(options) {
|
|
297
363
|
const workspace = resolve(options.directory);
|
|
298
364
|
const runtimeProfile = options.runtimeProfile ?? ONLINE_RUNTIME_PROFILE;
|
|
299
|
-
const { config, env } = await resolveEnv(workspace, runtimeProfile);
|
|
365
|
+
const { config, env } = await resolveEnv(workspace, runtimeProfile, options.larkProfile);
|
|
300
366
|
const items = collectLocalAIFunctions(workspace);
|
|
301
|
-
const data = syncAIFunctionsFullCover(config.appToken, items, env, workspace);
|
|
367
|
+
const { deletedFunctionIds, ...data } = syncAIFunctionsFullCover(config.appToken, items, env, workspace);
|
|
302
368
|
return {
|
|
303
369
|
ok: true,
|
|
304
370
|
appToken: config.appToken,
|
|
305
371
|
count: items.length,
|
|
306
372
|
results: data,
|
|
373
|
+
deletedFunctionIds,
|
|
307
374
|
};
|
|
308
375
|
}
|
|
309
376
|
// validateAIFunction 静态校验单个本地 AI Function(保存/发布前,按能力分支),不落库。
|
|
310
377
|
export async function validateAIFunction(functionKey, options) {
|
|
311
378
|
const workspace = resolve(options.directory);
|
|
312
379
|
const runtimeProfile = options.runtimeProfile ?? ONLINE_RUNTIME_PROFILE;
|
|
313
|
-
const { config, env } = await resolveEnv(workspace, runtimeProfile);
|
|
314
|
-
|
|
315
|
-
const target = items.find((item) => item.functionKey === functionKey);
|
|
316
|
-
if (!target) {
|
|
380
|
+
const { config, env } = await resolveEnv(workspace, runtimeProfile, options.larkProfile);
|
|
381
|
+
if (!localAIFunctionKeys(workspace).includes(functionKey))
|
|
317
382
|
throw new Error(`AI Function not found in ${AI_FUNCTIONS_DIR}/: ${functionKey}`);
|
|
318
|
-
|
|
383
|
+
const target = readAIFunctionMeta(join(aiFunctionsRoot(workspace), functionKey, 'meta.json'));
|
|
319
384
|
const response = runAIFunctionApi([
|
|
320
385
|
'api',
|
|
321
386
|
'POST',
|
|
322
|
-
`/open-apis/base/v3/sites/${encodeURIComponent(config.appToken)}/
|
|
387
|
+
`/open-apis/base/v3/sites/${encodeURIComponent(config.appToken)}/functions/validate`,
|
|
323
388
|
'--data',
|
|
324
|
-
JSON.stringify({
|
|
325
|
-
function: {
|
|
326
|
-
config_json: target.configJson,
|
|
327
|
-
description: target.description,
|
|
328
|
-
function_key: target.functionKey,
|
|
329
|
-
name: target.name,
|
|
330
|
-
},
|
|
331
|
-
}),
|
|
389
|
+
JSON.stringify({ kinds: ['ai'], functions: [aiFunctionWrite(target)] }),
|
|
332
390
|
], env, workspace);
|
|
333
391
|
const data = responseData(response);
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
throw new Error(
|
|
337
|
-
|
|
338
|
-
return
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (typeof raw !== 'string' || raw === '') {
|
|
348
|
-
throw new Error('AI Function detail is missing config_json');
|
|
349
|
-
}
|
|
350
|
-
let parsed;
|
|
351
|
-
try {
|
|
352
|
-
parsed = JSON.parse(raw);
|
|
353
|
-
}
|
|
354
|
-
catch {
|
|
355
|
-
throw new Error('AI Function detail config_json is not valid JSON');
|
|
356
|
-
}
|
|
357
|
-
if (!parsed || typeof parsed !== 'object') {
|
|
358
|
-
throw new Error('AI Function detail config_json is not an object');
|
|
359
|
-
}
|
|
360
|
-
const capability = parsed.capability_type;
|
|
361
|
-
if (typeof capability !== 'string' || !CAPABILITY_TYPES.has(capability)) {
|
|
362
|
-
throw new Error('AI Function detail contains invalid capability_type');
|
|
363
|
-
}
|
|
364
|
-
return capability;
|
|
392
|
+
assertFunctionResults(data, [functionKey]);
|
|
393
|
+
if (data.valid !== true)
|
|
394
|
+
throw new Error('AI Function validation failed');
|
|
395
|
+
const hints = responseFunctionHints(data, [functionKey]);
|
|
396
|
+
return {
|
|
397
|
+
...data,
|
|
398
|
+
...(hints.length
|
|
399
|
+
? {
|
|
400
|
+
functionHints: hints,
|
|
401
|
+
functionHintsGuide: `Review hints against ${AGENT_TOOL_DESIGN_REFERENCE}.`,
|
|
402
|
+
}
|
|
403
|
+
: {}),
|
|
404
|
+
};
|
|
365
405
|
}
|
|
366
|
-
// debugAIFunction 调试 text/vision 时走 unary 聚合;image/video 走统一 tasks 提交接口。
|
|
367
406
|
export async function debugAIFunction(functionKey, options) {
|
|
368
407
|
const workspace = resolve(options.directory);
|
|
369
408
|
const runtimeProfile = options.runtimeProfile ?? ONLINE_RUNTIME_PROFILE;
|
|
370
|
-
const { config, env } = await resolveEnv(workspace, runtimeProfile);
|
|
409
|
+
const { config, env } = await resolveEnv(workspace, runtimeProfile, options.larkProfile);
|
|
371
410
|
const token = encodeURIComponent(config.appToken);
|
|
372
411
|
const key = encodeURIComponent(functionKey);
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
412
|
+
const version = functionEnv(options.env);
|
|
413
|
+
if (options.params !== undefined && options.variables !== undefined)
|
|
414
|
+
throw new Error('--params and --variables are mutually exclusive');
|
|
415
|
+
if (options.variables !== undefined)
|
|
416
|
+
process.stderr.write('Warning: --variables 已弃用,请使用 --params。\n');
|
|
417
|
+
const params = options.params ?? options.variables ?? {};
|
|
418
|
+
if (!params || typeof params !== 'object' || Array.isArray(params))
|
|
419
|
+
throw new Error('--params must be a JSON object');
|
|
420
|
+
const detail = aiDetail(config.appToken, functionKey, version, env, workspace);
|
|
421
|
+
const capabilityType = detail.capabilityType;
|
|
422
|
+
if (typeof capabilityType !== 'string' ||
|
|
423
|
+
!CAPABILITY_TYPES.has(capabilityType))
|
|
424
|
+
throw new Error('AI Function detail contains invalid capabilityType');
|
|
425
|
+
const media = capabilityType === 'image' || capabilityType === 'video';
|
|
426
|
+
if (media ? options.messages !== undefined : options.input !== undefined)
|
|
427
|
+
throw new Error('AI capability does not accept the supplied messages/input');
|
|
428
|
+
const body = JSON.stringify(media
|
|
429
|
+
? { params, input: options.input ?? {} }
|
|
430
|
+
: { params, messages: options.messages ?? [] });
|
|
380
431
|
const response = runAIFunctionApi([
|
|
381
432
|
'api',
|
|
382
433
|
'POST',
|
|
383
|
-
`/open-apis/base/v3/sites/${token}/
|
|
434
|
+
`/open-apis/base/v3/sites/${token}/functions/${key}/invoke`,
|
|
384
435
|
'--params',
|
|
385
|
-
JSON.stringify({ env: options.env }),
|
|
436
|
+
JSON.stringify({ env: functionEnv(options.env), includeInternal: true }),
|
|
386
437
|
'--data',
|
|
387
438
|
body,
|
|
388
439
|
], env, workspace);
|
|
@@ -392,16 +443,17 @@ export async function debugAIFunction(functionKey, options) {
|
|
|
392
443
|
export async function getAIFunctionTask(functionKey, taskId, options) {
|
|
393
444
|
const workspace = resolve(options.directory);
|
|
394
445
|
const runtimeProfile = options.runtimeProfile ?? ONLINE_RUNTIME_PROFILE;
|
|
395
|
-
const { config, env } = await resolveEnv(workspace, runtimeProfile);
|
|
446
|
+
const { config, env } = await resolveEnv(workspace, runtimeProfile, options.larkProfile);
|
|
396
447
|
const token = encodeURIComponent(config.appToken);
|
|
397
448
|
const key = encodeURIComponent(functionKey);
|
|
398
449
|
const task = encodeURIComponent(taskId);
|
|
450
|
+
process.stderr.write(`AI task env=${functionEnv(options.env)};查询成功不等于生成成功,请按 status 判断。\n`);
|
|
399
451
|
const response = runAIFunctionApi([
|
|
400
452
|
'api',
|
|
401
453
|
'GET',
|
|
402
|
-
`/open-apis/base/v3/sites/${token}/
|
|
454
|
+
`/open-apis/base/v3/sites/${token}/functions/${key}/tasks/${task}`,
|
|
403
455
|
'--params',
|
|
404
|
-
JSON.stringify({ env: options.env }),
|
|
456
|
+
JSON.stringify({ env: functionEnv(options.env), includeInternal: true }),
|
|
405
457
|
], env, workspace);
|
|
406
458
|
return responseData(response);
|
|
407
459
|
}
|
|
@@ -409,7 +461,7 @@ export async function getAIFunctionTask(functionKey, taskId, options) {
|
|
|
409
461
|
export async function getAIFunctionUsage(options) {
|
|
410
462
|
const workspace = resolve(options.directory);
|
|
411
463
|
const runtimeProfile = options.runtimeProfile ?? ONLINE_RUNTIME_PROFILE;
|
|
412
|
-
const { config, env } = await resolveEnv(workspace, runtimeProfile);
|
|
464
|
+
const { config, env } = await resolveEnv(workspace, runtimeProfile, options.larkProfile);
|
|
413
465
|
const token = encodeURIComponent(config.appToken);
|
|
414
466
|
const params = {};
|
|
415
467
|
if (options.from !== undefined)
|