@tiangong-ai/cli 0.0.30 → 0.0.31
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 +36 -14
- package/dist/edge-search.js +57 -16
- package/dist/edge-search.js.map +1 -1
- package/dist/http.js +17 -1
- package/dist/http.js.map +1 -1
- package/dist/research/orchestration.js +9 -1
- package/dist/research/orchestration.js.map +1 -1
- package/dist/research/workspace/broker.js +60 -15
- package/dist/research/workspace/broker.js.map +1 -1
- package/dist/research/workspace/external-skills.d.ts +95 -0
- package/dist/research/workspace/external-skills.js +79 -25
- package/dist/research/workspace/external-skills.js.map +1 -1
- package/dist/research/workspace/preflight.d.ts +9 -0
- package/dist/research/workspace/preflight.js +44 -2
- package/dist/research/workspace/preflight.js.map +1 -1
- package/dist/research/workspace/projects.js +16 -0
- package/dist/research/workspace/projects.js.map +1 -1
- package/dist/research/workspace/setup-catalog.d.ts +20 -2
- package/dist/research/workspace/setup-catalog.js +202 -5
- package/dist/research/workspace/setup-catalog.js.map +1 -1
- package/dist/research/workspace/setup.d.ts +7 -1
- package/dist/research/workspace/setup.js +166 -45
- package/dist/research/workspace/setup.js.map +1 -1
- package/dist/research/workspace/types.d.ts +2 -0
- package/package.json +2 -1
|
@@ -8,10 +8,10 @@ import { CliError } from "../../errors.js";
|
|
|
8
8
|
import { loadCapabilityDeclarations } from "./capabilities.js";
|
|
9
9
|
import { inspectResearchContext } from "./context.js";
|
|
10
10
|
import { inspectCapabilityCredentialEnvironment, loadCapabilityCredentialMapForIds, reconcileCapabilityCredentialEnvironment, setCapabilityCredentialValue, } from "./credentials.js";
|
|
11
|
-
import { configureExternalSkillProfile,
|
|
11
|
+
import { configureExternalSkillProfile, configureTiangongDatabaseCapability, 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
|
-
import { inspectResearchSetupCatalog, RESEARCH_SETUP_CREDENTIALS, RESEARCH_SETUP_INSTALLER, RESEARCH_SETUP_SETTINGS, RESEARCH_SETUP_SKILLS, resolveSetupSkills, setupSkill, setupSource, setupTargetRoot, } from "./setup-catalog.js";
|
|
14
|
+
import { inspectResearchSetupCatalog, RESEARCH_SETUP_CREDENTIALS, RESEARCH_SETUP_INSTALLER, RESEARCH_SETUP_SETTINGS, RESEARCH_SETUP_SKILLS, resolveSetupSkills, setupSkill, setupSource, setupTargetRoot, verifyResearchSetupRuntimeContract, } from "./setup-catalog.js";
|
|
15
15
|
import { acquireFileLock, canonicalJson, ensureDirectory, fileSize, hashRegularTree, isObject, pathExists, REGULAR_TREE_HASH_ALGORITHM, readJsonFile, sha256File, sha256Text, workspacePaths, writeJsonAtomic, writeTextAtomic, } from "./storage.js";
|
|
16
16
|
import { packageVersion } from "./constants.js";
|
|
17
17
|
import { doctorResearchWorkspace, initializeResearchWorkspace, loadWorkspaceConfig, } from "./workspace.js";
|
|
@@ -969,22 +969,24 @@ export async function checkResearchSetupUpdates(workspace, environment = process
|
|
|
969
969
|
});
|
|
970
970
|
}
|
|
971
971
|
}
|
|
972
|
+
const cliVersionDrift = plan.cli.version === packageVersion()
|
|
973
|
+
? null
|
|
974
|
+
: { planned: plan.cli.version, active: packageVersion() };
|
|
975
|
+
const updateAvailable = drift.length > 0 || cliVersionDrift !== null;
|
|
972
976
|
return {
|
|
973
977
|
schemaVersion: 1,
|
|
974
978
|
workspace: root,
|
|
975
979
|
checkedAt: new Date().toISOString(),
|
|
976
|
-
updateAvailable
|
|
977
|
-
cliVersionDrift
|
|
978
|
-
? null
|
|
979
|
-
: { planned: plan.cli.version, active: packageVersion() },
|
|
980
|
+
updateAvailable,
|
|
981
|
+
cliVersionDrift,
|
|
980
982
|
drift,
|
|
981
983
|
currentInstaller: RESEARCH_SETUP_INSTALLER,
|
|
982
984
|
catalog,
|
|
983
985
|
policy: {
|
|
984
986
|
automaticUpdate: false,
|
|
985
987
|
floatingUpdate: false,
|
|
986
|
-
minimumAction:
|
|
987
|
-
? "Use the
|
|
988
|
+
minimumAction: updateAvailable
|
|
989
|
+
? "Use the active exact CLI release to create, review, and apply a replacement immutable plan; upgrade never runs a floating CLI or Skills update."
|
|
988
990
|
: "No catalog migration is required. Installed tree drift is reported separately by setup status/doctor.",
|
|
989
991
|
},
|
|
990
992
|
};
|
|
@@ -1719,6 +1721,7 @@ async function ensureSetupSourceCheckout(plan, sourceId, runner, environment) {
|
|
|
1719
1721
|
},
|
|
1720
1722
|
});
|
|
1721
1723
|
}
|
|
1724
|
+
await verifyResearchSetupRuntimeContract(sourcePath, setupSkill(skill.id));
|
|
1722
1725
|
}
|
|
1723
1726
|
return checkout;
|
|
1724
1727
|
}
|
|
@@ -1759,8 +1762,30 @@ async function installSetupSkills(input) {
|
|
|
1759
1762
|
}
|
|
1760
1763
|
async function configureSelectedCapabilities(plan, _environment) {
|
|
1761
1764
|
const hasBraveProfile = plan.selection.evidenceProfile !== "none";
|
|
1762
|
-
const
|
|
1763
|
-
|
|
1765
|
+
const tiangongDatabases = [
|
|
1766
|
+
{
|
|
1767
|
+
kind: "sci",
|
|
1768
|
+
skillId: "tiangong.kb-sci-search",
|
|
1769
|
+
settingPrefix: "tiangong.sci",
|
|
1770
|
+
catalogId: "first-party.tiangong.kb-sci-search",
|
|
1771
|
+
capabilityId: "database.tiangong.sci-search",
|
|
1772
|
+
},
|
|
1773
|
+
{
|
|
1774
|
+
kind: "report",
|
|
1775
|
+
skillId: "tiangong.kb-report-search",
|
|
1776
|
+
settingPrefix: "tiangong.report",
|
|
1777
|
+
catalogId: "first-party.tiangong.kb-report-search",
|
|
1778
|
+
capabilityId: "database.tiangong.report-search",
|
|
1779
|
+
},
|
|
1780
|
+
{
|
|
1781
|
+
kind: "patent",
|
|
1782
|
+
skillId: "tiangong.kb-patent-search",
|
|
1783
|
+
settingPrefix: "tiangong.patent",
|
|
1784
|
+
catalogId: "first-party.tiangong.kb-patent-search",
|
|
1785
|
+
capabilityId: "database.tiangong.patent-search",
|
|
1786
|
+
},
|
|
1787
|
+
].filter((entry) => plan.selection.skillIds.includes(entry.skillId));
|
|
1788
|
+
const codexRoot = hasBraveProfile || tiangongDatabases.length ? plannedTargetRoot(plan, "codex") : null;
|
|
1764
1789
|
if (plan.selection.evidenceProfile !== "none") {
|
|
1765
1790
|
await configureExternalSkillProfile({
|
|
1766
1791
|
workspace: plan.workspace.path,
|
|
@@ -1768,10 +1793,11 @@ async function configureSelectedCapabilities(plan, _environment) {
|
|
|
1768
1793
|
skillRoot: codexRoot,
|
|
1769
1794
|
});
|
|
1770
1795
|
}
|
|
1771
|
-
|
|
1772
|
-
const skill = setupSkill(
|
|
1796
|
+
for (const database of tiangongDatabases) {
|
|
1797
|
+
const skill = setupSkill(database.skillId);
|
|
1773
1798
|
const source = setupSource(skill.sourceId);
|
|
1774
|
-
await
|
|
1799
|
+
await configureTiangongDatabaseCapability({
|
|
1800
|
+
kind: database.kind,
|
|
1775
1801
|
workspace: plan.workspace.path,
|
|
1776
1802
|
skillPath: join(codexRoot, skill.skillName),
|
|
1777
1803
|
source: {
|
|
@@ -1780,19 +1806,19 @@ async function configureSelectedCapabilities(plan, _environment) {
|
|
|
1780
1806
|
immutableRef: source.immutableRef,
|
|
1781
1807
|
expectedTreeSha256: skill.expectedTreeSha256,
|
|
1782
1808
|
license: "MIT",
|
|
1783
|
-
catalogId:
|
|
1809
|
+
catalogId: database.catalogId,
|
|
1784
1810
|
},
|
|
1785
|
-
endpoint: plan.settings[
|
|
1786
|
-
...(plan.settings[
|
|
1811
|
+
endpoint: plan.settings[`${database.settingPrefix}.endpoint`],
|
|
1812
|
+
...(plan.settings[`${database.settingPrefix}.region`] === undefined
|
|
1787
1813
|
? {}
|
|
1788
|
-
: { region: plan.settings[
|
|
1814
|
+
: { region: plan.settings[`${database.settingPrefix}.region`] }),
|
|
1789
1815
|
});
|
|
1790
1816
|
}
|
|
1791
1817
|
await reconcileSetupManagedCapabilities({
|
|
1792
1818
|
workspace: plan.workspace.path,
|
|
1793
1819
|
selectedCapabilityIds: [
|
|
1794
1820
|
...BRAVE_PROFILE_SKILLS[plan.selection.evidenceProfile].map(setupManagedCapabilityId),
|
|
1795
|
-
...(
|
|
1821
|
+
...tiangongDatabases.map((database) => database.capabilityId),
|
|
1796
1822
|
],
|
|
1797
1823
|
});
|
|
1798
1824
|
}
|
|
@@ -1887,6 +1913,14 @@ async function assertRequiredCredentialPreflight(plan, environment) {
|
|
|
1887
1913
|
.join(", ")}), then retry this exact step.`,
|
|
1888
1914
|
retryCommand: `tiangong-ai research setup retry --step credential-preflight --workspace ${plan.workspace.path} --json`,
|
|
1889
1915
|
exitCode: 3,
|
|
1916
|
+
diagnostics: {
|
|
1917
|
+
executionMode: "setup-preflight",
|
|
1918
|
+
credentialScope: [
|
|
1919
|
+
...new Set(failures.map((failure) => definitions.find((definition) => definition.id === failure.id).storage)),
|
|
1920
|
+
].join("+"),
|
|
1921
|
+
networkAttempted: false,
|
|
1922
|
+
missingCredentialIds: failures.map((failure) => failure.id),
|
|
1923
|
+
},
|
|
1890
1924
|
});
|
|
1891
1925
|
}
|
|
1892
1926
|
}
|
|
@@ -2885,7 +2919,7 @@ function commandFailure(step, command, result, root, environment) {
|
|
|
2885
2919
|
exitCode: 3,
|
|
2886
2920
|
});
|
|
2887
2921
|
}
|
|
2888
|
-
async function setupDoctorCheck(checks, id, category, callback) {
|
|
2922
|
+
async function setupDoctorCheck(checks, id, category, callback, failureMinimumAction = `Resolve ${id}, then rerun research setup doctor.`) {
|
|
2889
2923
|
try {
|
|
2890
2924
|
checks.push({
|
|
2891
2925
|
id,
|
|
@@ -2901,7 +2935,7 @@ async function setupDoctorCheck(checks, id, category, callback) {
|
|
|
2901
2935
|
category,
|
|
2902
2936
|
status: "fail",
|
|
2903
2937
|
detail: sanitizeResearchText(error instanceof Error ? error.message : String(error)),
|
|
2904
|
-
minimumAction:
|
|
2938
|
+
minimumAction: failureMinimumAction,
|
|
2905
2939
|
});
|
|
2906
2940
|
}
|
|
2907
2941
|
}
|
|
@@ -2955,35 +2989,12 @@ async function appendDependencyChecks(checks, selected, runner, root, environmen
|
|
|
2955
2989
|
return `${dependency.requirement} is active.`;
|
|
2956
2990
|
}
|
|
2957
2991
|
throw new Error(`No automatic dependency check is declared for ${dependency.id}. ${dependency.minimumAction}`);
|
|
2958
|
-
});
|
|
2992
|
+
}, dependency.minimumAction);
|
|
2959
2993
|
}
|
|
2960
2994
|
}
|
|
2961
2995
|
async function appendCompanionLiveChecks(checks, input) {
|
|
2962
|
-
void input.sleeper;
|
|
2963
2996
|
if (input.selected.some((skill) => skill.id === "tiangong.academic-paper-download")) {
|
|
2964
|
-
await
|
|
2965
|
-
const apiKey = input.adapterCredentials.get("semantic-scholar.api-key");
|
|
2966
|
-
const headers = new Headers({ Accept: "application/json" });
|
|
2967
|
-
if (apiKey)
|
|
2968
|
-
headers.set("x-api-key", apiKey);
|
|
2969
|
-
const response = await input.fetcher("https://api.semanticscholar.org/graph/v1/paper/search?query=reproducible%20research&limit=1&fields=paperId", {
|
|
2970
|
-
method: "GET",
|
|
2971
|
-
headers,
|
|
2972
|
-
redirect: "manual",
|
|
2973
|
-
signal: AbortSignal.timeout(30_000),
|
|
2974
|
-
});
|
|
2975
|
-
if (response.status >= 300 && response.status < 400) {
|
|
2976
|
-
throw new Error("Semantic Scholar returned a redirect; credential-bearing redirects are not followed.");
|
|
2977
|
-
}
|
|
2978
|
-
if (!response.ok) {
|
|
2979
|
-
const detail = await boundedResponseText(response, 2_000, apiKey ? [apiKey] : []);
|
|
2980
|
-
throw new Error(`Semantic Scholar live check returned HTTP ${response.status}${detail ? `: ${detail}` : ""}.`);
|
|
2981
|
-
}
|
|
2982
|
-
await response.body?.cancel().catch(() => undefined);
|
|
2983
|
-
return apiKey
|
|
2984
|
-
? "Semantic Scholar accepted the configured optional API key."
|
|
2985
|
-
: "Semantic Scholar public API is reachable without an optional API key.";
|
|
2986
|
-
});
|
|
2997
|
+
await appendSemanticScholarLiveCheck(checks, input);
|
|
2987
2998
|
}
|
|
2988
2999
|
if (input.selected.some((skill) => skill.id === "tiangong.document-granular-decompose")) {
|
|
2989
3000
|
if (!input.allowSyntheticUnstructureUpload) {
|
|
@@ -3029,6 +3040,116 @@ async function appendCompanionLiveChecks(checks, input) {
|
|
|
3029
3040
|
}
|
|
3030
3041
|
}
|
|
3031
3042
|
}
|
|
3043
|
+
async function appendSemanticScholarLiveCheck(checks, input) {
|
|
3044
|
+
const apiKey = input.adapterCredentials.get("semantic-scholar.api-key");
|
|
3045
|
+
const headers = new Headers({ Accept: "application/json" });
|
|
3046
|
+
if (apiKey)
|
|
3047
|
+
headers.set("x-api-key", apiKey);
|
|
3048
|
+
const url = "https://api.semanticscholar.org/graph/v1/paper/search?query=reproducible%20research&limit=1&fields=paperId";
|
|
3049
|
+
try {
|
|
3050
|
+
let response;
|
|
3051
|
+
let retryAfterSeconds = null;
|
|
3052
|
+
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
3053
|
+
response = await input.fetcher(url, {
|
|
3054
|
+
method: "GET",
|
|
3055
|
+
headers,
|
|
3056
|
+
redirect: "manual",
|
|
3057
|
+
signal: AbortSignal.timeout(30_000),
|
|
3058
|
+
});
|
|
3059
|
+
if (response.status !== 429 || attempt === 1)
|
|
3060
|
+
break;
|
|
3061
|
+
retryAfterSeconds = setupRetryAfterSeconds(response.headers.get("retry-after"));
|
|
3062
|
+
await response.body?.cancel().catch(() => undefined);
|
|
3063
|
+
await input.sleeper(Math.min(5, retryAfterSeconds ?? 1) * 1_000);
|
|
3064
|
+
}
|
|
3065
|
+
retryAfterSeconds ??= setupRetryAfterSeconds(response.headers.get("retry-after"));
|
|
3066
|
+
if (response.status >= 300 && response.status < 400) {
|
|
3067
|
+
await response.body?.cancel().catch(() => undefined);
|
|
3068
|
+
checks.push({
|
|
3069
|
+
id: "live.semantic-scholar",
|
|
3070
|
+
category: "live-check",
|
|
3071
|
+
status: "fail",
|
|
3072
|
+
detail: "Semantic Scholar returned a redirect; credential-bearing redirects are not followed.",
|
|
3073
|
+
minimumAction: "Verify the fixed Semantic Scholar endpoint and network policy, then rerun setup doctor; do not fall back to a standalone source wrapper.",
|
|
3074
|
+
diagnostics: {
|
|
3075
|
+
code: "PROVIDER_REDIRECT_REJECTED",
|
|
3076
|
+
executionMode: "setup-doctor",
|
|
3077
|
+
credentialScope: "adapter",
|
|
3078
|
+
networkAttempted: true,
|
|
3079
|
+
httpStatus: response.status,
|
|
3080
|
+
},
|
|
3081
|
+
});
|
|
3082
|
+
return;
|
|
3083
|
+
}
|
|
3084
|
+
if (!response.ok) {
|
|
3085
|
+
const authenticationFailure = response.status === 401 || response.status === 403;
|
|
3086
|
+
const rateLimited = response.status === 429;
|
|
3087
|
+
const detail = authenticationFailure
|
|
3088
|
+
? ""
|
|
3089
|
+
: await boundedResponseText(response, 2_000, apiKey ? [apiKey] : []);
|
|
3090
|
+
checks.push({
|
|
3091
|
+
id: "live.semantic-scholar",
|
|
3092
|
+
category: "live-check",
|
|
3093
|
+
status: "fail",
|
|
3094
|
+
detail: sanitizeResearchText(`Semantic Scholar live check returned HTTP ${response.status}${detail ? `: ${detail}` : ""}.`, apiKey ? [apiKey] : []),
|
|
3095
|
+
minimumAction: rateLimited
|
|
3096
|
+
? `Wait for the provider quota window${retryAfterSeconds === null ? "" : ` (Retry-After ${retryAfterSeconds}s)`}, then rerun setup doctor. The production workflow remains blocked and must not downgrade to standalone search.`
|
|
3097
|
+
: authenticationFailure
|
|
3098
|
+
? "Replace or remove the optional Semantic Scholar API key, verify provider entitlement, and rerun setup doctor; do not expose the key in output."
|
|
3099
|
+
: "Verify Semantic Scholar availability and the fixed API contract, then rerun setup doctor; do not downgrade the research workflow.",
|
|
3100
|
+
diagnostics: {
|
|
3101
|
+
code: authenticationFailure
|
|
3102
|
+
? "PROVIDER_AUTHENTICATION_FAILED"
|
|
3103
|
+
: rateLimited
|
|
3104
|
+
? "PROVIDER_RATE_LIMITED"
|
|
3105
|
+
: "PROVIDER_REQUEST_FAILED",
|
|
3106
|
+
executionMode: "setup-doctor",
|
|
3107
|
+
credentialScope: "adapter",
|
|
3108
|
+
networkAttempted: true,
|
|
3109
|
+
httpStatus: response.status,
|
|
3110
|
+
retryAfterSeconds,
|
|
3111
|
+
},
|
|
3112
|
+
});
|
|
3113
|
+
return;
|
|
3114
|
+
}
|
|
3115
|
+
await response.body?.cancel().catch(() => undefined);
|
|
3116
|
+
checks.push({
|
|
3117
|
+
id: "live.semantic-scholar",
|
|
3118
|
+
category: "live-check",
|
|
3119
|
+
status: "pass",
|
|
3120
|
+
detail: apiKey
|
|
3121
|
+
? "Semantic Scholar accepted the configured optional API key."
|
|
3122
|
+
: "Semantic Scholar public API is reachable without an optional API key.",
|
|
3123
|
+
minimumAction: null,
|
|
3124
|
+
});
|
|
3125
|
+
}
|
|
3126
|
+
catch (error) {
|
|
3127
|
+
checks.push({
|
|
3128
|
+
id: "live.semantic-scholar",
|
|
3129
|
+
category: "live-check",
|
|
3130
|
+
status: "fail",
|
|
3131
|
+
detail: sanitizeResearchText(error instanceof Error ? error.message : String(error), apiKey ? [apiKey] : []),
|
|
3132
|
+
minimumAction: "Restore provider connectivity, then rerun setup doctor; the production workflow must remain blocked instead of using a standalone fallback.",
|
|
3133
|
+
diagnostics: {
|
|
3134
|
+
code: "PROVIDER_TRANSPORT_FAILED",
|
|
3135
|
+
executionMode: "setup-doctor",
|
|
3136
|
+
credentialScope: "adapter",
|
|
3137
|
+
networkAttempted: true,
|
|
3138
|
+
},
|
|
3139
|
+
});
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
function setupRetryAfterSeconds(value) {
|
|
3143
|
+
if (!value)
|
|
3144
|
+
return null;
|
|
3145
|
+
const seconds = Number(value);
|
|
3146
|
+
if (Number.isFinite(seconds) && seconds >= 0)
|
|
3147
|
+
return Math.ceil(seconds);
|
|
3148
|
+
const timestamp = Date.parse(value);
|
|
3149
|
+
if (!Number.isFinite(timestamp))
|
|
3150
|
+
return null;
|
|
3151
|
+
return Math.max(0, Math.ceil((timestamp - Date.now()) / 1_000));
|
|
3152
|
+
}
|
|
3032
3153
|
async function boundedResponseText(response, maximumBytes, secrets) {
|
|
3033
3154
|
if (!response.body)
|
|
3034
3155
|
return "";
|