@geoly-ai/social-hub-sdk 0.0.54 → 0.0.56

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/dist/index.d.ts CHANGED
@@ -759,6 +759,79 @@ export type ListReportsParams = {
759
759
  limit?: number;
760
760
  reportType?: "reddit-post-snapshot" | "account-status-snapshot" | "campaign-digest" | "agent-run-summary" | "style-curation-report" | "quota-violation-report" | "content-weekly";
761
761
  };
762
+ /** `user_feishu_identities.resolve_status`(迁移 0144)。 */
763
+ export type FeishuIdentityResolveStatus = "resolved" | "not_found" | "lookup_failed";
764
+ /**
765
+ * 列表过滤值。比 {@link FeishuIdentityResolveStatus} 多一个 `unmapped` ——
766
+ * 那是「映射表里根本没有这一行」,在库里表示为 NULL 而不是某个状态值。
767
+ */
768
+ export type FeishuIdentityListStatusFilter = FeishuIdentityResolveStatus | "unmapped";
769
+ export type FeishuIdentityListItem = {
770
+ userId: string;
771
+ email: string;
772
+ name: string;
773
+ userStatus: string;
774
+ provider: string | null;
775
+ appId: string | null;
776
+ /** 未映射为 null;非 admin 读到的是脱敏值(见 `redacted`)。 */
777
+ openId: string | null;
778
+ unionId: string | null;
779
+ /** `null` = 从未同步过,与 `not_found` 不同。 */
780
+ resolveStatus: FeishuIdentityResolveStatus | null;
781
+ lastAttemptedAt: string | null;
782
+ lastResolvedAt: string | null;
783
+ lastErrorCode: string | null;
784
+ };
785
+ export type FeishuIdentityListResponse = {
786
+ items: FeishuIdentityListItem[];
787
+ /** true 表示 `openId`/`unionId` 已脱敏,不可回写。 */
788
+ redacted: boolean;
789
+ summary: {
790
+ total: number;
791
+ resolved: number;
792
+ notFound: number;
793
+ lookupFailed: number;
794
+ unmapped: number;
795
+ };
796
+ };
797
+ /**
798
+ * 写入条目。三个状态的字段集**互斥**(与契约的 discriminated union 同源):
799
+ * 服务端 `.strict()`,多传字段是 400 而不是被忽略。
800
+ */
801
+ export type FeishuIdentityUpsertEntry = {
802
+ userId: string;
803
+ resolveStatus: "resolved";
804
+ /** `^ou_[A-Za-z0-9]{1,128}$` —— 会被拼进飞书卡片的 `<at id=...>`。 */
805
+ openId: string;
806
+ unionId?: string | null;
807
+ } | {
808
+ userId: string;
809
+ resolveStatus: "not_found";
810
+ } | {
811
+ userId: string;
812
+ resolveStatus: "lookup_failed";
813
+ /** 必填 `^[a-z0-9_.-]{1,64}$`:没有错误码的失败无法排查。 */
814
+ lastErrorCode: string;
815
+ };
816
+ export type FeishuIdentityUpsertResult = {
817
+ userId: string;
818
+ requestedStatus: FeishuIdentityResolveStatus;
819
+ /** 实际落库状态:`lookup_failed` 撞上既有 `resolved` 时这里仍是 `resolved`。 */
820
+ storedStatus: FeishuIdentityResolveStatus;
821
+ mentionable: boolean;
822
+ };
823
+ export type UpsertFeishuIdentitiesResponse = {
824
+ provider: string;
825
+ appId: string;
826
+ results: FeishuIdentityUpsertResult[];
827
+ counts: {
828
+ resolved: number;
829
+ notFound: number;
830
+ lookupFailed: number;
831
+ /** `lookup_failed` 但保住了上次成功解析结果的条数。 */
832
+ preservedResolved: number;
833
+ };
834
+ };
762
835
  /**
763
836
  * 四眼 registry proposal API 的结构化错误(D2):携带 HTTP status + 稳定 code + staleCode。
764
837
  * apply 的 409 + code PROPOSAL_STALE → isStale=true(消费方据此重新提案,保留 staleCode)。
@@ -794,6 +867,113 @@ export declare class SocialHubHttpError extends Error {
794
867
  body?: string;
795
868
  });
796
869
  }
870
+ /**
871
+ * 抓取结果状态。`reauth_required`/`human_required` 属立项 F(授权连接器),
872
+ * 当前 Hub 永不产出,仅预留以免将来扩枚举变成破坏性变更。
873
+ */
874
+ export type ScrapeOutcome = "content" | "login_wall" | "captcha" | "blocked" | "timeout" | "rate_limited" | "upstream_error" | "empty_content" | "unknown" | "unsupported" | "cancelled" | "reauth_required" | "human_required";
875
+ export type ScrapeFailureStage = "queued" | "dns" | "connect" | "proxy" | "navigate" | "render" | "wait" | "extract" | "parse" | "classify" | "transport" | "unknown";
876
+ export type ScrapeClassificationEvidence = {
877
+ ruleId: string;
878
+ ruleVersion: number;
879
+ strength: "strong" | "weak";
880
+ location: string;
881
+ signal: string;
882
+ excerpt: string | null;
883
+ };
884
+ /** 两层事实:provider 原样事实 + Hub 执行观测。未知一律 null。 */
885
+ export type ScrapeDiagnostics = {
886
+ provider: {
887
+ provider: "firecrawl" | "reddit_direct";
888
+ captureMethod: "firecrawl"
889
+ /** Hub 既有的 Reddit 直连(curl_cffi + TLS 指纹)通道。 */
890
+ | "direct_http" | "official_api" | "authorized_browser";
891
+ providerStatusCode: number | null;
892
+ providerErrorCode: string | null;
893
+ providerErrorType: string | null;
894
+ providerRequestId: string | null;
895
+ providerScrapeId: string | null;
896
+ targetStatusCode: number | null;
897
+ providerFinalUrl: string | null;
898
+ /** `[]` = 确认无重定向;`null` = provider 未提供该事实。 */
899
+ redirectChain: string[] | null;
900
+ };
901
+ execution: {
902
+ attemptCount: number;
903
+ attemptHistory: Array<{
904
+ attempt: number;
905
+ outcome: ScrapeOutcome;
906
+ providerStatusCode: number | null;
907
+ failureStage: ScrapeFailureStage | null;
908
+ durationMs: number;
909
+ waitedMs: number;
910
+ }>;
911
+ failureStage: ScrapeFailureStage | null;
912
+ retryAfterMs: number | null;
913
+ timings: {
914
+ totalMs: number;
915
+ providerMs: number;
916
+ backoffMs: number;
917
+ classifyMs: number;
918
+ };
919
+ errorSummary: string | null;
920
+ };
921
+ };
922
+ type ScrapeResponseCommon = {
923
+ url: string;
924
+ finalUrl: string;
925
+ /** 目标站点状态码(语义未变)。 */
926
+ statusCode: number | null;
927
+ metadata?: Record<string, unknown>;
928
+ requestId: string | null;
929
+ diagnostics: ScrapeDiagnostics;
930
+ classification: {
931
+ outcome: ScrapeOutcome;
932
+ classifierVersion: string;
933
+ classificationEvidence: ScrapeClassificationEvidence[];
934
+ };
935
+ provider: "firecrawl" | "reddit_direct";
936
+ providerRequestId: string | null;
937
+ providerStatusCode: number | null;
938
+ targetStatusCode: number | null;
939
+ attempts: number;
940
+ retryAfterMs: number | null;
941
+ failureStage: ScrapeFailureStage | null;
942
+ classifierVersion: string;
943
+ classificationEvidence: ScrapeClassificationEvidence[];
944
+ warnings: string[];
945
+ };
946
+ /**
947
+ * `ok` 是判别字段。被判定为「确定不是目标正文」的 outcome(login_wall / captcha /
948
+ * blocked / rate_limited / upstream_error / empty_content …)运行时不会带正文字段,
949
+ * 只有 `suppressedContent` 记录被抑制的格式与字符数;`outcome:"unknown"`(证据不足)
950
+ * 仍可能带正文,但 `warnings` 里会标明它未经确认——**不要**当成已验证的正文使用。
951
+ */
952
+ export type ScrapeUrlResponse = (ScrapeResponseCommon & {
953
+ ok: true;
954
+ outcome: "content";
955
+ markdown?: string;
956
+ html?: string;
957
+ rawHtml?: string;
958
+ links?: string[];
959
+ json?: unknown;
960
+ }) | (ScrapeResponseCommon & {
961
+ ok: false;
962
+ outcome: Exclude<ScrapeOutcome, "content">;
963
+ /**
964
+ * 被抑制的正文格式与字符数(`outcome` 属于「确定不是正文」那一类时出现)。
965
+ * `outcome:"unknown"` 不抑制正文,正文字段仍可能存在——**但它未经确认**。
966
+ */
967
+ suppressedContent?: Array<{
968
+ format: string;
969
+ chars: number;
970
+ }>;
971
+ markdown?: string;
972
+ html?: string;
973
+ rawHtml?: string;
974
+ links?: string[];
975
+ json?: unknown;
976
+ });
797
977
  /**
798
978
  * POST /content-review 的 422 稳定错误码:调用方显式声称 `authorRelationHint:"post_author"`,
799
979
  * 但 Hub 侧帖子快照证明发布账号不是该帖作者。
@@ -922,7 +1102,18 @@ export declare class SocialHubClient {
922
1102
  queued: number;
923
1103
  warning?: string;
924
1104
  }>;
925
- updateRedditPostSnapshot(teamId: string, snapshotId: string, body: Record<string, unknown>): Promise<{
1105
+ /**
1106
+ * 更新单条帖子快照。
1107
+ *
1108
+ * ⚠️ 把 `opsStatus` 改成 `deleted` / `banned` 时,**必须**同时带上必选项
1109
+ * `repostRequired`(布尔:是否需补发),否则服务端 400 —— 这是刻意的 fail-closed,
1110
+ * 「需补发」不会默认成 false。「需补发1 / 需补发2」的轮次由服务端按**原帖**的
1111
+ * `repostType` 派生(首发→1、补发一次→2、补发二次→不问也不写),客户端传数字无效。
1112
+ * 原帖 `repostType` 已是 `second` 时不需要带,带了也会被忽略。
1113
+ */
1114
+ updateRedditPostSnapshot(teamId: string, snapshotId: string, body: Record<string, unknown> & {
1115
+ repostRequired?: boolean;
1116
+ }): Promise<{
926
1117
  ok: boolean;
927
1118
  }>;
928
1119
  deleteRedditPostSnapshot(teamId: string, snapshotId: string): Promise<{
@@ -2336,6 +2527,37 @@ export declare class SocialHubClient {
2336
2527
  listSystemUsers(): Promise<{
2337
2528
  items: unknown[];
2338
2529
  }>;
2530
+ /**
2531
+ * `GET /v1/system/feishu-identities` —— Hub 用户 ↔ 飞书 open_id 的映射现状。
2532
+ *
2533
+ * 返回**全体用户**(users LEFT JOIN 映射表),所以 `resolveStatus === null` 表示
2534
+ * 「从未同步过」,与 `not_found`(查过、通讯录里没这人)是两回事。
2535
+ *
2536
+ * ⚠️ 非「不受限交互式 admin 登录」读到的 `openId`/`unionId` 是脱敏的 `ou_***`。
2537
+ * 判据是响应里的 `redacted`,不要拿 `openId` 的字符串形态去猜 —— 脱敏值也以
2538
+ * `ou_` 开头,直接透传会写出一个永远 @ 不到人的映射。
2539
+ */
2540
+ listFeishuIdentities(params?: {
2541
+ provider?: string;
2542
+ appId?: string;
2543
+ status?: FeishuIdentityListStatusFilter;
2544
+ includeInactiveUsers?: boolean;
2545
+ }): Promise<FeishuIdentityListResponse>;
2546
+ /**
2547
+ * `PUT /v1/system/feishu-identities` —— 批量落库(幂等,同一批重放结果相同)。
2548
+ *
2549
+ * 写入面只认不受限的交互式 admin 登录:**API key 一律 403**,所以这个方法在
2550
+ * 服务端到服务端的集成里不可用,它是给交互式运维工具(CLI/后台)用的。
2551
+ *
2552
+ * 语义上最贵的一条:`not_found` 会清空既有 `openId`(离职语义)。查询失败必须用
2553
+ * `lookup_failed`(服务端会保住上次成功解析的值),把「没问出来」写成 `not_found`
2554
+ * 等于静默注销一个本来能收到告警的人。
2555
+ */
2556
+ upsertFeishuIdentities(body: {
2557
+ provider?: "feishu";
2558
+ appId: string;
2559
+ entries: FeishuIdentityUpsertEntry[];
2560
+ }): Promise<UpsertFeishuIdentitiesResponse>;
2339
2561
  createSystemUser(body: {
2340
2562
  email: string;
2341
2563
  name: string;
@@ -2549,28 +2771,33 @@ export declare class SocialHubClient {
2549
2771
  * 通用网页抓取。Reddit .json(mode "reddit-json" 或 .json URL)走 API 内直连
2550
2772
  * curl_cffi+住宅代理返回结构化 JSON;HTML/普通网页经自托管 Firecrawl 返回
2551
2773
  * markdown/html/rawHtml/links。
2774
+ *
2775
+ * **务必先看 `ok`/`outcome`,不要只看 HTTP 状态**(立项 A):Firecrawl 返回 200
2776
+ * 但正文是登录页/验证码页时,Hub 会回 HTTP 200 + `ok:false` + `outcome`
2777
+ * (`login_wall`/`captcha`/`empty_content` 等)并**不回传正文字段**,同时给出
2778
+ * `classificationEvidence` 说明凭什么这么判。`outcome:"unknown"`(证据不足)仍会
2779
+ * 带正文,但它**未经确认**,`warnings` 里有显式提示——不要当成已验证正文使用。
2780
+ *
2781
+ * 技术层失败(provider 非 2xx / Hub 等待超时 / 网络错误)抛 `SocialHubHttpError`
2782
+ * (429/502/504),`code` 为 `SCRAPE_RATE_LIMITED`/`SCRAPE_TIMEOUT`/
2783
+ * `SCRAPE_UPSTREAM_ERROR`,`details` 里带 provider typed code/error/request id、
2784
+ * 失败阶段与尝试次数。
2552
2785
  */
2553
2786
  scrapeUrl(body: {
2554
2787
  url: string;
2555
2788
  formats?: Array<"markdown" | "html" | "rawHtml" | "links">;
2556
2789
  mode?: "raw" | "reddit-json";
2557
2790
  timeoutMs?: number;
2558
- }): Promise<{
2559
- url: string;
2560
- finalUrl: string;
2561
- statusCode: number | null;
2562
- markdown?: string;
2563
- html?: string;
2564
- rawHtml?: string;
2565
- links?: string[];
2566
- json?: unknown;
2567
- metadata?: Record<string, unknown>;
2568
- }>;
2791
+ }): Promise<ScrapeUrlResponse>;
2569
2792
  getScrapeStatus(): Promise<{
2570
2793
  configured: boolean;
2571
2794
  firecrawl?: boolean;
2572
2795
  redditDirect?: boolean;
2573
2796
  inFlight: number;
2797
+ /** 立项 A 新增:当前 2xx 页面分类器版本。 */
2798
+ classifierVersion?: string;
2799
+ /** 立项 A 新增:生效的尝试次数上限(含首次)。 */
2800
+ maxAttempts?: number;
2574
2801
  }>;
2575
2802
  /** GET subjects/:hotPostId — active spec 下该 hot_post 的 current proposed 分类(proposed|missing)。 */
2576
2803
  getContentClassificationSubject(hotPostId: string, params?: {
@@ -3370,4 +3597,5 @@ export type NotificationPullResponseDto = {
3370
3597
  * 与 contracts `compareNotificationSeq` 同口径;SDK 侧复刻是因为不能引 contracts。
3371
3598
  */
3372
3599
  export declare function compareNotificationSeqStrings(left: string, right: string): number;
3600
+ export {};
3373
3601
  //# sourceMappingURL=index.d.ts.map