openzca 0.1.17 → 0.1.18

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.
Files changed (2) hide show
  1. package/dist/cli.js +144 -7
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -806,6 +806,139 @@ async function refreshCacheForProfile(profile, api) {
806
806
  groups: groups.length
807
807
  };
808
808
  }
809
+ function parsePositiveIntFromEnv(name, fallback) {
810
+ const raw = process.env[name]?.trim();
811
+ if (!raw) return fallback;
812
+ const parsed = Number.parseInt(raw, 10);
813
+ if (!Number.isFinite(parsed) || parsed <= 0) return fallback;
814
+ return parsed;
815
+ }
816
+ function isListenerAlreadyStarted(error) {
817
+ if (!(error instanceof Error)) return false;
818
+ return /already started/i.test(error.message);
819
+ }
820
+ function toErrorText(error) {
821
+ return error instanceof Error ? error.message : String(error);
822
+ }
823
+ async function withTimeout(task, timeoutMs, message) {
824
+ let timeoutId;
825
+ try {
826
+ const timeout = new Promise((_, reject) => {
827
+ timeoutId = setTimeout(() => reject(new Error(message)), timeoutMs);
828
+ });
829
+ return await Promise.race([task, timeout]);
830
+ } finally {
831
+ if (timeoutId) clearTimeout(timeoutId);
832
+ }
833
+ }
834
+ async function withUploadListener(api, command, task) {
835
+ const connectTimeoutMs = parsePositiveIntFromEnv(
836
+ "OPENZCA_UPLOAD_LISTENER_CONNECT_TIMEOUT_MS",
837
+ 8e3
838
+ );
839
+ const uploadTimeoutMs = parsePositiveIntFromEnv(
840
+ "OPENZCA_UPLOAD_TIMEOUT_MS",
841
+ 12e4
842
+ );
843
+ let startedHere = false;
844
+ const sinkError = (error) => {
845
+ writeDebugLine(
846
+ "msg.upload.listener.error",
847
+ {
848
+ message: toErrorText(error)
849
+ },
850
+ command
851
+ );
852
+ };
853
+ const sinkClosed = (code, reason) => {
854
+ writeDebugLine(
855
+ "msg.upload.listener.closed",
856
+ {
857
+ code,
858
+ reason: reason || void 0
859
+ },
860
+ command
861
+ );
862
+ };
863
+ api.listener.on("error", sinkError);
864
+ api.listener.on("closed", sinkClosed);
865
+ try {
866
+ await new Promise((resolve, reject) => {
867
+ let settled = false;
868
+ let timeoutId;
869
+ const cleanup = () => {
870
+ if (timeoutId) clearTimeout(timeoutId);
871
+ api.listener.off("connected", onConnected);
872
+ api.listener.off("error", onConnectError);
873
+ api.listener.off("closed", onConnectClosed);
874
+ };
875
+ const finish = (error) => {
876
+ if (settled) return;
877
+ settled = true;
878
+ cleanup();
879
+ if (error) {
880
+ reject(error);
881
+ return;
882
+ }
883
+ resolve();
884
+ };
885
+ const onConnected = () => {
886
+ writeDebugLine("msg.upload.listener.connected", void 0, command);
887
+ finish();
888
+ };
889
+ const onConnectError = (error) => {
890
+ finish(new Error(`Upload listener connection error: ${toErrorText(error)}`));
891
+ };
892
+ const onConnectClosed = (code, reason) => {
893
+ finish(
894
+ new Error(
895
+ `Upload listener closed before ready (code=${code}${reason ? `, reason=${reason}` : ""}).`
896
+ )
897
+ );
898
+ };
899
+ timeoutId = setTimeout(() => {
900
+ finish(new Error(`Timed out waiting ${connectTimeoutMs}ms for upload listener connection.`));
901
+ }, connectTimeoutMs);
902
+ api.listener.on("connected", onConnected);
903
+ api.listener.on("error", onConnectError);
904
+ api.listener.on("closed", onConnectClosed);
905
+ try {
906
+ api.listener.start();
907
+ startedHere = true;
908
+ writeDebugLine(
909
+ "msg.upload.listener.start",
910
+ {
911
+ connectTimeoutMs,
912
+ uploadTimeoutMs
913
+ },
914
+ command
915
+ );
916
+ } catch (error) {
917
+ if (isListenerAlreadyStarted(error)) {
918
+ writeDebugLine("msg.upload.listener.already_started", void 0, command);
919
+ finish();
920
+ return;
921
+ }
922
+ finish(error);
923
+ }
924
+ });
925
+ return await withTimeout(
926
+ task(),
927
+ uploadTimeoutMs,
928
+ `Timed out waiting ${uploadTimeoutMs}ms for file upload completion.`
929
+ );
930
+ } finally {
931
+ api.listener.off("error", sinkError);
932
+ api.listener.off("closed", sinkClosed);
933
+ if (startedHere) {
934
+ try {
935
+ api.listener.stop();
936
+ writeDebugLine("msg.upload.listener.stop", void 0, command);
937
+ } catch {
938
+ }
939
+ }
940
+ }
941
+ }
809
942
  async function fetchRecentMessagesViaListener(api, threadId, threadType, count) {
810
943
  return new Promise((resolve, reject) => {
811
944
  let settled = false;
@@ -2137,13 +2270,17 @@ msg.command("upload <arg1> [arg2]").option("-u, --url <url>", "File URL (repeata
2137
2270
  );
2138
2271
  }
2139
2272
  await assertFilesExist(attachments);
2140
- const response = await api.sendMessage(
2141
- {
2142
- msg: "",
2143
- attachments
2144
- },
2145
- threadId,
2146
- asThreadType(opts.group)
2273
+ const response = await withUploadListener(
2274
+ api,
2275
+ command,
2276
+ async () => api.sendMessage(
2277
+ {
2278
+ msg: "",
2279
+ attachments
2280
+ },
2281
+ threadId,
2282
+ asThreadType(opts.group)
2283
+ )
2147
2284
  );
2148
2285
  output(response, false);
2149
2286
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openzca",
3
- "version": "0.1.17",
3
+ "version": "0.1.18",
4
4
  "description": "Open-source zca-compatible CLI to integrate Zalo with OpenClaw",
5
5
  "type": "module",
6
6
  "bin": {