@c4a/context-cli 0.7.29 → 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 +89 -10
- package/indexers/contracts/profile-contract.json +245 -245
- package/indexers/release-manifest.json +1 -1
- package/package.json +12 -12
- package/plugins/VERSION +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/commands/context-inspect-search.md +59 -21
- package/plugins/claude/skills/context-inspect-search/SKILL.md +59 -21
- package/plugins/codex/.codex-plugin/plugin.json +2 -2
- package/plugins/codex/skills/context-inspect-search/SKILL.md +59 -21
- package/plugins/cursor/.cursor-plugin/plugin.json +1 -1
- package/plugins/cursor/commands/c4a-context-inspect-search.md +59 -21
- package/plugins/cursor/skills/context-inspect-search/SKILL.md +59 -21
- package/plugins/skills/context-inspect-search/SKILL.md +59 -21
- package/providers/context/manifest.json +11 -11
- 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/source-capture-detailed.md +6 -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
|
@@ -98654,6 +98654,72 @@ var init_larkMarkdownImages = __esm(() => {
|
|
|
98654
98654
|
init_markdownLinks();
|
|
98655
98655
|
});
|
|
98656
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
|
+
|
|
98657
98723
|
// src/lib/feishu.ts
|
|
98658
98724
|
import { spawn as spawn4 } from "node:child_process";
|
|
98659
98725
|
async function detectDocsFetchCapabilities(runner2) {
|
|
@@ -98670,14 +98736,19 @@ ${result.stderr}`;
|
|
|
98670
98736
|
supportsDocFormat: result.exitCode === 0 && /\s--doc-format\b/u.test(help)
|
|
98671
98737
|
};
|
|
98672
98738
|
}
|
|
98673
|
-
function resolveDocsFetchPlan(requested, runner2) {
|
|
98739
|
+
function resolveDocsFetchPlan(requested, runner2, cacheKey = runner2, identity2 = "user") {
|
|
98674
98740
|
if (requested === "v1" || requested === "v2") {
|
|
98675
98741
|
return Promise.resolve({ apiVersion: requested, docFormat: "xml" });
|
|
98676
98742
|
}
|
|
98677
|
-
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);
|
|
98678
98749
|
if (capabilitiesPromise === undefined) {
|
|
98679
98750
|
capabilitiesPromise = detectDocsFetchCapabilities(runner2);
|
|
98680
|
-
|
|
98751
|
+
cached.set(identity2, capabilitiesPromise);
|
|
98681
98752
|
}
|
|
98682
98753
|
return capabilitiesPromise.then((capabilities) => {
|
|
98683
98754
|
if (!capabilities.supportsDocFormat) {
|
|
@@ -98974,20 +99045,25 @@ function hasEmptySubPageList(projection) {
|
|
|
98974
99045
|
return projection.fidelity.issues.some((issue) => issue.code === LARK_EMPTY_SUB_PAGE_LIST_CODE);
|
|
98975
99046
|
}
|
|
98976
99047
|
async function fetchFeishuDocSnapshot(input, runner2 = defaultRunner) {
|
|
98977
|
-
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);
|
|
98978
99053
|
const identityFetch = input.prefetched ? {
|
|
98979
99054
|
fetched: await fetchDocsResponse(input, docsFetchPlan, runner2, input.prefetched.identity),
|
|
98980
99055
|
identity: input.prefetched.identity,
|
|
98981
99056
|
fallback: false
|
|
98982
|
-
} : await fetchWithIdentity(input, docsFetchPlan, runner2);
|
|
99057
|
+
} : await fetchWithIdentity({ ...input, identity: preference }, docsFetchPlan, runner2);
|
|
98983
99058
|
let fetched = identityFetch.fetched;
|
|
98984
|
-
|
|
99059
|
+
let accessIdentity = input.prefetched?.identity ?? session.identity;
|
|
98985
99060
|
let projection;
|
|
98986
99061
|
for (let attempt = 0;attempt < MAX_STRUCTURAL_FETCH_ATTEMPTS && fetched.contentFormat === "xml"; attempt++) {
|
|
98987
99062
|
projection = projectLarkDocxXml({ xml: fetched.body, sourceUrl: input.url });
|
|
98988
99063
|
if (input.prefetched || !hasEmptySubPageList(projection) || attempt === MAX_STRUCTURAL_FETCH_ATTEMPTS - 1)
|
|
98989
99064
|
break;
|
|
98990
99065
|
fetched = await fetchDocsResponse(input, docsFetchPlan, runner2, accessIdentity);
|
|
99066
|
+
accessIdentity = session.identity;
|
|
98991
99067
|
}
|
|
98992
99068
|
let body2 = fetched.body;
|
|
98993
99069
|
let title2 = fetched.title;
|
|
@@ -99073,7 +99149,7 @@ ${body2}`,
|
|
|
99073
99149
|
fidelity,
|
|
99074
99150
|
resourceMaterialization,
|
|
99075
99151
|
accessIdentity,
|
|
99076
|
-
identityFallback: identityFetch.fallback
|
|
99152
|
+
identityFallback: identityFetch.fallback || session.usedFallback
|
|
99077
99153
|
};
|
|
99078
99154
|
}
|
|
99079
99155
|
return {
|
|
@@ -99084,7 +99160,7 @@ ${body2}`,
|
|
|
99084
99160
|
fidelity,
|
|
99085
99161
|
resourceMaterialization,
|
|
99086
99162
|
accessIdentity,
|
|
99087
|
-
identityFallback: identityFetch.fallback
|
|
99163
|
+
identityFallback: identityFetch.fallback || session.usedFallback
|
|
99088
99164
|
};
|
|
99089
99165
|
}
|
|
99090
99166
|
var LARK_BIN = "lark-cli", MAX_FETCH_PAGES = 50, MAX_STRUCTURAL_FETCH_ATTEMPTS = 2, LarkCliNotInstalledError, LarkCliError, defaultRunner = (args, options) => new Promise((resolve8, reject) => {
|
|
@@ -99116,6 +99192,7 @@ var init_feishu = __esm(() => {
|
|
|
99116
99192
|
init_larkResourceMaterialization();
|
|
99117
99193
|
init_larkCaptureReport();
|
|
99118
99194
|
init_larkMarkdownImages();
|
|
99195
|
+
init_larkReadIdentity();
|
|
99119
99196
|
LarkCliNotInstalledError = class LarkCliNotInstalledError extends Error {
|
|
99120
99197
|
constructor() {
|
|
99121
99198
|
super(`${LARK_BIN} not installed — see https://github.com/larksuite/cli for install instructions`);
|
|
@@ -99276,7 +99353,7 @@ function larkErrorRecovery(error, sourceName) {
|
|
|
99276
99353
|
if (/auth|login|permission|forbidden|unauthori[sz]ed|scope/iu.test(message)) {
|
|
99277
99354
|
return {
|
|
99278
99355
|
reasonCode: "external.authorization-required",
|
|
99279
|
-
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"
|
|
99280
99357
|
};
|
|
99281
99358
|
}
|
|
99282
99359
|
return {
|
|
@@ -99421,6 +99498,8 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
99421
99498
|
...input.prefetched === undefined ? {} : { prefetched: input.prefetched }
|
|
99422
99499
|
}, input.larkRunner);
|
|
99423
99500
|
} catch (error) {
|
|
99501
|
+
if (error instanceof ContextError)
|
|
99502
|
+
throw error;
|
|
99424
99503
|
const failure2 = normalizeLarkError(error, resolved.sourceName);
|
|
99425
99504
|
if (failure2.detail?.reason_code === "external.authorization-required" || failure2.detail?.reason_code === "external.tool-failed" && error instanceof LarkCliError && error.exitCode !== 0) {
|
|
99426
99505
|
throw new ContextError(failure2.code, failure2.message, { ...failure2.detail, reason_code: DOCUMENT_ACQUISITION_UNAVAILABLE, acquisition_cause: failure2.detail?.reason_code });
|
|
@@ -99594,7 +99673,7 @@ async function runCaptureLarkPhaseUnlocked(input) {
|
|
|
99594
99673
|
resource_materialization: fetched.resourceMaterialization,
|
|
99595
99674
|
diagnostics: [
|
|
99596
99675
|
...recovery === undefined ? [] : [recovery.diagnostic],
|
|
99597
|
-
...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`] : [],
|
|
99598
99677
|
...fetched.fidelity.issues.map((issue) => `${issue.severity}: ${issue.code}: ${issue.block_type} × ${issue.count}: ${issue.reason}`),
|
|
99599
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}`)
|
|
99600
99679
|
],
|