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.
- package/dist/cli.js +144 -7
- 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
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
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 {
|