image-skill 0.1.7 → 0.1.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/CHANGELOG.md +53 -0
- package/PROVENANCE.md +78 -0
- package/README.md +48 -10
- package/bin/image-skill.mjs +564 -8
- package/cli.md +340 -44
- package/llms.txt +45 -24
- package/package.json +22 -4
- package/skill.md +225 -23
- package/skills/image-skill/SKILL.md +562 -0
- package/skills/image-skill/references/cli.md +1263 -0
- package/skills/image-skill/references/llms.txt +248 -0
package/bin/image-skill.mjs
CHANGED
|
@@ -7,8 +7,13 @@ import { Readable } from "node:stream";
|
|
|
7
7
|
import { pipeline } from "node:stream/promises";
|
|
8
8
|
import os from "node:os";
|
|
9
9
|
|
|
10
|
-
const VERSION = "0.1.
|
|
10
|
+
const VERSION = "0.1.8";
|
|
11
11
|
const DEFAULT_API_BASE_URL = "https://api.image-skill.com";
|
|
12
|
+
const PROMPTLESS_EDIT_MODEL_IDS = new Set([
|
|
13
|
+
"fal.flux-dev-redux",
|
|
14
|
+
"fal.flux-krea-redux",
|
|
15
|
+
"fal.flux-schnell-redux",
|
|
16
|
+
]);
|
|
12
17
|
const DEFAULT_CONFIG_PATH = join(
|
|
13
18
|
process.env.XDG_CONFIG_HOME ?? join(os.homedir(), ".config"),
|
|
14
19
|
"image-skill",
|
|
@@ -499,7 +504,7 @@ async function credits(argv) {
|
|
|
499
504
|
idempotency_key: idempotency.value,
|
|
500
505
|
},
|
|
501
506
|
});
|
|
502
|
-
return result;
|
|
507
|
+
return withStripeCheckoutCopyFallback(result);
|
|
503
508
|
}
|
|
504
509
|
if (subcommand === "fake-purchase") {
|
|
505
510
|
const args = parseArgs(rest);
|
|
@@ -546,13 +551,14 @@ async function credits(argv) {
|
|
|
546
551
|
addQueryFlag(query, args, "payment-attempt-id", "payment_attempt_id");
|
|
547
552
|
addQueryFlag(query, args, "checkout-session-id", "checkout_session_id");
|
|
548
553
|
addQueryFlag(query, args, "receipt-id", "receipt_id");
|
|
549
|
-
|
|
554
|
+
const result = await apiRequest({
|
|
550
555
|
command: "image-skill credits status",
|
|
551
556
|
method: "GET",
|
|
552
557
|
apiBaseUrl: apiBase(args),
|
|
553
558
|
path: `/v1/credit-purchases/status?${query.toString()}`,
|
|
554
559
|
token: token.token,
|
|
555
560
|
});
|
|
561
|
+
return withStripeCheckoutCopyFallback(result);
|
|
556
562
|
}
|
|
557
563
|
return invalid(
|
|
558
564
|
"image-skill credits",
|
|
@@ -647,10 +653,28 @@ async function create(argv) {
|
|
|
647
653
|
if (!token.ok) {
|
|
648
654
|
return token.result;
|
|
649
655
|
}
|
|
656
|
+
const referencePlan = parseReferencePlan(args, "image-skill create");
|
|
657
|
+
if (!referencePlan.ok) {
|
|
658
|
+
return referencePlan.result;
|
|
659
|
+
}
|
|
650
660
|
const modelParameters = jsonObjectFlag(args, "model-parameters-json");
|
|
651
661
|
if (!modelParameters.ok) {
|
|
652
662
|
return modelParameters.result;
|
|
653
663
|
}
|
|
664
|
+
const outputCount = positiveIntegerFlag(args, "output-count", {
|
|
665
|
+
command: "image-skill create",
|
|
666
|
+
});
|
|
667
|
+
if (!outputCount.ok) {
|
|
668
|
+
return outputCount.result;
|
|
669
|
+
}
|
|
670
|
+
const references = await resolveReferences(
|
|
671
|
+
referencePlan.referencePlans,
|
|
672
|
+
args,
|
|
673
|
+
token.token,
|
|
674
|
+
);
|
|
675
|
+
if (!references.ok) {
|
|
676
|
+
return references.result;
|
|
677
|
+
}
|
|
654
678
|
return apiRequest({
|
|
655
679
|
command: "image-skill create",
|
|
656
680
|
method: "POST",
|
|
@@ -669,6 +693,12 @@ async function create(argv) {
|
|
|
669
693
|
? {}
|
|
670
694
|
: { intent: flagString(args, "intent") }),
|
|
671
695
|
aspect_ratio: flagString(args, "aspect-ratio") ?? "1:1",
|
|
696
|
+
...(references.references.length === 0
|
|
697
|
+
? {}
|
|
698
|
+
: { references: references.references }),
|
|
699
|
+
...(outputCount.value === null
|
|
700
|
+
? {}
|
|
701
|
+
: { output_count: outputCount.value }),
|
|
672
702
|
...(flagNumber(args, "max-estimated-usd-per-image") === null
|
|
673
703
|
? {}
|
|
674
704
|
: {
|
|
@@ -713,13 +743,14 @@ async function upload(argv) {
|
|
|
713
743
|
async function edit(argv) {
|
|
714
744
|
const args = parseArgs(argv);
|
|
715
745
|
const input = flagString(args, "input") ?? args.positionals[0];
|
|
746
|
+
const modelId = flagString(args, "model");
|
|
716
747
|
if (input === undefined) {
|
|
717
748
|
return invalid(
|
|
718
749
|
"image-skill edit",
|
|
719
750
|
"edit requires --input ASSET_ID_OR_PATH_OR_URL",
|
|
720
751
|
);
|
|
721
752
|
}
|
|
722
|
-
const prompt = await
|
|
753
|
+
const prompt = await editPromptValue(args, modelId);
|
|
723
754
|
if (!prompt.ok) {
|
|
724
755
|
return prompt.result;
|
|
725
756
|
}
|
|
@@ -727,10 +758,28 @@ async function edit(argv) {
|
|
|
727
758
|
if (!token.ok) {
|
|
728
759
|
return token.result;
|
|
729
760
|
}
|
|
761
|
+
const referencePlan = parseReferencePlan(args, "image-skill edit");
|
|
762
|
+
if (!referencePlan.ok) {
|
|
763
|
+
return referencePlan.result;
|
|
764
|
+
}
|
|
730
765
|
const assetId = await resolveInputAssetId(input, args, token.token);
|
|
731
766
|
if (!assetId.ok) {
|
|
732
767
|
return assetId.result;
|
|
733
768
|
}
|
|
769
|
+
const mask = flagString(args, "mask");
|
|
770
|
+
const maskAssetId =
|
|
771
|
+
mask === null ? null : await resolveInputAssetId(mask, args, token.token);
|
|
772
|
+
if (maskAssetId !== null && !maskAssetId.ok) {
|
|
773
|
+
return maskAssetId.result;
|
|
774
|
+
}
|
|
775
|
+
const references = await resolveReferences(
|
|
776
|
+
referencePlan.referencePlans,
|
|
777
|
+
args,
|
|
778
|
+
token.token,
|
|
779
|
+
);
|
|
780
|
+
if (!references.ok) {
|
|
781
|
+
return references.result;
|
|
782
|
+
}
|
|
734
783
|
const modelParameters = jsonObjectFlag(args, "model-parameters-json");
|
|
735
784
|
if (!modelParameters.ok) {
|
|
736
785
|
return modelParameters.result;
|
|
@@ -743,13 +792,15 @@ async function edit(argv) {
|
|
|
743
792
|
token: token.token,
|
|
744
793
|
body: {
|
|
745
794
|
input_asset_id: assetId.assetId,
|
|
746
|
-
|
|
795
|
+
...(maskAssetId === null ? {} : { mask_asset_id: maskAssetId.assetId }),
|
|
796
|
+
...(references.references.length === 0
|
|
797
|
+
? {}
|
|
798
|
+
: { references: references.references }),
|
|
799
|
+
...(prompt.value.length === 0 ? {} : { prompt: prompt.value }),
|
|
747
800
|
...(flagString(args, "provider") === null
|
|
748
801
|
? {}
|
|
749
802
|
: { provider: flagString(args, "provider") }),
|
|
750
|
-
...(
|
|
751
|
-
? {}
|
|
752
|
-
: { model: flagString(args, "model") }),
|
|
803
|
+
...(modelId === null ? {} : { model: modelId }),
|
|
753
804
|
...(flagString(args, "intent") === null
|
|
754
805
|
? {}
|
|
755
806
|
: { intent: flagString(args, "intent") }),
|
|
@@ -1040,6 +1091,308 @@ async function resolveInputAssetId(input, args, token) {
|
|
|
1040
1091
|
return { ok: true, assetId };
|
|
1041
1092
|
}
|
|
1042
1093
|
|
|
1094
|
+
function parseReferencePlan(args, command) {
|
|
1095
|
+
for (const flag of [
|
|
1096
|
+
"element-frontal",
|
|
1097
|
+
"element-reference",
|
|
1098
|
+
"reference-image",
|
|
1099
|
+
]) {
|
|
1100
|
+
if (
|
|
1101
|
+
args.flags.has(flag) &&
|
|
1102
|
+
args.flags.get(flag)?.some((value) => typeof value !== "string")
|
|
1103
|
+
) {
|
|
1104
|
+
return {
|
|
1105
|
+
ok: false,
|
|
1106
|
+
result: invalid(command, `--${flag} requires an image`),
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
const referencePlans = [];
|
|
1111
|
+
for (const value of flagStrings(args, "element-frontal")) {
|
|
1112
|
+
const parsed = parseElementReferenceFlag(value, {
|
|
1113
|
+
flag: "--element-frontal",
|
|
1114
|
+
allowReferenceIndex: false,
|
|
1115
|
+
command,
|
|
1116
|
+
});
|
|
1117
|
+
if (!parsed.ok) {
|
|
1118
|
+
return parsed;
|
|
1119
|
+
}
|
|
1120
|
+
referencePlans.push({
|
|
1121
|
+
input: parsed.input,
|
|
1122
|
+
role: "element_frontal",
|
|
1123
|
+
index: parsed.index,
|
|
1124
|
+
referenceIndex: null,
|
|
1125
|
+
referenceTask: null,
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
for (const value of flagStrings(args, "element-reference")) {
|
|
1129
|
+
const parsed = parseElementReferenceFlag(value, {
|
|
1130
|
+
flag: "--element-reference",
|
|
1131
|
+
allowReferenceIndex: true,
|
|
1132
|
+
command,
|
|
1133
|
+
});
|
|
1134
|
+
if (!parsed.ok) {
|
|
1135
|
+
return parsed;
|
|
1136
|
+
}
|
|
1137
|
+
referencePlans.push({
|
|
1138
|
+
input: parsed.input,
|
|
1139
|
+
role: "element_reference",
|
|
1140
|
+
index: parsed.index,
|
|
1141
|
+
referenceIndex: parsed.referenceIndex,
|
|
1142
|
+
referenceTask: null,
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
for (const value of flagStrings(args, "reference-image")) {
|
|
1146
|
+
const parsed = parseReferenceImageFlag(value, {
|
|
1147
|
+
flag: "--reference-image",
|
|
1148
|
+
command,
|
|
1149
|
+
});
|
|
1150
|
+
if (!parsed.ok) {
|
|
1151
|
+
return parsed;
|
|
1152
|
+
}
|
|
1153
|
+
referencePlans.push({
|
|
1154
|
+
input: parsed.input,
|
|
1155
|
+
role: "reference_image",
|
|
1156
|
+
index: parsed.index,
|
|
1157
|
+
referenceIndex: null,
|
|
1158
|
+
referenceTask: parsed.referenceTask,
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
const planValidation = validateElementReferencePlan(referencePlans, command);
|
|
1162
|
+
if (!planValidation.ok) {
|
|
1163
|
+
return planValidation;
|
|
1164
|
+
}
|
|
1165
|
+
return { ok: true, referencePlans };
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
async function resolveReferences(referencePlans, args, token) {
|
|
1169
|
+
const references = [];
|
|
1170
|
+
for (const plan of referencePlans) {
|
|
1171
|
+
const assetId = await resolveInputAssetId(plan.input, args, token);
|
|
1172
|
+
if (!assetId.ok) {
|
|
1173
|
+
return assetId;
|
|
1174
|
+
}
|
|
1175
|
+
if (plan.role === "element_frontal") {
|
|
1176
|
+
references.push({
|
|
1177
|
+
asset_id: assetId.assetId,
|
|
1178
|
+
role: "element_frontal",
|
|
1179
|
+
index: plan.index,
|
|
1180
|
+
});
|
|
1181
|
+
continue;
|
|
1182
|
+
}
|
|
1183
|
+
if (plan.role === "reference_image") {
|
|
1184
|
+
references.push({
|
|
1185
|
+
asset_id: assetId.assetId,
|
|
1186
|
+
role: "reference_image",
|
|
1187
|
+
index: plan.index,
|
|
1188
|
+
...(plan.referenceTask === null
|
|
1189
|
+
? {}
|
|
1190
|
+
: { reference_task: plan.referenceTask }),
|
|
1191
|
+
});
|
|
1192
|
+
continue;
|
|
1193
|
+
}
|
|
1194
|
+
references.push({
|
|
1195
|
+
asset_id: assetId.assetId,
|
|
1196
|
+
role: "element_reference",
|
|
1197
|
+
index: plan.index,
|
|
1198
|
+
...(plan.referenceIndex === null
|
|
1199
|
+
? {}
|
|
1200
|
+
: { reference_index: plan.referenceIndex }),
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
return { ok: true, references };
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
function validateElementReferencePlan(referencePlans, command) {
|
|
1207
|
+
if (referencePlans.length === 0) {
|
|
1208
|
+
return { ok: true };
|
|
1209
|
+
}
|
|
1210
|
+
const frontals = new Set();
|
|
1211
|
+
const referencesByElement = new Map();
|
|
1212
|
+
const elementIndexes = new Set();
|
|
1213
|
+
for (const plan of referencePlans) {
|
|
1214
|
+
if (plan.role === "reference_image") {
|
|
1215
|
+
continue;
|
|
1216
|
+
}
|
|
1217
|
+
elementIndexes.add(plan.index);
|
|
1218
|
+
if (plan.role === "element_frontal") {
|
|
1219
|
+
if (frontals.has(plan.index)) {
|
|
1220
|
+
return {
|
|
1221
|
+
ok: false,
|
|
1222
|
+
result: invalid(
|
|
1223
|
+
command,
|
|
1224
|
+
`only one --element-frontal is allowed for element ${plan.index}`,
|
|
1225
|
+
),
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
frontals.add(plan.index);
|
|
1229
|
+
} else {
|
|
1230
|
+
const count = referencesByElement.get(plan.index) ?? 0;
|
|
1231
|
+
referencesByElement.set(plan.index, count + 1);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
const sortedIndexes = [...elementIndexes].sort((left, right) => left - right);
|
|
1236
|
+
for (let expected = 0; expected < sortedIndexes.length; expected += 1) {
|
|
1237
|
+
if (sortedIndexes[expected] !== expected) {
|
|
1238
|
+
return {
|
|
1239
|
+
ok: false,
|
|
1240
|
+
result: invalid(
|
|
1241
|
+
command,
|
|
1242
|
+
"element indexes must be contiguous starting at 0",
|
|
1243
|
+
),
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
for (const [index, count] of referencesByElement.entries()) {
|
|
1248
|
+
if (!frontals.has(index)) {
|
|
1249
|
+
return {
|
|
1250
|
+
ok: false,
|
|
1251
|
+
result: invalid(
|
|
1252
|
+
command,
|
|
1253
|
+
`--element-reference for element ${index} requires --element-frontal for the same element`,
|
|
1254
|
+
),
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
if (count > 3) {
|
|
1258
|
+
return {
|
|
1259
|
+
ok: false,
|
|
1260
|
+
result: invalid(
|
|
1261
|
+
command,
|
|
1262
|
+
`element ${index} accepts at most 3 --element-reference images`,
|
|
1263
|
+
),
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
const referenceImageIndexes = new Set();
|
|
1268
|
+
for (const plan of referencePlans) {
|
|
1269
|
+
if (plan.role !== "reference_image") {
|
|
1270
|
+
continue;
|
|
1271
|
+
}
|
|
1272
|
+
if (referenceImageIndexes.has(plan.index)) {
|
|
1273
|
+
return {
|
|
1274
|
+
ok: false,
|
|
1275
|
+
result: invalid(
|
|
1276
|
+
command,
|
|
1277
|
+
`only one --reference-image is allowed for index ${plan.index}`,
|
|
1278
|
+
),
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
referenceImageIndexes.add(plan.index);
|
|
1282
|
+
}
|
|
1283
|
+
const sortedReferenceImageIndexes = [...referenceImageIndexes].sort(
|
|
1284
|
+
(left, right) => left - right,
|
|
1285
|
+
);
|
|
1286
|
+
for (
|
|
1287
|
+
let expected = 0;
|
|
1288
|
+
expected < sortedReferenceImageIndexes.length;
|
|
1289
|
+
expected += 1
|
|
1290
|
+
) {
|
|
1291
|
+
if (sortedReferenceImageIndexes[expected] !== expected) {
|
|
1292
|
+
return {
|
|
1293
|
+
ok: false,
|
|
1294
|
+
result: invalid(
|
|
1295
|
+
command,
|
|
1296
|
+
"reference image indexes must be contiguous starting at 0",
|
|
1297
|
+
),
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
return { ok: true };
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
function parseReferenceImageFlag(value, options) {
|
|
1305
|
+
const parsed = parseReferenceImageSuffix(value);
|
|
1306
|
+
if (parsed.input.length === 0) {
|
|
1307
|
+
return {
|
|
1308
|
+
ok: false,
|
|
1309
|
+
result: invalid(options.command, `${options.flag} requires an image`),
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
if (parsed.index > 9) {
|
|
1313
|
+
return {
|
|
1314
|
+
ok: false,
|
|
1315
|
+
result: invalid(
|
|
1316
|
+
options.command,
|
|
1317
|
+
`${options.flag} index must be between 0 and 9`,
|
|
1318
|
+
),
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
return { ok: true, ...parsed };
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
function parseReferenceImageSuffix(value) {
|
|
1325
|
+
const atIndex = value.lastIndexOf("@");
|
|
1326
|
+
if (atIndex === -1) {
|
|
1327
|
+
return { input: value, index: 0, referenceTask: null };
|
|
1328
|
+
}
|
|
1329
|
+
const suffix = value.slice(atIndex + 1);
|
|
1330
|
+
if (!/^\d+(?::(?:ip|id|style))?$/.test(suffix)) {
|
|
1331
|
+
return { input: value, index: 0, referenceTask: null };
|
|
1332
|
+
}
|
|
1333
|
+
const [index, referenceTask] = suffix.split(":");
|
|
1334
|
+
return {
|
|
1335
|
+
input: value.slice(0, atIndex),
|
|
1336
|
+
index: Number(index),
|
|
1337
|
+
referenceTask: referenceTask ?? null,
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
function parseElementReferenceFlag(value, options) {
|
|
1342
|
+
const parsed = parseElementReferenceSuffix(value);
|
|
1343
|
+
if (parsed.input.length === 0) {
|
|
1344
|
+
return {
|
|
1345
|
+
ok: false,
|
|
1346
|
+
result: invalid(options.command, `${options.flag} requires an image`),
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
if (!options.allowReferenceIndex && parsed.referenceIndex !== null) {
|
|
1350
|
+
return {
|
|
1351
|
+
ok: false,
|
|
1352
|
+
result: invalid(
|
|
1353
|
+
options.command,
|
|
1354
|
+
`${options.flag} accepts IMAGE[@ELEMENT_INDEX], not a reference index`,
|
|
1355
|
+
),
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1358
|
+
if (parsed.index > 9) {
|
|
1359
|
+
return {
|
|
1360
|
+
ok: false,
|
|
1361
|
+
result: invalid(
|
|
1362
|
+
options.command,
|
|
1363
|
+
`${options.flag} element index must be between 0 and 9`,
|
|
1364
|
+
),
|
|
1365
|
+
};
|
|
1366
|
+
}
|
|
1367
|
+
if (parsed.referenceIndex !== null && parsed.referenceIndex > 2) {
|
|
1368
|
+
return {
|
|
1369
|
+
ok: false,
|
|
1370
|
+
result: invalid(
|
|
1371
|
+
options.command,
|
|
1372
|
+
`${options.flag} reference index must be between 0 and 2`,
|
|
1373
|
+
),
|
|
1374
|
+
};
|
|
1375
|
+
}
|
|
1376
|
+
return { ok: true, ...parsed };
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
function parseElementReferenceSuffix(value) {
|
|
1380
|
+
const atIndex = value.lastIndexOf("@");
|
|
1381
|
+
if (atIndex === -1) {
|
|
1382
|
+
return { input: value, index: 0, referenceIndex: null };
|
|
1383
|
+
}
|
|
1384
|
+
const suffix = value.slice(atIndex + 1);
|
|
1385
|
+
if (!/^\d+(?::\d+)?$/.test(suffix)) {
|
|
1386
|
+
return { input: value, index: 0, referenceIndex: null };
|
|
1387
|
+
}
|
|
1388
|
+
const [index, referenceIndex] = suffix.split(":").map((part) => Number(part));
|
|
1389
|
+
return {
|
|
1390
|
+
input: value.slice(0, atIndex),
|
|
1391
|
+
index,
|
|
1392
|
+
referenceIndex: referenceIndex ?? null,
|
|
1393
|
+
};
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1043
1396
|
async function uploadPayload(input) {
|
|
1044
1397
|
const isRemote = /^https?:\/\//i.test(input);
|
|
1045
1398
|
let bytes;
|
|
@@ -1168,6 +1521,125 @@ function parseEnvelope(text, command, statusCode) {
|
|
|
1168
1521
|
};
|
|
1169
1522
|
}
|
|
1170
1523
|
|
|
1524
|
+
function withStripeCheckoutCopyFallback(result) {
|
|
1525
|
+
const data = result.envelope.data;
|
|
1526
|
+
if (!isRecord(data)) {
|
|
1527
|
+
return result;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
const updated = stripeCheckoutCopyFallbackData(data);
|
|
1531
|
+
if (updated === data) {
|
|
1532
|
+
return result;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
return {
|
|
1536
|
+
...result,
|
|
1537
|
+
envelope: {
|
|
1538
|
+
...result.envelope,
|
|
1539
|
+
data: updated,
|
|
1540
|
+
},
|
|
1541
|
+
};
|
|
1542
|
+
}
|
|
1543
|
+
|
|
1544
|
+
function stripeCheckoutCopyFallbackData(data) {
|
|
1545
|
+
let changed = false;
|
|
1546
|
+
const updated = { ...data };
|
|
1547
|
+
|
|
1548
|
+
if (addCheckoutCompactUrl(updated)) {
|
|
1549
|
+
changed = true;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
if (isRecord(updated.next)) {
|
|
1553
|
+
const next = { ...updated.next };
|
|
1554
|
+
let nextChanged = addCheckoutCompactUrl(next);
|
|
1555
|
+
if (
|
|
1556
|
+
typeof updated.checkout_compact_url === "string" &&
|
|
1557
|
+
typeof next.checkout_compact_url !== "string"
|
|
1558
|
+
) {
|
|
1559
|
+
next.checkout_compact_url = updated.checkout_compact_url;
|
|
1560
|
+
nextChanged = true;
|
|
1561
|
+
}
|
|
1562
|
+
if (nextChanged) {
|
|
1563
|
+
updated.next = next;
|
|
1564
|
+
changed = true;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
if (isRecord(updated.payment_attempt)) {
|
|
1569
|
+
const paymentAttempt = { ...updated.payment_attempt };
|
|
1570
|
+
if (addCheckoutCompactUrl(paymentAttempt)) {
|
|
1571
|
+
updated.payment_attempt = paymentAttempt;
|
|
1572
|
+
changed = true;
|
|
1573
|
+
}
|
|
1574
|
+
if (isRecord(updated.next)) {
|
|
1575
|
+
const next = { ...updated.next };
|
|
1576
|
+
if (
|
|
1577
|
+
next.human_action === "open_checkout_url" &&
|
|
1578
|
+
typeof paymentAttempt.checkout_compact_url === "string" &&
|
|
1579
|
+
typeof next.checkout_compact_url !== "string"
|
|
1580
|
+
) {
|
|
1581
|
+
next.checkout_compact_url = paymentAttempt.checkout_compact_url;
|
|
1582
|
+
updated.next = next;
|
|
1583
|
+
changed = true;
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
return changed ? updated : data;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
function addCheckoutCompactUrl(record) {
|
|
1592
|
+
const raw =
|
|
1593
|
+
typeof record.checkout_url === "string"
|
|
1594
|
+
? record.checkout_url
|
|
1595
|
+
: typeof record.fallback_checkout_url === "string"
|
|
1596
|
+
? record.fallback_checkout_url
|
|
1597
|
+
: null;
|
|
1598
|
+
if (raw === null || raw.length === 0) {
|
|
1599
|
+
return false;
|
|
1600
|
+
}
|
|
1601
|
+
const compact = stripeCheckoutCompactUrl(raw);
|
|
1602
|
+
let changed = false;
|
|
1603
|
+
if (record.checkout_compact_url !== compact) {
|
|
1604
|
+
record.checkout_compact_url = compact;
|
|
1605
|
+
changed = true;
|
|
1606
|
+
}
|
|
1607
|
+
if (
|
|
1608
|
+
typeof record.checkout_url === "string" &&
|
|
1609
|
+
record.checkout_url !== compact
|
|
1610
|
+
) {
|
|
1611
|
+
record.checkout_url = compact;
|
|
1612
|
+
changed = true;
|
|
1613
|
+
}
|
|
1614
|
+
if (
|
|
1615
|
+
typeof record.fallback_checkout_url === "string" &&
|
|
1616
|
+
record.fallback_checkout_url !== compact
|
|
1617
|
+
) {
|
|
1618
|
+
record.fallback_checkout_url = compact;
|
|
1619
|
+
changed = true;
|
|
1620
|
+
}
|
|
1621
|
+
return changed;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
function stripeCheckoutCompactUrl(checkoutUrl) {
|
|
1625
|
+
const trimmed = checkoutUrl.trim();
|
|
1626
|
+
if (trimmed.length === 0) {
|
|
1627
|
+
return checkoutUrl;
|
|
1628
|
+
}
|
|
1629
|
+
try {
|
|
1630
|
+
const parsed = new URL(trimmed);
|
|
1631
|
+
parsed.hash = "";
|
|
1632
|
+
return parsed.toString();
|
|
1633
|
+
} catch {
|
|
1634
|
+
const hashIndex = trimmed.indexOf("#");
|
|
1635
|
+
return hashIndex === -1 ? trimmed : trimmed.slice(0, hashIndex);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
function isRecord(value) {
|
|
1640
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1171
1643
|
async function promptValue(args) {
|
|
1172
1644
|
const prompt = flagString(args, "prompt");
|
|
1173
1645
|
const promptFile = flagString(args, "prompt-file");
|
|
@@ -1192,6 +1664,69 @@ async function promptValue(args) {
|
|
|
1192
1664
|
};
|
|
1193
1665
|
}
|
|
1194
1666
|
|
|
1667
|
+
async function editPromptValue(args, modelId) {
|
|
1668
|
+
if (args.flags.has("prompt") && flagString(args, "prompt") === null) {
|
|
1669
|
+
return {
|
|
1670
|
+
ok: false,
|
|
1671
|
+
result: invalid("image-skill edit", "--prompt requires a value"),
|
|
1672
|
+
};
|
|
1673
|
+
}
|
|
1674
|
+
if (
|
|
1675
|
+
args.flags.has("prompt-file") &&
|
|
1676
|
+
flagString(args, "prompt-file") === null
|
|
1677
|
+
) {
|
|
1678
|
+
return {
|
|
1679
|
+
ok: false,
|
|
1680
|
+
result: invalid("image-skill edit", "--prompt-file requires a value"),
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
1683
|
+
const prompt = flagString(args, "prompt");
|
|
1684
|
+
const promptFile = flagString(args, "prompt-file");
|
|
1685
|
+
if (prompt !== null && promptFile !== null) {
|
|
1686
|
+
return {
|
|
1687
|
+
ok: false,
|
|
1688
|
+
result: invalid(
|
|
1689
|
+
"image-skill edit",
|
|
1690
|
+
"provide either --prompt or --prompt-file, not both",
|
|
1691
|
+
),
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
const isPromptlessModel =
|
|
1695
|
+
modelId !== null && PROMPTLESS_EDIT_MODEL_IDS.has(modelId);
|
|
1696
|
+
let value = null;
|
|
1697
|
+
if (prompt !== null) {
|
|
1698
|
+
value = prompt;
|
|
1699
|
+
} else if (promptFile !== null) {
|
|
1700
|
+
value = await readFile(promptFile, "utf8");
|
|
1701
|
+
}
|
|
1702
|
+
const trimmed = value?.trim() ?? "";
|
|
1703
|
+
if (isPromptlessModel) {
|
|
1704
|
+
if (trimmed.length > 0) {
|
|
1705
|
+
return {
|
|
1706
|
+
ok: false,
|
|
1707
|
+
result: invalid(
|
|
1708
|
+
"image-skill edit",
|
|
1709
|
+
`model ${modelId} does not accept --prompt`,
|
|
1710
|
+
),
|
|
1711
|
+
};
|
|
1712
|
+
}
|
|
1713
|
+
return { ok: true, value: "" };
|
|
1714
|
+
}
|
|
1715
|
+
if (value === null) {
|
|
1716
|
+
return {
|
|
1717
|
+
ok: false,
|
|
1718
|
+
result: invalid("image-skill edit", "edit requires --prompt"),
|
|
1719
|
+
};
|
|
1720
|
+
}
|
|
1721
|
+
if (trimmed.length === 0) {
|
|
1722
|
+
return {
|
|
1723
|
+
ok: false,
|
|
1724
|
+
result: invalid("image-skill edit", "edit prompt cannot be empty"),
|
|
1725
|
+
};
|
|
1726
|
+
}
|
|
1727
|
+
return { ok: true, value: trimmed };
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1195
1730
|
async function resolveToken(args, options = {}) {
|
|
1196
1731
|
if (flagBool(args, "token-stdin")) {
|
|
1197
1732
|
if (process.stdin.isTTY) {
|
|
@@ -1348,6 +1883,12 @@ function flagString(args, name) {
|
|
|
1348
1883
|
return typeof value === "string" ? value : null;
|
|
1349
1884
|
}
|
|
1350
1885
|
|
|
1886
|
+
function flagStrings(args, name) {
|
|
1887
|
+
return (args.flags.get(name) ?? []).filter(
|
|
1888
|
+
(value) => typeof value === "string",
|
|
1889
|
+
);
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1351
1892
|
function flagBool(args, name) {
|
|
1352
1893
|
return args.flags.has(name) && args.flags.get(name)?.at(-1) !== "false";
|
|
1353
1894
|
}
|
|
@@ -1404,6 +1945,21 @@ function flagNumber(args, name) {
|
|
|
1404
1945
|
return Number.isFinite(number) ? number : null;
|
|
1405
1946
|
}
|
|
1406
1947
|
|
|
1948
|
+
function positiveIntegerFlag(args, name, input) {
|
|
1949
|
+
const value = flagString(args, name);
|
|
1950
|
+
if (value === null) {
|
|
1951
|
+
return { ok: true, value: null };
|
|
1952
|
+
}
|
|
1953
|
+
const number = Number(value);
|
|
1954
|
+
if (!Number.isInteger(number) || number <= 0) {
|
|
1955
|
+
return {
|
|
1956
|
+
ok: false,
|
|
1957
|
+
result: invalid(input.command, `${name} must be a positive integer`),
|
|
1958
|
+
};
|
|
1959
|
+
}
|
|
1960
|
+
return { ok: true, value: number };
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1407
1963
|
function jsonObjectFlag(args, name) {
|
|
1408
1964
|
const raw = flagString(args, name);
|
|
1409
1965
|
if (raw === null) {
|