@inferall/cli 0.2.1 → 0.2.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.
package/README.md CHANGED
@@ -35,6 +35,12 @@ The wizard:
35
35
 
36
36
  ## Commands
37
37
 
38
+ For a project with no existing AI client, `init` writes a first-request example:
39
+ `node inferall-hello.mjs` or `python3 inferall_hello.py`. Both use built-in
40
+ libraries, read your exported key or the `.env` beside the example, and select
41
+ the free model explicitly. A refusal or empty response exits nonzero and prints
42
+ an explanation to stderr. Existing example files are left intact.
43
+
38
44
  - `init`: the wizard above (default).
39
45
  - `doctor`: health-check an existing setup. Key present and valid, `.env`
40
46
  gitignore hygiene, whether every client call site points at the gateway, and
@@ -1 +1 @@
1
- {"sha":"2a3e95e","ref":"main","offMain":null}
1
+ {"sha":"51fd6a7","ref":"main","offMain":null}
package/bin/inferall.js CHANGED
@@ -1877,9 +1877,175 @@ export function isPlausiblePatch(text, site, base) {
1877
1877
  export function writeExample(root, kind) {
1878
1878
  const p = join(root, kind === "python" ? "inferall_hello.py" : "inferall-hello.mjs");
1879
1879
  if (existsSync(p)) return { path: null, problem: null }; // never clobber theirs
1880
+ // The examples are dependency-free (Node fetch, Python urllib) and read the
1881
+ // key from the environment, else from the .env next to the file, so a plain
1882
+ // `node`/`python3` run works right after init. String.raw keeps the regex
1883
+ // backslashes; the generated code avoids backticks and "${" for that reason.
1880
1884
  const body = kind === "python"
1881
- ? `"""InferAll hello. Run: python inferall_hello.py (pip install inferall-ai)"""\nfrom inferall import Inferall\n\nai = Inferall() # reads INFERALL_API_KEY from the environment\nprint(ai.text("Explain InferAll in one sentence."))\n`
1882
- : `// InferAll hello. Run: node --env-file=.env inferall-hello.mjs\nconst res = await fetch("${API_BASE}/chat/completions", {\n method: "POST",\n headers: {\n Authorization: \`Bearer \${process.env.INFERALL_API_KEY}\`,\n "Content-Type": "application/json",\n },\n body: JSON.stringify({\n model: "${FREE_MODEL}",\n messages: [{ role: "user", content: "Explain InferAll in one sentence." }],\n }),\n});\nconsole.log((await res.json()).choices?.[0]?.message?.content ?? (await res.text()));\n`;
1885
+ ? String.raw`"""InferAll hello. Run: python3 inferall_hello.py (no packages needed)"""
1886
+ import json
1887
+ import http.client
1888
+ import os
1889
+ import re
1890
+ import sys
1891
+ import urllib.error
1892
+ import urllib.request
1893
+ from pathlib import Path
1894
+
1895
+ API_BASE = ${JSON.stringify(API_BASE)}
1896
+ MODEL = ${JSON.stringify(FREE_MODEL)}
1897
+
1898
+
1899
+ def fail(message):
1900
+ print("inferall: " + message, file=sys.stderr)
1901
+ sys.exit(1)
1902
+
1903
+
1904
+ def read_key():
1905
+ # INFERALL_API_KEY from the environment first, else the .env beside this file.
1906
+ key = os.environ.get("INFERALL_API_KEY", "").strip()
1907
+ if key:
1908
+ return key
1909
+ try:
1910
+ text = (Path(__file__).resolve().parent / ".env").read_text(encoding="utf-8-sig")
1911
+ except FileNotFoundError:
1912
+ return ""
1913
+ except OSError as e:
1914
+ fail("could not read .env: %s" % e)
1915
+ pattern = re.compile(r"^\s*(?:export\s+)?INFERALL_API_KEY\s*=(.*)$")
1916
+ for line in text.splitlines():
1917
+ m = pattern.match(line)
1918
+ if m:
1919
+ value = m.group(1).strip()
1920
+ if value[:1] in ('"', "'"):
1921
+ close = value.find(value[0], 1)
1922
+ return value[1:close] if close != -1 else value
1923
+ return re.sub(r"\s+#.*$", "", value).strip()
1924
+ return ""
1925
+
1926
+
1927
+ key = read_key()
1928
+ if not key:
1929
+ fail("INFERALL_API_KEY is not set. Export it or put it in the .env next to this file.")
1930
+
1931
+ request = urllib.request.Request(
1932
+ API_BASE + "/chat/completions",
1933
+ data=json.dumps({
1934
+ "model": MODEL,
1935
+ "messages": [{"role": "user", "content": "Explain InferAll in one sentence."}],
1936
+ }).encode(),
1937
+ headers={
1938
+ "Authorization": "Bearer " + key,
1939
+ "Content-Type": "application/json",
1940
+ "User-Agent": "inferall-hello-example",
1941
+ },
1942
+ method="POST",
1943
+ )
1944
+ try:
1945
+ try:
1946
+ res = urllib.request.urlopen(request, timeout=120)
1947
+ except urllib.error.HTTPError as e:
1948
+ res = e
1949
+ with res:
1950
+ status, raw = res.status, res.read()
1951
+ except (OSError, http.client.HTTPException) as e:
1952
+ fail("request failed: %s" % e)
1953
+
1954
+ text = raw.decode("utf-8", "replace")
1955
+ try:
1956
+ data = json.loads(text)
1957
+ except ValueError:
1958
+ data = None
1959
+ if not isinstance(data, dict):
1960
+ data = {}
1961
+
1962
+ if not 200 <= status < 300:
1963
+ err = data.get("error")
1964
+ detail = err.get("message") if isinstance(err, dict) else err
1965
+ detail = detail or data.get("message") or text.strip()[:300] or "no response body"
1966
+ fail("HTTP %d: %s" % (status, detail))
1967
+
1968
+ try:
1969
+ answer = data["choices"][0]["message"]["content"]
1970
+ except (KeyError, IndexError, TypeError):
1971
+ answer = None
1972
+ if not isinstance(answer, str) or not answer.strip():
1973
+ fail("the gateway answered HTTP %d but the response had no answer text" % status)
1974
+ print(answer)
1975
+ `
1976
+ : String.raw`// InferAll hello. Run: node inferall-hello.mjs (Node 18+, no packages needed)
1977
+ import { readFileSync } from "node:fs";
1978
+
1979
+ const API_BASE = ${JSON.stringify(API_BASE)};
1980
+ const MODEL = ${JSON.stringify(FREE_MODEL)};
1981
+
1982
+ function fail(message) {
1983
+ console.error("inferall: " + message);
1984
+ process.exit(1);
1985
+ }
1986
+
1987
+ // INFERALL_API_KEY from the environment first, else the .env beside this file.
1988
+ function readKey() {
1989
+ const fromEnv = (process.env.INFERALL_API_KEY || "").trim();
1990
+ if (fromEnv) return fromEnv;
1991
+ let text;
1992
+ try {
1993
+ text = readFileSync(new URL(".env", import.meta.url), "utf8");
1994
+ } catch (err) {
1995
+ if (err.code === "ENOENT") return "";
1996
+ fail("could not read .env: " + err.message);
1997
+ }
1998
+ for (const line of text.split(/\r?\n/)) {
1999
+ const m = line.match(/^\s*(?:export\s+)?INFERALL_API_KEY\s*=(.*)$/);
2000
+ if (m) {
2001
+ const value = m[1].trim();
2002
+ if (value[0] === '"' || value[0] === "'") {
2003
+ const close = value.indexOf(value[0], 1);
2004
+ return close === -1 ? value : value.slice(1, close);
2005
+ }
2006
+ return value.replace(/\s+#.*$/, "").trim();
2007
+ }
2008
+ }
2009
+ return "";
2010
+ }
2011
+
2012
+ const key = readKey();
2013
+ if (!key) fail("INFERALL_API_KEY is not set. Export it or put it in the .env next to this file.");
2014
+
2015
+ let res;
2016
+ let raw;
2017
+ try {
2018
+ res = await fetch(API_BASE + "/chat/completions", {
2019
+ method: "POST",
2020
+ headers: { Authorization: "Bearer " + key, "Content-Type": "application/json" },
2021
+ body: JSON.stringify({
2022
+ model: MODEL,
2023
+ messages: [{ role: "user", content: "Explain InferAll in one sentence." }],
2024
+ }),
2025
+ signal: AbortSignal.timeout(120_000),
2026
+ });
2027
+ raw = await res.text();
2028
+ } catch (err) {
2029
+ fail("request failed: " + (err.name === "TimeoutError" ? "timed out after 120s" : err.message));
2030
+ }
2031
+
2032
+ let data;
2033
+ try {
2034
+ data = JSON.parse(raw);
2035
+ } catch {}
2036
+
2037
+ if (!res.ok) {
2038
+ const e = data?.error;
2039
+ const detail = (typeof e === "string" ? e : e?.message) || data?.message || raw.trim().slice(0, 300) || res.statusText;
2040
+ fail("HTTP " + res.status + ": " + detail);
2041
+ }
2042
+
2043
+ const answer = data?.choices?.[0]?.message?.content;
2044
+ if (typeof answer !== "string" || !answer.trim()) {
2045
+ fail("the gateway answered HTTP " + res.status + " but the response had no answer text");
2046
+ }
2047
+ console.log(answer);
2048
+ `;
1883
2049
  try {
1884
2050
  writeFileSync(p, body);
1885
2051
  return { path: p, problem: null };
@@ -2858,6 +3024,10 @@ async function main() {
2858
3024
  console.log(` ${c.red("✗")} ${example.problem}`);
2859
3025
  envResult.problems.push(example.problem);
2860
3026
  }
3027
+ if (example.path) {
3028
+ const run = project.kind === "node" ? "node inferall-hello.mjs" : "python3 inferall_hello.py";
3029
+ console.log(` ${c.dim("run:")} ${c.bold(run)} ${c.dim("(reads INFERALL_API_KEY from the environment or .env)")}`);
3030
+ }
2861
3031
  if (project.kind === "node") console.log(` ${c.dim("optional:")} npm install @inferall/sdk`);
2862
3032
  else console.log(` ${c.dim("optional:")} pip install inferall-ai`);
2863
3033
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inferall/cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Install InferAll into your project in one command: npx @inferall/cli init",
5
5
  "bin": {
6
6
  "inferall": "bin/inferall.js"