@clawsaga/cli 0.1.27 → 0.1.29

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
@@ -2,6 +2,16 @@
2
2
 
3
3
  Changes to this CLI and skill, newest first. Game, rule and API changes shared with MCP are in `clawsaga changelog`.
4
4
 
5
+ ## 0.1.29
6
+
7
+ - Supports response format v3.18, unchanged from 0.1.28.
8
+ - Tactics accept the `vulnerable`, `enfeebled`, `empowered` and `regen` statuses, and the `enemy_has_status` and `enemy_missing_status` conditions accept any status.
9
+ - Tactics `potion_items` accepts any potion item ID. When omitted, every potion usable in battle is drunk, weakest first.
10
+
11
+ ## 0.1.28
12
+
13
+ - Internal code cleanup. Commands, output and skill instructions are unchanged.
14
+
5
15
  ## 0.1.27
6
16
 
7
17
  - Updated the response format to v3.18. Older CLIs reject responses from the current server. World quests replace `reward_tiers`, `your_tier` and `next_tier_points` with `reward_per_stage` and `reward_point_cap`.
package/bin/clawsaga.mjs CHANGED
@@ -24074,7 +24074,11 @@ var combatStatusSchema = external_exports.enum([
24074
24074
  "guard",
24075
24075
  "barrier",
24076
24076
  "battle_song",
24077
- "soothing_song"
24077
+ "soothing_song",
24078
+ "vulnerable",
24079
+ "enfeebled",
24080
+ "empowered",
24081
+ "regen"
24078
24082
  ]);
24079
24083
  var tacticConditionSchema = external_exports.discriminatedUnion("kind", [
24080
24084
  external_exports.object({
@@ -24112,9 +24116,11 @@ var tacticConditionSchema = external_exports.discriminatedUnion("kind", [
24112
24116
  "Matches when the named self status has remaining ticks, or has none, respectively."
24113
24117
  ),
24114
24118
  external_exports.object({
24115
- kind: external_exports.literal("enemy_missing_status"),
24116
- status: external_exports.literal("poison")
24117
- }).strict().describe("Matches when the enemy has no remaining poison ticks.")
24119
+ kind: external_exports.enum(["enemy_has_status", "enemy_missing_status"]),
24120
+ status: combatStatusSchema
24121
+ }).strict().describe(
24122
+ "Matches when the named enemy status has more than one tick left, or not; one tick left counts as missing."
24123
+ )
24118
24124
  ]);
24119
24125
  var tacticActionSchema = external_exports.discriminatedUnion("kind", [
24120
24126
  external_exports.object({ kind: external_exports.enum(["attack", "defend", "potion", "retreat"]) }).strict().describe(
@@ -24124,10 +24130,6 @@ var tacticActionSchema = external_exports.discriminatedUnion("kind", [
24124
24130
  "Uses the ability when it is unlocked, off cooldown, affordable in MP and its effect is currently applicable."
24125
24131
  )
24126
24132
  ]);
24127
- var combatPotionIdSchema = external_exports.enum([
24128
- "healing_potion",
24129
- "manastone_potion"
24130
- ]);
24131
24133
  var tacticSchema = external_exports.object({
24132
24134
  rules: external_exports.array(
24133
24135
  external_exports.object({
@@ -24142,11 +24144,11 @@ var tacticSchema = external_exports.object({
24142
24144
  potion_limit: external_exports.number().int().min(0).describe(
24143
24145
  "Most potions one battle may use in total, capped by the bag at start. Each is consumed when used; zero disables potions."
24144
24146
  ),
24145
- potion_items: external_exports.array(combatPotionIdSchema).min(1).refine(
24147
+ potion_items: external_exports.array(itemIdSchema).min(1).refine(
24146
24148
  (itemIds) => new Set(itemIds).size === itemIds.length,
24147
24149
  "potion_items must not repeat"
24148
24150
  ).optional().describe(
24149
- "Potions in drinking order. Defaults to healing_potion, then manastone_potion."
24151
+ "Item IDs of potions in drinking order. When omitted, drinks every potion usable in battle, weakest first."
24150
24152
  )
24151
24153
  }).strict().meta({ id: "Tactic" });
24152
24154
  var presetIdSchema = external_exports.enum(["safe", "aggressive"]);
@@ -24565,7 +24567,7 @@ var CredentialStore = class {
24565
24567
  // package.json
24566
24568
  var package_default = {
24567
24569
  name: "@clawsaga/cli",
24568
- version: "0.1.27",
24570
+ version: "0.1.29",
24569
24571
  homepage: "https://clawsaga.net",
24570
24572
  repository: {
24571
24573
  type: "git",
@@ -24640,7 +24642,7 @@ var retryAfterSchema = external_exports.object({
24640
24642
  var schemaVersionSchema = external_exports.object({ schema_version: external_exports.string() });
24641
24643
  var [supportedSchemaMajor = 0, supportedSchemaMinor = 0] = agentSchemaVersion.split(".").map(Number);
24642
24644
  var checkOutcome = "The action may still have been applied; check its outcome before another change.";
24643
- async function readBody(response) {
24645
+ async function readJsonOrUndefined(response) {
24644
24646
  return response.json().catch(() => void 0);
24645
24647
  }
24646
24648
  function serverMessage(body) {
@@ -24668,9 +24670,9 @@ function needsUpdate(body) {
24668
24670
  var announcedCli;
24669
24671
  function noteAnnouncedCli(response) {
24670
24672
  const version2 = response.headers.get("ClawSaga-CLI-Version");
24671
- if (version2 && /^\d+\.\d+\.\d+$/.test(version2) && version2.localeCompare(package_default.version, "en", { numeric: true }) > 0) {
24672
- announcedCli = version2;
24673
- }
24673
+ if (!version2 || !/^\d+\.\d+\.\d+$/.test(version2)) return;
24674
+ const announcedIsNewer = version2.localeCompare(package_default.version, "en", { numeric: true }) > 0;
24675
+ if (announcedIsNewer) announcedCli = version2;
24674
24676
  }
24675
24677
  function announcedCliUpdate() {
24676
24678
  return announcedCli;
@@ -24713,13 +24715,13 @@ var GameClient = class {
24713
24715
  });
24714
24716
  if (response.status >= 500) throw new CliError("SERVICE_UNAVAILABLE");
24715
24717
  if (response.status === 429) {
24716
- throw rateLimited(response, await readBody(response));
24718
+ throw rateLimited(response, await readJsonOrUndefined(response));
24717
24719
  }
24718
24720
  return response;
24719
24721
  }
24720
24722
  async decodeTokens(response) {
24721
24723
  if (!response.ok) throw new CliError("AUTH_REQUIRED");
24722
- const parsed = tokensSchema.safeParse(await readBody(response));
24724
+ const parsed = tokensSchema.safeParse(await readJsonOrUndefined(response));
24723
24725
  if (!parsed.success) throw new CliError("INVALID_RESPONSE");
24724
24726
  return {
24725
24727
  ...parsed.data,
@@ -24733,7 +24735,7 @@ var GameClient = class {
24733
24735
  resource: `${this.origin}/`
24734
24736
  });
24735
24737
  if (!response.ok) throw new CliError("AUTH_START_FAILED");
24736
- const parsed = deviceSchema.safeParse(await readBody(response));
24738
+ const parsed = deviceSchema.safeParse(await readJsonOrUndefined(response));
24737
24739
  if (!parsed.success) throw new CliError("INVALID_RESPONSE");
24738
24740
  const device = parsed.data;
24739
24741
  if (new URL(device.verification_uri_complete).origin !== this.origin) {
@@ -24778,7 +24780,9 @@ var GameClient = class {
24778
24780
  entries[this.origin] = credential;
24779
24781
  return credential.access_token;
24780
24782
  }
24781
- const failure = errorSchema.safeParse(await readBody(polled));
24783
+ const failure = errorSchema.safeParse(
24784
+ await readJsonOrUndefined(polled)
24785
+ );
24782
24786
  if (!failure.success) {
24783
24787
  throw new CliError("AUTH_NOT_COMPLETED", {
24784
24788
  reason: "invalid_response"
@@ -24822,7 +24826,7 @@ var GameClient = class {
24822
24826
  body: JSON.stringify(input2)
24823
24827
  });
24824
24828
  noteAnnouncedCli(response);
24825
- const body = await readBody(response);
24829
+ const body = await readJsonOrUndefined(response);
24826
24830
  const message = serverMessage(body);
24827
24831
  if (response.status === 401) {
24828
24832
  throw new CliError("AUTH_REQUIRED", {
@@ -24862,10 +24866,13 @@ var GameClient = class {
24862
24866
  }
24863
24867
  return parsed.data;
24864
24868
  }
24865
- // Public reads stay usable before authorization and are not agent responses.
24869
+ /**
24870
+ * Reads a public document. It works before authorization, and the body is
24871
+ * not an agent game response.
24872
+ */
24866
24873
  async readDocument(path2, schema) {
24867
24874
  const response = await this.send(`/api/v1/${path2}`, { method: "GET" });
24868
- const body = await readBody(response);
24875
+ const body = await readJsonOrUndefined(response);
24869
24876
  if (response.status === 429) throw rateLimited(response, body);
24870
24877
  if (response.status >= 500) throw new CliError("SERVICE_UNAVAILABLE");
24871
24878
  const parsed = schema.safeParse(body);
@@ -24931,7 +24938,8 @@ function ambushNotes(response, character) {
24931
24938
  const activity = response.data.activity ?? void 0;
24932
24939
  const ambush = lastResult?.ambush;
24933
24940
  if (!lastResult || !ambush) return { notes };
24934
- if (activity?.kind === "combat" && activity.activity_id === ambush.activity_id) {
24941
+ const ambushIsCurrentActivity = activity?.kind === "combat" && activity.activity_id === ambush.activity_id;
24942
+ if (ambushIsCurrentActivity) {
24935
24943
  notes.push(
24936
24944
  `The ${lastResult.kind} activity is complete. Continue the ambush battle or retreat.`
24937
24945
  );
@@ -25139,8 +25147,11 @@ async function updateCli(server) {
25139
25147
  }
25140
25148
  throw new CliError("UPDATE_FAILED");
25141
25149
  }
25150
+ function isVerificationRead() {
25151
+ return process.env.CLAWSAGA_AUTO_UPDATE === "0";
25152
+ }
25142
25153
  async function updateAnnouncedCli(version2, notify) {
25143
- if (process.env.CLAWSAGA_AUTO_UPDATE === "0") return;
25154
+ if (isVerificationRead()) return;
25144
25155
  notify(
25145
25156
  `Updating the CLI to ${version2}. The result above is final; wait for this command to finish.`
25146
25157
  );
@@ -25154,8 +25165,7 @@ async function updateAnnouncedCli(version2, notify) {
25154
25165
  }
25155
25166
  }
25156
25167
  async function recoverUpdate(error61, notify) {
25157
- if (!(error61 instanceof CliError) || error61.code !== "UPDATE_REQUIRED" || // Set for the verification read, which must not start an update of its own.
25158
- process.env.CLAWSAGA_AUTO_UPDATE === "0") {
25168
+ if (!(error61 instanceof CliError) || error61.code !== "UPDATE_REQUIRED" || isVerificationRead()) {
25159
25169
  return error61;
25160
25170
  }
25161
25171
  const { server } = error61.detail;
@@ -25192,7 +25202,6 @@ function acceptedActivityNote(activity, nextPollAfterSeconds, requestId) {
25192
25202
  activity_id: activity.activity_id,
25193
25203
  kind: activity.kind,
25194
25204
  started_at: activity.started_at,
25195
- // 出力のJSONでは値のない項目が落ちる。
25196
25205
  completes_at: activity.completes_at,
25197
25206
  arrives_at: activity.arrives_at,
25198
25207
  time_limit_at: activity.time_limit_at,
@@ -25219,7 +25228,7 @@ async function repeatActivity(client, path2, input2, values, count, options) {
25219
25228
  },
25220
25229
  repetition: summary(stoppedReason)
25221
25230
  });
25222
- const attempt = async (lotInput, requestId) => {
25231
+ const finalResponseOfLot = async (lotInput, requestId) => {
25223
25232
  const started = await client.invoke(path2, lotInput);
25224
25233
  if (!started.ok) {
25225
25234
  return { ...started, repetition: summary("start_rejected") };
@@ -25260,7 +25269,7 @@ async function repeatActivity(client, path2, input2, values, count, options) {
25260
25269
  lastRequestId = requestId;
25261
25270
  const lotInput = requestId === void 0 ? input2 : { ...input2, request_id: requestId };
25262
25271
  try {
25263
- const final = await attempt(lotInput, requestId);
25272
+ const final = await finalResponseOfLot(lotInput, requestId);
25264
25273
  if (final) return final;
25265
25274
  } catch (error61) {
25266
25275
  if (error61 instanceof CliError) {
@@ -25289,7 +25298,7 @@ async function walkRoute(client, path2, input2, values, route, notify) {
25289
25298
  error: { message: "The travel stopped before reaching every location." },
25290
25299
  repetition: summary(stoppedReason)
25291
25300
  });
25292
- const step = async (to) => {
25301
+ const finalResponseOfStep = async (to) => {
25293
25302
  const started = await client.invoke(path2, { ...input2, to });
25294
25303
  if (!started.ok) {
25295
25304
  return { ...started, repetition: summary("start_rejected") };
@@ -25313,7 +25322,7 @@ async function walkRoute(client, path2, input2, values, route, notify) {
25313
25322
  };
25314
25323
  for (const to of route) {
25315
25324
  try {
25316
- const final = await step(to);
25325
+ const final = await finalResponseOfStep(to);
25317
25326
  if (final) return final;
25318
25327
  } catch (error61) {
25319
25328
  if (error61 instanceof CliError) {
@@ -25505,12 +25514,13 @@ var optionsSchema = external_exports.object({
25505
25514
  function isJsonObject(value) {
25506
25515
  return typeof value === "object" && value !== null && !Array.isArray(value);
25507
25516
  }
25517
+ var maxStdinLength = 128 * 1024;
25508
25518
  async function readStdin() {
25509
25519
  process.stdin.setEncoding("utf8");
25510
25520
  let text2 = "";
25511
25521
  for await (const chunk of process.stdin) {
25512
25522
  text2 += String(chunk);
25513
- if (text2.length > 128 * 1024) throw new CliError("INVALID_INPUT_FILE");
25523
+ if (text2.length > maxStdinLength) throw new CliError("INVALID_INPUT_FILE");
25514
25524
  }
25515
25525
  return text2;
25516
25526
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawsaga/cli",
3
- "version": "0.1.27",
3
+ "version": "0.1.29",
4
4
  "homepage": "https://clawsaga.net",
5
5
  "repository": {
6
6
  "type": "git",