@c4a/context-cli 0.7.26 → 0.7.30
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/README.md +12 -0
- package/README.zh-CN.md +8 -0
- package/cli.js +148 -51
- package/indexers/contracts/profile-contract.json +245 -245
- package/indexers/release-manifest.json +1 -1
- package/package.json +12 -12
- package/parserEntryWorker.js +90 -10
- package/plugins/README.md +1 -1
- package/plugins/README_CN.md +1 -1
- package/plugins/VERSION +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/commands/context-inspect-search.md +98 -25
- package/plugins/claude/skills/context-inspect-search/SKILL.md +98 -25
- package/plugins/codex/.codex-plugin/plugin.json +2 -2
- package/plugins/codex/skills/context-inspect-search/SKILL.md +98 -25
- package/plugins/cursor/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor/commands/c4a-context-inspect-search.md +98 -25
- package/plugins/cursor/skills/context-inspect-search/SKILL.md +98 -25
- package/plugins/skills/context-inspect-search/SKILL.md +98 -25
- package/providers/context/graphs/indexer.yaml +4 -0
- package/providers/context/manifest.json +25 -16
- package/providers/context/provider.yaml +1 -1
- package/providers/context/resources/dialogue/document-capture.md +8 -0
- package/providers/context/resources/manuals/guides/lark-resources.md +17 -1
- package/providers/context/resources/procedures/document-capture.md +88 -14
- package/providers/context/resources/procedures/document-code-investigation.md +45 -0
- package/providers/context/resources/procedures/repository-source-recovery.md +8 -1
- package/providers/context/resources/procedures/source-capture-detailed.md +6 -0
- package/providers/context/skills/work-production-stage/SKILL.md +4 -0
package/README.md
CHANGED
|
@@ -283,6 +283,18 @@ MIT.
|
|
|
283
283
|
|
|
284
284
|
## Updating an existing workspace
|
|
285
285
|
|
|
286
|
+
### Lark read identity
|
|
287
|
+
|
|
288
|
+
`CONTEXT_LARK_IDENTITY=user|bot` selects the identity for Lark acquisition
|
|
289
|
+
(default: `user`). Export it in each shell executing Context commands. In Bot
|
|
290
|
+
mode, a credential/scope/access failure retries once as the current user; the
|
|
291
|
+
rest of that capture keeps the fallback identity for pagination and resources.
|
|
292
|
+
Network, rate-limit and parsing errors do not trigger a user authorization
|
|
293
|
+
attempt. This does not change source-read consent or the audience of published
|
|
294
|
+
knowledge. Host-managed OAuth must use the host's authorization flow.
|
|
295
|
+
|
|
296
|
+
### Workspace updates
|
|
297
|
+
|
|
286
298
|
Start with `context entry --format json` and read the returned update guide when
|
|
287
299
|
adding material or changing published knowledge. Entry and status only observe;
|
|
288
300
|
they do not resume an earlier managed task before the new request is understood.
|
package/README.zh-CN.md
CHANGED
|
@@ -47,6 +47,14 @@ Provider Skill 投影到 Claude、Codex 和 Cursor 的用户技能目录。
|
|
|
47
47
|
方法并停止。它不会为每次调用增加安装预检,也不会把普通工作流中的 `not found`
|
|
48
48
|
诊断误判成可执行文件缺失。
|
|
49
49
|
|
|
50
|
+
## 飞书读取身份
|
|
51
|
+
|
|
52
|
+
`CONTEXT_LARK_IDENTITY=user|bot` 控制飞书来源采集,默认 `user`。每次执行 Context
|
|
53
|
+
命令的 shell 都需传入该环境变量;仅在提示词中写配置并不会自动导出环境变量。
|
|
54
|
+
`bot` 模式先使用应用身份,遇到凭证、scope 或访问权限错误时回退当前用户一次,
|
|
55
|
+
本次采集后续分页、引用与资源下载沿用回退身份。网络、限流和解析错误不切换身份。
|
|
56
|
+
该配置不扩大来源授权或知识产物可见范围;托管运行时的用户授权遵循宿主流程。
|
|
57
|
+
|
|
50
58
|
## 一个 Agent 入口如何驱动整套工作流
|
|
51
59
|
|
|
52
60
|
```text
|
package/cli.js
CHANGED
|
@@ -76780,22 +76780,14 @@ var init_indexerSourceBoundary = __esm(() => {
|
|
|
76780
76780
|
|
|
76781
76781
|
// src/project/indexerParserSourceMaterialization.ts
|
|
76782
76782
|
import { execFile as execFile3 } from "node:child_process";
|
|
76783
|
-
import { lstat as lstat4, readFile as readFile18 } from "node:fs/promises";
|
|
76783
|
+
import { lstat as lstat4, readFile as readFile18, stat as stat6 } from "node:fs/promises";
|
|
76784
76784
|
import { promisify as promisify3 } from "node:util";
|
|
76785
76785
|
import { basename as basename4, extname as extname8, join as join17 } from "node:path";
|
|
76786
76786
|
function sourceRefName(sourceRef) {
|
|
76787
76787
|
return sourceRef.startsWith("repo:") ? sourceRef.slice("repo:".length) : null;
|
|
76788
76788
|
}
|
|
76789
76789
|
async function trackedPaths(root, ref2) {
|
|
76790
|
-
|
|
76791
|
-
await execFileAsync2("git", ["diff", "--quiet", ref2, "--", "."], { cwd: root });
|
|
76792
|
-
} catch (error) {
|
|
76793
|
-
const exitCode = error !== null && typeof error === "object" && "code" in error ? error.code : null;
|
|
76794
|
-
if (exitCode === 1) {
|
|
76795
|
-
throw new TypeError(`materialized repository source differs from its pinned ref: ${root}`);
|
|
76796
|
-
}
|
|
76797
|
-
throw error;
|
|
76798
|
-
}
|
|
76790
|
+
await assertPinnedSource(root, ref2);
|
|
76799
76791
|
const { stdout } = await execFileAsync2("git", ["ls-files", "-z", "--", "."], {
|
|
76800
76792
|
cwd: root,
|
|
76801
76793
|
encoding: "buffer",
|
|
@@ -76804,6 +76796,20 @@ async function trackedPaths(root, ref2) {
|
|
|
76804
76796
|
return Buffer.from(stdout).toString("utf8").split("\x00").filter(Boolean).sort();
|
|
76805
76797
|
}
|
|
76806
76798
|
async function assertPinnedSource(root, ref2) {
|
|
76799
|
+
const sourceStat = await stat6(root).catch((error) => {
|
|
76800
|
+
if (error.code === "ENOENT" || error.code === "ENOTDIR")
|
|
76801
|
+
return;
|
|
76802
|
+
throw error;
|
|
76803
|
+
});
|
|
76804
|
+
if (!sourceStat?.isDirectory()) {
|
|
76805
|
+
throw new ContextError(ExitCode.WorkspaceStateError, `Repository source directory is unavailable: ${root}. Inspect recovery with context source recovery-plan --format json.`, {
|
|
76806
|
+
category: ErrorCategory.WorkspaceStateInvalid,
|
|
76807
|
+
reason_code: "repository-source-directory-unavailable",
|
|
76808
|
+
cwd: root,
|
|
76809
|
+
next_action: { command: "context source recovery-plan --format json" },
|
|
76810
|
+
input_schema: { type: "object", properties: {}, additionalProperties: false }
|
|
76811
|
+
});
|
|
76812
|
+
}
|
|
76807
76813
|
try {
|
|
76808
76814
|
await execFileAsync2("git", ["diff", "--quiet", ref2, "--", "."], { cwd: root });
|
|
76809
76815
|
} catch (error) {
|
|
@@ -76811,6 +76817,15 @@ async function assertPinnedSource(root, ref2) {
|
|
|
76811
76817
|
if (exitCode === 1) {
|
|
76812
76818
|
throw new TypeError(`materialized repository source differs from its pinned ref: ${root}`);
|
|
76813
76819
|
}
|
|
76820
|
+
if (exitCode === "ENOENT") {
|
|
76821
|
+
throw new ContextError(ExitCode.ExternalToolError, `Git could not start for repository source ${root}. Check git --version and the source directory in the same host environment.`, {
|
|
76822
|
+
category: ErrorCategory.ExternalToolFailed,
|
|
76823
|
+
reason_code: "repository-git-spawn-unavailable",
|
|
76824
|
+
cwd: root,
|
|
76825
|
+
next_action: { command: "git --version" },
|
|
76826
|
+
input_schema: { type: "object", properties: {}, additionalProperties: false }
|
|
76827
|
+
});
|
|
76828
|
+
}
|
|
76814
76829
|
throw error;
|
|
76815
76830
|
}
|
|
76816
76831
|
}
|
|
@@ -76874,6 +76889,9 @@ var init_indexerParserSourceMaterialization = __esm(() => {
|
|
|
76874
76889
|
init_src2();
|
|
76875
76890
|
init_indexerReadScopeAuthorization();
|
|
76876
76891
|
init_indexerSourceBoundary();
|
|
76892
|
+
init_errors3();
|
|
76893
|
+
init_exitCode();
|
|
76894
|
+
init_cliFeedback();
|
|
76877
76895
|
execFileAsync2 = promisify3(execFile3);
|
|
76878
76896
|
});
|
|
76879
76897
|
|
|
@@ -76882,7 +76900,7 @@ var exports_managedDocumentSnapshot = {};
|
|
|
76882
76900
|
__export(exports_managedDocumentSnapshot, {
|
|
76883
76901
|
readManagedDocumentSnapshot: () => readManagedDocumentSnapshot
|
|
76884
76902
|
});
|
|
76885
|
-
import { readFile as readFile19, stat as
|
|
76903
|
+
import { readFile as readFile19, stat as stat7 } from "node:fs/promises";
|
|
76886
76904
|
import { basename as basename5, dirname as dirname15 } from "node:path";
|
|
76887
76905
|
async function readManagedDocumentSnapshot(projectRoot, type, name2) {
|
|
76888
76906
|
const absolutePath = await assertManagedDocumentPath(projectRoot, type, name2);
|
|
@@ -76891,7 +76909,7 @@ async function readManagedDocumentSnapshot(projectRoot, type, name2) {
|
|
|
76891
76909
|
const manifest = createDocumentSnapshotManifest({
|
|
76892
76910
|
sourceType: type,
|
|
76893
76911
|
sourceName: name2,
|
|
76894
|
-
capturedAt: (await
|
|
76912
|
+
capturedAt: (await stat7(absolutePath)).mtime.toISOString(),
|
|
76895
76913
|
files: [{
|
|
76896
76914
|
path: file,
|
|
76897
76915
|
bytes: markdown,
|
|
@@ -79423,7 +79441,7 @@ __export(exports_productionStageStore, {
|
|
|
79423
79441
|
PRODUCTION_STAGES_ROOT: () => PRODUCTION_STAGES_ROOT
|
|
79424
79442
|
});
|
|
79425
79443
|
import { constants as constants4 } from "node:fs";
|
|
79426
|
-
import { readFile as readFile28, access as access2, stat as
|
|
79444
|
+
import { readFile as readFile28, access as access2, stat as stat8 } from "node:fs/promises";
|
|
79427
79445
|
import { join as join31 } from "node:path";
|
|
79428
79446
|
function productionStageDirectory(id3) {
|
|
79429
79447
|
productionAgentDirectory(id3);
|
|
@@ -79442,7 +79460,7 @@ async function readOptional(root, path2) {
|
|
|
79442
79460
|
async function hasReadableProjection(root, path2) {
|
|
79443
79461
|
const absolute = await safeProjectTarget(root, path2);
|
|
79444
79462
|
try {
|
|
79445
|
-
if (!(await
|
|
79463
|
+
if (!(await stat8(absolute)).isFile())
|
|
79446
79464
|
throw new TypeError(`Stage material must be a regular file: ${path2}`);
|
|
79447
79465
|
await access2(absolute, constants4.R_OK);
|
|
79448
79466
|
return true;
|
|
@@ -81693,19 +81711,19 @@ function failure(reason, message, next) {
|
|
|
81693
81711
|
}
|
|
81694
81712
|
async function entries(root, path2) {
|
|
81695
81713
|
await safeProjectTarget(root, path2);
|
|
81696
|
-
let
|
|
81714
|
+
let stat9;
|
|
81697
81715
|
try {
|
|
81698
|
-
|
|
81716
|
+
stat9 = await lstat7(join43(root, path2));
|
|
81699
81717
|
} catch (error) {
|
|
81700
81718
|
if (error.code === "ENOENT")
|
|
81701
81719
|
return [];
|
|
81702
81720
|
throw error;
|
|
81703
81721
|
}
|
|
81704
|
-
if (
|
|
81722
|
+
if (stat9.isSymbolicLink())
|
|
81705
81723
|
failure("preparation-symlink", `Refusing to clean linked task state: ${path2}`, "Inspect and relocate the link without deleting its target, then preview again.");
|
|
81706
|
-
if (
|
|
81724
|
+
if (stat9.isFile())
|
|
81707
81725
|
return [path2];
|
|
81708
|
-
if (!
|
|
81726
|
+
if (!stat9.isDirectory())
|
|
81709
81727
|
failure("preparation-file-type", `Unsupported task-state file: ${path2}`, "Inspect this file before preparing the workspace.");
|
|
81710
81728
|
const result = [];
|
|
81711
81729
|
for (const name3 of (await readdir10(join43(root, path2))).sort())
|
|
@@ -86351,7 +86369,7 @@ var init_packageSiteExtensions = __esm(() => {
|
|
|
86351
86369
|
// src/project/packageSite.ts
|
|
86352
86370
|
import { createHash as createHash17 } from "node:crypto";
|
|
86353
86371
|
import { spawn as spawn3 } from "node:child_process";
|
|
86354
|
-
import { mkdir as mkdir23, mkdtemp as mkdtemp2, readFile as readFile52, writeFile as writeFile19, rm as rm12, symlink as symlink2, cp, access as access5, stat as
|
|
86372
|
+
import { mkdir as mkdir23, mkdtemp as mkdtemp2, readFile as readFile52, writeFile as writeFile19, rm as rm12, symlink as symlink2, cp, access as access5, stat as stat9 } from "node:fs/promises";
|
|
86355
86373
|
import { createRequire as createRequire5 } from "node:module";
|
|
86356
86374
|
import { dirname as dirname28, join as join64, posix as posix6 } from "node:path";
|
|
86357
86375
|
function sitePagePath(identity) {
|
|
@@ -86675,7 +86693,7 @@ title: LLM Docs
|
|
|
86675
86693
|
const placeholder = [".css", ".js", ".mjs", ".cjs"].includes(extension2) ? `/* Intentionally empty. */
|
|
86676
86694
|
` : [".html", ".htm"].includes(extension2) ? `<!-- Intentionally empty. -->
|
|
86677
86695
|
` : null;
|
|
86678
|
-
if (placeholder !== null && (await
|
|
86696
|
+
if (placeholder !== null && (await stat9(file.absPath)).size === 0) {
|
|
86679
86697
|
await writeFile19(file.absPath, placeholder);
|
|
86680
86698
|
}
|
|
86681
86699
|
}
|
|
@@ -89996,7 +90014,7 @@ var init_sourceInputMutation = __esm(() => {
|
|
|
89996
90014
|
|
|
89997
90015
|
// src/project/documentCapture.ts
|
|
89998
90016
|
import { createHash as createHash21 } from "node:crypto";
|
|
89999
|
-
import { mkdir as mkdir28, readdir as readdir18, readFile as readFile63, rm as rm17, stat as
|
|
90017
|
+
import { mkdir as mkdir28, readdir as readdir18, readFile as readFile63, rm as rm17, stat as stat10, writeFile as writeFile23 } from "node:fs/promises";
|
|
90000
90018
|
import { basename as basename8, dirname as dirname34, extname as extname12, join as join75, relative as relative22, resolve as resolve24 } from "node:path";
|
|
90001
90019
|
function toPosixPath7(path2) {
|
|
90002
90020
|
return path2.split(/[\\/]+/u).filter((part) => part.length > 0).join("/");
|
|
@@ -90101,7 +90119,7 @@ function assignFlatSnapshotPaths(files, module) {
|
|
|
90101
90119
|
});
|
|
90102
90120
|
}
|
|
90103
90121
|
async function walkMarkdownFiles(input) {
|
|
90104
|
-
const rootStat = await
|
|
90122
|
+
const rootStat = await stat10(input.localRoot);
|
|
90105
90123
|
if (rootStat.isFile()) {
|
|
90106
90124
|
const sourcePath = normalizeSnapshotRelativePath(basename8(input.localRoot));
|
|
90107
90125
|
return {
|
|
@@ -98636,6 +98654,72 @@ var init_larkMarkdownImages = __esm(() => {
|
|
|
98636
98654
|
init_markdownLinks();
|
|
98637
98655
|
});
|
|
98638
98656
|
|
|
98657
|
+
// src/lib/larkReadIdentity.ts
|
|
98658
|
+
function resolveLarkReadIdentity() {
|
|
98659
|
+
const value = process.env.CONTEXT_LARK_IDENTITY?.trim() || "user";
|
|
98660
|
+
if (value === "user" || value === "bot")
|
|
98661
|
+
return value;
|
|
98662
|
+
throw new ContextError(ExitCode.UserError, "CONTEXT_LARK_IDENTITY must be user or bot", {
|
|
98663
|
+
reason_code: "lark.invalid-read-identity",
|
|
98664
|
+
valid_values: ["user", "bot"],
|
|
98665
|
+
next: "Set CONTEXT_LARK_IDENTITY to user or bot and rerun the same command"
|
|
98666
|
+
});
|
|
98667
|
+
}
|
|
98668
|
+
function credentialFailure(result) {
|
|
98669
|
+
for (const output of [result.stdout, result.stderr]) {
|
|
98670
|
+
const start2 = output.indexOf("{");
|
|
98671
|
+
if (start2 < 0)
|
|
98672
|
+
continue;
|
|
98673
|
+
try {
|
|
98674
|
+
const parsed = JSON.parse(output.slice(start2));
|
|
98675
|
+
if (!parsed || typeof parsed !== "object" || !("error" in parsed))
|
|
98676
|
+
continue;
|
|
98677
|
+
if (result.exitCode === 0 && (!("ok" in parsed) || parsed.ok !== false))
|
|
98678
|
+
continue;
|
|
98679
|
+
const error = parsed.error;
|
|
98680
|
+
if (!error || typeof error !== "object")
|
|
98681
|
+
continue;
|
|
98682
|
+
const type = "type" in error ? error.type : undefined;
|
|
98683
|
+
const subtype = "subtype" in error ? error.subtype : undefined;
|
|
98684
|
+
if (type === "authorization" || type === "authentication" || ["missing_scope", "permission_denied", "access_denied", "need_user_authorization"].includes(String(subtype))) {
|
|
98685
|
+
return true;
|
|
98686
|
+
}
|
|
98687
|
+
} catch {}
|
|
98688
|
+
}
|
|
98689
|
+
return result.exitCode !== 0 && /reason=(?:bot_unavailable|not_authorized)\b|\b(?:missing_scope|permission_denied|access_denied)\b/iu.test(result.stderr);
|
|
98690
|
+
}
|
|
98691
|
+
function createLarkReadSession(runner2, initial, fallback = initial === "bot") {
|
|
98692
|
+
let identity2 = initial;
|
|
98693
|
+
let usedFallback = false;
|
|
98694
|
+
const run = async (args, options) => {
|
|
98695
|
+
const explicit = args.indexOf("--as");
|
|
98696
|
+
const requested = explicit >= 0 ? args[explicit + 1] : identity2;
|
|
98697
|
+
const selected = usedFallback ? "user" : requested === "bot" ? "bot" : "user";
|
|
98698
|
+
const withIdentity = [...args];
|
|
98699
|
+
if (explicit >= 0)
|
|
98700
|
+
withIdentity[explicit + 1] = selected;
|
|
98701
|
+
else
|
|
98702
|
+
withIdentity.push("--as", selected);
|
|
98703
|
+
const result = await runner2(withIdentity, options);
|
|
98704
|
+
identity2 = selected;
|
|
98705
|
+
if (identity2 !== "bot" || !fallback || usedFallback || args.includes("--help") || !credentialFailure(result))
|
|
98706
|
+
return result;
|
|
98707
|
+
usedFallback = true;
|
|
98708
|
+
identity2 = "user";
|
|
98709
|
+
withIdentity[withIdentity.indexOf("--as") + 1] = "user";
|
|
98710
|
+
return runner2(withIdentity, options);
|
|
98711
|
+
};
|
|
98712
|
+
return { run, get identity() {
|
|
98713
|
+
return identity2;
|
|
98714
|
+
}, get usedFallback() {
|
|
98715
|
+
return usedFallback;
|
|
98716
|
+
} };
|
|
98717
|
+
}
|
|
98718
|
+
var init_larkReadIdentity = __esm(() => {
|
|
98719
|
+
init_errors3();
|
|
98720
|
+
init_exitCode();
|
|
98721
|
+
});
|
|
98722
|
+
|
|
98639
98723
|
// src/lib/feishu.ts
|
|
98640
98724
|
import { spawn as spawn4 } from "node:child_process";
|
|
98641
98725
|
async function detectDocsFetchCapabilities(runner2) {
|
|
@@ -98652,14 +98736,19 @@ ${result.stderr}`;
|
|
|
98652
98736
|
supportsDocFormat: result.exitCode === 0 && /\s--doc-format\b/u.test(help)
|
|
98653
98737
|
};
|
|
98654
98738
|
}
|
|
98655
|
-
function resolveDocsFetchPlan(requested, runner2) {
|
|
98739
|
+
function resolveDocsFetchPlan(requested, runner2, cacheKey = runner2, identity2 = "user") {
|
|
98656
98740
|
if (requested === "v1" || requested === "v2") {
|
|
98657
98741
|
return Promise.resolve({ apiVersion: requested, docFormat: "xml" });
|
|
98658
98742
|
}
|
|
98659
|
-
let
|
|
98743
|
+
let cached = docsFetchCapabilitiesCache.get(cacheKey);
|
|
98744
|
+
if (!cached) {
|
|
98745
|
+
cached = new Map;
|
|
98746
|
+
docsFetchCapabilitiesCache.set(cacheKey, cached);
|
|
98747
|
+
}
|
|
98748
|
+
let capabilitiesPromise = cached.get(identity2);
|
|
98660
98749
|
if (capabilitiesPromise === undefined) {
|
|
98661
98750
|
capabilitiesPromise = detectDocsFetchCapabilities(runner2);
|
|
98662
|
-
|
|
98751
|
+
cached.set(identity2, capabilitiesPromise);
|
|
98663
98752
|
}
|
|
98664
98753
|
return capabilitiesPromise.then((capabilities) => {
|
|
98665
98754
|
if (!capabilities.supportsDocFormat) {
|
|
@@ -98956,20 +99045,25 @@ function hasEmptySubPageList(projection) {
|
|
|
98956
99045
|
return projection.fidelity.issues.some((issue) => issue.code === LARK_EMPTY_SUB_PAGE_LIST_CODE);
|
|
98957
99046
|
}
|
|
98958
99047
|
async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
98959
|
-
const
|
|
99048
|
+
const preference = input.prefetched?.identity ?? input.identity ?? resolveLarkReadIdentity();
|
|
99049
|
+
const cacheKey = runner2;
|
|
99050
|
+
const session = createLarkReadSession(runner2, input.prefetched?.identity ?? (preference === "bot" ? "bot" : "user"), !input.prefetched && preference === "bot");
|
|
99051
|
+
runner2 = session.run;
|
|
99052
|
+
const docsFetchPlan = input.prefetched ? { apiVersion: "v2", docFormat: "xml" } : await resolveDocsFetchPlan(input.docsApiVersion ?? "auto", runner2, cacheKey, preference);
|
|
98960
99053
|
const identityFetch = input.prefetched ? {
|
|
98961
99054
|
fetched: await fetchDocsResponse(input, docsFetchPlan, runner2, input.prefetched.identity),
|
|
98962
99055
|
identity: input.prefetched.identity,
|
|
98963
99056
|
fallback: false
|
|
98964
|
-
} : await fetchWithIdentity(input, docsFetchPlan, runner2);
|
|
99057
|
+
} : await fetchWithIdentity({ ...input, identity: preference }, docsFetchPlan, runner2);
|
|
98965
99058
|
let fetched = identityFetch.fetched;
|
|
98966
|
-
|
|
99059
|
+
let accessIdentity = input.prefetched?.identity ?? session.identity;
|
|
98967
99060
|
let projection;
|
|
98968
99061
|
for (let attempt = 0;attempt < MAX_STRUCTURAL_FETCH_ATTEMPTS && fetched.contentFormat === "xml"; attempt++) {
|
|
98969
99062
|
projection = projectLarkDocxXml({ xml: fetched.body, sourceUrl: input.url });
|
|
98970
99063
|
if (input.prefetched || !hasEmptySubPageList(projection) || attempt === MAX_STRUCTURAL_FETCH_ATTEMPTS - 1)
|
|
98971
99064
|
break;
|
|
98972
99065
|
fetched = await fetchDocsResponse(input, docsFetchPlan, runner2, accessIdentity);
|
|
99066
|
+
accessIdentity = session.identity;
|
|
98973
99067
|
}
|
|
98974
99068
|
let body2 = fetched.body;
|
|
98975
99069
|
let title2 = fetched.title;
|
|
@@ -99055,7 +99149,7 @@ ${body2}`,
|
|
|
99055
99149
|
fidelity,
|
|
99056
99150
|
resourceMaterialization,
|
|
99057
99151
|
accessIdentity,
|
|
99058
|
-
identityFallback: identityFetch.fallback
|
|
99152
|
+
identityFallback: identityFetch.fallback || session.usedFallback
|
|
99059
99153
|
};
|
|
99060
99154
|
}
|
|
99061
99155
|
return {
|
|
@@ -99066,7 +99160,7 @@ ${body2}`,
|
|
|
99066
99160
|
fidelity,
|
|
99067
99161
|
resourceMaterialization,
|
|
99068
99162
|
accessIdentity,
|
|
99069
|
-
identityFallback: identityFetch.fallback
|
|
99163
|
+
identityFallback: identityFetch.fallback || session.usedFallback
|
|
99070
99164
|
};
|
|
99071
99165
|
}
|
|
99072
99166
|
var LARK_BIN = "lark-cli", MAX_FETCH_PAGES = 50, MAX_STRUCTURAL_FETCH_ATTEMPTS = 2, LarkCliNotInstalledError, LarkCliError, defaultRunner = (args, options) => new Promise((resolve8, reject) => {
|
|
@@ -99098,6 +99192,7 @@ var init_feishu = __esm(() => {
|
|
|
99098
99192
|
init_larkResourceMaterialization();
|
|
99099
99193
|
init_larkCaptureReport();
|
|
99100
99194
|
init_larkMarkdownImages();
|
|
99195
|
+
init_larkReadIdentity();
|
|
99101
99196
|
LarkCliNotInstalledError = class LarkCliNotInstalledError extends Error {
|
|
99102
99197
|
constructor() {
|
|
99103
99198
|
super(`${LARK_BIN} not installed — see https://github.com/larksuite/cli for install instructions`);
|
|
@@ -99258,7 +99353,7 @@ function larkErrorRecovery(error, sourceName) {
|
|
|
99258
99353
|
if (/auth|login|permission|forbidden|unauthori[sz]ed|scope/iu.test(message)) {
|
|
99259
99354
|
return {
|
|
99260
99355
|
reasonCode: "external.authorization-required",
|
|
99261
|
-
next: "
|
|
99356
|
+
next: "Check the selected Lark identity and document access. For host-managed credentials use the host authorization flow; use lark-cli auth login only for a locally managed user login, then rerun capture"
|
|
99262
99357
|
};
|
|
99263
99358
|
}
|
|
99264
99359
|
return {
|
|
@@ -99403,6 +99498,8 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
99403
99498
|
...input.prefetched === undefined ? {} : { prefetched: input.prefetched }
|
|
99404
99499
|
}, input.larkRunner);
|
|
99405
99500
|
} catch (error) {
|
|
99501
|
+
if (error instanceof ContextError)
|
|
99502
|
+
throw error;
|
|
99406
99503
|
const failure2 = normalizeLarkError(error, resolved.sourceName);
|
|
99407
99504
|
if (failure2.detail?.reason_code === "external.authorization-required" || failure2.detail?.reason_code === "external.tool-failed" && error instanceof LarkCliError && error.exitCode !== 0) {
|
|
99408
99505
|
throw new ContextError(failure2.code, failure2.message, { ...failure2.detail, reason_code: DOCUMENT_ACQUISITION_UNAVAILABLE, acquisition_cause: failure2.detail?.reason_code });
|
|
@@ -99576,7 +99673,7 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
99576
99673
|
resource_materialization: fetched.resourceMaterialization,
|
|
99577
99674
|
diagnostics: [
|
|
99578
99675
|
...recovery === undefined ? [] : [recovery.diagnostic],
|
|
99579
|
-
...fetched.identityFallback ? [`info: lark.capture.identity-fallback:
|
|
99676
|
+
...fetched.identityFallback ? [`info: lark.capture.identity-fallback: capture used an identity fallback; document identity: ${fetched.accessIdentity}; embedded reads may use the fallback identity`] : [],
|
|
99580
99677
|
...fetched.fidelity.issues.map((issue) => `${issue.severity}: ${issue.code}: ${issue.block_type} × ${issue.count}: ${issue.reason}`),
|
|
99581
99678
|
...fetched.resourceMaterialization.items.filter((item) => item.status === "failed" || item.kind === "poll" && item.reason?.includes("absent") === true).map((item) => `${item.status}: lark.capture.resource-${item.status}: ${item.kind}: ${item.reason ?? item.locator}`)
|
|
99582
99679
|
],
|
|
@@ -99938,15 +100035,15 @@ import { join as join100 } from "node:path";
|
|
|
99938
100035
|
import { createHash as createHash30, randomUUID as randomUUID7 } from "node:crypto";
|
|
99939
100036
|
async function inspectWriterLock(root2) {
|
|
99940
100037
|
const path3 = await safeProjectTarget(root2, lockRelative);
|
|
99941
|
-
let
|
|
100038
|
+
let stat11;
|
|
99942
100039
|
try {
|
|
99943
|
-
|
|
100040
|
+
stat11 = await lstat10(path3);
|
|
99944
100041
|
} catch (error) {
|
|
99945
100042
|
if (error.code === "ENOENT")
|
|
99946
100043
|
return null;
|
|
99947
100044
|
throw error;
|
|
99948
100045
|
}
|
|
99949
|
-
if (!
|
|
100046
|
+
if (!stat11.isDirectory() || stat11.isSymbolicLink())
|
|
99950
100047
|
throw new Error("Writer lock must be a real directory.");
|
|
99951
100048
|
const ownerPath = await safeProjectTarget(root2, `${lockRelative}/owner.json`);
|
|
99952
100049
|
const bytes = await readFile82(ownerPath, "utf8");
|
|
@@ -99961,7 +100058,7 @@ async function inspectWriterLock(root2) {
|
|
|
99961
100058
|
const code3 = error.code;
|
|
99962
100059
|
processState = code3 === "ESRCH" ? "not-running" : code3 === "EPERM" ? "running" : "unknown";
|
|
99963
100060
|
}
|
|
99964
|
-
const digest6 = `sha256:${createHash30("sha256").update(`${
|
|
100061
|
+
const digest6 = `sha256:${createHash30("sha256").update(`${stat11.dev}:${stat11.ino}:${stat11.birthtimeMs}:${bytes}`).digest("hex")}`;
|
|
99965
100062
|
return { ...owner, process_state: processState, digest: digest6, path: lockRelative };
|
|
99966
100063
|
}
|
|
99967
100064
|
async function recoverWriterLock(input) {
|
|
@@ -100033,22 +100130,22 @@ async function recoveryJournals(root2) {
|
|
|
100033
100130
|
const entries2 = [];
|
|
100034
100131
|
async function visit4(path3, depth) {
|
|
100035
100132
|
await safeProjectTarget(root2, path3);
|
|
100036
|
-
let
|
|
100133
|
+
let stat11;
|
|
100037
100134
|
try {
|
|
100038
|
-
|
|
100135
|
+
stat11 = await lstat11(join101(root2, path3));
|
|
100039
100136
|
} catch (error) {
|
|
100040
100137
|
if (error.code === "ENOENT")
|
|
100041
100138
|
return;
|
|
100042
100139
|
throw error;
|
|
100043
100140
|
}
|
|
100044
|
-
if (
|
|
100141
|
+
if (stat11.isSymbolicLink())
|
|
100045
100142
|
throw new TypeError("Recovery does not follow transaction symlinks.");
|
|
100046
|
-
if (
|
|
100143
|
+
if (stat11.isDirectory()) {
|
|
100047
100144
|
if (depth > 3)
|
|
100048
100145
|
throw new TypeError("Unexpected transaction directory depth; preserve it for diagnosis.");
|
|
100049
100146
|
for (const name3 of (await readdir25(join101(root2, path3))).sort())
|
|
100050
100147
|
await visit4(`${path3}/${name3}`, depth + 1);
|
|
100051
|
-
} else if (
|
|
100148
|
+
} else if (stat11.isFile())
|
|
100052
100149
|
entries2.push({ path: path3, digest: indexerProtocolDigest(await recoveryText(root2, path3)) });
|
|
100053
100150
|
}
|
|
100054
100151
|
await visit4(TRANSACTIONS_PATH, 0);
|
|
@@ -112531,7 +112628,7 @@ function conflict(path3) {
|
|
|
112531
112628
|
next: "Choose another --local directory, or move conflicting/customized skills aside before retrying."
|
|
112532
112629
|
});
|
|
112533
112630
|
}
|
|
112534
|
-
async function
|
|
112631
|
+
async function stat11(path3) {
|
|
112535
112632
|
try {
|
|
112536
112633
|
return await lstat14(path3);
|
|
112537
112634
|
} catch (error) {
|
|
@@ -112541,7 +112638,7 @@ async function stat10(path3) {
|
|
|
112541
112638
|
}
|
|
112542
112639
|
}
|
|
112543
112640
|
async function checkParents(path3) {
|
|
112544
|
-
const entry = await
|
|
112641
|
+
const entry = await stat11(path3);
|
|
112545
112642
|
if (entry && (!entry.isDirectory() || entry.isSymbolicLink()))
|
|
112546
112643
|
conflict(path3);
|
|
112547
112644
|
const parent = dirname46(path3);
|
|
@@ -112600,11 +112697,11 @@ ${frontmatter2[2].replace(/^disable-model-invocation:.*\r?\n?/mu, "")}${frontmat
|
|
|
112600
112697
|
const target = join112(commandsRoot, entry.name);
|
|
112601
112698
|
const marker = join112(commandsRoot, `.context-${entry.name}.json`);
|
|
112602
112699
|
for (const file of [target, marker]) {
|
|
112603
|
-
const existing = await
|
|
112700
|
+
const existing = await stat11(file);
|
|
112604
112701
|
if (existing && (!existing.isFile() || existing.isSymbolicLink()))
|
|
112605
112702
|
conflict(file);
|
|
112606
112703
|
}
|
|
112607
|
-
if (await
|
|
112704
|
+
if (await stat11(target)) {
|
|
112608
112705
|
let previous3;
|
|
112609
112706
|
try {
|
|
112610
112707
|
previous3 = JSON.parse(await readFile94(marker, "utf8"));
|
|
@@ -112614,7 +112711,7 @@ ${frontmatter2[2].replace(/^disable-model-invocation:.*\r?\n?/mu, "")}${frontmat
|
|
|
112614
112711
|
const current2 = createHash34("sha256").update(await readFile94(target)).digest("hex");
|
|
112615
112712
|
if (previous3?.owner !== "context-plugin-local" || previous3.digest !== current2)
|
|
112616
112713
|
conflict(target);
|
|
112617
|
-
} else if (await
|
|
112714
|
+
} else if (await stat11(marker))
|
|
112618
112715
|
conflict(marker);
|
|
112619
112716
|
commandPlans.push({ name: entry.name, target, marker, content: content3, hash: createHash34("sha256").update(content3).digest("hex") });
|
|
112620
112717
|
}
|
|
@@ -112623,21 +112720,21 @@ ${frontmatter2[2].replace(/^disable-model-invocation:.*\r?\n?/mu, "")}${frontmat
|
|
|
112623
112720
|
}
|
|
112624
112721
|
const plans = [];
|
|
112625
112722
|
for (const entry of (await readdir29(sourceRoot2, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
112626
|
-
if (!entry.isDirectory() || !await
|
|
112723
|
+
if (!entry.isDirectory() || !await stat11(join112(sourceRoot2, entry.name, "SKILL.md")))
|
|
112627
112724
|
continue;
|
|
112628
112725
|
const source2 = join112(sourceRoot2, entry.name);
|
|
112629
112726
|
const target = join112(skillsRoot, entry.name);
|
|
112630
112727
|
if (commandEntries.has(entry.name)) {
|
|
112631
|
-
if (await
|
|
112728
|
+
if (await stat11(target))
|
|
112632
112729
|
conflict(`${target} (use a fresh host directory to avoid duplicate command/skill entries)`);
|
|
112633
112730
|
continue;
|
|
112634
112731
|
}
|
|
112635
|
-
const existing = await
|
|
112732
|
+
const existing = await stat11(target);
|
|
112636
112733
|
if (existing) {
|
|
112637
112734
|
if (!existing.isDirectory() || existing.isSymbolicLink())
|
|
112638
112735
|
conflict(target);
|
|
112639
112736
|
const markerPath = join112(target, MARKER);
|
|
112640
|
-
if (!(await
|
|
112737
|
+
if (!(await stat11(markerPath))?.isFile())
|
|
112641
112738
|
conflict(target);
|
|
112642
112739
|
let marker;
|
|
112643
112740
|
try {
|