@lark-base-open/base-database-cli 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +4 -0
- package/dist/agent-tools.d.ts +1 -0
- package/dist/agent-tools.d.ts.map +1 -1
- package/dist/agent-tools.js +3 -1
- package/dist/agent-tools.js.map +1 -1
- package/dist/app-builder/core.d.mts.map +1 -1
- package/dist/app-builder/core.mjs +8 -4
- package/dist/app-builder/core.mjs.map +1 -1
- package/dist/app-builder/package.json +1 -1
- package/dist/bot-handoff.d.ts +16 -0
- package/dist/bot-handoff.d.ts.map +1 -0
- package/dist/bot-handoff.js +165 -0
- package/dist/bot-handoff.js.map +1 -0
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +13 -1
- package/dist/check.js.map +1 -1
- package/dist/contract-conversion.d.ts +1 -0
- package/dist/contract-conversion.d.ts.map +1 -1
- package/dist/contract-conversion.js +11 -0
- package/dist/contract-conversion.js.map +1 -1
- package/dist/contract-dependencies.d.ts +16 -0
- package/dist/contract-dependencies.d.ts.map +1 -0
- package/dist/contract-dependencies.js +52 -0
- package/dist/contract-dependencies.js.map +1 -0
- package/dist/distribution-commands.d.ts.map +1 -1
- package/dist/distribution-commands.js +10 -5
- package/dist/distribution-commands.js.map +1 -1
- package/dist/function-contracts.d.ts.map +1 -1
- package/dist/function-contracts.js +10 -15
- package/dist/function-contracts.js.map +1 -1
- package/dist/init-journal.d.ts +10 -0
- package/dist/init-journal.d.ts.map +1 -1
- package/dist/init-journal.js +5 -2
- package/dist/init-journal.js.map +1 -1
- package/dist/init.d.ts +29 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +201 -19
- package/dist/init.js.map +1 -1
- package/dist/lark-cli.d.ts +7 -0
- package/dist/lark-cli.d.ts.map +1 -1
- package/dist/lark-cli.js +113 -17
- package/dist/lark-cli.js.map +1 -1
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +12 -1
- package/dist/program.js.map +1 -1
- package/dist/sdk-codegen.d.ts +3 -1
- package/dist/sdk-codegen.d.ts.map +1 -1
- package/dist/sdk-codegen.js +37 -13
- package/dist/sdk-codegen.js.map +1 -1
- package/dist/sdk-compatibility.js +1 -1
- package/dist/sdk-compatibility.js.map +1 -1
- package/package.json +1 -1
- package/skills/lark-base-database/SKILL.md +3 -1
- package/skills/lark-base-database/references/base-table-design.md +37 -0
- package/skills/lark-base-database/references/collaborator-handoff.md +62 -0
- package/skills/lark-base-database/references/database-function.md +1 -1
- package/skills/lark-base-database/references/development.md +13 -1
- package/skills/lark-base-database/references/faas.md +6 -3
- package/skills/lark-base-database/references/mutations.md +1 -1
- package/skills/lark-base-database/references/permission-design.md +1 -0
- package/skills/lark-base-database/references/release.md +1 -1
- package/skills/lark-base-database/references/style-guide.md +1 -1
- package/skills/lark-base-database/references/workspace.md +18 -5
- package/templates/database-starter-v2/README.md +1 -1
- package/templates/database-starter-v2/package-lock.json +5 -5
- package/templates/database-starter-v2/page/package.json +1 -1
- package/templates/database-starter-v2/server/package.json +1 -1
package/dist/init.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type RuntimeProfile } from './environment.js';
|
|
2
|
+
import { type HandoffGrant, type HandoffTarget } from './bot-handoff.js';
|
|
2
3
|
import type { CreatedTable } from './types.js';
|
|
3
4
|
export interface InitOptions {
|
|
4
5
|
larkProfile?: string;
|
|
@@ -13,15 +14,17 @@ export interface InitOptions {
|
|
|
13
14
|
environment: string;
|
|
14
15
|
resume?: boolean;
|
|
15
16
|
runtimeProfile?: RuntimeProfile;
|
|
17
|
+
grantAdmin?: string;
|
|
16
18
|
}
|
|
17
19
|
export declare function initializeProject(options: InitOptions): Promise<{
|
|
18
20
|
ok: true;
|
|
19
|
-
warnings:
|
|
21
|
+
warnings: string[];
|
|
20
22
|
initialization: {
|
|
21
23
|
journal: string;
|
|
22
24
|
status: 'succeeded';
|
|
23
25
|
resumed?: boolean | undefined;
|
|
24
26
|
};
|
|
27
|
+
handoff?: HandoffSummary | undefined;
|
|
25
28
|
environment: string;
|
|
26
29
|
workspace: string;
|
|
27
30
|
larkProfile?: string | undefined;
|
|
@@ -91,5 +94,30 @@ export declare function initializeProject(options: InitOptions): Promise<{
|
|
|
91
94
|
command: string;
|
|
92
95
|
description: string;
|
|
93
96
|
})[];
|
|
97
|
+
} | {
|
|
98
|
+
ok: true;
|
|
99
|
+
warnings: string[];
|
|
100
|
+
initialization: {
|
|
101
|
+
journal: string;
|
|
102
|
+
status: 'succeeded';
|
|
103
|
+
resumed: boolean;
|
|
104
|
+
handoffOnly: boolean;
|
|
105
|
+
};
|
|
106
|
+
handoff: HandoffSummary;
|
|
107
|
+
environment: string;
|
|
108
|
+
workspace: string;
|
|
109
|
+
larkProfile?: string | undefined;
|
|
110
|
+
baseToken?: string | undefined;
|
|
111
|
+
appToken?: string | undefined;
|
|
94
112
|
}>;
|
|
113
|
+
export interface HandoffSummary {
|
|
114
|
+
identity: 'bot';
|
|
115
|
+
target?: HandoffTarget;
|
|
116
|
+
base: HandoffGrant & {
|
|
117
|
+
perm: 'full_access';
|
|
118
|
+
};
|
|
119
|
+
app: HandoffGrant & {
|
|
120
|
+
role: 'admin';
|
|
121
|
+
};
|
|
122
|
+
}
|
|
95
123
|
//# sourceMappingURL=init.d.ts.map
|
package/dist/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AAoCA,OAAO,EAGL,KAAK,cAAc,EACpB,MAAM,kBAAkB,CAAC;AAS1B,OAAO,EAKL,KAAK,YAAY,EACjB,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAiB,YAAY,EAAE,MAAM,YAAY,CAAC;AA8S9D,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAszBD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,WAAW;;;;;gBAilB5C,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YA4CjB,SAAS;YACT,MAAM;YACN,OAAO;YACP,MAAM;;;;QAIR,MAAM,EAAE,aAAa;QACrB,YAAY,EAAE,cAAc;QAC5B,OAAO;QACP,KAAK,EAAE,MAAM;QACb,aAAa,EAAE,SAAS;QACxB,WAAW;;;;;;;;;;;;;;;;;QAwPb,OAAO;QACP,MAAM,EAAE,WAAW;QACnB,OAAO;QACP,WAAW;;;;;;;;GApNhB;AAiJD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,KAAK,CAAC;IAChB,MAAM,CAAC,EAAE,aAAa,CAAC;IACvB,IAAI,EAAE,YAAY,GAAG;QAAE,IAAI,EAAE,aAAa,CAAA;KAAE,CAAC;IAC7C,GAAG,EAAE,YAAY,GAAG;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,CAAC;CACvC"}
|
package/dist/init.js
CHANGED
|
@@ -6,12 +6,13 @@ import { createHash } from 'node:crypto';
|
|
|
6
6
|
import { InitJournal, InitFailureError, assertNoInitReceipt, } from './init-journal.js';
|
|
7
7
|
import { readInitSchema, InitSchemaError } from './init-schema.js';
|
|
8
8
|
import { createFieldsWithRetry } from './init-field-retry.js';
|
|
9
|
-
import { isRecord } from './lark-cli-error.js';
|
|
9
|
+
import { isRecord, userAuthorizationHint } from './lark-cli-error.js';
|
|
10
10
|
import { starterRoot as legacyStarterRoot } from './assets.js';
|
|
11
11
|
import { DATABASE_FUNCTIONS_DIRECTORY, LEGACY_DATABASE_FUNCTIONS_DIRECTORY, databaseFunctionsDirectory, } from './project-layout.js';
|
|
12
12
|
import { CONFIG_FILE_NAME, LEGACY_TEMPLATE, readConfig, writeConfig, } from './config.js';
|
|
13
13
|
import { ONLINE_RUNTIME_PROFILE, resolveRuntimeProfile, } from './environment.js';
|
|
14
|
-
import { responseData, runLarkCli, resolveLarkProfileEnvironment, } from './lark-cli.js';
|
|
14
|
+
import { currentCliUserOpenId, responseData, runLarkCli, resolveLarkIdentity, resolveLarkProfileEnvironment, } from './lark-cli.js';
|
|
15
|
+
import { grantBaseAdmin, grantAppAdmin, resolveHandoffTarget, validateGrantAdmin, } from './bot-handoff.js';
|
|
15
16
|
import { writePageTitle } from './page-title.js';
|
|
16
17
|
const WRITE_INTERVAL_MS = 2_000;
|
|
17
18
|
function requiredString(value, label) {
|
|
@@ -485,7 +486,7 @@ function buildInitPlan(options, schema, resources) {
|
|
|
485
486
|
},
|
|
486
487
|
};
|
|
487
488
|
}
|
|
488
|
-
function prepareResume(journal, options, schema, env) {
|
|
489
|
+
function prepareResume(journal, options, schema, env, beforeRemote) {
|
|
489
490
|
for (const step of journal.receipt.steps) {
|
|
490
491
|
if (step.kind === 'write' && ['running', 'unknown'].includes(step.status))
|
|
491
492
|
recoveryBlocked(journal, step.id, `Write outcome is unknown for ${step.id}; recovery cannot continue`);
|
|
@@ -497,6 +498,8 @@ function prepareResume(journal, options, schema, env) {
|
|
|
497
498
|
!['not_attempted', 'succeeded'].includes(step.status))
|
|
498
499
|
recoveryBlocked(journal, step.id, 'Local scaffolding may be incomplete and will not be overwritten');
|
|
499
500
|
}
|
|
501
|
+
// 以上都是纯本地门禁;通过后、首个远端读取前解析并校验建站身份。
|
|
502
|
+
beforeRemote();
|
|
500
503
|
const receipt = journal.receipt;
|
|
501
504
|
const fieldRetries = new Map();
|
|
502
505
|
let remoteTables = [];
|
|
@@ -737,6 +740,7 @@ export async function initializeProject(options) {
|
|
|
737
740
|
throw new Error('--table-ids requires --base-token');
|
|
738
741
|
}
|
|
739
742
|
const planOptions = { ...options, tableIds: selectedTableIds };
|
|
743
|
+
const grantAdmin = validateGrantAdmin(options.grantAdmin);
|
|
740
744
|
let schema;
|
|
741
745
|
try {
|
|
742
746
|
schema = schemaPath ? readInitSchema(schemaPath) : undefined;
|
|
@@ -753,6 +757,15 @@ export async function initializeProject(options) {
|
|
|
753
757
|
});
|
|
754
758
|
}
|
|
755
759
|
const { env, larkProfile } = resolveLarkProfileEnvironment(await runtimeProfile.commandEnvironment(), options.larkProfile, undefined, runtimeProfile);
|
|
760
|
+
// 建站身份由 resolveLarkIdentity 统一解析(显式 --as > lark-cli 声明 > user),进程内只解析一次;
|
|
761
|
+
// 续跑在取得执行锁之后才解析,被本地门禁拦截的请求不调用 lark-cli。
|
|
762
|
+
const buildIdentity = () => {
|
|
763
|
+
const resolved = resolveLarkIdentity(env);
|
|
764
|
+
if (grantAdmin && resolved !== 'bot')
|
|
765
|
+
throw new Error('--grant-admin only applies when building as bot (--as bot); a user build already owns the created Base and app');
|
|
766
|
+
return resolved;
|
|
767
|
+
};
|
|
768
|
+
let identity;
|
|
756
769
|
let selectedBaseToken;
|
|
757
770
|
let selectedBaseUrl;
|
|
758
771
|
let selectedBaseName = databaseName;
|
|
@@ -773,7 +786,37 @@ export async function initializeProject(options) {
|
|
|
773
786
|
...(larkProfile ? { larkProfile } : {}),
|
|
774
787
|
planHash: plan.planHash,
|
|
775
788
|
}, plan.steps, plan.resources, env);
|
|
776
|
-
|
|
789
|
+
const resumed = journal;
|
|
790
|
+
const resolveResumeIdentity = () => {
|
|
791
|
+
identity = buildIdentity();
|
|
792
|
+
assertResumeIdentity(resumed, identity);
|
|
793
|
+
};
|
|
794
|
+
// 已完成的初始化只在交接未完成时允许续跑,且只补授权、不发任何建站写入。
|
|
795
|
+
if (journal.receipt.status === 'succeeded') {
|
|
796
|
+
if (!handoffPending(journal.receipt))
|
|
797
|
+
throw new InitFailureError('Initialization is already complete; recovery is not needed', {
|
|
798
|
+
ok: false,
|
|
799
|
+
status: 'blocked',
|
|
800
|
+
phase: 'resume-preflight',
|
|
801
|
+
journal: journal.path,
|
|
802
|
+
error: {
|
|
803
|
+
message: 'Initialization is already complete; recovery is not needed',
|
|
804
|
+
},
|
|
805
|
+
nextAction: 'Nothing to resume: the initialization and its admin handoff are complete. Continue with the next development step.',
|
|
806
|
+
});
|
|
807
|
+
resolveResumeIdentity();
|
|
808
|
+
return resumeHandoffOnly(journal, identity, grantAdmin, env, {
|
|
809
|
+
environment: runtimeProfile.name,
|
|
810
|
+
workspace,
|
|
811
|
+
larkProfile,
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
resumePreparation = prepareResume(journal, planOptions, schema, env, resolveResumeIdentity);
|
|
815
|
+
}
|
|
816
|
+
else {
|
|
817
|
+
identity = buildIdentity();
|
|
818
|
+
if (!existingBaseToken || !existingAppToken)
|
|
819
|
+
assertHandoffTarget(identity, grantAdmin, env);
|
|
777
820
|
}
|
|
778
821
|
if (existingBaseToken) {
|
|
779
822
|
selectedBaseToken = requiredString(existingBaseToken, '--base-token');
|
|
@@ -787,7 +830,7 @@ export async function initializeProject(options) {
|
|
|
787
830
|
if (!schema)
|
|
788
831
|
throw new Error('Schema is required');
|
|
789
832
|
const [first, ...remaining] = schema.tables;
|
|
790
|
-
journal ??= startJournal(workspace, planOptions, larkProfile, env, schema, {
|
|
833
|
+
journal ??= startJournal(workspace, planOptions, larkProfile, env, identity, schema, {
|
|
791
834
|
tables: [],
|
|
792
835
|
});
|
|
793
836
|
if (!first)
|
|
@@ -802,6 +845,7 @@ export async function initializeProject(options) {
|
|
|
802
845
|
selectedBaseUrl = baseUrl(currentBase);
|
|
803
846
|
selectedBaseName = baseName(currentBase) ?? databaseName;
|
|
804
847
|
tables = [saved];
|
|
848
|
+
ensureHandoff(journal, 'base', identity, grantAdmin, env);
|
|
805
849
|
}
|
|
806
850
|
else {
|
|
807
851
|
process.stderr.write(`Creating Base ${databaseName}...\n`);
|
|
@@ -824,6 +868,8 @@ export async function initializeProject(options) {
|
|
|
824
868
|
tables = [
|
|
825
869
|
{ name: first.name, tableId: tableId(createdBase, 'base-create') },
|
|
826
870
|
];
|
|
871
|
+
// 新建后立即交接:后续步骤失败时,接手的人也已能打开 Base 核对或续跑。
|
|
872
|
+
ensureHandoff(journal, 'base', identity, grantAdmin, env, createdBase);
|
|
827
873
|
}
|
|
828
874
|
for (const [index, table] of remaining.entries()) {
|
|
829
875
|
const stepId = `create-table:${index + 1}`;
|
|
@@ -878,21 +924,26 @@ export async function initializeProject(options) {
|
|
|
878
924
|
fields: remainingFields,
|
|
879
925
|
initialIndexes: retryIndexes ?? remainingFields.map((_, index) => index),
|
|
880
926
|
env,
|
|
881
|
-
execute: (indexes) =>
|
|
882
|
-
|
|
883
|
-
'
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
927
|
+
execute: (indexes) => {
|
|
928
|
+
const batch = indexes.map((index) => remainingFields[index]);
|
|
929
|
+
const needsGuide = batch.some((field) => field?.type === 'formula' || field?.type === 'lookup');
|
|
930
|
+
return runLarkCli([
|
|
931
|
+
'base',
|
|
932
|
+
'+field-create',
|
|
933
|
+
'--base-token',
|
|
934
|
+
selectedBaseToken,
|
|
935
|
+
'--table-id',
|
|
936
|
+
createdTable.tableId,
|
|
937
|
+
'--json',
|
|
938
|
+
JSON.stringify(batch),
|
|
939
|
+
...(needsGuide ? ['--i-have-read-guide'] : []),
|
|
940
|
+
], env);
|
|
941
|
+
},
|
|
891
942
|
}));
|
|
892
943
|
}
|
|
893
944
|
await pause();
|
|
894
945
|
}
|
|
895
|
-
journal ??= startJournal(workspace, planOptions, larkProfile, env, schema, {
|
|
946
|
+
journal ??= startJournal(workspace, planOptions, larkProfile, env, identity, schema, {
|
|
896
947
|
baseToken: selectedBaseToken,
|
|
897
948
|
tables,
|
|
898
949
|
});
|
|
@@ -933,6 +984,7 @@ export async function initializeProject(options) {
|
|
|
933
984
|
else {
|
|
934
985
|
if (journal.step('create-app').status === 'succeeded') {
|
|
935
986
|
selectedAppToken = requiredString(journal.receipt.resources.appToken, 'receipt application token');
|
|
987
|
+
ensureHandoff(journal, 'app', identity, grantAdmin, env);
|
|
936
988
|
}
|
|
937
989
|
else {
|
|
938
990
|
process.stderr.write(`Creating application and binding ${selectedTableIds ? 'selected' : 'all'} tables...\n`);
|
|
@@ -954,6 +1006,7 @@ export async function initializeProject(options) {
|
|
|
954
1006
|
appToken(response);
|
|
955
1007
|
});
|
|
956
1008
|
selectedAppToken = appToken(createdDatabase);
|
|
1009
|
+
ensureHandoff(journal, 'app', identity, grantAdmin, env);
|
|
957
1010
|
}
|
|
958
1011
|
}
|
|
959
1012
|
const encodedToken = encodeURIComponent(selectedAppToken);
|
|
@@ -1072,14 +1125,16 @@ export async function initializeProject(options) {
|
|
|
1072
1125
|
tables,
|
|
1073
1126
|
};
|
|
1074
1127
|
const baseAppUrl = catalogBaseAppUrl(catalog);
|
|
1128
|
+
const handoff = handoffSummary(journal.receipt, identity);
|
|
1075
1129
|
return {
|
|
1076
1130
|
ok: true,
|
|
1077
|
-
warnings: [],
|
|
1131
|
+
warnings: handoff ? handoffWarnings(handoff) : [],
|
|
1078
1132
|
initialization: {
|
|
1079
1133
|
journal: journal.path,
|
|
1080
1134
|
status: 'succeeded',
|
|
1081
1135
|
...(options.resume ? { resumed: true } : {}),
|
|
1082
1136
|
},
|
|
1137
|
+
...(handoff ? { handoff } : {}),
|
|
1083
1138
|
environment: runtimeProfile.name,
|
|
1084
1139
|
workspace,
|
|
1085
1140
|
...(larkProfile ? { larkProfile } : {}),
|
|
@@ -1144,7 +1199,7 @@ export async function initializeProject(options) {
|
|
|
1144
1199
|
action: 'generateContracts',
|
|
1145
1200
|
workingDirectory: workspace,
|
|
1146
1201
|
command: `${runtimeProfile.checkCommand.split(' ')[0]} contracts generate`,
|
|
1147
|
-
description: '
|
|
1202
|
+
description: 'Generate contracts before check; this command prepares locked dependencies as needed. The published SDK and a single root package-lock.json are required.',
|
|
1148
1203
|
},
|
|
1149
1204
|
{
|
|
1150
1205
|
action: 'check',
|
|
@@ -1172,12 +1227,139 @@ export async function initializeProject(options) {
|
|
|
1172
1227
|
journal?.close();
|
|
1173
1228
|
}
|
|
1174
1229
|
}
|
|
1175
|
-
function startJournal(workspace, options, larkProfile, env, schema, resources) {
|
|
1230
|
+
function startJournal(workspace, options, larkProfile, env, identity, schema, resources) {
|
|
1176
1231
|
const plan = buildInitPlan(options, schema, resources);
|
|
1177
1232
|
return new InitJournal(workspace, {
|
|
1178
1233
|
environment: options.environment,
|
|
1179
1234
|
...(larkProfile ? { larkProfile } : {}),
|
|
1180
1235
|
planHash: plan.planHash,
|
|
1236
|
+
identity,
|
|
1181
1237
|
}, plan.steps, plan.resources, env);
|
|
1182
1238
|
}
|
|
1239
|
+
// 续跑必须沿用建站身份:中途换身份会让后续写入与已建资源的属主/授权不一致。
|
|
1240
|
+
function assertResumeIdentity(journal, identity) {
|
|
1241
|
+
// 未记录身份的 receipt 来自引入身份记录之前,当时一律以 user 建站。
|
|
1242
|
+
const recorded = journal.receipt.identity ?? 'user';
|
|
1243
|
+
if (recorded !== identity)
|
|
1244
|
+
recoveryBlocked(journal, 'resume-preflight', `This initialization was started with the ${recorded} identity; resume it with --as ${recorded}`);
|
|
1245
|
+
}
|
|
1246
|
+
// handoffPending:只有以 bot 建站、且本次新建的资源尚未完成授权时,已完成的初始化才需要续跑。
|
|
1247
|
+
function handoffPending(receipt) {
|
|
1248
|
+
const { resources, handoff } = receipt;
|
|
1249
|
+
return (receipt.identity === 'bot' &&
|
|
1250
|
+
((resources.baseSource === 'created' && pendingGrant(handoff?.base)) ||
|
|
1251
|
+
(resources.appSource === 'created' && pendingGrant(handoff?.app))));
|
|
1252
|
+
}
|
|
1253
|
+
// bot 新建的资源属主是机器人:没有授权对象时建出来无人能打开,必须在任何写入前拦截。
|
|
1254
|
+
// 人用场景只引导登录(open_id 按应用区分,用户无从得知);--grant-admin 留给持有发起人 open_id 的宿主。
|
|
1255
|
+
function assertHandoffTarget(identity, grantAdmin, env) {
|
|
1256
|
+
if (identity !== 'bot' || grantAdmin || currentCliUserOpenId(env))
|
|
1257
|
+
return;
|
|
1258
|
+
const message = 'Building as bot needs someone to receive admin on the new Base and app, but no lark-cli user is logged in';
|
|
1259
|
+
const login = userAuthorizationHint(env) ?? 'Log in with: lark-cli auth login';
|
|
1260
|
+
throw new InitFailureError(message, {
|
|
1261
|
+
ok: false,
|
|
1262
|
+
status: 'blocked',
|
|
1263
|
+
phase: 'preflight',
|
|
1264
|
+
error: { message },
|
|
1265
|
+
nextAction: `${login}\nThen rerun the same init command. Do not ask the user for an open_id. No remote resources were created.`,
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
function pendingGrant(grant) {
|
|
1269
|
+
return (!grant || (grant.status !== 'granted' && grant.status !== 'not_applicable'));
|
|
1270
|
+
}
|
|
1271
|
+
// handoffTarget 确定授权对象:显式 --grant-admin 优先,其次沿用 receipt 已记录的对象,最后才读当前
|
|
1272
|
+
// lark-cli 用户。已有授权成功后不允许换人,避免同一批资源交给两个人。
|
|
1273
|
+
function handoffTarget(journal, grantAdmin, env) {
|
|
1274
|
+
const { handoff } = journal.receipt;
|
|
1275
|
+
const target = grantAdmin
|
|
1276
|
+
? { openId: grantAdmin, source: 'grant_admin' }
|
|
1277
|
+
: (handoff?.target ?? resolveHandoffTarget(env, undefined));
|
|
1278
|
+
const granted = [handoff?.base, handoff?.app].find((grant) => grant?.status === 'granted' && grant.userOpenId)?.userOpenId;
|
|
1279
|
+
if (target && granted && granted !== target.openId)
|
|
1280
|
+
recoveryBlocked(journal, 'handoff', `Admin was already granted to ${granted}; resume with the same grantee instead of ${target.openId}`);
|
|
1281
|
+
if (target &&
|
|
1282
|
+
(handoff?.target?.openId !== target.openId ||
|
|
1283
|
+
handoff.target.source !== target.source))
|
|
1284
|
+
journal.recordHandoff({ target });
|
|
1285
|
+
return target;
|
|
1286
|
+
}
|
|
1287
|
+
function recordGrant(journal, resource, grant) {
|
|
1288
|
+
// 重试只覆盖本次结果;首次建 Base 时 lark-cli 已授权的其他用户仍需保留在报告中。
|
|
1289
|
+
const alsoGrantedOpenId = journal.receipt.handoff?.[resource]?.alsoGrantedOpenId;
|
|
1290
|
+
journal.recordHandoff({
|
|
1291
|
+
[resource]: {
|
|
1292
|
+
...(alsoGrantedOpenId ? { alsoGrantedOpenId } : {}),
|
|
1293
|
+
...grant,
|
|
1294
|
+
},
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
// ensureHandoff:只在 bot 建站且资源由本次 init 新建时授权;非致命,结果记入 receipt,已授权的不重复执行。
|
|
1298
|
+
function ensureHandoff(journal, resource, identity, grantAdmin, env, createResponse) {
|
|
1299
|
+
const { resources, handoff } = journal.receipt;
|
|
1300
|
+
const isBase = resource === 'base';
|
|
1301
|
+
const source = isBase ? resources.baseSource : resources.appSource;
|
|
1302
|
+
const token = isBase ? resources.baseToken : resources.appToken;
|
|
1303
|
+
if (identity !== 'bot' || source !== 'created' || !token)
|
|
1304
|
+
return;
|
|
1305
|
+
if (!pendingGrant(handoff?.[resource]))
|
|
1306
|
+
return;
|
|
1307
|
+
const target = handoffTarget(journal, grantAdmin, env);
|
|
1308
|
+
recordGrant(journal, resource, isBase
|
|
1309
|
+
? grantBaseAdmin(token, target, env, createResponse)
|
|
1310
|
+
: grantAppAdmin(token, target, env));
|
|
1311
|
+
}
|
|
1312
|
+
function handoffSummary(receipt, identity) {
|
|
1313
|
+
if (identity !== 'bot')
|
|
1314
|
+
return undefined;
|
|
1315
|
+
const grant = (created, recorded) => {
|
|
1316
|
+
if (!created)
|
|
1317
|
+
return { status: 'not_applicable' };
|
|
1318
|
+
if (!recorded)
|
|
1319
|
+
return {
|
|
1320
|
+
status: 'failed',
|
|
1321
|
+
hint: 'Admin handoff was not attempted; rerun the same init command with --resume.',
|
|
1322
|
+
};
|
|
1323
|
+
return recorded;
|
|
1324
|
+
};
|
|
1325
|
+
return {
|
|
1326
|
+
identity: 'bot',
|
|
1327
|
+
...(receipt.handoff?.target ? { target: receipt.handoff.target } : {}),
|
|
1328
|
+
base: {
|
|
1329
|
+
...grant(receipt.resources.baseSource === 'created', receipt.handoff?.base),
|
|
1330
|
+
perm: 'full_access',
|
|
1331
|
+
},
|
|
1332
|
+
app: {
|
|
1333
|
+
...grant(receipt.resources.appSource === 'created', receipt.handoff?.app),
|
|
1334
|
+
role: 'admin',
|
|
1335
|
+
},
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
function handoffWarnings(handoff) {
|
|
1339
|
+
return [handoff.base, handoff.app].flatMap((grant) => grant.status === 'granted' || grant.status === 'not_applicable'
|
|
1340
|
+
? []
|
|
1341
|
+
: [grant.hint ?? `Admin handoff ${grant.status}`]);
|
|
1342
|
+
}
|
|
1343
|
+
function resumeHandoffOnly(journal, identity, grantAdmin, env, context) {
|
|
1344
|
+
const { resources } = journal.receipt;
|
|
1345
|
+
ensureHandoff(journal, 'base', identity, grantAdmin, env);
|
|
1346
|
+
ensureHandoff(journal, 'app', identity, grantAdmin, env);
|
|
1347
|
+
const handoff = handoffSummary(journal.receipt, identity);
|
|
1348
|
+
return {
|
|
1349
|
+
ok: true,
|
|
1350
|
+
warnings: handoffWarnings(handoff),
|
|
1351
|
+
initialization: {
|
|
1352
|
+
journal: journal.path,
|
|
1353
|
+
status: 'succeeded',
|
|
1354
|
+
resumed: true,
|
|
1355
|
+
handoffOnly: true,
|
|
1356
|
+
},
|
|
1357
|
+
handoff,
|
|
1358
|
+
environment: context.environment,
|
|
1359
|
+
workspace: context.workspace,
|
|
1360
|
+
...(context.larkProfile ? { larkProfile: context.larkProfile } : {}),
|
|
1361
|
+
...(resources.baseToken ? { baseToken: resources.baseToken } : {}),
|
|
1362
|
+
...(resources.appToken ? { appToken: resources.appToken } : {}),
|
|
1363
|
+
};
|
|
1364
|
+
}
|
|
1183
1365
|
//# sourceMappingURL=init.js.map
|