@lark-base-open/base-database-cli 1.0.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.
Files changed (149) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +169 -0
  3. package/changelog/CHANGELOG-0.x.md +103 -0
  4. package/config-schema.json +28 -0
  5. package/dist/agent-guide.d.ts +18 -0
  6. package/dist/agent-guide.d.ts.map +1 -0
  7. package/dist/agent-guide.js +30 -0
  8. package/dist/agent-guide.js.map +1 -0
  9. package/dist/archive.d.ts +16 -0
  10. package/dist/archive.d.ts.map +1 -0
  11. package/dist/archive.js +162 -0
  12. package/dist/archive.js.map +1 -0
  13. package/dist/assets.d.ts +4 -0
  14. package/dist/assets.d.ts.map +1 -0
  15. package/dist/assets.js +6 -0
  16. package/dist/assets.js.map +1 -0
  17. package/dist/auth.d.ts +17 -0
  18. package/dist/auth.d.ts.map +1 -0
  19. package/dist/auth.js +63 -0
  20. package/dist/auth.js.map +1 -0
  21. package/dist/cli.d.ts +3 -0
  22. package/dist/cli.d.ts.map +1 -0
  23. package/dist/cli.js +15 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/config.d.ts +5 -0
  26. package/dist/config.d.ts.map +1 -0
  27. package/dist/config.js +50 -0
  28. package/dist/config.js.map +1 -0
  29. package/dist/database-skill.d.ts +22 -0
  30. package/dist/database-skill.d.ts.map +1 -0
  31. package/dist/database-skill.js +135 -0
  32. package/dist/database-skill.js.map +1 -0
  33. package/dist/deploy.d.ts +28 -0
  34. package/dist/deploy.d.ts.map +1 -0
  35. package/dist/deploy.js +476 -0
  36. package/dist/deploy.js.map +1 -0
  37. package/dist/environment.d.ts +12 -0
  38. package/dist/environment.d.ts.map +1 -0
  39. package/dist/environment.js +27 -0
  40. package/dist/environment.js.map +1 -0
  41. package/dist/function-schema.d.ts +3 -0
  42. package/dist/function-schema.d.ts.map +1 -0
  43. package/dist/function-schema.js +310 -0
  44. package/dist/function-schema.js.map +1 -0
  45. package/dist/init.d.ts +77 -0
  46. package/dist/init.d.ts.map +1 -0
  47. package/dist/init.js +418 -0
  48. package/dist/init.js.map +1 -0
  49. package/dist/lark-cli.d.ts +8 -0
  50. package/dist/lark-cli.d.ts.map +1 -0
  51. package/dist/lark-cli.js +228 -0
  52. package/dist/lark-cli.js.map +1 -0
  53. package/dist/npm.d.ts +13 -0
  54. package/dist/npm.d.ts.map +1 -0
  55. package/dist/npm.js +93 -0
  56. package/dist/npm.js.map +1 -0
  57. package/dist/page-title.d.ts +3 -0
  58. package/dist/page-title.d.ts.map +1 -0
  59. package/dist/page-title.js +47 -0
  60. package/dist/page-title.js.map +1 -0
  61. package/dist/preview-receipt.d.ts +23 -0
  62. package/dist/preview-receipt.d.ts.map +1 -0
  63. package/dist/preview-receipt.js +133 -0
  64. package/dist/preview-receipt.js.map +1 -0
  65. package/dist/process.d.ts +17 -0
  66. package/dist/process.d.ts.map +1 -0
  67. package/dist/process.js +53 -0
  68. package/dist/process.js.map +1 -0
  69. package/dist/program.d.ts +12 -0
  70. package/dist/program.d.ts.map +1 -0
  71. package/dist/program.js +204 -0
  72. package/dist/program.js.map +1 -0
  73. package/dist/pull.d.ts +18 -0
  74. package/dist/pull.d.ts.map +1 -0
  75. package/dist/pull.js +324 -0
  76. package/dist/pull.js.map +1 -0
  77. package/dist/skill.d.ts +7 -0
  78. package/dist/skill.d.ts.map +1 -0
  79. package/dist/skill.js +28 -0
  80. package/dist/skill.js.map +1 -0
  81. package/dist/types.d.ts +32 -0
  82. package/dist/types.d.ts.map +1 -0
  83. package/dist/types.js +2 -0
  84. package/dist/types.js.map +1 -0
  85. package/dist/workspace-fingerprint.d.ts +8 -0
  86. package/dist/workspace-fingerprint.d.ts.map +1 -0
  87. package/dist/workspace-fingerprint.js +64 -0
  88. package/dist/workspace-fingerprint.js.map +1 -0
  89. package/package.json +54 -0
  90. package/skills/lark-base-database/SKILL.md +307 -0
  91. package/skills/lark-base-database/references/base-capability-routing.md +81 -0
  92. package/skills/lark-base-database/references/base-sql-unsupported.md +42 -0
  93. package/skills/lark-base-database/references/database-capability-skill.md +24 -0
  94. package/skills/lark-base-database/references/development.md +642 -0
  95. package/skills/lark-base-database/references/file-transfer.md +275 -0
  96. package/skills/lark-base-database/references/permission-design.md +138 -0
  97. package/skills/lark-base-database/references/release.md +81 -0
  98. package/skills/lark-base-database/references/sql-function.md +276 -0
  99. package/skills/lark-base-database/references/style-guide.md +88 -0
  100. package/templates/database-capability/SKILL.md.tmpl +41 -0
  101. package/templates/database-starter/README.md +11 -0
  102. package/templates/database-starter/gitignore +3 -0
  103. package/templates/database-starter/page/.prettierignore +4 -0
  104. package/templates/database-starter/page/.prettierrc.json +7 -0
  105. package/templates/database-starter/page/README.md +29 -0
  106. package/templates/database-starter/page/index.html +24 -0
  107. package/templates/database-starter/page/package-lock.json +2675 -0
  108. package/templates/database-starter/page/package.json +44 -0
  109. package/templates/database-starter/page/public/favicon.png +0 -0
  110. package/templates/database-starter/page/src/App.tsx +18 -0
  111. package/templates/database-starter/page/src/components/UserSelectorEditor/index.tsx +267 -0
  112. package/templates/database-starter/page/src/components/UserSelectorPanel/index.css +32 -0
  113. package/templates/database-starter/page/src/components/UserSelectorPanel/index.test.ts +24 -0
  114. package/templates/database-starter/page/src/components/UserSelectorPanel/index.tsx +478 -0
  115. package/templates/database-starter/page/src/components/UserSelectorPanel/user-search.test.ts +175 -0
  116. package/templates/database-starter/page/src/components/UserSelectorPanel/user-search.ts +201 -0
  117. package/templates/database-starter/page/src/components/database-runtime-ready.tsx +21 -0
  118. package/templates/database-starter/page/src/components/database-user-menu/index.tsx +238 -0
  119. package/templates/database-starter/page/src/components/feedback/page-state.tsx +66 -0
  120. package/templates/database-starter/page/src/components/floating-button/chatbot-entry.test.ts +23 -0
  121. package/templates/database-starter/page/src/components/floating-button/chatbot-entry.tsx +873 -0
  122. package/templates/database-starter/page/src/components/floating-button/index.tsx +196 -0
  123. package/templates/database-starter/page/src/components/floating-button/local-chat-panel.test.ts +25 -0
  124. package/templates/database-starter/page/src/components/floating-button/local-chat-panel.tsx +197 -0
  125. package/templates/database-starter/page/src/components/floating-button/utils/current-user.ts +56 -0
  126. package/templates/database-starter/page/src/components/floating-button/utils/env.ts +18 -0
  127. package/templates/database-starter/page/src/components/floating-button/utils/project-meta.test.ts +79 -0
  128. package/templates/database-starter/page/src/components/floating-button/utils/project-meta.ts +252 -0
  129. package/templates/database-starter/page/src/components/ui/button.tsx +56 -0
  130. package/templates/database-starter/page/src/index.css +67 -0
  131. package/templates/database-starter/page/src/lib/cn.test.ts +9 -0
  132. package/templates/database-starter/page/src/lib/cn.ts +7 -0
  133. package/templates/database-starter/page/src/lib/database-runtime-attachment.test.ts +168 -0
  134. package/templates/database-starter/page/src/lib/database-runtime-context.test.ts +111 -0
  135. package/templates/database-starter/page/src/lib/database-runtime.test.ts +103 -0
  136. package/templates/database-starter/page/src/lib/database-runtime.ts +389 -0
  137. package/templates/database-starter/page/src/main.tsx +34 -0
  138. package/templates/database-starter/page/src/vite-env.d.ts +1 -0
  139. package/templates/database-starter/page/tsconfig.json +23 -0
  140. package/templates/database-starter/page/vite.config.ts +13 -0
  141. package/templates/database-starter/prettierignore +1 -0
  142. package/templates/database-starter/server-functions/.prettierignore +3 -0
  143. package/templates/database-starter/server-functions/.prettierrc.json +7 -0
  144. package/templates/database-starter/server-functions/README.md +11 -0
  145. package/templates/database-starter/server-functions/package-lock.json +1687 -0
  146. package/templates/database-starter/server-functions/package.json +22 -0
  147. package/templates/database-starter/server-functions/scripts/run-tsc.mjs +50 -0
  148. package/templates/database-starter/server-functions/tsconfig.json +20 -0
  149. package/templates/database-starter/sql/README.md +8 -0
@@ -0,0 +1,53 @@
1
+ import { spawn, spawnSync } from 'node:child_process';
2
+ export class CommandExecutionError extends Error {
3
+ stderr;
4
+ exitCode;
5
+ constructor(message, stderr, exitCode) {
6
+ super(message);
7
+ this.stderr = stderr;
8
+ this.exitCode = exitCode;
9
+ this.name = 'CommandExecutionError';
10
+ }
11
+ }
12
+ export function runCommand(command, args, options = {}) {
13
+ const result = spawnSync(command, args, {
14
+ cwd: options.cwd,
15
+ env: options.env,
16
+ encoding: 'utf8',
17
+ stdio: options.capture ? 'pipe' : 'inherit',
18
+ timeout: options.timeout,
19
+ });
20
+ if (result.error)
21
+ throw result.error;
22
+ if (result.status !== 0) {
23
+ const detail = [result.stdout, result.stderr]
24
+ .filter(Boolean)
25
+ .join('\n')
26
+ .trim();
27
+ throw new Error(detail || `${command} exited with status ${String(result.status)}`);
28
+ }
29
+ return result.stdout ?? '';
30
+ }
31
+ export function runCommandAsync(command, args, options = {}) {
32
+ return new Promise((resolve, reject) => {
33
+ const child = spawn(command, args, {
34
+ cwd: options.cwd,
35
+ env: options.env,
36
+ stdio: options.captureStderr ? ['inherit', 'inherit', 'pipe'] : 'inherit',
37
+ });
38
+ let stderr = '';
39
+ child.stderr?.on('data', (chunk) => {
40
+ stderr = (stderr + chunk.toString('utf8')).slice(-16 * 1024);
41
+ process.stderr.write(chunk);
42
+ });
43
+ child.once('error', reject);
44
+ child.once('close', (code, signal) => {
45
+ if (code === 0) {
46
+ resolve();
47
+ return;
48
+ }
49
+ reject(new CommandExecutionError(`${command} failed${signal ? ` with signal ${signal}` : ` with status ${String(code)}`}`, stderr, code));
50
+ });
51
+ });
52
+ }
53
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../src/process.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { Command } from 'commander';
2
+ import type { RuntimeProfile } from './environment.js';
3
+ export interface BaseDatabaseProgramOptions {
4
+ commandName: string;
5
+ description: string;
6
+ version: string;
7
+ runtimeProfiles: readonly RuntimeProfile[];
8
+ includeSkillCommand?: boolean;
9
+ }
10
+ export declare function createBaseDatabaseProgram(options: BaseDatabaseProgramOptions): Command;
11
+ export declare function runBaseDatabaseProgram(program: Command): void;
12
+ //# sourceMappingURL=program.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAU,MAAM,WAAW,CAAC;AAI5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAYvD,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAMD,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,0BAA0B,GAClC,OAAO,CA6TT;AAED,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAO7D"}
@@ -0,0 +1,204 @@
1
+ import { Command, Option } from 'commander';
2
+ import { doctor, login, logout, whoami } from './auth.js';
3
+ import { deploymentStatus, deployProject } from './deploy.js';
4
+ import { initializeProject } from './init.js';
5
+ import { runProjectLarkCli } from './lark-cli.js';
6
+ import { pullProject } from './pull.js';
7
+ import { installSkill } from './skill.js';
8
+ import { discoveryArgs, installDatabaseSkill, invocationArgs, runDatabaseSkillRequest, } from './database-skill.js';
9
+ function printJson(value) {
10
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
11
+ }
12
+ export function createBaseDatabaseProgram(options) {
13
+ const [defaultProfile] = options.runtimeProfiles;
14
+ if (!defaultProfile) {
15
+ throw new Error('At least one runtime profile is required');
16
+ }
17
+ const profiles = new Map(options.runtimeProfiles.map((profile) => [profile.name, profile]));
18
+ if (profiles.size !== options.runtimeProfiles.length) {
19
+ throw new Error('Runtime profile names must be unique');
20
+ }
21
+ const runtimeProfile = (name) => {
22
+ const profile = profiles.get(name);
23
+ if (!profile)
24
+ throw new Error(`Runtime profile is not available: ${name}`);
25
+ return profile;
26
+ };
27
+ const environmentOption = () => new Option('--env <environment>', options.runtimeProfiles.length === 1
28
+ ? `Lark environment (${defaultProfile.name} only)`
29
+ : 'Lark environment')
30
+ .choices([...profiles.keys()])
31
+ .default(defaultProfile.name);
32
+ const program = new Command()
33
+ .name(options.commandName)
34
+ .description(options.description)
35
+ .version(options.version)
36
+ .showSuggestionAfterError()
37
+ .showHelpAfterError();
38
+ const auth = program
39
+ .command('auth')
40
+ .description('Manage lark-cli user access');
41
+ auth
42
+ .command('login')
43
+ .addOption(environmentOption())
44
+ .action(async (commandOptions) => login(runtimeProfile(commandOptions.env)));
45
+ auth
46
+ .command('logout')
47
+ .addOption(environmentOption())
48
+ .action(async (commandOptions) => logout(runtimeProfile(commandOptions.env)));
49
+ auth
50
+ .command('whoami')
51
+ .addOption(environmentOption())
52
+ .action(async (commandOptions) => printJson(await whoami(runtimeProfile(commandOptions.env))));
53
+ program
54
+ .command('doctor')
55
+ .description('Verify Node, lark-cli user auth, and database application services')
56
+ .addOption(environmentOption())
57
+ .action(async (commandOptions) => printJson(await doctor(runtimeProfile(commandOptions.env))));
58
+ if (options.includeSkillCommand !== false) {
59
+ const skill = program
60
+ .command('skill')
61
+ .description('Manage the Agent Skill');
62
+ skill
63
+ .command('install')
64
+ .description('Install or update the bundled lark-base-database Skill')
65
+ .addOption(new Option('--target <target>', 'Agent skills location')
66
+ .choices(['codex', 'agents', 'claude', 'all'])
67
+ .default('codex'))
68
+ .option('--dir <directory>', 'Custom Agent skills root')
69
+ .option('--force', 'Replace an existing installation', false)
70
+ .action((commandOptions) => {
71
+ if (commandOptions.dir && commandOptions.target !== 'codex') {
72
+ throw new Error('Use either --target or --dir, not both');
73
+ }
74
+ const installed = installSkill({
75
+ target: commandOptions.target,
76
+ directory: commandOptions.dir,
77
+ force: commandOptions.force,
78
+ });
79
+ printJson({ ok: true, installed });
80
+ });
81
+ }
82
+ const databaseSkill = program
83
+ .command('database-skill')
84
+ .description('Install and use an application capability Skill without a project directory');
85
+ databaseSkill
86
+ .command('install')
87
+ .requiredOption('--app-token <token>', 'Application token')
88
+ .requiredOption('--title <title>', 'Application name used for Skill discovery')
89
+ .option('--dir <directory>', 'Agent skills root (default: Codex user skills)')
90
+ .action((args) => {
91
+ printJson({
92
+ ok: true,
93
+ skill: installDatabaseSkill({
94
+ appToken: args.appToken,
95
+ title: args.title,
96
+ directory: args.dir,
97
+ profile: defaultProfile,
98
+ }),
99
+ });
100
+ });
101
+ databaseSkill
102
+ .command('discover')
103
+ .requiredOption('--app-token <token>', 'Application token')
104
+ .option('--page-size <count>', 'Functions per page', '20')
105
+ .option('--page-token <token>', 'Opaque discovery cursor')
106
+ .action(async (args) => {
107
+ const request = discoveryArgs(args.appToken, Number(args.pageSize), args.pageToken);
108
+ printJson(await runDatabaseSkillRequest(defaultProfile, request));
109
+ });
110
+ databaseSkill
111
+ .command('invoke')
112
+ .requiredOption('--app-token <token>', 'Application token')
113
+ .requiredOption('--function-id <id>', 'Discovered Function ID')
114
+ .requiredOption('--params <json>', 'Raw business params as JSON (not an envelope)')
115
+ .action(async (args) => {
116
+ const request = invocationArgs(args.appToken, args.functionId, args.params);
117
+ printJson(await runDatabaseSkillRequest(defaultProfile, request));
118
+ });
119
+ program
120
+ .command('init')
121
+ .description('Scaffold a database application using a new or existing Base')
122
+ .argument('[directory]', 'Project directory', '.')
123
+ .option('--schema <file>', 'Create a Base from this schema JSON file')
124
+ .option('--base-token <token>', 'Reuse and bind every table in an existing Base')
125
+ .option('--app-token <token>', 'Bind the selected Base to an existing application instead of creating another application')
126
+ .requiredOption('--name <name>', 'Application name; also names a Base created with --schema')
127
+ .option('--description <description>', 'Application description')
128
+ .option('--time-zone <timeZone>', 'Base time zone', 'Asia/Shanghai')
129
+ .addOption(environmentOption())
130
+ .action(async (directory, commandOptions) => {
131
+ const profile = runtimeProfile(commandOptions.env);
132
+ printJson(await initializeProject({
133
+ directory,
134
+ schema: commandOptions.schema,
135
+ baseToken: commandOptions.baseToken,
136
+ appToken: commandOptions.appToken,
137
+ name: commandOptions.name,
138
+ description: commandOptions.description,
139
+ timeZone: commandOptions.timeZone,
140
+ environment: commandOptions.env,
141
+ runtimeProfile: profile,
142
+ }));
143
+ });
144
+ program
145
+ .command('pull')
146
+ .description('Restore a database application from its source artifact and Catalog')
147
+ .argument('[directory]', 'Empty project directory', '.')
148
+ .requiredOption('--app-token <token>', 'Existing Application token')
149
+ .addOption(environmentOption())
150
+ .action(async (directory, commandOptions) => {
151
+ const profile = runtimeProfile(commandOptions.env);
152
+ printJson(await pullProject({
153
+ directory,
154
+ appToken: commandOptions.appToken,
155
+ environment: commandOptions.env,
156
+ runtimeProfile: profile,
157
+ }));
158
+ });
159
+ program
160
+ .command('deploy')
161
+ .description('Build and publish the current database application')
162
+ .argument('[directory]', 'Project directory', '.')
163
+ .option('--no-install', 'Reuse installed Page and FaaS dependencies')
164
+ .option('--force-install', 'Reinstall Page and FaaS dependencies', false)
165
+ .option('--production', 'Publish the verified artifact to Prod after Preview')
166
+ .option('--yes', 'Confirm the requested Prod publish non-interactively', false)
167
+ .action(async (directory, commandOptions) => {
168
+ if (commandOptions.yes && !commandOptions.production) {
169
+ throw new Error('--yes is only valid with --production');
170
+ }
171
+ printJson(await deployProject({
172
+ directory,
173
+ install: commandOptions.install,
174
+ forceInstall: commandOptions.forceInstall,
175
+ production: commandOptions.production,
176
+ yes: commandOptions.yes,
177
+ runtimeProfile: defaultProfile,
178
+ }));
179
+ });
180
+ program
181
+ .command('lark-cli')
182
+ .description('Run lark-cli with the current project routing (debug only)')
183
+ .argument('[arguments...]', 'Arguments passed unchanged to lark-cli')
184
+ .allowUnknownOption()
185
+ .action(async (args) => runProjectLarkCli(args, defaultProfile));
186
+ const deployments = program
187
+ .command('deployments')
188
+ .description('Inspect database application deployments');
189
+ deployments
190
+ .command('status')
191
+ .description('Read a publish task without creating a new deployment')
192
+ .argument('<publish-id>', 'Publish ID returned by deploy')
193
+ .argument('[directory]', 'Project directory', '.')
194
+ .option('--wait', 'Poll until the task finishes', false)
195
+ .action(async (publishId, directory, commandOptions) => printJson(await deploymentStatus(directory, publishId, commandOptions.wait, defaultProfile)));
196
+ return program;
197
+ }
198
+ export function runBaseDatabaseProgram(program) {
199
+ program.parseAsync().catch((error) => {
200
+ process.stderr.write(`${program.name()}: ${error instanceof Error ? error.message : String(error)}\n`);
201
+ process.exitCode = 1;
202
+ });
203
+ }
204
+ //# sourceMappingURL=program.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"program.js","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"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"}
package/dist/pull.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ import type { RuntimeProfile } from './environment.js';
2
+ interface PullOptions {
3
+ directory: string;
4
+ appToken: string;
5
+ environment: string;
6
+ runtimeProfile?: RuntimeProfile;
7
+ }
8
+ export declare function pullProject(options: PullOptions): Promise<{
9
+ ok: true;
10
+ environment: string;
11
+ workspace: string;
12
+ appToken: string;
13
+ source: 'artifact';
14
+ config: string;
15
+ catalog: string;
16
+ }>;
17
+ export {};
18
+ //# sourceMappingURL=pull.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../src/pull.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAgBvD,UAAU,WAAW;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAiXD,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW;;;;;YA+FxC,UAAU;;;GASvB"}
package/dist/pull.js ADDED
@@ -0,0 +1,324 @@
1
+ import { createWriteStream } from 'node:fs';
2
+ import { cp, lstat, mkdir, mkdtemp, readFile, readdir, rename, rm, stat, writeFile, } from 'node:fs/promises';
3
+ import { basename, dirname, join, posix, resolve } from 'node:path';
4
+ import { pipeline } from 'node:stream/promises';
5
+ import CRC32 from 'crc-32';
6
+ import yauzl from 'yauzl';
7
+ import { starterRoot } from './assets.js';
8
+ import { CONFIG_FILE_NAME, writeConfig } from './config.js';
9
+ import { ONLINE_RUNTIME_PROFILE, resolveRuntimeProfile, } from './environment.js';
10
+ import { responseData, runLarkCli, runLarkCliOutput } from './lark-cli.js';
11
+ const MAX_ARCHIVE_BYTES = 32 * 1024 * 1024;
12
+ const MAX_FILES = 20_000;
13
+ const MAX_EXPANDED_BYTES = 512 * 1024 * 1024;
14
+ const ALLOWED_ROOTS = new Set(['page', 'server-functions', 'sql']);
15
+ const APP_TOKEN_PATTERN = /^[A-Za-z0-9_-]{1,256}$/;
16
+ const UNIX_FILE_TYPE_MASK = 0o170000;
17
+ const UNIX_REGULAR_FILE = 0o100000;
18
+ const UNIX_DIRECTORY = 0o040000;
19
+ function pullError(code, message) {
20
+ return new Error(`${code}: ${message}`);
21
+ }
22
+ async function assertEmptyWorkspace(workspace) {
23
+ try {
24
+ const metadata = await lstat(workspace);
25
+ if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
26
+ throw pullError('WORKSPACE_NOT_EMPTY', 'Pull target must be an empty directory');
27
+ }
28
+ if ((await readdir(workspace)).length !== 0) {
29
+ throw pullError('WORKSPACE_NOT_EMPTY', 'Pull target must be completely empty');
30
+ }
31
+ return true;
32
+ }
33
+ catch (error) {
34
+ if (error.code === 'ENOENT')
35
+ return false;
36
+ throw error;
37
+ }
38
+ }
39
+ function validateEntryName(fileName, directory) {
40
+ if (fileName === '' ||
41
+ fileName.includes('\0') ||
42
+ fileName.includes('\\') ||
43
+ fileName.startsWith('/') ||
44
+ /^[A-Za-z]:/.test(fileName)) {
45
+ throw pullError('UNSAFE_SOURCE_ARCHIVE', `Unsafe ZIP entry path: ${fileName}`);
46
+ }
47
+ const withoutTrailingSlash = directory && fileName.endsWith('/') ? fileName.slice(0, -1) : fileName;
48
+ if (withoutTrailingSlash === '') {
49
+ throw pullError('UNSAFE_SOURCE_ARCHIVE', 'ZIP entry path must not be empty');
50
+ }
51
+ const normalized = posix.normalize(withoutTrailingSlash);
52
+ if (normalized !== withoutTrailingSlash ||
53
+ normalized === '..' ||
54
+ normalized.startsWith('../') ||
55
+ normalized
56
+ .split('/')
57
+ .some((part) => part === '' || part === '.' || part === '..')) {
58
+ throw pullError('UNSAFE_SOURCE_ARCHIVE', `Unsafe ZIP entry path: ${fileName}`);
59
+ }
60
+ const [root, ...rest] = normalized.split('/');
61
+ if (!root || !ALLOWED_ROOTS.has(root)) {
62
+ throw pullError('UNSUPPORTED_SOURCE_ENTRY', `ZIP entry is outside the supported source roots: ${fileName}`);
63
+ }
64
+ if (!directory && rest.length === 0) {
65
+ throw pullError('UNSAFE_SOURCE_ARCHIVE', `Source root must be a directory: ${fileName}`);
66
+ }
67
+ if (rest.some((part) => part === '.env' || part.startsWith('.env.'))) {
68
+ throw pullError('UNSAFE_SOURCE_ARCHIVE', `Environment files are not allowed in source archives: ${fileName}`);
69
+ }
70
+ return normalized;
71
+ }
72
+ function entryKind(entry) {
73
+ const directory = entry.fileName.endsWith('/');
74
+ const madeBy = entry.versionMadeBy >>> 8;
75
+ if (madeBy === 3) {
76
+ const mode = (entry.externalFileAttributes >>> 16) & 0xffff;
77
+ const kind = mode & UNIX_FILE_TYPE_MASK;
78
+ if (kind !== 0 && kind !== UNIX_REGULAR_FILE && kind !== UNIX_DIRECTORY) {
79
+ throw pullError('UNSAFE_SOURCE_ARCHIVE', `ZIP entry is not a regular file or directory: ${entry.fileName}`);
80
+ }
81
+ if (kind === UNIX_DIRECTORY && !directory) {
82
+ throw pullError('UNSAFE_SOURCE_ARCHIVE', `ZIP directory entry has an invalid name: ${entry.fileName}`);
83
+ }
84
+ if (kind === UNIX_REGULAR_FILE && directory) {
85
+ throw pullError('UNSAFE_SOURCE_ARCHIVE', `ZIP file entry has an invalid name: ${entry.fileName}`);
86
+ }
87
+ }
88
+ return directory ? 'directory' : 'file';
89
+ }
90
+ function openZip(archive) {
91
+ return new Promise((resolvePromise, reject) => {
92
+ yauzl.open(archive, { lazyEntries: true, autoClose: true, validateEntrySizes: true }, (error, zipFile) => {
93
+ if (error || !zipFile) {
94
+ reject(pullError('UNSUPPORTED_SOURCE_ARCHIVE', error?.message ?? 'Unable to open ZIP archive'));
95
+ return;
96
+ }
97
+ resolvePromise(zipFile);
98
+ });
99
+ });
100
+ }
101
+ function openEntryStream(zipFile, entry) {
102
+ return new Promise((resolvePromise, reject) => {
103
+ zipFile.openReadStream(entry, (error, stream) => {
104
+ if (error || !stream) {
105
+ reject(pullError('UNSAFE_SOURCE_ARCHIVE', error?.message ?? `Unable to read ${entry.fileName}`));
106
+ return;
107
+ }
108
+ resolvePromise(stream);
109
+ });
110
+ });
111
+ }
112
+ async function extractZip(archive, staging) {
113
+ const zipFile = await openZip(archive);
114
+ const paths = new Set();
115
+ let files = 0;
116
+ let expandedBytes = 0;
117
+ await new Promise((resolvePromise, reject) => {
118
+ let settled = false;
119
+ const finish = (error) => {
120
+ if (settled)
121
+ return;
122
+ settled = true;
123
+ if (error) {
124
+ zipFile.close();
125
+ reject(error);
126
+ }
127
+ else {
128
+ resolvePromise();
129
+ }
130
+ };
131
+ zipFile.once('error', finish);
132
+ zipFile.once('end', () => finish());
133
+ zipFile.on('entry', (entry) => {
134
+ void (async () => {
135
+ const kind = entryKind(entry);
136
+ const relativePath = validateEntryName(entry.fileName, kind === 'directory');
137
+ if (paths.has(relativePath)) {
138
+ throw pullError('UNSAFE_SOURCE_ARCHIVE', `ZIP contains a duplicate path: ${relativePath}`);
139
+ }
140
+ paths.add(relativePath);
141
+ const destination = join(staging, ...relativePath.split('/'));
142
+ if (kind === 'directory') {
143
+ await mkdir(destination, { recursive: true });
144
+ zipFile.readEntry();
145
+ return;
146
+ }
147
+ files += 1;
148
+ if (files > MAX_FILES) {
149
+ throw pullError('SOURCE_ARCHIVE_TOO_LARGE', `Source archive contains more than ${MAX_FILES} files`);
150
+ }
151
+ if (!Number.isSafeInteger(entry.uncompressedSize) ||
152
+ entry.uncompressedSize < 0) {
153
+ throw pullError('UNSAFE_SOURCE_ARCHIVE', `ZIP entry has an invalid size: ${entry.fileName}`);
154
+ }
155
+ expandedBytes += entry.uncompressedSize;
156
+ if (expandedBytes > MAX_EXPANDED_BYTES) {
157
+ throw pullError('SOURCE_ARCHIVE_TOO_LARGE', 'Expanded source archive exceeds 512 MiB');
158
+ }
159
+ await mkdir(dirname(destination), { recursive: true });
160
+ const stream = await openEntryStream(zipFile, entry);
161
+ let actualBytes = 0;
162
+ let crc = 0;
163
+ stream.on('data', (chunk) => {
164
+ actualBytes += chunk.length;
165
+ if (actualBytes > entry.uncompressedSize ||
166
+ actualBytes > MAX_EXPANDED_BYTES) {
167
+ stream.destroy(pullError('UNSAFE_SOURCE_ARCHIVE', `ZIP entry size does not match: ${entry.fileName}`));
168
+ return;
169
+ }
170
+ crc = CRC32.buf(chunk, crc);
171
+ });
172
+ await pipeline(stream, createWriteStream(destination, { flags: 'wx', mode: 0o644 }));
173
+ if (actualBytes !== entry.uncompressedSize ||
174
+ crc >>> 0 !== entry.crc32) {
175
+ throw pullError('UNSAFE_SOURCE_ARCHIVE', `ZIP entry integrity check failed: ${entry.fileName}`);
176
+ }
177
+ zipFile.readEntry();
178
+ })().catch(finish);
179
+ });
180
+ zipFile.readEntry();
181
+ });
182
+ }
183
+ async function validateSourceTree(staging) {
184
+ for (const directory of ALLOWED_ROOTS) {
185
+ let metadata;
186
+ try {
187
+ metadata = await lstat(join(staging, directory));
188
+ }
189
+ catch {
190
+ throw pullError('INVALID_SOURCE_ARCHIVE', `Source archive is missing ${directory}/`);
191
+ }
192
+ if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
193
+ throw pullError('INVALID_SOURCE_ARCHIVE', `Source archive ${directory}/ is not a directory`);
194
+ }
195
+ }
196
+ }
197
+ function catalogBinding(value) {
198
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
199
+ throw pullError('INVALID_CATALOG', 'Application Catalog must be an object');
200
+ }
201
+ const catalog = value;
202
+ if (!Array.isArray(catalog.tables) || catalog.tables.length === 0) {
203
+ throw pullError('INVALID_CATALOG', 'Application Catalog must contain at least one table');
204
+ }
205
+ const tables = catalog.tables.map((item) => {
206
+ if (!item || typeof item !== 'object' || Array.isArray(item)) {
207
+ throw pullError('INVALID_CATALOG', 'Application Catalog contains an invalid table');
208
+ }
209
+ const table = item;
210
+ if (typeof table.baseToken !== 'string' ||
211
+ table.baseToken.trim() === '' ||
212
+ typeof table.id !== 'string' ||
213
+ table.id.trim() === '') {
214
+ throw pullError('INVALID_CATALOG', 'Every Catalog table must contain baseToken and id');
215
+ }
216
+ return { baseToken: table.baseToken.trim(), id: table.id.trim() };
217
+ });
218
+ const baseTokens = [...new Set(tables.map((table) => table.baseToken))];
219
+ if (baseTokens.length !== 1) {
220
+ throw pullError('MULTIPLE_BASES_NOT_SUPPORTED', 'Artifact reconstruction currently supports exactly one Base');
221
+ }
222
+ const tableIds = [...new Set(tables.map((table) => table.id))];
223
+ if (tableIds.length !== tables.length) {
224
+ throw pullError('INVALID_CATALOG', 'Application Catalog contains duplicate table IDs');
225
+ }
226
+ return { catalog, baseToken: baseTokens[0], tableIds };
227
+ }
228
+ async function addAuxiliaryFiles(staging) {
229
+ await cp(join(starterRoot, 'README.md'), join(staging, 'README.md'));
230
+ await cp(join(starterRoot, 'gitignore'), join(staging, '.gitignore'));
231
+ await cp(join(starterRoot, 'prettierignore'), join(staging, '.prettierignore'));
232
+ }
233
+ async function replaceEmptyWorkspace(workspace, staging, existed) {
234
+ if (!existed) {
235
+ await rename(staging, workspace);
236
+ return;
237
+ }
238
+ if ((await readdir(workspace)).length !== 0) {
239
+ throw pullError('WORKSPACE_NOT_EMPTY', 'Pull target changed while source was being restored');
240
+ }
241
+ const backup = `${workspace}.pull-backup-${process.pid}-${Date.now()}`;
242
+ await rename(workspace, backup);
243
+ try {
244
+ await rename(staging, workspace);
245
+ }
246
+ catch (error) {
247
+ await rename(backup, workspace).catch(() => undefined);
248
+ throw error;
249
+ }
250
+ await rm(backup, { recursive: true, force: true });
251
+ }
252
+ export async function pullProject(options) {
253
+ const appToken = options.appToken.trim();
254
+ if (!APP_TOKEN_PATTERN.test(appToken)) {
255
+ throw pullError('INVALID_APP_TOKEN', 'A valid --app-token is required');
256
+ }
257
+ const runtimeProfile = resolveRuntimeProfile(options.environment, options.runtimeProfile ?? ONLINE_RUNTIME_PROFILE);
258
+ const workspace = resolve(options.directory);
259
+ const parent = dirname(workspace);
260
+ await mkdir(parent, { recursive: true });
261
+ const workspaceExisted = await assertEmptyWorkspace(workspace);
262
+ const temporaryRoot = await mkdtemp(join(parent, `.${basename(workspace)}.pull-`));
263
+ const archive = join(temporaryRoot, 'source.zip');
264
+ const staging = join(temporaryRoot, 'workspace');
265
+ await mkdir(staging);
266
+ try {
267
+ const env = await runtimeProfile.commandEnvironment();
268
+ runLarkCliOutput('GET', `/open-apis/base/v3/sites/${encodeURIComponent(appToken)}/source_code`, archive, env, parent);
269
+ const archiveMetadata = await stat(archive);
270
+ if (!archiveMetadata.isFile() || archiveMetadata.size === 0) {
271
+ throw pullError('UNSUPPORTED_SOURCE_ARCHIVE', 'source_code returned an empty artifact');
272
+ }
273
+ if (archiveMetadata.size > MAX_ARCHIVE_BYTES) {
274
+ throw pullError('SOURCE_ARCHIVE_TOO_LARGE', 'Compressed source archive exceeds 32 MiB');
275
+ }
276
+ const magic = (await readFile(archive)).subarray(0, 4);
277
+ if (magic.length < 4 ||
278
+ magic[0] !== 0x50 ||
279
+ magic[1] !== 0x4b ||
280
+ magic[2] !== 0x03 ||
281
+ magic[3] !== 0x04) {
282
+ throw pullError('UNSUPPORTED_SOURCE_ARCHIVE', 'source_code is not a supported ZIP archive');
283
+ }
284
+ await extractZip(archive, staging);
285
+ await validateSourceTree(staging);
286
+ const catalogResponse = runLarkCli([
287
+ 'api',
288
+ 'GET',
289
+ `/open-apis/base/v3/sites/${encodeURIComponent(appToken)}/catalog`,
290
+ ], env, parent);
291
+ const binding = catalogBinding(responseData(catalogResponse));
292
+ await writeFile(join(staging, 'catalog.json'), `${JSON.stringify(binding.catalog, null, 2)}\n`);
293
+ writeConfig(staging, {
294
+ configVersion: 1,
295
+ name: basename(workspace),
296
+ ...(runtimeProfile.name === ONLINE_RUNTIME_PROFILE.name
297
+ ? {}
298
+ : { runtimeProfile: runtimeProfile.name }),
299
+ appToken,
300
+ bindings: {
301
+ MAIN_BASE: {
302
+ type: 'base',
303
+ baseToken: binding.baseToken,
304
+ tableIds: binding.tableIds,
305
+ },
306
+ },
307
+ });
308
+ await addAuxiliaryFiles(staging);
309
+ await replaceEmptyWorkspace(workspace, staging, workspaceExisted);
310
+ return {
311
+ ok: true,
312
+ environment: runtimeProfile.name,
313
+ workspace,
314
+ appToken,
315
+ source: 'artifact',
316
+ config: join(workspace, CONFIG_FILE_NAME),
317
+ catalog: join(workspace, 'catalog.json'),
318
+ };
319
+ }
320
+ finally {
321
+ await rm(temporaryRoot, { recursive: true, force: true }).catch(() => undefined);
322
+ }
323
+ }
324
+ //# sourceMappingURL=pull.js.map