maoda-commander-tt 0.0.14 → 0.0.16
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/main.js +702 -14
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -1823,7 +1823,7 @@ var PippitCommand = class extends BaseSubcommandHost {
|
|
|
1823
1823
|
import { spawnSync } from "child_process";
|
|
1824
1824
|
var GitBaseCommand = class extends BaseCommand {
|
|
1825
1825
|
_configureArguments(cmd) {
|
|
1826
|
-
cmd.argument("[msg]", "\u63D0\u4EA4\u6D88\u606F", "update");
|
|
1826
|
+
cmd.argument("[msg]", "\u63D0\u4EA4\u6D88\u606F", "feat: update");
|
|
1827
1827
|
}
|
|
1828
1828
|
_configureOptions(cmd) {
|
|
1829
1829
|
cmd.option("-n, --no-verify", "\u8DF3\u8FC7 git hook \u6821\u9A8C");
|
|
@@ -1854,7 +1854,10 @@ var GitBaseCommand = class extends BaseCommand {
|
|
|
1854
1854
|
encoding: "utf8"
|
|
1855
1855
|
});
|
|
1856
1856
|
if (result.error) {
|
|
1857
|
-
return makeError(
|
|
1857
|
+
return makeError(
|
|
1858
|
+
1,
|
|
1859
|
+
`\u6267\u884C git ${args.join(" ")} \u5931\u8D25: ${result.error.message}`
|
|
1860
|
+
);
|
|
1858
1861
|
}
|
|
1859
1862
|
if (result.status !== 0) {
|
|
1860
1863
|
const signalText = result.signal ? `, signal: ${result.signal}` : "";
|
|
@@ -1936,10 +1939,7 @@ var GitBaseCommand = class extends BaseCommand {
|
|
|
1936
1939
|
}
|
|
1937
1940
|
if (result.status !== 0) {
|
|
1938
1941
|
const signalText = result.signal ? `, signal: ${result.signal}` : "";
|
|
1939
|
-
return this._makeError(
|
|
1940
|
-
result.status ?? 1,
|
|
1941
|
-
`\u6253\u5F00\u6D4F\u89C8\u5668\u5931\u8D25${signalText}`
|
|
1942
|
-
);
|
|
1942
|
+
return this._makeError(result.status ?? 1, `\u6253\u5F00\u6D4F\u89C8\u5668\u5931\u8D25${signalText}`);
|
|
1943
1943
|
}
|
|
1944
1944
|
return this._makeOk();
|
|
1945
1945
|
}
|
|
@@ -2078,9 +2078,9 @@ var ShortcutBaseCommand = class extends BaseCommand {
|
|
|
2078
2078
|
stdio: "inherit",
|
|
2079
2079
|
shell: true
|
|
2080
2080
|
});
|
|
2081
|
-
return new Promise((
|
|
2081
|
+
return new Promise((resolve8) => {
|
|
2082
2082
|
child.on("error", (error) => {
|
|
2083
|
-
|
|
2083
|
+
resolve8(
|
|
2084
2084
|
this._makeError(
|
|
2085
2085
|
1,
|
|
2086
2086
|
`\u6267\u884C ${cmdLine} \u5931\u8D25: ${error.message}`
|
|
@@ -2089,11 +2089,11 @@ var ShortcutBaseCommand = class extends BaseCommand {
|
|
|
2089
2089
|
});
|
|
2090
2090
|
child.on("close", (code, signal) => {
|
|
2091
2091
|
if (code === 0) {
|
|
2092
|
-
|
|
2092
|
+
resolve8(this._makeOk());
|
|
2093
2093
|
return;
|
|
2094
2094
|
}
|
|
2095
2095
|
const signalText = signal ? `, signal: ${signal}` : "";
|
|
2096
|
-
|
|
2096
|
+
resolve8(
|
|
2097
2097
|
this._makeError(
|
|
2098
2098
|
code ?? 1,
|
|
2099
2099
|
`\u6267\u884C ${cmdLine} \u5931\u8D25${signalText}`
|
|
@@ -2170,9 +2170,9 @@ var AiBaseCommand = class extends BaseCommand {
|
|
|
2170
2170
|
cwd: process.cwd(),
|
|
2171
2171
|
stdio: "inherit"
|
|
2172
2172
|
});
|
|
2173
|
-
return new Promise((
|
|
2173
|
+
return new Promise((resolve8) => {
|
|
2174
2174
|
child.on("error", (error) => {
|
|
2175
|
-
|
|
2175
|
+
resolve8(
|
|
2176
2176
|
this._makeError(
|
|
2177
2177
|
1,
|
|
2178
2178
|
`\u6267\u884C ${command} ${args.join(" ")} \u5931\u8D25: ${error.message}`
|
|
@@ -2181,11 +2181,11 @@ var AiBaseCommand = class extends BaseCommand {
|
|
|
2181
2181
|
});
|
|
2182
2182
|
child.on("close", (code, signal) => {
|
|
2183
2183
|
if (code === 0) {
|
|
2184
|
-
|
|
2184
|
+
resolve8(this._makeOk());
|
|
2185
2185
|
return;
|
|
2186
2186
|
}
|
|
2187
2187
|
const signalText = signal ? `, signal: ${signal}` : "";
|
|
2188
|
-
|
|
2188
|
+
resolve8(
|
|
2189
2189
|
this._makeError(
|
|
2190
2190
|
code ?? 1,
|
|
2191
2191
|
`\u6267\u884C ${command} ${args.join(" ")} \u5931\u8D25${signalText}`
|
|
@@ -2198,6 +2198,7 @@ var AiBaseCommand = class extends BaseCommand {
|
|
|
2198
2198
|
|
|
2199
2199
|
// src/constants/ak.ts
|
|
2200
2200
|
var ARK_AK = "6990de07-83be-487d-930d-f5dd3b6d5993";
|
|
2201
|
+
var OPENROUTER_AK = "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";
|
|
2201
2202
|
|
|
2202
2203
|
// src/commands/ai/ai-websearch-command.ts
|
|
2203
2204
|
var AiWebSearchCommand = class extends AiBaseCommand {
|
|
@@ -2955,6 +2956,691 @@ function inferImageExtension(remoteUrl, response) {
|
|
|
2955
2956
|
return "jpeg";
|
|
2956
2957
|
}
|
|
2957
2958
|
|
|
2959
|
+
// src/commands/ai/ai-video-command.ts
|
|
2960
|
+
import * as path8 from "path";
|
|
2961
|
+
import { Option as Option9 } from "commander";
|
|
2962
|
+
|
|
2963
|
+
// src/bedrock/uuid/uuid.ts
|
|
2964
|
+
var generateUuid = (() => {
|
|
2965
|
+
if (typeof crypto === "object" && typeof crypto.randomUUID === "function") {
|
|
2966
|
+
return crypto.randomUUID.bind(crypto);
|
|
2967
|
+
}
|
|
2968
|
+
let getRandomValues;
|
|
2969
|
+
if (typeof crypto === "object" && typeof crypto.getRandomValues === "function") {
|
|
2970
|
+
getRandomValues = crypto.getRandomValues.bind(crypto);
|
|
2971
|
+
} else {
|
|
2972
|
+
getRandomValues = function(bucket) {
|
|
2973
|
+
for (let i = 0; i < bucket.length; i++) {
|
|
2974
|
+
bucket[i] = Math.floor(Math.random() * 256);
|
|
2975
|
+
}
|
|
2976
|
+
return bucket;
|
|
2977
|
+
};
|
|
2978
|
+
}
|
|
2979
|
+
const data = new Uint8Array(16);
|
|
2980
|
+
const hex = [];
|
|
2981
|
+
for (let i = 0; i < 256; i++) {
|
|
2982
|
+
hex.push(i.toString(16).padStart(2, "0"));
|
|
2983
|
+
}
|
|
2984
|
+
return () => {
|
|
2985
|
+
getRandomValues(data);
|
|
2986
|
+
data[6] = data[6] & 15 | 64;
|
|
2987
|
+
data[8] = data[8] & 63 | 128;
|
|
2988
|
+
let i = 0;
|
|
2989
|
+
let result = "";
|
|
2990
|
+
result += hex[data[i++]];
|
|
2991
|
+
result += hex[data[i++]];
|
|
2992
|
+
result += hex[data[i++]];
|
|
2993
|
+
result += hex[data[i++]];
|
|
2994
|
+
result += "-";
|
|
2995
|
+
result += hex[data[i++]];
|
|
2996
|
+
result += hex[data[i++]];
|
|
2997
|
+
result += "-";
|
|
2998
|
+
result += hex[data[i++]];
|
|
2999
|
+
result += hex[data[i++]];
|
|
3000
|
+
result += "-";
|
|
3001
|
+
result += hex[data[i++]];
|
|
3002
|
+
result += hex[data[i++]];
|
|
3003
|
+
result += "-";
|
|
3004
|
+
result += hex[data[i++]];
|
|
3005
|
+
result += hex[data[i++]];
|
|
3006
|
+
result += hex[data[i++]];
|
|
3007
|
+
result += hex[data[i++]];
|
|
3008
|
+
result += hex[data[i++]];
|
|
3009
|
+
result += hex[data[i++]];
|
|
3010
|
+
return result;
|
|
3011
|
+
};
|
|
3012
|
+
})();
|
|
3013
|
+
|
|
3014
|
+
// src/commands/pippit/modules/submit-direct/submit-direct-cn.ts
|
|
3015
|
+
var SUBMIT_DIRECT_URL = "https://xyq.jianying.com/api/biz/v1/agent/submit_run";
|
|
3016
|
+
function normalizeToken4(token) {
|
|
3017
|
+
const trimmed = token.trim();
|
|
3018
|
+
const prefix = "sessionid_pippitcn_web=";
|
|
3019
|
+
if (trimmed.startsWith(prefix)) {
|
|
3020
|
+
return trimmed.slice(prefix.length).trim();
|
|
3021
|
+
}
|
|
3022
|
+
return trimmed;
|
|
3023
|
+
}
|
|
3024
|
+
function createCookieHeader3(token) {
|
|
3025
|
+
return `sessionid_pippitcn_web=${normalizeToken4(token)}`;
|
|
3026
|
+
}
|
|
3027
|
+
function parseErrorCode2(ret) {
|
|
3028
|
+
const parsed = typeof ret === "number" ? ret : typeof ret === "string" ? Number.parseInt(ret, 10) : Number.NaN;
|
|
3029
|
+
return Number.isFinite(parsed) ? parsed : 1;
|
|
3030
|
+
}
|
|
3031
|
+
function createUserInfo() {
|
|
3032
|
+
return readWebContext("cn" /* CN */).then((webContextResult) => {
|
|
3033
|
+
if (!webContextResult.ok) {
|
|
3034
|
+
return webContextResult;
|
|
3035
|
+
}
|
|
3036
|
+
const webContext = webContextResult.value;
|
|
3037
|
+
if (!webContext.uid || !webContext.workspaceId || !webContext.spaceId) {
|
|
3038
|
+
return missingWebContextError();
|
|
3039
|
+
}
|
|
3040
|
+
return makeOkWith({
|
|
3041
|
+
uid: webContext.uid,
|
|
3042
|
+
workspace_id: webContext.workspaceId,
|
|
3043
|
+
space_id: webContext.spaceId
|
|
3044
|
+
});
|
|
3045
|
+
});
|
|
3046
|
+
}
|
|
3047
|
+
async function submitDirectCn(options) {
|
|
3048
|
+
const userInfoResult = await createUserInfo();
|
|
3049
|
+
if (!userInfoResult.ok) {
|
|
3050
|
+
return userInfoResult;
|
|
3051
|
+
}
|
|
3052
|
+
const { durationSec, imageAssetIds, prompt } = options;
|
|
3053
|
+
const tokenResult = await readWebToken("cn" /* CN */);
|
|
3054
|
+
if (!tokenResult.ok) {
|
|
3055
|
+
return tokenResult;
|
|
3056
|
+
}
|
|
3057
|
+
const token = tokenResult.value?.trim();
|
|
3058
|
+
if (!token) {
|
|
3059
|
+
return missingWebTokenError();
|
|
3060
|
+
}
|
|
3061
|
+
const threadId = generateUuid();
|
|
3062
|
+
const runId = generateUuid();
|
|
3063
|
+
let duration = 5;
|
|
3064
|
+
if (durationSec && durationSec <= 8 && durationSec >= 2) {
|
|
3065
|
+
duration = Math.round(durationSec);
|
|
3066
|
+
}
|
|
3067
|
+
const message = {
|
|
3068
|
+
message_id: "",
|
|
3069
|
+
role: "user",
|
|
3070
|
+
thread_id: threadId,
|
|
3071
|
+
run_id: runId,
|
|
3072
|
+
created_at: Date.now(),
|
|
3073
|
+
content: [
|
|
3074
|
+
{
|
|
3075
|
+
type: "data",
|
|
3076
|
+
sub_type: "biz/x_data_direct_tool_call_req",
|
|
3077
|
+
data: JSON.stringify({
|
|
3078
|
+
param: JSON.stringify({
|
|
3079
|
+
prompt,
|
|
3080
|
+
images: [
|
|
3081
|
+
imageAssetIds?.map((assetId) => ({
|
|
3082
|
+
asset_id: assetId
|
|
3083
|
+
}))
|
|
3084
|
+
],
|
|
3085
|
+
duration_sec: duration,
|
|
3086
|
+
language: "zh",
|
|
3087
|
+
resolution: "720p",
|
|
3088
|
+
imitation_videos: [],
|
|
3089
|
+
videos: [],
|
|
3090
|
+
audios: [],
|
|
3091
|
+
model: "seedance2.0_vision"
|
|
3092
|
+
}),
|
|
3093
|
+
tool_name: "biz/x_tool_name_video_part"
|
|
3094
|
+
}),
|
|
3095
|
+
hidden: false,
|
|
3096
|
+
is_thought: false
|
|
3097
|
+
}
|
|
3098
|
+
]
|
|
3099
|
+
};
|
|
3100
|
+
const userInfo = {
|
|
3101
|
+
app_id: "795647",
|
|
3102
|
+
consumer_uid: userInfoResult.value.uid,
|
|
3103
|
+
workspace_id: userInfoResult.value.workspace_id,
|
|
3104
|
+
space_id: userInfoResult.value.space_id
|
|
3105
|
+
};
|
|
3106
|
+
const responseResult = await requestJson(
|
|
3107
|
+
SUBMIT_DIRECT_URL,
|
|
3108
|
+
{
|
|
3109
|
+
method: "POST",
|
|
3110
|
+
headers: {
|
|
3111
|
+
accept: "application/json, text/plain, */*",
|
|
3112
|
+
"content-type": "application/json",
|
|
3113
|
+
cookie: createCookieHeader3(token),
|
|
3114
|
+
Referer: "https://xyq.jianying.com/"
|
|
3115
|
+
},
|
|
3116
|
+
body: JSON.stringify({
|
|
3117
|
+
user_info: userInfo,
|
|
3118
|
+
message,
|
|
3119
|
+
agent_name: "pippit_video_part_agent",
|
|
3120
|
+
entrance_from: "web",
|
|
3121
|
+
run_extra: "{}"
|
|
3122
|
+
})
|
|
3123
|
+
},
|
|
3124
|
+
{
|
|
3125
|
+
networkRequestFailed: (message2) => networkRequestFailedError3(message2),
|
|
3126
|
+
responseParseFailed: (message2) => responseParseFailedError2(message2)
|
|
3127
|
+
}
|
|
3128
|
+
);
|
|
3129
|
+
if (!responseResult.ok) {
|
|
3130
|
+
return responseResult;
|
|
3131
|
+
}
|
|
3132
|
+
const response = responseResult.value;
|
|
3133
|
+
console.log("=====response", response);
|
|
3134
|
+
if (String(response.ret) !== "0") {
|
|
3135
|
+
return makeError(
|
|
3136
|
+
parseErrorCode2(response.ret),
|
|
3137
|
+
response.errmsg?.trim() || "\u76F4\u63A5\u63D0\u4EA4\u5931\u8D25\u3002"
|
|
3138
|
+
);
|
|
3139
|
+
}
|
|
3140
|
+
return makeOkWith(response.data);
|
|
3141
|
+
}
|
|
3142
|
+
|
|
3143
|
+
// src/commands/ai/ai-video-command.ts
|
|
3144
|
+
var AiVideoCommand = class extends AiBaseCommand {
|
|
3145
|
+
_meta() {
|
|
3146
|
+
return {
|
|
3147
|
+
name: "video",
|
|
3148
|
+
description: "\u4E0A\u4F20\u53C2\u8003\u7D20\u6750\u5E76\u8C03\u7528\u89C6\u9891\u751F\u6210\u63A5\u53E3"
|
|
3149
|
+
};
|
|
3150
|
+
}
|
|
3151
|
+
_configureOptions(cmd) {
|
|
3152
|
+
cmd.addOption(
|
|
3153
|
+
new Option9(
|
|
3154
|
+
"-r, --reference <path>",
|
|
3155
|
+
"\u53C2\u8003\u56FE\u7247\u6216\u89C6\u9891\u8DEF\u5F84\uFF0C\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u4E5F\u652F\u6301\u7528\u82F1\u6587\u9017\u53F7\u5206\u9694\u591A\u4E2A\u8DEF\u5F84"
|
|
3156
|
+
).argParser(parseReferencePaths2).default([])
|
|
3157
|
+
);
|
|
3158
|
+
}
|
|
3159
|
+
_configureArguments(cmd) {
|
|
3160
|
+
cmd.argument("[content...]", "\u89C6\u9891\u751F\u6210\u63D0\u793A\u8BCD");
|
|
3161
|
+
}
|
|
3162
|
+
async _execute(ctx) {
|
|
3163
|
+
const prompt = this._readPrompt(ctx);
|
|
3164
|
+
if (!prompt) {
|
|
3165
|
+
this._outputJsonError(1, "\u8BF7\u8F93\u5165\u89C6\u9891\u751F\u6210\u63D0\u793A\u8BCD");
|
|
3166
|
+
return this._makeOk();
|
|
3167
|
+
}
|
|
3168
|
+
const uploadResult = await this._uploadReferences(ctx.options.reference);
|
|
3169
|
+
if (!uploadResult.ok) {
|
|
3170
|
+
this._outputJsonError(uploadResult.code, uploadResult.msg);
|
|
3171
|
+
return this._makeOk();
|
|
3172
|
+
}
|
|
3173
|
+
const generationResult = await this._requestVideoGeneration(
|
|
3174
|
+
prompt,
|
|
3175
|
+
uploadResult.value
|
|
3176
|
+
);
|
|
3177
|
+
if (!generationResult.ok) {
|
|
3178
|
+
this._outputJsonError(generationResult.code, generationResult.msg);
|
|
3179
|
+
return this._makeOk();
|
|
3180
|
+
}
|
|
3181
|
+
this._outputJsonOk({
|
|
3182
|
+
prompt,
|
|
3183
|
+
assets: uploadResult.value,
|
|
3184
|
+
generation: generationResult.value
|
|
3185
|
+
});
|
|
3186
|
+
return this._makeOk();
|
|
3187
|
+
}
|
|
3188
|
+
async _uploadReferences(references) {
|
|
3189
|
+
if (references.length === 0) {
|
|
3190
|
+
return makeOkWith([]);
|
|
3191
|
+
}
|
|
3192
|
+
const uploadResults = await Promise.all(
|
|
3193
|
+
references.map(async (referencePath) => {
|
|
3194
|
+
const result = await uploadAsset({
|
|
3195
|
+
filePath: referencePath,
|
|
3196
|
+
region: "cn" /* CN */
|
|
3197
|
+
});
|
|
3198
|
+
if (!result.ok) {
|
|
3199
|
+
return result;
|
|
3200
|
+
}
|
|
3201
|
+
return makeOkWith({
|
|
3202
|
+
asset_id: result.value.assetId,
|
|
3203
|
+
asset_type: result.value.fileInfo.assetType,
|
|
3204
|
+
file_path: path8.resolve(process.cwd(), referencePath),
|
|
3205
|
+
region: result.value.region
|
|
3206
|
+
});
|
|
3207
|
+
})
|
|
3208
|
+
);
|
|
3209
|
+
for (const result of uploadResults) {
|
|
3210
|
+
if (!result.ok) {
|
|
3211
|
+
return result;
|
|
3212
|
+
}
|
|
3213
|
+
}
|
|
3214
|
+
return makeOkWith(uploadResults.map((result) => result.value));
|
|
3215
|
+
}
|
|
3216
|
+
async _requestVideoGeneration(prompt, assets) {
|
|
3217
|
+
return submitDirectCn({
|
|
3218
|
+
prompt,
|
|
3219
|
+
imageAssetIds: assets.map((asset) => asset.asset_id),
|
|
3220
|
+
durationSec: 5
|
|
3221
|
+
});
|
|
3222
|
+
}
|
|
3223
|
+
};
|
|
3224
|
+
function parseReferencePaths2(value, previous) {
|
|
3225
|
+
return previous.concat(
|
|
3226
|
+
value.split(",").map((item) => item.trim()).filter((item) => item.length > 0)
|
|
3227
|
+
);
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3230
|
+
// src/commands/ai/ai-vid-command.ts
|
|
3231
|
+
import * as fs7 from "fs/promises";
|
|
3232
|
+
import * as path9 from "path";
|
|
3233
|
+
import { Option as Option10 } from "commander";
|
|
3234
|
+
var VIDEO_GENERATION_URL = "https://openrouter.ai/api/v1/videos";
|
|
3235
|
+
var VIDEO_MODEL = "bytedance/seedance-2.0";
|
|
3236
|
+
var POLLING_LOG_FILE_NAME = "ai-vid-polling.log";
|
|
3237
|
+
var POLL_INTERVAL_MS = 3e3;
|
|
3238
|
+
var POLL_STATUS_LOG_INTERVAL_MS = 1e4;
|
|
3239
|
+
var POLL_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
3240
|
+
var SUBMIT_TIMEOUT_MS = 3e4;
|
|
3241
|
+
var AiVidCommand = class extends AiBaseCommand {
|
|
3242
|
+
_meta() {
|
|
3243
|
+
return {
|
|
3244
|
+
name: "vid",
|
|
3245
|
+
description: "\u901A\u8FC7 OpenRouter \u8C03\u7528 bytedance/seedance-2.0 \u751F\u6210\u89C6\u9891"
|
|
3246
|
+
};
|
|
3247
|
+
}
|
|
3248
|
+
_configureOptions(cmd) {
|
|
3249
|
+
cmd.addOption(
|
|
3250
|
+
new Option10(
|
|
3251
|
+
"-r, --reference <path>",
|
|
3252
|
+
"\u53C2\u8003\u56FE\u7247\u672C\u5730\u8DEF\u5F84\uFF0C\u53EF\u91CD\u590D\u4F20\u5165\uFF0C\u4E5F\u652F\u6301\u7528\u82F1\u6587\u9017\u53F7\u5206\u9694\u591A\u4E2A\u8DEF\u5F84\uFF1B\u6700\u7EC8\u8F6C\u4E3A base64 data URL"
|
|
3253
|
+
).argParser(parseReferencePaths3).default([])
|
|
3254
|
+
);
|
|
3255
|
+
cmd.addOption(
|
|
3256
|
+
new Option10("--resolution <resolution>", "\u89C6\u9891\u5206\u8FA8\u7387").choices(["480p", "720p"]).default("480p")
|
|
3257
|
+
);
|
|
3258
|
+
cmd.addOption(
|
|
3259
|
+
new Option10(
|
|
3260
|
+
"--aspect-ratio <ratio>",
|
|
3261
|
+
"\u89C6\u9891\u957F\u5BBD\u6BD4\uFF0C\u4F8B\u5982 16:9\u30019:16\u30011:1\uFF0C\u7F3A\u7701\u4E0D\u6307\u5B9A"
|
|
3262
|
+
)
|
|
3263
|
+
);
|
|
3264
|
+
cmd.addOption(
|
|
3265
|
+
new Option10("--duration <seconds>", "\u89C6\u9891\u65F6\u957F\uFF08\u79D2\uFF09\uFF0C\u7F3A\u7701\u4E0D\u6307\u5B9A").argParser(
|
|
3266
|
+
parseDuration
|
|
3267
|
+
)
|
|
3268
|
+
);
|
|
3269
|
+
cmd.addOption(
|
|
3270
|
+
new Option10(
|
|
3271
|
+
"--polling-only",
|
|
3272
|
+
"\u4EC5\u63D0\u4EA4\u5E76\u8FD4\u56DE polling_url\uFF0C\u4E0D\u5728\u672C\u5730\u8F6E\u8BE2\u7ED3\u679C"
|
|
3273
|
+
).default(false)
|
|
3274
|
+
);
|
|
3275
|
+
cmd.addOption(
|
|
3276
|
+
new Option10(
|
|
3277
|
+
"-o, --output <path>",
|
|
3278
|
+
"\u89C6\u9891\u4E0B\u8F7D\u8F93\u51FA\u8DEF\u5F84\uFF0C\u53EF\u4F20\u6587\u4EF6\u8DEF\u5F84\u6216\u76EE\u5F55\u8DEF\u5F84\uFF1B\u672A\u4F20\u65F6\u9ED8\u8BA4\u5199\u5165\u5F53\u524D\u76EE\u5F55\u5E76\u81EA\u52A8\u547D\u540D"
|
|
3279
|
+
)
|
|
3280
|
+
);
|
|
3281
|
+
}
|
|
3282
|
+
_configureArguments(cmd) {
|
|
3283
|
+
cmd.argument("[content...]", "\u89C6\u9891\u751F\u6210\u63D0\u793A\u8BCD");
|
|
3284
|
+
}
|
|
3285
|
+
async _execute(ctx) {
|
|
3286
|
+
const prompt = this._readPrompt(ctx);
|
|
3287
|
+
if (!prompt) {
|
|
3288
|
+
this._outputJsonError(1, "\u8BF7\u8F93\u5165\u89C6\u9891\u751F\u6210\u63D0\u793A\u8BCD");
|
|
3289
|
+
return this._makeOk();
|
|
3290
|
+
}
|
|
3291
|
+
const referenceResult = await prepareReferenceImages(ctx.options.reference);
|
|
3292
|
+
if (!referenceResult.ok) {
|
|
3293
|
+
this._outputJsonError(referenceResult.code, referenceResult.msg);
|
|
3294
|
+
return this._makeOk();
|
|
3295
|
+
}
|
|
3296
|
+
const requestBody = {
|
|
3297
|
+
model: VIDEO_MODEL,
|
|
3298
|
+
prompt,
|
|
3299
|
+
resolution: ctx.options.resolution
|
|
3300
|
+
};
|
|
3301
|
+
if (ctx.options.aspectRatio) {
|
|
3302
|
+
requestBody.aspect_ratio = ctx.options.aspectRatio;
|
|
3303
|
+
}
|
|
3304
|
+
if (typeof ctx.options.duration === "number") {
|
|
3305
|
+
requestBody.duration = ctx.options.duration;
|
|
3306
|
+
}
|
|
3307
|
+
if (referenceResult.value.length > 0) {
|
|
3308
|
+
requestBody.input_references = referenceResult.value.map((item) => ({
|
|
3309
|
+
type: "image_url",
|
|
3310
|
+
image_url: { url: item.dataUrl }
|
|
3311
|
+
}));
|
|
3312
|
+
}
|
|
3313
|
+
const submitResult = await this._submitGeneration(requestBody);
|
|
3314
|
+
if (!submitResult.ok) {
|
|
3315
|
+
this._outputJsonError(submitResult.code, submitResult.msg);
|
|
3316
|
+
return this._makeOk();
|
|
3317
|
+
}
|
|
3318
|
+
const submitPayload = submitResult.value;
|
|
3319
|
+
const pollingUrl = submitPayload.polling_url?.trim();
|
|
3320
|
+
if (!pollingUrl) {
|
|
3321
|
+
this._outputJsonError(1, "\u63D0\u4EA4\u6210\u529F\u4F46\u8FD4\u56DE\u6570\u636E\u7F3A\u5C11 polling_url");
|
|
3322
|
+
return this._makeOk();
|
|
3323
|
+
}
|
|
3324
|
+
const logPathResult = await appendPollingUrl(pollingUrl, submitPayload.id);
|
|
3325
|
+
const pollingLogPath = logPathResult.ok ? logPathResult.value : void 0;
|
|
3326
|
+
if (!logPathResult.ok) {
|
|
3327
|
+
this._debug(`\u8BB0\u5F55 polling_url \u5931\u8D25: ${logPathResult.msg}`);
|
|
3328
|
+
}
|
|
3329
|
+
if (ctx.options.pollingOnly) {
|
|
3330
|
+
this._outputJsonOk({
|
|
3331
|
+
polling_url: pollingUrl,
|
|
3332
|
+
generation_id: submitPayload.id,
|
|
3333
|
+
polling_log_path: pollingLogPath
|
|
3334
|
+
});
|
|
3335
|
+
return this._makeOk();
|
|
3336
|
+
}
|
|
3337
|
+
const pollResult = await this._pollUntilComplete(pollingUrl);
|
|
3338
|
+
if (!pollResult.ok) {
|
|
3339
|
+
this._outputJsonError(pollResult.code, pollResult.msg);
|
|
3340
|
+
return this._makeOk();
|
|
3341
|
+
}
|
|
3342
|
+
const videoUrl = pollResult.value.unsigned_urls?.[0];
|
|
3343
|
+
if (!videoUrl) {
|
|
3344
|
+
this._outputJsonError(1, "\u89C6\u9891\u751F\u6210\u5B8C\u6210\uFF0C\u4F46\u672A\u62FF\u5230 unsigned_urls[0]");
|
|
3345
|
+
return this._makeOk();
|
|
3346
|
+
}
|
|
3347
|
+
const outputTargetResult = await resolveOutputTarget2(ctx.options.output);
|
|
3348
|
+
if (!outputTargetResult.ok) {
|
|
3349
|
+
this._outputJsonError(outputTargetResult.code, outputTargetResult.msg);
|
|
3350
|
+
return this._makeOk();
|
|
3351
|
+
}
|
|
3352
|
+
const downloadResult = await downloadVideoToFile(
|
|
3353
|
+
videoUrl,
|
|
3354
|
+
outputTargetResult.value
|
|
3355
|
+
);
|
|
3356
|
+
if (!downloadResult.ok) {
|
|
3357
|
+
this._outputJsonError(downloadResult.code, downloadResult.msg);
|
|
3358
|
+
return this._makeOk();
|
|
3359
|
+
}
|
|
3360
|
+
this._outputJsonOk({
|
|
3361
|
+
video_path: downloadResult.value,
|
|
3362
|
+
video_url: videoUrl,
|
|
3363
|
+
polling_url: pollingUrl,
|
|
3364
|
+
generation_id: submitPayload.id,
|
|
3365
|
+
polling_log_path: pollingLogPath
|
|
3366
|
+
});
|
|
3367
|
+
return this._makeOk();
|
|
3368
|
+
}
|
|
3369
|
+
async _submitGeneration(requestBody) {
|
|
3370
|
+
const token = process.env.OPENROUTER_AK?.trim() || OPENROUTER_AK;
|
|
3371
|
+
if (!token) {
|
|
3372
|
+
return makeError(1, "\u7F3A\u5C11 OPENROUTER_AK\uFF0C\u8BF7\u5148\u914D\u7F6E\u53EF\u7528\u7684 API Key");
|
|
3373
|
+
}
|
|
3374
|
+
const controller = new AbortController();
|
|
3375
|
+
const timer = setTimeout(() => controller.abort(), SUBMIT_TIMEOUT_MS);
|
|
3376
|
+
let response;
|
|
3377
|
+
try {
|
|
3378
|
+
response = await fetch(VIDEO_GENERATION_URL, {
|
|
3379
|
+
method: "POST",
|
|
3380
|
+
headers: {
|
|
3381
|
+
Authorization: `Bearer ${token}`,
|
|
3382
|
+
"Content-Type": "application/json"
|
|
3383
|
+
},
|
|
3384
|
+
body: JSON.stringify(requestBody),
|
|
3385
|
+
signal: controller.signal
|
|
3386
|
+
});
|
|
3387
|
+
} catch (error) {
|
|
3388
|
+
const message = error instanceof Error && error.name === "AbortError" ? "\u8BF7\u6C42\u8D85\u65F6" : error instanceof Error ? error.message : String(error);
|
|
3389
|
+
return makeError(1, `\u63D0\u4EA4\u89C6\u9891\u751F\u6210\u8BF7\u6C42\u5931\u8D25: ${message}`);
|
|
3390
|
+
} finally {
|
|
3391
|
+
clearTimeout(timer);
|
|
3392
|
+
}
|
|
3393
|
+
const bodyText = await response.text().catch(() => "");
|
|
3394
|
+
const payload = parseJsonSafely2(bodyText);
|
|
3395
|
+
if (!response.ok) {
|
|
3396
|
+
const detail = extractErrorText(payload) ?? bodyText.trim();
|
|
3397
|
+
const suffix = detail ? `: ${detail}` : "";
|
|
3398
|
+
return makeError(
|
|
3399
|
+
response.status,
|
|
3400
|
+
`\u89C6\u9891\u751F\u6210\u8BF7\u6C42\u5931\u8D25: HTTP ${response.status}${suffix}`
|
|
3401
|
+
);
|
|
3402
|
+
}
|
|
3403
|
+
if (!payload) {
|
|
3404
|
+
return makeError(1, "\u89C6\u9891\u751F\u6210\u63A5\u53E3\u8FD4\u56DE\u975E JSON \u54CD\u5E94");
|
|
3405
|
+
}
|
|
3406
|
+
if (payload.error) {
|
|
3407
|
+
return makeError(1, `\u89C6\u9891\u751F\u6210\u8BF7\u6C42\u88AB\u62D2\u7EDD: ${payload.error}`);
|
|
3408
|
+
}
|
|
3409
|
+
if (!payload.id || !payload.polling_url) {
|
|
3410
|
+
return makeError(
|
|
3411
|
+
1,
|
|
3412
|
+
`\u89C6\u9891\u751F\u6210\u63A5\u53E3\u8FD4\u56DE\u6570\u636E\u5F02\u5E38: ${bodyText.slice(0, 500)}`
|
|
3413
|
+
);
|
|
3414
|
+
}
|
|
3415
|
+
return makeOkWith(payload);
|
|
3416
|
+
}
|
|
3417
|
+
async _pollUntilComplete(pollingUrl) {
|
|
3418
|
+
const token = process.env.OPENROUTER_AK?.trim() || OPENROUTER_AK;
|
|
3419
|
+
const startedAt = Date.now();
|
|
3420
|
+
let lastStatusLogAt = 0;
|
|
3421
|
+
while (true) {
|
|
3422
|
+
const elapsed = Date.now() - startedAt;
|
|
3423
|
+
if (elapsed >= POLL_TIMEOUT_MS) {
|
|
3424
|
+
return makeError(
|
|
3425
|
+
1,
|
|
3426
|
+
`\u8F6E\u8BE2\u8D85\u65F6\uFF0C\u8D85\u8FC7 ${Math.round(POLL_TIMEOUT_MS / 1e3)}s \u4ECD\u672A\u5B8C\u6210\uFF0C\u53EF\u4F7F\u7528 --polling-only \u62FF\u5230 polling_url \u540E\u7A0D\u540E\u91CD\u8BD5`
|
|
3427
|
+
);
|
|
3428
|
+
}
|
|
3429
|
+
let response;
|
|
3430
|
+
try {
|
|
3431
|
+
response = await fetch(pollingUrl, {
|
|
3432
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
3433
|
+
});
|
|
3434
|
+
} catch (error) {
|
|
3435
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3436
|
+
this._debug(`\u8F6E\u8BE2\u8BF7\u6C42\u5931\u8D25\uFF0C\u5C06\u91CD\u8BD5: ${message}`);
|
|
3437
|
+
await delay(POLL_INTERVAL_MS);
|
|
3438
|
+
continue;
|
|
3439
|
+
}
|
|
3440
|
+
const bodyText = await response.text().catch(() => "");
|
|
3441
|
+
const payload = parseJsonSafely2(bodyText);
|
|
3442
|
+
if (!response.ok) {
|
|
3443
|
+
const detail = extractErrorText(payload) ?? bodyText.trim();
|
|
3444
|
+
return makeError(
|
|
3445
|
+
response.status,
|
|
3446
|
+
`\u8F6E\u8BE2\u8BF7\u6C42\u5931\u8D25: HTTP ${response.status}${detail ? `: ${detail}` : ""}`
|
|
3447
|
+
);
|
|
3448
|
+
}
|
|
3449
|
+
if (!payload) {
|
|
3450
|
+
this._debug("\u8F6E\u8BE2\u8FD4\u56DE\u975E JSON \u6570\u636E\uFF0C\u5C06\u91CD\u8BD5");
|
|
3451
|
+
await delay(POLL_INTERVAL_MS);
|
|
3452
|
+
continue;
|
|
3453
|
+
}
|
|
3454
|
+
const status = payload.status;
|
|
3455
|
+
if (status === "completed") {
|
|
3456
|
+
return makeOkWith(payload);
|
|
3457
|
+
}
|
|
3458
|
+
if (status === "failed" || status === "cancelled" || status === "expired") {
|
|
3459
|
+
const errorText = payload.error?.trim() || `\u89C6\u9891\u751F\u6210\u7ED3\u675F\u4E8E\u72B6\u6001 ${status}`;
|
|
3460
|
+
return makeError(1, errorText);
|
|
3461
|
+
}
|
|
3462
|
+
const now = Date.now();
|
|
3463
|
+
if (now - lastStatusLogAt >= POLL_STATUS_LOG_INTERVAL_MS) {
|
|
3464
|
+
this._debug(
|
|
3465
|
+
`\u89C6\u9891\u751F\u6210\u72B6\u6001: ${status ?? "unknown"} (\u5DF2\u7B49\u5F85 ${Math.round(elapsed / 1e3)}s)`
|
|
3466
|
+
);
|
|
3467
|
+
lastStatusLogAt = now;
|
|
3468
|
+
}
|
|
3469
|
+
await delay(POLL_INTERVAL_MS);
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
};
|
|
3473
|
+
function parseReferencePaths3(value, previous) {
|
|
3474
|
+
return previous.concat(
|
|
3475
|
+
value.split(",").map((item) => item.trim()).filter((item) => item.length > 0)
|
|
3476
|
+
);
|
|
3477
|
+
}
|
|
3478
|
+
function parseDuration(value) {
|
|
3479
|
+
const parsed = Number.parseInt(value, 10);
|
|
3480
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
3481
|
+
throw new Error(`duration \u9700\u8981\u662F\u6B63\u6574\u6570\uFF0C\u6536\u5230: ${value}`);
|
|
3482
|
+
}
|
|
3483
|
+
return parsed;
|
|
3484
|
+
}
|
|
3485
|
+
function parseJsonSafely2(text) {
|
|
3486
|
+
if (!text.trim()) {
|
|
3487
|
+
return void 0;
|
|
3488
|
+
}
|
|
3489
|
+
try {
|
|
3490
|
+
return JSON.parse(text);
|
|
3491
|
+
} catch {
|
|
3492
|
+
return void 0;
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
function extractErrorText(payload) {
|
|
3496
|
+
if (!payload || typeof payload !== "object") {
|
|
3497
|
+
return void 0;
|
|
3498
|
+
}
|
|
3499
|
+
const record = payload;
|
|
3500
|
+
if (typeof record.error === "string" && record.error.trim().length > 0) {
|
|
3501
|
+
return record.error.trim();
|
|
3502
|
+
}
|
|
3503
|
+
if (record.error && typeof record.error === "object") {
|
|
3504
|
+
const inner = record.error;
|
|
3505
|
+
if (typeof inner.message === "string" && inner.message.trim().length > 0) {
|
|
3506
|
+
return inner.message.trim();
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
if (typeof record.message === "string" && record.message.trim().length > 0) {
|
|
3510
|
+
return record.message.trim();
|
|
3511
|
+
}
|
|
3512
|
+
return void 0;
|
|
3513
|
+
}
|
|
3514
|
+
function delay(ms) {
|
|
3515
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
3516
|
+
}
|
|
3517
|
+
async function appendPollingUrl(pollingUrl, generationId) {
|
|
3518
|
+
const locationResult = await resolveConfigLocation();
|
|
3519
|
+
if (!locationResult.ok) {
|
|
3520
|
+
return locationResult;
|
|
3521
|
+
}
|
|
3522
|
+
const logFilePath = path9.join(
|
|
3523
|
+
locationResult.value.configDir,
|
|
3524
|
+
POLLING_LOG_FILE_NAME
|
|
3525
|
+
);
|
|
3526
|
+
try {
|
|
3527
|
+
await fs7.mkdir(locationResult.value.configDir, { recursive: true });
|
|
3528
|
+
const line = `${(/* @__PURE__ */ new Date()).toISOString()} ${generationId} ${pollingUrl}
|
|
3529
|
+
`;
|
|
3530
|
+
await fs7.appendFile(logFilePath, line, "utf8");
|
|
3531
|
+
return makeOkWith(logFilePath);
|
|
3532
|
+
} catch (error) {
|
|
3533
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3534
|
+
return makeError(1, `\u5199\u5165 polling \u65E5\u5FD7\u5931\u8D25 ${logFilePath}: ${message}`);
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
async function resolveOutputTarget2(outputPath) {
|
|
3538
|
+
if (!outputPath) {
|
|
3539
|
+
const directoryPath = process.cwd();
|
|
3540
|
+
return makeOkWith({
|
|
3541
|
+
directoryPath,
|
|
3542
|
+
filePath: path9.join(directoryPath, buildAutoFileName2()),
|
|
3543
|
+
autoNamed: true
|
|
3544
|
+
});
|
|
3545
|
+
}
|
|
3546
|
+
const resolvedPath = path9.resolve(process.cwd(), outputPath);
|
|
3547
|
+
try {
|
|
3548
|
+
const stats = await fs7.stat(resolvedPath);
|
|
3549
|
+
if (stats.isDirectory()) {
|
|
3550
|
+
return makeOkWith({
|
|
3551
|
+
directoryPath: resolvedPath,
|
|
3552
|
+
filePath: path9.join(resolvedPath, buildAutoFileName2()),
|
|
3553
|
+
autoNamed: true
|
|
3554
|
+
});
|
|
3555
|
+
}
|
|
3556
|
+
return makeOkWith({
|
|
3557
|
+
directoryPath: path9.dirname(resolvedPath),
|
|
3558
|
+
filePath: resolvedPath,
|
|
3559
|
+
autoNamed: false
|
|
3560
|
+
});
|
|
3561
|
+
} catch (error) {
|
|
3562
|
+
const nodeError = error;
|
|
3563
|
+
if (nodeError.code !== "ENOENT") {
|
|
3564
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3565
|
+
return makeError(1, `\u8BFB\u53D6\u8F93\u51FA\u8DEF\u5F84\u5931\u8D25: ${resolvedPath}: ${message}`);
|
|
3566
|
+
}
|
|
3567
|
+
if (path9.extname(resolvedPath)) {
|
|
3568
|
+
return makeOkWith({
|
|
3569
|
+
directoryPath: path9.dirname(resolvedPath),
|
|
3570
|
+
filePath: resolvedPath,
|
|
3571
|
+
autoNamed: false
|
|
3572
|
+
});
|
|
3573
|
+
}
|
|
3574
|
+
return makeOkWith({
|
|
3575
|
+
directoryPath: resolvedPath,
|
|
3576
|
+
filePath: path9.join(resolvedPath, buildAutoFileName2()),
|
|
3577
|
+
autoNamed: true
|
|
3578
|
+
});
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
async function downloadVideoToFile(remoteUrl, target) {
|
|
3582
|
+
try {
|
|
3583
|
+
await fs7.mkdir(target.directoryPath, { recursive: true });
|
|
3584
|
+
} catch (error) {
|
|
3585
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3586
|
+
return makeError(
|
|
3587
|
+
1,
|
|
3588
|
+
`\u521B\u5EFA\u8F93\u51FA\u76EE\u5F55\u5931\u8D25: ${target.directoryPath}: ${message}`
|
|
3589
|
+
);
|
|
3590
|
+
}
|
|
3591
|
+
let response;
|
|
3592
|
+
try {
|
|
3593
|
+
response = await fetch(remoteUrl);
|
|
3594
|
+
} catch (error) {
|
|
3595
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3596
|
+
return makeError(1, `\u4E0B\u8F7D\u89C6\u9891\u5931\u8D25: ${message}`);
|
|
3597
|
+
}
|
|
3598
|
+
if (!response.ok) {
|
|
3599
|
+
return makeError(response.status, `\u4E0B\u8F7D\u89C6\u9891\u5931\u8D25: HTTP ${response.status}`);
|
|
3600
|
+
}
|
|
3601
|
+
const bytes = Buffer.from(await response.arrayBuffer());
|
|
3602
|
+
const targetPath = target.autoNamed ? replaceFileExtension2(target.filePath, inferVideoExtension(remoteUrl, response)) : target.filePath;
|
|
3603
|
+
try {
|
|
3604
|
+
await fs7.writeFile(targetPath, bytes);
|
|
3605
|
+
} catch (error) {
|
|
3606
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3607
|
+
return makeError(1, `\u5199\u5165\u89C6\u9891\u5931\u8D25: ${targetPath}: ${message}`);
|
|
3608
|
+
}
|
|
3609
|
+
return makeOkWith(path9.resolve(targetPath));
|
|
3610
|
+
}
|
|
3611
|
+
function buildAutoFileName2() {
|
|
3612
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3613
|
+
return `ai-vid-${timestamp}.mp4`;
|
|
3614
|
+
}
|
|
3615
|
+
function replaceFileExtension2(filePath, extension) {
|
|
3616
|
+
const ext = path9.extname(filePath);
|
|
3617
|
+
if (!ext) {
|
|
3618
|
+
return `${filePath}.${extension}`;
|
|
3619
|
+
}
|
|
3620
|
+
return `${filePath.slice(0, -ext.length)}.${extension}`;
|
|
3621
|
+
}
|
|
3622
|
+
function inferVideoExtension(remoteUrl, response) {
|
|
3623
|
+
const contentType = response.headers.get("content-type")?.toLowerCase() ?? "";
|
|
3624
|
+
if (contentType.includes("mp4")) {
|
|
3625
|
+
return "mp4";
|
|
3626
|
+
}
|
|
3627
|
+
if (contentType.includes("webm")) {
|
|
3628
|
+
return "webm";
|
|
3629
|
+
}
|
|
3630
|
+
if (contentType.includes("quicktime") || contentType.includes("mov")) {
|
|
3631
|
+
return "mov";
|
|
3632
|
+
}
|
|
3633
|
+
try {
|
|
3634
|
+
const url = new URL(remoteUrl);
|
|
3635
|
+
const ext = path9.extname(url.pathname).toLowerCase();
|
|
3636
|
+
if (ext === ".mp4" || ext === ".webm" || ext === ".mov") {
|
|
3637
|
+
return ext.slice(1);
|
|
3638
|
+
}
|
|
3639
|
+
} catch {
|
|
3640
|
+
}
|
|
3641
|
+
return "mp4";
|
|
3642
|
+
}
|
|
3643
|
+
|
|
2958
3644
|
// src/commands/ai/ai-command.ts
|
|
2959
3645
|
var AiCommand = class extends BaseSubcommandHost {
|
|
2960
3646
|
_meta() {
|
|
@@ -2969,6 +3655,8 @@ var AiCommand = class extends BaseSubcommandHost {
|
|
|
2969
3655
|
this._addSubcommand(new AiCodexCommand());
|
|
2970
3656
|
this._addSubcommand(new AiRunCommand());
|
|
2971
3657
|
this._addSubcommand(new AiImageCommand());
|
|
3658
|
+
this._addSubcommand(new AiVideoCommand());
|
|
3659
|
+
this._addSubcommand(new AiVidCommand());
|
|
2972
3660
|
}
|
|
2973
3661
|
};
|
|
2974
3662
|
|