@dodo-planet/cli 0.1.4 → 0.1.6

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.
Files changed (2) hide show
  1. package/dist/index.js +692 -127
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -23,20 +23,20 @@ function getConfigDir() {
23
23
  function getConfigPath() {
24
24
  return join(getConfigDir(), "config.json");
25
25
  }
26
- async function ensureDir(path2) {
27
- if (!existsSync(path2)) {
28
- await mkdir(path2, { recursive: true, mode: 448 });
26
+ async function ensureDir(path3) {
27
+ if (!existsSync(path3)) {
28
+ await mkdir(path3, { recursive: true, mode: 448 });
29
29
  }
30
30
  }
31
31
  async function readConfig() {
32
32
  const envApiUrl = process.env.DODO_API_URL?.trim();
33
33
  const envLocale = process.env.DODO_LOCALE?.trim();
34
34
  const envOutput = process.env.DODO_OUTPUT?.trim();
35
- const path2 = getConfigPath();
35
+ const path3 = getConfigPath();
36
36
  let fileCfg = {};
37
- if (existsSync(path2)) {
37
+ if (existsSync(path3)) {
38
38
  try {
39
- fileCfg = JSON.parse(await readFile(path2, "utf8"));
39
+ fileCfg = JSON.parse(await readFile(path3, "utf8"));
40
40
  } catch {
41
41
  }
42
42
  }
@@ -89,10 +89,10 @@ function getCredentialsPath() {
89
89
  async function getToken() {
90
90
  const fromEnv = process.env.DODO_TOKEN;
91
91
  if (fromEnv && fromEnv.trim()) return fromEnv.trim();
92
- const path2 = getCredentialsPath();
93
- if (!existsSync2(path2)) return null;
92
+ const path3 = getCredentialsPath();
93
+ if (!existsSync2(path3)) return null;
94
94
  try {
95
- const stat = statSync(path2);
95
+ const stat = statSync(path3);
96
96
  const mode = stat.mode & 511;
97
97
  if (mode !== 384 && process.env.DODO_STRICT_PERMS === "1") {
98
98
  throw new Error(
@@ -102,7 +102,7 @@ async function getToken() {
102
102
  } catch {
103
103
  }
104
104
  try {
105
- const raw = await readFile2(path2, "utf8");
105
+ const raw = await readFile2(path3, "utf8");
106
106
  const parsed = JSON.parse(raw);
107
107
  return parsed.token || null;
108
108
  } catch {
@@ -119,14 +119,14 @@ async function saveToken(token, travelerId) {
119
119
  travelerId,
120
120
  savedAt: (/* @__PURE__ */ new Date()).toISOString()
121
121
  };
122
- const path2 = getCredentialsPath();
123
- await writeFile2(path2, JSON.stringify(payload, null, 2), { mode: 384 });
124
- await chmod(path2, 384);
122
+ const path3 = getCredentialsPath();
123
+ await writeFile2(path3, JSON.stringify(payload, null, 2), { mode: 384 });
124
+ await chmod(path3, 384);
125
125
  }
126
126
  async function clearToken() {
127
- const path2 = getCredentialsPath();
128
- if (existsSync2(path2)) {
129
- await unlink(path2);
127
+ const path3 = getCredentialsPath();
128
+ if (existsSync2(path3)) {
129
+ await unlink(path3);
130
130
  }
131
131
  }
132
132
  function isTokenFromEnv() {
@@ -165,8 +165,8 @@ var init_exit_codes = __esm({
165
165
  });
166
166
 
167
167
  // src/lib/api-client.ts
168
- function buildUrl(base, path2, query) {
169
- const url = new URL(path2, base);
168
+ function buildUrl(base, path3, query) {
169
+ const url = new URL(path3, base);
170
170
  if (query) {
171
171
  for (const [k, v] of Object.entries(query)) {
172
172
  if (v !== void 0 && v !== "") url.searchParams.set(k, v);
@@ -174,9 +174,9 @@ function buildUrl(base, path2, query) {
174
174
  }
175
175
  return url.toString();
176
176
  }
177
- async function apiRequest(path2, opts = {}) {
177
+ async function apiRequest(path3, opts = {}) {
178
178
  const cfg = await readConfig();
179
- const url = buildUrl(cfg.apiUrl, path2, opts.query);
179
+ const url = buildUrl(cfg.apiUrl, path3, opts.query);
180
180
  const headers = {
181
181
  accept: "application/json",
182
182
  ...opts.headers
@@ -381,10 +381,10 @@ function getStatePath() {
381
381
  return join3(getConfigDir(), "state.json");
382
382
  }
383
383
  async function readState() {
384
- const path2 = getStatePath();
385
- if (!existsSync3(path2)) return {};
384
+ const path3 = getStatePath();
385
+ if (!existsSync3(path3)) return {};
386
386
  try {
387
- const raw = await readFile3(path2, "utf8");
387
+ const raw = await readFile3(path3, "utf8");
388
388
  return JSON.parse(raw);
389
389
  } catch {
390
390
  return {};
@@ -473,7 +473,12 @@ var init_function_catalog_shared = __esm({
473
473
  { name: "get_friends", category: "friend", verb: "list", requiresTrip: false, isWrite: false, isDestructive: false },
474
474
  { name: "add_friend", category: "friend", verb: "add", requiresTrip: false, isWrite: true, isDestructive: false },
475
475
  { name: "get_invitations", category: "invite", verb: "list", requiresTrip: true, isWrite: false, isDestructive: false },
476
- { name: "invite_to_trip", category: "invite", verb: "create", requiresTrip: true, isWrite: true, isDestructive: false }
476
+ { name: "invite_to_trip", category: "invite", verb: "create", requiresTrip: true, isWrite: true, isDestructive: false },
477
+ // ── 여행 프로필 (4개, M2)
478
+ { name: "get_travel_profile", category: "profile", verb: "show", requiresTrip: false, isWrite: false, isDestructive: false },
479
+ { name: "update_travel_profile_section", category: "profile", verb: "update", requiresTrip: false, isWrite: true, isDestructive: false },
480
+ { name: "finalize_travel_profile", category: "profile", verb: "finalize", requiresTrip: false, isWrite: false, isDestructive: false, isBuilderOnly: true },
481
+ { name: "clear_travel_profile", category: "profile", verb: "clear", requiresTrip: false, isWrite: true, isDestructive: true }
477
482
  ];
478
483
  CATALOG_BY_NAME = new Map(
479
484
  FUNCTION_CATALOG.map((meta) => [meta.name, meta])
@@ -501,6 +506,14 @@ async function runFunctionCommand(opts) {
501
506
  if (ctx.mode === "json") emitJsonError("catalog_missing", msg);
502
507
  else printError(msg, ctx);
503
508
  process.exit(ExitCode.GeneralError);
509
+ return;
510
+ }
511
+ if (meta.isBuilderOnly) {
512
+ const msg = `${opts.function}\uC740(\uB294) \uBE4C\uB354 \uC804\uC6A9 \uD568\uC218\uC785\uB2C8\uB2E4. \`dodo profile chat\` \uC73C\uB85C \uC9C4\uD589\uD558\uC138\uC694.`;
513
+ if (ctx.mode === "json") emitJsonError("builder_only", msg);
514
+ else printError(msg, ctx);
515
+ process.exit(ExitCode.InvalidArgs);
516
+ return;
504
517
  }
505
518
  let tripId;
506
519
  if (!opts.noTripContext) {
@@ -576,7 +589,7 @@ var init_command_helpers = __esm({
576
589
  });
577
590
 
578
591
  // src/index.ts
579
- import { defineCommand as defineCommand16, runMain } from "citty";
592
+ import { defineCommand as defineCommand17, runMain } from "citty";
580
593
 
581
594
  // src/commands/auth.ts
582
595
  init_credentials();
@@ -1006,7 +1019,8 @@ var create = defineCommand3({
1006
1019
  name: args.name,
1007
1020
  start_date: args["start-date"],
1008
1021
  end_date: args["end-date"],
1009
- country: args.country,
1022
+ // 서버 router는 args.country_code를 읽음 (declarations.ts의 create_trip)
1023
+ country_code: args.country,
1010
1024
  cities: cities ? JSON.stringify(cities) : void 0,
1011
1025
  ...extraArgs
1012
1026
  },
@@ -1029,13 +1043,539 @@ var tripCommand = defineCommand3({
1029
1043
  subCommands: { list, current, switch: switchCmd, create }
1030
1044
  });
1031
1045
 
1046
+ // src/commands/profile.ts
1047
+ init_api_client();
1048
+ init_output();
1049
+ init_config();
1050
+ init_credentials();
1051
+ init_exit_codes();
1052
+ init_prompt();
1053
+ import { defineCommand as defineCommand4 } from "citty";
1054
+ import { mkdtemp, writeFile as writeFile4, readFile as readFile4, rm } from "fs/promises";
1055
+ import { tmpdir } from "os";
1056
+ import path2 from "path";
1057
+ import { spawn } from "child_process";
1058
+ function splitProfileBody(text3) {
1059
+ const re = /^[ \t]*-{3,}[ \t]*safety[ \t]*-{3,}[ \t]*$/im;
1060
+ const match = re.exec(text3);
1061
+ if (!match) return { summary: text3.trim(), safety_notes: "" };
1062
+ return {
1063
+ summary: text3.slice(0, match.index).trim(),
1064
+ safety_notes: text3.slice(match.index + match[0].length).trim()
1065
+ };
1066
+ }
1067
+ async function runEditorImpl(filepath) {
1068
+ const editor = process.env.EDITOR ?? process.env.VISUAL ?? "vi";
1069
+ await new Promise((resolve, reject) => {
1070
+ const child = spawn(editor, [filepath], { stdio: "inherit" });
1071
+ child.on(
1072
+ "exit",
1073
+ (code) => code === 0 ? resolve() : reject(new Error(`${editor} exited with code ${code}`))
1074
+ );
1075
+ child.on("error", reject);
1076
+ });
1077
+ }
1078
+ var _internal = {
1079
+ runEditor: runEditorImpl,
1080
+ /** 편집된 임시 파일을 다시 읽는다. utf8 디코딩까지 책임진다. */
1081
+ readEditedFile: async (filepath) => (await readFile4(filepath, "utf8")).toString(),
1082
+ /**
1083
+ * 임시 파일에 초기 본문을 쓴다.
1084
+ *
1085
+ * 테스트가 실패 시나리오(ENOSPC/EACCES)를 주입하기 위해 `_internal`을 거쳐 호출한다.
1086
+ * ESM에서는 `node:fs/promises`의 named export를 `vi.spyOn`으로 갈아끼울 수 없어
1087
+ * (module namespace not configurable) `_internal` namespace를 거치는 것이 유일한 안전 경로.
1088
+ */
1089
+ writeFile: async (filepath, contents) => {
1090
+ await writeFile4(filepath, contents, "utf8");
1091
+ },
1092
+ /** 임시 디렉토리를 통째로 제거. ESM spy 한계 때문에 `_internal`을 거친다. */
1093
+ rm: async (target) => {
1094
+ await rm(target, { recursive: true, force: true });
1095
+ }
1096
+ };
1097
+ var show = defineCommand4({
1098
+ meta: { name: "show", description: "\uD65C\uC131 \uC5EC\uD589 \uD504\uB85C\uD544 + \uD1A0\uAE00 + \uC548\uC804 \uB178\uD2B8 \uCD9C\uB825." },
1099
+ args: {
1100
+ locale: { type: "string", description: "ko | en | es | fr" },
1101
+ output: { type: "string", description: "pretty | json" }
1102
+ },
1103
+ async run({ args }) {
1104
+ const ctx = await resolveOutputContext(args.output);
1105
+ const locale = await resolveLocale(args.locale);
1106
+ try {
1107
+ const result = await executeFunction(
1108
+ "get_travel_profile",
1109
+ {},
1110
+ { locale }
1111
+ );
1112
+ if (ctx.mode === "json") {
1113
+ emitJsonOk({ profile: result.profile, enabled: result.enabled });
1114
+ return;
1115
+ }
1116
+ const c = colorize(ctx);
1117
+ if (!result.profile) {
1118
+ printLine(
1119
+ c.dim("\uC5EC\uD589 \uD504\uB85C\uD544\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `dodo profile chat` \uC73C\uB85C \uBE4C\uB354\uB97C \uC2DC\uC791\uD558\uC138\uC694.")
1120
+ );
1121
+ return;
1122
+ }
1123
+ const toggleLabel = result.enabled ? c.green("(ON)") : c.dim("(OFF)");
1124
+ printLine(c.bold(`\uC5EC\uD589 \uD504\uB85C\uD544 ${toggleLabel}`));
1125
+ printLine("");
1126
+ printLine(c.bold("\uC694\uC57D:"));
1127
+ printLine(result.profile.summary);
1128
+ if (result.profile.safety_notes && result.profile.safety_notes.trim()) {
1129
+ printLine("");
1130
+ printLine(c.bold("\uC548\uC804 \uB178\uD2B8:"));
1131
+ printLine(result.profile.safety_notes);
1132
+ }
1133
+ printLine("");
1134
+ printLine(c.dim(`\uB9C8\uC9C0\uB9C9 \uC5C5\uB370\uC774\uD2B8: ${result.profile.updated_at}`));
1135
+ } catch (err) {
1136
+ if (err instanceof ApiError) {
1137
+ if (ctx.mode === "json") emitJsonError("show_failed", err.message);
1138
+ else printError(err.message, ctx);
1139
+ process.exit(err.exitCode);
1140
+ }
1141
+ throw err;
1142
+ }
1143
+ }
1144
+ });
1145
+ var toggle = defineCommand4({
1146
+ meta: { name: "toggle", description: "\uC5EC\uD589 \uD504\uB85C\uD544 \uD65C\uC131/\uBE44\uD65C\uC131 \uC804\uD658." },
1147
+ args: {
1148
+ state: {
1149
+ type: "positional",
1150
+ description: "on | off",
1151
+ // citty가 누락 시 자동 에러를 내지 않도록 false. 우리가 직접 검증한다.
1152
+ required: false
1153
+ },
1154
+ output: { type: "string", description: "pretty | json" }
1155
+ },
1156
+ async run({ args }) {
1157
+ const ctx = await resolveOutputContext(args.output);
1158
+ if (args.state !== "on" && args.state !== "off") {
1159
+ const msg = "Usage: dodo profile toggle <on|off>";
1160
+ if (ctx.mode === "json") emitJsonError("invalid_args", msg);
1161
+ else printError(msg, ctx);
1162
+ process.exit(ExitCode.InvalidArgs);
1163
+ return;
1164
+ }
1165
+ const enabled = args.state === "on";
1166
+ try {
1167
+ const result = await apiRequest(
1168
+ "/api/travelers/me/active-profile",
1169
+ { method: "POST", body: { enabled } }
1170
+ );
1171
+ if (ctx.mode === "json") emitJsonOk(result);
1172
+ else
1173
+ printLine(
1174
+ colorize(ctx).green(`\u2713 \uD504\uB85C\uD544 ${enabled ? "\uD65C\uC131\uD654" : "\uBE44\uD65C\uC131\uD654"}\uB428`)
1175
+ );
1176
+ } catch (err) {
1177
+ if (err instanceof ApiError) {
1178
+ if (ctx.mode === "json") emitJsonError("toggle_failed", err.message);
1179
+ else printError(err.message, ctx);
1180
+ process.exit(err.exitCode);
1181
+ }
1182
+ throw err;
1183
+ }
1184
+ }
1185
+ });
1186
+ var edit = defineCommand4({
1187
+ meta: { name: "edit", description: "$EDITOR\uB85C \uD504\uB85C\uD544\uC744 \uC9C1\uC811 \uD3B8\uC9D1 (\uB099\uAD00\uC801 \uB3D9\uC2DC\uC131)." },
1188
+ args: {
1189
+ locale: { type: "string", description: "ko | en | es | fr" },
1190
+ output: { type: "string", description: "pretty | json" }
1191
+ },
1192
+ async run({ args }) {
1193
+ const ctx = await resolveOutputContext(args.output);
1194
+ const locale = await resolveLocale(args.locale);
1195
+ try {
1196
+ const current2 = await executeFunction(
1197
+ "get_travel_profile",
1198
+ {},
1199
+ { locale }
1200
+ );
1201
+ if (!current2.profile) {
1202
+ const msg = "\uD504\uB85C\uD544\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. `dodo profile chat`\uC73C\uB85C \uBE4C\uB354\uB97C \uC2DC\uC791\uD558\uC138\uC694.";
1203
+ if (ctx.mode === "json") emitJsonError("no_profile", msg);
1204
+ else printError(msg, ctx);
1205
+ process.exit(ExitCode.GeneralError);
1206
+ return;
1207
+ }
1208
+ const tempDir = await mkdtemp(path2.join(tmpdir(), "dodo-profile-"));
1209
+ const filepath = path2.join(tempDir, "profile.md");
1210
+ const initial = `${current2.profile.summary}
1211
+
1212
+ --- safety ---
1213
+ ${current2.profile.safety_notes ?? ""}
1214
+ `;
1215
+ try {
1216
+ await _internal.writeFile(filepath, initial);
1217
+ await _internal.runEditor(filepath);
1218
+ const next = await _internal.readEditedFile(filepath);
1219
+ if (next.trim() === initial.trim()) {
1220
+ if (ctx.mode === "json") emitJsonOk({ changed: false });
1221
+ else printLine(colorize(ctx).dim("\uBCC0\uACBD \uC5C6\uC74C."));
1222
+ return;
1223
+ }
1224
+ const { summary, safety_notes } = splitProfileBody(next);
1225
+ const result = await apiRequest(
1226
+ `/api/travel-profiles/${current2.profile.id}`,
1227
+ {
1228
+ method: "PATCH",
1229
+ body: {
1230
+ summary,
1231
+ safety_notes,
1232
+ expected_updated_at: current2.profile.updated_at
1233
+ }
1234
+ }
1235
+ );
1236
+ if (ctx.mode === "json") emitJsonOk(result);
1237
+ else printLine(colorize(ctx).green("\u2713 \uC800\uC7A5\uB428."));
1238
+ } finally {
1239
+ await _internal.rm(tempDir);
1240
+ }
1241
+ } catch (err) {
1242
+ if (err instanceof ApiError) {
1243
+ const isConflict = isProfileConflictError(err);
1244
+ const msg = isConflict ? "\uD504\uB85C\uD544\uC774 \uB2E4\uB978 \uACF3\uC5D0\uC11C \uC218\uC815\uB418\uC5B4 \uBCC0\uACBD\uC744 \uC800\uC7A5\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. `dodo profile show` \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694." : err.message;
1245
+ if (ctx.mode === "json") emitJsonError(isConflict ? "conflict" : "edit_failed", msg);
1246
+ else printError(msg, ctx);
1247
+ process.exit(err.exitCode);
1248
+ return;
1249
+ }
1250
+ if (err instanceof Error) {
1251
+ if (ctx.mode === "json") emitJsonError("editor_error", err.message);
1252
+ else printError(`\uC624\uB958: ${err.message}`, ctx);
1253
+ process.exit(ExitCode.GeneralError);
1254
+ return;
1255
+ }
1256
+ throw err;
1257
+ }
1258
+ }
1259
+ });
1260
+ var clear = defineCommand4({
1261
+ meta: { name: "clear", description: "\uC5EC\uD589 \uD504\uB85C\uD544\uC744 \uC601\uAD6C \uC0AD\uC81C\uD569\uB2C8\uB2E4 (--confirm \uD544\uC218)." },
1262
+ args: {
1263
+ confirm: { type: "boolean", description: "\uBA85\uC2DC \uB3D9\uC758 \u2014 \uC774 \uD50C\uB798\uADF8\uAC00 \uC5C6\uC73C\uBA74 \uAC70\uC808." },
1264
+ locale: { type: "string", description: "ko | en | es | fr" },
1265
+ output: { type: "string", description: "pretty | json" }
1266
+ },
1267
+ async run({ args }) {
1268
+ const ctx = await resolveOutputContext(args.output);
1269
+ if (!args.confirm) {
1270
+ const msg = "\uC774 \uC791\uC5C5\uC740 \uC601\uAD6C \uC0AD\uC81C\uC785\uB2C8\uB2E4. \uC9C4\uD589\uD558\uB824\uBA74 --confirm \uD50C\uB798\uADF8\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.";
1271
+ if (ctx.mode === "json") emitJsonError("confirm_required", msg);
1272
+ else printError(msg, ctx);
1273
+ process.exit(ExitCode.InvalidArgs);
1274
+ return;
1275
+ }
1276
+ const locale = await resolveLocale(args.locale);
1277
+ try {
1278
+ const current2 = await executeFunction(
1279
+ "get_travel_profile",
1280
+ {},
1281
+ { locale }
1282
+ );
1283
+ if (!current2.profile) {
1284
+ if (ctx.mode === "json") emitJsonOk({ ok: true, message: "no profile" });
1285
+ else printLine(colorize(ctx).dim("\uD504\uB85C\uD544\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."));
1286
+ return;
1287
+ }
1288
+ await apiRequest(`/api/travel-profiles/${current2.profile.id}`, {
1289
+ method: "DELETE"
1290
+ });
1291
+ if (ctx.mode === "json") emitJsonOk({ ok: true });
1292
+ else printLine(colorize(ctx).green("\u2713 \uC0AD\uC81C\uB428."));
1293
+ } catch (err) {
1294
+ if (err instanceof ApiError) {
1295
+ if (ctx.mode === "json") emitJsonError("clear_failed", err.message);
1296
+ else printError(err.message, ctx);
1297
+ process.exit(err.exitCode);
1298
+ return;
1299
+ }
1300
+ throw err;
1301
+ }
1302
+ }
1303
+ });
1304
+ function renderFinalizeCard(draft, opts) {
1305
+ const c = colorize({ mode: "pretty", color: opts.color });
1306
+ const lines = [];
1307
+ lines.push(c.bold("\u2500\u2500\u2500 \uC5EC\uD589 \uD504\uB85C\uD544 \uC81C\uC548 \u2500\u2500\u2500"));
1308
+ lines.push(c.bold("\uC694\uC57D:"));
1309
+ lines.push(draft.summary);
1310
+ if (draft.safety_notes && draft.safety_notes.trim()) {
1311
+ lines.push("");
1312
+ lines.push(c.bold("\uC548\uC804 \uB178\uD2B8:"));
1313
+ lines.push(draft.safety_notes);
1314
+ }
1315
+ lines.push(c.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
1316
+ return lines;
1317
+ }
1318
+ function confirmFinalizeAction(input) {
1319
+ const v = input.trim().toLowerCase();
1320
+ if (v === "y" || v === "yes") return "save";
1321
+ if (v === "e" || v === "edit") return "edit";
1322
+ return "cancel";
1323
+ }
1324
+ async function* parseSseStream(response) {
1325
+ if (!response.body) return;
1326
+ const reader = response.body.getReader();
1327
+ const decoder = new TextDecoder();
1328
+ let buffer = "";
1329
+ while (true) {
1330
+ const { value, done } = await reader.read();
1331
+ if (done) break;
1332
+ buffer += decoder.decode(value, { stream: true });
1333
+ let separator;
1334
+ while ((separator = buffer.indexOf("\n\n")) !== -1) {
1335
+ const block = buffer.slice(0, separator);
1336
+ buffer = buffer.slice(separator + 2);
1337
+ let event = "message";
1338
+ let dataStr = "";
1339
+ for (const line of block.split("\n")) {
1340
+ if (line.startsWith("event:")) event = line.slice(6).trim();
1341
+ else if (line.startsWith("data:")) dataStr += line.slice(5).trim();
1342
+ }
1343
+ if (!event) continue;
1344
+ let data = {};
1345
+ try {
1346
+ data = dataStr ? JSON.parse(dataStr) : {};
1347
+ } catch {
1348
+ }
1349
+ yield { event, data };
1350
+ }
1351
+ }
1352
+ }
1353
+ async function streamProfileBuilderTurn(history, opts) {
1354
+ const config = await readConfig();
1355
+ const token = await getToken();
1356
+ if (!token) {
1357
+ throw new ApiError(
1358
+ "\uC778\uC99D\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. `dodo auth login` \uC73C\uB85C \uD1A0\uD070\uC744 \uB4F1\uB85D\uD558\uC138\uC694.",
1359
+ 401,
1360
+ ExitCode.Unauthorized
1361
+ );
1362
+ }
1363
+ const url = new URL("/api/cli/chat", config.apiUrl).toString();
1364
+ let response;
1365
+ try {
1366
+ response = await fetch(url, {
1367
+ method: "POST",
1368
+ headers: {
1369
+ authorization: `Bearer ${token}`,
1370
+ "content-type": "application/json",
1371
+ accept: "text/event-stream"
1372
+ },
1373
+ body: JSON.stringify({
1374
+ messages: history,
1375
+ locale: opts.locale,
1376
+ mode: "profile_builder"
1377
+ })
1378
+ });
1379
+ } catch (err) {
1380
+ const msg = err instanceof Error ? err.message : "network error";
1381
+ throw new ApiError(`\uC11C\uBC84\uC5D0 \uC5F0\uACB0\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${msg}`, 0, ExitCode.Network);
1382
+ }
1383
+ if (!response.ok) {
1384
+ const body = await response.text().catch(() => "");
1385
+ let parsed = {};
1386
+ try {
1387
+ parsed = JSON.parse(body);
1388
+ } catch {
1389
+ }
1390
+ throw new ApiError(
1391
+ parsed.error ?? `HTTP ${response.status}`,
1392
+ response.status,
1393
+ exitCodeForStatus(response.status)
1394
+ );
1395
+ }
1396
+ let reply = "";
1397
+ let finalizeDraft = null;
1398
+ for await (const evt of parseSseStream(response)) {
1399
+ if (evt.event === "text") {
1400
+ const t = evt.data.text;
1401
+ if (t) {
1402
+ process.stdout.write(t);
1403
+ reply += t;
1404
+ }
1405
+ } else if (evt.event === "tool" && evt.data.name === "finalize_travel_profile") {
1406
+ const result = evt.data.result;
1407
+ if (result?.ok && result.draft) finalizeDraft = result.draft;
1408
+ } else if (evt.event === "done") {
1409
+ process.stdout.write("\n");
1410
+ break;
1411
+ } else if (evt.event === "error") {
1412
+ const message = String(evt.data.message ?? "stream error");
1413
+ throw new ApiError(message, 0, ExitCode.GeneralError);
1414
+ }
1415
+ }
1416
+ return { reply, finalizeDraft };
1417
+ }
1418
+ function isProfileConflictError(err) {
1419
+ if (!(err instanceof ApiError)) return false;
1420
+ if (err.status === 409) return true;
1421
+ return /Conflict|409|expected_updated_at/i.test(err.message);
1422
+ }
1423
+ async function saveProfileDraft(draft, snapshot) {
1424
+ if (snapshot.profile) {
1425
+ await apiRequest(`/api/travel-profiles/${snapshot.profile.id}`, {
1426
+ method: "PATCH",
1427
+ body: {
1428
+ summary: draft.summary,
1429
+ safety_notes: draft.safety_notes,
1430
+ expected_updated_at: snapshot.profile.updated_at
1431
+ }
1432
+ });
1433
+ } else {
1434
+ await apiRequest("/api/travel-profiles", {
1435
+ method: "POST",
1436
+ body: {
1437
+ summary: draft.summary,
1438
+ safety_notes: draft.safety_notes
1439
+ }
1440
+ });
1441
+ }
1442
+ }
1443
+ async function editDraftInEditor(draft) {
1444
+ const tempDir = await mkdtemp(path2.join(tmpdir(), "dodo-profile-draft-"));
1445
+ const filepath = path2.join(tempDir, "draft.md");
1446
+ try {
1447
+ await _internal.writeFile(
1448
+ filepath,
1449
+ `${draft.summary}
1450
+
1451
+ --- safety ---
1452
+ ${draft.safety_notes}
1453
+ `
1454
+ );
1455
+ await _internal.runEditor(filepath);
1456
+ return splitProfileBody(await _internal.readEditedFile(filepath));
1457
+ } finally {
1458
+ await _internal.rm(tempDir);
1459
+ }
1460
+ }
1461
+ var chat = defineCommand4({
1462
+ meta: { name: "chat", description: "\uC5EC\uD589 \uD504\uB85C\uD544 \uBE4C\uB354 \u2014 \uD14D\uC2A4\uD2B8 REPL." },
1463
+ args: {
1464
+ locale: { type: "string", description: "ko | en | es | fr" },
1465
+ output: { type: "string", description: "pretty | json" }
1466
+ },
1467
+ async run({ args }) {
1468
+ const ctx = await resolveOutputContext(args.output);
1469
+ if (ctx.mode === "json") {
1470
+ emitJsonError("non_json", "REPL \uBA85\uB839\uC740 pretty \uBAA8\uB4DC\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4.");
1471
+ process.exit(ExitCode.InvalidArgs);
1472
+ return;
1473
+ }
1474
+ if (!isInteractive()) {
1475
+ printError("\uD130\uBBF8\uB110 \uBE44\uB300\uD654 \uD658\uACBD\uC5D0\uC11C\uB294 REPL\uC744 \uC2DC\uC791\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", ctx);
1476
+ process.exit(ExitCode.InvalidArgs);
1477
+ return;
1478
+ }
1479
+ const locale = await resolveLocale(args.locale);
1480
+ const c = colorize(ctx);
1481
+ printLine(c.dim("\uD504\uB85C\uD544 \uBE4C\uB354\uB97C \uC2DC\uC791\uD569\uB2C8\uB2E4. \uC885\uB8CC\uD558\uB824\uBA74 :q"));
1482
+ let initialSnapshot;
1483
+ try {
1484
+ initialSnapshot = await executeFunction(
1485
+ "get_travel_profile",
1486
+ {},
1487
+ { locale }
1488
+ );
1489
+ } catch (err) {
1490
+ if (err instanceof ApiError) {
1491
+ printError(err.message, ctx);
1492
+ process.exit(err.exitCode);
1493
+ return;
1494
+ }
1495
+ throw err;
1496
+ }
1497
+ const history = [];
1498
+ while (true) {
1499
+ const userInput = await text2("you> ");
1500
+ if (userInput === null) break;
1501
+ const trimmed = userInput.trim();
1502
+ if (trimmed === ":q") break;
1503
+ if (!trimmed) continue;
1504
+ history.push({ role: "user", text: trimmed });
1505
+ try {
1506
+ const { reply, finalizeDraft } = await streamProfileBuilderTurn(history, { locale });
1507
+ history.push({ role: "model", text: reply });
1508
+ if (finalizeDraft) {
1509
+ for (const line of renderFinalizeCard(finalizeDraft, { color: ctx.color })) {
1510
+ printLine(line);
1511
+ }
1512
+ const choice = await text2("[y]es / [N]o / [e]dit > ");
1513
+ const action = confirmFinalizeAction(choice ?? "");
1514
+ if (action === "save") {
1515
+ try {
1516
+ await saveProfileDraft(finalizeDraft, initialSnapshot);
1517
+ printLine(c.green("\u2713 \uC800\uC7A5\uB428."));
1518
+ } catch (saveErr) {
1519
+ if (isProfileConflictError(saveErr)) {
1520
+ printError(
1521
+ "\uD504\uB85C\uD544\uC774 \uB2E4\uB978 \uACF3\uC5D0\uC11C \uC218\uC815\uB418\uC5B4 \uBCC0\uACBD\uC744 \uC800\uC7A5\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. `dodo profile show` \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.",
1522
+ ctx
1523
+ );
1524
+ process.exit(ExitCode.GeneralError);
1525
+ return;
1526
+ }
1527
+ throw saveErr;
1528
+ }
1529
+ break;
1530
+ } else if (action === "edit") {
1531
+ const edited = await editDraftInEditor(finalizeDraft);
1532
+ try {
1533
+ await saveProfileDraft(edited, initialSnapshot);
1534
+ printLine(c.green("\u2713 \uC800\uC7A5\uB428 (\uC218\uC815\uBCF8)."));
1535
+ } catch (saveErr) {
1536
+ if (isProfileConflictError(saveErr)) {
1537
+ printError(
1538
+ "\uD504\uB85C\uD544\uC774 \uB2E4\uB978 \uACF3\uC5D0\uC11C \uC218\uC815\uB418\uC5B4 \uBCC0\uACBD\uC744 \uC800\uC7A5\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. `dodo profile show` \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.",
1539
+ ctx
1540
+ );
1541
+ process.exit(ExitCode.GeneralError);
1542
+ return;
1543
+ }
1544
+ throw saveErr;
1545
+ }
1546
+ break;
1547
+ } else {
1548
+ printLine(c.dim("\uCDE8\uC18C. \uB300\uD654\uB97C \uACC4\uC18D\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
1549
+ }
1550
+ }
1551
+ } catch (err) {
1552
+ if (err instanceof ApiError) {
1553
+ printError(err.message, ctx);
1554
+ process.exit(err.exitCode);
1555
+ return;
1556
+ }
1557
+ if (err instanceof Error) {
1558
+ printError(`\uC624\uB958: ${err.message}`, ctx);
1559
+ process.exit(ExitCode.GeneralError);
1560
+ return;
1561
+ }
1562
+ throw err;
1563
+ }
1564
+ }
1565
+ }
1566
+ });
1567
+ var profileCommand = defineCommand4({
1568
+ meta: { name: "profile", description: "\uC5EC\uD589 \uD504\uB85C\uD544 (show/toggle/edit/clear/chat)." },
1569
+ subCommands: { show, toggle, edit, clear, chat }
1570
+ });
1571
+
1032
1572
  // src/commands/expense.ts
1033
1573
  init_api_client();
1034
1574
  init_exit_codes();
1035
1575
  init_output();
1036
1576
  init_config();
1037
1577
  init_state();
1038
- import { defineCommand as defineCommand4 } from "citty";
1578
+ import { defineCommand as defineCommand5 } from "citty";
1039
1579
  async function resolveTripId(flagTrip, ctx) {
1040
1580
  const direct = await getActiveTripId(flagTrip);
1041
1581
  if (direct) return direct;
@@ -1054,7 +1594,7 @@ async function resolveTripId(flagTrip, ctx) {
1054
1594
  }
1055
1595
  return state.activeTrip.id;
1056
1596
  }
1057
- var list2 = defineCommand4({
1597
+ var list2 = defineCommand5({
1058
1598
  meta: { name: "list", description: "\uD65C\uC131 trip\uC758 \uACBD\uBE44 \uBAA9\uB85D\uC744 \uD45C\uC2DC\uD569\uB2C8\uB2E4." },
1059
1599
  args: {
1060
1600
  limit: { type: "string", description: "\uCD5C\uB300 \uD45C\uC2DC \uAC74\uC218 (\uAE30\uBCF8 20)" },
@@ -1105,7 +1645,7 @@ var list2 = defineCommand4({
1105
1645
  }
1106
1646
  }
1107
1647
  });
1108
- var add = defineCommand4({
1648
+ var add = defineCommand5({
1109
1649
  meta: { name: "add", description: "\uACBD\uBE44\uB97C \uCD94\uAC00\uD569\uB2C8\uB2E4." },
1110
1650
  args: {
1111
1651
  amount: { type: "string", description: "\uAE08\uC561 (\uC22B\uC790)", required: true },
@@ -1143,7 +1683,8 @@ var add = defineCommand4({
1143
1683
  amount: String(amount),
1144
1684
  currency: args.currency,
1145
1685
  description: args.description,
1146
- type,
1686
+ // 서버 router는 args.expense_type을 읽음 (router.ts:125)
1687
+ expense_type: type,
1147
1688
  category: args.category,
1148
1689
  date: args.date
1149
1690
  },
@@ -1161,7 +1702,7 @@ var add = defineCommand4({
1161
1702
  }
1162
1703
  }
1163
1704
  });
1164
- var update = defineCommand4({
1705
+ var update = defineCommand5({
1165
1706
  meta: { name: "update", description: "\uACBD\uBE44\uB97C \uC218\uC815\uD569\uB2C8\uB2E4." },
1166
1707
  args: {
1167
1708
  id: { type: "positional", description: "\uACBD\uBE44 ID", required: true },
@@ -1183,11 +1724,13 @@ var update = defineCommand4({
1183
1724
  const result = await executeFunction(
1184
1725
  "update_expense",
1185
1726
  {
1186
- id: args.id,
1727
+ // 서버 router는 args.expense_id를 읽음 (router.ts:197)
1728
+ expense_id: args.id,
1187
1729
  amount: args.amount,
1188
1730
  currency: args.currency,
1189
1731
  description: args.description,
1190
- type: args.type,
1732
+ // 서버 router는 args.expense_type을 읽지 않음 — update는 type 변경 미지원
1733
+ // (router.ts:191-208에 expense_type 호출 없음). 본 인자는 의도적으로 미전송.
1191
1734
  category: args.category,
1192
1735
  date: args.date
1193
1736
  },
@@ -1205,7 +1748,7 @@ var update = defineCommand4({
1205
1748
  }
1206
1749
  }
1207
1750
  });
1208
- var del = defineCommand4({
1751
+ var del = defineCommand5({
1209
1752
  meta: { name: "delete", description: "\uACBD\uBE44\uB97C \uC0AD\uC81C\uD569\uB2C8\uB2E4 (--yes \uC5C6\uC73C\uBA74 confirm)." },
1210
1753
  args: {
1211
1754
  id: { type: "positional", description: "\uACBD\uBE44 ID", required: true },
@@ -1218,7 +1761,8 @@ var del = defineCommand4({
1218
1761
  const { runFunctionCommand: runFunctionCommand2 } = await Promise.resolve().then(() => (init_command_helpers(), command_helpers_exports));
1219
1762
  await runFunctionCommand2({
1220
1763
  function: "delete_expense",
1221
- args: { id: args.id },
1764
+ // 서버 router는 args.expense_id를 읽음 (router.ts:210)
1765
+ args: { expense_id: args.id },
1222
1766
  outputFlag: args.output,
1223
1767
  localeFlag: args.locale,
1224
1768
  tripFlag: args.trip,
@@ -1227,7 +1771,7 @@ var del = defineCommand4({
1227
1771
  });
1228
1772
  }
1229
1773
  });
1230
- var expenseCommand = defineCommand4({
1774
+ var expenseCommand = defineCommand5({
1231
1775
  meta: { name: "expense", description: "\uACBD\uBE44 \uAD00\uB9AC (list/add/update/delete)." },
1232
1776
  subCommands: { list: list2, add, update, delete: del }
1233
1777
  });
@@ -1235,8 +1779,8 @@ var expenseCommand = defineCommand4({
1235
1779
  // src/commands/booking.ts
1236
1780
  init_command_helpers();
1237
1781
  init_output();
1238
- import { defineCommand as defineCommand5 } from "citty";
1239
- var list3 = defineCommand5({
1782
+ import { defineCommand as defineCommand6 } from "citty";
1783
+ var list3 = defineCommand6({
1240
1784
  meta: { name: "list", description: "\uD65C\uC131 trip\uC758 \uC608\uC57D \uBAA9\uB85D." },
1241
1785
  args: {
1242
1786
  type: { type: "string", description: "flight|train|accommodation|activity|car_rental|transfer" },
@@ -1273,7 +1817,7 @@ var list3 = defineCommand5({
1273
1817
  });
1274
1818
  }
1275
1819
  });
1276
- var add2 = defineCommand5({
1820
+ var add2 = defineCommand6({
1277
1821
  meta: { name: "add", description: "\uC608\uC57D \uCD94\uAC00." },
1278
1822
  args: {
1279
1823
  type: { type: "string", required: true, description: "flight|train|accommodation|activity|car_rental|transfer" },
@@ -1301,15 +1845,19 @@ var add2 = defineCommand5({
1301
1845
  }
1302
1846
  await runFunctionCommand({
1303
1847
  function: "add_booking",
1848
+ // 서버 router(router.ts:129-178)는 다음 args 키를 읽는다:
1849
+ // args.type, args.title, args.date, args.notes, args.booking_scope,
1850
+ // + 다양한 detail 키 (flight_number, check_in, hotel_name 등)
1851
+ // 다음 CLI 플래그는 서버 함수 시그니처에 대응 인자가 없어 무시된다:
1852
+ // --vendor, --amount, --currency, --end-date
1853
+ // 따라서 dispatched args에서 제외해 false-pretend("성공"인데 데이터 누락)을 막는다.
1854
+ // --start-date는 서버의 단일 `date` 필드로 매핑 (시작일이 booking_date의 의미와 부합).
1855
+ // 추후 서버가 vendor/amount/currency/end_date를 지원하게 되면 args 추가.
1304
1856
  args: {
1305
1857
  type: args.type,
1306
1858
  title: args.title,
1307
- scope: args.scope ?? "shared",
1308
- start_date: args["start-date"],
1309
- end_date: args["end-date"],
1310
- vendor: args.vendor,
1311
- amount: args.amount,
1312
- currency: args.currency,
1859
+ booking_scope: args.scope ?? "shared",
1860
+ date: args["start-date"],
1313
1861
  ...extraArgs
1314
1862
  },
1315
1863
  outputFlag: args.output,
@@ -1319,7 +1867,7 @@ var add2 = defineCommand5({
1319
1867
  });
1320
1868
  }
1321
1869
  });
1322
- var update2 = defineCommand5({
1870
+ var update2 = defineCommand6({
1323
1871
  meta: { name: "update", description: "\uC608\uC57D \uC218\uC815." },
1324
1872
  args: {
1325
1873
  id: { type: "positional", required: true, description: "\uC608\uC57D ID" },
@@ -1346,14 +1894,13 @@ var update2 = defineCommand5({
1346
1894
  }
1347
1895
  await runFunctionCommand({
1348
1896
  function: "update_booking",
1897
+ // 서버 router(router.ts:211-258)는 args.booking_id, args.title, args.date,
1898
+ // args.notes 외 detailsInput용 detail 키들을 읽는다. add_booking과 동일 이유로
1899
+ // --vendor/--amount/--currency/--end-date는 미지원 — dispatched args 제외.
1349
1900
  args: {
1350
- id: args.id,
1901
+ booking_id: args.id,
1351
1902
  title: args.title,
1352
- start_date: args["start-date"],
1353
- end_date: args["end-date"],
1354
- vendor: args.vendor,
1355
- amount: args.amount,
1356
- currency: args.currency,
1903
+ date: args["start-date"],
1357
1904
  ...extraArgs
1358
1905
  },
1359
1906
  outputFlag: args.output,
@@ -1363,7 +1910,7 @@ var update2 = defineCommand5({
1363
1910
  });
1364
1911
  }
1365
1912
  });
1366
- var del2 = defineCommand5({
1913
+ var del2 = defineCommand6({
1367
1914
  meta: { name: "delete", description: "\uC608\uC57D \uC0AD\uC81C." },
1368
1915
  args: {
1369
1916
  id: { type: "positional", required: true, description: "\uC608\uC57D ID" },
@@ -1375,7 +1922,8 @@ var del2 = defineCommand5({
1375
1922
  async run({ args }) {
1376
1923
  await runFunctionCommand({
1377
1924
  function: "delete_booking",
1378
- args: { id: args.id },
1925
+ // 서버 router는 args.booking_id를 읽음 (declarations.ts의 delete_booking)
1926
+ args: { booking_id: args.id },
1379
1927
  outputFlag: args.output,
1380
1928
  localeFlag: args.locale,
1381
1929
  tripFlag: args.trip,
@@ -1384,7 +1932,7 @@ var del2 = defineCommand5({
1384
1932
  });
1385
1933
  }
1386
1934
  });
1387
- var bookingCommand = defineCommand5({
1935
+ var bookingCommand = defineCommand6({
1388
1936
  meta: { name: "booking", description: "\uC608\uC57D \uAD00\uB9AC." },
1389
1937
  subCommands: { list: list3, add: add2, update: update2, delete: del2 }
1390
1938
  });
@@ -1392,8 +1940,8 @@ var bookingCommand = defineCommand5({
1392
1940
  // src/commands/itinerary.ts
1393
1941
  init_command_helpers();
1394
1942
  init_output();
1395
- import { defineCommand as defineCommand6 } from "citty";
1396
- var show = defineCommand6({
1943
+ import { defineCommand as defineCommand7 } from "citty";
1944
+ var show2 = defineCommand7({
1397
1945
  meta: { name: "show", description: "\uC77C\uC815 \uD45C\uC2DC." },
1398
1946
  args: {
1399
1947
  date: { type: "string", description: "YYYY-MM-DD (\uD2B9\uC815 \uB0A0\uB9CC)" },
@@ -1422,7 +1970,7 @@ var show = defineCommand6({
1422
1970
  });
1423
1971
  }
1424
1972
  });
1425
- var add3 = defineCommand6({
1973
+ var add3 = defineCommand7({
1426
1974
  meta: { name: "add", description: "\uC77C\uC815\uC5D0 \uC7A5\uC18C \uCD94\uAC00." },
1427
1975
  args: {
1428
1976
  date: { type: "string", required: true, description: "YYYY-MM-DD" },
@@ -1444,7 +1992,7 @@ var add3 = defineCommand6({
1444
1992
  });
1445
1993
  }
1446
1994
  });
1447
- var remove = defineCommand6({
1995
+ var remove = defineCommand7({
1448
1996
  meta: { name: "remove", description: "\uC77C\uC815\uC5D0\uC11C \uC7A5\uC18C \uC0AD\uC81C." },
1449
1997
  args: {
1450
1998
  date: { type: "string", required: true, description: "YYYY-MM-DD" },
@@ -1466,7 +2014,7 @@ var remove = defineCommand6({
1466
2014
  });
1467
2015
  }
1468
2016
  });
1469
- var reorder = defineCommand6({
2017
+ var reorder = defineCommand7({
1470
2018
  meta: { name: "reorder", description: "\uC77C\uC815 \uC21C\uC11C \uBCC0\uACBD." },
1471
2019
  args: {
1472
2020
  date: { type: "string", required: true, description: "YYYY-MM-DD" },
@@ -1487,16 +2035,16 @@ var reorder = defineCommand6({
1487
2035
  });
1488
2036
  }
1489
2037
  });
1490
- var itineraryCommand = defineCommand6({
2038
+ var itineraryCommand = defineCommand7({
1491
2039
  meta: { name: "itinerary", description: "\uC77C\uC815 \uAD00\uB9AC." },
1492
- subCommands: { show, add: add3, remove, reorder }
2040
+ subCommands: { show: show2, add: add3, remove, reorder }
1493
2041
  });
1494
2042
 
1495
2043
  // src/commands/search.ts
1496
2044
  init_command_helpers();
1497
2045
  init_output();
1498
- import { defineCommand as defineCommand7 } from "citty";
1499
- var flightSearch = defineCommand7({
2046
+ import { defineCommand as defineCommand8 } from "citty";
2047
+ var flightSearch = defineCommand8({
1500
2048
  meta: { name: "search", description: "\uD56D\uACF5\uD3B8 \uAC80\uC0C9 (Amadeus)." },
1501
2049
  args: {
1502
2050
  from: { type: "string", required: true, description: "\uCD9C\uBC1C IATA (\uC608: ICN)" },
@@ -1535,7 +2083,7 @@ var flightSearch = defineCommand7({
1535
2083
  });
1536
2084
  }
1537
2085
  });
1538
- var hotelSearch = defineCommand7({
2086
+ var hotelSearch = defineCommand8({
1539
2087
  meta: { name: "search", description: "\uD638\uD154 \uAC80\uC0C9 (Amadeus)." },
1540
2088
  args: {
1541
2089
  city: { type: "string", required: true, description: "\uB3C4\uC2DC IATA \uB610\uB294 \uC774\uB984" },
@@ -1551,7 +2099,9 @@ var hotelSearch = defineCommand7({
1551
2099
  await runFunctionCommand({
1552
2100
  function: "search_hotels",
1553
2101
  args: {
1554
- city: args.city,
2102
+ // 서버 router는 args.city_code를 읽음 (router.ts:81). CLI --city 플래그명은
2103
+ // 사용자 친화 표기로 유지하되, 서버에 보낼 때 키를 정정한다.
2104
+ city_code: args.city,
1555
2105
  check_in_date: args["check-in"],
1556
2106
  check_out_date: args["check-out"],
1557
2107
  adults: args.adults ?? "2",
@@ -1569,7 +2119,7 @@ var hotelSearch = defineCommand7({
1569
2119
  });
1570
2120
  }
1571
2121
  });
1572
- var activitySearch = defineCommand7({
2122
+ var activitySearch = defineCommand8({
1573
2123
  meta: { name: "search", description: "\uC561\uD2F0\uBE44\uD2F0 \uAC80\uC0C9 (Amadeus)." },
1574
2124
  args: {
1575
2125
  city: { type: "string", required: true, description: "\uB3C4\uC2DC \uC774\uB984" },
@@ -1594,7 +2144,7 @@ var activitySearch = defineCommand7({
1594
2144
  });
1595
2145
  }
1596
2146
  });
1597
- var transferSearch = defineCommand7({
2147
+ var transferSearch = defineCommand8({
1598
2148
  meta: { name: "search", description: "\uAD50\uD1B5\uD3B8 \uAC80\uC0C9 (Amadeus, \uACF5\uD56D-\uC8FC\uC18C \uD53D\uC5C5 \uB4F1)." },
1599
2149
  args: {
1600
2150
  "start-time": { type: "string", required: true, description: "\uCD9C\uBC1C \uC2DC\uAC01 ISO" },
@@ -1620,12 +2170,19 @@ var transferSearch = defineCommand7({
1620
2170
  }
1621
2171
  await runFunctionCommand({
1622
2172
  function: "search_transfers",
2173
+ // 서버 router(router.ts:89-100)는 다음 키를 읽는다:
2174
+ // args.start_date_time, args.origin_iata, args.origin_address,
2175
+ // args.destination_address, args.destination_place_id,
2176
+ // args.transfer_type, args.passengers, args.currency
2177
+ // CLI 플래그명(--start-time, --from-*, --to-*)은 사용자 친화 표기로 유지하되,
2178
+ // 서버 전송 키를 router 시그니처에 맞춰 정정한다.
2179
+ // 주의: --to-iata는 서버에 destination IATA 인자가 없어 destination_place_id로
2180
+ // 매핑할 수 없다. 보존되지 않으므로 dispatched args에서 제외.
1623
2181
  args: {
1624
- start_time: args["start-time"],
1625
- from_iata: args["from-iata"],
1626
- from_address: args["from-address"],
1627
- to_iata: args["to-iata"],
1628
- to_address: args["to-address"],
2182
+ start_date_time: args["start-time"],
2183
+ origin_iata: args["from-iata"],
2184
+ origin_address: args["from-address"],
2185
+ destination_address: args["to-address"],
1629
2186
  passengers: args.passengers ?? "1",
1630
2187
  ...extraArgs
1631
2188
  },
@@ -1641,19 +2198,19 @@ var transferSearch = defineCommand7({
1641
2198
  });
1642
2199
  }
1643
2200
  });
1644
- var flightCommand = defineCommand7({
2201
+ var flightCommand = defineCommand8({
1645
2202
  meta: { name: "flight", description: "\uD56D\uACF5\uD3B8 \uAC80\uC0C9 / \uC0C1\uD0DC \uC870\uD68C." },
1646
2203
  subCommands: { search: flightSearch }
1647
2204
  });
1648
- var hotelCommand = defineCommand7({
2205
+ var hotelCommand = defineCommand8({
1649
2206
  meta: { name: "hotel", description: "\uD638\uD154 \uAC80\uC0C9." },
1650
2207
  subCommands: { search: hotelSearch }
1651
2208
  });
1652
- var activityCommand = defineCommand7({
2209
+ var activityCommand = defineCommand8({
1653
2210
  meta: { name: "activity", description: "\uC561\uD2F0\uBE44\uD2F0 \uAC80\uC0C9." },
1654
2211
  subCommands: { search: activitySearch }
1655
2212
  });
1656
- var transferCommand = defineCommand7({
2213
+ var transferCommand = defineCommand8({
1657
2214
  meta: { name: "transfer", description: "\uAD50\uD1B5\uD3B8 \uAC80\uC0C9." },
1658
2215
  subCommands: { search: transferSearch }
1659
2216
  });
@@ -1661,8 +2218,8 @@ var transferCommand = defineCommand7({
1661
2218
  // src/commands/travel.ts
1662
2219
  init_command_helpers();
1663
2220
  init_output();
1664
- import { defineCommand as defineCommand8 } from "citty";
1665
- var placeSearch = defineCommand8({
2221
+ import { defineCommand as defineCommand9 } from "citty";
2222
+ var placeSearch = defineCommand9({
1666
2223
  meta: { name: "search", description: "\uC7A5\uC18C \uAC80\uC0C9 (Google Places, \uB3C4\uC2DC \uB610\uB294 \uD604\uC7AC \uC704\uCE58 \uAE30\uC900)." },
1667
2224
  args: {
1668
2225
  query: { type: "positional", required: true, description: "\uAC80\uC0C9\uC5B4 \uB610\uB294 \uC7A5\uC18C \uC720\uD615" },
@@ -1688,7 +2245,7 @@ var placeSearch = defineCommand8({
1688
2245
  });
1689
2246
  }
1690
2247
  });
1691
- var placeSearchText = defineCommand8({
2248
+ var placeSearchText = defineCommand9({
1692
2249
  meta: { name: "search-text", description: "\uD14D\uC2A4\uD2B8 \uAC80\uC0C9 (\uB3C4\uC2DC \uBC94\uC704)." },
1693
2250
  args: {
1694
2251
  query: { type: "positional", required: true, description: "\uAC80\uC0C9\uC5B4" },
@@ -1712,7 +2269,7 @@ var placeSearchText = defineCommand8({
1712
2269
  });
1713
2270
  }
1714
2271
  });
1715
- var placeDetails = defineCommand8({
2272
+ var placeDetails = defineCommand9({
1716
2273
  meta: { name: "details", description: "\uC7A5\uC18C \uC0C1\uC138 \uC815\uBCF4 (place_id\uB85C \uC870\uD68C)." },
1717
2274
  args: {
1718
2275
  placeId: { type: "positional", required: true, description: "Google place_id" },
@@ -1738,11 +2295,11 @@ var placeDetails = defineCommand8({
1738
2295
  });
1739
2296
  }
1740
2297
  });
1741
- var placeCommand = defineCommand8({
2298
+ var placeCommand = defineCommand9({
1742
2299
  meta: { name: "place", description: "\uC7A5\uC18C \uAC80\uC0C9/\uC0C1\uC138." },
1743
2300
  subCommands: { search: placeSearch, "search-text": placeSearchText, details: placeDetails }
1744
2301
  });
1745
- var directionsCommand = defineCommand8({
2302
+ var directionsCommand = defineCommand9({
1746
2303
  meta: { name: "directions", description: "\uACBD\uB85C \uC548\uB0B4." },
1747
2304
  args: {
1748
2305
  from: { type: "string", required: true, description: "\uCD9C\uBC1C\uC9C0" },
@@ -1770,7 +2327,7 @@ var directionsCommand = defineCommand8({
1770
2327
  });
1771
2328
  }
1772
2329
  });
1773
- var weatherCommand = defineCommand8({
2330
+ var weatherCommand = defineCommand9({
1774
2331
  meta: { name: "weather", description: "\uB0A0\uC528 \uC870\uD68C." },
1775
2332
  args: {
1776
2333
  city: { type: "positional", required: true, description: "\uB3C4\uC2DC \uC774\uB984" },
@@ -1795,7 +2352,7 @@ var weatherCommand = defineCommand8({
1795
2352
  });
1796
2353
  }
1797
2354
  });
1798
- var flightStatusCommand = defineCommand8({
2355
+ var flightStatusCommand = defineCommand9({
1799
2356
  meta: { name: "flight-status", description: "\uD56D\uACF5\uD3B8 \uC0C1\uD0DC \uC870\uD68C." },
1800
2357
  args: {
1801
2358
  flightNumber: { type: "positional", required: true, description: "\uD56D\uACF5\uD3B8 \uBC88\uD638 (KE901)" },
@@ -1825,8 +2382,8 @@ var flightStatusCommand = defineCommand8({
1825
2382
  // src/commands/feed.ts
1826
2383
  init_command_helpers();
1827
2384
  init_output();
1828
- import { defineCommand as defineCommand9 } from "citty";
1829
- var list4 = defineCommand9({
2385
+ import { defineCommand as defineCommand10 } from "citty";
2386
+ var list4 = defineCommand10({
1830
2387
  meta: { name: "list", description: "\uD53C\uB4DC \uBAA9\uB85D." },
1831
2388
  args: {
1832
2389
  limit: { type: "string", description: "\uCD5C\uB300 \uD45C\uC2DC (\uAE30\uBCF8 20)" },
@@ -1854,7 +2411,7 @@ var list4 = defineCommand9({
1854
2411
  });
1855
2412
  }
1856
2413
  });
1857
- var post = defineCommand9({
2414
+ var post = defineCommand10({
1858
2415
  meta: { name: "post", description: "\uD53C\uB4DC \uAE00 \uC791\uC131." },
1859
2416
  args: {
1860
2417
  content: { type: "positional", required: true, description: "\uAE00 \uB0B4\uC6A9" },
@@ -1874,7 +2431,7 @@ var post = defineCommand9({
1874
2431
  });
1875
2432
  }
1876
2433
  });
1877
- var del3 = defineCommand9({
2434
+ var del3 = defineCommand10({
1878
2435
  meta: { name: "delete", description: "\uD53C\uB4DC \uAE00 \uC0AD\uC81C." },
1879
2436
  args: {
1880
2437
  id: { type: "positional", required: true, description: "\uD3EC\uC2A4\uD2B8 ID" },
@@ -1886,7 +2443,8 @@ var del3 = defineCommand9({
1886
2443
  async run({ args }) {
1887
2444
  await runFunctionCommand({
1888
2445
  function: "delete_feed_post",
1889
- args: { id: args.id },
2446
+ // 서버 router는 args.post_id를 읽음 (router.ts:284)
2447
+ args: { post_id: args.id },
1890
2448
  outputFlag: args.output,
1891
2449
  localeFlag: args.locale,
1892
2450
  tripFlag: args.trip,
@@ -1895,7 +2453,7 @@ var del3 = defineCommand9({
1895
2453
  });
1896
2454
  }
1897
2455
  });
1898
- var feedCommand = defineCommand9({
2456
+ var feedCommand = defineCommand10({
1899
2457
  meta: { name: "feed", description: "\uD53C\uB4DC \uAD00\uB9AC." },
1900
2458
  subCommands: { list: list4, post, delete: del3 }
1901
2459
  });
@@ -1903,8 +2461,8 @@ var feedCommand = defineCommand9({
1903
2461
  // src/commands/baby.ts
1904
2462
  init_command_helpers();
1905
2463
  init_output();
1906
- import { defineCommand as defineCommand10 } from "citty";
1907
- var list5 = defineCommand10({
2464
+ import { defineCommand as defineCommand11 } from "citty";
2465
+ var list5 = defineCommand11({
1908
2466
  meta: { name: "list", description: "\uB4F1\uB85D\uB41C \uC544\uC774 \uBAA9\uB85D." },
1909
2467
  args: {
1910
2468
  locale: { type: "string", description: "ko|en|es|fr" },
@@ -1926,7 +2484,7 @@ var list5 = defineCommand10({
1926
2484
  });
1927
2485
  }
1928
2486
  });
1929
- var info = defineCommand10({
2487
+ var info = defineCommand11({
1930
2488
  meta: { name: "info", description: "\uC544\uC774 \uC815\uBCF4 \uC870\uD68C (\uC774\uB984 \uB610\uB294 \uBAA8\uB4E0 \uC544\uC774)." },
1931
2489
  args: {
1932
2490
  name: { type: "string", description: "\uC544\uC774 \uC774\uB984 (\uBBF8\uC9C0\uC815 \uC2DC \uBAA8\uB4E0 \uC544\uC774)" },
@@ -1937,7 +2495,8 @@ var info = defineCommand10({
1937
2495
  async run({ args }) {
1938
2496
  await runFunctionCommand({
1939
2497
  function: "get_baby_info",
1940
- args: { name: args.name, field: args.field ?? "all" },
2498
+ // 서버 router는 args.name_or_id ?? args.baby_name ?? args.name로 selector를 읽음
2499
+ args: { name_or_id: args.name, field: args.field ?? "all" },
1941
2500
  outputFlag: args.output,
1942
2501
  localeFlag: args.locale,
1943
2502
  noTripContext: true,
@@ -1947,7 +2506,7 @@ var info = defineCommand10({
1947
2506
  });
1948
2507
  }
1949
2508
  });
1950
- var add4 = defineCommand10({
2509
+ var add4 = defineCommand11({
1951
2510
  meta: { name: "add", description: "\uC544\uC774 \uCD94\uAC00." },
1952
2511
  args: {
1953
2512
  name: { type: "string", required: true, description: "\uC774\uB984" },
@@ -1977,7 +2536,7 @@ var add4 = defineCommand10({
1977
2536
  });
1978
2537
  }
1979
2538
  });
1980
- var update3 = defineCommand10({
2539
+ var update3 = defineCommand11({
1981
2540
  meta: { name: "update", description: "\uC544\uC774 \uC815\uBCF4 \uC218\uC815." },
1982
2541
  args: {
1983
2542
  name: { type: "string", required: true, description: "\uC544\uC774 \uC774\uB984" },
@@ -1995,7 +2554,8 @@ var update3 = defineCommand10({
1995
2554
  }
1996
2555
  await runFunctionCommand({
1997
2556
  function: "update_baby_info",
1998
- args: { name: args.name, ...extraArgs },
2557
+ // 서버 router는 args.name_or_id ?? args.baby_name ?? args.name selector를 읽음
2558
+ args: { name_or_id: args.name, ...extraArgs },
1999
2559
  outputFlag: args.output,
2000
2560
  localeFlag: args.locale,
2001
2561
  noTripContext: true,
@@ -2003,7 +2563,7 @@ var update3 = defineCommand10({
2003
2563
  });
2004
2564
  }
2005
2565
  });
2006
- var status = defineCommand10({
2566
+ var status = defineCommand11({
2007
2567
  meta: { name: "status", description: "\uC5EC\uD589 \uC911 \uC544\uC774 \uC0C1\uD0DC \uC5C5\uB370\uC774\uD2B8." },
2008
2568
  args: {
2009
2569
  name: { type: "string", required: true, description: "\uC544\uC774 \uC774\uB984" },
@@ -2015,7 +2575,8 @@ var status = defineCommand10({
2015
2575
  async run({ args }) {
2016
2576
  await runFunctionCommand({
2017
2577
  function: "update_baby_status",
2018
- args: { name: args.name, status: args.text },
2578
+ // 서버 router는 args.name_or_id ?? args.baby_name ?? args.name로 selector를 읽음
2579
+ args: { name_or_id: args.name, status: args.text },
2019
2580
  outputFlag: args.output,
2020
2581
  localeFlag: args.locale,
2021
2582
  tripFlag: args.trip,
@@ -2023,7 +2584,7 @@ var status = defineCommand10({
2023
2584
  });
2024
2585
  }
2025
2586
  });
2026
- var note2 = defineCommand10({
2587
+ var note2 = defineCommand11({
2027
2588
  meta: { name: "note", description: "\uC5EC\uD589 \uBA54\uBAA8 \uCD94\uAC00." },
2028
2589
  args: {
2029
2590
  name: { type: "string", required: true, description: "\uC544\uC774 \uC774\uB984" },
@@ -2035,7 +2596,8 @@ var note2 = defineCommand10({
2035
2596
  async run({ args }) {
2036
2597
  await runFunctionCommand({
2037
2598
  function: "add_baby_travel_note",
2038
- args: { name: args.name, note: args.text },
2599
+ // 서버 router는 args.name_or_id ?? args.baby_name ?? args.name로 selector를 읽음
2600
+ args: { name_or_id: args.name, note: args.text },
2039
2601
  outputFlag: args.output,
2040
2602
  localeFlag: args.locale,
2041
2603
  tripFlag: args.trip,
@@ -2043,7 +2605,7 @@ var note2 = defineCommand10({
2043
2605
  });
2044
2606
  }
2045
2607
  });
2046
- var del4 = defineCommand10({
2608
+ var del4 = defineCommand11({
2047
2609
  meta: { name: "delete", description: "\uC544\uC774 \uC815\uBCF4 \uC0AD\uC81C." },
2048
2610
  args: {
2049
2611
  name: { type: "positional", required: true, description: "\uC544\uC774 \uC774\uB984" },
@@ -2054,7 +2616,8 @@ var del4 = defineCommand10({
2054
2616
  async run({ args }) {
2055
2617
  await runFunctionCommand({
2056
2618
  function: "delete_baby",
2057
- args: { name: args.name },
2619
+ // 서버 router는 args.name_or_id를 읽음 (router.ts:338)
2620
+ args: { name_or_id: args.name },
2058
2621
  outputFlag: args.output,
2059
2622
  localeFlag: args.locale,
2060
2623
  noTripContext: true,
@@ -2063,7 +2626,7 @@ var del4 = defineCommand10({
2063
2626
  });
2064
2627
  }
2065
2628
  });
2066
- var babyCommand = defineCommand10({
2629
+ var babyCommand = defineCommand11({
2067
2630
  meta: { name: "baby", description: "\uC544\uC774 \uC815\uBCF4 \uAD00\uB9AC." },
2068
2631
  subCommands: { list: list5, info, add: add4, update: update3, status, note: note2, delete: del4 }
2069
2632
  });
@@ -2071,8 +2634,8 @@ var babyCommand = defineCommand10({
2071
2634
  // src/commands/social.ts
2072
2635
  init_command_helpers();
2073
2636
  init_output();
2074
- import { defineCommand as defineCommand11 } from "citty";
2075
- var familyList = defineCommand11({
2637
+ import { defineCommand as defineCommand12 } from "citty";
2638
+ var familyList = defineCommand12({
2076
2639
  meta: { name: "list", description: "\uAC00\uC871 \uAD6C\uC131\uC6D0 \uBAA9\uB85D." },
2077
2640
  args: {
2078
2641
  locale: { type: "string", description: "ko|en|es|fr" },
@@ -2094,7 +2657,7 @@ var familyList = defineCommand11({
2094
2657
  });
2095
2658
  }
2096
2659
  });
2097
- var familyAddToTrip = defineCommand11({
2660
+ var familyAddToTrip = defineCommand12({
2098
2661
  meta: { name: "add-to-trip", description: "\uAC00\uC871 \uAD6C\uC131\uC6D0\uC744 \uD65C\uC131 trip\uC5D0 \uCD94\uAC00." },
2099
2662
  args: {
2100
2663
  names: { type: "string", description: "\uC27C\uD45C \uAD6C\uBD84 \uC774\uB984 \uBAA9\uB85D (\uBBF8\uC9C0\uC815 \uC2DC \uBAA8\uB4E0 \uAC00\uC871)" },
@@ -2105,7 +2668,8 @@ var familyAddToTrip = defineCommand11({
2105
2668
  async run({ args }) {
2106
2669
  await runFunctionCommand({
2107
2670
  function: "add_family_to_trip",
2108
- args: { names: args.names },
2671
+ // 서버 router는 args.member_names를 읽음 (router.ts:430)
2672
+ args: { member_names: args.names },
2109
2673
  outputFlag: args.output,
2110
2674
  localeFlag: args.locale,
2111
2675
  tripFlag: args.trip,
@@ -2113,11 +2677,11 @@ var familyAddToTrip = defineCommand11({
2113
2677
  });
2114
2678
  }
2115
2679
  });
2116
- var familyCommand = defineCommand11({
2680
+ var familyCommand = defineCommand12({
2117
2681
  meta: { name: "family", description: "\uAC00\uC871 \uAD00\uB9AC." },
2118
2682
  subCommands: { list: familyList, "add-to-trip": familyAddToTrip }
2119
2683
  });
2120
- var friendList = defineCommand11({
2684
+ var friendList = defineCommand12({
2121
2685
  meta: { name: "list", description: "\uCE5C\uAD6C \uBAA9\uB85D." },
2122
2686
  args: {
2123
2687
  locale: { type: "string", description: "ko|en|es|fr" },
@@ -2139,7 +2703,7 @@ var friendList = defineCommand11({
2139
2703
  });
2140
2704
  }
2141
2705
  });
2142
- var friendAdd = defineCommand11({
2706
+ var friendAdd = defineCommand12({
2143
2707
  meta: { name: "add", description: "\uCE5C\uAD6C \uCD94\uAC00 (\uC774\uBA54\uC77C)." },
2144
2708
  args: {
2145
2709
  email: { type: "positional", required: true, description: "\uC774\uBA54\uC77C \uC8FC\uC18C" },
@@ -2157,11 +2721,11 @@ var friendAdd = defineCommand11({
2157
2721
  });
2158
2722
  }
2159
2723
  });
2160
- var friendCommand = defineCommand11({
2724
+ var friendCommand = defineCommand12({
2161
2725
  meta: { name: "friend", description: "\uCE5C\uAD6C \uAD00\uB9AC." },
2162
2726
  subCommands: { list: friendList, add: friendAdd }
2163
2727
  });
2164
- var inviteList = defineCommand11({
2728
+ var inviteList = defineCommand12({
2165
2729
  meta: { name: "list", description: "\uD65C\uC131 trip\uC758 \uCD08\uB300 \uBAA9\uB85D." },
2166
2730
  args: {
2167
2731
  trip: { type: "string", description: "Trip UUID" },
@@ -2184,7 +2748,7 @@ var inviteList = defineCommand11({
2184
2748
  });
2185
2749
  }
2186
2750
  });
2187
- var inviteCreate = defineCommand11({
2751
+ var inviteCreate = defineCommand12({
2188
2752
  meta: { name: "create", description: "trip \uCD08\uB300 \uC0DD\uC131." },
2189
2753
  args: {
2190
2754
  email: { type: "string", description: "\uCD08\uB300\uD560 \uC774\uBA54\uC77C (\uC0DD\uB7B5 \uC2DC \uACF5\uAC1C \uCF54\uB4DC \uBC1C\uAE09)" },
@@ -2210,15 +2774,15 @@ var inviteCreate = defineCommand11({
2210
2774
  });
2211
2775
  }
2212
2776
  });
2213
- var inviteCommand = defineCommand11({
2777
+ var inviteCommand = defineCommand12({
2214
2778
  meta: { name: "invite", description: "\uCD08\uB300 \uAD00\uB9AC." },
2215
2779
  subCommands: { list: inviteList, create: inviteCreate }
2216
2780
  });
2217
2781
 
2218
2782
  // src/commands/web.ts
2219
2783
  init_command_helpers();
2220
- import { defineCommand as defineCommand12 } from "citty";
2221
- var webCommand = defineCommand12({
2784
+ import { defineCommand as defineCommand13 } from "citty";
2785
+ var webCommand = defineCommand13({
2222
2786
  meta: { name: "web", description: "\uC6F9 \uAC80\uC0C9 (Perplexity)." },
2223
2787
  args: {
2224
2788
  query: { type: "positional", required: true, description: "\uAC80\uC0C9\uC5B4" },
@@ -2251,8 +2815,8 @@ var webCommand = defineCommand12({
2251
2815
 
2252
2816
  // src/commands/raw.ts
2253
2817
  init_command_helpers();
2254
- import { defineCommand as defineCommand13 } from "citty";
2255
- var rawCommand = defineCommand13({
2818
+ import { defineCommand as defineCommand14 } from "citty";
2819
+ var rawCommand = defineCommand14({
2256
2820
  meta: {
2257
2821
  name: "raw",
2258
2822
  description: "\uD568\uC218\uB97C \uC774\uB984\uC73C\uB85C \uC9C1\uC811 \uD638\uCD9C (escape hatch)."
@@ -2297,8 +2861,8 @@ init_config();
2297
2861
  init_output();
2298
2862
  init_exit_codes();
2299
2863
  init_prompt();
2300
- import { defineCommand as defineCommand14 } from "citty";
2301
- async function* parseSseStream(response) {
2864
+ import { defineCommand as defineCommand15 } from "citty";
2865
+ async function* parseSseStream2(response) {
2302
2866
  if (!response.body) return;
2303
2867
  const reader = response.body.getReader();
2304
2868
  const decoder = new TextDecoder();
@@ -2357,7 +2921,7 @@ async function streamChatTurn(history, options) {
2357
2921
  let reply = "";
2358
2922
  const toolCalls = [];
2359
2923
  const c = colorize({ mode: options.mode, color: options.color });
2360
- for await (const ev of parseSseStream(response)) {
2924
+ for await (const ev of parseSseStream2(response)) {
2361
2925
  if (ev.event === "text") {
2362
2926
  const chunk = String(ev.data.text ?? "");
2363
2927
  reply += chunk;
@@ -2376,7 +2940,7 @@ async function streamChatTurn(history, options) {
2376
2940
  if (options.mode === "pretty") process.stdout.write("\n");
2377
2941
  return { reply, toolCalls };
2378
2942
  }
2379
- var chatCommand = defineCommand14({
2943
+ var chatCommand = defineCommand15({
2380
2944
  meta: { name: "chat", description: "AI \uCC57 (\uC790\uC5F0\uC5B4 \u2192 \uD568\uC218 \uD638\uCD9C). \uC778\uC790 \uC788\uC73C\uBA74 single-shot." },
2381
2945
  args: {
2382
2946
  prompt: { type: "positional", required: false, description: "\uC9C8\uBB38 (\uC0DD\uB7B5 \uC2DC \uBA40\uD2F0\uD134 REPL)" },
@@ -2427,7 +2991,7 @@ var chatCommand = defineCommand14({
2427
2991
  });
2428
2992
 
2429
2993
  // src/commands/completion.ts
2430
- import { defineCommand as defineCommand15 } from "citty";
2994
+ import { defineCommand as defineCommand16 } from "citty";
2431
2995
  var COMMAND_TREE = {
2432
2996
  auth: ["login", "logout", "whoami", "tokens"],
2433
2997
  config: ["get", "set", "path"],
@@ -2525,7 +3089,7 @@ function fishCompletion() {
2525
3089
  }
2526
3090
  return lines.join("\n") + "\n";
2527
3091
  }
2528
- var completionCommand = defineCommand15({
3092
+ var completionCommand = defineCommand16({
2529
3093
  meta: { name: "completion", description: "\uC178 \uC790\uB3D9\uC644\uC131 \uC2A4\uD06C\uB9BD\uD2B8 \uCD9C\uB825 (bash|zsh|fish)." },
2530
3094
  args: {
2531
3095
  shell: { type: "positional", required: true, description: "bash | zsh | fish" }
@@ -2552,7 +3116,7 @@ var completionCommand = defineCommand15({
2552
3116
  });
2553
3117
 
2554
3118
  // src/index.ts
2555
- var main = defineCommand16({
3119
+ var main = defineCommand17({
2556
3120
  meta: {
2557
3121
  name: "dodo",
2558
3122
  version: "0.1.0",
@@ -2564,6 +3128,7 @@ var main = defineCommand16({
2564
3128
  config: configCommand,
2565
3129
  // 핵심 데이터
2566
3130
  trip: tripCommand,
3131
+ profile: profileCommand,
2567
3132
  expense: expenseCommand,
2568
3133
  booking: bookingCommand,
2569
3134
  itinerary: itineraryCommand,