image-skill 0.1.61 → 0.1.63

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 CHANGED
@@ -4,6 +4,24 @@ This changelog tracks the public `image-skill` CLI package and public skill
4
4
  mirror. The npm package metadata remains the authority for tarball integrity and
5
5
  provenance; this file is the human- and agent-readable release map.
6
6
 
7
+ ## 0.1.63 - 2026-06-17
8
+
9
+ - Release (activation/self-fund): make quota/payment error recovery top-up
10
+ commands copy-runnable from ephemeral `npx image-skill@latest` runs. When
11
+ hosted create/edit or payment commands return `error.recovery.top_up`, the
12
+ public CLI now prefixes `suggested_command`, `suggested_commands`,
13
+ `top_up.first_command`, `top_up.quote_command`, nested `top_up.commands.*`,
14
+ and workflow step commands with the same zero-install handoff used by
15
+ `usage quota --json` and `create --guide`.
16
+
17
+ ## 0.1.62 - 2026-06-17
18
+
19
+ - Release (activation/self-fund): make `usage quota --json` top-up handoffs
20
+ copy-runnable from ephemeral `npx image-skill@latest` runs. Quota
21
+ `top_up`, generated `next_actions.self_fund`, and create-guide embedded
22
+ quota top-up commands now carry the public handoff prefix instead of bare
23
+ `image-skill ...` commands.
24
+
7
25
  ## 0.1.61 - 2026-06-17
8
26
 
9
27
  - Release (activation/self-fund): add payment-attempt continuation actions.
@@ -15,7 +15,7 @@ import { Readable } from "node:stream";
15
15
  import { pipeline } from "node:stream/promises";
16
16
  import os from "node:os";
17
17
 
18
- const VERSION = "0.1.61";
18
+ const VERSION = "0.1.63";
19
19
  const PACKAGE_NAME = "image-skill";
20
20
  const DEFAULT_API_BASE_URL = "https://api.image-skill.com";
21
21
  const DEFAULT_DOCS_BASE_URL = "https://image-skill.com";
@@ -1228,23 +1228,24 @@ async function credits(argv) {
1228
1228
  payment_method: paymentMethod,
1229
1229
  idempotency_key: idempotency.value,
1230
1230
  };
1231
- const result = await apiRequest({
1232
- command: "image-skill credits quote",
1233
- method: "POST",
1234
- apiBaseUrl: apiBase(args),
1235
- path: "/v1/credit-quotes",
1236
- token: token.token,
1237
- body,
1238
- });
1231
+ const commandPrefix = createGuideCommandPrefix();
1232
+ const result = withCopyRunnableQuotaRecoveryCommands(
1233
+ await apiRequest({
1234
+ command: "image-skill credits quote",
1235
+ method: "POST",
1236
+ apiBaseUrl: apiBase(args),
1237
+ path: "/v1/credit-quotes",
1238
+ token: token.token,
1239
+ body,
1240
+ }),
1241
+ commandPrefix,
1242
+ );
1239
1243
  if (idempotency.generated) {
1240
1244
  result.envelope.warnings.push(
1241
1245
  `generated idempotency key ${idempotency.value}; pass --idempotency-key for stable retries`,
1242
1246
  );
1243
1247
  }
1244
- return withCopyRunnableCreditQuoteCommands(
1245
- result,
1246
- createGuideCommandPrefix(),
1247
- );
1248
+ return withCopyRunnableCreditQuoteCommands(result, commandPrefix);
1248
1249
  }
1249
1250
  if (subcommand === "buy") {
1250
1251
  const args = parseArgs(rest);
@@ -1285,20 +1286,24 @@ async function credits(argv) {
1285
1286
  provider === "stripe_x402"
1286
1287
  ? "/v1/credit-purchases/stripe-x402-deposits"
1287
1288
  : "/v1/credit-purchases/stripe-checkout-sessions";
1288
- const result = await apiRequest({
1289
- command: "image-skill credits buy",
1290
- method: "POST",
1291
- apiBaseUrl: apiBase(args),
1292
- path: purchasePath,
1293
- token: token.token,
1294
- body: {
1295
- quote_id: quoteId,
1296
- idempotency_key: idempotency.value,
1297
- },
1298
- });
1289
+ const commandPrefix = createGuideCommandPrefix();
1290
+ const result = withCopyRunnableQuotaRecoveryCommands(
1291
+ await apiRequest({
1292
+ command: "image-skill credits buy",
1293
+ method: "POST",
1294
+ apiBaseUrl: apiBase(args),
1295
+ path: purchasePath,
1296
+ token: token.token,
1297
+ body: {
1298
+ quote_id: quoteId,
1299
+ idempotency_key: idempotency.value,
1300
+ },
1301
+ }),
1302
+ commandPrefix,
1303
+ );
1299
1304
  return withCopyRunnablePaymentNextActionCommands(
1300
1305
  withStripeCheckoutCopyFallback(result),
1301
- createGuideCommandPrefix(),
1306
+ commandPrefix,
1302
1307
  );
1303
1308
  }
1304
1309
  if (subcommand === "status") {
@@ -1312,16 +1317,20 @@ async function credits(argv) {
1312
1317
  addQueryFlag(query, args, "payment-attempt-id", "payment_attempt_id");
1313
1318
  addQueryFlag(query, args, "checkout-session-id", "checkout_session_id");
1314
1319
  addQueryFlag(query, args, "receipt-id", "receipt_id");
1315
- const result = await apiRequest({
1316
- command: "image-skill credits status",
1317
- method: "GET",
1318
- apiBaseUrl: apiBase(args),
1319
- path: `/v1/credit-purchases/status?${query.toString()}`,
1320
- token: token.token,
1321
- });
1320
+ const commandPrefix = createGuideCommandPrefix();
1321
+ const result = withCopyRunnableQuotaRecoveryCommands(
1322
+ await apiRequest({
1323
+ command: "image-skill credits status",
1324
+ method: "GET",
1325
+ apiBaseUrl: apiBase(args),
1326
+ path: `/v1/credit-purchases/status?${query.toString()}`,
1327
+ token: token.token,
1328
+ }),
1329
+ commandPrefix,
1330
+ );
1322
1331
  return withCopyRunnablePaymentNextActionCommands(
1323
1332
  withStripeCheckoutCopyFallback(result),
1324
- createGuideCommandPrefix(),
1333
+ commandPrefix,
1325
1334
  );
1326
1335
  }
1327
1336
  return invalid(
@@ -1746,16 +1755,22 @@ async function createGuide(args, options = {}) {
1746
1755
  maxEstimatedUsdPerImage ??
1747
1756
  estimatedDebitUsdPerImage ??
1748
1757
  (estimatedCredits === null ? 0.07 : estimatedCredits / 100);
1758
+ const quotaCommandPrefix = createGuideCommandPrefix({
1759
+ configPath: configuredImageSkillConfigPath(),
1760
+ });
1749
1761
  const quota =
1750
1762
  token.token === null
1751
1763
  ? null
1752
- : await apiRequest({
1753
- command: "image-skill create --guide",
1754
- method: "GET",
1755
- apiBaseUrl,
1756
- path: "/v1/quota",
1757
- token: token.token,
1758
- });
1764
+ : withQuotaNextActions(
1765
+ await apiRequest({
1766
+ command: "image-skill create --guide",
1767
+ method: "GET",
1768
+ apiBaseUrl,
1769
+ path: "/v1/quota",
1770
+ token: token.token,
1771
+ }),
1772
+ quotaCommandPrefix,
1773
+ );
1759
1774
  const authenticated = quota?.envelope.data?.authenticated === true;
1760
1775
  const publicTokenSource =
1761
1776
  token.source === "anonymous" ? "none" : token.source;
@@ -4042,48 +4057,51 @@ async function create(argv) {
4042
4057
  argv,
4043
4058
  })
4044
4059
  : null;
4045
- const result = await apiRequest({
4046
- command: "image-skill create",
4047
- method: "POST",
4048
- apiBaseUrl: apiBase(args),
4049
- path: "/v1/create",
4050
- ...(token.token === null ? {} : { token: token.token }),
4051
- body: {
4052
- prompt: prompt.value,
4053
- ...(flagString(args, "provider") === null
4054
- ? {}
4055
- : { provider: flagString(args, "provider") }),
4056
- ...(flagString(args, "model") === null
4057
- ? {}
4058
- : { model: flagString(args, "model") }),
4059
- ...(flagString(args, "intent") === null
4060
- ? {}
4061
- : { intent: flagString(args, "intent") }),
4062
- aspect_ratio: flagString(args, "aspect-ratio") ?? "1:1",
4063
- ...(references.references.length === 0
4064
- ? {}
4065
- : { references: references.references }),
4066
- ...(outputCount.value === null
4067
- ? {}
4068
- : { output_count: outputCount.value }),
4069
- ...(flagNumber(args, "max-estimated-usd-per-image") === null
4070
- ? {}
4071
- : {
4072
- max_estimated_usd_per_image: flagNumber(
4073
- args,
4074
- "max-estimated-usd-per-image",
4075
- ),
4076
- }),
4077
- ...(modelParameters.value === null
4078
- ? {}
4079
- : { model_parameters: modelParameters.value }),
4080
- // Retry-safe dedupe (#1228/#1789): a live create always carries a key so a
4081
- // retry (or an interrupted-then-recovered run) dedupes to one charge.
4082
- ...(idempotencyKey === null ? {} : { idempotency_key: idempotencyKey }),
4083
- dry_run: flagBool(args, "dry-run"),
4084
- accept_unknown_cost: flagBool(args, "accept-unknown-cost"),
4085
- },
4086
- });
4060
+ const result = withCopyRunnableQuotaRecoveryCommands(
4061
+ await apiRequest({
4062
+ command: "image-skill create",
4063
+ method: "POST",
4064
+ apiBaseUrl: apiBase(args),
4065
+ path: "/v1/create",
4066
+ ...(token.token === null ? {} : { token: token.token }),
4067
+ body: {
4068
+ prompt: prompt.value,
4069
+ ...(flagString(args, "provider") === null
4070
+ ? {}
4071
+ : { provider: flagString(args, "provider") }),
4072
+ ...(flagString(args, "model") === null
4073
+ ? {}
4074
+ : { model: flagString(args, "model") }),
4075
+ ...(flagString(args, "intent") === null
4076
+ ? {}
4077
+ : { intent: flagString(args, "intent") }),
4078
+ aspect_ratio: flagString(args, "aspect-ratio") ?? "1:1",
4079
+ ...(references.references.length === 0
4080
+ ? {}
4081
+ : { references: references.references }),
4082
+ ...(outputCount.value === null
4083
+ ? {}
4084
+ : { output_count: outputCount.value }),
4085
+ ...(flagNumber(args, "max-estimated-usd-per-image") === null
4086
+ ? {}
4087
+ : {
4088
+ max_estimated_usd_per_image: flagNumber(
4089
+ args,
4090
+ "max-estimated-usd-per-image",
4091
+ ),
4092
+ }),
4093
+ ...(modelParameters.value === null
4094
+ ? {}
4095
+ : { model_parameters: modelParameters.value }),
4096
+ // Retry-safe dedupe (#1228/#1789): a live create always carries a key so a
4097
+ // retry (or an interrupted-then-recovered run) dedupes to one charge.
4098
+ ...(idempotencyKey === null ? {} : { idempotency_key: idempotencyKey }),
4099
+ dry_run: flagBool(args, "dry-run"),
4100
+ accept_unknown_cost: flagBool(args, "accept-unknown-cost"),
4101
+ },
4102
+ }),
4103
+ createGuideCommandPrefix(),
4104
+ );
4087
4105
  await clearInFlightSpendForResult(inFlight, result);
4088
4106
  return result;
4089
4107
  }
@@ -4176,45 +4194,48 @@ async function edit(argv) {
4176
4194
  argv,
4177
4195
  })
4178
4196
  : null;
4179
- const result = await apiRequest({
4180
- command: "image-skill edit",
4181
- method: "POST",
4182
- apiBaseUrl: apiBase(args),
4183
- path: "/v1/edit",
4184
- token: token.token,
4185
- body: {
4186
- input_asset_id: assetId.assetId,
4187
- ...(maskAssetId === null ? {} : { mask_asset_id: maskAssetId.assetId }),
4188
- ...(references.references.length === 0
4189
- ? {}
4190
- : { references: references.references }),
4191
- ...(prompt.value.length === 0 ? {} : { prompt: prompt.value }),
4192
- ...(flagString(args, "provider") === null
4193
- ? {}
4194
- : { provider: flagString(args, "provider") }),
4195
- ...(modelId === null ? {} : { model: modelId }),
4196
- ...(flagString(args, "intent") === null
4197
- ? {}
4198
- : { intent: flagString(args, "intent") }),
4199
- aspect_ratio: flagString(args, "aspect-ratio") ?? "auto",
4200
- ...(flagNumber(args, "max-estimated-usd-per-image") === null
4201
- ? {}
4202
- : {
4203
- max_estimated_usd_per_image: flagNumber(
4204
- args,
4205
- "max-estimated-usd-per-image",
4206
- ),
4207
- }),
4208
- ...(modelParameters.value === null
4209
- ? {}
4210
- : { model_parameters: modelParameters.value }),
4211
- ...(flagBool(args, "dry-run") ? { dry_run: true } : {}),
4212
- // Retry-safe dedupe (#1228/#1789): a live edit always carries a key so a
4213
- // retry (or an interrupted-then-recovered run) dedupes to one charge.
4214
- ...(idempotencyKey === null ? {} : { idempotency_key: idempotencyKey }),
4215
- accept_unknown_cost: flagBool(args, "accept-unknown-cost"),
4216
- },
4217
- });
4197
+ const result = withCopyRunnableQuotaRecoveryCommands(
4198
+ await apiRequest({
4199
+ command: "image-skill edit",
4200
+ method: "POST",
4201
+ apiBaseUrl: apiBase(args),
4202
+ path: "/v1/edit",
4203
+ token: token.token,
4204
+ body: {
4205
+ input_asset_id: assetId.assetId,
4206
+ ...(maskAssetId === null ? {} : { mask_asset_id: maskAssetId.assetId }),
4207
+ ...(references.references.length === 0
4208
+ ? {}
4209
+ : { references: references.references }),
4210
+ ...(prompt.value.length === 0 ? {} : { prompt: prompt.value }),
4211
+ ...(flagString(args, "provider") === null
4212
+ ? {}
4213
+ : { provider: flagString(args, "provider") }),
4214
+ ...(modelId === null ? {} : { model: modelId }),
4215
+ ...(flagString(args, "intent") === null
4216
+ ? {}
4217
+ : { intent: flagString(args, "intent") }),
4218
+ aspect_ratio: flagString(args, "aspect-ratio") ?? "auto",
4219
+ ...(flagNumber(args, "max-estimated-usd-per-image") === null
4220
+ ? {}
4221
+ : {
4222
+ max_estimated_usd_per_image: flagNumber(
4223
+ args,
4224
+ "max-estimated-usd-per-image",
4225
+ ),
4226
+ }),
4227
+ ...(modelParameters.value === null
4228
+ ? {}
4229
+ : { model_parameters: modelParameters.value }),
4230
+ ...(flagBool(args, "dry-run") ? { dry_run: true } : {}),
4231
+ // Retry-safe dedupe (#1228/#1789): a live edit always carries a key so a
4232
+ // retry (or an interrupted-then-recovered run) dedupes to one charge.
4233
+ ...(idempotencyKey === null ? {} : { idempotency_key: idempotencyKey }),
4234
+ accept_unknown_cost: flagBool(args, "accept-unknown-cost"),
4235
+ },
4236
+ }),
4237
+ createGuideCommandPrefix(),
4238
+ );
4218
4239
  await clearInFlightSpendForResult(inFlight, result);
4219
4240
  return result;
4220
4241
  }
@@ -6341,7 +6362,10 @@ function withCommand(result, command) {
6341
6362
  };
6342
6363
  }
6343
6364
 
6344
- function withQuotaNextActions(result) {
6365
+ function withQuotaNextActions(
6366
+ result,
6367
+ commandPrefix = createGuideCommandPrefix(),
6368
+ ) {
6345
6369
  const data = result.envelope?.data;
6346
6370
  if (
6347
6371
  result.envelope?.ok !== true ||
@@ -6350,22 +6374,132 @@ function withQuotaNextActions(result) {
6350
6374
  ) {
6351
6375
  return result;
6352
6376
  }
6353
- const nextActions = quotaTopUpNextActions(data.top_up);
6377
+ const quotaData = quotaDataWithCopyRunnableTopUpCommands(data, commandPrefix);
6378
+ const nextActions = quotaTopUpNextActions(quotaData.top_up);
6354
6379
  if (nextActions === null) {
6355
- return result;
6380
+ return quotaData === data
6381
+ ? result
6382
+ : {
6383
+ ...result,
6384
+ envelope: {
6385
+ ...result.envelope,
6386
+ data: quotaData,
6387
+ },
6388
+ };
6356
6389
  }
6357
6390
  return {
6358
6391
  ...result,
6359
6392
  envelope: {
6360
6393
  ...result.envelope,
6361
6394
  data: {
6362
- ...data,
6395
+ ...quotaData,
6363
6396
  next_actions: nextActions,
6364
6397
  },
6365
6398
  },
6366
6399
  };
6367
6400
  }
6368
6401
 
6402
+ function quotaDataWithCopyRunnableTopUpCommands(data, commandPrefix) {
6403
+ if (data.top_up === undefined || data.top_up === null) {
6404
+ return data;
6405
+ }
6406
+ const topUp = quotaTopUpWithCopyRunnableCommands(data.top_up, commandPrefix);
6407
+ return topUp === data.top_up ? data : { ...data, top_up: topUp };
6408
+ }
6409
+
6410
+ function quotaTopUpWithCopyRunnableCommands(topUp, commandPrefix) {
6411
+ const commands = topUp.commands;
6412
+ const workflow = topUp.workflow;
6413
+ if (topUp === null || typeof topUp !== "object") {
6414
+ return topUp;
6415
+ }
6416
+ const render = (command) =>
6417
+ renderCopyRunnablePaymentCommand(commandPrefix, command);
6418
+ const renderIfString = (value) =>
6419
+ typeof value === "string" ? render(value) : value;
6420
+ return {
6421
+ ...topUp,
6422
+ first_command: renderIfString(topUp.first_command),
6423
+ quote_command: renderIfString(topUp.quote_command),
6424
+ commands:
6425
+ commands !== null && typeof commands === "object"
6426
+ ? {
6427
+ ...commands,
6428
+ inspect_methods: renderIfString(commands.inspect_methods),
6429
+ inspect_packs: renderIfString(commands.inspect_packs),
6430
+ quote: renderIfString(commands.quote),
6431
+ buy: renderIfString(commands.buy),
6432
+ status: renderIfString(commands.status),
6433
+ fallback_quote: renderIfString(commands.fallback_quote),
6434
+ fallback_buy: renderIfString(commands.fallback_buy),
6435
+ }
6436
+ : commands,
6437
+ workflow:
6438
+ workflow !== null &&
6439
+ typeof workflow === "object" &&
6440
+ Array.isArray(workflow.steps)
6441
+ ? {
6442
+ ...workflow,
6443
+ steps: workflow.steps.map((step) =>
6444
+ step !== null &&
6445
+ typeof step === "object" &&
6446
+ typeof step.command === "string"
6447
+ ? { ...step, command: render(step.command) }
6448
+ : step,
6449
+ ),
6450
+ }
6451
+ : workflow,
6452
+ };
6453
+ }
6454
+
6455
+ function withCopyRunnableQuotaRecoveryCommands(
6456
+ result,
6457
+ commandPrefix = createGuideCommandPrefix(),
6458
+ ) {
6459
+ const recovery = result.envelope?.error?.recovery;
6460
+ if (
6461
+ result.envelope?.ok === true ||
6462
+ recovery === null ||
6463
+ typeof recovery !== "object" ||
6464
+ recovery.top_up === undefined ||
6465
+ recovery.top_up === null
6466
+ ) {
6467
+ return result;
6468
+ }
6469
+ return {
6470
+ ...result,
6471
+ envelope: {
6472
+ ...result.envelope,
6473
+ error: {
6474
+ ...result.envelope.error,
6475
+ recovery: quotaRecoveryWithCopyRunnableCommands(
6476
+ recovery,
6477
+ commandPrefix,
6478
+ ),
6479
+ },
6480
+ },
6481
+ };
6482
+ }
6483
+
6484
+ function quotaRecoveryWithCopyRunnableCommands(recovery, commandPrefix) {
6485
+ const render = (command) =>
6486
+ renderCopyRunnablePaymentCommand(commandPrefix, command);
6487
+ return {
6488
+ ...recovery,
6489
+ ...(typeof recovery.suggested_command === "string"
6490
+ ? { suggested_command: render(recovery.suggested_command) }
6491
+ : {}),
6492
+ ...(Array.isArray(recovery.suggested_commands)
6493
+ ? {
6494
+ suggested_commands: recovery.suggested_commands.map((command) =>
6495
+ typeof command === "string" ? render(command) : command,
6496
+ ),
6497
+ }
6498
+ : {}),
6499
+ top_up: quotaTopUpWithCopyRunnableCommands(recovery.top_up, commandPrefix),
6500
+ };
6501
+ }
6502
+
6369
6503
  function quotaTopUpNextActions(topUp) {
6370
6504
  const commands = topUp?.commands;
6371
6505
  if (
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "image-skill",
3
- "version": "0.1.61",
3
+ "version": "0.1.63",
4
4
  "description": "Zero-setup durable creative-media CLI for agents (image + video + audio + 3D): guide-first creation, model and cost inspection, owned URLs, JSON recovery, payments, reusable assets, and feedback.",
5
5
  "type": "module",
6
6
  "private": false,