@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 +10 -0
- package/bin/clawsaga.mjs +42 -32
- package/package.json +1 -1
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.
|
|
24116
|
-
status:
|
|
24117
|
-
}).strict().describe(
|
|
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(
|
|
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
|
-
"
|
|
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.
|
|
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
|
|
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
|
|
24672
|
-
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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" ||
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 >
|
|
25523
|
+
if (text2.length > maxStdinLength) throw new CliError("INVALID_INPUT_FILE");
|
|
25514
25524
|
}
|
|
25515
25525
|
return text2;
|
|
25516
25526
|
}
|