bailian-cli-core 1.0.2 → 1.1.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/LICENSE CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  Apache License
2
3
  Version 2.0, January 2004
3
4
  http://www.apache.org/licenses/
@@ -137,8 +138,8 @@
137
138
 
138
139
  6. Trademarks. This License does not grant permission to use the trade
139
140
  names, trademarks, service marks, or product names of the Licensor,
140
- except as required for describing the origin of the Work and
141
- reproducing the content of the NOTICE file.
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
142
143
 
143
144
  7. Disclaimer of Warranty. Unless required by applicable law or
144
145
  agreed to in writing, Licensor provides the Work (and each
@@ -163,15 +164,15 @@
163
164
  has been advised of the possibility of such damages.
164
165
 
165
166
  9. Accepting Warranty or Additional Liability. While redistributing
166
- the Work or Derivative Works thereof, You may accept and charge a
167
- fee for, acceptance of support, warranty, indemnity, or other
168
- liability obligations and/or rights consistent with this License.
169
- However, in accepting such obligations, You may act only on Your
170
- own behalf and on the sole responsibility of Your contributors,
171
- and only if You agree to indemnify, defend, and hold each Contributor
172
- harmless for any liability incurred by, or claims asserted against,
173
- such Contributor by reason of your accepting any such warranty or
174
- additional liability.
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
175
176
 
176
177
  END OF TERMS AND CONDITIONS
177
178
 
package/dist/index.d.mts CHANGED
@@ -711,6 +711,7 @@ interface RequestOpts {
711
711
  stream?: boolean;
712
712
  noAuth?: boolean;
713
713
  async?: boolean;
714
+ signal?: AbortSignal;
714
715
  }
715
716
  declare function request(config: Config, opts: RequestOpts): Promise<Response>;
716
717
  declare function requestJson<T>(config: Config, opts: RequestOpts): Promise<T>;
@@ -772,9 +773,11 @@ interface ConsoleGatewayRequest {
772
773
  }
773
774
  /**
774
775
  * Invoke a Bailian **console** OpenAPI via the CLI gateway (`/cli/api.json`).
775
- * Requires a console `access_token` (from `bl auth login --console`), not a DashScope API key.
776
+ * `token` is the console `access_token` (from `bl auth login --console`); when
777
+ * omitted the request is sent without an Authorization header, which works for
778
+ * public console APIs that don't require a login session.
776
779
  */
777
- declare function callConsoleGateway(config: Config, token: string, {
780
+ declare function callConsoleGateway(config: Config, token: string | undefined, {
778
781
  api,
779
782
  data,
780
783
  region
@@ -825,6 +828,7 @@ interface UploadOptions {
825
828
  apiKey: string;
826
829
  model: string;
827
830
  filePath: string;
831
+ signal?: AbortSignal;
828
832
  }
829
833
  /**
830
834
  * Upload a local file to DashScope temporary storage and return the oss:// URL.
@@ -839,7 +843,9 @@ declare function isLocalFile(input: string): boolean;
839
843
  * Resolve a file argument: if it's a local path, upload it and return the oss:// URL.
840
844
  * If it's already a URL, return as-is.
841
845
  */
842
- declare function resolveFileUrl(input: string, apiKey: string, model: string): Promise<string>;
846
+ declare function resolveFileUrl(input: string, apiKey: string, model: string, opts?: {
847
+ signal?: AbortSignal;
848
+ }): Promise<string>;
843
849
  //#endregion
844
850
  //#region src/types/command.d.ts
845
851
  interface OptionDef {
@@ -855,7 +861,7 @@ interface Command {
855
861
  options?: OptionDef[];
856
862
  examples?: string[];
857
863
  apiDocs?: string;
858
- execute(config: Config, flags: GlobalFlags): Promise<void>;
864
+ execute: (config: Config, flags: GlobalFlags) => Promise<void>;
859
865
  }
860
866
  interface CommandSpec {
861
867
  name: string;
@@ -864,7 +870,7 @@ interface CommandSpec {
864
870
  options?: OptionDef[];
865
871
  examples?: string[];
866
872
  apiDocs?: string;
867
- run(config: Config, flags: GlobalFlags): Promise<void>;
873
+ run: (config: Config, flags: GlobalFlags) => Promise<void>;
868
874
  }
869
875
  declare function defineCommand(spec: CommandSpec): Command;
870
876
  /** Global flags shared by all commands — drives the parser's type resolution. */
package/dist/index.mjs CHANGED
@@ -486,13 +486,13 @@ async function request(config, opts) {
486
486
  console.error(`> x-dashscope-source-config: ${SOURCE_CONFIG}`);
487
487
  }
488
488
  }
489
- const timeoutMs = (opts.timeout ?? config.timeout) * 1e3;
489
+ const requestSignal = createRequestSignal((opts.timeout ?? config.timeout) * 1e3, opts.signal);
490
490
  const res = await fetch(opts.url, {
491
491
  method: opts.method ?? "GET",
492
492
  headers,
493
493
  body: opts.body ? isFormData ? opts.body : JSON.stringify(opts.body) : void 0,
494
- signal: AbortSignal.timeout(timeoutMs)
495
- });
494
+ signal: requestSignal.signal
495
+ }).finally(requestSignal.cleanup);
496
496
  if (config.verbose) {
497
497
  console.error(`< ${res.status} ${res.statusText}`);
498
498
  const reqId = res.headers.get("x-request-id");
@@ -507,6 +507,22 @@ async function request(config, opts) {
507
507
  }
508
508
  return res;
509
509
  }
510
+ function createRequestSignal(timeoutMs, parentSignal) {
511
+ const controller = new AbortController();
512
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
513
+ const abortFromParent = () => controller.abort(parentSignal?.reason);
514
+ const cleanup = () => {
515
+ clearTimeout(timeout);
516
+ parentSignal?.removeEventListener("abort", abortFromParent);
517
+ };
518
+ if (parentSignal?.aborted) abortFromParent();
519
+ else parentSignal?.addEventListener("abort", abortFromParent, { once: true });
520
+ controller.signal.addEventListener("abort", cleanup, { once: true });
521
+ return {
522
+ signal: controller.signal,
523
+ cleanup
524
+ };
525
+ }
510
526
  async function requestJson(config, opts) {
511
527
  const res = await request(config, opts);
512
528
  let data;
@@ -694,7 +710,9 @@ function buildGatewayParams(api, data) {
694
710
  }
695
711
  /**
696
712
  * Invoke a Bailian **console** OpenAPI via the CLI gateway (`/cli/api.json`).
697
- * Requires a console `access_token` (from `bl auth login --console`), not a DashScope API key.
713
+ * `token` is the console `access_token` (from `bl auth login --console`); when
714
+ * omitted the request is sent without an Authorization header, which works for
715
+ * public console APIs that don't require a login session.
698
716
  */
699
717
  async function callConsoleGateway(config, token, { api, data, region = "cn-beijing" }) {
700
718
  const params = buildGatewayParams(api, data);
@@ -704,13 +722,14 @@ async function callConsoleGateway(config, token, { api, data, region = "cn-beiji
704
722
  });
705
723
  const timeoutMs = config.timeout * 1e3;
706
724
  const gatewayBase = config.consoleGatewayUrl;
725
+ const headers = {
726
+ Accept: "*/*",
727
+ "Content-Type": "application/x-www-form-urlencoded"
728
+ };
729
+ if (token) headers.Authorization = `Bearer ${token}`;
707
730
  const res = await fetch(`${gatewayBase}/cli/api.json?action=${GATEWAY_ACTION}&product=${GATEWAY_PRODUCT}&api=${encodeURIComponent(api)}`, {
708
731
  method: "POST",
709
- headers: {
710
- Accept: "*/*",
711
- Authorization: `Bearer ${token}`,
712
- "Content-Type": "application/x-www-form-urlencoded"
713
- },
732
+ headers,
714
733
  body: body.toString(),
715
734
  signal: AbortSignal.timeout(timeoutMs)
716
735
  });
@@ -733,16 +752,17 @@ const UPLOAD_API = `${REGIONS.cn}/api/v1/uploads`;
733
752
  /**
734
753
  * Step 1: Fetch the upload policy (presigned credentials) from DashScope.
735
754
  */
736
- async function getUploadPolicy(apiKey, model) {
755
+ async function getUploadPolicy(apiKey, model, signal) {
737
756
  const url = `${UPLOAD_API}?action=getPolicy&model=${encodeURIComponent(model)}`;
757
+ const policySignal = combineWithTimeout(15e3, signal);
738
758
  const res = await fetch(url, {
739
759
  headers: {
740
760
  Authorization: `Bearer ${apiKey}`,
741
761
  "Content-Type": "application/json",
742
762
  ...trackingHeaders()
743
763
  },
744
- signal: AbortSignal.timeout(15e3)
745
- });
764
+ signal: policySignal.signal
765
+ }).finally(policySignal.cleanup);
746
766
  if (!res.ok) {
747
767
  const text = await res.text().catch(() => "");
748
768
  throw new BailianError(`Failed to get upload policy (HTTP ${res.status}): ${text}`, ExitCode.GENERAL);
@@ -752,7 +772,7 @@ async function getUploadPolicy(apiKey, model) {
752
772
  /**
753
773
  * Step 2: Upload the file to OSS using the policy.
754
774
  */
755
- async function uploadToOSS(policy, filePath) {
775
+ async function uploadToOSS(policy, filePath, signal) {
756
776
  const fileName = basename(filePath);
757
777
  const key = `${policy.upload_dir}/${fileName}`;
758
778
  const fileData = readFileSync(filePath);
@@ -765,12 +785,13 @@ async function uploadToOSS(policy, filePath) {
765
785
  form.append("key", key);
766
786
  form.append("success_action_status", "200");
767
787
  form.append("file", new Blob([fileData]), fileName);
788
+ const uploadSignal = combineWithTimeout(12e4, signal);
768
789
  const res = await fetch(policy.upload_host, {
769
790
  method: "POST",
770
791
  headers: { ...trackingHeaders() },
771
792
  body: form,
772
- signal: AbortSignal.timeout(12e4)
773
- });
793
+ signal: uploadSignal.signal
794
+ }).finally(uploadSignal.cleanup);
774
795
  if (!res.ok) {
775
796
  const text = await res.text().catch(() => "");
776
797
  throw new BailianError(`Failed to upload file to OSS (HTTP ${res.status}): ${text}`, ExitCode.GENERAL);
@@ -782,10 +803,10 @@ async function uploadToOSS(policy, filePath) {
782
803
  * The URL is valid for 48 hours.
783
804
  */
784
805
  async function uploadFile(opts) {
785
- const { apiKey, model, filePath } = opts;
806
+ const { apiKey, model, filePath, signal } = opts;
786
807
  if (!existsSync(filePath)) throw new BailianError(`File not found: ${filePath}`, ExitCode.USAGE);
787
808
  if (!statSync(filePath).isFile()) throw new BailianError(`Not a file: ${filePath}`, ExitCode.USAGE);
788
- return uploadToOSS(await getUploadPolicy(apiKey, model), filePath);
809
+ return uploadToOSS(await getUploadPolicy(apiKey, model, signal), filePath, signal);
789
810
  }
790
811
  /**
791
812
  * Check if a string looks like a local file path (not a URL).
@@ -800,14 +821,31 @@ function isLocalFile(input) {
800
821
  * Resolve a file argument: if it's a local path, upload it and return the oss:// URL.
801
822
  * If it's already a URL, return as-is.
802
823
  */
803
- async function resolveFileUrl(input, apiKey, model) {
824
+ async function resolveFileUrl(input, apiKey, model, opts = {}) {
804
825
  if (!isLocalFile(input)) return input;
805
826
  return uploadFile({
806
827
  apiKey,
807
828
  model,
808
- filePath: input
829
+ filePath: input,
830
+ signal: opts.signal
809
831
  });
810
832
  }
833
+ function combineWithTimeout(timeoutMs, parentSignal) {
834
+ const controller = new AbortController();
835
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
836
+ const abortFromParent = () => controller.abort(parentSignal?.reason);
837
+ const cleanup = () => {
838
+ clearTimeout(timeout);
839
+ parentSignal?.removeEventListener("abort", abortFromParent);
840
+ };
841
+ if (parentSignal?.aborted) abortFromParent();
842
+ else parentSignal?.addEventListener("abort", abortFromParent, { once: true });
843
+ controller.signal.addEventListener("abort", cleanup, { once: true });
844
+ return {
845
+ signal: controller.signal,
846
+ cleanup
847
+ };
848
+ }
811
849
  //#endregion
812
850
  //#region src/types/command.ts
813
851
  function defineCommand(spec) {
@@ -1969,6 +2007,7 @@ const PARAM_ALLOWLIST = new Set([
1969
2007
  "pitch",
1970
2008
  "rate",
1971
2009
  "volume",
2010
+ "api",
1972
2011
  "mode",
1973
2012
  "download",
1974
2013
  "noWait",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bailian-cli-core",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "Core SDK for bailian-cli. See https://www.npmjs.com/package/bailian-cli for usage.",
5
5
  "homepage": "https://bailian.console.aliyun.com/cli",
6
6
  "license": "Apache-2.0",