jimeng-cli 0.3.1 → 0.3.3
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 +1 -1
- package/dist/{chunk-2IIK4X7C.js → chunk-ZMTQQZFH.js} +394 -321
- package/dist/{chunk-2IIK4X7C.js.map → chunk-ZMTQQZFH.js.map} +1 -1
- package/dist/cli/index.cjs +831 -706
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +324 -268
- package/dist/cli/index.js.map +1 -1
- package/dist/mcp/index.cjs +236 -166
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.js +6 -5
- package/dist/mcp/index.js.map +1 -1
- package/package.json +4 -4
package/dist/cli/index.js
CHANGED
|
@@ -11,13 +11,14 @@ import {
|
|
|
11
11
|
getTaskResponse,
|
|
12
12
|
getTokenLiveStatus,
|
|
13
13
|
logger_default,
|
|
14
|
+
maskToken,
|
|
14
15
|
parseRegionCode,
|
|
15
16
|
receiveCredit,
|
|
16
17
|
refreshAllTokenModels,
|
|
17
18
|
session_pool_default,
|
|
18
19
|
upscaleImage,
|
|
19
20
|
waitForTaskResponse
|
|
20
|
-
} from "../chunk-
|
|
21
|
+
} from "../chunk-ZMTQQZFH.js";
|
|
21
22
|
|
|
22
23
|
// src/cli/app.ts
|
|
23
24
|
import process2 from "process";
|
|
@@ -27,11 +28,6 @@ import { constants as fsConstants } from "fs";
|
|
|
27
28
|
import path from "path";
|
|
28
29
|
import { access, readFile } from "fs/promises";
|
|
29
30
|
import minimist from "minimist";
|
|
30
|
-
function maskToken(token) {
|
|
31
|
-
const n = token.length;
|
|
32
|
-
if (n <= 10) return "***";
|
|
33
|
-
return `${token.slice(0, 4)}...${token.slice(-4)}`;
|
|
34
|
-
}
|
|
35
31
|
function formatUnixMs(value) {
|
|
36
32
|
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return "-";
|
|
37
33
|
return new Date(value).toISOString();
|
|
@@ -44,14 +40,14 @@ function printTokenEntriesTable(items) {
|
|
|
44
40
|
console.log("token region enabled live lastCredit lastCheckedAt failures");
|
|
45
41
|
for (const item of items) {
|
|
46
42
|
if (!item || typeof item !== "object") continue;
|
|
47
|
-
const
|
|
48
|
-
const token = typeof
|
|
49
|
-
const region = typeof
|
|
50
|
-
const enabled = typeof
|
|
51
|
-
const live = typeof
|
|
52
|
-
const lastCredit = typeof
|
|
53
|
-
const lastCheckedAt = formatUnixMs(
|
|
54
|
-
const failures = typeof
|
|
43
|
+
const e = item;
|
|
44
|
+
const token = typeof e.token === "string" ? e.token : "-";
|
|
45
|
+
const region = typeof e.region === "string" ? e.region : "-";
|
|
46
|
+
const enabled = typeof e.enabled === "boolean" ? String(e.enabled) : "-";
|
|
47
|
+
const live = typeof e.live === "boolean" ? String(e.live) : "-";
|
|
48
|
+
const lastCredit = typeof e.lastCredit === "number" ? String(e.lastCredit) : "-";
|
|
49
|
+
const lastCheckedAt = formatUnixMs(e.lastCheckedAt);
|
|
50
|
+
const failures = typeof e.consecutiveFailures === "number" ? String(e.consecutiveFailures) : "-";
|
|
55
51
|
console.log(`${token} ${region} ${enabled} ${live} ${lastCredit} ${lastCheckedAt} ${failures}`);
|
|
56
52
|
}
|
|
57
53
|
}
|
|
@@ -90,6 +86,30 @@ ${usage}`);
|
|
|
90
86
|
}
|
|
91
87
|
return deduped;
|
|
92
88
|
}
|
|
89
|
+
function resolveTokenRegionPairs(explicitTokens, regionCode, deps, opts) {
|
|
90
|
+
if (explicitTokens.length > 0) {
|
|
91
|
+
return explicitTokens.map((token) => {
|
|
92
|
+
var _a;
|
|
93
|
+
const entryRegion = (_a = session_pool_default.getTokenEntry(token)) == null ? void 0 : _a.region;
|
|
94
|
+
const finalRegion = regionCode || entryRegion;
|
|
95
|
+
if (!finalRegion) {
|
|
96
|
+
deps.fail(`Missing region for token ${maskToken(token)}. Provide --region or register token in token-pool.`);
|
|
97
|
+
}
|
|
98
|
+
return { token, region: finalRegion };
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const requireLive = (opts == null ? void 0 : opts.requireLive) ?? true;
|
|
102
|
+
const entries = session_pool_default.getEntries(false).filter((item) => {
|
|
103
|
+
if (!item.enabled || !item.region) return false;
|
|
104
|
+
if (requireLive && item.live === false) return false;
|
|
105
|
+
if (regionCode && item.region !== regionCode) return false;
|
|
106
|
+
return true;
|
|
107
|
+
});
|
|
108
|
+
if (entries.length === 0) {
|
|
109
|
+
deps.fail("No token available. Provide --token or configure token-pool.");
|
|
110
|
+
}
|
|
111
|
+
return entries.map((item) => ({ token: item.token, region: item.region }));
|
|
112
|
+
}
|
|
93
113
|
function createTokenSubcommands(deps) {
|
|
94
114
|
const handleTokenCheck = async (argv) => {
|
|
95
115
|
const args = minimist(argv, {
|
|
@@ -101,74 +121,67 @@ function createTokenSubcommands(deps) {
|
|
|
101
121
|
console.log(usage);
|
|
102
122
|
return;
|
|
103
123
|
}
|
|
104
|
-
const
|
|
105
|
-
const regionCode = deps.parseRegionOrFail(
|
|
106
|
-
if (!regionCode) {
|
|
107
|
-
deps.fail("Missing region. Use --region cn/us/hk/jp/sg.");
|
|
108
|
-
}
|
|
109
|
-
const tokens = await collectTokensFromArgs(args, usage, deps, true);
|
|
110
|
-
if (!args.json) {
|
|
111
|
-
console.log(`Checking ${tokens.length} token(s)`);
|
|
112
|
-
}
|
|
124
|
+
const explicitRegion = deps.getSingleString(args, "region");
|
|
125
|
+
const regionCode = explicitRegion ? deps.parseRegionOrFail(explicitRegion) : void 0;
|
|
113
126
|
await deps.ensureTokenPoolReady();
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
const explicitTokens = await collectTokensFromArgs(args, usage, deps, false);
|
|
128
|
+
const pairs = resolveTokenRegionPairs(explicitTokens, regionCode, deps, { requireLive: false });
|
|
129
|
+
if (!args.json) {
|
|
130
|
+
console.log(`Checking ${pairs.length} token(s)`);
|
|
131
|
+
}
|
|
132
|
+
const results = await Promise.all(
|
|
133
|
+
pairs.map(async ({ token, region }) => {
|
|
134
|
+
const masked = maskToken(token);
|
|
135
|
+
try {
|
|
136
|
+
const live = await getTokenLiveStatus(token, buildRegionInfo(region));
|
|
137
|
+
await session_pool_default.syncTokenCheckResult(token, live);
|
|
138
|
+
if (live) {
|
|
139
|
+
if (!args.json) console.log(`[OK] ${masked} (${region}) live=true`);
|
|
140
|
+
return { token_masked: masked, region, live: true };
|
|
141
|
+
} else {
|
|
142
|
+
if (!args.json) console.log(`[FAIL] ${masked} (${region}) live=false`);
|
|
143
|
+
return { token_masked: masked, region, live: false };
|
|
144
|
+
}
|
|
145
|
+
} catch (error) {
|
|
146
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
147
|
+
if (!args.json) console.log(`[ERROR] ${masked} (${region}) ${message}`);
|
|
148
|
+
return { token_masked: masked, region, error: message };
|
|
127
149
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (!args.json) console.log(`[ERROR] ${masked} ${message}`);
|
|
133
|
-
results.push({ token_masked: masked, error: message });
|
|
134
|
-
}
|
|
135
|
-
}
|
|
150
|
+
})
|
|
151
|
+
);
|
|
152
|
+
const invalid = results.filter((r) => r.live === false).length;
|
|
153
|
+
const requestErrors = results.filter((r) => r.error).length;
|
|
136
154
|
if (args.json) {
|
|
137
155
|
deps.printCommandJson("token.check", results, {
|
|
138
|
-
total:
|
|
156
|
+
total: pairs.length,
|
|
139
157
|
invalid,
|
|
140
158
|
request_errors: requestErrors
|
|
141
159
|
});
|
|
142
160
|
} else {
|
|
143
|
-
console.log(`Summary: total=${
|
|
161
|
+
console.log(`Summary: total=${pairs.length} invalid=${invalid} request_errors=${requestErrors}`);
|
|
144
162
|
}
|
|
145
163
|
if (requestErrors > 0) process.exit(3);
|
|
146
164
|
if (invalid > 0) process.exit(2);
|
|
147
165
|
};
|
|
148
166
|
const handleTokenList = async (argv) => {
|
|
149
|
-
const args = minimist(argv, {
|
|
150
|
-
boolean: ["help", "json"]
|
|
151
|
-
});
|
|
152
|
-
const usage = deps.getUsage("list");
|
|
167
|
+
const args = minimist(argv, { boolean: ["help", "json"] });
|
|
153
168
|
if (args.help) {
|
|
154
|
-
console.log(
|
|
169
|
+
console.log(deps.getUsage("list"));
|
|
155
170
|
return;
|
|
156
171
|
}
|
|
157
172
|
await deps.ensureTokenPoolReady();
|
|
158
|
-
const
|
|
173
|
+
const snapshot = buildTokenPoolSnapshot();
|
|
159
174
|
if (args.json) {
|
|
160
|
-
deps.printCommandJson("token.list",
|
|
175
|
+
deps.printCommandJson("token.list", snapshot);
|
|
161
176
|
return;
|
|
162
177
|
}
|
|
163
|
-
const body =
|
|
164
|
-
|
|
165
|
-
if (summary && typeof summary === "object") {
|
|
178
|
+
const body = snapshot && typeof snapshot === "object" ? snapshot : {};
|
|
179
|
+
if (body.summary && typeof body.summary === "object") {
|
|
166
180
|
console.log("Summary:");
|
|
167
|
-
deps.printJson(summary);
|
|
181
|
+
deps.printJson(body.summary);
|
|
168
182
|
}
|
|
169
|
-
const items = Array.isArray(body.items) ? body.items : [];
|
|
170
183
|
console.log("Entries:");
|
|
171
|
-
printTokenEntriesTable(items);
|
|
184
|
+
printTokenEntriesTable(Array.isArray(body.items) ? body.items : []);
|
|
172
185
|
};
|
|
173
186
|
const handleTokenPointsOrReceive = async (argv, action) => {
|
|
174
187
|
const args = minimist(argv, {
|
|
@@ -180,47 +193,42 @@ function createTokenSubcommands(deps) {
|
|
|
180
193
|
console.log(usage);
|
|
181
194
|
return;
|
|
182
195
|
}
|
|
183
|
-
const
|
|
184
|
-
const regionCode = deps.parseRegionOrFail(
|
|
196
|
+
const regionArg = deps.getSingleString(args, "region");
|
|
197
|
+
const regionCode = regionArg ? deps.parseRegionOrFail(regionArg) : void 0;
|
|
185
198
|
await deps.ensureTokenPoolReady();
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
199
|
+
const explicitTokens = await collectTokensFromArgs(args, usage, deps, false);
|
|
200
|
+
const pairs = resolveTokenRegionPairs(explicitTokens, regionCode, deps);
|
|
201
|
+
const toErrorResult = (token, region, error) => ({
|
|
202
|
+
token_masked: maskToken(token),
|
|
203
|
+
region,
|
|
204
|
+
error: error instanceof Error ? error.message : String(error)
|
|
205
|
+
});
|
|
206
|
+
const fetchPoints = async ({ token, region }) => {
|
|
207
|
+
try {
|
|
208
|
+
return { token_masked: maskToken(token), region, points: await getCredit(token, buildRegionInfo(region)) };
|
|
209
|
+
} catch (error) {
|
|
210
|
+
return toErrorResult(token, region, error);
|
|
193
211
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
token: item.token,
|
|
202
|
-
points: await getCredit(item.token, buildRegionInfo(item.region))
|
|
203
|
-
}))
|
|
204
|
-
) : await Promise.all(
|
|
205
|
-
resolvedTokens.map(async (item) => {
|
|
206
|
-
const currentCredit = await getCredit(item.token, buildRegionInfo(item.region));
|
|
207
|
-
if (currentCredit.totalCredit <= 0) {
|
|
208
|
-
try {
|
|
209
|
-
await receiveCredit(item.token, buildRegionInfo(item.region));
|
|
210
|
-
const updatedCredit = await getCredit(item.token, buildRegionInfo(item.region));
|
|
211
|
-
return { token: item.token, credits: updatedCredit, received: true };
|
|
212
|
-
} catch (error) {
|
|
213
|
-
return {
|
|
214
|
-
token: item.token,
|
|
215
|
-
credits: currentCredit,
|
|
216
|
-
received: false,
|
|
217
|
-
error: (error == null ? void 0 : error.message) || String(error)
|
|
218
|
-
};
|
|
219
|
-
}
|
|
212
|
+
};
|
|
213
|
+
const processReceive = async ({ token, region }) => {
|
|
214
|
+
const regionInfo = buildRegionInfo(region);
|
|
215
|
+
try {
|
|
216
|
+
const currentCredit = await getCredit(token, regionInfo);
|
|
217
|
+
if (currentCredit.totalCredit > 0) {
|
|
218
|
+
return { token_masked: maskToken(token), region, credits: currentCredit, received: false };
|
|
220
219
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
220
|
+
try {
|
|
221
|
+
await receiveCredit(token, regionInfo);
|
|
222
|
+
const updatedCredit = await getCredit(token, regionInfo);
|
|
223
|
+
return { token_masked: maskToken(token), region, credits: updatedCredit, received: true };
|
|
224
|
+
} catch (error) {
|
|
225
|
+
return { token_masked: maskToken(token), region, credits: currentCredit, received: false, ...toErrorResult(token, region, error) };
|
|
226
|
+
}
|
|
227
|
+
} catch (error) {
|
|
228
|
+
return toErrorResult(token, region, error);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
const payload = action === "points" ? await Promise.all(pairs.map(fetchPoints)) : await Promise.all(pairs.map(processReceive));
|
|
224
232
|
if (args.json) {
|
|
225
233
|
deps.printCommandJson(`token.${action}`, payload);
|
|
226
234
|
return;
|
|
@@ -237,28 +245,32 @@ function createTokenSubcommands(deps) {
|
|
|
237
245
|
console.log(usage);
|
|
238
246
|
return;
|
|
239
247
|
}
|
|
240
|
-
const region = deps.getRegionWithDefault(args);
|
|
241
248
|
await deps.ensureTokenPoolReady();
|
|
242
249
|
const tokens = await collectTokensFromArgs(args, usage, deps, true);
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
250
|
+
let payload;
|
|
251
|
+
let jsonMeta;
|
|
252
|
+
if (action === "add") {
|
|
253
|
+
const region = deps.getRegionWithDefault(args);
|
|
254
|
+
const regionCode = deps.parseRegionOrFail(region);
|
|
255
|
+
payload = {
|
|
256
|
+
...await session_pool_default.addTokens(tokens, { defaultRegion: regionCode || void 0 }),
|
|
257
|
+
summary: session_pool_default.getSummary()
|
|
258
|
+
};
|
|
259
|
+
jsonMeta = { region };
|
|
260
|
+
} else {
|
|
261
|
+
payload = {
|
|
262
|
+
...await session_pool_default.removeTokens(tokens),
|
|
263
|
+
summary: session_pool_default.getSummary()
|
|
264
|
+
};
|
|
265
|
+
}
|
|
251
266
|
if (args.json) {
|
|
252
|
-
deps.printCommandJson(`token.${action}`, deps.unwrapBody(payload),
|
|
267
|
+
deps.printCommandJson(`token.${action}`, deps.unwrapBody(payload), jsonMeta);
|
|
253
268
|
return;
|
|
254
269
|
}
|
|
255
270
|
deps.printJson(deps.unwrapBody(payload));
|
|
256
271
|
};
|
|
257
272
|
const handleTokenEnableOrDisable = async (argv, action) => {
|
|
258
|
-
const args = minimist(argv, {
|
|
259
|
-
string: ["token"],
|
|
260
|
-
boolean: ["help", "json"]
|
|
261
|
-
});
|
|
273
|
+
const args = minimist(argv, { string: ["token"], boolean: ["help", "json"] });
|
|
262
274
|
const usage = deps.getUsage(action);
|
|
263
275
|
if (args.help) {
|
|
264
276
|
console.log(usage);
|
|
@@ -280,44 +292,37 @@ function createTokenSubcommands(deps) {
|
|
|
280
292
|
deps.printJson(deps.unwrapBody(payload));
|
|
281
293
|
};
|
|
282
294
|
const handleTokenPool = async (argv) => {
|
|
283
|
-
const args = minimist(argv, {
|
|
284
|
-
boolean: ["help", "json"]
|
|
285
|
-
});
|
|
286
|
-
const usage = deps.getUsage("pool");
|
|
295
|
+
const args = minimist(argv, { boolean: ["help", "json"] });
|
|
287
296
|
if (args.help) {
|
|
288
|
-
console.log(
|
|
297
|
+
console.log(deps.getUsage("pool"));
|
|
289
298
|
return;
|
|
290
299
|
}
|
|
291
300
|
await deps.ensureTokenPoolReady();
|
|
292
|
-
const
|
|
301
|
+
const snapshot = buildTokenPoolSnapshot();
|
|
293
302
|
if (args.json) {
|
|
294
|
-
deps.printCommandJson("token.pool",
|
|
303
|
+
deps.printCommandJson("token.pool", snapshot);
|
|
295
304
|
return;
|
|
296
305
|
}
|
|
297
|
-
const body =
|
|
306
|
+
const body = snapshot && typeof snapshot === "object" ? snapshot : {};
|
|
298
307
|
console.log("Summary:");
|
|
299
308
|
deps.printJson(body.summary ?? {});
|
|
300
309
|
console.log("Entries:");
|
|
301
310
|
printTokenEntriesTable(Array.isArray(body.items) ? body.items : []);
|
|
302
311
|
};
|
|
303
312
|
const handleTokenPoolCheckOrReload = async (argv, action) => {
|
|
304
|
-
const args = minimist(argv, {
|
|
305
|
-
boolean: ["help", "json"]
|
|
306
|
-
});
|
|
307
|
-
const usage = deps.getUsage(action);
|
|
313
|
+
const args = minimist(argv, { boolean: ["help", "json"] });
|
|
308
314
|
if (args.help) {
|
|
309
|
-
console.log(
|
|
315
|
+
console.log(deps.getUsage(action));
|
|
310
316
|
return;
|
|
311
317
|
}
|
|
312
318
|
await deps.ensureTokenPoolReady();
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
summary: session_pool_default.getSummary()
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
summary: session_pool_default.getSummary(),
|
|
319
|
-
|
|
320
|
-
});
|
|
319
|
+
let payload;
|
|
320
|
+
if (action === "pool-check") {
|
|
321
|
+
payload = { ...await session_pool_default.runHealthCheck(), summary: session_pool_default.getSummary() };
|
|
322
|
+
} else {
|
|
323
|
+
session_pool_default.reloadFromDisk();
|
|
324
|
+
payload = { reloaded: true, summary: session_pool_default.getSummary(), items: buildTokenPoolSnapshot().items };
|
|
325
|
+
}
|
|
321
326
|
if (args.json) {
|
|
322
327
|
deps.printCommandJson(`token.${action}`, deps.unwrapBody(payload));
|
|
323
328
|
return;
|
|
@@ -334,12 +339,12 @@ function createTokenSubcommands(deps) {
|
|
|
334
339
|
},
|
|
335
340
|
{
|
|
336
341
|
name: "check",
|
|
337
|
-
description: "Validate tokens
|
|
338
|
-
usageLine: " jimeng token check
|
|
342
|
+
description: "Validate tokens",
|
|
343
|
+
usageLine: " jimeng token check [options]",
|
|
339
344
|
options: [
|
|
340
|
-
" --token <token> Token, can be repeated",
|
|
345
|
+
" --token <token> Token, can be repeated (default: all enabled tokens)",
|
|
341
346
|
" --token-file <path> Read tokens from file (one per line, # for comments)",
|
|
342
|
-
" --region <region>
|
|
347
|
+
" --region <region> Override region (default: token's registered region)",
|
|
343
348
|
deps.jsonOption,
|
|
344
349
|
deps.helpOption
|
|
345
350
|
],
|
|
@@ -347,12 +352,12 @@ function createTokenSubcommands(deps) {
|
|
|
347
352
|
},
|
|
348
353
|
{
|
|
349
354
|
name: "points",
|
|
350
|
-
description: "Query token points
|
|
355
|
+
description: "Query token points",
|
|
351
356
|
usageLine: " jimeng token points [options]",
|
|
352
357
|
options: [
|
|
353
358
|
" --token <token> Token, can be repeated",
|
|
354
359
|
" --token-file <path> Read tokens from file (one per line, # for comments)",
|
|
355
|
-
" --region <region> Filter tokens by
|
|
360
|
+
" --region <region> Filter tokens by region (cn/us/hk/jp/sg)",
|
|
356
361
|
deps.jsonOption,
|
|
357
362
|
deps.helpOption
|
|
358
363
|
],
|
|
@@ -360,12 +365,12 @@ function createTokenSubcommands(deps) {
|
|
|
360
365
|
},
|
|
361
366
|
{
|
|
362
367
|
name: "receive",
|
|
363
|
-
description: "Receive token credits
|
|
368
|
+
description: "Receive token credits",
|
|
364
369
|
usageLine: " jimeng token receive [options]",
|
|
365
370
|
options: [
|
|
366
371
|
" --token <token> Token, can be repeated",
|
|
367
372
|
" --token-file <path> Read tokens from file (one per line, # for comments)",
|
|
368
|
-
" --region <region> Filter tokens by
|
|
373
|
+
" --region <region> Filter tokens by region (cn/us/hk/jp/sg)",
|
|
369
374
|
deps.jsonOption,
|
|
370
375
|
deps.helpOption
|
|
371
376
|
],
|
|
@@ -497,11 +502,71 @@ function printTaskInfo(task, deps) {
|
|
|
497
502
|
deps.printJson(task.data);
|
|
498
503
|
}
|
|
499
504
|
}
|
|
505
|
+
function outputTaskResult(command, normalized, isJson, deps) {
|
|
506
|
+
const taskInfo = collectTaskInfo(normalized, deps);
|
|
507
|
+
if (!taskInfo) {
|
|
508
|
+
const body = deps.unwrapBody(normalized);
|
|
509
|
+
if (isJson) deps.printCommandJson(command, body);
|
|
510
|
+
else deps.printJson(body);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
if (isJson) deps.printCommandJson(command, taskInfo);
|
|
514
|
+
else printTaskInfo(taskInfo, deps);
|
|
515
|
+
}
|
|
516
|
+
function resolveSingleQueryToken(explicitToken, explicitRegion, deps) {
|
|
517
|
+
var _a;
|
|
518
|
+
const regionCode = explicitRegion ? deps.parseRegionOrFail(explicitRegion) : void 0;
|
|
519
|
+
if (explicitToken) {
|
|
520
|
+
const poolRegion = (_a = session_pool_default.getTokenEntry(explicitToken)) == null ? void 0 : _a.region;
|
|
521
|
+
const region = regionCode || poolRegion;
|
|
522
|
+
if (!region) {
|
|
523
|
+
deps.fail("Missing region for token. Provide --region or register token in token-pool.");
|
|
524
|
+
}
|
|
525
|
+
return { token: explicitToken, region };
|
|
526
|
+
}
|
|
527
|
+
if (!regionCode) {
|
|
528
|
+
const entry = session_pool_default.getEntries(false).find(
|
|
529
|
+
(item) => item.enabled && item.live !== false && item.region
|
|
530
|
+
);
|
|
531
|
+
if (!entry) {
|
|
532
|
+
deps.fail("No token available. Provide --token, --region, or --all.");
|
|
533
|
+
}
|
|
534
|
+
return { token: entry.token, region: entry.region };
|
|
535
|
+
}
|
|
536
|
+
return { token: void 0, region: regionCode };
|
|
537
|
+
}
|
|
538
|
+
function printModelIds(models) {
|
|
539
|
+
for (const item of models) {
|
|
540
|
+
if (!item || typeof item !== "object") continue;
|
|
541
|
+
const id = item.id;
|
|
542
|
+
if (typeof id === "string" && id.length > 0) console.log(id);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function printModelVerbose(models) {
|
|
546
|
+
for (const item of models) {
|
|
547
|
+
if (!item || typeof item !== "object") continue;
|
|
548
|
+
const m = item;
|
|
549
|
+
const id = typeof m.id === "string" ? m.id : "";
|
|
550
|
+
if (!id) continue;
|
|
551
|
+
const type = typeof m.model_type === "string" ? m.model_type : "-";
|
|
552
|
+
const desc = typeof m.description === "string" ? m.description : "-";
|
|
553
|
+
const caps = Array.isArray(m.capabilities) ? m.capabilities.filter((c) => typeof c === "string").join(",") : "-";
|
|
554
|
+
console.log(`${id} [${type}] ${desc}`);
|
|
555
|
+
console.log(` capabilities: ${caps}`);
|
|
556
|
+
const params = m.params;
|
|
557
|
+
if (params && typeof params === "object") {
|
|
558
|
+
for (const [key, vals] of Object.entries(params)) {
|
|
559
|
+
if (Array.isArray(vals)) {
|
|
560
|
+
console.log(` ${key}: ${vals.join(", ")}`);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
console.log("");
|
|
565
|
+
}
|
|
566
|
+
}
|
|
500
567
|
function createQueryCommandHandlers(deps) {
|
|
501
568
|
const handleModelsRefresh = async (argv) => {
|
|
502
|
-
const args = minimist2(argv, {
|
|
503
|
-
boolean: ["help", "json"]
|
|
504
|
-
});
|
|
569
|
+
const args = minimist2(argv, { boolean: ["help", "json"] });
|
|
505
570
|
if (args.help) {
|
|
506
571
|
console.log(deps.usageModelsRefresh());
|
|
507
572
|
return;
|
|
@@ -529,48 +594,72 @@ function createQueryCommandHandlers(deps) {
|
|
|
529
594
|
const handleModelsList = async (argv) => {
|
|
530
595
|
const args = minimist2(argv, {
|
|
531
596
|
string: ["region", "token"],
|
|
532
|
-
boolean: ["help", "json", "verbose"]
|
|
597
|
+
boolean: ["help", "json", "verbose", "all"]
|
|
533
598
|
});
|
|
534
599
|
if (args.help) {
|
|
535
600
|
console.log(deps.usageModelsList());
|
|
536
601
|
return;
|
|
537
602
|
}
|
|
538
|
-
const
|
|
539
|
-
const
|
|
540
|
-
const
|
|
603
|
+
const isJson = Boolean(args.json);
|
|
604
|
+
const isVerbose = Boolean(args.verbose);
|
|
605
|
+
const explicitRegion = deps.getSingleString(args, "region");
|
|
606
|
+
const explicitToken = deps.getSingleString(args, "token");
|
|
541
607
|
await deps.ensureTokenPoolReady();
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
608
|
+
if (args.all) {
|
|
609
|
+
const entries = session_pool_default.getEntries(false).filter(
|
|
610
|
+
(item) => item.enabled && item.live !== false && item.region
|
|
611
|
+
);
|
|
612
|
+
if (entries.length === 0) {
|
|
613
|
+
deps.fail("No enabled+live tokens with region found in pool.");
|
|
614
|
+
}
|
|
615
|
+
const results = await Promise.all(
|
|
616
|
+
entries.map(async (entry) => {
|
|
617
|
+
const masked = maskToken(entry.token);
|
|
618
|
+
try {
|
|
619
|
+
const direct2 = await getLiveModels(`Bearer ${entry.token}`, entry.region);
|
|
620
|
+
return {
|
|
621
|
+
token: masked,
|
|
622
|
+
region: entry.region,
|
|
623
|
+
source: direct2.source,
|
|
624
|
+
models: isVerbose ? direct2.data : direct2.data.map((m) => m.id)
|
|
625
|
+
};
|
|
626
|
+
} catch (error) {
|
|
627
|
+
return { token: masked, region: entry.region, error: error instanceof Error ? error.message : String(error) };
|
|
628
|
+
}
|
|
629
|
+
})
|
|
630
|
+
);
|
|
631
|
+
if (isJson) {
|
|
632
|
+
deps.printCommandJson("models.list", results);
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
635
|
+
for (const r of results) {
|
|
636
|
+
console.log(`[${r.region}] ${r.token}`);
|
|
637
|
+
if (r.error) {
|
|
638
|
+
console.log(` error: ${r.error}`);
|
|
639
|
+
} else if (isVerbose) {
|
|
640
|
+
printModelVerbose(r.models);
|
|
641
|
+
} else {
|
|
642
|
+
for (const id of r.models) console.log(` ${id}`);
|
|
643
|
+
}
|
|
644
|
+
console.log("");
|
|
645
|
+
}
|
|
547
646
|
return;
|
|
548
647
|
}
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
const model = item;
|
|
558
|
-
const id = typeof model.id === "string" ? model.id : "";
|
|
559
|
-
if (!id) continue;
|
|
560
|
-
const modelType = typeof model.model_type === "string" ? model.model_type : "-";
|
|
561
|
-
const description = typeof model.description === "string" ? model.description : "-";
|
|
562
|
-
const capabilities = Array.isArray(model.capabilities) ? model.capabilities.filter((cap) => typeof cap === "string").join(",") : "-";
|
|
563
|
-
console.log(`${id} type=${modelType} desc=${description} capabilities=${capabilities}`);
|
|
564
|
-
}
|
|
648
|
+
const { token, region } = resolveSingleQueryToken(explicitToken, explicitRegion, deps);
|
|
649
|
+
const direct = await getLiveModels(token ? `Bearer ${token}` : void 0, region);
|
|
650
|
+
const models = direct.data;
|
|
651
|
+
if (isJson) {
|
|
652
|
+
deps.printCommandJson("models.list", { object: "list", data: models }, {
|
|
653
|
+
region: region || null,
|
|
654
|
+
token: token ? `${token.slice(0, 4)}...` : null
|
|
655
|
+
});
|
|
565
656
|
return;
|
|
566
657
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
const id = item.id;
|
|
570
|
-
if (typeof id === "string" && id.length > 0) {
|
|
571
|
-
console.log(id);
|
|
572
|
-
}
|
|
658
|
+
if (models.length === 0) {
|
|
659
|
+
deps.fail("No models found.");
|
|
573
660
|
}
|
|
661
|
+
if (isVerbose) printModelVerbose(models);
|
|
662
|
+
else printModelIds(models);
|
|
574
663
|
};
|
|
575
664
|
const handleTaskGet = async (argv) => {
|
|
576
665
|
const args = minimist2(argv, {
|
|
@@ -587,42 +676,13 @@ function createQueryCommandHandlers(deps) {
|
|
|
587
676
|
${deps.usageTaskGet()}`);
|
|
588
677
|
const type = parseTaskTypeOrFail(deps.getSingleString(args, "type"), deps);
|
|
589
678
|
const responseFormat = parseResponseFormatOrFail(deps.getSingleString(args, "response-format"), deps);
|
|
590
|
-
const
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
const pick = await deps.pickDirectTokenForTask(token, region);
|
|
594
|
-
const normalized = await getTaskResponse(
|
|
595
|
-
taskId,
|
|
596
|
-
pick.token,
|
|
597
|
-
buildRegionInfo(pick.region),
|
|
598
|
-
{
|
|
599
|
-
type,
|
|
600
|
-
responseFormat
|
|
601
|
-
}
|
|
602
|
-
);
|
|
603
|
-
const taskInfo = collectTaskInfo(normalized, deps);
|
|
604
|
-
if (!taskInfo) {
|
|
605
|
-
if (isJson) {
|
|
606
|
-
deps.printCommandJson("task.get", deps.unwrapBody(normalized));
|
|
607
|
-
} else {
|
|
608
|
-
deps.printJson(deps.unwrapBody(normalized));
|
|
609
|
-
}
|
|
610
|
-
return;
|
|
611
|
-
}
|
|
612
|
-
if (isJson) deps.printCommandJson("task.get", taskInfo);
|
|
613
|
-
else printTaskInfo(taskInfo, deps);
|
|
679
|
+
const pick = await deps.pickDirectTokenForTask(deps.getSingleString(args, "token"), deps.getSingleString(args, "region"));
|
|
680
|
+
const normalized = await getTaskResponse(taskId, pick.token, buildRegionInfo(pick.region), { type, responseFormat });
|
|
681
|
+
outputTaskResult("task.get", normalized, Boolean(args.json), deps);
|
|
614
682
|
};
|
|
615
683
|
const handleTaskWait = async (argv) => {
|
|
616
684
|
const args = minimist2(argv, {
|
|
617
|
-
string: [
|
|
618
|
-
"token",
|
|
619
|
-
"region",
|
|
620
|
-
"task-id",
|
|
621
|
-
"type",
|
|
622
|
-
"response-format",
|
|
623
|
-
"wait-timeout-seconds",
|
|
624
|
-
"poll-interval-ms"
|
|
625
|
-
],
|
|
685
|
+
string: ["token", "region", "task-id", "type", "response-format", "wait-timeout-seconds", "poll-interval-ms"],
|
|
626
686
|
boolean: ["help", "json"]
|
|
627
687
|
});
|
|
628
688
|
if (args.help) {
|
|
@@ -633,41 +693,18 @@ ${deps.usageTaskGet()}`);
|
|
|
633
693
|
if (!taskId) deps.fail(`Missing required --task-id.
|
|
634
694
|
|
|
635
695
|
${deps.usageTaskWait()}`);
|
|
636
|
-
const token = deps.getSingleString(args, "token");
|
|
637
|
-
const region = deps.getRegionWithDefault(args);
|
|
638
|
-
const isJson = Boolean(args.json);
|
|
639
|
-
const body = {};
|
|
640
696
|
const type = parseTaskTypeOrFail(deps.getSingleString(args, "type"), deps);
|
|
641
697
|
const responseFormat = parseResponseFormatOrFail(deps.getSingleString(args, "response-format"), deps);
|
|
642
|
-
if (type) body.type = type;
|
|
643
|
-
body.response_format = responseFormat;
|
|
644
698
|
const waitTimeoutSeconds = parsePositiveNumberOption(args, "wait-timeout-seconds", deps);
|
|
645
|
-
if (waitTimeoutSeconds !== void 0) body.wait_timeout_seconds = waitTimeoutSeconds;
|
|
646
699
|
const pollIntervalMs = parsePositiveNumberOption(args, "poll-interval-ms", deps);
|
|
647
|
-
|
|
648
|
-
const
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
responseFormat,
|
|
656
|
-
waitTimeoutSeconds: typeof body.wait_timeout_seconds === "number" ? body.wait_timeout_seconds : void 0,
|
|
657
|
-
pollIntervalMs: typeof body.poll_interval_ms === "number" ? body.poll_interval_ms : void 0
|
|
658
|
-
}
|
|
659
|
-
);
|
|
660
|
-
const taskInfo = collectTaskInfo(normalized, deps);
|
|
661
|
-
if (!taskInfo) {
|
|
662
|
-
if (isJson) {
|
|
663
|
-
deps.printCommandJson("task.wait", deps.unwrapBody(normalized));
|
|
664
|
-
} else {
|
|
665
|
-
deps.printJson(deps.unwrapBody(normalized));
|
|
666
|
-
}
|
|
667
|
-
return;
|
|
668
|
-
}
|
|
669
|
-
if (isJson) deps.printCommandJson("task.wait", taskInfo);
|
|
670
|
-
else printTaskInfo(taskInfo, deps);
|
|
700
|
+
const pick = await deps.pickDirectTokenForTask(deps.getSingleString(args, "token"), deps.getSingleString(args, "region"));
|
|
701
|
+
const normalized = await waitForTaskResponse(taskId, pick.token, buildRegionInfo(pick.region), {
|
|
702
|
+
type,
|
|
703
|
+
responseFormat,
|
|
704
|
+
waitTimeoutSeconds,
|
|
705
|
+
pollIntervalMs
|
|
706
|
+
});
|
|
707
|
+
outputTaskResult("task.wait", normalized, Boolean(args.json), deps);
|
|
671
708
|
};
|
|
672
709
|
const handleTaskList = async (argv) => {
|
|
673
710
|
const args = minimist2(argv, {
|
|
@@ -678,24 +715,18 @@ ${deps.usageTaskWait()}`);
|
|
|
678
715
|
console.log(deps.usageTaskList());
|
|
679
716
|
return;
|
|
680
717
|
}
|
|
681
|
-
const token = deps.getSingleString(args, "token");
|
|
682
|
-
const region = deps.getRegionWithDefault(args);
|
|
683
718
|
const type = deps.getSingleString(args, "type");
|
|
684
|
-
const countRaw = deps.getSingleString(args, "count");
|
|
685
|
-
const count = countRaw ? Number(countRaw) : 20;
|
|
686
|
-
const isJson = Boolean(args.json);
|
|
687
719
|
if (type && type !== "image" && type !== "video" && type !== "all") {
|
|
688
720
|
deps.fail(`Invalid --type: ${type}. Use image, video, or all.`);
|
|
689
721
|
}
|
|
690
|
-
const
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
);
|
|
722
|
+
const countRaw = deps.getSingleString(args, "count");
|
|
723
|
+
const count = countRaw ? Number(countRaw) : 20;
|
|
724
|
+
const isJson = Boolean(args.json);
|
|
725
|
+
const pick = await deps.pickDirectTokenForTask(deps.getSingleString(args, "token"), deps.getSingleString(args, "region"));
|
|
726
|
+
const result = await getAssetList(pick.token, buildRegionInfo(pick.region), {
|
|
727
|
+
count: Number.isFinite(count) && count > 0 ? count : 20,
|
|
728
|
+
type
|
|
729
|
+
});
|
|
699
730
|
if (isJson) {
|
|
700
731
|
deps.printCommandJson("task.list", {
|
|
701
732
|
has_more: result.hasMore,
|
|
@@ -714,9 +745,7 @@ ${deps.usageTaskWait()}`);
|
|
|
714
745
|
const modelShort = item.modelName || item.modelReqKey || "-";
|
|
715
746
|
const promptShort = item.prompt.length > 50 ? item.prompt.slice(0, 50) + "..." : item.prompt;
|
|
716
747
|
console.log(`${item.id} ${typeLabel} ${statusLabel.padEnd(4)} ${time} ${modelShort.padEnd(20)} ${promptShort}`);
|
|
717
|
-
if (item.imageUrl) {
|
|
718
|
-
console.log(` ${item.imageUrl}`);
|
|
719
|
-
}
|
|
748
|
+
if (item.imageUrl) console.log(` ${item.imageUrl}`);
|
|
720
749
|
}
|
|
721
750
|
};
|
|
722
751
|
return {
|
|
@@ -726,12 +755,12 @@ ${deps.usageTaskWait()}`);
|
|
|
726
755
|
handleTaskWait,
|
|
727
756
|
handleTaskList,
|
|
728
757
|
printTaskInfo: (task) => {
|
|
729
|
-
const
|
|
730
|
-
if (!
|
|
758
|
+
const info = collectTaskInfo(task, deps);
|
|
759
|
+
if (!info) {
|
|
731
760
|
deps.printJson(task);
|
|
732
761
|
return;
|
|
733
762
|
}
|
|
734
|
-
printTaskInfo(
|
|
763
|
+
printTaskInfo(info, deps);
|
|
735
764
|
}
|
|
736
765
|
};
|
|
737
766
|
}
|
|
@@ -965,7 +994,19 @@ function applyWaitOptionsToBody(body, args, deps, includeWaitFlag = true) {
|
|
|
965
994
|
return wait;
|
|
966
995
|
}
|
|
967
996
|
async function downloadBinary(url, deps) {
|
|
968
|
-
const
|
|
997
|
+
const controller = new AbortController();
|
|
998
|
+
const timeout = setTimeout(() => controller.abort(), 12e4);
|
|
999
|
+
let response;
|
|
1000
|
+
try {
|
|
1001
|
+
response = await fetch(url, { signal: controller.signal });
|
|
1002
|
+
} catch (err) {
|
|
1003
|
+
clearTimeout(timeout);
|
|
1004
|
+
if (err.name === "AbortError") {
|
|
1005
|
+
deps.fail(`Download timed out after 120s: ${url}`);
|
|
1006
|
+
}
|
|
1007
|
+
throw err;
|
|
1008
|
+
}
|
|
1009
|
+
clearTimeout(timeout);
|
|
969
1010
|
if (!response.ok) {
|
|
970
1011
|
deps.fail(`Download failed (${response.status}): ${url}`);
|
|
971
1012
|
}
|
|
@@ -1034,6 +1075,9 @@ function createMediaCommandHandlers(deps) {
|
|
|
1034
1075
|
if (!Number.isFinite(parsed)) {
|
|
1035
1076
|
deps.fail(`Invalid --sample-strength: ${sampleStrengthRaw}`);
|
|
1036
1077
|
}
|
|
1078
|
+
if (parsed < 0 || parsed > 1) {
|
|
1079
|
+
deps.fail(`Invalid --sample-strength: ${sampleStrengthRaw} (must be between 0 and 1)`);
|
|
1080
|
+
}
|
|
1037
1081
|
body.sample_strength = parsed;
|
|
1038
1082
|
}
|
|
1039
1083
|
const pick = await deps.pickDirectTokenForGeneration(
|
|
@@ -1350,11 +1394,14 @@ import { execSync, spawn } from "child_process";
|
|
|
1350
1394
|
import { existsSync } from "fs";
|
|
1351
1395
|
import path4 from "path";
|
|
1352
1396
|
import os from "os";
|
|
1353
|
-
import { fileURLToPath } from "url";
|
|
1354
1397
|
import minimist4 from "minimist";
|
|
1355
|
-
var
|
|
1356
|
-
|
|
1357
|
-
|
|
1398
|
+
var LOGIN_SCRIPT = path4.join(
|
|
1399
|
+
path4.dirname(new URL(import.meta.url).pathname),
|
|
1400
|
+
"..",
|
|
1401
|
+
"..",
|
|
1402
|
+
"scripts",
|
|
1403
|
+
"jimeng_login_helper.py"
|
|
1404
|
+
);
|
|
1358
1405
|
function createLoginCommandHandler(deps) {
|
|
1359
1406
|
return async (argv) => {
|
|
1360
1407
|
const args = minimist4(argv, {
|
|
@@ -1558,10 +1605,20 @@ function usageRoot() {
|
|
|
1558
1605
|
}
|
|
1559
1606
|
function usageModelsList() {
|
|
1560
1607
|
return buildUsageText(" jimeng models list [options]", [
|
|
1561
|
-
" --
|
|
1608
|
+
" --token <token> Query with specific token",
|
|
1609
|
+
" --region <region> Query with specific region (cn/us/hk/jp/sg)",
|
|
1610
|
+
" --all Query all tokens in pool, grouped by token/region",
|
|
1562
1611
|
" --verbose Print rich model fields",
|
|
1563
1612
|
" --json Print full JSON response",
|
|
1564
1613
|
HELP_OPTION
|
|
1614
|
+
], [
|
|
1615
|
+
{
|
|
1616
|
+
title: "Notes:",
|
|
1617
|
+
lines: [
|
|
1618
|
+
" Without --token, --region, or --all, uses the first available token in pool.",
|
|
1619
|
+
" With --all, queries every enabled+live token and groups results by token/region."
|
|
1620
|
+
]
|
|
1621
|
+
}
|
|
1565
1622
|
]);
|
|
1566
1623
|
}
|
|
1567
1624
|
function usageModelsRefresh() {
|
|
@@ -1721,7 +1778,7 @@ function usageVideoGenerate() {
|
|
|
1721
1778
|
function usageTaskGet() {
|
|
1722
1779
|
return buildUsageText(" jimeng task get --task-id <id> [options]", [
|
|
1723
1780
|
" --token <token> Optional, override token-pool selection",
|
|
1724
|
-
" --region <region>
|
|
1781
|
+
" --region <region> Filter token by region (cn/us/hk/jp/sg)",
|
|
1725
1782
|
" --task-id <id> Required history/task id",
|
|
1726
1783
|
" --type <type> Optional image or video",
|
|
1727
1784
|
" --response-format <fmt> Optional url or b64_json",
|
|
@@ -1732,7 +1789,7 @@ function usageTaskGet() {
|
|
|
1732
1789
|
function usageTaskWait() {
|
|
1733
1790
|
return buildUsageText(" jimeng task wait --task-id <id> [options]", [
|
|
1734
1791
|
" --token <token> Optional, override token-pool selection",
|
|
1735
|
-
" --region <region>
|
|
1792
|
+
" --region <region> Filter token by region (cn/us/hk/jp/sg)",
|
|
1736
1793
|
" --task-id <id> Required history/task id",
|
|
1737
1794
|
" --type <type> Optional image or video",
|
|
1738
1795
|
" --response-format <fmt> Optional url or b64_json",
|
|
@@ -1745,7 +1802,7 @@ function usageTaskWait() {
|
|
|
1745
1802
|
function usageTaskList() {
|
|
1746
1803
|
return buildUsageText(" jimeng task list [options]", [
|
|
1747
1804
|
" --token <token> Optional, override token-pool selection",
|
|
1748
|
-
" --region <region>
|
|
1805
|
+
" --region <region> Filter token by region (cn/us/hk/jp/sg)",
|
|
1749
1806
|
" --type <type> Filter by type: image, video, or all (default all)",
|
|
1750
1807
|
" --count <num> Number of items per page (default 20)",
|
|
1751
1808
|
JSON_OPTION,
|
|
@@ -1890,7 +1947,6 @@ var queryHandlers = createQueryCommandHandlers({
|
|
|
1890
1947
|
usageTaskWait,
|
|
1891
1948
|
usageTaskList,
|
|
1892
1949
|
getSingleString,
|
|
1893
|
-
getRegionWithDefault,
|
|
1894
1950
|
parseRegionOrFail,
|
|
1895
1951
|
ensureTokenPoolReady,
|
|
1896
1952
|
pickDirectTokenForTask,
|