pi-tensorix-provider 1.0.7 → 1.0.8

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
@@ -29,7 +29,6 @@ _32 models — DeepSeek, Kimi, GLM, Qwen, MiniMax, Llama — all **free** for [p
29
29
  | Chatterbox Turbo | 131K | ❌ | ❌ | **Free** | **Free** |
30
30
  | Deepseek R1 0528 | 131K | ❌ | ✅ | **Free** | **Free** |
31
31
  | Deepseek V3.2 | 164K | ❌ | ✅ | **Free** | **Free** |
32
- | Deepseek V4 Flash | 1.0M | ❌ | ✅ | **Free** | **Free** |
33
32
  | Deepseek V4 Flash 0731 | 131K | ❌ | ✅ | **Free** | **Free** |
34
33
  | Deepseek V4 Pro | 1.0M | ❌ | ✅ | **Free** | **Free** |
35
34
  | Glm 5 | 203K | ❌ | ✅ | **Free** | **Free** |
@@ -46,6 +45,7 @@ _32 models — DeepSeek, Kimi, GLM, Qwen, MiniMax, Llama — all **free** for [p
46
45
  | Qwen3 235b A22b 2507 | 262K | ❌ | ✅ | **Free** | **Free** |
47
46
  | Qwen3.5 122b A10b | 262K | ❌ | ✅ | **Free** | **Free** |
48
47
  | Qwen3.5 9b | 262K | ❌ | ✅ | **Free** | **Free** |
48
+ | Qwen3.8 2.4t A95b | 131K | ❌ | ❌ | **Free** | **Free** |
49
49
 
50
50
  ## Installation
51
51
 
@@ -83,5 +83,22 @@
83
83
  "contextWindow": 131072,
84
84
  "maxTokens": 131072,
85
85
  "deprecatedAt": "2026-08-01T02:00:23.121Z"
86
+ },
87
+ "deepseek/deepseek-v4-flash": {
88
+ "id": "deepseek/deepseek-v4-flash",
89
+ "name": "Deepseek V4 Flash",
90
+ "reasoning": false,
91
+ "input": [
92
+ "text"
93
+ ],
94
+ "cost": {
95
+ "input": 0,
96
+ "output": 0,
97
+ "cacheRead": 0,
98
+ "cacheWrite": 0
99
+ },
100
+ "contextWindow": 131072,
101
+ "maxTokens": 131072,
102
+ "deprecatedAt": "2026-08-09T02:00:20.006Z"
86
103
  }
87
104
  }
package/models.json CHANGED
@@ -47,22 +47,6 @@
47
47
  "contextWindow": 131072,
48
48
  "maxTokens": 131072
49
49
  },
50
- {
51
- "id": "deepseek/deepseek-v4-flash",
52
- "name": "Deepseek V4 Flash",
53
- "reasoning": false,
54
- "input": [
55
- "text"
56
- ],
57
- "cost": {
58
- "input": 0,
59
- "output": 0,
60
- "cacheRead": 0,
61
- "cacheWrite": 0
62
- },
63
- "contextWindow": 131072,
64
- "maxTokens": 131072
65
- },
66
50
  {
67
51
  "id": "deepseek/deepseek-v4-flash-0731",
68
52
  "name": "Deepseek V4 Flash 0731",
@@ -239,6 +223,22 @@
239
223
  "contextWindow": 131072,
240
224
  "maxTokens": 131072
241
225
  },
226
+ {
227
+ "id": "qwen/qwen3.8-2.4t-a95b",
228
+ "name": "Qwen3.8 2.4t A95b",
229
+ "reasoning": false,
230
+ "input": [
231
+ "text"
232
+ ],
233
+ "cost": {
234
+ "input": 0,
235
+ "output": 0,
236
+ "cacheRead": 0,
237
+ "cacheWrite": 0
238
+ },
239
+ "contextWindow": 131072,
240
+ "maxTokens": 131072
241
+ },
242
242
  {
243
243
  "id": "z-ai/glm-5",
244
244
  "name": "Glm 5",
package/package.json CHANGED
@@ -1,16 +1,9 @@
1
1
  {
2
2
  "name": "pi-tensorix-provider",
3
- "version": "1.0.7",
4
- "packageManager": "pnpm@11.18.0",
3
+ "version": "1.0.8",
5
4
  "description": "Tensorix provider extension for pi - Access DeepSeek, GLM, Kimi, Qwen, MiniMax, Llama, and more through the Tensorix API",
6
5
  "type": "module",
7
6
  "main": "index.ts",
8
- "scripts": {
9
- "clean": "echo 'nothing to clean'",
10
- "build": "echo 'nothing to build'",
11
- "check": "echo 'nothing to check'",
12
- "update-models": "node scripts/update-models.js"
13
- },
14
7
  "keywords": [
15
8
  "pi",
16
9
  "extension",
@@ -31,5 +24,11 @@
31
24
  "extensions": [
32
25
  "./index.ts"
33
26
  ]
27
+ },
28
+ "scripts": {
29
+ "clean": "echo 'nothing to clean'",
30
+ "build": "echo 'nothing to build'",
31
+ "check": "echo 'nothing to check'",
32
+ "update-models": "node scripts/update-models.js"
34
33
  }
35
- }
34
+ }
@@ -12,14 +12,120 @@
12
12
  * reasoning capability and adds compatibility overrides at runtime.
13
13
  *
14
14
  * Merge order for README: models.json → apply patch.json → merge custom-models.json
15
+ *
16
+ * API key: the stored `tensorix` credential in ~/.pi/agent/auth.json wins, then
17
+ * the TENSORIX_API_KEY environment variable. The script refuses to run without one.
15
18
  */
16
19
 
17
20
  import fs from 'fs';
21
+ import os from 'os';
22
+ import { execSync } from 'child_process';
18
23
  import path from 'path';
19
24
  import { fileURLToPath } from 'url';
20
25
 
21
26
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
22
27
 
28
+ // pi's agent directory: PI_CODING_AGENT_DIR (with ~ expansion) or ~/.pi/agent.
29
+ function piAgentDir() {
30
+ const envDir = process.env.PI_CODING_AGENT_DIR;
31
+ if (envDir) {
32
+ return envDir.startsWith('~/') || envDir === '~'
33
+ ? path.join(os.homedir(), envDir.slice(1))
34
+ : envDir;
35
+ }
36
+ return path.join(os.homedir(), '.pi', 'agent');
37
+ }
38
+
39
+ const AUTH_JSON_PATH = path.join(piAgentDir(), 'auth.json');
40
+
41
+ /**
42
+ * Resolve a configured value using pi's semantics (resolve-config-value.ts in
43
+ * pi-mono): "!command" runs via the shell (10s timeout) and uses trimmed
44
+ * stdout; "$VAR" / "${VAR}" interpolate environment variables ("$$" escapes a
45
+ * literal "$", "$!" a literal "!"); anything else is a literal. Returns
46
+ * undefined when a referenced env var is unset or a command fails.
47
+ */
48
+ function resolveConfigValue(config, env) {
49
+ if (typeof config !== 'string' || config.length === 0) return undefined;
50
+ if (config.startsWith('!')) {
51
+ try {
52
+ const out = execSync(config.slice(1), {
53
+ encoding: 'utf8',
54
+ timeout: 10000,
55
+ stdio: ['ignore', 'pipe', 'ignore'],
56
+ });
57
+ return out.trim() || undefined;
58
+ } catch {
59
+ return undefined;
60
+ }
61
+ }
62
+ const ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
63
+ let resolved = '';
64
+ let index = 0;
65
+ while (index < config.length) {
66
+ const dollar = config.indexOf('$', index);
67
+ if (dollar < 0) {
68
+ resolved += config.slice(index);
69
+ break;
70
+ }
71
+ resolved += config.slice(index, dollar);
72
+ const next = config[dollar + 1];
73
+ let name;
74
+ if (next === '$' || next === '!') {
75
+ resolved += next;
76
+ index = dollar + 2;
77
+ continue;
78
+ } else if (next === '{') {
79
+ const end = config.indexOf('}', dollar + 2);
80
+ if (end < 0) {
81
+ resolved += '$';
82
+ index = dollar + 1;
83
+ continue;
84
+ }
85
+ const inner = config.slice(dollar + 2, end);
86
+ if (!ENV_NAME_RE.test(inner)) {
87
+ resolved += config.slice(dollar, end + 1);
88
+ index = end + 1;
89
+ continue;
90
+ }
91
+ name = inner;
92
+ index = end + 1;
93
+ } else {
94
+ const match = config.slice(dollar + 1).match(/^[A-Za-z_][A-Za-z0-9_]*/);
95
+ if (!match) {
96
+ resolved += '$';
97
+ index = dollar + 1;
98
+ continue;
99
+ }
100
+ name = match[0];
101
+ index = dollar + 1 + name.length;
102
+ }
103
+ const value = (env && env[name]) || process.env[name] || undefined;
104
+ if (value === undefined) return undefined;
105
+ resolved += value;
106
+ }
107
+ return resolved;
108
+ }
109
+
110
+ /**
111
+ * The API key, resolved the way pi itself resolves it for this provider: the
112
+ * stored `tensorix` credential in ~/.pi/agent/auth.json wins, then
113
+ * the TENSORIX_API_KEY environment variable.
114
+ */
115
+ function resolveApiKey() {
116
+ try {
117
+ const auth = JSON.parse(fs.readFileSync(AUTH_JSON_PATH, 'utf8'));
118
+ const credential = auth?.tensorix;
119
+ if (credential && credential.type === 'api_key' && typeof credential.key === 'string') {
120
+ const key = resolveConfigValue(credential.key, credential.env);
121
+ if (key) return key;
122
+ }
123
+ } catch {
124
+ // Missing or unparseable auth.json: fall through to the env var.
125
+ }
126
+ return process.env.TENSORIX_API_KEY || undefined;
127
+ }
128
+
23
129
  const MODELS_API_URL = 'https://api.tensorix.ai/v1/models';
24
130
  const MODELS_JSON_PATH = path.join(__dirname, '..', 'models.json');
25
131
  const PATCH_JSON_PATH = path.join(__dirname, '..', 'patch.json');
@@ -263,9 +369,16 @@ async function main() {
263
369
  console.log(`Fetching models from ${MODELS_API_URL}...`);
264
370
 
265
371
  try {
266
- const apiKey = process.env.TENSORIX_API_KEY;
267
- const headers = {};
268
- if (apiKey) headers.Authorization = `Bearer ${apiKey}`;
372
+ // Without a key the API answers 200 with only the PUBLIC model list, so an
373
+ // unauthenticated run does not fail: it silently rewrites models.json (and
374
+ // the README table) with every gated model missing. Refuse to run rather
375
+ // than corrupt the generated files.
376
+ const apiKey = resolveApiKey();
377
+ if (!apiKey) {
378
+ console.error('❌ No API key found: no `tensorix` credential resolved from ' + AUTH_JSON_PATH + ' and TENSORIX_API_KEY is not set.');
379
+ process.exit(1);
380
+ }
381
+ const headers = { Authorization: `Bearer ${apiKey}` };
269
382
 
270
383
  const response = await fetch(MODELS_API_URL, { headers });
271
384
  if (!response.ok) {