@liustack/modlens 2.7.1 → 2.7.2

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/main.js +49 -16
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -724,6 +724,7 @@ function listProviders() {
724
724
  }
725
725
  const DEFAULT_TIMEOUT_MS = 18e4;
726
726
  const KILL_GRACE_MS = 3e4;
727
+ const DRAIN_GRACE_MS = 500;
727
728
  async function analyzeImage(options) {
728
729
  const resolvedInput = resolveInput(options.input);
729
730
  if (resolvedInput.kind === "local") {
@@ -806,18 +807,60 @@ function runCommand(providerName, invocation, timeoutMs) {
806
807
  let stdout = "";
807
808
  let stderr = "";
808
809
  let timedOut = false;
810
+ let settled = false;
811
+ let drainTimer;
809
812
  const timer = setTimeout(() => {
810
813
  timedOut = true;
811
814
  child.kill("SIGTERM");
812
815
  }, timeoutMs);
816
+ const settle = (code) => {
817
+ if (settled) {
818
+ return;
819
+ }
820
+ settled = true;
821
+ clearTimeout(timer);
822
+ clearTimeout(drainTimer);
823
+ child.stdout?.destroy();
824
+ child.stderr?.destroy();
825
+ child.unref();
826
+ if (timedOut) {
827
+ reject(new Error(`${providerName} provider timed out after ${timeoutMs} ms.`));
828
+ return;
829
+ }
830
+ if (code !== 0) {
831
+ reject(
832
+ new Error(
833
+ `${providerName} provider failed with code ${code}.${stderr ? ` stderr: ${stderr.trim()}` : ""}`
834
+ )
835
+ );
836
+ return;
837
+ }
838
+ resolve({ stdout, stderr });
839
+ };
840
+ let exitCode = null;
841
+ let exited = false;
842
+ const restartDrain = () => {
843
+ if (!exited || settled) {
844
+ return;
845
+ }
846
+ clearTimeout(drainTimer);
847
+ drainTimer = setTimeout(() => settle(exitCode), DRAIN_GRACE_MS);
848
+ };
813
849
  child.stdout.on("data", (chunk) => {
814
850
  stdout += chunk.toString();
851
+ restartDrain();
815
852
  });
816
853
  child.stderr.on("data", (chunk) => {
817
854
  stderr += chunk.toString();
855
+ restartDrain();
818
856
  });
819
857
  child.on("error", (error) => {
858
+ if (settled) {
859
+ return;
860
+ }
861
+ settled = true;
820
862
  clearTimeout(timer);
863
+ clearTimeout(drainTimer);
821
864
  if (error.code === "ENOENT") {
822
865
  reject(
823
866
  new Error(
@@ -828,22 +871,12 @@ function runCommand(providerName, invocation, timeoutMs) {
828
871
  }
829
872
  reject(error);
830
873
  });
831
- child.on("close", (code) => {
832
- clearTimeout(timer);
833
- if (timedOut) {
834
- reject(new Error(`${providerName} provider timed out after ${timeoutMs} ms.`));
835
- return;
836
- }
837
- if (code !== 0) {
838
- reject(
839
- new Error(
840
- `${providerName} provider failed with code ${code}.${stderr ? ` stderr: ${stderr.trim()}` : ""}`
841
- )
842
- );
843
- return;
844
- }
845
- resolve({ stdout, stderr });
874
+ child.on("exit", (code) => {
875
+ exitCode = code;
876
+ exited = true;
877
+ restartDrain();
846
878
  });
879
+ child.on("close", (code) => settle(code));
847
880
  });
848
881
  }
849
882
  const EXT_BY_MIME = {
@@ -1236,7 +1269,7 @@ function recoverPastedImages(options = {}) {
1236
1269
  return result;
1237
1270
  }
1238
1271
  const program = new Command();
1239
- program.name("modlens").description("Plug-in vision for text-only LLMs: image in, structured JSON evidence out").version("2.7.1");
1272
+ program.name("modlens").description("Plug-in vision for text-only LLMs: image in, structured JSON evidence out").version("2.7.2");
1240
1273
  program.command("analyze", { isDefault: true }).description("Analyze an image into structured JSON evidence (default command)").requiredOption("-i, --input <path|url>", "Input image path or https URL").option("-o, --output <path>", "Write result JSON to a file").option("-m, --model <name>", "Provider model name").option("-p, --provider <name>", `Vision provider (${listProviders().join(", ")})`).option("--prompt <text>", "Extra focus for this image").option("--timeout <ms>", "Provider timeout in milliseconds", "180000").option("--provider-bin <path>", "Provider binary path (default: agy)").option("--workdir <path>", "Working directory for the provider").action(async (options) => {
1241
1274
  try {
1242
1275
  const timeoutMs = Number.parseInt(options.timeout, 10);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liustack/modlens",
3
- "version": "2.7.1",
3
+ "version": "2.7.2",
4
4
  "description": "Plug-in vision for text-only LLMs, powered by the free Antigravity CLI",
5
5
  "type": "module",
6
6
  "bin": {