@tiangong-ai/cli 0.0.27 → 0.0.29
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/AGENTS.md +1 -1
- package/README.md +114 -48
- package/dist/research/orchestration.js +38 -0
- package/dist/research/orchestration.js.map +1 -1
- package/dist/research/setup-command.js +45 -17
- package/dist/research/setup-command.js.map +1 -1
- package/dist/research/workspace/broker.d.ts +5 -0
- package/dist/research/workspace/broker.js +98 -9
- package/dist/research/workspace/broker.js.map +1 -1
- package/dist/research/workspace/capabilities.js +32 -11
- package/dist/research/workspace/capabilities.js.map +1 -1
- package/dist/research/workspace/credentials.d.ts +21 -1
- package/dist/research/workspace/credentials.js +45 -10
- package/dist/research/workspace/credentials.js.map +1 -1
- package/dist/research/workspace/executor.js +43 -3
- package/dist/research/workspace/executor.js.map +1 -1
- package/dist/research/workspace/external-skills.d.ts +31 -0
- package/dist/research/workspace/external-skills.js +161 -13
- package/dist/research/workspace/external-skills.js.map +1 -1
- package/dist/research/workspace/preflight.d.ts +34 -2
- package/dist/research/workspace/preflight.js +76 -21
- package/dist/research/workspace/preflight.js.map +1 -1
- package/dist/research/workspace/runtime.js +135 -50
- package/dist/research/workspace/runtime.js.map +1 -1
- package/dist/research/workspace/schemas.d.ts +4 -0
- package/dist/research/workspace/schemas.js +29 -3
- package/dist/research/workspace/schemas.js.map +1 -1
- package/dist/research/workspace/setup-catalog.d.ts +3 -2
- package/dist/research/workspace/setup-catalog.js +22 -1
- package/dist/research/workspace/setup-catalog.js.map +1 -1
- package/dist/research/workspace/setup-wizard.d.ts +6 -0
- package/dist/research/workspace/setup-wizard.js +379 -132
- package/dist/research/workspace/setup-wizard.js.map +1 -1
- package/dist/research/workspace/setup.d.ts +17 -0
- package/dist/research/workspace/setup.js +190 -95
- package/dist/research/workspace/setup.js.map +1 -1
- package/dist/research/workspace/types.d.ts +2 -0
- package/dist/research/workspace/workspace.d.ts +2 -1
- package/dist/research/workspace/workspace.js +62 -13
- package/dist/research/workspace/workspace.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,8 +7,8 @@ import { setTimeout as sleep } from "node:timers/promises";
|
|
|
7
7
|
import { CliError } from "../../errors.js";
|
|
8
8
|
import { loadCapabilityDeclarations } from "./capabilities.js";
|
|
9
9
|
import { inspectResearchContext } from "./context.js";
|
|
10
|
-
import { inspectCapabilityCredentialEnvironment,
|
|
11
|
-
import { configureExternalSkillProfile, configureTiangongSciCapability, doctorExternalCapabilities, EXTERNAL_SKILL_CONTEXT_PROFILE, EXTERNAL_SKILL_MEDIA_PROFILE, EXTERNAL_SKILL_PROFILE, } from "./external-skills.js";
|
|
10
|
+
import { inspectCapabilityCredentialEnvironment, loadCapabilityCredentialMapForIds, reconcileCapabilityCredentialEnvironment, setCapabilityCredentialValue, } from "./credentials.js";
|
|
11
|
+
import { configureExternalSkillProfile, configureTiangongSciCapability, doctorExternalCapabilities, EXTERNAL_SKILL_CONTEXT_PROFILE, EXTERNAL_SKILL_MEDIA_PROFILE, EXTERNAL_SKILL_PROFILE, reconcileSetupManagedCapabilities, } from "./external-skills.js";
|
|
12
12
|
import { appendJournalEvent } from "./journal.js";
|
|
13
13
|
import { configuredResearchSecrets, isSensitiveEnvironmentName, sanitizeResearchRecord, sanitizeResearchText, } from "./sanitization.js";
|
|
14
14
|
import { inspectResearchSetupCatalog, RESEARCH_SETUP_CREDENTIALS, RESEARCH_SETUP_INSTALLER, RESEARCH_SETUP_SETTINGS, RESEARCH_SETUP_SKILLS, resolveSetupSkills, setupSkill, setupSource, setupTargetRoot, } from "./setup-catalog.js";
|
|
@@ -162,7 +162,7 @@ export async function createResearchSetupPlan(input) {
|
|
|
162
162
|
globalMutation: scope === "global",
|
|
163
163
|
agentSmokeCost: input.agentSmoke === true,
|
|
164
164
|
},
|
|
165
|
-
mutations: setupMutations(root, targets, selected
|
|
165
|
+
mutations: setupMutations(root, targets, selected),
|
|
166
166
|
};
|
|
167
167
|
const plan = {
|
|
168
168
|
...unsigned,
|
|
@@ -312,6 +312,12 @@ export async function applyResearchSetupPlan(planPath, options = {}) {
|
|
|
312
312
|
state = await startSetupStep(root, state, "credential-preflight");
|
|
313
313
|
await assertRequiredCredentialPreflight(plan, environment);
|
|
314
314
|
state = await completeSetupStep(root, state, "credential-preflight");
|
|
315
|
+
// Persist an explicitly supplied credential before any installer or source
|
|
316
|
+
// download. A later installation failure can then resume from the immutable
|
|
317
|
+
// plan without asking the operator to expose the value again.
|
|
318
|
+
state = await startSetupStep(root, state, "credentials");
|
|
319
|
+
await configurePlanCredentials(plan, environment);
|
|
320
|
+
state = await completeSetupStep(root, state, "credentials");
|
|
315
321
|
state = await startSetupStep(root, state, "installation-preflight");
|
|
316
322
|
const selected = plan.selection.skillIds.map(setupSkill);
|
|
317
323
|
const installInspection = await inspectSelectedInstallations(plan, selected, environment);
|
|
@@ -381,6 +387,7 @@ export async function applyResearchSetupPlan(planPath, options = {}) {
|
|
|
381
387
|
}
|
|
382
388
|
state = await startSetupStep(root, state, "capability-configuration");
|
|
383
389
|
await configureSelectedCapabilities(plan, environment);
|
|
390
|
+
await reconcilePlanCredentialStores(plan);
|
|
384
391
|
state = await completeSetupStep(root, state, "capability-configuration");
|
|
385
392
|
state = await startSetupStep(root, state, "settings");
|
|
386
393
|
await writeJsonAtomic(paths.setupConfig, {
|
|
@@ -391,9 +398,6 @@ export async function applyResearchSetupPlan(planPath, options = {}) {
|
|
|
391
398
|
updatedAt: new Date().toISOString(),
|
|
392
399
|
});
|
|
393
400
|
state = await completeSetupStep(root, state, "settings");
|
|
394
|
-
state = await startSetupStep(root, state, "credentials");
|
|
395
|
-
await configurePlanCredentials(plan, environment);
|
|
396
|
-
state = await completeSetupStep(root, state, "credentials");
|
|
397
401
|
await appendJournalEvent(paths.journal, "research.setup.applied", "workspace", {
|
|
398
402
|
planSha256: plan.planSha256,
|
|
399
403
|
selectedSkillIds: plan.selection.skillIds,
|
|
@@ -425,6 +429,7 @@ export async function applyResearchSetupPlan(planPath, options = {}) {
|
|
|
425
429
|
runner,
|
|
426
430
|
...(options.fetcher === undefined ? {} : { fetcher: options.fetcher }),
|
|
427
431
|
...(options.sleeper === undefined ? {} : { sleeper: options.sleeper }),
|
|
432
|
+
...(options.executor === undefined ? {} : { executor: options.executor }),
|
|
428
433
|
});
|
|
429
434
|
state = await updateSetupState(root, {
|
|
430
435
|
...state,
|
|
@@ -498,19 +503,7 @@ export async function inspectResearchSetupStatus(workspace, environment = proces
|
|
|
498
503
|
}
|
|
499
504
|
export async function setResearchSetupCredentialFromEnvironment(input) {
|
|
500
505
|
const root = requireAbsoluteWorkspace(input.workspace);
|
|
501
|
-
const
|
|
502
|
-
const selected = selectedCredentialDefinitions(plan);
|
|
503
|
-
const credential = selected.find((candidate) => candidate.id === input.credentialId);
|
|
504
|
-
if (!credential) {
|
|
505
|
-
throw setupError({
|
|
506
|
-
code: "RESEARCH_SETUP_CREDENTIAL_INVALID",
|
|
507
|
-
step: "credentials",
|
|
508
|
-
reason: `Credential is not declared by the selected setup plan: ${input.credentialId}.`,
|
|
509
|
-
minimumAction: "Inspect the setup catalog and selected plan credential IDs.",
|
|
510
|
-
retryCommand: `tiangong-ai research setup status --workspace ${root} --json`,
|
|
511
|
-
exitCode: 2,
|
|
512
|
-
});
|
|
513
|
-
}
|
|
506
|
+
const { credential } = await selectedSetupCredential(root, input.credentialId);
|
|
514
507
|
assertEnvironmentName(input.environmentName);
|
|
515
508
|
const value = input.environment[input.environmentName];
|
|
516
509
|
if (typeof value !== "string" || Buffer.byteLength(value, "utf8") < credential.minimumUtf8Bytes) {
|
|
@@ -523,27 +516,85 @@ export async function setResearchSetupCredentialFromEnvironment(input) {
|
|
|
523
516
|
exitCode: 3,
|
|
524
517
|
});
|
|
525
518
|
}
|
|
519
|
+
return persistResearchSetupCredential({
|
|
520
|
+
root,
|
|
521
|
+
credentialId: input.credentialId,
|
|
522
|
+
value,
|
|
523
|
+
inputMethod: "environment",
|
|
524
|
+
sourceEnvironmentName: input.environmentName,
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
export async function setResearchSetupCredentialValue(input) {
|
|
528
|
+
const root = requireAbsoluteWorkspace(input.workspace);
|
|
529
|
+
const { credential } = await selectedSetupCredential(root, input.credentialId);
|
|
530
|
+
if (Buffer.byteLength(input.value, "utf8") < credential.minimumUtf8Bytes) {
|
|
531
|
+
throw setupError({
|
|
532
|
+
code: "RESEARCH_SETUP_CREDENTIAL_INVALID",
|
|
533
|
+
step: "credentials",
|
|
534
|
+
reason: "Credential value is missing or does not meet the selected provider minimum.",
|
|
535
|
+
minimumAction: "Retry with secure input, stdin, or a configured owner environment variable.",
|
|
536
|
+
retryCommand: `tiangong-ai research setup credential set --id ${credential.id} --prompt --workspace ${root} --json`,
|
|
537
|
+
exitCode: 3,
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
return persistResearchSetupCredential({
|
|
541
|
+
root,
|
|
542
|
+
credentialId: input.credentialId,
|
|
543
|
+
value: input.value,
|
|
544
|
+
inputMethod: input.inputMethod,
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
async function selectedSetupCredential(root, credentialId) {
|
|
548
|
+
const plan = await loadAndVerifyResearchSetupPlan(workspacePaths(root).setupPlan);
|
|
549
|
+
const selected = selectedCredentialDefinitions(plan);
|
|
550
|
+
const credential = selected.find((candidate) => candidate.id === credentialId);
|
|
551
|
+
if (!credential) {
|
|
552
|
+
throw setupError({
|
|
553
|
+
code: "RESEARCH_SETUP_CREDENTIAL_INVALID",
|
|
554
|
+
step: "credentials",
|
|
555
|
+
reason: `Credential is not declared by the selected setup plan: ${credentialId}.`,
|
|
556
|
+
minimumAction: "Inspect the setup catalog and selected plan credential IDs.",
|
|
557
|
+
retryCommand: `tiangong-ai research setup status --workspace ${root} --json`,
|
|
558
|
+
exitCode: 2,
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
return { plan, selected, credential };
|
|
562
|
+
}
|
|
563
|
+
async function persistResearchSetupCredential(input) {
|
|
564
|
+
const { selected, credential } = await selectedSetupCredential(input.root, input.credentialId);
|
|
526
565
|
if (credential.storage === "broker") {
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
566
|
+
const currentDeclarations = (await pathExists(workspacePaths(input.root).capabilityDeclarations))
|
|
567
|
+
? await loadCapabilityDeclarations(input.root)
|
|
568
|
+
: { capabilities: [] };
|
|
569
|
+
await setCapabilityCredentialValue({
|
|
570
|
+
root: input.root,
|
|
571
|
+
declaredCredentialIds: [
|
|
572
|
+
...new Set([
|
|
573
|
+
...currentDeclarations.capabilities.flatMap((capability) => capability.credentials.map((candidate) => candidate.id)),
|
|
574
|
+
...selected
|
|
575
|
+
.filter((candidate) => candidate.storage === "broker")
|
|
576
|
+
.map((candidate) => candidate.id),
|
|
577
|
+
]),
|
|
578
|
+
],
|
|
531
579
|
credentialId: credential.id,
|
|
532
|
-
|
|
533
|
-
|
|
580
|
+
value: input.value,
|
|
581
|
+
minimumUtf8Bytes: credential.minimumUtf8Bytes,
|
|
534
582
|
});
|
|
535
583
|
}
|
|
536
584
|
else {
|
|
537
|
-
await setAdapterCredential(root, selected, credential.id, value);
|
|
585
|
+
await setAdapterCredential(input.root, selected.filter((candidate) => candidate.storage === "adapter"), credential.id, input.value);
|
|
538
586
|
}
|
|
539
|
-
await appendJournalEvent(workspacePaths(root).journal, "research.setup.credential.configured", "workspace", {
|
|
587
|
+
await appendJournalEvent(workspacePaths(input.root).journal, "research.setup.credential.configured", "workspace", {
|
|
540
588
|
credentialId: credential.id,
|
|
541
|
-
|
|
589
|
+
inputMethod: input.inputMethod,
|
|
590
|
+
...(input.sourceEnvironmentName === undefined
|
|
591
|
+
? {}
|
|
592
|
+
: { sourceEnvironmentNameSha256: sha256Text(input.sourceEnvironmentName) }),
|
|
542
593
|
storage: credential.storage,
|
|
543
594
|
});
|
|
544
595
|
return {
|
|
545
596
|
schemaVersion: 1,
|
|
546
|
-
workspace: root,
|
|
597
|
+
workspace: input.root,
|
|
547
598
|
credentialId: credential.id,
|
|
548
599
|
configured: true,
|
|
549
600
|
storage: credential.storage,
|
|
@@ -693,11 +744,13 @@ export async function doctorResearchSetup(workspace, options = {}) {
|
|
|
693
744
|
checks.push({
|
|
694
745
|
id: `setting.${setting.id}`,
|
|
695
746
|
category: "configuration",
|
|
696
|
-
status: configured
|
|
747
|
+
status: configured || !setting.required ? "pass" : "fail",
|
|
697
748
|
detail: configured
|
|
698
749
|
? "Declared non-secret setting is configured."
|
|
699
|
-
:
|
|
700
|
-
|
|
750
|
+
: setting.required
|
|
751
|
+
? "Required setting is not configured."
|
|
752
|
+
: "Optional setting was explicitly omitted.",
|
|
753
|
+
minimumAction: configured || !setting.required
|
|
701
754
|
? null
|
|
702
755
|
: `Create a reviewed replacement plan with the ${setting.id} setting.`,
|
|
703
756
|
});
|
|
@@ -736,13 +789,15 @@ export async function doctorResearchSetup(workspace, options = {}) {
|
|
|
736
789
|
checks.push({
|
|
737
790
|
id: `credential.${credential.id}`,
|
|
738
791
|
category: "credential",
|
|
739
|
-
status: configured
|
|
792
|
+
status: configured || !credential.required ? "pass" : "fail",
|
|
740
793
|
detail: configured
|
|
741
794
|
? "Credential is present in an owner-only store; its value was not emitted."
|
|
742
|
-
:
|
|
743
|
-
|
|
795
|
+
: credential.required
|
|
796
|
+
? "Required credential is not configured."
|
|
797
|
+
: "Optional credential was explicitly omitted.",
|
|
798
|
+
minimumAction: configured || !credential.required
|
|
744
799
|
? null
|
|
745
|
-
: `Run research setup credential set --id ${credential.id} --
|
|
800
|
+
: `Run research setup credential set --id ${credential.id} --prompt --workspace ${root}.`,
|
|
746
801
|
});
|
|
747
802
|
}
|
|
748
803
|
await appendDependencyChecks(checks, selected, runner, root, environment);
|
|
@@ -784,15 +839,6 @@ export async function doctorResearchSetup(workspace, options = {}) {
|
|
|
784
839
|
allowSyntheticUnstructureUpload: options.allowSyntheticUnstructureUpload === true,
|
|
785
840
|
});
|
|
786
841
|
}
|
|
787
|
-
else {
|
|
788
|
-
checks.push({
|
|
789
|
-
id: "live-provider-checks",
|
|
790
|
-
category: "live-check",
|
|
791
|
-
status: "warn",
|
|
792
|
-
detail: "Live provider checks were not requested.",
|
|
793
|
-
minimumAction: `Run tiangong-ai research setup doctor --live --workspace ${root} --json after reviewing quota impact.`,
|
|
794
|
-
});
|
|
795
|
-
}
|
|
796
842
|
let workspaceDoctor = null;
|
|
797
843
|
try {
|
|
798
844
|
workspaceDoctor = await doctorResearchWorkspace(root, {
|
|
@@ -800,15 +846,23 @@ export async function doctorResearchSetup(workspace, options = {}) {
|
|
|
800
846
|
capabilitySmoke: options.live === true,
|
|
801
847
|
environment,
|
|
802
848
|
capabilityFetcher: fetcher,
|
|
849
|
+
...(options.executor === undefined ? {} : { executor: options.executor }),
|
|
803
850
|
});
|
|
851
|
+
const requiredRuntimeChecks = options.agentSmoke === true || options.live === true;
|
|
852
|
+
const runtimeBlocked = workspaceDoctor.status !== "ready" && requiredRuntimeChecks;
|
|
853
|
+
const failedWorkspaceChecks = workspaceDoctor.checks
|
|
854
|
+
.filter((check) => check.status === "fail")
|
|
855
|
+
.map((check) => check.id);
|
|
804
856
|
checks.push({
|
|
805
857
|
id: "production-runtime",
|
|
806
858
|
category: "research-runtime",
|
|
807
|
-
status: workspaceDoctor.status === "ready" ? "pass" : "warn",
|
|
859
|
+
status: workspaceDoctor.status === "ready" ? "pass" : runtimeBlocked ? "fail" : "warn",
|
|
808
860
|
detail: `Workspace doctor reported ${workspaceDoctor.status}.`,
|
|
809
861
|
minimumAction: workspaceDoctor.status === "ready"
|
|
810
862
|
? null
|
|
811
|
-
:
|
|
863
|
+
: runtimeBlocked
|
|
864
|
+
? `Resolve the failed workspace doctor checks (${failedWorkspaceChecks.join(", ") || "unknown"}); an explicitly requested smoke failure blocks readiness.`
|
|
865
|
+
: "Configure explicit production models/pricing and run the separately confirmed agent/capability smoke checks.",
|
|
812
866
|
});
|
|
813
867
|
}
|
|
814
868
|
catch (error) {
|
|
@@ -820,6 +874,20 @@ export async function doctorResearchSetup(workspace, options = {}) {
|
|
|
820
874
|
minimumAction: "Repair workspace runtime state, then rerun setup doctor.",
|
|
821
875
|
});
|
|
822
876
|
}
|
|
877
|
+
if (!options.live) {
|
|
878
|
+
const attestedCapabilitySmoke = workspaceDoctor?.checks.some((check) => check.id === "capability-live-smoke" && check.status === "pass");
|
|
879
|
+
checks.push({
|
|
880
|
+
id: "live-provider-checks",
|
|
881
|
+
category: "live-check",
|
|
882
|
+
status: attestedCapabilitySmoke ? "pass" : "warn",
|
|
883
|
+
detail: attestedCapabilitySmoke
|
|
884
|
+
? "Reused the unexpired, runtime-bound capability smoke attestation."
|
|
885
|
+
: "Live provider checks were not requested and no reusable attestation is available.",
|
|
886
|
+
minimumAction: attestedCapabilitySmoke
|
|
887
|
+
? null
|
|
888
|
+
: `Run tiangong-ai research setup doctor --live --workspace ${root} --json after reviewing quota impact.`,
|
|
889
|
+
});
|
|
890
|
+
}
|
|
823
891
|
const readiness = checks.some((check) => check.status === "fail")
|
|
824
892
|
? "BLOCKED"
|
|
825
893
|
: checks.some((check) => check.status === "warn")
|
|
@@ -1012,6 +1080,7 @@ function parseResearchSetupPlan(value) {
|
|
|
1012
1080
|
typeof skill.expectedTreeSha256 !== "string" ||
|
|
1013
1081
|
!/^[0-9a-f]{64}$/.test(skill.expectedTreeSha256) ||
|
|
1014
1082
|
![
|
|
1083
|
+
"orchestrator",
|
|
1015
1084
|
"evidence-capability",
|
|
1016
1085
|
"input-preprocessor",
|
|
1017
1086
|
"acquisition-adapter",
|
|
@@ -1129,7 +1198,7 @@ function assertPlanMatchesCatalog(plan) {
|
|
|
1129
1198
|
if (plan.checks.agentSmoke !== plan.confirmations.agentSmokeCost) {
|
|
1130
1199
|
throw planCatalogDrift("agent smoke confirmation");
|
|
1131
1200
|
}
|
|
1132
|
-
const expectedMutations = setupMutations(plan.workspace.path, plan.install.targets, selected
|
|
1201
|
+
const expectedMutations = setupMutations(plan.workspace.path, plan.install.targets, selected);
|
|
1133
1202
|
if (canonicalJson(plan.mutations) !== canonicalJson(expectedMutations)) {
|
|
1134
1203
|
throw planCatalogDrift("declared mutations");
|
|
1135
1204
|
}
|
|
@@ -1668,11 +1737,9 @@ async function installSetupSkills(input) {
|
|
|
1668
1737
|
}
|
|
1669
1738
|
}
|
|
1670
1739
|
async function configureSelectedCapabilities(plan, _environment) {
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
}
|
|
1675
|
-
const codexRoot = plannedTargetRoot(plan, "codex");
|
|
1740
|
+
const hasBraveProfile = plan.selection.evidenceProfile !== "none";
|
|
1741
|
+
const hasTiangongSci = plan.selection.skillIds.includes("tiangong.kb-sci-search");
|
|
1742
|
+
const codexRoot = hasBraveProfile || hasTiangongSci ? plannedTargetRoot(plan, "codex") : null;
|
|
1676
1743
|
if (plan.selection.evidenceProfile !== "none") {
|
|
1677
1744
|
await configureExternalSkillProfile({
|
|
1678
1745
|
workspace: plan.workspace.path,
|
|
@@ -1680,7 +1747,7 @@ async function configureSelectedCapabilities(plan, _environment) {
|
|
|
1680
1747
|
skillRoot: codexRoot,
|
|
1681
1748
|
});
|
|
1682
1749
|
}
|
|
1683
|
-
if (
|
|
1750
|
+
if (hasTiangongSci) {
|
|
1684
1751
|
const skill = setupSkill("tiangong.kb-sci-search");
|
|
1685
1752
|
const source = setupSource(skill.sourceId);
|
|
1686
1753
|
await configureTiangongSciCapability({
|
|
@@ -1700,6 +1767,47 @@ async function configureSelectedCapabilities(plan, _environment) {
|
|
|
1700
1767
|
: { region: plan.settings["tiangong.sci.region"] }),
|
|
1701
1768
|
});
|
|
1702
1769
|
}
|
|
1770
|
+
await reconcileSetupManagedCapabilities({
|
|
1771
|
+
workspace: plan.workspace.path,
|
|
1772
|
+
selectedCapabilityIds: [
|
|
1773
|
+
...BRAVE_PROFILE_SKILLS[plan.selection.evidenceProfile].map(setupManagedCapabilityId),
|
|
1774
|
+
...(hasTiangongSci ? ["database.tiangong.sci-search"] : []),
|
|
1775
|
+
],
|
|
1776
|
+
});
|
|
1777
|
+
}
|
|
1778
|
+
function setupManagedCapabilityId(skillId) {
|
|
1779
|
+
const mapping = {
|
|
1780
|
+
"brave.web-search": "method.brave.web-search",
|
|
1781
|
+
"brave.news-search": "method.brave.news-search",
|
|
1782
|
+
"brave.llm-context": "method.brave.llm-context",
|
|
1783
|
+
"brave.images-search": "method.brave.images-search",
|
|
1784
|
+
"brave.videos-search": "method.brave.videos-search",
|
|
1785
|
+
};
|
|
1786
|
+
const capabilityId = mapping[skillId] ?? null;
|
|
1787
|
+
if (!capabilityId) {
|
|
1788
|
+
throw setupError({
|
|
1789
|
+
code: "RESEARCH_SETUP_CATALOG_DRIFT",
|
|
1790
|
+
step: "capability-configuration",
|
|
1791
|
+
reason: `Setup-managed evidence Skill has no capability mapping: ${skillId}.`,
|
|
1792
|
+
minimumAction: "Use the exact CLI/catalog release and regenerate the setup plan.",
|
|
1793
|
+
retryCommand: "tiangong-ai research setup catalog --json",
|
|
1794
|
+
exitCode: 3,
|
|
1795
|
+
});
|
|
1796
|
+
}
|
|
1797
|
+
return capabilityId;
|
|
1798
|
+
}
|
|
1799
|
+
async function reconcilePlanCredentialStores(plan) {
|
|
1800
|
+
const declarations = await loadCapabilityDeclarations(plan.workspace.path);
|
|
1801
|
+
await reconcileCapabilityCredentialEnvironment(plan.workspace.path, declarations.capabilities);
|
|
1802
|
+
const adapterPath = workspacePaths(plan.workspace.path).setupAdapterEnv;
|
|
1803
|
+
if (!(await pathExists(adapterPath)))
|
|
1804
|
+
return;
|
|
1805
|
+
const definitions = selectedCredentialDefinitions(plan).filter((credential) => credential.storage === "adapter");
|
|
1806
|
+
const configured = await loadAdapterCredentials(plan.workspace.path, definitions, {
|
|
1807
|
+
ignoreUndeclared: true,
|
|
1808
|
+
});
|
|
1809
|
+
const serialized = Object.fromEntries([...configured.entries()].sort(([left], [right]) => left.localeCompare(right)));
|
|
1810
|
+
await writeTextAtomic(adapterPath, `${ADAPTER_ENV_KEY}=${JSON.stringify(serialized)}\n`, 0o600);
|
|
1703
1811
|
}
|
|
1704
1812
|
async function configurePlanCredentials(plan, environment) {
|
|
1705
1813
|
for (const credential of plan.credentialSources) {
|
|
@@ -1723,22 +1831,16 @@ async function assertRequiredCredentialPreflight(plan, environment) {
|
|
|
1723
1831
|
const definitions = selectedCredentialDefinitions(plan);
|
|
1724
1832
|
let adapterCredentials = new Map();
|
|
1725
1833
|
try {
|
|
1726
|
-
adapterCredentials = await loadAdapterCredentials(plan.workspace.path, definitions
|
|
1834
|
+
adapterCredentials = await loadAdapterCredentials(plan.workspace.path, definitions, {
|
|
1835
|
+
ignoreUndeclared: true,
|
|
1836
|
+
});
|
|
1727
1837
|
}
|
|
1728
1838
|
catch (error) {
|
|
1729
1839
|
if (error instanceof CliError)
|
|
1730
1840
|
throw error;
|
|
1731
1841
|
}
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
const declarations = await loadCapabilityDeclarations(plan.workspace.path);
|
|
1735
|
-
const status = await inspectCapabilityCredentialEnvironment(plan.workspace.path, declarations.capabilities);
|
|
1736
|
-
configuredBrokerIds = new Set(status.configuredIds);
|
|
1737
|
-
}
|
|
1738
|
-
catch {
|
|
1739
|
-
// A first apply has not configured capability declarations yet. The
|
|
1740
|
-
// reviewed plan's environment mapping remains the only accepted source.
|
|
1741
|
-
}
|
|
1842
|
+
const brokerDefinitions = definitions.filter((definition) => definition.storage === "broker");
|
|
1843
|
+
const configuredBrokerIds = new Set((await loadCapabilityCredentialMapForIds(plan.workspace.path, brokerDefinitions.map((definition) => definition.id), { ignoreUndeclared: true })).keys());
|
|
1742
1844
|
const failures = [];
|
|
1743
1845
|
for (const definition of definitions) {
|
|
1744
1846
|
const planned = plan.credentialSources.find((candidate) => candidate.id === definition.id);
|
|
@@ -1749,10 +1851,7 @@ async function assertRequiredCredentialPreflight(plan, environment) {
|
|
|
1749
1851
|
Buffer.byteLength(environment[planned.fromEnvironment] ?? "", "utf8") >=
|
|
1750
1852
|
definition.minimumUtf8Bytes;
|
|
1751
1853
|
if ((!planned && definition.required && !stored) || (planned && !supplied && !stored)) {
|
|
1752
|
-
failures.push({
|
|
1753
|
-
id: definition.id,
|
|
1754
|
-
environmentName: planned?.fromEnvironment ?? null,
|
|
1755
|
-
});
|
|
1854
|
+
failures.push({ id: definition.id });
|
|
1756
1855
|
}
|
|
1757
1856
|
}
|
|
1758
1857
|
if (failures.length) {
|
|
@@ -1762,8 +1861,8 @@ async function assertRequiredCredentialPreflight(plan, environment) {
|
|
|
1762
1861
|
reason: `Required or explicitly selected credentials are unavailable: ${failures
|
|
1763
1862
|
.map((failure) => failure.id)
|
|
1764
1863
|
.join(", ")}.`,
|
|
1765
|
-
minimumAction: `
|
|
1766
|
-
.map((failure) =>
|
|
1864
|
+
minimumAction: `Configure each unavailable logical credential with research setup credential set --prompt, --from-stdin, or --from-env before any download (${failures
|
|
1865
|
+
.map((failure) => failure.id)
|
|
1767
1866
|
.join(", ")}), then retry this exact step.`,
|
|
1768
1867
|
retryCommand: `tiangong-ai research setup retry --step credential-preflight --workspace ${plan.workspace.path} --json`,
|
|
1769
1868
|
exitCode: 3,
|
|
@@ -2211,7 +2310,7 @@ function companionArtifactError(root, reason) {
|
|
|
2211
2310
|
exitCode: 3,
|
|
2212
2311
|
});
|
|
2213
2312
|
}
|
|
2214
|
-
async function loadAdapterCredentials(root, definitions) {
|
|
2313
|
+
async function loadAdapterCredentials(root, definitions, options = {}) {
|
|
2215
2314
|
const path = workspacePaths(root).setupAdapterEnv;
|
|
2216
2315
|
if (!(await pathExists(path)))
|
|
2217
2316
|
return new Map();
|
|
@@ -2282,6 +2381,8 @@ async function loadAdapterCredentials(root, definitions) {
|
|
|
2282
2381
|
const result = new Map();
|
|
2283
2382
|
for (const [id, credentialValue] of Object.entries(value)) {
|
|
2284
2383
|
const definition = allowed.get(id);
|
|
2384
|
+
if (!definition && options.ignoreUndeclared)
|
|
2385
|
+
continue;
|
|
2285
2386
|
if (!definition ||
|
|
2286
2387
|
typeof credentialValue !== "string" ||
|
|
2287
2388
|
Buffer.byteLength(credentialValue, "utf8") < definition.minimumUtf8Bytes) {
|
|
@@ -2299,7 +2400,7 @@ async function loadAdapterCredentials(root, definitions) {
|
|
|
2299
2400
|
return result;
|
|
2300
2401
|
}
|
|
2301
2402
|
async function setAdapterCredential(root, definitions, credentialId, value) {
|
|
2302
|
-
const configured = await loadAdapterCredentials(root, definitions);
|
|
2403
|
+
const configured = await loadAdapterCredentials(root, definitions, { ignoreUndeclared: true });
|
|
2303
2404
|
configured.set(credentialId, value);
|
|
2304
2405
|
const serialized = Object.fromEntries([...configured.entries()].sort(([left], [right]) => left.localeCompare(right)));
|
|
2305
2406
|
await writeTextAtomic(workspacePaths(root).setupAdapterEnv, `${ADAPTER_ENV_KEY}=${JSON.stringify(serialized)}\n`, 0o600);
|
|
@@ -2498,7 +2599,7 @@ function validateSetupSetting(id, validation, value) {
|
|
|
2498
2599
|
});
|
|
2499
2600
|
}
|
|
2500
2601
|
}
|
|
2501
|
-
function setupMutations(root, targets, selected
|
|
2602
|
+
function setupMutations(root, targets, selected) {
|
|
2502
2603
|
const mutations = [
|
|
2503
2604
|
{
|
|
2504
2605
|
step: "workspace",
|
|
@@ -2515,27 +2616,21 @@ function setupMutations(root, targets, selected, credentialSources) {
|
|
|
2515
2616
|
});
|
|
2516
2617
|
}
|
|
2517
2618
|
}
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
mutations.push({
|
|
2534
|
-
step: "credentials",
|
|
2535
|
-
target: workspacePaths(root).setupAdapterEnv,
|
|
2536
|
-
reason: "Store selected companion-adapter credentials in an owner-only file.",
|
|
2537
|
-
});
|
|
2538
|
-
}
|
|
2619
|
+
mutations.push({
|
|
2620
|
+
step: "capability-configuration",
|
|
2621
|
+
target: workspacePaths(root).capabilityDeclarations,
|
|
2622
|
+
reason: "Reconcile and lock the explicitly selected setup-managed evidence capabilities while preserving custom declarations.",
|
|
2623
|
+
});
|
|
2624
|
+
mutations.push({
|
|
2625
|
+
step: "credentials",
|
|
2626
|
+
target: workspacePaths(root).env,
|
|
2627
|
+
reason: "Reconcile selected broker credentials in the owner-only workspace environment file.",
|
|
2628
|
+
});
|
|
2629
|
+
mutations.push({
|
|
2630
|
+
step: "credentials",
|
|
2631
|
+
target: workspacePaths(root).setupAdapterEnv,
|
|
2632
|
+
reason: "Reconcile selected companion-adapter credentials in an owner-only file.",
|
|
2633
|
+
});
|
|
2539
2634
|
return mutations.sort((left, right) => `${left.step}\0${left.target}`.localeCompare(`${right.step}\0${right.target}`));
|
|
2540
2635
|
}
|
|
2541
2636
|
function setupLockPayload(planSha256) {
|