@zleap-ai/sag-cli 0.5.0 → 0.6.0

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/cli.js CHANGED
@@ -1,13 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import os5 from "os";
4
+ import { mkdtemp, rm as rm3 } from "fs/promises";
5
+ import os5, { tmpdir } from "os";
6
+ import path15 from "path";
5
7
  import { checkbox, confirm, input, select } from "@inquirer/prompts";
6
8
 
7
9
  // package.json
8
10
  var package_default = {
9
11
  name: "@zleap-ai/sag-cli",
10
- version: "0.5.0",
12
+ version: "0.6.0",
11
13
  description: "Command-line client and diagnostics for SAG knowledge bases",
12
14
  type: "module",
13
15
  bin: {
@@ -22,7 +24,7 @@ var package_default = {
22
24
  "skills/sag-mcp"
23
25
  ],
24
26
  scripts: {
25
- build: "tsup",
27
+ build: "tsup && node scripts/sync-skill-runtime.mjs",
26
28
  dev: "tsx src/cli.ts",
27
29
  test: "vitest run",
28
30
  "test:watch": "vitest",
@@ -94,6 +96,32 @@ import path from "path";
94
96
  import { z } from "zod";
95
97
 
96
98
  // src/core/errors.ts
99
+ function toSkillErrorCode(code) {
100
+ switch (code) {
101
+ case "AUTH_REQUIRED":
102
+ case "PERMISSION_DENIED":
103
+ case "NETWORK_UNREACHABLE":
104
+ case "INVALID_ARGUMENT":
105
+ case "MCP_FAILED":
106
+ case "HOST_NOT_FOUND":
107
+ case "HOST_CONFIG_FAILED":
108
+ case "CONFIG_CONFLICT":
109
+ case "DEPENDENCY_MISSING":
110
+ case "INVALID_RESPONSE":
111
+ case "INTERNAL_ERROR":
112
+ return code;
113
+ case "EXTERNAL_DEPENDENCY_MISSING":
114
+ return "DEPENDENCY_MISSING";
115
+ case "SERVICE_NOT_READY":
116
+ return "PLATFORM_UNAVAILABLE";
117
+ case "RESOURCE_NOT_FOUND":
118
+ return "DOCUMENT_NOT_FOUND";
119
+ case "DOCUMENT_NOT_READY":
120
+ return "PROCESSING_FAILED";
121
+ default:
122
+ return "INTERNAL_ERROR";
123
+ }
124
+ }
97
125
  var CliError = class extends Error {
98
126
  code;
99
127
  exitCode;
@@ -145,6 +173,14 @@ function toCliError(error) {
145
173
 
146
174
  // src/agents/adapter.ts
147
175
  import { createHash } from "crypto";
176
+ async function isAgentInstalled(adapter) {
177
+ try {
178
+ await adapter.detect();
179
+ return true;
180
+ } catch {
181
+ return false;
182
+ }
183
+ }
148
184
  function canonicalize(value) {
149
185
  if (Array.isArray(value)) {
150
186
  return value.map(canonicalize);
@@ -476,8 +512,8 @@ function parseTomlTablePath(line) {
476
512
  function findHeaderTables(config, name) {
477
513
  const target = ["mcp_servers", name, "http_headers"];
478
514
  return config.split(/\r?\n/u).flatMap((line, index) => {
479
- const path11 = parseTomlTablePath(line);
480
- return path11 && path11.length === target.length && path11.every((part, partIndex) => part === target[partIndex]) ? [index] : [];
515
+ const path16 = parseTomlTablePath(line);
516
+ return path16 && path16.length === target.length && path16.every((part, partIndex) => part === target[partIndex]) ? [index] : [];
481
517
  });
482
518
  }
483
519
  function readHttpHeaders(config, name) {
@@ -1443,6 +1479,8 @@ function defaultManagedConnectionsPath(options = {}) {
1443
1479
  var defaultEnterpriseMcpConnectionsPath = (options = {}) => path6.join(path6.dirname(defaultConfigPath(options)), "mcp-connections.yaml");
1444
1480
  var defaultEnterpriseMcpManagedStatePath = (options = {}) => path6.join(path6.dirname(defaultConfigPath(options)), "mcp-managed-state.yaml");
1445
1481
  var defaultEnterpriseMcpCredentialsPath = (options = {}) => path6.join(path6.dirname(defaultConfigPath(options)), "mcp-credentials.json");
1482
+ var defaultSkillStatePath = (options = {}) => path6.join(options.homeDirectory ?? os4.homedir(), ".sag", "skill", "state.json");
1483
+ var defaultSkillAccountConfigPath = (options = {}) => path6.join(options.homeDirectory ?? os4.homedir(), ".sag", "config.json");
1446
1484
 
1447
1485
  // src/credentials/file.ts
1448
1486
  import { randomUUID as randomUUID2 } from "crypto";
@@ -1594,11 +1632,13 @@ var MemoryCredentialStore = class {
1594
1632
  }
1595
1633
  };
1596
1634
 
1597
- // src/credentials/keychain.ts
1598
- var SERVICE_NAME = "@zleap-ai/sag-cli";
1635
+ // src/credentials/namespaces.ts
1636
+ var PERSONAL_SERVICE_NAME = "@zleap-ai/sag-cli";
1599
1637
  var ENTERPRISE_MCP_SERVICE_NAME = "@zleap-ai/sag-cli/enterprise-mcp";
1638
+
1639
+ // src/credentials/keychain.ts
1600
1640
  var KeychainCredentialStore = class {
1601
- constructor(entryFactory, serviceName = SERVICE_NAME) {
1641
+ constructor(entryFactory, serviceName = PERSONAL_SERVICE_NAME) {
1602
1642
  this.entryFactory = entryFactory;
1603
1643
  this.serviceName = serviceName;
1604
1644
  }
@@ -2681,46 +2721,562 @@ var NodeProcessRunner = class {
2681
2721
  }
2682
2722
  };
2683
2723
 
2724
+ // src/skill/state-store.ts
2725
+ import { randomUUID as randomUUID4 } from "crypto";
2726
+ import { mkdir as mkdir7, open as open3, readFile as readFile9, rename as rename8, unlink as unlink8 } from "fs/promises";
2727
+ import path10 from "path";
2728
+ import { lock as lock6 } from "proper-lockfile";
2729
+ import { z as z10 } from "zod";
2730
+ var agentSchema = z10.enum(["codex", "claude-code", "workbuddy"]);
2731
+ var digestSchema = z10.string().regex(/^sha256:[a-f0-9]{64}$/u);
2732
+ var managedSkillSchema = z10.strictObject({
2733
+ agent: agentSchema,
2734
+ backupPath: z10.string().min(1).optional(),
2735
+ fingerprint: digestSchema,
2736
+ target: z10.string().min(1),
2737
+ version: z10.string().min(1)
2738
+ });
2739
+ var skillStateSchema = z10.strictObject({
2740
+ schema: z10.literal(2),
2741
+ managedSkills: z10.record(z10.string(), managedSkillSchema)
2742
+ });
2743
+ function emptyState5() {
2744
+ return { schema: 2, managedSkills: {} };
2745
+ }
2746
+ function invalidState5(filePath) {
2747
+ return new CliError("CONFIG_CONFLICT", "Skill installation state is invalid", {
2748
+ exitCode: exitCodes.configConflict,
2749
+ hint: `Remove the unpublished legacy state at ${filePath}, then reinstall the Skill.`
2750
+ });
2751
+ }
2752
+ function managedSkillKey(agent, target) {
2753
+ return `${agent}:${target}`;
2754
+ }
2755
+ var SkillStateStore = class {
2756
+ constructor(filePath) {
2757
+ this.filePath = filePath;
2758
+ }
2759
+ filePath;
2760
+ async #withLock(operation) {
2761
+ await mkdir7(path10.dirname(this.filePath), { recursive: true });
2762
+ let release;
2763
+ try {
2764
+ release = await lock6(this.filePath, {
2765
+ realpath: false,
2766
+ stale: 1e4,
2767
+ update: 2e3,
2768
+ retries: { retries: 100, factor: 1, minTimeout: 10, maxTimeout: 10 }
2769
+ });
2770
+ } catch {
2771
+ throw new CliError("CONFIG_CONFLICT", "Skill installation state is busy", {
2772
+ exitCode: exitCodes.configConflict
2773
+ });
2774
+ }
2775
+ try {
2776
+ return await operation();
2777
+ } finally {
2778
+ await release();
2779
+ }
2780
+ }
2781
+ async load() {
2782
+ try {
2783
+ return skillStateSchema.parse(JSON.parse(await readFile9(this.filePath, "utf8")));
2784
+ } catch (error) {
2785
+ if (error.code === "ENOENT") return emptyState5();
2786
+ throw invalidState5(this.filePath);
2787
+ }
2788
+ }
2789
+ async #saveUnlocked(state) {
2790
+ const validated = skillStateSchema.parse(state);
2791
+ const directory = path10.dirname(this.filePath);
2792
+ await mkdir7(directory, { recursive: true });
2793
+ const temporaryPath = path10.join(
2794
+ directory,
2795
+ `.${path10.basename(this.filePath)}.${randomUUID4()}.tmp`
2796
+ );
2797
+ let temporaryCreated = false;
2798
+ try {
2799
+ const handle = await open3(temporaryPath, "wx", 384);
2800
+ temporaryCreated = true;
2801
+ try {
2802
+ await handle.chmod(384);
2803
+ await handle.writeFile(JSON.stringify(validated), "utf8");
2804
+ } finally {
2805
+ await handle.close();
2806
+ }
2807
+ await rename8(temporaryPath, this.filePath);
2808
+ } catch (error) {
2809
+ if (temporaryCreated) await unlink8(temporaryPath).catch(() => void 0);
2810
+ throw error;
2811
+ }
2812
+ }
2813
+ async getManagedSkill(agent, target) {
2814
+ return (await this.load()).managedSkills[managedSkillKey(agent, target)] ?? null;
2815
+ }
2816
+ async saveManagedSkill(managedSkill) {
2817
+ const validated = managedSkillSchema.parse(managedSkill);
2818
+ await this.#withLock(async () => {
2819
+ const state = await this.load();
2820
+ state.managedSkills[managedSkillKey(validated.agent, validated.target)] = validated;
2821
+ await this.#saveUnlocked(state);
2822
+ });
2823
+ }
2824
+ async deleteManagedSkill(agent, target) {
2825
+ await this.#withLock(async () => {
2826
+ const state = await this.load();
2827
+ delete state.managedSkills[managedSkillKey(agent, target)];
2828
+ await this.#saveUnlocked(state);
2829
+ });
2830
+ }
2831
+ };
2832
+
2833
+ // src/skill/account-auth-client.ts
2834
+ import { z as z12 } from "zod";
2835
+
2836
+ // src/skill/account-config.ts
2837
+ import { createHash as createHash2, randomUUID as randomUUID5 } from "crypto";
2838
+ import { mkdir as mkdir8, open as open4, readFile as readFile10, rename as rename9, unlink as unlink9 } from "fs/promises";
2839
+ import path11 from "path";
2840
+ import { lock as lock7 } from "proper-lockfile";
2841
+ import { z as z11 } from "zod";
2842
+ var recentKnowledgeBaseSchema = z11.strictObject({
2843
+ id: z11.string().min(1),
2844
+ name: z11.string().min(1)
2845
+ });
2846
+ var skillAccountSchema = z11.strictObject({
2847
+ accessToken: z11.string().min(1),
2848
+ refreshToken: z11.string().min(1),
2849
+ accessExpiresAt: z11.string().datetime({ offset: true }),
2850
+ refreshExpiresAt: z11.string().datetime({ offset: true }),
2851
+ user: z11.strictObject({ id: z11.string().min(1), name: z11.string().min(1) }),
2852
+ recentKnowledgeBase: recentKnowledgeBaseSchema.optional()
2853
+ });
2854
+ var skillAccountConfigSchema = z11.object({
2855
+ version: z11.literal(1),
2856
+ currentOrigin: z11.string().optional(),
2857
+ accounts: z11.record(z11.string(), skillAccountSchema)
2858
+ }).passthrough();
2859
+ function configurationConflict(message, filePath) {
2860
+ return new CliError("CONFIG_CONFLICT", message, {
2861
+ exitCode: exitCodes.configConflict,
2862
+ hint: filePath ? `Review or restore ${filePath}.` : "Provide a pure SAG Origin."
2863
+ });
2864
+ }
2865
+ function normalizeSkillOrigin(input2) {
2866
+ try {
2867
+ const parsed = new URL(input2);
2868
+ if (!["http:", "https:"].includes(parsed.protocol) || !parsed.hostname || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
2869
+ throw new Error("not a pure HTTP Origin");
2870
+ }
2871
+ return parsed.origin;
2872
+ } catch {
2873
+ throw configurationConflict("SAG address must be a pure HTTP or HTTPS Origin");
2874
+ }
2875
+ }
2876
+ var SkillAccountConfigStore = class {
2877
+ constructor(filePath) {
2878
+ this.filePath = filePath;
2879
+ }
2880
+ filePath;
2881
+ async load() {
2882
+ let raw;
2883
+ try {
2884
+ raw = await readFile10(this.filePath, "utf8");
2885
+ } catch (error) {
2886
+ if (error.code === "ENOENT") {
2887
+ return { version: 1, accounts: {} };
2888
+ }
2889
+ throw error;
2890
+ }
2891
+ try {
2892
+ return skillAccountConfigSchema.parse(JSON.parse(raw));
2893
+ } catch {
2894
+ throw configurationConflict(
2895
+ "SAG account configuration is invalid",
2896
+ this.filePath
2897
+ );
2898
+ }
2899
+ }
2900
+ async saveAccount(origin, account) {
2901
+ const normalizedOrigin = normalizeSkillOrigin(origin);
2902
+ let validated;
2903
+ try {
2904
+ validated = skillAccountSchema.parse(account);
2905
+ } catch {
2906
+ throw configurationConflict("SAG account is invalid", this.filePath);
2907
+ }
2908
+ await this.#withLock(async () => {
2909
+ const config = await this.load();
2910
+ config.accounts[normalizedOrigin] = validated;
2911
+ config.currentOrigin = normalizedOrigin;
2912
+ await this.#saveUnlocked(config);
2913
+ });
2914
+ }
2915
+ async removeAccount(origin) {
2916
+ const normalizedOrigin = normalizeSkillOrigin(origin);
2917
+ await this.#withLock(async () => {
2918
+ const config = await this.load();
2919
+ delete config.accounts[normalizedOrigin];
2920
+ if (config.currentOrigin === normalizedOrigin) {
2921
+ config.currentOrigin = Object.keys(config.accounts).sort()[0];
2922
+ }
2923
+ await this.#saveUnlocked(config);
2924
+ });
2925
+ }
2926
+ async setCurrentOrigin(origin) {
2927
+ const normalizedOrigin = normalizeSkillOrigin(origin);
2928
+ await this.#withLock(async () => {
2929
+ const config = await this.load();
2930
+ config.currentOrigin = normalizedOrigin;
2931
+ await this.#saveUnlocked(config);
2932
+ });
2933
+ }
2934
+ async getAccount(origin) {
2935
+ return (await this.load()).accounts[normalizeSkillOrigin(origin)] ?? null;
2936
+ }
2937
+ async saveRecentKnowledgeBase(origin, knowledgeBase) {
2938
+ const normalizedOrigin = normalizeSkillOrigin(origin);
2939
+ const validated = recentKnowledgeBaseSchema.parse(knowledgeBase);
2940
+ await this.#withLock(async () => {
2941
+ const config = await this.load();
2942
+ const account = config.accounts[normalizedOrigin];
2943
+ if (!account) {
2944
+ throw configurationConflict(
2945
+ "SAG account authorization is required",
2946
+ this.filePath
2947
+ );
2948
+ }
2949
+ config.accounts[normalizedOrigin] = {
2950
+ ...account,
2951
+ recentKnowledgeBase: validated
2952
+ };
2953
+ config.currentOrigin = normalizedOrigin;
2954
+ await this.#saveUnlocked(config);
2955
+ });
2956
+ }
2957
+ async withRefreshLock(origin, operation) {
2958
+ const normalizedOrigin = normalizeSkillOrigin(origin);
2959
+ const digest2 = createHash2("sha256").update(normalizedOrigin).digest("hex");
2960
+ await mkdir8(path11.dirname(this.filePath), { recursive: true });
2961
+ let release;
2962
+ try {
2963
+ release = await lock7(`${this.filePath}.refresh-${digest2}`, {
2964
+ realpath: false,
2965
+ stale: 1e4,
2966
+ update: 2e3,
2967
+ retries: { retries: 300, factor: 1, minTimeout: 100, maxTimeout: 100 }
2968
+ });
2969
+ } catch {
2970
+ throw new CliError("CONFIG_CONFLICT", "SAG account refresh is busy", {
2971
+ exitCode: exitCodes.configConflict,
2972
+ hint: "Wait for the other SAG Skill refresh to finish and retry."
2973
+ });
2974
+ }
2975
+ try {
2976
+ return await operation();
2977
+ } finally {
2978
+ await release();
2979
+ }
2980
+ }
2981
+ async #withLock(operation) {
2982
+ await mkdir8(path11.dirname(this.filePath), { recursive: true });
2983
+ let release;
2984
+ try {
2985
+ release = await lock7(this.filePath, {
2986
+ realpath: false,
2987
+ stale: 1e4,
2988
+ update: 2e3,
2989
+ retries: { retries: 100, factor: 1, minTimeout: 10, maxTimeout: 10 }
2990
+ });
2991
+ } catch {
2992
+ throw new CliError("CONFIG_CONFLICT", "SAG account configuration is busy", {
2993
+ exitCode: exitCodes.configConflict,
2994
+ hint: "Wait for the other SAG Skill operation to finish and retry."
2995
+ });
2996
+ }
2997
+ try {
2998
+ return await operation();
2999
+ } finally {
3000
+ await release();
3001
+ }
3002
+ }
3003
+ async #saveUnlocked(config) {
3004
+ let validated;
3005
+ try {
3006
+ validated = skillAccountConfigSchema.parse(config);
3007
+ } catch {
3008
+ throw configurationConflict(
3009
+ "SAG account configuration is invalid",
3010
+ this.filePath
3011
+ );
3012
+ }
3013
+ const directory = path11.dirname(this.filePath);
3014
+ await mkdir8(directory, { recursive: true });
3015
+ const temporaryPath = path11.join(
3016
+ directory,
3017
+ `.${path11.basename(this.filePath)}.${randomUUID5()}.tmp`
3018
+ );
3019
+ let temporaryCreated = false;
3020
+ try {
3021
+ const handle = await open4(temporaryPath, "wx", 384);
3022
+ temporaryCreated = true;
3023
+ try {
3024
+ await handle.chmod(384).catch(() => void 0);
3025
+ await handle.writeFile(JSON.stringify(validated), "utf8");
3026
+ } finally {
3027
+ await handle.close();
3028
+ }
3029
+ await rename9(temporaryPath, this.filePath);
3030
+ } catch (error) {
3031
+ if (temporaryCreated) await unlink9(temporaryPath).catch(() => void 0);
3032
+ throw error;
3033
+ }
3034
+ }
3035
+ };
3036
+
3037
+ // src/skill/model.ts
3038
+ var SkillError = class extends Error {
3039
+ code;
3040
+ details;
3041
+ exitCode;
3042
+ problemCode;
3043
+ constructor(code, message, options) {
3044
+ super(message);
3045
+ this.name = "SkillError";
3046
+ this.code = code;
3047
+ this.details = options.details;
3048
+ this.exitCode = options.exitCode;
3049
+ this.problemCode = options.problemCode;
3050
+ }
3051
+ };
3052
+
3053
+ // src/skill/account-auth-client.ts
3054
+ var dateTimeSchema = z12.string().datetime({ offset: true });
3055
+ var sessionIdSchema = z12.uuid();
3056
+ var createSessionSchema = z12.strictObject({
3057
+ session_id: sessionIdSchema,
3058
+ verification_url: z12.url(),
3059
+ expires_at: dateTimeSchema
3060
+ });
3061
+ var loginSessionSchema = z12.strictObject({
3062
+ session_id: sessionIdSchema,
3063
+ status: z12.enum(["pending", "approved", "denied", "consumed"]),
3064
+ expires_at: dateTimeSchema
3065
+ });
3066
+ var accountSchema = z12.strictObject({
3067
+ access_token: z12.string().min(1),
3068
+ refresh_token: z12.string().min(1),
3069
+ access_expires_at: dateTimeSchema,
3070
+ refresh_expires_at: dateTimeSchema,
3071
+ user: z12.strictObject({ id: z12.string().min(1), name: z12.string().min(1) })
3072
+ });
3073
+ var problemSchema = z12.object({ code: z12.string().min(1) }).passthrough();
3074
+ var SkillAccountAuthHttpClient = class {
3075
+ #fetch;
3076
+ #timeoutMs;
3077
+ constructor(options = {}) {
3078
+ this.#fetch = options.fetchImplementation ?? fetch;
3079
+ this.#timeoutMs = options.timeoutMs ?? 1e4;
3080
+ }
3081
+ async createLoginSession(origin, pkceChallenge) {
3082
+ const normalizedOrigin = normalizeSkillOrigin(origin);
3083
+ const response = await this.#request(
3084
+ normalizedOrigin,
3085
+ "/api/v1/integrations/skill-account-logins",
3086
+ { method: "POST", body: { pkce_challenge: pkceChallenge } }
3087
+ );
3088
+ const parsed = await this.#parse(response, createSessionSchema);
3089
+ if (normalizeSkillOrigin(new URL(parsed.verification_url).origin) !== normalizedOrigin) {
3090
+ throw invalidResponse();
3091
+ }
3092
+ return {
3093
+ sessionId: parsed.session_id,
3094
+ verificationUrl: parsed.verification_url,
3095
+ expiresAt: parsed.expires_at
3096
+ };
3097
+ }
3098
+ async getLoginSession(origin, sessionId) {
3099
+ const validatedId = this.#sessionId(sessionId);
3100
+ const response = await this.#request(
3101
+ normalizeSkillOrigin(origin),
3102
+ `/api/v1/integrations/skill-account-logins/${encodeURIComponent(validatedId)}`,
3103
+ { method: "GET" }
3104
+ );
3105
+ const parsed = await this.#parse(response, loginSessionSchema);
3106
+ if (parsed.session_id !== validatedId) throw invalidResponse();
3107
+ return {
3108
+ sessionId: parsed.session_id,
3109
+ status: parsed.status,
3110
+ expiresAt: parsed.expires_at
3111
+ };
3112
+ }
3113
+ async exchange(origin, input2) {
3114
+ const sessionId = this.#sessionId(input2.sessionId);
3115
+ const response = await this.#request(
3116
+ normalizeSkillOrigin(origin),
3117
+ `/api/v1/integrations/skill-account-logins/${encodeURIComponent(sessionId)}/exchange`,
3118
+ { method: "POST", body: { pkce_verifier: input2.pkceVerifier } }
3119
+ );
3120
+ return asAccount(await this.#parse(response, accountSchema));
3121
+ }
3122
+ async refresh(origin, refreshToken) {
3123
+ const response = await this.#request(
3124
+ normalizeSkillOrigin(origin),
3125
+ "/api/v1/integrations/skill-account-sessions/refresh",
3126
+ { method: "POST", body: { refresh_token: refreshToken } }
3127
+ );
3128
+ return asAccount(await this.#parse(response, accountSchema));
3129
+ }
3130
+ async logout(origin, accessToken, refreshToken) {
3131
+ const response = await this.#request(
3132
+ normalizeSkillOrigin(origin),
3133
+ "/api/v1/integrations/skill-account-sessions/current",
3134
+ {
3135
+ method: "DELETE",
3136
+ body: { refresh_token: refreshToken },
3137
+ authorization: `Bearer ${accessToken}`
3138
+ }
3139
+ );
3140
+ if (response.status !== 204) throw invalidResponse();
3141
+ }
3142
+ #sessionId(value) {
3143
+ const parsed = sessionIdSchema.safeParse(value);
3144
+ if (!parsed.success) {
3145
+ throw new SkillError("INVALID_ARGUMENT", "Skill login session is invalid", {
3146
+ exitCode: exitCodes.invalidArgument
3147
+ });
3148
+ }
3149
+ return parsed.data;
3150
+ }
3151
+ async #request(origin, requestPath, options) {
3152
+ let response;
3153
+ try {
3154
+ response = await this.#fetch(new URL(requestPath, origin), {
3155
+ headers: {
3156
+ Accept: "application/json",
3157
+ "Cache-Control": "no-store",
3158
+ ...options.body ? { "Content-Type": "application/json" } : {},
3159
+ ...options.authorization ? { Authorization: options.authorization } : {}
3160
+ },
3161
+ method: options.method,
3162
+ redirect: "error",
3163
+ signal: AbortSignal.timeout(this.#timeoutMs),
3164
+ ...options.body ? { body: JSON.stringify(options.body) } : {}
3165
+ });
3166
+ } catch {
3167
+ throw new SkillError(
3168
+ "NETWORK_UNREACHABLE",
3169
+ "Cannot reach the SAG account authorization service",
3170
+ { exitCode: exitCodes.networkUnreachable }
3171
+ );
3172
+ }
3173
+ if (response.redirected || response.status >= 300 && response.status < 400) {
3174
+ throw invalidResponse();
3175
+ }
3176
+ if (!response.ok) {
3177
+ let code = "unknown";
3178
+ try {
3179
+ const problem = problemSchema.safeParse(await response.json());
3180
+ if (problem.success) code = problem.data.code;
3181
+ } catch {
3182
+ }
3183
+ throw problemError(code, response.status);
3184
+ }
3185
+ return response;
3186
+ }
3187
+ async #parse(response, schema) {
3188
+ try {
3189
+ const parsed = schema.safeParse(await response.json());
3190
+ if (parsed.success) return parsed.data;
3191
+ } catch {
3192
+ }
3193
+ throw invalidResponse();
3194
+ }
3195
+ };
3196
+ function asAccount(value) {
3197
+ return {
3198
+ accessToken: value.access_token,
3199
+ refreshToken: value.refresh_token,
3200
+ accessExpiresAt: value.access_expires_at,
3201
+ refreshExpiresAt: value.refresh_expires_at,
3202
+ user: value.user
3203
+ };
3204
+ }
3205
+ function invalidResponse() {
3206
+ return new SkillError(
3207
+ "INVALID_RESPONSE",
3208
+ "SAG account authorization returned an incompatible response",
3209
+ { exitCode: exitCodes.internalError }
3210
+ );
3211
+ }
3212
+ function problemError(code, status2) {
3213
+ if (code === "access_denied" || status2 === 403) {
3214
+ return new SkillError("PERMISSION_DENIED", "SAG account authorization was denied", {
3215
+ exitCode: exitCodes.permissionDenied,
3216
+ problemCode: "access_denied"
3217
+ });
3218
+ }
3219
+ if (code === "login_session_expired" || code === "invalid_grant" || status2 === 410) {
3220
+ return new SkillError("AUTH_EXPIRED", "SAG account authorization has expired", {
3221
+ exitCode: exitCodes.authRequired,
3222
+ problemCode: code
3223
+ });
3224
+ }
3225
+ if (status2 === 401) {
3226
+ return new SkillError("AUTH_REQUIRED", "SAG account authorization is required", {
3227
+ exitCode: exitCodes.authRequired
3228
+ });
3229
+ }
3230
+ if (status2 === 429 || code === "rate_limited") {
3231
+ return new SkillError("RATE_LIMITED", "SAG account authorization is rate limited", {
3232
+ exitCode: exitCodes.networkUnreachable,
3233
+ problemCode: "rate_limited"
3234
+ });
3235
+ }
3236
+ return invalidResponse();
3237
+ }
3238
+
2684
3239
  // src/program.ts
3240
+ import { randomUUID as randomUUID7 } from "crypto";
2685
3241
  import { Command, CommanderError, Option } from "commander";
2686
3242
 
2687
3243
  // src/api/schemas.ts
2688
- import { z as z10 } from "zod";
2689
- var dateTime = z10.string().min(1);
2690
- var rootSchema = z10.object({
2691
- name: z10.string().min(1),
2692
- version: z10.string().min(1),
2693
- docs: z10.string()
3244
+ import { z as z13 } from "zod";
3245
+ var dateTime = z13.string().min(1);
3246
+ var rootSchema = z13.object({
3247
+ name: z13.string().min(1),
3248
+ version: z13.string().min(1),
3249
+ docs: z13.string()
2694
3250
  }).passthrough();
2695
- var readySchema = z10.object({
2696
- status: z10.string(),
2697
- db: z10.boolean()
3251
+ var readySchema = z13.object({
3252
+ status: z13.string(),
3253
+ db: z13.boolean()
2698
3254
  }).passthrough();
2699
- var capabilitiesSchema = z10.record(z10.string(), z10.unknown());
2700
- var userSchema = z10.object({
2701
- id: z10.string().min(1),
2702
- email: z10.string(),
2703
- name: z10.string(),
3255
+ var capabilitiesSchema = z13.record(z13.string(), z13.unknown());
3256
+ var userSchema = z13.object({
3257
+ id: z13.string().min(1),
3258
+ email: z13.string(),
3259
+ name: z13.string(),
2704
3260
  created_at: dateTime.optional()
2705
3261
  }).passthrough();
2706
- var loginResponseSchema = z10.object({
2707
- access_token: z10.string().min(1),
3262
+ var loginResponseSchema = z13.object({
3263
+ access_token: z13.string().min(1),
2708
3264
  user: userSchema
2709
3265
  }).passthrough();
2710
- var sourceSchema = z10.object({
2711
- id: z10.string().min(1),
2712
- name: z10.string(),
2713
- description: z10.string(),
2714
- source_type: z10.enum(["document", "web", "message", "audio"]),
2715
- connector_kind: z10.string(),
2716
- status: z10.enum(["active", "paused", "error"]),
2717
- document_count: z10.number().int().nonnegative(),
2718
- chunk_count: z10.number().int().nonnegative(),
2719
- event_count: z10.number().int().nonnegative(),
3266
+ var sourceSchema = z13.object({
3267
+ id: z13.string().min(1),
3268
+ name: z13.string(),
3269
+ description: z13.string(),
3270
+ source_type: z13.enum(["document", "web", "message", "audio"]),
3271
+ connector_kind: z13.string(),
3272
+ status: z13.enum(["active", "paused", "error"]),
3273
+ document_count: z13.number().int().nonnegative(),
3274
+ chunk_count: z13.number().int().nonnegative(),
3275
+ event_count: z13.number().int().nonnegative(),
2720
3276
  created_at: dateTime,
2721
3277
  updated_at: dateTime
2722
3278
  }).passthrough();
2723
- var documentStatusSchema = z10.enum([
3279
+ var documentStatusSchema = z13.enum([
2724
3280
  "pending",
2725
3281
  "loading",
2726
3282
  "extracting",
@@ -2728,102 +3284,102 @@ var documentStatusSchema = z10.enum([
2728
3284
  "ready",
2729
3285
  "failed"
2730
3286
  ]);
2731
- var documentSchema = z10.object({
2732
- id: z10.string().min(1),
2733
- source_id: z10.string().min(1),
2734
- filename: z10.string(),
2735
- content_type: z10.string(),
2736
- size_bytes: z10.number().int().nonnegative(),
3287
+ var documentSchema = z13.object({
3288
+ id: z13.string().min(1),
3289
+ source_id: z13.string().min(1),
3290
+ filename: z13.string(),
3291
+ content_type: z13.string(),
3292
+ size_bytes: z13.number().int().nonnegative(),
2737
3293
  status: documentStatusSchema,
2738
- chunk_count: z10.number().int().nonnegative(),
2739
- event_count: z10.number().int().nonnegative(),
2740
- progress: z10.number().int().min(0).max(100),
2741
- token_usage: z10.number().int().nonnegative(),
2742
- error: z10.string().nullable(),
3294
+ chunk_count: z13.number().int().nonnegative(),
3295
+ event_count: z13.number().int().nonnegative(),
3296
+ progress: z13.number().int().min(0).max(100),
3297
+ token_usage: z13.number().int().nonnegative(),
3298
+ error: z13.string().nullable(),
2743
3299
  created_at: dateTime,
2744
3300
  updated_at: dateTime
2745
3301
  }).passthrough();
2746
- var searchSectionSchema = z10.object({
2747
- chunk_id: z10.string().nullable(),
2748
- heading: z10.string(),
2749
- content: z10.string(),
2750
- score: z10.number(),
2751
- rank: z10.number().int(),
2752
- source_id: z10.string().nullable(),
2753
- source_name: z10.string().nullable().optional()
3302
+ var searchSectionSchema = z13.object({
3303
+ chunk_id: z13.string().nullable(),
3304
+ heading: z13.string(),
3305
+ content: z13.string(),
3306
+ score: z13.number(),
3307
+ rank: z13.number().int(),
3308
+ source_id: z13.string().nullable(),
3309
+ source_name: z13.string().nullable().optional()
2754
3310
  }).passthrough();
2755
- var searchEventSchema = z10.object({
2756
- id: z10.string(),
2757
- title: z10.string(),
2758
- summary: z10.string(),
2759
- rank: z10.number().int(),
2760
- score: z10.number()
3311
+ var searchEventSchema = z13.object({
3312
+ id: z13.string(),
3313
+ title: z13.string(),
3314
+ summary: z13.string(),
3315
+ rank: z13.number().int(),
3316
+ score: z13.number()
2761
3317
  }).passthrough();
2762
- var sourceHitSchema = z10.object({
2763
- source_id: z10.string(),
2764
- source_name: z10.string().nullable().optional(),
2765
- event_hits: z10.number().int(),
2766
- max_score: z10.number(),
2767
- latest_event_time: z10.string().nullable().optional()
3318
+ var sourceHitSchema = z13.object({
3319
+ source_id: z13.string(),
3320
+ source_name: z13.string().nullable().optional(),
3321
+ event_hits: z13.number().int(),
3322
+ max_score: z13.number(),
3323
+ latest_event_time: z13.string().nullable().optional()
2768
3324
  }).passthrough();
2769
- var searchResponseSchema = z10.object({
2770
- query: z10.string(),
2771
- sections: z10.array(searchSectionSchema),
2772
- events: z10.array(searchEventSchema),
2773
- entities: z10.array(z10.record(z10.string(), z10.unknown())),
2774
- relations: z10.array(z10.record(z10.string(), z10.unknown())),
2775
- source_hits: z10.array(sourceHitSchema),
2776
- summary: z10.string(),
2777
- exploration_id: z10.string().nullable(),
2778
- stats: z10.record(z10.string(), z10.unknown())
3325
+ var searchResponseSchema = z13.object({
3326
+ query: z13.string(),
3327
+ sections: z13.array(searchSectionSchema),
3328
+ events: z13.array(searchEventSchema),
3329
+ entities: z13.array(z13.record(z13.string(), z13.unknown())),
3330
+ relations: z13.array(z13.record(z13.string(), z13.unknown())),
3331
+ source_hits: z13.array(sourceHitSchema),
3332
+ summary: z13.string(),
3333
+ exploration_id: z13.string().nullable(),
3334
+ stats: z13.record(z13.string(), z13.unknown())
2779
3335
  }).passthrough();
2780
- var mcpDescriptorSchema = z10.object({
2781
- name: z10.string(),
2782
- scope: z10.string(),
2783
- source_count: z10.number().int().nonnegative(),
2784
- tools: z10.array(z10.string()),
2785
- http: z10.object({
2786
- transport: z10.string(),
2787
- url: z10.string().url(),
2788
- headers: z10.record(z10.string(), z10.string())
3336
+ var mcpDescriptorSchema = z13.object({
3337
+ name: z13.string(),
3338
+ scope: z13.string(),
3339
+ source_count: z13.number().int().nonnegative(),
3340
+ tools: z13.array(z13.string()),
3341
+ http: z13.object({
3342
+ transport: z13.string(),
3343
+ url: z13.string().url(),
3344
+ headers: z13.record(z13.string(), z13.string())
2789
3345
  }).passthrough()
2790
3346
  }).passthrough();
2791
- var outlineItemSchema = z10.object({
2792
- rank: z10.number().int(),
2793
- heading: z10.string(),
2794
- chunk_id: z10.string()
3347
+ var outlineItemSchema = z13.object({
3348
+ rank: z13.number().int(),
3349
+ heading: z13.string(),
3350
+ chunk_id: z13.string()
2795
3351
  }).passthrough();
2796
- var outlineSchema = z10.object({
2797
- document_id: z10.string(),
2798
- filename: z10.string(),
2799
- outline: z10.array(outlineItemSchema)
3352
+ var outlineSchema = z13.object({
3353
+ document_id: z13.string(),
3354
+ filename: z13.string(),
3355
+ outline: z13.array(outlineItemSchema)
2800
3356
  }).passthrough();
2801
- var grepMatchSchema = z10.object({
2802
- chunk_id: z10.string(),
2803
- heading: z10.string(),
2804
- snippet: z10.string()
3357
+ var grepMatchSchema = z13.object({
3358
+ chunk_id: z13.string(),
3359
+ heading: z13.string(),
3360
+ snippet: z13.string()
2805
3361
  }).passthrough();
2806
- var grepResponseSchema = z10.object({
2807
- pattern: z10.string(),
2808
- matches: z10.array(grepMatchSchema),
2809
- count: z10.number().int().nonnegative()
3362
+ var grepResponseSchema = z13.object({
3363
+ pattern: z13.string(),
3364
+ matches: z13.array(grepMatchSchema),
3365
+ count: z13.number().int().nonnegative()
2810
3366
  }).passthrough();
2811
- var readResponseSchema = z10.object({
2812
- document_id: z10.string(),
2813
- filename: z10.string(),
2814
- total_lines: z10.number().int().nonnegative(),
2815
- offset: z10.number().int().positive(),
2816
- limit: z10.number().int().positive(),
2817
- lines: z10.array(z10.string())
3367
+ var readResponseSchema = z13.object({
3368
+ document_id: z13.string(),
3369
+ filename: z13.string(),
3370
+ total_lines: z13.number().int().nonnegative(),
3371
+ offset: z13.number().int().positive(),
3372
+ limit: z13.number().int().positive(),
3373
+ lines: z13.array(z13.string())
2818
3374
  }).passthrough();
2819
- var entityContextSchema = z10.object({
2820
- entity_id: z10.string(),
2821
- name: z10.string(),
2822
- type: z10.string(),
2823
- description: z10.string(),
2824
- context: z10.string(),
2825
- source_id: z10.string(),
2826
- source_name: z10.string()
3375
+ var entityContextSchema = z13.object({
3376
+ entity_id: z13.string(),
3377
+ name: z13.string(),
3378
+ type: z13.string(),
3379
+ description: z13.string(),
3380
+ context: z13.string(),
3381
+ source_id: z13.string(),
3382
+ source_name: z13.string()
2827
3383
  }).passthrough();
2828
3384
 
2829
3385
  // src/api/client.ts
@@ -2932,7 +3488,7 @@ var SagClient = class {
2932
3488
  entityContextSchema
2933
3489
  );
2934
3490
  }
2935
- async #request(path11, schema, options = {}) {
3491
+ async #request(path16, schema, options = {}) {
2936
3492
  const authenticated = options.authenticated ?? true;
2937
3493
  if (authenticated && !this.#token) {
2938
3494
  throw new CliError("AUTH_REQUIRED", "No SAG token is configured", {
@@ -2952,7 +3508,7 @@ var SagClient = class {
2952
3508
  }
2953
3509
  let response;
2954
3510
  try {
2955
- response = await this.#fetch(new URL(path11, `${this.#origin}/`), {
3511
+ response = await this.#fetch(new URL(path16, `${this.#origin}/`), {
2956
3512
  method: options.method ?? "GET",
2957
3513
  headers,
2958
3514
  ...options.body !== void 0 ? { body: JSON.stringify(options.body) } : {},
@@ -3532,9 +4088,9 @@ async function agentStatus(runtime2, agent, serverName) {
3532
4088
  }
3533
4089
  }
3534
4090
  async function statusLocalAgents(runtime2, input2) {
3535
- const agents = input2.agent ? [input2.agent] : ["codex", "claude-code"];
4091
+ const agents2 = input2.agent ? [input2.agent] : ["codex", "claude-code"];
3536
4092
  return Promise.all(
3537
- agents.map((agent) => agentStatus(runtime2, agent, input2.serverName))
4093
+ agents2.map((agent) => agentStatus(runtime2, agent, input2.serverName))
3538
4094
  );
3539
4095
  }
3540
4096
  async function listAgentHosts(runtime2) {
@@ -3829,7 +4385,7 @@ function credentialStoreForEnterpriseMcpConnection(connection, stores) {
3829
4385
  }
3830
4386
 
3831
4387
  // src/mcp/enterprise/import.ts
3832
- import { createHash as createHash2 } from "crypto";
4388
+ import { createHash as createHash3 } from "crypto";
3833
4389
  function invalidImport(message, cause) {
3834
4390
  return new CliError("INVALID_ARGUMENT", message, {
3835
4391
  exitCode: exitCodes.invalidArgument,
@@ -3958,7 +4514,7 @@ function selectEnterpriseMcpCandidates(servers, requested = []) {
3958
4514
  return selectByServerKey(servers, requested);
3959
4515
  }
3960
4516
  function connectionIdForCollision(serverKey, url) {
3961
- return `${serverKey}-${createHash2("sha256").update(new URL(url).toString()).digest("hex").slice(0, 8)}`;
4517
+ return `${serverKey}-${createHash3("sha256").update(new URL(url).toString()).digest("hex").slice(0, 8)}`;
3962
4518
  }
3963
4519
 
3964
4520
  // src/mcp/enterprise/runtime.ts
@@ -3997,7 +4553,7 @@ import { isDeepStrictEqual } from "util";
3997
4553
  function stateId(agent, connectionId) {
3998
4554
  return connectionKey(agent, "user", connectionId);
3999
4555
  }
4000
- function configurationConflict(message) {
4556
+ function configurationConflict2(message) {
4001
4557
  return new CliError("CONFIG_CONFLICT", message, {
4002
4558
  exitCode: exitCodes.configConflict,
4003
4559
  hint: "\u8BF7\u68C0\u67E5\u8BE5 Agent \u7684\u540C\u540D MCP \u914D\u7F6E\u540E\u91CD\u8BD5\u3002"
@@ -4078,7 +4634,7 @@ async function attachEnterpriseMcp(runtime2, input2) {
4078
4634
  connectionId: input2.connection.id
4079
4635
  });
4080
4636
  if (action === "blocked-drift") {
4081
- throw configurationConflict("\u53D7\u7BA1 Agent MCP \u914D\u7F6E\u5DF2\u5728 SAG CLI \u4E4B\u5916\u88AB\u4FEE\u6539");
4637
+ throw configurationConflict2("\u53D7\u7BA1 Agent MCP \u914D\u7F6E\u5DF2\u5728 SAG CLI \u4E4B\u5916\u88AB\u4FEE\u6539");
4082
4638
  }
4083
4639
  if (action === "unchanged") {
4084
4640
  return success(input2.agent, "skipped", "Agent \u4E2D\u7684\u4F01\u4E1A MCP \u914D\u7F6E\u5DF2\u7ECF\u662F\u6700\u65B0\u72B6\u6001\u3002");
@@ -4100,7 +4656,7 @@ async function attachEnterpriseMcp(runtime2, input2) {
4100
4656
  await adapter.add(input2.connection.id, "user", input2.spec);
4101
4657
  const readBack = await adapter.read(input2.connection.id, "user");
4102
4658
  if (!readBack || fingerprintConnection(readBack.spec) !== fingerprintConnection(input2.spec)) {
4103
- throw configurationConflict("Agent MCP \u56DE\u8BFB\u7ED3\u679C\u4E0E\u8BF7\u6C42\u7684\u8FDE\u63A5\u4E0D\u4E00\u81F4");
4659
+ throw configurationConflict2("Agent MCP \u56DE\u8BFB\u7ED3\u679C\u4E0E\u8BF7\u6C42\u7684\u8FDE\u63A5\u4E0D\u4E00\u81F4");
4104
4660
  }
4105
4661
  await runtime2.verifier.verify(readBack.spec, { timeoutMs: input2.timeoutMs });
4106
4662
  await runtime2.managed.setAgent(
@@ -4146,10 +4702,10 @@ async function detachEnterpriseMcp(runtime2, input2) {
4146
4702
  runtime2.managed.getAgent(id)
4147
4703
  ]);
4148
4704
  if (!managed || managed.connectionId !== input2.connection.id || !host) {
4149
- throw configurationConflict("\u8BE5 Agent MCP \u914D\u7F6E\u4E0D\u53D7\u5F53\u524D\u4F01\u4E1A\u8FDE\u63A5\u7BA1\u7406");
4705
+ throw configurationConflict2("\u8BE5 Agent MCP \u914D\u7F6E\u4E0D\u53D7\u5F53\u524D\u4F01\u4E1A\u8FDE\u63A5\u7BA1\u7406");
4150
4706
  }
4151
4707
  if (host.fingerprint !== managed.fingerprint) {
4152
- throw configurationConflict("\u53D7\u7BA1 Agent MCP \u914D\u7F6E\u5DF2\u5728 SAG CLI \u4E4B\u5916\u88AB\u4FEE\u6539");
4708
+ throw configurationConflict2("\u53D7\u7BA1 Agent MCP \u914D\u7F6E\u5DF2\u5728 SAG CLI \u4E4B\u5916\u88AB\u4FEE\u6539");
4153
4709
  }
4154
4710
  if (input2.dryRun) {
4155
4711
  return success(
@@ -4187,7 +4743,7 @@ async function refreshManagedAgents(runtime2, input2) {
4187
4743
  return Promise.all(
4188
4744
  matching.map(async (state) => {
4189
4745
  const agent = agentForState(state, input2.connection.id);
4190
- if (!agent) return failed(state.id, configurationConflict("\u53D7\u7BA1 Agent \u72B6\u6001\u65E0\u6548"));
4746
+ if (!agent) return failed(state.id, configurationConflict2("\u53D7\u7BA1 Agent \u72B6\u6001\u65E0\u6548"));
4191
4747
  try {
4192
4748
  return await attachEnterpriseMcp(runtime2, { ...input2, agent, yes: true });
4193
4749
  } catch (cause) {
@@ -4198,10 +4754,10 @@ async function refreshManagedAgents(runtime2, input2) {
4198
4754
  }
4199
4755
 
4200
4756
  // src/mcp/enterprise/skill-service.ts
4201
- import { createHash as createHash3 } from "crypto";
4202
- import { cp, lstat as lstat2, mkdir as mkdir7, readFile as readFile9, readdir, rename as rename8, rm } from "fs/promises";
4757
+ import { createHash as createHash4 } from "crypto";
4758
+ import { cp, lstat as lstat2, mkdir as mkdir9, readFile as readFile11, readdir, rename as rename10, rm } from "fs/promises";
4203
4759
  import { fileURLToPath } from "url";
4204
- import path10 from "path";
4760
+ import path12 from "path";
4205
4761
  var skillName = "sag-mcp";
4206
4762
  var skillDescription = "Use connected enterprise SAG MCP servers to search, read, and manage authorized knowledge.";
4207
4763
  var requiredReferences = [
@@ -4211,32 +4767,32 @@ var requiredReferences = [
4211
4767
  function skillTarget(agent, options) {
4212
4768
  switch (agent) {
4213
4769
  case "codex":
4214
- return path10.join(
4215
- options.CODEX_HOME || path10.join(options.home, ".codex"),
4770
+ return path12.join(
4771
+ options.CODEX_HOME || path12.join(options.home, ".codex"),
4216
4772
  "skills",
4217
4773
  skillName
4218
4774
  );
4219
4775
  case "claude-code":
4220
- return path10.join(options.home, ".claude", "skills", skillName);
4776
+ return path12.join(options.home, ".claude", "skills", skillName);
4221
4777
  case "workbuddy":
4222
- return path10.join(options.home, ".workbuddy", "skills", skillName);
4778
+ return path12.join(options.home, ".workbuddy", "skills", skillName);
4223
4779
  }
4224
4780
  }
4225
4781
  function managedSkillId(agent) {
4226
4782
  return `${agent}:${skillName}`;
4227
4783
  }
4228
4784
  function bundledSkillPath() {
4229
- const moduleDirectory = path10.dirname(fileURLToPath(import.meta.url));
4230
- const packageRoot = path10.basename(moduleDirectory) === "dist" ? path10.dirname(moduleDirectory) : path10.resolve(moduleDirectory, "../../..");
4231
- return path10.join(packageRoot, "skills", skillName);
4785
+ const moduleDirectory = path12.dirname(fileURLToPath(import.meta.url));
4786
+ const packageRoot = path12.basename(moduleDirectory) === "dist" ? path12.dirname(moduleDirectory) : path12.resolve(moduleDirectory, "../../..");
4787
+ return path12.join(packageRoot, "skills", skillName);
4232
4788
  }
4233
4789
  function timestamp(now) {
4234
4790
  return now.toISOString().replace(/[-:.]/gu, "");
4235
4791
  }
4236
4792
  function siblingPath(target, prefix, now) {
4237
- return path10.join(
4238
- path10.dirname(target),
4239
- `${path10.basename(target)}.${prefix}-${timestamp(now)}-${process.pid}`
4793
+ return path12.join(
4794
+ path12.dirname(target),
4795
+ `${path12.basename(target)}.${prefix}-${timestamp(now)}-${process.pid}`
4240
4796
  );
4241
4797
  }
4242
4798
  async function exists(target) {
@@ -4249,26 +4805,26 @@ async function exists(target) {
4249
4805
  }
4250
4806
  }
4251
4807
  async function validateSkillBundle(bundlePath) {
4252
- const skill = await readFile9(path10.join(bundlePath, "SKILL.md"), "utf8");
4808
+ const skill = await readFile11(path12.join(bundlePath, "SKILL.md"), "utf8");
4253
4809
  const lines = skill.split(/\r?\n/u);
4254
4810
  const lineCount = skill.endsWith("\n") ? lines.length - 1 : lines.length;
4255
4811
  if (lineCount >= 100 || lines[0] !== "---" || lines[1] !== `name: ${skillName}` || lines[2] !== `description: ${skillDescription}` || lines[3] !== "---") {
4256
4812
  throw new Error("\u968F\u5305\u53D1\u5E03\u7684 SAG MCP Skill frontmatter \u65E0\u6548\u3002");
4257
4813
  }
4258
4814
  for (const reference of requiredReferences) {
4259
- await readFile9(path10.join(bundlePath, reference), "utf8");
4815
+ await readFile11(path12.join(bundlePath, reference), "utf8");
4260
4816
  if (!skill.includes(`(${reference})`)) {
4261
4817
  throw new Error(`\u968F\u5305\u53D1\u5E03\u7684 SAG MCP Skill \u672A\u5F15\u7528 ${reference}\u3002`);
4262
4818
  }
4263
4819
  }
4264
4820
  }
4265
4821
  async function updateDirectoryFingerprint(hash, root, relative = "") {
4266
- const current = path10.join(root, relative);
4822
+ const current = path12.join(root, relative);
4267
4823
  const entries = await readdir(current, { withFileTypes: true });
4268
4824
  for (const entry of entries.sort(
4269
4825
  (left, right) => left.name.localeCompare(right.name)
4270
4826
  )) {
4271
- const entryRelative = path10.join(relative, entry.name);
4827
+ const entryRelative = path12.join(relative, entry.name);
4272
4828
  if (entry.isDirectory()) {
4273
4829
  hash.update(`directory:${entryRelative}
4274
4830
  `);
@@ -4280,12 +4836,12 @@ async function updateDirectoryFingerprint(hash, root, relative = "") {
4280
4836
  }
4281
4837
  hash.update(`file:${entryRelative}
4282
4838
  `);
4283
- hash.update(await readFile9(path10.join(root, entryRelative)));
4839
+ hash.update(await readFile11(path12.join(root, entryRelative)));
4284
4840
  }
4285
4841
  }
4286
4842
  async function fingerprintSkillBundle(bundlePath) {
4287
4843
  await validateSkillBundle(bundlePath);
4288
- const hash = createHash3("sha256");
4844
+ const hash = createHash4("sha256");
4289
4845
  await updateDirectoryFingerprint(hash, bundlePath);
4290
4846
  return `sha256:${hash.digest("hex")}`;
4291
4847
  }
@@ -4321,7 +4877,7 @@ async function restoreTarget(input2) {
4321
4877
  if (!input2.targetMutated) return;
4322
4878
  if (input2.hadTarget && input2.backupPath && await exists(input2.backupPath)) {
4323
4879
  await rm(input2.target, { recursive: true, force: true });
4324
- await rename8(input2.backupPath, input2.target);
4880
+ await rename10(input2.backupPath, input2.target);
4325
4881
  return;
4326
4882
  }
4327
4883
  if (!input2.hadTarget) {
@@ -4341,7 +4897,7 @@ async function installEnterpriseSkill(input2) {
4341
4897
  try {
4342
4898
  hadTarget = await exists(target);
4343
4899
  await validateSkillBundle(bundlePath);
4344
- await mkdir7(path10.dirname(target), { recursive: true });
4900
+ await mkdir9(path12.dirname(target), { recursive: true });
4345
4901
  await cp(bundlePath, temporaryPath, {
4346
4902
  recursive: true,
4347
4903
  errorOnExist: true,
@@ -4350,10 +4906,10 @@ async function installEnterpriseSkill(input2) {
4350
4906
  await validateSkillBundle(temporaryPath);
4351
4907
  if (hadTarget) {
4352
4908
  backupPath = siblingPath(target, "backup", input2.now());
4353
- await rename8(target, backupPath);
4909
+ await rename10(target, backupPath);
4354
4910
  targetMutated = true;
4355
4911
  }
4356
- await rename8(temporaryPath, target);
4912
+ await rename10(temporaryPath, target);
4357
4913
  targetMutated = true;
4358
4914
  const fingerprint = await fingerprintSkillBundle(target);
4359
4915
  await input2.managed.setSkill({
@@ -4408,11 +4964,11 @@ async function uninstallEnterpriseSkill(input2) {
4408
4964
  throw conflict5("\u53D7 SAG CLI \u7BA1\u7406\u7684 SAG MCP Skill \u5DF2\u88AB\u5916\u90E8\u4FEE\u6539\u3002");
4409
4965
  }
4410
4966
  const removedPath = siblingPath(target, "remove", input2.now());
4411
- await rename8(target, removedPath);
4967
+ await rename10(target, removedPath);
4412
4968
  try {
4413
4969
  await input2.managed.deleteSkill(state.id);
4414
4970
  } catch (cause) {
4415
- await rename8(removedPath, target);
4971
+ await rename10(removedPath, target);
4416
4972
  throw cause;
4417
4973
  }
4418
4974
  await rm(removedPath, { recursive: true, force: true }).catch(() => void 0);
@@ -4428,11 +4984,11 @@ async function uninstallEnterpriseSkill(input2) {
4428
4984
  throw conflict5("\u6CA1\u6709\u53EF\u79FB\u9664\u7684 SAG MCP Skill\u3002");
4429
4985
  }
4430
4986
  const backupPath = siblingPath(target, "backup", input2.now());
4431
- await rename8(target, backupPath);
4987
+ await rename10(target, backupPath);
4432
4988
  try {
4433
4989
  await input2.managed.deleteSkill(managedSkillId(input2.agent));
4434
4990
  } catch (cause) {
4435
- await rename8(backupPath, target).catch(() => void 0);
4991
+ await rename10(backupPath, target).catch(() => void 0);
4436
4992
  throw cause;
4437
4993
  }
4438
4994
  return {
@@ -4844,7 +5400,7 @@ async function connectEnterpriseMcp(input2) {
4844
5400
  );
4845
5401
  }
4846
5402
  }
4847
- const agents = verified.length ? await selectInstalledAgents(input2) : [];
5403
+ const agents2 = verified.length ? await selectInstalledAgents(input2) : [];
4848
5404
  const initialActive = await input2.runtime.store.active();
4849
5405
  const connections = [];
4850
5406
  const skillEligibleAgents = /* @__PURE__ */ new Set();
@@ -4952,19 +5508,19 @@ async function connectEnterpriseMcp(input2) {
4952
5508
  spec: item.parsed.connection
4953
5509
  });
4954
5510
  }
4955
- for (const agent of agents) {
5511
+ for (const agent of agents2) {
4956
5512
  stages.push(skippedAgentStage(agent));
4957
5513
  zeroToolSkillAgents.add(agent);
4958
5514
  }
4959
5515
  continue;
4960
5516
  }
4961
5517
  let riskAccepted = input2.yes || input2.dryRun || !item.risk.hasWarning;
4962
- if (!riskAccepted && agents.length) {
5518
+ if (!riskAccepted && agents2.length) {
4963
5519
  riskAccepted = await input2.runtime.agents.confirm(
4964
5520
  `MCP ${connection.id} \u542B\u6709\u5199\u5165\u3001\u5220\u9664\u6216\u98CE\u9669\u672A\u77E5\u7684\u5DE5\u5177\uFF0C\u662F\u5426\u7EE7\u7EED\u63A5\u5165\u6240\u9009 Agent\uFF1F`
4965
5521
  );
4966
5522
  }
4967
- for (const agent of agents) {
5523
+ for (const agent of agents2) {
4968
5524
  if (!riskAccepted) {
4969
5525
  stages.push(
4970
5526
  stageSuccess(
@@ -4987,7 +5543,7 @@ async function connectEnterpriseMcp(input2) {
4987
5543
  if (attached.stage.ok && attached.connected) successfulAgents.add(agent);
4988
5544
  }
4989
5545
  for (const agent of successfulAgents) {
4990
- if (agents.includes(agent)) skillEligibleAgents.add(agent);
5546
+ if (agents2.includes(agent)) skillEligibleAgents.add(agent);
4991
5547
  reverifications.push({
4992
5548
  agent,
4993
5549
  connectionId: connection.id,
@@ -4995,7 +5551,7 @@ async function connectEnterpriseMcp(input2) {
4995
5551
  });
4996
5552
  }
4997
5553
  }
4998
- for (const agent of agents) {
5554
+ for (const agent of agents2) {
4999
5555
  if (skillEligibleAgents.has(agent)) {
5000
5556
  stages.push(await installSkillForAgent(input2, agent));
5001
5557
  } else if (zeroToolSkillAgents.has(agent)) {
@@ -5294,7 +5850,7 @@ async function inspectEnterpriseMcpStatus(input2) {
5294
5850
  } catch {
5295
5851
  health = "unavailable";
5296
5852
  }
5297
- const agents = await Promise.all(
5853
+ const agents2 = await Promise.all(
5298
5854
  supportedAgents.map(async (agent) => {
5299
5855
  const [mcpInspection, skillInspection] = await Promise.allSettled([
5300
5856
  inspectEnterpriseAgent(input2.runtime.agents, {
@@ -5322,7 +5878,7 @@ async function inspectEnterpriseMcpStatus(input2) {
5322
5878
  return { agent, mcp, skill, nextActions: nextActions2 };
5323
5879
  })
5324
5880
  );
5325
- const nextActions = agents.flatMap((agent) => agent.nextActions);
5881
+ const nextActions = agents2.flatMap((agent) => agent.nextActions);
5326
5882
  if (health !== "ready") {
5327
5883
  nextActions.unshift(`\u68C0\u67E5\u4F01\u4E1A MCP ${connection.id} \u7684\u7F51\u7EDC\u548C\u51ED\u636E`);
5328
5884
  }
@@ -5336,7 +5892,7 @@ async function inspectEnterpriseMcpStatus(input2) {
5336
5892
  health,
5337
5893
  toolCount,
5338
5894
  risk,
5339
- agents,
5895
+ agents: agents2,
5340
5896
  nextActions
5341
5897
  };
5342
5898
  }
@@ -5694,9 +6250,9 @@ async function sourceStatus(client, sourceId) {
5694
6250
  }
5695
6251
 
5696
6252
  // src/core/context.ts
5697
- import { createHash as createHash4 } from "crypto";
6253
+ import { createHash as createHash5 } from "crypto";
5698
6254
  function directCredentialRef(url) {
5699
- const fingerprint = createHash4("sha256").update(url).digest("hex").slice(0, 16);
6255
+ const fingerprint = createHash5("sha256").update(url).digest("hex").slice(0, 16);
5700
6256
  return `sag-cli/direct-${fingerprint}`;
5701
6257
  }
5702
6258
  async function createRuntimeContext(input2) {
@@ -5943,12 +6499,14 @@ async function runDoctor(client, input2, localMcpChecks) {
5943
6499
  // src/output/redaction.ts
5944
6500
  var SENSITIVE_KEYS = /* @__PURE__ */ new Set([
5945
6501
  "authorization",
6502
+ "authorization_code",
5946
6503
  "token",
5947
6504
  "access_token",
5948
6505
  "password",
5949
6506
  "secret",
5950
6507
  "secretkey",
5951
- "secret_key"
6508
+ "secret_key",
6509
+ "ticket"
5952
6510
  ]);
5953
6511
  var BEARER_TOKEN = /\bBearer\s+[^\s"',}\]]+/giu;
5954
6512
  function redactString(value) {
@@ -6095,7 +6653,7 @@ function renderEnterpriseMcpStages(value) {
6095
6653
  function renderEnterpriseMcpStatus(value) {
6096
6654
  const connection = isRecord(value.connection) ? value.connection : {};
6097
6655
  const risk = isRecord(value.risk) ? value.risk : {};
6098
- const agents = Array.isArray(value.agents) ? value.agents.filter(isRecord) : [];
6656
+ const agents2 = Array.isArray(value.agents) ? value.agents.filter(isRecord) : [];
6099
6657
  const nextActions = Array.isArray(value.nextActions) ? value.nextActions.map(String) : [];
6100
6658
  const lines = [
6101
6659
  `\u8FDE\u63A5\uFF1A${String(connection.id ?? "unknown")}${connection.active ? "\uFF08\u5F53\u524D\uFF09" : ""}`,
@@ -6103,7 +6661,7 @@ function renderEnterpriseMcpStatus(value) {
6103
6661
  `\u5065\u5EB7\uFF1A${String(value.health ?? "unknown")}`,
6104
6662
  `\u5DE5\u5177\uFF1A${String(value.toolCount ?? 0)}`,
6105
6663
  `\u98CE\u9669\uFF1A\u53EA\u8BFB ${String(risk.readOnly ?? 0)}\uFF0C\u5199\u5165 ${String(risk.write ?? 0)}\uFF0C\u5220\u9664 ${String(risk.destructive ?? 0)}\uFF0C\u672A\u77E5 ${String(risk.unknown ?? 0)}`,
6106
- ...agents.map(
6664
+ ...agents2.map(
6107
6665
  (agent) => `Agent ${String(agent.agent ?? "unknown")}\uFF1AMCP ${String(agent.mcp ?? "unknown")}\uFF0CSkill ${String(agent.skill ?? "unknown")}`
6108
6666
  ),
6109
6667
  ...nextActions.length ? ["\u4E0B\u4E00\u6B65\uFF1A", ...nextActions.map((action) => ` ${action}`)] : []
@@ -6208,190 +6766,1836 @@ function renderJson(result) {
6208
6766
  `;
6209
6767
  }
6210
6768
 
6211
- // src/program.ts
6212
- function parseInteger(value) {
6213
- return Number.parseInt(value, 10);
6214
- }
6215
- function parsePositiveInteger(value) {
6216
- const parsed = Number.parseInt(value, 10);
6217
- if (!Number.isSafeInteger(parsed) || parsed <= 0) {
6218
- throw new CliError("INVALID_ARGUMENT", "\u53C2\u6570\u5FC5\u987B\u662F\u6B63\u6574\u6570", {
6219
- exitCode: exitCodes.invalidArgument
6220
- });
6769
+ // src/output/ndjson.ts
6770
+ var terminalEvents = /* @__PURE__ */ new Set([
6771
+ "completed",
6772
+ "failed",
6773
+ "detached",
6774
+ "completed_with_errors"
6775
+ ]);
6776
+ var SkillEventWriter = class {
6777
+ constructor(write) {
6778
+ this.write = write;
6779
+ }
6780
+ write;
6781
+ terminal = false;
6782
+ emit(event) {
6783
+ if (this.terminal) throw new Error("Cannot emit after terminal event");
6784
+ if (terminalEvents.has(event.event)) {
6785
+ throw new Error("terminal event must be sent with complete");
6786
+ }
6787
+ this.write(`${JSON.stringify(redact(event))}
6788
+ `);
6221
6789
  }
6222
- return parsed;
6223
- }
6224
- function parseAgent(value) {
6225
- if (value === "codex" || value === "claude-code") {
6226
- return value;
6790
+ complete(event) {
6791
+ if (!terminalEvents.has(event.event)) {
6792
+ throw new Error("A terminal event is required");
6793
+ }
6794
+ if (this.terminal) throw new Error("Cannot emit a second terminal event");
6795
+ this.terminal = true;
6796
+ this.write(`${JSON.stringify(redact(event))}
6797
+ `);
6227
6798
  }
6228
- throw new CliError("INVALID_ARGUMENT", `Unsupported Agent: ${value}`, {
6229
- exitCode: exitCodes.invalidArgument,
6230
- hint: "Choose codex or claude-code."
6231
- });
6799
+ };
6800
+
6801
+ // src/skill/installer.ts
6802
+ import { createHash as createHash6, randomUUID as randomUUID6 } from "crypto";
6803
+ import {
6804
+ cp as cp2,
6805
+ lstat as lstat3,
6806
+ mkdir as mkdir10,
6807
+ readFile as readFile12,
6808
+ readdir as readdir2,
6809
+ rename as rename11,
6810
+ rm as rm2,
6811
+ writeFile as writeFile6
6812
+ } from "fs/promises";
6813
+ import { fileURLToPath as fileURLToPath2 } from "url";
6814
+ import path13 from "path";
6815
+ import { lock as lock8 } from "proper-lockfile";
6816
+ var skillName2 = "sag-knowledge";
6817
+ var packageName = "@zleap-ai/sag-cli";
6818
+ var agents = ["codex", "claude-code", "workbuddy"];
6819
+ var requiredFiles = [
6820
+ "SKILL.md",
6821
+ "references/cli-reference.md",
6822
+ "references/ndjson-contract.md",
6823
+ "references/safety.md",
6824
+ "scripts/sag-knowledge.cjs"
6825
+ ];
6826
+ function bundledSkillPath2() {
6827
+ const directory = path13.dirname(fileURLToPath2(import.meta.url));
6828
+ const packageRoot = path13.basename(directory) === "dist" ? path13.dirname(directory) : path13.resolve(directory, "../..");
6829
+ return path13.join(packageRoot, "skill");
6232
6830
  }
6233
- function parseEnterpriseAgent(value) {
6234
- if (value === "codex" || value === "claude-code" || value === "workbuddy") {
6235
- return value;
6831
+ function uniqueAgents(selected) {
6832
+ return [...new Set(selected ?? agents)];
6833
+ }
6834
+ function skillTargetPath(agent, input2) {
6835
+ const home = path13.resolve(input2.home);
6836
+ const codexHome = input2.environment.CODEX_HOME ? path13.resolve(input2.environment.CODEX_HOME) : path13.join(home, ".codex");
6837
+ switch (agent) {
6838
+ case "codex":
6839
+ return path13.join(codexHome, "skills", skillName2);
6840
+ case "claude-code":
6841
+ return path13.join(home, ".claude", "skills", skillName2);
6842
+ case "workbuddy":
6843
+ return path13.join(home, ".workbuddy", "skills", skillName2);
6236
6844
  }
6237
- throw new CliError("INVALID_ARGUMENT", `\u4E0D\u652F\u6301\u7684 Agent\uFF1A${value}`, {
6238
- exitCode: exitCodes.invalidArgument,
6239
- hint: "\u8BF7\u9009\u62E9 codex\u3001claude-code \u6216 workbuddy\u3002"
6240
- });
6241
6845
  }
6242
- function parseCredentialStore(value) {
6243
- if (value === "keychain" || value === "file") return value;
6244
- throw new CliError("INVALID_ARGUMENT", `\u4E0D\u652F\u6301\u7684\u51ED\u636E\u5B58\u50A8\uFF1A${value}`, {
6245
- exitCode: exitCodes.invalidArgument,
6246
- hint: "\u8BF7\u9009\u62E9 keychain \u6216 file\u3002"
6247
- });
6846
+ function sourceTargetError() {
6847
+ throw new Error("Skill target is outside the configured user-level Agent directory");
6248
6848
  }
6249
- function resolveEnterpriseSkillAgent(positional, option) {
6250
- if (positional && option && positional !== option) {
6251
- throw new CliError(
6252
- "INVALID_ARGUMENT",
6253
- "\u4F4D\u7F6E\u53C2\u6570 Agent \u4E0E --agent \u6307\u5B9A\u7684 Agent \u4E0D\u4E00\u81F4",
6254
- { exitCode: exitCodes.invalidArgument }
6255
- );
6256
- }
6257
- const agent = option ?? positional;
6258
- if (!agent) {
6259
- throw new CliError("INVALID_ARGUMENT", "\u8BF7\u4F7F\u7528 --agent \u6307\u5B9A Agent", {
6260
- exitCode: exitCodes.invalidArgument,
6261
- hint: "\u8BF7\u9009\u62E9 codex\u3001claude-code \u6216 workbuddy\u3002"
6262
- });
6263
- }
6264
- return agent;
6849
+ function assertUserTarget(target, agent, runtime2) {
6850
+ const parent = agent === "codex" ? path13.resolve(
6851
+ runtime2.environment.CODEX_HOME ?? path13.join(runtime2.home, ".codex"),
6852
+ "skills"
6853
+ ) : path13.resolve(
6854
+ runtime2.home,
6855
+ agent === "claude-code" ? ".claude/skills" : ".workbuddy/skills"
6856
+ );
6857
+ if (path13.dirname(target) !== parent || path13.basename(target) !== skillName2)
6858
+ sourceTargetError();
6265
6859
  }
6266
- function parseMcpToolArguments(value) {
6860
+ var SkillTargetLeaseError = class extends Error {
6861
+ };
6862
+ async function withTargetLease(target, operation) {
6863
+ await mkdir10(path13.dirname(target), { recursive: true });
6864
+ let release;
6267
6865
  try {
6268
- const parsed = JSON.parse(value);
6269
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
6270
- throw new Error("not an object");
6271
- }
6272
- return parsed;
6273
- } catch (cause) {
6274
- throw new CliError("INVALID_ARGUMENT", "--arguments-json \u5FC5\u987B\u662F\u4E00\u4E2A JSON \u5BF9\u8C61", {
6275
- exitCode: exitCodes.invalidArgument,
6276
- cause
6866
+ release = await lock8(path13.join(path13.dirname(target), `.${skillName2}.lease`), {
6867
+ realpath: false,
6868
+ stale: 1e4,
6869
+ update: 2e3,
6870
+ retries: { retries: 100, factor: 1, minTimeout: 10, maxTimeout: 10 }
6277
6871
  });
6872
+ } catch {
6873
+ throw new SkillTargetLeaseError();
6874
+ }
6875
+ try {
6876
+ return await operation();
6877
+ } finally {
6878
+ await release();
6278
6879
  }
6279
6880
  }
6280
- function collect(value, previous) {
6281
- return [...previous, value];
6881
+ async function exists2(location) {
6882
+ try {
6883
+ await lstat3(location);
6884
+ return true;
6885
+ } catch (error) {
6886
+ if (error.code === "ENOENT") return false;
6887
+ throw error;
6888
+ }
6282
6889
  }
6283
- function commandOptions2(program) {
6284
- return program.opts();
6890
+ async function moveTarget(runtime2, from, to) {
6891
+ if (runtime2.filesystem?.rename) return runtime2.filesystem.rename(from, to);
6892
+ await rename11(from, to);
6285
6893
  }
6286
- function emit(program, dependencies2, data) {
6287
- const options = commandOptions2(program);
6288
- dependencies2.writeStdout(
6289
- options.json ? renderJson(success3(data)) : renderHuman(data, options.quiet ?? false)
6290
- );
6894
+ async function removeTarget(runtime2, target) {
6895
+ if (runtime2.filesystem?.remove) return runtime2.filesystem.remove(target);
6896
+ await rm2(target, { recursive: true, force: true });
6291
6897
  }
6292
- async function runtime(program, dependencies2) {
6293
- const options = commandOptions2(program);
6294
- return createRuntimeContext({
6295
- options: {
6296
- ...options.url ? { url: options.url } : {},
6297
- ...options.profile ? { profile: options.profile } : {}
6298
- },
6299
- environment: dependencies2.environment,
6300
- configStore: dependencies2.configStore,
6301
- credentialStore: dependencies2.credentialStore,
6302
- createClient: dependencies2.createClient
6303
- });
6898
+ function sameManagedSkill(left, right) {
6899
+ return left?.agent === right.agent && left.target === right.target && left.fingerprint === right.fingerprint && left.version === right.version && left.backupPath === right.backupPath;
6304
6900
  }
6305
- function localMcpRuntime(dependencies2) {
6306
- if (!dependencies2.localMcp) {
6307
- throw new CliError("DEPENDENCY_MISSING", "Local MCP runtime is unavailable", {
6308
- exitCode: exitCodes.dependencyMissing
6309
- });
6901
+ async function deactivateUnmanagedTarget(input2) {
6902
+ try {
6903
+ await moveTarget(input2.runtime, input2.target, input2.removed);
6904
+ } catch {
6905
+ return "active-unmanaged";
6310
6906
  }
6311
- return dependencies2.localMcp;
6907
+ return await exists2(input2.target) ? "active-unmanaged" : "incomplete";
6312
6908
  }
6313
- function enterpriseMcpRuntime(dependencies2) {
6314
- if (!dependencies2.enterpriseMcp) {
6315
- throw new CliError("DEPENDENCY_MISSING", "\u4F01\u4E1A MCP \u8FD0\u884C\u73AF\u5883\u4E0D\u53EF\u7528", {
6316
- exitCode: exitCodes.dependencyMissing
6317
- });
6909
+ async function recoverUninstall(input2) {
6910
+ try {
6911
+ await moveTarget(input2.runtime, input2.removed, input2.target);
6912
+ } catch {
6913
+ return "incomplete";
6318
6914
  }
6319
- return dependencies2.enterpriseMcp;
6915
+ if (!await exists2(input2.target)) return "incomplete";
6916
+ try {
6917
+ await validateBundle(input2.target, input2.agent);
6918
+ if (await fingerprintDirectory(input2.target) !== input2.managed.fingerprint) {
6919
+ return deactivateUnmanagedTarget(input2);
6920
+ }
6921
+ } catch {
6922
+ return deactivateUnmanagedTarget(input2);
6923
+ }
6924
+ try {
6925
+ const current = await input2.runtime.state.getManagedSkill(
6926
+ input2.agent,
6927
+ input2.target
6928
+ );
6929
+ if (sameManagedSkill(current, input2.managed)) return "restored";
6930
+ await input2.runtime.state.saveManagedSkill(input2.managed);
6931
+ const restored = await input2.runtime.state.getManagedSkill(
6932
+ input2.agent,
6933
+ input2.target
6934
+ );
6935
+ if (sameManagedSkill(restored, input2.managed)) return "restored";
6936
+ } catch {
6937
+ }
6938
+ return deactivateUnmanagedTarget(input2);
6320
6939
  }
6321
- function enterpriseMcpCommandRuntime(dependencies2, timeoutMs) {
6322
- return {
6323
- ...enterpriseMcpRuntime(dependencies2),
6324
- cliVersion: dependencies2.version,
6325
- timeoutMs
6326
- };
6940
+ function digest(value) {
6941
+ return `sha256:${createHash6("sha256").update(value).digest("hex")}`;
6327
6942
  }
6328
- function rejectEnterpriseProfile(program) {
6329
- if (!commandOptions2(program).profile) return;
6330
- throw new CliError("INVALID_ARGUMENT", "\u4F01\u4E1A MCP \u547D\u4EE4\u4E0D\u4F7F\u7528 Profile", {
6331
- exitCode: exitCodes.invalidArgument,
6332
- hint: "\u8BF7\u79FB\u9664 --profile\uFF1B\u4E2A\u4EBA Profile \u53EA\u7528\u4E8E sag mcp test \u548C sag agent\u3002"
6333
- });
6943
+ function parseManifest(raw) {
6944
+ const value = JSON.parse(raw);
6945
+ if (!value || typeof value !== "object" || Array.isArray(value))
6946
+ throw new Error("invalid manifest");
6947
+ const manifest = value;
6948
+ if (manifest.schema !== 1 || manifest.name !== skillName2 || manifest.package !== packageName || typeof manifest.packageVersion !== "string" || !manifest.packageVersion || typeof manifest.bundleDigest !== "string" || !/^sha256:[a-f0-9]{64}$/u.test(manifest.bundleDigest) || !agents.includes(manifest.agent) && manifest.agent !== "template") {
6949
+ throw new Error("invalid manifest");
6950
+ }
6951
+ return manifest;
6334
6952
  }
6335
- async function selectedProfileName(program, dependencies2) {
6336
- const options = commandOptions2(program);
6337
- if (options.profile) {
6338
- await dependencies2.configStore.getProfile(options.profile);
6339
- return options.profile;
6953
+ async function validateBundle(bundle, expectedAgent) {
6954
+ const [rawManifest, runtime2] = await Promise.all([
6955
+ readFile12(path13.join(bundle, "manifest.json"), "utf8"),
6956
+ readFile12(path13.join(bundle, "scripts", "sag-knowledge.cjs"))
6957
+ ]);
6958
+ const manifest = parseManifest(rawManifest);
6959
+ if (manifest.agent !== expectedAgent || manifest.bundleDigest !== digest(runtime2)) {
6960
+ throw new Error("invalid Skill bundle");
6961
+ }
6962
+ await Promise.all(requiredFiles.map((file) => readFile12(path13.join(bundle, file))));
6963
+ return manifest;
6964
+ }
6965
+ async function fingerprintDirectory(root, relative = "") {
6966
+ const hash = createHash6("sha256");
6967
+ async function visit(nested = "") {
6968
+ const entries = await readdir2(path13.join(root, nested), { withFileTypes: true });
6969
+ for (const entry of entries.sort(
6970
+ (left, right) => left.name.localeCompare(right.name)
6971
+ )) {
6972
+ const entryPath = path13.join(nested, entry.name);
6973
+ if (entry.isDirectory()) {
6974
+ hash.update(`directory:${entryPath}
6975
+ `);
6976
+ await visit(entryPath);
6977
+ } else if (entry.isFile()) {
6978
+ hash.update(`file:${entryPath}
6979
+ `);
6980
+ hash.update(await readFile12(path13.join(root, entryPath)));
6981
+ } else {
6982
+ throw new Error("unsupported Skill file");
6983
+ }
6984
+ }
6340
6985
  }
6341
- return (await dependencies2.configStore.load()).currentProfile ?? "local";
6986
+ await visit(relative);
6987
+ return `sha256:${hash.digest("hex")}`;
6342
6988
  }
6343
- async function selectedServerName(program, dependencies2, explicitName) {
6344
- const profile = await selectedProfileName(program, dependencies2);
6345
- return {
6346
- profile,
6347
- serverName: explicitName ?? `sag-knowledge-${profile}`
6989
+ function sibling(target, suffix) {
6990
+ return path13.join(path13.dirname(target), `.${skillName2}.${suffix}-${randomUUID6()}`);
6991
+ }
6992
+ async function restore(input2) {
6993
+ let recovered = true;
6994
+ try {
6995
+ await removeTarget(input2.runtime, input2.temporary);
6996
+ } catch {
6997
+ recovered = false;
6998
+ }
6999
+ const targetMatchesReplacement = async () => {
7000
+ if (!input2.replacementFingerprint || !await exists2(input2.target)) return false;
7001
+ try {
7002
+ return await fingerprintDirectory(input2.target) === input2.replacementFingerprint;
7003
+ } catch {
7004
+ return false;
7005
+ }
6348
7006
  };
7007
+ if (input2.backup && await exists2(input2.backup)) {
7008
+ if (await exists2(input2.target)) {
7009
+ if (!await targetMatchesReplacement()) return false;
7010
+ try {
7011
+ await removeTarget(input2.runtime, input2.target);
7012
+ } catch {
7013
+ return false;
7014
+ }
7015
+ }
7016
+ try {
7017
+ await moveTarget(input2.runtime, input2.backup, input2.target);
7018
+ } catch {
7019
+ return false;
7020
+ }
7021
+ } else if (!input2.hadTarget && await exists2(input2.target)) {
7022
+ if (!await targetMatchesReplacement()) return false;
7023
+ try {
7024
+ await removeTarget(input2.runtime, input2.target);
7025
+ } catch {
7026
+ return false;
7027
+ }
7028
+ }
7029
+ return recovered;
6349
7030
  }
6350
- function addProfileCommands(program, dependencies2) {
6351
- const profile = program.command("profile").description("Manage SAG profiles");
6352
- profile.command("add").argument("<name>").argument("<url>").action(async (name, url) => {
6353
- emit(program, dependencies2, await dependencies2.configStore.addProfile(name, url));
6354
- });
6355
- profile.command("list").action(async () => {
6356
- emit(program, dependencies2, await dependencies2.configStore.listProfiles());
6357
- });
6358
- profile.command("use").argument("<name>").action(async (name) => {
6359
- emit(program, dependencies2, await dependencies2.configStore.useProfile(name));
6360
- });
6361
- profile.command("show").argument("[name]").action(async (name) => {
6362
- emit(program, dependencies2, await dependencies2.configStore.getProfile(name));
6363
- });
6364
- profile.command("remove").argument("<name>").action(async (name) => {
6365
- if (!commandOptions2(program).yes && !await dependencies2.confirm(`Remove profile ${name}?`)) {
6366
- emit(program, dependencies2, { removed: null, cancelled: true });
6367
- return;
7031
+ async function inspectOne(runtime2, agent, inspectExplicitTarget = false) {
7032
+ const target = skillTargetPath(agent, runtime2);
7033
+ assertUserTarget(target, agent, runtime2);
7034
+ if (!inspectExplicitTarget && !await runtime2.agents[agent].installed()) {
7035
+ return { agent, target, status: "unavailable", message: "Agent is not installed." };
7036
+ }
7037
+ const managed = await runtime2.state.getManagedSkill(agent, target);
7038
+ if (!await exists2(target)) {
7039
+ return managed ? {
7040
+ agent,
7041
+ target,
7042
+ status: "drifted",
7043
+ code: "INSTALL_TARGET_CONFLICT",
7044
+ message: "Managed Skill directory is missing."
7045
+ } : { agent, target, status: "missing", message: "Skill is not installed." };
7046
+ }
7047
+ if (!managed) {
7048
+ return {
7049
+ agent,
7050
+ target,
7051
+ status: "unmanaged",
7052
+ code: "INSTALL_TARGET_CONFLICT",
7053
+ message: "Existing Skill directory is not managed by SAG CLI."
7054
+ };
7055
+ }
7056
+ try {
7057
+ const manifest = await validateBundle(target, agent);
7058
+ const fingerprint = await fingerprintDirectory(target);
7059
+ if (managed.fingerprint !== fingerprint || managed.version !== manifest.packageVersion) {
7060
+ return {
7061
+ agent,
7062
+ target,
7063
+ status: "drifted",
7064
+ code: "INSTALL_TARGET_CONFLICT",
7065
+ message: "Managed Skill directory has changed outside SAG CLI."
7066
+ };
6368
7067
  }
6369
- await dependencies2.configStore.removeProfile(name);
6370
- emit(program, dependencies2, { removed: name });
6371
- });
7068
+ return {
7069
+ agent,
7070
+ target,
7071
+ status: "installed",
7072
+ version: manifest.packageVersion,
7073
+ package: manifest.package,
7074
+ message: "Managed Skill is installed."
7075
+ };
7076
+ } catch {
7077
+ return {
7078
+ agent,
7079
+ target,
7080
+ status: "drifted",
7081
+ code: "INSTALL_TARGET_CONFLICT",
7082
+ message: "Managed Skill directory has changed outside SAG CLI."
7083
+ };
7084
+ }
6372
7085
  }
6373
- function addAuthCommands(program, dependencies2) {
6374
- const auth = program.command("auth").description("Manage SAG credentials");
6375
- auth.command("login").option("--name <name>", "SAG user name for the existing local login").action(async (options) => {
6376
- const context = await runtime(program, dependencies2);
6377
- const result = await login({
6378
- credentialRef: context.credentialRef,
6379
- ...context.connection.environmentToken ? { environmentToken: context.connection.environmentToken } : {},
6380
- ...options.name ? { name: options.name } : {},
6381
- promptName: dependencies2.promptName,
6382
- authenticate: (name) => dependencies2.createClient({ origin: context.connection.url }).login(name),
6383
- validate: async (token) => dependencies2.createClient({ origin: context.connection.url, token }).me(),
6384
- store: dependencies2.credentialStore
6385
- });
6386
- emit(program, dependencies2, result);
6387
- });
6388
- auth.command("status").action(async () => {
6389
- const context = await runtime(program, dependencies2);
6390
- const result = await status({
6391
- credentialRef: context.credentialRef,
6392
- ...context.connection.environmentToken ? { environmentToken: context.connection.environmentToken } : {},
6393
- validate: async (token) => dependencies2.createClient({ origin: context.connection.url, token }).me(),
6394
- store: dependencies2.credentialStore
7086
+ async function activeAgents(runtime2, selected) {
7087
+ const choices = uniqueAgents(selected);
7088
+ if (selected) return choices;
7089
+ const installed = await Promise.all(
7090
+ choices.map(async (agent) => ({
7091
+ agent,
7092
+ installed: await runtime2.agents[agent].installed()
7093
+ }))
7094
+ );
7095
+ return installed.filter((choice) => choice.installed).map((choice) => choice.agent);
7096
+ }
7097
+ async function requireConfirmation(runtime2, selection, action) {
7098
+ if (selection.yes) return true;
7099
+ return await runtime2.confirm?.(`${action} SAG Knowledge Skill?`) ?? false;
7100
+ }
7101
+ async function inspectSkillTargets(runtime2, input2) {
7102
+ const source = await validateBundle(
7103
+ runtime2.bundlePath ?? bundledSkillPath2(),
7104
+ "template"
7105
+ );
7106
+ const selected = input2.agents ? uniqueAgents(input2.agents) : agents;
7107
+ const targets = await Promise.all(
7108
+ selected.map((agent) => inspectOne(runtime2, agent, input2.agents !== void 0))
7109
+ );
7110
+ return {
7111
+ package: source.package,
7112
+ version: source.packageVersion,
7113
+ targets,
7114
+ completedWithErrors: targets.some((target) => target.status === "drifted")
7115
+ };
7116
+ }
7117
+ async function installSkillTargets(runtime2, input2) {
7118
+ if (input2.update && !runtime2.resolveLatestBundle) {
7119
+ throw new CliError(
7120
+ "DEPENDENCY_MISSING",
7121
+ "Unable to resolve @latest Skill package",
7122
+ {
7123
+ exitCode: exitCodes.dependencyMissing,
7124
+ hint: "Run this update through `npx @zleap-ai/sag-cli@latest` or restore npm access."
7125
+ }
7126
+ );
7127
+ }
7128
+ const resolved = input2.update ? await runtime2.resolveLatestBundle?.() : void 0;
7129
+ const sourcePath = resolved?.bundlePath ?? runtime2.bundlePath ?? bundledSkillPath2();
7130
+ const source = await validateBundle(sourcePath, "template");
7131
+ const selected = await activeAgents(runtime2, input2.agents);
7132
+ if (selected.length === 0) {
7133
+ return {
7134
+ package: source.package,
7135
+ version: source.packageVersion,
7136
+ targets: agents.map((agent) => ({
7137
+ agent,
7138
+ target: skillTargetPath(agent, runtime2),
7139
+ status: "unavailable",
7140
+ message: "No supported Agent was detected on this device."
7141
+ })),
7142
+ completedWithErrors: true
7143
+ };
7144
+ }
7145
+ if (!await requireConfirmation(runtime2, input2, "Install")) {
7146
+ return {
7147
+ package: source.package,
7148
+ version: source.packageVersion,
7149
+ targets: selected.map((agent) => ({
7150
+ agent,
7151
+ target: skillTargetPath(agent, runtime2),
7152
+ status: "skipped",
7153
+ message: "Installation was not confirmed."
7154
+ })),
7155
+ completedWithErrors: false
7156
+ };
7157
+ }
7158
+ const targets = [];
7159
+ try {
7160
+ for (const agent of selected) {
7161
+ const target = skillTargetPath(agent, runtime2);
7162
+ assertUserTarget(target, agent, runtime2);
7163
+ try {
7164
+ targets.push(
7165
+ await withTargetLease(target, async () => {
7166
+ const inspection = await inspectOne(
7167
+ runtime2,
7168
+ agent,
7169
+ input2.agents !== void 0
7170
+ );
7171
+ if (inspection.status === "installed" && inspection.version === source.packageVersion) {
7172
+ return {
7173
+ ...inspection,
7174
+ status: "skipped",
7175
+ message: "Managed Skill is already current."
7176
+ };
7177
+ }
7178
+ if (inspection.status === "unmanaged" || inspection.status === "drifted") {
7179
+ return { ...inspection, status: "conflict" };
7180
+ }
7181
+ if (inspection.status === "unavailable") return inspection;
7182
+ const temporary = sibling(target, "tmp");
7183
+ const hadTarget = inspection.status === "installed";
7184
+ let backup;
7185
+ let replacementFingerprint;
7186
+ try {
7187
+ await cp2(sourcePath, temporary, {
7188
+ recursive: true,
7189
+ errorOnExist: true,
7190
+ force: false
7191
+ });
7192
+ const temporaryManifestPath = path13.join(temporary, "manifest.json");
7193
+ const temporaryManifest = await validateBundle(temporary, "template");
7194
+ await writeFile6(
7195
+ temporaryManifestPath,
7196
+ `${JSON.stringify({ ...temporaryManifest, agent }, null, 2)}
7197
+ `,
7198
+ "utf8"
7199
+ );
7200
+ await validateBundle(temporary, agent);
7201
+ replacementFingerprint = await fingerprintDirectory(temporary);
7202
+ await runtime2.agents[agent].installSkill?.();
7203
+ if (hadTarget) {
7204
+ backup = sibling(target, "backup");
7205
+ await moveTarget(runtime2, target, backup);
7206
+ }
7207
+ await moveTarget(runtime2, temporary, target);
7208
+ if (await fingerprintDirectory(target) !== replacementFingerprint) {
7209
+ throw new Error("Skill target changed during installation");
7210
+ }
7211
+ const managed = {
7212
+ agent,
7213
+ target,
7214
+ version: source.packageVersion,
7215
+ fingerprint: replacementFingerprint,
7216
+ ...backup ? { backupPath: backup } : {}
7217
+ };
7218
+ await runtime2.state.saveManagedSkill(managed);
7219
+ return {
7220
+ agent,
7221
+ target,
7222
+ status: hadTarget ? "updated" : "created",
7223
+ version: source.packageVersion,
7224
+ package: source.package,
7225
+ message: "Skill installed."
7226
+ };
7227
+ } catch {
7228
+ const restored = await restore({
7229
+ runtime: runtime2,
7230
+ target,
7231
+ temporary,
7232
+ ...backup ? { backup } : {},
7233
+ hadTarget,
7234
+ ...replacementFingerprint ? { replacementFingerprint } : {}
7235
+ }).catch(() => false);
7236
+ return {
7237
+ agent,
7238
+ target,
7239
+ status: "failed",
7240
+ message: restored ? "Skill installation failed and this target was restored." : "Skill installation failed and recovery was incomplete."
7241
+ };
7242
+ }
7243
+ })
7244
+ );
7245
+ } catch (error) {
7246
+ if (!(error instanceof SkillTargetLeaseError)) throw error;
7247
+ targets.push({
7248
+ agent,
7249
+ target,
7250
+ status: "conflict",
7251
+ code: "INSTALL_TARGET_CONFLICT",
7252
+ message: "Skill target is busy; wait for the active SAG CLI operation and retry."
7253
+ });
7254
+ }
7255
+ }
7256
+ return {
7257
+ package: source.package,
7258
+ version: source.packageVersion,
7259
+ targets,
7260
+ completedWithErrors: targets.some(
7261
+ (target) => ["failed", "conflict"].includes(target.status)
7262
+ )
7263
+ };
7264
+ } finally {
7265
+ await resolved?.cleanup();
7266
+ }
7267
+ }
7268
+ async function uninstallSkillTargets(runtime2, input2) {
7269
+ const source = await validateBundle(
7270
+ runtime2.bundlePath ?? bundledSkillPath2(),
7271
+ "template"
7272
+ );
7273
+ const selected = await activeAgents(runtime2, input2.agents);
7274
+ if (!await requireConfirmation(runtime2, input2, "Uninstall")) {
7275
+ return {
7276
+ package: source.package,
7277
+ version: source.packageVersion,
7278
+ targets: selected.map((agent) => ({
7279
+ agent,
7280
+ target: skillTargetPath(agent, runtime2),
7281
+ status: "skipped",
7282
+ message: "Uninstall was not confirmed."
7283
+ })),
7284
+ completedWithErrors: false
7285
+ };
7286
+ }
7287
+ const targets = [];
7288
+ for (const agent of selected) {
7289
+ const target = skillTargetPath(agent, runtime2);
7290
+ assertUserTarget(target, agent, runtime2);
7291
+ try {
7292
+ targets.push(
7293
+ await withTargetLease(target, async () => {
7294
+ const inspection = await inspectOne(
7295
+ runtime2,
7296
+ agent,
7297
+ input2.agents !== void 0
7298
+ );
7299
+ if (inspection.status === "missing") return inspection;
7300
+ if (inspection.status !== "installed") {
7301
+ return {
7302
+ ...inspection,
7303
+ status: "conflict",
7304
+ code: "INSTALL_TARGET_CONFLICT"
7305
+ };
7306
+ }
7307
+ const removed = sibling(inspection.target, "remove");
7308
+ const managed = await runtime2.state.getManagedSkill(agent, inspection.target);
7309
+ try {
7310
+ await moveTarget(runtime2, inspection.target, removed);
7311
+ await runtime2.state.deleteManagedSkill(agent, inspection.target);
7312
+ await removeTarget(runtime2, removed);
7313
+ return {
7314
+ agent,
7315
+ target: inspection.target,
7316
+ status: "removed",
7317
+ ...inspection.version ? { version: inspection.version } : {},
7318
+ package: source.package,
7319
+ message: "Managed Skill was removed."
7320
+ };
7321
+ } catch {
7322
+ const recovery = managed && await exists2(removed) ? await recoverUninstall({
7323
+ runtime: runtime2,
7324
+ agent,
7325
+ target: inspection.target,
7326
+ removed,
7327
+ managed
7328
+ }) : "incomplete";
7329
+ return {
7330
+ agent,
7331
+ target: inspection.target,
7332
+ status: "failed",
7333
+ message: recovery === "restored" ? "Skill uninstall failed; target and managed state were restored." : recovery === "active-unmanaged" ? "Skill uninstall failed; recovery is incomplete and the active target is unmanaged." : "Skill uninstall failed and recovery was incomplete."
7334
+ };
7335
+ }
7336
+ })
7337
+ );
7338
+ } catch (error) {
7339
+ if (!(error instanceof SkillTargetLeaseError)) throw error;
7340
+ targets.push({
7341
+ agent,
7342
+ target,
7343
+ status: "conflict",
7344
+ code: "INSTALL_TARGET_CONFLICT",
7345
+ message: "Skill target is busy; wait for the active SAG CLI operation and retry."
7346
+ });
7347
+ }
7348
+ }
7349
+ return {
7350
+ package: source.package,
7351
+ version: source.packageVersion,
7352
+ targets,
7353
+ completedWithErrors: targets.some(
7354
+ (target) => ["failed", "conflict"].includes(target.status)
7355
+ )
7356
+ };
7357
+ }
7358
+ var skillInstallerExitCode = exitCodes.configConflict;
7359
+
7360
+ // src/skill/account-auth.ts
7361
+ import { createHash as createHash7, randomBytes as nodeRandomBytes } from "crypto";
7362
+ import { setTimeout as delay } from "timers/promises";
7363
+ async function authorizeSkillAccount(runtime2, origin) {
7364
+ const normalizedOrigin = normalizeSkillOrigin(origin);
7365
+ const random = runtime2.randomBytes ?? nodeRandomBytes;
7366
+ const verifier2 = random(32).toString("base64url");
7367
+ const challenge = createHash7("sha256").update(verifier2).digest("base64url");
7368
+ const session = await runtime2.client.createLoginSession(normalizedOrigin, challenge);
7369
+ await runtime2.openBrowser(session.verificationUrl);
7370
+ const sleep = runtime2.sleep ?? delay;
7371
+ for (; ; ) {
7372
+ if (runtime2.now().getTime() >= new Date(session.expiresAt).getTime()) {
7373
+ throw expiredAuthorization();
7374
+ }
7375
+ const current = await runtime2.client.getLoginSession(
7376
+ normalizedOrigin,
7377
+ session.sessionId
7378
+ );
7379
+ if (current.status === "denied") {
7380
+ throw new SkillError(
7381
+ "PERMISSION_DENIED",
7382
+ "SAG account authorization was denied",
7383
+ { exitCode: exitCodes.permissionDenied }
7384
+ );
7385
+ }
7386
+ if (current.status === "consumed") throw expiredAuthorization();
7387
+ if (current.status === "approved") {
7388
+ const account = await runtime2.client.exchange(normalizedOrigin, {
7389
+ sessionId: session.sessionId,
7390
+ pkceVerifier: verifier2
7391
+ });
7392
+ await runtime2.config.saveAccount(normalizedOrigin, account);
7393
+ return { origin: normalizedOrigin, account, browserOpened: true };
7394
+ }
7395
+ await sleep(1e3);
7396
+ }
7397
+ }
7398
+ async function withFreshSkillAccount(runtime2, origin, operation) {
7399
+ const normalizedOrigin = normalizeSkillOrigin(origin);
7400
+ const current = await requireAccount(runtime2, normalizedOrigin);
7401
+ if (isFuture(current.accessExpiresAt, runtime2.now())) {
7402
+ return operation(current.accessToken);
7403
+ }
7404
+ const accessToken = await runtime2.config.withRefreshLock(
7405
+ normalizedOrigin,
7406
+ async () => {
7407
+ const latest = await requireAccount(runtime2, normalizedOrigin);
7408
+ if (isFuture(latest.accessExpiresAt, runtime2.now())) return latest.accessToken;
7409
+ if (!isFuture(latest.refreshExpiresAt, runtime2.now())) {
7410
+ await runtime2.config.removeAccount(normalizedOrigin);
7411
+ throw expiredAuthorization();
7412
+ }
7413
+ try {
7414
+ const rotated = await runtime2.client.refresh(
7415
+ normalizedOrigin,
7416
+ latest.refreshToken
7417
+ );
7418
+ const next = {
7419
+ ...rotated,
7420
+ ...latest.recentKnowledgeBase && !rotated.recentKnowledgeBase ? { recentKnowledgeBase: latest.recentKnowledgeBase } : {}
7421
+ };
7422
+ await runtime2.config.saveAccount(normalizedOrigin, next);
7423
+ return next.accessToken;
7424
+ } catch (error) {
7425
+ if (error instanceof SkillError && (error.code === "AUTH_EXPIRED" || error.code === "AUTH_REQUIRED")) {
7426
+ await runtime2.config.removeAccount(normalizedOrigin);
7427
+ }
7428
+ throw error;
7429
+ }
7430
+ }
7431
+ );
7432
+ return operation(accessToken);
7433
+ }
7434
+ async function logoutSkillAccount(runtime2, origin) {
7435
+ const normalizedOrigin = normalizeSkillOrigin(origin);
7436
+ const account = await runtime2.config.getAccount(normalizedOrigin);
7437
+ if (!account) return false;
7438
+ await runtime2.client.logout(
7439
+ normalizedOrigin,
7440
+ account.accessToken,
7441
+ account.refreshToken
7442
+ );
7443
+ await runtime2.config.removeAccount(normalizedOrigin);
7444
+ return true;
7445
+ }
7446
+ async function requireAccount(runtime2, origin) {
7447
+ const account = await runtime2.config.getAccount(origin);
7448
+ if (account) return account;
7449
+ throw new SkillError("AUTH_REQUIRED", "SAG account authorization is required", {
7450
+ exitCode: exitCodes.authRequired
7451
+ });
7452
+ }
7453
+ function isFuture(value, now) {
7454
+ return new Date(value).getTime() > now.getTime();
7455
+ }
7456
+ function expiredAuthorization() {
7457
+ return new SkillError("AUTH_EXPIRED", "SAG account authorization has expired", {
7458
+ exitCode: exitCodes.authRequired
7459
+ });
7460
+ }
7461
+
7462
+ // src/skill/account-knowledge.ts
7463
+ async function renameSkillAccountDocument(runtime2, origin, input2) {
7464
+ const knowledgeBase = await selectedKnowledgeBase(
7465
+ runtime2,
7466
+ origin,
7467
+ input2.knowledgeBase
7468
+ );
7469
+ const renamed = await withSkillAccountKnowledgeClient(
7470
+ runtime2,
7471
+ origin,
7472
+ (client) => client.renameDocument({
7473
+ knowledgeBaseId: knowledgeBase.id,
7474
+ documentId: input2.documentId,
7475
+ expectedVersion: input2.expectedVersion,
7476
+ idempotencyKey: runtime2.createId(),
7477
+ title: input2.title
7478
+ })
7479
+ );
7480
+ await remember(runtime2, origin, knowledgeBase);
7481
+ return renamed;
7482
+ }
7483
+ async function deleteSkillAccountDocument(runtime2, origin, input2) {
7484
+ const knowledgeBase = await selectedKnowledgeBase(
7485
+ runtime2,
7486
+ origin,
7487
+ input2.knowledgeBase
7488
+ );
7489
+ const deleted = await withSkillAccountKnowledgeClient(
7490
+ runtime2,
7491
+ origin,
7492
+ (client) => client.deleteDocument({
7493
+ knowledgeBaseId: knowledgeBase.id,
7494
+ documentId: input2.documentId,
7495
+ expectedVersion: input2.expectedVersion,
7496
+ idempotencyKey: runtime2.createId()
7497
+ })
7498
+ );
7499
+ await remember(runtime2, origin, knowledgeBase);
7500
+ return deleted;
7501
+ }
7502
+ async function listSkillAccountKnowledgeBases(runtime2, origin) {
7503
+ return withSkillAccountKnowledgeClient(
7504
+ runtime2,
7505
+ origin,
7506
+ (client) => client.listKnowledgeBases()
7507
+ );
7508
+ }
7509
+ async function listSkillAccountDocuments(runtime2, origin, selector) {
7510
+ const normalizedOrigin = normalizeSkillOrigin(origin);
7511
+ const knowledgeBases = await listSkillAccountKnowledgeBases(
7512
+ runtime2,
7513
+ normalizedOrigin
7514
+ );
7515
+ const knowledgeBase = resolveKnowledgeBase(knowledgeBases, selector);
7516
+ const documents = await withSkillAccountKnowledgeClient(
7517
+ runtime2,
7518
+ normalizedOrigin,
7519
+ (client) => client.listDocuments(knowledgeBase.id)
7520
+ );
7521
+ await runtime2.config.saveRecentKnowledgeBase(normalizedOrigin, {
7522
+ id: knowledgeBase.id,
7523
+ name: knowledgeBase.name
7524
+ });
7525
+ return { knowledgeBase, documents };
7526
+ }
7527
+ async function askSkillAccountKnowledge(runtime2, origin, question, selector) {
7528
+ const normalizedOrigin = normalizeSkillOrigin(origin);
7529
+ let knowledgeBase;
7530
+ if (selector) {
7531
+ knowledgeBase = resolveKnowledgeBase(
7532
+ await listSkillAccountKnowledgeBases(runtime2, normalizedOrigin),
7533
+ selector
7534
+ );
7535
+ }
7536
+ const answer = await withSkillAccountKnowledgeClient(
7537
+ runtime2,
7538
+ normalizedOrigin,
7539
+ (client) => {
7540
+ if (!client.ask) {
7541
+ throw new SkillError(
7542
+ "PLATFORM_UNAVAILABLE",
7543
+ "SAG account question answering is unavailable",
7544
+ { exitCode: exitCodes.serviceNotReady }
7545
+ );
7546
+ }
7547
+ return client.ask(question, knowledgeBase?.id);
7548
+ }
7549
+ );
7550
+ if (knowledgeBase) {
7551
+ await runtime2.config.saveRecentKnowledgeBase(normalizedOrigin, {
7552
+ id: knowledgeBase.id,
7553
+ name: knowledgeBase.name
7554
+ });
7555
+ }
7556
+ return answer;
7557
+ }
7558
+ function resolveKnowledgeBase(knowledgeBases, selector) {
7559
+ const exactId = knowledgeBases.find((item) => item.id === selector);
7560
+ if (exactId) return exactId;
7561
+ const exactAlias = knowledgeBases.filter((item) => item.alias === selector);
7562
+ if (exactAlias.length === 1) return exactAlias[0];
7563
+ const normalized = selector.trim().toLocaleLowerCase();
7564
+ const names = knowledgeBases.filter(
7565
+ (item) => item.name.toLocaleLowerCase() === normalized
7566
+ );
7567
+ if (names.length === 1) return names[0];
7568
+ throw new SkillError(
7569
+ "INVALID_ARGUMENT",
7570
+ names.length > 1 ? "Knowledge Base name is ambiguous" : "Knowledge Base was not found",
7571
+ {
7572
+ details: { selector },
7573
+ exitCode: exitCodes.invalidArgument
7574
+ }
7575
+ );
7576
+ }
7577
+ async function selectedKnowledgeBase(runtime2, origin, selector) {
7578
+ return resolveKnowledgeBase(
7579
+ await listSkillAccountKnowledgeBases(runtime2, origin),
7580
+ selector
7581
+ );
7582
+ }
7583
+ async function remember(runtime2, origin, knowledgeBase) {
7584
+ await runtime2.config.saveRecentKnowledgeBase(origin, {
7585
+ id: knowledgeBase.id,
7586
+ name: knowledgeBase.name
7587
+ });
7588
+ }
7589
+ async function withSkillAccountKnowledgeClient(runtime2, origin, operation) {
7590
+ const normalizedOrigin = normalizeSkillOrigin(origin);
7591
+ return withFreshSkillAccount(
7592
+ runtime2.auth,
7593
+ normalizedOrigin,
7594
+ (accessToken) => operation(runtime2.createClient({ accessToken, origin: normalizedOrigin }))
7595
+ );
7596
+ }
7597
+
7598
+ // src/skill/account-upload.ts
7599
+ import { createHash as createHash8 } from "crypto";
7600
+ import { createReadStream } from "fs";
7601
+ import { stat as stat3 } from "fs/promises";
7602
+ import path14 from "path";
7603
+ var DEFAULT_MAX_UPLOAD_BYTES = 25 * 1024 * 1024;
7604
+ async function uploadSkillAccountFile(runtime2, origin, filePath, knowledgeBaseSelector, waitMs = 30 * 60 * 1e3, overwriteDocumentId) {
7605
+ const file = await inspectFile(filePath);
7606
+ const knowledgeBase = resolveKnowledgeBase(
7607
+ await listSkillAccountKnowledgeBases(runtime2, origin),
7608
+ knowledgeBaseSelector
7609
+ );
7610
+ const serverLimit = await withSkillAccountKnowledgeClient(
7611
+ runtime2,
7612
+ origin,
7613
+ (client) => {
7614
+ if (!client.getServerLimit) return Promise.resolve(void 0);
7615
+ return client.getServerLimit(knowledgeBase.id);
7616
+ }
7617
+ );
7618
+ const limit = Math.min(
7619
+ DEFAULT_MAX_UPLOAD_BYTES,
7620
+ serverLimit ?? DEFAULT_MAX_UPLOAD_BYTES
7621
+ );
7622
+ if (file.size > limit) {
7623
+ throw new SkillError(
7624
+ "FILE_TOO_LARGE",
7625
+ `This file exceeds the ${limit}-byte Knowledge Base upload limit.`,
7626
+ {
7627
+ details: { limit_bytes: limit, size_bytes: file.size },
7628
+ exitCode: exitCodes.invalidArgument
7629
+ }
7630
+ );
7631
+ }
7632
+ const digest2 = await sha256(filePath);
7633
+ const receipt = await withSkillAccountKnowledgeClient(runtime2, origin, (client) => {
7634
+ if (!client.upload) return unavailableUpload();
7635
+ return client.upload({
7636
+ fileDigest: digest2,
7637
+ fileName: file.name,
7638
+ filePath,
7639
+ fileSize: file.size,
7640
+ idempotencyKey: runtime2.createId(),
7641
+ knowledgeBaseId: knowledgeBase.id,
7642
+ ...overwriteDocumentId ? { overwriteDocumentId } : {}
7643
+ });
7644
+ });
7645
+ const accepted = {
7646
+ event: "accepted",
7647
+ importId: receipt.importId,
7648
+ documentId: receipt.documentId,
7649
+ jobId: receipt.jobId
7650
+ };
7651
+ await runtime2.onEvent?.(accepted);
7652
+ if (waitMs === 0) {
7653
+ const detached = { ...accepted, event: "detached" };
7654
+ await runtime2.onEvent?.(detached);
7655
+ return [accepted, detached];
7656
+ }
7657
+ const terminal = await poll(
7658
+ runtime2,
7659
+ origin,
7660
+ knowledgeBase.id,
7661
+ receipt.importId,
7662
+ waitMs
7663
+ );
7664
+ return [accepted, terminal];
7665
+ }
7666
+ async function inspectSkillAccountUpload(runtime2, origin, importId, knowledgeBaseSelector) {
7667
+ const knowledgeBase = resolveKnowledgeBase(
7668
+ await listSkillAccountKnowledgeBases(runtime2, origin),
7669
+ knowledgeBaseSelector
7670
+ );
7671
+ return readStatus(runtime2, origin, knowledgeBase.id, importId);
7672
+ }
7673
+ async function poll(runtime2, origin, knowledgeBaseId, importId, waitMs) {
7674
+ const startedAt = runtime2.now().getTime();
7675
+ for (; ; ) {
7676
+ const event = await readStatus(runtime2, origin, knowledgeBaseId, importId);
7677
+ await runtime2.onEvent?.(event);
7678
+ if (event.event !== "processing") return event;
7679
+ if (runtime2.now().getTime() - startedAt >= waitMs) {
7680
+ const detached = { event: "detached", importId };
7681
+ await runtime2.onEvent?.(detached);
7682
+ return detached;
7683
+ }
7684
+ await runtime2.sleep(2e3);
7685
+ }
7686
+ }
7687
+ async function readStatus(runtime2, origin, knowledgeBaseId, importId) {
7688
+ const current = await withSkillAccountKnowledgeClient(runtime2, origin, (client) => {
7689
+ if (!client.getImportStatus) return unavailableUpload();
7690
+ return client.getImportStatus(knowledgeBaseId, importId);
7691
+ });
7692
+ return {
7693
+ event: current.searchable ? "completed" : current.state === "failed" ? "failed" : "processing",
7694
+ importId,
7695
+ ...current.failure ? { failure: current.failure } : {}
7696
+ };
7697
+ }
7698
+ async function inspectFile(filePath) {
7699
+ let metadata;
7700
+ try {
7701
+ metadata = await stat3(filePath);
7702
+ } catch {
7703
+ throw new SkillError("FILE_NOT_FOUND", "The upload file could not be found.", {
7704
+ exitCode: exitCodes.invalidArgument
7705
+ });
7706
+ }
7707
+ if (!metadata.isFile()) {
7708
+ throw new SkillError(
7709
+ "UNSUPPORTED_FILE_TYPE",
7710
+ "Upload accepts one regular file, not a directory or batch.",
7711
+ { exitCode: exitCodes.invalidArgument }
7712
+ );
7713
+ }
7714
+ return { name: path14.basename(filePath), size: metadata.size };
7715
+ }
7716
+ async function sha256(filePath) {
7717
+ const digest2 = createHash8("sha256");
7718
+ for await (const chunk of createReadStream(filePath)) digest2.update(chunk);
7719
+ return `sha256:${digest2.digest("hex")}`;
7720
+ }
7721
+ function unavailableUpload() {
7722
+ throw new SkillError("PLATFORM_UNAVAILABLE", "SAG upload is unavailable", {
7723
+ exitCode: exitCodes.serviceNotReady
7724
+ });
7725
+ }
7726
+
7727
+ // src/skill/knowledge-client.ts
7728
+ import { z as z14 } from "zod";
7729
+ import { openAsBlob } from "fs";
7730
+ var knowledgeBaseSchema = z14.strictObject({
7731
+ alias: z14.string().min(1).optional(),
7732
+ id: z14.string().min(1),
7733
+ name: z14.string().min(1)
7734
+ });
7735
+ var knowledgeBaseDirectorySchema = z14.strictObject({
7736
+ items: z14.array(knowledgeBaseSchema)
7737
+ });
7738
+ var documentSchema2 = z14.strictObject({
7739
+ id: z14.string().min(1),
7740
+ knowledge_base_id: z14.string().min(1),
7741
+ title: z14.string().min(1),
7742
+ version: z14.number().int().positive()
7743
+ });
7744
+ var documentDirectorySchema = z14.strictObject({
7745
+ items: z14.array(documentSchema2)
7746
+ });
7747
+ var deletedDocumentSchema = z14.strictObject({
7748
+ deleted_at: z14.string().datetime({ offset: true }),
7749
+ document_id: z14.string().min(1),
7750
+ recoverable: z14.literal(true),
7751
+ version: z14.number().int().positive()
7752
+ });
7753
+ var uploadStateSchema = z14.enum([
7754
+ "queued",
7755
+ "accepted",
7756
+ "processing",
7757
+ "completed",
7758
+ "failed"
7759
+ ]);
7760
+ var uploadResponseSchema = z14.strictObject({
7761
+ document_id: z14.string().min(1),
7762
+ import_id: z14.string().min(1),
7763
+ job_id: z14.string().min(1),
7764
+ state: uploadStateSchema
7765
+ });
7766
+ var uploadLimitSchema = z14.strictObject({
7767
+ max_upload_bytes: z14.number().int().positive()
7768
+ });
7769
+ var importStatusSchema = z14.strictObject({
7770
+ failure: z14.string().min(1).optional(),
7771
+ searchable: z14.boolean(),
7772
+ state: uploadStateSchema
7773
+ });
7774
+ var answerSchema = z14.strictObject({
7775
+ answer: z14.string().min(1),
7776
+ citations: z14.array(
7777
+ z14.strictObject({
7778
+ number: z14.number().int().positive(),
7779
+ knowledge_base_id: z14.string().min(1),
7780
+ document_id: z14.string().min(1),
7781
+ document_version_id: z14.string().min(1),
7782
+ excerpt: z14.string(),
7783
+ location: z14.string().min(1).optional()
7784
+ })
7785
+ ),
7786
+ search_completeness: z14.enum(["complete", "partial"]),
7787
+ search_partial_reasons: z14.array(z14.string().min(1))
7788
+ });
7789
+ var problemSchema2 = z14.object({
7790
+ code: z14.string().min(1),
7791
+ data: z14.unknown().optional()
7792
+ }).passthrough();
7793
+ var duplicateImportDetailsSchema = z14.object({
7794
+ conflict_type: z14.enum([
7795
+ "same_filename_same_checksum",
7796
+ "same_filename_changed_checksum"
7797
+ ]),
7798
+ existing_document_id: z14.string().min(1),
7799
+ existing_import_id: z14.string().min(1),
7800
+ existing_filename: z14.string().min(1),
7801
+ current_filename: z14.string().min(1),
7802
+ checksum_sha256: z14.string().min(1),
7803
+ existing_state: z14.string().min(1),
7804
+ overwrite_allowed: z14.boolean()
7805
+ });
7806
+ function httpError(status2, problemCode, problemData) {
7807
+ const normalizedProblemCode = problemCode.toLowerCase();
7808
+ const details = { problemCode };
7809
+ if (normalizedProblemCode === "invalid_grant" || normalizedProblemCode === "expired_token") {
7810
+ return new SkillError("AUTH_EXPIRED", "SAG account authorization has expired", {
7811
+ exitCode: exitCodes.authRequired,
7812
+ ...details
7813
+ });
7814
+ }
7815
+ if (normalizedProblemCode === "document_title_conflict") {
7816
+ return new SkillError("DOCUMENT_TITLE_CONFLICT", "The document title conflicts", {
7817
+ exitCode: exitCodes.configConflict,
7818
+ ...details
7819
+ });
7820
+ }
7821
+ if (normalizedProblemCode === "document_import_duplicate") {
7822
+ const details2 = duplicateImportDetailsSchema.safeParse(problemData);
7823
+ return new SkillError(
7824
+ "DOCUMENT_IMPORT_DUPLICATE",
7825
+ "A file with the same name already exists in this Knowledge Base",
7826
+ {
7827
+ ...details2.success ? { details: details2.data } : {},
7828
+ exitCode: exitCodes.configConflict,
7829
+ problemCode
7830
+ }
7831
+ );
7832
+ }
7833
+ if (normalizedProblemCode === "stale_version") {
7834
+ return new SkillError("STALE_VERSION", "The document version is stale", {
7835
+ exitCode: exitCodes.configConflict,
7836
+ ...details
7837
+ });
7838
+ }
7839
+ if (status2 === 401) {
7840
+ return new SkillError("AUTH_REQUIRED", "Skill authorization is required", {
7841
+ exitCode: exitCodes.authRequired,
7842
+ ...details
7843
+ });
7844
+ }
7845
+ if (status2 === 403) {
7846
+ return new SkillError("PERMISSION_DENIED", "Skill authorization was denied", {
7847
+ exitCode: exitCodes.permissionDenied,
7848
+ ...details
7849
+ });
7850
+ }
7851
+ if (status2 === 404) {
7852
+ return new SkillError(
7853
+ "DOCUMENT_NOT_FOUND",
7854
+ "Skill Knowledge resource was not found",
7855
+ {
7856
+ exitCode: exitCodes.resourceNotFound,
7857
+ ...details
7858
+ }
7859
+ );
7860
+ }
7861
+ if (status2 === 409) {
7862
+ return new SkillError("CONFIG_CONFLICT", "Skill Knowledge request conflicts", {
7863
+ exitCode: exitCodes.configConflict,
7864
+ ...details
7865
+ });
7866
+ }
7867
+ if (status2 === 429) {
7868
+ return new SkillError(
7869
+ "RATE_LIMITED",
7870
+ "Skill Knowledge service rate limit reached",
7871
+ {
7872
+ exitCode: exitCodes.networkUnreachable,
7873
+ ...details
7874
+ }
7875
+ );
7876
+ }
7877
+ if (status2 >= 500) {
7878
+ return new SkillError(
7879
+ "PLATFORM_UNAVAILABLE",
7880
+ "Skill Knowledge service is unavailable",
7881
+ {
7882
+ exitCode: exitCodes.serviceNotReady,
7883
+ ...details
7884
+ }
7885
+ );
7886
+ }
7887
+ return new SkillError(
7888
+ "INVALID_RESPONSE",
7889
+ "Skill Knowledge service returned an incompatible response",
7890
+ {
7891
+ exitCode: exitCodes.internalError,
7892
+ ...details
7893
+ }
7894
+ );
7895
+ }
7896
+ function throwIfAborted(signal) {
7897
+ if (!signal?.aborted) return;
7898
+ throw signal.reason instanceof Error ? signal.reason : new DOMException("The request was interrupted.", "AbortError");
7899
+ }
7900
+ var SkillKnowledgeHttpClient = class {
7901
+ #accessToken;
7902
+ #fetch;
7903
+ #origin;
7904
+ #timeoutMs;
7905
+ constructor(options) {
7906
+ try {
7907
+ this.#origin = normalizeSkillOrigin(options.origin);
7908
+ } catch {
7909
+ throw new SkillError("CONFIG_CONFLICT", "SAG address is invalid", {
7910
+ exitCode: exitCodes.configConflict
7911
+ });
7912
+ }
7913
+ if (!options.accessToken) {
7914
+ throw new SkillError("AUTH_REQUIRED", "Skill authorization is required", {
7915
+ exitCode: exitCodes.authRequired
7916
+ });
7917
+ }
7918
+ this.#accessToken = options.accessToken;
7919
+ this.#fetch = options.fetchImplementation ?? fetch;
7920
+ this.#timeoutMs = options.timeoutMs ?? 15e3;
7921
+ }
7922
+ async ask(question, knowledgeBaseId) {
7923
+ const answer = this.#parse(
7924
+ answerSchema,
7925
+ await this.#request("/api/v1/integrations/skill/v1/questions", {
7926
+ body: {
7927
+ question,
7928
+ ...knowledgeBaseId ? { knowledge_base_id: knowledgeBaseId } : {}
7929
+ },
7930
+ method: "POST"
7931
+ })
7932
+ );
7933
+ return {
7934
+ answer: answer.answer,
7935
+ citations: answer.citations.map((citation) => ({
7936
+ number: citation.number,
7937
+ knowledgeBaseId: citation.knowledge_base_id,
7938
+ documentId: citation.document_id,
7939
+ documentVersionId: citation.document_version_id,
7940
+ excerpt: citation.excerpt,
7941
+ ...citation.location ? { location: citation.location } : {}
7942
+ })),
7943
+ completeness: answer.search_completeness,
7944
+ partialReasons: answer.search_partial_reasons
7945
+ };
7946
+ }
7947
+ async listKnowledgeBases() {
7948
+ const parsed = await this.#request(
7949
+ "/api/v1/integrations/skill/v1/knowledge-bases",
7950
+ {
7951
+ method: "GET"
7952
+ }
7953
+ );
7954
+ return this.#parse(knowledgeBaseDirectorySchema, parsed).items.map(
7955
+ (knowledgeBase) => ({
7956
+ id: knowledgeBase.id,
7957
+ name: knowledgeBase.name,
7958
+ ...knowledgeBase.alias ? { alias: knowledgeBase.alias } : {}
7959
+ })
7960
+ );
7961
+ }
7962
+ async listDocuments(knowledgeBaseId) {
7963
+ const parsed = await this.#request(
7964
+ `/api/v1/integrations/skill/v1/knowledge-bases/${encodeURIComponent(knowledgeBaseId)}/documents`,
7965
+ { method: "GET" }
7966
+ );
7967
+ return this.#parse(documentDirectorySchema, parsed).items.map((document) => ({
7968
+ id: document.id,
7969
+ knowledgeBaseId: document.knowledge_base_id,
7970
+ title: document.title,
7971
+ version: document.version
7972
+ }));
7973
+ }
7974
+ async renameDocument(input2) {
7975
+ const document = this.#parse(
7976
+ documentSchema2,
7977
+ await this.#request(this.#mutationPath(input2), {
7978
+ body: { title: input2.title },
7979
+ headers: this.#mutationHeaders(input2),
7980
+ method: "PATCH"
7981
+ })
7982
+ );
7983
+ if (document.id !== input2.documentId || document.knowledge_base_id !== input2.knowledgeBaseId) {
7984
+ throw new SkillError(
7985
+ "INVALID_RESPONSE",
7986
+ "Skill Knowledge service returned a different document",
7987
+ {
7988
+ exitCode: exitCodes.internalError
7989
+ }
7990
+ );
7991
+ }
7992
+ return {
7993
+ id: document.id,
7994
+ knowledgeBaseId: document.knowledge_base_id,
7995
+ title: document.title,
7996
+ version: document.version
7997
+ };
7998
+ }
7999
+ async deleteDocument(input2) {
8000
+ const deleted = this.#parse(
8001
+ deletedDocumentSchema,
8002
+ await this.#request(this.#mutationPath(input2), {
8003
+ headers: this.#mutationHeaders(input2),
8004
+ method: "DELETE"
8005
+ })
8006
+ );
8007
+ if (deleted.document_id !== input2.documentId) {
8008
+ throw new SkillError(
8009
+ "INVALID_RESPONSE",
8010
+ "Skill Knowledge service returned a different document",
8011
+ {
8012
+ exitCode: exitCodes.internalError
8013
+ }
8014
+ );
8015
+ }
8016
+ return {
8017
+ deletedAt: deleted.deleted_at,
8018
+ documentId: deleted.document_id,
8019
+ recoverable: deleted.recoverable,
8020
+ version: deleted.version
8021
+ };
8022
+ }
8023
+ async getServerLimit(knowledgeBaseId, signal) {
8024
+ return this.#parse(
8025
+ uploadLimitSchema,
8026
+ await this.#request(
8027
+ `/api/v1/integrations/skill/v1/knowledge-bases/${encodeURIComponent(knowledgeBaseId)}/upload-limit`,
8028
+ { method: "GET", ...signal ? { signal } : {} }
8029
+ )
8030
+ ).max_upload_bytes;
8031
+ }
8032
+ async upload(input2, signal) {
8033
+ throwIfAborted(signal);
8034
+ const form = new FormData();
8035
+ form.set("file", await openAsBlob(input2.filePath), input2.fileName);
8036
+ if (input2.overwriteDocumentId) {
8037
+ form.set("overwrite_document_id", input2.overwriteDocumentId);
8038
+ }
8039
+ const payload = await this.#multipartRequest(
8040
+ `/api/v1/integrations/skill/v1/knowledge-bases/${encodeURIComponent(input2.knowledgeBaseId)}/documents`,
8041
+ form,
8042
+ input2.idempotencyKey,
8043
+ signal
8044
+ );
8045
+ const response = this.#parse(uploadResponseSchema, payload);
8046
+ return {
8047
+ documentId: response.document_id,
8048
+ importId: response.import_id,
8049
+ jobId: response.job_id,
8050
+ state: response.state
8051
+ };
8052
+ }
8053
+ async getImportStatus(knowledgeBaseId, importId, signal) {
8054
+ const status2 = this.#parse(
8055
+ importStatusSchema,
8056
+ await this.#request(
8057
+ `/api/v1/integrations/skill/v1/knowledge-bases/${encodeURIComponent(knowledgeBaseId)}/imports/${encodeURIComponent(importId)}`,
8058
+ { method: "GET", ...signal ? { signal } : {} }
8059
+ )
8060
+ );
8061
+ return {
8062
+ searchable: status2.searchable,
8063
+ state: status2.state,
8064
+ ...status2.failure ? { failure: status2.failure } : {}
8065
+ };
8066
+ }
8067
+ async #request(path16, options) {
8068
+ let response;
8069
+ try {
8070
+ throwIfAborted(options.signal);
8071
+ response = await this.#fetch(new URL(path16, this.#origin), {
8072
+ headers: {
8073
+ Accept: "application/json",
8074
+ Authorization: `Bearer ${this.#accessToken}`,
8075
+ "Cache-Control": "no-store",
8076
+ ...options.headers,
8077
+ ...options.body === void 0 ? {} : { "Content-Type": "application/json" }
8078
+ },
8079
+ method: options.method,
8080
+ redirect: "error",
8081
+ signal: this.#requestSignal(options.signal),
8082
+ ...options.body === void 0 ? {} : { body: JSON.stringify(options.body) }
8083
+ });
8084
+ } catch (error) {
8085
+ if (options.signal?.aborted) throw error;
8086
+ throw new SkillError(
8087
+ "NETWORK_UNREACHABLE",
8088
+ "Cannot reach the Skill Knowledge service",
8089
+ {
8090
+ exitCode: exitCodes.networkUnreachable
8091
+ }
8092
+ );
8093
+ }
8094
+ if (response.redirected || response.status >= 300 && response.status < 400) {
8095
+ throw new SkillError(
8096
+ "INVALID_RESPONSE",
8097
+ "Skill Knowledge service redirect was rejected",
8098
+ {
8099
+ exitCode: exitCodes.internalError
8100
+ }
8101
+ );
8102
+ }
8103
+ const payload = await response.json().catch(() => void 0);
8104
+ if (!response.ok) {
8105
+ const problem = problemSchema2.safeParse(payload);
8106
+ throw httpError(
8107
+ response.status,
8108
+ problem.success ? problem.data.code : "unknown",
8109
+ problem.success ? problem.data.data : void 0
8110
+ );
8111
+ }
8112
+ return payload;
8113
+ }
8114
+ #mutationPath(input2) {
8115
+ return `/api/v1/integrations/skill/v1/knowledge-bases/${encodeURIComponent(input2.knowledgeBaseId)}/documents/${encodeURIComponent(input2.documentId)}`;
8116
+ }
8117
+ #mutationHeaders(input2) {
8118
+ return {
8119
+ "Idempotency-Key": input2.idempotencyKey,
8120
+ "If-Match": `"document.${input2.documentId}.v${input2.expectedVersion}"`
8121
+ };
8122
+ }
8123
+ async #multipartRequest(path16, body, idempotencyKey, signal) {
8124
+ let response;
8125
+ try {
8126
+ throwIfAborted(signal);
8127
+ response = await this.#fetch(new URL(path16, this.#origin), {
8128
+ headers: {
8129
+ Accept: "application/json",
8130
+ Authorization: `Bearer ${this.#accessToken}`,
8131
+ "Cache-Control": "no-store",
8132
+ "Idempotency-Key": idempotencyKey
8133
+ },
8134
+ method: "POST",
8135
+ redirect: "error",
8136
+ signal: this.#requestSignal(signal),
8137
+ body
8138
+ });
8139
+ } catch (error) {
8140
+ if (signal?.aborted) throw error;
8141
+ throw new SkillError(
8142
+ "NETWORK_UNREACHABLE",
8143
+ "Cannot reach the Skill Knowledge service",
8144
+ {
8145
+ exitCode: exitCodes.networkUnreachable
8146
+ }
8147
+ );
8148
+ }
8149
+ if (response.redirected || response.status >= 300 && response.status < 400) {
8150
+ throw new SkillError(
8151
+ "INVALID_RESPONSE",
8152
+ "Skill Knowledge service redirect was rejected",
8153
+ {
8154
+ exitCode: exitCodes.internalError
8155
+ }
8156
+ );
8157
+ }
8158
+ const payload = await response.json().catch(() => void 0);
8159
+ if (!response.ok) {
8160
+ const problem = problemSchema2.safeParse(payload);
8161
+ throw httpError(
8162
+ response.status,
8163
+ problem.success ? problem.data.code : "unknown",
8164
+ problem.success ? problem.data.data : void 0
8165
+ );
8166
+ }
8167
+ return payload;
8168
+ }
8169
+ #requestSignal(signal) {
8170
+ const timeout = AbortSignal.timeout(this.#timeoutMs);
8171
+ return signal ? AbortSignal.any([signal, timeout]) : timeout;
8172
+ }
8173
+ #parse(schema, payload) {
8174
+ const parsed = schema.safeParse(payload);
8175
+ if (parsed.success) return parsed.data;
8176
+ throw new SkillError(
8177
+ "INVALID_RESPONSE",
8178
+ "Skill Knowledge service returned an incompatible response",
8179
+ {
8180
+ exitCode: exitCodes.internalError
8181
+ }
8182
+ );
8183
+ }
8184
+ };
8185
+
8186
+ // src/program.ts
8187
+ function parseInteger(value) {
8188
+ return Number.parseInt(value, 10);
8189
+ }
8190
+ function parsePositiveInteger(value) {
8191
+ const parsed = Number.parseInt(value, 10);
8192
+ if (!Number.isSafeInteger(parsed) || parsed <= 0) {
8193
+ throw new CliError("INVALID_ARGUMENT", "\u53C2\u6570\u5FC5\u987B\u662F\u6B63\u6574\u6570", {
8194
+ exitCode: exitCodes.invalidArgument
8195
+ });
8196
+ }
8197
+ return parsed;
8198
+ }
8199
+ function parseNonNegativeInteger(value) {
8200
+ const parsed = Number.parseInt(value, 10);
8201
+ if (!Number.isSafeInteger(parsed) || parsed < 0) {
8202
+ throw new CliError("INVALID_ARGUMENT", "\u53C2\u6570\u5FC5\u987B\u662F\u975E\u8D1F\u6574\u6570", {
8203
+ exitCode: exitCodes.invalidArgument
8204
+ });
8205
+ }
8206
+ return parsed;
8207
+ }
8208
+ function parseAgent(value) {
8209
+ if (value === "codex" || value === "claude-code") {
8210
+ return value;
8211
+ }
8212
+ throw new CliError("INVALID_ARGUMENT", `Unsupported Agent: ${value}`, {
8213
+ exitCode: exitCodes.invalidArgument,
8214
+ hint: "Choose codex or claude-code."
8215
+ });
8216
+ }
8217
+ function parseEnterpriseAgent(value) {
8218
+ if (value === "codex" || value === "claude-code" || value === "workbuddy") {
8219
+ return value;
8220
+ }
8221
+ throw new CliError("INVALID_ARGUMENT", `\u4E0D\u652F\u6301\u7684 Agent\uFF1A${value}`, {
8222
+ exitCode: exitCodes.invalidArgument,
8223
+ hint: "\u8BF7\u9009\u62E9 codex\u3001claude-code \u6216 workbuddy\u3002"
8224
+ });
8225
+ }
8226
+ function parseCredentialStore(value) {
8227
+ if (value === "keychain" || value === "file") return value;
8228
+ throw new CliError("INVALID_ARGUMENT", `\u4E0D\u652F\u6301\u7684\u51ED\u636E\u5B58\u50A8\uFF1A${value}`, {
8229
+ exitCode: exitCodes.invalidArgument,
8230
+ hint: "\u8BF7\u9009\u62E9 keychain \u6216 file\u3002"
8231
+ });
8232
+ }
8233
+ function resolveEnterpriseSkillAgent(positional, option) {
8234
+ if (positional && option && positional !== option) {
8235
+ throw new CliError(
8236
+ "INVALID_ARGUMENT",
8237
+ "\u4F4D\u7F6E\u53C2\u6570 Agent \u4E0E --agent \u6307\u5B9A\u7684 Agent \u4E0D\u4E00\u81F4",
8238
+ { exitCode: exitCodes.invalidArgument }
8239
+ );
8240
+ }
8241
+ const agent = option ?? positional;
8242
+ if (!agent) {
8243
+ throw new CliError("INVALID_ARGUMENT", "\u8BF7\u4F7F\u7528 --agent \u6307\u5B9A Agent", {
8244
+ exitCode: exitCodes.invalidArgument,
8245
+ hint: "\u8BF7\u9009\u62E9 codex\u3001claude-code \u6216 workbuddy\u3002"
8246
+ });
8247
+ }
8248
+ return agent;
8249
+ }
8250
+ function parseMcpToolArguments(value) {
8251
+ try {
8252
+ const parsed = JSON.parse(value);
8253
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
8254
+ throw new Error("not an object");
8255
+ }
8256
+ return parsed;
8257
+ } catch (cause) {
8258
+ throw new CliError("INVALID_ARGUMENT", "--arguments-json \u5FC5\u987B\u662F\u4E00\u4E2A JSON \u5BF9\u8C61", {
8259
+ exitCode: exitCodes.invalidArgument,
8260
+ cause
8261
+ });
8262
+ }
8263
+ }
8264
+ function collect(value, previous) {
8265
+ return [...previous, value];
8266
+ }
8267
+ function commandOptions2(program) {
8268
+ return program.opts();
8269
+ }
8270
+ var skillWriters = /* @__PURE__ */ new WeakMap();
8271
+ function skillOperation(program) {
8272
+ const [command, subcommand, action] = program.args;
8273
+ if (command === "auth" || command === "skill" && subcommand === "auth") {
8274
+ return "auth";
8275
+ }
8276
+ if (command === "skill" && subcommand === "knowledge") {
8277
+ if (action === "upload" || action === "upload-status") return "upload";
8278
+ if (action === "ask") return "search";
8279
+ return "document";
8280
+ }
8281
+ if (command === "search") return "search";
8282
+ if (command === "document" && (subcommand === "upload" || subcommand === "upload-status")) {
8283
+ return "upload";
8284
+ }
8285
+ if (command === "mcp" || command === "agent") return "install";
8286
+ if (command === "document" || command === "knowledge") return "document";
8287
+ return "install";
8288
+ }
8289
+ function skillData(program, data) {
8290
+ const base = data && typeof data === "object" && !Array.isArray(data) ? data : Array.isArray(data) ? { items: data } : { value: data };
8291
+ if (program.args[0] === "knowledge") {
8292
+ return { resource: "knowledge_base", ...base };
8293
+ }
8294
+ return base;
8295
+ }
8296
+ function commandOutputIsNdjson(arguments_) {
8297
+ return arguments_.some(
8298
+ (argument, index) => argument === "--output=ndjson" || argument === "--output" && arguments_[index + 1] === "ndjson"
8299
+ );
8300
+ }
8301
+ function skillOperationId() {
8302
+ return `cli-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
8303
+ }
8304
+ function skillEvent(program, event, data, error, operationId = skillOperationId()) {
8305
+ return {
8306
+ schema: "sag.skill.ndjson.v1",
8307
+ operation: skillOperation(program),
8308
+ event,
8309
+ operation_id: operationId,
8310
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
8311
+ ...data ? { data } : {},
8312
+ ...error ? { error } : {}
8313
+ };
8314
+ }
8315
+ function emit(program, dependencies2, data) {
8316
+ const options = commandOptions2(program);
8317
+ if (options.output === "ndjson") {
8318
+ skillWriters.get(program)?.complete(skillEvent(program, "completed", skillData(program, data)));
8319
+ return;
8320
+ }
8321
+ dependencies2.writeStdout(
8322
+ options.json ? renderJson(success3(data)) : renderHuman(data, options.quiet ?? false)
8323
+ );
8324
+ }
8325
+ async function runtime(program, dependencies2) {
8326
+ const options = commandOptions2(program);
8327
+ return createRuntimeContext({
8328
+ options: {
8329
+ ...options.url ? { url: options.url } : {},
8330
+ ...options.profile ? { profile: options.profile } : {}
8331
+ },
8332
+ environment: dependencies2.environment,
8333
+ configStore: dependencies2.configStore,
8334
+ credentialStore: dependencies2.credentialStore,
8335
+ createClient: dependencies2.createClient
8336
+ });
8337
+ }
8338
+ function localMcpRuntime(dependencies2) {
8339
+ if (!dependencies2.localMcp) {
8340
+ throw new CliError("DEPENDENCY_MISSING", "Local MCP runtime is unavailable", {
8341
+ exitCode: exitCodes.dependencyMissing
8342
+ });
8343
+ }
8344
+ return dependencies2.localMcp;
8345
+ }
8346
+ function enterpriseMcpRuntime(dependencies2) {
8347
+ if (!dependencies2.enterpriseMcp) {
8348
+ throw new CliError("DEPENDENCY_MISSING", "\u4F01\u4E1A MCP \u8FD0\u884C\u73AF\u5883\u4E0D\u53EF\u7528", {
8349
+ exitCode: exitCodes.dependencyMissing
8350
+ });
8351
+ }
8352
+ return dependencies2.enterpriseMcp;
8353
+ }
8354
+ function enterpriseMcpCommandRuntime(dependencies2, timeoutMs) {
8355
+ return {
8356
+ ...enterpriseMcpRuntime(dependencies2),
8357
+ cliVersion: dependencies2.version,
8358
+ timeoutMs
8359
+ };
8360
+ }
8361
+ function skillInstallerRuntime(dependencies2) {
8362
+ if (!dependencies2.skill) {
8363
+ throw new CliError(
8364
+ "DEPENDENCY_MISSING",
8365
+ "SAG Knowledge Skill runtime is unavailable",
8366
+ {
8367
+ exitCode: exitCodes.dependencyMissing
8368
+ }
8369
+ );
8370
+ }
8371
+ return dependencies2.skill;
8372
+ }
8373
+ function skillAccountAuthRuntime(dependencies2) {
8374
+ const runtime2 = dependencies2.skillAccount;
8375
+ if (!runtime2) {
8376
+ throw new CliError(
8377
+ "DEPENDENCY_MISSING",
8378
+ "SAG account authorization runtime is unavailable",
8379
+ { exitCode: exitCodes.dependencyMissing }
8380
+ );
8381
+ }
8382
+ return runtime2;
8383
+ }
8384
+ function skillAccountKnowledgeRuntime(dependencies2) {
8385
+ const account = dependencies2.skillAccount;
8386
+ if (!account) {
8387
+ throw new CliError(
8388
+ "DEPENDENCY_MISSING",
8389
+ "SAG account Knowledge runtime is unavailable",
8390
+ { exitCode: exitCodes.dependencyMissing }
8391
+ );
8392
+ }
8393
+ return {
8394
+ auth: skillAccountAuthRuntime(dependencies2),
8395
+ createId: randomUUID7,
8396
+ config: account.config,
8397
+ createClient: dependencies2.createSkillKnowledgeClient ?? ((options) => new SkillKnowledgeHttpClient(options))
8398
+ };
8399
+ }
8400
+ function skillAccountUploadRuntime(dependencies2) {
8401
+ return {
8402
+ ...skillAccountKnowledgeRuntime(dependencies2),
8403
+ now: () => /* @__PURE__ */ new Date(),
8404
+ sleep: async (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds))
8405
+ };
8406
+ }
8407
+ function emitSkillAccountUpload(program, dependencies2, events) {
8408
+ if (commandOptions2(program).output !== "ndjson") {
8409
+ emit(program, dependencies2, { events });
8410
+ return;
8411
+ }
8412
+ const operationId = skillOperationId();
8413
+ const writer = skillWriters.get(program);
8414
+ events.forEach((event, index) => {
8415
+ const data = {
8416
+ import_id: event.importId,
8417
+ ...event.documentId ? { document_id: event.documentId } : {},
8418
+ ...event.jobId ? { job_id: event.jobId } : {},
8419
+ ...event.failure ? { failure: event.failure } : {}
8420
+ };
8421
+ const frame = skillEvent(program, event.event, data, void 0, operationId);
8422
+ if (index === events.length - 1) writer?.complete(frame);
8423
+ else writer?.emit(frame);
8424
+ });
8425
+ }
8426
+ function emitSkillAccountUploadStatus(program, dependencies2, status2) {
8427
+ if (commandOptions2(program).output !== "ndjson") {
8428
+ emitSkillAccountUpload(program, dependencies2, [status2]);
8429
+ return;
8430
+ }
8431
+ skillWriters.get(program)?.complete(
8432
+ skillEvent(program, "completed", {
8433
+ import_id: status2.importId,
8434
+ state: status2.event,
8435
+ ...status2.documentId ? { document_id: status2.documentId } : {},
8436
+ ...status2.jobId ? { job_id: status2.jobId } : {},
8437
+ ...status2.failure ? { failure: status2.failure } : {}
8438
+ })
8439
+ );
8440
+ }
8441
+ async function resolveSkillOrigin(dependencies2, explicitOrigin, promptWhenMissing = false) {
8442
+ if (explicitOrigin) return normalizeSkillOrigin(explicitOrigin);
8443
+ const runtime2 = dependencies2.skillAccount;
8444
+ if (!runtime2) {
8445
+ throw new CliError(
8446
+ "DEPENDENCY_MISSING",
8447
+ "SAG account authorization runtime is unavailable",
8448
+ { exitCode: exitCodes.dependencyMissing }
8449
+ );
8450
+ }
8451
+ const current = (await runtime2.config.load()).currentOrigin;
8452
+ if (current) return normalizeSkillOrigin(current);
8453
+ if (promptWhenMissing && dependencies2.promptSkillOrigin) {
8454
+ return normalizeSkillOrigin(await dependencies2.promptSkillOrigin());
8455
+ }
8456
+ throw new SkillError("AUTH_REQUIRED", "SAG address is required", {
8457
+ exitCode: exitCodes.authRequired,
8458
+ details: { required_option: "--origin" }
8459
+ });
8460
+ }
8461
+ var ReportedSkillTargetFailure = class extends Error {
8462
+ constructor(exitCode) {
8463
+ super("Skill target operation completed with errors");
8464
+ this.exitCode = exitCode;
8465
+ }
8466
+ exitCode;
8467
+ };
8468
+ function emitSkillTargets(program, dependencies2, report, options = {}) {
8469
+ if (commandOptions2(program).output !== "ndjson") {
8470
+ emit(program, dependencies2, report);
8471
+ return;
8472
+ }
8473
+ const writer = skillWriters.get(program);
8474
+ const operationId = options.operationId ?? skillOperationId();
8475
+ for (const target of report.targets) {
8476
+ const failed3 = target.status === "failed" || target.status === "conflict";
8477
+ writer?.emit(
8478
+ skillEvent(
8479
+ program,
8480
+ failed3 ? "target_failed" : "target_completed",
8481
+ {
8482
+ agent: target.agent,
8483
+ target: target.target,
8484
+ status: target.status,
8485
+ ...target.version ? { version: target.version } : {},
8486
+ ...target.package ? { package: target.package } : {},
8487
+ ...target.code ? { code: target.code } : {}
8488
+ },
8489
+ void 0,
8490
+ operationId
8491
+ )
8492
+ );
8493
+ }
8494
+ const terminal = options.terminal ?? true;
8495
+ const summary = {
8496
+ package: report.package,
8497
+ version: report.version,
8498
+ target_count: report.targets.length
8499
+ };
8500
+ if (!terminal && (!report.completedWithErrors || options.progressOnErrors)) {
8501
+ writer?.emit(
8502
+ skillEvent(
8503
+ program,
8504
+ options.progressEvent ?? "installation_completed",
8505
+ options.progressData ?? summary,
8506
+ void 0,
8507
+ operationId
8508
+ )
8509
+ );
8510
+ return;
8511
+ }
8512
+ writer?.complete(
8513
+ skillEvent(
8514
+ program,
8515
+ report.completedWithErrors ? "completed_with_errors" : "completed",
8516
+ options.terminalData ?? summary,
8517
+ void 0,
8518
+ operationId
8519
+ )
8520
+ );
8521
+ }
8522
+ async function executeSkillTargets(program, dependencies2, report) {
8523
+ const resolved = await report;
8524
+ emitSkillTargets(program, dependencies2, resolved);
8525
+ if (resolved.completedWithErrors) {
8526
+ const conflict6 = resolved.targets.some((target) => target.status === "conflict");
8527
+ throw new ReportedSkillTargetFailure(
8528
+ conflict6 ? exitCodes.configConflict : exitCodes.internalError
8529
+ );
8530
+ }
8531
+ }
8532
+ function rejectEnterpriseProfile(program) {
8533
+ if (!commandOptions2(program).profile) return;
8534
+ throw new CliError("INVALID_ARGUMENT", "\u4F01\u4E1A MCP \u547D\u4EE4\u4E0D\u4F7F\u7528 Profile", {
8535
+ exitCode: exitCodes.invalidArgument,
8536
+ hint: "\u8BF7\u79FB\u9664 --profile\uFF1B\u4E2A\u4EBA Profile \u53EA\u7528\u4E8E sag mcp test \u548C sag agent\u3002"
8537
+ });
8538
+ }
8539
+ async function selectedProfileName(program, dependencies2) {
8540
+ const options = commandOptions2(program);
8541
+ if (options.profile) {
8542
+ await dependencies2.configStore.getProfile(options.profile);
8543
+ return options.profile;
8544
+ }
8545
+ return (await dependencies2.configStore.load()).currentProfile ?? "local";
8546
+ }
8547
+ async function selectedServerName(program, dependencies2, explicitName) {
8548
+ const profile = await selectedProfileName(program, dependencies2);
8549
+ return {
8550
+ profile,
8551
+ serverName: explicitName ?? `sag-knowledge-${profile}`
8552
+ };
8553
+ }
8554
+ function addProfileCommands(program, dependencies2) {
8555
+ const profile = program.command("profile").description("Manage SAG profiles");
8556
+ profile.command("add").argument("<name>").argument("<url>").action(async (name, url) => {
8557
+ emit(program, dependencies2, await dependencies2.configStore.addProfile(name, url));
8558
+ });
8559
+ profile.command("list").action(async () => {
8560
+ emit(program, dependencies2, await dependencies2.configStore.listProfiles());
8561
+ });
8562
+ profile.command("use").argument("<name>").action(async (name) => {
8563
+ emit(program, dependencies2, await dependencies2.configStore.useProfile(name));
8564
+ });
8565
+ profile.command("show").argument("[name]").action(async (name) => {
8566
+ emit(program, dependencies2, await dependencies2.configStore.getProfile(name));
8567
+ });
8568
+ profile.command("remove").argument("<name>").action(async (name) => {
8569
+ if (!commandOptions2(program).yes && !await dependencies2.confirm(`Remove profile ${name}?`)) {
8570
+ emit(program, dependencies2, { removed: null, cancelled: true });
8571
+ return;
8572
+ }
8573
+ await dependencies2.configStore.removeProfile(name);
8574
+ emit(program, dependencies2, { removed: name });
8575
+ });
8576
+ }
8577
+ function addAuthCommands(program, dependencies2) {
8578
+ const auth = program.command("auth").description("Manage SAG credentials");
8579
+ auth.command("login").option("--name <name>", "SAG user name for the existing local login").action(async (options) => {
8580
+ const context = await runtime(program, dependencies2);
8581
+ const result = await login({
8582
+ credentialRef: context.credentialRef,
8583
+ ...context.connection.environmentToken ? { environmentToken: context.connection.environmentToken } : {},
8584
+ ...options.name ? { name: options.name } : {},
8585
+ promptName: dependencies2.promptName,
8586
+ authenticate: (name) => dependencies2.createClient({ origin: context.connection.url }).login(name),
8587
+ validate: async (token) => dependencies2.createClient({ origin: context.connection.url, token }).me(),
8588
+ store: dependencies2.credentialStore
8589
+ });
8590
+ emit(program, dependencies2, result);
8591
+ });
8592
+ auth.command("status").action(async () => {
8593
+ const context = await runtime(program, dependencies2);
8594
+ const result = await status({
8595
+ credentialRef: context.credentialRef,
8596
+ ...context.connection.environmentToken ? { environmentToken: context.connection.environmentToken } : {},
8597
+ validate: async (token) => dependencies2.createClient({ origin: context.connection.url, token }).me(),
8598
+ store: dependencies2.credentialStore
6395
8599
  });
6396
8600
  emit(program, dependencies2, result);
6397
8601
  });
@@ -6751,7 +8955,7 @@ function addLocalMcpCommands(program, dependencies2) {
6751
8955
  }
6752
8956
  return dependencies2.selectMcpServers(servers);
6753
8957
  },
6754
- selectAgents: async (agents) => dependencies2.selectAgents ? dependencies2.selectAgents(agents) : [],
8958
+ selectAgents: async (agents2) => dependencies2.selectAgents ? dependencies2.selectAgents(agents2) : [],
6755
8959
  selectCollisionAction: async (collision) => {
6756
8960
  if (!dependencies2.selectCollisionAction) {
6757
8961
  throw new CliError("DEPENDENCY_MISSING", "\u65E0\u6CD5\u5904\u7406\u540C\u540D MCP \u8FDE\u63A5", {
@@ -6818,15 +9022,258 @@ function addAgentCommands(program, dependencies2) {
6818
9022
  );
6819
9023
  });
6820
9024
  }
9025
+ function addSkillCommands(program, dependencies2) {
9026
+ const skill = program.command("skill").description("Install and manage the SAG Knowledge Skill");
9027
+ const selectedAgents = (value, previous) => [
9028
+ ...previous,
9029
+ parseEnterpriseAgent(value)
9030
+ ];
9031
+ const addInstall = (name, description) => {
9032
+ const command = skill.command(name).description(description).option(
9033
+ "--agent <agent>",
9034
+ "Target codex, claude-code, or workbuddy",
9035
+ selectedAgents,
9036
+ []
9037
+ );
9038
+ command.option("--origin <origin>", "Full SAG HTTP or HTTPS Origin");
9039
+ command.action(async (options) => {
9040
+ if (options.origin) {
9041
+ const account = dependencies2.skillAccount;
9042
+ if (!account) {
9043
+ throw new CliError(
9044
+ "DEPENDENCY_MISSING",
9045
+ "SAG account configuration is unavailable",
9046
+ { exitCode: exitCodes.dependencyMissing }
9047
+ );
9048
+ }
9049
+ await account.config.setCurrentOrigin(options.origin);
9050
+ }
9051
+ await executeSkillTargets(
9052
+ program,
9053
+ dependencies2,
9054
+ installSkillTargets(skillInstallerRuntime(dependencies2), {
9055
+ ...options.agent.length ? { agents: options.agent } : {},
9056
+ yes: commandOptions2(program).yes ?? false,
9057
+ update: name === "update"
9058
+ })
9059
+ );
9060
+ });
9061
+ };
9062
+ addInstall("install", "Install SAG Knowledge for every detected Agent");
9063
+ addInstall("update", "Resolve the running @latest package and update managed Skills");
9064
+ const auth = skill.command("auth").description("Authorize one SAG account Origin");
9065
+ auth.command("login").description("Open browser approval and authorize the current SAG account").option("--origin <origin>", "Full SAG HTTP or HTTPS Origin").action(async (options) => {
9066
+ const origin = await resolveSkillOrigin(dependencies2, options.origin, true);
9067
+ emit(
9068
+ program,
9069
+ dependencies2,
9070
+ await authorizeSkillAccount(skillAccountAuthRuntime(dependencies2), origin)
9071
+ );
9072
+ });
9073
+ auth.command("status").description("Inspect the saved SAG account authorization").option("--origin <origin>", "Full SAG HTTP or HTTPS Origin").action(async (options) => {
9074
+ const origin = await resolveSkillOrigin(dependencies2, options.origin);
9075
+ const account = await skillAccountAuthRuntime(dependencies2).config.getAccount(origin);
9076
+ emit(program, dependencies2, {
9077
+ authenticated: Boolean(account),
9078
+ origin,
9079
+ ...account ? { user: account.user } : {}
9080
+ });
9081
+ });
9082
+ auth.command("logout").description("Revoke and remove the saved SAG account authorization").option("--origin <origin>", "Full SAG HTTP or HTTPS Origin").action(async (options) => {
9083
+ const origin = await resolveSkillOrigin(dependencies2, options.origin);
9084
+ const removed = await logoutSkillAccount(
9085
+ skillAccountAuthRuntime(dependencies2),
9086
+ origin
9087
+ );
9088
+ emit(program, dependencies2, { origin, removed });
9089
+ });
9090
+ const accountKnowledge = skill.command("knowledge").description("Use Knowledge Bases available to the authorized SAG account");
9091
+ accountKnowledge.command("list").description("List Knowledge Bases available to the current SAG account").option("--origin <origin>", "Full SAG HTTP or HTTPS Origin").action(async (options) => {
9092
+ const origin = await resolveSkillOrigin(dependencies2, options.origin);
9093
+ emit(
9094
+ program,
9095
+ dependencies2,
9096
+ await listSkillAccountKnowledgeBases(
9097
+ skillAccountKnowledgeRuntime(dependencies2),
9098
+ origin
9099
+ )
9100
+ );
9101
+ });
9102
+ accountKnowledge.command("documents").description("List documents in one exact Knowledge Base").requiredOption(
9103
+ "--knowledge-base <id-or-alias-or-name>",
9104
+ "Exact Knowledge Base ID, alias, or unique name"
9105
+ ).option("--origin <origin>", "Full SAG HTTP or HTTPS Origin").action(async (options) => {
9106
+ const origin = await resolveSkillOrigin(dependencies2, options.origin);
9107
+ emit(
9108
+ program,
9109
+ dependencies2,
9110
+ await listSkillAccountDocuments(
9111
+ skillAccountKnowledgeRuntime(dependencies2),
9112
+ origin,
9113
+ options.knowledgeBase
9114
+ )
9115
+ );
9116
+ });
9117
+ accountKnowledge.command("ask").description("Ask SAG and relay its answer and citations").argument("<question>", "Question for SAG").option("--origin <origin>", "Full SAG HTTP or HTTPS Origin").option(
9118
+ "--knowledge-base <id-or-alias-or-name>",
9119
+ "Optional exact Knowledge Base scope; omit for SAG global search"
9120
+ ).action(
9121
+ async (question, options) => {
9122
+ const origin = await resolveSkillOrigin(dependencies2, options.origin);
9123
+ emit(
9124
+ program,
9125
+ dependencies2,
9126
+ await askSkillAccountKnowledge(
9127
+ skillAccountKnowledgeRuntime(dependencies2),
9128
+ origin,
9129
+ question,
9130
+ options.knowledgeBase
9131
+ )
9132
+ );
9133
+ }
9134
+ );
9135
+ accountKnowledge.command("upload").description("Upload one regular file to an exact Knowledge Base").argument("<file>", "One regular file").requiredOption(
9136
+ "--knowledge-base <id-or-alias-or-name>",
9137
+ "Confirmed Knowledge Base ID, alias, or unique name"
9138
+ ).option("--origin <origin>", "Full SAG HTTP or HTTPS Origin").option(
9139
+ "--wait <milliseconds>",
9140
+ "Use 0 to detach after acceptance",
9141
+ parseNonNegativeInteger
9142
+ ).option(
9143
+ "--overwrite-document-id <document-id>",
9144
+ "Existing document ID returned by a duplicate upload conflict"
9145
+ ).action(
9146
+ async (filePath, options) => {
9147
+ const origin = await resolveSkillOrigin(dependencies2, options.origin);
9148
+ const events = await uploadSkillAccountFile(
9149
+ skillAccountUploadRuntime(dependencies2),
9150
+ origin,
9151
+ filePath,
9152
+ options.knowledgeBase,
9153
+ options.wait,
9154
+ options.overwriteDocumentId
9155
+ );
9156
+ emitSkillAccountUpload(program, dependencies2, events);
9157
+ }
9158
+ );
9159
+ accountKnowledge.command("upload-status").description("Read upload progress using the returned import ID").argument("<import-id>", "Import ID returned by upload").requiredOption(
9160
+ "--knowledge-base <id-or-alias-or-name>",
9161
+ "Exact Knowledge Base ID, alias, or unique name"
9162
+ ).option("--origin <origin>", "Full SAG HTTP or HTTPS Origin").action(
9163
+ async (importId, options) => {
9164
+ const origin = await resolveSkillOrigin(dependencies2, options.origin);
9165
+ emitSkillAccountUploadStatus(
9166
+ program,
9167
+ dependencies2,
9168
+ await inspectSkillAccountUpload(
9169
+ skillAccountUploadRuntime(dependencies2),
9170
+ origin,
9171
+ importId,
9172
+ options.knowledgeBase
9173
+ )
9174
+ );
9175
+ }
9176
+ );
9177
+ accountKnowledge.command("rename").description("Rename one exact document version").argument("<document-id>", "Exact document ID").requiredOption("--title <title>", "New document title").requiredOption(
9178
+ "--expected-version <version>",
9179
+ "Current document version",
9180
+ parsePositiveInteger
9181
+ ).requiredOption(
9182
+ "--knowledge-base <id-or-alias-or-name>",
9183
+ "Confirmed Knowledge Base ID, alias, or unique name"
9184
+ ).option("--origin <origin>", "Full SAG HTTP or HTTPS Origin").action(
9185
+ async (documentId, options) => {
9186
+ const origin = await resolveSkillOrigin(dependencies2, options.origin);
9187
+ emit(
9188
+ program,
9189
+ dependencies2,
9190
+ await renameSkillAccountDocument(
9191
+ skillAccountKnowledgeRuntime(dependencies2),
9192
+ origin,
9193
+ {
9194
+ knowledgeBase: options.knowledgeBase,
9195
+ documentId,
9196
+ expectedVersion: options.expectedVersion,
9197
+ title: options.title
9198
+ }
9199
+ )
9200
+ );
9201
+ }
9202
+ );
9203
+ accountKnowledge.command("delete").description("Move one exact document version to the recoverable recycle bin").argument("<document-id>", "Exact document ID").requiredOption(
9204
+ "--expected-version <version>",
9205
+ "Current document version",
9206
+ parsePositiveInteger
9207
+ ).requiredOption(
9208
+ "--knowledge-base <id-or-alias-or-name>",
9209
+ "Confirmed Knowledge Base ID, alias, or unique name"
9210
+ ).option("--origin <origin>", "Full SAG HTTP or HTTPS Origin").action(
9211
+ async (documentId, options) => {
9212
+ const origin = await resolveSkillOrigin(dependencies2, options.origin);
9213
+ emit(
9214
+ program,
9215
+ dependencies2,
9216
+ await deleteSkillAccountDocument(
9217
+ skillAccountKnowledgeRuntime(dependencies2),
9218
+ origin,
9219
+ {
9220
+ knowledgeBase: options.knowledgeBase,
9221
+ documentId,
9222
+ expectedVersion: options.expectedVersion
9223
+ }
9224
+ )
9225
+ );
9226
+ }
9227
+ );
9228
+ skill.command("status").description("Inspect managed SAG Knowledge Skill targets").option(
9229
+ "--agent <agent>",
9230
+ "Target codex, claude-code, or workbuddy",
9231
+ selectedAgents,
9232
+ []
9233
+ ).action(async (options) => {
9234
+ emitSkillTargets(
9235
+ program,
9236
+ dependencies2,
9237
+ await inspectSkillTargets(skillInstallerRuntime(dependencies2), {
9238
+ ...options.agent.length ? { agents: options.agent } : {}
9239
+ })
9240
+ );
9241
+ });
9242
+ skill.command("uninstall").description("Remove unchanged SAG CLI-managed Skills").option(
9243
+ "--agent <agent>",
9244
+ "Target codex, claude-code, or workbuddy",
9245
+ selectedAgents,
9246
+ []
9247
+ ).action(async (options) => {
9248
+ await executeSkillTargets(
9249
+ program,
9250
+ dependencies2,
9251
+ uninstallSkillTargets(skillInstallerRuntime(dependencies2), {
9252
+ ...options.agent.length ? { agents: options.agent } : {},
9253
+ yes: commandOptions2(program).yes ?? false
9254
+ })
9255
+ );
9256
+ });
9257
+ }
6821
9258
  function createProgram(dependencies2) {
6822
9259
  const program = new Command();
6823
- program.name("sag").description("Command-line client and diagnostics for SAG").showHelpAfterError().exitOverride().option("--json", "Output a stable JSON envelope").option("--quiet", "Output only essential values").option("--profile <name>", "Use a configured SAG profile").option("--url <origin>", "Use a SAG origin without saving it").option("--yes", "Confirm safe local configuration changes").configureOutput({
9260
+ skillWriters.set(program, new SkillEventWriter(dependencies2.writeStdout));
9261
+ program.name("sag").description("Command-line client and diagnostics for SAG").showHelpAfterError().exitOverride().option("--json", "Output a stable JSON envelope").addOption(
9262
+ new Option("--output <format>", "Output format").choices(["human", "ndjson"]).conflicts("json")
9263
+ ).option("--quiet", "Output only essential values").option("--profile <name>", "Use a configured SAG profile").option("--url <origin>", "Use a SAG origin without saving it").option("--yes", "Confirm safe local configuration changes").configureOutput({
6824
9264
  writeOut: dependencies2.writeStdout,
6825
9265
  writeErr: dependencies2.writeStderr
6826
9266
  });
6827
9267
  program.command("version").action(() => {
6828
9268
  const options = commandOptions2(program);
6829
- if (options.json) {
9269
+ if (options.output === "ndjson") {
9270
+ skillWriters.get(program)?.complete(
9271
+ skillEvent(program, "completed", {
9272
+ name: "sag",
9273
+ version: dependencies2.version
9274
+ })
9275
+ );
9276
+ } else if (options.json) {
6830
9277
  dependencies2.writeStdout(
6831
9278
  renderJson(success3({ name: "sag", version: dependencies2.version }))
6832
9279
  );
@@ -6840,22 +9287,70 @@ function createProgram(dependencies2) {
6840
9287
  addKnowledgeCommands(program, dependencies2);
6841
9288
  addLocalMcpCommands(program, dependencies2);
6842
9289
  addAgentCommands(program, dependencies2);
9290
+ addSkillCommands(program, dependencies2);
6843
9291
  return program;
6844
9292
  }
6845
9293
  async function runCli(arguments_, dependencies2) {
6846
9294
  const json = arguments_.includes("--json");
9295
+ const program = createProgram(
9296
+ commandOutputIsNdjson(arguments_) ? { ...dependencies2, writeStderr: () => void 0 } : dependencies2
9297
+ );
6847
9298
  try {
6848
- await createProgram(dependencies2).parseAsync(arguments_);
9299
+ await program.parseAsync(arguments_);
6849
9300
  return exitCodes.success;
6850
9301
  } catch (error) {
9302
+ if (error instanceof ReportedSkillTargetFailure) return error.exitCode;
6851
9303
  if (error instanceof CommanderError && error.exitCode === 0) {
6852
9304
  return 0;
6853
9305
  }
9306
+ if (error instanceof SkillError) {
9307
+ if (json) {
9308
+ dependencies2.writeStdout(
9309
+ renderJson({
9310
+ schema: "sag.cli.v1",
9311
+ ok: false,
9312
+ error: {
9313
+ code: error.code,
9314
+ message: error.message,
9315
+ ...error.details ? { details: error.details } : {}
9316
+ }
9317
+ })
9318
+ );
9319
+ } else if (commandOutputIsNdjson(arguments_)) {
9320
+ const event = skillEvent(program, "failed", void 0, {
9321
+ code: error.code,
9322
+ message: error.message,
9323
+ retryable: error.code === "NETWORK_UNREACHABLE",
9324
+ ...error.details ? { details: error.details } : {}
9325
+ });
9326
+ const operationId = error.details?.operation_id;
9327
+ skillWriters.get(program)?.complete({
9328
+ ...event,
9329
+ ...typeof operationId === "string" ? { operation_id: operationId } : {}
9330
+ });
9331
+ } else {
9332
+ const operationId = error.details?.operation_id;
9333
+ dependencies2.writeStderr(
9334
+ `Error [${error.code}]: ${error.message}${typeof operationId === "string" ? `
9335
+ Operation ID: ${operationId}` : ""}
9336
+ `
9337
+ );
9338
+ }
9339
+ return error.exitCode;
9340
+ }
6854
9341
  const cliError = error instanceof CommanderError ? new CliError("INVALID_ARGUMENT", error.message, {
6855
9342
  exitCode: exitCodes.invalidArgument
6856
9343
  }) : toCliError(error);
6857
9344
  if (json) {
6858
9345
  dependencies2.writeStdout(renderJson(failure(cliError)));
9346
+ } else if (commandOutputIsNdjson(arguments_)) {
9347
+ skillWriters.get(program)?.complete(
9348
+ skillEvent(program, "failed", void 0, {
9349
+ code: toSkillErrorCode(cliError.code),
9350
+ message: cliError.message,
9351
+ retryable: cliError.code === "NETWORK_UNREACHABLE"
9352
+ })
9353
+ );
6859
9354
  } else {
6860
9355
  dependencies2.writeStderr(
6861
9356
  `Error [${cliError.code}]: ${cliError.message}${cliError.hint ? `
@@ -6886,6 +9381,51 @@ function readStdin() {
6886
9381
  process.stdin.once("error", reject);
6887
9382
  });
6888
9383
  }
9384
+ async function resolveLatestSkillBundle(processRunner2) {
9385
+ const directory = await mkdtemp(path15.join(tmpdir(), "sag-skill-latest-"));
9386
+ const cleanup = () => rm3(directory, { recursive: true, force: true });
9387
+ try {
9388
+ const packed = await processRunner2.run({
9389
+ command: "npm",
9390
+ args: [
9391
+ "pack",
9392
+ "@zleap-ai/sag-cli@latest",
9393
+ "--ignore-scripts",
9394
+ "--json",
9395
+ "--pack-destination",
9396
+ directory,
9397
+ "--cache",
9398
+ path15.join(directory, "npm-cache"),
9399
+ "--prefer-online"
9400
+ ],
9401
+ timeoutMs: 6e4
9402
+ });
9403
+ if (packed.exitCode !== 0) throw new Error("npm pack failed");
9404
+ const result = JSON.parse(packed.stdout);
9405
+ const filename = Array.isArray(result) && typeof result[0] === "object" && result[0] !== null && typeof result[0].filename === "string" ? result[0].filename : null;
9406
+ if (!filename || path15.basename(filename) !== filename) {
9407
+ throw new Error("npm pack returned an invalid filename");
9408
+ }
9409
+ const extracted = await processRunner2.run({
9410
+ command: "tar",
9411
+ args: ["-xzf", path15.join(directory, filename), "-C", directory],
9412
+ timeoutMs: 3e4
9413
+ });
9414
+ if (extracted.exitCode !== 0) throw new Error("tar extraction failed");
9415
+ return { bundlePath: path15.join(directory, "package", "skill"), cleanup };
9416
+ } catch (cause) {
9417
+ await cleanup();
9418
+ throw new CliError(
9419
+ "NETWORK_UNREACHABLE",
9420
+ "Unable to resolve @latest Skill package",
9421
+ {
9422
+ exitCode: exitCodes.networkUnreachable,
9423
+ cause,
9424
+ hint: "Check npm network access and retry; the installed Skill was not changed."
9425
+ }
9426
+ );
9427
+ }
9428
+ }
6889
9429
  var credentialSelection = await createCredentialStore();
6890
9430
  if (credentialSelection.warning) {
6891
9431
  process.stderr.write(`Warning: ${credentialSelection.warning}
@@ -6901,6 +9441,16 @@ var adapters = {
6901
9441
  var enterpriseManaged = new EnterpriseMcpManagedStateStore(
6902
9442
  defaultEnterpriseMcpManagedStatePath()
6903
9443
  );
9444
+ var skillState2 = new SkillStateStore(defaultSkillStatePath());
9445
+ var skillAccountConfig = new SkillAccountConfigStore(defaultSkillAccountConfigPath());
9446
+ async function openSkillAuthorizationBrowser(processRunner2, verificationUrl) {
9447
+ const command = process.platform === "darwin" ? { command: "open", args: [verificationUrl] } : process.platform === "win32" ? { command: "cmd", args: ["/c", "start", "", verificationUrl] } : { command: "xdg-open", args: [verificationUrl] };
9448
+ const result = await processRunner2.run({
9449
+ ...command,
9450
+ timeoutMs: 1e4
9451
+ });
9452
+ if (result.exitCode !== 0) throw new Error("browser opener failed");
9453
+ }
6904
9454
  function unavailableEnterpriseKeychain() {
6905
9455
  const unavailable = () => {
6906
9456
  throw new CliError("DEPENDENCY_MISSING", "\u7CFB\u7EDF\u51ED\u636E\u5E93\u4E0D\u53EF\u7528", {
@@ -6925,6 +9475,7 @@ var dependencies = {
6925
9475
  credentialStore: credentialSelection.store,
6926
9476
  createClient: defaultClientFactory,
6927
9477
  promptName: () => input({ message: "SAG user name" }),
9478
+ promptSkillOrigin: () => input({ message: "SAG address" }),
6928
9479
  confirm: (message) => confirm({ message, default: false }),
6929
9480
  readStdin,
6930
9481
  stdinIsTty: Boolean(process.stdin.isTTY),
@@ -7002,6 +9553,31 @@ var dependencies = {
7002
9553
  now: () => /* @__PURE__ */ new Date()
7003
9554
  },
7004
9555
  now: () => /* @__PURE__ */ new Date()
9556
+ },
9557
+ skill: {
9558
+ home: os5.homedir(),
9559
+ environment: { CODEX_HOME: process.env.CODEX_HOME },
9560
+ state: skillState2,
9561
+ agents: {
9562
+ codex: { id: "codex", installed: () => isAgentInstalled(adapters.codex) },
9563
+ "claude-code": {
9564
+ id: "claude-code",
9565
+ installed: () => isAgentInstalled(adapters["claude-code"])
9566
+ },
9567
+ workbuddy: {
9568
+ id: "workbuddy",
9569
+ installed: () => isAgentInstalled(adapters.workbuddy)
9570
+ }
9571
+ },
9572
+ now: () => /* @__PURE__ */ new Date(),
9573
+ confirm: (message) => confirm({ message, default: false }),
9574
+ resolveLatestBundle: () => resolveLatestSkillBundle(processRunner)
9575
+ },
9576
+ skillAccount: {
9577
+ client: new SkillAccountAuthHttpClient(),
9578
+ config: skillAccountConfig,
9579
+ now: () => /* @__PURE__ */ new Date(),
9580
+ openBrowser: (verificationUrl) => openSkillAuthorizationBrowser(processRunner, verificationUrl)
7005
9581
  }
7006
9582
  };
7007
9583
  process.exitCode = await runCli(process.argv, dependencies);