e10-ebuilder-prototype 0.5.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 (54) hide show
  1. package/README.md +113 -0
  2. package/dist/api.d.ts +12 -0
  3. package/dist/api.js +125 -0
  4. package/dist/application.d.ts +7 -0
  5. package/dist/application.js +16 -0
  6. package/dist/archive.d.ts +130 -0
  7. package/dist/archive.js +151 -0
  8. package/dist/capture.d.ts +15 -0
  9. package/dist/capture.js +440 -0
  10. package/dist/common.d.ts +20 -0
  11. package/dist/common.js +87 -0
  12. package/dist/dom.d.mts +1 -0
  13. package/dist/dom.mjs +58 -0
  14. package/dist/form-context.d.ts +3 -0
  15. package/dist/form-context.js +58 -0
  16. package/dist/form-runtime.d.mts +2 -0
  17. package/dist/form-runtime.mjs +149 -0
  18. package/dist/forms.d.ts +51 -0
  19. package/dist/forms.js +603 -0
  20. package/dist/html.d.ts +22 -0
  21. package/dist/html.js +427 -0
  22. package/dist/index.d.ts +2 -0
  23. package/dist/index.js +370 -0
  24. package/dist/menus.d.ts +32 -0
  25. package/dist/menus.js +330 -0
  26. package/dist/model.d.ts +164 -0
  27. package/dist/model.js +8 -0
  28. package/dist/offline-store.d.mts +5 -0
  29. package/dist/offline-store.mjs +80 -0
  30. package/dist/platform.d.ts +10 -0
  31. package/dist/platform.js +123 -0
  32. package/dist/readiness.d.ts +124 -0
  33. package/dist/readiness.js +529 -0
  34. package/dist/runtime-support.d.mts +52 -0
  35. package/dist/runtime-support.mjs +279 -0
  36. package/dist/site.d.ts +34 -0
  37. package/dist/site.js +195 -0
  38. package/dist/store.d.ts +90 -0
  39. package/dist/store.js +296 -0
  40. package/dist/templates/form-guide.md +539 -0
  41. package/dist/templates/index.html +803 -0
  42. package/dist/templates/placeholder.html +143 -0
  43. package/dist/templates/workflow-guide.md +95 -0
  44. package/dist/templates/workflow-presets.json +89 -0
  45. package/dist/temporary-records.d.ts +15 -0
  46. package/dist/temporary-records.js +286 -0
  47. package/dist/vendor/environment-auth.d.ts +61 -0
  48. package/dist/vendor/environment-auth.js +455 -0
  49. package/dist/workflow-runtime.d.mts +2 -0
  50. package/dist/workflow-runtime.mjs +298 -0
  51. package/dist/workflows.d.ts +28 -0
  52. package/dist/workflows.js +90 -0
  53. package/docs/PROTOCOL.md +299 -0
  54. package/package.json +45 -0
@@ -0,0 +1,61 @@
1
+ export type EnvironmentAuthStatus = {
2
+ authenticated: boolean;
3
+ status: 'missing' | 'invalid' | 'expired' | 'tenant-unavailable' | 'authenticated';
4
+ baseUrl?: string;
5
+ userId?: string;
6
+ tenantKey?: string;
7
+ profile?: string;
8
+ };
9
+ export type E10AuthContext = {
10
+ baseUrl: string;
11
+ userId: string;
12
+ tenantKey: string;
13
+ eteamsId: string;
14
+ };
15
+ export type EnvironmentAuthProfile = {
16
+ name: string;
17
+ active: boolean;
18
+ configured: boolean;
19
+ };
20
+ export type EnvironmentAuthPaths = {
21
+ stateRoot: string;
22
+ authRoot: string;
23
+ profilesDir: string;
24
+ activeProfileFile: string;
25
+ keyFile: string;
26
+ };
27
+ export declare class E10LoginRequiredError extends Error {
28
+ readonly authStatus: EnvironmentAuthStatus['status'];
29
+ readonly code = "E10_LOGIN_REQUIRED";
30
+ constructor(authStatus?: EnvironmentAuthStatus['status'], message?: string);
31
+ }
32
+ export declare function productStateRoot(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform, homeDirectory?: string): string;
33
+ export declare function environmentAuthPaths(env?: NodeJS.ProcessEnv): EnvironmentAuthPaths;
34
+ export declare function normalizeEnvironmentBaseUrl(value: string): string;
35
+ export declare function profileNameFromUrl(baseUrl: string): string;
36
+ export declare function readActiveEnvironmentAuthProfile(env?: NodeJS.ProcessEnv): string | undefined;
37
+ export declare function readEnvironmentAuthContext(_statePath?: string, env?: NodeJS.ProcessEnv): E10AuthContext;
38
+ export declare function readEnvironmentAuthStatus(_statePath?: string, env?: NodeJS.ProcessEnv): EnvironmentAuthStatus;
39
+ export declare const readEnvironmentAuthStatusSync: typeof readEnvironmentAuthStatus;
40
+ export declare function readTaskEnvironmentAuthStatus(_projectDir: string, env?: NodeJS.ProcessEnv): EnvironmentAuthStatus;
41
+ export declare const readTaskEnvironmentAuthStatusSync: typeof readTaskEnvironmentAuthStatus;
42
+ export declare function setEnvironmentAuth(options: {
43
+ baseUrl: string;
44
+ eteamsId: string;
45
+ fetchImpl?: typeof fetch;
46
+ env?: NodeJS.ProcessEnv;
47
+ }): Promise<{
48
+ profile: string;
49
+ baseUrl: string;
50
+ userId: string;
51
+ tenantKey: string;
52
+ }>;
53
+ export declare function verifyEnvironmentAuth(options?: {
54
+ fetchImpl?: typeof fetch;
55
+ env?: NodeJS.ProcessEnv;
56
+ }): Promise<EnvironmentAuthStatus>;
57
+ export declare function listEnvironmentAuthProfiles(env?: NodeJS.ProcessEnv): EnvironmentAuthProfile[];
58
+ export declare function useEnvironmentAuthProfile(name: string, env?: NodeJS.ProcessEnv): E10AuthContext;
59
+ export declare function assertEnvironmentForDataAccess(requestedBaseUrl?: string, _statePath?: string, authContext?: E10AuthContext): EnvironmentAuthStatus;
60
+ export declare function assertEnvironmentAuth(status: EnvironmentAuthStatus): void;
61
+ export declare function environmentAuthGuidance(_statusOrNext: EnvironmentAuthStatus | string, _requestedBaseUrl?: string): string[];
@@ -0,0 +1,455 @@
1
+ import { productStateRoot as sharedStateRoot, renameWithRetrySync, reservedWindowsName, environmentValue, } from '../runtime-support.mjs';
2
+ // Adapted from ui-code-agent environment-auth.ts (local 0.1.63), 2026-09-11.
3
+ import { createCipheriv, createDecipheriv, randomBytes, timingSafeEqual } from 'node:crypto';
4
+ import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync, } from 'node:fs';
5
+ import { createRequire } from 'node:module';
6
+ import os from 'node:os';
7
+ import path from 'node:path';
8
+ const ALGORITHM = 'aes-256-gcm';
9
+ const IV_LENGTH = 12;
10
+ const TAG_LENGTH = 16;
11
+ // Stable storage identity preserves authentication across the product rename.
12
+ const KEYCHAIN_SERVICE = 'e10-page-capture';
13
+ const KEYCHAIN_ACCOUNT = 'auth-key';
14
+ const AUTH_TIMEOUT_MS = 30_000;
15
+ const PROFILE_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
16
+ const require = createRequire(import.meta.url);
17
+ const keyCache = new Map();
18
+ export class E10LoginRequiredError extends Error {
19
+ authStatus;
20
+ code = 'E10_LOGIN_REQUIRED';
21
+ constructor(authStatus = 'missing', message = 'e10-ebuilder-prototype 尚未配置可用的 E10 认证') {
22
+ super(message);
23
+ this.authStatus = authStatus;
24
+ this.name = 'E10LoginRequiredError';
25
+ }
26
+ }
27
+ export function productStateRoot(env = process.env, platform = process.platform, homeDirectory = os.homedir()) {
28
+ return sharedStateRoot(env, platform, homeDirectory);
29
+ }
30
+ export function environmentAuthPaths(env = process.env) {
31
+ const stateRoot = productStateRoot(env);
32
+ const authRoot = path.join(stateRoot, 'auth');
33
+ return {
34
+ stateRoot,
35
+ authRoot,
36
+ profilesDir: path.join(authRoot, 'profiles'),
37
+ activeProfileFile: path.join(authRoot, 'profile'),
38
+ keyFile: path.join(authRoot, '.key'),
39
+ };
40
+ }
41
+ export function normalizeEnvironmentBaseUrl(value) {
42
+ try {
43
+ const url = new URL(value.trim());
44
+ if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password)
45
+ throw new Error();
46
+ return url.origin;
47
+ }
48
+ catch {
49
+ throw new Error('E10 环境地址必须是有效且不含账号密码的 http/https URL');
50
+ }
51
+ }
52
+ function validIdentity(value) {
53
+ return (typeof value === 'string' &&
54
+ Boolean(value.trim()) &&
55
+ value.length <= 1024 &&
56
+ !/[\r\n]/u.test(value));
57
+ }
58
+ function normalizeEteamsId(value) {
59
+ const normalized = value.trim();
60
+ if (!normalized || normalized.length > 8192 || /[\s;]/u.test(normalized)) {
61
+ throw new Error('ETEAMSID 格式无效');
62
+ }
63
+ return normalized;
64
+ }
65
+ function secureDirectory(directory) {
66
+ mkdirSync(directory, { recursive: true, mode: 0o700 });
67
+ try {
68
+ chmodSync(directory, 0o700);
69
+ }
70
+ catch {
71
+ // Windows does not implement POSIX permission bits.
72
+ }
73
+ }
74
+ function atomicWrite(filename, content, mode = 0o600) {
75
+ secureDirectory(path.dirname(filename));
76
+ const temporary = `${filename}.${process.pid}.${randomBytes(6).toString('hex')}.tmp`;
77
+ try {
78
+ writeFileSync(temporary, content, { encoding: 'utf8', mode, flag: 'wx' });
79
+ renameWithRetrySync(temporary, filename);
80
+ try {
81
+ chmodSync(filename, mode);
82
+ }
83
+ catch {
84
+ // Windows does not implement POSIX permission bits.
85
+ }
86
+ }
87
+ finally {
88
+ rmSync(temporary, { force: true });
89
+ }
90
+ }
91
+ function validKey(value) {
92
+ if (!value || !/^[A-Za-z0-9+/=]{40,}$/u.test(value))
93
+ return undefined;
94
+ const key = Buffer.from(value, 'base64');
95
+ return key.length === 32 ? key : undefined;
96
+ }
97
+ function fileEncryptionKey(paths) {
98
+ if (existsSync(paths.keyFile)) {
99
+ try {
100
+ const stored = validKey(readFileSync(paths.keyFile, 'utf8').trim());
101
+ if (stored)
102
+ return stored;
103
+ }
104
+ catch {
105
+ // Replace malformed fallback state with a new key.
106
+ }
107
+ }
108
+ const fresh = randomBytes(32);
109
+ atomicWrite(paths.keyFile, `${fresh.toString('base64')}\n`);
110
+ return fresh;
111
+ }
112
+ function keychainEncryptionKey() {
113
+ try {
114
+ const module = require('@napi-rs/keyring');
115
+ const entry = new module.Entry(KEYCHAIN_SERVICE, KEYCHAIN_ACCOUNT);
116
+ let stored = null;
117
+ try {
118
+ stored = entry.getPassword();
119
+ }
120
+ catch {
121
+ // A missing keychain entry is expected on first use.
122
+ }
123
+ const existing = validKey(stored);
124
+ if (existing)
125
+ return existing;
126
+ const fresh = randomBytes(32);
127
+ entry.setPassword(fresh.toString('base64'));
128
+ return fresh;
129
+ }
130
+ catch {
131
+ return undefined;
132
+ }
133
+ }
134
+ function encryptionKey(env = process.env) {
135
+ const paths = environmentAuthPaths(env);
136
+ const cached = keyCache.get(paths.authRoot);
137
+ if (cached)
138
+ return cached;
139
+ const key = environmentValue(env, 'E10_PAGE_CAPTURE_HOME')?.trim()
140
+ ? undefined
141
+ : keychainEncryptionKey();
142
+ const resolved = key ?? fileEncryptionKey(paths);
143
+ keyCache.set(paths.authRoot, resolved);
144
+ return resolved;
145
+ }
146
+ function encryptAuth(data, env = process.env) {
147
+ const iv = randomBytes(IV_LENGTH);
148
+ const cipher = createCipheriv(ALGORITHM, encryptionKey(env), iv, {
149
+ authTagLength: TAG_LENGTH,
150
+ });
151
+ const encrypted = Buffer.concat([cipher.update(JSON.stringify(data), 'utf8'), cipher.final()]);
152
+ return {
153
+ enc: true,
154
+ v: 1,
155
+ data: Buffer.concat([iv, encrypted, cipher.getAuthTag()]).toString('base64'),
156
+ };
157
+ }
158
+ function decryptAuth(wrapper, env = process.env) {
159
+ try {
160
+ const combined = Buffer.from(wrapper.data, 'base64');
161
+ if (combined.length < IV_LENGTH + TAG_LENGTH + 1)
162
+ return undefined;
163
+ const iv = combined.subarray(0, IV_LENGTH);
164
+ const tag = combined.subarray(combined.length - TAG_LENGTH);
165
+ const encrypted = combined.subarray(IV_LENGTH, combined.length - TAG_LENGTH);
166
+ const decipher = createDecipheriv(ALGORITHM, encryptionKey(env), iv, {
167
+ authTagLength: TAG_LENGTH,
168
+ });
169
+ decipher.setAuthTag(tag);
170
+ return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString('utf8');
171
+ }
172
+ catch {
173
+ return undefined;
174
+ }
175
+ }
176
+ function asRecord(value) {
177
+ return value && typeof value === 'object' && !Array.isArray(value)
178
+ ? value
179
+ : undefined;
180
+ }
181
+ function profileName(value) {
182
+ const normalized = value.trim();
183
+ if (!PROFILE_PATTERN.test(normalized)) {
184
+ throw new Error('Profile 名称只能包含字母、数字、点、下划线和连字符');
185
+ }
186
+ return normalized;
187
+ }
188
+ export function profileNameFromUrl(baseUrl) {
189
+ const host = new URL(normalizeEnvironmentBaseUrl(baseUrl)).hostname;
190
+ const parts = host.split('.').filter(Boolean);
191
+ const candidate = parts.length >= 2 ? parts.at(-2) : host;
192
+ const normalized = candidate.replace(/[^A-Za-z0-9._-]/gu, '-').slice(0, 64);
193
+ return profileName(reservedWindowsName(normalized) ? `env-${normalized}` : normalized || 'default');
194
+ }
195
+ function profilePaths(name, env = process.env) {
196
+ const directory = path.join(environmentAuthPaths(env).profilesDir, profileName(name));
197
+ return {
198
+ directory,
199
+ authFile: path.join(directory, 'auth'),
200
+ configFile: path.join(directory, 'config.json'),
201
+ };
202
+ }
203
+ export function readActiveEnvironmentAuthProfile(env = process.env) {
204
+ const filename = environmentAuthPaths(env).activeProfileFile;
205
+ if (!existsSync(filename))
206
+ return undefined;
207
+ try {
208
+ return profileName(readFileSync(filename, 'utf8').trim());
209
+ }
210
+ catch {
211
+ throw new E10LoginRequiredError('invalid', '当前 E10 Profile 状态无效');
212
+ }
213
+ }
214
+ function validateStoredAuth(value) {
215
+ const record = asRecord(value);
216
+ const cookies = asRecord(record?.cookies);
217
+ const eteamsId = typeof cookies?.ETEAMSID === 'string' ? normalizeEteamsId(cookies.ETEAMSID) : '';
218
+ const userId = validIdentity(record?.userId) ? record.userId.trim() : '';
219
+ const tenantKey = validIdentity(record?.tenantKey) ? record.tenantKey.trim() : '';
220
+ const baseUrl = typeof record?.baseUrl === 'string' ? normalizeEnvironmentBaseUrl(record.baseUrl) : '';
221
+ const passportUrl = typeof record?.passportUrl === 'string'
222
+ ? normalizeEnvironmentBaseUrl(record.passportUrl)
223
+ : baseUrl;
224
+ if (!eteamsId || !userId || !tenantKey || !baseUrl) {
225
+ throw new E10LoginRequiredError('invalid', '当前 E10 Profile 认证数据无效');
226
+ }
227
+ return { cookies: { ETEAMSID: eteamsId }, userId, tenantKey, baseUrl, passportUrl };
228
+ }
229
+ function readStoredAuth(name, env = process.env) {
230
+ const filename = profilePaths(name, env).authFile;
231
+ if (!existsSync(filename))
232
+ throw new E10LoginRequiredError('missing');
233
+ try {
234
+ const wrapper = asRecord(JSON.parse(readFileSync(filename, 'utf8')));
235
+ if (wrapper?.enc !== true || wrapper.v !== 1 || typeof wrapper.data !== 'string') {
236
+ throw new Error();
237
+ }
238
+ const plaintext = decryptAuth(wrapper, env);
239
+ if (!plaintext)
240
+ throw new Error();
241
+ return validateStoredAuth(JSON.parse(plaintext));
242
+ }
243
+ catch (error) {
244
+ if (error instanceof E10LoginRequiredError)
245
+ throw error;
246
+ throw new E10LoginRequiredError('invalid', '当前 E10 Profile 无法读取或解密');
247
+ }
248
+ }
249
+ function writeActiveProfile(name, env = process.env) {
250
+ atomicWrite(environmentAuthPaths(env).activeProfileFile, `${profileName(name)}\n`);
251
+ }
252
+ function saveStoredAuth(name, data, env = process.env) {
253
+ const files = profilePaths(name, env);
254
+ secureDirectory(files.directory);
255
+ atomicWrite(files.authFile, `${JSON.stringify(encryptAuth(data, env))}\n`);
256
+ atomicWrite(files.configFile, `${JSON.stringify({
257
+ baseUrl: data.baseUrl,
258
+ passportUrl: data.passportUrl,
259
+ tenantKey: data.tenantKey,
260
+ }, null, 2)}\n`);
261
+ writeActiveProfile(name, env);
262
+ }
263
+ export function readEnvironmentAuthContext(_statePath, env = process.env) {
264
+ const active = readActiveEnvironmentAuthProfile(env);
265
+ if (!active)
266
+ throw new E10LoginRequiredError('missing');
267
+ const data = readStoredAuth(active, env);
268
+ return {
269
+ baseUrl: data.baseUrl,
270
+ userId: data.userId,
271
+ tenantKey: data.tenantKey,
272
+ eteamsId: data.cookies.ETEAMSID,
273
+ };
274
+ }
275
+ export function readEnvironmentAuthStatus(_statePath, env = process.env) {
276
+ try {
277
+ const profile = readActiveEnvironmentAuthProfile(env);
278
+ const auth = readEnvironmentAuthContext(undefined, env);
279
+ return { authenticated: true, status: 'authenticated', profile, ...auth };
280
+ }
281
+ catch (error) {
282
+ return {
283
+ authenticated: false,
284
+ status: error instanceof E10LoginRequiredError ? error.authStatus : 'invalid',
285
+ };
286
+ }
287
+ }
288
+ export const readEnvironmentAuthStatusSync = readEnvironmentAuthStatus;
289
+ export function readTaskEnvironmentAuthStatus(_projectDir, env = process.env) {
290
+ return readEnvironmentAuthStatus(undefined, env);
291
+ }
292
+ export const readTaskEnvironmentAuthStatusSync = readTaskEnvironmentAuthStatus;
293
+ function nestedString(record, pathParts) {
294
+ let cursor = record;
295
+ for (const part of pathParts)
296
+ cursor = asRecord(cursor)?.[part];
297
+ return validIdentity(cursor) ? cursor.trim() : '';
298
+ }
299
+ function responseFailed(payload) {
300
+ if (payload.success === false || payload.status === false || payload.fail === true)
301
+ return true;
302
+ const code = payload.code ?? payload.statusCode;
303
+ return code !== undefined && !['0', '200'].includes(String(code));
304
+ }
305
+ function responseMessage(payload) {
306
+ const message = payload.msg ?? payload.message;
307
+ return validIdentity(message) ? message.trim().slice(0, 300) : '';
308
+ }
309
+ async function queryAuthIdentity(baseUrl, eteamsId, fetchImpl = fetch) {
310
+ const controller = new AbortController();
311
+ const timer = setTimeout(() => controller.abort(), AUTH_TIMEOUT_MS);
312
+ let response;
313
+ try {
314
+ const endpoint = new URL('/api/baseserver/layout/teamsCheck', baseUrl);
315
+ endpoint.search = new URLSearchParams({
316
+ clientType: 'not_xinchuang',
317
+ client: 'WEB',
318
+ domainName: baseUrl,
319
+ }).toString();
320
+ response = await fetchImpl(endpoint, {
321
+ method: 'POST',
322
+ headers: {
323
+ accept: 'application/json',
324
+ 'content-type': 'application/json',
325
+ cookie: `ETEAMSID=${eteamsId}`,
326
+ origin: baseUrl,
327
+ },
328
+ redirect: 'manual',
329
+ signal: controller.signal,
330
+ });
331
+ }
332
+ catch (error) {
333
+ const name = error instanceof Error ? error.name : '';
334
+ throw new Error(name === 'AbortError' ? 'E10 认证检查超时' : '无法连接 E10 环境进行认证检查');
335
+ }
336
+ finally {
337
+ clearTimeout(timer);
338
+ }
339
+ if (response.status >= 300 && response.status < 400) {
340
+ throw new E10LoginRequiredError('expired', 'ETEAMSID 已失效或环境要求重新登录');
341
+ }
342
+ if (!response.ok) {
343
+ throw new E10LoginRequiredError(response.status === 401 || response.status === 403 ? 'expired' : 'invalid', `E10 认证检查返回 HTTP ${response.status}`);
344
+ }
345
+ let payload;
346
+ try {
347
+ payload = asRecord(await response.json()) ?? {};
348
+ }
349
+ catch {
350
+ throw new E10LoginRequiredError('invalid', 'E10 认证检查未返回有效 JSON');
351
+ }
352
+ if (responseFailed(payload)) {
353
+ const message = responseMessage(payload);
354
+ throw new E10LoginRequiredError('expired', message ? `E10 认证检查失败: ${message}` : 'E10 认证检查失败');
355
+ }
356
+ const userId = response.headers.get('employeeId')?.trim() ||
357
+ nestedString(payload, ['data', 'employeeId']) ||
358
+ nestedString(payload, ['data', 'id']) ||
359
+ nestedString(payload, ['employeeId']);
360
+ const tenantKey = nestedString(payload, ['currentUser', 'tenantKey']) ||
361
+ nestedString(payload, ['currentTenant', 'tenantKey']) ||
362
+ nestedString(payload, ['data', 'tenantKey']) ||
363
+ nestedString(payload, ['data', 'tenantkey']) ||
364
+ nestedString(payload, ['tenantKey']) ||
365
+ nestedString(payload, ['tenantkey']);
366
+ if (!validIdentity(userId)) {
367
+ throw new E10LoginRequiredError('invalid', 'E10 认证检查未返回 userId');
368
+ }
369
+ if (!validIdentity(tenantKey)) {
370
+ throw new E10LoginRequiredError('tenant-unavailable', 'E10 认证检查未返回 tenantKey');
371
+ }
372
+ return { userId: userId.trim(), tenantKey: tenantKey.trim() };
373
+ }
374
+ export async function setEnvironmentAuth(options) {
375
+ const env = options.env ?? process.env;
376
+ const baseUrl = normalizeEnvironmentBaseUrl(options.baseUrl);
377
+ const eteamsId = normalizeEteamsId(options.eteamsId);
378
+ const identity = await queryAuthIdentity(baseUrl, eteamsId, options.fetchImpl);
379
+ const profile = profileNameFromUrl(baseUrl);
380
+ saveStoredAuth(profile, {
381
+ cookies: { ETEAMSID: eteamsId },
382
+ userId: identity.userId,
383
+ tenantKey: identity.tenantKey,
384
+ baseUrl,
385
+ passportUrl: baseUrl,
386
+ }, env);
387
+ return { profile, baseUrl, ...identity };
388
+ }
389
+ function equalIdentity(left, right) {
390
+ const leftBuffer = Buffer.from(left);
391
+ const rightBuffer = Buffer.from(right);
392
+ return leftBuffer.length === rightBuffer.length && timingSafeEqual(leftBuffer, rightBuffer);
393
+ }
394
+ export async function verifyEnvironmentAuth(options = {}) {
395
+ const env = options.env ?? process.env;
396
+ const profile = readActiveEnvironmentAuthProfile(env);
397
+ const auth = readEnvironmentAuthContext(undefined, env);
398
+ const identity = await queryAuthIdentity(auth.baseUrl, auth.eteamsId, options.fetchImpl);
399
+ if (!equalIdentity(identity.userId, auth.userId) ||
400
+ !equalIdentity(identity.tenantKey, auth.tenantKey)) {
401
+ throw new E10LoginRequiredError('invalid', 'E10 当前身份与本地 Profile 不一致,请重新执行 auth set');
402
+ }
403
+ return { authenticated: true, status: 'authenticated', profile, ...auth };
404
+ }
405
+ export function listEnvironmentAuthProfiles(env = process.env) {
406
+ const paths = environmentAuthPaths(env);
407
+ const active = readActiveEnvironmentAuthProfile(env);
408
+ if (!existsSync(paths.profilesDir))
409
+ return [];
410
+ return readdirSync(paths.profilesDir, { withFileTypes: true })
411
+ .filter((entry) => entry.isDirectory() && PROFILE_PATTERN.test(entry.name))
412
+ .map((entry) => ({
413
+ name: entry.name,
414
+ active: entry.name === active,
415
+ configured: existsSync(profilePaths(entry.name, env).authFile),
416
+ }))
417
+ .sort((left, right) => left.name.localeCompare(right.name));
418
+ }
419
+ export function useEnvironmentAuthProfile(name, env = process.env) {
420
+ const normalized = profileName(name);
421
+ const auth = readStoredAuth(normalized, env);
422
+ writeActiveProfile(normalized, env);
423
+ return {
424
+ baseUrl: auth.baseUrl,
425
+ userId: auth.userId,
426
+ tenantKey: auth.tenantKey,
427
+ eteamsId: auth.cookies.ETEAMSID,
428
+ };
429
+ }
430
+ export function assertEnvironmentForDataAccess(requestedBaseUrl, _statePath, authContext) {
431
+ const auth = authContext ?? readEnvironmentAuthContext();
432
+ if (requestedBaseUrl &&
433
+ normalizeEnvironmentBaseUrl(requestedBaseUrl) !== normalizeEnvironmentBaseUrl(auth.baseUrl)) {
434
+ const error = new Error('用户指定的 E10 系统环境与当前 e10-ebuilder-prototype Profile 不一致');
435
+ error.authStatus = { authenticated: true, status: 'authenticated', ...auth };
436
+ throw error;
437
+ }
438
+ return { authenticated: true, status: 'authenticated', ...auth };
439
+ }
440
+ export function assertEnvironmentAuth(status) {
441
+ if (!status.authenticated || status.status !== 'authenticated') {
442
+ throw new E10LoginRequiredError(status.status);
443
+ }
444
+ }
445
+ export function environmentAuthGuidance(_statusOrNext, _requestedBaseUrl) {
446
+ return [
447
+ '# 需要配置 E10 认证',
448
+ '',
449
+ '当前命令需要有效的 E10 认证。请使用 e10-ebuilder-prototype 自带的 auth set 配置当前环境。',
450
+ '不需要安装或调用额外的 E10 登录 Skill。',
451
+ '',
452
+ 'FIX: E10_LOGIN_REQUIRED: 当前本地 Profile 缺失、无效或认证已失效。',
453
+ 'NEXT: e10-ebuilder-prototype auth set --eteamsid <ETEAMSID> --base-url <E10环境地址>',
454
+ ];
455
+ }
@@ -0,0 +1,2 @@
1
+ export function workflowRuntime(templates: any): string;
2
+ export function attachWorkflowRuntime(source: any, templates: any): any;